前言

  此文总结了属性描述符的作用和特性,以及限制对象操作的部分方法。

Object.defineProperty

  Object.defineProperty 用于指定对象属性的描述符。

  函数的第三个参数descriptor为属性的描述符,包括数据描述符和存取描述符两种。

var object = {}// 数据描述符
Object.defineProperty(object, 'foo', {configurable: true,enumerable: true,writable: true,value: 1,
})// 存取描述符
Object.defineProperty(object, 'bar', {configurable: true,enumerable: true,get() {},set() {},
})

  注意属性描述符固定包括configurableenumerablewritablevalue四个键,存取描述符固定包括configurableenumerablegetset四个键。

描述符中公共键为configurableenumerable,数据描述符的writablevalue键成对,存取描述符的getset键成对

默认键值

  属性描述符的键是有默认值的,但是由于对象属性的指定方式不同,存在差异。

Object.getOwnPropertyDescriptors 用于获取对象自身所有属性的描述符

  字面量方式。

var object = {foo: 1,get bar() {},
}Object.getOwnPropertyDescriptors(object)
// {//   bar: {//     configurable: true,
//     enumerable: true,
//     get: f bar(),
//     set: undefined,
//   },
//   foo: {//     configurable: true,
//     enumerable: true,
//     value: 1,
//     writable: true,
//   },
// }

  defineProperty方式。

var object = {}Object.defineProperty(object, 'foo', {value: 1,
})
Object.defineProperty(object, 'bar', {get() {},
})
Object.defineProperty(object, 'baz', {})Object.getOwnPropertyDescriptors(object)
// {//   bar: {//     configurable: false,
//     enumerable: false,
//     get: f bar(),
//     set: undefined,
//   },
//   baz: {//     configurable: false,
//     enumerable: false,
//     value: undefined,
//     writable: false,
//   },
//   foo: {//     configurable: false,
//     enumerable: false,
//     value: 1,
//     writable: false,
//   },
// }

  比较发现,字面量方式指定的属性的描述符默认键值都为true

  而defineProperty方式则相对为false,很好理解,defineProperty旨在细化地描述属性,即指定什么就是什么,未指定的当然为false

  另外defineProperty指定为空描述符时,默认为数据描述符形式。存取描述符中的getset指定了才会有,不指定默认为undefined

configurable

  是否可以删除此属性。

  注意非严格模式下删除静默失败,严格模式将抛出错误。

'use strict'var object = {}Object.defineProperty(object, 'foo', {value: 1,configurable: false,
})delete object.foo // Uncaught TypeError: Cannot delete property 'foo' of #<Object>object // {foo: 1}

  是否可以修改描述符的键值,例如修改enumerable

var object = {}Object.defineProperty(object, 'foo', {configurable: false,enumerable: true,value: 1,
})Object.defineProperty(object, 'foo', {enumerable: false, // Uncaught TypeError: Cannot redefine property: foo at Function.defineProperty (<anonymous>)
})

  注意有两个特例,valuewritable

  value键的值可以修改。

var object = {}Object.defineProperty(object, 'foo', {configurable: false,writable: true,value: 1,
})Object.defineProperty(object, 'foo', {value: 12,
})object // {foo: 12}

  writable键的值只能由true修改为false

var object = {}Object.defineProperty(object, 'foo', {configurable: false,writable: true,value: 1,
})Object.defineProperty(object, 'foo', {writable: false,
})object // {foo: 1}

  但是不能由false修改为true

var object = {}Object.defineProperty(object, 'foo', {configurable: false,writable: false,value: 1,
})Object.defineProperty(object, 'foo', {writable: true, // Uncaught TypeError: Cannot redefine property: foo at Function.defineProperty (<anonymous>)
})

  所以configurable用于表示属性是否可删除,描述符的键是否可修改。

  当configurablefalse时,属性不能被删除。除了writablevalue之外的键(包括configurableenumerablesetget)不能修改,注意writable也只能修改为false

enumerable

  是否可以枚举此属性。

var object = { foo: 1 }Object.defineProperty(object, 'bar', {enumerable: false,value: 2,
})for (var prop in object) {console.log(prop) // foo
}Object.keys(object) // ['foo']JSON.stringify(object) // {"foo":1}Object.assign({}, object) // {foo: 1}object // {foo: 1, bar: 2}

Object.prototype.propertyIsEnumerable 可用于判断对象的属性值是否可枚举

  几种遍历对象的方式。

  • for...in:遍历对象自身和继承的可枚举属性,不包括Symbol属性
  • Objects.keys / Objects.values / Objects.entries:返回对象自身的键或值组成的数组,也是可枚举属性,不包括Symbol
  • Object.getOwnPropertyNames:返回对象自身的属性,不包括Symbol属性
  • Object.getOwnPropertySymbols:返回对象自身的Symbol属性
  • Reflect.ownKeys:返回对象自身的所有属性

