前端基础,你都掌握了么?

127 阅读17分钟

HTML、CSS

HTML

1、如何理解HTML语义化?

两个方面来理解:

  • 让代码更易理解,更具有可读性。
  • 让搜索引擎更容易读懂,也就是方便做SEO

2、默认情况下,哪些HTML标签是块级元素,哪些是内联元素?

  • 块级元素:display:block/table;有div、h1、h2、table、ul、ol、p等。
  • 内联元素:display:inline-block/inline;有span、img、input、button、i、b等。

CSS

布局

1、盒子模型的宽度如何计算?(盒子模型的考察)

offsetWidth = width + padding + border + scrollbar(如果有)

拓展:box-sizing中的两个属性content-box和border-box的区别是什么?

  • 浏览器默认设置的是content-box,即width就是内容的宽度,如果设置padding和border,会在元素内容区域外叠加宽度。
  • 如果设置为border-box,padding和border会在width内绘制,也就是说width其实包含了内容区、padding以及border。

2、观察以下代码,AAA和BBB的距离是多少?(margin纵向重叠的问题)

<style>
    p {
        font-size: 16px;
        line-height: 1;
        margin-bottom: 15px;
        margin-top: 10px;
    }
</style>
<p>AAA</p>
<p></p>
<p></p>
<p></p>
<p>BBB</p>

答案:15px

解析:1、相邻元素的margin-top和margin-bottom会重叠。2、空白元素内容的

也会重叠。

3、margin-top、margin-left、margin-bottom、margin-right分别设置负值会怎么样(margin负值的问题)

  • margin-top 设置负值,元素会往上移。
  • margin-left 设置负值,元素会往左移。
  • margin-rigt 设置负值,右侧元素往左移,元素自身不动。
  • margin-bottom 设置负值,下侧元素往上移,元素自身不动。

4、什么是BFC?如何应用?(BFC的理解和应用)

  • BFC 即Block format context,块级格式化上下文。它是一块独立渲染区,内部元素的渲染不会影响边界以外的元素。
  • 形成BFC的常见条件:①float不是none;②position不是absolute或fixed③overflow不是是visible④display是inline-block或flex。
  • 应用:清除浮动。

5、手写圣杯布局和双飞翼布局 (float布局的问题)

圣杯布局和双飞翼布局的目的:

  • 三栏布局,中间一栏最先加载和渲染
  • 两边内容固定,中间内容随着宽度自适应
  • 一般用于PC网页

圣杯布局和双飞翼布局的技术总结:

  • 使用float布局
  • 两侧使用margin负值,以便和中间内容横向重叠。
  • 为了防止中间内容被两侧覆盖,一个用padding,一个用margin

圣杯布局:

<style>
    body {
      min-width: 550px;
    }

    #header, #footer {
      background: grey;
      text-align: center;
    }
    
    #container {
        padding-left: 200px;
        padding-right: 150px;
    }
    
    #container .column {
      float: left;
    }
    
    #center {
      background: yellow;
      width: 100%;
    }
    
    #left {
      position: relative;
      background: red;
      margin-left: -100%;
      right: 200px;
      width: 200px;
    }
    
    #right {
      background: blue;
      margin-right: -150px;
      width: 150px;
    }

    #footer {
      clear: both;
    }
</style>

<body>
    <div id="header">
      Header
    </div>
    <div id="container">
      <div id="center" class="column">Center</div>
      <div id="left" class="column">Left</div>
      <div id="right" class="column">Right</div>
    </div>
    <div id="footer">
      Footer
    </div>
</body>

双飞翼布局:

<style>
    body {
      min-width: 550px;
    }
    
    .col {
      float: left;
      height: 200px;
      text-align: center;
    }
    
    #main {
        background: yellow;
        width: 100%;
    }
    
    #main-wrap {
      margin: 0 200px 0 200px;
    }
    
    #left {
      background: red;
      margin-left: -100%;
      width: 200px;
    }
    
    #right {
      background: blue;
      margin-left: -200px;
      width: 200px;
    }
</style>

<body>
    <div id="main" class="col">
      <div id="main-wrap">
        this is main.
      </div>
    </div>
    <div id="left" class="col">
      this is left
    </div>
    <div id="right" class="col">
      this is right
    </div>
</body>

6、手写一个clearfix (clearfix的考察)

.clearFix::after {
    content: "";
    display: table;
    clear: both;
}

定位

7、absolute和relative分别依据什么定位?

  • relative是依据自身去定位。
  • absolute是依据最近一层的定位元素定位。

8、居中对齐的方式有哪些?

水平居中:

  • inline元素: text-align: center。
  • block元素: margin:auto。
  • absolute元素:left:50%; + margin-left 负的一半的宽度。
  • absolute元素:left:50%; + transform: translateX(-50%)。
  • absolute元素:left:0;right:0;margin:auto;

垂直居中:

  • inline元素:将line-height和height设置的一样。
  • absolute元素:top:50%; + margin-top 负的一半的高度。
  • absolute元素:top:50%; + transform: translateY(-50%)。
  • absolute元素:top:0;bottom:0;margin:auto;

图文样式

9、line-height 如何继承?

如下代码P标签的行高是多少?

<style>
    body {
        font-size: 20px;
        line-height: 200%;
    }
    
    p {
        font-size: 16px;
    }
</style>
<body>
    <p>AAAA</p>
</body>

答案:40px。 line-height的继承原则:

  • 具体数值,如30px,直接继承该数值。
  • 具体比例,如1.5,直接继承该比例。
  • 百分比,如200%,继承计算后的值。

响应式

10、什么是rem?em与rem的区别?

  • rem是个相对长度单位,相对于根元素,常用于响应式布局。
  • em是个相对长度单位,相对于父元素,不常用。

11、单位vw、vh

  • 1vw是网页视口宽度的1/100。
  • 1vh是网页视口高度的1/100。
  • vmax取两者最大值,vmin取两者最小值。

CSS3

12、CSS3新增伪类有哪些?

  • first-of-type
  • last-of-type
  • nth-child(n)
  • only-of-type
  • only-child
  • enabled
  • disabled
  • checked
  • before
  • after

13、CSS3新增特性(参考掘金收藏)

JavaScript

JS基础

变量的基本类型和计算

1、typeof 能判断哪些类型

  • 识别所有值类型
  • 识别所有函数
  • 判断是否是引用类型

2、何时使用===, 什么时候使用==?

除了 == null之外,其他时候都用 ===。if(obj.a == null) {} 相当于if(obj.a === null || obj.a === undefined){}

3、手写一个深拷贝。

    const obj1 = {
        age: 20,
        name: 'XXX',
        address: {
            city: 'shangHai'
        },
        arr: ['a', 'b', 'c']
    };
    
    const obj2 = deepClone(obj1);
    
    function deepClone(obj) {
        // obj 是null,或者是对象或者数组
        if (typeof obj !== 'object' || typeof obj == null) {
            return obj;
        }
        
        let result;
        
        if (obj instanceof Array) {
            result = [];
        } else {
            result = {};
        }
        
        for (const key in obj) {
            // 保证key不是原型的属性
            if (obj.hasOwnProperty(key)) {
                result[key] = deepClone(obj[key]);
            }
        }
        
        return result;
    }
    
    obj2.city = 'Beijing';
    console.log(obj2.city, obj1.city); // 'Beijing', 'ShangHai'
    

4、类型转换

  • 字符串拼接
  • ==
100 == '100' //true  
0 == '' //true 
false == '' //true
0 == false // true
null == undefined //true
  • if语句和逻辑运算
// 以下是falsely变量,除此之外都是truly变量。
!!0 === false
!! '' === false
!! null === false
!! undefined === false
!! false === false

原型和原型链

1、如何判断一个变量是不是数组

用instanceof Array来判断

2、class的原型本质,怎么理解?

原型关系:

  • 每个class都有显示原型Prototype
  • 每个实例都有隐式原型_proto_
  • 实例的_proto_指向对应的class的Prototype

基于原型的执行规则:

  • 获取属性或者执行方法时,先在自身属性和方法寻找,如果找不到则自动去_proto_中去寻找。

闭包和作用域

1、this的不同应用场景,如何取值?

应用场景:

  • 在class方法中调用
  • 箭头函数

取值:

  • 一、在一般函数方法中使用 this 指代全局对象
  • 二、作为对象方法调用,this 指代上级对象,数组同理
  • 三、函数作为window内置函数的回调函数调用:this指向window对象(setInterval、setTimeout 等)
  • 四、作为构造函数调用,this 指代 new 实例化的对象
  • 五、apply、call、bind改变函数的调用对象,此方法的第一个参数为改变后调用这个函数的对象
var x = 0;
function test(){
  console.log(this.x)
}
var obj = {}
obj.x = 1;
obj.m = test;
obj.m.apply(); //0
obj.m.apply(obj); //1
  • 六、匿名函数的执行环境具有全局性,this对象通常指向window对象

2、手写bind函数

Function.prototype.bind1 = function() {
    //将参数拆解为数组
    const args = Array.prototype.slice.call(arguments);
    //获取this(数组第一项)
    const t = args.shift();
    //fn1 中的bind(...)中的fn1
    const self = this;
    //返回一个函数
    return function() {
        return self.apply(t, args);
    }
}

3、实际开发中闭包的应用场景,举例说明。

  • 闭包隐藏数据,只提供API
function createCache() {
    const data = {};
    return {
        set: function(key, val) {
            data[key] = val;
        },
        get: funtion(key) {
            return data[key]
        }
    }
}

const c = createCache();
c.set('a', 100);
console.log(c.get('a'));

4、什么是自由变量?

  • 一个变量在当前作用域没有被定义,但是被使用了。
  • 向上级作用域,一层一层依次寻找,直至找到为止。
  • 如果到全局作用域依然没有找到,则报错 XX is not defined.

5、什么是闭包?

作用域应用的特殊情况,有两种表现:

  • 函数作为参数被传递。
  • 函数作为返回值被返回。
    function create(){
        const a = 100;
        return function(){
            console.log(a);
        }
    }
    
    const fn = create();
    const a = 200;
    fn(); //100
    function print(fn) {
        const a = 200;
        fn();
    }
    
    const a = 100;
    function fn() {
        console.log(a);
    }
    
    print(fn); //100

自由变量的查找, 是在函数定义的地方,向上级作用域查找,不是在执行的地方。

异步和同步

1、异步和同步的区别是是什么?

由于js是单线程,所以同步可能会发生代码的阻塞,但是异步不会。

2、手写用promise加载一张图片

    function loadImg(src){
        return new Promise((resolve, reject) => {
            const img = document.createElement('img');
            
            img.onload = () => {
                resolve(img);
            };
            
            img.onerror = () => {
                const err = '图片加载失败';
                
                reject(err);
            }
            
            img.src = src;
        });
    }
    
    const url = 'https://pics4.baidu.com/feed/902397dda144ad34944818f8148d3efc30ad8596.jpeg?token=456bb4130259b0a298dfbbb9e6c44aee&s=CF916A810AB7F0DE4215A59E0300C0A2';
    
    loadImg(url).then(img => {
        console.log(img.height);
        return img;
    }).then(img => {
        console.log(img.width);
    }).catch(err => {
        console.error(err);
    })

JS异步进阶

1、请描述event loop(事件循环/事件轮询)的机制,可画图

call stack、web apis、callback queue、event loop.

  • 1、同步代码,一行行放入call stack执行
  • 2、遇到异步会先记录下,然后等待执行时机
  • 3、时机到了之后,移入callback queue
  • 4、call stack 为空之后,启动event loop
  • 5、轮询查找callback queue, 如有则移动到call stack中执行
  • 6、继续轮询查找

2、什么是宏任务和微任务,两者有什么区别?

  • 宏任务:在DOM渲染后触发,是由浏览器规定的,有setTimeout、setInterval、Ajax、DOM事件
  • 微任务:在DOM渲染前触发,是由ES6语法规定的,有promise、async-await
  • 微任务执行时机比宏任务早。

3、Promise有哪三种状态?如何变化?

  • pending、resolved、rejected
  • pending -> resolved, pending -> rejected
  • 变化不可逆

4、场景题-promise then 和 catch的连接

// 第一题
Promise.resolve().then(() => {
    console.log(1);
}).catch(() => {
    console.log(2);
}).then(() => {
    console.log(3)
});// 1 2

// 第二题
Promise.resolve().then(() => {
    console.log(1);
    throw new Error('erro1');
}).catch(() => {
    console.log(2);
}).then(()) => {
    console.log(3);
}// 1 2 3

// 第三题
Promise.resolve().then(() => {
    console.log(1);
    throw new Error('erro1');
}).catch(() => {
    console.log(2);
}).catch(()) => {
    console.log(3);
}// 1 3

5、场景题-async/await 语法

// 第一题
async function fn() {
    return 100;
}

(async function() {
    const a = fn();
    const b = await fn();
})();// a 是个resolved状态的Promise,b是100

// 第二题
(async function() {
    console.log('start');
    const a = await 100;
    console.log('a', a);
    const b = await Promise.resolve(200);
    console.log('b', b);
    const c = await Promise.reject(300);
    console.log('c', c);
    console.log('end');
})();// start, a 100, b 200 报错终止后续执行

6、场景题-promise和setTimeout的顺序

console.log(100);

setTimeout(() => {
    console.log(200);
});

Promise.reslove().then(() => {
    console.log(300);
});

console.log(400);

// 100 400 300 200

7、场景题-外加async/await的顺序问题

async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}

async function async2() {
    console.log('async2');
}

setTimeout(() => {
    console.log('setTimeout');
}, 0);

console.log('script start');
async1();

//初始化promise时,被传入的函数会被立即执行
new Promise(function(resolve){
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});

console.log('script end');

// script start
// async1 start
// async2
// promise1
// script end 
// async1 end
// promise2 
// setTimeout

8、async-await和Promise有什么关系?

  • 执行async函数返回的是一个Promise对象。
  • await相当于Promise的then
  • try...catch可捕获异常,代替了Promise的catch

DOM

1、如何优化DOM操作性能?

  • 将DOM查询做缓存
// 不缓存查询结果
for (let i = 0; i < document.getElementsByTagName('p').length; i++) {
    //每次循环都会计算length,频繁查询dom.
}

// 缓存查询结果
const pList = document.getElementsByTagName('p');
const length = pList.length;

for (let i = 0; i < length; i++) {
    // 缓存length,只进行一次DOM查询
}
  • 将频繁操作改为一次性操作
const listNode = document.getElementById('list');
// 创建一个文档片段,此时还没有插入到DOM树中!
const frag = document.createDocumentFragment();

for (let i = 0; i < 10; i++) {
    const li = document.createElement('li');
    
    li.innerHTML = 'List item' + i;
    frag.apendChild(li);
}

// 都完成后一次性插入DOM
listNode.appendChild(frag);

2、attribute和property的区别

  • property,修改的是对象属性,不会体现到html结构中
  • attribute,修改的是html属性,会体现到HTML结构中
  • 两者都有可能引起DOM的重新渲染

BOM

1、如何识别浏览器的类型

// navigator
const ua = navigator.userAgent;
const isChrome = ua.indexOf('Chrome');

console.log(isChrome);

2、如何解析URL

  • location的各个api: href、protocol、pathname、search、hash
  • history的各个api: forward、back

事件

1、编写一个通用的事件监听函数

// 考虑用到事件代理的情况,传 selector 就代表用到事件代理了
function bindEvent(ele, type, selector, fn) {
    if (fn === null) {
        fn = selector;
        selector = null;
    }
    
    ele.addEventListener(type, event => {
        const target = event.target;
        
        if (selector) {
             if(target.matches(selector)) {
                fn.call(selector, event);
            }
        } else {
            fn.call(selector, event);
        }
    });
}

2、描述事件流

  • 有捕获阶段、目标阶段以及冒泡阶段。
  • 捕获从上至下,冒泡从下至上。

3、无限下拉的图片列表,如何监听每个图片的点击?

  • 运用事件代理
  • 获取target
  • 用matches去判断

4、什么是事件代理

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

Ajax

1、用XMLHttpRequest手写一个Ajax.

function ajax(url) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        
        xhr.open('GET', url, true);
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    resolve(
                        JSON.parse(xhr.responseText);
                    );
                } else if (xhr.status === 404) {
                    reject(new Error('404 not found'));
                }
            }
        };
        
        xhr.send(null);
    });
}

2、Ajax 核心API-XHR

xhr.readyState

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

xhr.statu

  • 2XX 表示成功处理请求,例如200
  • 3XX 重定向,例如301(永久),302(临时),304(资源未改变)
  • 4XX 客户端请求错误,例如404(找不到), 403(没有权限)
  • 5XX 服务端错误,例如500, 501, 504

3、什么是跨域

同源策略就是,ajax请求时,浏览器要求当前网页必须与server同源。同源:协议、域名、端口三者一致。

4、实现跨域的常见方式:Jsonp和cors

jsonp:

  • script不受跨域的限制
  • 服务端可以动态拼接返回数据

cors: 服务器设置http header的方式。

存储

1、描述cookie、sessionStorage、localStorage的区别

  • 容量上:sessionStorage和localStorage的存储最大都有5M, cookiede 存储大小最大只有4KB
  • 是否发送到服务端:http请求时cookie需要发送到服务端,增加了请求数据量, 而sessionStorage和localStorage不需要
  • 修改方式上:Cookie只能通过document.cookie的方式修改,非常简陋。sessionStorage和localStorage有简单易用的API:setItem、getItem。
  • 存储时效上:sessionStorage和localStorage的区别在于localStorage可以永久存储,sessionStorage是页面关闭后存储就消失了。

HTTP

1、HTTP的状态码有哪些?

  • 1xx,服务端收到请求
  • 2xx,请求成功,例如200
  • 3xx,重定向,例如301(永久),302(临时)、304(资源未修改)
  • 4xx,客户端错误,例如404
  • 5xx,服务端错误,例如500(服务器错误)、504(网关超时)、502(网关请求错误)

2、HTTP的methods有哪些?

  • Get 获取数据
  • Post 新建数据
  • patch/put 更新数据
  • Delete 删除数据

3、HTTP常见的Header有哪些?

Request Headers:

  • Accept 浏览器可接收的数据格式。
  • Accept-Encoding 浏览器可接收的压缩算法,如gzip
  • Accept-Language 浏览器可接收的语言,如zh-CN
  • Connection: keep-alive 一次TCP连接重复使用
  • Cookie
  • Host
  • User-Agent 浏览器信息
  • content-type 发送数据的格式,如application/json

Response Headers:

  • Content-type 返回数据的格式
  • Content-length 返回数据的大小,多少字节
  • Content-Encoding 返回数据的压缩算法,如gzip
  • Set-Cookie 服务端向客户端设置cookie的时候

缓存相关的Headers:

  • Cache-control
  • Expires
  • Last-Modified
  • If-Modified-Since
  • Etag
  • If-None-Match

4、什么是Restful API?

  • 是一种新的API设计方法。
  • 传统的API设计:把每个URL当做一个功能。
  • Restful API设计:把每个URL当做一个为一个的资源。

5、描述一下HTTP缓存机制。

  • 强制缓存: 直接从浏览器中读取缓存。
  • 协商缓存:与服务端建立通信,带着资源标识请求以判断资源是否是最新的,未更新的话直接从浏览器缓存中读取资源,如果已更新,返回200和最新资源。
  • 缓存标志:etag、Last-modified,etag优于last-modified。
  • 以画流程图的形式去描述。

运行环境

1、从输入URL到渲染页面的整个过程。

2、window.onload和DOMContentLoaded的区别

  • onload,全部资源加载完成才会执行,包括图片、视频等。
  • DOMContentLoaded,DOM加载完成就会执行,不用等到图片、视频全部加载完毕。

3、前端性能优化有哪些方式

让加载更快:

  • 减少资源体积:压缩代码
  • 减少访问次数: 合并代码,SSR服务端渲染,缓存
  • 使用更快的网络:CDN

让渲染更快:

  • CSS放在head里,js放在body的最下面
  • 尽早开始执行JS,用DOMContentLoaded触发
  • 懒加载
  • 缓存DOM查询
  • 频繁操作DOM,统一处理一次性插入
  • 节流、防抖

4、手写一个防抖

