如何确定是否使用`.call`或`.apply`调用函数?

时间:2017-06-05 15:47:14

标签: javascript this babeljs

我创建了这个函数,以便我可以将方法链接在一起:

export default function bindable(fn) {
    return function boundFn(...args) {
        return this === undefined ? fn(...args) : fn(this, ...args);
    }
}

适用于bind operator。例如,您可以定义一个新函数,如

 export const flatten = bindable(arrayOfArrays => Array.prototype.concat(...arrayOfArrays));

然后像这样使用它:

[[1,2,3],[4,5,6]]::flatten()
像这样

flatten([[1,2,3],[4,5,6]])

Babel REPL

这开始很有效,直到我意识到如果我像第二种情况一样使用它,但是将辅助方法作为模块导入,它会破坏上下文!

 import * as Arr from './array-helper-methods';
 Arr.flatten([1,2,3]); // `this` is now `Arr`

所以我的问题是:有没有办法可以可靠地检测是否使用绑定操作符调用了函数?

3 个答案:

答案 0 :(得分:2)

这是绑定运算符的一个很大但尚未解决的问题。但不,它只适用于方法。如果你想支持两种风格,你应该提供两个版本的功能。

动态地做这件事很难。不,您无法检测是否使用()来调用该函数,作为一种方法,绑定了callapply等等。您真的应该无论如何都不能。< / p>

对于你的情况,我会选择

function bindable(fn, isContext) {
    return function boundFn(...args) {
        return isContext(this) ? fn(this, ...args) : fn(...args);
    }
}
export default bindable(bindable, f => typeof f == "function");

然后,您可以将bindable(flatten, Array.isArray)flatten::bindable(Array.isArray)用于flatten。对于更复杂的情况,您还可以合并函数的arity,即fn.length + 1 == args.length

答案 1 :(得分:1)

这是一种解决方法而不是真正的解决方案,但您可以检查this是否包含flatten属性:

export default function bindable(fn) {
    return function boundFn(...args) {
        return (this === undefined || 'flatten' in this) ? fn(...args) : fn(this, ...args);
    }
}

答案 2 :(得分:1)

更通用(但性能不太好)的解决方案是检查this的任何方法是否为boundFn函数:

export default function bindable(fn) {
    return function boundFn(...args) {
        return (this == null || Object.values(this).some(x => x === boundFn))
            ? fn(...args)
            : fn(this, ...args);
    }
}