一. 属性的简洁表示法
ES6 允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
var foo = 'bar' ;
var baz = {foo};
baz // {foo : 'bar'}
//等同于
var baz = {foo : foo}
function f(x, y){
return {x, y}
}
//等同于
function f(x, y){
return { x : x, y : y};
}
f(1, 2) // object { x : 1, y : 2}
//方法也可以简写
var o = {
method(){
return "hello!" ;
}
};
//等同于
var o = {
method : function(){
return "hello!" ;
}
};
var birth = '2000/01/01';
var person {
name : '张三',
//等同于birth : birth
birth,
//等同于hello : functon()....
hello() {console.log(this.name)}
}
这种写法用于函数的返回值,将会非常方便。
function getPoint() {
var x = 1;
var y =10;
return {x, y};
}
getPoint()
// { x : 1, y : 10}
二. 属性名表达式
obj . foo = true;
obj['a' + 'bc'] = 123;
如果使用字面量方式定义对象(使用大括号),在 ES5 中只能使用方法一(标识符)定义属性。
var obj = {
foo : true,
abc : 123
};
ES6 允许字面量定义对象时,用表达式作为对象的属性名,即把表达式放在方括号内。
let propKey = 'foo';
let obj = {
[propKey] : true,
['a' + 'bc'] : 123
};
var lastWord = 'last word';
var 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
属性名表达式与简洁表示法,不能同时使用,会报错。
//报错
var foo = 'bar';
var bar = 'abc';
var baz = { [foo] };
//正确
var foo = 'bar';
var baz = {[foo] : 'abc'}
属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串[object Object],这一点要特别小心。
const keyA = { a : 1};
const keyB = { b : 2};
const myObject ={
[KeyA] : 'valueA',
[KeyB] : 'valueB'
};
三. Object.assign()
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
var target = { a : 1 };
var source1 = { b : 2 };
var source2 = { c : 3 };
Object.assignj(traget, souce1, source2);
target // {a : 1, b : 2, c : 3}
Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。
如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
var target = {a : 1, b : 2};
var source1 = { b : 2, c : 2};
var source2 = { c : 3};
Objcet.assign(target, source1, source2);
target //{a : 1, b : 2, c :3}
//如果只有一个参数,Object.assign会直接返回该参数。
var obj = {a: 1};
Object.assign(obj) === obj // true
Object.assign方法实行的是浅拷贝,而不是深拷贝。也就是说,如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
var obj1 = {a : {b : 1}};
var obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2
*Tips:源对象obj1的a属性的值是一个对象,Object.assign拷贝得到的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。*
//对于这种嵌套的对象,一旦遇到同名属性,Object.assign的处理方法是替换,而不是添加。
var target = {a : {b : 'c', d : 'e'}}
var source = {a : {b : 'hello'}}
Object.assign(target, source);
// {a : { b : 'hello'}}
Object.assign可以用来处理数组,但是会把数组视为对象。
Object.assign([1,2, 3], [4,5]);
//[4,5,3]
//为属性指定默认值
const DEFAULTS = {
logLevel: 0,
outputFormat: 'html'
};
function processContent(options) {
options = Object.assign({}, DEFAULTS, options);
console.log(options);
// ...
}
四.属性的可枚举性和遍历
对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象。
let obj = { foo : 123};
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 的原型的方法都是不可枚举的。
ES6 一共有5种方法可以遍历对象的属性。
for...in
for...in循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。
Object.keys(obj)
Object.keys返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)。
Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)。
Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols返回一个数组,包含对象自身的所有 Symbol 属性。
Reflect.ownKeys(obj)
Reflect.ownKeys返回一个数组,包含对象自身的所有属性,不管属性名是 Symbol 或字符串,也不管是否可枚举。
五. Object.keys(),Object.values(),Object.entries()
ES5 引入了Object.keys方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。
var obj = { foo : 'bar', baz : 42}
Object.keys(obj)
//["foo", "baz"]
ES2017 引入了跟Object.keys配套的Object.values和Object.entries,作为遍历一个对象的补充手段,供for...of循环使用。
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 key 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方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值。
var obj = { foo : 'bar', baz : 42}
Object.values(obj)
//['bar', 42]
var obj = { 100: 'a', 2: 'b', 7: 'c' };
Object.values(obj)
//["b", "c", "a"]
如果Object.values方法的参数是一个字符串,会返回各个字符组成的一个数组。
Object.values('foo')
// ['f', 'o', 'o']
Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组。
var obj = { foo : 'bar', baz : 42}
Object.entries(obj)
//[['foo', 'bar'], ['baz', 42]]
Object.entries方法的另一个用处是,将对象转为真正的Map结构。
var obj = { foo: 'bar', baz: 42 };
var obj = { foo: 'bar', baz: 42 };
map // Map { foo: "bar", baz: 42 }
六. 对象的扩展运算符
ES2017将扩展运算符(...)引入对象
1.解构赋值
let { x , y , ...z} = {x : 1, y : 2, a : 3, b : 4}
解构赋值必须是最后一个参数,否则会报错。
构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。
let obj = { a : { b : 1}}
let {...x} = obj;
obj.a.b = 2;
x.a.b // 2
扩展运算符(...)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
let z = { a : 3, b : 4}
let n = {...z}
n // { a: 3, b: 4 }
//等同于
let aClone = { ...a }
let aClone = Object.assign({}, a);
扩展运算符可以用于合并两个对象。
let ab = {...a, ...b}
//等同于
let ab = Object.assign({}, a, b);
与数组的扩展运算符一样,对象的扩展运算符后面可以跟表达式。
const obj = {
...(x > 1? { a : 1 } : {}),
b : 2,
};