0

0

0

修罗

站点介绍

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

手写vue(上) defineProperty

修罗 2020-10-27 1517 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)


最新评论

  • 1

    1

  • 1

    1

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

    1

  • 1

    1

  • 1

    1

  • 1

    1

  • 1

    1

  • @@5Qa2D

    1

  • 1

    1

  • 1

    1

日历

2025年09月

 123456
78910111213
14151617181920
21222324252627
282930    

文章目录

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