1.屬性的簡潔表示法
ES6 允許直接寫入變量和函數(shù)佃扼,作為對象的屬性和方法。這樣的書寫更加簡潔连锯。
const foo = 'bar';
const baz = {foo};
baz // {foo: "bar"}
// 等同于
const baz = {foo: foo};
除了屬性簡寫撮竿,方法也可以簡寫泉手。
const o = {
? method() {
? ? return "Hello!";
? }
};
// 等同于
const o = {
? method: function() {
? ? return "Hello!";
? }
};
4.Object.is()
ES5 比較兩個(gè)值是否相等铣口,只有兩個(gè)運(yùn)算符:相等運(yùn)算符(==)和嚴(yán)格相等運(yùn)算符(===)滤钱。它們都有缺點(diǎn),前者會(huì)自動(dòng)轉(zhuǎn)換數(shù)據(jù)類型枷踏,后者的NaN不等于自身,以及+0等于-0掰曾。JavaScript 缺乏一種運(yùn)算旭蠕,在所有環(huán)境中,只要兩個(gè)值是一樣的旷坦,它們就應(yīng)該相等掏熬。
它用來比較兩個(gè)值是否嚴(yán)格相等,與嚴(yán)格比較運(yùn)算符(===)的行為基本一致秒梅。
Object.is('foo', 'foo')
// true
Object.is({}, {})
// false
不同之處只有兩個(gè):一是+0不等于-0旗芬,二是NaN等于自身。
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
ES5 可以通過下面的代碼捆蜀,部署Object.is疮丛。
Object.defineProperty(Object, 'is', {
? value: function(x, y) {
? ? if (x === y) {
? ? ? // 針對+0 不等于 -0的情況
? ? ? return x !== 0 || 1 / x === 1 / y;
? ? }
? ? // 針對NaN的情況
? ? return x !== x && y !== y;
? },
? configurable: true,
? enumerable: false,
? writable: true
});
5.Object.assign()
基本用法
Object.assign方法用于對象的合并,將源對象(source)的所有可枚舉屬性辆它,復(fù)制到目標(biāo)對象(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方法的第一個(gè)參數(shù)是目標(biāo)對象,后面的參數(shù)都是源對象锰茉。
注意呢蔫,如果目標(biāo)對象與源對象有同名屬性,或多個(gè)源對象有同名屬性飒筑,則后面的屬性會(huì)覆蓋前面的屬性片吊。
如果該參數(shù)不是對象绽昏,則會(huì)先轉(zhuǎn)成對象,然后返回俏脊。
typeof Object.assign(2) // "object"
由于undefined和null無法轉(zhuǎn)成對象全谤,所以如果它們作為參數(shù),就會(huì)報(bào)錯(cuò)联予。
Object.assign(undefined) // 報(bào)錯(cuò)
Object.assign(null) // 報(bào)錯(cuò)
如果非對象參數(shù)出現(xiàn)在源對象的位置(即非首參數(shù))啼县,那么處理規(guī)則有所不同。首先沸久,這些參數(shù)都會(huì)轉(zhuǎn)成對象季眷,如果無法轉(zhuǎn)成對象,就會(huì)跳過卷胯。這意味著子刮,如果undefined和null不在首參數(shù),就不會(huì)報(bào)錯(cuò)窑睁。
let obj = {a: 1};
Object.assign(obj, undefined) === obj // true
Object.assign(obj, null) === obj // true
其他類型的值(即數(shù)值挺峡、字符串和布爾值)不在首參數(shù),也不會(huì)報(bào)錯(cuò)担钮。但是橱赠,除了字符串會(huì)以數(shù)組形式,拷貝入目標(biāo)對象箫津,其他值都不會(huì)產(chǎn)生效果狭姨。
const v1 = 'abc';
const v2 = true;
const v3 = 10;
const obj = Object.assign({}, v1, v2, v3);
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
上面代碼中,v1苏遥、v2饼拍、v3分別是字符串、布爾值和數(shù)值田炭,結(jié)果只有字符串合入目標(biāo)對象(以字符數(shù)組的形式)师抄,數(shù)值和布爾值都會(huì)被忽略。這是因?yàn)橹挥凶址陌b對象教硫,會(huì)產(chǎn)生可枚舉屬性叨吮。
注意點(diǎn) § ?
(1)淺拷貝
Object.assign方法實(shí)行的是淺拷貝,而不是深拷貝瞬矩。也就是說挤安,如果源對象某個(gè)屬性的值是對象,那么目標(biāo)對象拷貝得到的是這個(gè)對象的引用丧鸯。
const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2
上面代碼中蛤铜,源對象obj1的a屬性的值是一個(gè)對象,Object.assign拷貝得到的是這個(gè)對象的引用。這個(gè)對象的任何變化围肥,都會(huì)反映到目標(biāo)對象上面剿干。
(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屬性整個(gè)替換掉了榜轿,而不會(huì)得到{ a: { b: 'hello', d: 'e' } }的結(jié)果。這通常不是開發(fā)者想要的朵锣,需要特別小心谬盐。
一些函數(shù)庫提供Object.assign的定制版本(比如 Lodash 的_.defaultsDeep方法),可以得到深拷貝的合并诚些。
(3)數(shù)組的處理
Object.assign可以用來處理數(shù)組飞傀,但是會(huì)把數(shù)組視為對象。
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
上面代碼中诬烹,Object.assign把數(shù)組視為屬性名為 0砸烦、1、2 的對象绞吁,因此源數(shù)組的 0 號屬性4覆蓋了目標(biāo)數(shù)組的 0 號屬性1幢痘。
(4)取值函數(shù)的處理
Object.assign只能進(jìn)行值的復(fù)制,如果要復(fù)制的值是一個(gè)取值函數(shù)家破,那么將求值后再復(fù)制颜说。
const source = {
? get foo() { return 1 }
};
const target = {};
Object.assign(target, source)
// { foo: 1 }
上面代碼中,source對象的foo屬性是一個(gè)取值函數(shù)员舵,Object.assign不會(huì)復(fù)制這個(gè)取值函數(shù)脑沿,只會(huì)拿到值以后藕畔,將這個(gè)值復(fù)制過去马僻。
常見用途 § ?
Object.assign方法有很多用處。
(1)為對象添加屬性
class Point {
? constructor(x, y) {
? ? Object.assign(this, {x, y});
? }
}
上面方法通過Object.assign方法注服,將x屬性和y屬性添加到Point類的對象實(shí)例韭邓。
(2)為對象添加方法
Object.assign(SomeClass.prototype, {
? someMethod(arg1, arg2) {
? ? ···
? },
? anotherMethod() {
? ? ···
? }
});
// 等同于下面的寫法
SomeClass.prototype.someMethod = function (arg1, arg2) {
? ···
};
SomeClass.prototype.anotherMethod = function () {
? ···
};
上面代碼使用了對象屬性的簡潔表示法,直接將兩個(gè)函數(shù)放在大括號中溶弟,再使用assign方法添加到SomeClass.prototype之中女淑。
(3)克隆對象
function clone(origin) {
? return Object.assign({}, origin);
}
上面代碼將原始對象拷貝到一個(gè)空對象,就得到了原始對象的克隆辜御。
不過鸭你,采用這種方法克隆,只能克隆原始對象自身的值,不能克隆它繼承的值袱巨。如果想要保持繼承鏈阁谆,可以采用下面的代碼。
function clone(origin) {
? let originProto = Object.getPrototypeOf(origin);
? return Object.assign(Object.create(originProto), origin);
}
(4)合并多個(gè)對象
將多個(gè)對象合并到某個(gè)對象愉老。
const merge =
? (target, ...sources) => Object.assign(target, ...sources);
如果希望合并后返回一個(gè)新對象场绿,可以改寫上面函數(shù),對一個(gè)空對象合并嫉入。
const merge =
? (...sources) => Object.assign({}, ...sources);
(5)為屬性指定默認(rèn)值
const DEFAULTS = {
? logLevel: 0,
? outputFormat: 'html'
};
function processContent(options) {
? options = Object.assign({}, DEFAULTS, options);
? console.log(options);
? // ...
}
上面代碼中焰盗,DEFAULTS對象是默認(rèn)值,options對象是用戶提供的參數(shù)咒林。Object.assign方法將DEFAULTS和options合并成一個(gè)新對象熬拒,如果兩者有同名屬性,則option的屬性值會(huì)覆蓋DEFAULTS的屬性值映九。
注意梦湘,由于存在淺拷貝的問題,DEFAULTS對象和options對象的所有屬性的值件甥,最好都是簡單類型捌议,不要指向另一個(gè)對象。否則引有,DEFAULTS對象的該屬性很可能不起作用瓣颅。
const DEFAULTS = {
? url: {
? ? host: 'example.com',
? ? port: 7070
? },
};
processContent({ url: {port: 8000} })
// {
//? url: {port: 8000}
// }
上面代碼的原意是將url.port改成 8000,url.host不變譬正。實(shí)際結(jié)果卻是options.url覆蓋掉DEFAULTS.url宫补,所以url.host就不存在了。
Object.setPrototypeOf()
Object.setPrototypeOf方法的作用與__proto__相同曾我,用來設(shè)置一個(gè)對象的prototype對象粉怕,返回參數(shù)對象本身。它是 ES6 正式推薦的設(shè)置原型對象的方法抒巢。
// 格式
Object.setPrototypeOf(object, prototype)
// 用法
const o = Object.setPrototypeOf({}, null);
該方法等同于下面的函數(shù)贫贝。
function (obj, proto) {
? obj.__proto__ = proto;
? return obj;
}
下面是一個(gè)例子。
let proto = {};
let obj = { x: 10 };
Object.setPrototypeOf(obj, proto);
proto.y = 20;
proto.z = 40;
obj.x // 10
obj.y // 20
obj.z // 40
上面代碼將proto對象設(shè)為obj對象的原型蛉谜,所以從obj對象可以讀取proto對象的屬性稚晚。
9.super 關(guān)鍵字 § ?
我們知道,this關(guān)鍵字總是指向函數(shù)所在的當(dāng)前對象型诚,ES6 又新增了另一個(gè)類似的關(guān)鍵字super客燕,指向當(dāng)前對象的原型對象。
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)鍵字表示原型對象時(shí)傍妒,只能用在對象的方法之中楚昭,用在其他地方都會(huì)報(bào)錯(cuò)。
Object.keys()拍顷,Object.values()抚太,Object.entries()
都返回一個(gè)數(shù)組
ES2017 引入了跟Object.keys配套的Object.values和Object.entries,作為遍歷一個(gè)對象的補(bǔ)充手段昔案,供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.keys() § ?
ES5 引入了Object.keys方法,返回一個(gè)數(shù)組踏揣,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵名庆亡。
var obj = { foo: 'bar', baz: 42 };
Object.keys(obj)
// ["foo", "baz"]
Object.values() § ?
Object.values方法返回一個(gè)數(shù)組,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值捞稿。
const obj = { foo: 'bar', baz: 42 };
Object.values(obj)
// ["bar", 42]
Object.entries § ?
Object.entries方法返回一個(gè)數(shù)組又谋,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值對數(shù)組。
const obj = { foo: 'bar', baz: 42 };
Object.entries(obj)
// [ ["foo", "bar"], ["baz", 42] ]
11.對象的擴(kuò)展運(yùn)算符
《數(shù)組的擴(kuò)展》一章中娱局,已經(jīng)介紹過擴(kuò)展運(yùn)算符(...)彰亥。
const [a, ...b] = [1, 2, 3];
a // 1
b // [2, 3]
ES2018 將這個(gè)運(yùn)算符引入了對象。
解構(gòu)賦值
對象的解構(gòu)賦值用于從一個(gè)對象取值衰齐,相當(dāng)于將目標(biāo)對象自身的所有可遍歷的(enumerable)任斋、但尚未被讀取的屬性,分配到指定的對象上面耻涛。所有的鍵和它們的值废酷,都會(huì)拷貝到新對象上面。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }
上面代碼中抹缕,變量z是解構(gòu)賦值所在的對象澈蟆。它獲取等號右邊的所有尚未讀取的鍵(a和b),將它們連同值一起拷貝過來卓研。
由于解構(gòu)賦值要求等號右邊是一個(gè)對象趴俘,所以如果等號右邊是undefined或null,就會(huì)報(bào)錯(cuò)鉴分,因?yàn)樗鼈儫o法轉(zhuǎn)為對象哮幢。
let { x, y, ...z } = null; // 運(yùn)行時(shí)錯(cuò)誤
let { x, y, ...z } = undefined; // 運(yùn)行時(shí)錯(cuò)誤
解構(gòu)賦值必須是最后一個(gè)參數(shù)带膀,否則會(huì)報(bào)錯(cuò)志珍。
let { ...x, y, z } = obj; // 句法錯(cuò)誤
let { x, ...y, ...z } = obj; // 句法錯(cuò)誤
上面代碼中,解構(gòu)賦值不是最后一個(gè)參數(shù)垛叨,所以會(huì)報(bào)錯(cuò)伦糯。
注意柜某,解構(gòu)賦值的拷貝是淺拷貝,即如果一個(gè)鍵的值是復(fù)合類型的值(數(shù)組敛纲、對象喂击、函數(shù))、那么解構(gòu)賦值拷貝的是這個(gè)值的引用淤翔,而不是這個(gè)值的副本翰绊。
let obj = { a: { b: 1 } };
let { ...x } = obj;
obj.a.b = 2;
x.a.b // 2