Object.defineProperty()和Proxy相關

Vue3.0版本會將數據劫持的方式從Object.defineProperty切換爲Proxy,因此找了時間從新回顧了一下屬性描述,並瞭解瞭如下Proxyvue

1. Object.defineProperty

給對象的屬性設置屬性描述,接受三個參數bash

/*
* obj:須要定義屬性的對象
* prop:定義描述的屬性名
* descriptor: 屬性描述
*/
Object.defineProperty(obj, prop, descriptor);
複製代碼

1.1 descriptor

用於描述屬性的對象,能夠包含如下值app

1.1.1 get

屬性在獲取值的時候會調用該方法ui

var obj = {}
Object.defineProperty(obj, 'a', {
    get() {
        console.log('get a');
        return 1;
    }
})
console.log(obj.a)
// 優先輸出'get a', 以後輸出:1
複製代碼

1.1.2 set

屬性設置值的時候會調用該方法spa

var obj = {}
Object.defineProperty(obj, 'a', {
    set(value) {
        console.log(`set:${value}`);
    }
})
obj.a = 1
// 輸出 `set:1`
複製代碼

1.1.3 value

用於設置對象屬性的初始值,沒法同getset方法同時設置代理

var obj = {}
Object.defineProperty(obj, 'a', {
    value: 1
})
console.log(obj.a) // 1
複製代碼

1.1.4 enumerable

設置屬性是否能夠枚舉,用於for in枚舉屬性時候是否能夠獲取code

var obj = {}
Object.defineProperty(obj, 'a', {
    value: 1,
    enumerable: false, // 設置false沒法經過for in獲取
})
console.log(obj.a) // 1
for(let key in obj) {
    console.log(`key:${key}`) // 不會執行
}
複製代碼

1.1.5 configurable

設置屬性是否能夠再次定義屬性描述orm

var obj = {}
Object.defineProperty(obj, 'a', {
    value: 1,
    configurable: false, // 設置false沒法再次配置屬性
})
Object.defineProperty(obj, 'a', {
    value: 2,
})
// 拋出異常:Uncaught TypeError: Cannot redefine property 'a'
複製代碼

PS:對於已有屬性能夠修改valueenumerablewritable的值(例如:若是obj = {a: 1}這裏修改屬性a的這三個值不會報錯)cdn

1.1.6 writable

設置屬性是否能夠賦值,沒法同getset方法同時設置對象

var obj = {}
Object.defineProperty(obj, 'a', {
    value: 1,
    writable: false, // 設置false,沒法被普通賦值
})
obj.a = 2
console.log(obj.a) // 1
複製代碼

1.2 Object.preventExtensions()

阻止對象擴展新的屬性,不過並不限制對象原型上的屬性擴展

var obj = {a: 1}
Object.preventExtensions(obj)
obj.b = 2 // 嚴格模式下拋出TypeError異常
console.log(obj) // {a: 1}
複製代碼

可使用Object.isExtensible(obj)來判斷是否已經阻止擴展了

1.3 Object.seal()

將使得對象禁止擴展屬性,同時禁止現有屬性的configurable

var obj = {a: 1}
Object.seal(obj)
console.log(Object.isExtensible(obj)) // false
Object.defineProperty(obj, 'a', {
    value: 2
}) // 拋出異常
複製代碼

至關於Object.preventExtensions的基礎上,將已有屬性的configurable都設置爲false

var obj = {a: 1}
Object.preventExtensions(obj)
Object.defineProperty(obj, 'a', {
	configurable: false
})
console.log(Object.isSealed(obj)) // true
複製代碼

可使用Object.isSealed(obj)判斷是否屬性屬於該狀況

1.4 Object.freeze()

Object.seal()的基礎上,將屬性的writable設置爲false

var obj = {a: 1}
Object.freeze(obj)
console.log(Object.isSealed(obj)) // true
obj.a = 2
console.log(obj.a) // 1
複製代碼

至關於

var obj = {a: 1}
Object.seal(obj)
Object.defineProperty(obj, 'a', {
    writable: false
})
console.log(Object.isFrozen(obj)) // true
複製代碼

可使用Object.isFrozen(obj)判斷是否屬性屬於該狀況

2. Object.defineProperties

Object.defineProperty,可一次性批量定義多個對象屬性

Object.defineProperties(obj, {
    prop1: {
        get() {}
        set() {}
        ...
    },
    prop2: {
        get() {}
        set() {}
        ...
    }
})
複製代碼

3. Proxy

使用Proxy能夠建立一個對象的代理

2.1 new Proxy()

使用new Proxy(target, handler)能夠建立對象targetproxy對象,操做proxy對象的時候,根據設置的handler,能夠設置對象操做的各時期的具體操做

  1. getPrototypeOf():在調用Object.getPrototypeOf()的時候
  2. setPrototypeOf():在使用Object.setPrototypeOf()的時候
  3. isExtensible():在使用Object.isExtensible()的時候
  4. preventExtensions():在使用Object.preventExtensions()的時候
  5. getOwnPropertyDescriptor():在使用Object.getOwnPropertyDescriptor()的時候
  6. defineProperty():在使用Object.defineProperty()的時候
  7. has():在使用in操做符的時候
  8. get():在獲取屬性值的是歐
  9. set():在設置屬性值的時候
  10. deleteProperty():在delete刪除屬性的時候
  11. ownKeys():在Object.getOwnPropertyNames()Object.getOwnPropertySymbols()的時候
  12. apply():在對象做爲方法調用的時候
  13. construct():在使用new操做符的時候

set舉例說明:

var obj = {}
var proxy = new Proxy(obj, {
    set (target, prop, value) {
        console.log('set value')
        target[prop] = value
    }
})
proxy.a = 1 // 'set value'
console.log(obj) // {a: 1}
複製代碼

操做proxy對象能夠修改對應對象的屬性信息,可是直接操做target對象,並不會觸發proxy對象中設置的操做:

var obj = {}
var proxy = new Proxy(obj, {
    set (target, prop, value) {
        console.log('set value')
        target[prop] = value
    }
})
obj.a = 1 // 並不會輸出任何信息
console.log(obj) // {a: 1}
複製代碼

2.2 Proxy.revocable()

建立一個能夠revocable對象,能夠在須要廢棄proxy對象的時候銷燬

var revocable = Proxy.revocable({}, {
    set (target, prop, value) {
        console.log('set value')
        target[prop] = value
    }
})
revocable.proxy.a = 1 // 'set value'
revocable.revoke() // 銷燬對象
revocable.proxy.a = 2 // Uncaught TypeError: Cannot perform 'set' on a proxy that has been revoked
複製代碼

4. 總結

4.1 相同點

從目的來看ProxydefineProperty都是爲了擴展對象的特性,若是要用來實現MVVM,兩種方案均可以完成

4.2 不一樣點:

從三個方面來講明

  1. 做用目標不一樣:defineProperty主要是用於對象定義屬性,注重的是設置對象中屬性的描述,而Proxy用於處理對象,注重的是對象的相關操做
  2. 操做目標不一樣:defineProperty的時候,是須要直接操做對象自己,來觸發相關屬性設置,而Proxy則須要操做new建立的proxy對象,對原對象操做並不會觸發相關內容
  3. 提供的觸發事件不一樣:defineProperty只提供了set,get方法能夠做爲切入口,而Proxy提供了更豐富的對象操做切入口

總的來講vue3.0使用Proxy的目的在於對對象劫持的時候,不用遍歷全部屬性,能夠直接使用對象的proxy對象,同時在對象追加屬性的增長劫持的時候,不用再手動使用$set添加劫持

固然和Proxy密切相關的Reflect,這個就留在下次再說了

6. 參考

MDN Object.defineProperty

MDN Proxy

本文存在的問題還望各位指正,謝謝

相關文章
相關標籤/搜索