經(jīng)常有童鞋在碰到會困惑于JavaScript的對象屬性的(properties)的特性(attribute不過文中多次以屬性properties,也就是屬性的屬性出現(xiàn),只過個人覺得這么叫容易引起困惑)問題,看到國外有網(wǎng)友很形象地分析了,很值得細品與分享,遂譯。如題,Javascript屬性(properties) 是可枚舉、可寫、和可配置的,我們用得最多的屬性的一個特性是value,基余的幾個分別是enumerable, writable and configurable。
對象Objects是Javascript最重要的一個部分, JS的對象語法非常的簡潔易用,所以我們可以毫不費力地不斷地建立和使用他們。
下面是關(guān)于 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(值):這里是我們經(jīng)常用到的——給對象的屬性賦值
- Enumerable(可枚舉): 我可以使用for..in循環(huán)訪問所有的屬性, 同時,一個對象的可枚舉屬性的鍵值返回所使用的是Object.keys方法
- Writable(可寫): 我可以修改它們的值,我能更新一個屬性property,僅僅只需分配一個新值給它,如:
ob.a = 1000 - Configurable(可配置): 我可以修改一個屬性的表現(xiàn)behavior,因此,我可以使得他們變成不可枚舉(non-enumerable),不可寫(non-writable)甚至不可配置(non-cofigurable),如果我覺得有必要這么做的話??膳渲脤傩裕–onfigurable properties)是使用delete操作時唯一能被刪除的。
我敢打賭,你應(yīng)該已經(jīng)知道對象屬性的前三個特征,但是只有很少的開發(fā)者知道他們可以使用對象(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方法來看一看任何屬性的描述符。
有趣的是,對于那一類已經(jīng)配置的對象來說,當使用Object.defineProperty分配并添加一個屬性的時候,對象屬性的默認選項相關(guān)值是完全相反的:
屬性property默認指定的設(shè)置為不可枚舉(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)實例化對象的話,在對象建立時定義對象屬性同樣是可以的。它接收一個對象的屬性描述符作為第二個參數(shù),一般可以按如下方式使用:
<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循環(huán)來訪問,因此不可枚舉non-enumerable的屬性就不支持。基本上講,在使用大多數(shù)的函數(shù)來把對象作為hashmaps來處理的時,不可枚舉屬性是不可用的。
- 他們不可用于
for..in循環(huán) - 在使用
Object.keys函數(shù)時,他們不會出現(xiàn) - 當使用
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循環(huán)
// 1
// 2
// 3
Object.keys( ob );
// => ["a", "b", "c"]在使用Object.keys函數(shù)時,他們不會出現(xiàn)
JSON.stringify( ob ); // => "{a:1,b:2,c:3}"
ob.d;
// => 4當使用JSON.stringify時,他們不會系列序列化(serialized)
</pre>
基于對像屬性的這一類屬性(properties或特性attribute)不會被序列化(serialize),我發(fā)現(xiàn)他們真的是非常非常的好用,在我需要處理對象數(shù)據(jù)模型時(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組成的數(shù)組。
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>
總結(jié):
Object.defineProperty 在ES5時被引入, 你可以現(xiàn)在立刻就使用它,一般所有的主流瀏覽器都支持,包括IE 9 (甚至IE 8也支持,但僅限于DOM對象。用一種與平常不同的方式來使用JavaScript的基本用法,這通常是非常用趣的,而且它是非常易學的新東西僅僅只需要觀察JS的核心對象是如何開展工作的。
Object.defineProperty同樣給了我們一個機會來為對象的屬性建立個性定制(getters and setters),但是關(guān)于這方面我今天不會寫,如果你對此感興趣或想了解更多參看amazing Mozilla的文檔。
譯自:http://arqex.com/967/javascript-properties-enumerable-writable-configurable
Tag:ECMAScript2015, ES6, JavaScript
發(fā)布時間:2015年08月25日
博客被黑,挪窩簡書安家……