乐趣区

2019前端中高级面试精华100题一js篇有答案

以下问题大部分偏初中级,答案也都是简明扼要,可以在面试时应付一下,免得无点可说在第一面就被 PASS。如果这些问题能让你快速回忆起平时所学,串联起前后的技能点,在面试时有个好的发挥,也就不费我整理这些面试题花的时间。最后,这些答案仅供参考,不要死记硬背。愿你们都能找到满意的工作。
我是李古拉雷,曾全栈开发工程师,前今日头条前端架构师,今创业公司 CEO。关注我的公众号可以获得更多前端技术和职场经验。我的人生信条分享即价值!

JS 篇

1.let、var、const 区别?

答:
1.var 声明的变量会挂载在 window 上,而 let 和 const 声明的变量不会
2.var 声明变量存在变量提升,let 和 const 不存在变量提升
3.let 和 const 声明形成块作用域,var 变量提升不会形成作用域
4. 同一作用域下 let 和 const 不能声明同名变量,而 var 可以
5.var 和 let 可以可以修改声明的变量,const 不可以
6.const 定义的变量时必须初始化
7.let、const 存在暂时性死区

2. 什么是函数柯里化?

答:属于高阶函数应用,传递给函数部分参数来调用柯里化函数,让它返回一个函数去处理剩下的参数。//
把接受多个参数的函数转换成接受一个单一参数的函数 // 柯里化 var foo = function(x) {

    return function(y) {return x + y}
}    
foo(3)(4)       // 7    
// 普通方法
var add = function(x, y) {return x + y;}    
add(3, 4)       //7

3. 什么是 Promise 对象,有哪些用法?

答:Promise 对象的定义
Promise 是异步编程的一种解决方案,它是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise
是一个对象,从它可以获取异步操作的消息。可以用来解决“回调地狱”的问题。Promise 提供统一的
API,各种异步操作都可以用同样的方法进行处理。promise 对象是一个构造函数,用来生成 Promise 实例;Promise 对象的特点
(1)对象的状态不受外界影响,promise 对象代表一个异步操作,有三种状态,pending(进行中)、fulfilled(已成功)、rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态,这也是 promise 这个名字的由来“承若”;
(2)一旦状态改变就不会再变,任何时候都可以得到这个结果,promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled,从 pending 变为 rejected。这时就称为 resolved(已定型)。如果改变已经发生了,你再对 promise 对象添加回调函数,也会立即得到这个结果,这与事件(event)完全不同,事件的特点是:如果你错过了它,再去监听是得不到结果的。
有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。
Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
Promise 对象的用法
是一个构造函数,这个构造函数里有两个参数,分别是:resolve(成功之后的回调函数)、reject(失败之后的回调函数)。
因为 promise 表示的是一个异步操作,每当我们 new 一个 promise 实例,就表示一个具体的异步操作,那么这个异步操作的结果就只能有两种状态:成功 / 失败,两者都需要回调函数 resolve/reject 返回。所以内部拿到操作的结果后,无法使用 return 把操作结果返回给调用者,这时候只能用回调函数的形式来把成功或失败的结果返回给调用者。
promise 实例生成以后,可以用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数

then 方法可以接受连个回调函数作为参数,第一个回调函数是 promise 对象的状态变为 resolved 时调用,第二个回调函数是 promise 对象的状态变为 rejected 时调用,其中,第二个函数是可选的,不一定要提供,这两个函数都接受 promise 对象传出的值作为参数;
* 通过。then 指定回调函数的时候,成功的回调函数必须传,失败的回调函数可以胜利。如果前面的 promise 执行失败,不详影响后续操作终止,捕获异常的两种方式:①可以为每个 promise 指定失败回调;
function(err){console.log(……)}) ②最后加 catch(function(err){
console.log(……)})// 表示如前面有任意一个有报错,立即报错,并终止后面的;如果前面无报错,前面正常执行。

4. 如何通过 Promise 对象实现 ajax?


getJSON 是对 XMLHTTPRequest 对象的封装,用于发出一个针对 JSON 数据的 HTTP 请求,并且返回一个 promise 对象,需要注意的是,在 getJSON 内部,resolve 函数和 reject 函数调用时,都带有参数;
如果调用 resolve 函数和 reject 函数时带有参数,那么他们的参数会被传递给回调函数,reject 函数的参数通常是 Error 对象的实例,表示抛出的错误,resolve 函数的参数除了正常的值以外,还可以是另一个 promise 实例;

