经常有童鞋在碰到会困惑于JavaScript的对象属性的(properties)的特性(attribute不过文中多次以属性properties,也就是属性的属性出现,只过个人觉得这么叫容易引起困惑)问题,看到国外有网友很形象地分析了,很值得细品与分享,遂译。如题,Javascript属性(properties) 是可枚举、可写、和可配置的,我们用得最多的属性的一个特性是value,基余的几个分别是enumerable, writable and configurable。
对象Objects是Javascript最重要的一个部分, JS的对象语法非常的简洁易用,所以我们可以毫不费力地不断地建立和使用他们。
下面是关于 JS 对象Objects的基本用法:
<pre>
// My beloved object ob
var ob = {a: 1};
// Accessing to a property
ob.a; // => 1
// Modifying the value of a property
ob.a = 0;
ob.a; // => 0;
// Creating a new property
ob.b = 2;
ob.b; // => 2
// Deleting a property
delete ob.b;
ob.b; // => undefined
</pre>
但是, 你是否同时也知道上述例子中所有的对象属性(object properties)是可枚举、可写和可配置的呢?我的意思是:
- value(值):这里是我们经常用到的——给对象的属性赋值
- Enumerable(可枚举): 我可以使用for..in循环访问所有的属性, 同时,一个对象的可枚举属性的键值返回所使用的是Object.keys方法
- Writable(可写): 我可以修改它们的值,我能更新一个属性property,仅仅只需分配一个新值给它,如:
ob.a = 1000
- Configurable(可配置): 我可以修改一个属性的表现behavior,因此,我可以使得他们变成不可枚举(non-enumerable),不可写(non-writable)甚至不可配置(non-cofigurable),如果我觉得有必要这么做的话。可配置属性(Configurable properties)是使用delete操作时唯一能被删除的。
我敢打赌,你应该已经知道对象属性的前三个特征,但是只有很少的开发者知道他们可以使用对象(Object)的一个被称为defineProperty的方法,来建立和更新这些对象属性为不可枚举(non-enumerable) 或不可变更。
<pre>
// Adding a property to ob using Object.defineProperty
Object.defineProperty( ob, 'c', {
value: 3,
enumerable: false,
writable: false,
configurable: false
});
ob.c; // => 3
Object.getOwnPropertyDescriptor( ob, 'c' );
// => {value: 3, enumerable: false, writable: false, configurable: false}
</pre>
我个人觉得这种语法相比我们常用的其它命令来说算不上友好,但是,拥有这类属性对于某些特定目的来说可以说是真的很方便,定义对象(Object)的属性被称为描述符(descriptor),而你则可以使用Object.getOwnPropertyDescriptor
方法来看一看任何属性的描述符。
有趣的是,对于那一类已经配置的对象来说,当使用Object.defineProperty
分配并添加一个属性的时候,对象属性的默认选项相关值是完全相反的:
属性property默认指定的设置为不可枚举(non-enumerable)、不可写(non-writable)、不可配置(non-configurable)。
<pre>
// The 'f' property will be non-enumerable. non-writable and non-configurable
Object.defineProperty( ob, 'f', {value: 6} );
</pre>
如果你使用Objet.create(prototype,properties)
实例化对象的话,在对象建立时定义对象属性同样是可以的。它接收一个对象的属性描述符作为第二个参数,一般可以按如下方式使用:
<pre>
var ob = Object.create(Object.prototype, {
a: { writable:true, enumerable:true, value: 1 },
b: { enumerable: true, value: 2 }
}});
ob; // => {a:1, b:2}
</pre>
对象的不可枚举属性non-enumerable properties
就像我之前说的,枚举属性指的是可否使用for...in循环来访问,因此不可枚举non-enumerable的属性就不支持。基本上讲,在使用大多数的函数来把对象作为hashmaps来处理的时,不可枚举属性是不可用的。
- 他们不可用于
for..in
循环 - 在使用
Object.keys
函数时,他们不会出现 - 当使用
JSON.stringify
时,他们不会系列序列化(serialized)
因此,他们是属于一类秘密的变量, 但你还是能够直接地访问到他们。
<pre>
var ob = {a:1, b:2};
ob.c = 3;
Object.defineProperty(ob, 'd', {
value: 4,
enumerable: false
});
ob.d;
//=> 4你可以直接地访问到他们的value值
for( var key in ob ) console.log( ob[key] );
// Console will print out他们不可用于for..in
循环
// 1
// 2
// 3
Object.keys( ob );
// => ["a", "b", "c"]在使用Object.keys
函数时,他们不会出现
JSON.stringify( ob ); // => "{a:1,b:2,c:3}"
ob.d;
// => 4当使用JSON.stringify
时,他们不会系列序列化(serialized)
</pre>
基于对像属性的这一类属性(properties或特性attribute)不会被序列化(serialize),我发现他们真的是非常非常的好用,在我需要处理对象数据模型时(data model objects),因为我可以使用不可枚举特性(属性)添加处理信息到他们。
注:其实这里的属性个人觉得可以按W3CSchools上面的称为attribute比较合适,毕竟他们不属于标准的property属性。
<pre>
// 假定这个model描绘了一辆汽车,他有一个引用reference到他自身
// reference to its owner using owner's id in the owner attribute
var car = {
id: 123,
color: red,
owner: 12
};
// I also have fetched the owner from the DB
// Of course, the car is mine :)
var owner = {
id: 12,
name: Javi
}
// I can add the owner data to the car model
// with a non-enumerable property, maybe it can
// be useful in the future
Object.defineProperty( car, 'ownerOb', {value: owner} );
// I need the owner data now
car.ownerOb; // => {id:12, name:Javi}
// But if I serialize the car object, I can't see me
JSON.stringify( car ); // => '{id: 123, color: "red", owner: 12}'
</pre>
你能想像这对于建立一个如上例中的 ORM 库(ORM library)是多么有用吗?
在这个案例中你需要知道一个对象中的所有属性properties,可枚举和不可枚举的每一个属性,Object.getOwnPropertyNames
方法就可以返回一个由所有的键名names组成的数组。
Object’s non-writable properties
While the world waits for ES6 to finally arrive with the desired const statement, non-writable properties are the most similar thing to a constant that we have in Javascript. Once its value is defined, it is not possible to change it using assignments.
<pre>
var ob = {a: 1};
Object.defineProperty( ob, 'B', {value: 2, writable:false} );
ob.B; // => 2
ob.B = 10;
ob.B; // => 2
</pre>
As you can see, the assignment didn’t affect the value of ob.B property. You need to be careful, because the assignment always returns the value assigned, even if the property is non-writable like the one in the example. In strict mode, trying to modifying a non-writable property would throw an TypeError exception:
<pre>
var ob = {a: 1};
Object.defineProperty( ob, 'B', {value: 2, writable:false} );
// Assingments returns the value
ob.B = 6; // => 6
ob.B = 1000; // => 1000
// But the property remains the same
ob.B; => 2;
function updateB(){
'use strict';
ob.B = 4; // This would throw an exception
}
updateB(); // Throws the exception. I told you.
</pre>
It is also needed to keep in mind that if the non-writable property contains an object, the reference to the object is what is not writable, but the object itself can be modified yet:
<pre>
var ob = {a: 1};
Object.defineProperty( ob, 'OB', {value: {c:3}, writable:false} );
ob.OB.c = 4;
ob.OB.d = 5;
ob.OB; // => {c:4, d:5}
ob.OB = 'hola';
ob.OB; // => {c:4, d:5}
</pre>
If you want to have a property with an completely non-writable object, you can use the function Object.freeze
. freeze
will make impossible to add, delete or update any object’s property, and you will get a TypeError if you try so in strict mode.
<pre>
var ob = { a: 1, b: 2 };
ob.c = 3;
// Freeze!
Object.freeze( ob ); // => {a:1,b:2,c:3}
ob.d = 4;
ob.a = -10;
delete ob.b;
Object.defineProperty( 'ob', 'e', {value: 5} );
// Every modification was ignored
ob; // => {a:1,b:2,c:3}
</pre>
Object’s non-configurable properties
You can update the previous behaviors of the properties if they are defined as configurable. You can use defineProperty once and again to change the property to writable or to non-enumerable. But once you have defined the property as non-configurable, there is only one behaviour you can change: If the property is writable, you can convert it to non-writable. Any other try of definition update will fail throwing a TypeError.
<pre>
var ob = {};
Object.defineProperty( ob, 'a', {configurable:false, writable:true} );
Object.defineProperty(ob, 'a', { enumerable: true }); // throws a TypeError
Object.defineProperty(ob, 'a', { value: 12 }); // throws a TypeError
Object.defineProperty(ob, 'a', { writable: false }); // This is allowed!!
Object.defineProperty(ob, 'a', { writable: true }); // throws a TypeError
</pre>
An important thing to know about the non-configurable properties is that they can’t be removed from the object using the operator delete. So if you create a property non-configurable and non-writable you have a frozen property.
<pre>
var ob = {};
Object.defineProperty( ob, 'a', {configurable: true, value: 1} );
ob; // => {a:1}
delete ob.a; // => true
ob; // => {}
Object.defineProperty( ob, 'a', {configurable: false, value: 1} );
ob; // => {a:1}
delete ob.a; // => false
ob; // => {a:1}
</pre>
总结:
Object.defineProperty
在ES5时被引入, 你可以现在立刻就使用它,一般所有的主流浏览器都支持,包括IE 9 (甚至IE 8也支持,但仅限于DOM对象。用一种与平常不同的方式来使用JavaScript的基本用法,这通常是非常用趣的,而且它是非常易学的新东西仅仅只需要观察JS的核心对象是如何开展工作的。
Object.defineProperty
同样给了我们一个机会来为对象的属性建立个性定制(getters and setters),但是关于这方面我今天不会写,如果你对此感兴趣或想了解更多参看amazing Mozilla的文档。
译自:http://arqex.com/967/javascript-properties-enumerable-writable-configurable
Tag:ECMAScript2015, ES6, JavaScript
发布时间:2015年08月25日
博客被黑,挪窝简书安家……