ES6 Proxy和Reflect

目录

Proxy

概述

基本用法

Proxy 实例方法

1.get(target, propKey, receiver)

2、set(target, propKey, value, receiver)

3、apply(target, ctx, args) 

4、has(target, propKey) 

5、construct(target, args, newTarget) 

6、deleteProperty(target, propKey)

7、defineProperty(target, propKey, descriptor)

8、getOwnPropertyDescriptor(target, propKey)

9、getPrototypeOf(target)

10、 isExtensible(target)

11、 ownKeys(target)

12、preventExtensions(target) 

13、 setPrototypeOf(target, proto)

Proxy.revocable() 

Reflect

概述

静态方法

1、Reflect.get(target, name, receiver)

2、Reflect.set(target, name, value, receiver)

3、Reflect.has(obj, name)

4、Reflect.deleteProperty(obj, name)

5、Reflect.construct(target, args)

6、 Reflect.getPrototypeOf(obj)

7、Reflect.setPrototypeOf(obj, newProto)

8、Reflect.apply(func, thisArg, args)

9、Reflect.defineProperty(target, propertyKey, attributes)

10、Reflect.getOwnPropertyDescriptor(target, propKey)

11、Reflect.isExtensible(target)

12、Reflect.preventExtensions(target)

13、Reflect.ownKeys(target)

实例:使用Proxy实现观察者模式


Proxy

概述

Proxy是 ES6 为了操作对象引入的 API ,Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。

proxy这个词的意思是"代理",在这里表示由它来"代理"一些操作,可以译为"代理器"。

基本用法

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

let target = { name: "Tom", age: 2 };
let handler = {
  get: (target, key) => {
    console.log("getting %s!", key);
    return target[key];
  },
  set: (target, key, value) => {
    console.log("setting %s 的值为 %s!", key, value);
    target[key] = value;
  },
};
let proxy = new Proxy(target, handler);
proxy.name; // getting name!  实际执行 handler.get 方法
proxy.name = "Jerry"; // setting name 的值为 Jerry!  实际执行 handler.set 方法

请看上面代码,target参数就是要拦截的目标对象,handler参数就是拦截对应的操作。

handler可以设置为空对象,相当于没有设置任何拦截,等同于直接访问原对象。

let proxy = new Proxy({ name: "Tom", age: 2 }, {});
console.log(proxy.name); // Tom

Proxy 实例方法

1.get(target, propKey, receiver)

用于拦截目标的读取操作,接受三个参数,target表示目标对象、propKey表示属性名、receiver表示proxy实例本身。第三个参数非必须。

let proxy = new Proxy(
  { name: "Tom", age: 2 },
  {
    get: (target, propKey) => {
      // 拦截操作
      console.log("正在读取 %s 属性", propKey);
      return target[propKey];
    },
  }
);
proxy.name; // 正在读取 name 属性

// get方法可继承
let obj = Object.create(proxy);
obj.name; // 正在读取 name 属性

2、set(target, propKey, value, receiver)

用于拦截 target 对象上的 propKey 的赋值操作。接受四个参数,target表示目标对象、propKey表示属性名、value表示属性值、receiver表示proxy实例本身。第四个参数非必须。

let handler = {
  set: (target, propKey, value) => {
    if (propKey === "age" && !Number.isInteger(value)) {
      throw new TypeError("The age is not a integet");
    }
    if (propKey === "age" && value > 20) {
      throw new RangeError("The age seems invalid");
    }
    target[propKey] = value;
    return true;
  },
};
let proxy = new Proxy({ name: "Tom", age: 2 }, handler);
proxy.age = 4;
console.log(proxy.age); // 4
proxy.age = 21; // RangeError: The age seems invalid
proxy.age = "4岁"; // TypeError: The age is not a integet

第四个参数 receiver 表示原始操作行为所在对象,一般是 Proxy 实例本身。

贴一个第四个参数的例子。

let handler = {
  set: (target, propKey, value, receiver) => {
    target[propKey] = receiver;
    return true;
  },
};
let proxy = new Proxy({}, handler);
proxy.name = "Tom";
console.log(proxy.name === proxy); // true

 如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用。

