! , , apply , , , get:
var obj = {
counter: 0,
method1: function(a) { this.counter += a; return this; },
method2: function(a, b) { this.counter += a*b; return this; },
};
Object.defineProperty(obj, "getter1", {get:function() { this.counter += 7; return this; }});
Object.defineProperty(obj, "getter2", {get:function() { this.counter += 13; return this; }});
var p = new Proxy(obj, {
capturedCalls: [],
get: function(target, property, receiver) {
if(property === "execute") {
let result = target;
for(let call of this.capturedCalls) {
if(call.type === "getter") {
result = result[call.name]
} else if(call.type === "method") {
result = result[call.name].apply(target, call.args)
}
}
return result;
} else {
let desc = Object.getOwnPropertyDescriptor(target, property);
if(desc.value && typeof desc.value === 'function') {
let callDesc = {type:"method", name:property, args:null};
this.capturedCalls.push(callDesc);
return function(...args) { callDesc.args = args; return receiver; };
} else {
this.capturedCalls.push({type:"getter", name:property})
return receiver;
}
}
},
});
return function(...args) { callDesc.args = args; return receiver; }; - , . , " ", , , . p.getter1.method2(1,2).execute ( yeilds obj obj.counter===9)
, , , , - .
.. " " , obj. , obj "" - .
:
, , , , , . , this. " " , :
var fn = function(){};
var obj = {
counter: 0,
method1: function(a) { this.counter += a; return this; },
method2: function(a, b) { this.counter += a*b; return this; },
[Symbol.toPrimitive]: function(hint) { console.log(hint); return this.counter; }
};
Object.defineProperty(obj, "getter1", {get:function() { this.counter += 7; return this; }});
Object.defineProperty(obj, "getter2", {get:function() { this.counter += 13; return this; }});
let fn = function(){};
fn.obj = obj;
let rootProxy = new Proxy(fn, {
capturedCalls: [],
executionProperties: [
"toString",
"valueOf",
Symbol.hasInstance,
Symbol.isConcatSpreadable,
Symbol.iterator,
Symbol.match,
Symbol.prototype,
Symbol.replace,
Symbol.search,
Symbol.species,
Symbol.split,
Symbol.toPrimitive,
Symbol.toStringTag,
Symbol.unscopables,
Symbol.for,
Symbol.keyFor
],
executeChain: function(target, calls) {
let result = target.obj;
if(this.capturedCalls.length === 0) {
return target.obj;
}
let lastResult, secondLastResult;
for(let i = 0; i < capturedCalls.length; i++) {
let call = capturedCalls[i];
secondLastResult = lastResult;
lastResult = result;
if(call.type === "get") {
result = result[call.name];
} else if(call.type === "apply") {
result = result.apply(secondLastResult, call.args);
}
if(result.___isProxy) {
leftOverCalls = capturedCalls.slice(i+1);
let allCalls = [...result.___proxyHandler.capturedCalls, ...leftOverCalls];
return this.executeChain(result.___proxyTarget, allCalls);
}
}
return result;
},
get: function(target, property, receiver) {
if(property === "___isProxy") { return true; }
if(property === "___proxyTarget") { return target; }
if(property === "___proxyHandler") { return this; }
if(this.executionProperties.includes(property)) {
let result = this.executeChain(target, this.capturedCalls);
let finalResult = result[property];
if(typeof finalResult === 'function') {
finalResult = finalResult.bind(result);
}
return finalResult;
} else {
let newHandler = {};
Object.assign(newHandler, this);
newHandler.capturedCalls = this.capturedCalls.slice(0);
newHandler.capturedCalls.push({type:"get", name:property});
let np = new Proxy(target, newHandler)
return np;
}
},
apply: function(target, thisArg, args) {
let newHandler = {};
Object.assign(newHandler, this);
newHandler.capturedCalls = this.capturedCalls.slice(0);
newHandler.capturedCalls.push({type:"apply", args});
let np = new Proxy(target, newHandler);
return np;
},
isExtensible: function(target) { return Object.isExtensible(this.executeChain(target)); },
preventExtensions: function(target) { return Object.preventExtensions(this.executeChain(target)); },
getOwnPropertyDescriptor: function(target, prop) { return Object.getOwnPropertyDescriptor(this.executeChain(target), prop); },
defineProperty: function(target, property, descriptor) { return Object.defineProperty(this.executeChain(target), property, descriptor); },
has: function(target, prop) { return (prop in this.executeChain(target)); },
set: function(target, property, value, receiver) { Object.defineProperty(this.executeChain(target), property, {value, writable:true, configurable:true}); return value; },
deleteProperty: function(target, property) { return delete this.executeChain(target)[property]; },
ownKeys: function(target) { return Reflect.ownKeys(this.executeChain(target)); }
});
, , apply. , . , , , . , , DSL - , , , , .