writable

  是否可以修改此属性。

  注意非严格模式下修改静默失败,严格模式将抛出错误。

'use strict'var object = {}Object.defineProperty(object, 'foo', {writable: false,value: 1,
})object.foo = 2 // Uncaught TypeError: Cannot assign to read only property 'foo' of object '#<Object>'object // {foo: 1}

  configurabletrue时,可以先删除,再添加属性,达到修改属性的目的。

'use strict'var object = {}Object.defineProperty(object, 'foo', {configurable: true,writable: false,value: 1,
})delete object.foo
object.foo = 2object // {foo: 2}

value

  指定属性值,默认为undefined

var object = {}Object.defineProperty(object, 'foo', {})object // {foo: undefined}

get

  访问器getter,在访问对象属性时触发。

var object = {}Object.defineProperty(object, 'foo', {get() {console.log('get')},
})object.foo // get

  特别注意get函数内this会静默绑定。

var object = {}Object.defineProperty(object, 'foo', {get() {console.log(this === object) // true},
})object.foo

  观察发现,点运算符(.)左边的对象是谁,this就指向谁。

var object = {}Object.defineProperty(object, 'foo', {get() {console.log(this === o) // true},
})var o = Object.create(object)o.foo

  换句话说this指向触发读取操作时的对象。

set

  访问器setter,在修改对象属性值时触发。

var object = {}Object.defineProperty(object, 'foo', {set() {console.log('set')},
})object.foo = 1 // set

  类似的,set函数内this指向触发修改操作时的对象。

小结

  • 描述符分为数据和存取两种类型,数据类型包括configurableenumerablewritablevalue,存取类型包括configurableenumerablegetset
  • 字面量指定的属性描述符的默认键值为truedefinePropertyfalse
  • 若属性的configurable键值为false,不能删除此属性,也不能修改除了writablevalue之外的键,且writable也只能修改为false
  • 若属性的enumerablefalse,不可枚举此属性,或者说会被忽略。例如for...inObject.keysJSON.stringifyObject.assign
  • 若属性的writablefalse,不可修改此属性
  • 访问器属性有getset,在访问对象属性时触发get,在修改对象属性值时触发set,两者内部都会静默绑定this,指向触发操作时的对象

对象

  除了属性描述符可以限制对象的操作之外,Object上也有方法限制对象的操作。

Object.preventExtensions

  Object.preventExtensions 表示让对象不可拓展。

  不能添加新的属性。

  注意非严格模式下添加属性静默失败,严格模式将抛出错误。

'use strict'var object = {}Object.preventExtensions(object)object.foo = 1 // Uncaught TypeError: Cannot add property foo, object is not extensibleobject // {}

  另外对象的内部槽[[prototype]]也不可变,即不能修改原型的指向。

  __proto__修改将抛出错误。

var object = {}Object.preventExtensions(object)object.__proto__ = {} // Uncaught TypeError: #<Object> is not extensible at set __proto__ [as __proto__] (<anonymous>)

  setPrototypeOf也会抛出错误。

var object = {}Object.preventExtensions(object)Object.setPrototypeOf(object, {}) // Uncaught TypeError: #<Object> is not extensible at Function.setPrototypeOf (<anonymous>)

  所以preventExtensions作用的对象,不能添加新的属性,也不能修改原型的指向。

Object.isExtensible 可用于判断对象是否可扩展

Object.seal

  Object.seal 表示封闭对象。

  seal封闭对象时,也会将对象变为不可拓展。所以封闭的对象也有类似preventExtensions的性质,例如不能添加属性,不可修改原型的指向。

var object = { foo: 1 }Object.seal(object)Object.isExtensible(object) // false

  seal还会将对象的所有属性的描述符中的configurable置为false

  所以seal作用的对象上的所有属性,都不能删除,也不能修改描述符中除了writablevalue之外的键,且writable只能修改为false

var object = {foo: 1,get bar() {},
}Object.defineProperty(object, 'baz', {configurable: true,value: 2,
})Object.seal(object)Object.getOwnPropertyDescriptors(object)
// {//   bar: {//     configurable: false,
//     enumerable: true,
//     get: f bar(),
//     set: undefined,
//   },
//   baz: {//     configurable: false,
//     enumerable: false,
//     value: 2,
//     writable: false,
//   },
//   foo: {//     configurable: false,
//     enumerable: true,
//     value: 1,
//     writable: true,
//   },
// }

Object.isSealed 用于判断对象是否封闭

Object.freeze

  Object.freeze 表示冻结对象。

  freeze冻结对象时,会将对象封闭并变为不可拓展。

var object = { foo: 1 }Object.freeze(object)Object.isSealed(object) // trueObject.isExtensible(object) // false

  非严格模式修改属性静默失败,严格模式将抛出错误。

'use strict'var object = { foo: 1 }Object.freeze(object)object.foo = 2 // ncaught TypeError: Cannot assign to read only property 'foo' of object '#<Object>'object // {foo: 1}

  freeze还会将对象的所有属性的数据描述符中的writable置为false

var object = { foo: 1 }Object.defineProperty(object, 'bar', {configurable: true,writable: true,value: 2
})Object.freeze(object)Object.getOwnPropertyDescriptors(object)
// {//   bar: {//     configurable: false,
//     enumerable: false,
//     value: 2,
//     writable: false,
//   },
//   foo: {//     configurable: false,
//     enumerable: true,
//     value: 1,
//     writable: false,
//   },
// }

  所以freeze冻结的对象几乎不能做任何操作,但是注意freeze只是浅冻结。

var object = {foo: 1,bar: {baz: 2,},
}Object.freeze(object)object.bar.baz = 3object
// {//   bar: { baz: 3 },
//   foo: 1,
// }

Object.isFrozen 用于判断对象是否冻结

深度冻结

  兼容IE9

Object.deepFreeze = function (object) {var prop, value, propNames = Object.getOwnPropertyNames(object)for (var i = 0; i < propNames.length; i++) {prop = propNames[i]value = object[prop]if (typeof value === 'object' && value !== null) {Object.deepFreeze(value)}}return Object.freeze(object)
}

注意Object.keys只能获取到对象自身的不可枚举属性,而getOwnPropertyNames可以获取对象自身的所有非Symbol属性

解冻

  冻结实际上是不可逆的,无法解冻原对象。

  但是可以通过深度克隆并覆盖原对象,达到解冻的目的。

var object = {foo: 1,bar: {},
}Object.deepFreeze(object)// or JSON.parse(JSON.stringify(object))
object = deepClone(object)object.bar.baz = 2object
// {//   foo: 1,
//   bar: { baz: 2 },
// }

  本质上仅仅是将变量的内存空间指向了另一个对象,原对象还是冻结的。另外若变量objectconst的方式声明,此方式也将失效。

注意 JSON.stringify 深拷贝时,可能会丢失掉部分属性

数组

  数组也是一类特殊的对象,数组下标相当于是对象的键。

var array = [1]Object.defineProperty(array, 1, {get() {},
})Object.getOwnPropertyDescriptors(array)
// {//   0: {//     configurable: true,
//     enumerable: true,
//     value: 1,
//     writable: true,
//   },
//   1: {//     configurable: false,
//     enumerable: false,
//     get: f get(),
//     set: undefined,
//   },
//   length: {//     configurable: false,
//     enumerable: false,
//     value: 2,
//     writable: true,
//   },
// }

preventExtensions

  preventExtensions作用的数组不能添加元素,也不能修改原型的指向。

var array = [1, 2]Object.preventExtensions(array)array.push(3) // Uncaught TypeError: Cannot add property 2, object is not extensible at Array.push (<anonymous>)

seal

  seal与对象类似,除了有preventExtensions特性外,也不能删除元素等。

var array = [1]Object.seal(array)array.pop() // Uncaught TypeError: Cannot delete property '0' of [object Array] at Array.pop (<anonymous>)

freeze

  freeze也于对象类似,除了有preventExtensionsseal特性外,也不能修改元素。

  非严格模式修改元素静默失败,严格模式将抛出错误。

'use strict'var array = [1]Object.freeze(array)array[0] = 2 // index.html:23 Uncaught TypeError: Cannot assign to read only property '0' of object '[object Array]'array // [1]

小结

  • preventExtensions会将对象变为不可拓展,即不能添加属性,也不能修改原型的指向
  • seal封闭对象,不仅会将对象变为不可拓展,还会将所有属性的描述符中的configurable置为false。所以除了有preventExtensions的特性外,也不能删除属性,不能修改除了writablevalue之外的键,writable只能修改为false
  • freeze冻结对象,不仅会将对象变为不可拓展和封闭,还会将所有属性的数据描述符的writable置为false。所以除了有preventExtensionsseal特性外,不能修改属性和任何描述符的键值
  • 数组是特殊的对象,preventExtensionssealfreeze作用数组时,与作用对象的特性高度相似

preventExtensionssealfreeze均是不可逆的,并作用于对象或数组

  特性表。

特性 preventExtensions seal freeze
isExtensible false false false
isSealed false true true
isFrozen false false true
添加属性
修改原型的指向
configurable置为false
是否可删除属性
修改描述符的键值 可修改 不能修改除了writablevalue之外的键,writable只能修改为false 都不能修改
writable置为false

  关系图。

JavaScript 属性描述符相关推荐

  1. javascript --- 属性描述符

    从ES5开始,所有的属性都具备了属性描述符 var myObject = {a: 2 };Object.getOwnPropertyDescriptor(myObject, "a" ...

  2. JavaScript(3)之——对象的属性描述符

      对象的属性描述符是一个初学者容易忽略但是非常重要的特性,像是vue的数据双向绑定就是用它做文章.且关于它的方法和属性也很多,今天我来总结一下. 属性描述符概述   对象的每个属性都具备了属性描述符 ...

  3. Javascript高级教程:数据属性描述符与存储属性描述符

    属性描述符 let obj = {name: "ziu",age: 18 } Object.defineProperty(obj, "height", {val ...

  4. Python:高级主题之(属性取值和赋值过程、属性描述符、装饰器)

    属性取值和赋值过程 一切皆是对象,类型也是对象. 对象包含一个__class__属性指向其所属类型. 对象包含一个__dict__属性指向其所包含的成员(属性和方法). 取值过程(下面是伪代码) 1 ...

  5. python 属性描述符

    文章目录 1. 描述符示例:验证属性 2. 自动获取储存属性的名称 3. 继承改进 4. 覆盖型与非覆盖型描述符对比 4.1 覆盖型描述符 4.2 没有 `__get__` 方法的覆盖型描述符 4.3 ...

  6. 从 JavaScript 属性描述器剖析 Vue.js 响应式视图

    学习每一门语言,一般都是从其数据结构开始,JavaScript也是一样,而JavaScript的数据结构中对象(Object)是最基础也是使用最频繁的概念和语法,坊间有言,JavaScript中,一切 ...

  7. 从JavaScript属性描述器剖析Vue.js响应式视图

    学习每一门语言,一般都是从其数据结构开始,JavaScript也是一样,而JavaScript的数据结构中对象(Object)是最基础也是使用最频繁的概念和语法,坊间有言,JavaScript中,一切 ...

  8. 假装Python高手,你真的懂属性描述符类!

    Python学了好几年,发现功力还是那样,很多同学经常这样抱怨!都说Python入门容易,精通难,确实是这样的,每当我们打开一些牛逼的源码框架,进去看窥探大牛的源码,比如Python里面非常著名的Dj ...

  9. java中的scr是什么意思,javascrpt中属性描述符的理解与使用

    javascrpt中属性描述符的理解与使用 属性描述符是ES5出现的概念.顾名思义:它用于描述对象里面的属性应该是什么样,例如是否只读,能否可枚举,能否可配置等.怎样?好理解吧. 既然是对象里面的属性 ...

最新文章

  1. 利用日志审计追踪APT***
  2. C语言再学习 -- 三字母词(转)
  3. 数字后端——可制造性设计
  4. Ubuntu下安装Oracle11g(图文教程)
  5. rtl8812au linux驱动下载,Ubuntu 16.04 安装 rtl8812au系列 (DWA-182) wireless adapter driver
  6. HDU1420 Prepared for New Acmer【快速模幂】
  7. three 查看版本号
  8. PO、VO、BO、DTO、POJO、DAO之间的关系
  9. delphi 去掉字符串中所有的标点符号_[话俾你知]Python使用正则处理字符串技巧(分割、替换)...
  10. Spring的开幕式——Spring概述与设计思想
  11. 惯性导航系统--百科笔记
  12. 字母数字-ASCII码表(最详细,最直观)
  13. 每个人都应该具备点批判性思维
  14. AI大佬怼怼怼的背后,究竟暗藏哪些玄机?
  15. 从0到1亿美元 ---- PopCap创始人John Vechey自述
  16. 重庆对口高职计算机类专业答案2020,2020年重庆市高职分类考试教育类专业招生试题及答案...
  17. IPSec ×××实验
  18. 5G手机占比逼近四成,华为和小米将加速5G普及
  19. 四种不同单源最短路径算法性能比较
  20. 拆掉思维里的墙-阅读记录

热门文章

  1. snmp获取服务器HBA卡信息,HPE Gen10服务器使用SNMP无法获取对应网卡和HBA卡的OID的返回值...
  2. 如何使用Tomcat自带的日志实现tomcat-juli.jar
  3. UE4 Level Variant
  4. 1087: 获取出生日期(多实例测试) Python
  5. OJ题之OOP身份证扩展(类构造与析构)
  6. css中white-space:nowrap,css强制不换行white-space:nowrap
  7. 计算机一级复习知识点小自考,《数字图像处理》自考复习知识点.doc
  8. 操作系统 之 「信号量机制解决进程同步问题」
  9. wamp64显示黄色图标不能忍
  10. 2018年12月云栖技术活动最全资料汇总:50+直播与Meetup分享