Add events to a NodeList - javascript

https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/#tidier-event-binding-with-queryselectorall demonstrates an approach of adding events to a NodeList:
var unboundForEach = Array.prototype.forEach,
forEach = Function.prototype.call.bind(unboundForEach);
forEach(document.querySelectorAll('.klasses'), function (el) {
el.addEventListener('click', someFunction);
});
I don't quite understand two things:
Why does it need to bind the unboundForEach?
Why does it use Function.prototype.call.bind instead of Function.prototype.bind?

What .bind does is it creates a function similar to the original function, except:
The first argument is the this value to be used inside the function
The second, third, fourth, etc arguments passed to the bound function are passed as the first, second, and third etc arguments to the function
Using .bind here is one way of allowing for the creation a function that can take a collection as the first parameter (the this) and a callback as the second parameter. That said, as you've noticed, the way they're constructing this is pretty confusing.
var unboundForEach = Array.prototype.forEach,
forEach = Function.prototype.call.bind(unboundForEach);
To unpack this - when .call is used, it requires a this of the function to be invoked. For example
function fn() {
console.log(this.foo);
}
fn.call({ foo: 'foo' });
The above invokes Function.prototype.call with a this value of fn. The argument passed in is then used as the this value when fn is called. Equivalently, you could do
function fn() {
console.log(this.foo);
}
const invokeFnWithAThisOfTheArgument = Function.prototype.call.bind(fn);
invokeFnWithAThisOfTheArgument({ foo: 'foo' });
Your code's Function.prototype.call.bind(unboundForEach); is doing the same thing - .bind is used so that .call's this is bound to the prototype method, so that the prototype method is what gets invoked when the function returned by .call gets invoked.
Why does it use Function.prototype.call.bind instead of Function.prototype.bind?
Using only .bind
forEach = Function.prototype.bind(unboundForEach);
would mean that you'd eventually be invoking Function.prototype with a this value of unboundForEach. But Function.prototype doesn't do anything with its arguments or parameters - it's a no-op.
Now, while you can use the .call.bind approach, it's pretty hard to decipher. I'd highly recommend against it, and instead do something like
document.querySelectorAll('.klasses').forEach(
or, for the more general case, write out a higher-order function
const bindToNew = (fn) => (thisToPassToFn, ...args) => {
return fn.apply(thisToPassToFn, args);
};
const forEach = bindToNew(Array.prototype.forEach);
forEach(document.querySelectorAll('.klasses'), function (el) {

Stand alone forEach
The posted forEach function uses the syntax
forEach( arrrayLikeObject, callback)
similar to jQuery's $.forEach utility function, excepting that the callback arguments are those used by Array.prototype.forEach and the function won't iterate over enumerable keys of objects that are not array like - Array.prototype.forEach is intentionally generic and can be called on objects that are not instances of Array.
Calling the function with a node list argument serves as an example of calling it with an array-like argument.
Using Array.prototype.forEach
One of the ways to call forEach on an array-like object is
Array.prototype.forEach.call(arrayLike, callback)
where
Array.prototype.forEach is a function object to be called which needs an array like object as its this value ,
Function.prototype.call is a function method to call its this value, a function object, using the this value supplied as its first argument and passing any remaining argument values to the called function.
Normally call is inherited through the prototype chain of whatever function it is called on, and sees the function it is called on as its this value.
Looking closely at forEach.call,
forEach inherits call from Function.prototype on which forEach is prototyped.
call can be directly accessed as Function.prototype.call
call has a this value of forEach because its called on the forEach (function) object.
The this value of call can be set in an exotic function created by Function.prototype.bind
Hence to set the this value of call to forEach we can use
const callForEach = Function.prototype.call.bind( Array.prototype.forEach);
Note the order of method calls: bind is being called on call, and forEach becomes the bound function's this value.
All that's left now is to write the stand-alone forEach function using the callForEach trick:
function forEach( arrayLikeOject, callback) {
const callForEach = Function.prototype.call.bind( Array.prototype.forEach);
callForEach( arrayLikeObject, callBack);
}
This is equivalent to the posted code.
Answers
Q1 Why does it need to bindunboundForEach?
A1 It doesn't. It binds Function.prototype.call using Array.prototype.forEach as its this value.
Q2 Why does it use Function.prototype.call.bind instead of Function.prototype.bind?
A2 It does use Function.prototype.bind - but it's being inherited by call, the function object being bound.
Variable names
It would seem the biggest problem here is a variable name chosen by the original authors: unboundForEach makes readers infer there is a bound version of forEach somewhere. There isn't.

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)

How does this Array.prototype and bind combination replace this arrow function

I'm reading trough this: https://hackernoon.com/functional-javascript-resolving-promises-sequentially-7aac18c4431e
and in one section the author talks about replacing the second arrow function with
Promise.resolve([])
.then(all => funcs[0].then(result => all.concat(result)))
this
Promise.resolve([])
.then(all => funcs[0].then(Array.prototype.concat.bind(all)))
I'm having trouble understanding how this works... Is the returned result being added as an argument implicitly into the concat function?
whatEverMethod.bind(thisValue) binds this to thisValue.
To understand, we can assume (though not actually) every method call
obj.method(arg0)
to equal to
obj.method.apply(obj, arg0)
where the first argument of .apply explicitly tells: which object I am working on (because likely in the definition of .method you may reference to some this value, such as this.prop0 = 10)
What bind does is very simple: bind a this value to a method call, so that when called, no longer uses the default this value based on environment.
For example:
let obj0 = {a: 1}
let obj1 = {a: 2}
obj0.change = function(value) {
this.a = value;
} // when declared, default environment is obj0, since it is a method of obj0
// Now, explicitly bind `this` inside of obj0.change to obj1
let changeFunc = obj0.change.bind(obj1);
// This creates a function that has `this` set to obj1, which has the format changeFunc(value)
changeFunc(10);
console.log(obj1.a) // should be 10, since it is now operating on obj1 (due to binding)
Therefore,
(Array.prototype.concat.bind(all))(someArr)
// is basically
all.concat(someArr)
// due to having the exactly the same `this` value
The reason why we may want to do this is probably all may not be an array. For example, it might be an array-like object, such as the arguments for functions, which looks like an array, but missing common array methods.
bind operates on a function, taking a value for this and returning a new function that calls the original function with the specified this value and the same arguments¹. In other words,
Array.prototype.concat.bind(all)
means
(...args) => Array.prototype.concat.call(all, ...args)
which is
(...args) => all.concat(...args)
and since one argument is being passed, that’s
result => all.concat(result)
There is no reason not to just write the arrow function here, by the way. It’s clearer and has no downsides.
¹ it can also add arguments.
As you must know, the function then receives a function/callback.
The function bind returns a function attaching a specific object as context this.
Array.prototype.concat.bind(all))
^
|
+---- This object 'all' will be the context
'this' for the function 'concat'.
So, the function then will call the callback (passing the all object implicitly) which is the new function returned by the function bind, in this case, the function concat from Array prototype.
Resource
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.

Why (function(){return this}).bind(1).bind(2)() returns 1? [duplicate]

From the MDN:
The bind() method creates a new function that, when called, has its this keyword set to the provided value
And I can happily see it working in this example:
(function () {
console.log(this);
}).bind({foo:"bar"})();
which logs Object { foo="bar"}.
But if I chain another bind call, or even a "call" call, I'm still getting the function invoked with "this" assigned to the object passed to the first bind. Examples:
(function () {
console.log(this);
}).bind({foo:"bar"}).bind({oof:"rab"})();
&
(function () {
console.log(this);
}).bind({foo:"bar"}).call({oof:"rab"});
Both log Object { foo="bar"} instead of what I would expect: Object { oof="rab"}.
No matter how many bind calls I chain, only the first one seems to have an effect.
Why?
This might help. I just found out jQuery's version is behaving the same way! :O
jQuery.proxy(
jQuery.proxy(function() {
console.log(this);
},{foo:"bar"})
,{oof:"rab"})();
logs Object { foo="bar"}
It is tempting to think of bind as somehow modifying a function to use a new this. In this (incorrect) interpretation, people think of bind as adding some kind of magic flag to the function telling it to use a different this next time it's called. If that were the case, then it should be possible to "override" and change the magic flag. And one would then ask, what is the reason for arbitrarily restricting the ability to do so?
But in fact, that's not how it works. bind creates and returns a new function which when called invokes the first function with a particular this. The behavior of this newly created function, to use the specified this to call the original function, is burned in when the function is created. It cannot be changed any more than the internals of any other function returned by a function could be changed after the fact.
It may help to look at a real simple implementation of bind:
// NOT the real bind; just an example
Function.prototype.bind = function(ctxt) {
var fn = this;
return function bound_fn() {
return fn.apply(ctxt, arguments);
};
}
my_bound_fn = original_fn.bind(obj);
As you can see, nowhere in bound_fn, the function returned from bind, does it refer to the this with which the bound function was called. It's ignored, so that
my_bound_fn.call(999, arg) // 999 is ignored
or
obj = { fn: function () { console.log(this); } };
obj.fn = obj.fn.bind(other_obj);
obj.fn(); // outputs other_obj; obj is ignored
So I can bind the function returned from bind "again", but that is not rebinding the original function; it's merely binding the outer function, which has no effect on the inner function, since it is already set up to call the underlying function with the context (this value) passed to bind. I can bind again and again but all I end up doing is creating more outer functions which may be bound to something but still end up calling the innermost function returned from the first bind.
Therefore, it is somewhat misleading to say that bind "cannot be overridden".
If I want to "rebind" a function, then I can just do a new binding on the original function. So if I've bound it once:
function orig() { }
my_bound_fn = orig.bind(my_obj);
and then I want to arrange for my original function to be called with some other this, then I don't rebind the bound function:
my_bound_fn = my_bound_fn.bind(my_other_obj); // No effect
Instead, I just create a new function bound to the original one:
my_other_bound_fn = orig.bind(my_other_obj);
I found this line on MDN:
The bind() function creates a new function (a bound function) with the
same function body (internal call property in ECMAScript 5 terms) 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(),
which cannot be overridden.
so maybe it's really cannot be overridden once it is set.
torazaburo's excellent answer gave me an idea. It would be possible for a bind-like function, instead of baking the receiver (this) into the call inside a closure, to put it as a property on the function object and then use it when the call is made. That would allow a rebind to update the property before the call is made, effectively giving the rebind results that you expected.
For example,
function original_fn() {
document.writeln(JSON.stringify(this));
}
Function.prototype.rebind = function(obj) {
var fn = this;
var bound = function func() {
fn.call(func.receiver, arguments);
};
bound.receiver = obj;
bound.rebind = function(obj) {
this.receiver = obj;
return this;
};
return bound;
}
var bound_fn = original_fn.rebind({foo: 'bar'});
bound_fn();
var rebound_fn = bound_fn.rebind({fred: 'barney'});
rebound_fn();
Or, the output from node.js is as follows.
{ foo: 'bar' }
{ fred: 'barney' }
Note that the first call to rebind is calling the one that was added to Function.prototype since it is being called on ordinary function original_fn, but the second call is calling the rebind that was added as a property to the bound function (and any subsequent calls will call this one, as well). That rebind simply updates receiver and returns the same function object.
It was possible to access the receiver property within the bound function by making it a named function expression.
Okay, this is going to be mostly speculation but I'll try and reason through it.
The ECMAScript specification (which is currently down) states the following for the bind function (emphasis my own):
15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, …]])
The bind method takes one or more arguments, thisArg and (optionally)
arg1, arg2, etc, and returns a new function object by performing the
following steps:
Let Target be the this value.
If IsCallable(Target) is false, throw a TypeError exception.
Let A be a new (possibly empty) internal list of all of the argument values provided after thisArg (arg1, arg2 etc), in order.
Let F be a new native ECMAScript object .
Set all the internal methods, except for [[Get]], of F as specified in 8.12.
Set the [[Get]] internal property of F as specified in 15.3.5.4.
Set the [[TargetFunction]] internal property of F to Target.
Set the [[BoundThis]] internal property of F to the value of thisArg.
Set the [[BoundArgs]] internal property of F to A.
Set the [[Class]] internal property of F to "Function".
Set the [[Prototype]] internal property of F to the standard built-in Function prototype object as specified in
15.3.3.1.
Set the [[Call]] internal property of F as described in 15.3.4.5.1.
Set the [[Construct]] internal property of F as described in 15.3.4.5.2.
Set the [[HasInstance]] internal property of F as described in 15.3.4.5.3.
If the [[Class]] internal property of Target is "Function", then a. Let L be the length property of Target minus the length of A. b.
Set the length own property of F to either 0 or L, whichever is
larger.
Else set the length own property of F to 0.
Set the attributes of the length own property of F to the values specified in 15.3.5.1.
Set the [[Extensible]] internal property of F to true.
Let thrower be the [[ThrowTypeError]] function Object (13.2.3).
Call the [[DefineOwnProperty]] internal method of F with arguments "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower,
[[Enumerable]]: false, [[Configurable]]: false}, and false.
Call the [[DefineOwnProperty]] internal method of F with arguments "arguments", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower,
[[Enumerable]]: false, [[Configurable]]: false}, and false.
Return F
And when you call a function on your Object that was created with bind:
15.3.4.5.1 [[Call]]
When the [[Call]] internal method of a function object, F, which was created using the bind function is called with a
this value and a list of arguments ExtraArgs, the following steps are
taken:
Let boundArgs be the value of F’s [[BoundArgs]] internal property.
Let boundThis be the value of F’s [[BoundThis]] internal property.
Let target be the value of F’s [[TargetFunction]] internal property.
Let args be a new list containing the same values as the list boundArgs in the same order followed by the same values as the list
ExtraArgs in the same order.
Return the result of calling the [[Call]] internal method of target providing boundThis as the this value and providing args as the
arguments
Call specifies how every function is called. And somewhat resembles the JavaScript call:
someFunction.[[call]](thisValue, arguments) {
}
However when [[call]] is used on a bound function, the thisValue is overridden with the value of [[BoundThis]]. In the case of calling bind a second time, the thisValue that you attempt to override the first with is replaced by [[BoundThis]], essentially incurring no effect whatsoever on the value of thisValue:
boundFunction.[[call]](thisValue, arguments) {
thisValue = boundFunction.[[BoundThis]];
}
You'll notice that if you try and use call or apply then they will also have no effect because their attempt to override the thisValue property will be reversed when [[call]] invokes the next function.
These simplified examples of how bind() works explain it better.
Here is what function bound once looks like:
function bound_function() {
function original_function() {
console.log(self);
}
var self = 1;
original_function();
}
bound_function()
Here is what happens if we wrap original function twice:
function bound_function2() {
function bound_function1() {
function original_function() {
console.log(self);
}
var self = 1;
original_function();
}
var self = 2;
bound_function1();
}
bound_function2()
I think the way to think about it is: When you call bind() the first time the value of 'this' inside the function returned by the call to bind() is FIXED, to the given value. That is possible BECAUSE it wasn't fixed before, it was unbound. But once it is fixed it can not be fixed to anything else because it is no longer unfixed, it is no longer a "variable".
In theory there could be an opposite operation to bind called "unbind" which you could call like:
myFunk.bind(something)
.unbind(); // -> has same behavior as original myFunk
The name "bind" indicates that the (pseudo-) variable 'this' is BOUND to something, it is not simply ASSIGNED a value, which could then be assigned again and again.
When something is "bound" it has a value and that value can not be replaced --because it is "bound". So you would need an unbind() operation to make that possible. But since you assumably have the original function around somewhere
there is no need for "unbind" really.
I agree this behavior is perhaps surprising and unexpected and thus possibly error-prone because if you get a function as argument there seems to be no way to tell whether your bind() on it has any effect or not.
HOWEVER if you don't know much about such function-argument it would also be impossible to know what kind of value you CAN bind to it without breaking the calls it makes to 'this' inside it.
SO the bind() operation itself is rather hazardous. Re-binding would be doubly hazardous. So you are best off trying to avoid doing that if possible.

Are function-arguments not necessarily objects?

I'm learning functional programming and node.js, and I came across this odd problem when using Function.prototype.apply and .bind.
function Spy(target, method) {
var obj = {count: 0};
var original = target[method]
target[method] = function (){//no specified arguments
obj.count++
original.apply(this, arguments)//only arguments property passed
}
return obj;
}
module.exports = Spy
This code works, it successfully spies on target.method.
//same code here
target[method] = function (args){//args specified
obj.count++
original.apply(this, args)//and passed here
}
//same code here
This code, however, does not. It gives an error message: TypeError: CreateListFromArrayLike called on non-object.
And then the biggest surprise is, this method works perfectly fine.
//same code here
target[method] = function (args){
obj.count++
original.bind(this, args)
}
//same code here
So why exactly do I get this error? Is it because function arguments are not necessarily objects? Or is it because apply has a stricter description than bind?
In this version:
target[method] = function (args){//args specified
obj.count++
original.apply(this, args)//and passed here
}
Here you are not taking all the arguments but just one, named args. Since apply expects an array like object you cannot use args since it is only the first argument passed to the original target.
You can change it to:
target[method] = function (arg){ //only one argument specified
obj.count++
original.apply(this,[arg]) //one argument passed here
}
Now it works, but you can only spy on one argument functions. Using call would be better since you only have one extra argument:
target[method] = function (arg){ //only one argument specified
obj.count++
original.call(this,arg) //one argument passed here
}
Now bind is a totally different animal. It partial applies functions, thus return functions. Imagine you need to send a callback that takes no arguments but calls a function with some arguments you have when making it. You see code like:
var self = this;
return function() {
self.method(a, b);
}
Well. bind does this for you:
return this.method.bind(this, a, b);
When calling either of these returned functions the same happens. The method method is called with the arguments a and b. So calling bind on a function returns a partial applied version of that function and does not call it like call or apply does.
bind is called the same way as call is, even though they do very different things.
If you really wanted to use bind in this way. You could use the spread operator (ES2015) to expand the arguments 'array' to individual arguments:
original.bind(null, ...args);
That will bind the original function with the array values as individual arguments.

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