let target = {};
Object.defineProperty(target, "age", {
  value: 4,
  writable: false, // 属性是否可以被修改
});
let handler = {
  set: (target, propKey, value, receiver) => {
    target[propKey] = receiver;
  },
};
let proxy = new Proxy(target, handler);
console.log(proxy.age); // 4
proxy.age = 5;
console.log(proxy.age); // 4

3、apply(target, ctx, args) 

用于拦截函数的调用、call 和 apply 操作。接受三个参数,target 表示目标对象,ctx 表示目标对象上下文,args 表示目标对象的参数数组。

每当执行proxy函数(直接调用或callapply调用),就会被apply方法拦截。

let target = (x, y) => x + y;
let handler = {
  apply(target, ctx, args) {
    return Reflect.apply(...arguments) * 2;
  },
};
let proxy = new Proxy(target, handler);
console.log(proxy(1, 2)); // 6
console.log(proxy.call(null, 3, 4)); // 14
console.log(proxy.apply(null, [5, 6])); // 22

4、has(target, propKey) 

用来拦截hasProperty方法,即判断对象是否具有某个属性时,会被这个方法方法拦截。典型的操作就是in运算符。接受两个属性,分别是目标对象和要查询的属性名。has方法不判断是自身属性还是继承属性。注意:此方法对 for...in 不生效。

let handler = {
  has(target, propKey) {
    if (propKey === "age" && target[propKey] < 0.3) {
      console.log(`${target.name}是幼猫, 还不可以吃零食`);
      return false;
    }
    console.log("handler has");
    return propKey in target;
  },
};
let proxy = new Proxy({ name: "Tom" }, handler);
console.log("name" in proxy); // handler has  true
let Tom = { name: "Tom", age: 2 };
let yuanDan = { name: "yuanDanDan", age: 0.2 };
let cat1 = new Proxy(Tom, handler);
let cat2 = new Proxy(yuanDan, handler);
console.log("age" in cat1); // handler has  true
console.log("age" in cat2); // yuanDanDan是幼猫, 还不可以吃零食  false
// for...in 不拦截
for (let k in cat2) {
  console.log(cat2[k]); // yuanDanDan  0.2  
}

5、construct(target, args, newTarget) 

用于拦截 new 命令,接受三个参数,分别是目标对象、构造函数参数数组,new命令作用的构造函数。construct返回的必须是对象,否则会报错。因为construct拦截的是构造函数,他的目标对象必须是函数,否则也会报错。construct方法中this指向hanldler,不是实例对象。

let handler = {
  construct(target, args, newTarget) {
    console.log(this === handler);
    return Reflect.construct(target, args, newTarget);
  },
};
class Cat {
  constructor(name) {
    this.name = name;
  }
}
let proxy = new Proxy(Cat, handler);
let cat = new proxy("Tom");
console.log(cat); // true  Cat {name: 'Tom'}
console.log(cat.name); // Tom

6、deleteProperty(target, propKey)

用于拦截delete操作,如果返回false或者抛出错误,则propKey无法被delete命令删除。

let handler = {
  deleteProperty(target, propKey) {
    if (propKey[0] === "_") {
      throw new Error(
        `Invalid attempt to delete private "${propKey}" property`
      );
    }
    delete target[propKey];
    return true;
  },
};
let proxy = new Proxy({ name: "Tom", _age: 2 }, handler);
delete proxy.name;
console.log(proxy.name); // undefined
delete proxy._age; // Error: Invalid attempt to delete private "_age" property

7、defineProperty(target, propKey, descriptor)

用于拦截Object.defineProperty方法的操作。

let handler = {
  defineProperty(target, propKey, descriptor) {
    return false;
  },
};
let proxy = new Proxy({}, handler);
proxy.name = "Tom";
console.log(proxy.name); // undefined

8、getOwnPropertyDescriptor(target, propKey)

用于拦截Object.getOwnPropertyDescriptor方法,返回一个属性描述对象或者undefined。

let handler = {
  getOwnPropertyDescriptor(target, key) {
    let descriptor = Object.getOwnPropertyDescriptor(target, key);
    console.log(descriptor);
    return descriptor;
  },
};
let proxy = new Proxy({ name: "Tom" }, handler);
Object.getOwnPropertyDescriptor(proxy, "name"); // {value: 'Tom', writable: true, enumerable: true, configurable: true}

