为什么 Proxy 对象反映的变化超出了 target 对象?

Why does the Proxy object reflect changes beyond the target object?

我想尝试一下 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 函数)。

我在这里错过了什么?如果能指出正确的方向,我们将不胜感激。

你缺少的是当你在原型链中有一个代理对象时,当你修改子对象时设置处理程序将被调用。

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

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

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

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

因此,在您的示例中,我们可以使用 Reflect API,您会看到 Person.prototype 没有得到 "polluted"。

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