- 屬性的簡潔表示法
- 屬性名表達式
- 方法的 name 屬性
- Object.is()
- Object.assign()
- 屬性的可枚舉性和遍歷
- Object.getOwnPropertyDescriptors()
- proto屬性谭贪,Object.setPrototypeOf()涎拉,Object.getPrototypeOf()
- super 關(guān)鍵字
- Object.keys(),Object.values()伙单,Object.entries()
- 對象的擴展運算符
1屬性的簡潔表示法 § ?
ES6 允許直接寫入變量和函數(shù)瓣履,作為對象的屬性和方法檐薯。這樣的書寫更加簡潔。
const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}
// 等同于
const baz = {foo: foo};
上面代碼表明袍冷,ES6 允許在對象之中磷醋,直接寫變量。這時胡诗,屬性名為變量名, 屬性值為變量的值邓线。下面是另一個例子淌友。
function f(x, y) {
return {x, y};
}
// 等同于
function f(x, y) {
return {x: x, y: y};
}
f(1, 2) // Object {x: 1, y: 2}
let birth = '2000/01/01';
const Person = {
name: '張三',
//等同于birth: birth
birth,
// 等同于hello: function ()...
hello() { console.log('我的名字是', this.name); }
};
2屬性名表達式 § ?
JavaScript 定義對象的屬性,有兩種方法褂痰。
// 方法一
obj.foo = true;
// 方法二
obj['a' + 'bc'] = 123;
上面代碼的方法一是直接用標識符作為屬性名亩进,方法二是用表達式作為屬性名,這時要將表達式放在方括號之內(nèi)缩歪。
let lastWord = 'last word';
const a = {
'first word': 'hello',
[lastWord]: 'world'
};
a['first word'] // "hello"
a[lastWord] // "world"
a['last word'] // "world"
表達式還可以用于定義方法名归薛。
let obj = {
['h' + 'ello']() {
return 'hi';
}
};
obj.hello() // hi
3方法的 name 屬性 § ?
函數(shù)的name屬性,返回函數(shù)名匪蝙。對象方法也是函數(shù)主籍,因此也有name屬性。
const person = {
sayName() {
console.log('hello!');
},
};
person.sayName.name // "sayName"
如果對象的方法使用了取值函數(shù)(getter)和存值函數(shù)(setter)逛球,則name屬性不是在該方法上面千元,而是該方法的屬性的描述對象的get和set屬性上面,返回值是方法名前加上get和set颤绕。
有兩種特殊情況:bind方法創(chuàng)造的函數(shù)幸海,name屬性返回bound加上原函數(shù)的名字;Function構(gòu)造函數(shù)創(chuàng)造的函數(shù)奥务,name屬性返回anonymous物独。
4 Object.is() § ?
ES5 比較兩個值是否相等,只有兩個運算符:相等運算符(==)和嚴格相等運算符(===)氯葬。它們都有缺點挡篓,前者會自動轉(zhuǎn)換數(shù)據(jù)類型,后者的NaN不等于自身帚称,以及+0等于-0官研。JavaScript 缺乏一種運算,在所有環(huán)境中闯睹,只要兩個值是一樣的戏羽,它們就應(yīng)該相等。
ES6 提出“Same-value equality”(同值相等)算法楼吃,用來解決這個問題始花。Object.is就是部署這個算法的新方法。它用來比較兩個值是否嚴格相等所刀,與嚴格比較運算符(===)的行為基本一致。
不同之處只有兩個:一是+0不等于-0捞挥,二是NaN等于自身浮创。
5Object.assign() § ?
Object.assign
方法用于對象的合并,將源對象(source)的所有可枚舉屬性砌函,復(fù)制到目標對象(target)斩披。
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
Object.assign方法的第一個參數(shù)是目標對象溜族,后面的參數(shù)都是源對象。
注意垦沉,如果目標對象與源對象有同名屬性煌抒,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性厕倍。
const target = { a: 1, b: 1 };
const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
注意點 § ?
(1)淺拷貝
Object.assign方法實行的是淺拷貝寡壮,而不是深拷貝。也就是說讹弯,如果源對象某個屬性的值是對象况既,那么目標對象拷貝得到的是這個對象的引用。
const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2
上面代碼中组民,源對象obj1的a屬性的值是一個對象棒仍,Object.assign拷貝得到的是這個對象的引用。這個對象的任何變化臭胜,都會反映到目標對象上面莫其。
(2)同名屬性的替換
對于這種嵌套的對象,一旦遇到同名屬性耸三,Object.assign的處理方法是替換乱陡,而不是添加。
const target = { a: { b: 'c', d: 'e' } }
const source = { a: { b: 'hello' } }
Object.assign(target, source)
// { a: { b: 'hello' } }
上面代碼中吕晌,target對象的a屬性被source對象的a屬性整個替換掉了蛋褥,而不會得到{ a: { b: 'hello', d: 'e' } }的結(jié)果。這通常不是開發(fā)者想要的睛驳,需要特別小心烙心。
一些函數(shù)庫提供Object.assign的定制版本(比如 Lodash 的_.defaultsDeep方法),可以得到深拷貝的合并乏沸。
(3)數(shù)組的處理
Object.assign可以用來處理數(shù)組淫茵,但是會把數(shù)組視為對象。
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
上面代碼中蹬跃,Object.assign把數(shù)組視為屬性名為 0匙瘪、1、2 的對象蝶缀,因此源數(shù)組的 0 號屬性4覆蓋了目標數(shù)組的 0 號屬性1丹喻。
(4)取值函數(shù)的處理
Object.assign只能進行值的復(fù)制,如果要復(fù)制的值是一個取值函數(shù)翁都,那么將求值后再復(fù)制碍论。
const source = {
get foo() { return 1 }
};
const target = {};
Object.assign(target, source)
// { foo: 1 }
上面代碼中,source對象的foo屬性是一個取值函數(shù)柄慰,Object.assign不會復(fù)制這個取值函數(shù)鳍悠,只會拿到值以后税娜,將這個值復(fù)制過去。
常見用途 § ?
Object.assign方法有很多用處藏研。
(1)為對象添加屬性
class Point {
constructor(x, y) {
Object.assign(this, {x, y});
}
}
上面方法通過Object.assign方法敬矩,將x屬性和y屬性添加到Point類的對象實例。
(2)為對象添加方法
Object.assign(SomeClass.prototype, {
someMethod(arg1, arg2) {
···
},
anotherMethod() {
···
}
});
// 等同于下面的寫法
SomeClass.prototype.someMethod = function (arg1, arg2) {
···
};
SomeClass.prototype.anotherMethod = function () {
···
};
上面代碼使用了對象屬性的簡潔表示法蠢挡,直接將兩個函數(shù)放在大括號中弧岳,再使用assign方法添加到SomeClass.prototype之中。
(3)克隆對象
function clone(origin) {
return Object.assign({}, origin);
}
上面代碼將原始對象拷貝到一個空對象袒哥,就得到了原始對象的克隆缩筛。
克隆。
不過堡称,采用這種方法克隆瞎抛,只能克隆原始對象自身的值,不能克隆它繼承的值却紧。如果想要保持繼承鏈桐臊,可以采用下面的代碼。
function clone(origin) {
let originProto = Object.getPrototypeOf(origin);
return Object.assign(Object.create(originProto), origin);
}
(4)合并多個對象
將多個對象合并到某個對象晓殊。
const merge =
(target, ...sources) => Object.assign(target, ...sources);
如果希望合并后返回一個新對象断凶,可以改寫上面函數(shù),對一個空對象合并巫俺。
const merge =
(...sources) => Object.assign({}, ...sources);
(5)為屬性指定默認值
const DEFAULTS = {
logLevel: 0,
outputFormat: 'html'
};
function processContent(options) {
options = Object.assign({}, DEFAULTS, options);
console.log(options);
// ...
}
6屬性的可枚舉性和遍歷 § ?
可枚舉性 § ?
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
// {
// value: 123,
// writable: true,
// enumerable: true,
// configurable: true
// }
描述對象的enumerable屬性认烁,稱為”可枚舉性“,如果該屬性為false介汹,就表示某些操作會忽略當前屬性却嗡。
目前,有四個操作會忽略enumerable為false的屬性嘹承。
1 .for...in循環(huán):只遍歷對象自身的和繼承的可枚舉的屬性窗价。
- Object.keys():返回對象自身的所有可枚舉的屬性的鍵名。
- JSON.stringify():只串行化對象自身的可枚舉的屬性叹卷。
- Object.assign(): 忽略enumerable為false的屬性撼港,只拷貝對象自身的可枚舉的屬性。
這四個操作之中骤竹,前三個是 ES5 就有的帝牡,最后一個Object.assign()是 ES6 新增的。其中蒙揣,只有for...in會返回繼承的屬性靶溜,其他三個方法都會忽略繼承的屬性,只處理對象自身的屬性鸣奔。實際上墨技,引入“可枚舉”(enumerable)這個概念的最初目的,就是讓某些屬性可以規(guī)避掉for...in操作挎狸,不然所有內(nèi)部屬性和方法都會被遍歷到扣汪。比如,對象原型的toString方法锨匆,以及數(shù)組的length屬性崭别,就通過“可枚舉性”,從而避免被for...in遍歷到恐锣。
另外茅主,ES6 規(guī)定,所有 Class 的原型的方法都是不可枚舉的土榴。
Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable
// false
總的來說诀姚,操作中引入繼承的屬性會讓問題復(fù)雜化,大多數(shù)時候玷禽,我們只關(guān)心對象自身的屬性赫段。所以,盡量不要用for...in循環(huán)矢赁,而用Object.keys()代替糯笙。
屬性的遍歷 § ?
ES6 一共有 5 種方法可以遍歷對象的屬性。
(1)for...in
for...in循環(huán)遍歷對象自身的和繼承的可枚舉屬性(不含 Symbol 屬性)撩银。
(2)Object.keys(obj)
Object.keys返回一個數(shù)組给涕,包括對象自身的(不含繼承的)所有可枚舉屬性(不含 Symbol 屬性)的鍵名。
(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一個數(shù)組额获,包含對象自身的所有屬性(不含 Symbol 屬性够庙,但是包括不可枚舉屬性)的鍵名。
(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols返回一個數(shù)組咪啡,包含對象自身的所有 Symbol 屬性的鍵名首启。
(5)Reflect.ownKeys(obj)
Reflect.ownKeys返回一個數(shù)組,包含對象自身的所有鍵名撤摸,不管鍵名是 Symbol 或字符串毅桃,也不管是否可枚舉。
以上的 5 種方法遍歷對象的鍵名准夷,都遵守同樣的屬性遍歷的次序規(guī)則钥飞。
首先遍歷所有數(shù)值鍵,按照數(shù)值升序排列衫嵌。
其次遍歷所有字符串鍵读宙,按照加入時間升序排列。
最后遍歷所有 Symbol 鍵楔绞,按照加入時間升序排列结闸。
7Object.getOwnPropertyDescriptors() § ?
前面說過唇兑,Object.getOwnPropertyDescriptor方法會返回某個對象屬性的描述對象(descriptor)。ES2017 引入了Object.getOwnPropertyDescriptors方法桦锄,返回指定對象所有自身屬性(非繼承屬性)的描述對象扎附。
該方法的引入目的,主要是為了解決Object.assign()無法正確拷貝get屬性和set屬性的問題结耀。
8 proto屬性留夜,Object.setPrototypeOf(),Object.getPrototypeOf() § ?
proto屬性(前后各兩個下劃線)图甜,用來讀取或設(shè)置當前對象的prototype對象碍粥。目前,所有瀏覽器(包括 IE11)都部署了這個屬性黑毅。
Object.setPrototypeOf() § ?
Object.setPrototypeOf方法的作用與proto相同嚼摩,用來設(shè)置一個對象的prototype對象,返回參數(shù)對象本身矿瘦。它是 ES6 正式推薦的設(shè)置原型對象的方法低斋。
Object.getPrototypeOf() § ?
9 super 關(guān)鍵字 § ?
我們知道,this關(guān)鍵字總是指向函數(shù)所在的當前對象匪凡,ES6 又新增了另一個類似的關(guān)鍵字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關(guān)鍵字表示原型對象時买猖,只能用在對象的方法之中,用在其他地方都會報錯滋尉。
10 Object.keys()玉控,Object.values(),Object.entries() § ?
Object.keys() § ?
ES5 引入了Object.keys方法狮惜,返回一個數(shù)組高诺,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵名。
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]
ES2017 引入了跟Object.keys
配套的Object.values
和Object.entries
碾篡,作為遍歷一個對象的補充手段虱而,供for...of
循環(huán)使用。
let {keys, values, entries} = Object;
let obj = { a: 1, b: 2, c: 3 };
for (let key of keys(obj)) {
console.log(key); // 'a', 'b', 'c'
}
for (let value of values(obj)) {
console.log(value); // 1, 2, 3
}
for (let [key, value] of entries(obj)) {
console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}
Object.values() § ?
Object.values方法返回一個數(shù)組开泽,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值牡拇。
const obj = { foo: 'bar', baz: 42 };
Object.values(obj)
// ["bar", 42]
返回數(shù)組的成員順序,與本章的《屬性的遍歷》部分介紹的排列規(guī)則一致。
Object.values只返回對象自身的可遍歷屬性惠呼。
Object.entries § ?
Object.entries方法返回一個數(shù)組导俘,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值對數(shù)組。
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
除了返回值不一樣剔蹋,該方法的行為與Object.values基本一致趟畏。
如果原對象的屬性名是一個 Symbol 值,該屬性會被忽略滩租。
Object.entries({ [Symbol()]: 123, foo: 'abc' });
// [ [ 'foo', 'abc' ] ]
Object.entries方法的另一個用處是,將對象轉(zhuǎn)為真正的Map結(jié)構(gòu)利朵。
對象的擴展運算符 § ?
《數(shù)組的擴展》一章中律想,已經(jīng)介紹過擴展運算符(...)。
解構(gòu)賦值 § ?
對象的解構(gòu)賦值用于從一個對象取值绍弟,相當于將目標對象自身的所有可遍歷的(enumerable)技即、但尚未被讀取的屬性,分配到指定的對象上面樟遣。所有的鍵和它們的值而叼,都會拷貝到新對象上面。