Reputation: 14318
Suppose I have a function, foo
, and a bound version of it, bar
. Is there a built-in method I can test if bar
is a bound version of foo
more definitively than checking the name?
function isBoundTo(bound, fn) {
return /bound (.+)$/.exec(bound.name)[1] === fn.name;
}
function foo() {
return this && this.x !== undefined ? this.x : 'foo';
}
function notfoo() {
return 'notfoo';
}
const bar = foo.bind({ x: 'bar' });
const boundbar = bar.bind({ x: 'boundbar' });
const baz = {
foo() {
return 'baz.foo';
}
};
const results = [
['foo()'],
['notfoo()'],
['bar()'],
['boundbar()'],
['baz.foo()'],
['isBoundTo(bar, foo)'],
['isBoundTo(bar, notfoo)'],
['isBoundTo(boundbar, bar)'],
['isBoundTo(boundbar, foo)'],
['isBoundTo(bar, baz.foo)', 'false positive'],
].reduce((obj, [exp, comment]) => {
const val = eval(exp);
return {
...obj,
[exp]: {
result: typeof val === 'string' ? JSON.stringify(val) : val,
comment: comment || ''
}
};
}, {});
console.table(results);
document.querySelector('.as-console-table thead th').innerText = 'expression';
/*<ignore>*/console.config({maximize:true,timeStamps:false,autoScroll:false});/*</ignore>*/
<script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
I know I could overwrite Function.prototype.bind
with a proxy. I'd just rather not (though the more I've worked on this question, the less I'm opposed to using a proxy).
Function.prototype.bind = new Proxy(Function.prototype.bind, {
apply: function (target, thisArg, args) {
let fn = Reflect.apply(target, thisArg, args);
Object.defineProperty(fn, 'targetFunction', {
value: thisArg.targetFunction || thisArg,
configurable: false,
writable: false
});
return fn;
}
});
function foo() {
return this && this.x !== undefined ? this.x : 'foo';
}
function notfoo() {
return 'notfoo';
}
const bar = foo.bind({ x: 'bar' });
const boundbar = bar.bind({ x: 'boundbar' });
const baz = {
foo() {
return 'baz.foo';
}
};
const results = [
'foo()',
'notfoo()',
'bar()',
'boundbar()',
'baz.foo()',
'foo.targetFunction',
'bar.targetFunction === foo',
'bar.targetFunction === notfoo',
'boundbar.targetFunction === bar',
'boundbar.targetFunction === foo',
'bar.targetFunction === baz.foo'
].reduce((obj, exp) => {
const val = eval(exp);
return {
...obj,
[exp]: {
result: typeof val === 'string' ? JSON.stringify(val) : val
}
};
}, {});
console.table(results, ['expression', 'result']);
document.querySelector('.as-console-table thead th').innerText = 'expression';
/*<ignore>*/console.config({maximize:true,timeStamps:false,autoScroll:false});/*</ignore>*/
<script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
When I debugged this and inspected fn
, there was a property listed as [[TargetFunction]]
, but I have no idea how to reference it. It doesn't appear to be a symbol (which are denoted [Symbol('name')]
). I thought maybe it was specific to Node.JS, but it also appears in Chrome. It doesn't appear in Firefox, but since I'm only using this for testing in Node, I don't really care. I imagine it must be accessible somehow if the debugger knows it exists and can retrieve the value of it.
'Cause some of ya'll want me to justify the need for this, I wrote up a simplified version of what I'm working on. (I didn't choose the pattern for opening a dialog, but I do need to test it.) Note: The code doesn't actually run because repl.it wouldn't let me install the dependencies needed for Enzyme. https://repl.it/repls/BitesizedWeirdElectricity
Upvotes: 3
Views: 255
Reputation: 18609
There's no way to do that. [[TargetFunction]]
is an internal property, that isn't exposed.
However, you might implement a custom bind
function, that keeps track of that:
const boundFns = new WeakMap()
function bind(fn, thisArg, ...args){
const newFn = function (...moreArgs) {
const allArgs = args.concat(moreArgs)
return new.target
? Reflect.construct(fn, allArgs, new.target)
: Reflect.apply(fn, thisArg, allArgs)
}
Object.defineProperty(newFn, 'name', {value: 'bound ' + fn.name})
Object.defineProperty(newFn, 'length', {value: fn.length})
Object.defineProperty(newFn, 'toString', {
value: function toString(){
return fn.toString()
},
configurable: true
})
boundFns.set(newFn, fn)
return newFn
}
function getTargetFunction(fn){
return boundFns.get(fn)
}
Or even simpler (as @Ry- pointed out), that uses native bind
:
const boundFns = new WeakMap()
function bind(fn, ...args){
const newFn = fn.bind(...args)
boundFns.set(newFn, fn)
return newFn
}
function getTargetFunction(fn){
return boundFns.get(fn)
}
Upvotes: 1
Reputation: 2181
You can check if the original function prototype is the prototype of objects created from the bound function but you will have to execute it:
function foo(){}
var bar = foo.bind({},1,2,3);
function baz() {}
Object.setPrototypeOf(foo.prototype, baz.prototype);
foo.prototype === Object.getPrototypeOf(new bar()); // true
baz.prototype === Object.getPrototypeOf(new bar()); // false
Edit: check it is the same prototype instead of part of the prototype chain.
Upvotes: 0