經過Object.defineProperty數據攔截對比,體驗一下proxy有多優秀

前言

vue3出來也有段時間了,無疑被說的最多應該就是vue的數據攔截用proxy重寫了(以前用的是Object.defineProperty)和 Composition API 了。本篇文,來體驗一下proxy的攔截能力到底有多優秀。javascript

Object.defineProperty數據攔截和proxy數據攔截的對比

這裏經過vue1.x,vue2.x時的數據攔截來講一下Object.defineProperty前端

提早說明,這裏的數據攔截,不包含vue依賴收集,視圖更新,否則又給可愛的掘友罵死。vue

Object.defineProperty

先來用Object.defineProperty實現一下對象的攔截。java

let data = {
  m:234,
  n:[1,34,4,5676],
  h:{
    c:34
  }
}

function observer(data){
  if(typeof data === 'object'){
    Object.keys(data).forEach(key=>{
      defineReactive(data,key,data[key])
    })
  }
}

function defineReactive(obj,key,val){
  Object.defineProperty(obj,key,{
    get(){
      console.log('get')
      return val
    },
    set(newVal){
      console.log('set')
      if(newVal !== val ) val = newVal
    }
  })
}

複製代碼

上面經過遍歷data的數據,進行了一次簡單的攔截;看似沒有問題,但若是咱們改變data.h.c是不會觸發set鉤子的,爲何?由於這裏尚未實現遞歸,因此只攔截了最表面的一層,裏面的則沒有被攔截。數組

遞歸攔截對象markdown

function defineReactive(obj,key,val){
  observer(val)
  Object.defineProperty(obj,key,{
    get(){
      console.log('get')
      return val
    },
    set(newVal){
      console.log('set')
      if(newVal !== val ) val = newVal
    }
  })
}

複製代碼

遞歸攔截,只要在defineReactive函數再調一次observer函數把要攔截的值傳給它就行。這樣,就實現了對象的多層攔截。可是呢,如今是攔截不到數組的,當咱們調用push,pop等方法它是不會觸發set鉤子的,爲何?由於Object.defineProperty壓根就不支持數組的攔截。既然它不支持,那麼咱們只能攔截它的這些('push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse')改變自身數據的方法了。app

Object.defineProperty數組的攔截

function arrayMethods(){

  const arrProto = Array.prototype

  const arrayMethods = Object.create(arrProto)

  const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']

  methods.forEach(function (method) {

      const original = arrProto[method]

      Object.defineProperty(arrayMethods, method, {

          value: function v(...args) {
              console.log('set arrayMethods')
              return original.apply(this, args)

          }

      })

  })
  return arrayMethods
}

複製代碼

以上就是對這些數組的原型方法進行了一個攔截,而後把它覆蓋要攔截的數組的原型就行,下面簡單修改一下observer函數

function observer(data){
  if(typeof data === 'object'){
    if(Array.isArray(data)){
      data.__proto__ = arrayMethods()
    }else{
      Object.keys(data).forEach(key=>{
        defineReactive(data,key,data[key])
      })
    }
  }
}

複製代碼

vue中,還會判斷該key有沒有__proto__,若是沒有就直接把這些方法放到這個key的自身上,若是有就直接覆蓋這個__proto__post

完整代碼

function arrayMethods(){

  const arrProto = Array.prototype

  const arrayMethods = Object.create(arrProto)

  const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']

  methods.forEach(function (method) {

      const original = arrProto[method]

      Object.defineProperty(arrayMethods, method, {

          value: function v(...args) {
              console.log('set arrayMethods')
              return original.apply(this, args)

          }

      })

  })
  return arrayMethods
}

function observer(data){
  if(typeof data === 'object'){
    if(Array.isArray(data)){
      data.__proto__ = arrayMethods()
    }else{
      Object.keys(data).forEach(key=>{
        defineReactive(data,key,data[key])
      })
    }
  }
}

function defineReactive(obj,key,val){
  observer(val)
  Object.defineProperty(obj,key,{
    get(){
      console.log('get')
      return val
    },
    set(newVal){
      console.log('set')
      if(newVal !== val ) val = newVal
    }
  })
}

observer(data)

複製代碼

以上,就完成了對對象和數組的攔截(說明:vue2.x中的實現會比這裏複雜,但大概思路和大概實現是這樣),看似辛苦點,換來一個完美的結果挺不錯的。但真的是你想的那樣嗎?試一下調用data.n[1] = xxx,它是不會觸發set鉤子的,這也是在proxy出現以前,無能無力的,因此在vue中提供了$set,$deleteAPI。ui

霸氣的proxy登場

這裏就不介紹proxy了,就當你對它有了解過了。直接上代碼

let data = {
  m:234,
  n:[1,34,4,5676],
  h:{
    c:34
  }
}

function defineReactive(obj){
  Object.keys(obj).forEach((key) => {
    if(typeof obj[key] === 'object'){
      obj[key] = defineReactive(obj[key])
    }
  })
  return new Proxy(obj,{
    get(target,key){
      console.log('get')
      return target[key]
    },
    set(target,key,val){
      console.log('set')
      return target[key] = val
    }
  })
}

 data = defineReactive(data)
複製代碼

就這麼一點代碼就實現了對對象和數組的攔截(說明:這裏不是vue3的實現方法,vue3怎麼實現的,我還不知道,還沒看過它的源碼,有興趣本身去看一下,而後順便告訴我一下怎麼實現的,哈哈哈)Object.defineProperty實現不了的,它能實現;Object.defineProperty實現的了,它也能實現。不管你調push,pop等方法它能攔截,你調data.n[1] = xxx也能攔截,簡直不要太爽,這個兩個版本的實現,給我我的的感受就是一個韓紅版(肉多腿短),一個迪麗熱巴版(苗條腿長),哈哈哈,本身品。

最後

這裏只是經過對對象和數組的攔截,來體驗了一下proxy的威力;proxy能作的遠遠不止這樣。

若是你想了解更多proxy的特性,和更多的用例能夠,看看以上兩篇文章(自願看哈,我不想給罵,說什麼廣告,瞎jb推薦之類的,🐶保命)。

相關文章
相關標籤/搜索