什麼是ECMA6?ES6常用語法

ECMA6

1. 什麼是ECMA6

ECMAScript 6.0(簡稱 ES6)是 JavaScript 語言的下一代標準,在2015年6月正式發佈。它的目標,是使得 JavaScript 語言可以用來編寫復雜的大型應用程序,成為企業級開發語言。

2. ECMAScript 和 JavaScript 的關系

ECMAScript是JavaScript的規格,JavaScript是ECMAScript的一種實現。日常場合,這兩個詞是可以互換的。

另外,ActionScript也是 ECMAScript的實現

1996年11月,JavaScript 的創造者 網景公司,將 JavaScript 提交給國際標準化組織ECMA,希望這種語言能夠成為國際標準。次年,ECMA 發佈262號標準文件(ECMA-262)的第一版,規定瞭瀏覽器腳本語言的標準,並將這種語言稱為 ECMAScript,這個版本就是1.0版。

該標準從一開始就是針對 JavaScript 語言制定的,但是之所以不叫 JavaScript,有兩個原因。一是商標,Java 是 Sun 公司的商標,根據授權協議,隻有網景公司可以合法地使用 JavaScript 這個名字,且 JavaScript 本身也已經被網景公司註冊為商標。二是想體現這門語言的制定者是 ECMA,不是網景,這樣有利於保證這門語言的開放性和中立性。

3. ECMA2015

ES6 的第一個版本,在2015年6月發佈,正式名稱是《ECMAScript 2015標準》(簡稱 ES2015)。

2016年6月,小幅修訂的《ECMAScript 2016標準》(簡稱 ES2016)發佈,這個版本可以看作是 ES6.1 版。

2017年6月會發佈 ES2017 標準。

因此,ES6 既是一個歷史名詞,也是一個泛指,含義是5.1版以後的 JavaScript 的下一代標準,涵蓋瞭ES2015、ES2016、ES2017等等,而ES2015 則是正式名稱,特指該年發佈的正式版本的語言標準。

4. ES6的兼容性問題

目前各大瀏覽器對ES6的支持度已經越來越高瞭,超過90%的 ES6 語法特性都實現瞭。

nodejs 微信開發 完全支持ES6的語法

5. Babel 轉碼器

該轉碼器可以吧ES6語法轉碼為ES5語法,意味著,可以借助於Babel轉碼器提高ES6的兼容性。

ES6常用語法

1. 變量擴展

1.1. let 關鍵字

用來聲明變量。它的用法類似於var,但是所聲明的變量,隻在let命令所在的代碼塊內有效

{
  let a = 10;
  var b = 1;
}

a // ReferenceError: a is not defined.
b // 1

for循環的計數器,就很合適使用let命令

for (let i = 0; i < 10; i++) {}

console.log(i);
//ReferenceError: i is not defined

不存在變量提升

// var 的情況
console.log(foo); // 輸出undefined
var foo = 2;

// let 的情況
console.log(bar); // 報錯ReferenceError
let bar = 2;

不允許重復聲明

let a = 100;
let a = 200; //報錯

let b = 100;
b = 200; //正確

1.2 const 關鍵字

const聲明一個隻讀的常量。一旦聲明,常量的值就不能改變。

const PI = 3.1415;
PI // 3.1415

PI = 3; //報錯

同,let一樣,也不存在變量提升

1.3 頂層對象和全局變量

ES6中將頂層對象(瀏覽器中是window對象,node中是Global對象)與全局變量不再掛鉤。
但是,為瞭保持兼容性,var命令和function命令聲明的全局變量,依舊是頂層對象的屬性;另一方面規定,let命令、const命令、class命令聲明的全局變量,不屬於頂層對象的屬性。

2. 對象的簡化寫法

2.1 屬性的簡化寫法

var foo = 'name1';
var bar = 'name2';

var obj = {foo,bar};
//等同於
var obj = {foo:foo, bar:bar}

2.2 方法的簡化寫法

{
    fn:function(){
    }
}
//可簡化為
{
    fn(){
    }
}

