0

0

0

修罗

站点介绍

只有了解事实才能获得真正的自由

手写vue(上) defineProperty

修罗 2020-10-27 1748 0条评论 vue

首页 / 正文

手写vue(上) defineProperty

通过Object.defineProperty函数可以对对象属性的设置和获取进行拦截

定义函数defineReactive

该函数传入对象,属性,值;对传入的对象的属性操作进行拦截。

每次获取此属性前打印get xxx,每次对此属性设置值前打印set xxx 新值

function defineReactive(obj, key, val){
  // 对传入obj进行访问拦截,注意:对数组无效
  Object.defineProperty(obj, key, {
    // val被引用,形成闭包不被释放,保存当前状态
    // 暴露在函数作用域之外,下面用obj.xxx通过get方法在全局作用域能访问到
    get(){
      console.log('get '+key);
      return val
    },
    set(newVal){
      if(newVal !== val){
        console.log('set ' + key + ': ' + newVal);
        val = newVal
      }
    }
  })
}

调用

const obj = {}
defineReactive(obj, 'foo', 'bar')

obj.foo  // get foo 
obj.foo = 'foobar' // set foo: foobar

在对属性set时进行一些界面更新

set(newVal){
    if(newVal !== val){
        console.log('set ' + key + ': ' + newVal);
        val = newVal
        // 更新函数
        update()
    }
}

function update(){
    document.body.innerText = obj.foo
}

使用

defineReactive(obj, 'foo', 'bar')

// 页面加载显示时间
obj.foo = new Date().toLocaleTimeString()

// 每隔一秒后时间更新
setInterval(() => {
    obj.foo = new Date().toLocaleTimeString()
},1000)

1603802113622.png

递归拦截,set拦截

假如对象多层嵌套,上面无法拦截

const obj = { foo: 'bar', baz: 'bar', foobar: { a: 1 } }

定义observe函数,对循环循环拦截

function observe(obj){
  if(typeof obj !== 'object' || obj == null) return

    Object.keys(obj).forEach(key => {
      defineReactive(obj, key, obj[key])
    })
}

对象嵌套的情况要递归拦截,如果传入的newVal依然是obj,要对其做响应式处理

function defineReactive(obj, key, val){
  // 递归
  observe(val)

  // 对传入obj进行访问拦截
  Object.defineProperty(obj, key, {
    // val被引用,形成闭包不被释放,保存当前状态
    get(){
      console.log('get '+key);
      return val
    },
    set(newVal){
      if(newVal !== val){
        console.log('set ' + key + ': ' + newVal);
        // 如果传入的newVal依然是obj,要对其做响应式处理
        observe(newVal)
        val = newVal
      }
    }
  })
}

当添加新属性,直接赋值无法拦截,需要经过defineProperty再拦截

function set(obj, key, val){
  defineReactive(obj, key, val)
}

使用

const obj = { foo: 'bar', baz: 'bar', foobar: { a: 1 } }

// 遍历响应化处理
observe(obj)
obj.foo // get foo
obj.foo = 'foobar' // set foo: foobar
obj.baz // get baz
obj.baz = 'foobarbaz' // set baz: foobarbaz
obj.foobar.a = 10 // 先get foobar 后set a: 10
// 添加新属性
// obj.dong = 'dong'
set(obj, 'dong', 'dong')
obj.dong // get dong

Object.defineProperty没法拦截数组

替换数组原型中7个方法

push、pop、unshift、shift、reverse、splice、sort

// 数组响应式
// 1.替换数组原型中7个方法
const orginalProto = Array.prototype
// 备份一份,修改备份
const arrayProto = Object.create(orginalProto);

['push', 'pop', 'shift', 'unshift'].forEach(method => {
  arrayProto[method] = function(){
    // 原始操作
    orginalProto[method].apply(this, arguments)
    // 覆盖操作,通知更新
    console.log('数组执行 '+ method +' 操作');
  }
});

function defineReactive(obj, key, val){
  // 递归
  observe(val)

  // 对传入obj进行访问拦截
  Object.defineProperty(obj, key, {
    // val被引用,形成闭包不被释放,保存当前状态
    get(){
      console.log('get '+key);
      return val
    },
    set(newVal){
      if(newVal !== val){
        console.log('set ' + key + ': ' + newVal);
        // 如果传入的newVal依然是obj,要对其做响应式处理
        observe(newVal)
        val = newVal
      }
    }
  })
}

function observe(obj){
  if(typeof obj !== 'object' || obj == null) return

  // 判断传入obj类型
  if(Array.isArray(obj)){
    // 覆盖原型,替换7个变更操作
    obj.__proto__ = arrayProto
    // 对数组内部元素执行响应化
    const keys = Object.keys(obj)
    for(let i = 0; i< obj.length; i++){
      observe(obj[i])
    }
  }else{
    Object.keys(obj).forEach(key => {
      defineReactive(obj, key, obj[key])
    })
  }

}

function set(obj, key, val){
  defineReactive(obj, key, val)
}

// 响应式
const obj = { foo: 'bar', baz: 'bar', foobar: { a: 1 }, arr: [] }

// 遍历响应化处理
observe(obj)
obj.foo
obj.foo = 'foobar'
obj.baz
obj.baz = 'foobarbaz'
obj.foobar.a = 10
// 添加新属性
// obj.dong = 'dong'
set(obj, 'dong', 'dong')
obj.dong
obj.arr.push(1)

评论(0)


最新评论

  • https://www.studyglobus.com/author/carroll34a5552/

    Ценники на вывоз мусора сейчас космос, любые лайфхаки по экономии в тему.

  • Richardgrold

    1xbet onlayn kazino platforması ilə vaxtınızı daha səmərəli və maraqlı keçirin. Saytın canlı kazino bölməsi istifadəçilərə fərqli bir dünya təklif edir. Burada canlı dilerli kazino oyunlarının hər bir növünü sınaqdan keçirə bilərsiniz. Krupyelərin peşəkarlığı oyunun dinamikasını və marağını artırır. Müntəzəm olaraq yenilənən oyun siyahısı cansıxıcılığa yol vermir. Hesabınızı artırmaq üçün onlarla fərqli və etibarlı üsul mövcuddur. Qazanılan pullar cəmi bir neçə dəqiqə ərzində şəxsi hesabınıza köçürülür. Aktiv fəaliyyətinizə görə fərdi bonuslar və hədiyyələr qazana bilərsiniz. Dəstək xidməti həm çat, həm də e-poçt vasitəsilə kömək göstərir. 1xbet ailəsinə qoşulun və böyük uduşlar üçün ilk addımı atın. onlayn kazino

  • 砂岩白丘

    我一直梦想, 看到你们相册那样的地方。真棒。

  • 城市地標

    我喜欢, 这里分享真实经验。你的项目 就是 属于这里的。请继续。

  • 宗教地標

    我热爱这样的想法, 去那么多国家。真的很鼓舞。

  • 觀景高地

    能感受到热爱。继续保持 感受。

  • 最佳季節

    我关注这样的资源, 写得很实在。你的网站 就是 属于这里的。加油。

  • 1

    1

  • 1

    1

  • -1' OR 2+158-158-1=0+0+0+1 or 'TKCTZnRa'='

    1

日历

2026年07月

   1234
567891011
12131415161718
19202122232425
262728293031 

文章目录

推荐关键字: Linux webpack js 算法 MongoDB laravel JAVA jquery javase redis