对象的扩展
属性的简洁表示法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
const foo = 'bar'; const baz = { foo }; console.log(baz); //{ foo: 'bar' }
上面代码中,变量foo
直接写在大括号里面。这时,属性名就是变量名, 属性值就是变量值。下面是另一个例子。
function f(x, y) { return { x, y }; } // 等同于 function f(x, y) { return { x: x, y: y }; } f(1, 2); // {x: 1, y: 2}
除了属性简写,方法也可以简写。
const o = { method() { return "Hello!"; } }; // 等同于 const o = { method: function () { return "Hello!"; } };
下面是一个实际的例子。
let birth = '2000/01/01'; const Person = { name: '张三', //等同于birth: birth birth, // 等同于hello: function ()... hello() { console.log('我的名字是', this.name); } }; Person.hello(); // 我的名字是 张三
这种写法用于函数的返回值,将会非常方便。
function getPoint() { const x = 1; const y = 10; return { x, y }; } var result = getPoint() console.log(result); // {x:1, y:10}
CommonJS 模块输出一组变量,就非常合适使用简洁写法。
let ms = {}; function getItem(key) { return key in ms ? ms[key] : null; } function setItem(key, value) { ms[key] = value; } function clear() { ms = {}; } module.exports = { getItem, setItem, clear }; // 等同于 module.exports = { getItem: getItem, setItem: setItem, clear: clear };
属性名表达式
JavaScript 定义对象的属性,有两种方法。
// 方法一 obj.foo = true; // 方法二 obj['a' + 'bc'] = 123;
ES6 允许字面量定义对象时,用方法二(表达式)作为对象的属性名,即把表达式放在方括号内。
let propKey = 'foo'; let obj = { [propKey]: true, ['a' + 'bc']: 123 };
下面是另一个例子
let lastWord = 'last word'; const a = { 'first word': 'hello', [lastWord]: 'world' }; console.log(a['first word']); // "hello" console.log(a[lastWord]); // "world" console.log(a['last word']); // "world"
表达式还可以用于定义方法名。
let obj = { ['h' + 'ello']() { return 'hi'; } }; obj.hello() // hi
注意,属性名表达式如果是一个对象,默认情况下会自动将对象转为字符串[object Object]
,这一点要特别小心。
const keyA = { a: 1 }; const keyB = { b: 2 }; const myObject = { [keyA]: 'valueA', [keyB]: 'valueB' }; console.log(myObject); //{ '[object Object]': 'valueB' }
super 关键字
我们知道,this
关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super
,指向当前对象的原型对象。
const proto = { foo: 'hello' }; const obj = { foo: 'world', find() { return super.foo; // } }; Object.setPrototypeOf(obj, proto); // 设置obj实例的原型对象为proto console.log(obj.find()); // "hello"
上面代码中,对象obj.find()
方法之中,通过super.foo
引用了原型对象proto
的foo
属性。
注意,super
关键字表示原型对象时,只能用在对象的简写方法之中,用在其他地方都会报错。
const proto = { x: 'hello', foo() { console.log(this.x); }, }; const obj = { x: 'world', foo() { super.foo(); } } Object.setPrototypeOf(obj, proto); obj.foo() // "world"
上面代码中,super.foo
指向原型对象proto
的foo
方法,但是绑定的this
却还是当前对象obj
,因此输出的就是world
。
对象的扩展运算符
对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; console.log(x); // 1 console.log(y); // 2 console.log(z); // {a: 3, b: 4}
上面代码中,变量z
是解构赋值所在的对象。它获取等号右边的所有尚未读取的键(a
和b
),将它们连同值一起拷贝过来。
注意,解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。
let obj = { a: { b: 1 } }; let { ...x } = obj; obj.a.b = 2; x.a.b // 2
上面代码中,x
是解构赋值所在的对象,拷贝了对象obj
的a
属性。a
属性引用了一个对象,修改这个对象的值,会影响到解构赋值对它的引用。
扩展运算符的解构赋值,不能复制继承自原型对象的属性。
let o1 = {a:1}; let o2 = {b:2}; o2.__proto__ = o1; let {...o3} = o2; console.log(o3.b); // 2 console.log(o3.a); // undefined
下面是另一个例子。
const o = Object.create({ x: 1, y: 2 }); // 原型对象上的属性 console.log(o); // {} o.z = 3; // 自身的属性 let { x, ...newObj } = o; let { y, z } = newObj; x // 1 y // undefined z // 3 // Object.create创建一个新的对象{}返回被o接受,将现有对象{ x: 1, y: 2 }作为新对象的__proto__. // 上面代码中,变量x是单纯的解构赋值,所以可以读取对象o继承的属性;变量y和z是扩展运算符的解构赋值,只能读取对象o自身的属性,所以变量z可以赋值成功,变量y取不到值。
扩展运算符
对象的扩展运算符(...
)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。
let z = { a: 3, b: 4 }; let n = { ...z }; n // { a: 3, b: 4 }
由于数组是特殊的对象,所以对象的扩展运算符也可以用于数组。
let foo = { ...['a', 'b', 'c'] }; foo // {0: "a", 1: "b", 2: "c"}