對javscript中Object.defineProperty的理解

  本身在使用vue的過程當中常常會用到聽到數據雙向綁定這個詞,並且咱們還能夠直接經過調用this.msg(this表示vue實例),來獲取data上的數據,之前一直不太明白爲何能夠這樣獲取,直到有一天我在論壇裏看到了尋找海藍96這位大佬寫的文章,才明白其原理,因此在此記錄一下。vue

一、Object.defineProperty(obj,key,descriptor)

  Object.defineProperty主要是經過Get和Set這兩個訪問器屬性來實現數據雙向綁定的。說到這裏必須先介紹如下js對象的兩種特性。 ECMAScript中有兩種屬性:數據屬性和訪問器屬性。這是js中任何對象都擁有的特性。

數據屬性主要是用於對數據的描述。數據屬性主要有如下4個特性。
一、Configurable:(字面意思是可配置的,我理解爲可操做(刪除、修改))表示可否經過delete操做來刪除屬性,以及可否修改屬性,或者可否將屬性修改成訪問器屬性。直接定義在對象上的屬性,該特性值默認爲true。
二、Enumerable:字面意思是可枚舉的,表示可否經過for-in循環來遍歷屬性。直接在對象上定義的屬性,該特性值默認爲true。
三、Writable:表示可否修改屬性的值。直接定義在對象上的該特性值默認也爲true。
四、Value:這個特性包含着屬性的數據值。對對象屬性的讀寫操做都是在這個特性上。該特性的默認值爲undefined。函數

  想要修改上面的4個特性就得調用Object.defineProperty(obj,key,descriptor)方法,descriptor表示的是一個對象,對象中的屬性必須是這四個特性中的一項或多項。this

注意: 一、屢次調用Object.defineProperty()方法修改同一個屬性,只要把configurable特性設置爲false後,就不能再把它變成可配置的了(再次調用Object.defineProperty()將configurable特性設置爲true會報錯),即這個過程是不可逆的,不能再對對象屬性進行delete操做,可是還能夠對對象屬性進行修改操做。spa

Object.defineProperty(obj,"name",{
    configurable:false,
    enumerable:true,
    writable:true,
    value:"james"
});
delete obj.name;
console.log(obj);//{name: "james"},可見不能對obj進行刪除操做了。
obj.name="test";
console.log(obj.name);//test 仍是能夠對屬性進行修改
複製代碼

二、調用Object.defineProperty()方法時,若是不指定Configurable、Enumerable和writable等特性的值,默認爲false。雙向綁定

訪問器屬性也有4個。可是不包含數據值
一、Configurable:(字面意思是可配置的,我理解爲可操做(刪除、修改))表示可否經過delete操做來刪除屬性,以及可否修改屬性,或者可否將屬性修改成訪問器屬性。直接定義在對象上的屬性,該特性值默認爲true。
二、Enumerable:字面意思是可枚舉的,表示可否經過for-in循環來遍歷屬性。直接在對象上定義的屬性,該特性值默認爲true。
三、Get:在讀取屬性時調用的函數。 默認值爲undefined。
四、Set:在寫入屬性時調用的函數。的默認值爲undefined。code

注意:不能使用Object.defineProperty()方法同時修改默認數據屬性和訪問器屬性。即set和get訪問器屬性不能與writable以及value特性共存。對象

Object.defineProperty(obj,"name",{
    configurable:true,
    enumerable:true,
    writable:true,
    value:"james",
    get(){
        return this.val;
    },
    set(newVal){
        this.val=newval;
    }
});
//Uncaught TypeError: Invalid property descriptor. Cannot both specify accessors and a value or writable attribute, #<Object>
複製代碼

  我的以爲數據屬性和訪問器屬性的Configurable和Enumerable沒什麼區別。訪問器屬性主要用的是Get和Set這兩個。ip

能夠經過Object.defineProperty()實現簡單的數據雙向綁定。實現代碼以下:ci

<input id="test1"/>
<input id="test2"/>
<script>
    let obj={};
    Object.defineProperty(obj,"name",{
        configurable:true,
        enumerable:true,
        writable:true,
        // value:"",
        set(newValue){
            document.querySelector("#test1").value=newValue;
            document.querySelector("#test2").value=newValue;
        },
        get(){
            return obj["name"];
        }
    });
    document.addEventListener("keyup",function(e){
        obj.name=e.target.value;
    })
</script>

//經過操做能夠發現,在test1中輸入會改變test2中的值,在test2中輸入也能改變test1中的值。
複製代碼

vue中爲何能夠直接經過this.msg獲取到data中的msg,原理代碼以下:get

class Vue{
        constructor(options={}){
            this.$options=options;
            this._data=options.data;
            let data=this._data;
            Object.keys(data).forEach((key)=>{
                this._proxy(key)
            })
        };
        _proxy(key){
            //以this作爲obj,將data對象上的屬性所有綁定到vue實例上來
            Object.defineProperty(this,key,{
                configurable:true,
                enumerable:true,
                get(){
                    return this._data[key];
                },
                set(newval){
                    this._data[key]=newval;
                }
            })
        }
    }
複製代碼
相關文章
相關標籤/搜索