引言
各位大佬在评论中指出的种种问题小弟万分感谢。由于这一年来,出了不少变动,所以才一直耽搁,现已修复各位大佬指出的问题和建议。请大家放心食用!感恩~🥳
当下,正面临着近几年来的最严重的互联网寒冬,听得最多的一句话便是:相见于江湖~🤣。缩减HC、裁员不绝于耳,大家都是人心惶惶,年前如此,年后想必肯定又是一场更为惨烈的江湖厮杀。但博主始终相信,寒冬之中,人才更是尤为珍贵。只要有过硬的操作和装备,在逆风局下,同样也能来一波收割翻盘。
给大家推荐一个实用面试题库
1、前端面试题库 (面试必备) 推荐:★★★★★
地址:前端面试题库
修炼原则
想必大家很厌烦笔试和考察知识点。因为其实在平时实战中,讲究的是开发效率,很少会去刻意记下一些细节和深挖知识点,脑海中都是一些分散的知识点,无法系统性地关联成网,一直处于似曾相识的状态。不知道多少人和博主一样,至今每次写阻止冒泡都需要谷歌一番如何拼写。🤪。
以如此的状态,定然是无法在面试的战场上纵横的。其实面试就犹如考试,大家回想下高考之前所做的事,无非就是 理解 和 系统性关联记忆。本秘籍的知识点较多,花点时间一个个理解并记忆后,自然也就融会贯通,无所畏惧。
由于本秘籍为了便于记忆,快速达到应试状态,类似于复习知识大纲。知识点会尽量的精简与提炼知识脉络,并不去展开深入细节,面面俱到。有兴趣或者有疑问的童鞋可以自行谷歌下对应知识点的详细内容。😋
CSS
1. 盒模型
页面渲染时,dom 元素所采用的 布局模型。可通过box-sizing
进行设置。根据计算宽高的区域可分为:
content-box
(W3C 标准盒模型)border-box
(IE 盒模型)padding-box
(FireFox 曾经支持)margin-box
(浏览器未实现)
Tips: 理论上是有上面 4 种盒子,但现在 w3c 与 mdn 规范中均只支持
content-box
与border-box
;
2. BFC
块级格式化上下文,是一个独立的渲染区域,让处于 BFC 内部的元素与外部的元素相互隔离,使内外元素的定位不会相互影响。
IE下为 Layout,可通过 zoom:1 触发
- 触发条件:
- 根元素
position: absolute/fixed
display: inline-block / table
float
元素ovevflow
!==visible
- 规则:
- 属于同一个 BFC 的两个相邻 Box 垂直排列
- 属于同一个 BFC 的两个相邻 Box 的 margin 会发生重叠
- BFC 中子元素的 margin box 的左边, 与包含块 (BFC) border box的左边相接触 (子元素 absolute 除外)
- BFC 的区域不会与 float 的元素区域重叠
- 计算 BFC 的高度时,浮动子元素也参与计算
- 文字层不会被浮动层覆盖,环绕于周围
- 应用:
- 阻止
margin
重叠 - 可以包含浮动元素 —— 清除内部浮动(清除浮动的原理是两个
div
都位于同一个 BFC 区域之中) - 自适应两栏布局
- 可以阻止元素被浮动元素覆盖
3.层叠上下文
元素提升为一个比较特殊的图层,在三维空间中 (z轴) 高出普通元素一等。
- 触发条件
- 根层叠上下文(
html
) position
- css3属性
flex
transform
opacity
filter
will-change
-webkit-overflow-scrolling
- 层叠等级:层叠上下文在z轴上的排序
- 在同一层叠上下文中,层叠等级才有意义
z-index
的优先级最高
4. 居中布局
- 水平居中
- 行内元素:
text-align: center
- 块级元素:
margin: 0 auto
absolute + transform
flex + justify-content: center
- 垂直居中
line-height: height
absolute + transform
flex + align-items: center
table
- 水平垂直居中
absolute + transform
flex + justify-content + align-items
5. 选择器优先级
!important
> 行内样式 >#id
>.class
>tag
> * > 继承 > 默认- 选择器 从右往左 解析
6.去除浮动影响,防止父级高度塌陷
- 通过增加尾元素清除浮动
:after /
: clear: both
- 创建父级 BFC
- 父级设置高度
7.link 与 @import 的区别
link
功能较多,可以定义 RSS,定义 Rel 等作用,而@import
只能用于加载 css- 当解析到
link
时,页面会同步加载所引的 css,而@import
所引用的 css 会等到页面加载完才被加载 @import
需要 IE5 以上才能使用link
可以使用 js 动态引入,@import
不行
8. CSS预处理器(Sass/Less/Postcss)
CSS预处理器的原理: 是将类 CSS 语言通过 Webpack 编译 转成浏览器可读的真正 CSS。在这层编译之上,便可以赋予 CSS 更多更强大的功能,常用功能:
- 嵌套
- 变量
- 循环语句
- 条件语句
- 自动前缀
- 单位转换
- mixin复用
面试中一般不会重点考察该点,一般介绍下自己在实战项目中的经验即可~
9.CSS动画
transition
: 过渡动画
transition-property
: 属性transition-duration
: 间隔transition-timing-function
: 曲线transition-delay
: 延迟- 常用钩子:
transitionend
animation / keyframes
animation-name
: 动画名称,对应@keyframes
animation-duration
: 间隔animation-timing-function
: 曲线animation-delay
: 延迟animation-iteration-count
: 次数
infinite
: 循环动画
animation-direction
: 方向
alternate
: 反向播放
animation-fill-mode
: 静止模式
forwards
: 停止时,保留最后一帧backwards
: 停止时,回到第一帧both
: 同时运用forwards / backwards
- 常用钩子:
animationend
- 动画属性: 尽量使用动画属性进行动画,能拥有较好的性能表现
translate
scale
rotate
skew
opacity
color
经验
通常,CSS 并不是重点的考察领域,但这其实是由于现在国内业界对 CSS 的专注不够导致的,真正精通并专注于 CSS 的团队和人才并不多。因此如果能在 CSS 领域有自己的见解和经验,反而会为相当的加分和脱颖而出。
JavaScript
1. 原型 / 构造函数 / 实例
- 原型
(prototype)
: 一个简单的对象,用于实现对象的 属性继承。可以简单的理解成对象的爹。在 Firefox 和 Chrome 中,每个JavaScript
对象中都包含一个__proto__
(非标准)的属性指向它爹(该对象的原型),可obj.__proto__
进行访问。 - 构造函数: 可以通过
new
来 新建一个对象 的函数。 - 实例: 通过构造函数和
new
创建出来的对象,便是实例。 实例通过__proto__
指向原型,通过constructor
指向构造函数。
说了一大堆,大家可能有点懵逼,这里来举个栗子,以Object
为例,我们常用的Object
便是一个构造函数,因此我们可以通过它构建实例。
// 实例 const instance = new Object() 复制代码
则此时, 实例为instance
, 构造函数为Object
,我们知道,构造函数拥有一个prototype
的属性指向原型,因此原型为:
// 原型 const prototype = Object.prototype 复制代码
这里我们可以来看出三者的关系:
实例.__proto__ === 原型 原型.constructor === 构造函数 构造函数.prototype === 原型 // 这条线其实是是基于原型进行获取的,可以理解成一条基于原型的映射线 // 例如: // const o = new Object() // o.constructor === Object --> true // o.__proto__ = null; // o.constructor === Object --> false // 注意: 其实实例上并不是真正有 constructor 这个指针,它其实是从原型链上获取的 // instance.hasOwnProperty('constructor') === false (感谢 刘博海 Brian 童鞋🥳) 实例.constructor === 构造函数 复制代码
此处感谢 caihaihong 童鞋的指出。
放大来看,我画了张图供大家彻底理解:
2.原型链:
原型链是由原型对象组成,每个对象都有 __proto__
属性,指向了创建该对象的构造函数的原型,__proto__
将对象连接起来组成了原型链。是一个用来实现继承和共享属性的有限的对象链。
- 属性查找机制: 当查找对象的属性时,如果实例对象自身不存在该属性,则沿着原型链往上一级查找,找到时则输出,不存在时,则继续沿着原型链往上一级查找,直至最顶级的原型对象
Object.prototype
,如还是没找到,则输出undefined
; - 属性修改机制: 只会修改实例对象本身的属性,如果不存在,则进行添加该属性,如果需要修改原型的属性时,则可以用:
b.prototype.x = 2
;但是这样会造成所有继承于该对象的实例的属性发生改变。
3. 执行上下文(EC)
执行上下文可以简单理解为一个对象:
- 它包含三个部分:
- 变量对象(VO)
- 作用域链(词法作用域)
this
指向
- 它的类型:
- 全局执行上下文
- 函数执行上下文
eval
执行上下文
- 代码执行过程:
- 创建 全局上下文 (global EC)
- 全局执行上下文 (caller) 逐行 自上而下 执行。遇到函数时,函数执行上下文 (callee) 被
push
到执行栈顶层 - 函数执行上下文被激活,成为 active EC, 开始执行函数中的代码,caller 被挂起
- 函数执行完后,callee 被
pop
移除出执行栈,控制权交还全局上下文 (caller),继续执行
2.变量对象
变量对象,是执行上下文中的一部分,可以抽象为一种 数据作用域,其实也可以理解为就是一个简单的对象,它存储着该执行上下文中的所有 变量和函数声明(不包含函数表达式)。
活动对象 (AO): 当变量对象所处的上下文为 active EC 时,称为活动对象。
3. 作用域
执行上下文中还包含作用域链。理解作用域之前,先介绍下作用域。作用域其实可理解为该上下文中声明的 变量和声明的作用范围。可分为 块级作用域 和 函数作用域
特性:
- 声明提前: 一个声明在函数体内都是可见的, 函数优先于变量
- 非匿名自执行函数,函数变量为 只读 状态,无法修改
let foo = function() { console.log(1) }; (function foo() { foo = 10 // 由于foo在函数中只为可读,因此赋值无效 console.log(foo) }()) // 结果打印: ƒ foo() { foo = 10 ; console.log(foo) } 复制代码
4.作用域链
我们知道,我们可以在执行上下文中访问到父级甚至全局的变量,这便是作用域链的功劳。作用域链可以理解为一组对象列表,包含 父级和自身的变量对象,因此我们便能通过作用域链访问到父级里声明的变量或者函数。
- 由两部分组成:
[[scope]]
属性: 指向父级变量对象和作用域链,也就是包含了父级的[[scope]]
和AO
- AO: 自身活动对象
如此 [[scopr]]
包含[[scope]]
,便自上而下形成一条 链式作用域。
5. 闭包
闭包属于一种特殊的作用域,称为 静态作用域。它的定义可以理解为: 父函数被销毁 的情况下,返回出的子函数的[[scope]]
中仍然保留着父级的单变量对象和作用域链,因此可以继续访问到父级的变量对象,这样的函数称为闭包。
- 闭包会产生一个很经典的问题:
- 多个子函数的
[[scope]]
都是同时指向父级,是完全共享的。因此当父级的变量对象被修改时,所有子函数都受到影响。
- 解决:
- 变量可以通过 函数参数的形式 传入,避免使用默认的
[[scope]]
向上查找 - 使用
setTimeout
包裹,通过第三个参数传入 - 使用 块级作用域,让变量成为自己上下文的属性,避免共享
6. script 引入方式:
- html 静态
</code>引入</li><li style="text-align: left;">js 动态插入<code><script></code></li><li style="text-align: left;"><code><script defer></code>: 延迟加载,元素解析完成后执行</li><li style="text-align: left;"><code><script async></code>: 异步加载,但执行时会阻塞元素渲染</li></ul><h4 id="XPCyI" style="text-align: left;"><a name="t24"></a>7. 对象的拷贝</h4><ul style="text-align: left;"><li style="text-align: left;">浅拷贝: 以赋值的形式拷贝引用对象,仍指向同一个地址,<strong>修改时原对象也会受到影响</strong></li></ul><ul data-lake-indent="1" style="text-align: left;"><li style="text-align: left;"><code>Object.assign</code></li><li style="text-align: left;">展开运算符(...)</li></ul><ul style="text-align: left;"><li style="text-align: left;">深拷贝: 完全拷贝一个新对象,<strong>修改时原对象不再受到任何影响</strong></li></ul><ul data-lake-indent="1" style="text-align: left;"><li style="text-align: left;"><code>JSON.parse(JSON.stringify(obj))</code>: 性能最快</li></ul><ul data-lake-indent="2" style="text-align: left;"><li style="text-align: left;">具有循环引用的对象时,报错</li><li style="text-align: left;">当值为函数、<code>undefined</code>、或<code>symbol</code>时,无法拷贝</li></ul><ul data-lake-indent="1" style="text-align: left;"><li style="text-align: left;">递归进行逐一赋值</li></ul><h4 id="waJMA" style="text-align: left;"><a name="t25"></a>8. new运算符的执行过程</h4><ul style="text-align: left;"><li style="text-align: left;">新生成一个对象</li><li style="text-align: left;">链接到原型: <code>obj.__proto__ = Con.prototype</code></li><li style="text-align: left;">绑定this: <code>apply</code></li><li style="text-align: left;">返回新对象(如果构造函数有自己 retrun 时,则返回该值)</li></ul><h4 id="CAVMn" style="text-align: left;"><a name="t26"></a>9. instanceof原理</h4><div style="text-align: left;">能在实例的 <strong>原型对象链</strong> 中找到该构造函数的<code>prototype</code>属性所指向的 <strong>原型对象</strong>,就返回<code>true</code>。即:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22%2F%2F%20__proto__%3A%20%E4%BB%A3%E8%A1%A8%E5%8E%9F%E5%9E%8B%E5%AF%B9%E8%B1%A1%E9%93%BE%5Cninstance.%5B__proto__...%5D%20%3D%3D%3D%20instance.constructor.prototype%5Cn%20%5Cn%2F%2F%20return%20true%5Cn%E5%A4%8D%E5%88%B6%E4%BB%A3%E7%A0%81%22%2C%22id%22%3A%22rGNzB%22%7D"></div><h4 id="Z89Ut" style="text-align: left;"><a name="t27"></a>10. 代码的复用</h4><div style="text-align: left;">当你发现任何代码开始写第二遍时,就要开始考虑如何复用。一般有以下的方式:</div><ul style="text-align: left;"><li style="text-align: left;">函数封装</li><li style="text-align: left;">继承</li><li style="text-align: left;">复制<code>extend</code></li><li style="text-align: left;">混入<code>mixin</code></li><li style="text-align: left;">借用<code>apply/call</code></li></ul><h4 id="eEWSf" style="text-align: left;"><a name="t28"></a>11. 继承</h4><div style="text-align: left;">在 JS 中,继承通常指的便是 <strong>原型链继承</strong>,也就是通过指定原型,并可以通过原型链继承原型上的属性或者方法。</div><ul style="text-align: left;"><li style="text-align: left;">最优化: <strong>圣杯模式</strong></li></ul><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22var%20inherit%20%3D%20(function(c%2Cp)%7B%5Cn%5Ctvar%20F%20%3D%20function()%7B%7D%3B%5Cn%5Ctreturn%20function(c%2Cp)%7B%5Cn%5Ct%5CtF.prototype%20%3D%20p.prototype%3B%5Cn%5Ct%5Ctc.prototype%20%3D%20new%20F()%3B%5Cn%5Ct%5Ctc.uber%20%3D%20p.prototype%3B%5Cn%5Ct%5Ctc.prototype.constructor%20%3D%20c%3B%5Cn%5Ct%7D%5Cn%7D)()%3B%5Cn%E5%A4%8D%E5%88%B6%E4%BB%A3%E7%A0%81%22%2C%22id%22%3A%22Z9f2Z%22%7D"></div><ul style="text-align: left;"><li style="text-align: left;">使用 ES6 的语法糖 <code>class / extends</code></li></ul><h4 id="pXog4" style="text-align: left;"><a name="t29"></a>12. 类型转换</h4><div style="text-align: left;">大家都知道 JS 中在使用运算符号或者对比符时,会自带隐式转换,规则如下:</div><ul style="text-align: left;"><li style="text-align: left;">-、*、/、% :一律转换成数值后计算</li><li style="text-align: left;">+:</li></ul><ul data-lake-indent="1" style="text-align: left;"><li style="text-align: left;">数字 + 字符串 = 字符串, 运算顺序是从左到右</li><li style="text-align: left;">数字 + 对象, 优先调用对象的<code>valueOf</code> -> <code>toString</code></li><li style="text-align: left;">数字 + <code>boolean/null</code> -> 数字</li><li style="text-align: left;">数字 + <code>undefined</code> -> <code>NaN</code></li></ul><ul style="text-align: left;"><li style="text-align: left;"><code>[1].toString() === '1'</code></li><li style="text-align: left;"><code>{}.toString() === '[object object]'</code></li><li style="text-align: left;"><code>NaN</code> !== <code>NaN</code> 、<code>+undefined 为 NaN</code></li></ul><h4 id="mRYdA" style="text-align: left;"><a name="t30"></a>13. 类型判断</h4><div style="text-align: left;">判断 Target 的类型,单单用 typeof 并无法完全满足,这其实并不是 bug,本质原因是 JS 的万物皆对象的理论。因此要真正完美判断时,我们需要区分对待:</div><ul style="text-align: left;"><li style="text-align: left;">基本类型(<code>null</code>): 使用 <code>String(null)</code></li><li style="text-align: left;">基本类型(<code>string / number / boolean / undefined</code>) + <code>function</code>: 直接使用 <code>typeof</code>即可</li><li style="text-align: left;">其余引用类型(<code>Array / Date / RegExp Error</code>): 调用<code>toString</code>后根据<code>[object XXX]</code>进行判断</li></ul><div style="text-align: left;">很稳的判断封装:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22let%20class2type%20%3D%20%7B%7D%5Cn'Array%20Date%20RegExp%20Object%20Error'.split('%20').forEach(e%20%3D%3E%20class2type%5B%20'%5Bobject%20'%20%2B%20e%20%2B%20'%5D'%20%5D%20%3D%20e.toLowerCase())%20%5Cn%20%5Cnfunction%20type(obj)%20%7B%5Cn%20%20%20%20if%20(obj%20%3D%3D%20null)%20return%20String(obj)%5Cn%20%20%20%20return%20typeof%20obj%20%3D%3D%3D%20'object'%20%3F%20class2type%5B%20Object.prototype.toString.call(obj)%20%5D%20%7C%7C%20'object'%20%3A%20typeof%20obj%5Cn%7D%5Cn%E5%A4%8D%E5%88%B6%E4%BB%A3%E7%A0%81%22%2C%22id%22%3A%22slJ4Z%22%7D"></div><h4 id="xwWmM" style="text-align: left;"><a name="t31"></a>14. 模块化</h4><div style="text-align: left;">模块化开发在现代开发中已是必不可少的一部分,它大大提高了项目的可维护、可拓展和可协作性。通常,我们 <strong>在浏览器中使用 ES6 的模块化支持,在 Node 中使用 commonjs 的模块化支持。</strong></div><ul style="text-align: left;"><li style="text-align: left;">分类:</li></ul><ul data-lake-indent="1" style="text-align: left;"><li style="text-align: left;">es6: <code>import / export</code></li><li style="text-align: left;">commonjs: <code>require / module.exports / exports</code></li><li style="text-align: left;">amd: <code>require / defined</code></li></ul><ul style="text-align: left;"><li style="text-align: left;"><code>require</code>与<code>import</code>的区别</li></ul><ul data-lake-indent="1" style="text-align: left;"><li style="text-align: left;"><code>require</code>支持 <strong>动态导入</strong>,<code>import</code>不支持,正在提案 (babel 下可支持)</li><li style="text-align: left;"><code>require</code>是 <strong>同步</strong> 导入,<code>import</code>属于 <strong>异步</strong> 导入</li><li style="text-align: left;"><code>require</code>是 <strong>值拷贝</strong>,导出值变化不会影响导入值;<code>import</code>指向 <strong>内存地址</strong>,导入值会随导出值而变化</li></ul><h4 id="tivfD" style="text-align: left;"><a name="t32"></a>15. 防抖与节流</h4><div style="text-align: left;">防抖与节流函数是一种最常用的 <strong>高频触发优化方式</strong>,能对性能有较大的帮助。</div><ul style="text-align: left;"><li style="text-align: left;"><strong>防抖 (debounce)</strong>: 将多次高频操作优化为只在最后一次执行,通常使用的场景是:用户输入,只需再输入完成后做一次输入校验即可。</li></ul><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22function%20debounce(fn%2C%20wait%2C%20immediate)%20%7B%5Cn%20%20%20%20let%20timer%20%3D%20null%5Cn%20%5Cn%20%20%20%20return%20function()%20%7B%5Cn%20%20%20%20%20%20%20%20let%20args%20%3D%20arguments%5Cn%20%20%20%20%20%20%20%20let%20context%20%3D%20this%5Cn%20%5Cn%20%20%20%20%20%20%20%20if%20(immediate%20%26%26%20!timer)%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20fn.apply(context%2C%20args)%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%5Cn%20%20%20%20%20%20%20%20if%20(timer)%20clearTimeout(timer)%5Cn%20%20%20%20%20%20%20%20timer%20%3D%20setTimeout(()%20%3D%3E%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20fn.apply(context%2C%20args)%5Cn%20%20%20%20%20%20%20%20%7D%2C%20wait)%5Cn%20%20%20%20%7D%5Cn%7D%5Cn%E5%A4%8D%E5%88%B6%E4%BB%A3%E7%A0%81%22%2C%22id%22%3A%22rUDXP%22%7D"></div><ul style="text-align: left;"><li style="text-align: left;"><strong>节流(throttle)</strong>: 每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作,通常使用场景: 滚动条事件 或者 resize 事件,通常每隔 100~500 ms执行一次即可。</li></ul><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22function%20throttle(fn%2C%20wait%2C%20immediate)%20%7B%5Cn%20%20%20%20let%20timer%20%3D%20null%5Cn%20%20%20%20let%20callNow%20%3D%20immediate%5Cn%20%20%20%20%5Cn%20%20%20%20return%20function()%20%7B%5Cn%20%20%20%20%20%20%20%20let%20context%20%3D%20this%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20args%20%3D%20arguments%5Cn%20%5Cn%20%20%20%20%20%20%20%20if%20(callNow)%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20fn.apply(context%2C%20args)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20callNow%20%3D%20false%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%5Cn%20%20%20%20%20%20%20%20if%20(!timer)%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20timer%20%3D%20setTimeout(()%20%3D%3E%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fn.apply(context%2C%20args)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20timer%20%3D%20null%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%20wait)%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%7D%5Cn%7D%5Cn%E5%A4%8D%E5%88%B6%E4%BB%A3%E7%A0%81%22%2C%22id%22%3A%227qtSt%22%7D"></div><h4 id="7mw5I" style="text-align: left;"><a name="t33"></a>16. 函数执行改变this</h4><div style="text-align: left;">由于 JS 的设计原理: 在函数中,可以引用运行环境中的变量。因此就需要一个机制来让我们可以在函数体内部获取当前的运行环境,这便是<code>this</code>。</div><div style="text-align: left;">因此要明白 <code>this</code> 指向,其实就是要搞清楚 函数的运行环境,说人话就是,谁调用了函数。例如:</div><ul style="text-align: left;"><li style="text-align: left;"><code>obj.fn()</code>,便是 <code>obj</code> 调用了函数,既函数中的 <code>this === obj</code></li><li style="text-align: left;"><code>fn()</code>,这里可以看成 <code>window.fn()</code>,因此 <code>this === window</code></li></ul><div style="text-align: left;">但这种机制并不完全能满足我们的业务需求,因此提供了三种方式可以手动修改 <code>this</code> 的指向:</div><ul style="text-align: left;"><li style="text-align: left;"><code>call: fn.call(target, 1, 2)</code></li><li style="text-align: left;"><code>apply: fn.apply(target, [1, 2])</code></li><li style="text-align: left;"><code>bind: fn.bind(target)(1,2)</code></li></ul><h4 id="jZKqG" style="text-align: left;"><a name="t34"></a>17. ES6/ES7</h4><div style="text-align: left;">由于 Babel 的强大和普及,现在 ES6/ES7 基本上已经是现代化开发的必备了。通过新的语法糖,能让代码整体更为简洁和易读。</div><ul style="text-align: left;"><li style="text-align: left;">声明</li></ul><ul data-lake-indent="1" style="text-align: left;"><li style="text-align: left;"><code>let / const</code>: 块级作用域、不存在变量提升、暂时性死区、不允许重复声明</li><li style="text-align: left;"><code>const</code>: 声明常量,无法修改</li></ul><ul style="text-align: left;"><li style="text-align: left;">解构赋值<br /></li><li style="text-align: left;"><code>class / extend</code>: 类声明与继承<br /></li><li style="text-align: left;"><code>Set / Map</code>: 新的数据结构<br /></li><li style="text-align: left;">异步解决方案:</li></ul><ul data-lake-indent="1" style="text-align: left;"><li style="text-align: left;"><code>Promise</code>的使用与实现<br /></li><li style="text-align: left;"><code>generator</code>:</li></ul><ul data-lake-indent="2" style="text-align: left;"><li style="text-align: left;"><code>yield</code>: 暂停代码</li><li style="text-align: left;"><code>next()</code>: 继续执行代码</li></ul><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22function*%20helloWorld()%20%7B%5Cn%20%20yield%20'hello'%3B%5Cn%20%20yield%20'world'%3B%5Cn%20%20return%20'ending'%3B%5Cn%7D%5Cn%20%5Cnconst%20generator%20%3D%20helloWorld()%3B%5Cn%20%5Cngenerator.next()%20%20%2F%2F%20%7B%20value%3A%20'hello'%2C%20done%3A%20false%20%7D%5Cn%20%5Cngenerator.next()%20%20%2F%2F%20%7B%20value%3A%20'world'%2C%20done%3A%20false%20%7D%5Cn%20%5Cngenerator.next()%20%20%2F%2F%20%7B%20value%3A%20'ending'%2C%20done%3A%20true%20%7D%5Cn%20%5Cngenerator.next()%20%20%2F%2F%20%7B%20value%3A%20undefined%2C%20done%3A%20true%20%7D%5Cn%20%5Cn%E5%A4%8D%E5%88%B6%E4%BB%A3%E7%A0%81%22%2C%22id%22%3A%22NjPFQ%22%7D"></div><ul data-lake-indent="1" style="text-align: left;"><li style="text-align: left;"><code>await / async</code>: 是<code>generator</code>的语法糖, babel中是基于<code>promise</code>实现。</li></ul><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22async%20function%20getUserByAsync()%7B%5Cn%20%20%20let%20user%20%3D%20await%20fetchUser()%3B%5Cn%20%20%20return%20user%3B%5Cn%7D%5Cn%20%5Cnconst%20user%20%3D%20await%20getUserByAsync()%5Cnconsole.log(user)%5Cn%E5%A4%8D%E5%88%B6%E4%BB%A3%E7%A0%81%22%2C%22id%22%3A%2262G6F%22%7D"></div><h4 id="p7rf7" style="text-align: left;"><a name="t35"></a>18. AST</h4><div style="text-align: left;"><strong>抽象语法树 (Abstract Syntax Tree)</strong>,是将代码逐字母解析成 <strong>树状对象</strong> 的形式。这是语言之间的转换、代码语法检查,代码风格检查,代码格式化,代码高亮,代码错误提示,代码自动补全等等的基础。例如:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22function%20square(n)%7B%5Cn%5Ctreturn%20n%20*%20n%5Cn%7D%5Cn%E5%A4%8D%E5%88%B6%E4%BB%A3%E7%A0%81%22%2C%22id%22%3A%22hgUVK%22%7D"></div><div style="text-align: left;">通过解析转化成的<code>AST</code>如下图:</div><div style="text-align: left;"><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2Fvni5rsbqj2axk_3176fba39dd941e48e9eeb63d662e617.png%22%2C%22originWidth%22%3A842%2C%22originHeight%22%3A554%2C%22size%22%3A0%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Afalse%2C%22bottom%22%3Afalse%7D%2C%22width%22%3A842%2C%22height%22%3A554%7D"></span></div><h2 id="X416M" style="text-align: left;"><a name="t36"></a></h2><h2 id="Soym2" style="text-align: left;"><a name="t37"></a>总结给大家推荐一个实用面试题库</h2><h4 id="VSg2i" style="text-align: left;"><a name="t38"></a><strong> 1、前端面试题库 (</strong>面试必备)<strong> <strong>推荐:★★★★★</strong></strong></h4><div style="text-align: left;">地址:<a href="https://mp.weixin.qq.com/s?spm=a2c6h.13046898.publish-article.46.5c656ffam3QjcL&__biz=MzU5NzA0NzQyNg==&mid=2247485824&idx=3&sn=70cd26a7c0c683de64802f6cb9835003&scene=21#wechat_redirect" target="_blank">前端面试题库</a></div><div style="text-align: left;"> </div><h4 id="IyxXn" style="text-align: left;"><a name="t39"></a>19. babel编译原理</h4><ul style="text-align: left;"><li style="text-align: left;">babylon 将 ES6/ES7 代码解析成 AST</li><li style="text-align: left;">babel-traverse 对 AST 进行遍历转译,得到新的 AST</li><li style="text-align: left;">新 AST 通过 babel-generator 转换成 ES5</li></ul><h4 id="u87uZ" style="text-align: left;"><a name="t40"></a>20. 函数柯里化</h4><div style="text-align: left;">在一个函数中,首先填充几个参数,然后再返回一个新的函数的技术,称为函数的柯里化。通常可用于在不侵入函数的前提下,为函数 <strong>预置通用参数</strong>,供多次重复调用。</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22const%20add%20%3D%20function%20add(x)%20%7B%5Cn%5Ctreturn%20function%20(y)%20%7B%5Cn%5Ct%5Ctreturn%20x%20%2B%20y%5Cn%5Ct%7D%5Cn%7D%5Cn%20%5Cnconst%20add1%20%3D%20add(1)%5Cn%20%5Cnadd1(2)%20%3D%3D%3D%203%5Cnadd1(20)%20%3D%3D%3D%2021%5Cn%E5%A4%8D%E5%88%B6%E4%BB%A3%E7%A0%81%22%2C%22id%22%3A%22vJ70T%22%7D"></div><div><br /></div>
中高级前端大厂面试秘籍,为你保驾护航金三银四,直通大厂(上)(一):https://developer.aliyun.com/article/1413863