本博客 hjy-xh ,转载请申明出处
属性描述符 属性描述符是ES5引入的概念,它用于描述对象的特征.
对象里目前存在的属性描述符有两种主要形式:数据描述符 和存取描述符 .
MDN中的描述: 数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。 存取描述符是由getter-setter函数对描述的属性。 描述符必须是这两种形式之一;不能同时是两者。
数据描述符和存取描述符均具有以下可选键值(默认值是在使用Object.defineProperty()定义属性的情况下)
configurable
: 可配置性 .当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除.默认为 false
.
enumerable
: 可枚举性 .当且仅当该属性的enumerable为true
时,该属性才能够出现在对象的枚举属性中.默认为 false
.
数据描述符同时具有以下可选键值:
writable
: 可写性 .当且仅当该属性的writable为true时,value才能被赋值运算符改变.默认为 false
.
value
: 属性值 .该属性对应的值.可以是任何有效的 JavaScript 值(数值,对象,函数等).默认为 undefined
.
存取描述符同时具有以下可选键值:
get
: 一个给属性提供 getter 的方法,如果没有 getter 则为 undefined
.当访问该属性时.该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象).
set
: 一个给属性提供 setter 的方法,如果没有 setter 则为 undefined
.当属性值修改时,触发执行该方法.该方法将接受唯一参数,即该属性新的参数值.
描述符可同时具有的键值: | | configurable | enumerable | value | writable | get | set | ——– | —–: | :—-: | :—-: | :—-: | :—-: | :—-: | | 数据描述符 | Yes | Yes | Yes | Yes | No | No | | 存取描述符 | Yes | Yes | No | No | Yes | Yes |
如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符.如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常.
常用API
Object.defineProperty(obj, prop, descriptor)
obj:在其上定义或修改属性的对象.
prop:要定义或修改的属性的名称.
descriptor:将被定义或修改的属性描述符.
Object.defineProperties(obj, props)
obj:在其上定义或修改属性的对象.
props:要定义其可枚举属性或修改的属性描述符的对象.对象中存在的属性描述符主要有两种:数据描述符和访问器描述符
Object.getOwnPropertyDescriptors(obj)
obj:任意对象.
Object.getOwnPropertyDescriptor(obj, prop)
obj:需要查找的目标对象.
prop:目标对象内属性名称
用法示例 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 let obj = {};Object .defineProperty(obj, 'name' , { configurable : false , enumerable : true , writable : true , value : 'Cola' }); Object .defineProperties(obj, { color : { configurable : false , enumerable : true , writable : true , value : 'red' }, price : { configurable : false , enumerable : true , writable : true , value : '3.5' } }); Object .defineProperty(obj, 'updatePrice' , { configurable : false , enumerable : true , get : function ( ) { return this .capacity; }, set : function (price ) { this .price = price; } }); console .log(obj);obj.updatePrice = 5 ; console .log(obj);
规则
如果属性不可配置,则不能修改它的可配置性和可枚举性,否则抛出异常
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 let obj = { name : 'Cola' } Object .defineProperty(obj, 'color' , { configurable : false , enumerable : false }) try { Object .defineProperty(obj, 'color' , { configurable : true }) } catch (e) { console .log(e); } try { Object .defineProperty(obj, 'color' , { enumerable : true }) } catch (e) { console .log(e); } console .log(Object .getOwnPropertyDescriptor(obj, 'color' ));
如果存取器属性是不可配置的,则不能修改get
和set
方法,也不能将它转换为数据属性
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 42 let obj = { name : 'Cola' } Object .defineProperty(obj, 'updateName' , { configurable : false , enumerable : true , get : function ( ) { return this .name; }, set : function (name ) { this .name = name; } }); try { Object .defineProperty(obj, 'updateName' , { get : function ( ) { return 'Coca ' + this .name; } }); } catch (e) { console .log(e); } try { Object .defineProperty(obj, 'updateName' , { set : function (val ) { this .name = val + this .name; } }); } catch (e) { console .log(e); } try { Object .defineProperty(obj, 'updateName' , { value : 'Coco Cola' }); } catch (e) { console .log(e); }
如果数据属性是不可配置的,则不能将它转换为存取器属性;同时也不能将它的可写性从false
修改成true
,但可以从true
修改为false
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 42 43 44 45 46 47 48 49 50 51 52 53 let obj = { name : 'Cola' } Object .defineProperty(obj, 'color' , { configurable : false , writable : false , value : 'red' }); try { Object .defineProperty(obj, 'color' , { writable : true }); } catch (e) { console .log(e); } try { Object .defineProperty(obj, 'color' , { get : function ( ) { return this .name; } }); } catch (e) { console .log(e); } Object .defineProperty(obj, 'price' , { configurable : false , writable : true , value : '3.5' }); try { Object .defineProperty(obj, 'price' , { writable : false , value : '5' }); } catch (e) { console .log(e); } try { Object .defineProperty(obj, 'price' , { value : '6' }); } catch (e) { console .log(e); } console .log(Object .getOwnPropertyDescriptor(obj, 'color' )); console .log(Object .getOwnPropertyDescriptor(obj, 'price' ));
如果数据属性是不可配置且不可写的,就不能修改它的值;如果是可配置但不可写,则可以修改值
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 let obj = { name : 'Cola' } Object .defineProperty(obj, 'color' , { configurable : false , writable : false , value : 'red' }); try { Object .defineProperty(obj, 'color' , { value : 'blue' }); } catch (e) { console .log(e); } Object .defineProperty(obj, 'price' , { configurable : true , writable : false , value : '3.5' }); Object .defineProperty(obj, 'price' , { configurable : true , writable : false , value : '12' }); console .log(obj.price); console .log(Object .getOwnPropertyDescriptor(obj, 'price' )); Object .defineProperty(obj, 'price' , { configurable : true , writable : true , value : '3.5' }); obj.price = 5 ; console .log(obj.price);
严格模式下,只指定get
时,如果对该属性赋值将会抛出类型错误异常,只指定set
时,如果读取该属性将返回undefined,非严格模式下都不抛出异常
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 'use strict' let obj = { name : 'Cola' } Object .defineProperty(obj, 'color' , { get : function ( ) { return 'Coca ' + this .name; } }); try { obj.color = 'blue' ; } catch (e) { console .log(e); } console .log(obj.color); Object .defineProperty(obj, 'price' , { set : function (val ) { this .name = val; } }); try { console .log(obj.price); } catch (e) { console .log(e); }
扩展特性 如果一个对象可以添加新的属性,那么这个对象是可扩展的.如何检验对象是否可扩展及如何让它变得不可扩展呢?
常用API
Object.isExtensible(obj)
obj:需要检测的对象
Object.preventExtensions(obj)
obj:将要变得不可扩展的对象
用法示例 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 let obj = { name : 'Cola' } console .log(Object .isExtensible(obj)); Object .preventExtensions(obj);console .log(Object .isExtensible(obj)); obj.color = 'red' ; console .log(obj); try { Object .defineProperty(obj, "name" , { value : 'Coca Cola' }); Object .defineProperty(obj, "price" , { value : 3.5 }); } catch (e) { console .log(e); } finally { console .log(obj); } console .log(Object .getOwnPropertyDescriptor(obj, 'name' ));Object .defineProperty(obj, "name" , { value : 'Coca Cola' , writable : false , }); obj.name = 'Sprite' ; console .log(obj);
密封特性 密封对象是指不可扩展,且自身所有属性都不可配置的对象.
也就是说密封对象要满足以下条件:
不能添加新属性
不能删除已有属性
不能修改已有属性的可枚举性、可配置性、可写性,但可能可以修改已有属性的值的对象
常用API
Object.isSealed(obj)
obj:需要检测的对象
Object.seal(obj)
obj:需要被密封的对象
用法示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let obj = { name : 'Cola' } console .log(Object .isSealed(obj)); Object .preventExtensions(obj);Object .defineProperty(obj, 'name' , { configurable : false }) console .log(Object .isSealed(obj)); let obj2 = {}Object .preventExtensions(obj2);console .log(Object .isSealed(obj2));
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let obj = { name : 'Cola' } Object .seal(obj);console .log(Object .isSealed(obj)); obj.color = 'red' ; delete obj.name;console .log(obj); obj.name = 'Coca Cola' ; console .log(obj);
冻结特性 冻结对象要满足以下条件:
不能添加新属性
不能删除已有属性
不能修改已有属性的可枚举性、可配置性、可写性
即这个对象永远是不可变的.
常用API
Object.isFrozen(obj)
obj:需要检测的对象
Object.freeze(obj)
obj:需要被冻结的对象
用法示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 let obj = { name : 'Cola' } console .log(Object .isFrozen(obj)); Object .preventExtensions(obj);delete obj.name;console .log(Object .isFrozen(obj));console .log(Object .isSealed(Object .preventExtensions({}))); console .log(Object .isFrozen(Object .preventExtensions({})));
1 2 3 4 5 6 7 8 9 10 let obj = { name: 'Cola' } Object.freeze(obj); console.log(Object.isFrozen(obj)); //true //对冻结对象的任何操作都会失败 obj.name = 'Coca Cola'; delete obj.name; console.log(obj);
注意:再严格模式下,对冻结对象的操作会抛出类型异常
浅冻结 1 2 3 4 5 6 7 8 9 10 11 let obj = { name : 'Cola' , color : { CocaCola : 'red' , PepsiCola : 'blue' } } Object .freeze(obj);obj.name = 'Sprite' obj.color.CocaCola = 'white' ; console .log(obj);
深冻结 1 2 3 4 5 6 7 8 9 10 11 12 function completelyFreezeObj (obj ) { if (Object .prototype.toString.call(obj) != "[object Object]" ) { console .error("obj不是对象" ); return ; } Object .freeze(obj); Object .keys(obj).forEach((key ) => { if (Object .prototype.toString.call(obj[key]) == "[object Object]" ) { completelyFreezeObj(obj[key]); } }); };