返回顶部

js对象的那些事...可扩展特性(extensible)和对象的属性的四个特性(value、writable、enumerable、configurable)

对象相信大家已经非常熟悉了,作为javascript中最最基本的类型,今天我们来谈谈其中奥秘!

let obj = {a:0,b:1}
console.log(obj.a)    // 0
console.log(obj['b']) // 1
obj.a = 2
obj['b'] = 2
console.log(obj)      // {a:2,b:2}
obj.c = 2
obj['d'] = 2
console.log(obj)      // {a:2,b:2,c:2,d:2}

这里用对象字面量的方式创建了一个简单的对象,我们可以通过对象调用属性的方式(对象名.属性名)或者数组下标的方式(对象名['属性名'])来获取对象或者修改对象的属性值、和添加新的属性。那有没有可能这些操作会被受到限制呢?这主要和对象的一些特性有关

对象的特性

  可扩展性(extensible):是否可以给对象添加新属性(是对象的特性,不属于对象属性的特性)

对象属性的特性

  属性值(value):该属性的值;

  可写(writable):是否可以修改该属性的值;

  可枚举(enumerable):是否可以通过for/in循环或者Obeject.keys()方法枚举该属性;

  可配置(configurable):是否可以删除该属性、是否可以修改该属性的特性;

默认情况下我们所用代码所创建的对象,其的属性都是可写、可枚举、可配置的,所以我们才可以实现上述代码的操作。我们还可以通过一些操作对对象的属性进行一些限制。

对象属性特性的相关操作

  Object.getOwnPropertyDescriptor() 查询自有属性的特性

  Object.defineProperty() 设置属性的特性(如果没有该属性则创建)

  Object.defineProperties() 创建或者修改多个属性的特性

let obj = {a:0,b:1}
console.log(Object.getOwnPropertyDescriptor(obj,'a')) 
// { value: 0, writable: true, enumerable: true, configurable: true }

可以看到我们通过Object.getOwnPropertyDescriptor查看obj对象的'a'属性的特性是,值为0、可写、可枚举、可配置

let obj = {a:0,b:1}
Object.defineProperty(obj,'a',{
    value: 1
})
console.log(obj) // { a: 1, b: 1 }
Object.defineProperty(obj,'a',{
    writable: false
})
obj.a = 100
obj.b = 100
console.log(obj) // { a: 1, b: 100 }

可以看到我们通过Object.defineProperty将obj的'a'属性的value特性修改为1,可以起到修改属性值的效果;设置'a'属性特性为writable:false使得'a'属性不可写,我们就不能通过obj.a的方式或者obj['a']的方式修改a属性的值(不报错但修改失败)。但我们可以在不可写的情况下修改'a'属性的value特性来实现修改'a'属性的值

let obj = {a:0,b:1}
Object.defineProperty(obj,'a',{
    writable: false
})
obj.a = 100
obj.b = 100
console.log(obj) // { a: 1, b: 100 }
Object.defineProperty(obj,'a',{
    value: 100
})
console.log(obj) // { a: 100, b: 100 }

我们修改属性的enumerable特性使属性不可枚举

let obj = {a:0,b:1}
for(let prop in obj){
    console.log(prop)
}
//  a 
//  b 
Object.defineProperty(obj,'a',{
    enumerable: false
})
for(let prop in obj){
    console.log(prop,obj[prop])
}
// a 

不可枚举的属性无法被for/in循环检测到,就和对象继承而来的属性一样(继承的属性默认不可枚举)。

设置属性特性不可配置(configurable:false)的时后要注意,不可配置和其他特性搭配有着不同的效果

  属性不可配置时,不能再将该属性该为可配置(所以不可配置是不可逆的)

let obj = {a:0,b:1}
Object.defineProperty(obj,'a',{
    configurable: false
})
Object.defineProperty(obj,'a',{ // 抛出异常 TypeError: Cannot redefine property: a
    configurable: true
})

  属性不可配置时不能删除该属性(不报错但不成功)

let obj = {a:0,b:1}
Object.defineProperty(obj,'a',{
    configurable: false
})
delete obj['a']
console.log(obj) // { a: 0, b: 1 }

  属性不可配置时不能修改该属性的enumerable值

let obj = {a:0,b:1}
Object.defineProperty(obj,'a',{
    configurable: false,
    enumerable: true,
    writable: true
})
Object.defineProperty(obj,'a',{ // 抛出异常 TypeError: Cannot redefine property: a
    enumerable: false
})

  属性不可配置但可写时,可以修改value特性、可以设置不可写(writable从true改为false)

let obj = {a:0,b:1}
Object.defineProperty(obj,'a',{
    configurable: false,
    enumerable: true,
    writable: true
})
Object.defineProperty(obj,'a',{
    value: 100,
    writable: false
})
console.log(obj)  // { a: 100, b: 1 }

  属性不可配置且不可写时,不可修改value特性,不可修改writable特性(从false该为true)