5. 什么是 REST,用起来有什么好处?

答:
REST 是一种设计 API 的模式。最常用的数据格式是 JSON。由于 JSON 能直接被 JavaScript 读取,所以,以 JSON 格式编写的 REST 风格的 API 具有简单、易读、易用的特点。通过 REST 模式设计的 API 可以把 web
app 全部功能进行封装,可以很容易的实现前后端分离,使的前端代码易编写,后端代码易测试。

6. 什么是闭包,举个例子说明一下?

答:
“闭包就是能够读取其他函数内部变量的函数。例如在 javascript 中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。”
举例:创建闭包最常见方式,就是在一个函数内部创建另一个函数。下面例子中的 closure 就是一个闭包,function func(){
vara =1 ,b = 2; funciton closure(){ return a+b;} return
closure; }

7.promise、generator、async/await 怎么使用,有什么区别?

答:我们知道 JavaScript 是单线程语言,如果没有异步编程非得卡死。以前,异步编程的方法有下面四种 回调函数 事件监听 发布 / 订阅
Promise 对象 现在据说异步编程终极解决方案是——async/await 更详细的介绍参考下面这篇文章:
https://www.jianshu.com/p/1c9…

8.apply/call/bind 有什么区别?

答:这三者的作用就是改变函数运行时 this 的指向。call 方法:语法:call([thisObj[,arg1[, arg2[,
[,.argN]]]]]) 定义:调用一个对象的一个方法,以另一个对象替换当前对象。说明:call
方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。apply 方法:语法:apply([thisObj[,argArray]]) 定义:应用某一对象的一个方法,用另一个对象替换当前对象。说明:如果
argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。

    如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作 thisObj,并且无法被传递任何参数。bind 方法:语法:bind(thisArg[, arg1[, arg2[, ...]]])

定义:将接受多个参数的函数变换成接受一个单一参数。
说明:bind()方法所返回的函数的 length(形参数量)等于原函数的形参数量减去传入 bind()方法中的实参数量(第一个参数以后的所有参数),因为传入 bind 中的实参都会绑定到原函数的形参。

9. 什么是变量提升、函数提升?

答:变量提升:
简单说就是在 js 代码执行前引擎会先进行预编译,预编译期间会将变量声明与函数声明提升至其对应作用域的最顶端,函数内声明的变量只会提升至该函数作用域最顶层。
当函数内部定义的一个变量与外部相同时,那么函数体内的这个变量就会被上升到最顶端。举例来说:console.log(a); //
undefined var a = 3;

// 预编译后的代码结构可以看做如下运行顺序 var a; // 将变量 a 的声明提升至最顶端,赋值逻辑不提升。console.log(a);
// undefined a = 3; // 代码执行到原位置即执行原赋值逻辑

函数提升:函数提升只会提升函数声明式写法,函数表达式的写法不存在函数提升。
函数提升的优先级大于变量提升的优先级,即函数提升在变量提升之上。

10. 什么是事件冒泡,它是如何工作的?如何阻止事件冒泡?

答:
在一个对象上触发某类事件(比如单击 onclick 事件),如果此对象定义了此事件的处理程序,那么此事件就会调用这个处理程序,如果没有定义此事件处理程序或者事件返回 true,那么这个事件会向这个对象的父级对象传播,从里到外,直至它被处理(父级对象所有同类事件都将被激活),或者它到达了对象层次的最顶层,即 document 对象(有些浏览器是 window)
阻止事件冒泡的几种方法 第一种:event.stopPropagation(); 第二种:return false; 第三种:
event.preventDefault();

11. 简单说说 js 中的继承?

答:有以下六种方法
1. 原型链继承 JavaScript 实现继承的基本思想:通过原型将一个引用类型继承另一个引用类型的属性和方法。
2. 借用构造函数继承(伪造对象或经典继承) JavaScript 实现继承的基本思想:在子类构造函数内部调用超类型构造函数。通过使用 apply() 和 call()方法可以在新创建的子类对象上执行构造函数。
3. 组合继承 (原型 + 借用构造)(伪经典继承) JavaScript 实现继承的基本思想:将原型链和借用构造函数的技术组合在一块,从而发挥两者之长的一种继承模式。
将原型链和借用构造函数的技术组合到一起,从而取长补短发挥两者长处的一种继承模式。
4. 原型式继承 JavaScript 实现继承的基本思想:借助原型可以基于已有的对象创建新对象,同时还不必须因此创建自定义的类型。
5. 寄生式继承 JavaScript 实现继承的基本思想:创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真正是它做了所有工作一样返回对象。
寄生式继承是原型式继承的加强版。
6. 寄生组合式继承 JavaScript 实现继承的基本思想:通过借用函数来继承属性,通过原型链的混成形式来继承方法。

12. 常用的 js 数组操作方法有哪些?

答:

  1. Array.shift()—— 删除并返回第一个元素 作用:从数组中删除第一个元素(即下标为 0 的元素),并返回该元素。注意:1)删除元素之后,数组的长度 -1。

         2)如果数组是空的,那么 shift() 方法将不进行任何操作,返回 undefined 值。

    2.Array.pop()—— 删除并返回最后一个元素 作用:从数组中删除最后一个元素(即下标为 length- 1 的元素),并返回该元素。注意:1)删除元素之后,数组的长度 -1。

         2)如果数组是空的,那么 shift() 方法将不进行任何操作,返回 undefined 值。
  2. Array.push(param1[,param2,…paramN])—— 尾部添加元素 作用:在数组的尾部添加一个元素,并返回新数组的长度。注意:1)它是直接修改该数组,而不是重新创建一个数组。

        2)它和 pop 是一对相反的先进后出的栈功能方法。3)它可以同时给一个数组添加多个元素。
  3. Array.unshift(newElement1[,newElement2,…newElementN])—— 头部添加元素 作用:在数组的头部添加一个或多个元素,并返回新数组的长度。注意:1)它是直接修改该数组,而不是重新创建一个数组。

         2)IE 浏览器不支持该方法。

5.Array.join([separator])—— 转换成字符串 作用:把数组的所有元素放入到一个字符串中。注意:1)参数 separator 表示字符串中元素的分隔符,可以为空,默认为半角逗号。

     2)该方法并不修改数组。
  1. Array.contact(array1[,array2,…arrayN])—— 连接数组 作用:将两个或两个以上的数组连接成一个数组,并返回连接后的数组。注意:1)该方法并不会改变现有的数组,而是返回被连接的多个数组的一个副本。
    2)如果多个数组里有值相同的元素,那也不会重复出现,而不会把重复的元素过滤掉。

7.Array.reverse()—— 反转数组 作用:把数组的所有元素顺序反转。注意:1)该方法会直接修改数组,而不会创建新的数组。

  1. Array.slice(start[, end])—— 截取数组 作用:截取数组中指定位置的元素,并返回一个新的子数组。注意:1)该方法并不会改变现有的数组,而是原数组的一个子数组。
    2)参数 start 是必选,表示开始下标,如果 start 为负数,表示从末尾开始,- 1 表示最后一个元素,依次类推。
    3)end 是可选表示结束下标,如果没有指定,表示到结尾元素。

9.Array.splice()—— 删除指定元素 作用:从数组指定位置删除指定数量的元素,并返回被删除的元素。注意:1)该方法会直接修改数组。

     2)splice() 方法与 slice() 方法的作用是不同的,splice() 方法会直接对数组进行修改,而 slice 只是截取原数组的一部分后返回一个子数组,并不会修改原数组。
  1. Array.toString()—— 转换成字符串 作用:数组转换为字符串,并返回该字符串。注意:1)该方法和不带参数的 join()方法效果一样。

13.js 数组去重,能用几种方法实现?

答:
1. 使用 es6 set 方法 […new Set(arr)] let arr = [1,2,3,4,3,2,3,4,6,7,6]; let unique = (arr)=> […new Set(arr)]; unique(arr);//[1, 2, 3, 4, 6, 7]
2. 利用新数组 indexOf 查找 indexOf() 方法可返回某个指定的元素在数组中首次出现的位置。如果没有就返回 -1。
3.for 双重循环 通过判断第二层循环,去重的数组中是否含有该元素,如果有就退出第二层循环,如果没有 j ==result.length 就相等,然后把对应的元素添加到最后的数组里面。
let arr = [1,2,3,4,3,2,3,4,6,7,6]; let result = []; for(var i = 0 ; i
< arr.length; i++) {

for(var j = 0 ; j < result.length ; j++) {if( arr[i] === result[j]){break;};
};
if(j == result.length){result.push(arr[i]);
}; }; console.log(result);