3. 變量解構賦值

ES6 允許按照一定模式,從數組和對象中提取值,對變量進行賦值,這被稱為解構(Destructuring)。

3.1 數組的解構賦值

以前,變量賦值,隻能這麼寫

var a = 1;
var b = 2;
var c = 3;

現在,也可以這麼寫

let [a, b, c] = [1, 2, 3];

上面代碼表示,可以從數組中提取值,按照對應位置,對變量賦值。

本質上,這種寫法屬於“模式匹配”,隻要等號兩邊的模式相同,左邊的變量就會被賦予對應的值。下面是一些使用嵌套數組進行解構的例子。

let [foo, [[bar], baz]] = [1, [[2], 3]];
foo // 1
bar // 2
baz // 3

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

let [x, , y] = [1, 2, 3];
x // 1
y // 3

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

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

如果解構不成功,變量的值就等於undefined
如果等號左邊的形式是數組,而右邊不是數組,將會報錯

解構變量可以有默認值

let [foo = true] = [];
foo // true

let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

3.2 對象的解構

解構不僅可以用於數組,還可以用於對象

let { foo, bar } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"

對象的解構與數組有一個重要的不同。數組的元素是按次序排列的,變量的取值由它的位置決定;而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。

如果變量名與屬性名不一致,必須寫成下面這樣。
var { foo: baz } = { foo: ‘aaa’, bar: ‘bbb’ };
baz // “aaa”

let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
f // 'hello'
l // 'world'

這實際上說明,對象的解構賦值是下面形式的簡寫

let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" };

和數組一樣,解構也可以用於嵌套解構的對象

let obj = {
  p: [
    'Hello',
    { y: 'World' }
  ]
};

let { p: [x, { y }] } = obj;
x // "Hello"
y // "World"

註意,這時p是模式,不是變量,因此不會被賦值。

對象的解構也可以指定默認值

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

var { message: msg = 'Something went wrong' } = {};
msg // "Something went wrong"

3.3 特殊對象的解構

JavaScript中一切皆對象,向字符串、佈爾值、數值都可以被解構,而且字符串可以當做字符組成的數組

const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"

let {length : len} = 'hello';   //因為字符串具有屬性length
len // 5

let {toString: s} = 123;
s === Number.prototype.toString // true

3.4 實際用途

交換變量的值

let x = 1;
let y = 2;

[x, y] = [y, x];

提取JSON數據

let jsonData = {
  id: 42,
  status: "OK",
  data: [867, 5309]
};

let { id, status, data: number } = jsonData;

console.log(id, status, number);
// 42, "OK", [867, 5309]

函數參數的默認值

jQuery.ajax = function (url, {
  async = true,
  beforeSend = function () {},
  cache = true,
  complete = function () {},
  crossDomain = false,
  global = true,
  // ... more config
}) {
  // ... do stuff
};

輸入模塊的指定方法

const { SourceMapConsumer, SourceNode } = require("source-map");

4. 擴展運算符(…)

擴展運算符(…)可以將某些數據結構轉為數組。
可以將兩類對象轉為真正的數組:類似數組的對象(array-like object)和可遍歷(iterable)的對象(包括ES6新增的數據結構Set和Map)。

// arguments對象
function foo() {
  var args = [...arguments];
}

// NodeList對象
[...document.querySelectorAll('p')]

5. for…of 循環

ES6 借鑒 C++、Java、C# 和 Python 語言,引入瞭for…of循環,作為遍歷所有數據結構的統一的方法。
for…of循環可以使用的范圍包括數組、Set 和 Map 結構、某些類似數組的對象(比如arguments對象、DOM NodeList 對象)以及字符串

const arr = ['red', 'green', 'blue'];

for(let v of arr) {
  console.log(v); // red green blue
}

6. Class關鍵字

6.1 定義類

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ')';
};

var p = new Point(1, 2);

JS傳統方法中定義對象是通過構造函數,這種寫法跟傳統的面向對象語言(比如C++和Java)差異很大,很容易讓新學習這門語言的程序員感到困惑。

