ES6 主要是為了解決 ES5 的先天不足,在原先ES5的基礎上新增了許多內容,本篇文章將列舉出ES6中新增的10大特性。
一、 let 和 const
與var不同,let和const都是用於命名局部變量,都是塊級作用域。具體可參考阮一峰老師的文章:http://es6.ruanyifeng.com/#docs/let。
這三者的用法區別如下:
1 var val = "全局變量"; 2 3 { 4 let val = "局部變量"; 5 console.log(val); // 局部變量 6 } 7 8 console.log(val); // 全局變量 9 10 const val = "常量"; 11 val = "123"; // Uncaught TypeError: Assignment to constant variable.
前面說const聲明的是常量,一旦聲明就不可再進行修改。但是當用const聲明對象時,又會出現一種新情況,舉個栗子:
1 const person = {name: "Peter", age: "22"}; 2 person.age = 23; // 不會報錯,person的age變量會被改成23 3 person = {name: "Lily", age: "18"}; // 報錯
如果用const聲明一個對象,對象所包含的值是可以被修改的。換一句話來說,只要對象指向的地址不被修改,就是允許的。
關於let和const有幾個小tips:
- let 關鍵詞聲明的變量不具備變量提升(變量提升:https://blog.csdn.net/qq_42606051/article/details/82016733)特性;
- let 和 const 聲明只在最靠近的一個塊中有效;
- 當使用常量 const 聲明時,請使用大寫變量,如:CAPITAL_CASING;
- const 在聲明時必須被賦值,否則會報錯。
- 使用let會出現暫時性死區,原因是let所聲明的變量會鎖在它所在的作用域里,不允許訪問,就是說,它也會先掃描一遍,把let聲明的變量保存起來,但是不允許使用,這時候你訪問a,由於此作用域中有a,就不用訪問外面的a了,但是你不能在它聲明之前訪問它。
var a = 1; if(ture){ console.log(a); //ReferrenceError let a = 1; }
6. 使用let和const時不能重復聲明。
7. 使用var聲明的全局變量或者未聲明的變量都會歸屬到window對象下,但是使用let和const聲明全局變量時不會發生這種情況。
拓展: 如何使用ES5標准來實現let,換言之就是如何實現塊級作用域。(答:使用匿名函數)
二、 模版字符串
在過去我們想要將字符串和變量拼接起來,只能通過運算符“+”來實現,若內容過多還要用“\”來表示換行,如:
var person = {name: "Peter", age: 22, career: "student"}; $(".introduction").html("Hello, my name is " + person.name + ", and my \ career is " + person.career + ".");
而在ES6中,可以將反引號(``)將內容括起來,在反引號中,可以使用${}來寫入需要引用到的變量。如:
var person = {name: "Peter", age: 22, career: "student"}; $(".introduction").html(`Hello, my name is ${person.name}, and my career is ${person.career}.`);
所以在ES6中,我們可以更方便地將字符串和變量連接起來。
三、 箭頭函數
在ES6中引入了一種新的函數表達方式,它是函數的一種簡寫方法,有以下三個特點:
- 不需要用關鍵字function來定義函數;
- 一般情況下可以省略return;
- 在箭頭函數內部,this並不會跟其他函數一樣指向調用它的對象,而是繼承上下文的this指向的對象。
在上面的三點中,第三點尤為重要,初學者在使用時經常會忽略這一點。
下面舉幾個箭頭函數的使用方法:
1 /* 2 ** 對應上面所說的第3點 3 ** 在箭頭函數中,this的指向與它的上下文有關 4 ** 在本例中,箭頭函數fun的上下文是window,所以this指向的也是window 5 */ 6 window.val = 12; 7 let fun = () => { 8 let val = 13; 9 console.log(val); // 13 10 console.log(this.val); // 12 11 console.log(this == window); // true 12 } 13 fun(); 14 15 /* 16 ** 普通函數使用 17 */ 18 19 let add = function(a, b){ 20 return a + b; 21 } 22 23 /* 24 ** 箭頭函數使用 25 */ 26 let add1 = (a, b) => a + b; 27 28 // 當參數只有一個時,可以將括號省略 29 let sqrt = a => a*a;
四、 函數可以設置默認參數值
在這之前,我們想要在函數中設置默認值,只能通過以下方法進行設置:
1 function printText(text){ 2 var text = text || "hello world!"; 3 console.log(text); 4 } 5 6 printText("My name is Peter"); // "My name is Peter"; 7 printText(); // "hello world!";
但是在ES6中定義了一種新方法,開發者可以直接使用如下方法設置函數的參數默認值:
1 function printText(text = "hello world!") { 2 console.log(text); 3 } 4 5 printText("My name is Peter"); // "My name is Peter"; 6 printText(); // "hello world!";
五、 Spread / Rest 操作符
Rest運算符用於獲取函數調用時傳入的參數。舉個栗子:
1 let fun = function(...args) { 2 console.log(args); 3 } 4 5 const list = ["Peter", "Lily", "Tom"]; 6 fun(list); // ["Peter", "Lily", "Tom"]
Spread運算符用於數組的構造,析構,以及在函數調用時使用數組填充參數列表。再舉個栗子:
1 /* 2 ** 使用Spread運算符合並數組 3 */ 4 const list1 = ["Peter", "Tom"]; 5 const list2 = ["Lily", "Mary"]; 6 const list = [...list1, ...list2]; 7 console.log(list); // ["Peter", "Tom", "Lily", "Mary"]] 8 9 /* 10 ** 使用Spread運算符析構數組 11 */ 12 const [person, ...list3] = list; 13 console.log(person); // Peter 14 console.log(list3); // ["Tom", "Lily", "Mary"]
更多關於Rest和Spread運算法的使用方法,可以參考一下阮一峰老師的文章:http://es6.ruanyifeng.com/#docs/array。
六、 二進制和八進制的字面量
ES6支持二進制和八進制的字面量,通過在數字前面增加0o或者0O可以將數字轉換為八進制。
1 let val1 = 0o10; 2 console.log(val1); // 8,八進制的0o10對應十進制的8 3 4 let val2 = 0b10; 5 console.log(val2); // 2,二進制的0b10對應十進制的2
七、 對象和數組解構
ES6可以將對象中的屬性或者數組中的元素進行解構,操作方式與前面所提到的Rest和Spread操作符類似,看一下下面這個栗子:
1 let person = { 2 name: "Peter", 3 age: 22, 4 career: "student" 5 } 6 7 const {name, age, career } = person; 8 console.log(`Hello, my name is ${name}, and my career is ${career}.`);
9 //Hello, my name is Peter, and my career is student.
八、 允許在對象中使用super方法
super方法應該都不陌生,在java中用來代表調用父類的構造函數。由於js不是面向對象語言,所以也沒有繼承這以說法。但是在ES6中,可以通過調用setPrototypeOf()方法來設置一個對象的prototype對象,與面向對象語言中的繼承有相似之處,所以也可以理解成這是js中用來實現繼承的方法。(這段話純屬個人理解,如果有誤請指出。)所以,在ES6中,通過使用super可以調用某個對象的prototype對象的方法或獲取參數。栗子如下:
1 var father = { 2 text: "Hello from the Father.", 3 foo() { 4 console.log("Hello from the Father."); 5 } 6 } 7 8 var son = { 9 foo() { 10 super.foo(); 11 console.log(super.text); 12 console.log("Hello from the Son."); 13 } 14 } 15 16 /* 17 ** 將father設置成son的prototpe 18 ** 當在son中調用super時,可以直接調用到它的prototype對象,即father的方法和變量 19 */ 20 Object.setPrototypeOf(son, father); 21 son.foo(); 22 // Hello from the Fater. 23 // Hello from the Fater. 24 // Hello from the Son.
九、 迭代器iterator、for...of和for...in
首先你要理解什么是iterator,可參考http://es6.ruanyifeng.com/#docs/iterator#for---of-%E5%BE%AA%E7%8E%AF。
了解完iterator之后,便可以來深入了解一下for...of和for...in這兩種方法了。用一句話來總結就是,無論是for…in還是for…of語句,都是用來迭代數據,它們之間的最主要的區別在於它們的迭代方式不同。
- for…in 語句以原始插入順序迭代對象的可枚舉屬性,簡單理解就是for...in是用來循環遍歷屬性,遍歷出的是自身和原型上的可枚舉非symbol屬性,但是遍歷不一定按照順序(tips:for...in在ES5中就已經出現了)
- for…of 語句遍歷可迭代對象定義要迭代的數據,也就是說,for...of只可以循環可迭代對象的可迭代屬性,不可迭代屬性在循環中被忽略了。(tips:for...of是ES6才提出來的)
關於for...in和for...of的用法,可以看以下栗子:
1 Object.prototype.objCustom = function() {}; 2 Array.prototype.arrCustom = function() {}; 3 4 let iterable = [3, 5, 7]; 5 iterable.foo = 'hello'; 6 //for in 會繼承 7 for (let i in iterable) { 8 console.log(i); // 依次打印 0, 1, 2, "foo", "arrCustom", "objCustom" 9 } 10 11 for (let i in iterable) { 12 if (iterable.hasOwnProperty(i)) { 13 console.log(i); // 依次打印 0, 1, 2, "foo" 14 } 15 } 16 17 // for of 18 for (let i of iterable) { 19 console.log(i); // 依次打印 3, 5, 7 20 }
對於這一塊的應用,我自己也不是很理解。例子是參考別的博主寫的。在實際開發過程中,似乎不太建議使用for...in,因為不同環境下對for...in的遍歷算法的實現不一樣,而且在for...in的過程中對這個對象屬性的添加,修改,刪除操作不能被保證, MDN不建議用這個來遍歷對象。
十、 class
ES6 提供了更接近傳統語言的寫法,引入了 class(類)這個概念,作為對象的模板。通過class關鍵字,可以定義類。
基本上,ES6 的class可以看作只是一個語法糖,它的絕大部分功能,ES5 都可以做到,新的class寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已。在ES5中,我們更多是使用原型鏈來實現繼承。