简体   繁体   中英

JS Array.prototype.filter on prototype method

Is there an easier way to call filter on a prototype method without an anonymous function?

I wonder if there is an equivalent to myArray.filter(function(it){ it.method() }) .

This looks close to what might work (it doesn't):

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

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

Instead I get a TypeError in both latest Firefox and Chrome, it's because it doesn't quite do what I want:

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

I tried using bind, maybe I'm missing it, but if it's not a one-liner, it's not any better than the anonymous method form:

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);

Let's set up some variables:

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

Your attempt can now be written as:

array.filter(method.call);

The problem is that call is getting called but with no this . It needs a this of method . method.call is precisely the same as the raw Function.prototype.call , with no binding to any this . Merely saying method.call does not give you a version of call bound to method . To arrange for call to be bound to the right this , namely method , you need to, well, bind it:

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

Walking through this:

  1. method.call.bind(method) returns a new version of Function#call which is bound to X#method ; think of it as method.call(waiting) , which is waiting to be called with a value which will call X#method against a particular instance of X.

  2. Array#filter passes each argument in the array to that bound version of Function#call , which results in method.call(elt, remaining_args...) , which is the equivalent of elt.method(remaining_args...) .

Output:

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

Some sugar

One could make this more semantic and readable with a little wrapper, which we will call thisify :

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

array.filter(thisify(method));

Using the context parameter to filter

You could use the little-used context parameter of filter and its brethren (except reduce ), essentially, letting filter do the binding for you, if you choose to look at it that way, since

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

So we can just write:

array.filter(method.call, method);

That actually looks cleaner to me. I doubt if it could get much simpler than that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM