Hard binding in javascript - javascript

Can anyone explain me on how hard binding works in javascript?.
function foo() {
console.log(this.a);
}
var obj = {
a: 2
};
var bar = function() {
foo.call(obj);
};
bar(); // 2
setTimeout(bar, 100); //
I am more interested in this function.
var bar = function() {
foo.call(obj);
};
Why do we wrap foo.call(obj) inside another function?. We can use it directly right?.
setTimeout(foo.call(obj), 100); // still results in 2.

The .call (and .apply) methods simply let you manually set the value of this in the function being invoked.
So when you do foo.call(obj), the value of this in foo will be obj.
As to the setTimeout, what you're doing is calling it immediately instead of waiting for 100ms. Change it to 10000, and you'll see more clearly it doesn't wait.
So that's why the function is needed. You need to pass a function to setTimeout, and it will get invoked after the duration you provide.
There's also the .bind() method that creates a new function with its this value permanently bound to the first argument you provide. So that would actually be a hard binding example
setTimeout(foo.bind(obj), 100);
So in that example, a function is returned that will always have obj set as the this value. So now that setTimeout is being passed a function, it will be invoked after the given duration.
You can also bind arguments to the function. All arguments passed to .bind() after the first argument will be permanently bound to the returned function so that any arguments passed to that function will be placed after the bound ones.

You don't need setTimeout to achieve hard binding.
function foo() {
console.log(this.bar);
}
var obj1 = {bar:10};
var obj2 = {bar:5};
var originalFoo = foo;
OriginalFoo now has the reference to foo
Now override foo function and use originalFoo.call to set the this context to always be that of obj1
foo = function() {
originalFoo.call(obj1);
}
foo(); // returns 10
foo.call(obj2); //returns 10 even when obj2 passed as arg

Related

Does .bind(this) pass by reference or by value?

I create a function somewhere and I bind it to this so that I can use the parent block's meaning of this as the value of this within the function. For example:
var foo = function() {
// some stuff involving other stuff
}.bind(this);
Is the this I pass as an argument to bind passed by reference, or by value? So if I change the parameters of the this object a bit later in the outer block of code, and afterwards call foo, will foo use the value of this at the time I called bind, or at the time I called foo?
So if I change the parameters of the this object a bit later in the
outer block of code, and afterwards call foo, will foo use the value
of this at the time I called bind, or at the time I called foo?
at the time you called foo.
this is a reference to Object. That means Object may get mutated at some point and you will get "fresh - up to date" values of it.
If you will change the value of this object, then foo will get the fresh value of this at the time foo is called.
var module = {
x: 42,
getX: function () {
return this.x;
}
}
var retrieveX = module.getX;
console.log(retrieveX()); // The function gets invoked at the global scope
// expected output: undefined
var boundGetX = retrieveX.bind(module);
module.x = 52;
console.log(boundGetX());
Sorry, I don't have enough reputation to comment.
If it's by reference then why are the outputs of both the blocks same here:
var module = {
x: 5,
b:{
a:5,
getX: function () {
console.log(this.a)
return "hello world";
}
}
}
const boundGetX = module.b.getX.bind(module.b);
console.log(boundGetX());
module.b={
a:45678,
getX: function () {
return "hello world2";
}
}
console.log(boundGetX());

Context of function after used with Meteor.bindEnvironment?

