ES6總結篇(一)小弟的第一滴血,希望大家多多包涵哈

MUM發表於2020-03-14

簡介

相信大家都看過阮一峰老師的《ECMAScript6簡介》,博主也讀過很多次了,每次閱讀都有新的收穫,然後博主打算整理成一套完整筆記,做一套ES6總結系列。老話常說,讀書先把厚書讀薄,然後再把薄書讀厚,博主現在做的事是把書讀薄的過程,當然了,也是為了方便大家快速的瞭解ES6的特性以及本書的重要知識點,廢話不多說,直接開車。

第一章、EACMScript 6 簡介

1. EACMScript與JavaScript的關係

大家對JavaScript起源都有一定了解,這個就不再贅述,直接引用ES6的總結:

ECMAScript 和 JavaScript 的關係是,前者是後者的規格,後者是前者的一種實現。
另外的 ECMAScript 方言還有 JScript 和ActionScript。日常場合,這兩個詞是可以互換的。
複製程式碼

2. Babel 轉碼器

  1. Babel的作用
  • 1.1 Babel babeljs.io/ 是一個廣泛使用的 ES6 轉碼器,可以將 ES6 程式碼轉為 ES5 程式碼,從而在老版本的瀏覽器執行
// 轉碼前
input.map(item => item + 1);

// 轉碼後
input.map(function (item) {
  return item + 1;
});
複製程式碼

由於ES6有相容性問題,導致有些語法糖不能在某些瀏覽器上正確執行,經過Babel轉碼之後變成大家都相容的ES5,就可以正常執行了。

  1. 安裝以及配置
  • 2.2.1 在專案的命令列安裝 $ npm install --save-dev @babel/core
  • 2.2.2 .babelrc檔案用來設定轉碼規則和外掛,基本規格如下:
{
    "presets": [
      "@babel/env", //最新轉碼規則
      "@babel/preset-react" //react 轉碼規則
    ],
    "plugins": []
}
複製程式碼

presets欄位設定轉碼規則: 官方提供以下的規則集(需要先安裝再在**.babelrc檔案**中使用,安裝如下)

# 最新轉碼規則
$ npm install --save-dev @babel/preset-env
# react 轉碼規則
$ npm install --save-dev @babel/preset-reac
複製程式碼

注意,以下所有 Babel 工具和模組的使用,都必須先寫好.babelrc。

  • 2.2.3 命令列工具@babel/cli、@babel/node、@babel/register 它的安裝命令如下:
$ npm install --save-dev @babel/cli
$ npm install --save-dev @babel/node #提供一個支援 ES6 的 REPL 環境
$ npm install --save-dev @babel/register 
#改寫require命令,為它加上一個鉤子。
#每當使用require載入.js、.jsx、.es和.es6
#字尾名的檔案,就會先用 Babel 進行轉碼
複製程式碼

@babel/cli 基本用法(babel指令):

# 轉碼結果輸出到標準輸出
$ npx babel example.js

# 轉碼結果寫入一個檔案
# --out-file 或 -o 引數指定輸出檔案
$ npx babel example.js --out-file compiled.js
# 或者
$ npx babel example.js -o compiled.js

# 整個目錄轉碼
# --out-dir 或 -d 引數指定輸出目錄
$ npx babel src --out-dir lib
# 或者
$ npx babel src -d lib

# -s 引數生成source map檔案
$ npx babel src -d lib -s
複製程式碼