9、getPrototypeOf(target)

主要用于拦截获取对象原型的操作。 具体以下几种:

  • Object.prototype.__proto__
  • Object.prototype.isPrototypeOf()
  • Object.getPrototypeOf()
  • Reflect.getPrototypeOf()
  • instanceof
let target = {}, handler = {
  getPrototypeOf(target) {
    return target;
  }
}
let proxy = new Proxy(target, handler)
console.log(Object.getPrototypeOf(proxy) === target); // true

 注意:getPrototypeOf 方法的返回值必须是对象或者 null,否则就会报错。如果目标对象不可扩展(non-extensible),getPrototypeOf 方法必须返回目标对象的原型对象。

10、 isExtensible(target)

用于拦截Object.isExtensible操作。

注意:它的返回值必须与目标对象的isExtensible属性保持一致,否则会抛出错误。

let proxy = new Proxy(
  {},
  {
    isExtensible(target) {
      console.log("handler isExtensible");
      return true;
    },
  }
);
Object.isExtensible(proxy); // handler isExtensible
let proxy1 = new Proxy(
  {},
  {
    isExtensible(target) {
      return false;
    },
  }
);
Object.isExtensible(proxy1); //TypeError: 'isExtensible' on proxy: trap result does not reflect extensibility of proxy target (which is 'true')

11、 ownKeys(target)

用来拦截对象自身属性的读取操作。具体拦截以下几种:

  • Object.keys()
  • Object.getOwnPropertySymbols()
  • Object.getOwnPropertyNames()
  • for...in
// 拦截第一个字符为下划线的属性名
let target = { name: "Tom", _age: 2, _sex: "小公猫" };
let handler = {
  ownKeys(target) {
    return Reflect.ownKeys(target).filter((el) => el[0] !== "_");
  },
};
let proxy = new Proxy(target, handler);
console.log(Object.keys(proxy)); // ['name']

使用Object.keys()方法时,有三类属性回呗ownKeys()方法自动过滤,不会返回。

  • 目标对象不存在的属性
  • 属性名为Symbol值
  • 不可枚举属性
let target = { name: "Tom", [Symbol.for("friend")]: "Jerry" };
let handler = {
  ownKeys(target) {
    return ["name", Symbol.for("friend"), "sex", "age"];
  },
};
Object.defineProperty(target, "age", {
  enumerable: false,
  value: 2,
});
let proxy = new Proxy(target, handler);
console.log(Object.keys(proxy)); // ["name"]
// 打印出来只有一个name属性,其他不存在的属性和Sybmol和不可枚举属性都被自动过滤掉

方法返回的数组成员,只能是字符串或 Symbol 值。如果有其他类型的值,或者返回的根本不是数组,就会报错。

let proxy = new Proxy(
  {},
  {
    ownKeys(target) {
      return [null];
    },
  }
);
Object.keys(proxy); // TypeError: null is not a valid property name

 如果目标对象自身包含不可配置的属性,则该属性必须被ownKeys()方法返回,否则报错。

let target = { name: "Tom" };
Object.defineProperty(target, "sex", {
  configurable: false,
  value: "男",
});
let proxy = new Proxy(target, {
  ownKeys(target) {
    return ["name"];
  },
});
Object.keys(proxy); // TypeError: 'ownKeys' on proxy: trap result did not include 'sex'

如果对象是不可扩展的 (not-extensible),此时ownKeys()方法返回的数组必须包含目标对象的所有属性,且不能包含其他多余的属性,否则就会报错。

let target = { name: "Tom", age: 2, sex: "男" };
let handler = {
  ownKeys(target) {
    return ["name", "age", "sex", "friend"];
  },
};
Object.preventExtensions(target);
let proxy = new Proxy(target, handler);
// 包含多余属性 friend 所以报错
Object.keys(proxy); //  TypeError: 'ownKeys' on proxy: trap returned extra keys but proxy target is non-extensible

12、preventExtensions(target) 

用于拦截 Object.preventExtensions 操作。返回一个 Boolean 值,否则会自动转为 Boolean 值。

