Proxy VS Object.defineProperty()數組
在 ES6
標準中新增的一個很是強大的功能是 Proxy
,它能夠自定義一些經常使用行爲如查找、賦值、枚舉、函數調用等。經過 Proxy
這個名稱也能夠看出來它包含了「代理」的含義,只要有「代理」的訴求均可以考慮使用 Proxy
來實現。app
PS: 相似租房找中介,中介能夠屏蔽原始信息。
let p = new Proxy(target, handler)
參數 | 含義 | 必選 |
---|---|---|
target | 用 Proxy 包裝的目標對象(能夠是任何類型的對象,包括原生數組,函數,甚至另外一個代理) | Y |
handler | 一個對象,其屬性是當執行一個操做時定義代理的行爲的函數 | Y |
第一個參數 target
就是用來代理的「對象」,被代理以後它是不能直接被訪問的,而 handler
就是實現代理的過程。dom
//o是房東的角色,被代理的對象 let o = { name: 'xiaoming', price: 190 } // d是一箇中介的角色,代理 let d = new Proxy(o, {}) console.log(d.price, d.name) // 190 'xiaoming' // 由於傳的是空對象,因此是透傳 let d = new Proxy(o, { get(target, key) { //target是指o,代理的對象,key指的是屬性 if(key === 'price') { // 若是等於價格的時候進行加20的操做 return target[key] + 20 } else { return target[key] } } }) console.log(d.price, d.name) //210 "xiaoming"
let o = { name: 'xiaoming', age: 20 } console.log(o.name) // xiaoming console.log(o.age) // 20 console.log(o.from) // undefined // 當咱們讀取from的時候,由於o裏面沒有這個屬性,因此返回undefined,若是咱們不想在調用的時候出現undefined // ES5方式處理 // console.log(o.from || '') // ES6方式處理 let o = { name: 'xiaoming', age: 20 } let handler = { get(obj, key) { return Reflect.has(obj, key) ? obj[key] : '' } } let p = new Proxy(o, handler) console.log(p.from) // ''
拿走備份,不影響原始數據函數
在ES5中的應用學習
let o = { name: 'xiaoming', price: 190 } for(let [key] of Object.entries(o)) { Object.defineProperty(o, key, { writable: false }) } console.log(o.name, o.price)// xiaoming 190 o.price = 300 console.log(o.name, o.price)// xiaoming 190
ES6this
let o = { name: 'xiaoming', price: 190 } let d = new Proxy(o, { get (target, key) { return target[key] }, set (target, key, value) { return false } }) //只讀不能寫 d.price = 300 console.log(d.price, d.name) // 190 "xiaoming"
和代理的區別,在於這個所有鎖死,可是ES6
中用戶只讀,可是代理能夠作操做spa
判斷若是價格 >300
就不讓修改,若是沒有這個屬性就返回空字符串雙向綁定
let o = { name: 'xiaoming', price: 190 } let d = new Proxy(o, { get (target, key) { return target[key] || '' }, set (target, key, value) { if (Reflect.has(target, key)) { if (key === 'price') { if(value > 300){ return false } else { target[key] = value } } else { target[key] = value } } else { return false } } }) d.price = 280 console.log(d.price, d.name)// 280 "xiaoming" d.price = 301 // 沒有生效,由於校驗沒有經過 d.name = 'xiaohong' console.log(d.price, d.name)// 280 "xiaohong" d.age = 40 // 沒有這個屬性,set時候返回,get的時候賦值爲空字符串 console.log(d.price, d.name, d.age)// 280 "xiaohong" ""
去掉耦合,將驗證函數抽離成一個驗證函數代理
let o = { name: 'xiaoming', price: 190 } let validator = (target, key, value) => { if (Reflect.has(target, key)) { if (key === 'price') { if(value > 300){ return false } else { target[key] = value } } else { target[key] = value } } else { return false } } let d = new Proxy(o, { get (target, key) { return target[key] || '' }, set: validator }) d.price = 280 console.log(d.price, d.name)// 280 "xiaoming" d.price = 301 d.name = 'xiaohong' console.log(d.price, d.name)// 280 "xiaohong" d.age = 40 console.log(d.price, d.name, d.age)// 280 "xiaohong" ""
整理成一個組件code
// Validator.js export default (obj, key, value) => { if (Reflect.has(key) && value > 20) { obj[key] = value } } import Validator from './Validator' let data = new Proxy(response.data, { set: Validator })
window.addEventListener('error', (e) => { console.log(e.message) // 上報 // report('...') }, true) //捕獲 let o = { name: 'xiaoming', price: 190 } let validator = (target, key, value) => { if (Reflect.has(target, key)) { if (key === 'price') { if(value > 300){ //不知足要觸發錯誤 throw new TypeError('price exceed 300') } else { target[key] = value } } else { target[key] = value } } else { return false } } let d = new Proxy(o, { get (target, key) { return target[key] || '' }, set: validator }) d.price = 280 console.log(d.price, d.name)// 280 "xiaoming" d.price = 301 d.name = 'xiaohong' console.log(d.price, d.name)// 280 "xiaohong" d.age = 40 console.log(d.price, d.name, d.age)// 280 "xiaohong" ""
// 探索一 class Component { constructor() { this.id = Math.random().toString(36).slice(-8) } } let com = new Component() let com2 = new Component() for (let i = 0 ; i < 10 ; i++) { console.log(com.id) } for (let i = 0 ; i < 10 ; i++) { console.log(com2.id) } com.id = 'abc' console.log(com.id,com2.id) // 這種方式能夠每次生成一個id,可是能夠修改,不符合要求 // (10) 4robfncs // (13) 93ukz26i // 能夠修改 // abc 93ukz26i
// 探索二 class Component { get id () { return Math.random().toString(36).slice(-8) } } let com = new Component() let com2 = new Component() for (let i = 0 ; i < 10 ; i++) { console.log(com.id) } for (let i = 0 ; i < 10 ; i++) { console.log(com2.id) } com.id = 'abc' console.log(com.id,com2.id) // 這種方式不能夠修改,可是每此都生成了一個新的,不符合要求 // nqwlamib // l9ojsjiq // gad3vm2a // i1jew3bd // owquntob // rcpce268 // va6mry5v // lvqxv0m4 // a900358x // jahi7079 // vukusf5k // rg8hyzf3 // 50vxv0hk // tjeyes1v // 4g8zwsxz // 5r1cbx1k // v9k2v7hd // 0mgn3heb // n0zc9v66 // rdjevl2i // 9rjmwrd9 kxdxtywe
// 探索三 class Component { constructor() { this.proxy = new Proxy({ id: Math.random().toString(36).slice(-8) }, {}) } get id () { return this.proxy.id } } let com = new Component() let com2 = new Component() for (let i = 0 ; i < 10 ; i++) { console.log(com.id) } for (let i = 0 ; i < 10 ; i++) { console.log(com2.id) } com.id = 'abc' console.log(com.id,com2.id) // 知足要求 // (10)e9e8jsks // (10)tfs2rrvg // e9e8jsks tfs2rrvg
除了常規代理,還能夠建立臨時代理,臨時代理能夠撤銷。
一旦revoke
被調用,proxy
就失效了,就起到了臨時代理的做用。
let o = { name: 'xiaoming', price: 190 } // 這裏不能使用new,只能使用Proxy.revocable去聲明代理 let d = Proxy.revocable(o, { get(target, key) { if(key === 'price') { return target[key] + 20 } else { return target[key] } } }) // d裏面包含了代理數據和撤銷操做 console.log(d.proxy.price) // 210 console.log(d) // {proxy: Proxy, revoke: ƒ} setTimeout(function(){ // 對代理進行撤銷操做 d.revoke() setTimeout(function(){ console.log(d.proxy.price) // Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked },100) },1000)
若是想要監聽某個對象屬性的改變,可使用Object.defineProperty
這個方法去添加屬性,那麼就能夠捕捉到對象中屬性的讀寫過程, VUE3.0
以前的版本就是經過這個實現的數據雙向綁定。從VUE3.0
開始就使用proxy
來實現內部響應了。
proxy
是專門爲對象設置代理器的,那麼proxy
就能夠輕鬆監視到對象的讀寫過程。相比較defineProperty
,proxy
的功能更增強大,使用起來也更爲方便。
defineProperty
只能監視屬性的讀寫,proxy
可以監視到更多對象的操做,例如刪除屬性操做
const person = { name: 'xm', age: 20 } const personProxy = new Proxy(person, { deleteProperty (target, property) { console.log('delete ' + property) // delete age delete target[property] } }) delete personProxy.age console.log(person) // { name: 'xm' }
handler ⽅法 | 觸發⽅式 |
---|---|
get | 讀取某個屬性 |
set | 寫⼊某個屬性 |
has | in 操做符 |
deleteProperty | delete 操做符 |
getProperty | Object.getPropertypeOf() |
setProperty | Object.setPrototypeOf() |
isExtensible | Object.isExtensible() |
preventExtensions | Object.preventExtensions() |
getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor() |
defineProperty | Object.defineProperty() |
ownKeys | Object.keys() 、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols() |
apply | 調⽤⼀個函數 |
construct | ⽤ new 調⽤⼀個函數 |
以往Object.defineProperty()
使用的是重寫數組的操做方法
const list = [] const listProxy = new Proxy(list, { set(target, property, value) { console.log('set', property, value) target[property] = value return true // 表示設置成功 } }) listProxy.push(100) // set 0 100 // set length 1 listProxy.push(200) // set 1 200 // set length 2