So, I've got some code that looks like this (genericised from a closed-source project).
UserWizard = {
init: function(name) {
this.firstInit.call(name);
this.secondInit.call(name);
},
firstInit: function(name) {
// ...
},
secondInit: function(name) {
// ...
}
}
It's the first time I've ever seen the call method used in JS and it appears to be exactly the same as just calling the function with brackets, e.g.
this.firstInit(name);
So what is call doing here? Does it act any differently?
When calling
UserWizard.init('some name');
The this object of the firstInit and the secondInit functions will be the string 'some name' and the parameter name value will be undefined
UserWizard.firstInit('some name')
Is the same as:
UserWizard.firstInit.call(UserWizard, 'some name');
Hope I was clear
call() is not doing what you think it's doing here. It's actually changing the context of this within both firstInit and secondInit.
Function.prototype.call() is the link to the mozilla docs, quoting from there there:
A different this object can be assigned when calling an existing function. this refers to the current object, the calling object. 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. - by Mozilla Contributors
There's another function Function.prototype.bind() which I'd encourage you to look at. I find generally I use this more often, but it's a similar sort of idea, used to assign this to the function when it may get called later on. This is good for preventing issues like:
var person = {
name: 'First Last',
getName: function (){
return this.name;
}
};
var getName = person.getName;
getName(); // returns undefined as `this` is the global window object
Related
I have the following problem:
I'm trying to overwrite a function to apply it then with angular ($scope.$apply()), but my this-context doesn't seem to be the right one.
The original function (in another file) looks like the following:
Anno.prototype.switchTo = function(otherAnno) {
if (otherAnno != null) {
this.hideAnno();
return otherAnno.show();
} else {
console.warn("Can't switchTo a null object. Hiding instead.");
return this.hide();
}
};
And then in another file I "overwrite" it like the following:
var switchToFunction = AnnoModule.Anno.prototype.switchTo;
AnnoModule.Anno.prototype.switchTo = function(otherAnno) {
switchToFunction(otherAnno);
$scope.$apply();
};
So actually I save the original function, then redefine the original function to call the original one and then apply the scope.
Now comes the problem: As you can see, the function uses this.hideAnno() in it, but in my redefined function, the context is another one, that's why chrome is throwing an error saying "this.hideAnno() is not a function". But now I'm not sure how I can get the right context. I tried to understand this, but I find JavaScript is so confusing that I really don't get it.
Can somebody help me understand that JavaScript confusion?
When a function is called as a method in js, the this inside of it refers to the object the method belongs to.
On the other hand, when a function is called on its own, this inside of it refers to the global object or undefined in strict mode.
You are extracting (and then calling) a function defined as a method into a standalone function, that's why this doesn't do what you expect it to.
What you need in this case is to call or apply your switchToFunction, setting the value of this to what you need. In other words you set the this of the old method to be the this of the new method you created:
var switchToFunction = AnnoModule.Anno.prototype.switchTo;
AnnoModule.Anno.prototype.switchTo = function(otherAnno, that) {
switchToFunction.call(this, otherAnno); // sets `this` of the old method to be this of the new method you created
$scope.$apply();
};
To understand the problem, I think first we should understand how this keyword works and how it can be tweaked.
In JavaScript the this object inside of any function will be the object on which the method is invoked.
Consider these following example,
var obj1 = {
foo: function() {
console.log(this);
}
};
function bar() {
console.log(this);
}
Now when the methods are invoked we get output like below,
obj1.foo(); // obj1
bar(); // window
Because foo method is invoked on obj1, so this inside of foo method became obj1. Similarly this inside bar method will be window object.
Now, what if I want to invoke the bar method with obj1 as this inside the function. For this JavaScript provides call, apply and bind methods to change the this of function dynamically. Let us see how we can achieve this using call method.
bar.call(obj1); // obj1
Similarly,
obj1.foo.call(window); // window
call method takes an thisArg object as argument which will be this inside the bar function. And if bar function also expects arguments that also can be passed through call method following thisArg. See MDN for information about call.
So the solution for your problem will be,
var switchToFunction = AnnoModule.Anno.prototype.switchTo;
AnnoModule.Anno.prototype.switchTo = function(otherAnno) {
switchToFunction.call(this, otherAnno);
$scope.$apply();
};
Hope this makes it clear for you to understand the problem.
I'm just wondering if I can make some of my helper functions(for stuff like benching, logging and caching) a bit neater. Right now I'm doing this
function doSomethingToMethodsObject(object, methodname) {
//do things to object and object[methodname]
}
so I can use it like this
var myObject = function() {
this.myString = "hello";
this.myMethod = function() { return this.myString; }.bind(this);
doSomethingToMethodsObject(this, "myMethod");
}
but it would be better if I could call it like this
doSomethingToMethodsObject(this.myMethod);
and then break down the function reference to this and "myMethod" inside doSomethingToMethodsObject.
So is there a way to figure out what objects a function belongs to?
So is there a way to figure out what objects a function belongs to?
A function does not belong to only a single object. There can be lots of references to that same function on lots of different objects. So, no you can't specifically figure out what object a function belongs to by purely looking at the function.
In fact, why you pass this.myMethod as an argument, there is no connection whatsoever to the this part that is passed. It just gets a reference to the function and passes that.
You can use .bind() to pass a bound reference to the function, but that only lets you call the method in the context of the right object - it doesn't allow you to separate out the object from the method. If you need both object and method separately in your function, then you will need to find some way to make them both separately accessible inside the function - the most obvious way to do that is just pass them both as arguments.
Depending upon the specific context (which you don't really share very much of), sometimes you can use parent scoped variables like var self = this; to maintain a reference to this that can be accessed by child functions, but I'm not sure that applies to what you're doing.
Here are some options:
doSomethingToMethodsObject(this, this.myMethod);
doSomethingToMethodsObject({obj: this, method: this.myMethod});
When you use this.myMethod, JS creates a reference whose base is this and whose referenced name is "myMethod".
However, when you use it as an argument, JS resolves the reference into the property value. Therefore, in doSomethingToMethodsObject, it's too late to get the base.
The only way to obtain the base of this.myMethod is when myMethod is an accessor property with a getter which redirects to the underlying object value. Then you can use this inside the getter, and store it in a property of the underlying value.
For example:
var obj = {
_myMethod: function(arg) { /* Underlying function */
return {thisArg: this, arg: arg};
},
get myMethod() { /* Stores reference parts and redirects */
return Object.assign(this._myMethod, {
refBase: this,
refName: "_myMethod"
});
}
};
function doSomethingToMethodsObject(value) {
return value.call(
value.refBase,
value === value.refBase[value.refName]
);
}
obj.myMethod(123); // {thisArg: obj, arg: 123}
doSomethingToMethodsObject(obj.myMethod); // {thisArg: obj, arg: true}
I am new to JS so bear with me.
What is the difference between
function get (attr) {
return function (object) { return object[attr]; }
}
and
function get (attr) {
return object[attr];
}
Why the first function worked but second did not? could not find a logical explanation for this.
Working Example code: https://jsfiddle.net/xf2u0ncL/
In your second code, where does object come from?! That's the entire problem.
The first code is used like this:
// same as getter = function (object) { return object['foo']; }
var getter = get('foo');
var value = getter({ foo: 'bar' });
get accepts a property name and returns a function which accepts an object and returns the value. Your second function doesn't accept any object at any point, so what's it supposed to return?
Looks like you've got a method where your trying to return a property of an object.
Both methods above will work but expect variables in different scopes and will be executed differently.
the first method returns a second method where you pass it an object to get the property from. So
get('foo')({foo: 'bar'}); //returns 'bar'
Whereas the second works like this
var object = {
foo: 'bar'
}
get('foo'); //returns 'bar';
the second excepts object to be a global variable or at least in a higher scope as it is not defined in the function it self.
the first method is passed an object to search for a property from. The first, whilst a little convoluted would be better and more easily tested than the second which expects and relies on global variables.
see fiddle: http://jsfiddle.net/b327nr74/
I am trying to understand some basic javascript .
I have the following snippet. We have the name variable which in both cases are referenced over getName Method.
First alert outputs HocusPocus, while the second one outputs GeorgeThomas. But I do not understand how this refers to name in this case
var name = 'Gerorge Thomas';
var obj = {
name: 'Cinderella',
value: {
name: 'HocusPocus',
getName: function() {
return this.name;
}
}
};
alert( obj.value.getName() );
var testing = obj.value.getName;
alert(testing());
To understand this issue, you have to understand how Javascript sets the value of this in a function call.
There's a summary of all the methods here: When you pass 'this' as an argument
For your particular case, when you do this:
var testing = obj.value.getName;
You now have a reference to the getName function. You no longer have any connection at all to obj.value. So, test() just calls getName as an ordinary function. In Javascript when an ordinary function call is made, then the value of this will be either the global object (window in a browser) or in strict mode, it will be undefined.
In your case, this becomes the window object so this.name is window.name which points to the global variable name and thus you get the result 'Gerorge Thomas'.
In fact, if you run your code in strict mode, it will cause an error which is, in fact, one of the benefits of strict mode that it points out accidental errors like this.
On the other hand, if you execute a function with the form of obj.method(), then this is set to be obj. So, when you do:
obj.value.getName()
that is equivalent to:
var o = obj.value;
o.getName()
which is the obj.method() form of calling a function which will set the this pointer to be the object, which in this case is obj.value.
It is possible to work around this issue in a couple of ways. Here's an example of working around it using .bind().
var name = 'Gerorge Thomas';
var obj = {
name: 'Cinderella',
value: {
name: 'HocusPocus',
getName: function() {
return this.name;
}
}
};
document.write( obj.value.getName() + "<br>");
var testing = obj.value.getName.bind(obj.value);
document.write(testing());
Well it only needs some reflection, let's analyse it:
In the first alert we are calling the getName() method of the
obj.value object so it returns obj.value.name which is
"HocusPocus".
alert( obj.value.getName() ); we call this.name where this refers to obj.value so name is HocusPocus
But in the second alert we are creating a new function testing()
with the body of the getName() method, so it will be attached to
the global window object and if we call it we will get the global
name value wich is 'Gerorge Thomas'.
alert(testing()); we are dealing with this.name where this refers to the global scope so name is 'Cinderella'.
First, you're calling a method from inside the obj object. The 2nd time, you're making a copy of the function (not really, it's just a reference) in the global scope. So you could also write window.testing() for the 2nd call. I think that makes it clear.
I'm using the following JavaScript code:
var emp= new Object();
emp["name"]="pooja";
emp["salary"]=725;
emp["paycheck"]=function()
{
var monthly=this["salary"]/12;
alert(this["name"]+":"+monthly);
};
emp["paycheck"](); --work properly
document.write("<br/>");
var f=emp["paycheck"]; --dosen't work
f();
f() have to get reference on emp["paycheck"] function and display a suitable answer.
but insted i get NaN.
As i understood f() dosen't see the property of emp object("name" and "salary").
My question is why f() dosen't see the properties?
You refer to salary as this["salary"]. If you store the function and call it later, the this value is lost. It is only bound to the object if you directly call it, as in emp.paycheck(). You could pass the this value explicitly, though:
f.call(emp);
But you might rather want to refer to salary in the function as emp["salary"], since that will always work.
Note that instead of foo["bar"] you can use foo.bar, and that the new Object() part can just be:
var emp = {
name: "pooja",
salary: 725,
paycheck: function() {
...
}
};
The reason why is you are calling the function without a this parameter. You need to use apply to pass it an explicit this on which it can call the name and salary properties.
f.apply(emp);
you reference on a function, not on all object.
f = emp;
f.paycheck();
You didn't copy those properties to f. There are, unfortunately, no native ways to do this in JavaScript. See How do I correctly clone a JavaScript object? or What is the most efficient way to deep clone an object in JavaScript?, look at jQuery's extend(), or google "javascript deep copy object" or "javascript clone object".
Others have explained what's going on and how you don't pass this.
However, if you want to do something like this (a custom from Python perhaps?), you can make a helper function:
function makeDelegate(obj, fun) {
return function() {
fun.apply(obj, arguments);
};
}
Then use it like this:
var f = makeDelegate(emp, emp["paycheck"]);
f();