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

简介:


概念

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...

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

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

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

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

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

API 用法

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

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

规则

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

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

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

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

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

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

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

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

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

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

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

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

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

操作对象可扩展性的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来判断对象是否可扩展。

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

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

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

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

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

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

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

结语

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

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


作者:chenBright

来源:51CTO

目录
打赏
0
0
0
0
16427
分享
相关文章
前端原生Js批量修改页面元素属性的2个方法
原生 Js 的 getElementsByClassName 和 querySelectorAll 都能获取批量的页面元素,但是它们之间有些细微的差别,稍不注意,就很容易弄错!
确定使用 `defer` 属性还是 `async` 属性来异步加载 JavaScript
【10月更文挑战第24天】选择使用 `defer` 属性还是 `async` 属性来异步加载 JavaScript 是一个需要综合考虑多个因素的决策。需要根据脚本之间的依赖关系、页面加载性能要求、脚本的功能和重要性等因素来进行权衡。在实际应用中,需要通过测试和验证来确定最适合的加载方式,以提供更好的用户体验和页面性能。
JavaScript与TypeScript的对比,分析了两者的特性及在实际项目中的应用选择
本文深入探讨了JavaScript与TypeScript的对比,分析了两者的特性及在实际项目中的应用选择。JavaScript以其灵活性和广泛的生态支持著称,而TypeScript通过引入静态类型系统,提高了代码的可靠性和可维护性,特别适合大型项目。文章还讨论了结合使用两种语言的优势,以及如何根据项目需求和技术背景做出最佳选择。
82 4
ECMAScript 6(以下简称 ES6)的出现为 JavaScript 带来了许多新的特性和改进,其中 let 和 const 是两个非常重要的关键字。
ES6 引入了 `let` 和 `const` 关键字,为 JavaScript 的变量管理带来了革新。`let` 提供了块级作用域和暂存死区特性,避免变量污染,增强代码可读性和安全性;`const` 用于声明不可重新赋值的常量,但允许对象和数组的内部修改。两者在循环、函数内部及复杂项目中广泛应用,有助于实现不可变数据结构,提升代码质量。
36 5
ECMAScript 6 的出现为 JavaScript 带来了许多新的特性和改进
这些只是ES6的一些主要特性,它们极大地增强了JavaScript的功能和表现力,使得JavaScript在大型应用开发、前端框架等领域能够更加高效地编写复杂的应用程序。
使用 `defer` 属性异步加载 JavaScript
【10月更文挑战第24天】使用 `defer` 属性异步加载 JavaScript 是一种有效的提高页面性能和用户体验的方法。通过合理设置 `defer` 属性,可以在不影响页面渲染的情况下异步加载脚本,并确保脚本的执行顺序。在实际应用中,需要根据具体情况选择合适的加载方式,并注意处理可能出现的问题,以确保页面能够正常加载和执行。
掌握现代化JavaScript:ECMAScript提案与特性
【10月更文挑战第13天】本文介绍了ECMAScript(ES)的最新提案与特性,包括可选链、空值合并运算符、类字段和顶层Await等。通过跟踪TC39提案、使用Babel或TypeScript、测试兼容性以及逐步迁移,开发者可以高效地采用这些新特性,简化代码、提高开发效率并增强应用功能。文章还提供了实战技巧,帮助开发者在现代Web开发中充分利用这些现代化的特性。
JavaScript ES6及后续版本:新增的常用特性与亮点解析
JavaScript ES6及后续版本:新增的常用特性与亮点解析
99 4
原生js如何获取dom元素的自定义属性
原生js如何获取dom元素的自定义属性
106 4
JavaScript新纪元:ES6+特性深度解析与实战应用
【10月更文挑战第29天】本文深入解析ES6+的核心特性,包括箭头函数、模板字符串、解构赋值、Promise、模块化和类等,结合实战应用,展示如何利用这些新特性编写更加高效和优雅的代码。
61 0
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等