Chaining .bind() calls in JavaScript. Unexpected result?
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);
Chaining .bind() calls in JavaScript. Unexpected result?
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);
Re-bind function with new arguments to originally bound object
If you just want to add arguments, you can do that with bind
. It doesn't matter what you supply for the first bind
argument, because it will be ignored when you call bind
on a bound function (so null
is a reasonable choice for that first argument).
Because the first argument is ignored, your original code would work unchanged, but it would be misleading (boundFunc
has no owner
property, so boundFunc.owner
yields undefined
). But better to use null
or something else to avoid misreading people reading the code later.
Only change is formatting and a missing ;
, plus the ***
line:
// Object definitionfunction OriginalOwner(prop) { this.prop = prop;}OriginalOwner.prototype.aFunc = function(arg) { return this.prop + arg;};
// Object instancevar owner = new OriginalOwner("Returned ");
// Example function bindingsvar boundFunc = owner.aFunc.bind(owner);var reboundFunc = boundFunc.bind(null, "myArgument"); // ***
// Calling rebound functionconsole.log(reboundFunc()); // outputs "Returned myArgument"
Javascript Rebinding An Bound Founction
When you use bind
, you get a new function which is some kind of proxy: when you call that proxy, it ignores the passed this
and calls the original function with the bound this
.
Therefore:
hi
does something withthis
marcie
ignores itsthis
and callshi
with{_hi: 'Sir'}
as thethis
patty
ignores itsthis
and callsmarcie
with{_hi: 'Lucille'}
as thethis
However, since marcie
ignores this
, patty
is useless.
Instead, you should use bind
always on the original function:
function hi() {
return 'Hello ' + this._hi;
}
var marcie = hi.bind({_hi: 'Sir'}),
patty = hi.bind({_hi: 'Lucille'});
[marcie(), patty()]; // ["Hello Sir", "Hello Lucille"]
hi
does something withthis
marcie
ignores itsthis
and callshi
with{_hi: 'Sir'}
as thethis
patty
ignores itsthis
and callshi
with{_hi: 'Lucille'}
as thethis
Does bind create a new copy of the function?
Does
bind
create a new copy of the [underlying] function?
No, it doesn't.
It creates a new function which, when called, invokes the underlying function.
For all practical purposes, bind
is:
function bind(fn, thisArg) {
return function() {
return fn.apply(thisArg, arguments);
};
}
As you can see, in no way, shape or form is fn
being copied.
In the case of:
var incObj = inc.bind(obj);
Will now the
inc
function be copied in the memory with the given bound value or a single function is stored in the memory and only a reference to the bound value is saved? Where is that reference saved?
The latter, however it would be more accurate to say "a single new function is stored in memory and within it only a reference to the function on which bind
was called".
In other words, inc
remains exactly as it was. A new bound function object incObj
is created which points internally to inc
. In the example above, inc
is "stored" by virtue of being closed over by the internal anonymous function. In actuality, inc
, meaning a reference to it, is stored within the engine's internal bound function object.
I want to know if a memory leak could be created making bind calls. (actually I care about garbage collection, not about memory leaks)
Merely creating objects is not a "memory leak"; it is only a memory leak if the object will never be GC'd. Simply creating a bound function would never cause a memory leak, because when the bound function goes out of scope, so will the underlying function, so it will be GC'd eventually. If you're actually worried not about memory leaks but about creating objects, which will require more GC and potentially cause GC jag, that is the case for any object you create, not just bound functions.
Can you rebind a rebound function using `bind`
This may not directly answer the question about getting a officially documented specification validating this behavior, but we can base our conclusions on the source code provided in MDN, specifically in the documentation for Function.prototype.bind(), under section Polyfill, where they provide an example of how a polyfill bind
function would look like.
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP();
return fBound;
};
}
We can see that the oThis
parameter is used in the closure fBound
which is the one ultimately returned by the bind
function.
This means that when you invoke the bind
function, you get a closure function in return which, when invoked, accesses the oThis
free variable provided as parameter in the original invocation of bind
.
As such, it doesn't matter how many more times you rebind the bound fBound
function, this function is already bound forever to the original context oThis
within its closure.
The MDN documentation also points to Raynos bind implementation for further reference, which seems to correspond to this example as well.
understanding MDN's example of bind
In this.declare.bind(this)
, the first this
is used to get the current object's declare
method. Then we use bind
to create a new function that will call that method with a specific this
value, and that's the argument to bind()
. It just so happens that we're binding it to the current object, so we use this
in both place, but we don't have to. We could write:
var another = new LateBloomer;
setTimeout(this.declare.bind(another), 1000);
This gets the current object's declare
method, and arranges to call it on another
in 1 second.
Can't implement simple optional chaining
You can use null coalescing in conjunction with optional chaining:
const updateState = item => ({
userObj.firstName = item?.firstName ?? userObj.firstName ,
userObj.lastName = item?.lastName ?? userObj.lastName ,
userObj.userID = item?.userID ?? userObj.userId ,
});
You could use the spread operator:
const updateState = item => {
userObj = { ...userObj, ...item };
return userObj;
}
Or you can use lodash's defaults()
function:
const _ = require('lodash');
const updateState = item => {
userObj = _.defaults(userObj, item)
return userObj;
}
Or... if you really want to mutate the state object, rather than creating a new one and replacing it, roll your own, similar:
const updateState = item => {
for (const key in item ) {
const hasValue = key != null && key != undefined && key != NaN ;
if ( hasValue ) {
userObj[prop] = item[prop];
}
}
}
There is, as they say, more than one way to skin a cat.
[Edited: Add explanation of the spread operator]
The spread operator,
const obj = { ...obj1, ...obj2, . . . , ...objN };
is somewhat akin to calling a function like this:
const obj = mergeObjects( obj1, obj2, . . ., objN );
where mergeObjects()
is defined as:
function mergeObjects(...objects) {
const mergedObject = {};
for (const obj of objects ) {
for (const key in obj ) {
mergedObject[key] = item[key];
}
}
return mergedObject;
}
Or perhaps a better explanation might be done using Object.assign()
. One could say that an expression like:
const obj = {
prop1: 'a' ,
prop2: 'b' ,
...obj1 ,
prop3: 'c' ,
prop4: 'd' ,
...obj2 ,
prop5: 'e' ,
prop6: 'f' ,
...obj3 ,
}
is the equivalent of this:
const obj = Object.assign( {},
{
prop1: 'a' ,
prop2: 'b' ,
},
obj1 ,
{
prop3: 'c' ,
prop4: 'd' ,
} ,
obj2 ,
{
prop5: 'e' ,
prop6: 'f' ,
} ,
obj3 ,
);
Related Topics
How to Add a Class to a Dom Element in JavaScript
Angularjs - How to Get an Ngrepeat Filtered Result Reference
Why Do Arrow Functions Not Have the Arguments Array
JavaScript Fuzzy Search That Makes Sense
Getting a Unhandledpromiserejectionwarning When Testing Using Mocha/Chai
How to Read Get Data from a Url Using JavaScript
Array Like Objects in JavaScript
!Function(){ }() VS (Function(){ })()
Microsoft Cdn for Jquery or Google Cdn
Convert MySQL Datetime Stamp into JavaScript's Date Format
JavaScript Pass Object as Reference
Recursively Print All Permutations of a String (Javascript)
How to Create a Simple Http Proxy in Node.Js
How to Replace the Entire HTML Node Using Jquery
Ajax Call and Clean JSON But Syntax Error: Missing ; Before Statement