深刻理解ES6筆記(四)擴展的對象功能

主要知識點有對象類別、屬性速記法、方法簡寫、需計算屬性名、Object.is()方法、Object.assign()方法、可容許重複的屬性、自有屬性的枚舉順序、Object.setPrototypeOf()方法、super引用、方法定義
圖片描述

《深刻理解ES6》筆記 目錄java

對象類別

  • 普通對象:擁有 JS 對象全部默認的內部行爲。
  • 奇異對象:其內部行爲在某些方面有別於默認行爲。
  • 標準對象:在 ES6 中被定義的對象,例如 Array 、 Date ,等等。標準對象能夠是普通的,也能夠是奇異的。
  • 內置對象:在腳本開始運行時由 JS 運行環境提供的對象。全部的標準對象都是內置對象。

對象字面量語法的擴展

屬性初始化器的速記法

屬性初始化器的速記法能夠用來消除屬性名和本地變量的重複狀況,可使用做用域內的變量值賦值給同名屬性:segmentfault

ES5 及更早版本中:函數

function createPerson(name, age) {
    return {
        name: name,
        age: age
    };
}

ES6簡寫:this

function createPerson(name, age) {
    return {
        name,
        age
    };
}

方法簡寫

在對象字面量的寫法中,爲一個對象添加一個方法,須要指定對象的屬性以及具體的函數聲明。ES6提供了一種方法簡寫語法,經過省略function關鍵字,可以讓爲對象添加方法的語法更加簡潔。
ES5中的寫法:spa

var person = {
    name: "Nicholas",
    sayName: function() {
        console.log(this.name);
    }
};

ES6簡寫語法:3d

var person = {
    name: "Nicholas",
    sayName() {
        console.log(this.name);
    }
};

需計算屬性名

需計算屬性名規則容許對象字面量中屬性名是變量、字符串字面量或者由變量計算而得的,具體寫法是經過方括號[]包含屬性名。code

var lastName = "last name";
var person = {
    "first name": "Nicholas",
    [lastName]: "Zakas"
};
console.log(person["first name"]); // "Nicholas"
console.log(person[lastName]); // "Zakas"

var suffix = " name";
var person = {
    ["first" + suffix]: "Nicholas",
    ["last" + suffix]: "Zakas"
};
console.log(person["first name"]); // "Nicholas"
console.log(person["last name"]); // "Zakas"

const id = 5
const obj = {
  [`my-${id}`]: id
}
console.log(obj['my-5']) // 5

新的方法:Object.is()和Object.assign()

Object.is() 方法

JS中比較兩個值是否相同的時候會使用嚴格等於運算符===,可是,使用嚴格運算符式,+0和-0會認爲這二者是相等的,而NaN===NaN是不相等的,使用Object.is()方法來判斷這二者狀況與使用嚴格相等符會有所不一樣,其餘狀況和使用嚴格相等運算符基本一致;對象

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.assign() 方法

混入( Mixin )是在 JS 中組合對象時最流行的模式。在一次混入中,一個對象會從另外一個對象中接收屬性與方法。
下面是一個mixin方法的實現,這個方法實現的是淺拷貝。將b對象的屬性拷貝到了a對象,合併成一個新的對象。blog

//mixin不僅有這一種實現方法。
function mixin(receiver, supplier) {
  Object.keys(supplier).forEach((key) => {
    receiver[key] = supplier[key]
  })
  return receiver
}

let a = {name: 'sb'};
let b = {
  c: {
    d: 5
    }
  }
console.log(mixin(a, b)) // {"name":"sb","c":{"d":5}}

寫這樣一個mixin方法是否是很煩,並且每一個項目都得引入這個方法,如今,ES6給咱們提供了一個現成的方法Object.assign()來作mixin的事情。圖片

假設要實現上面的mixin方法,你只須要給Object.assign()傳入參數便可。

console.log(Object.assign(a, b))// {"name":"sb","c":{"d":5}}

重複的對象字面量屬性

ES5 嚴格模式爲重複的對象字面量屬性引入了一個檢查,若找到重複的屬性名,就會拋出錯誤。

"use strict";
var person = {
    name: "Nicholas",
    name: "Greg" // 在 ES5 嚴格模式中是語法錯誤
};

ES6 移除了重複屬性的檢查,嚴格模式與非嚴格模式都再也不檢查重複的屬性。當存在重複屬性時,排在後面的屬性的值會成爲該屬性的實際值:

