JS属性特性(属性描述符)

开发 前端
ECMAScript 5 中定义了一个名叫“属性描述符”的对象,用于描述了的各种特征。“属性描述符”对象只能在Object.defineProperty或Object.defineProperties中使用。

[[174790]]

概念

ECMAScript 5 中定义了一个名叫“属性描述符”的对象,用于描述了的各种特征。属性描述符对象有4个属性:

  • configurable:可配置性,控制着其描述的属性的修改,表示能否修改属性的特性,能否把属性修改为访问器属性,或者能否通过delete删除属性从而重新定义属性。默认值为true。
  • enumerable:可枚举性,表示能否通过for-in遍历得到属性。默认值为true。
  • writable:可写性,表示能否修改属性的值。默认值为true。
  • value:数据属性,表示属性的值。默认值为undefined。

除了上面的属性,还有两个存取器属性,分别是get和set,可以代替value和writable。

  • get:在读取属性时调用的函数。只指定get则表示属性为只读属性。默认值为undefined。
  • set:在写入属性时调用的函数。只指定set则表示属性为只写属性。默认值为undefined。

使用

“属性描述符”对象只能在Object.defineProperty或Object.defineProperties中使用。

API 用法

Object.defineProperty:https://developer.mozilla.org...

Object.defineProperties: https://developer.mozilla.org...

var hello = {} 
 
Object.defineProperty(hello, 'girl', { 
    configurable: false
    enumberable: false
    writable: true
    value: 'sexy' 
}) 
 
// 存取器 
Object.defineProperty(hello, 'woman', { 
    configurable: false
    enumberable: false
    get: function() { 
        return this.girl 
    }, 
    setfunction(val) { 
        this.girl = val 
    } 
}) 
 
// 定义多个属性 
Object.defineProperties(hello, { 
    boy: { 
        configurable: false
        enumberable: false
        writable: false
        value: 'handsome' 
    }, 
    man: { 
        configurable: false
        enumberable: false
        writable: true
        get: function() { 
            return this.boy 
        } 
    } 
})  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.

当用Object.defineProperty或Object.defineProperties操作(新建或者修改)那些不允许创建或修改的属性时,会抛出类型错误异常。

// 此例子运行在前面的例子的基础上 
Object.defineProperty(hello, 'boy', { 
    writable: true 
})    // Uncaught TypeError: Cannot redefine property: boy  
  • 1.
  • 2.
  • 3.
  • 4.

因为前面boy属性已经被设置为不可配置,所以这里修改writable会抛出类型错误异常。

通过Object.getOwnPropertyDescriptor或者Object.getOwnPropertyDescriptors可以得到属性描述符。

API 用法

Object.getOwnPropertyDscriptor:https://developer.mozilla.org...

Object.getOwnPropertyDescriptors:https://developer.mozilla.org...

规则

