面试时,你被要求手写常见原理了吗?

如今前端工程师的技术要求越来越高,会使用常见的API已经不能满足现如今前端日益快速发展的脚步。现在中大厂基本都会要求面试者手写前端常见API的原理,以此来证明你对该知识点的理解程度。接下来,我将列举我面试时以及自认为比较重要的CSS部分、JS部分常见手写原理题!

CSS部分
经典Flex布局
如今Flex布局不管是移动端还是PC端的应用已经非常广泛了,下面我列举几个平时项目中非常常见的几个需求。以下例子我们都以Vue项目为例~

flex布局均匀分布后换行问题

需求一:ul下有多个li,每三个li排一列,多余的换行显示。
很显然,绝大部分的小伙伴都会使用Flex布局,很显然会出现一个问题就是如果li是3的倍数的话就能正常显示,若不是的话,布局就不是产品经理满意的结果。
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
复制代码
解决方案:
我们在ul的底部新增li,个数为数组总长度%3的余数即可。

  • 复制代码 两栏布局

    两栏布局:左右两栏,左边固定,右边自适应

    效果图

    第一种方式 — 浮动
    HTML部分:

    1-left
    1-right
    复制代码CSS部分: .outer1 .left { width: 200px; float: left; } .outer1 .right { width: auto; margin-left: 200px; } 复制代码

    第二种方式 — flex
    HTML部分:

    2-left
    2-right
    复制代码CSS部分: .outer2 { display: flex; } .outer2 .left { flex: 0 0 200px; /* flex-grow: 0; flex-shrink:0; flex-basis:200px; */ } .outer2 .right { flex: auto; }

    注意:flex: 0 0 200px是flex: flex-grow flex-shrink flex-basis的简写
    复制代码

    第三种方式 — position
    HTML部分:

    3-left
    3-right
    复制代码CSS部分: .outer3 { position: relative; } .outer3 .left { position: absolute; width: 200px; } .outer3 .right { margin-left: 200px; } 复制代码

    第四种方式 — position again
    HTML部分:

    4-left
    4-right
    复制代码CSS部分: .outer4 { position: relative; } .outer4 .left { width: 200px; } .outer4 .right { position: absolute; top: 0; left: 200px; right: 0; } 复制代码

    三栏布局

    三栏布局: 中间列自适应宽度,旁边两侧固定宽度

    效果图

    第一种方式 — 定位
    HTML部分:

    1-left
    1-middle
    1-right
    复制代码CSS部分: .outer1 { position: relative; } .outer1 .left { position: absolute; width: 100px; } .outer1 .middle { margin: 0 200px 0 100px; } .outer1 .right { position: absolute; width: 200px; top: 0; right: 0; } 注意:左右分别使用绝对定位,中间设置外边距 复制代码

    第二种方式 — flex布局
    HTML部分:

    2-left
    2-middle
    2-right
    复制代码CSS部分: .outer2 { display: flex; } .outer2 .left { flex: 0 0 100px; } .outer2 .middle { flex: auto; } .outer2 .right { flex: 0 0 200px; } 复制代码

    第三种方式 — 浮动原理
    HTML部分:

    3-left
    3-right
    3-middle
    复制代码CSS部分: .outer3 .left{ float: left; width: 100px; } .outer3 .right { float: right; width: 200px; } .outer3 .middle { margin: 0 200px 0 100px; } 复制代码

    圣杯布局

    圣杯布局: 中间的优先渲染,独立的左中右结构

    具体实现圣杯布局的步骤:

    让左右浮动在一行显示,相对定位
    让中间模块的middle宽度为100%
    让左边的色块移动到middle前面,margin-left:-100%
    让右边的色块移动到middle的后面,margin-left:-宽度
    给三个小块的父元素加一个内填充的属性padding,为的是填充挤到中间
    给左边的块移动到左边left:-200px, 给右边的块移动到右边right:-200px

    效果图

    HTML部分:

    header
    midlle
    left
    right
    footer
    复制代码CSS部分: header, footer { height: 100px; width: 100%; background-color: antiquewhite; } .container { height: 200px; padding-left: 200px; padding-right: 300px; } .container > div { float: left; position: relative; height: 100%; } .left { width: 200px; height: 200px; background-color: burlywood; margin-left: -100%; left: -200px; } .right { width: 300px; height: 200px; background-color: burlywood; margin-left: -300px; right: -300px; } .middle { width: 100%; height: 200px; background-color: #b0f9c2; } 复制代码双飞翼布局

    双飞翼布局

    具体实现双飞翼布局的步骤:

    给左,中,右 加浮动,在一行显示
    给middle宽度为100%
    让左边的模块移动middle的左边 margin-left:-100%
    让右边的模块移动middle的右边 margin-left:-自己宽度
    给middle里面的容器添加外间距 margin: 左右

    效果:

    html部分

    中间
    左边
    右边
    复制代码css部分 .main>div { float:left; position: relative; height: 300px; } .middle { width: 100%; background-color: lightgreen } .left { width:200px; margin-left:-100%; background-color:#b0f9c2 } .right { width: 200px; margin-left:-200px; background-color:pink } .middle-inner{ margin:0 200px; background-color: burlywood; height:300px; } 复制代码水平垂直居中 html部分
    石小明
    复制代码css部分 公共部分 body { width: 100vw; height: 100vh; overflow: hidden; } .box { box-sizing: border-box; width: 100px; height: 50px; line-height: 50px; text-align: center; font-size: 16px; border: 1px solid lightblue; background: lightcyan; } 复制代码第一种:定位 .box { position: absolute; top: 50%; left: 50%; margin-left: -50px; margin-top: -25px; } 复制代码注意:上面的方式是一定要知道具体的宽高。但下面的方式是知道宽高,但是没有用到宽高。 第二种:flex body { display: flex; justify-content: center; align-items: center; } 复制代码注意:这种方式也是兼容性不是很好 第三种:JavaScript let html = document.documentElement, winW = html.clientWidth, winH = html.clientHeight, boxW = box.offsetWidth, // offsetWidth带边框 boxH = box.offsetHeight;
            box.style.position = 'absolute';
            box.style.left = (winW - boxW) / 2 + 'px';
            box.style.top = (winH - boxH) / 2 + 'px';
    

    复制代码第四种:table-cell
    body {
    display: table-cell;
    vertical-align: middle;
    text-align: center;
    }
    复制代码
    JS 部分
    统计网页中出现的标签
    实现步骤:

    获取所有的DOM节点
    NodeList集合转化为数组
    获取数组每个元素的标签名
    去重

    new Set([…document.querySelectorAll(’*’)].map(ele=>ele.tagName)).size
    复制代码
    JS深浅拷贝
    对象深浅拷贝,是面试常见的面试题之一。
    原对象:
    let obj = {
    a: 100,
    b: [100, 200, 300],
    c: {
    x: 10
    },
    d: /^\d+$/
    }
    复制代码
    浅克隆

    浅克隆 只克隆第一层
    方法一:
    let obj2 = {…obj};
    复制代码方法二:
    let obj2 = {};
    for(let key in obj) {
    if(!obj.hasOwnProperty(key)) break;
    obj2[key] = obj[key];
    }
    复制代码
    深克隆

    注意:在函数、日期、正则表达式时,JSON.stringify时,都会被转换成对象{}
    方法一:
    let obj3 = JSON.parse(JSON.stringify(obj));
    复制代码方法二:
    function deepClone(obj) {
    // 过滤一些特殊情况
    if(obj === null) return null;
    if(typeof obj !== “object”) return obj;
    if (typeof window !== ‘undefined’ && window.JSON) { // 浏览器环境下 并支持window.JSON 则使用 JSON
    return JSON.parse(JSON.stringify(obj));
    }
    if(obj instanceof RegExp) { // 正则
    return new RegExp(obj);
    }
    if(obj instanceof Date) { // 日期
    return new Date(obj);
    }
    // let newObj = {}
    // let newObj = new Object()
    let newObj = new obj.constructor; // 不直接创建空对象的目的:克隆的结果和之前保持所属类 =》 即能克隆普通对象,又能克隆某个实例对象
    for(let key in obj) {
    if(obj.hasOwnProperty(key)) {
    newObj[key] = deepClone(obj[key]);
    }
    }
    // let newObj = obj.constructor === Array ? [] : {};
    //for(let key in obj) {
    // newObj[key] = typeof obj[key] === ‘object’ ? deepCopy(obj[key]) : //obj[key];
    //}
    return newObj;
    }
    复制代码原生Ajax

    一个完整的 ajax 请求一般包括以下步骤:

    实例化 XMLHttpRequest 对象
    连接服务器
    发送请求
    介绍

    function ajax(options) {
    let method = options.method || ‘GET’, // 不传则默认为GET请求
    params = options.params, // GET请求携带的参数
    data = options.data, // POST请求传递的参数
    url = options.url + (params ? ‘?’ + Object.keys(params).map(key => key + ‘=’ + params[key]).join(’&’) : ‘’),
    async = options.async === false ? false : true,
    success = options.success,
    headers = options.headers;

    let xhr;
    // 创建xhr对象
    if(window.XMLHttpRequest) {
    xhr = new XMLHttpRequest();
    } else {
    xhr = new ActiveXObject(‘Microsoft.XMLHTTP’);
    }

    xhr.onreadystatechange = function() {
    if(xhr.readyState === 4 && xhr.status === 200) {
    success && success(xhr.responseText);
    }
    }

    xhr.open(method, url, async);

    if(headers) {
    Object.keys(Headers).forEach(key => xhr.setRequestHeader(key, headers[key]))
    }

    method === ‘GET’ ? xhr.send() : xhr.send(data)
    }
    复制代码注意:IE5、6不兼容XMLHttpRequest,所以要使用ActiveXObject()对象,并传入 ‘Microsoft.XMLHTTP’,达到兼容目的。

    readyState的五种状态详解:
    0 - (未初始化)还没有调用send()方法
    1 - (载入)已调用send()方法,正在发送请求
    2 - (载入完成)send()方法执行完成,已经接收到全部响应内容
    3 - (交互)正在解析响应内容
    4 - (完成)响应内容解析完成,可以在客户端调用了

    防抖和节流
    如今前端界面效果越来越复杂,有一些频繁操作会导致页面性能和用户体验度低。像:输入框搜索会频繁调端口接口、放大缩小窗口等。

    防抖 - debounce 当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。

    const debounce = (fn, delay) => {
    let timer = null;
    return (…args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
    fn.apply(this, args);
    }, delay);
    };
    };
    复制代码
    节流 - throttle 当持续触发事件时,保证一定时间段内只调用一次事件处理函数。

    const throttle = (fn, delay = 500) => {
    let flag = true;
    return (…args) => {
    if (!flag) return;
    flag = false;
    setTimeout(() => {
    fn.apply(this, args);
    flag = true;
    }, delay);
    };
    };
    复制代码解析 URL 参数
    function parseParam(url) {
    // 将浏览器地址中 ‘?’ 后面的字符串取出来
    const paramsStr = /.+?(.+)KaTeX parse error: Expected 'EOF', got '&' at position 34: … // 将截取的字符串以 ‘&̲’ 分割后存到数组中 …/.test(val) ? parseFloat(val) : val;
    // 判断存放对象中是否存在key属性
    if (paramsObj.hasOwnProperty(key)) {
    // 存在的话就存放一个数组
    paramsObj[key] = [].concat(paramsObj[key], val);
    } else {
    // 不存在就存放一个对象
    paramsObj[key] = val;
    }
    } else {
    // 没有value的情况
    paramsObj[param] = true;
    }
    })
    return paramsObj;
    }
    复制代码let url = ‘https://www.baidu.com?username=%22tmc%22&password=%22123456%22&dutiy=%E5%89%8D%E7%AB%AF%E6%94%BB%E5%9F%8E%E7%8B%AE&flag’;
    console.log(parseParam(url))

    { username: ‘“tmc”’,
    password: ‘“123456”’,
    dutiy: ‘前端攻城狮’,
    flag: true
    }
    复制代码Jsonp的原理
    function jsonp({url, params, cb}) {
    return new Promise((resolve, reject) => {
    window[cb] = function (data) { // 声明全局变量
    resolve(data)
    document.body.removeChild(script)
    }
    params = {…params, cb}
    let arrs = []
    for(let key in params) {
    arrs.push(${key}=${params[key]})
    }
    let script = document.createElement(‘script’)
    script.src = ${url}?${arrs.join('&')}
    document.body.appendChild(script)
    })
    }
    复制代码jsonp的缺点:

    只能发送Get请求 不支持post put delete
    不安全 xss攻击

    apply的原理
    apply 的实现原理和 call 的实现原理差不多,只是参数形式不一样。— 数组
    Function.prototype.apply = function(content = window) {
    content.fn = this;
    let result;
    // 判断是否有第二个参数
    if(arguments[1]) {
    result = content.fn(…arguments[1]);
    } else {
    result = content.fn();
    }
    delete content.fn;
    return result;
    }
    复制代码注意:当apply传入的第一个参数为null时,函数体内的this会指向window。
    bind的原理

    bind 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。

    Function.prototype.bind = function(content) {
    if(typeof this != ‘function’) {
    throw Error(‘not a function’);
    }
    let _this = this;
    let args = […arguments].slice(1);
    return function F() {
    // 判断是否被当做构造函数使用
    if(this instanceof F) {
    return _this.apply(this, args.concat([…arguments]))
    }
    return _this.apply(content, args.concat([…arguments]))
    }
    }
    复制代码call的原理

    call语法:fun.call(thisArg, arg1, arg2, arg3, …)

    call 的核心原理:

    将函数设为对象的属性
    执行和删除这个函数
    指定this到函数并传入给定参数执行函数
    如果不传参数,默认指向window

    Function.prototype.call2 = function(content = window) {
    // 判断是否是underfine和null
    // if(typeof content === ‘undefined’ || typeof content === null){
    // content = window
    // }
    content.fn = this;
    let args = […arguments].slice(1);
    let result = content.fn(…args);
    delete content.fn;
    return result;
    }

    发布了15 篇原创文章 · 获赞 0 · 访问量 842

    猜你喜欢

    转载自blog.csdn.net/srhgsr/article/details/105206558
    今日推荐