ES6中對象的擴展

1. 屬性的簡潔表示法

2. 屬性名錶達式

3. 方法的 name 屬性

4. 屬性的可枚舉性和遍歷

5. super 關鍵字

6. 對象的擴展運算符

ES6對對象進行了重大的升級,具體包括下面:

1. 屬性的簡潔表示法
const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}

// 等同於
const baz = {foo: foo};

屬性名就是變量名, 屬性值就是變量值。

//ES6
function f(x, y) {
  return {x, y};
}

// 等同於 ES5

function f(x, y) {
  return {x: x, y: y};
}

f(1, 2) // Object {x: 1, y: 2}

對象的方法的簡寫

// ES5
let obj = {
	method:(){
		return "你好!"
    }
}

//ES6
let obj = {
	method(){
		return "你好!"
    }
}
2. 屬性名錶達式
// 方法一
obj.foo = true;

// 方法二
obj['a' + 'bc'] = 123;

方法一是直接用標識符作爲屬性名,方法二是用表達式作爲屬性名,此時要將表達式放在方括號內。

var obj = {
  foo: true,
  abc: 123
};
//如果使用字面量方式定義對象(使用大括號),在 ES5 中只能使用方法一(標識符)定義屬性

let propKey = 'foo';

let obj = {
  [propKey]: true,
  ['a' + 'bc']: 123
};
//ES6 允許字面量定義對象時,用方法二(表達式)作爲對象的屬性名,即把表達式放在方括號內。

屬性名與簡潔表示法不能同時使用,否則會報錯。

// 報錯
const foo = 'bar';
const bar = 'abc';
const baz = { [foo] };

// 正確
const foo = 'bar';
const baz = { [foo]: 'abc'};

當屬性名錶達式是一個對象時,默認情況下會自動將對象轉爲字符串[object Object]

const keyA = {a: 1};
const keyB = {b: 2};

const myObject = {
  [keyA]: 'valueA',
  [keyB]: 'valueB'
};
myObject // Object {[object Object]: "valueB"}

//[keyA]和[keyB]得到的都是[object Object],所以[keyB]會把[keyA]覆蓋掉,而myObject最後只有一個[object Object]屬性
3. 對象方法的 name 屬性
let obj = {
	method(){
		console.log("你好!")
	}
}
console.log(obj.method.name) //method
//方法的name屬性返回函數名(即方法名

bind方法創造的函數,name屬性返回bound加上原函數的名字;Function構造函數創造的函數,name屬性返回anonymous。

(new Function()).name // "anonymous"

var doSomething = function() {
  // ...
};
doSomething.bind().name // "bound doSomething"

如果對象的方法是一個 Symbol 值,那麼name屬性返回的是這個 Symbol 值的描述。

const key1 = Symbol('description');
const key2 = Symbol();
let obj = {
  [key1]() {},
  [key2]() {},
};
obj[key1].name // "[description]"
obj[key2].name // ""

// key1對應的 Symbol 值有描述,key2沒有。
4. 屬性的可枚舉性和遍歷

可枚舉性
對象的每個屬性都有一個描述對象(Descriptor),用來控制該屬性的行爲。Object.getOwnPropertyDescriptor方法可以獲取該屬性的描述對象。

let obj = { foo: 123 };
console.log(Object.getOwnPropertyDescriptor(obj, 'foo'));
// {value: 123, writable: true, enumerable: true, configurable: true}

描述對象的enumerable屬性,稱爲“可枚舉性”,如果該屬性爲false,就表示某些操作會忽略當前屬性。

下列四種會忽略enumerable爲false的屬性:

  • for...in循環:只遍歷對象自身的和繼承的可枚舉的屬性。
  • Object.keys():返回對象自身的所有可枚舉的屬性的鍵名。
  • JSON.stringify():只串行化對象自身的可枚舉的屬性。
  • Object.assign(): 忽略enumerable爲false的屬性,只拷貝對象自身的可枚舉的屬性。

