本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出 原文鏈接,博客地址爲 http://www.cnblogs.com/jasonnode/ 。該系列課程是匯智網 整理編寫的,課程地址爲 http://www.dwz.cn/3e6Ymlnode
ES6容許直接寫入變量和函數,做爲對象的屬性和方法。這樣的書寫更加簡潔。es6
function f( x, y ) { return { x, y }; } // 等同於 function f( x, y ) { return { x: x, y: y }; }
示例:數組
var Person = { name: '張三', birth:'1990-01-01', // 等同於hello: function ()... hello() { document.write('個人名字是', this.name); } }; Person.hello();
這種寫法用於函數的返回值,將會很是方便。瀏覽器
function getPoint() { var x = 1; var y = 10; return {x, y}; } getPoint() // {x:1, y:10}
JavaScript語言定義對象的屬性,有兩種方法。app
let obj = {}; // 方法一 obj.foo = true; // 方法二 obj['a'+'bc'] = 123; document.write(obj);
上面代碼的方法一是直接用標識符做爲屬性名,方法二是用表達式做爲屬性名,這時要將表達式放在方括號以內。函數
若是使用字面量方式定義對象(使用大括號),在ES5中只能使用方法一(標識符)定義屬性。工具
var obj = { foo: true, abc: 123 };
ES6容許字面量定義對象時,用方法二(表達式)做爲對象的屬性名,即把表達式放在方括號內。優化
let propKey = 'foo'; let obj = { [propKey]: true, ['a'+'bc']: 123 };
表達式還能夠用於定義方法名。this
let obj = { ['h'+'ello']() { return 'hi'; } }; document.write(obj.hello()); // hi
Object.is()用來比較兩個值是否嚴格相等。它與嚴格比較運算符(===)的行爲基本一致,不一樣之處只有兩個:一是+0不等於-0,二是NaN等於自身。es5
+0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // true
Object.assign方法用來將源對象(source)的全部可枚舉屬性,複製到目標對象(target)。它至少須要兩個對象做爲參數,第一個參數是目標對象,後面的參數都是源對象。只要有一個參數不是對象,就會拋出TypeError錯誤。
var target = { a: 1 }; var source1 = { b: 2 }; var source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
注意,若是目標對象與源對象有同名屬性,或多個源對象有同名屬性,則後面的屬性會覆蓋前面的屬性。
var target = { a: 1, b: 1 }; var source1 = { b: 2, c: 2 }; var source2 = { c: 3 }; Object.assign(target, source1, source2); target // {a:1, b:2, c:3}
proto屬性,用來讀取或設置當前對象的prototype對象。該屬性一度被正式寫入ES6草案,但後來又被移除。目前,全部瀏覽器(包括IE11)都部署了這個屬性。
// es6的寫法 var obj = { __proto__: someOtherObj, method: function() { ... } } // es5的寫法 var obj = Object.create(someOtherObj); obj.method = function() { ... }
ES6引入了一種新的原始數據類型Symbol,表示獨一無二的ID。凡是屬性名屬於Symbol類型,就都是獨一無二的,能夠保證不會與其餘屬性名產生衝突。
let s = Symbol(); typeof s // "symbol"
typeof運算符的結果,代表變量s是Symbol數據類型,而不是字符串之類的其餘類型。
注意,Symbol函數前不能使用new命令,不然會報錯。這是由於生成的Symbol是一個原始類型的值,不是對象。
Symbol類型的值不能與其餘類型的值進行運算,會報錯。
var sym = Symbol('My symbol'); "your symbol is " + sym // TypeError: can't convert symbol to string `your symbol is ${sym}` // TypeError: can't convert symbol to string
可是,Symbol類型的值能夠轉爲字符串。
var sym = Symbol('My symbol'); String(sym) // 'Symbol(My symbol)' sym.toString() // 'Symbol(My symbol)'
Proxy 內置的一個代理工具,使用他能夠在對象處理上加一層屏障:
S6原生提供Proxy構造函數,用來生成Proxy實例。
var proxy = new Proxy(target, handler) new Proxy()表示生成一個Proxy實例,它的target參數表示所要攔截的目標對象,handler參數也是一個對象,用來定製攔截行爲。 var plain = { name : "hubwiz" }; var proxy = new Proxy(plain, { get: function(target, property) { return property in target ? target[property] : "匯智網"; } }); proxy.name // "hubwiz" proxy.title // "匯智網"
Proxy(target, handler), 這裏的 handler有以下的方法:
若是目標對象是函數,那麼還有兩種額外操做能夠攔截。
如今能夠在定義函數的時候指定參數的默認值了,而不用像之前那樣經過邏輯或操做符來達到目的了。
function sayHello(name){ //傳統的指定默認參數的方式 var name = name||'hubwiz'; document.write('Hello '+name); } //運用ES6的默認參數 function sayHello2(name='hubwiz'){ document.write(`Hello ${name}`); } sayHello(); //輸出:Hello hubwiz sayHello('匯智網'); //輸出:Hello 匯智網 sayHello2(); //輸出:Hello hubwiz sayHello2('匯智網'); //輸出:Hello 匯智網
rest參數(形式爲「...變量名」)能夠稱爲不定參數,用於獲取函數的多餘參數,這樣就不須要使用arguments對象了。
rest參數搭配的變量是一個數組,該變量將多餘的參數放入數組中。
function add(...values) { let sum = 0; for (var val of values) { sum += val; } return sum; } add(1, 2, 3) // 6
不定參數的格式是三個句點後跟表明全部不定參數的變量名。好比以上示例中,...values 表明了全部傳入add函數的參數。
擴展運算符(spread)是三個點(...)。它比如rest參數的逆運算,將一個數組轉爲用逗號分隔的參數序列。該運算符主要用於函數調用。
它容許傳遞數組或者類數組直接作爲函數的參數而不用經過apply。
var people=['張三','李四','王五']; //sayHello函數原本接收三個單獨的參數people1,people2和people3 function sayHello(people1,people2,people3){ document.write(`Hello ${people1},${people2},${people3}`); } //可是咱們將一個數組以拓展參數的形式傳遞,它能很好地映射到每一個單獨的參數 sayHello(...people); //輸出:Hello 張三,李四,王五 //而在之前,若是須要傳遞數組當參數,咱們須要使用函數的apply方法 sayHello.apply(null,people); //輸出:Hello 張三,李四,王五
箭頭函數是使用=>語法的函數簡寫形式。這在語法上與 C#、Java 8 和 CoffeeScript 的相關特性很是類似。
var array = [1, 2, 3]; //傳統寫法 array.forEach(function(v, i, a) { document.write(v); }); //ES6 array.forEach(v => document.write(v));
它們同時支持表達式體和語句體。與(普通的)函數所不一樣的是,箭頭函數和其上下文中的代碼共享同一個具備詞法做用域的this。
var evens = [1,2,3,4,5]; var fives = []; // 表達式體 var odds = evens.map(v => v + 1); var nums = evens.map((v, i) => v + i); var pairs = evens.map(v => ({even: v, odd: v + 1})); // 語句體 nums.forEach(v => { if (v % 5 === 0) fives.push(v); }); document.write(fives); // 具備詞法做用域的 this var bob = { _name: "Bob", _friends: ["Amy", "Bob", "Cinne", "Dylan", "Ellen"], printFriends() { this._friends.forEach(f => document.write(this._name + " knows " + f)); } } bob.printFriends();
上面三點中,第一點尤爲值得注意。this對象的指向是可變的,可是在箭頭函數中,它是固定的。
函數綁定運算符是並排的兩個雙引號(::),雙引號左邊是一個對象,右邊是一個函數。該運算符會自動將左邊的對象,做爲上下文環境(即this對象),綁定到右邊的函數上面。
let log = ::console.log; // 等同於 var log = console.log.bind(console); foo::bar; // 等同於 bar.call(foo); foo::bar(...arguments); i// 等同於 bar.apply(foo, arguments);
尾調用的概念很是簡單,一句話就能說清楚,就是指某個函數的最後一步是調用另外一個函數。
function f(x){ return g(x); }
上面代碼中,函數f的最後一步是調用函數g,這就叫尾調用。
如下三種狀況,都不屬於尾調用。
// 狀況一 function f(x){ let y = g(x); return y; } // 狀況二 function f(x){ return g(x) + 1; } // 狀況三 function f(x){ g(x); }
以上的示例中,狀況1、二是調用函數g以後,有其餘操做。狀況三等同於下面的代碼。
function f(x){ g(x); return undefined; }
尾調用因爲是函數的最後一步操做,因此不須要保留外層函數的調用記錄,由於調用位置、內部變量等信息都不會再用到了,只要直接用內層函數的調用記錄,取代外層函數的調用記錄就能夠了。
function f() { let m = 1; let n = 2; return g(m + n); } f(); // 等同於 function f() { return g(3); } f(); // 等同於 g(3);
上面代碼中,若是函數g不是尾調用,函數f就須要保存內部變量m和n的值、g的調用位置等信息。但因爲調用g以後,函數f就結束了,因此執行到最後一步,徹底能夠刪除 f(x) 的調用幀,只保留g(3) 的調用幀。
「尾調用優化」(Tail call optimization),即只保留內層函數的調用幀,這樣能夠節省內存。