"use strict";
var person = {
    name: "Nicholas",
    name: "Greg" // 在 ES6 嚴格模式中不會出錯
};
console.log(person.name); // "Greg"

自有屬性的枚舉順序

ES5 並無定義對象屬性的枚舉順序,而是把該問題留給了 JS 引擎廠商。而 ES6 則嚴格定義了對象自有屬性在被枚舉時返回的順序。這對 Object.getOwnPropertyNames() 與Reflect.ownKeys)如何返回屬性形成了影響,還一樣影響了Object.assign() 處理屬性的順序。
自有屬性枚舉時基本順序以下:

  1. 全部的數字類型鍵,按升序排列。
  2. 全部的字符串類型鍵,按被添加到對象的順序排列。
  3. 全部的符號類型(詳見第六章)鍵,也按添加順序排列。
const state = {
  id: 1,
  5: 5,
  name: "eryue",
  3: 3
}

Object.getOwnPropertyNames(state) 
//["3","5","id","name"] 枚舉key

Object.assign(state, null)
//{"3":3,"5":5,"id":1,"name":"eryue"}
for-in 循環的枚舉順序仍未被明確規定,由於並不是全部的 JS 引擎都採用相同的方式。而 Object.keys() 和 JSON.stringify() 也使用了與 for-in 同樣的枚舉順序。

更強大的原型

通常來講,對象的原型會在經過構造器或 Object.create() 方法建立該對象時被指定。
ES5 添加了 Object.getPrototypeOf() 方法來從任意指定對象中獲取其原型;
缺乏在初始化以後更改對象原型的標準方法。

修改對象的原型

ES6 經過添加 Object.setPrototypeOf() 方法而改變了這種假定,此方法容許你修改任意指定對象的原型。它接受兩個參數:須要被修改原型的對象,以及將會成爲前者原型的對象

let person = {
    getGreeting() {
        return "Hello";
    }
};
let dog = {
    getGreeting() {
        return "Woof";
    }
};
// 原型爲 person
let friend = Object.create(person);
console.log(friend.getGreeting()); // "Hello"
console.log(Object.getPrototypeOf(friend) === person); // true
// 將原型設置爲 dog
Object.setPrototypeOf(friend, dog);
console.log(friend.getGreeting()); // "Woof"
console.log(Object.getPrototypeOf(friend) === dog); // true

使用 super 引用的簡單原型訪問

可以使用super引用,來訪問原型中的方法:

const proto = {
  foo: 'hello'
};

const obj = {
  foo: 'world',
  find() {
    return super.foo;
  }
};

Object.setPrototypeOf(obj, proto);
obj.find() // "hello"

注意,super關鍵字表示原型對象時,只能用在對象的簡寫方法之中,用在其餘地方都會報錯。

// 報錯
const obj = {
  foo: super.foo
}

// 報錯
const obj = {
  foo: () => super.foo
}

// 報錯
const obj = {
  foo: function () {
    return super.foo
  }
}

javaScript 引擎內部,super.foo等同於Object.getPrototypeOf(this).foo(屬性)或Object.getPrototypeOf(this).foo.call(this)(方法)。

const proto = {
  x: 'hello',
  foo() {
    console.log(this.x);
  },
};

const obj = {
  x: 'world',
  foo() {
    super.foo();
  }
}

Object.setPrototypeOf(obj, proto);

obj.foo() // "world"

上面代碼中,super.foo指向原型對象proto的foo方法,可是綁定的this卻仍是當前對象obj,所以輸出的就是world。

正式的「方法」定義

ES6 則正式作出了定義:方法是一個擁有 [[HomeObject]] 內部屬性的函數,此內部屬性指向該方法所屬的對象。

let person = {
// 方法
    getGreeting() {
        return "Hello";
    }
};
// 並不是方法
function shareGreeting() {
    return "Hi!";
}

大多數狀況下,這種差別並不重要,然而使用 super 引用時就徹底不一樣了。

let person = {
    getGreeting() {
        return "Hello";
    }
};
// 原型爲 person
let friend = {
    getGreeting() {
        return super.getGreeting() + ", hi!";
    }
};
Object.setPrototypeOf(friend, person);
console.log(friend.getGreeting()); // "Hello, hi!"
相關文章
相關標籤/搜索