天天看點

ECMAScript 6新特性簡介簡介ECMAScript和JavaScript的關系let和const

簡介

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

今天我們将會講解一下ES6中引入的文法新特性。

ECMAScript和JavaScript的關系

1996年11月,JavaScript 的創造者 Netscape 公司,決定将 JavaScript 送出給國際标準化組織ECMA.

1997年, ECMA 釋出262号标準檔案 ECMAScript 1.0。

ECMAScript 和 JavaScript 的關系是,前者是後者的規格,後者是前者的一種實作。

我們看一下ECMAScript的發行曆史:

ECMAScript 6新特性簡介簡介ECMAScript和JavaScript的關系let和const

從2015年ES2015,也就是ES6釋出以來,ECMAScript以每年一個版本的發行速度發行到了ES2020。

後面的文章我們會講解一下這些新版本的ECMAScript的新特性。

let和const

ES6中引入了let和const,是為了解決之前的var變量的種種問題。

在ES6之前,JS中變量的作用域有兩種:全局作用域和函數作用域。

全局作用域很好了解,我們在浏覽器控制台或者 Node.js 互動終端中開始編寫 JavaScript 時,即進入了所謂的全局作用域。

全局作用域的變量可以在任何其他作用域中通路。

函數作用域就是定義在函數内部的變量,在函數内部都可以通路到該變量。

這兩種作用域會有一些問題:

  1. 變量提升

var指令會發生”變量提升”現象,即變量可以在聲明之前使用,值為undefined.

// var 的情況 
console.log(foo);  // 輸出undefined 
var foo = 2;            
  1. 變量覆寫

當我們在函數作用域使用全局變量的時候,如果函數作用域中定義了同樣名字的變量,不管是在哪裡定義的,都會覆寫掉全局的變量。如下所示:

var tmp = new Date(); 
function f() { 
console.log(tmp); 
if (false) { var tmp = "hello world"; 
} } 
f(); // undefined           
  1. 變量洩露

變量洩露的意思是,我們本來隻希望在小範圍作用域使用的變量,結果洩露到了範圍外面,如下所示:

var s = 'hello'; 
for (var i = 0; i < s.length; i++) { 
console.log(s[i]); 
} 
console.log(i); // 5
~~

為了解決上面兩個問題,ES6引入了let和const。

這兩個都是塊級作用域。不同的是const定義的變量初始化之後就不能變化了。

什麼是塊級作用域呢?類似于 if、switch 條件選擇或者 for、while 這樣的循環體即是所謂的塊級作用域,或者更簡單一點使用大括号括起來的就叫做塊級作用域。

塊級作用域的最大好處就是不會産生作用域提升,如下所示:
           

{

let a = 10;

var b = 1;

}

a // ReferenceError: a is not defined.

b // 1

# 解構指派

什麼是解構指派呢?

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

如下所示:
           

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

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

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

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

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

解構指派還可以設定預設值,我們來看下面的幾個例子:
           

let [foo = true] = [];

foo // true

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

// x='a', y='b'

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

// x='a', y='b’

let [x = 1] = [undefined];

x // 1

let [x = 1] = [null];

x // null

如果解構的預設值是一個函數,那麼可以觸發惰性指派:
           

function f() {

console.log('aaa');

let [x = f()] = [1];

上面的例子中,f函數将不會被執行。

除了結構變量之外,還可以結構對象:
           

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

foo // "aaa"

bar // "bbb"

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

baz // undefined

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 obj = { p: [ 'Hello', { y: 'World' } ] };

let { p: [x, { y }] } = obj;

x // "Hello"

y // "World"

解構指派有兩個非常重要的作用。

第一就是交換變量:
           

let x = 1;

let y = 2;

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

我們就可以不再使用中間變量,直接進行兩個變量值的互動。

第二個作用就是從函數中傳回多個值:
           

// 傳回一個數組

function example() { return [1, 2, 3]; }

let [a, b, c] = example();

// 傳回一個對象

function example() { return { foo: 1, bar: 2 }; }

let { foo, bar } = example();

//提取JSON資料

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

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

# 數組的擴充

ES6中的Array.from方法用于将下面兩類對象轉為真正的數組:

* 類似數組的對象(array-like object)
* 可周遊(iterable)的對象(包括ES6新增的資料結構Set和Map)。

什麼是類似數組對象呢?

所謂類似數組的對象,本質特征隻有一點,即必須有length屬性。是以,任何有length屬性的對象,都可以通過Array.from方法轉為數組。

下面的變量就是類數組變量:
           

let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 };

這個類數組對象怎麼轉換成為數組呢?
           

// ES5的寫法

var arr1 = [].slice.call(arrayLike);

// ['a', 'b', 'c']

// ES6的寫法 let arr2 = Array.from(arrayLike);

我們看下通常的使用場景:
           

// NodeList對象

let ps = document.querySelectorAll('p');

Array.from(ps).forEach(function (p) { console.log(p); });

// arguments對象

function foo() { var args = Array.from(arguments);

// ...

}

什麼是可周遊對象呢?

隻要是部署了Iterator接口的資料結構,都叫做可周遊對象。

我們看下下面的例子:
           

Array.from('hello') // ['h', 'e', 'l', 'l', 'o']

let namesSet = new Set(['a', 'b'])

Array.from(namesSet) // ['a', 'b']

同時還引入了擴充運算符(...),通過擴充運算符,也可以很友善的轉換為數組對象:
           

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

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

Array.from方法還可以接收第二個參數,用來對數組中的元素進行操作:
           

Array.from(arrayLike, x => x * x);

// 等同于

Array.from(arrayLike).map(x => x * x);

Array.from([1, 2, 3], (x) => x * x)

// [1, 4, 9]

Array.of方法可以很友善的建立新的數組:
           

Array.of(3, 11, 8) // [3,11,8]

Array.of(3) // [3]

Array.of(3).length // 1

Array() // []

Array(3) // [, , ,]

Array(3, 11, 8) // [3, 11, 8]

# 函數的擴充

ES6,可以支援函數的預設值了:
           

function log(x, y = 'World') { console.log(x, y); }

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

函數的預設值可以和解構指派預設值組合起來使用:
           

function foo({x, y = 5}) { console.log(x, y); }

foo({}) // undefined, 5

foo({x: 1}) // 1, 5

foo({x: 1, y: 2}) // 1, 2

foo() // TypeError: Cannot read property 'x' of undefined

接下來,我們看一個複雜的例子:
           

// 寫法一

function m1({x = 0, y = 0} = {})

{ return [x, y]; }

// 寫法二

function m2({x, y} = { x: 0, y: 0 })

{ return [x, y]; }

我們來看一下,上面的兩種寫法有什麼不同呢?

當函數沒有參數的情況:
           

m1() // [0, 0]

m2() // [0, 0]

當x和y都有值的情況:
           

m1({x: 3, y: 8}) // [3, 8]

m2({x: 3, y: 8}) // [3, 8]

當x有值,y無值的情況 :
           

m1({x: 3}) // [3, 0]

m2({x: 3}) // [3, undefined]

當x和y都無值的情況:
           

m1({}) // [0, 0];

m2({}) // [undefined, undefined]

m1({z: 3}) // [0, 0]

m2({z: 3}) // [undefined, undefined]

看出差別了嗎? m1的解構指派,對于x,y來說是有預設值0的。而m2的解構指派對于x,y來說是沒有預設值的。

> 本文作者:flydean程式那些事
> 
> 本文連結:[http://www.flydean.com/ecmascript-6-startup/](http://www.flydean.com/ecmascript-6-startup/)
> 
> 本文來源:flydean的部落格
> 
> 歡迎關注我的公衆号:「程式那些事」最通俗的解讀,最深刻的幹貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!

[![阿裡雲](https://img.alicdn.com/tfs/TB1nkoQDlv0gK0jSZKbXXbK2FXa-440-240.jpg)](https://www.aliyun.com/activity/daily/cloud?userCode=cvht8c6v)













           

繼續閱讀