1. 程式人生 > >es6重難點

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 【待更新】
模組化詳解 【待更新】