为什么代理对象反映的变化超出了目标对象?

时间:2019-09-26 13:02:34

标签: javascript prototype javascript-objects function-prototypes

我想对Proxy object进行试验,并得到了一些意想不到的结果,如下所示:

测试脚本

function Person(first, last, age) {
  this.first = first;
  this.last = last;
  this.age = age;
}

Person.prototype.greeting = function () {
  return `Hello my name is ${this.first} and I am ${this.age} years old`;
};

因此,在跟踪prototype对象的修改方式时,我添加了以下包装器:

let validator = {
    set: function(target, key, value) {
        console.log(`The property ${key} has been updated with ${value}`);
        target[key] = value;
        return true;
    }
};

Person.prototype = new Proxy(Person.prototype, validator);

let george = new Person('George', 'Clooney', 55);

Person.prototype.farewell = function () {
  return `Hello my name is ${this.first} and I will see you later`;
};

我所期望的

The property: "farewell" has been updated with: "function () {
  return `Hello my name is ${this.first} and I will see you later`;
}"

没别的。

当然,每次我从prototype添加或删除某些内容时,即Person.prototypeinstance.constructor.prototype,我都希望看到console.log()消息。

但是我没有希望在实例上进行设置时看到任何东西,例如:

george.someProp = 'another value'; // did NOT expect to see the console.log()


输出

The property: "first" has been updated with: "george"
The property: "last" has been updated with: "clooney"
The property: "age" has been updated with: "55"
The property: "farewell" has been updated with: "function () {
  return `Hello my name is ${this.first} and I will see you later`;
}"
Person.prototype
Proxy {greeting: ƒ, first: "George", last: "Clooney", age: 55, farewell: ƒ, constructor: ƒ}

它在prototype上设置了所有属性,而在实例上没有设置任何属性,并且每次我在instance上进行设置时,它都会直接在prototype上设置。

显然,这不是默认行为,就好像我删除了Proxy一样,每个用this设置的属性都将设置在实例本身上,而prototype将开始为空(或仅使用greeting函数)。

我在这里想念什么?朝正确方向的观点将不胜感激。

1 个答案:

答案 0 :(得分:5)

缺少的事实是,当原型链中有一个Proxy对象时,修改子对象时将调用set处理程序。

在您的示例中,当您在新实例上设置属性时,将执行set陷阱,target将是包装的Person.prototype对象,但是有第四个参数, receiver 。此参数指向已访问该属性的对象。

要正确执行属性分配,可以使用Reflect.set API进行设置:

Reflect.set(target, key, value, receiver);

这就是Reflect API与代理陷阱参数匹配的原因。

因此,在您的示例中,我们可以使用Reflect API,您将看到Person.prototype不会被“污染”。

function Person(first, last, age) {
  this.first = first;
  this.last = last;
  this.age = age;
}

Person.prototype.greeting = function () {
  return `Hello my name is ${this.first} and I am ${this.age} years old`;
};


const validator = {
    set: function(target, key, value, receiver) {
        console.log(`The property ${key} has been updated with ${value}`);
        Reflect.set(target, key, value, receiver)
        return true;
    }
};

Person.prototype = new Proxy(Person.prototype, validator);

const george = new Person('George', 'Clooney', 55);

Person.prototype.farewell = function () {
  return `Hello my name is ${this.first} and I will see you later`;
};

console.log(george.hasOwnProperty('first')); // true
console.log(Person.prototype.hasOwnProperty('first')); // false