Vue3.0版本會將資料劫持的方式從Object.defineProperty
切換為Proxy
,所以找了時間重新回顧了一下屬性描述,並瞭解了以下Proxy
1. Object.defineProperty
給物件的屬性設定屬性描述,接受三個引數
/*
* obj:需要定義屬性的物件
* prop:定義描述的屬性名
* descriptor: 屬性描述
*/
Object.defineProperty(obj, prop, descriptor);
複製程式碼
1.1 descriptor
用於描述屬性的物件,可以包含以下值
1.1.1 get
屬性在獲取值的時候會呼叫該方法
var obj = {}
Object.defineProperty(obj, 'a', {
get() {
console.log('get a');
return 1;
}
})
console.log(obj.a)
// 優先輸出'get a', 之後輸出:1
複製程式碼
1.1.2 set
屬性設定值的時候會呼叫該方法
var obj = {}
Object.defineProperty(obj, 'a', {
set(value) {
console.log(`set:${value}`);
}
})
obj.a = 1
// 輸出 `set:1`
複製程式碼
1.1.3 value
用於設定物件屬性的初始值,無法同get
和set
方法同時設定
var obj = {}
Object.defineProperty(obj, 'a', {
value: 1
})
console.log(obj.a) // 1
複製程式碼
1.1.4 enumerable
設定屬性是否可以列舉,用於for in
列舉屬性時候是否可以獲取
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
設定屬性是否可以再次定義屬性描述
var obj = {}
Object.defineProperty(obj, 'a', {
value: 1,
configurable: false, // 設定false無法再次配置屬性
})
Object.defineProperty(obj, 'a', {
value: 2,
})
// 丟擲異常:Uncaught TypeError: Cannot redefine property 'a'
複製程式碼
PS:對於已有屬性可以修改value
和enumerable
和writable
的值(例如:如果obj = {a: 1}
這裡修改屬性a
的這三個值不會報錯)
1.1.6 writable
設定屬性是否可以賦值,無法同get
和set
方法同時設定
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)
可以建立物件target
的proxy
物件,操作proxy
物件的時候,根據設定的handler
,可以設定物件操作的各時期的具體操作
getPrototypeOf()
:在呼叫Object.getPrototypeOf()
的時候setPrototypeOf()
:在使用Object.setPrototypeOf()
的時候isExtensible()
:在使用Object.isExtensible()
的時候preventExtensions()
:在使用Object.preventExtensions()
的時候getOwnPropertyDescriptor()
:在使用Object.getOwnPropertyDescriptor()
的時候defineProperty()
:在使用Object.defineProperty()
的時候has()
:在使用in
操作符的時候get()
:在獲取屬性值的是歐set()
:在設定屬性值的時候deleteProperty()
:在delete
刪除屬性的時候ownKeys()
:在Object.getOwnPropertyNames()
和Object.getOwnPropertySymbols()
的時候apply()
:在物件作為方法呼叫的時候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 相同點
從目的來看Proxy
和defineProperty
都是為了擴充套件物件的特性,如果要用來實現MVVM,兩種方案都可以完成
4.2 不同點:
從三個方面來說明
- 作用目標不同:
defineProperty
主要是用於物件定義屬性,注重的是設定物件中屬性的描述,而Proxy
用於處理物件,注重的是物件的相關操作 - 操作目標不同:
defineProperty
的時候,是需要直接操作物件本身,來觸發相關屬性設定,而Proxy
則需要操作new
建立的proxy
物件,對原物件操作並不會觸發相關內容 - 提供的觸發事件不同:
defineProperty
只提供了set
,get
方法可以作為切入口,而Proxy
提供了更豐富的物件操作切入口
總的來說vue3.0使用Proxy
的目的在於對物件劫持的時候,不用遍歷所有屬性,可以直接使用物件的proxy
物件,同時在物件追加屬性的增加劫持的時候,不用再手動使用$set
新增劫持
當然和Proxy
密切相關的Reflect
,這個就留在下次再說了
6. 參考
本文存在的問題還望各位指正,謝謝