【问题标题】:JS Array.prototype.filter on prototype method原型方法上的 JS Array.prototype.filter
【发布时间】:2026-01-12 13:30:01
【问题描述】:

有没有更简单的方法在没有匿名函数的原型方法上调用过滤器?

不知道有没有等价于myArray.filter(function(it){ it.method() })

这看起来很接近可行的方法(它不可行):

function X() {}
X.prototype.method = function() { console.log(this); }

[new X(), new X()].filter(X.prototype.method.call);

相反,我在最新的 Firefox 和 Chrome 中都收到了 TypeError,这是因为它并没有完全符合我的要求:

x = function() { console.log(this) }
x.call(123) //logs 123
y = x.call //reports that y is of type function in console
y(123) //TypeError: Function.prototype.call called on incompatible undefined
y.call(x, 123); //this is what you really need

我试过用bind,可能是我漏掉了,但如果不是单行的话,也比不上匿名方法形式:

function X() {}
X.prototype.method = function() { console.log(this); }

y = X.prototype.method.call
y.bind(X.prototype.method)

[new X(), new X()].filter(y);

【问题讨论】:

  • .bind 返回一个新函数,它不会修改函数本身。所以,y = y.bind(...)

标签: javascript


【解决方案1】:

让我们设置一些变量:

var method = X.prototype.method,
    array = [new X(), new X()];    

你的尝试现在可以写成:

array.filter(method.call);

问题是call 被调用但没有this。它需要method 中的thismethod.call 与原始Function.prototype.call 完全相同,没有绑定到任何this。仅仅说method.call 并不会给您绑定到methodcall 版本。要将call绑定到右边的this,即method,你需要,好吧,绑定它:

array.filter(method.call.bind(method));

走过这个:

  1. method.call.bind(method) 返回一个新的 绑定到X#methodFunction#call 版本;把它想象成 method.call(waiting),等待被调用 将针对特定的 X 实例调用 X#method 的值。

  2. Array#filter 将数组中的每个参数传递给绑定的版本 的Function#call,结果为method.call(elt, remaining_args...), 相当于elt.method(remaining_args...)

输出:

> array.filter(method.call.bind(method));
  X {method: function}
  X {method: function}

一些糖

我们可以用一个小包装器使这个更语义化和可读性更强,我们称之为thisify

function thisify(fn) { return fn.call.bind(fn); }

array.filter(thisify(method));

使用context参数到filter

您可以使用filter 的很少使用的context 参数及其兄弟(reduce 除外),本质上,让filter 为您进行绑定,如果您选择这样看的话,因为

Array#filter(fn, context) === Array#filter(fn.bind(context))

所以我们可以写:

array.filter(method.call, method);

这对我来说实际上看起来更干净。我怀疑它是否会比这更简单。

【讨论】:

  • 谢谢,非常有用。最后一种使用上下文参数的方法我比绑定更喜欢。我现在意识到的是,匿名函数的优点是您不需要知道该方法是在原型上还是在每个实例上。但是,我认为在某些情况下,这可能是一种更短的方式。我很想有一天在 JSPerf 中尝试一下,看看在性能方面是否存在差异。