4. 利用 for 嵌套 for,然后 splice 去重 functionunique(arr){for(vari=0; i<arr.length; i++){for(varj=i+1; j<arr.length; j++){if(arr[i]==arr[j]){
// 第一个等同于第二个,splice 方法删除第二个 arr.splice(j,1); j–; } } } returnarr; }
5. 利用 filter let arr = [1,2,3,4,3,2,3,4,6,7,6]; let unique = (arr) => {

return arr.filter((item,index) => {return arr.indexOf(item) === index;
}) }; unique(arr);

5.let arr = [1,2,3,4,3,2,3,4,6,7,6]; let unique = (arr) => {

return arr.filter((item,index) => {return arr.indexOf(item) === index;
}) }; unique(arr);

6. 利用 Map 数据结构去重 let arr = [1,2,3,4,3,2,3,4,6,7,6]; let unique = (arr)=> {

let seen = new Map();
return arr.filter((item) => {return !seen.has(item) && seen.set(item,1);
}); }; unique(arr);

14. 数组中的 forEach 和 map 的区别?

答:相同点 都是循环遍历数组中的每一项
forEach 和 map 方法里每次执行匿名函数都支持 3 个参数,参数分别是 item(当前每一项),index(索引值),arr(原数组)
匿名函数中的 this 都是指向 window 只能遍历数组 都不会改变原数组 区别 map 方法
1.map 方法返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。
2.map 方法不会对空数组进行检测,map 方法不会改变原始数组。
3. 浏览器支持:chrome、Safari1.5+、opera 都支持,IE9+, 若 arr 为空数组,则 map 方法返回的也是一个空数组。forEach 方法
1.forEach 方法用来调用数组的每个元素,将元素传给回调函数
2.forEach 对于空数组是不会调用回调函数的。无论 arr 是不是空数组,forEach 返回的都是 undefined。这个方法只是将数组中的每一项作为 callback 的参数执行一次。

15.jQueryajax、fetch、axios 有什么异同,适用场景有哪些?

答:
1.jQuery ajax $.ajax({type: ‘POST’, url: url, data: data, dataType: dataType, success: function () {}, error: function ()
{}}); 优缺点:本身是针对 MVC 的编程, 不符合现在前端 MVVM 的浪潮
JQuery 整个项目太大,单纯使用 ajax 却要引入整个 JQuery 非常的不合理(采取个性化打包的方案又不能享受 CDN 服务)
2.axios axios({

method: 'post',
url: '/user/12345',
data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
} }) .then(function (response) {console.log(response); }) 客户端支持防止 CSRF/XSRF 自动转换 JSON 数据 取消请求 转换请求和响应数据 拦截请求和响应 支持 Promise API 从 node.js 发出 http 请求 从浏览器中创建

XMLHttpRequest axios 是一个基于 Promise 用于浏览器和 nodejs 的 HTTP 客户端,它本身具有以下特征:
为什么要用 axios? 4)fetch 没有办法原生监测请求的进度,而 XHR 可以
3)fetch 不支持 abort,不支持超时控制,使用 setTimeout 及 Promise.reject 的实现的超时控制并不能阻止请求过程继续在后台运行,造成了量的浪费
2)fetch 默认不会带 cookie,需要添加配置项 1)fetchtch 只对网络请求报错,对 400,500 都当做成功的请求,需要封装去处理
脱离了 XHR,是 ES 规范里新的实现方式 更加底层,提供的 API 丰富(request, response)更好更方便的写法
符合关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里 优缺点:} console.log(“Oops,
error”, e); } catch(e) {console.log(data); let data =
response.json(); let response = await fetch(url); try {
3.fetch 提供了一些并发请求的接口(重要,方便了很多的操作)客户端支持防止 CSRF 支持 Promise API 从 node.js 创建 http 请求 优缺点:});

console.log(error); .catch(function (error) {})

16.es6 扩展运算符可以解决哪些问题?

答:.. 代表着扩展运算符或 Rest(剩余)运算符

17. 是么是 js 事件循环 event loop?

答:主线程从 ” 任务队列 ” 中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为 Event Loop(事件循环)。
这里推荐一下阮一峰老师的这篇讲解 event
loop 的博文:http://www.ruanyifeng.com/blo…
讲的比较详细,深入浅出很容易理解。

