歷時將近6年的時間來制定的新 ECMAScript 標準 ECMAScript 6(亦稱 ECMAScript Harmony,簡稱 ES6)終於在 2015 年 6 月正式發佈。自從上一個標準版本 ES5 在 2009 年發佈之後,ES6 就一直以新語法、新特性的優越性吸引著衆多 JavaScript 開發者,驅使他們積極嚐鮮。javascript
因爲ES6是在2015年發佈的,因此也叫ES2015。java
之後ESCMAScript標準一年一更新,統一使用年份命名:ES201六、ES201七、….node
下面開始介紹ES6經常使用的一些新特性:es6
在ES5以前,不存在塊級做用域,在編程的時候不少時候會帶來不少的不便,ES6新增了塊級做用域,補足了這方面的缺陷。算法
塊級聲明指的是該聲明的變量沒法被代碼塊外部訪問。塊做用域,又被稱爲詞法做用域(lexical scopes),能夠在以下的條件下建立:express
塊級做用域是不少類C語言的工做機制,ECMAScript 6 引入塊級聲明的目的是加強 javascript 的靈活性,同時又能與其它編程語言保持一致。編程
使用let聲明變量的語法和使用var聲明的語法是同樣的。可是let聲明的變量的做用域會限制在當前的代碼塊中。這是let與var的最大區別。數組
<script type="text/javascript"> let a = 10; if(a > 5){ console.log(b); //用let聲明的變量沒有聲明提早這一特性,因此此處也訪問不到(報錯) let b = 20; console.log(b); } console.log(b); //因爲b是在if塊中使用let聲明的,因此此處沒法訪問到。(報錯) </script>
注意:安全
在 ES6 使用const來聲明的變量稱之爲常量。這意味着它們不能再次被賦值。因爲這個緣由,全部的 const 聲明的變量都必須在聲明處初始化。const聲明的常量和let變量同樣也是具備塊級做用域的特性。數據結構
<script type="text/javascript"> var a = 20; if (true) { const b = 20; b = 30; //錯誤! 常量不能從新賦值 const c; //錯誤! 常量聲明的同時必須賦值。 } </script>
注意:
使用var聲明的循環變量在循環結束後仍然能夠訪問到。 使用let聲明的循環變量,在循環結束以後會當即銷燬。
<script type="text/javascript"> for(let i = 0; i < 3; i++){ // 循環結束以後會當即銷燬 i console.log(i); } console.log(i); //此處沒法訪問到 i 。 </script>
看下面的代碼,是輸出10個10,而不是0,1,2,…
<script type="text/javascript"> var funcs = []; for (var i = 0; i < 10; i++) { funcs.push(function () { console.log(i); }); } funcs.forEach(function (func) { func(); // 輸出 "10" 共10次 }); </script>
解決辦法須要使用函數的自執行特性。
var funcs = []; for (var i = 0; i < 10; i++) { funcs.push((function(value) { return function() { console.log(value); } }(i))); } funcs.forEach(function(func) { func(); // 輸出 0,1,2 ... 9 });
若是使用let聲明變量,則徹底能夠避免前面的問題。 這是ES6規範中專門定義的特性。在for … in和for … of循環中也適用
<script type="text/javascript"> var funcs = []; for (let i = 0; i < 10; i++) { funcs.push(function () { console.log(i); }); } funcs.forEach(function (func) { func(); // 輸出 0,1,2 ... 9 }) </script>
說明:
JavaScript函數的最大的一個特色就是在傳遞參數的時候,參數的個數不受限制的。爲了健壯性考慮,通常在函數內部須要作一些默認值的處理。
function makeRequest(url, timeout, callback) { timeout = timeout || 2000; callback = callback || function() {}; }
其實上面的默認值方法有個bug:當timeout是0的時候也會當作假值來處理,從而給賦值默認值2000.
ES6從語言層面面上增長了 默認值的 支持。看下面的代碼:
//這個函數若是隻傳入第一個參數,後面兩個不傳入,則會使用默認值。若是後面兩個也傳入了參數,則不會使用默認值。 function makeRequest(url, timeout = 2000, callback = function() {}) { // 其他代碼 }
在非嚴格模式下,arguments老是能反映出命名參數的變化。看下面的代碼:
<script type="text/javascript"> function foo(a, b) { //非嚴格模式 console.log(arguments[0] === a); //true console.log(arguments[1] === b); //true a = 10; b = 20; console.log(arguments[0] === a); //true console.log(arguments[1] === b); //true } foo(1, 2); </script>
在ES5的嚴格模式下,arguments只反映參數的初始值,而再也不反映命名參數的變化!
<script type="text/javascript"> function foo(a, b) { //嚴格模式 "use strict" console.log(arguments[0] === a); //true console.log(arguments[1] === b); //true a = 10; b = 20; console.log(arguments[0] === a); //false。 修改a的值不會影響到arguments[0]的值 console.log(arguments[1] === b); //false } foo(1, 2); </script>
當使用ES6參數默認值的時候,無論是不是在嚴格模式下,都和ES5的嚴格模式相同。看下面的代碼:
<script type="text/javascript"> function foo(a, b = 30) { console.log(arguments[0] === a); //true console.log(arguments[1] === b); //true a = 10; b = 20; console.log(arguments[0] === a); //false。 因爲b使用了默認值。雖然a沒有使用默認值,可是仍然表現的和嚴格模式同樣。 console.log(arguments[1] === b); //false。 b使用了默認值,因此表現的和嚴格模式同樣。 } foo(1, 2); </script>
注意:若是這樣調用foo(1),則 a == 1, b == 30, arguments[0] == 1, arguments[1] == undefined。也就是說默認值並不會賦值給arguments參數。
參數的默認值,也能夠是一個表達式或者函數調用等。看下面的代碼
<script type="text/javascript"> function getValue() { return 5; } function add(first, second = getValue()) { //表示使用getValue這個函數的返回值做爲second的默認值。 return first + second; } console.log(add(1, 1)); // 2. 調用add函數的時候,傳入了第二個參數,則以傳入的參數爲準。 console.log(add(1)); // 6。 調用add函數的時候,沒有傳入第二個參數,則會調用getValue函數。 </script>
有一點須要要注意:getValue()只會在調用add且不傳入第二個參數的時候纔會去調用。不是在解析階段調用的。
<script type="text/javascript"> let value = 5; function getValue() { return value++; } function add(first, second = getValue()) { // return first + second; } console.log(add(1, 1)); // 2 console.log(add(1)); // 6。 console.log(add(1)); // 7 console.log(add(1)); // 8 </script>
因爲默認值能夠表達式,因此咱們甚至可使用前面的參數做爲後面參數的默認值。
function add(first, second = first) { // 使用第一個參數做爲第二個參數的默認值 return first + second; }
注意:能夠把前面的參數做爲後面參數的默認值,可是不能把後面的參數做爲第一個參數的默認值。這能夠前面說的let和const的暫存性死區一個意思。
function add(first = second, second)) { // 這種寫法是錯誤的 return first + second; }
Javascript並不限制傳入的參數的數量。在調用函數的時候,傳入的實參的個數超過形參的個數的時候,超過的部分就成爲了未命名參數。在ES5以前,咱們通常能夠經過arguments對象來獲取到未命名參數的值。可是羅顯繁瑣。
<script type="text/javascript"> function foo(a) { console.log(a); console.log(arguments[1]) //取得傳入的多餘的參數。 } foo(2, 3); </script>
ES6,提供了一種更加優雅處理未命名參數的問題:剩餘參數( Rest Parameters )
語法:function a(a, … b){ }
剩餘參數使用三個點( … )和變量名來表示。
<script type="text/javascript"> function foo(a, ...b) { console.log(a); console.log(b instanceof Array); //true .多餘的參數都被放入了b中。b其實就是一個數組。 } foo(2, 3, 4, 6); </script>
注意:
例如:Math中的max函數能夠返回任意多個參數中的最大值。可是若是這些參數在一個數組中,則沒有辦法直接傳入。之前通用的作法是使用applay方法。
看下面的代碼:
<script type="text/javascript"> let values = [25, 50, 75, 100] console.log(Math.max.apply(Math, values)); // 100 </script>
上面這種方法雖然可行,可是老是不是那麼直觀。
使用ES6提供的擴展運算符能夠很容易的解決這個問題。在數組前加前綴 … (三個點)。
<script type="text/javascript"> let values = [25, 50, 75, 100] console.log(Math.max(...values)); //使用擴展運算符。至關於拆解了數組了。 console.log(Math.max(...values, 200)); //也可使用擴展運算符和參數的混用,則這個時候就有 5 個數參與比較了。 </script>
注意:剩餘參數和擴展運算符都是 使用三個點做爲前綴。可是他們使用的位置是不同的。
- **剩餘參數是用在函數的聲明的時候的參數列表中,並且必須在參數列表的後面
- 擴展運算符是用在函數調用的時候做爲實參來傳遞的,在實參中的位置沒有限制。
ECMAScript 6 最有意思的部分之一就是箭頭函數。正如其名,箭頭函數由 「箭頭」(=>)這種新的語法來定義。
其實在別的語言中早就有了這種語法結構,不過他們叫拉姆達表達式。
基本語法以下:
(形參列表)=>{
//函數體 }
箭頭函數能夠賦值給變量,也能夠像匿名函數同樣直接做爲參數傳遞。
<script type="text/javascript"> var sum = (num1, num2) =>{ return num1 + num2; } console.log(sum(3, 4)); //前面的箭頭函數等同於下面的傳統函數 var add = function (num1, num2) { return num1 + num2; } console.log(add(2, 4)) </script>
若是函數體內只有一行代碼,則包裹函數體的 大括號 ({ })徹底能夠省略。若是有return,return關鍵字也能夠省略。
若是函數體內有多條語句,則 {} 不能省略。
<script type="text/javascript"> var sum = (num1, num2) => num1 + num2; console.log(sum(5, 4)); //前面的箭頭函數等同於下面的傳統函數 var add = function (num1, num2) { return num1 + num2; } console.log(add(2, 4)); //若是這一行代碼是沒有返回值的,則方法的返回自也是undefined var foo = (num1, num2) => console.log("aaa"); console.log(foo(3,4)); //這個地方的返回值就是undefined </script>
若是箭頭函數只有一個參數,則包裹參數的小括號能夠省略。其他狀況下都不能夠省略。固然若是不傳入參數也不能夠省略
<script type="text/javascript"> var foo = a=> a+3; //由於只有一個參數,因此()能夠省略 console.log(foo(4)); // 7 </script>
若是想直接返回一個js對象,並且還不想添加傳統的大括號和return,則必須給整個對象添加一個小括號 ()
<script type="text/javascript"> var foo = ()=>({name:"lisi", age:30}); console.log(foo()); //等同於下面的; var foo1 = ()=>{ return { name:"lisi", age : 30 }; } </script>
<script type="text/javascript"> var person = (name => { return { name: name, age: 30 } } )("zs"); console.log(person); </script>
在ES5以前this的綁定是個比較麻煩的問題,稍不注意就達不到本身想要的效果。由於this的綁定和定義位置無關,只和調用方式有關。
在箭頭函數中則沒有這樣的問題,在箭頭函數中,this和定義時的做用域相關,不用考慮調用方式
箭頭函數沒有 this 綁定,意味着 this 只能經過查找做用域鏈來肯定。若是箭頭函數被另外一個不包含箭頭函數的函數囊括,那麼 this 的值和該函數中的 this 相等,不然 this 的值爲 window。
<script type="text/javascript"> var PageHandler = { id: "123456", init: function () { document.addEventListener("click", event => this.doSomething(event.type), false); // 在此處this的和init函數內的this相同。 }, doSomething: function (type) { console.log("Handling " + type + " for " + this.id); } }; PageHandler.init(); </script>
看下面的一段代碼:
<script type="text/javascript"> var p = { foo:()=>console.log(this) //此處this爲window } p.foo(); //輸出爲 window對象。 並非我想要的。因此在定義對象的方法的時候應該避免使用箭頭函數。 //箭頭函數通常用在傳遞參數,或者在函數內部聲明函數的時候使用。 </script>
說明:
雖然箭頭函數沒有本身的arguments對象,可是在箭頭函數內部仍是可使用它外部函數的arguments對象的。
<script type="text/javascript"> function foo() { //這裏的arguments是foo函數的arguments對象。箭頭函數本身是沒有 arguments 對象的。 return ()=>arguments[0]; //箭頭函數的返回值是foo函數的第一個參數 } var arrow = foo(4, 5); console.log(arrow()); // 4 </script>
在JavaScript中,幾乎全部的類型都是對象,因此使用好對象,對提示JavaScript的性能很重要。
ECMAScript 6 給對象的各個方面,從簡單的語法擴展到操做與交互,都作了改進。
ECMAScript 6 規範明肯定義了每種對象類別。理解該術語對於從總體上認識該門語言顯得十分重要。對象類別包括:
<script type="text/javascript"> function createPerson(name, age) { //返回一個對象:屬性名和參數名相同。 return { name:name, age:age } } console.log(createPerson("lisi", 30)); // {name:"lisi", age:30} //在ES6中,上面的寫法能夠簡化成以下形式 </script>
在ES6中,上面的寫法能夠簡化成以下的形式:
<script type="text/javascript"> function createPerson(name, age) { //返回一個對象:屬性名和參數名相同。 return { name, //當對象屬性名和本地變量名相同時,能夠省略冒號和值 age } } console.log(createPerson("lisi", 30)); // {name:"lisi", age:30} </script>
當對象字面量中的屬性只有屬性名的時候,JavaScript 引擎會在該做用域內尋找是否有和屬性同名的變量。在本例中,本地變量 name 的值被賦給了對象字面量中的 name 屬性。
該項擴展使得對象字面量的初始化變得簡明的同時也消除了命名錯誤。對象屬性被同名變量賦值在 JavaScript 中是一種廣泛的編程模式,因此這項擴展的添加很是受歡迎。
<script type="text/javascript"> var person = { name:'lisi', sayHell:function () { console.log("個人名字是:" + this.name); } } person.sayHell() </script>
在ES6中,上面的寫法能夠簡化成以下的形式:
<script type="text/javascript"> var person = { name:'李四', sayHell() { console.log("個人名字是:" + this.name); } } person.sayHell() </script>
省略了冒號和function看起來更簡潔
在ES5以前,若是屬性名是個變量或者須要動態計算,則只能經過 對象.[變量名] 的方式去訪問。並且這種動態計算屬性名的方式 在字面量中 是沒法使用的。
<script type="text/javascript"> var p = { name : '李四', age : 20 } var attName = 'name'; console.log(p[attName]) //這裏 attName表示的是一個變量名。 </script>
而下面的方式使用時沒有辦法訪問到attName這個變量的。
<script type="text/javascript"> var attName = 'name'; var p = { attName : '李四', // 這裏的attName是屬性名,至關於各級p定義了屬性名叫 attName的屬性。 age : 20 } console.log(p[attName]) // undefined </script>
在ES6中,把屬性名用[ ]括起來,則括號中就能夠引用提早定義的變量。
<script type="text/javascript"> var attName = 'name'; var p = { [attName] : '李四', // 引用了變量attName。至關於添加了一個屬性名爲name的屬性 age : 20 } console.log(p[attName]) // 李四 </script>
ECMAScript 從第五版開始避免在 Object.prototype 上添加新的全局函數或方法,轉而去考慮具體的對象類型如數組)應該有什麼方法。當某些方法不適合這些具體類型時就將它們添加到全局 Object 上 。
ECMAScript 6 在全局 Object 上添加了幾個新的方法來輕鬆地完成一些特定任務。
在 JavaSciprt 中當你想比較兩個值時,你極有可能使用比較操做符(==)或嚴格比較操做符(===)。許多開發者爲了不在比較的過程當中發生強制類型轉換,更傾向於後者。但即便是嚴格等於操做符,它也不是萬能的。例如,它認爲 +0 和 -0 是相等的,雖然它們在 JavaScript 引擎中表示的方式不一樣。一樣 NaN === NaN 會返回 false,因此必須使用 isNaN() 函數才能判斷 NaN 。
ECMAScript 6 引入了 Object.is() 方法來補償嚴格等於操做符怪異行爲的過失。該函數接受兩個參數並在它們相等的返回 true 。只有二者在類型和值都相同的狀況下才會判爲相等。以下所示:
console.log(+0 == -0); // true console.log(+0 === -0); // true console.log(Object.is(+0, -0)); // false console.log(NaN == NaN); // false console.log(NaN === NaN); // false console.log(Object.is(NaN, NaN)); // true console.log(5 == 5); // true console.log(5 == "5"); // true console.log(5 === 5); // true console.log(5 === "5"); // false console.log(Object.is(5, 5)); // true console.log(Object.is(5, "5")); // false
不少狀況下 Object.is() 的表現和 === 是相同的。它們之間的區別是前者 認爲 +0 和 -0 不相等而 NaN 和 NaN 則是相同的。不過棄用後者是徹底沒有必要的。什麼時候選擇 Object.is() 與 == 或 === 取決於代碼的實際狀況。
使用assign主要是爲了簡化對象的混入(mixin)。混入是指的在一個對象中引用另外一個對象的屬性或方法。
assing能夠把一個對象的屬性和訪問完整的轉copy到另一個對象中。
<script type="text/javascript"> var p = { name : "lisi", age : 20, friends : ['張三', '李四'] } var p1 = {}; Object.assign(p1, p); //則p1中就有了與p相同的屬性和方法. p1是接受者,p是提供者 console.log(p1); //這種copy是淺copy,也就是說若是屬性值是對象的話,只是copy的對象的地址值(引用) console.log(p1.friends == p.friends); //true p1和p的friends同事指向了同一個數組。 p.friends.push("王五"); console.log(p1.friends); //['張三', '李四', '王五'] </script>
assign方法能夠接受任意多的提供者。意味着後面提供者的同名屬性和覆蓋前面提供者的屬性值。
<script type="text/javascript"> var p = { name : "lisi", age : 20, friends : ['張三', '李四'] } var p1 = { name : 'zs', } var p2 = {}; Object.assign(p2, p, p1); //p和p1都是提供者 console.log(p2.name); // zs </script>
在之前在字符串中查找字符串的時候,都是使用indexOf方法。
ES6新增了三個方法來查找字符串。
每一個方法都接收兩個參數:須要搜索的文本和可選的起始索引值。當提供第二個參數後,includes() 和 startsWith() 會以該索引爲起始點進行匹配,而 endsWith() 將字符串的長度與參數值相減並將獲得的值做爲檢索的起始點。若第二個參數未提供,includes() 和 startsWith() 會從字符串的起始中開始檢索,endsWith() 則是從字符串的末尾。實際上,第二個參數減小了須要檢索的字符串的總量。如下是使用這些方法的演示:
var msg = "Hello world!"; console.log(msg.startsWith("Hello")); // true console.log(msg.endsWith("!")); // true console.log(msg.includes("o")); // true console.log(msg.startsWith("o")); // false console.log(msg.endsWith("world!")); // true console.log(msg.includes("x")); // false console.log(msg.startsWith("o", 4)); // true console.log(msg.endsWith("o", 8)); // true console.log(msg.includes("o", 8)); // false
ECMAScript 6 還向字符串添加了 repeat() 方法,它接受一個數字參數做爲字符串的重複次數。該方法返回一個重複包含初始字符串的新字符串,重複次數等於參數。例如:
console.log("x".repeat(3)); // "xxx" console.log("hello".repeat(2)); // "hellohello" console.log("abc".repeat(4)); // "abcabcabcabc"
模板字面量是 ECMAScript 6 針對 JavaScript 直到 ECMAScript 5 依然缺失的以下功能的迴應:
- 多行字符串 針對多行字符串的形式概念(formal concept)。
- 基本的字符串格式化 將字符串中的變量置換爲值的能力。
- 轉義 HTML 能將字符串進行轉義並使其安全地插入到 HTML 的能力。
模板字面量以一種全新的表現形式解決了這些問題而不須要向 JavaScript 已有的字符串添加額外的功能。
使用一對反引號 「(tab正上方的按鍵)來表示模板字面量。
let message = `Hello world!`; //使用模板字面量建立了一個字符串 console.log(message); // "Hello world!" console.log(typeof message); // "string" console.log(message.length); // 12
注意:若是模板字符串中使用到了反引號,則應該轉義。可是單雙引號不須要轉義
在ES5以前JavaScript是不支持多行字符串的。(可是在之前的版本中有一個你們都認爲是bug的方式能夠寫出多行字符串,就是在尾部添加一個反斜槓 \)
<body> <script type="text/javascript"> var s = "abc \ aaaaaa"; console.log(s); //可是輸出的結果中不包括換行 </script> </body>
可是在ES6中字符串的模板字面量輕鬆的解決了多行字符串的問題,並且沒有任何新的語法
<script type="text/javascript"> var s = `abc aaaaa dsalfja dfadfja`; console.log(s); </script>
可是要注意: 反引號中的全部空格和縮進都是有效字符。
置換容許你將 JavaScript 表達式嵌入到模板字面量中並將其結果做爲輸出字符串中的一部分。
語法:${變量名、表達式、任意運算、方法調用等}
能夠嵌入任何有效的JavaScript代碼
<script type="text/javascript"> var name = "李四"; var msg = `歡迎你${name}同窗`; console.log(msg) </script>
模板字面量真正的強大之處來源於模板標籤。一個模板標籤能夠被轉換爲模板字面量並做爲最終值返回。標籤在模板的頭部,即左 ` 字符以前指定,以下所示:
let message = myTag`Hello world`;
在上面的代碼中,myTag就是模板標籤。
myTag實際上是一個函數,這個函數會被調用來處理這個模板字符串。
一個標籤僅表明一個函數,他接受須要處理的模板字面量。標籤分別接收模板字面量中的片斷,且必須將它們組合以得出結果。函數的首個參數爲包含普通 JavaScript 字符串的數組。餘下的參數爲每次置換的對應值。
標籤函數通常使用剩餘參數來定義,以便輕鬆地處理數據。以下:
<script type="text/javascript"> let name = '張三', age = 20, message = show`我來給你們介紹${name}的年齡是${age}.`; /* 應該定義一個函數show: 參數1:一個字符串數組。在本例中包含三個元素。 0:"我來給你們介紹" 1:"的年齡是" 2:"." 參數2和參數3:表示須要置換的字符串的值。 */ function show(stringArr, value1, value2) { console.log(stringArr); // console.log(value1); // 張三 console.log(value2); // 20 return "abc"; } console.log(message); //abc </script>
爲了簡化書寫,通常把Value1和Value2寫成剩餘字符串的形式
function show(stringArr, ...values){ }
在 ECMAScript 5 或更早的版本中,從對象或數組中獲取特定的數據並賦值給本地變量須要書寫不少而且類似的代碼。例如:
let options = { repeat: true, save: false }; // 從對象中提取數據 let repeat = options.repeat, save = options.save;
這段代碼反覆地提取在 options 上存儲地屬性值並將它們傳遞給同名的本地變量。雖然這些看起來不是那麼複雜,不過想象一下若是你的一大批變量有着相同的需求,你就只能一個一個地賦值。並且,若是你須要從對象內部嵌套的結構來查找想要的數據,你極有可能爲了一小塊數據而訪問了整個數據結構。
這也是 ECMAScript 6 給對象和數組添加解構的緣由。當你想要把數據結構分解爲更小的部分時,從這些部分中提取數據會更容易些。不少語言都能使用精簡的語法來實現解構操做。ECMAScript 6 解構的實際語法或許你已經很是熟悉:對象和數組字面量。
對象結構的語法就是在賦值語句的左側使用相似對象字面量的結構。
let node = { type: "Identifier", name: "foo" }; //這裏就至關於聲明瞭兩個變量: type = node.type; name:node.name let { type, name } = node; console.log(type); // "Identifier" console.log(name); // "foo"
在上面的結構中必需要初始化。不然會出現語法錯誤。
// 語法錯誤! var { type, name }; // 語法錯誤! let { type, name }; // 語法錯誤! const { type, name };
若是聲明的變量想改變他們的值,也可使用解構表達式。
<script type="text/javascript"> let node = { type: "Identifier", name: "foo" }, type = "Literal", name = 5; //注意:此處必需要在圓括號內才能使用解構表達式 ({type, name} = node); console.log(type); // "Identifier" console.log(name); // "foo"" </script>
若是賦值號右邊的對象中沒有與左邊變量同名的屬性,則左邊的變量會是 undefined
let node = { type: "Identifier", name: "foo" }; //由於node中沒有叫value的屬性,因此valued的值將會是undefined let { type, name, value } = node; console.log(type); // "Identifier" console.log(name); // "foo" console.log(value); // undefined
不過咱們也能夠手動指定他的默認值。(這個和函數的參數默認值很像)
<script type="text/javascript"> let node = { type: "Identifier", name: "foo" }; //手動添加value的默認值爲3 let { type, name, value = 3} = node; console.log(type); // "Identifier" console.log(name); // "foo" console.log(value); // 3 </script>
在前面的操做中,都是把對象的屬性值,賦值給同名變量。
其實也能夠賦值給不一樣名的變量。
<script type="text/javascript"> let node = { type: "Identifier", name: "foo" }; // localType纔是要定義的新的變量。 type是node的屬性 let {type: localType, name: localName} = node; console.log(localType); // "Identifier" console.log(localName); // "foo" </script>
注意:冒號後面纔是要定義的新的變量,這個能夠咱們的對象字面量不太同樣!
這個地方也可使用默認值。
let node = { type: "Identifier" }; let { type: localType, name: localName = "bar" } = node; console.log(localType); // "Identifier" console.log(localName); // "bar"
數據解構的語法和對象解構看起來相似,只是將對象字面量替換成了數組字面量,並且解構操做的是數組內部的位置(索引)而不是對象中的命名屬性,例如:
let colors = [ "red", "green", "blue" ]; let [ firstColor, secondColor ] = colors; console.log(firstColor); // "red" console.log(secondColor); // "green"
若是隻想取數組中的某一項,則能夠不用命名。
let colors = [ "red", "green", "blue" ]; //只取數組中的第三項。 let [ , , thirdColor ] = colors; console.log(thirdColor); // "blue"
你能夠想要賦值的狀況下使用數組的解構賦值表達式,可是和對象解構不一樣,不必將它們包含在圓括號中,例如:
let colors = [ "red", "green", "blue" ], firstColor = "black", secondColor = "purple"; [ firstColor, secondColor ] = colors; //能夠不用加括號。固然添加也不犯法 console.log(firstColor); // "red" console.log(secondColor); // "green"
數組解構表達式有一個很經常使用的地方,就是交換兩個變量的值。在之前通常定義一個第三方變量進行交換,例以下面的代碼:
<script type="text/javascript"> let a = 3, b = 4, temp; temp = a; a = b; b = temp; console.log(a); console.log(b) </script>
那麼在ES6中徹底能夠拋棄第三方變量這種方式,使用咱們的數組解構表達式
<script type="text/javascript"> let a = 3, b = 4; //左側和前面的案例是同樣的,右側是一個新建立的數組字面量。 [a, b] = [b, a]; console.log(a); console.log(b) </script>
之前咱們有5種基本數據類型:Number、String、Boolean、Null、Undefined
ES6新增了一種新的數據類型:Symbol
在ES5以前咱們都沒辦法建立私有變量,只能想辦法去封裝。symbol 來建立私有成員,這也是 JavaScript 開發者長久以來期待的一項特性。
Symbol在基本數據類型中是比較特別的。咱們之前的均可以用字面量去建立基本數據類型的數據,可是Symbol卻不可使用字面量的是形式去建立。
咱們可使用symbol全局函數來建立Symbol。
<script type="text/javascript"> let firstName = Symbol(); //建立一個Symbol let person = {}; person[firstName] = "張三"; console.log(person[firstName]); // "張三" </script>
說明:上面的代碼中,firstName 做爲 symbol 類型被建立並賦值給 person 對象以做其屬性。每次訪問這個屬性時必須使用該 symbol 。
在建立Symbol的時候,也能夠傳入字符串,這個字符串也僅僅是在調試輸出的時候方便,實際沒有啥用處。
<script type="text/javascript"> var s1 = Symbol("abc"); var s2 = Symbol("abc"); console.log(s1 == s2); //false </script>
注意:任意兩個Symbol都不會相等,即便建立他們的時候使用了相同的參數。
既然 symbol 是基礎類型,你可使用 typeof 操做符來判斷變量是否爲 symbol 。ECMAScript 6 拓展了 typeof 使其操做 symbol 時返回 「symbol」。例如:
let symbol = Symbol(); console.log(typeof symbol); // "symbol"
因爲每個Symbol值都是不相等的,這意味着Symbol值能夠做爲標識符,用於對象的屬性名,就能保證不會出現同名的屬性。這對於一個對象由多個模塊構成的狀況很是有用,能防止某一個鍵被不當心改寫或覆蓋。
var mySymbol = Symbol(); // 第一種寫法 var a = {}; a[mySymbol] = 'Hello!'; // 第二種寫法 var a = { [mySymbol]: 'Hello!' }
以上兩種寫法都是相同的結果
注意:
symbol做爲對象的屬性的時候,只能使用 [ ] 去訪問,不能使用點去訪問。
symbol做爲對象的屬性名使用的時候,該屬性仍是公開屬性,不是私有屬性。可是這個時候使用for… in和for…of
時沒法遍歷到這個symbol屬性的。
Symbol 做爲屬性名,該屬性不會出如今for…in、for…of循環中,也不會被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。可是,它也不是私有屬性,有一個Object.getOwnPropertySymbols方法,能夠獲取指定對象的全部 Symbol 屬性名。
看下面的代碼
<script type="text/javascript"> var obj = {}; var a = Symbol('a'); var b = Symbol('b'); obj[a] = 'Hello'; obj[b] = 'World'; // 返回obj對象全部Symbol類型的屬性名組成的數組。 var objectSymbols = Object.getOwnPropertySymbols(obj); console.log(objectSymbols) //[Symbol(a), Symbol(b)] </script>
看下面的代碼
var obj = {}; var foo = Symbol("foo"); obj[foo] = "lisi"; for (var i in obj) { console.log(i); // 無輸出 。 由於遍歷不到Symbol型的屬性 } Object.getOwnPropertyNames(obj);// [] 只能拿到非Symbol類型的屬性 Object.getOwnPropertySymbols(obj) //[Symbol(foo)]
還有一個新API能夠拿到全部類型的屬性,包括常規和Symbol型的。
Reflect.ownKeys
let obj = { [Symbol('my_key')]: 1, enum: 2, nonEnum: 3 }; Reflect.ownKeys(obj);// ["enum", "nonEnum", Symbol(my_key)]
說明:
1、Symbol.for(字符串參數):在全局環境中搜索 以該字符串做爲參數的Symbol值,若是搜到則返回這個sybol,若是搜不到則建立一個Symbol,並把它註冊在全局環境中。
<script type="text/javascript"> //第一次搜不到,則新建立一個返回,並在全局環境(window)中註冊 var a = Symbol.for("foo"); //第二次搜到上次建立的 var b = Symbol.for("foo"); console.log(a === b); //由於兩次搜到的是同一個Symbol,因此此處是true </script>
Symbol.for()和Symbol()均可以建立Symbol類型的數據。
兩者區別:
- Symbol.for()對一樣的字符串,每次獲得結果確定是同樣的。由於都是從全局環境中搜索。
- Symbol()則不會有搜索的過程,每次都是一個全新的不一樣的symbol,並且也不會向全局環境中註冊。
看下面的代碼
<script type="text/javascript"> var a = Symbol("foo"); var b = Symbol.for("foo"); console.log(a == b); //false </script>
2、Symbol.keyFor(symbol):返回一個已經註冊的symbol的」key」。
<script type="text/javascript"> var a = Symbol("foo"); var b = Symbol.for("foo"); console.log(Symbol.keyFor(a)); // undefined. 由於a沒有想全局環境中登記,因此是undefinded console.log(Symbol.keyFor(b)); // foo </script>
JavaScript 在絕大部分歷史時期內只有一種集合類型,那就是數組。數組在 JavaScript 中的使用方式和其它語言很類似,可是其它集合類型的缺少致使數組也常常被看成隊列(queues)和棧(stacks)來使用。
由於數組的索引只能是數字類型,當開發者以爲非數字類型的索引是必要的時候會使用非數組對象。這項用法促進了以非類數組對象爲基礎的 set 和 map 集合類型的實現。
Set是相似數組的一種結構,能夠存儲數據,與數組的區別主要是 Set中的元素不能重複,而數組中的元素能夠重複。
一句話總結:Set類型是一個包含無重複元素的有序列表
Set自己是一個構造函數。
<script type="text/javascript"> //建立Set數據結構對象。 var s = new Set(); //調用set對象的add方法,向set中添加元素 s.add("a"); s.add("c"); s.add("b"); //set的size屬性能夠獲取set中元素的個數 console.log(s.size) </script>
<script type="text/javascript"> var s = new Set(); s.add("a"); s.add("c"); s.add("b"); s.add("a"); //重複,因此添加失敗。注意這個地方並不會保存。 console.log(s.size); // 長度是3 </script>
看下面的代碼:
<script type="text/javascript"> var s = new Set(); s.add(5); s.add("5"); console.log(s.size); // 長度是2 </script>
在上面的代碼中,數字5和字符串5都會添加成功。爲何呢?
Set是使用什麼機制來判斷兩個元素是否相等的呢?
是經過咱們前面說過的 Object.is(a, b) 來判斷兩個元素是否相等。
回憶一下:這個方法除了 +0和-0、NaN和NaN認爲相等,其他和三個 === 是徹底同樣的。
<script type="text/javascript"> var s = new Set(); s.add(+0); s.add(-0); //重複添加不進去 s.add(NaN); s.add(NaN); //重複添加不進去 s.add([]); s.add([]); //兩個空數組不相等,因此能夠添加進去 s.add({}); s.add({}); // 兩個空對象也不重複,因此也能夠添加進去 console.log(s.size); // 長度是6 </script>
<script type="text/javascript"> //使用數組中的元素來初始化Set,固然碰到重複的也不會添加進去。 var s = new Set([2, 3, 2, 2, 4]); console.log(s.size) </script>
使用Set的 has() 方法能夠判斷一個值是否在這個set中。
<script type="text/javascript"> let set = new Set(); set.add(5); set.add("5"); console.log(set.has(5)); // true console.log(set.has(6)); // false </script>
delete(要刪除的值) :刪除單個值
clear():清空全部的值
<script type="text/javascript"> let set = new Set(); set.add(5); set.add("5"); console.log(set.has(5)); // true set.delete(5); console.log(set.has(5)); // false console.log(set.size); // 1 set.clear(); console.log(set.has("5")); // false console.log(set.size); // 0 </script>
數組有個方法forEach能夠遍歷數組。
- Set也有forEach能夠遍歷Set。
使用Set的forEach遍歷時的回調函數有三個參數:
function (value, key, ownerSet){
}
參數1:遍歷到的元素的值
參數2:對set集合來講,參數2的值和參數1的值是徹底同樣的。
參數3:這個set本身
<script type="text/javascript"> let set = new Set(["a", "c", "b", 9]); set.forEach(function (v, k, s) { console.log(v + " " + (v === k) + " " + (s === set)); // 永遠是true }) </script>
- for…of也能夠遍歷set。
for(var v of set){ console.log(v) }
將數組轉換爲Set至關容易,你只須要在建立Set集合時把數組做爲參數傳遞進去便可。
把Set轉換爲數組使用前面講到的擴展運算符也很容易
<script type="text/javascript"> let set = new Set([1, 2, 3, 3, 3, 4, 5]), arr = [...set]; //使用擴展運算符。那麼新的數組中已經沒有了重複元素。注意,此對set並無什麼影響 console.log(arr); // [1,2,3,4,5] </script>
這種狀況在須要去數組中重複元素的時候很是好用。
<script type="text/javascript"> function eliminateDuplicates(items) { return [...new Set(items)]; } let numbers = [1, 2, 3, 3, 3, 4, 5, 5, 2, 1, 1], //返回的是新的沒有重複元素的數組。 noDuplicates = eliminateDuplicates(numbers); console.log(noDuplicates); // [1,2,3,4,5] </script>
Set提供了處理一系列值的方式,不過若是想給這些值添加一些附加數據則顯得力不從心,因此又提供了一種新的數據結構:Map
ECMAScript 6 中的 map 類型包含一組有序的鍵值對,其中鍵和值能夠是任何類型。
鍵的比較結果由 Object.is() 來決定,因此你能夠同時使用 5 和 「5」 作爲鍵來存儲,由於它們是不一樣的類型。
這和使用對象屬性作爲值的方法截然不同,由於 對象的屬性會被強制轉換爲字符串類型。
- Map建立也是使用Map構造函數
- 向Map存儲鍵值對使用set(key, value);方法
- 可使用get(key),來獲取指定key對應的value
<script type="text/javascript"> var map = new Map(); map.set("a", "lisi"); map.set("b", "zhangsan"); map.set("b", "zhangsan222"); // 第二次添加,新的value會替換掉舊的 console.log(map.get("a")); console.log(map.get("b")); //zhangsan222 console.log(map.get("c")); //undefined.若是key不存在,則返回undefined console.log(map.size); //2 </script>
建立Map的時候也能夠像Set同樣傳入數組。可是傳入的數組中必須有兩個元素,這個兩個元素分別是一個數組。
也就是傳入的實際是一個二維數組!
<script type="text/javascript"> //map接受一個二維數組 var map = new Map([ //每個數組中,第一個是是map的能夠,第二個是map的value。若是隻有第一個,則值是undefined ["name", "lisi"], ["age", 20], ["sex", "nan"] ]); console.log(map.size); console.log(map.get("name")) </script>
<script type="text/javascript"> var map = new Map([ ["name", "李四"], ["age", 20], ["sex", "nan"] ]); /* 回調函數有函數: 參數1:鍵值對的value 參數2:鍵值對的key 參數3:map對象自己 */ map.forEach(function (value, key, ownMap) { console.log(`key=${key} ,vlue=${value}`); console.log(this); }) </script>
var colors = ["red", "green", "blue"]; for (var i = 0, len = colors.length; i < len; i++) { console.log(colors[i]); }
上面的代碼寫起來簡單,可是實際使用的過程當中,咱們需求本身去控制變量,若是有嵌套的狀況下,還要控制多個變量,很容易出錯。
迭代器就是爲了解決這個問題的。
迭代器只是帶有特殊接口(方法)的對象。全部迭代器對象都帶有 next() 方法並返回一個包含兩個屬性的結果對象。這些屬性分別是 value 和 done,前者表明下一個位置的值,後者在沒有更多值可供迭代的時候爲 true 。迭代器帶有一個內部指針,來指向集合中某個值的位置。當 next() 方法調用後,指針下一位置的值會被返回。
若你在末尾的值被返回以後繼續調用 next(),那麼返回的 done 屬性值爲 true,value 的值則由迭代器設定。該值並不屬於數據集,而是專門爲數據關聯的附加信息,如若該信息並未指定則返回 undefined 。迭代器返回的值和函數返回值有些相似,由於二者都是返回給調用者信息的最終手段。
咱們能夠用ES5以前的知識手動建立一個迭代器:
function createIterator(items) { var i = 0; return { next: function() { var done = (i >= items.length); var value = !done ? items[i++] : undefined; return { done: done, value: value }; } }; } //建立一個能夠在指定數組上面迭代的迭代器對象。 var iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: 3, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }" // for all further calls console.log(iterator.next()); // "{ value: undefined, done: true }"
從以上的示例來看,根據 ECMAScript 6 規範模擬實現的迭代器仍是有些複雜。
幸運的是,ECMAScript 6 還提供了生成器,使得迭代器對象的建立容易了許多。
生成器函數就是返回迭代器的函數!
生成器函數由 function 關鍵字和以後的星號(*)標識,同時還能使用新的 yield 關鍵字。
看下面代碼:
<script type="text/javascript"> //生成器函數。 注意中間的 * 不能丟 function * createIterator() { //每一個yield的後面的值表示咱們迭代到的值。 yield也定義了咱們迭代的順序。 yield 3; yield 4; yield 2; } var it = createIterator(); console.log(it.next().value); // 2 console.log(it.next().value); // 4 console.log(it.next().value); // 2 console.log(it.next().value); //undefined </script>
迭代器函數也是函數,因此他能夠像正常的函數同樣調用,可是生成器函數會自動返回一個迭代器對象。
每調用一次迭代器的next方法,若是碰到yield都會返回一個迭代到的一個對象,而後中止繼續執行,直到下次調用next方法,會從上次中止的地方繼續執行。
//這個迭代器函數返回的迭代器能夠迭代傳入的數組中的全部元素。 function *createIterator(items) { for (let i = 0; i < items.length; i++) { //每調用一次next,碰到yild程序就會中止,並返回迭代到的對象 {value : items[i], done : true} yield items[i]; } } let iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: 3, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }" // 進一步調用 console.log(iterator.next()); // "{ value: undefined, done: true }"
注意:
你可使用函數表達式來建立生成器,只需在 function 關鍵字和圓括號之間添加星號(*)。例如:
let createIterator = function *(items) { for (let i = 0; i < items.length; i++) { yield items[i]; } }; let iterator = createIterator([1, 2, 3]); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: 2, done: false }" console.log(iterator.next()); // "{ value: 3, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }" // 進一步調用 console.log(iterator.next()); // "{ value: undefined, done: true }"
注意:沒法使用箭頭函數來建立生成器。
迭代器的主要工做就是迭代數據,可是不是全部的數據都是能夠迭代的。
與迭代器緊密相關的是,可迭代類型是指那些包含 Symbol.iterator 屬性的對象。
該 symbol 類型定義了返回迭代器的函數。在 ECMAScript 6 中,全部的集合對象(數組,set 和 map)與字符串都是可迭代類型,所以它們都有默認的迭代器。可迭代類型是爲了 ECMAScript6 新添加的 for-of 循環而設計的。
換句話說,默認狀況下只有 數組、set、Map和字符串纔可使用迭代器去迭代。 (也就可使用for…of了)
for…of循環只迭代出來的元素,根本無論索引!無論索引!無論索引!重要的問題重複三遍!
使用 for…of 迭代數組:
<script type="text/javascript"> var arr = ["a", "c", "b", "d"]; for(var item of arr){ console.log(item) } </script>
使用 for…of 迭代Set:
<script type="text/javascript"> var set = new Set(["a", "c", "b", "d"]); for(var item of set){ console.log(item) } </script>
使用 for…of 迭代Map:
<script type="text/javascript"> var map = new Map([["name", "lisi"],["sex", "男"],["age", 20]]); map.set("aaa", "bbb") for(var item of map){ console.log(item); //注意:這裏迭代到的是由key和value組成的數組。 } </script>
使用for … of迭代字符串
<script type="text/javascript"> var s = "abcd"; for(let c of s){ console.log(c) } </script>
注意:for…of 只能迭代能夠迭代的對象,對於非可迭代對象使用for…of會拋出異常
說明:以數組爲例。
for-of 循環首先會調用 values 數組的 Symbol.iterator 方法來獲取迭代器(Symbol.iterator 方法由幕後的 JavaScript 引擎調用)。以後再調用 iterator.next() 並將結果對象中的 value 屬性值,即 1,2,3,依次賦給 num 變量。當檢測到結果對象中的 done 爲 true,循環會退出,因此 num 不會被賦值爲 undefined 。
若是你只想簡單的迭代數組或集合中的元素,那麼 for-of 循環比 for 要更好。for-of 通常不容易出錯,由於要追蹤的條件更少。因此仍是把 for 循環留給複雜控制條件的需求吧。
Symbol.iterator是可迭代類型的一個方法,調用這個方法就能夠獲取到他的默認迭代器。
<script type="text/javascript"> let s = "abcd"; let it = s[Symbol.iterator](); //調用字符串的Symbol.iterator方法 console.log(it.next()); //返回迭代器迭代到的第一個對象 </script>
由於Symbol能夠返回一個對象的默認迭代器,因此咱們可使用它來判斷一個對象是否可迭代
<script type="text/javascript"> function isIterable(object) { return typeof object[Symbol.iterator] === "function"; } console.log(isIterable([1, 2, 3])); // true console.log(isIterable("Hello")); // true console.log(isIterable(new Map())); // true console.log(isIterable(new Set())); // true console.log(isIterable({"name":"李四"})); // false。普通對象不可迭代 </script>
開發者自定義的對象默認是不可迭代類型,可是你能夠爲它們建立 Symbol.iterator 屬性並指定一個生成器來使這個對象可迭代。例如:
let collection = { items: [], *[Symbol.iterator]() { for (let item of this.items) { yield item; } } }; collection.items.push(1); collection.items.push(2); collection.items.push(3); for (let x of collection) { console.log(x); }
和大多數面向對象的語言(object-oriented programming language)不一樣,JavaScript 在誕生之初並不支持使用類和傳統的類繼承並做爲主要的定義方式來建立類似或關聯的對象。
這很令開發者困惑,並且在早於 ECMAScript 1 到 ECMAScript 5 這段時期,不少庫都建立了一些實用工具(utility)來讓 JavaScript 從表層上支持類。
儘管一些 JavaScript 開發者強烈主張該語言不須要類,但因爲大量的庫都對類作了實現,ECMAScript 6 也順勢將其引入。
在 ECMAScript 5 或更早的版本中,JavaScript 沒有類。和類這個概念及行爲最接近的是建立一個構造函數並在構造函數的原型上添加方法,這種實現也被稱爲自定義的類型建立,例如:
function PersonType(name) { this.name = name; } PersonType.prototype.sayName = function() { console.log(this.name); }; let person = new PersonType("Nicholas"); person.sayName(); // 輸出 "Nicholas" console.log(person instanceof PersonType); // true console.log(person instanceof Object); // true
說明:
前面的PersonType咱們之前一直叫作構造函數,其實他就是一個類型,由於他確實表示了一種類型。
在ES6直接借鑑其餘語言,引入了類的概念。因此再實現上面那種模擬 的類就容易了不少。
//class關鍵字必須是小寫。 後面就是跟的類名 class PersonClass { // 等效於 PersonType 構造函數。 constructor(name) { //這個表示類的構造函數。constuctor也是關鍵字必須小寫。 this.name = name; //建立屬性。 也叫當前類型的自有屬性。 } // 等效於 PersonType.prototype.sayName. 這裏的sayName使用了咱們前面的簡寫的方式。 sayName() { console.log(this.name); } } let person = new PersonClass("Nicholas"); person.sayName(); // 輸出 "Nicholas" console.log(person instanceof PersonClass); // true console.log(person instanceof Object); // true console.log(typeof PersonClass); // "function" console.log(typeof PersonClass.prototype.sayName); // "function"
說明:
雖然類和之前的使用構造函數+原型的方式很像,可是仍是有一些不太相同的地方,並且要牢記
函數有函數表達式,類也有類表達式。
類表達式的功能和前面的類的聲明是同樣的。
let PersonClass = class { // 等效於 PersonType 構造函數 constructor(name) { this.name = name; } // 等效於 PersonType.prototype.sayName sayName() { console.log(this.name); } }; let person = new PersonClass("Nicholas"); person.sayName(); // 輸出 "Nicholas" console.log(person instanceof PersonClass); // true console.log(person instanceof Object); // true console.log(typeof PersonClass); // "function" console.log(typeof PersonClass.prototype.sayName); // "function"
let PersonClass = class PersonClass2{ // 等效於 PersonType 構造函數 constructor(name) { this.name = name; } // 等效於 PersonType.prototype.sayName sayName() { console.log(this.name); } };
注意:具名類表達式中PersonClass2這個類名只能在類的內部訪問到,在外面是訪問不到的.
在JavaScript中,函數是做爲一等公民存在的。(也叫一等函數)。
類也是一等公民。
function createObject(classDef) { return new classDef(); } let obj = createObject(class { sayHi() { console.log("Hi!"); } }); obj.sayHi(); // "Hi!"
let person = new class { constructor(name) { this.name = name; } sayName() { console.log(this.name); } }("Nicholas"); person.sayName(); // "Nicholas"
類的成員,也能夠像咱們前面的對象的屬性同樣能夠動態計算.( 使用[ ] 來計算)
let methodName = "sayName"; class PersonClass { constructor(name) { this.name = name; } [methodName]() { console.log(this.name); } } let me = new PersonClass("Nicholas"); me.sayName(); // "Nicholas"
在ES5中,咱們能夠直接給構造函數添加屬性或方法來模擬靜態成員。
function PersonType(name) { this.name = name; } // 靜態方法。 直接添加到構造方法上。 (實際上是把構造函數當作一個普通的對象來用。) PersonType.create = function(name) { return new PersonType(name); }; // 實例方法 PersonType.prototype.sayName = function() { console.log(this.name); }; var person = PersonType.create("Nicholas");
在上面的create方法在其餘語言中通常都是做爲靜態方法來使用的。
下面高能,請注意:
ECMAScript 6 的類經過在方法以前使用正式的 static 關鍵字簡化了靜態方法的建立。例如,下例中的類和上例相比是等效的:
class PersonClass { // 等效於 PersonType 構造函數 constructor(name) { this.name = name; } // 等效於 PersonType.prototype.sayName sayName() { console.log(this.name); } // 等效於 PersonType.create。 static create(name) { return new PersonClass(name); } } let person = PersonClass.create("Nicholas");
注意:靜態成員經過實例對象不能訪問,只能經過類名訪問!!!
經過和ES5模擬靜態方法的例子你應該知道爲啥了吧
在ES6以前要完成繼承,須要寫不少的代碼。看下面的繼承的例子:
<script type="text/javascript"> function Father(name) { this.name = name; } Father.prototype.sayName = function () { console.log(this.name); } function Son(name,age) { Father.call(this, name); this.age = age; } Son.prototype = new Father(); Son.prototype.constructor = Son; Son.prototype.sayAge = function () { console.log(this.age); } var son1 = new Son("兒子", 20); son1.sayAge(); //20 son1.sayName(); //兒子 </script>
若是在ES6經過類的方式完成繼承就簡單了不少。
須要用到一個新的關鍵字:extends
<script type="text/javascript"> class Father{ constructor(name){ this.name = name; } sayName(){ console.log(this.name); } } class Son extends Father{ //extents後面跟表示要繼承的類型 constructor(name, age){ super(name); //至關於之前的:Father.call(this, name); this.age = age; } //子類獨有的方法 sayAge(){ console.log(this.age); } } var son1 = new Son("李四", 30); son1.sayAge(); son1.sayName(); console.log(son1 instanceof Son); // true console.log(son1 instanceof Father); //true </script>
這種繼承方法,和咱們前面提到的構造函數+原型的繼承方式本質是同樣的。可是寫起來更簡單,可讀性也更好。
關於super的使用,有幾點須要注意:
若是在子類中聲明與父類中的同名的方法,則會覆蓋父類的方法。(這種狀況在其餘語言中稱之爲 方法的覆寫、重寫 )
<script type="text/javascript"> class Father{ constructor(name){ this.name = name; } sayName(){ console.log(this.name); } } class Son extends Father{ //extents後面跟表示要繼承的類型 constructor(name, age){ super(name); //至關於之前的:Father.call(this, name); this.age = age; } //子類獨有的方法 sayAge(){ console.log(this.age); } //子類中的方法會屏蔽到父類中的同名方法。 sayName(){ super.syaName(); //調用被覆蓋的父類中的方法。 console.log("我是子類的方法,我屏蔽了父類:" + name); } } var son1 = new Son("李四", 30); son1.sayAge(); son1.sayName(); </script>
若是在子類中又確實須要調用父類中被覆蓋的方法,能夠經過super.方法()來完成。
注意:
- 若是是調用構造方法,則super不要加點,並且必須是在子類構造方法的第一行調用父類的構造方法
- 普通方法調用須要使用super.父類的方法() 來調用。
原文地址:http://blog.csdn.net/u012468376/article/details/54565068<script type="text/javascript"> class Father{ static foo(){ console.log("我是父類的靜態方法"); } } class Son extends Father{ } Son.foo(); //子類也繼承了父類的靜態方法。 這種方式調用和直接經過父類名調用時同樣的。 </script>