-
Notifications
You must be signed in to change notification settings - Fork 0
/
setPrototypeOf.js
85 lines (69 loc) · 3.49 KB
/
setPrototypeOf.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
if(!Object.setPrototypesOf) {
/*
* How it will work?
* We set the prototype of the object, on which we called setPrototypesOf(), to a Proxy, and
* it will dispatch the [[Get]]/[[Set]] requests to the prototypes
* Why not directly wrap the object into a proxy and return it?
* Because we would then have to use the Proxy returned and no more the object directly
* */
Object.setPrototypesOf = function(target, ...prototypes) {
if(!target || typeof target != "object") {
throw new TypeError("'target' must be an object");
}
if(prototypes.length == 0) {
throw new TypeError("insert two or more prototypes");
}
if(prototypes.length == 1) {
throw new TypeError("use Object.setPrototypeOf instead");
}
if(prototypes.some(prototype => typeof prototype != "object" || !prototype)) {
throw new TypeError("a prototype is not an object");
}
// target == the object on which we called setPrototypesOf()
target[Symbol.for("[[Prototypes]]")] = [...prototypes];
const proxy = new Proxy({}, {
get(target, propertyKey, receiver) {
// if the trap is executed, it means that the object
// did not own this property directly, because if it had been so
// the proxy would not have been triggered
// target = the empty unused wrapped obj
// receiver = the object on which we called setPrototypesOf()
for (const prototype of receiver[Symbol.for("[[Prototypes]]")]) {
if(Reflect.has(prototype, propertyKey)) {
// if the property were a getter
// this inside them will be the receiver (inheritance/polymorphism principle)
// console.log("GET");
return Reflect.get(prototype, propertyKey, receiver);
}
}
// property no found
return undefined;
},
set(target, propertyKey, value, receiver) {
// if the trap is executed, it means that the object
// did not own this property directly, because if it had been so
// the proxy would not have been triggered
// target = the empty unused wrapped obj
// receiver = the object on which we called setPrototypesOf()
for (const prototype of receiver[Symbol.for("[[Prototypes]]")]) {
if(Reflect.has(prototype, propertyKey)) {
// if the property were a setter
// this inside them will be the receiver (inheritance/polymorphism principle)
// Generally, even if a property were found in one of the prototypes
// we have to set it on the receiver
// console.log("SET");
return Reflect.set(prototype, propertyKey, value, receiver);
}
}
// property no found in the prototypes?
// we have to set it to the receiver without reusing Reflect.set
// because the trap would be triggered again in an endless cycle
return Reflect.defineProperty(receiver, propertyKey, {
value
});
},
});
Object.setPrototypeOf(target, proxy);
return target;
}
}