@babel/node 基本用法(babel-node指令

$ npx babel-node
> (x => x * 2)(1)
2
複製程式碼

@babel/register 基本用法

//使用時必須首先載入@babel/register
require('@babel/register');
require('./es6.js');
複製程式碼
  • 2.2.4 polyfill (用來轉換babel無法轉換的API以及一些語法) 使用core-js和regenerator-runtime(提供generator函式的轉碼)
  • 安裝:$ npm install --save-dev core-js regenerator-runtime
  • 使用
import 'core-js';
import 'regenerator-runtime/runtime';
// 或者
require('core-js');
require('regenerator-runtime/runtime);
複製程式碼

Babel 預設不轉碼的 API 非常多,詳細清單可以檢視babel-plugin-transform-runtime模組的github.com/babel/babel…檔案

  • 瀏覽器環境
  • Babel 也可以用於瀏覽器環境,使用@babel/standalone模組提供的瀏覽器版本,將其插入網頁。
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
// Your ES6 code
</script>
複製程式碼

注意,網頁實時將 ES6 程式碼轉為 ES5,對效能會有影響。生產環境需要載入已經轉碼完成的指令碼。

Babel 提供一個REPL 線上編譯器,可以線上將 ES6 程式碼轉為 ES5 程式碼。轉換後的程式碼,可以直接作為 ES5 程式碼插入網頁執行。

第二章、let 和 const 命令

1. let 的特性以及用法

  1. 首先,我先總結一下var缺點
  • 1.1 允許重複的變數宣告:導致資料被覆蓋
  • 1.2 變數提升:怪異的資料訪問、閉包問題
  • 1.3 全域性變數掛載到全域性物件:全域性物件成員汙染問題
// 1. 允許重複的變數宣告:導致資料被覆蓋
var a = 1;
function print(){
    console.log(a)
 }
// 假設這裡有一千行程式碼
var a = 2;
print(); // 2

// 2. 變數提升:怪異的資料訪問

if (Math.random() < 0.5) {
    var a = "abc";
    console.log(a);
 }
else {
   console.log(a);
}
console.log(a); // 無論怎樣訪問都是abc

// 3. 全域性變數掛載到全域性物件:全域性物件成員汙染問題

var abc = "123";
console.log(window.abc); // 123

// var console = "abc";
// console.log(console) // abc 這個很可怕
複製程式碼
  1. let (完美的解決了這些問題)
    1. let宣告的變數不會掛載到全域性物件
    2. let宣告的變數,不允許當前作用域範圍內重複宣告
    3. 使用let不會有變數提升,因此,不能在定義let變數之前使用它(形成暫時性死區)
  • 暫時性死區:在程式碼塊內,使用let命令宣告變數之前,該變數都是不可用的。這在語法上,稱為“暫時性死區”(temporal dead zone,簡稱 TDZ)。
  • 塊級作用域:程式碼執行時遇到花括號,會建立一個塊級作用域,花括號結束,銷燬塊級作用域
// 1. let宣告的變數不會掛載到全域性物件

let a = 123;
console.log(window.a) // undefined

// 2. let宣告的變數,不允許當前作用域範圍內重複宣告

if (Math.random() < 0.5) {
    let a = 123; //定義在當前塊級作用域
    console.log(a) //當前塊級作用域中的a
} else {
    //這是另外一個塊級作用域,該作用域中找不到a
    console.log(a)
}

//3. 使用let不會有變數提升,因此,不能在定義let變數之前使用它

console(c); // Uncaught ReferenceError: c is not defined 形成了暫時性死區
let c = 6;
複製程式碼

暫時性死區的本質就是,只要一進入當前作用域,所要使用的變數就已經存在了,但是不可獲取,只有等到宣告變數的那一行程式碼出現,才可以獲取和使用該變數。

2. const 特性以及方法

  • const和let完全相同,僅在於用const宣告的變數,必須在宣告時賦值,而且不可以重新賦值。(這裡就不再舉例其使用了)
  • 實際上,在開發中,應該儘量使用const來宣告變數,以保證變數的值不會隨意篡改,原因:
  1. 根據經驗,開發中的很多變數,都是不會更改,也不應該更改的。
  2. 後續的很多框架或者是第三方JS庫,都要求資料不可變,使用常量可以一定程度上保證這一點。

注意

  1. 常量不可變,是指宣告的常量的記憶體空間不可變,並不保證記憶體空間中的地址指向的其他空間不可變
  2. 常量的命名
    1. 特殊的常量:該常量從字面意義上,一定是不可變的,比如圓周率、月地距地或其他一些絕不可能變化的配置。通常,該常量的名稱全部使用大寫,多個單詞之間用下劃線分割
    2. 普通的常量:使用和之前一樣的命名即可
  3. 在for迴圈中,迴圈變數不可以使用常量,只能用let 關於主義的第一點,我舉個例子
const a = {
    name: "kevin",
    age: 123
};
a.name = "abc";
console.log(a) // abc

// const PI = 3.14;
// const MOON_EARTH_DISTANCE = 3245563424; //月地距離
複製程式碼

3. 頂層物件屬性

  1. 頂層物件,在瀏覽器環境指的是window物件,在 Node 指的是global物件
var a = 1;
// 如果在 Node 的 REPL 環境,可以寫成 global.a
// 或者採用通用方法,寫成 this.a
window.a // 1

let b = 1;
window.b // undefined let命令、const命令、class命令宣告的全域性變數,不屬於頂層物件的屬性
複製程式碼
  1. globalThis 物件 因為環境的不同,頂層物件在各種實現裡面是不統一的
    1. 瀏覽器裡面,頂層物件是window,但 Node 和 Web Worker 沒有window。
    2. 瀏覽器和 Web Worker 裡面,self也指向頂層物件,但是 Node 沒有self。
    3. Node 裡面,頂層物件是global,但其他環境都不支援。 在不同環境中獲取頂層物件的this ES6 提供了兩個方法
// 方法一
(typeof window !== 'undefined'
   ? window
   : (typeof process === 'object' &&
      typeof require === 'function' &&
      typeof global === 'object')
     ? global
     : this);

// 方法二
var getGlobal = function () {
  if (typeof self !== 'undefined') { return self; }
  if (typeof window !== 'undefined') { return window; }
  if (typeof global !== 'undefined') { return global; }
  throw new Error('unable to locate global object');
};
複製程式碼

而且,ES6最後提到:

  • ES2020 在語言標準的層面,引入globalThis作為頂層物件。也就是說,任何環境下,globalThis都是存在的,都可以從它拿到頂層物件,指向全域性環境下的this。
  • 墊片庫global-this模擬了這個提案,可以在所有環境拿到globalThis。

第三章、變數的解構賦值

  • 在此,我這裡主要介紹工作中我們常用的幾種解構賦值:
  1. 物件的結構
  2. 陣列的結構
  3. 引數的結構
  4. 其他的結構

1. 物件的結構

1. 什麼是解構

  • ES6 允許按照一定模式,從陣列和物件中提取值,對變數進行賦值,這被稱為解構(Destructuring) 解構不會對被解構的目標造成任何影響
  • 在解構中使用預設值
{同名變數 = 預設值}
const user = {
    name: "kevin",
    age: 11,
    sex: "男",
    address: {
        province: "四川",
        city: "成都"
    }
}
//解構出user中的name、province
//定義兩個變數name、province
//再解構
const { name, address: { province } } = user;
console.log(name, province) 
// name: kevin  province: 四川
複製程式碼
  • 非同名屬性解構
{屬性名:變數名}
let obj = {};
let arr = [];
({ foo: obj.prop, bar: arr[0] } = { foo: 123, bar: true });
obj // {prop:123}
arr // [true]
//如果解構模式是巢狀的物件,而且子物件所在的父屬性不存在,那麼將會報錯

let {foo: {bar}} = {baz: 'baz'};
// 報錯
複製程式碼

2. 陣列的結構

let [a, b, c] = [1, 2, 3];
// a:1 b:2 c:3
複製程式碼

這種寫法屬於模式匹配,只要等號兩邊的模式相同,左邊的變數就會被賦予對應的值

  • 下面一些使用巢狀陣列進行解構的例子以及一些情況
let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1    bar // 2    baz // 3

let [ , , third] = ["foo", "bar", "baz"];
third // "baz"

// 1. 不完全解構,即等號左邊的模式,只匹配一部分的等號右邊的陣列。這種情況下,解構依然可以成功。
let [x, y] = [1, 2, 3];
x // 1  y // 2

let [a, [b], d] = [1, [2, 3], 4];
a // 1  b // 2  d // 4
// 2. 陣列解構出 值和陣列(運用擴充套件運算子... 在接下來的章節裡會就介紹到)

let [head, ...tail] = [1, 2, 3, 4];
head // 1   tail // [2, 3, 4]

// 3. 如果解構不成功,變數的值就等於undefined。

let [x, y, ...z] = ['a'];
x // "a"    y // undefined  z // []

// 4. 如果等號的右邊不是陣列(或者嚴格地說,不是可遍歷的結構,參見《Iterator》一章),那麼將會報錯。

// 4.1 要麼轉為物件以後不具備 Iterator 介面

let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;

// 4.2 本身就不具備 Iterator 介面

let [foo] = {};
複製程式碼

從最後的例子可以看出,只要某種資料結構具有 Iterator 介面,都可以採用陣列形式的解構賦值。

function* fibs() {
  let a = 0;
  let b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

let [first, second, third, fourth, fifth, sixth] = fibs();
sixth // 5
複製程式碼

上面程式碼中,fibs是一個 Generator 函式(參見《Generator 函式》一章),原生具有 Iterator 介面。解構賦值會依次從這個介面獲取值。

3. 引數的結構

廢話不多說,直接上例子

function add([x, y]){
  return x + y;
}
add([1, 2]); // 3
// 函式add的參數列面上是一個陣列,但在傳入引數的那一刻,陣列引數就被解構成變數x和y。
// 對於函式內部的程式碼來說,它們能感受到的引數就是x和y。
複製程式碼

箭頭函式也可以解構賦值

[[1, 2], [3, 4]].map(([a, b]) => a + b);
// [ 3, 7 ]
複製程式碼

函式引數的解構也可以使用預設值 在第五章會給出介紹

function move({x = 0, y = 0} = {}) { //x y 在引數上設定預設值
  return [x, y];
}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
// 函式move的引數是一個物件,通過對這個物件進行解構,得到變數x和y的值。
// 如果解構失敗,x和y等於預設值。
複製程式碼

undefined就會觸發函式引數的預設值。

[1, undefined, 3].map((x = 'yes') => x);
// [ 1, 'yes', 3 ]
複製程式碼

4. 其他的解構

  1. 物件的解構也可以指定預設值 設定預設值
var {x = 3} = {};
x // 3

var {x, y = 5} = {x: 1};
x // 1
y // 5

var {x: y = 3} = {};
y // 3

var {x: y = 3} = {x: 5};
y // 5

var { message: msg = 'Something went wrong' } = {};
msg // "Something went wrong"
複製程式碼
  • 預設值生效的條件是,物件的屬性值嚴格等於undefined。
var {x = 3} = {x: undefined};
x // 3

var {x = 3} = {x: null};
x // null  因為null與undefined不嚴格相等,所以是個有效的賦值,導致預設值3不會生效
複製程式碼

注意點

  1. 如果要將一個已經宣告的變數用於解構賦值,必須非常小心。
// 錯誤的寫法
let x;
{x} = {x: 1};
// SyntaxError: syntax error因為JavaScript引擎會將{x}理解成一個程式碼塊,從而發生語法錯誤。
// 只有不將大括號寫在行首,避免 JavaScript 將其解釋為程式碼塊,才能解決這個問題。
// 正確的寫法
let x;
({x} = {x: 1});
複製程式碼
  1. 解構賦值允許等號左邊的模式之中,不放置任何變數名。因此,可以寫出非常古怪的賦值表示式
({} = [true, false]);
({} = 'abc');
({} = []);
複製程式碼
  1. 由於陣列本質是特殊的物件,因此可以對陣列進行物件屬性的解構。
let arr = [1, 2, 3];
let {0 : first, [arr.length - 1] : last} = arr;
first // 1
last // 3 
複製程式碼

第四章、字串的擴充套件

1. 更好的Unicode支援

首先,介紹一下碼元:

  1. 早期,由於儲存空間寶貴,Unicode使用16位二進位制來儲存文字。我們將一個16位的二進位制編碼叫做一個碼元(Code Unit)。後來,由於技術的發展,Unicode對文字編碼進行了擴充套件,將某些文字擴充套件到了32位(佔用兩個碼元),並且,將某個文字對應的二進位制數字叫做碼點(Code Point)

ES6為了解決這個困擾,為字串提供了方法:codePointAt,根據字串碼元的位置得到其碼點。

同時,ES6為正規表示式新增了一個flag: u,如果新增了該配置,則匹配時,使用碼點匹配

const text = "?"; //佔用了兩個碼元(32位)

console.log("字串長度:", text.length); // 2
console.log("使用正則測試:", /^.$/u.test(text)); // true
console.log("得到第一個碼元:", text.charCodeAt(0)); // 55362
console.log("得到第二個碼元:", text.charCodeAt(1)); // 57271

//?:\ud842\udfb7
console.log("得到第一個碼點:", text.codePointAt(0)); // 134071
console.log("得到第二個碼點:", text.codePointAt(1)); // 57271
複製程式碼

然後我們完成一個自己的判斷字串char,是32位,還是16位方法

function is32bit(char, i) {
    //如果碼點大於了16位二進位制的最大值,則其是32位的
    return char.codePointAt(i) > 0xffff;
}
複製程式碼

得到一個字串碼點的真實長度

function getLengthOfCodePoint(str) {
    var len = 0;
    for (let i = 0; i < str.length; i++) {
        //i在索引碼元
        if (is32bit(str, i)) {
            //當前字串,在i這個位置,佔用了兩個碼元
            i++;
        }
        len++;
    }
    return len;
}

console.log("?是否是32位的:", is32bit("?", 0)) // ?是否是32位的: true
console.log("ab?ab的碼點長度:", getLengthOfCodePoint("ab?ab")) // ab?ab的碼點長度: 5
複製程式碼

2. 增加的字串API(只舉出工作中常用的四種)

  • includes 判斷字串中是否包含指定的子字串
  • startsWith 判斷字串中是否以指定的字串開始
  • endsWith 判斷字串中是否以指定的字串結尾
  • repeat 將字串重複指定的次數,然後返回一個新字串
const text = "我真是狠人";

console.log("是否包含“狠”:", text.includes("狠"));
console.log("是否以“我”開頭:", text.startsWith("我"));
console.log("是否以“狠人”結尾:",

text.endsWith("狠人"));
console.log("重複4次:", text.repeat(4));
// 由於過於簡單,就不把console結果展示啦,有興趣的可以直接試一下,哈哈哈
複製程式碼

3. 模板字串

模板字串是此章節的靈魂

  • ES6之前處理字串繁瑣的兩個方面:
  1. 多行字串
  2. 字串拼接
  • 在ES6中,提供了模板字串的書寫,可以非常方便的換行和拼接,要做的,僅僅是將字串的開始或結尾改為 ` 符號

如果要在字串中拼接js表示式,只需要在模板字串中使用${JS表示式}

var love1 = "秋葵";
var love2 = "香菜";

var text = `A喜歡${love1}
B也喜歡${love2}
表示式可以是任何有意義的資料${1 + 3 * 2 / 0.5}
表示式是可以巢狀的:${`表示式中的模板字串${love1 + love2}`}
\n\n
奧布瓦的發順豐
在模板字串中使用\${JS表示式}可以進行插值
`;

console.log(text);
複製程式碼

在模板字串書寫之前,可以加上標記:

標記名`模板字串`
複製程式碼

標記是一個函式,函式引數如下:

  1. 引數1:被插值分割的字串陣列
  2. 後續引數:所有的插值
var love1 = "秋葵";
var love2 = "香菜";

var text = myTag`鄧哥喜歡${love1},鄧哥也喜歡${love2}。`;

//相當於: 
// text = myTag(["A喜歡", ",B也喜歡", "。"], "秋葵", "香菜")

function myTag(parts) {
    const values = Array.prototype.slice.apply(arguments).slice(1);
    let str = "";
    for (let i = 0; i < values.length; i++) {
        str += `${parts[i]}${values[i]}`;
        if (i === values.length - 1) {
            str += parts[i + 1];
        }
    }
    return str;
}

console.log(text); //A喜歡:秋葵,B也喜歡:香菜
複製程式碼

第五章、函式的擴充套件

1. 在書寫形參時,直接給形參賦值,附的值即為預設值(在解構的時候有過應用)

這樣一來,當呼叫函式時,如果沒有給對應的引數賦值(給它的值是undefined),則會自動使用預設值

  1. 留意暫時性死區 形參
  • 和ES6中的let或const宣告一樣,具有作用域,並且根據引數的宣告順序,存在暫時性死區。
  1. 對arguments的影響
  • 只要給函式加上引數預設值,該函式會自動變數嚴格模式下的規則:arguments和形參脫離

  • 暫時性死區

function test(a = b, b) {
    console.log(a, b);
}
test(undefined, 2);// 報錯 因為傳a值undefined, 然後會給a賦預設值 b ,但是此時 b還沒有賦值,所以報錯。在上邊解構賦值中也介紹過

function sum(a, b = 1, c = 2) {
    return a + b + c;
}

console.log(sum(10, undefined, undefined))
console.log(sum(11))
console.log(sum(1, undefined, 5))  // 這種情況才是正常
複製程式碼
  • 對arguments的影響
function test(a, b = 1) {
    console.log("arugments", arguments[0], arguments[1]); //arugments 1 2
    console.log("a:", a, "b:", b); //a: 1 b: 2
    a = 3;
    console.log("arugments", arguments[0], arguments[1]); //arugments 1 2
    console.log("a:", a, "b:", b); //a: 3 b: 2
}

test(1, 2);
複製程式碼

ES6的剩餘引數專門用於手機末尾的所有引數,將其放置到一個形引數組中。語法:

function (...形參名){

}
複製程式碼

注意:

  1. 一個函式,僅能出現一個剩餘引數
  2. 一個函式,如果有剩餘引數,剩餘引數必須是最後一個引數
function test(a, b, ...args) {
    console.log(a, b, args);
}

test(1, 32, 46, 7, 34); //1 32 [46, 7, 34]
複製程式碼

2. 展開運算子:...要展開的東西

const arr1 = [3, 67, 8, 5];

//克隆arr1陣列到arr2

const arr2 = [0, ...arr1, 1];

console.log(arr2, arr1 === arr2)
// -------------------------------------
const obj1 = {
    name: "A",
    age: 18,
    love: "B",
    address: {
        country: "中國",
        province: "BJ",
        city: "BJ"
    }
}

// 淺克隆到obj2

const obj2 = {
    ...obj1,
    name: "C"
};

console.log(obj2)

console.log(obj1.address === obj2.address)
複製程式碼

3. 明確函式的雙重用途

ES6提供了一個特殊的API,可以使用該API在函式內部,判斷該函式是否使用了new來呼叫

new.target 
//該表示式,得到的是:如果沒有使用new來呼叫函式,則返回undefined
//如果使用new呼叫函式,則得到的是new關鍵字後面的函式本身

function Person(firstName, lastName) {
    //判斷是否是使用new的方式來呼叫的函式

    // //過去的判斷方式
    // if (!(this instanceof Person)) {
    //     throw new Error("該函式沒有使用new來呼叫")
    // }

    if (new.target === undefined) {
        throw new Error("該函式沒有使用new來呼叫")
    }
    this.firstName = firstName;
    this.lastName = lastName;
    this.fullName = `${firstName} ${lastName}`;
}

const p1 = new Person("張", "三");
console.log(p1)



const p2 = Person("張", "三");
console.log(p2); // 報錯,該函式沒有使用new來呼叫

const p3 = Person.call(p1, "張", "三")
console.log(p3);// 報錯,該函式沒有使用new來呼叫
複製程式碼

4. 箭頭函式

  1. ES5中this的問題(本文主要是來介紹ES6的,暫且將之前的總結直接放在這裡,在之後的博文中會仔細描述一下ES5系列)

    1. 通過物件呼叫函式,this指向物件
    2. 直接呼叫函式,this指向全域性物件
    3. 如果通過new呼叫函式,this指向新建立的物件
    4. 如果通過apply、call、bind呼叫函式,this指向指定的資料
    5. 如果是DOM事件函式,this指向事件源
  2. 箭頭函式是一個函式表示式,理論上,任何使用函式表示式的場景都可以使用箭頭函式

完整語法:

(引數1, 引數2, ...)=>{
    //函式體
}
複製程式碼

如果引數只有一個,可以省略小括號

引數 => {

}
複製程式碼

如果箭頭函式只有一條返回語句,可以省略大括號,和return關鍵字

引數 => 返回值
複製程式碼
const print = num => {
    console.log("給我的數字是:", num)
}
print(2);

const arr = [1,2,4,56,22,43,23,2,23,12,1];
arr.sort((a, b)=> a - b);
console.log(arr);
複製程式碼

注意

  • 箭頭函式中,不存在this、arguments、new.target,如果使用了,則使用的是函式外層的對應的this、arguments、new.target 也就是說在箭頭函式中,this是固定的。因為箭頭函式本身就是引用的外層函式的this
  • 箭頭函式沒有原型
  • 箭頭函式不能作用建構函式使用
  • 不可以使用yield命令,因此箭頭函式不能用作 Generator 函式
// ES6
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

// ES5
function foo() {
  var _this = this;

  setTimeout(function () {
    console.log('id:', _this.id);
  }, 100);
}
複製程式碼

除了this,arguments、super、new.target這三個變數在箭頭函式之中也是不存在的,指向外層函式的對應變數:arguments、super、new.target。

function foo() {
  setTimeout(() => {
    console.log('args:', arguments);
  }, 100);
}

foo(2, 4, 6, 8)
// args: [2, 4, 6, 8]
複製程式碼

另外,由於箭頭函式沒有自己的this,所以當然也就不能用call()、apply()、bind()這些方法去改變this的指向。

(function() {
  return [
    (() => this.x).bind({ x: 'inner' })()
  ];
}).call({ x: 'outer' });
// ['outer']
//程式碼中,箭頭函式沒有自己的this,所以bind方法無效,內部的this指向外部的this。
複製程式碼

應用比較多的場景

  1. 臨時性使用的函式,並不會可以呼叫它,比如:
    1. 事件處理函式
    2. 非同步處理函式
    3. 其他臨時性的函式
  2. 為了繫結外層this的函式
  3. 在不影響其他程式碼的情況下,保持程式碼的簡潔,最常見的,陣列方法中的回撥函式

本文主要引用阮一峰老師的《ECMAScript6簡介》

  • es6.ruanyifeng.com/#README,大家可以多讀幾遍這本書,我所總結的只是目前工作中常用的,而書中有很多其他的我沒有提到的知識。

感謝大家能讀到最後,我再說兩點。

  1. 這是博主的第一篇文章,文章內容肯定會有很多的問題,希望大家可以多多提意見。小弟在此先送上感謝。
  2. ES6系列總結這邊暫時我打算分成4篇,這是第一篇,已經開始了,就要把這個東東搞完。所以希望有興趣的小夥伴們能加個關注,以便後續的文章第一時間通知到大家。

相關文章