5.ES8新增:Object.values(),Object.entries()
ES7中新增加的 Object.values()
和Object.entries()
与之前的Object.keys()
类似,返回数组类型。
回顾下Object.keys()
:
var a = { f1: 'hi', f2: 'leo'}; Object.keys(a); // ['f1', 'f2']
5.1 Object.values()
返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值。
let a = { f1: 'hi', f2: 'leo'}; Object.values(a); // ['hi', 'leo']
如果参数不是对象,则返回空数组:
Object.values(10); // [] Object.values(true); // []
5.2 Object.entries()
返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历属性的键值对数组。
let a = { f1: 'hi', f2: 'leo'}; Object.entries(a); // [['f1','hi'], ['f2', 'leo']]
- 用途1:
遍历对象属性。
let a = { f1: 'hi', f2: 'leo'}; for (let [k, v] of Object.entries(a)){ console.log( `${JSON.stringfy(k)}:${JSON.stringfy(v)}` ) } // 'f1':'hi' // 'f2':'leo'
- 用途2: 将对象转为真正的Map结构。
let a = { f1: 'hi', f2: 'leo'}; let map = new Map(Object.entries(a));
手动实现Object.entries()
方法:
// Generator函数实现: function* entries(obj){ for (let k of Object.keys(obj)){ yield [k ,obj[k]]; } } // 非Generator函数实现: function entries (obj){ let arr = []; for(let k of Object.keys(obj)){ arr.push([k, obj[k]]); } return arr; }
6.ES8新增:Object.getOwnPropertyDescriptors()
之前有Object.getOwnPropertyDescriptor
方法会返回某个对象属性的描述对象,新增的Object.getOwnPropertyDescriptors()
方法,返回指定对象所有自身属性(非继承属性)的描述对象,所有原对象的属性名都是该对象的属性名,对应的属性值就是该属性的描述对象
let a = { a1:1, get f1(){ return 100} } Object.getOwnPropetyDescriptors(a); /* { a:{ configurable:true, enumerable:true, value:1, writeable:true} f1:{ configurable:true, enumerable:true, get:f, set:undefined} } */
实现原理:
function getOwnPropertyDescriptors(obj) { const result = {}; for (let key of Reflect.ownKeys(obj)) { result[key] = Object.getOwnPropertyDescriptor(obj, key); } return result; }
引入这个方法,主要是为了解决Object.assign()
无法正确拷贝get
属性和set
属性的问题。
let a = { set f(v){ console.log(v) } } let b = {}; Object.assign(b, a); Object.a(b, 'f'); /* f = { configurable: true, enumable: true, value: undefined, writeable: true } */
value
为undefined
是因为Object.assign
方法不会拷贝其中的get
和set
方法,使用getOwnPropertyDescriptors
配合Object.defineProperties
方法来实现正确的拷贝:
let a = { set f(v){ console.log(v) } } let b = {}; Object.defineProperties(b, Object.getOwnPropertyDescriptors(a)); Object.getOwnPropertyDescriptor(b, 'f') /* configurable: true, enumable: true, get: undefined, set: function(){...} */
Object.getOwnPropertyDescriptors
方法的配合Object.create
方法,将对象属性克隆到一个新对象,实现浅拷贝。
const clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); // 或者 const shallowClone = (obj) => Object.create( Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj) );
7.ES9新增:对象的拓展运算符
7.1 介绍
对象的拓展运算符,即对象的Rest/Spread属性,可将对象解构赋值用于从一个对象取值,搜键值对分配到指定对象上,与数组的拓展运算符类似:
let {x, y, ...z} = {x:1, y:2, a:3, b:4}; x; // 1 y; // 2 z; // {a:3, b:4}
对象的解构赋值要求等号右边必须是个对象,所以如果等号右边是undefined
或null
就会报错无法转成对象。
let {a, ...b} = null; // 运行时报错 let {a, ...b} = undefined; // 运行时报错
解构赋值必须是最后一个参数,否则报错。
let {...a, b, c} = obj; // 语法错误 let {a, ...b, c} = obj; // 语法错误
注意:
- 1.解构赋值是浅拷贝。
let a = {a1: {a2: 'leo'}}; let {...b} = a; a.a1.a2 = 'leo'; b.a1.a2 = 'leo';
- 2.拓展运算符的解构赋值,不能复制继承自原型对象的属性。
let o1 = { a: 1 }; let o2 = { b: 2 }; o2.__proto__ = o1; let { ...o3 } = o2; o3; // { b: 2 } o3.a; // undefined
7.2 使用场景
- 1.取出参数对象所有可遍历属性,拷贝到当前对象中。
let a = { a1:1, a2:2 }; let b = { ...a }; b; // { a1:1, a2:2 } // 类似Object.assign方法
- 2.合并两个对象。
let a = { a1:1, a2:2 }; let b = { b1:11, b2:22 }; let ab = { ...a, ...b }; // {a1: 1, a2: 2, b1: 11, b2: 22} // 等同于 let ab = Object.assign({}, a, b);
- 3.将自定义属性放在拓展运算符后面,覆盖对象原有的同名属性。
let a = { a1:1, a2:2, a3:3 }; let r = { ...a, a3:666 }; // r {a1: 1, a2: 2, a3: 666} // 等同于 let r = { ...a, ...{ a3:666 }}; // r {a1: 1, a2: 2, a3: 666} // 等同于 let r = Object.assign({}, a, { a3:666 }); // r {a1: 1, a2: 2, a3: 666}
- 4.将自定义属性放在拓展运算符前面,就会成为设置新对象的默认值。
let a = { a1:1, a2:2 }; let r = { a3:666, ...a }; // r {a3: 666, a1: 1, a2: 2} // 等同于 let r = Object.assign({}, {a3:666}, a); // r {a3: 666, a1: 1, a2: 2} // 等同于 let r = Object.assign({a3:666}, a); // r {a3: 666, a1: 1, a2: 2}
- 5.拓展运算符后面可以使用表达式。
let a = { ...(x>1? {a:!:{}), b:2 }
- 6.拓展运算符后面如果是个空对象,则没有任何效果。
{...{}, a:1}; // {a:1}
- 7.若参数是
null
或undefined
则忽略且不报错。
let a = { ...null, ...undefined }; // 不报错
- 8.若有取值函数
get
则会执行。
// 不会打印 因为f属性只是定义 而不没执行 let a = { ...a1, get f(){console.log(1)} } // 会打印 因为f执行了 let a = { ...a1, ...{ get f(){console.log(1)} } }