I have problem with this code recently:
function doSth() {
console.log(this);
}
const fWithMeteorEnv = Meteor.bindEnvironment(doSth);
fWithMeteorEnv.call({}); // expect to see a plain object in console
What I expect is to see a plain object in console but not, it is something else. It seems that Meteor.bindEnvironment prevents the returned function to be called with another context. Are there any way to get around this?
I think that what you're trying to achieve is not possible, i.e. you will need to bind your context at the moment you're calling Meteor.bindEnvironment. You can do it with .bind(), e.g.
const fWithMeteorEnv = Meteor.bindEnvironment(doSth.bind(context));
or you can pass the context as the third argument to Meteor.bindEnvironemnt(), e.g.
const fWithMeteorEnv = Meteor.bindEnvironment(doSth, null, context);
The second argument is an exception callback.
Meteor.bindEnvironment(func, onException, _this) accepts 3 arguments and the function it returns is bound to the third argument. You need to bind it at the time it is created and using apply or call on it will pass the arguments, but the this reference will be overridden.
function doSth() {
console.log(this.foo);
}
const fWithMeteorEnv = Meteor.bindEnvironment(doSth, null, {foo: 'foo'});
fWithMeteorEnv.call({foo: 'bar'}); // will print 'foo'
This is rather similar to what you should expect with Function.prototype.bind. You should not expect to call a bound function and have its this context to be your argument.
let f = function() {
console.log(this);
}.bind({foo:'foo'});
f.call({foo: 'bar'}); // will log `{foo: 'foo'}`.
If you really need to set the this context for some function, you can wrap it and pass it as a parameter to the wrapper function instead (e.g, use the first argument to the wrapper as the this context of the original function).
If you need to have both the call semantics of the returned function, this can be done in a rather convoluted way.
/**
* Wraps the function.
* When the returned function is called, it sets the wrapped function's `this` to its first
* argument and passes it the rest of its arguments.
*/
function _wrapToMakeCallable(fn) {
return function() {
var _this = Array.prototype.shift.apply(arguments);
fn.apply(_this, arguments);
}
}
/**
* This function wraps the boundWithEnvironment function and maps the arguments such
* that it can be `call`ed or `apply`-ed as normal, using `wrapper`.
*/
function callableWrapAsync(fn) {
const bound = Meteor.bindEnvironment(_wrapToMakeCallable(fn));
return function() {
Array.prototype.splice.call(arguments, 0, 0, this);
bound.apply(this, arguments);
}
}
function doSth() {
console.log(this.foo);
}
Meteor.startup(function() {
const fWithMeteorEnv = Meteor.bindEnvironment(doSth, null, {foo: 'foo'});
fWithMeteorEnv.call({foo:'bar'}); // will print 'foo'
const callable = callableWrapAsync(doSth);
callable.call({foo:'bar'}); // will print 'bar'
});

binding an instance method vs wrapping in an anonymous function

[This is related to Bound function instead of closure to inject extra arguments, but that was neither clearly asked nor answered.]
I'm calling a function that expects a function as its argument. I want to pass a method from my class, bound to an instance of my class. To make it clear, assume my class looks like:
var MyClass = function() {}
MyClass.prototype.myMethod = function() { ... }
var my_instance = new MyClass();
Is there any substantive difference between using bind:
doSomething(my_instance.myMethod.bind(my_instance))
and wrapping the call in an anonymous function:
doSomething(function() { my_instance.myMethod(); })
?
If a prototype within your class needs to generate a callback, it may not know its instance's name. As a result, you'll need to use this, but the value of this depends on where the callback was executed.
Consider the following example:
var MyClass = function (x) { this.something = x; };
MyClass.prototype.makeCall = function () {
var myBadCallback = function() { console.log(this.something); };
var myGoodCallback = function() { console.log(this.something); }.bind(this);
// When called, the value of "this" points to... we don't know
callMeBack( myBadCallback );
// When called, the value of "this" points to this instance
callMeBack( myGoodCallback );
};
function callMeBack( callback ) { callback(); };
var foo = new MyClass('Hello World!');
var bar = new MyClass('Goodbye!');
// Probably prints "undefined", then prints "Hello World!"
foo.makeCall();
// Probably prints "undefined", then prints "Goodbye!"
bar.makeCall();
In the above example, the first output probably prints undefined because the context (what this refers to) has changed by the time the callback has executed.
This example may seem contrived, but these sort of situations do arise, a common case being AJAX callbacks.

not able to change a value assigned to this object using .bind

I'm using angular to develop a web app and I'm trying to use a function's .bind method to assign the this value on a method of one of my controllers. It looks like this:
var welcomeCtrl = function (userManager) {
this.partnerName = userManager.getName('partner');
this.yourName = userManager.getName('you');
this.beginScan = false;
var self = this;
};
welcomeCtrl.prototype.scanThumb = function (callback) {
function doScan() {
alert(this);
alert(this.beginScan);
this.finishedScanning = callback;
this.beginScan = true;
}
doScan.bind(welcomeCtrl)();
};
So what happens is that a directive passes the scanThumb method to a service which executes it, which should then trigger another directive waiting for this.beginScan to be true.
Since its the service that calls the method and it is not called from the welcomCtrl class, I need to bind this back to welcomeCtrl so I use .bind and pass in welcomeCtrl
This should work, when I do alert(this) the welcomeCtrl function definition alerts fine, but when I do alert(this.beginScan) I get Undefined
Am I not understanding how the .bind method works in this circumstance?
Whenever you are using an object's inner function (in this case, welcomeCtrl) this refers to the current object.
Take the following example:
var Foo = function(){
this.thing = 'bar';
}
Foo.prototype.setThing = function(newthing){
//our inner function
function doInnerThing(){
//this is now bound to our current Foo instance
console.log(this);
//setting our current Foo.thing to new value
this.thing = newthing;
};
//fire function, bound to current object scope (Foo)
doInnerThing.bind(this)();
};
Foo.prototype.doThing = function(){
alert(this.thing);
};
var newFoo = new Foo();
var newFoo2 = new Foo();
newFoo.setThing('newFoo');
newFoo.doThing(); //alerts 'newFoo', the overridden value
newFoo2.doThing();//alerts 'bar', the base value
As #Jesse Kernaghan suggested I was simply was passing the uninitiated constructor as the the thisParam. I fixed this by modifying my service to take 2 parameters, a callback, and a thisParam. I had then had to pass in the scope as the thisParam from my directive, and in my service call the callback with a .bind(thisParam) and now everything works.

Get right object in callback

I have target object
function Foo() {
this.someVar = 'some var';
};
Foo.prototype.callback() {
console.log(this);
};
And object, that will call this callback
function Bar(callback) {
this.callback = callback;
};
Bar.prototype.onSomeAction = function() {
this.callback();
};
And initial code
foo = new Foo();
bar = new Bar();
bar.callback = foo.callback;
bar.onSomeAction();
Result: i have logged to console Bar()'s context instead of Foo().
How can i get context of Foo() in the Foo() callback?
PS: I tried closures
Foo.prototype.callback() {
var foo = this;
return function(foo) {
console.log(foo);
};
};
but it does nothing. I have not fully understanding of the closures :(
The reason your original code didn't work is that the value of this inside of a method call is the value of the object it's being called on. That means when you say:
bar.callback = foo.callback;
And then you call:
bar.callback();
The code defined here:
Foo.prototype.callback = function () {
console.log(this);
};
gets called with this being a reference to bar because bar is to the left of the . on the method call. So whenever you assign a function as an object property, calling it on that object will call it with the object as this.
You could also have written:
function callback() {
console.log(this);
}
bar.callback = callback;
bar.callback();
And you would find that this still references bar.
In fact, if you call the plain function callback(); as defined above, you'll find that this is a reference to the global object, usually window in web browsers. That's because all global variables and functions are properties of window, so callback(); is implicitly window.callback();
The fact that the value of this depends on what object is calling a function can be a problem when passing callbacks around, since sometimes you want this to reference the original object the function was a property of. The bind method was design to solve this problem, and Yuri Sulyma gave the right answer:
bar.callback = foo.callback.bind(foo);
However, the way you would do this using closures is to capture an instance of Foo within an anonymous function that calls the correct method on the correct object:
foo = new Foo();
bar = new Bar();
bar.callback = function () {
foo.callback();
};
bar.onSomeAction();
Which is essentially what bind does. In fact, we call write our own naive version of bind using a closure:
Function.prototype.bind = function (obj) {
var fn = this;
return function () {
fn.call(obj);
};
};
call let's you call a function with the value of this explicitly defined. This allows you to "set the context" the function is called in so that it's the same as calling obj.fn() when you call bar.callback(). Since when we call foo.callback.bind(foo);, obj is foo and fn is foo.callback, the result is that calling bar.callback() becomes the same as calling foo.callback().
That's where Dalorzo's answer comes from. He uses call to explicitly set the context.
There's also another function for setting the context called apply that also takes an array representing the arguments for the function as its second argument. This allows us to write a more complete version of bind by taking advantage of the special arguments variable:
Function.prototype.bind = function (obj) {
var fn = this;
return function () {
fn.apply(obj, arguments);
};
};
bar.callback = foo.callback.bind(foo);
You can polyfill Function.prototype.bind() if necessary: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
Try using these changes:
Use call to set context:
bar.onSomeAction.call(foo);
And I think your callback function needs to change to:
Foo.prototype.callback=function() {
console.log(this);
};

Categories

Resources