經(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),如果我覺得有必要這么做的話确憨∫胲瘢可配置屬性(Configurable properties)是使用delete操作時唯一能被刪除的。
我敢打賭休弃,你應該已經(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默認指定的設置為不可枚舉(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日
博客被黑狰住,挪窩簡書安家……