Extracting a method in Javascript - 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

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)

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

How to set a function along with its arguments as a callback?

Consider the following code snippet:
$scope.delete=function(){
foo('x',3);
};
Is there any cleaner way to write this? I mean when the callback function consist of one line with call another function? (clearly, I can not write it as $scope.delete=foo('x',3))
You can use Function.prototype.bind()
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.
For example:
$scope.delete = foo.bind(null, 'x', 3);
You can use bind as follows:
$scope.delete=foo.bind(this, 'x',3);
Where the first argument is the function context. That is, the value this will assume when the function is executed.
From the MDN 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.

How does Function.prototype.call.bind work?

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)

Categories

Resources