es6重难点

es6重难点总结归纳

2018已经成为过去,2019年最重要的事就是换工作,换工作,换工作!!!得不到的永远在骚动,从去年的一些面试试水来看,各大厂的面试要求那是万变不离其中,进行自我总结了一下,无非就是以下知识点:

今天我在这里和各位看官一起探讨学习一下 es6 的重难点,es6 在过去的一年里可以说是站在风口浪尖上的猪,没有一家公司的面试官不涉猎这本葵花宝典,不需要全部都弄通透和明白,一些高逼格好用语法,就可以让我们的代码变得高大上,今天我们来聊一聊 所谓 es6 的重难点:

let 和 const **

let 和 const 都是用来声明变量的命令,用法和var差不多,区别:

  • let 声明的变量只能在命令所在的代码块内有效
  • const 一般用来声明常量,且不可改变

let 和 const 具体使用参见添加链接描述
这里我只强调几个点:

  • let 存在暂时性死区(未声明先使用会报错)
  • 不允许重复声明(这个实际生产中还是蛮有用的)
  • 存在块级作用域
  • const 声明的变量不是完全不能改动,如果是引用类型的值,eg: const arr = [1,2,3] arr[0] = 10; 只要引用的地址不被重新修改,是可以任意修改引用类型数据的值的,基本类型的值是不可以进行修改的
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;

默认值也是很简单的

  • 这里需要注意的是,只有赋值为undefined 或者 不赋值 的情况下,默认值才会生效
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参数**

函数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实现的原理

我们知道 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继承****

参见 读懂class继承

Set 和 Map 数据结构【待更新】
Promise【待更新】
async 【待更新】
模块化详解 【待更新】

猜你喜欢

转载自blog.csdn.net/woleigequshawanyier/article/details/85643314