这儿有20道大厂面试题等你查收

今年来,各大公司都缩减了HC,甚至是采起了“裁人”措施,在这样的大环境之下,想要得到一份更好的工做,必然须要付出更多的努力。

本文挑选了20道大厂面试题,建议在阅读时,先思考一番,不要直接看解析。尽管,本文全部的答案,都是我在翻阅各类资料,思考并验证以后,才给出的。但因水平有限,本人的答案未必是最优的,若是您有更好的答案,欢迎给我留言。若是有错误,也请在评论区指出,谢谢。javascript

本文篇幅较长,但愿小伙伴们可以坚持读完,若是想加入交流群,能够经过文末的公众号添加我为好友。css

更多文章可戳github.com/YvetteLau/B…html

1. new的实现原理是什么?

查看解析

new 的实现原理:前端

  1. 建立一个空对象,构造函数中的this指向这个空对象
  2. 这个新对象被执行 [[原型]] 链接
  3. 执行构造函数方法,属性和方法被添加到this引用的对象中
  4. 若是构造函数中没有返回其它对象,那么返回this,即建立的这个的新对象,不然,返回构造函数中返回的对象。

2. 如何正确判断this的指向?

查看解析

若是用一句话说明 this 的指向,那么便是: 谁调用它,this 就指向谁。html5

可是仅经过这句话,咱们不少时候并不能准确判断 this 的指向。所以咱们须要借助一些规则去帮助本身:java

this 的指向能够按照如下顺序判断:node

全局环境中的 this

浏览器环境:不管是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象 window;git

node 环境:不管是否在严格模式下,在全局执行环境中(在任何函数体外部),this 都是空对象 {};github

是不是 new 绑定

若是是 new 绑定,而且构造函数中没有返回 function 或者是 object,那么 this 指向这个新对象。以下:面试

构造函数返回值不是 function 或 object。new Super() 返回的是 this 对象。

构造函数返回值是 function 或 object,new Super()是返回的是Super种返回的对象。

函数是否经过 call,apply 调用,或者使用了 bind 绑定,若是是,那么this绑定的就是指定的对象【归结为显式绑定】。

这里一样须要注意一种特殊状况,若是 call,apply 或者 bind 传入的第一个参数值是 undefined 或者 null,严格模式下 this 的值为传入的值 null /undefined。非严格模式下,实际应用的默认绑定规则,this 指向全局对象(node环境为global,浏览器环境为window)

隐式绑定,函数的调用是在某个对象上触发的,即调用位置上存在上下文对象。典型的隐式调用为: xxx.fn()

默认绑定,在不能应用其它绑定规则时使用的默认规则,一般是独立函数调用。

非严格模式: node环境,指向全局对象 global,浏览器环境,指向全局对象 window。

严格模式:执行 undefined

箭头函数的状况:

箭头函数没有本身的this,继承外层上下文绑定的this。

3. 深拷贝和浅拷贝的区别是什么?实现一个深拷贝

查看解析

深拷贝和浅拷贝是针对复杂数据类型来讲的,浅拷贝只拷贝一层,而深拷贝是层层拷贝。

深拷贝

深拷贝复制变量值,对于非基本类型的变量,则递归至基本类型变量后,再复制。 深拷贝后的对象与原来的对象是彻底隔离的,互不影响,对一个对象的修改并不会影响另外一个对象。

浅拷贝

浅拷贝是会将对象的每一个属性进行依次复制,可是当对象的属性值是引用类型时,实质复制的是其引用,当引用指向的值改变时也会跟着变化。

可使用 for inObject.assign、 扩展运算符 ...Array.prototype.slice()Array.prototype.concat() 等,例如:

能够看出浅拷贝只最第一层属性进行了拷贝,当第一层的属性值是基本数据类型时,新的对象和原对象互不影响,可是若是第一层的属性值是复杂数据类型,那么新对象和原对象的属性值其指向的是同一块内存地址。

深拷贝实现

1.深拷贝最简单的实现是: JSON.parse(JSON.stringify(obj))

