Chaining .Bind() Calls in JavaScript. Unexpected Result

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 with this
  • marcie ignores its this and calls hi with {_hi: 'Sir'} as the this
  • patty ignores its this and calls marcie with {_hi: 'Lucille'} as the this

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 with this
  • marcie ignores its this and calls hi with {_hi: 'Sir'} as the this
  • patty ignores its this and calls hi with {_hi: 'Lucille'} as the this

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



Leave a reply



Submit