属性的简洁表示法
ES6 允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
let a = 'name'
let obj = {a}
console.log(obj) // {a: "name"}
上面代码表明,ES6 允许在对象之中,直接写变量。这时,属性名为变量名, 属性值为变量的值。下面是另一个例子。
方法也可以简写
let obj = {
fn(){
console.log('aaa')
}
}
属性名表达式
JavaScript 定义对象的属性,有两种方法。
let obj = {};
obj.name = 1; // 第一种
obj['a'+'b'] = 2; // 第二种
console.log(obj) // {name:1,ab:2}
上面代码的方法一是直接用标识符作为属性名(es5),方法二是用表达式作为属性名,这时要将表达式放在方括号之内(es6)。
表达式也可以定义方法名
let obj = {
['a'+'b'](){
console.log('aaa')
}
}
obj.ab() // aaa
注意,属性名表达式与简洁表示法,不能同时使用,会报错。
方法的name属性
函数的name属性,返回函数名。对象方法也是函数,因此也有name属性。
let obj = {
fn(){}
}
console.log(obj.fn.name) // fn
上面代码中,方法的name属性返回函数名(即方法名)。
如果对象的方法使用了取值函数(getter)和存值函数(setter),则name属性不是在该方法上面,而是该方法的属性的描述对象的get和set属性上面,返回值是方法名前加上get和set。
let obj = {
get foo() {},
set foo(x) {}
};
// console.log(obj.foo.name) // TypeError: Cannot read property 'name' of undefined
let descriptor = Object.getOwnPropertyDescriptor(obj,'foo')
console.log(descriptor.get.name) // get foo
有两种特殊情况:bind方法创造的函数,name属性返回bound加上函数的名字;Function构造函数创造的函数,name属性返回
function fn(){
}
console.log(fn.bind().name) // bound fn
console.log(new Function().name) // anonymous
如果对象的方法是一个 Symbol 值,那么name属性返回的是这个 Symbol 值的描述。
let key = Symbol('name')
let obj = {
[key](){}
}
console.log(obj[key].name) // [name]
属性的可枚举和遍历
- 可枚举性
对象的每个属性都有一个描述对象(Descriptor),用来控制该属性的行为。Object.getOwnPropertyDescriptor方法可以获取该属性的描述对象
let obj = {
name:'1'
}
let descriptor = Object.getOwnPropertyDescriptor(obj,'name')
console.log(descriptor) // {value: "1", 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一共有5种方法可以遍历对象的属性
- for...in :循环遍历对象自身的和继承的可枚举属性(不含Symbol属性)
- Object.keys(): 返回一个数组,包括对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)的键名
- Object.getOwnPropertyNames:返回一个数组,包含对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。
let obj = {
name:'1',
age:11
}
console.log(Object.getOwnPropertyNames(obj)) // ["name", "age"]
- Object.getOwnPropertySymbols:返回一个数组,包含对象自身的所有 Symbol 属性的键名。
let name = Symbol('mm')
let obj = {
[name](){},
age:11
}
console.log(Object.getOwnPropertySymbols(obj)) // [Symbol(mm)]
- Reflect.ownKeys(obj):返回一个数组,包含对象自身的所有键名,不管键名是Symbol或字符串,也不管是否可枚举
let name = Symbol('mm')
let obj = {
[name](){},
age:11
}
console.log(Reflect.ownKeys(obj)) // ["age", Symbol(mm)]
以上的5种方法遍历对象的键名,都遵守同样的属性遍历的次序规则
- 首先遍历所有数值减,按照竖直升序排列
- 其次遍历所有字符串健,按照加入事件升序排列
- 最后遍历所有Symbol键,按照加入时间排序
super 关键字
我们知道,this关键字总是指向函数所在的当前对象,ES6 又新增了另一个类似的关键字super,指向当前对象的原型对象。
const proto = {
foo: 'hello'
};
const obj = {
foo: 'world',
find() {
return super.foo;
}
};
Object.setPrototypeOf(obj, proto);
console.log(obj.find()) // "hello"
上面代码中,对象obj.find()方法之中,通过super.foo引用了原型对象proto的foo属性。
注意,super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。
对象的扩展运算符
对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面
let {a,...b} = {a:1,b:2,c:3,d:4}
console.log(a,b) // 1 {b: 2, c: 3, d: 4}
解构赋值必须是最后一个参数,否则会报错。
注意,解构赋值的拷贝是浅拷贝,即如果一个键的值是复合类型的值(数组、对象、函数)、那么解构赋值拷贝的是这个值的引用,而不是这个值的副本。
let obj = {a:1,b:{c:2}}
let {a,...x} = obj
obj.b.c = 222;
console.log(x) // {b: {c: 222}}