var rules = { 
    common: 'test' 
 
  • 1.
  • 2.
  • 3.

如果属性是不可配置的,则不能修改它的可配置性和可枚举性。

Object.defineProperty(rules, 'rule1', { 
    configurable: false
    enumberable: false 
}) 
 
// 修改configurable会抛出类型错误异常 
Object.defineProperty(rules, 'rule1', { 
    configurable: true 
})    // Uncaught TypeError: Cannot redefine property: rule1 
 
// 修改enumberable不会抛出异常,但enmuberable没有被修改 
Object.defineProperty(rules, 'rule1', { 
    enumberable: true 
}) 
Object.getOwnPropertyDescriptor(rules, 'rule1')    // Object {value: undefined, writable: false, enumerable: false, configurable: false 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

如果存取器属性是不可配置的,则不能修改get和set方法,也不能将它转换为数据属性。

Object.defineProperty(rules, 'rule2', { 
    configurable: false
    enumberable: false
    get: function() { 
        return this.common 
    }, 
    setfunction(val) { 
        this.common = val 
    } 
}) 
 
// 修改get或者set方法会抛出类型错误异常 
Object.defineProperty(rules, 'rule2', { 
    get: function() { 
        return this.common + 'rule2' 
    } 
})    // Uncaught TypeError: Cannot redefine property: rule2 
 
Object.defineProperty(rules, 'rule2', { 
    setfunction(val) { 
        this.common = 'rule2' 
    } 
})    // Uncaught TypeError: Cannot redefine property: rule2 
 
// 将它转换为数据属性同样会抛出类型错误异常 
Object.defineProperty(rules, 'rule2', { 
    value: 'rule2' 
})    // Uncaught TypeError: Cannot redefine property: rule2  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

如果数据属性是不可配置的,则不能将它转换为存取器属性;同时,也不能将它的可写性从false修改为true,但可以从true修改为false。

Object.defineProperty(rules, 'rule3', { 
    configurable: false
    writable: false
    value: 'rule3' 
}) 
 
// 修改writable为true会抛出类型错误异常 
Object.defineProperty(rules, 'rule3', { 
    writable: true 
}) 
 
 
Object.defineProperty(rules, 'rule4', { 
    configurable: false
    writable: true
    value: 'rule4' 
}) 
 
// 可以修改writable为false 
Object.defineProperty(rules, 'rule4', { 
    writable: false 
}) 
Object.getOwnPropertyDescriptor(rules, 'rule4')    //   Object {value: "rule4", writable: false, enumerable: false, configurable: false 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

如果数据属性是不可配置且不可写的,则不能修改他的值;如果是可配置但不可写,则可以修改他的值(实际上是先将它标记为可写的,然后修改它的值,***再将它标记回不可写)。

其实这里所说的修改值,是通过Object.defineProperty或Object.defineProperties方法修改。通过直接赋值的方法在数据属性不可配置的情况下是不能修改属性值的。

Object.defineProperty(rules, 'rule5', { 
    configurable: false
    writable: false
    value: 'rule5' 
}) 
 
// 修改属性值会抛出类型错误异常 
Object.defineProperty(rules, 'rule5', { 
    value: 'rule55' 
})    // Uncaught TypeError: Cannot redefine property: rule5 
 
rules.rule5 = 'rule55' 
// 值没有被修改,也不会抛出异常 
rules.rule5            // 'rule5' 
 
 
Object.defineProperty(rules, 'rule6', { 
    configurable: true
    writable: false
    value: 'rule6' 
}) 
 
// 修改属性值 
Object.defineProperty(rules, 'rule6', { 
    value: 'rule66' 
}) 
rules.rule6            // 'rule66' 
 
rules.rule6 = 'rule6' 
// 值没有被修改,也不会修改 
rules.rule6            // 'rule6'  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.

只指定set不能读,如果尝试读取该属性值,返回undefined。(红宝书上说在严格模式下才抛出异常,但没有)

Object.defineProperty(rules, 'rule7', { 
    get: function() { 
        return this.common 
    } 
}) 
rules.rule7 = 'rule7'    // Uncaught TypeError: Cannot redefine property: rule7  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

如果对象是不可扩展的,则可以编辑已有的自有属性,但不能给它添加新属性。

操作对象可扩展性的API有三个:Object.preventExtensions、Object.seal、Object.freeze。

API 用法

Object.preventExtensions:https://developer.mozilla.org...

Object.seal:https://developer.mozilla.org...

Object.freeze:https://developer.mozilla.org...

Object.isExtensions:https://developer.mozilla.org...

Object.isSealed:https://developer.mozilla.org...

Object.isFrozen:https://developer.mozilla.org...

使用Object.preventExtensions可以将对象转换为不可扩展。

使用Object.isExtensions来判断对象是否可扩展。

var ex = {} 
Object.defineProperty(ex, 'ex1', { 
    configurable: true
    writable: true
    value: 'ex1' 
}) 
Object.isExtensible(ex)        // true 
Object.preventExtensions(ex) 
Object.isExtensible(ex)        // false 
 
// 可以修改已有的属性 
Object.defineProperty(ex, 'ex1', { 
    writable: false
    value: 'ex11' 
}) 
Object.getOwnPropertyDescriptor(ex, 'ex1')    // Object {value: "ex11", writable: false, enumerable: false, configurable: true
 
// 添加属性会抛出类型错误异常 
Object.defineProperty(ex, 'ex2', { 
    value: 'ex2' 
})    // Uncaught TypeError: Cannot define property:ex2, object is not extensible.  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

使用Object.seal除了可以将对象转换为不可扩展的,还可以将对象的所有自有属性都转换为不可配置的。即不能给对象添加新属性,而且它已有的属性也不能删除或者配置(这里同样会遵循前面的规则)。

使用Object.isSealed来判断对象是否封闭(sealed)。

var se = {} 
Object.defineProperty(se, 'se1', { 
    configurable: true
    writable: false
    value: 'se1' 
}) 
Object.isSealed(se)        // false 
Object.seal(se) 
Object.isSealed(se)        // true 
 
// 修改已有的属性会抛出类型错误异常 
Object.defineProperty(se, 'se1', { 
    writable: true
    value: 'se11' 
})    // Uncaught TypeError: Cannot redefine property: se1 
 
// 添加属性会抛出类型错误异常 
Object.defineProperty(se, 'se2', { 
    value: 'se2' 
})    // Uncaught TypeError: Cannot define property:se2, object is not extensible.  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.

使用Object.freeze除了将对象转换为不可扩展的和将其属性转换为不可配置的之外,还可以将自有属性转换为只读。(如果对象设置了set,存取器属性将不会受影响,仍可以调用set方法,而且不会抛出异常,但如果set方法是改变该对象的属性,则不能修改成功)

使用Object.isFrozen来检测对象是否冻结(frozen)。

var fr = {} 
Object.defineProperty(fr, 'fr1', { 
    configurable: true
    writable: false
    value: 'fr1' 
}) 
Object.isFrozen(fr)        // false 
Object.freeze(fr) 
Object.isFrozen(fr)        // true 
 
// 修改已有的属性会抛出类型错误异常 
Object.defineProperty(fr, 'fr1', { 
    writable: true
    value: 'fr11' 
})    // Uncaught TypeError: Cannot redefine property: fr1 
 
// 添加属性会抛出类型错误异常 
Object.defineProperty(fr, 'fr2', { 
    value: 'fr2' 
})    // Uncaught TypeError: Cannot define property:fr2, object is not extensible. 
 
fr.fr1 = 'fr11' 
// 不能修fr1属性 
fr.fr1            // 'fr1' 
var set = {} 
Object.defineProperty(set'set1', { 
    configurable: true
    value: 'set1' 
}) 
Object.defineProperty(set'set2', { 
    configurable: true
    setfunction(val) { 
        this.set1 = val 
    } 
}) 
Object.isFrozen(set)        // false 
Object.freeze(set
Object.isFrozen(set)        // true 
 
set.set2 = 'set2' 
set.set1                    // 'set1'  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.

结语

我对属性描述符很不熟悉,主要是因为平时用得少。不过最近,开始学写一些小的库(虽然很挫),就感觉属性描述符有使用的场景了。我暂时能想到的就是将库对象的一些属性设置为只读,以防止对象的一些属性被用户重写覆盖了。还有一个用法是在知乎和学vue的时候知道的,就是通过getter和setter实现“监听”对象属性的数据更新(在这里挖一个坑。后面学习一下这种方法,再写一篇“监听”对象属性的数据更新的文章)。

***,如果大家知道更多属性描述符的使用后场景,希望大家能在评论区留下你们的高见。

责任编辑:庞桂玉 来源: segmentfault
相关推荐

2012-08-08 10:31:41

IBMdW

2017-02-05 10:06:53

Python黑魔法描述符

2009-07-08 09:46:45

Servlet注释部署描述符

2025-01-10 15:13:38

2023-04-06 15:22:15

Linux进程系统

2019-03-05 22:15:08

BashLinux命令

2020-02-07 18:16:01

进程线程底层原理

2009-09-04 14:04:53

C#文档

2023-12-13 14:01:34

Elasticsea文件描述符操作系统

2021-06-18 06:02:24

内核文件传递

2009-08-27 10:01:52

C#自动属性

2021-05-19 14:48:58

Linux文件fd

2009-08-28 09:30:48

C#命名属性

2009-09-07 15:49:55

C#属性化的方法

2019-07-05 14:20:45

RPC服务器模型

2010-09-15 17:05:33

CSS display

2009-12-28 09:19:21

ADO属性

2009-12-22 13:49:40

ADO.net属性

2019-07-09 14:30:16

LinuxJava 服务器

2019-07-09 15:30:31

Linuxulimit文件描述符
点赞
收藏

51CTO技术栈公众号