Need of a call(), apply() and bind() method - javascript

I have read lot of theory regarding call(), apply() and bind().
I am not able to understand if we can call method directly why we need call() , apply() or bind()?
Can somebody explain in laymen terms and little in terms of JavaScript?

That's the flexibility offered by Javascript, an object and it's method don't have to be coupled with each other.
Given following object 'a' with a member method 'play'
var a = { play: function (content) { console.log("what is this:", this, content)} }
Use
a.play('Hello')
Is equivalent to
a.play.call(a, 'Hello')
As for your question why need the second way 'call'. Because 'call' gives you a way to call something else instead of 'this', which is 'a' in the example above, so you can do:
a.play.call(document, 'Hello')
About 'apply', it's just another version of 'call', which needs you to pass arguments as an array instead of comma separated parameters. To use apply, you do:
a.play.apply(a, ['Hello'])
As for 'bind', it is to link a function with a 'this' object at first, then get a returned callable object, which you can either call with the rest arguments directly, or pass it into anything else to be called later.
A typical 'bind' use case is React component's event handler. To define an event handler to be passed into a component. You need to use 'bind' in a React class component like this:
handleClick = this.handleClick.bind(this)
Then, in render function:
<Button onClick={this.handleClick} />
Check https://reactjs.org/docs/handling-events.html for the full information.

Related

chaining call and bind with slice() to convert array-like objects to arrays [duplicate]

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?
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.
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.
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.
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.
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!' });
...
}
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 .
This should make sense to you
Object.prototype.toString.call([]) //work well
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
You need to use bind to correct where this points to.
const toStr = Object.prototype.toString.call.bind(Object.prototype.toString)
toStr([]) //work well
Object.prototype.toString.call is just the same as Function.prototype.call
const toStr = Function.prototype.call.bind(Object.prototype.toString)
toStr([]) //done
summary
function f(){console.log(this, arguments)}
f.call is just the same as Function.prototype.call.bind(f)
f.call(1,2,3) is just the same as Function.prototype.call.bind(f,1)(2,3)

Extracting a method in Javascript

I learned that if I wanted to extract a method from this example.
var jane={
name:'jane',
describe:function(){
return 'Person named '+this.name;
}
};
I cannot do as follows.
var func =jane.describe;
func();
As it does not work, why does this not work? Also I was told the solution is as follows
var func =jane.describe.bind(jane);
func();
I do not understand this, what is this "bind" property of functions and why is "jane" passed into the bind property?
This will not work because the context of this changes. When you use bind, you pass the object jane to bind so then when you call describe and it uses this it references jane.
As per the documentation:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.
Reading Material
bind

bind this is optional in class if no constructor?

I have below class, but I'm confused by the bind this here. The class don't have a constructor and super(), why did he need to do bind this on setAll method? I removed the bind this, the application still work and no error.
class PushScheduleActions {
/**
* Request all API
*/
fetchAll(params) {
return dispatch => {
dispatch(params);
PushScheduleApi.getAll(params)
.then(this.setAll.bind(this));//why use bind this here?
.catch(this.setError.bind(this));
}
}
setAll(data) {
return data;
}
}
There is no relation between constructor, super and bind.
What bind does?
it returns a new function with the context user passed.
In your case, PushScheduleApi success case you are passing a new setAll function which is returned by bind method.
since your setAll function doesn't use context, you don't need to pass the context.
dispatch is an arrow function, so context is inherited. So you dont need bind method. you can simply use this.setAll
The class don't have a constructor and super()
That's completely irrelevant.
why did he need to do bind this on setAll method?
The method only needs to be bound if
it uses thisand
this should refer to what this refers to inside fetchAll (likely an instance of PushScheduleActions).
Since setAll doesn't reference this, there is no reason to bind.
However, the whole setAll method is unnecessary. The code would work exactly the same without .then(this.setAll.bind(this)). That makes me think that PushScheduleActions is supposed to be subclassed and child classes are supposed to overwrite setAll. Now, the child class' setAll might use this and expect it to refer to the instance. Since the parent class (PushScheduleActions) can't know that, binding the method is safer since it will ensure that child class implementations of setAll will work regardless if they use this or not.
The bind() function creates a new bound function (BF). A BF is an exotic function object (a term from ECMAScript 2015) that wraps the original function object. Calling a BF generally, results in the execution of its wrapped function.
For more detail follow this link i hope it will help you a lot to understand to bind() method
Use of the JavaScript 'bind' method

Cannot map bind over array

I was trying to solve https://twitter.com/secoif/status/730207047892017153 when I got an error message I don't understand. I get the error when running this code
const fns = [
function () {
console.log(1)
},
function () {
console.log(2)
},
function () {
console.log(3)
}
]
fns.map(Function.prototype.call.bind)
Chrome tells me "Bind must be called on a function", which I don't understand. The following line, which should be equivalent, does not throw the same error.
fns.map((x) => Function.prototype.call.bind(x))
To solve the JS pop quiz, you can do this:
for (var x in fns) fns[x]();
However, I realize that's not what you're asking :).
There are a few things I don't understand in your approach:
1) Why are you using .map()? Map is used for returning another array, which is not needed, so why not forEach() instead?
2) I'm not sure why you are using bind. When using map(), the callback is passed 3 parameters: the current function, the index of the function in the array, and the array itself. When you look at the syntax for bind(), you'll notice the first param for bind is the 'this' object, followed by the parameters to be passed in the function being bound to. In this case, 'this' will be set to the current function, index and array will be passed as parameters to the function.
3) Using bind on call. call() will take the same parameters a bind(), where the first one is the 'this' and the rest are parameters to be passed into the function being called. When you used .bind(), it will set the 'this' object as function and the first param will be the index. So from the perspective of .call(), you're setting it's 'this' to the function, and passing the index as the first param to call(), which is call's 'this', which then passes the whole array as the first parameter to the function.
Long story short, you're getting your values all mixed up and your overcomplicating this.
from the docs mdn docs for map
thisArg Optional. Value to use as this when executing callback.
Default value is the Window object
As marked before, you can map:
fns.map(Function.prototype.call.bind, Function.prototype.call.bind)
If you call:
fns.map(Function.prototype.call.bind)
Bind apply on object not function! and error raised, because object has not bind method.

About JavaScript bind method

reference code segment
this.localizationChanged = this.localizationChanged.bind(this);
Who can tell me why to write like that?
Who can tell me why to write like that?
localizationChanged is used as event handler:
LocalizationStore.addChangeListener(this.localizationChanged);`
If the handler was not bound to the component instance, this would not refer to the component instance and it wouldn't be possible to call the setState method of the component (this.setState(...)).
The bind() method creates a new function that, when called, has its
this keyword set to the provided value, with a given sequence of
arguments preceding any provided when the new function is called.
from MDN.
If you are trying to access this in localizationChanged function you bind this.
However in ES2015 you don't require this.You can use arrow operator :
localizationChanged =()=>{
console.log(this);
}

Categories

Resources