JSON.parse(JSON.stringify(obj)) 是最简单的实现方式,可是有一些缺陷:

  1. 对象的属性值是函数时,没法拷贝。
  2. 原型链上的属性没法拷贝
  3. 不能正确的处理 Date 类型的数据
  4. 不能处理 RegExp
  5. 会忽略 symbol
  6. 会忽略 undefined

2.实现一个 deepClone 函数

  1. 若是是基本数据类型,直接返回
  2. 若是是 RegExp 或者 Date 类型,返回对应类型
  3. 若是是复杂数据类型,递归。
  4. 考虑循环引用的问题

4. call/apply 的实现原理是什么?

查看解析

callapply 的功能相同,都是改变 this 的执行,并当即执行函数。区别在于传参方式不一样。

  • func.call(thisArg, arg1, arg2, ...):第一个参数是 this 指向的对象,其它参数依次传入。

  • func.apply(thisArg, [argsArray]):第一个参数是 this 指向的对象,第二个参数是数组或类数组。

一块儿思考一下,如何模拟实现 call

首先,咱们知道,函数均可以调用 call,说明 call 是函数原型上的方法,全部的实例均可以调用。即: Function.prototype.call

  • call 方法中获取调用call()函数
  • 若是第一个参数没有传入,那么默认指向 window / global(非严格模式)
  • 传入 call 的第一个参数是 this 指向的对象,根据隐式绑定的规则,咱们知道 obj.foo(), foo() 中的 this 指向 obj;所以咱们能够这样调用函数 thisArgs.func(...args)
  • 返回执行结果

apply 的实现思路和 call 一致,仅参数处理略有差异。以下:

5. 柯里化函数实现

查看解析

在开始以前,咱们首先须要搞清楚函数柯里化的概念。

函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,而且返回接受余下的参数并且返回结果的新函数的技术。

函数柯里化的主要做用:

  • 参数复用
  • 提早返回 – 返回接受余下的参数且返回结果的新函数
  • 延迟执行 – 返回新函数,等待执行

6. 如何让 (a == 1 && a == 2 && a == 3) 的值为true?

查看解析
  1. 利用隐式类型转换

== 操做符在左右数据类型不一致时,会先进行隐式转换。

a == 1 && a == 2 && a == 3 的值意味着其不多是基本数据类型。由于若是 a 是 null 或者是 undefined bool类型,都不可能返回true。

所以能够推测 a 是复杂数据类型,JS 中复杂数据类型只有 object,回忆一下,Object 转换为原始类型会调用什么方法?

  • 若是部署了 [Symbol.toPrimitive] 接口,那么调用此接口,若返回的不是基本数据类型,抛出错误。

  • 若是没有部署 [Symbol.toPrimitive] 接口,那么根据要转换的类型,先调用 valueOf / toString

    1. 非Date类型对象,hintdefault 时,调用顺序为:valueOf >>> toString,即valueOf 返回的不是基本数据类型,才会继续调用 valueOf,若是toString 返回的还不是基本数据类型,那么抛出错误。
    2. 若是 hintstring(Date对象的hint默认是string) ,调用顺序为:toString >>> valueOf,即toString 返回的不是基本数据类型,才会继续调用 valueOf,若是valueOf 返回的还不是基本数据类型,那么抛出错误。
    3. 若是 hintnumber,调用顺序为: valueOf >>> toString

  1. 利用数据劫持(Proxy/Object.defineProperty)

  1. 数组的 toString 接口默认调用数组的 join 方法,重写 join 方法

7. 什么是BFC?BFC的布局规则是什么?如何建立BFC?

查看解析

Box 是 CSS 布局的对象和基本单位,页面是由若干个Box组成的。

元素的类型 和 display 属性,决定了这个 Box 的类型。不一样类型的 Box 会参与不一样的 Formatting Context。

Formatting Context

Formatting Context 是页面的一块渲染区域,而且有一套渲染规则,决定了其子元素将如何定位,以及和其它元素的关系和相互做用。

Formatting Context 有 BFC (Block formatting context),IFC (Inline formatting context),FFC (Flex formatting context) 和 GFC (Grid formatting context)。FFC 和 GFC 为 CC3 中新增。

BFC布局规则

  • BFC内,盒子依次垂直排列。
  • BFC内,两个盒子的垂直距离由 margin 属性决定。属于同一个BFC的两个相邻Box的margin会发生重叠【符合合并原则的margin合并后是使用大的margin】
  • BFC内,每一个盒子的左外边缘接触内部盒子的左边缘(对于从右到左的格式,右边缘接触)。即便在存在浮动的状况下也是如此。除非建立新的BFC。
  • BFC的区域不会与float box重叠。
  • BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
  • 计算BFC的高度时,浮动元素也参与计算。

如何建立BFC

  • 根元素
  • 浮动元素(float 属性不为 none)
  • position 为 absolute 或 fixed
  • overflow 不为 visible 的块元素
  • display 为 inline-block, table-cell, table-caption

BFC 的应用

  1. 防止 margin 重叠 (同一个BFC内的两个两个相邻Box的 margin 会发生重叠,触发生成两个BFC,即不会重叠)
  2. 清除内部浮动 (建立一个新的 BFC,由于根据 BFC 的规则,计算 BFC 的高度时,浮动元素也参与计算)
  3. 自适应多栏布局 (BFC的区域不会与float box重叠。所以,能够触发生成一个新的BFC)

8. 异步加载JS脚本的方式有哪些?

查看解析

<script> 标签中增长 async(html5) 或者 defer(html4) 属性,脚本就会异步加载。

<script src="../XXX.js" defer></script>

deferasync 的区别在于:

  • defer 要等到整个页面在内存中正常渲染结束(DOM 结构彻底生成,以及其余脚本执行完成),在window.onload 以前执行;
  • async 一旦下载完,渲染引擎就会中断渲染,执行这个脚本之后,再继续渲染。
  • 若是有多个 defer 脚本,会按照它们在页面出现的顺序加载
  • 多个 async 脚本不能保证加载顺序

动态建立 script 标签

动态建立的 script ,设置 src 并不会开始下载,而是要添加到文档中,JS文件才会开始下载。

XHR 异步加载JS

9. ES5有几种方式能够实现继承?分别有哪些优缺点?

查看解析

ES5 有 6 种方式能够实现继承,分别为:

1. 原型链继承

原型链继承的基本思想是利用原型让一个引用类型继承另外一个引用类型的属性和方法。

缺点:

  1. 经过原型来实现继承时,原型会变成另外一个类型的实例,原先的实例属性变成了如今的原型属性,该原型的引用类型属性会被全部的实例共享。
  2. 在建立子类型的实例时,没有办法在不影响全部对象实例的状况下给超类型的构造函数中传递参数。
2. 借用构造函数

借用构造函数的技术,其基本思想为:

在子类型的构造函数中调用超类型构造函数。

优势:

  1. 能够向超类传递参数
  2. 解决了原型中包含引用类型值被全部实例共享的问题

缺点:

  1. 方法都在构造函数中定义,函数复用无从谈起,另外超类型原型中定义的方法对于子类型而言都是不可见的。
3. 组合继承(原型链 + 借用构造函数)

组合继承指的是将原型链和借用构造函数技术组合到一块,从而发挥两者之长的一种继承模式。基本思路:

使用原型链实现对原型属性和方法的继承,经过借用构造函数来实现对实例属性的继承,既经过在原型上定义方法来实现了函数复用,又保证了每一个实例都有本身的属性。

缺点:

  • 不管什么状况下,都会调用两次超类型构造函数:一次是在建立子类型原型的时候,另外一次是在子类型构造函数内部。

优势:

  • 能够向超类传递参数
  • 每一个实例都有本身的属性
  • 实现了函数复用
4. 原型式继承

原型继承的基本思想:

借助原型能够基于已有的对象建立新对象,同时还没必要所以建立自定义类型。

object() 函数内部,先穿甲一个临时性的构造函数,而后将传入的对象做为这个构造函数的原型,最后返回了这个临时类型的一个新实例,从本质上讲,object() 对传入的对象执行了一次浅拷贝。

ECMAScript5经过新增 Object.create()方法规范了原型式继承。这个方法接收两个参数:一个用做新对象原型的对象和(可选的)一个为新对象定义额外属性的对象(能够覆盖原型对象上的同名属性),在传入一个参数的状况下,Object.create()object() 方法的行为相同。

在没有必要建立构造函数,仅让一个对象与另外一个对象保持类似的状况下,原型式继承是能够胜任的。

缺点:

同原型链实现继承同样,包含引用类型值的属性会被全部实例共享。

5. 寄生式继承

寄生式继承是与原型式继承紧密相关的一种思路。寄生式继承的思路与寄生构造函数和工厂模式相似,即建立一个仅用于封装继承过程的函数,该函数在内部已某种方式来加强对象,最后再像真地是它作了全部工做同样返回对象。

基于 person 返回了一个新对象 -—— person2,新对象不只具备 person 的全部属性和方法,并且还有本身的 sayHi() 方法。在考虑对象而不是自定义类型和构造函数的状况下,寄生式继承也是一种有用的模式。

缺点:

  • 使用寄生式继承来为对象添加函数,会因为不能作到函数复用而效率低下。
  • 同原型链实现继承同样,包含引用类型值的属性会被全部实例共享。
6. 寄生组合式继承

所谓寄生组合式继承,即经过借用构造函数来继承属性,经过原型链的混成形式来继承方法,基本思路:

没必要为了指定子类型的原型而调用超类型的构造函数,咱们须要的仅是超类型原型的一个副本,本质上就是使用寄生式继承来继承超类型的原型,而后再将结果指定给子类型的原型。寄生组合式继承的基本模式以下所示:

  • 第一步:建立超类型原型的一个副本
  • 第二步:为建立的副本添加 constructor 属性
  • 第三步:将新建立的对象赋值给子类型的原型

至此,咱们就能够经过调用 inheritPrototype 来替换为子类型原型赋值的语句:

优势:

只调用了一次超类构造函数,效率更高。避免在SuberType.prototype上面建立没必要要的、多余的属性,与其同时,原型链还能保持不变。

所以寄生组合继承是引用类型最理性的继承范式。

10. 隐藏页面中的某个元素的方法有哪些?

查看解析

隐藏类型

屏幕并非惟一的输出机制,好比说屏幕上看不见的元素(隐藏的元素),其中一些依然可以被读屏软件阅读出来(由于读屏软件依赖于可访问性树来阐述)。为了消除它们之间的歧义,咱们将其归为三大类:

  • 彻底隐藏:元素从渲染树中消失,不占据空间。
  • 视觉上的隐藏:屏幕中不可见,占据空间。
  • 语义上的隐藏:读屏软件不可读,但正常占据空。

彻底隐藏

1.display 属性
display: none;
复制代码
2.hidden 属性

HTML5 新增属性,至关于 display: none

<div hidden>
</div>
复制代码

视觉上的隐藏

1.利用 position 和 盒模型 将元素移出可视区范围
  1. 设置 posoitionabsolutefixed,经过设置 topleft 等值,将其移出可视区域。
position:absolute;
left: -99999px;
复制代码
  1. 设置 positionrelative,经过设置 topleft 等值,将其移出可视区域。
position: relative;
left: -99999px;
height: 0
复制代码
  1. 设置 margin 值,将其移出可视区域范围(可视区域占位)。
margin-left: -99999px;
height: 0;
复制代码
2.利用 transfrom
  1. 缩放
transform: scale(0);
height: 0;
复制代码
  1. 移动 translateX, translateY
transform: translateX(-99999px);
height: 0
复制代码
  1. 旋转 rotate
transform: rotateY(90deg);
复制代码
3.设置其大小为0
  1. 宽高为0,字体大小为0:
height: 0;
width: 0;
font-size: 0;
复制代码
  1. 宽高为0,超出隐藏:
height: 0;
width: 0;
overflow: hidden;
复制代码
4.设置透明度为0
opacity: 0;
复制代码
5.visibility属性
visibility: hidden;
复制代码
6.层级覆盖,z-index 属性
position: relative;
z-index: -999;
复制代码

再设置一个层级较高的元素覆盖在此元素上。

7.clip-path 裁剪
clip-path: polygon(0 0, 0 0, 0 0, 0 0);
复制代码

