对象的定义与赋值
obj.prop = value //第一种
obj['prop'] = vlue //第二种
Object.defineProperty(obj,prop,description) //第三种
前两种定义对象并赋值的方法简单明了,为什么还要使用第三种呢?
这是因为,前两种定义的对象的属性可以删除也可以修改。而Object.defineProperty()定义的属性可以设置不能修改或删除等,它可以通过描述符的设置进行更精准地控制对象属性。
javascript对象有三种类型地属性:
- 命名数据属性: 拥有一个确定的值的属性。这也是最常见的属性。
- 命名访问器属性: 通过getter和setter进行读取和赋值的属性
- 内部属性: 由于JS引擎内部使用的数据,不能通过js代码直接访问到,不可以设置。比如,每个对象都有一个内部属性
[[Prototype]]
。
属性描述符
刚才说到,Object.defineProperty()可以通过描述符精准控制对象属性。Object.defineProperty定义属性有两种形式,且不能混用!分别为数据描述符
和存取描述符
数据描述符
数据描述符有4个属性,如下
属性名 | 默认值 |
---|---|
value | undefined |
writable | false |
enumerable | false |
configurable | false |
let Person = {}
Object.defineProperty(Person,'name',{
value: 'jack',
writable: true // true表示该属性name可改变
})
如果没有设置writable属性,那么默认writable为false。即不可再修改。
Person.name = 'rose' //无效!打印出来仍然是 ‘Jack’
看完configurable后请回到这里!!!
存取描述符
属性 | 默认值 |
---|---|
get | undefined |
set | undefined |
enumerable | false |
configurable | false |
getter和setter是设置对象属性的另一种方法,如下
let Person = {}
let temp = null
Object.defineProperty(Person,'name',{
get: function(){
return temp
},
set: function(val){
temp = val
}
})
Person.name = 'jack'
console.log(Person.name)// 打印出jack,成功给Person设置了name属性,且赋值为jack
无论是 数据描述符
还是存取描述符
都有configrable和enumerable
//数据描述符的方法定义对象属性
let Person = {}
Object.defineProperty(Person,'name',{
value: 'jack',
configurable: false//描述属性是否可删除,是否可以重新定义
writable:true,//属性是否可更改
enumerable: true
})
delete Person.name //error Cannot delete propety 'name' of <#Object>
Object.defineProperty(Person,'name',{
value: 'rose'
})// error Cannot redefine property: name
由此可见,若configurable为false则,不可删除和重复定义属性
好了,configurable大致了解了,回到writable的位置。
之前说writable为false的情况下,对象的属性不可修改。
的确,writable为false的清空,对象的属性不可以赋值的方式直接修改
let Person = {}
Object.defineProperty(Person, 'name',{
value: 'jack',
writable: false,
configurable: true //请注意!这里说明可以重新定义属性
})
Object.defineProperty(Person, 'name',{
value: 'rose'
})
Person.name // 结果为rose
但是这个configurable有一个bug!!
let Person = {}
Object.defineProperty(Person, 'name',{
value: 'jack',
writable: true,
configurable: false //请注意!这里说明可以重新定义属性
})
Object.defineProperty(Person, 'name',{
value: 'rose'
})
Person.name // 它居然还能打印出rose?!!不管了,记住吧
delete Person.name // false 不能delete!
好了,看看enumerable。
let Person = {}
Object.defineProperty(Person,'name',{
value: 'jack',
enumerable: false
})
Person.gender = 'male'
Object.defineProperty(Person,'age',{
value: '18',
enumerable: true //定义数据为可枚举属性
})
Object.keys(Person ) // ['gender','age']
for(let item in Person){
console.log(item) // gender, age
}
普通对象属性赋值 vs Object.defineProperty
经过上面的了解,你可能发现了一个现象
Person.gender = 'male'
通过赋值得到的属性,可删除,可枚举,可修改。
也就是说 等价于
Object.defineProperty(Person,'gender',{ value: 'male', writable:true, configurable:true, enumerable:true })
而
Object.defineProperty(Person,'gender',{
value: 'male'
})
等价于
Object.defineProperty(Person,'gender',{
value: 'male',
writable:false,
configurable:false,
enumerable:false
})
不变性
- 对象常量
- 禁止扩展
- 密封
- 冻结
对象常量
let Person = {}
Object.defineProperty(Person,'name',{
value: 'jack',
writable: false, //不可重新赋值
configurable: false //不可删除
})
禁止扩展
使用Object.preventExtensions(…) 禁止对象再添加其他属性
(虽然不能扩展,但是能够配置)
var Person = {
name: 'jack'
}
Object.preventExtensions(Person)
Person.gender = 'male' //error Can't add property gender,
object is not extensible
密封
密封比禁止扩展更猛,不仅不能扩展,还不能配置!
使用Object.seal()
(但是它能直接修改Person.name哦~ )
var Person = {
name: 'jack'
}
Object.seal(Person)
冻结
这个是终极boss了吧,不能扩展,不能配置,连原有属性都不给修改了!!
Object.freeze()
var Person = {
name: 'jack'
}
Object.freeze(Person)
赋值的作用和影响
赋值可能会调用原型上的setter
let proto = {
get bar(){
console.log('Getter!')
return 'a'
}
}
let obj = Object.create(proto)
obj.bar = 'hello'// Cannot set property bar of #<Object> which has only a getter
console.log(obj.bar)// a
let proto = {
get bar(){
console.log('Getter!')
return 'a'
}
}
let obj = Object.create(proto)
obj.bar = 'hello'// Cannot set property bar of #<Object> which has only a getter
console.log(obj.bar)// a
//但是我可以通过定义操作呀!
Object.defineProperty(obj, 'bar', {
value: 'hello'
})
console.log(obj.bar) //hello