let proxy = new Proxy(
  {},
  {
    preventExtensions(target) {
      return true;
    },
  }
);
Object.preventExtensions(proxy); // TypeError: 'preventExtensions' on proxy: trap returned truish but the proxy target is extensible

注意:只有目标对象不可扩展时,才可以返回 true,否则就会报错。所以,需要在 preventExtensions() 方法中把目标对象改成不可扩展才可以返回 true。

let proxy = new Proxy(
  {},
  {
    preventExtensions(target) {
      console.log("handler preventExtensions");
      Reflect.preventExtensions(target);
      return true;
    },
  }
);
console.log(Object.preventExtensions(proxy)); // handler preventExtensions  Proxy {}

13、 setPrototypeOf(target, proto)

用于拦截 Object.setPrototypeOf() 方法。返回一个 Boolean 值,否则会自动转为 Boolean 值。如果目标对象不可扩展(not-extensible),setPrototypeOf() 方法不得改变目标对象的原型。

let proto = {};
let proxy = new Proxy(function () {}, {
  setPrototypeOf(target, proto) {
    // 只要修改target的原型对象,就抛出错误。
    throw new Error("禁止更改原型 ");
  },
});
Object.setPrototypeOf(proxy, proto); // Error: 禁止更改原型 

Proxy.revocable() 

Proxy.revocable()方法返回一个对象,该对象的proxy属性是Proxy实例,revoke属性是一个函数,可以取消Proxy实例。

let target = { name: "Tom" },
  handler = {};
let { proxy, revoke } = Proxy.revocable(target, handler);
console.log(proxy.name); // Tom
revoke();
console.log(proxy.name); // TypeError: Cannot perform 'get' on a proxy that has been revoked

Reflect

概述

Reflect对象与Proxy对象一样,也是ES6为了操作对象提供的API。

ES6 中将 Object 的一些明显属于语言内部的方法移植到了 Reflect 对象上(当前某些方法会同时存在于 Object 和 Reflect 对象上),未来的新方法会只部署在 Reflect 对象上。

Reflect 对象对某些方法的返回结果进行了修改,使其更合理。

Reflect 对象使用函数的方式实现了 Object 的命令式操作。

静态方法

Reflect对象一共有13个静态方法。

  • Reflect.apply(target, thisArg, args)
  • Reflect.construct(target, args)
  • Reflect.get(target, name, receiver)
  • Reflect.set(target, name, value, receiver)
  • Reflect.defineProperty(target, name, desc)
  • Reflect.deleteProperty(target, name)
  • Reflect.has(target, name)
  • Reflect.ownKeys(target)
  • Reflect.isExtensible(target)
  • Reflect.preventExtensions(target)
  • Reflect.getOwnPropertyDescriptor(target, name)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target, prototype)

这些方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。

1、Reflect.get(target, name, receiver)

Refect.get 方法查找并返回 target 对象的 name 属性,如果没有,则返回undefined。

当 name 属性部署了 getter 方法,则 getter 方法的 this 会绑定 receiver。

如果第一个参数不是对象,Refect.get 方法会报错。

let target = {
  name: "Tom",
  age: 2,
  get info() {
    return `name: ${this.name}, 年龄: ${this.age}`;
  },
};
let receiver = {
  name: "Jerry",
  age: 2,
};
console.log(Reflect.get(target, "name")); // Tom
console.log(Reflect.get(target, "age")); // 2
console.log(Reflect.get(target, "info")); // name: Tom, 年龄: 2
console.log(Reflect.get(target, "info", receiver)); // name: Jerry, 猫龄: 2
console.log(Reflect.get(null, "name")); // TypeError: Reflect.get called on non-object

2、Reflect.set(target, name, value, receiver)

将 target 的 name 属性设置为 value。返回一个 Boolean 值,true 表示成功,false 表示失败。

当 name 属性设置了 setter 方法,则 setter 方法的 this 会绑定 receiver。

value 为空时会将 name 属性清除。

如果第一个参数不是对象,Refect.set 方法会报错。

