瞭解下數據屬性和訪問器屬性的特性

通常我們會這樣定義一個普通對象,

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的configurablewritable,將它們都置爲false了,這也是之後無法改變obj屬性的原因。

以上簡單介紹了數據屬性的幾個特性,順帶也瞭解下訪問器屬性的特性吧。
差不多,configurableenumerablevaluewritable ,換成getset
先來個簡單的例子吧。

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'
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章