一、对象的定义与赋值
我们经常使用的定义与赋值方法 obj.xxx = value 或 obj['xxx'] = value,并且可以定义任意类型的值,如下所示:
let obj = {};
obj.name = 'bjl';
obj['age'] = 18;
obj.sayHi = function() {console.log('Hi')};
console.log(obj) // {name: 'bjl', age: 18, sayHi: fn}
二、Object.defineProperty() 语法
Object.defineProperty() 的作用就是直接在一个对象上定义新属性,或者修改一个已经存在的属性。语法格式为:
Object.defineProperty(object, propName, descriptor);
- object:需要定义属性的当前对象
- propName:当前需要定义的属性名
- descriptor:属性描述符
三、属性描述符
都有哪些属性描述符呢?如下:
- value:设置属性的值
- writable:值是否可以重写
- set:目标属性设置值的方法
- get:目标属性获取值的方法
- enumerable:目标属性是否可以被枚举(是否可以遍历)
- configurable:目标属性是否可以被删除或是否可以再次修改特性
通过 Object.defineProperty() 为对象定义属性有两种形式,但不能混合使用,分别是数据描述符、存取描述符。
1. 数据描述符(value、writable)
当使用了 value 和 writable 属性,不允许使用 getter 或 setter 这两个方法。
- value:设置属性值
let obj = {};
Object.defineProperty(obj, 'name', {
value: 'bjl'
})
console.log(obj.name) // bjl
- writable:描述对象是否可写,默认值为false,表示只读属性
let obj = {};
Object.defineProperty(obj, 'name', {
value: 'bjl'
})
obj.name = 'bao';
console.log(obj.name) // bjl
let obj = {};
Object.defineProperty(obj, 'name', {
value: 'bjl',
writable: true // 表示可以进行修改
})
obj.name = 'bao';
console.log(obj.name) // bao
let obj = {
name: 'bjl'
};
Object.defineProperty(obj, 'name', {
writable: false //手动设置name属性不可被修改
})
obj.name = 'bao';
console.log(obj.name) // bjl
当声明一个对象时,它里面的属性的内部属性的默认值都为 true,也就是说 writable 这时的默认值为 true,这就是为什么上面的 name 如果不想被修改就需要手动去设置 writable 属性的原因。我们可以使用 Object.getOwnPropertyDescriptors() 去检测属性的内部属性的具体描述,如下:
let obj = {
name: 'bjl'
}
console.log(Object.getOwnPropertyDescriptors(obj))
Object.defineProperty(obj, 'age', {
value: 18
})
console.log(Object.getOwnPropertyDescriptors(obj))
打印如下:
2. 存储描述符(get、set)
当使用 get 或 set 方法,不允许使用 value 和 writable 这两个属性。
- get:一个给属性提供 getter 的方法,默认值为 undefined
- set:一个给属性提供 setter 的方法。默认值为 undefined,该方法接受唯一的参数,并将该参数的新值分配给该属性
let obj = {};
let temp = 'bjl';
Object.defineProperty(obj, 'name', {
get: function() {
return temp
},
set: function(val) {
temp = val
}
})
console.log(obj.name) // bjl
// 当修改属性时,就会触发方法里的set方法
obj.name = 'bao';
console.log(obj.name) // bao
3. enumerable:表示目标属性是否可以被枚举
有时我们会对对象进行遍历,只有被枚举的属性可以被遍历到,如下所示:
- for...in...
let obj = {
name: 'bjl',
age: 18
}
Object.defineProperty(obj, 'sex', {
value: '女'
})
for(let key in obj) {
console.log(key) // name age
}
- Object.keys()
let obj = {
name: 'bjl',
age: 18
}
Object.defineProperty(obj, 'sex', {
value: '女'
})
console.log(Object.keys(obj)) // ['name', 'age']
Object.keys(obj).map(key => {
console.log(key) // name age
})
我们可以看出,使用 Object.defineProperty() 添加的属性不能被枚举到,也就是不能被遍历到。Object.keys() 返回的是可以被枚举到的数组,所以打印结果如上所述。
添加 enumerable 描述属性可以改变,如下所示:
let obj = {
name: 'bjl',
age: 18
}
Object.defineProperty(obj, 'sex', {
value: '女',
enumerable: true
})
console.log(Object.keys(obj)) // ['name', 'age', 'sex']
Object.keys(obj).map(key => {
console.log(key) // name age sex
})
4. configurable:目标属性是否可以被删除或是否可以再次修改特性
- 对象原属性默认为 true,可以被删除
let obj = {
name: 'bjl',
age: 18
}
delete obj.name;
console.log(obj) // {age: 18}
- Object.defineProperty() 内部属性为 false,不可被删除
let obj = {
name: 'bjl',
age: 18
}
Object.defineProperty(obj, 'sex', {
value: '女'
})
delete obj.sex;
console.log(obj) // {name: 'bjl', age: 18, sex: '女'}
- 使用 configurable 属性设置可以删除属性
let obj = {
name: 'bjl',
age: 18
}
Object.defineProperty(obj, 'sex', {
value: '女',
configurable: true
})
Object.defineProperty(obj, 'name', {
configurable: true
})
delete obj.sex;
delete obj.name;
console.log(obj) // {age: 18}