18.Math.min()和 Math.max()大小比较?

答:Math.max() < Math.min() Math.min()如果没有参数,则返回 Infinity。Infinity 是
javascript 中全局对象的一个属性,在浏览器环境中就是 window 对象的一个属性,表示无穷大。而 Math.max()
没有传递参数时返回的是 -Infinity。因此 Math.min() 要比 Math.max() 大。

19. 怎么实现对象的深浅拷贝?

答:浅拷贝很容易,只需要使用赋值运算符 (=) 即可

20. 文件上传如何做断点续传?

答:文件断点续传是 HTML5 引入的新特性,HTML5 的 FILE
api,有一个 slice 方法,可以将 BLOB 对象进行分割。前端通过 FileList 对象获取到相应的文件,按照指定的分割方式将大文件分段,然后一段一段地传给后端,后端再按顺序一段段将文件进行拼接。
断点续传原理
目前比较常用的断点续传的方法有两种,一种是通过 websocket 接口进行文件上传,另一种是通过 ajax,两种方法各有千秋,虽然 websocket 听起来比较高端些,但是除了用了不同的协议外其他的算法基本上都是很相似的,并且服务端要开启 ws 接口,这里用相对方便的 ajax 来说明断点上传的思路。
说来说去,断点续传最核心的内容就是把文件“切片”然后再一片一片的传给服务器。
首先是文件的识别,一个文件被分成了若干份之后如何告诉服务器你切了多少块,以及最终服务器应该如何把你上传上去的文件进行合并?
因此在文件开始上传之前,我们和服务器要有一个“握手”的过程,告诉服务器文件信息,然后和服务器约定切片的大小,当和服务器达成共识之后就可以开始后续的文件传输了。
前台要把每一块的文件传给后台,成功之后前端和后端都要标识一下,以便后续的断点。
当文件传输中断之后用户再次选择文件就可以通过标识来判断文件是否已经上传了一部分,如果是的话,那么我们可以接着上次的进度继续传文件,以达到续传的功能。

21.js 如何处理防抖和节流?

答:
在进行窗口的 resize、scroll,输入框内容校验等操作时,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕。
此时我们可以采用 debounce(防抖)和 throttle(节流)的方式来减少调用频率,同时又不影响实际效果。函数防抖
函数防抖(debounce):当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。
如下,持续触发 scroll 事件时,并不执行 handle 函数,当 1000 毫秒内没有触发 scroll 事件时,才会延时触发 scroll 事件。
function debounce(fn, wait) {

var timeout = null;   
return function() {if(timeout !== null) clearTimeout(timeout);       
    timeout = setTimeout(fn, wait);   
} } // 处理函数 function handle() {console.log(Math.random()); } // 滚动事件 window.addEventListener('scroll', debounce(handle, 1000)); 函数节流

函数节流(throttle):当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
节流通俗解释就比如我们水龙头放水,阀门一打开,水哗哗的往下流,秉着勤俭节约的优良传统美德,我们要把水龙头关小点,最好是如我们心意按照一定规律在某个时间间隔内一滴一滴的往下滴。
如下,持续触发 scroll 事件时,并不立即执行 handle 函数,每隔 1000 毫秒才会执行一次 handle 函数。var throttle =
function(func, delay) {

var prev = Date.now();           
return function() {               
    var context = this;               
    var args = arguments;               
    var now = Date.now();               
    if (now - prev >= delay) {func.apply(context, args);                   
        prev = Date.now();}           
}        }        function handle() {console.log(Math.random());        }        window.addEventListener('scroll', throttle(handle, 1000)); 总结

函数防抖:将几次操作合并为一此操作进行。原理是维护一个计时器,规定在 delay 时间后触发函数,但是在 delay 时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
函数节流:使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数。区别:
函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次
Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。

22. 事件委托以及优缺点?

