溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JS對(duì)象屬性的特性和defineProperty方法的應(yīng)用

發(fā)布時(shí)間:2020-06-21 19:32:39 來(lái)源:億速云 閱讀:187 作者:鴿子 欄目:web開(kāi)發(fā)

對(duì)象是無(wú)序?qū)傩缘募?,而這些屬性在創(chuàng)建是都帶有一些特征值(可以理解為屬性的屬性,天生自帶的),這些特征值是為了實(shí)現(xiàn)JavaScript引擎用的,因此JavaScript不能直接訪問(wèn)。

JavaScript通過(guò)這些特征值來(lái)定義屬性的行為(屬性是否刪除,枚舉,修改等)。

例如,在全局定義的屬性是會(huì)掛載到window上的。當(dāng)想刪除window上的這個(gè)屬性,是不可以的。也就是說(shuō)window上的屬性是不可配置的。delete window.obj     //false

Function.prototype當(dāng)你修改成其他值,其原始值并沒(méi)有改變。是不可寫的。

在比如,我們的for in是可以枚舉原型鏈上屬性的,但所有的原型頂端都是Object.prototype.但for in 并沒(méi)有枚舉出來(lái)。所以O(shè)bject.prototype是不可枚舉的。

屬性分為兩種類型:1數(shù)據(jù)屬性 2訪問(wèn)器屬性。例如:一般我們自己在對(duì)象設(shè)置的屬性默認(rèn)是數(shù)據(jù)屬性,而Window上的那么屬性是訪問(wèn)器屬性。

怎樣知道這個(gè)屬性到底是數(shù)據(jù)屬性還是訪問(wèn)器屬性?

使用Object.getOwnPropertyDescriptor(屬性所在的對(duì)象,屬性)方法。 返回一個(gè)對(duì)象,當(dāng)時(shí)訪問(wèn)器屬性時(shí),該對(duì)象屬性有enumerable,configurable,get,set。當(dāng)時(shí)數(shù)據(jù)屬性,該對(duì)象返回的屬性有value,writable,enumerable,configurable.

var obj  = {name:'zwq',age:18};
console.log(Object.getOwnPropertyDescriptor(obj,'name'));  //name屬性屬性
//{value: "zwq", writable: true, enumerable: true, configurable: true}

console.log(Object.getOwnPropertyDescriptor(window,'name'));  //window上的name屬性時(shí)訪問(wèn)器屬性
//enumerable: true, configurable: true, get: ?, set: ?}

數(shù)據(jù)屬性

 ● 數(shù)據(jù)上行包含一個(gè)數(shù)據(jù)值的位置,可以讀取和寫入值,數(shù)據(jù)屬性有4個(gè)描述其行為的屬性,由于是這些值不能直接訪問(wèn),是內(nèi)部值,所以該規(guī)范把他們放在兩對(duì)括號(hào)中。

 ● 屬性是否可配置:[[Configurable]]:表能否通過(guò)delete刪除屬性,能夠修改屬性的特性,能否把屬性修改為訪問(wèn)器屬性

 ● 屬性是否可枚舉:[[Enumerable]]:表能否通過(guò)for-in循環(huán)返回屬性

 ● 屬性是否可修改:[[Writable]]

 ● 屬性的數(shù)據(jù)值 :[[value]]讀取屬性的時(shí)候,從這個(gè)位置讀,寫入屬性的時(shí)候,把新值保存到這個(gè)位置。

普通定義的屬性 默認(rèn)值前三個(gè)都是true,最后一個(gè)是undefined。

當(dāng)我們想到修改屬性默認(rèn)的特性,使用Object.defineProperty(屬性所在對(duì)象,屬性的名字,描述符對(duì)象)方法。

當(dāng)使用Object.defineProperty方法第二個(gè)參數(shù)屬性的名字不存在時(shí),該方法會(huì)創(chuàng)建這個(gè)屬性,并且該屬性的特性除了value剩下的特性的默認(rèn)值都是false。也就是說(shuō)當(dāng)你想讓這個(gè)用Object.defineProperty方法創(chuàng)建的屬性跟正常的屬性一樣可枚舉,配置,寫入,必須把這個(gè)屬性值的特性都改為true。否則就是false。

var obj  = {name:'zwq',age:18};

        Object.defineProperty(obj,'name',{ 修改name屬性的特性,值為haha,并且name屬性不能修改值
            value:'haha',
            writable:false  //默認(rèn)值是true,改為false,不可寫。
        })

        Object.defineProperty(obj,'sex',{創(chuàng)建一個(gè)sex屬性,這個(gè)屬性不可枚舉
            value:'woman',
            writable:true,
            configurable:true,
        })

JS對(duì)象屬性的特性和defineProperty方法的應(yīng)用

訪問(wèn)器屬性

訪問(wèn)器屬性不包含writable和value,他包含的是一對(duì)getter和setter函數(shù),在讀取訪問(wèn)器屬性是,會(huì)調(diào)用getter函數(shù),并返回有效的值,在寫入訪問(wèn)器屬性時(shí)(修改屬性)會(huì)調(diào)用setter函數(shù)并傳入新值。訪問(wèn)器包含4個(gè)特性

 ● 屬性是否可配置:[[Configurable]]:表能否通過(guò)delete刪除屬性,能夠修改屬性的特性,能否把屬性修改為訪問(wèn)器屬性

 ● 屬性是否可枚舉:[[Enumerable]]:表能否通過(guò)for-in循環(huán)返回屬性

 ● [[Get]]:在讀取屬性時(shí)調(diào)用的函數(shù)。默認(rèn)值是undefined。

 ● [[Set]]:在寫入(或修改)屬性時(shí)調(diào)用的函數(shù)。默認(rèn)值undefined。

定義訪問(wèn)器屬性,同樣也必須商用Object.defineProperty().

function Person(){
            this._name = 'zwq',
            this.age =  18
        }
        var person = new Person();
        
        
        Object.defineProperty(person,'name',{
            set(newValue){
                console.log('set');
                this._name = newValue   //設(shè)置或修改屬性時(shí),會(huì)調(diào)用set函數(shù),把設(shè)置的值通過(guò)參數(shù)傳進(jìn)去后,用一個(gè)變量或?qū)傩员4?。并且?dāng)調(diào)用get,return就是返回的這個(gè)值
            },
            get(){
                return this._name;   //當(dāng)讀取屬性時(shí) 返回return的值
            }
            
        })

不一定非要同時(shí)指定getter和setter,只指定getter意味著屬性是不能寫。

vue的雙向數(shù)據(jù)劫持綁定(主要應(yīng)用于表單中)的原理就是利用Object.defineProperty來(lái)檢測(cè)數(shù)據(jù)的變化。

雙向劫持綁定時(shí)當(dāng)視圖(頁(yè)面的某一元素)發(fā)生改變時(shí),數(shù)據(jù)跟著改變,當(dāng)數(shù)據(jù)改變時(shí),視圖也跟著改變。例如下面的輸入框里面的內(nèi)容改變時(shí),數(shù)據(jù)(對(duì)象或數(shù)組)改變。檢測(cè)數(shù)據(jù)改變。底下的p文本根據(jù)數(shù)據(jù)的改變而改變。

上面我們介紹到,當(dāng)數(shù)據(jù)改變時(shí)會(huì)觸發(fā)set方法。由此我們就可以檢測(cè)數(shù)據(jù)的變化。

//檢測(cè)對(duì)象的變化。
     var input = document.getElementById('Oinput');
        var view = document.getElementById('view');

        var data = {
            valueObj :{
                value:'zwq'
            }
        }
        //當(dāng)輸入框數(shù)據(jù)發(fā)生改變時(shí),數(shù)據(jù)跟著改變
        input.oninput = function(){
            data.valueObj.value = this.value;
        }
        
        // 更新視圖
        function upData(){   
            view.innerText = data.valueObj.value;
        }
        
        upData(data);
        obServe(data);

        // 監(jiān)控某個(gè)對(duì)象是否發(fā)生改變
        function obServe(data){
           //判斷當(dāng)前傳的是否是對(duì)象,如果不是,直接return
            if(!data || !(data instanceof Object)){return data}
       //獲取所有屬性名。使用keys方法可以獲取所有屬性名(包括原型上的)并保存帶數(shù)組中
            var arrProperty = Object.keys(data);
       //遍歷數(shù)組,調(diào)用defindRective檢測(cè)每一個(gè)屬性值的改變 
            arrProperty.forEach(function(key){
          defindRective(data,key,data[key]); //傳入3個(gè)參數(shù),當(dāng)前對(duì)象,當(dāng)前屬性,當(dāng)前屬性值
            })
        }

        function defindRective(obj,key,val){
           obServe(val);   //使用遞歸,當(dāng)想上面的數(shù)組,對(duì)象套對(duì)象的形式,由于里面的對(duì)象是一個(gè)引用值,無(wú)法檢測(cè)里面的數(shù)據(jù)變化,所以使用遞歸。
            Object.defineProperty(obj,key,{  //核心:使用Object,definPropert的set檢測(cè)數(shù)據(jù)的改變。
                set(newValue){
                    console.log(5);
                    if(newValue == val) return val;
                    val = newValue;
                    upData();   //當(dāng)數(shù)據(jù)變化,跟新視圖
                },
                get(){
                    return  val;
                }
            })
        }
// 監(jiān)測(cè)數(shù)組,將數(shù)組原型重寫
// 當(dāng)操作數(shù)組的arr push pop unshift slice...才會(huì)檢測(cè)
        let {push} = Array.prototype;
        var arr = [1,2,3];
        function upData(){
            console.log('更新');
        }
        Object.defineProperty(Array.prototype,'push',{
            value:(function(){
                return (...arg) => {
                    upData();
                    push.apply(arr,arg);
                }
            })()
        })
 arr.push(8,9);

由于使用Object.defineProperty檢測(cè)數(shù)組和對(duì)象變化要分開(kāi)實(shí)現(xiàn)。而且,當(dāng)添加數(shù)據(jù)時(shí),不會(huì)檢測(cè)到的。

所以ES6中添加了Proxy來(lái)實(shí)現(xiàn)。

Proxy&reflect簡(jiǎn)介:植入代理的模式,以簡(jiǎn)潔易懂的方式控制對(duì)外部對(duì)象的訪問(wèn),利用set,get方法控制屬性的讀寫功能,還有其余的has,desProperty。。等方法。但proxy兼容性不好,使用時(shí)要注意。

proxy是一個(gè)構(gòu)造函數(shù),通過(guò)代理的方式將你想到代理的對(duì)象傳給構(gòu)造函數(shù),并且需要傳入?yún)?shù)對(duì)象對(duì)讀和寫進(jìn)行控制。使用new方法實(shí)例化代理的對(duì)象,

此后,當(dāng)修改,或添加屬性都使用代理的對(duì)象。

let data = {
    value:'zwq',
}
// let data = [1,2];
let oProxyData = new Proxy(data,{
    set(target,key,value,receiver){  //傳入4個(gè)參數(shù) 對(duì)象 屬性 屬性值 代理的對(duì)象
        // target[key] = value;
        Reflect.set(target,key,value); //等同于上一步
        upData();
  },
    get(target,key,receiver){
        // console.log(target,key);
        Reflect.get(target, key);
    },
    has(target,key){ //當(dāng)使用in時(shí)觸發(fā)當(dāng)前函數(shù)。
        return key in target;   //in --檢測(cè)對(duì)象能否訪問(wèn)該屬性,能訪問(wèn)返回true,不能false,無(wú)論是在實(shí)例還是原型中。
    }
});

console.log('valu' in oProxyData);

function upData(){
    console.log("更新啦");
}

oProxyData.value = 20;

以上就是淺談JavaScript對(duì)象屬性的特性和defineProperty方法的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注億速云其它相關(guān)文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI