Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Object.defineProperty 与 Proxy #26

Open
Vibing opened this issue Jun 22, 2020 · 0 comments
Open

Object.defineProperty 与 Proxy #26

Vibing opened this issue Jun 22, 2020 · 0 comments

Comments

@Vibing
Copy link
Owner

Vibing commented Jun 22, 2020

Object.definedProperty

在 vue2.x 版本中使用 Object.definedProperty 来劫持数据,实现数据双向绑定。我们来实现一个简单的数据劫持:

function observer(obj) {
  if (typeof obj === 'object') {
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        listener(obj, key);
      }
    }
  }
}

function listener(obj, key) {
  let curValue = obj[key];
  observer(curValue); // 如果curValue是对象,进入递归

  Object.defineProperty(obj, key, {
    get() {
      console.log('get-->', curValue);
      return curValue;
    },
    set(newVal) {
      console.log('set-->', newVal);
      curValue = newVal;
    }
  });
}

上面写了个简单版的数据劫持,现在我们来测试一下:

const obj = {
  name: 'Jack',
  age: 20
};

observer(obj)

现在我们将 obj.name 改为 Tom:

灰常好,set 方法执行了,我们在更改数据前,劫持了数据。

我们再看看获取 name:

灰常好,get 方法也执行了,我们在获取数据前,劫持了数据。

看上去很美好,但 Object.definedProperty 并不是完美的,它在有些情况下无法劫持数据:

  • 对删除和新增的属性无法监听到
  • 数组的变化无法监听,虽然可以触发 get,但无法触发 set 的劫持
  • 如果对象过大、层级过深,那么遍历的时间会更久,引发性能问题

Proxy

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

语法

const p  = new Proxy(target, handler)
  • target: 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
  • handler: 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

还记得 Object.defineProperty 的语法吗:

Object.defineProperty(obj, key, options);

从语法上就能发现一个最大的不同点:Object.defineProperty 监听的是对象的属性,而 Proxy 监听的是整个对象

所以我们不需要遍历对象,而是直接监听对象:

const obj = {
  arr: []
}

const handler = {
  get(target, key, receiver){
    console.log('get->', target[key])
    if(typeof target[key] === 'object' && target[key] !== null) {
      return new Proxy(target[key], handler)
    }
    return target[key]
  },
  set(target, key, value){
    console.log('set->', key, value)
    return Reflect.set(target, key, value)
  }
}
const p = new Proxy(obj, handler)

上面我们监听的是一个空的对象,我们直接添加属性看看:

  p.name = 'Jack';

这里注意,我们代理的是 obj,返回的是 p,所以要对 p 进行操作:

真好,set 触发了。

我们再看看获取 name:

真好,get 也触发了。

我们再看看数组:

都可以非常好的监听到 get 、set,太强大了

关于 Proxy 的更多用法和说明,请看 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant