Reputation: 1783
I wanted to experiment a bit with the Proxy object, and got some unexpected results, as follows:
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`;
};
So in tracking how the prototype
object is being modified, I added this wrapper:
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`;
}"
and nothing else.
And of course, each time I added or removed something from the prototype
, i.e Person.prototype
or instance.constructor.prototype
I expected to see the console.log()
message.
However I did not expect to see anything when setting something on the instance, like:
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: ƒ}
It set all the properties on the prototype
and nothing on the instance, and each time I set something on the instance
it sets it straight on the prototype
.
Evidently this is not the default behaviour, as if I remove that Proxy
, every property set with this
will be set on the instance itself and the prototype
will start up empty (or in our case with just the greeting
function).
What am I missing here ? A point in the right direction would be appreciated.
Upvotes: 7
Views: 1335
Reputation: 827496
What you are missing is the fact that when you have a Proxy object in the prototype chain, the set handler will be called when you modify the child object.
In your example, when you set a property on the new instance, the set trap will be executed, the target
will be the wrapped Person.prototype
object, but there's a fourth argument, the receiver
. This argument points to the object that the property has been accessed on.
To properly do the property assignment, you can use the Reflect.set
API to set it:
Reflect.set(target, key, value, receiver);
That's why the Reflect
API matches the proxy traps arguments.
So, in your example, we could use the Reflect API and you will see that Person.prototype
doesn't get "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
Upvotes: 5