這四個操作之中,前三個是 ES5 就有的,最後一個Object.assign()是 ES6 新增的。其中,只有for…in會返回繼承的屬性,其他三個方法都會忽略繼承的屬性,只處理對象自身的屬性。實際上,引入“可枚舉”(enumerable)這個概念的最初目的,就是讓某些屬性可以規避掉for…in操作,不然所有內部屬性和方法都會被遍歷到。比如,對象原型的toString方法,以及數組的length屬性,就通過“可枚舉性”,從而避免被for…in遍歷到。
ES6 規定,所有 Class 的原型的方法都是不可枚舉的。

Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable
// false

屬性的遍歷
ES6 一共有 5 種方法可以遍歷對象的屬性。

  • for...in循環遍歷對象自身的和繼承的可枚舉屬性(不含 Symbol 屬性)。
  • Object.keys(obj)返回一個數組,包括對象自身的(不含繼承的)所有可枚舉屬性(不含 Symbol 屬性)的鍵名。
  • Object.getOwnPropertyNames(obj)返回一個數組,包含對象自身的所有屬性(不含 Symbol
    屬性,但是包括不可枚舉屬性)的鍵名。
  • Object.getOwnPropertySymbols(obj)返回一個數組,包含對象自身的所有 Symbol 屬性的鍵名。
  • Reflect.ownKeys(obj)返回一個數組,包含對象自身的所有鍵名,不管鍵名是 Symbol 或字符串,也不管是否可枚舉。
    這5種方法遍歷對象的鍵名遵循:首先遍歷所有數值鍵,按照數值升序排列。其次遍歷所有字符串鍵,按照加入時間升序排列。最後遍歷所有 Symbol 鍵,按照加入時間升序排列的規則
5. super 關鍵字

this 關鍵字指向函數所在的對象,ES6 新增的關鍵字 super 指向當前對象的原型對象。

const proto = {
  foo: 'hello'
};

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

Object.setPrototypeOf(obj, proto);
obj.find() // "hello"
//對象obj.find()方法之中,通過super.foo引用了原型對象proto的foo屬性。

super關鍵字表示原型對象時,只能用在對象的方法之中,用在其他地方都會報錯,只有對象方法的簡寫法可以讓 JavaScript 引擎確認,定義的是對象的方法。

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。

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

6. 對象的擴展運算符

解構賦值

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }

擴展運算符
對象的擴展運算符(…)用於取出參數對象的所有可遍歷屬性,拷貝到當前對象之中。

let z = { a: 3, b: 4 };
let n = { ...z };
n // { a: 3, b: 4 }

對象的擴展運算符等同於使用Object.assign()方法。

let aClone = { ...a };
// 等同於
let aClone = Object.assign({}, a);

上面只是拷貝了對象實例的屬性,如果想完整克隆一個對象,還拷貝對象原型的屬性,可以採用下面的寫法。

// 寫法一
const clone1 = {
  __proto__: Object.getPrototypeOf(obj),
  ...obj
};
//__proto__屬性在非瀏覽器的環境不一定部署。一般不使用該方法。

// 寫法二
const clone2 = Object.assign(
  Object.create(Object.getPrototypeOf(obj)),
  obj
);

// 寫法三
const clone3 = Object.create(
  Object.getPrototypeOf(obj),
  Object.getOwnPropertyDescriptors(obj)
)

擴展運算符可以用於合併兩個對象。

let ab = { ...a, ...b };
// 等同於
let ab = Object.assign({}, a, b);

擴展運算符的參數對象之中,如果有取值函數get,這個函數是會執行的。

// 並不會拋出錯誤,因爲 x 屬性只是被定義,但沒執行
let aWithXGetter = {
  ...a,
  get x() {
    throw new Error('not throw yet');
  }
};

// 會拋出錯誤,因爲 x 屬性被執行了
let runtimeError = {
  ...a,
  ...{
    get x() {
      throw new Error('throw now');
    }
  }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章