答:优点:
1. 减少事件注册,节省内存。比如,
2. 在 table 上代理所有 td 的 click 事件。
3. 在 ul 上代理所有 li 的 click 事件。
4. 简化了 dom 节点更新时,相应事件的更新。比如
5. 不用在新添加的 li 上绑定 click 事件。
6. 当删除某个 li 时,不用移解绑上面的 click 事件。缺点:
1. 事件委托基于冒泡,对于不冒泡的事件不支持。
2. 层级过多,冒泡过程中,可能会被某层阻止掉。
3. 理论上委托会导致浏览器频繁调用处理函数,虽然很可能不需要处理。所以建议就近委托,比如在 table 上代理 td,而不是在 document 上代理 td。
4. 把所有事件都用代理就可能会出现事件误判。比如,在 document 中代理了所有 button 的 click 事件,另外的人在引用改 js 时,可能不知道,造成单击 button 触发了两个 click 事件。

23. 介绍 this 各种情况?

答:

  • this 的情况:
    1. 以函数形式调用时,this 永远都是 window
    2. 以方法的形式调用时,this 是调用方法的对象
    3. 以构造函数的形式调用时,this 是新创建的那个对象
    4. 使用 call 和 apply 调用时,this 是指定的那个对象
    5. 箭头函数:箭头函数的 this 看外层是否有函数

       如果有,外层函数的 this 就是内部箭头函数的 this
       如果没有,就是 window

    6. 特殊情况:通常意义上 this 指针指向为最后调用它的对象。这里需要注意的一点就是如果返回值是一个对象,那么 this 指向的就是那个返回的对象,如果返回值不是一个对象那么 this 还是指向函数的实例

24.== 和 === 的区别,什么情况下用相等 ==?

答:
==:运算符称作相等,用来检测两个操作数是否相等,这里的相等定义的非常宽松,可以允许进行类型转换
===:用来检测两个操作数是否严格相等 1、对于 string,number 等基础类型,== 和 === 是有区别的 不同类型间比较,== 之比较“转化成同一类型后的值”看“值”是否相等,=== 如果类型不同,其结果就是不等
同类型比较,直接进行“值”比较,两者结果一样 2、对于 Array,Object 等高级类型,== 和 === 是没有区别的
3、基础类型与高级类型,== 和 === 是有区别的 对于 ==,将高级转化为基础类型,进行“值”比较,因为类型不同,=== 结果为 false

25. 介绍下原型链(解决的是继承问题吗)

答:JavaScript 原型:每个对象都会在其内部初始化一个属性,就是 prototype(原型)。原型链:
当我们访问一个对象的属性时,如果这个对象内部不存在这个属性,那么他就会去 prototype 里找这个属性,这个 prototype 又会有自己的 prototype,于是就这样一直找下去,也就是我们平时所说的原型链的概念。
特点:
JavaScript 对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变。

26.JS 里垃圾回收机制是什么,常用的是哪种,怎么处理的?

答:
JS 的垃圾回收机制是为了以防内存泄漏,内存泄漏的含义就是当已经不需要某块内存时这块内存还存在着,垃圾回收机制就是间歇的不定期的寻找到不再使用的变量,并释放掉它们所指向的内存。
JS 中最常见的垃圾回收方式是标记清除。
工作原理:是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
工作流程:

  1. 垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记。
  2. 去掉环境中的变量以及被环境中的变量引用的变量的标记。
  3. 再被加上标记的会被视为准备删除的变量。
  4. 垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。

27.Promise 和 setTimeout 的区别?

