ES6容許直接寫入變量和函數,做爲對象的屬性和方法。這樣的書寫更加簡潔。javascript
function f( x, y ) { return { x, y }; }// 等同於function f( x, y ) { return { x: x, y: y }; }
示例:java
var Person = { name: '張三', birth:'1990-01-01',// 等同於hello: function ()...hello() { document.write('個人名字是', this.name); } }; Person.hello();
這種寫法用於函數的返回值,將會很是方便。node
function getPoint() { var x = 1; var y = 10; return {x, y}; } getPoint() // {x:1, y:10}
JavaScript語言定義對象的屬性,有兩種方法。angularjs
let obj = {};// 方法一obj.foo = true;// 方法二obj['a'+'bc'] = 123; document.write(obj);
上面代碼的方法一是直接用標識符做爲屬性名,方法二是用表達式做爲屬性名,這時要將表達式放在方括號以內。es6
若是使用字面量方式定義對象(使用大括號),在ES5中只能使用方法一(標識符)定義屬性。數組
var obj = { foo: true, abc: 123};
ES6容許字面量定義對象時,用方法二(表達式)做爲對象的屬性名,即把表達式放在方括號內。瀏覽器
let propKey = 'foo'; let obj = { [propKey]: true, ['a'+'bc']: 123};
表達式還能夠用於定義方法名。app
let obj = { ['h'+'ello']() { return 'hi'; } }; document.write(obj.hello()); // hi
Object.is()用來比較兩個值是否嚴格相等。它與嚴格比較運算符(===)的行爲基本一致,不一樣之處只有兩個:一是+0不等於-0,二是NaN等於自身。函數
+0 === -0 //trueNaN === NaN // false Object.is(+0, -0) // falseObject.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有以下的方法:
get(target, propKey, receiver):攔截對象屬性的讀取,好比proxy.foo和proxy['foo'],返回類型不限。最後一個參數receiver可選,當target對象設置了propKey屬性的get函數時,receiver對象會綁定get函數的this對象。
set(target, propKey, value, receiver):攔截對象屬性的設置,好比proxy.foo = v或proxy['foo'] = v,返回一個布爾值。
has(target, propKey):攔截propKey in proxy的操做,返回一個布爾值。
deleteProperty(target, propKey) :攔截delete proxy[propKey]的操做,返回一個布爾值。
enumerate(target):攔截for (var x in proxy),返回一個遍歷器。
hasOwn(target, propKey):攔截proxy.hasOwnProperty('foo'),返回一個布爾值。
ownKeys(target):攔截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy),返回一個數組。該方法返回對象全部自身的屬性,而Object.keys()僅返回對象可遍歷的屬性。
getOwnPropertyDescriptor(target, propKey) :攔截Object.getOwnPropertyDescriptor(proxy, propKey),返回屬性的描述對象。
defineProperty(target, propKey, propDesc):攔截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一個布爾值。
preventExtensions(target):攔截Object.preventExtensions(proxy),返回一個布爾值。
getPrototypeOf(target) :攔截Object.getPrototypeOf(proxy),返回一個對象。
isExtensible(target):攔截Object.isExtensible(proxy),返回一個布爾值。
setPrototypeOf(target, proto):攔截Object.setPrototypeOf(proxy, proto),返回一個布爾值。
若是目標對象是函數,那麼還有兩種額外操做能夠攔截。
apply(target, object, args):攔截Proxy實例做爲函數調用的操做,好比proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。
construct(target, args, proxy):攔截Proxy實例做爲構造函數調用的操做,好比new proxy(...args)。
如今能夠在定義函數的時候指定參數的默認值了,而不用像之前那樣經過邏輯或操做符來達到目的了。
function sayHello(name){//傳統的指定默認參數的方式var name = name||'hubwiz'; document.write('Hello '+name); } //運用ES6的默認參數function sayHello2(name='hubwiz'){ document.write(`Hello ${name}`); } sayHello(); //輸出:Hello hubwizsayHello('匯智網'); //輸出:Hello 匯智網sayHello2(); //輸出:Hello hubwizsayHello2('匯智網'); //輸出: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和people3function 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); });//ES6array.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); // 具備詞法做用域的 thisvar bob = { _name: "Bob", _friends: ["Amy", "Bob", "Cinne", "Dylan", "Ellen"], printFriends() { this._friends.forEach(f => document.write(this._name + " knows " + f)); } } bob.printFriends();
函數體內的this對象,綁定定義時所在的對象,而不是使用時所在的對象。
不能夠看成構造函數,也就是說,不可使用new命令,不然會拋出一個錯誤。
不可使用arguments對象,該對象在函數體內不存在。
上面三點中,第一點尤爲值得注意。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),即只保留內層函數的調用幀,這樣能夠節省內存。
標籤: nodejs, es6, javascript, angularjs