The following piece of code (from msdn) is a simple implementation of the 'bind' function:
/* Approximation of `Function.prototype.bind` from ES5 (without error checking) */
Function.prototype.bind = function(thisArg) {
var fn = this, args = *Array.prototype.slice.call(arguments, 1)*;
return function() {
return fn.apply(thisArg, args.concat(*Array.prototype.slice.call(arguments, 0)*));
};
};
Can anyone explain the first call to Array.prototype.slice.call ? I understand that arguments is not an array and one needs to turn it into an array before using slice and concat. I don't understand the first call - aren't we losing the first element when calling
Array.prototype.slice.call(arguments, 1)?
You are correct.
The zeroth element of arguments is thisArg, which is why it is getting removed.
According to the docs about bind, the first argument (arguments[0]) is the custom this value to be used as the value of this within the the function returned by bind (the "bound function").
What follows (arguments[1] - arguments[n]) are arguments that are to be prepended when calling the bound function, in addition to the arguments that are provided to when called.
What the first Array.prototype.slice.call does is to slice the arguments passed to bind call and get the arguments to be prepended starting from the second argument passed, leaving behind the first argument which would be our this.
For example
var newFN = someFunction.bind(myNewThis,foo,bar,baz);
The first Array.prototype.slice.call takes foo, bar and baz.
In the returned function, foo, bar and baz get prepended to the arguments provided when calling the bound function:
//fn - original function
//args - extracted arguments foo, bar and baz
//thisArg - the provided `this` value, myNewThis
//this code basically:
// - calls the original function (fn)
// - provides a custom `this` value (thisArg)
// - provides arguments that comprise the extracted arguments + the passed arguments
fn.apply(thisArg, args.concat(Array.prototype.slice.call(arguments, 0)));
So when you use the new "bound" function, you get a custom this value, as well as a "preset", prepended arguments list:
newFN('ban','bam'); //arguments === ['foo','bar','baz','ban','bam'];
Related
isInSet requires two arguments, but binds passes only theSet as first argument. I am not able to figure out how bind method works in this case
function isInSet(set , person) {
return set.indexOf(person.name) > -1;//checks for existence of person.name in theSet
}
console.log(ancestry .filter(function(person) {
return isInSet( theSet , person) ;
}) ) ;
// ! [{ name : " Maria van B r u s s e l " , ...} ,
// { name : " Carel H a v e r b e k e " , ...}]
console.log(ancestry.filter(isInSet.bind(null, theSet))) ;//**how does this work?**
// !... same result
bind() creates a new function from the function that it is called on. It sets the this keyword in the newly created function to be the first argument that you pass it (if null is passed, then it does not overwrite the default this keyword). You can also pass in extra arguments to bind() and if you do, they will always be inserted into the new function. So for instance, let's say you have a sum function that takes 2 arguments.
function sum (a, b) {
return a + b;
}
Now we could create a new function from this using bind() and always pass in one argument.
var boundSum = sum.bind(null, 2);
This will always bind 2 as the first argument in the sum() function. Now anytime you call that boundSum() function, it will only take one argument, as the 2 is already bound.
boundSum(3); // <-- this would return 5
Your example is using a similar principle. Because you are calling isInSet.bind(null, theSet) it is always binding theSet to the first parameter passed in the isInSet function. However, it is still missing the second parameter. The reason why it works is because you are putting that inside of an ancestry.filter() function. filter() essentially loops over an array and passes each element to the function inside of it (check the docs). So therefore, each element in the ancestry array is getting passed to that bound function, which makes it the second parameter of isInSet().
Read the text from the book a few times and compare the unbound and the bound version.
Calling .bind() on a function, return a new function, with some of the arguments already filled in. The isInSet function, expects two parameters: the set to filter, and a person having a name.
The .filter() method of an array, expects one parameter, namely the function to which each element of the array has to be sent to.
So when you look to the unbound version, you see that the filter function used, just returns isInSet(theSet, person).
So to make the two compatible, we .bind() to create a new function with only one parameter left (namely 'person'), that is bound to theSet. So every time this new function returned by bind gets called, it will use theSet as it's first parameter and will expect only one parameter, 'person'.
Here, person, the second parameter of the original isInSet function we didn't bind, is used as the first and only parameter of the bound function.
So we have exactly what we need after using the bind. A function that will take a 'person' as a parameters and will always look inside the same array (theSet).
If you read the docs for .bind(), you see that it accepts a variable list of arguments. The first argument is the 'this' argument. So if you have a function that uses 'this' inside it and you want to bind it to eg. an object, you can use the 1st parameter. Eg. myFunc.bind( objectToCallUpon, firstArg, secondArg );
Since we don't actually use the 'this' argument in the isInSet() function, we just pass 'null' as the 'this' value.
All the other parameters used are the arguments of isInSet you want to make 'fixed'. Since we want the first parameter to always be theSet, we bind that value.
If the isInSet function would accept two parameters, you would use:
isInSet.bind( null, theSet, secondParameter );
According to this JavaScript reference:
The value null is a JavaScript literal representing null or an "empty"
value, i.e. no object value is present. It is one of JavaScript's
primitive values.
function getMax(arr){
return Math.max.apply(null, arr);
}
Wouldn't explicitly passing the keyword this be clearer, or at least more readable? Then again, at this point I may not understand why you would use null.
Why would you pass 'null' to 'apply' or 'call'?
When there is no value you wish to specify for the this pointer inside the function and the function you're calling is not expecting a particular this value in order to function properly.
Wouldn't explicitly passing the keyword this be clearer? Or at least
more human readable. Then again at this point I may not understand why
you would use null.
In your specific case, probably the best thing to pass is the Math object:
function getMax(arr){
return Math.max.apply(Math, arr);
}
While it turns out that it doesn't matter what you pass as the first argument for Math.max.apply(...) (only because of the implementation specifics of Math.max()), passing Math sets the this pointer to the exact same thing that it would be set to when calling it normally like Math.max(1,2,3) so that is the safest option since you are best simulating a normal call to Math.max().
Why would you pass 'null' to 'apply' or 'call'?
Here are some more details... When using .call() or .apply(), null can be passed when you have no specific value that you want to set the this pointer to and you know that the function you are calling is not expecting this to have any specific value (e.g. it does not use this in its implementation).
Note: Using null with .apply() or .call() is only usually done with functions that are methods for namespace reasons only, not for object-oriented reasons. In other words, the function max() is a method on the Math object only because of namespacing reasons, not because the Math object has instance data that the method .max() needs to access.
If you were doing it this way:
function foo() {
this.multiplier = 1;
}
foo.prototype.setMultiplier = function(val) {
this.multiplier = val;
}
foo.prototype.weightNumbers = function() {
var sum = 0;
for (var i = 0; i < arguments.length; i++) {
sum += (arguments[i] * this.multiplier);
}
return sum / arguments.length;
}
var x = new foo();
x.setMultiplier(3);
var numbers = [1, 2, 3]
console.log(x.weightNumbers.apply(x, numbers));
When the method you are calling .apply() on needs to access instance data, then you MUST pass the appropriate object as the first argument so that the method has the right this pointer to do its job as expected.
Calling apply with null as the first argument is like calling the function without providing any object for the this.
What does the apply method do?
The apply() method calls a function with a given this value and
arguments provided as an array (or an array-like object).
fun.apply(thisArg, [argsArray])
thisArg
The value of this provided for the call to fun. Note that this may not
be the actual value seen by the method: if the method is a function in
non-strict mode code, null and undefined will be replaced with the
global object, and primitive values will be boxed.
Further documentation can be found here.
One case where I have found this useful is when the function I'm calling is already bound to a particular context.
Because bound functions cannot be rebound, and they will always be called with the thisArg that was passed into bind, there is no use in passing a thisArg into call or apply. From source:
The bind() function creates a new bound function (BF).... When bound function is called, it calls internal method [[Call]] on [[BoundTargetFunction]], with following arguments Call(boundThis, args).
Here's an example:
class C {
constructor() {
this.a = 1;
}
}
function f(n, m) {
console.log(this.a + n + m);
}
let c = new C();
var boundF = f.bind(c, 2); // the context `c` is now bound to f
boundF.apply(null, [3]); // no reason to supply any context, since we know it's going to be `c`
I am bit late to answer this. I will try to give a long descriptive explanation here.
What is null in JavaScript?
The value null is a literal (not a property of the global object like undefined can be). It is one of JavaScript's primitive values.
In APIs, null is often retrieved in place where an object can be expected but no object is relevant.
fun.apply(thisArg, [argsArray])
thisArg: The value of this provided for the call to fun. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed.
argsArray: An array-like object, specifying the arguments with which fun should be called, or null or undefined if no arguments should be provided to the function. Starting with ECMAScript 5 these arguments can be a generic array-like object instead of an array. See below for browser compatibility information.
If you are using 'strict mode', then it is advisable to pass the this or
Math as the parameter.
Apply is useful when you want to pass along the responsibility for doing something to a function that is determined at run time, and pass a variable number of arguments to that function. You may or may not have any appropriate "this" context when you're doing that.
For example I use a library I wrote to facilitate listening for and raising application events that uses apply.
I wanted to be able to be able to raise an event like this:
EventManager.raise('some:event-name', arg1, arg2, arg3, ..);
..and have all of the registered handlers for that event get called with that list of arguments (arg1, arg2, etc). So in the raise function, it goes through the handlers that are registered for that event name and calls them, passing all the passed in arguments except for the event name, like this:
var args = [];
Array.prototype.push.apply(args, arguments);
args.shift();
for (var l in listeners) {
var listener = listeners[l];
listener.callback.apply(listener.context, args);
}
When a registered handler (listener.callback) is called, apply is used to pass along a variable number of arguments. Here I have allowed the listener to supply a this context for its event handler when the listener is defined, but that context might not be defined or it might be null, and that's perfectly fine.
For a long time the raise function didn't even facilitate using any callback context. I eventually came across a need for it, so I put in support for it, but most of the time I don't really need or use it.
This question already has answers here:
Applying a Function to Null in Javascript
(5 answers)
Closed 7 years ago.
I am learning about call and apply in javaScript from a online TUT. This function allows more arguments to be passed, rather than having a fixed amount.
var calculate = function(){
var fn = Array.prototype.pop.apply(arguments);
return fn.apply(null, arguments);
};
What I am having difficulty wrapping my head around is this statement.
var fn = Array.prototype.pop.apply(arguments);
The presenter of the of the TUT, explained it as the following:
We are binding the apply method onto the arguments object. This is going to give us the Function Object and assign it to the fn variable. It will also remove the Function Object from the argumentsObject. Because the Array's pop method takes the final element in the array, it removes it from the Array and then assigns to what ever called the method. In this case the fn variable.
What confused me was the following:
We are binding the apply method onto the arguments object. This is
going to give us the Function Object
It will also remove the Function Object from the arguments
Object.
And when we write in the return statement:
return fn.apply(null, arguments);
Why are we including null?
Array.prototype.pop.apply(arguments);
When you have a function, there's automatically an arguments objects, which is an Array-like object of arguments. If you call this fake function:
someFunction('hello', 'world');
and someFunction looks like this:
function someFunction() {
console.log(arguments);
}
The console.log will output ['hello', 'world']. However, don't be confused... That is not an Array object! It is an "array-like" object. Therefore, you can't say arguments.pop()... because arguments doesn't have that method (it belongs to Array.prototype). However, normal Array objects do have access to Array.prototype (e.g. [1,2,3].pop() // => [1,2]).
When you say .apply(), the first argument is the context... It sets the this. So really, Array.prototype.pop.apply(arguments) is a clever way of mimicking arguments.pop(). But you can't do arguments.pop(), because it doesn't have a pop method.
In return fn.apply(null, arguments);, null is the first arguments because we don't need to set a new context for this example. arguments is the second arguments because it's being passed in to use with fn.
.apply() returns a function object, so it returns something like this:
function() { ... }
We can then later invoke that function.
By the way, .pop() mutates the original object (in this case, the array-like object arguments). So you're passing in arguments to fn, but it's missing the last item that was in it previously.
According to MDN:
Syntax
fun.apply(thisArg, [argsArray])
Parameters
thisArg:
The value of this provided for the call to fun. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed.
argsArray:
An array-like object, specifying the arguments with which fun should be called, or null or undefined if no arguments should be provided to the function. Starting with ECMAScript 5 these arguments can be a generic array-like object instead of an array. See below for browser compatibility information.
The last argument passed to calculate is assumed to be a function. It is popped from the arguments list. (Using apply because arguments is not a real array.)
This popped function (fn) is called with the rest of the arguments list. (All other arguments passed to calculate). The arguments list no longer contains fn because pop() modifies the original object.
NULL is used because fn is called without a value for this. (See MDN)
If you call calculate for instance like
calculate(2, 3, function(a, b){ return a + b });
it will return 5.
I come across this code in jsGarden, and I cannot figure the meaning to chain call and apply together. Both will execute the function with a given context object, why it could be chained?
function Foo() {}
Foo.prototype.method = function(a, b, c) {
console.log(this, a, b, c);
};
// Create an unbound version of "method"
// It takes the parameters: this, arg1, arg2...argN
Foo.method = function() {
// Result: Foo.prototype.method.call(this, arg1, arg2... argN)
Function.call.apply(Foo.prototype.method, arguments);
};
It's making a call to call via apply; that is, it's using call to call a function ("method"), and it's using apply to make the call because it's got the arguments in the form of an (almost) array.
So to take it apart:
Function.call
That's a reference to the call() function available on all Function instances, inherited from the Function prototype.
Function.call.apply
That's a reference, via the reference to the call function, to apply. Because apply is referenced via the call object, when the call to apply is made the this value will be a reference to the call function.
Function.call.apply(Foo.prototype.method, arguments);
So we're invoking the call function via apply, and passing Foo.prototype.method to be the this value, and the arguments to "Foo.mmethod" as the arguments.
I think it's basically the same effect as this:
Foo.method = function() {
var obj = arguments[0], args = [].slice.call(arguments, 1);
Foo.prototype.method.apply(obj, args);
}
but I'll have to try it to make sure. edit Yes that seems to be it. So I can summarize the point of that trick as being a way to invoke apply() when the desired this value is the first element of the array holding the parameters. In other words, usually when you call apply() you've got the desired this object reference, and you've got the parameters (in an array). Here, however, since the idea is that you pass in the desired this as a parameter, then it needs to be separated out in order for a call to apply to be made. Personally I would do it as in my "translation" because it's a little less mind-bending (to me), but I suppose one could get used to it. Not a common situation, in my experience.
I think the code should be like this:
function Foo() {}
Foo.prototype.method = function(a, b, c) {
console.log(this, a, b, c);
};
Foo.method = function() {
//Notice this line:
Function.apply.call(Foo.prototype.method, this, arguments);
};
then
Foo.method(1,2,3) => function Foo() {} 1 2 3
Other examples:
Function.apply.call(Array,this,[1,2]) => [1, 2]
Function.call.apply(Array,this,[1,2]) => [window]
Function.call.call(Array,this,[1,2]) => [[1, 2]]
apply does take an array as the second argument, call takes single parameters.
// lets take call,
var callfn = Function.prototype.call;
// an ordinary function from elsewhere
var method = Foo.prototype.method;
// and apply the arguments object on it:
callfn.apply(method, arguments);
So, the first arguments item will be the this value of the method, and the subsequent will fill the single parameters.
The result is a static function method on the Foo constructor, that takes a Foo instance (or something similiar) as the first argument and applies the prototype method on it. A possible usecase were to define a Object.hasOwnProperty function, which is normally only available as Object.prototype.hasOwnProperty.
Ultimately, it makes the invocation of the method one "prototype" and one "call" shorter if you need to apply it on objects that a) don't inherit it or b) overwrite it.
Person.prototype.fullname = function(joiner, options) {
options = options || { order: "western" };
var first = options.order === "western" ? this.first : this.last;
var last = options.order === "western" ? this.last : this.first;
return first + (joiner || " ") + last;
};
// Create an unbound version of "fullname", usable on any object with 'first'
// and 'last' properties passed as the first argument. This wrapper will
// not need to change if fullname changes in number or order of arguments.
Person.fullname = function() {
// Result: Person.prototype.fullname.call(this, joiner, ..., argN);
return Function.call.apply(Person.prototype.fullname, arguments);
};
The Code from Javascript Garden.
Notice that it state the Function.call.apply(Person.prototype.fullname, arguments);
will become this:
Person.prototype.fullname.call(this, joiner, ..., argN);
It means the apply() function will be excuated first, then the call() function will be execuated.
Pattern: The right most call() / apply() will get execuated first
So right most apply() will get executed first
The context of the apply() becomes the caller of the call() function, so now Person.prototype,fullname.call()
apply()can only take one single array of parameters, so apply() provides arguments to the call() function, so now Person.prototype,fullname.call(arguments)
Examples from #foxiris
1st One:
Function.apply.call(Array,this,[1,2])
The right most call() will get execuated first
The context of call() becomes the caller of the apply() ,so now Array.apply()
The call() can accept multiple arugments, so it can provide this and [1, 2] to apply(), so now Array.apply(this, [1, 2]);, which will output [1, 2]
2nd One:
Function.call.apply(Array,this,[1,2])
The right most apply() will get execuated first
The context of apply() becomes the caller of the call() ,so nowArray.call()
The apply() can only take one single array parameter, so it can only provide this to call(), so now Array.call(this);, output is [].
3rd One:
Function.call.call(Array,this,[1,2])
The right most call() will get execuated first
The context of call()(right most one) becomes the caller of call()(the second to the right), so now Array.call()
The call() can take multiple parameters, so it can provide this and [1, 2] to another call(), so now Array.call(this, [1, 2]);, output is [[1, 2]].
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
return function(){
return fn.apply(object,
**args.concat(Array.prototype.slice.call(arguments))**);
};
};
This function is in Prototype. Does it equal to:
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
return function(){
return fn.apply(object,**args**);
};
};
In my opinion, args.concat(Array.prototype.slice.call(arguments)) == args, since the anonymous
function haven't any arguments. What is the matter?
No, they aren't the same.
Tthe purpose of concatenating the arguments is to provide a way of partially apply (or curry) the function, pre-filling arguments when bind is used and being able to add more later when the function that bind returns is used, for example:
var obj = {
fx: function() {
alert(Array.prototype.join.call(arguments, ', '));
}
};
var fx2 = obj.fx.bind(obj, 1, 2, 3);
fx2(4, 5); // Alerts "1, 2, 3, 4, 5"
As you can see in the last two lines of code, when I declare fx2, I'm passing obj as the first argument (this will ensure the context, used as the object variable on the bind implementation), then I pass the values 1,2 and 3.
Those values are stored in the args variable of the outer closure of bind, then as you see in the bind implementation, another function is returned.
That function returned in my example is fx2 after the assignment, in the last line you see I call that function, passing two additionally arguments.
Finally the returned function will call obj.fx with the two argument lists, the arguments we pre-filled when calling bind (1,2,3) and the arguments when the function actually executed (4,5).
That is why makes sense concatenating the two argument objects.
the anonymous function haven't any arguments
The anonymous function can actually have arguments (as can the bind method itself, which also doesn't declare any arguments).
Type-checking in JavaScript is non-existent: you can pass fewer arguments into a function than are declared in the function(...) signature (in which case the arguments that aren't passed get received as undefined), and you can pass more than declared, in which case the only way to read them is through the arguments array, which always contains exactly how many arguments were passed in, regardless of what's in the function signature.
It's generally considered polite to put a comment in the signature (function(x, /* y, ... */)) to indicate that more arguments will be read using the arguments array.