ES6提供瞭更接近傳統語言的寫法,引入瞭Class(類)這個概念。

class Bar {
  doStuff() {
    console.log('stuff');
  }
}

var b = new Bar();
b.doStuff() // "stuff"

如果你瞭解過Java或者PHP或者C++或者.net 你將會對這種寫法非常熟悉

6.2 constructor 構造方法

class Bar{
    constructro() {

    }
}

在類被實例化的時候, 構造函數會被自動調用

6.3 extends 繼承

Class之間可以通過extends關鍵字實現繼承,這比ES5的通過修改原型鏈實現繼承,要清晰和方便很多。

class ColorPoint extends Point {}

上面代碼定義瞭一個ColorPoint類,該類通過extends關鍵字,繼承瞭Point類的所有屬性和方法。

6.4 super

class ColorPoint extends Point {
  constructor(x, y, color) {
    super(x, y); // 調用父類的constructor(x, y)
    this.color = color;
  }

  toString() {
    return this.color + ' ' + super.toString(); // 調用父類的toString()
  }
}

上面代碼中,constructor方法和toString方法之中,都出現瞭super關鍵字,它在這裡表示父類的構造函數,用來新建父類的this對象。

子類必須在constructor方法中調用super方法,否則新建實例時會報錯。這是因為子類沒有自己的this對象,而是繼承父類的this對象,然後對其進行加工。如果不調用super方法,子類就得不到this對象。

7. 箭頭函數

ES6允許使用“箭頭”(=>)定義函數。

var f = v => v;
//等同於
var f = function(v) {
  return v;
};

var f = () => 5;
// 等同於
var f = function () { return 5 };

var sum = (num1, num2) => num1 + num2;
// 等同於
var sum = function(num1, num2) {
  return num1 + num2;
};

如果箭頭函數的代碼塊部分多於一條語句,就要使用大括號將它們括起來,並且使用return語句返回。

var sum = (num1, num2) => { 
    let sum =  num1 + num2; 
    return sum; 
}

箭頭函數的一個用處是簡化回調函數
// 正常函數寫法
var result = values.sort(function (a, b) {
return a – b;
});

// 箭頭函數寫法
var result = values.sort((a, b) => a - b);

函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象。

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

var id = 21;

foo.call({ id: 42 });
// id: 42

上面代碼中,setTimeout的參數是一個箭頭函數,這個箭頭函數的定義生效是在foo函數生成時,而它的真正執行要等到100毫秒後。如果是普通函數,執行時this應該指向全局對象window,這時應該輸出21。但是,箭頭函數導致this總是指向函數定義生效時所在的對象(本例是{id: 42}),所以輸出的是42。

箭頭函數 不可以當作構造函數,也就是說,不可以使用new命令,否則會拋出一個錯誤。

箭頭函數 不可以使用arguments對象,該對象在函數體內不存在。如果要用,可以用Rest參數代替。

8. ES6函數參數問題

8.1 函數默認值

ES6 允許為函數的參數設置默認值,即直接寫在參數定義的後面。不必那ES5那樣,采用些特殊手段

//ES6寫法
function log(x, y = 'World') {
  console.log(x, y);
}

//ES5寫法
function log(x, y) {
    if (y === undefined) {
        y = 'World';
    }
    console.log(x,y);
}

8.2 rest參數

ES6 引入 rest 參數(形式為“…變量名”),用於獲取函數的多餘參數,這樣就不需要使用arguments對象瞭

function add(...values) {
  let sum = 0;

  for (var val of values) {
    sum += val;
  }

  return sum;
}

add(2, 5, 3) // 10

9. 模板字符串

傳統的JavaScript語言,輸出模板通常是這樣寫的。

$('#result').append(
  'There are ' + basket.count + ' ' +
  'items in your basket, ' +
  '' + basket.onSale +
  ' are on sale!'
);

上面這種寫法相當繁瑣不方便,ES6引入瞭模板字符串解決這個問題