语义上的隐藏

aria-hidden 属性

读屏软件不可读,占据空间,可见。

<div aria-hidden="true">
</div>
复制代码

11. let、const、var 的区别有哪些?

查看解析
声明方式 变量提高 暂时性死区 重复声明 块做用域有效 初始值 从新赋值
var 不存在 容许 不是 非必须 容许
let 不会 存在 不容许 非必须 容许
const 不会 存在 不容许 必须 不容许

1.let/const 定义的变量不会出现变量提高,而 var 定义的变量会提高。

2.相同做用域中,let 和 const 不容许重复声明,var 容许重复声明。

3.const 声明变量时必须设置初始值

4.const 声明一个只读的常量,这个常量不可改变。

这里有一个很是重要的点便是:在JS中,复杂数据类型,存储在栈中的是堆内存的地址,存在栈中的这个地址是不变的,可是存在堆中的值是能够变得。有没有至关常量指针/指针常量~

一图胜万言,以下图所示,不变的是栈内存中 a 存储的 20,和 b 中存储的 0x0012ff21(瞎编的一个数字)。而 {age: 18, star: 200} 是可变的。

12. 说一说你对JS执行上下文栈和做用域链的理解?

查看解析
在开始说明JS上下文栈和做用域以前,咱们先说明下JS上下文以及做用域的概念。

JS执行上下文

执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念, JavaScript 中运行任何的代码都是在执行上下文中运行。

执行上下文类型分为:

  • 全局执行上下文
  • 函数执行上下文

执行上下文建立过程当中,须要作如下几件事:

  1. 建立变量对象:首先初始化函数的参数arguments,提高函数声明和变量声明。
  2. 建立做用域链(Scope Chain):在执行期上下文的建立阶段,做用域链是在变量对象以后建立的。
  3. 肯定this的值,即 ResolveThisBinding

做用域

做用域负责收集和维护由全部声明的标识符(变量)组成的一系列查询,并实施一套很是严格的规则,肯定当前执行的代码对这些标识符的访问权限。—— 摘录自《你不知道的JavaScript》(上卷)

做用域有两种工做模型:词法做用域和动态做用域,JS采用的是词法做用域工做模型,词法做用域意味着做用域是由书写代码时变量和函数声明的位置决定的。(witheval 可以修改词法做用域,可是不推荐使用,对此不作特别说明)

做用域分为:

  • 全局做用域
  • 函数做用域
  • 块级做用域

JS执行上下文栈(后面简称执行栈)

执行栈,也叫作调用栈,具备 LIFO (后进先出) 结构,用于存储在代码执行期间建立的全部执行上下文。

规则以下:

  • 首次运行JavaScript代码的时候,会建立一个全局执行的上下文并Push到当前的执行栈中,每当发生函数调用,引擎都会为该函数建立一个新的函数执行上下文并Push当前执行栈的栈顶。
  • 当栈顶的函数运行完成后,其对应的函数执行上下文将会从执行栈中Pop出,上下文的控制权将移动到当前执行栈的下一个执行上下文。

以一段代码具体说明:

Global Execution Context (即全局执行上下文)首先入栈,过程以下:

伪代码:

//全局执行上下文首先入栈
ECStack.push(globalContext);

//执行fun1();
ECStack.push(<fun1> functionContext);

//fun1中又调用了fun2;
ECStack.push(<fun2> functionContext);

//fun2中又调用了fun3;
ECStack.push(<fun3> functionContext);

//fun3执行完毕
ECStack.pop();

//fun2执行完毕
ECStack.pop();

//fun1执行完毕
ECStack.pop();

//javascript继续顺序执行下面的代码,但ECStack底部始终有一个 全局上下文(globalContext);
复制代码

做用域链

做用域链就是从当前做用域开始一层一层向上寻找某个变量,直到找到全局做用域仍是没找到,就宣布放弃。这种一层一层的关系,就是做用域链。

如:

fn2做用域链 = [fn2做用域, fn1做用域,全局做用域]

13. 防抖函数的做用是什么?请实现一个防抖函数

查看解析
> 防抖函数的做用

