最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Chaining 'bind' and 'call' in JavaScript? - Stack Overflow

programmeradmin1浏览0评论

When I reading this answer, find var g = f.call.bind(f);. I can't understand this with my first sight.

So does it has some direct meaning, and has some appropriate usage scenarios?

And further when you using call(or apply) or bind or both in chaining, what will happen? Is there some laws?

When I reading this answer, find var g = f.call.bind(f);. I can't understand this with my first sight.

So does it has some direct meaning, and has some appropriate usage scenarios?

And further when you using call(or apply) or bind or both in chaining, what will happen? Is there some laws?

Share Improve this question edited May 23, 2017 at 12:17 CommunityBot 11 silver badge asked Jul 3, 2015 at 4:30 2hu2hu 3192 silver badges6 bronze badges 2
  • Have you tried calling g with some arguments and looked how they are accessible in f? – Bergi Commented Jul 3, 2015 at 4:42
  • Have a look at this. – Bergi Commented Jul 3, 2015 at 4:44
Add a ment  | 

3 Answers 3

Reset to default 4

var g = f.call.bind(f);. I can't understand this with my first sight.

I assume you're familar with both the .call() and .bind() Function methods? Well, it binds the f.call method to the function f.

Notice that f.call is just Function.prototype.call, it doesn't matter that we access it as a property on f because we don't call it here.

So does it has some direct meaning?

It might bee more obvious when we look at the ES6 equivalent:

(Function.prototype.call).bind(f) // or
(f.call).bind(f) // is basically
(...args) => f.call(...args) // or more clear
(ctx, ...args) => f.call(ctx, ...args)

Does it have some appropriate usage scenarios?

Well, now that you know what it does, yes. It can take a prototype method and transforms it to a static function that takes the instance as the first argument. An example:

function Example(n) { this.name = n; }
Example.prototype.display = function() { console.log(this.name); }

Example.display = Function.call.bind(Example.prototype.display);

var e = new Example;
e.display(); // is the same as
Example.display(e);

Are there any laws for further chaining call/apply/bind?

Yes: as always, only the last property in the chain is actually called as a method. In the above example, theres no difference between f.call.bind(…), Function.prototype.call.bind(…) or Function.call.apply.bind.call.bind(…) - it always calls bind on call.

However, by passing them as arguments to each other, you can do some crazy things which are more or less useful.

Good question. Let's start off by considering an example that's e up on StackOverflow before: mapping all the strings in an array to lowercase. Of course I can write

strings . map(function(string) { return string.toLowerCase(); })

but that seems a bit verbose. I'd rather write

strings . map(CALL_LOWERCASE_WITH_ELT_AS_THIS)

So I might try

strings . map(String.prototype.toLowerCase)

or, to use the shorter idiom some prefer

strings . map(''.toLowerCase)

because ''.toLowerCase is exactly equal to String.prototype.toLowerCase.

But this won't work, of course, because map passes each element to the specified function as its first argument, not as its this. Therefore, we need somehow to specify a function whose first argument is used to call some other function as its this. That, of course, is exactly what Function.call does:

function.call(context)

The first argument to call ("context") is used as the this when calling function.

So, problem solved? We ought to be able to just say:

strings . map(''.toLowerCase.call)

and people have tried this and then wonder why it didn't work. The reason is that even though we are passing call of toLowerCase as the callback to map, map still has no idea that the callback is supposed to be called with a this of ''.toLowerCase. We need to explicitly tell map which this to use to call the function, which in the case of map we can do with its second "context" argument:

strings . map(''.toLowerCase.call, ''.toLowerCase)

Actually, since call is the same on any function object, we can simplify this to just

strings . map(Function.call, ''.toLowerCase)

This works and gets the job done beautifully.

However, whereas map provides this second "context" argument to specify the this to call the callback with, that is not something we can depend on being available in all situations. We need a more general way to say "make a function which calls Function.call with some particular function as this".

That is exactly what bind does. It says "take a function and make another function which calls it with a particular this":

function.bind(context)

In our case, what we want to do is to "take the function Function.call and make another function which calls it with a this of ''.toLowerCase. That is simply

Function.call.bind(''.toLowerCase)

Now we can pass this to map without having to use the second argument:

strings . map(Function.call.bind(''.toLowerCase))

That works exactly the same as strings . map(Function.call, ''.toLowerCase), because in general map(fn, ctxt) is precisely equal to map(fn.bind(ctxt)).

The following breaks this down into a readable form, step by step:

Function .           // From the Function object
  call .             // take the `call` method
  bind(              // and make a new function which calls it with a 'this' of
    ''.toLowerCase   // `toLowerCase`
  )

When this construct is specified as a callback, such as to map, it means:

Invoke call with the first argument passed in and ''.toLowerCase as this, which by virtue of the definition of call, means to call toLowerCase with that argument as this.

Some people prefer to simplify this a bit by saying

var call = Function.call;
var toLowerCase = ''.toLowerCase;

strings . map(call.bind(toLowerCase))

or, using the second argument provided by map, just

strings . map(call, toLowerCase)

which is almost readable as English: "map each string to the result of calling toLowerCase.

Another mon, related use case would be specifying the callback in a then on a promise. Consider the following code:

promise . then(function(result) { result.frombulate(); })

That's fine, but it's a bit verbose. And then has no way to pass in a context to be used as this when invoking the success or failure handler. But with the above, we can now write:

promise . then(call.bind(frombulate))

There are other use cases for the call.bind idiom, but this is one of the most mon ones: define a callback whose effect is to invoke some function with the parameter passed to the callback as its this.

With ES6 fat arrow functions, of course, I can write

promise . then(result => result.frombulate())

so there is relatively less advantage in the shorthand offered by call.bind(frombulate), and it is hard to deny that the fat-arrow version is more readable than that using bind.

The following question might be of interest too: Array.map and lifted functions in Javascript.

m.call.bind(m)

can be used as shorthand for:

function(x){return m.bind(x)()}

The former is the "point-free" form of the latter, arguments are implicit. It would be useful with list-operations like map(), making them shorter. You can write stuff like:

let m   = "".toUpperCase;
let fun = m.call.bind(m);
let see = ['a','b'].map(fun);
发布评论

评论列表(0)

  1. 暂无评论