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

Mar*_*icu 7 javascript prototype function-prototypes javascript-objects

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

测试脚本

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`;
};
Run Code Online (Sandbox Code Playgroud)

因此,在跟踪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`;
};
Run Code Online (Sandbox Code Playgroud)

我所期望的

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

没什么

当然,我每次添加或删除的东西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`;
}"
Run Code Online (Sandbox Code Playgroud)
Person.prototype
Proxy {greeting: ƒ, first: "George", last: "Clooney", age: 55, farewell: ƒ, constructor: ƒ}
Run Code Online (Sandbox Code Playgroud)

它在上设置了所有属性,在prototype实例上没有设置任何属性,而每次我在上instance设置内容时,它都直接在上设置了属性prototype

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

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

CMS*_*CMS 5

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

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

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

Reflect.set(target, key, value, receiver);
Run Code Online (Sandbox Code Playgroud)

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

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

Reflect.set(target, key, value, receiver);
Run Code Online (Sandbox Code Playgroud)

  • @PatrickRoberts为了避免每次创建实例时都记录日志,您可以将console.log语句包装在if(Person.prototype == receiver)中,以便仅记录对Person.prototype的修改。 (2认同)