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');
}
}
};