防抖函数的做用就是控制函数在必定时间内的执行次数。防抖意味着N秒内函数只会被执行一次,若是N秒内再次被触发,则从新计算延迟时间。

举例说明: 小思最近在减肥,可是她很是吃吃零食。为此,与其男友约定好,若是10天不吃零食,就能够购买一个包(不要问为何是包,由于包治百病)。可是若是中间吃了一次零食,那么就要从新计算时间,直到小思坚持10天没有吃零食,才能购买一个包。因此,管不住嘴的小思,没有机会买包(悲伤的故事)... 这就是 防抖

防抖函数实现

  1. 事件第一次触发时,timeoutnull,调用 later(),若 immediatetrue,那么当即调用 func.apply(this, params);若是 immediatefalse,那么过 wait 以后,调用 func.apply(this, params)
  2. 事件第二次触发时,若是 timeout 已经重置为 null(即 setTimeout 的倒计时结束),那么流程与第一次触发时同样,若 timeout 不为 null(即 setTimeout 的倒计时未结束),那么清空定时器,从新开始计时。

immediate 为 true 时,表示函数在每一个等待时延的开始被调用。immediate 为 false 时,表示函数在每一个等待时延的结束被调用。

防抖的应用场景

  1. 搜索框输入查询,若是用户一直在输入中,没有必要不停地调用去请求服务端接口,等用户中止输入的时候,再调用,设置一个合适的时间间隔,有效减轻服务端压力。
  2. 表单验证
  3. 按钮提交事件。
  4. 浏览器窗口缩放,resize事件(如窗口中止改变大小以后从新计算布局)等。

14. 节流函数的做用是什么?有哪些应用场景,请实现一个节流函数

查看解析
> 节流函数的做用

节流函数的做用是规定一个单位时间,在这个单位时间内最多只能触发一次函数执行,若是这个单位时间内屡次触发函数,只能有一次生效。

节流函数实现

禁用第一次首先执行,传递 {leading: false} ;想禁用最后一次执行,传递 {trailing: false}

节流的应用场景

  1. 按钮点击事件
  2. 拖拽事件
  3. onScoll
  4. 计算鼠标移动的距离(mousemove)

15. 什么是闭包?闭包的做用是什么?

查看解析
闭包的定义

《JavaScript高级程序设计》:

闭包是指有权访问另外一个函数做用域中的变量的函数

《JavaScript权威指南》:

从技术的角度讲,全部的JavaScript函数都是闭包:它们都是对象,它们都关联到做用域链。

《你不知道的JavaScript》

当函数能够记住并访问所在的词法做用域时,就产生了闭包,即便函数是在当前词法做用域以外执行。

建立一个闭包

闭包使得函数能够继续访问定义时的词法做用域。拜 fn 所赐,在 foo() 执行后,foo 内部做用域不会被销毁。

闭包的做用
  1. 可以访问函数定义时所在的词法做用域(阻止其被回收)。

  2. 私有化变量

  1. 模拟块级做用域

  1. 建立模块

模块模式具备两个必备的条件(来自《你不知道的JavaScript》)

  • 必须有外部的封闭函数,该函数必须至少被调用一次(每次调用都会建立一个新的模块实例)
  • 封闭函数必须返回至少一个内部函数,这样内部函数才能在私有做用域中造成闭包,而且能够访问或者修改私有的状态。

16. 实现 Promise.all 方法

查看解析
在实现 Promise.all 方法以前,咱们首先要知道 Promise.all 的功能和特色,由于在清楚了 Promise.all 功能和特色的状况下,咱们才能进一步去写实现。

Promise.all 功能

Promise.all(iterable) 返回一个新的 Promise 实例。此实例在 iterable 参数内全部的 promisefulfilled 或者参数中不包含 promise 时,状态变成 fulfilled;若是参数中 promise 有一个失败rejected,此实例回调失败,失败缘由的是第一个失败 promise 的返回结果。

let p = Promise.all([p1, p2, p3]);
复制代码

p的状态由 p1,p2,p3决定,分红如下;两种状况:

