JavaScript 属性描述符
前言
此文总结了属性描述符的作用和特性,以及限制对象操作的部分方法。
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() {},
})
注意属性描述符固定包括configurable
、enumerable
、writable
和value
四个键,存取描述符固定包括configurable
、enumerable
、get
和set
四个键。
描述符中公共键为
configurable
和enumerable
,数据描述符的writable
与value
键成对,存取描述符的get
和set
键成对
默认键值
属性描述符的键是有默认值的,但是由于对象属性的指定方式不同,存在差异。
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
指定为空描述符时,默认为数据描述符形式。存取描述符中的get
和set
指定了才会有,不指定默认为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>)
})
注意有两个特例,value
和writable
。
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
用于表示属性是否可删除,描述符的键是否可修改。
当configurable
为false
时,属性不能被删除。除了writable
和value
之外的键(包括configurable
、enumerable
、set
和get
)不能修改,注意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}
configurable
为true
时,可以先删除,再添加属性,达到修改属性的目的。
'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
指向触发修改操作时的对象。
小结
- 描述符分为数据和存取两种类型,数据类型包括
configurable
、enumerable
、writable
和value
,存取类型包括configurable
、enumerable
、get
和set
- 字面量指定的属性描述符的默认键值为
true
,defineProperty
为false
- 若属性的
configurable
键值为false
,不能删除此属性,也不能修改除了writable
和value
之外的键,且writable
也只能修改为false
- 若属性的
enumerable
为false
,不可枚举此属性,或者说会被忽略。例如for...in
、Object.keys
、JSON.stringify
和Object.assign
- 若属性的
writable
为false
,不可修改此属性 - 访问器属性有
get
和set
,在访问对象属性时触发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
作用的对象上的所有属性,都不能删除,也不能修改描述符中除了writable
和value
之外的键,且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 },
// }
本质上仅仅是将变量的内存空间指向了另一个对象,原对象还是冻结的。另外若变量object
以const
的方式声明,此方式也将失效。
注意 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
也于对象类似,除了有preventExtensions
和seal
特性外,也不能修改元素。
非严格模式修改元素静默失败,严格模式将抛出错误。
'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
的特性外,也不能删除属性,不能修改除了writable
和value
之外的键,writable
只能修改为false
freeze
冻结对象,不仅会将对象变为不可拓展和封闭,还会将所有属性的数据描述符的writable
置为false
。所以除了有preventExtensions
和seal
特性外,不能修改属性和任何描述符的键值- 数组是特殊的对象,
preventExtensions
、seal
和freeze
作用数组时,与作用对象的特性高度相似
preventExtensions
、seal
和freeze
均是不可逆的,并浅
作用于对象或数组
特性表。
特性 |
preventExtensions
|
seal
|
freeze
|
---|---|---|---|
isExtensible
|
false
|
false
|
false
|
isSealed
|
false
|
true
|
true
|
isFrozen
|
false
|
false
|
true
|
添加属性 | 否 | 否 | 否 |
修改原型的指向 | 否 | 否 | 否 |
configurable 置为false
|
否 | 是 | 是 |
是否可删除属性 | 是 | 否 | 否 |
修改描述符的键值 | 可修改 |
不能修改除了writable 和value 之外的键,writable 只能修改为false
|
都不能修改 |
writable 置为false
|
否 | 否 | 是 |
关系图。
JavaScript 属性描述符相关推荐
- javascript --- 属性描述符
从ES5开始,所有的属性都具备了属性描述符 var myObject = {a: 2 };Object.getOwnPropertyDescriptor(myObject, "a" ...
- JavaScript(3)之——对象的属性描述符
对象的属性描述符是一个初学者容易忽略但是非常重要的特性,像是vue的数据双向绑定就是用它做文章.且关于它的方法和属性也很多,今天我来总结一下. 属性描述符概述 对象的每个属性都具备了属性描述符 ...
- Javascript高级教程:数据属性描述符与存储属性描述符
属性描述符 let obj = {name: "ziu",age: 18 } Object.defineProperty(obj, "height", {val ...
- Python:高级主题之(属性取值和赋值过程、属性描述符、装饰器)
属性取值和赋值过程 一切皆是对象,类型也是对象. 对象包含一个__class__属性指向其所属类型. 对象包含一个__dict__属性指向其所包含的成员(属性和方法). 取值过程(下面是伪代码) 1 ...
- python 属性描述符
文章目录 1. 描述符示例:验证属性 2. 自动获取储存属性的名称 3. 继承改进 4. 覆盖型与非覆盖型描述符对比 4.1 覆盖型描述符 4.2 没有 `__get__` 方法的覆盖型描述符 4.3 ...
- 从 JavaScript 属性描述器剖析 Vue.js 响应式视图
学习每一门语言,一般都是从其数据结构开始,JavaScript也是一样,而JavaScript的数据结构中对象(Object)是最基础也是使用最频繁的概念和语法,坊间有言,JavaScript中,一切 ...
- 从JavaScript属性描述器剖析Vue.js响应式视图
学习每一门语言,一般都是从其数据结构开始,JavaScript也是一样,而JavaScript的数据结构中对象(Object)是最基础也是使用最频繁的概念和语法,坊间有言,JavaScript中,一切 ...
- 假装Python高手,你真的懂属性描述符类!
Python学了好几年,发现功力还是那样,很多同学经常这样抱怨!都说Python入门容易,精通难,确实是这样的,每当我们打开一些牛逼的源码框架,进去看窥探大牛的源码,比如Python里面非常著名的Dj ...
- java中的scr是什么意思,javascrpt中属性描述符的理解与使用
javascrpt中属性描述符的理解与使用 属性描述符是ES5出现的概念.顾名思义:它用于描述对象里面的属性应该是什么样,例如是否只读,能否可枚举,能否可配置等.怎样?好理解吧. 既然是对象里面的属性 ...
最新文章
- 利用日志审计追踪APT***
- C语言再学习 -- 三字母词(转)
- 数字后端——可制造性设计
- Ubuntu下安装Oracle11g(图文教程)
- rtl8812au linux驱动下载,Ubuntu 16.04 安装 rtl8812au系列 (DWA-182) wireless adapter driver
- HDU1420 Prepared for New Acmer【快速模幂】
- three 查看版本号
- PO、VO、BO、DTO、POJO、DAO之间的关系
- delphi 去掉字符串中所有的标点符号_[话俾你知]Python使用正则处理字符串技巧(分割、替换)...
- Spring的开幕式——Spring概述与设计思想
- 惯性导航系统--百科笔记
- 字母数字-ASCII码表(最详细,最直观)
- 每个人都应该具备点批判性思维
- AI大佬怼怼怼的背后,究竟暗藏哪些玄机?
- 从0到1亿美元 ---- PopCap创始人John Vechey自述
- 重庆对口高职计算机类专业答案2020,2020年重庆市高职分类考试教育类专业招生试题及答案...
- IPSec ×××实验
- 5G手机占比逼近四成,华为和小米将加速5G普及
- 四种不同单源最短路径算法性能比较
- 拆掉思维里的墙-阅读记录
热门文章
- snmp获取服务器HBA卡信息,HPE Gen10服务器使用SNMP无法获取对应网卡和HBA卡的OID的返回值...
- 如何使用Tomcat自带的日志实现tomcat-juli.jar
- UE4 Level Variant
- 1087: 获取出生日期(多实例测试) Python
- OJ题之OOP身份证扩展(类构造与析构)
- css中white-space:nowrap,css强制不换行white-space:nowrap
- 计算机一级复习知识点小自考,《数字图像处理》自考复习知识点.doc
- 操作系统 之 「信号量机制解决进程同步问题」
- wamp64显示黄色图标不能忍
- 2018年12月云栖技术活动最全资料汇总:50+直播与Meetup分享
从ES5开始,所有的属性都具备了属性描述符 var myObject = {a: 2 };Object.getOwnPropertyDescriptor(myObject, "a" ...
对象的属性描述符是一个初学者容易忽略但是非常重要的特性,像是vue的数据双向绑定就是用它做文章.且关于它的方法和属性也很多,今天我来总结一下. 属性描述符概述 对象的每个属性都具备了属性描述符 ...
属性描述符 let obj = {name: "ziu",age: 18 } Object.defineProperty(obj, "height", {val ...
属性取值和赋值过程 一切皆是对象,类型也是对象. 对象包含一个__class__属性指向其所属类型. 对象包含一个__dict__属性指向其所包含的成员(属性和方法). 取值过程(下面是伪代码) 1 ...
文章目录 1. 描述符示例:验证属性 2. 自动获取储存属性的名称 3. 继承改进 4. 覆盖型与非覆盖型描述符对比 4.1 覆盖型描述符 4.2 没有 `__get__` 方法的覆盖型描述符 4.3 ...
学习每一门语言,一般都是从其数据结构开始,JavaScript也是一样,而JavaScript的数据结构中对象(Object)是最基础也是使用最频繁的概念和语法,坊间有言,JavaScript中,一切 ...
学习每一门语言,一般都是从其数据结构开始,JavaScript也是一样,而JavaScript的数据结构中对象(Object)是最基础也是使用最频繁的概念和语法,坊间有言,JavaScript中,一切 ...
Python学了好几年,发现功力还是那样,很多同学经常这样抱怨!都说Python入门容易,精通难,确实是这样的,每当我们打开一些牛逼的源码框架,进去看窥探大牛的源码,比如Python里面非常著名的Dj ...
javascrpt中属性描述符的理解与使用 属性描述符是ES5出现的概念.顾名思义:它用于描述对象里面的属性应该是什么样,例如是否只读,能否可枚举,能否可配置等.怎样?好理解吧. 既然是对象里面的属性 ...