JS入门难点解析13-属性描述符,数据属性和访问器属性
2018-03-25 00:53
531 查看
(注1:如果有问题欢迎留言探讨,一起学习!转载请注明出处,喜欢可以点个赞哦!)
(注2:本文首发于我的简书,更多内容请查看我的目录。)
ECMAScript中有两种属性:数据属性和访问器属性。
1. 可配置性 [[Configurable]] : 表示能否通过delete删除属性,能否修改属性特性,能否把数据属性修改为访问器属性。
2. 可枚举性[[Enumerable]]:表示能否通过for-in循环返回属性。
3. 可写入性[[Writable]]:表示能否修改属性值。
4. 属性值[[Value]]:表示属性值。
1. 可配置性 [[Configurable]]:表示能否通过delete删除属性,能否修改属性特性,能否把访问器属性修改为数据属性。
2. 可枚举性[[Enumerable]]:表示能否通过for-in循环返回属性。
3. 读取属性函数[[Get]]:在读取属性时调用的函数。
4. 写入属性函数[[Set]]:在写入属性时调用的函数。
属性所在对象,属性名字很好理解。那么属性描述符对象呢?它其实是和属性的特性一一对应,只不过没有了两对中括号,我们可以通过ECMAScript5提供的一些方法来修改描述符对象。另外,可以用Object.getOwnPropertyDescriptor()方法,取得指定对象指定属性的描述符。这个方法接收两个参数:属性所在对象,属性名字。更多的方法我们会在文章后面详细介绍。
1. 可配置性 configurable : 表示能否通过delete删除属性,能否修改属性特性,能否把数据属性修改为访问器属性。
2. 可枚举性enumerable:表示能否通过for-in循环返回属性。
3. 可写入性writable:表示能否修改属性值。
4. 属性值value:表示属性值。
1. 可配置性 configurable:表示能否通过delete删除属性,能否修改属性特性,能否把访问器属性修改为数据属性。
2. 可枚举性enumerable:表示能否通过for-in循环返回属性。
3. 读取属性方法get:在读取属性值时调用的函数。
4. 写入属性方法set:在写入属性值时调用的函数。
对于数据属性,我们通过value来定义和获取其属性值。如下:
可以看到,读取obj的属性a的值时,即为获取其描述符对象的value属性值。
4.1.2 可写入性writable
如果需要改变a的值,可以有如下两种办法:
我们发现打印出来的值就是a的value值,再一次证明了obj的属性a的值即为其描述符对象的value属性值。但如果我们修改writable的值为false呢?看如下代码:
可以看到,当writable的值为false以后,无法再对属性value直接使用赋值修改,但是仍然可以用Object.defineProperty()方法进行修改。
4.1.3 可配置性configurable
还记得我们在4.1.2使用了两种方法修改a的值,但是当writable为false以后,直接赋值的方法失效了,但是Object.defineProperty()方法仍然有效,那我们来看一下configurable设置为false以后会是什么情况呢?
我们发现writable: true,configurable: false时两种方法都可以修改value值。那么我们看看此时若将writable设置为false会有什么情况:
结果第二次修改,会有如下报错:
我们发现writable: false,configurable: false时两种方法都不可以再修改value值。
此时,我们尝试将writable修改回true:
发现此时报图demo4.1.3-2所示错误,已经无法将writable: false修改回writable: true。
而对于configurable: false时是否可以修改enumerable呢?来看一下:
而对于configurable: false时是否可以修改configurable呢?来看一下:
发现此时报图demo4.1.3-2所示错误,无法修改configurable属性。
然后我们再看一下是否能删除整个属性值。
可以看出,configurable: false时,无法删除属性值。
总结以上信息,可配置性configurable有如下特征:
1. configurable值为true时,可以删除属性值;configurable值为为false时,不能删除属性值;
2. configurable值为true时,可以修改任意描述符属性值;configurable值为false时,修改规则遵循3,4,5。
3. configurable值为为false时,如果writable值为true,可以使用Object.defineProperty()方法改变value值;configurable值为为false时,如果writable值为false,不能使用Object.defineProperty()方法改变value值;
4. configurable值为false时,如果writable值为true,可以修改其为false;configurable值为false时,如果writable值为false,不能修改其为true;
5. configurable值为false时,除3,4两种情况,不可修改描述符属性值。
4.1.4 可枚举性enumerable
该属性值控制的是属性是否会出现在对象的属性枚举中,比如说for…in循环(会遍历对象自身的和继承的可枚举属性)或者Object.keys()方法(返回对象自身的所有可枚举属性)。
可以看到,enumerable控制了属性的可枚举性。
读取属性值时所调用的方法,会触发隐藏的getter操作。设置了get或set方法的属性,会自动变成访问器属性,不再从value读取属性值。用Object.getOwnPropertyDescriptor方法也不再返回value和writable。
另外,你也可以在对象字面量定义中直接指定属性的get方法来触发getter操作。
4.2.2 set方法
设置属性值时所调用的方法,会触发隐藏的setter操作。设置了get或set方法的属性,会自动变成访问器属性,不再从value读取属性值。用Object.getOwnPropertyDescriptor方法也不再返回value和writable。
另外,你也可以在对象字面量定义中直接指定属性的set方法来触发setter操作。
4.2.3 可配置性 configurable
和在数据属性描述符对象中功能一样。表示能否通过delete删除属性,能否修改属性特性,能否把访问器属性修改为数据属性。
4.2.4 可枚举性enumerable
和在数据属性描述符对象中功能一样。表示能否通过for-in循环返回属性。
其实,我们从第5节内容可以看到,如果不使用Object.defineProperty()或者Object.defineProperties()以及指定get和set等特殊方法定义的对象属性,默认都是数据属性。这其实是由我们的需求场景来定的。
1. 在一般情况下,我们关注的只是属性的赋值与取值,也就是仅仅关注数据属性的value。
2. 有只读需求的情况,即如果不允许直接以赋值方式更改属性值,就需要用到数据属性的writable特性,将其设置为false。当然如果此时configurable若为true,仍然允许使用Object.defineProperty()方法来更改属性值。如果要完全返回固定值,且不允许任何方式修改,可以设置configurable为false。当然这里使用访问器属性一样能达到只读效果。
3. 如果要求对用户的输入进行特殊处理,或者设置属性的依赖关系,就需要用到访问器属性了。
综上,访问器属性的功能最强大,但是在一般需求情况下,我们使用数据属性即可。
有的人可能会想,那么此时我如果去修改访问器属性值呢?事实上,对访问器属性的修改实际是通过内部的数据属性为桥梁进行修改的,此时内部的数据属性已经无法修改了,那么对访问器的属性修改也是无效的了。
BOOK-《你不知道的JavaScript》 第2部分
JS属性特性(属性描述符)
ECMAScript 5中的属性描述符详解
stackoverflow:Difference between accessor property and data property in ECMAScript?
msdn.microsoft.com/library:数据属性和访问器属性
(注2:本文首发于我的简书,更多内容请查看我的目录。)
1. 简介
JS的对象是一组无序属性的集合。那么对于这组无序属性来说,拥有哪些特性呢?ECMAScrip5中定义了属性的这些特性,但这些特性是为了实现JS引擎用的,在JS中无法直接来访问它们。为了表示这些特性是内部值,这些特性被放在两对方括号中。例如[[Enumerable]]。ECMAScript中有两种属性:数据属性和访问器属性。
2. 数据属性和访问器属性
2.1 数据属性
数据属性包含一个数据值的位置,在这个位置可以读取和写入值。数据属性有4个描述其行为的特性。1. 可配置性 [[Configurable]] : 表示能否通过delete删除属性,能否修改属性特性,能否把数据属性修改为访问器属性。
2. 可枚举性[[Enumerable]]:表示能否通过for-in循环返回属性。
3. 可写入性[[Writable]]:表示能否修改属性值。
4. 属性值[[Value]]:表示属性值。
2.2 访问器属性
访问器属性不包含数据值,而是包含一对getter和setter函数(这两个函数非必须)。在读取访问器属性时,调用getter函数,在写入访问器属性时,调用setter函数。访问器属性由4个特性:1. 可配置性 [[Configurable]]:表示能否通过delete删除属性,能否修改属性特性,能否把访问器属性修改为数据属性。
2. 可枚举性[[Enumerable]]:表示能否通过for-in循环返回属性。
3. 读取属性函数[[Get]]:在读取属性时调用的函数。
4. 写入属性函数[[Set]]:在写入属性时调用的函数。
3. 属性描述符概述
对于数据属性和访问器属性,其特性值是无法直接访问的。要对这些特性值进行修改,可以使用ECMAScript5提供的Object.defineProperty()方法。该方法接受三个参数:属性所在对象,属性名字和一个描述符对象。属性所在对象,属性名字很好理解。那么属性描述符对象呢?它其实是和属性的特性一一对应,只不过没有了两对中括号,我们可以通过ECMAScript5提供的一些方法来修改描述符对象。另外,可以用Object.getOwnPropertyDescriptor()方法,取得指定对象指定属性的描述符。这个方法接收两个参数:属性所在对象,属性名字。更多的方法我们会在文章后面详细介绍。
3.1 数据属性描述符对象
数据属性描述符对象有4个属性。1. 可配置性 configurable : 表示能否通过delete删除属性,能否修改属性特性,能否把数据属性修改为访问器属性。
2. 可枚举性enumerable:表示能否通过for-in循环返回属性。
3. 可写入性writable:表示能否修改属性值。
4. 属性值value:表示属性值。
3.2 访问器属性描述符对象
访问器属性描述符对象有4个属性。1. 可配置性 configurable:表示能否通过delete删除属性,能否修改属性特性,能否把访问器属性修改为数据属性。
2. 可枚举性enumerable:表示能否通过for-in循环返回属性。
3. 读取属性方法get:在读取属性值时调用的函数。
4. 写入属性方法set:在写入属性值时调用的函数。
4. 属性描述符详解
4.1 数据属性描述符对象
4.1.1 属性值value对于数据属性,我们通过value来定义和获取其属性值。如下:
// demo4.1.1 var obj = {}; // 指定属性a,其value外的所有描述符属性都为true Object.defineProperty(obj, 'a', { value: 1, writable: true, enumerable: true, configurable: true }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 1, writable: true, enumerable: true, configurable: true} console.log(obj.a); // 1
可以看到,读取obj的属性a的值时,即为获取其描述符对象的value属性值。
4.1.2 可写入性writable
如果需要改变a的值,可以有如下两种办法:
// demo4.1.2-1 // 接demo4.1.1 // 方法1,直接赋值 obj.a = 2; console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 2, writable: true, enumerable: true, configurable: true} console.log(obj.a); // 2 // 方法2,Object.defineProperty Object.defineProperty(obj, 'a', { value: 3, writable: true, enumerable: true, configurable: true }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 3, writable: true, enumerable: true, configurable: true} console.log(obj.a); // 3
我们发现打印出来的值就是a的value值,再一次证明了obj的属性a的值即为其描述符对象的value属性值。但如果我们修改writable的值为false呢?看如下代码:
// demo4.1.2-2 // 接demo4.1.2-1 Object.defineProperty(obj, 'a', { value: 1, writable: false, enumerable: true, configurable: true }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 1, writable: false, enumerable: true, configurable: true} console.log(obj.a); // 1 // 尝试修改a的值 // 方法1,直接赋值 obj. 4000 a = 2; console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 1, writable: true, enumerable: true, configurable: true} console.log(obj.a); // 1 // 方法2,Object.defineProperty Object.defineProperty(obj, 'a', { value: 3, writable: false, enumerable: true, configurable: true }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 3, writable: false, enumerable: true, configurable: true} console.log(obj.a); // 3
可以看到,当writable的值为false以后,无法再对属性value直接使用赋值修改,但是仍然可以用Object.defineProperty()方法进行修改。
4.1.3 可配置性configurable
还记得我们在4.1.2使用了两种方法修改a的值,但是当writable为false以后,直接赋值的方法失效了,但是Object.defineProperty()方法仍然有效,那我们来看一下configurable设置为false以后会是什么情况呢?
// demo4.1.3-1 var obj = {}; Object.defineProperty(obj, 'a', { value: 1, writable: true, enumerable: true, configurable: false }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 1, writable: true, enumerable: true, configurable: false} console.log(obj.a); // 1 // 尝试修改a的值 // 方法1,直接赋值可以成功 obj.a = 2; console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 2, writable: true, enumerable: true, configurable: false} console.log(obj.a); // 2 // 方法2,Object.defineProperty可以成功 Object.defineProperty(obj, 'a', { value: 3, writable: true, enumerable: true, configurable: false }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 3, writable: true, enumerable: true, configurable: false} console.log(obj.a); // 3
我们发现writable: true,configurable: false时两种方法都可以修改value值。那么我们看看此时若将writable设置为false会有什么情况:
// demo4.1.3-2 // 接demo4.1.3-1 // 第一次修改,修改前writable: true Object.defineProperty(obj, 'a', { value: 4, writable: false, enumerable: true, configurable: false }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 4, writable: false, enumerable: true, configurable: false} console.log(obj.a); // 4 // 第二次修改,修改前writable: false Object.defineProperty(obj, 'a', { value: 5, writable: false, enumerable: true, configurable: false }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); console.log(obj.a);
结果第二次修改,会有如下报错:
我们发现writable: false,configurable: false时两种方法都不可以再修改value值。
此时,我们尝试将writable修改回true:
// demo4.1.3-3 // 接demo4.1.3-2 Object.defineProperty(obj, 'a', { value: 4, writable: true, enumerable: true, configurable: false }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); console.log(obj.a);
发现此时报图demo4.1.3-2所示错误,已经无法将writable: false修改回writable: true。
而对于configurable: false时是否可以修改enumerable呢?来看一下:
// demo4.1.3-4 var obj = {}; Object.defineProperty(obj, 'a', { value: 1, writable: true, enumerable: true, configurable: false }); Object.defineProperty(obj, 'a', { value: 1, writable: true, enumerable: false, configurable: false });
而对于configurable: false时是否可以修改configurable呢?来看一下:
// demo4.1.3-5 var obj = {}; Object.defineProperty(obj, 'a', { value: 1, writable: true, enumerable: true, configurable: false }); Object.defineProperty(obj, 'a', { value: 1, writable: true, enumerable: true, configurable: true });
发现此时报图demo4.1.3-2所示错误,无法修改configurable属性。
然后我们再看一下是否能删除整个属性值。
// demo4.1.3-6 var obj = {}; Object.defineProperty(obj, 'a', { value: 1, writable: true, enumerable: true, configurable: true }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 1, writable: true, enumerable: true, configurable: true} console.log(obj.a); // 1 delete(obj.a); console.log(obj.a); // undefined Object.defineProperty(obj, 'a', { value: 1, writable: true, enumerable: true, configurable: false }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 1, writable: true, enumerable: true, configurable: false} console.log(obj.a); // 1 delete(obj.a); console.log(obj.a); // 1
可以看出,configurable: false时,无法删除属性值。
总结以上信息,可配置性configurable有如下特征:
1. configurable值为true时,可以删除属性值;configurable值为为false时,不能删除属性值;
2. configurable值为true时,可以修改任意描述符属性值;configurable值为false时,修改规则遵循3,4,5。
3. configurable值为为false时,如果writable值为true,可以使用Object.defineProperty()方法改变value值;configurable值为为false时,如果writable值为false,不能使用Object.defineProperty()方法改变value值;
4. configurable值为false时,如果writable值为true,可以修改其为false;configurable值为false时,如果writable值为false,不能修改其为true;
5. configurable值为false时,除3,4两种情况,不可修改描述符属性值。
4.1.4 可枚举性enumerable
该属性值控制的是属性是否会出现在对象的属性枚举中,比如说for…in循环(会遍历对象自身的和继承的可枚举属性)或者Object.keys()方法(返回对象自身的所有可枚举属性)。
// demo4.1.4 var obj = {}; // 指定属性a,其value外的所有描述符属性都为true Object.defineProperty(obj, 'a', { value: 1, writable: true, enumerable: true, configurable: true }); // 指定属性b,其value外的所有描述符属性都为true Object.defineProperty(obj, 'b', { value: 2, writable: true, enumerable: true, configurable: true }); console.log(Object.keys(obj)); // ["a", "b"] for (let key in obj) { console.log(`${key}: ${obj[key]}`); } // a: 1 b:2 //将属性b的描述符对象的可枚举属性更改为false Object.defineProperty(obj, 'b', { enumerable: false, }); console.log(Object.keys(obj)); // ["a"] for (let key in obj) { console.log(`${key}: ${obj[key]}`); } // a: 1
可以看到,enumerable控制了属性的可枚举性。
4.2 访问器属性描述符对象
4.2.1 get方法读取属性值时所调用的方法,会触发隐藏的getter操作。设置了get或set方法的属性,会自动变成访问器属性,不再从value读取属性值。用Object.getOwnPropertyDescriptor方法也不再返回value和writable。
// demo4.2.1-1 var obj = {}; Object.defineProperty(obj, 'a', { // 属性a的值由get函数返回 get: function() { return 1; } }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {get: ƒ, set: undefined, enumerable: false, configurable: false} console.log(obj.a); // 1
另外,你也可以在对象字面量定义中直接指定属性的get方法来触发getter操作。
// demo4.2.1-2 var obj = { get a() { return 1; 11316 } }; console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {get: ƒ, set: undefined, enumerable: false, configurable: false} console.log(obj.a); // 1
4.2.2 set方法
设置属性值时所调用的方法,会触发隐藏的setter操作。设置了get或set方法的属性,会自动变成访问器属性,不再从value读取属性值。用Object.getOwnPropertyDescriptor方法也不再返回value和writable。
// demo 4.2.2-1 var obj = { _a: 1, _doubleA: 2 }; Object.defineProperty(obj, 'a', { get: function() { return this._a; }, // set方法能接收用户传入的值,并利用该值对对象的任一属性做操作 set: function(val) { this._a = val; this._doubleA = 2 * this._a; } }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {get: ƒ, set: ƒ, enumerable: false, configurable: false} console.log(Object.getOwnPropertyDescriptor(obj, '_a')); // {value: 1, writable: true, enumerable: true, configurable: true} console.log(Object.getOwnPropertyDescriptor(obj, '_doubleA')); // {value: 2, writable: true, enumerable: true, configurable: true} console.log(obj.a); // 1 console.log(obj._a); // 1 console.log(obj._doubleA); // 2 obj.a = 2; console.log(Object.getOwnPropertyDescriptor(obj, '_a')); // {value: 2, writable: true, enumerable: true, configurable: true} console.log(Object.getOwnPropertyDescriptor(obj, '_doubleA')); // {value: 4, writable: true, enumerable: true, configurable: true} console.log(obj.a); // 2 console.log(obj._a); // 2 console.log(obj._doubleA); // 4
另外,你也可以在对象字面量定义中直接指定属性的set方法来触发setter操作。
// demo 4.2.2-2 var obj = { _a: 1, _doubleA: 2, get a() { return this._a; }, set a(val){ this._a = val; this._doubleA = 2 * this._a; } }; console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {get: ƒ, set: ƒ, enumerable: false, configurable: false} console.log(Object.getOwnPropertyDescriptor(obj, '_a')); // {value: 1, writable: true, enumerable: true, configurable: true} console.log(Object.getOwnPropertyDescriptor(obj, '_doubleA')); // {value: 2, writable: true, enumerable: true, configurable: true} console.log(obj.a); // 1 console.log(obj._a); // 1 console.log(obj._doubleA); // 2 obj.a = 2; console.log(Object.getOwnPropertyDescriptor(obj, '_a')); // {value: 2, writable: true, enumerable: true, configurable: true} console.log(Object.getOwnPropertyDescriptor(obj, '_doubleA')); // {value: 4, writable: true, enumerable: true, configurable: true} console.log(obj.a); // 2 console.log(obj._a); // 2 console.log(obj._doubleA); // 4
4.2.3 可配置性 configurable
和在数据属性描述符对象中功能一样。表示能否通过delete删除属性,能否修改属性特性,能否把访问器属性修改为数据属性。
4.2.4 可枚举性enumerable
和在数据属性描述符对象中功能一样。表示能否通过for-in循环返回属性。
5. 不同方法创建对象属性以后的属性描述符默认值
5.1 对象字面量指定属性
// demo5.1 // 普通的对象字面量定义方法,是数据属性,writable,enumerable和configurable默认都是true var obj1 = { a: 1 }; // 对象字面量中指定某个属性的get方法或者set方法,是访问器属性,enumerable和configurable默认是true,get或者set仅指定一者的情况下另一个为undefined var obj2 = { get a() { return 1; } } console.log(Object.getOwnPropertyDescriptor(obj1, 'a')); // {value: 1, writable: true, enumerable: true, configurable: true} console.log(Object.getOwnPropertyDescriptor(obj2, 'a')); // {get: ƒ, set: undefined, enumerable: true, configurable: true}
5.2 构造函数
// demo 5.2 // 构造函数定义方法,是数据属性,writable,enumerable和configurable默认都是true function Obj() { this.a = 1; } var obj = new Obj; console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 1, writable: true, enumerable: true, configurable: true}
5.3 Object.create()方法
Object.create()方法其实是在一个空对象的原形上添加属性并返回该对象。// demo 5.3 var obj = Object.create({a:1}); // 返回一个空对象 // console.log(obj); // {} console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // undefined // 返回的对象,其原形上面该属性为数据属性,writable,enumerable和configurable默认都是true console.log(Object.getOwnPropertyDescriptor(obj.__proto__, 'a')); // {value: 1, writable: true, enumerable: true, configurable: true}
5.4 Object.defineProperty()方法和Object.defineProperties()方法
// demo 5.4 var obj = {}; // Object.defineProperty()方法指定的属性,如果描述符对象为空,默认是数据属性,value默认是undefined,writable,enumerable和configurable默认都是false Object.defineProperty(obj, 'a', { }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: undefined, writable: false, enumerable: false, configurable: false} // Object.defineProperty()方法指定的数据属性,value默认是undefined,writable,enumerable和configurable默认都是false Object.defineProperty(obj, 'b', { value: 1 }); console.log(Object.getOwnPropertyDescriptor(obj, 'b')); // {value: 1, writable: false, enumerable: false, configurable: false} // Object.defineProperty()方法指定的访问器属性,set或者get未指定默认是undefined,enumerable和configurable默认都是false Object.defineProperty(obj, 'c', { get: function() { return 1; } }); console.log(Object.getOwnPropertyDescriptor(obj, 'c')); // {get: ƒ, set: undefined, enumerable: false, configurable: false} // 用Object.defineProperties()方法同时定义的多个属性,与Object.defineProperty()方法定义的表现相同 Object.defineProperties(obj, { 'd': { value: true, writable: true }, 'e': { value: 'Hello', writable: false } // etc. etc. }); console.log(Object.getOwnPropertyDescriptor(obj, 'd')); // {value: 1, writable: false, enumerable: false, configurable: false} console.log(Object.getOwnPropertyDescriptor(obj, 'e')); // {get: ƒ, set: undefined, enumerable: false, configurable: false}
6. 数据属性和访问器属性的区别和使用场景
到这里,你应该会有这样的疑惑,为什么要有两种属性呢,它们有什么关系,又有哪些应用场景呢?其实,我们从第5节内容可以看到,如果不使用Object.defineProperty()或者Object.defineProperties()以及指定get和set等特殊方法定义的对象属性,默认都是数据属性。这其实是由我们的需求场景来定的。
1. 在一般情况下,我们关注的只是属性的赋值与取值,也就是仅仅关注数据属性的value。
2. 有只读需求的情况,即如果不允许直接以赋值方式更改属性值,就需要用到数据属性的writable特性,将其设置为false。当然如果此时configurable若为true,仍然允许使用Object.defineProperty()方法来更改属性值。如果要完全返回固定值,且不允许任何方式修改,可以设置configurable为false。当然这里使用访问器属性一样能达到只读效果。
3. 如果要求对用户的输入进行特殊处理,或者设置属性的依赖关系,就需要用到访问器属性了。
综上,访问器属性的功能最强大,但是在一般需求情况下,我们使用数据属性即可。
7. 能够操纵属性描述符的方法
7.1 为目标对象设置单个属性
设置单个属性可以使用Object.defineProperty()方法,要注意的是,对于同一个属性,不可以同时在描述符中指定属于数据描述符的value,writable和属于访问器描述符的get,set,否则会报错。7.2 为目标对象设置多个属性
设置多个属性可以使用Object.defineProperties()方法。7.3 禁止目标对象扩展(不允许添加新属性)
禁止添加新属性可以使用Object.preventExtensions()方法,该方法接收一个目标对象传入,使用后该对象禁止添加新属性。使该对象禁止扩展。// demo7.3 var obj = { a:1 }; Object.preventExtensions(obj); obj.b = 2; obj.b; // undefined // 也不能使用Object.defineProperty方法进行扩展,会报错,报错如图7.3示 Object.defineProperty(obj, 'c', { value: 3, writable: true, enumerable: true, configurable: true });
7.4 密封目标对象(不允许扩展且不允许进行属性配置)
我们可以使用Object.seal() 方法创建一个“密封”的对象,这个方法实际上会在一个现有对象上调用 Object.preventExtensions()方法 并把所有现有属性标记为configurable:false。密封之后不仅不能添加新属性,也不能重新配置或者删除任何现有属性(但是可以修改属性的值)// demo7.4 var obj = { a:1 }; console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 1, writable: true, enumerable: true, configurable: true} Object.seal(obj); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 1, writable: true, enumerable: true, configurable: false} obj.a = 2; console.log(obj.a); // 2
7.5 冻结目标对象(密封对象且不允许修改)
Object.freeze(..) 会创建一个冻结对象,这个方法实际上会在一个现有对象上调用 Object.seal(..) 并把所有“数据访问”属性标记为 writable:false,这样就无法修改它们的值。// demo7.5-1 var obj = { a:1 }; console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 1, writable: true, enumerable: true, configurable: true} Object.freeze(obj); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {value: 1, writable: false, enumerable: true, configurable: false} obj.a = 2; console.log(obj.a); // 1
有的人可能会想,那么此时我如果去修改访问器属性值呢?事实上,对访问器属性的修改实际是通过内部的数据属性为桥梁进行修改的,此时内部的数据属性已经无法修改了,那么对访问器的属性修改也是无效的了。
// demo7.5-2 // 对比demo 4.2.2-1 var obj = { _a: 1, _doubleA: 2 }; Object.defineProperty(obj, 'a', { get: function() { return this._a; }, // set方法能接收用户传入的值,并利用该值对对象的任一属性做操作 set: function(val) { this._a = val; this._doubleA = 2 * this._a; } }); console.log(Object.getOwnPropertyDescriptor(obj, 'a')); // {get: ƒ, set: ƒ, enumerable: false, configurable: false} console.log(Object.getOwnPropertyDescriptor(obj, '_a')); // {value: 1, writable: true, enumerable: true, configurable: true} console.log(Object.getOwnPropertyDescriptor(obj, '_doubleA')); // {value: 2, writable: true, enumerable: true, configurable: true} console.log(obj.a); // 1 console.log(obj._a); // 1 console.log(obj._doubleA); // 2 // 冻结对象导致_a变成只读属性,从而依赖其返回的a和_doubleA也变成只读属性。此处将_a的writable和configurable属性设置为false,得出的结果会是一样的。 Object.freeze(obj); obj.a = 2; console.log(Object.getOwnPropertyDescriptor(obj, '_a')); // {value: 1, writable: false, enumerable: true, configurable: false} console.log(Object.getOwnPropertyDescriptor(obj, '_doubleA')); // {value: 2, writable: false, enumerable: true, configurable: false} console.log(obj.a); // 1 console.log(obj._a); // 1 console.log(obj._doubleA); // 2
参考
BOOK-《JavaScript高级程序设计(第3版)》第6章BOOK-《你不知道的JavaScript》 第2部分
JS属性特性(属性描述符)
ECMAScript 5中的属性描述符详解
stackoverflow:Difference between accessor property and data property in ECMAScript?
msdn.microsoft.com/library:数据属性和访问器属性
相关文章推荐
- JS入门难点解析1-JS的数据类型和按值传参
- JS入门难点解析4-执行上下文栈
- 属性动画图片从上移动到屏幕中间,放大图片的二倍再缩小到原来,自定义圆实现倒计时,解析数据显示,点击条目实现js交互
- js属性类型(数据属性和访问器属性)
- JS入门难点解析5-变量对象
- JS对象高级属性:数据属性与访问器属性
- JS属性-------内部属性(数据属性和访问器属性)
- JS入门难点解析6-作用域链
- 属性动画图片从上移动到屏幕中间,放大图片的二倍再缩小到原来,自定义圆实现倒计时,解析数据显示,点击条目实现js交互
- 属性的特征描述可以分为两类:数据属性和访问器属性
- JS入门难点解析2-JS的变量提升和函数提升
- JS 数据属性 & 访问器属性
- JS中的克隆与数据属性和访问器属性
- 【JS】对象3种属性:数据属性/访问器属性/内部属性
- JS入门难点解析10-创建对象
- JS入门难点解析12-继承的实现方式与优缺点
- JS数据属性和访问器属性
- JS中的数据属性和访问器属性
- JS解析json数据(json字符串与js对象的互相转换)
- js动态修改input输入框的type属性(实现方法解析)