(1)只有p一、p二、p3的状态都变成 fulfilled,p的状态才会变成 fulfilled,此时p一、p二、p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p一、p二、p3之中有一个被 rejected,p的状态就变成 rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

Promise.all 的特色

Promise.all 的返回值是一个 promise 实例

  • 若是传入的参数为空的可迭代对象,Promise.all同步 返回一个已完成状态的 promise
  • 若是传入的参数中不包含任何 promise,Promise.all异步 返回一个已完成状态的 promise
  • 其它状况下,Promise.all 返回一个 处理中(pending) 状态的 promise.

Promise.all 返回的 promise 的状态

  • 若是传入的参数中的 promise 都变成完成状态,Promise.all 返回的 promise 异步地变为完成。
  • 若是传入的参数中,有一个 promise 失败,Promise.all 异步地将失败的那个结果给失败状态的回调函数,而无论其它 promise 是否完成
  • 在任何状况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组

Promise.all 实现

17. 请实现一个 flattenDeep 函数,把嵌套的数组扁平化

例如:

flattenDeep([1, [2, [3, [4]], 5]]); //[1, 2, 3, 4, 5]
复制代码
查看解析
> 利用 Array.prototype.flat

ES6 为数组实例新增了 flat 方法,用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数组没有影响。

flat 默认只会 “拉平” 一层,若是想要 “拉平” 多层的嵌套数组,须要给 flat 传递一个整数,表示想要拉平的层数。

当传递的整数大于数组嵌套的层数时,会将数组拉平为一维数组,JS能表示的最大数字为 Math.pow(2, 53) - 1,所以咱们能够这样定义 flattenDeep 函数

利用 reduce 和 concat

使用 stack 无限反嵌套多层嵌套数组

18. 请实现一个 uniq 函数,实现数组去重

例如:

uniq([1, 2, 3, 5, 3, 2]);//[1, 2, 3, 5]
复制代码
查看解析

法1: 利用ES6新增数据类型 Set

Set相似于数组,可是成员的值都是惟一的,没有重复的值。

法2: 利用 indexOf

法3: 利用 includes

法4:利用 reduce

法5:利用 Map

19. 可迭代对象有哪些特色

查看解析
ES6 规定,默认的 `Iterator` 接口部署在数据结构的 `Symbol.iterator` 属性,换个角度,也能够认为,一个数据结构只要具备 `Symbol.iterator` 属性(`Symbol.iterator` 方法对应的是遍历器生成函数,返回的是一个遍历器对象),那么就能够其认为是可迭代的。

可迭代对象的特色

  • 具备 Symbol.iterator 属性,Symbol.iterator() 返回的是一个遍历器对象
  • 可使用 for ... of 进行循环
  • 经过被 Array.from 转换为数组

原生具备 Iterator 接口的数据结构:

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

20. JSONP 的原理是什么?

查看解析

尽管浏览器有同源策略,可是 <script> 标签的 src 属性不会被同源策略所约束,能够获取任意服务器上的脚本并执行。jsonp 经过插入 script 标签的方式来实现跨域,参数只能经过 url 传入,仅能支持 get 请求。

实现原理:

  • Step1: 建立 callback 方法
  • Step2: 插入 script 标签
  • Step3: 后台接受到请求,解析前端传过去的 callback 方法,返回该方法的调用,而且数据做为参数传入该方法
  • Step4: 前端执行服务端返回的方法调用

jsonp源码实现

使用:

服务端代码(node):

参考文章:

[1] [JavaScript高级程序设计第六章]

[2] Step-By-Step】高频面试题深刻解析 / 周刊01

[3] Step-By-Step】高频面试题深刻解析 / 周刊02

[4] Step-By-Step】高频面试题深刻解析 / 周刊03

[5] Step-By-Step】高频面试题深刻解析 / 周刊04

谢谢各位小伙伴愿意花费宝贵的时间阅读本文,若是本文给了您一点帮助或者是启发,请不要吝啬你的赞和Star,您的确定是我前进的最大动力。 github.com/YvetteLau/B…

本文中的代码使用了图片,若是您想直接复制代码,请移步这儿有20道大厂面试题等你查收

关注公众号,加入技术交流群

相关文章
相关标签/搜索