adodo0829 / blog

搭建知识体系
29 stars 4 forks source link

对象的属性描述符 #29

Open adodo0829 opened 4 years ago

adodo0829 commented 4 years ago

对象的属性描述

对象的属性描述符是用来描述对象的值、是否可配置、是否可修改以及是否可枚举...

数据属性 访问器属性

数据属性

数据属性包含一个数据值的位置, 在这个位置可以读取和写入值。 数据属性有4个特性:

1.Configurable(可配置性): 是否可以使用delete删除属性以及是否可以修改属性描述符的特性,默认值为true
2.Enumerable(可枚举性): 是否出现在对象的属性枚举中,比如是否可用for-in循环返回该属性,默认值为true
3.Writable(可写性): 是否可以修改属性的值,默认值为true
4.Value(属性值): 包含这个属性的数据值,读取属性值的时候,从这个位置读;
                写入属性值的时候,把新值保存在这个位置。默认值为undefined

访问器属性

对象属性是名字、值和一组属性描述符构成的。而属性值可以用一个或两个方法替代,这两个方法就是getter和setter。 而这种属性类型叫访问器属性:

1.Configurable(可配置性): 是否可以使用delete删除属性以及是否可以修改属性描述符的特性,默认值为true
2.Enumerable(可枚举性): 是否出现在对象的属性枚举中,比如是否可用for-in循环返回该属性,默认值为true
3.getter: 在读取属性时调用的函数。默认值为undefined
4.setter: 在写入属性时调用的函数。默认值为undefined

和数据属性不同,访问器属性不具有可写性(Writable)。如果属性同时具有getter和setter方法,那么它是一个读/写属性。
如果它只有getter方法,那么它是一个只读属性。
如果它只有setter方法,那么它是一个只写属性。读取只写属性总是返回undefined

描述符方法(都来自于 Object 构造函数)

Object.getOwnPropertyDescriptor(obj, prop)

用于查询一个属性的描述符,并以对象的形式返回

var obj = {a:1};
Object.getOwnPropertyDescriptor(obj, 'a')
// {value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor(obj, 'b') // 对象不存在改属性时返回 undefined

Object.defineProperty(obj, prop, config)

用于创建或配置对象的一个属性的描述符,返回配置后的对象 使用该方法创建或配置对象属性的描述符时,如果不针对该属性进行描述符的配置,则该项描述符默认为false

var obj = {}
Object.defineProperty(obj, 'a', {
    value:1,
    writable: true
}) // 返回 { a: 1 }

Object.getOwnPropertyDescriptor(obj,'a') 
// {value: 1, writable: true, enumerable: false, configurable: false}

Object.defineProperties()

用于创建或配置对象的多个属性的描述符,返回配置后的对象

var obj = { a:1 }
Object.defineProperties(obj,{
    a: { writable: false },
    b: { value: 2 }
  })

Object.create()

使用指定的原型和属性来创建一个对象

var o = Object.create(Object.prototype,
  {
    a: { writable: false, value: 1, enumerable: true }
  }
)
o // { a:1 }
Object.getOwnPropertyDescriptor(obj,'a')
// {value: 1, writable: false, enumerable: true, configurable: true}

描述符详解

可配置性(Configurable)

可配置性决定是否可以使用delete删除属性,以及是否可以修改属性描述符的特性,默认值为true

# 设置Configurable:false后,无法使用delete删除属性
var o = { a:1 };
Object.defineProperty(o, 'a', {
    configurable:false
});
delete o.a;      // false
console.log(o.a);// 1

# 严格模式下删除为configurable为false的属性,会提示类型错误

# 使用var命令声明变量时, 变量的configurable为false
var a = 1;
Object.getOwnPropertyDescriptor(this,'a');
// {value: 1, writable: true, enumerable: true, configurable: false}

# 设置Configurable:false后,将无法再使用defineProperty()方法来修改属性描述符
// 设置Configurable:false后,只允许writable的状态从true变为false

可写性(writable)

可写性决定是否可以修改属性的值,默认值为true

# 设置 writable:false后,赋值语句会失效
var o = {a:1};
Object.defineProperty(o, 'a', {
    writable: false
});
o.a; // 1
o.a = 22 // 不起作用

# 严格模式下通过赋值语句为writable为false的属性赋值,会报错

可枚举性(Enumerable)

可枚举性决定属性是否出现在对象的属性枚举中,具体来说,for-in循环、Object.keys方法、JSON.stringify方法是否会取到该属性 用户定义的普通属性默认是可枚举的,而原生继承的属性默认是不可枚举的

var o = {a:1, b: 'bb'};
for(var i in o){
  console.log(o[i]);// 1 bb
}
// 由于原生继承的属性默认不可枚举,所以只取得自定义的属性 a:1, b:'bb'

var o = {a:1};
Object.defineProperty(o,'a',{ enumerable:false });
for(var i in o){
    console.log(o[i]); // undefined
}

# propertyIsEnumerable()方法用于判断对象的属性是否可枚举
var o = { a:1 };
console.log(o.propertyIsEnumerable('a')); // true

get和set

get是一个隐藏函数,在获取属性值时调用。 set也是一个隐藏函数,在设置属性值时调用,它们的默认值都是undefined。 Object.definedProperty()中的get和set对应于对象字面量中get和set方法

getter和setter取代了数据属性中的value和writable属性


# 给只设置get方法,没有设置set方法的对象赋值会静默失败,在严格模式下会报错
var o = {
get a() {
return 2;
}
}    
o.a // 2
o.a = 111 // 没有设置set方法, 设置值无效

Object.defineProperty(o,'a',{ get: function(){ return 22; } }) o.a // 22

只设置set方法,而不设置get方法,则对象属性值为undefined

var o = { set a(val) { return 2; } }
o.a = 1; console.log(o.a); // undefined

Object.defineProperty(o,'a',{ set: function(){ return 2; } }) o.a = 1; console.log(o.a); // undefined

set和get方法一起出现时

var o = { get a(){ return this._a; }, set a(val){ this._a = val + 100; } } o.a = 1; // 设置值 console.log(o.a); // 访问,获取值 101

Object.defineProperty(o,'a',{ get: function(){ return this._a; }, set: function(val){ this._a = val + 100; } }) o.a = 1; console.log(o.a); // 101



## 对象的状态
属性描述符只能用来控制对象中一个属性的状态。而如果要控制对象的状态, 需要用到 Object 构造函数的一些其他方法
- Object.preventExtensions()
> 使一个对象无法再添加新的属性,并返回当前对象

- Object.isExtensible()
> 检测该对象是否可以扩展

- Object.seal()
> 对象封印又叫对象密封,使一个对象不可扩展并且所有属性不可配置,并返回当前对象

- Object.isSealed()
> 检测该方对象是否被封印

- Object.freeze()
> Object.freeze()方法使一个对象不可扩展,不可配置,也不可改写,变成一个仅可以枚举的只读常量,并返回当前对象

- Object.isFrozen()
> 检测一个对象是否被冻结