function debounce(fn, delay = 500) {
    let timer = null;
    
    return function() {
        if (timer) {
            clearTimeout(timer);
        }
        
        timer = setTimeout(() => {
            fn.apply(this, arguments);
            timer = null;
        },delay)
    }
}

5、手写一个节流

节流的应用场景:

1、拖拽一个元素时,要随时拿到该元素被拖拽的位置

2、直接用drag事件,则会频繁触发,很容易造成卡顿

3、节流:无论拖拽速度有多快,都会每隔一段时间(比如100ms)触发一次。

function throttle(fn, delay = 100) {
    let timer = null;

    return function() {
       if(timer) {
           return;
       }
       
       timer = setTimeout(() => {
           fn.apply(this, arguments);
           timer = null;
       }, delay);
    }
}

6、防抖和节流的区别

防抖:动作发生一定时间后再执行,如果在这段时间内再次触发,则需要重新等待一段时间后再触发。

防抖是虽然事件持续触发,但只有等事件停止触发后n秒才执行函数

节流:动作发生后一定时间后再执行,如果在这段时间内再次触发,则忽略该动作,直到执行完成,如有触发才会再次响应。

节流是持续触发的时候,每隔n秒执行一次函数。

7、常见的web前端攻击方式有哪些?

  • XSS跨站请求攻击,预防:替换特殊字符,前后端都替换。如<变为&lt,>变为&gt
  • XSFR跨站请求伪造, 预防:使用Post接口;增加验证,比如指纹支付、验证码、密码等。

习题

1、何为变量提升?

1、var 和 let const 的区别

  • var存在变量提升,let、const 没有
  • let、var是变量可修改,const 是常量不可修改。
  • let、const是块级作用域,var不是。

2、typeof 返回那些类型

  • undefined、string、number、boolean、symbol
  • object
  • function

3、列举强制类型转换和隐式类型转换

  • 强制:parseInt、parseFloat、toString等
  • 隐式:if、逻辑运算、== 、+拼接字符串。

4、JavaScript的数据类型。

  • 值类型(基本类型):string、number、Boolean、undefined、symbol、null
  • 引用类型:Object、function、array

2、手写深度比较isEqual.

1、手写深度比较,模拟lodash.isEqual.

// 判断是否为对象,包括数组
function isObject(obj) {
    return typeof obj === 'object' && obj !== null;
}

function isEqual(obj1, obj2) {
    if(!isObject(obj1) || !isObject(obj2)) {
        return obj1 === obj2;
    }

    // 判断是否传入的是同一个对象
    if (obj1 === obj2) {
        return true;
    }

    // 判断两个对象的key的长度是否相等
    const objKeys1 = Object.keys(obj1);
    const objKeys2 = Object.keys(obj2);

    if (objKeys1.length !== objKeys2.length) {
        return false;
    }

    // 递归判断value值
    for (let key in obj1) {
        const res = isEqual(obj1[key], obj2[key]);

        if (!res) {
            return false;
        }
    }

    return true;
}

2、split()和join()的区别

  • split是把字符串分割成数组。
  • join是把数组连接成字符串。

3、数组的pop、push、unshift、shift分别做什么?

  • push是尾部插入、unshift是头部插入,返回的是数组的长度
  • pop是尾部删除,shift是头部删除,返回的是被删除的元素。
  • 哪些纯函数的数组API,即不会改变原数组,返回原数组的? concat、map、filter、slice

3、是否会真的用数组map.

1、数组的slice和splice的区别

  • 功能:slice是切片截取,splice是剪接
  • 参数和返回值:slice的参数是开始位置和结束位置。splice是开始位置和剪接长度,后面还可以跟要插入数组的参数。
  • 是否为纯函数:slice是纯函数,splice不是,splice会改变原数组。

2、[10,20,30].map(parseInt)返回值是什么?

[10,20,30].map(parseInt) 相当于是 [10,20,30].map((number, index) => parseInt(number, index))。parseInt(10, 0)默认返回湿巾纸,parseInt(20, 1)和parseInt(30, 2)都无法转,所以是NAN。最终结果是[10, NAN, NAN]

4、闭包

1、函数call和apply的区别

第二个参数不一样,call的第二个参数是列表,apply的第二个参数是参数数组。

2、事件代理(委托)是什么?

事件代理就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

3、闭包是什么,有什么特性?有什么负面影响?

5、回顾DOM操作和优化 (看上文)

6、是否用过Object.create()?

1、函数声明和函数表达式的区别

函数声明会在代码执行前预加载,而函数表达式不会。

2、new Object()和Object.create()的区别

  • {} 等同于 new Object(),原型Object.prototype
  • Object.create(null)没有原型

3、关于this的场景题

const User = {
    count: 1,
    getCount: function() {
        return this.count;
    }
};

console.log(User.getCount()); // 1
const func = User.getCount; 
console.log(func()); // undefined

7、常见的正则表达式。

1、关于作用域和自由变量的场景题一

let i;
for (i=0; i<=3; i++) {
    setTimeout(() => {
        console.log(i);
    },0);
}

//  4 4 4 4

2、关于作用域和自由变量的场景题二

let a = 100;

function test() {
    alert(a);
    a = 10;
    alert(a);
}

test();
alert(a);

// 100 10 10

3、判断字符串以字母开头,后面字母数字下划线,长度6-30

const reg = /^[a-zA-Z]\w{5,29}$/

8、如何获取最大值。

1、手写一个trim方法,保证浏览器兼容性

    if (!String.prototype.trim) {
        String.prototype.trim = function(){
            return this.replace(/^\s+/, '').replace(/^\s+$/, '');
        }
    }

2、如何获取多个数字中的最大值

方法一:
function max() {
    const nums = Array.prototype.slice.call(argument);
    
    let max = nums[0];
    for (let n of nums) {
        if(n > max) {
            max = n;
        }
    }
    
    return max;
}
方法二:Math.max();

3、如何用JS实现继承?(看上文)

9、解析url参数。

1、如何捕获JS中的异常?

  • try...catch
  • 用window.onerror自动捕获。这个方法有两个缺陷:①对于跨域的js,如CDN的,不会有报错信息。②对于压缩的js,还要配合sourceMap反查到未压缩代码的行、列

2、什么是json?

json 是一种数据格式,本质上是一个字符串。通过window.JSON这个对象的JSON.stringify和JSON.parse进行转换。

3、获取当前页面url参数。

// 传统方式
function query(name) {
    const search = location.search.substr(1);
    
    const params = search.split('&');
    
    params.forEach(item => {
        const key = item.split('=')[0];
        let result;
        
        if(name === key) {
            result = item.split('=')[1];
            
            return;
        }
    })
    
    return result;
}

// URLSearchParams的方式
function query(name) {
    const search = location.search;
    const p = new URLSearchParams(search);
    return p.get(name);
}

10、数组去重有几种方式?

1、将url参数解析为JS对象

// 传统方式
function queryToObj() {
    let result = {};
    const search = location.search.substr(1);
    const params = search.split('&');
    
    params.forEach(item => {
        const arr = item.split('=');
        const key = arr[0];
        const val = arr[1];
        
        result[key]= val;
    })
    
    return result;
}

// URLSearchParams的方式
function queryToObj() {
    let result = {};
    const pList = new URLSearchParams(location.search);
    
    pList.forEach((val, key) => {
        result[key] = val;
    })
    
    return result;
}

2、手写数组flatten, 考虑多层级

    function flatten(arr) {
        const isDeep = arr.some(item => item instanceof Array);
        
        if (!isDeep) {
            return arr;
        }
        
        const res = Array.prototype.concat.apply([], arr);
        
        return flatten(res);
    }

3、数组去重

  • 遍历push进空数组
    function unique(arr) {
        const res = [];
        
        arr.forEach(item => {
            if(res.indexOf(item) < 0) {
                res.push(item);
            }
        })
        
        return res;
    }
  • [ ...new Set(arr)]

11、是否用过requestAnimationFrame

1、手写深拷贝(见上文)

2、介绍一下requestAnimationFrame

  • 要想动画流畅,更新频率为60帧/s。
  • setTimeout要手动控制频率,而RAF浏览器会自动控制。
  • 后台标签或隐藏iframe中,RAF会暂停,但是setTimeout依然执行。