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
|
// Assigning to a non-existing property of a plain object defines that
// property on that object, even if a proxy is on the proto chain.
// Create an object that behaves just like obj except it throws (instead of
// returning undefined) if you try to get a property that doesn't exist.
function throwIfNoSuchProperty(obj) {
return new Proxy(obj, {
get(t, id) {
if (id in t)
return t[id];
throw new Error("no such handler method: " + id);
}
});
}
// Use a touchy object as our proxy handler in this test.
var hits = 0, savedDesc = undefined;
var touchyHandler = throwIfNoSuchProperty({
set: undefined
});
var target = {};
var proto = new Proxy(target, touchyHandler);
var receiver = Object.create(proto);
// This assignment `receiver.x = 2` results in a series of [[Set]] calls,
// starting with:
//
// - receiver.[[Set]]()
// - receiver is an ordinary object.
// - This looks for an own property "x" on receiver. There is none.
// - So it walks the prototype chain, doing a tail-call to:
// - proto.[[Set]]()
// - proto is a proxy.
// - This does handler.[[Get]]("set") to look for a set trap
// (that's why we need `set: undefined` on the handler, above)
// - Since there's no "set" handler, it tail-calls:
// - target.[[Set]]()
// - ordinary
// - no own property "x"
// - tail call to:
// - Object.prototype.[[Set]]()
// - ordinary
// - no own property "x"
// - We're at the end of the line: there's nothing left on the proto chain.
// - So at last we call:
// - receiver.[[DefineOwnProperty]]()
// - ordinary
// - creates the property
//
// Got all that? Let's try it.
//
receiver.x = 2;
assertEq(receiver.x, 2);
var desc = Object.getOwnPropertyDescriptor(receiver, "x");
assertEq(desc.enumerable, true);
assertEq(desc.configurable, true);
assertEq(desc.writable, true);
assertEq(desc.value, 2);
|