2018已经成为过去,2019年最重要的事就是换工作,换工作,换工作!!!得不到的永远在骚动,从去年的一些面试试水来看,各大厂的面试要求那是万变不离其中,进行自我总结了一下,无非就是以下知识点:
今天我在这里和各位看官一起探讨学习一下 es6 的重难点,es6 在过去的一年里可以说是站在风口浪尖上的猪,没有一家公司的面试官不涉猎这本葵花宝典,不需要全部都弄通透和明白,一些高逼格好用语法,就可以让我们的代码变得高大上,今天我们来聊一聊 所谓 es6 的重难点:
let 和 const 都是用来声明变量的命令,用法和var差不多,区别:
let 和 const 具体使用参见添加链接描述
这里我只强调几个点:
console.log(a) // 不存在变量声明提升
let a = 123
// 面试相关
(function () {
let arr = [];
for (let i=0;; i< 3;i++) { // for循环作用域
// 循环体内部作用域
arr.push(function () {
console.log(i)
})
}
arr[0](); // 3
arr[1](); // 3
arr[2](); // 3
})()
面试相关:let 和 const 的区别?
const 使用来声明常量,不可以进行修改,如果你这样回答就中计了。本质还是和数据类型有关
eg: let [a,b,c] = [1,2,3]
当数据多了,看起来不是那么清晰明了,个人比较喜欢
eg: let a=1, b=2, c=3;
默认值也是很简单的
let [x = 1] = [undefined];
x // 1
let [x = 1] = [null];
x // null
let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
// 实际上是
let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };
当键值和键名一样的时候,就可以省略,这个在一等功名中早有领略其风骚
let arr = {
goToUserDetail() {},
}
更高逼格点的用法参见
了解了数组的对象的结构赋值,我们即将进入正题,来看看函数参数的结构赋值,这个可是屡试不爽,在根据不同参数请求接口的时候特别好使
在讲解函数的结构赋值之前,我们先来看看函数的默认值
// 参数y的默认值为 ’World‘
function log(x, y = 'World') {
console.log("x=" + x, "y=" + y)
}
log('Hello') // x=Hello y=World
log('Hello', 'China') // x=Hello y=China
log('Hello', '') // x=Hello y= ''
log('Hello', undefined) // x=Hello y=World
log('Hello', null) // x=Hello y=null
// 对比以上输出结果可以知道,只有在参数为undefined的情况下,默认值才会生效
需求:
如果现在我想在不改变上面代码的情况下,调用log(‘9999’),输出 x = undefined, y = 9999,上面的代码明显不符合需求,这时候就不得不提到对象的结构赋值了
function log({x, y} = {x : undefined, y : 'World'}) {
console.log("x=" + x, "y=" + y)
}
log({y : 9999}) // x = undefined, y = 9999
function move({x = 0, y = 0} = {}) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
将上面略做修改
function move({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]
再修改
function move({x, y}) {
return [x, y];
}
console.log(move({ x: 3, y: 8 })); // [3, 8]
console.log(move({ x: 3 })); // [3, undefined]
console.log(move({})); // [undefined, undefined]
console.log(move()) // Cannot destructure property `x` of 'undefined' or 'null'.
函数reset参数参见
这里几个注意的知识点就是
1、 rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
2、rest 参数之后不能再有其他参数(即只能是最后一个参数)
function push(array, ...items) {
console.log(array, items)
}
push(4, 1, 2, 3) // 4, [1, 2, 3] 后面没有正常匹配的多余的参数放入一个数组
function push(array, ...items, last) {
console.log(array, items, last)
}
push(4, 1, 2, 3) // Rest parameter must be last formal parameter
上面代码,除了第一个参数正常匹配,其余的参数放入了一个数组,所以last相当于并没有传入,在函数内部是无法获取到last变量的
这个同样不赘述,依然是参见阮大大的 es6入门 唯一要注意的一点是,箭头函数没有自己的 this,都是继承而来,具体参见博客 this全面解析
es6入门 关于尾调用优化,铭记一点就是:函数的最后一步 只能是 调用另一个函数(不能是赋值操作,不能是还有下一步操作,也不可以没有return 值)
// 以下三种情况,都不属于尾调用
// 情况一
function f(x){
let y = g(x);
return y;
}
// 情况二
function f(x){
return g(x) + 1;
}
// 情况三
function f(x){
g(x);
}
上面代码中,情况一是调用函数g之后,还有赋值操作,所以不属于尾调用,即使语义完全一样。情况二也属于调用后还有操作,即使写在一行内。情况三等同于下面的代码。
function f(x){
g(x);
return undefined;
}
function f(x) {
if (x > 0) {
return m(x)
}
return n(x);
}
数组的扩展添加链接描述、对象的扩展添加链接描述 都是在 es5 的基础上新增的一些 API ,参见文档就好,比较简单
先附上一片面试官眼中的 promise
接下来我们浅谈一下promise实现的原理
我们知道 Promise 有三种状态,pending(进行中)、fulfilled(已成功)和rejected(已失败),而且接受一个函数参数,且该函数有两个参数
function Promise(excutor) {
let self = this
self.status = 'pending'
self.value = null
self.reason = null
self.onFulfilledCallbacks = []
self.onRejectedCallbacks = []
// 什么时候调用,什么时候改变状态,且状态不可逆
function resolve(value) {
if (self.status === 'pending') {
self.value = value
self.status = 'fulfilled'
// 以下方法会在先调用then回调之后,在resolve的时候执行
self.onFulfilledCallbacks.length && self.onFulfilledCallbacks.forEach(item => item())
}
}
function reject(reason) {
if (self.status === 'pending') {
self.reason = reason
self.status = 'rejected'
self.onFulfilledCallbacks.length && self.onRejectedCallbacks.forEach(item => item())
}
}
try {
excutor(resolve, reject) // 构造函数传进来的函数
} catch (err) {
reject(err)
}
}
Promise.prototype.then = function (onFulfilled, onRejected) {
let self = this
if (self.status === 'fulfilled') {
// 返回新的Promise
return new Promise((resolve, reject) => {
try {
let x = onFulfilled(self.value)
// then 的回调也有可以返回一个Promise
(x instanceof Promise) ? x.then(resolve, reject) : resolve(x)
} catch (err) {
reject(err)
}
})
}
if (self.status === 'rejected') {
return new Promise((resolve, reject) => {
try {
let x = onRejected(self.reason)
(x instanceof Promise) ? x.then(resolve, reject) : resolve(x)
} catch (err) {
reject(err)
}
})
}
if (self.status === 'pending') {
return new Promise((resolve, reject) => {
self.onFulfilledCallbacks.push(() => {
let x = onFulfilled(self.value)
(x instanceof Promise) ? x.then(resolve, reject) : resolve(x)
})
self.onRejectedCallbacks.push(() => {
let x = onRejected(self.reason)
(x instanceof Promise) ? x.then(resolve, reject) : resolve(x)
})
})
}
}
Promise.prototype.catch = function (fn) {
return this.then(null, fn)
}
参见 读懂class继承