一. es6對象的擴(kuò)展
1. 屬性的簡潔表示法
ES6 允許直接寫入變量和函數(shù),作為對象的屬性和方法跃脊。這樣的書寫更加簡潔。
let birth ='foo';
const Person ={
name:'張三';
//等同于birth: birth
birth,
// 等同于hello: function ()...
hello(){ console.log('我的名字是',this.name);}
};
2. 屬性名表達(dá)式
JavaScript 定義對象的屬性,有兩種方法献幔。
// 方法一
obj.foo =true;
// 方法二
obj['a'+'bc']=123;
ES6 允許字面量定義對象時(shí)煮寡,用表達(dá)式作為對象的屬性名虹蓄。
let propKey ='foo';
let obj ={
[propKey]:true,
['a'+'bc']:123
};
表達(dá)式還可以用于定義方法名。
let obj ={
['h'+'ello'](){
return 'hi' ;
}
};
obj.hello()
3.屬性的遍歷
遍歷對象屬性的方法:
(1)for...in 循環(huán)遍歷對象自身的和繼承的可枚舉屬性(不含 Symbol 屬性)幸撕。
(2)Object.keys(obj) 返回一個(gè)數(shù)組薇组,包括對象自身的(不含繼承的)所有可枚舉屬性(不含 Symbol 屬性)的鍵名。
(3)Object.getOwnPropertyNames(obj) 返回一個(gè)數(shù)組坐儿,包含對象自身的所有屬性(不含 Symbol 屬性律胀,但是包括不可枚舉屬性)的鍵名。
(4)Object.getOwnPropertySymbols(obj) 返回一個(gè)數(shù)組貌矿,包含對象自身的所有 Symbol 屬性的鍵名炭菌。
(5)Reflect.ownKeys(obj) 返回一個(gè)數(shù)組,包含對象自身的所有鍵名逛漫,不管鍵名是 Symbol 或字符串黑低,也不管是否可枚舉。
以上的 5 種方法遍歷對象的鍵名尽楔,都遵守同樣的屬性遍歷的次序規(guī)則投储。
- 首先遍歷所有數(shù)值鍵,按照數(shù)值升序排列阔馋。
- 其次遍歷所有字符串鍵玛荞,按照加入時(shí)間升序排列。
- 最后遍歷所有 Symbol 鍵呕寝,按照加入時(shí)間升序排列勋眯。
Reflect.ownKeys({[Symbol()]:0, b:0,10:0,2:0, a:0})
// [2, 10, b, a, Symbol()]
4.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()
JavaScript 引擎內(nèi)部塞蹭,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()
5.解構(gòu)賦值
對象的解構(gòu)賦值用于將目標(biāo)對象自身的所有可遍歷的(enumerable)讶坯、但尚未被讀取的屬性番电,分配到指定的對象上面。所有的鍵和它們的值辆琅,都會拷貝到新對象上面漱办。
let{ x, y,...aa }={ x:1, y:2, a:3, b:4};
x // 1
y // 2
z // { a: 3, b: 4 }
解構(gòu)賦值的拷貝是淺拷貝,即如果一個(gè)鍵的值是復(fù)合類型的值(數(shù)組婉烟、對象娩井、函數(shù))、那么解構(gòu)賦值拷貝的是這個(gè)值的引用似袁。
let obj ={ a:{ b:1}};
let{...x }= obj;
obj.a.b =2;
x.a.b // 2
另外洞辣,擴(kuò)展運(yùn)算符的解構(gòu)賦值,不能復(fù)制繼承自原型對象的屬性昙衅。
let o1 ={ a:1};
let o2 ={ b:2};
o2.__proto__ = o1;
let{...o3 }= o2;
o3 // { b: 2 }
o3.a// undefined
下面是另一個(gè)例子扬霜。
const o = Object.create({ x:1, y:2});
o.z =3;
let{ x,...newObj }= o;
let{ y, z }= newObj;
x // 1
y // undefined
z // 3
6.擴(kuò)展運(yùn)算符
對象的擴(kuò)展運(yùn)算符(...)用于取出參數(shù)對象的所有可遍歷屬性,拷貝到當(dāng)前對象之中绒尊。
let z ={ a:3, b:4};
let n ={...z };
n // { a: 3, b: 4 }
數(shù)組是特殊的對象畜挥,所以對象的擴(kuò)展運(yùn)算符也可以用于數(shù)組仔粥。
let foo ={...[a,b,c]};
foo
// {0: a, 1: b, 2: c}
如果擴(kuò)展運(yùn)算符后面不是對象婴谱,則會自動將其轉(zhuǎn)為對象。
// 等同于 {...Object(1)}
{...1} // {}
上面代碼中躯泰,擴(kuò)展運(yùn)算符后面是整數(shù)1谭羔,會自動轉(zhuǎn)為數(shù)值的包裝對象Number{1}。由于該對象沒有自身屬性麦向,所以返回一個(gè)空對象瘟裸。
如果擴(kuò)展運(yùn)算符后面是字符串,它會自動轉(zhuǎn)成一個(gè)類似數(shù)組的對象诵竭,因此返回的不是空對象话告。
{...'hello'}
// {0: 'h', 1: 'e', 2: 'l', 3: 'l', 4: 'o'}
擴(kuò)展運(yùn)算符的參數(shù)對象之中,如果有取值函數(shù)get卵慰,這個(gè)函數(shù)是會執(zhí)行的沙郭。
// 并不會拋出錯(cuò)誤,因?yàn)?x 屬性只是被定義裳朋,但沒執(zhí)行
let aWithXGetter ={
...a,
getx(){
throw new Error('not throw yet');
}
};
// 會拋出錯(cuò)誤病线,因?yàn)?x 屬性被執(zhí)行了
let runtimeError ={
...a,
...{
getaa(){
throw new Error('throw now');
}
}
};
二.Es6對象的新增方法
1.Object.is()
ES5 比較兩個(gè)值是否相等:相等運(yùn)算符(==)和嚴(yán)格相等運(yùn)算符(===)。
Object.is它用來比較兩個(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
2.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}
如果目標(biāo)對象與源對象有同名屬性司澎,或多個(gè)源對象有同名屬性对扶,則后面的屬性會覆蓋前面的屬性。
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}
如果該參數(shù)不是對象惭缰,則會先轉(zhuǎn)成對象浪南,然后返回。
typeof Object.assign(2) // [object]
由于undefined和null無法轉(zhuǎn)成對象漱受,所以如果它們作為參數(shù)络凿,就會報(bào)錯(cuò)。
Object.assign(undefined) // 報(bào)錯(cuò)
Object.assign(null) // 報(bào)錯(cuò)
Object.assign只拷貝源對象的自身屬性(不拷貝繼承屬性)昂羡,也不拷貝不可枚舉的屬性(enumerable: false)
Object.assign({b:c},
Object.defineProperty({},'invisible',{
enumerable:false,
value:'hello';
})
)
// { b: c }
上面代碼中絮记,Object.assign要拷貝的對象只有一個(gè)不可枚舉屬性invisible,這個(gè)屬性并沒有被拷貝進(jìn)去虐先。
注意點(diǎn)
(1)淺拷貝
Object.assign方法實(shí)行的是淺拷貝怨愤,而不是深拷貝。也就是說蛹批,如果源對象某個(gè)屬性的值是對象撰洗,那么目標(biāo)對象拷貝得到的是這個(gè)對象的引用。
(2)同名屬性的替換
對于這種嵌套的對象腐芍,一旦遇到同名屬性差导,Object.assign的處理方法是替換,而不是添加猪勇。
const target ={ a:{ b:c, d:e;}}
const source ={ a:{ b:'hello';}}
Object.assign(target, source)
// { a: { b: 'hello' } }
(3)數(shù)組的處理
Object.assign可以用來處理數(shù)組设褐,但是會把數(shù)組視為對象。
Object.assign([1,2,3],[4,5])
(4)取值函數(shù)的處理
Object.assign只能進(jìn)行值的復(fù)制泣刹,如果要復(fù)制的值是一個(gè)取值函數(shù)助析,那么將求值后再復(fù)制。
const source ={
getfoo(){return1}
};
const target ={};
Object.assign(target, source)
// { foo: 1 }
常見用途
(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){
···
}
});
// 等同于下面的寫法
SomeClass.prototype.someMethod =function(arg1, arg2){
···
};
(3)克隆對象
functionclone(origin){
return Object.assign({}, origin);
}
不過,采用這種方法克隆襟沮,只能克隆原始對象自身的值锥惋,不能克隆它繼承的值昌腰。如果想要保持繼承鏈,可以采用下面的代碼膀跌。
functionclone(origin){
let originProto = Object.getPrototypeOf(origin);
return Object.assign(Object.create(originProto), origin);
}
3.Object.getOwnPropertyDescriptors()
ES5 的Object.getOwnPropertyDescriptor()方法會返回某個(gè)對象屬性的描述對象(descriptor)遭商。
ES2017 引入了Object.getOwnPropertyDescriptors()方法,返回指定對象所有自身屬性(非繼承屬性)的描述對象捅伤。
const obj ={
foo:123,
getbar(){return'abc'}
};
Object.getOwnPropertyDescriptors(obj)
// { foo:
// { value: 123,
// writable: true,
// enumerable: true,
// configurable: true },
// bar:
// { get: [Function: get bar],
// set: undefined,
// enumerable: true,
// configurable: true } }
該方法的引入目的劫流,主要是為了解決Object.assign()無法正確拷貝get屬性和set屬性的問題。
const source ={
setfoo(value){
console.log(value);
}
};
const target1 ={};
Object.assign(target1, source);
Object.getOwnPropertyDescriptor(target1,'foo')
// { value: undefined,
// writable: true,
// enumerable: true,
// configurable: true }
上面代碼中丛忆,source對象的foo屬性的值是一個(gè)賦值函數(shù)祠汇,Object.assign方法將這個(gè)屬性拷貝給target1對象,結(jié)果該屬性的值變成了undefined熄诡。這是因?yàn)镺bject.assign方法總是拷貝一個(gè)屬性的值可很,而不會拷貝它背后的賦值方法或取值方法。
這時(shí)凰浮,Object.getOwnPropertyDescriptors()方法配合Object.defineProperties()方法我抠,就可以實(shí)現(xiàn)正確拷貝。
const source ={
setfoo(value){
console.log(value);
}
};
const target2 ={};
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
Object.getOwnPropertyDescriptor(target2, 'foo')
// { get: undefined,
// set: [Function: set foo],
// enumerable: true,
// configurable: true }
Object.getOwnPropertyDescriptors()方法的另一個(gè)用處袜茧,是配合Object.create()方法菜拓,將對象屬性克隆到一個(gè)新對象。這屬于淺拷貝笛厦。
const clone = Object.create(Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj));
另外纳鼎,Object.getOwnPropertyDescriptors()方法可以實(shí)現(xiàn)一個(gè)對象繼承另一個(gè)對象。
const obj = Object.create(
prot,
Object.getOwnPropertyDescriptors({
foo:123,
})
)
4.__proto__屬性裳凸,Object.setPrototypeOf()贱鄙,Object.getPrototypeOf()
JavaScript 語言的對象繼承是通過原型鏈實(shí)現(xiàn)的。ES6 提供了更多原型對象的操作方法登舞。
(1)Object.setPrototypeOf()
Object.setPrototypeOf方法的作用與__proto__相同贰逾,用來設(shè)置一個(gè)對象的prototype對象悬荣,返回參數(shù)對象本身菠秒。它是 ES6 正式推薦的設(shè)置原型對象的方法。
// 格式
Object.setPrototypeOf(object, prototype)
// 用法
const o = Object.setPrototypeOf({},null);
該方法等同于下面的函數(shù)氯迂。
functionsetPrototypeOf(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對象的屬性嚼蚀。
如果第一個(gè)參數(shù)不是對象禁灼,會自動轉(zhuǎn)為對象。但是由于返回的還是第一個(gè)參數(shù)轿曙,所以這個(gè)操作不會產(chǎn)生任何效果弄捕。
(2)Object.getPrototypeOf()
該方法與Object.setPrototypeOf方法配套僻孝,用于讀取一個(gè)對象的原型對象。
functionRectangle(){
// ...
}
const rec =newRectangle();
Object.getPrototypeOf(rec)=== Rectangle.prototype
// true
如果參數(shù)是undefined或null守谓,它們無法轉(zhuǎn)為對象穿铆,所以會報(bào)錯(cuò)。
Object.getPrototypeOf(null)
// TypeError: Cannot convert undefined or null to object
5.Object.keys()斋荞,Object.values()荞雏,Object.entries()
(1)Object.keys()
ES5 的Object.keys方法,返回一個(gè)數(shù)組平酿,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵名凤优。
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]
}
(2)Object.values()
Object.values方法返回一個(gè)數(shù)組,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值幸逆。
const obj ={ foo:'bar', baz:42};
Object.values(obj)
// ['bar', 42]
返回?cái)?shù)組的成員順序挖垛,與本章的《屬性的遍歷》部分介紹的排列規(guī)則一致。
const obj ={100:'a',2:'b',7:'c'};
Object.values(obj)
// ['b', 'c', 'a']
上面代碼中秉颗,屬性名為數(shù)值的屬性痢毒,是按照數(shù)值大小,從小到大遍歷的蚕甥,因此返回的順序是b哪替、c、a菇怀。
Object.values只返回對象自身的可遍歷屬性凭舶。
const obj = Object.create({},{p:{value:42}});
Object.values(obj) // []
上面代碼中,Object.create方法的第二個(gè)參數(shù)添加的對象屬性(屬性p)爱沟,如果不顯式聲明帅霜,默認(rèn)是不可遍歷的,因?yàn)閜的屬性描述對象的enumerable默認(rèn)是false呼伸,Object.values不會返回這個(gè)屬性身冀。只要把enumerable改成true,Object.values就會返回屬性p的值括享。
const obj = Object.create({},{p:
{
value:42,
enumerable:true
}
});
Object.values(obj) // [42]
Object.values會過濾屬性名為 Symbol 值的屬性
Object.values({[Symbol()]:123, foo:'abc'});
// ['abc']
如果Object.values方法的參數(shù)是一個(gè)字符串搂根,會返回各個(gè)字符組成的一個(gè)數(shù)組。
Object.values('foo')
// ['f', 'o','o']
(3)Object.entries()
Object.entries()方法返回一個(gè)數(shù)組铃辖,成員是參數(shù)對象自身的(不含繼承的)所有可遍歷(enumerable)屬性的鍵值對數(shù)組剩愧。
const obj ={ foo:'bar', baz:42};
Object.entries(obj)
// [['foo', 'bar'], ['baz', 42] ]
除了返回值不一樣,該方法的行為與Object.values基本一致娇斩。
如果原對象的屬性名是一個(gè) Symbol 值仁卷,該屬性會被忽略穴翩。
Object.entries({[Symbol()]:123, foo:'abc'});
// [[ 'foo', 'abc'] ]
上面代碼中,原對象有兩個(gè)屬性锦积,Object.entries只輸出屬性名非 Symbol 值的屬性藏否。將來可能會有Reflect.ownEntries()方法,返回對象自身的所有屬性充包。
Object.entries的基本用途是遍歷對象的屬性副签。
let obj ={ one:1, two:2};
for(let[k, v] of Object.entries(obj)){
console.log(
`${JSON.stringify(k)}: ${JSON.stringify(v)}`
);
}
// one: 1
// two: 2
Object.entries方法的另一個(gè)用處是,將對象轉(zhuǎn)為真正的Map結(jié)構(gòu)基矮。
const obj ={ foo:'bar', baz:42};
const map =newMap(Object.entries(obj));
map // Map { foo: 'bar', baz: 42 }
(4)Object.fromEntries()
Object.fromEntries()方法是Object.entries()的逆操作淆储,用于將一個(gè)鍵值對數(shù)組轉(zhuǎn)為對象。因此特別適合將 Map 結(jié)構(gòu)轉(zhuǎn)為對象家浇。
// 例一
const entries =newMap([
['foo','bar'],
['baz',42]
]);
Object.fromEntries(entries)
// { foo: 'bar', baz: 42 }
// 例二
const map =newMap().set('foo',true).set('bar',false);
Object.fromEntries(map)
// { foo: true, bar: false }
原文:https://yolkpie.net/2020/02/04/es6%E6%96%B0%E7%89%B9%E6%80%A7/