Reference to a function call - javascript

As we can assign a function call to a new reference in javascript for example we have a function display and when we call it we use display(). And we know in javascript we can also create a reference for this for example var call = display(),So can i invoke this function using this call() reference ? below is my code it is not working it is showing outerDisplay is not a function why is this happening ?
<script>
var obj = {
name: 'Saurabh',
display: function () {
console.log(this.name); // 'this' points to obj
}
};
var outerDisplay = obj.display(); // Saurabh
outerDisplay();
</script>

outerDisplay is not a function because it already called obj.display().
However if you changed code like the below one, it won't work:
var outerDisplay = obj.display;
outerDisplay(); // it won't work!
Because it lost the "owner" of this function. You have to tell who is the owner with call.
var obj = {
name: 'Saurabh',
display: function(){
console.log(this.name); // 'this' points to obj
}
};
var outerDisplay=obj.display; // Saurabh
outerDisplay.call(obj);
MDN web docs link: Function.prototype.call()

there are 2 things which you are getting wrong.
First when you want to assign a function to a variable you should not call it.
calling a function while assigning will evaluate to the return value of your function;
so if you do
var outerDisplay = obj.display; // this will assign the function and you may call it as
outerDisplay(); // will call your assigned function
if you do
var outerDisplay = obj.display(); // this will call it immediately and assign what is returned
outerDisplay(); // throw an error as outerDisplay is not a function
Second thing you missed;
when you assign a function to another variable then calling the variable will run in a different context.
and here this point to the window object;
window.name = "Prabhat"
var outerDisplay = obj.display;
outerDisplay(); // print Prabhat

Related

Why is the value of this variable undefined

I am currently learning javascript and came across this example
var t = function()
{
this.name = "Jam";
no = "123";
}
console.log(t.no); //Undefined
var m = new t();
console.log(m.name);
Why is the first statement undefined ?
t is a function object. As any other object, the function may have properties assigned. So in order your code to work you shall assign "123" to no property of your function (line A):
var t = function()
{
this.name = "Jam";
}
t.no = "123"; // line A
console.log(t.no); // "123"
var m = new t();
console.log(m.name);
Why is the first statement undefined ?
Because t doesn't have a property no.
First of all, the code inside the function, namely
this.name = "Jam";
no = "123";
is only executed when the function is called. You are doing this with var m = new t();, which comes after console.log(t.no);.
Secondly, no = "123"; does not create a property on the function object. It will attempt to set the value of variable no. Since the variable doesn't exist in your example, that line will either create a global variable no, or throw in error if your code is in strict mode.
Consider the following example:
var no = 21;
function foo() {
no = 42;
}
console.log(no); // 21
foo();
console.log(no); // 42
Because t is a function, that would be executed by t();. no on the other hand is a global scooed variable that is reached without prefix from everywhere.
t is a function expression. You you can access a returned object of a function like t().no or you can create a new object by using the function as a constructor like this
myT = new t()
console.log(t.no);
But your no variable is just a global variable inside the function and it is not a part of what it returns nor it is not attached to the returning object of the constructor function.
Here is a very good tutorial which covers all these topics at depths.

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);
};

Is it possible to modify a function itself when its property function is called?

Basically I want to do this:
someFunction() // do something
someFunction.somePropertyFunction()
someFunction() // Now someFunction is modified; it should now exhibit a different behaviour
Is this possible?
EDIT:
I'm not looking for what #Kolink was suggesting. Basically I want to augment a function's functionality by calling one of it's property function.
Specifically, I need to: 1. have access to the original function inside my property function (which is entirely doable using this), and 2. bind a new function to the original function's name (which I'm not sure if it's possible).
Just to be clear, I don't have access to the internal definition of the function that I want to augment. I want to attach a function to Function.prototype (so that it will be available as a property of the function that I want to augment), and then I will call func.augmentThis(), and then func should be augmented. But I'm not sure how, hence the question :P
Easily. Here's an example:
var derp = 123;
someFunction = function() {alert(derp);};
someFunction.somePropertyFunction = function() {derp = 456;};
someFunction(); // alerts 123
someFunction.somePropertyFunction();
someFunction(); // alerts 456
Okay, that's an oversimplified example, but yeah, it's entirely possible.
If your question is whether a function attached as a property to another function has a way to access the function to which it is attached, the answer is no. After all, the same function could be attached to any number of functions of objects.
So one alternative is to explicitly refer to the "mother" function within the function that is attached to it and intended to change its behavior:
function f (n) { alert (n + f.offset); }
f.offset = 0;
f.change_offset = function (i) { f.offset = i; };
f (1); //1
f.change_offset (100);
f (1); //101
Here, f is hard-wired into the definition of change_offset. If this bothers you, or you want something slightly more general, write a little routine to set a function as a property on another function, while binding its this to the function being attached to:
function set_func_as_func_prop ( propname, func_to_set, func_to_set_on ) {
func_to_set_on[propname] = func_to_set.bind(func_to_set_on);
}
Now you can write the function more generally
function change_offset (i) {
this.offset = i;
}
and set it on f or any other function.
set_func_as_func_prop ("change_offset", change_offset, f);
set_func_as_func_prop ("change_offset", change_offset, g);
Sort of:
function someFunction() {
return realFunction.apply(this, arguments);
}
function someFunctionA(name) {
return 'Hello, ' + name + '!';
}
function someFunctionB(name) {
return 'Goodbye, ' + name + '...';
}
var realFunction = someFunctionA;
someFunction.somePropertyFunction = function () {
realFunction = someFunctionB;
};
Sure it's possible. It's not recommended, but it's possible. For example:
function a() {
alert("a");
}
function b() {
alert("b");
}
function c() {
return c.f.apply(this, arguments);
}
c.f = a;
c.toggle = function () {
c.f = c.f === a ? b : a;
};
Now let's test it:
c(); // alerts "a"
c.toggle();
c(); // alerts "b"
See the demo: http://jsfiddle.net/LwKM3/
I want to attach a function to Function.prototype. Then I need to bind a new function to the original function's name (which I'm not sure if it's possible).
That indeed is impossible, you don't know what refers to the function. And you cannot change the internal representation of a function, which is immutable.
The only thing you can do is to create a new function and return that, to let the caller of your method use it somehow - specifically assigning it to the original variable:
somefunction = somefunction.augmentSomehow();
Your method for that will look like this:
Function.prototype.augmentSomehow = function() {
var origFn = this;
return function() {
// in here, do something special
// which might include invoking origFn() in a different way
};
};
Not sure if this helps, but I would implement described problem in following way:
// defined by somebody else - unknown to developer
var someFunction = function() {
alert("this is initial behavior");
}
someFunction(); // returns "this is initial behavior"
// defines parent object on which someFunction() is called
var parentObject = this; // returns window object (as called direclty in the
// browser)
// if you are calling someFunction from some object (object.someFunction())
// it would be:
// var parentObject = object;
// augumentThis definition
someFunction.augumentThis = function() {
var newFunction = function() {
alert("this is changed behavior");
};
parentObject.someFunction.somePropertyFunction = function() {
parentObject.someFunction = newFunction;
parentObject.someFunction();
};
};
someFunction.augumentThis(); // change function behavior
someFunction(); // "this is initial behavior"
someFunction.somePropertyFunction(); // "this is changed behavior"
someFunction(); // "this is changed behavior"

Categories

Resources