let target = {
  name: "Jerry",
  age: 2,
  set info(value) {
    return (this.age = value);
  },
};
let receiver = {
  name: "Tom",
  age: 2,
};
Reflect.set(target, "age", 3);
console.log(target.age); // 3
console.log(receiver.age); // 2
Reflect.set(target, "age", 4, receiver);
console.log(target.age); // 3
console.log(receiver.age); // 4
Reflect.set(target, "age");
console.log(target.age); // undefined
Reflect.set(null, "age"); // TypeError: Reflect.set called on non-object

3、Reflect.has(obj, name)

是name in obj 指令的函数化,用于查找 name 属性在 obj 对象中是否存在。返回值为 boolean 值。如果第一个参数不是对象,Reflect.has 方法会报错。

let target = {
  name: "Jerry",
};
console.log("name" in target); // true
console.log(Reflect.has(target, "name")); // true
console.log("age" in target); // false
console.log(Reflect.has(target, "age")); // false
Reflect.has(null, "name"); // TypeError: Reflect.has called on non-object

4、Reflect.deleteProperty(obj, name)

是delete obj[name] 的函数化,用于删除对象属性,返回一个 Boolean 值。如果第一个参数不是对象,Reflect.deleteProperty 方法会报错。

let obj = { name: "Tom", age: 2 };
console.log(obj); // {name: 'Tom', age: 2}
Reflect.deleteProperty(obj, "name");
console.log(obj); // {age: 2}
Reflect.deleteProperty(null, "name"); // TypeError: Reflect.deleteProperty called on non-object

5、Reflect.construct(target, args)

Reflect.construct 方法 等于 new target(...args)。

function cat(name, age) {
  this.name = name;
  this.age = age;
}
console.log(new cat("Tom", 2)); // {name: 'Tom', age: 2}
console.log(Reflect.construct(cat, ["Tom", 2])); // {name: 'Tom', age: 2}

6、 Reflect.getPrototypeOf(obj)

用于读取 obj 的 _proto_ 属性,相当于 Object.getPrtotypeOf(obj)。Object.getPrptptypeOf 方法如果参数不是对象,会吧参数转成对象,然后读取。Reflect.getPrototypeOf 方法如果参数不是对象会报错。

class Cat {}
let obj = new Cat();
console.log(Reflect.getPrototypeOf(obj)); // {constructor: ƒ}
console.log(Cat.prototype); // {constructor: ƒ}
console.log(Reflect.getPrototypeOf(obj) === Cat.prototype); // true
Reflect.getPrototypeOf(1); // TypeError: Reflect.getPrototypeOf called on non-object

7、Reflect.setPrototypeOf(obj, newProto)

用于设置咪表对象的 prototype。相当于 Object.setProrotypeOf(obj, newProto)。返回一个 Boolean 值,表示是否设置成功。如果第一个参数不是对象,Object.setPrototypeOf 方法会返回第一个参数本身,Reflect.setPrototypeOf 方法会报错。如果参数是null,则都会报错。

let cat = {};
console.log(Object.setPrototypeOf(cat, Array.prototype)); // Array {}
console.log(Reflect.setPrototypeOf(cat, Array.prototype)); // true
console.log(Object.setPrototypeOf(1, Array.prototype)); // 1
console.log(Reflect.setPrototypeOf(1, Array.prototype)); // TypeError: Reflect.setPrototypeOf called on non-object

8、Reflect.apply(func, thisArg, args)

相当于Function.prototype.apply.call(func, thisArg, agts),用于绑定 this 对象后执行给定函数。

let arr = [9, 1, 2, 3, 4, 5, 6, 7, 8];
// 旧写法
let max = Math.max.apply(Math, arr);
console.log(max); // 9
// 新写法
let newmax = Reflect.apply(Math.max, Math, arr);
console.log(newmax); // 9

9、Reflect.defineProperty(target, propertyKey, attributes)

相当于 Object.deginfProperty(target, propertyKey, attr),用于为目标对象定义属性。如果第一个参数不是对象会报错。

let cat = {};
Object.defineProperty(cat, "age", { value: 2 });
console.log(cat); // {age: 2}
Reflect.defineProperty(cat, "name", { value: "Tom" });
console.log(cat); // {age: 2, name: 'Tom'}
Reflect.defineProperty(null, "name", { value: "Tom" }); // TypeError: Reflect.defineProperty called on non-object

