ECMAScript 6教程 (二) 對象和函數



對象

屬性的簡潔表示法


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屬性


proto屬性,用來讀取或設置當前對象的prototype對象。該屬性一度被正式寫入ES6草案,但後來又被移除。目前,全部瀏覽器(包括IE11)都部署了這個屬性。

複製代碼

// es6的寫法
 var obj = {
__proto__: someOtherObj,
method: function() { ... }
} 
// es5的寫法
 var obj = Object.create(someOtherObj);
obj.method = function() { ... }

複製代碼

Symbol類型


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參數


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),即只保留內層函數的調用幀,這樣能夠節省內存。

標籤: nodejses6javascriptangularjs

相關文章
相關標籤/搜索