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

ES6 系列之 defineProperty 与 proxy #80

Open
yangtao2o opened this issue Apr 6, 2020 · 0 comments
Open

ES6 系列之 defineProperty 与 proxy #80

yangtao2o opened this issue Apr 6, 2020 · 0 comments

Comments

@yangtao2o
Copy link
Owner

yangtao2o commented Apr 6, 2020

ES6 系列之 defineProperty 与 proxy

definePropety

ES5 提供了 Object.defineProperty 方法,该方法可以在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。

语法

Object.defineProperty(obj, prop, descriptor);

参数

  • obj: 要在其上定义属性的对象。
  • prop: 要定义或修改的属性的名称。
  • descriptor: 将被定义或修改的属性的描述符。
var obj = {};
Object.defineProperty(obj, "num", {
  value: 1,
  writable: true,
  enumerable: true,
  configurable: true
});
//  对象 obj 拥有属性 num,值为 1

函数的第三个参数 descriptor 所表示的属性描述符有两种形式:数据描述符存取描述符

两者均具有以下两种键值

  • configurable

当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,也能够被删除。默认为 false。

  • enumerable

当且仅当该属性的 enumerable 为 true 时,该属性才能够出现在对象的枚举属性中。默认为 false。

数据描述符同时具有以下可选键值

  • value

该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。

  • writable

当且仅当该属性的 writable 为 true 时,该属性才能被赋值运算符改变。默认为 false。

存取描述符同时具有以下可选键值

  • get

一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。该方法返回值被用作属性值。默认为 undefined。

  • set

一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为 undefined。

注意:属性描述符必须是数据描述符或者存取描述符两种形式之一,不能同时是两者 。

此外,所有的属性描述符都是非必须的,但是 descriptor 这个字段是必须的,如果不进行任何配置,你可以这样:

var obj = Object.defineProperty({}, "num", {});
console.log(obj.num); // undefined

Setters 和 Getters

我们要使用存取描述符中的 get 和 set,这两个方法又被称为 getter 和 setter。由 getter 和 setter 定义的属性称做”存取器属性“。

例子:watch 可以监控对象属性值的改变,并且可以根据属性值的改变,添加回调函数

(function() {
  var root = this;
  function watch(obj, name, fn) {
    var value = obj[name];
    Object.defineProperty(obj, name, {
      get: function() {
        return value;
      },
      set: function(newValue) {
        value = newValue;
        fn(value);
      }
    });
    if (value) obj[name] = value;
  }
  this.watch = watch;
})();

使用:

var obj = {
  value: 1
};

watch(obj, "value", function(newvalue) {
  document.getElementById("container").innerHTML = newvalue;
});

document.getElementById("button").addEventListener("click", function() {
  obj.value += 1;
});

proxy

使用 defineProperty 只能重定义属性的读取(get)和设置(set)行为,到了 ES6,提供了 Proxy,可以重定义更多的行为,比如 in、delete、函数调用等更多行为。

Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。

var proxy = new Proxy(target, handler);

new Proxy()表示生成一个 Proxy 实例,target 参数表示所要拦截的目标对象,handler 参数也是一个对象,用来定制拦截行为。

例子:

(function() {
  var root = this;
  function watch(target, fn) {
    return new Proxy(target, {
      get(target, prop) {
        return target[prop];
      },
      set(target, prop, value) {
        target[prop] = value;
        fn(prop, value);
      }
    });
  }
  this.watchProxy = watch;
})();

var obj = {
  value: 1
};

var newObj = watchProxy(obj, function(key, newvalue) {
  if (key === "value") {
    document.getElementById("container").innerHTML = newvalue;
  }
});

document.getElementById("button").addEventListener("click", function() {
  newObj.value += 1;
});

可以发现,使用 defineProperty 和 proxy 的区别,当使用 defineProperty,我们修改原来的 obj 对象就可以触发拦截,而使用 proxy,就必须修改代理对象,即 Proxy 的实例才可以触发拦截。

Proxy

  • 代理的是 对象
  • 可以拦截到数组的变化
  • 拦截的方法多达 13 种
  • 返回一个拦截后的数据

Object.defineProperty

  • 代理的是属性
  • 对数组数据的变化无能为力
  • 直接修改原始数据

原文链接:ES6 系列之 defineProperty 与 proxy

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

No branches or pull requests

1 participant