Bind more arguments of an already bound function in Javascript
I try to sort my thoughts about how javascript's bind() works.
I see that if I do
var f = function (a) { ... }
var g = f.bind(obj);
g(1)
then f is called with obj
as this
and 1
as a
.
What I thought is g is a wrapper function around f.
But when I do
var f = function (a) { ... }
var g = f.bind(obj);
g.call(1)
then f is called with 1
as this
and a
undefined.
So it seems g is not just a simple wrapper, but call
somehow differentiates between normal and bound functions.
One more thing is I cannot partially apply a function more times.
var f = function (a) { ... }
var g = f.bind(obj);
var h = g.bind(1);
h();
Then f is called with obj
as this
and a
undefined.
What is the cause of this behavior?
Edit
The constructs in this question are actually wrong, see the accepted answer on what they should look like (in general I haven't noticed that call
and bind
do always need the context argument as the first argument).
Once you bound an object to a function with bind
, you cannot override it. It's clearly written in the specs, as you can see in MDN documentation:
"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."
That means, also if you do:
g.call(1);
You will get obj
as this
, and not 1
– on the browsers that follows the specs.
You can of course binds multiple arguments, so:
var sum = function(a, b, c) { return a + b + c };
var sumAB = sum.bind(null, 1, 5);
var sumC = sumAB.bind(null, 2);
console.log(sumC());
But the context object will be always the one specified with the first bind
, because it cannot be overwritten.
Just to avoid confusion, the first argument of call is the context object (this
), then you will have the rest of the argument.
It means:
var obj = { foo: function(bar) { console.log(bar) } };
obj.foo('hello');
// equivalent to:
var foo = obj.foo;
foo.call(obj, 'hello');
Hope it helps.
You never passed any arguments — you only ever set context. call
's first argument is received as context (this
), and arguments 2 onwards are received as the called function's arguments 1 and onwards. Meanwhile, bind
creates a new function with a new context — arguments are passed when it's invoked.
Here are ways of passing 1
as function f
's argument a
following on from your first code block:
f( 1 );
g( 1 );
g.call( this, 1 );
g.apply( this, [ 1 ] );
Function.prototype.call()
With the call() method, you can write a method that can be used on different objects. In other words with call(), an object can use a method belonging to another object. More information
const person = {
fullName: function() {
return this.firstName + " " + this.lastName;
}
}
const person1 = {
firstName:"John",
lastName: "Doe"
}
const person2 = {
firstName:"Mary",
lastName: "Doe"
}
// This will return "John Doe":
console.log(person.fullName.call(person1));
The call() allows for a function/method belonging to one object to be assigned and called for a different object.
call() provides a new value of this to the function/method. 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.
> Using call to chain constructors for an object
You can use call to chain constructors for an object (similar to Java).
In the following example, the constructor for the Product object is defined with two parameters: name and price.
Two other functions, Food and Toy, invoke Product, passing this, name, and price. Product initializes the properties name and price, both specialized functions define the category.
function Product(name, price) {
this.name = name;
this.price = price;
}
function Food(name, price) {
Product.call(this, name, price);
this.category = 'food';
}
function Toy(name, price) {
Product.call(this, name, price);
this.category = 'toy';
}
const cheese = new Food('feta', 5);
const fun = new Toy('robot', 40);
console.log(cheese);
console.log(fun);
> Using call to invoke an anonymous function
In this example, we create an anonymous function and use call to invoke it on every object in an array.
The main purpose of the anonymous function here is to add a print function to every object, which is able to print the correct index of the object in the array.
const animals = [
{ species: 'Lion', name: 'King' },
{ species: 'Whale', name: 'Fail' }
];
for (let i = 0; i < animals.length; i++) {
(function(i) {
this.print = function() {
console.log('#' + i + ' ' + this.species
+ ': ' + this.name);
}
this.print();
}).call(animals[i], i);
}
> Using call to invoke a function and specifying the context for 'this'
In the example below, when we call greet, the value of this will be bound to object obj.
function greet() {
const reply = [this.animal, 'typically sleep between', this.sleepDuration].join(' ');
console.log(reply);
}
const obj = {
animal: 'cats', sleepDuration: '12 and 16 hours'
};
greet.call(obj); // cats typically sleep between 12 and 16 hours
> Using call to invoke a function and without specifying the first argument
In the example below, we invoke the display function without passing the first argument. If the first argument is not passed, the value of this is bound to the global object.
var sData = 'Wisen';
function display() {
console.log('sData value is %s ', this.sData);
}
display.call(); // sData value is Wisen
Note: In strict mode, the value of this will be undefined. See below.
'use strict';
var sData = 'Wisen';
function display() {
console.log('sData value is %s ', this.sData);
}
display.call(); // Cannot read the property of 'sData' of undefined
For more read you can visit reference
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.
const module = {
x: 42,
getX: function() {
return this.x;
}
};
const unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined
const boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42
> Creating a bound function
The simplest use of bind() is to make a function that, no matter how it is called, is called with a particular this value.
A common mistake for new JavaScript programmers is to extract a method from an object, then to later call that function and expect it to use the original object as its this (e.g., by using the method in callback-based code).
Without special care, however, the original object is usually lost. Creating a bound function from the function, using the original object, neatly solves this problem:
this.x = 9; // 'this' refers to global 'window' object here in a browser
const module = {
x: 81,
getX: function() { return this.x; }
};
module.getX();
// returns 81
const retrieveX = module.getX;
retrieveX();
// returns 9; the function gets invoked at the global scope
// Create a new function with 'this' bound to module
// New programmers might confuse the
// global variable 'x' with module's property 'x'
const boundGetX = retrieveX.bind(module);
console.log(boundGetX());
// returns 81
> Partially applied functions
The next simplest use of bind() is to make a function with pre-specified initial arguments.
These arguments (if any) follow the provided this value and are then inserted at the start of the arguments passed to the target function, followed by whatever arguments are passed to the bound function at the time it is called.
function list() {
return Array.prototype.slice.call(arguments);
}
function addArguments(arg1, arg2) {
return arg1 + arg2;
}
const list1 = list(1, 2, 3);
// [1, 2, 3]
const result1 = addArguments(1, 2);
// 3
// Create a function with a preset leading argument
const leadingThirtysevenList = list.bind(null, 37);
// Create a function with a preset first argument.
const addThirtySeven = addArguments.bind(null, 37);
const list2 = leadingThirtysevenList();
// [37]
const list3 = leadingThirtysevenList(1, 2, 3);
// [37, 1, 2, 3]
const result2 = addThirtySeven(5);
// 37 + 5 = 42
const result3 = addThirtySeven(5, 10);
// 37 + 5 = 42
// (the second argument is ignored)
> With setTimeout()
By default within setTimeout(), the this keyword will be set to the window (or global) object. When working with class methods that require this to refer to class instances, you may explicitly bind this to the callback function, in order to maintain the instance.
function LateBloomer() {
this.petalCount = Math.floor(Math.random() * 12) + 1;
}
// Declare bloom after a delay of 1 second
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 1000);
};
LateBloomer.prototype.declare = function() {
console.log(`I am a beautiful flower with ${this.petalCount} petals!`);
};
const flower = new LateBloomer();
flower.bloom();
// after 1 second, calls 'flower.declare()'
And if you want to know more about bind method, read this resource