通常我們會這樣定義一個普通對象,
var obj = {
foo:"bar"
}
console.log(obj.foo);//輸出'bar'
我們可以修改這個對象的屬性,
obj.foo = "baz";
console.log(obj.foo);//輸出'baz'
但是,一旦Object.freeze(obj)
了,就無法修改對象的屬性了。
var obj = {
foo:"bar"
}
console.log(obj.foo);//輸出'bar'
Object.freeze(obj);
obj.foo = "baz";
console.log(obj.foo);//輸出'baz'
爲啥?Object.freeze()
有什麼神奇魔力?
這個得從數據屬性
的幾個特性
說起了。
var obj = {foo:"bar"}
,foo屬性
就是obj
的數據屬性
。
在控制檯試試Object.getOwnPropertyDescriptors(obj)
或者Object.getOwnPropertyDescriptor(obj,'foo')
。
configurable,enumerable,value和writable就是foo這個屬性的特性。
value
:屬性值,比如obj.foo的屬性值就是bar;
writable
:是否可改寫。我們不妨試試把writable改成false。
var obj = {
foo:"bar"
}
Object.defineProperty(obj,"foo",{
writable:false
});
obj.foo = "world";
console.log(obj.foo);//輸出"bar"
writable置爲false後,就無法改寫obj.foo了。
enumerable
:是否可枚舉。在使用Object.keys()
和Object.getOwnPropertyNames()
時,能夠感受更明顯一些。
var obj = {
foo:"bar"
}
Object.defineProperty(obj,"foo",{
enumerable:false
});
let res = Object.keys(obj);//返回實例的所有可枚舉屬性
let res2 = Object.getOwnPropertyNames(obj);//返回實例的所有屬性,不論是否可枚舉
console.log(res,res2);//輸出[] ['foo']
configurable
:是否可配置。configurable置爲false,delete obj.foo
無效,即無法刪除obj上的foo屬性。且一旦configurable爲false了,就無法重新將其設置爲true,即不可逆。
看看下面的例子就明白了。
var obj = {
foo:"bar"
}
Object.defineProperty(obj,"foo",{
configurable:false
});
delete obj.foo;
console.log(obj.foo)//輸出'bar'
Object.defineProperty(obj,"foo",{
configurable:true
});//報錯, Cannot redefine property: foo
好了,現在來對比下obj在Object.freeze()前後,這四個特性有啥變化。
var obj = {
foo:"bar"
}
//{value: "bar", writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(obj,'foo'));
Object.freeze(obj);
//{value: "bar", writable: false, enumerable: true, configurable: false}
console.log(Object.getOwnPropertyDescriptor(obj,'foo'));
可以看出,Object.freeze()
同時改變了foo的configurable
和writable
,將它們都置爲false了,這也是之後無法改變obj屬性的原因。
以上簡單介紹了數據屬性
的幾個特性,順帶也瞭解下訪問器屬性
的特性吧。
差不多,configurable
,enumerable
, ,value
,換成writable
get
和set
。
先來個簡單的例子吧。
var obj = {
_foo:"bar",
get foo(){
return this._foo;
},
set foo(val){
this._foo = val;
}
}
console.log(Object.getOwnPropertyDescriptors(obj));
console.log(obj.foo);//輸出'bar'
obj.foo = "baz";
console.log(obj.foo);//輸出'baz'
注意哈:這裏一個是foo
,另一個是_foo
(前綴下劃線)。其中,foo是訪問器屬性,_foo是數據屬性。
如果兩個都是foo
,訪問或修改obj.foo時都會進入死循環,最後堆棧溢出。
//如果都是foo
var obj = {
foo:"bar",
get foo(){
return this.foo;
},
set foo(val){
this.foo = val;
}
}
console.log(obj.foo);//會死循環,導致堆棧溢出
除了get foo(){}
和set foo(){}
,還有幾種定義訪問器屬性
的方法,這裏簡單羅列一下吧。
Object.defineProperty()
var obj = {
_foo:"bar"
};
Object.defineProperty(obj,"foo",{
configurable:true,
enumerable:true,
get:function(){
return this._foo;
},
set:function(val){
this._foo = val;
}
});
console.log(obj.foo);//輸出'bar'
obj.foo = "baz";
console.log(obj.foo);//輸出'baz'
Object.defineProperties()
var obj = {
_foo:"bar"
}
Object.defineProperties(obj,{
"foo":{
configurable:true,
enumerable:true,
get:function(){
return this._foo;
},
set:function(val){
this._foo = val;
}
}
});
console.log(obj.foo);//輸出'bar'
obj.foo = "baz";
console.log(obj.foo);//輸出'baz'
obj.__defineGetter__和obj.__defineSetter__
var obj = {
_foo:"bar"
}
obj.__defineSetter__("foo",function(val){
this._foo = val;
});
obj.__defineGetter__("foo",function(){
return this._foo;
});
console.log(obj.foo);//輸出'bar'
obj.foo = "baz";
console.log(obj.foo);//輸出'baz'