$('#result').append(`
  There are ${basket.count} items
   in your basket, ${basket.onSale}
  are on sale!
`);

模板字符串(template string)是增強版的字符串,用反引號 ` 標識。它可以當作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量。

模板字符串中嵌入變量,需要將變量名寫在${}之中。
大括號內部可以放入任意的JavaScript表達式,可以進行運算,以及引用對象屬性。

`User ${user.name} is not authorized to do ${action}.`
`${x} + ${y} = ${x + y}`
`foo ${fn()} bar`

如果在模板字符串中需要使用反引號,則前面要用反斜杠轉義。

10. Symbol

ES6引入瞭一種新的原始數據類型Symbol,表示獨一無二的值。它是JavaScript語言的第七種數據類型,前六種是:Undefined、Null、佈爾值(Boolean)、字符串(String)、數值(Number)、對象(Object)
Symbol值通過Symbol函數生成。
Symbol函數可以接受一個字符串作為參數,表示對Symbol實例的描述,主要是為瞭在控制臺顯示,或者轉為字符串時,比較容易區分。

let s = Symbol();

typeof s
// "symbol"

var s1 = Symbol('foo');
var s2 = Symbol('bar');

s1 // Symbol(foo)
s2 // Symbol(bar)

s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"

註意,Symbol函數前不能使用new命令,否則會報錯。這是因為生成的Symbol是一個原始類型的值,不是對象。

任意兩個Symbol類型的數據都不想等

// 沒有參數的情況
var s1 = Symbol();
var s2 = Symbol();

s1 === s2 // false

// 有參數的情況
var s1 = Symbol('foo');
var s2 = Symbol('foo');

s1 === s2 // false

Symbol值不能與其他類型的值進行運算,會報錯。

由於每一個Symbol值都是不相等的,這意味著Symbol值可以作為標識符,用於對象的屬性名,就能保證不會出現同名的屬性。這對於一個對象由多個模塊構成的情況非常有用,能防止某一個鍵被不小心改寫或覆蓋。
var mySymbol = Symbol();

// 第一種寫法
var a = {};
a[mySymbol] = 'Hello!';

// 第二種寫法
var a = {
};

// 第三種寫法
var a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 以上寫法都得到同樣結果
a[mySymbol] // "Hello!"

註意,Symbol值作為對象屬性名時,不能用點運算符。

11. Set 數據結構

ES6 提供瞭新的數據結構 Set。它類似於數組,但是成員的值都是唯一的,沒有重復的值。
Set 本身是一個構造函數,用來生成 Set 數據結構。

const s = new Set();

[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));

for (let i of s) {
  console.log(i);
}
// 2 3 5 4

上面代碼通過add方法向 Set 結構加入成員,結果表明 Set 結構不會添加重復的值

Set 函數可以接受一個數組(或類似數組的對象)作為參數,用來初始化。

var set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]

// 例二
var items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5

12. Map 數據結構

ES6提供瞭Map數據結構。它類似於對象,也是鍵值對的集合,但是“鍵”的范圍不限於字符串,各種類型的值(包括對象)都可以當作鍵。也就是說,Object結構提供瞭“字符串—值”的對應,Map結構提供瞭“值—值”的對應,是一種更完善的Hash結構實現。如果你需要“鍵值對”的數據結構,Map比Object更合適。

var m = new Map();
var o = {p: 'Hello World'};

m.set(o, 'content')
m.get(o) // "content"

m.has(o) // true
m.delete(o) // true
m.has(o) // false

作為構造函數,Map也可以接受一個數組作為參數。該數組的成員是一個個表示鍵值對的數組。

var map = new Map([
  ['name', '張三'],
  ['title', 'Author']
]);

map.size // 2
map.has('name') // true
map.get('name') // "張三"
map.has('title') // true
map.get('title') // "Author"

以上介紹的是ES6中最為常用的也較為容易掌握一些新語法。 查看更為詳細的ES6語法,請訪問阮一峰老師 ECMAScript6入門

(完)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *