Vue3.0的雙向綁定將使用Proxy代替Object.defineProperty,據尤大說,速度提升了1倍。
本文我們來探討一下Proxy對比Object.defineProperty究竟有哪些優劣呢?
首先介紹一下什么是Proxy?
Proxy在ES6規范中被正式發布,Proxy可以理解成在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。
Proxy語法:
ES6原生提供Proxy構造函數,用來生成Proxy實例
var proxy = new Proxy(target,handler);
Proxy接受兩個參數:
target:要代理目標對象
handler: 處理函數,該函數將攔截對應的操作
下面是 Proxy 支持的攔截操作一覽,一共 13 種。
- get(target, propKey, receiver):攔截對象屬性的讀取,比如
proxy.foo
和proxy['foo']
。 - set(target, propKey, value, receiver):攔截對象屬性的設置,比如
proxy.foo = v
或proxy['foo'] = v
,返回一個布爾值。 - has(target, propKey):攔截
propKey in proxy
的操作,返回一個布爾值。 - deleteProperty(target, propKey):攔截
delete proxy[propKey]
的操作,返回一個布爾值。 - ownKeys(target):攔截
Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
、for...in
循環,返回一個數組。該方法返回目標對象所有自身的屬性的屬性名,而Object.keys()
的返回結果僅包括目標對象自身的可遍歷屬性。 - getOwnPropertyDescriptor(target, propKey):攔截
Object.getOwnPropertyDescriptor(proxy, propKey)
,返回屬性的描述對象。 - defineProperty(target, propKey, propDesc):攔截
Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一個布爾值。 - preventExtensions(target):攔截
Object.preventExtensions(proxy)
,返回一個布爾值。 - getPrototypeOf(target):攔截
Object.getPrototypeOf(proxy)
,返回一個對象。 - isExtensible(target):攔截
Object.isExtensible(proxy)
,返回一個布爾值。 - setPrototypeOf(target, proto):攔截
Object.setPrototypeOf(proxy, proto)
,返回一個布爾值。如果目標對象是函數,那么還有兩種額外操作可以攔截。 - apply(target, object, args):攔截 Proxy 實例作為函數調用的操作,比如
proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。 - construct(target, args):攔截 Proxy 實例作為構造函數調用的操作,比如
new proxy(...args)
。
下面介紹兩個常用的攔截方法:get方法和set方法。
//get方法用於攔截某個屬性的讀取操作 //接受三個參數,依次為目標對象、屬性名和Proxy實例本身,最后一個參數可選 //set方法用於攔截某個屬性的賦值操作 //接受四個參數,一次為目標對象、屬性名、屬性值和Proxy實例本身,最后一個參數可選 var person = { name:'Jack', age:20 }; var handler = { get(target,key){ if(key in target){ console.log(`${key}被讀取`); return target[key] }else{ throw new ReferenceError(`Property ${key} does not exist`) } }, set(target,key,value){ console.log(`${key}被設置為${value}`) target[key] = value } }; let instance = new Proxy(person,handler); instance.name //name被讀取 instance.age = 25 //age被設置為25
Object.defineProperty缺點:
1. 對數組的支持不好,無法監聽到數組的變化,在Vue官方文檔說明了可以監聽到數組的變動,但只限於push、pop、shift、unshift、splice、sort、reverse方法。實際上是他們對這幾種方法進行了重寫。
var arrayProto = Array.prototype; var arrayMethods = Object.create(arrayProto); [ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ].forEach(function(item){ Object.defineProperty(arrayMethods,item,{ value:function mutator(){ //緩存原生方法,之后調用 console.log('array被訪問'); var original = arrayProto[item] var args = Array.from(arguments) original.apply(this,args) // console.log(this); }, }) })
2.Object.defineProperty監聽的是對象的屬性,當一個對象為深層嵌套的時候,必須進行遞歸遍歷,比較麻煩。
Proxy對比Object.defineProperty:
優點:
1. Proxy可以劫持整個對象,這樣以來操作便利程度遠遠優於Object.defineProperty。
2. Proxy可以直接監聽數組的變化,無需進行數組方法重寫。
var arr = [1,2,3,4]; var instance = new Proxy(arr,{ get(target,key){ console.log('數組被讀取') return Reflect.get(target,key) }, set(target,key,val){ console.log('監聽到數組更新') return Reflect.set(target,key,val) } }) instance[0] = 5 //監聽到數組更新 instance.push(6) //數組被讀取 監聽到數組更新
3. Proxy支持13種攔截操作,是Object.defineProperty不具備的。
缺點:Proxy的兼容性不是太好,不兼容IE,且無法通過polyfill提供兼容。