// begin :: closed code
const obj = {
valueOf() {
return { foo: this.foo, bar: this.bar };
},
toString(link = '-') {
return [this.foo, this.bar].join(link);
},
foo: 'Foo',
bar: 'Bar',
baz: 'BAAAZZ'
};
// end :: closed code
console.log(
'obj.valueOf() ...',
obj.valueOf()
);
console.log(
'obj.toString() ...',
obj.toString()
);
enableMethodModifierPrototypes();
function concatBazAdditionally(proceed, handler, [ link ]) {
const result = proceed.call(this, link);
return `${ result }${ link }${ this.baz }`;
}
obj.toString = obj.toString.around(concatBazAdditionally, obj);
// obj.toString = aroundModifier(obj.toString, concatBazAdditionally, obj)
console.log(
'`around` modified ... obj.toString("--") ...',
obj.toString("--")
);
function logWithResult(result, args) {
console.log({ modifyerLog: { result, args, target: this.valueOf() } });
}
obj.toString = obj.toString.after(logWithResult, obj);
// obj.toString = afterModifier(obj.toString, logWithResult, obj)
console.log(
'`around` and `after` modified ... obj.toString("##") ...',
obj.toString("##")
);
function logAheadOfInvocation(args) {
console.log({ stats: { args, target: this } });
}
obj.valueOf = obj.valueOf.before(logAheadOfInvocation, obj);
// obj.valueOf = beforeModifier(obj.valueOf, logAheadOfInvocation, obj)
console.log(
'`before` modified ... obj.valueOf() ...',
obj.valueOf()
);
restoreDefaultFunctionPrototype();
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
function isFunction(value) {
return (
typeof value === 'function' &&
typeof value.call === 'function' &&
typeof value.apply === 'function'
);
}
function getSanitizedTarget(value) {
return value ?? null;
}
function around(handler, target) {
target = getSanitizedTarget(target);
const proceed = this;
return (
isFunction(handler) &&
isFunction(proceed) &&
function aroundType(...args) {
const context = getSanitizedTarget(this) ?? target;
return handler.call(context, proceed, handler, args);
}
) || proceed;
}
around.toString = () => 'around() { [native code] }';
function before(handler, target) {
target = getSanitizedTarget(target);
const proceed = this;
return (
isFunction(handler) &&
isFunction(proceed) &&
function beforeType(...args) {
const context = getSanitizedTarget(this) ?? target;
handler.call(context, [...args]);
return proceed.apply(context, args);
}
) || proceed;
}
before.toString = () => 'before() { [native code] }';
function after(handler, target) {
target = getSanitizedTarget(target);
const proceed = this;
return (
isFunction(handler) &&
isFunction(proceed) &&
function afterReturningType(...args) {
const context = getSanitizedTarget(this) ?? target;
const result = proceed.apply(context, args);
handler.call(context, result, args);
return result;
}
) || proceed;
}
after.toString = () => 'after() { [native code] }';
function aroundModifier(proceed, handler, target) {
return around.call(proceed, handler, target);
}
function beforeModifier(proceed, handler, target) {
return before.call(proceed, handler, target);
}
function afterModifier(proceed, handler, target) {
return after.call(proceed, handler, target);
}
const { prototype: fctPrototype } = Function;
const methodIndex = {
around,
before,
after/*Returning*/,
// afterThrowing,
// afterFinally,
};
const methodNameList = Reflect.ownKeys(methodIndex);
function restoreDefaultFunctionPrototype() {
methodNameList.forEach(methodName =>
Reflect.deleteProperty(fctPrototype, methodName),
);
}
function enableMethodModifierPrototypes() {
methodNameList.forEach(methodName =>
Reflect.defineProperty(fctPrototype, methodName, {
configurable: true,
writable: true,
value: methodIndex[methodName],
}),
);
}
</script>
<!--
<script src="https://closure-compiler.appspot.com/code/jscd16735554a0120b563ae21e9375a849d/default.js"></script>
<script>
const {
disablePrototypes: restoreDefaultFunctionPrototype,
enablePrototypes: enableMethodModifierPrototypes,
beforeModifier,
aroundModifier,
afterModifier,
} = modifiers;
</script>
//-->