面试题精选二
::: tip 基础第二番
整理时间:2019-11-25
:::
本文挑选了20道大厂面试题,建议在阅读时,先思考一番,不要直接看解析。尽管,本文所有的答案,都是我在翻阅各种资料,思考并验证之后,才给出的。但因水平有限,本人的答案未必是最优的,如果您有更好的答案,欢迎给我留言。如果有错误,也请在评论区指出,谢谢。
1.new的实现原理是什么
new
的实现原理:
- 创建一个空对象,构造函数中的this指向这个空对象
- 这个新对象被执行 [[原型]] 连接
- 执行构造函数方法,属性和方法被添加到this引用的对象中
- 如果构造函数中没有返回其它对象,那么返回this,即创建的这个的新对象,否则,返回构造函数中返回的对象。
2. 如何正确判断this的指向?
如果用一句话说明 this 的指向,那么即是: 谁调用它,this 就指向谁。
但是仅通过这句话,我们很多时候并不能准确判断 this 的指向。因此我们需要借助一些规则去帮助自己:
this 的指向可以按照以下顺序判断:
全局环境中的 this
浏览器环境:无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象 window
;
node 环境:无论是否在严格模式下,在全局执行环境中(在任何函数体外部),this 都是空对象 {}
;
是否是 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 in
、 Object.assign
、 扩展运算符 ...
、Array.prototype.slice()
、Array.prototype.concat()
等,例如:
可以看出浅拷贝只最第一层属性进行了拷贝,当第一层的属性值是基本数据类型时,新的对象和原对象互不影响,但是如果第一层的属性值是复杂数据类型,那么新对象和原对象的属性值其指向的是同一块内存地址。
深拷贝实现
1.深拷贝最简单的实现是:
JSON.parse(JSON.stringify(obj))
JSON.parse(JSON.stringify(obj))
是最简单的实现方式,但是有一些缺陷:
- 对象的属性值是函数时,无法拷贝。
- 原型链上的属性无法拷贝
- 不能正确的处理 Date 类型的数据
- 不能处理 RegExp
- 会忽略 symbol
- 会忽略 undefined
2.实现一个 deepClone 函数
- 如果是基本数据类型,直接返回
- 如果是
RegExp
或者Date
类型,返回对应类型 - 如果是复杂数据类型,递归。
- 考虑循环引用的问题
4. call/apply 的实现原理是什么?
call
和 apply
的功能相同,都是改变 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?
利用隐式类型转换
==
操作符在左右数据类型不一致时,会先进行隐式转换。
a == 1 && a == 2 && a == 3
的值意味着其不可能是基本数据类型。因为如果 a 是 null 或者是 undefined bool类型,都不可能返回true。
因此可以推测 a 是复杂数据类型,JS 中复杂数据类型只有 object
,回忆一下,Object 转换为原始类型会调用什么方法?
- 如果部署了
[Symbol.toPrimitive]
接口,那么调用此接口,若返回的不是基本数据类型,抛出错误。 - 如果没有部署
[Symbol.toPrimitive]
接口,那么根据要转换的类型,先调用valueOf
/toString
- 非Date类型对象,
hint
是default
时,调用顺序为:valueOf
>>>toString
,即valueOf
返回的不是基本数据类型,才会继续调用valueOf
,如果toString
返回的还不是基本数据类型,那么抛出错误。 - 如果
hint
是string
(Date对象的hint默认是string) ,调用顺序为:toString
>>>valueOf
,即toString
返回的不是基本数据类型,才会继续调用valueOf
,如果valueOf
返回的还不是基本数据类型,那么抛出错误。 - 如果
hint
是number
,调用顺序为:valueOf
>>>toString
- 非Date类型对象,
利用数据劫持(Proxy/Object.defineProperty)
数组的 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内,两个盒子的垂直距离由
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 的应用
- 防止 margin 重叠 (同一个BFC内的两个两个相邻Box的
margin
会发生重叠,触发生成两个BFC,即不会重叠) - 清除内部浮动 (创建一个新的 BFC,因为根据 BFC 的规则,计算 BFC 的高度时,浮动元素也参与计算)
- 自适应多栏布局 (BFC的区域不会与float box重叠。因此,可以触发生成一个新的BFC)