这个方法可以与 Proxy.defineProperty 配合使用。Proxy.defineProperty 对属性赋值进行拦截,然后使用 Reflect.defineProperty 完成赋值。

let target = {},
  handler = {
    defineProperty(target, prop, desctiptor) {
      Reflect.defineProperty(target, prop, desctiptor);
    },
  };
let proxy = new Proxy(target, handler);
proxy.name = "Tom";
console.log(proxy); // Proxy {name: 'Tom'}

10、Reflect.getOwnPropertyDescriptor(target, propKey)

相当于 Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象。如属性不存在,返回 undefined。如果第一个参数不是对象,则 Object.getOwnPropertyDescriptor 返回 undefined,Reflect.getOwnPropertyDescriptor会报错。

let cat = {};
Reflect.defineProperty(cat, "name", { value: "Tom" }); // {name: 'Tom'}
console.log(Object.getOwnPropertyDescriptor(cat, "name")); // {value: 'Tom', writable: false, enumerable: false, configurable: false}
console.log(Reflect.getOwnPropertyDescriptor(cat, "name")); // {value: 'Tom', writable: false, enumerable: false, configurable: false}
console.log(Reflect.getOwnPropertyDescriptor(cat, "age")); // undefined
console.log(Reflect.getOwnPropertyDescriptor(cat, "sex")); // undefined
console.log(Object.getOwnPropertyDescriptor(1, "name")); // undefined
console.log(Reflect.getOwnPropertyDescriptor(1, "name")) // TypeError: Reflect.getOwnPropertyDescriptor called on non-object

11、Reflect.isExtensible(target)

相当于 Object.isExtensible,返回一个 Boolean 值,表示目标对象是否可扩展。如果参数不是对象,Object.isExtensible 方法会返回 false,而 Reflect.isExtensible 会报错。

let cat = {};
console.log(Object.isExtensible(cat)); // true
console.log(Reflect.isExtensible(cat)); // true
console.log(Object.isExtensible(null)); // false
console.log(Reflect.isExtensible(null)); // TypeError: Reflect.isExtensible called on non-object

12、Reflect.preventExtensions(target)

用相当于 Object.preventExtensions,用于让对象变为不可扩展。如果 target 参数不是对象,会抛出错误。

let cat = {};
console.log(Object.preventExtensions(cat)); // {}
console.log(Reflect.preventExtensions(cat)); // true
console.log(Reflect.isExtensible(cat)); // false
console.log(Object.preventExtensions(null)); // null
console.log(Reflect.preventExtensions(null)); // TypeError: Reflect.preventExtensions called on non-object

13、Reflect.ownKeys(target)

相当于 Object.getOwnPropertyNames 和 Object.getOwnPropertySymbols 之和。用于返回对象的所有属性。

let cat = {
  name: "Tom",
  [Symbol.for("age")]: 2,
};
console.log(Object.getOwnPropertyNames(cat)); // ['name']
console.log(Object.getOwnPropertySymbols(cat)); // [Symbol(age)]
console.log(Reflect.ownKeys(cat)); // ['name', Symbol(age)]

实例:使用Proxy实现观察者模式

观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。

先定义一个Set集合,把所有观察者添加放入集合,然后使用observable函数创建Proxy代理,进行拦截操作。拦截函数中用Array.forEach方法执行所有观察者。

let target = { name: "Tom", age: 2 };
let handler = {
  set(target, key, value, receiver) {
    let result = Reflect.set(target, key, value, receiver);
    queuedObservers.forEach((observer) => observer());
    return result;
  },
};
// 定义Set集合
let queuedObservers = new Set();
// 观察者
let print1 = () => console.log(`观察者1:${cat.name}, ${cat.age}`);
let print2 = () => console.log(`观察者2:${cat.name}, ${cat.age}`);
// 添加观察者
let observe = (fn) => queuedObservers.add(fn);
observe(print1);
observe(print2);
// 创建Proxy代理
let observable = (target) => new Proxy(target, handler);
// 观察目标
let cat = observable(target);
cat.name = "Jerry"; // 观察者1:Jerry, 2  观察者2:Jerry, 2
cat.age = 3; // 观察者1:Jerry, 3  观察者2:Jerry, 3

你可能感兴趣的:(ES6,es6,vue,javascript,前端,html)