答:
回顾 JavaScript 事件循环并发模型,我们了解了 setTimeout 和 Promise 调用的都是异步任务,这一点是它们共同之处,也即都是通过任务队列进行管理/调度。那么它们有什么区别吗?
任务队列
前文已经介绍了任务队列的基础内容和机制,可选择查看,本文对任务队列进行拓展介绍。JavaScript 通过任务队列管理所有异步任务,而任务队列还可以细分为 MacroTask
Queue 和 MicoTask Queue 两类。MacroTask Queue MacroTask
Queue(宏任务队列)主要包括 setTimeout,setInterval, setImmediate,
requestAnimationFrame, NodeJS 中的 `I/ O 等。MicroTask Queue MicroTask
Queue(微任务队列)主要包括两类:独立回调 microTask:如 Promise,其成功/失败回调函数相互独立;
复合回调 microTask:如 Object.observe, MutationObserver 和 NodeJs 中的
process.nextTick,不同状态回调在同一函数体;MacroTask 和 MicroTask
JavaScript 将异步任务分为 MacroTask 和 MicroTask,那么它们区别何在呢?依次执行同步代码直至执行完毕;
检查 MacroTask 队列,若有触发的异步任务,则取第一个并调用其事件处理函数,然后跳至第三步,若没有需处理的异步任务,则直接跳至第三步;
检查 MicroTask 队列,然后执行所有已触发的异步任务,依次执行事件处理函数,直至执行完毕,然后跳至第二步,若没有需处理的异步任务中,则直接返回第二步,依次执行后续步骤;
最后返回第二步,继续检查 MacroTask 队列,依次执行后续步骤;如此往复,若所有异步任务处理完成,则结束;

28. 介绍下广度优先遍历(BFS)和深度优先遍历(DFS)?

答:
1. 广度优先遍历 英文缩写为 BFS 即 Breadth FirstSearch。其过程检验来说是对每一层节点依次访问,访问完一层进入下一层,而且每个节点只能访问一次。对于上面的例子来说,广度优先遍历的
结果是:A,B,C,D,E,F,G,H,I(假设每层节点从左到右访问)。先往队列中插入左节点,再插右节点,这样出队就是先左节点后右节点了。
广度优先遍历树,需要用到队列(Queue)来存储节点对象, 队列的特点就是先进先出。例如,上面这颗树的访问如下:
首先将 A 节点插入队列中,队列中有元素(A);
将 A 节点弹出,同时将 A 节点的左、右节点依次插入队列,B 在队首,C 在队尾,(B,C),此时得到 A 节点;
继续弹出队首元素,即弹出 B,并将 B 的左、右节点插入队列,C 在队首,E 在队尾(C,D,E),此时得到 B 节点;
继续弹出,即弹出 C,并将 C 节点的左、中、右节点依次插入队列,(D,E,F,G,H),此时得到 C 节点;
将 D 弹出,此时 D 没有子节点,队列中元素为(E,F,G,H),得到 D 节点;。。。以此类推。。
2. 深度优先遍历 英文缩写为 DFS 即 Depth First Search. 其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。对于上面的例子来说深度优先遍历的结果就是:A,B,D,E,I,C,F,G,H.(假设先走子节点的的左侧)。
深度优先遍历各个节点,需要使用到栈(Stack)这种数据结构。stack 的特点是是先进后出。整个遍历过程如下:
先往栈中压入右节点,再压左节点,这样出栈就是先左节点后右节点了。首先将 A 节点压入栈中,stack(A);
将 A 节点弹出,同时将 A 的子节点 C,B 压入栈中,此时 B 在栈的顶部,stack(B,C);
将 B 节点弹出,同时将 B 的子节点 E,D 压入栈中,此时 D 在栈的顶部,stack(D,E,C);
将 D 节点弹出,没有子节点压入, 此时 E 在栈的顶部,stack(E,C);将 E 节点弹出,同时将 E 的子节点 I 压入,stack(I,C);
…依次往下,最终遍历完成。代码:也是以二叉树为例。

29.for in 和 for of 的区别

答:
1. 推荐在循环对象属性的时候,使用 for…in, 在遍历数组的时候的时候使用 for…of
2.for…in 循环出的是 key,for…of 循环出的是 value
3. 注意,for…of 是 ES6 新引入的特性。修复了 ES5 引入的 for…in 的不足
4.for…of 不能循环普通的对象,需要通过和 Object.keys() 搭配使用

30.typeof 和 instanceof 区别?

答:在 javascript 中,判断一个变量的类型可以用 typeof (1)
数字类型、typeof 返回的值是 number。比如说:typeof(1),返回值是 number (2)
字符串类型,typeof 返回的值是 string。比如 typeof(“123”返回值时 string) (3)
布尔类型,typeof 返回的值是 boolean。比如 typeof(true)返回值时 boolean (4)
对象、数组、null 返回的值是 object。比如 typeof(window),typeof(document),typeof(null)返回的值都是 object
(5) 函数类型,返回的值是 function。比如:typeof(eval),typeof(Date)返回的值都是 function。
(6)
不存在的变量、函数或者 undefined,将返回 undefined。比如:typeof(abc)、typeof(undefined)都返回 undefined
在 javascript 中,instanceof 用于判断某个对象是否被另一个函数构造。
使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回”object”。ECMAScript 引入了另一个 Java 运算符 instanceof 来解决这个问题。Instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型。与 typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型

31. 常见的继承有几种方法?

答:
1. 原型链继承
2. 构造函数继承(经典继承)
3. 组合方式继承(构造函数 + 原型链)
4.es6 方法继承

退出移动版