let obj = {a:0,b:1}
Object.defineProperty(obj,'a',{
    configurable: false,
    enumerable: true,
    writable: false
})
Object.defineProperty(obj,'a',{ // 抛出异常 TypeError: Cannot redefine property: a
    value: 100,
})
Object.defineProperty(obj,'a',{ // // 抛出异常 TypeError: Cannot redefine property: a
    writable: true,
})

Object.defineProperties就是一次可以修改多个属性,像这样,没什么好说的。

let obj = {a:0,b:1}
Object.defineProperties(obj,{
    a: { value: 100},
    b: { value: 200}
})
console.log(obj) // { a: 100, b: 200 }

 

接下来讲讲对象的扩展特性(extensible),和对象的几种状态(封存、冻结)

  对象不可扩展:不可给对象添加新属性、不可修改对象原型

  对象被封存:不可扩展、不可配置

  对象被冻结: 不可扩展、不可配置、不可写

判断对象状态的操作

  Object.isExtensible(): 判断对象是否可扩展

  Object.isSealed(): 判断对象是否被封存

  Object.isFrozen():判断对象是否被冻结

修改对象状态的操作:(注意以下操作和设置对象属性不可配置一样都是不可逆的)

  Object.preventExtensions():使对象不可扩展

  Object.seal():封存对象

  Object.freeze():冻结对象

// 不可扩展对象
let obj = {a:0,b:1} console.log(obj.__proto__) // [Object: null prototype] {} console.log(Object.isExtensible(obj)) // true Object.preventExtensions(obj) console.log(Object.isExtensible(obj)) // false obj.c = 1 console.log(obj) // { a: 0, b: 1 } let obj2 = {d: 100} obj.__proto__ = obj2 // 抛出异常 TypeError: #<Object> is not extensible

可以看到,对象不可扩展添加新属性不成功(严格模式抛出异常),不能修改对象原型

// 封存对象
let obj = {a:0,b:1}
console.log(Object.isSealed(obj)) // false
Object.seal(obj)
console.log(Object.isSealed(obj)) // true
console.log(Object.isExtensible(obj)) // false
console.log(Object.getOwnPropertyDescriptor(obj,'a')) // { value: 0, writable: true, enumerable: true, configurable: false }
console.log(Object.getOwnPropertyDescriptor(obj,'b')) // { value: 1, writable: true, enumerable: true, configurable: false }

可以看到,对象被封存了之后,对象变得不可扩展,其所有自有属性变得不可配置

// 冻结对象
let obj = {a:0,b:1}
console.log(Object.isFrozen(obj)) // false
Object.freeze(obj)
console.log(Object.isFrozen(obj)) // true
console.log(Object.isSealed(obj)) // true
console.log(Object.isExtensible(obj)) // false
console.log(Object.getOwnPropertyDescriptor(obj,'a')) // { value: 0, writable: false, enumerable: true, configurable: false }
console.log(Object.getOwnPropertyDescriptor(obj,'b')) // { value: 1, writable: false, enumerable: true, configurable: false }

可以看到,对象被冻结了之后,对象变得不可扩展,其所有自有属性变得不可配置、不可写;并且被冻结的对象也默认被封存(isSealed()返回true)

 

另外对象除了有普通的属性外还有一种叫访问器属性的东西(get、set),这种访问器属性没有value和writable特性

// 访问器属性
let obj = {a:0, get b() {return b}, set b(newValue) {b = newValue} } obj.b = 2 console.log(obj) // { a: 0, b: [Getter/Setter] } console.log(obj.b) // 2 console.log(Object.getOwnPropertyDescriptor(obj,'b')) // { // get: [Function: get b], // set: [Function: set b], // enumerable: true, // configurable: true // }

当然你也可以通过Object.defineProperty()修改访问器属性的get和set,还可以将访问器属性修改为普通属性(前提是对象该属性是可配置)

访问器属性修改为普通属性,默认value值为undefined,默认writable:false

let obj = {a:0,
    get b() {return b},
    set b(newValue) {b = newValue}
}
Object.defineProperty(obj,'b',{
    writable: true
})
console.log(Object.getOwnPropertyDescriptor(obj,'b'))
// { value: undefined,writable: true,enumerable: true,configurable: true}
let obj = {a:0,
    get b() {return b},
    set b(newValue) {b = newValue}
}
Object.defineProperty(obj,'b',{
    value: 100
})
console.log(Object.getOwnPropertyDescriptor(obj,'b')) 
// { value: 100, writable: false, enumerable: true, configurable: true }

同样的,普通属性也可以修改成访问器属性(默认的get/set为undefined)

let obj = {a:0,
    get b() {return b},
    set b(newValue) {b = newValue}
}
Object.defineProperty(obj,'a',{
    get() {return 2},
})
console.log(Object.getOwnPropertyDescriptor(obj,'a'))
// {
//     get: [Function: get],
//     set: undefined,
//     enumerable: true,
//     configurable: true
// }
console.log(obj.a) // 2
obj.a = 100
console.log(obj.a) // 2

像这样只有get没有set的访问器属性相当于只读属性,不可修改该属性的值

 

 

 

 

 

 


 

 

 

 

posted on 2022-02-28 15:32  孤僻而冷漠  阅读(823)  评论(0编辑  收藏  举报

导航