33

I am having some trouble wrapping my head around this function:

var toStr = Function.prototype.call.bind( Object.prototype.toString );
toStr([]) // [object Array]​​​​​​​​​​​​​​​​​​​​​​​​​​​

How does this function accept an argument as seen in line 2?

2

7 Answers 7

33

Well,

  • Function.prototype.call references the "call" function, which is used to invoke functions with chosen this values;
  • The subsequent .bind refers to the "bind" function on the Function prototype (remember: "call" is a function too), which returns a new function that will always have this set to the passed-in argument.
  • The argument passed to "bind" is the "toString" function on the Object prototype, so the result of that whole expression is a new function that will run the "call" function with this set to the "toString" function.

The result, therefore, is like this code: Object.prototype.toString.call( param ). Then, the "console.log" call passes that function an array, and there you have it.

edit Note that Object.prototype.toString.call( param ) is like param.toString() really, when "param" is an object. When it's not, then the semantics of the "call" function are to turn it into one in the normal ways JavaScript does that (numbers -> Number, strings -> String, etc).

edit, 24 May2016 — That last sentence above is not accurate with ES2015. New JavaScript runtimes do not "autobox" primitive types when those are involved with a function call as a this value.

Sign up to request clarification or add additional context in comments.

Comments

10

I assume you already know what .call and .bind do

toStr is now a function that essentially does:

function toStr( obj ) {
    return Function.prototype.call.call( Object.prototype.toString, obj );
}

I.E it .calls the .call function with context argument set to the .toString function. Normally that part is already taken care of because you normally use .call as a property of some function which sets the function as the context for the .call.

Comments

3

The two lines of code are a function definition and then execution call of that definition with an empty array passed inside. The complexity lies in interpreting what 'this' will point to and why.

To help deduce the value of this I copied content from two links below to MDN's definitions of call and bind.

The bind() function creates a new function (a bound function) with the same function body as the function it is being called on (the bound function's target function) with the this value bound to the first argument of bind(). Your code looks similar to a 'shortcut function' described on the bind page.

var unboundSlice = Array.prototype.slice; // same as "slice" in the previous example
var slice = Function.prototype.call.bind(unboundSlice);

// ...

slice(arguments);

With call, you can assign a different this object when calling an existing function. this refers to the current object, the calling object.With call, you can write a method once and then inherit it in another object, without having to rewrite the method for the new object.

When toStr is called it passes in an array to bind, of which the this pointer is bound. With bind(), this can be simplified.

toStr() is a bound function to the call() function of Function.prototype, with the this value set to the toStr() function of Array.prototype. This means that additional call() calls can be eliminated.

In essence, it looks like a shortcut function override to the toString method.

Comments

3

JS to English translation -

var toStr = Function.prototype.call.bind( Object.prototype.toString );

The bind creates a new call function of Object.prototype.toString.

Now you can call this new function with any context which will just be applied to Object.prototype.toString.

Like this:

toStr([]) // [object Array]​​​​​​​​​​​​​​​​​​​​​​​​​

Why not just calling Object.prototype.toString.call([]) ??

Answer:

Of course, you could. But the OPs method creates a dedicated and de-methodized function just for this purpose.

This really is called demethodizing.

Comments

2

bind() - Creates a new function that, when called, itself calls this function in the context of the provided this value, with a given sequence of arguments preceding any provided when the new function was called.

Read this documentation for more information about bind() in JavaScript

Angular example:

Parent component where we have a defined function and bind it to an object:

public callback: object;

constructor() {
    this.callback= this.myFunction.bind(this);
}

public myFunction($event: any) {
    // Do something with $event ...
}

(Parent html) passing the binded object to child component:

<child-component (callbackFunction)="callback($event)"></child-component>

Child component that receives the object that is bind to the parent function:

@Output() callbackFunction: EventEmitter<object> = new EventEmitter<object>();

public childFunction() {
    ...
    this.callbackFunction.emit({ message: 'Hello!' });
    ...
}

Comments

2
  1. This should make sense to you
Object.prototype.toString.call([]) // work well
  1. You think this form is too long and cumbersome and you want to simplify it? This makes it easier to use later:
const toStr = Object.prototype.toString.call
toStr([]) // unfortunately, this does not work
  1. You need to use bind to correct where this points to.
const toStr = Object.prototype.toString.call.bind(Object.prototype.toString)
toStr([]) // work well
  1. Object.prototype.toString.call is just the same as Function.prototype.call
const toStr = Function.prototype.call.bind(Object.prototype.toString)
toStr([]) // done
  1. Function is also a function, so Function.call refers exactly to Function.prototype.call
const toStr = Function.call.bind(Object.prototype.toString) // even simpler
toStr([]) // done

summary

f.call is just the same as Function.call.bind(f)

f.call(1,2,3) is just the same as Function.call.bind(f,1)(2,3)

Comments

1

You could do :

var toStr = Object.prototype.toString.call([]);

Problem with this is : call executes instantly. What you want is to delay the execution of call.

*As function is an object too in Javascript, you can use any function as the first argument in 'call' instead of passing an object as 1st argument.*Also, you can use the dot on call like on any object.

Function.prototype.call.bind( Object.prototype.toString ) , makes a copy of the call function with it's 'this' sets to Object.prototype.toString . You hold this new copy of call function in 'toStr'.

var toStr = Function.prototype.call.bind( Object.prototype.toString );

Now you can executes this new copy of call any time you need without the need of setting 'this' as it's 'this' is already bind to Object.prototype.toString .

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.