es6重難點
es6重難點總結歸納
2018已經成為過去,2019年最重要的事就是換工作,換工作,換工作!!!得不到的永遠在騷動,從去年的一些面試試水來看,各大廠的面試要求那是萬變不離其中,進行自我總結了一下,無非就是以下知識點:
- 呼叫堆疊
- 作用域閉包
- this全面解析
- 深淺拷貝的原理
- 原型prototype
- 事件機制、Event Loop
- Promise機制
- async / await原理、
- 防抖/節流原理
- 模組化詳解
- es6重難點、
- 瀏覽器薰染原理
- webpack配置(原理)
- 前端監控
- 跨域和安全
- 效能優化(
- VirtualDom原理、Diff演算法、
- 資料的雙向繫結
今天我在這裡和各位看官一起探討學習一下 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繼承