IIFE context issues - javascript

In the following construct:
(function(){
var x = function(){
alert('hi!');
}
var y = function(){
alert("hi again!");
}
this.show = function(){
alert("This is show function!");
}
})();
Why does this refer to window object? Should everything inside IIFE be isolated from global scope? Are x and y functions also properties of window global object?
Also, even if I use put var h = ... at the beginning:
var h = (function(){
var x = function(){
alert('hi!');
}
var y = function(){
alert("hi again!");
}
this.show = function(){
alert("This is show function!");
}
})();
this still refers to window object -- I can just call show() from the global scope! How come?

The global context (window in a browser) is the value this gets when there's no other value to use.
Your local variables are local (that is, not properties of window). They're declared inside the function with var.
The reason why adding var h = (function(){... makes no difference is because of the way you call the function. The function reference is not a property value of an object (like something.func()), and you don't invoke it with .call() or .apply(), so therefore this refers to the global (window) object. That's just the way the language is defined to act.

#Pointy is correct, but he doesn't present the whole issue - you might be interested in this related answer. The issue here is that if you aren't using the new keyword, you aren't instantiating an object, so there's no instance for this to refer to. In the absence of an instance, this refers to the window object.
In general, you don't need this within an IIFE, because you have direct access to any function or variable defined in the anonymous function's scope - show() can call x() and y() directly, so there's no need for a this reference. There may be a valid use case for instantiating an IIFE with new, but I've never come across it.

Related

Why are these "this" and "that" variables both undefined?

Why in the below code are both:
this.fadeTime
that.fadeTime
undefined, and what is the best way to save internal variables like this in a Javascript class?
var SITE = SITE || {};
SITE.initialize = function() {
var fadeTime = 100;
that = this;
$('li#linkHome').click(function() {
resetPageLinks();
$('li#linkHome').addClass('active');
resetPages();
console.log(this.fadeTime); //undefined
console.log(that.fadeTime); //undefined
$('div#pageHome').fadeIn(that.fadeTime);
});
$('li#linkInfo').click(function() {
resetPageLinks();
$('li#linkInfo').addClass('active');
resetPages();
$('div#pageInfo').fadeIn(that.fadeTime);
});
$('li#linkAbout').click(function() {
resetPageLinks();
$('li#linkAbout').addClass('active');
resetPages();
$('div#pageAbout').fadeIn(that.fadeTime);
});
function resetPageLinks() {
$('ul.nav li').removeClass('active');
}
function resetPages() {
$('div.sitePage').hide();
}
}
When your "click" handlers are invoked, this will refer to the DOM element involved. Because DOM elements generally don't have a "fadeTime" property, the value is undefined.
Your declaration of "that" is missing the var keyword. The variable is therefore global. It refers to the "SITE" object if that "initialize" function is invoked in the likely way:
SITE.initialize();
However, the variable "fadeTime" is a local variable, not a property of the "SITE" object. Therefore, referring to "fadeTime" as a property of the "SITE" object also gives you undefined.
In the "initialize" function, you could do this:
var that = this;
this.fadeTime = 100;
Then references to "fadeTime" as a property of "that" would work.
You can just call fadeTime since it is in the scope of your initialize function. this.fadeTime or that.fadetime are indeed not there because you do not assign the variable on the this scope.
As a sidenote, your fadeTime variable seems more like a constant. In JS it is common to use all caps variable names to define a constant:
var MY_CONSTANT = "some-value";
In your code, that refers to SITE, even in the event handler functions. Therefore, that.fadeTime refers to SITE.fadeTime -- but SITE.fadeTime is never defined.
Instead, you can either do:
$('div#pageHome').fadeIn(fadeTime);
in your event listeners, to refer to the fadeTime variable you declared in the lexical scope of initialize, or you can do
this.fadeTime = 100;
at the top of initialize to set SITE.fadeTime (and therefore set that.fadeTime as well).
when you mark the variable with var then it's a local variable. when you wanna create a instance variable which you can access with this you have to declare the variable with
this.fadeTime = 100;
instead of
var fadeTime = 100;

'this' in a function VS 'this' in a method

From Javascript-Garden:
Foo.method = function() {
function test() {
//this is set to the global object
}
test();
}
In order to gain access to Foo from within test, it is necessary to create a local variable inside of method that refers to Foo:
Foo.method = function() {
var that = this;
function test(){
//Use that instead of this here
}
test();
}
Could anyone explain this? As far as I understood, this refers to the global object if it's called in the global scope. But here it's called inside of a function, which is inside a method (first example). Why exactly does it refer to the global object, while the second example doesn't?
As far as I understood, this refers to the global object if it's called in the global scope.
No. this will refer to the default object if the function is called without explicit context. The scope is irrelevant. (In strict mode it will refer to undefined instead).
Why exactly does it refer to the global object
We can't tell what it refers to. The value of this is determined by how the function is called, not how it is defined.
Now you have updated the example, we can see that it is called without context, so this (in the inner function) will be the default object, which in a web browser is window (it would be undefined in strict mode).
while the second example doesn't?
In the second example, the inner function doesn't use this (which will have the same value as the previous example).
The second example uses that instead. that is defined in the scope of the outer function and is set to whatever the value of this is when that function is called.
Assuming that function is called as Foo.method() then (outer) this (and hence that) will be Foo because that is the context on which method was called.
this in a function isn't set when you define the function. It's only dynamically defined to the receiver of the function call.
If you call foo.test(), this in test will be foo.
But if you do
var f = foo.test;
f();
then this in f (which is foo.test) will be the external object (window if you're executing it at the root level).
It would be the same with
foo.test.call(window);
The second example uses a closure to place the outer functions variables on the scope chain of the inner function.
Foo.method = function() {
var that = this;
function test(){
//This function has access to the outer variables scope chain.
}
}

Javascript: Need assistance with making a factory-ish function

I need something like so
function updateRender(ClassName){
if(!(this.currentRender instanceof ClassName)){
doPreprocessing();
this.currentRender = new ClassName();
doPostProcessing();
}
}
So I would be able to call updateRender with a new render object, which can be different type.
updateRender( SolidRender );
updateRender( HollowRender );
updateRender( HollowRender ); //does nothing because currentRender is HollowRender
You've already got your answer in the comments, so this is just an FYI:
You're using this.currentRender, which - if the function is in the global scope - will refer to a variable the global scope. I.e. to the window object of the browser. And putting things in global scope is very rarely a good idea.
Technically, you should put all your code in a single namespace or even inside a function that's invoked immediately so it doesn't pollute the global scope. However, you can start by simply getting the currentRender variable out of the global scope by doing this:
var updateRender = (function () {
var currentRender = null;
return function (klass) {
doPreprocessing();
currentRender = new klass();
doPostProcessing();
};
}());
the updateRender function will still be in the global scope, but at least the currentRender variable is safely hidden inside a closure, so only updateRender can change it (aka privileged access).
As for using klass instead of Class, that's entirely up to you. Using klass is just a common way of getting around the "class is a keyword" problem in Ruby.

specifics of closures in JavaScript and anonymous functions

This code results in "!" being logged on the console.
var g = {};
(function() {
var t = this;
t.x = "x";
g.a = function() {
console.log(t.x);
};
})();
(function() {
var t = this;
t.x = "!";
g.b = function() {
console.log(t.x);
};
})();
g.a();
Do anonymous functions share a this? Am I using this wrong? I don't really understand what's going on here.
I'd like for g.a() to continue returning the value of x defined in the first anonymous function.
I'm using node.js if it makes a difference.
In the immediate functions, this refers to the global object [docs]. So in this case in both functions this indeed refers to the same element and you are overwriting x with the second call.
What object this refers to is determined by how the function is called.
If you just execute a function with funcName();, then this refers to the global object.
If the function is assigned to a property of an object, obj.funcName() , this refers to the object.
If you call the function with the new operator, new funcName();, this refers to an empty object that inherits from the functions prototype.
You can also explicitly set this by using call [docs] or apply [docs].
Instead referring to this, you could create a new object in both functions:
var t = {};
Additional note: It makes no difference whether you run the code in the browser or with node.js. The global object is part of the specification and has to be provided by the execution environment. In browsers it is the window object, I don't what it is in node.js, but it does not matter as long as it follows the specification.
Felix Kling is the right long answer. But I wanted to chime in with what I think you actually want:
var g = {};
(function() {
var x = "x";
g.a = function() {
console.log(x);
};
})();
(function() {
var x = "!";
g.b = function() {
console.log(x);
};
})();
g.a(); // "x"
g.b(); // "!"
Now g.a() and g.b() both print out x, but each function has their own separate x shared with the closure. If these vars should be private an only accessible internally to each of these functions, this is how you hide them and persist them through multiple calls.
When I look at this script in the debugger in Chrome as you've shown it, the "this" value in both anonymous functions is set to the global variable "window". That means that each anonymous function is setting the value of window.x so the last one executed wins and is the value that survives, thus window.x == "!" after the second anonymous function executes.
It's unclear to me what you expected "this" to be or what you're actually trying to accomplish with this code so I don't know what alternative to suggest. If you just want the previous state in the anonymous function to survive for the internal function, then you can just rely on local variables (which will survive in the closure) and not use the "this" reference at all. Squeegy's example shows that.

Javascript question: about variable definition

I have no idea why it didn't work if I specify a variable with 'var':
like this:
var mytool = function(){
return {
method: function(){}
}
}();
And later I use it in the same template: mytool.method. This will output mytool was not defined.
But if I define it like this:
mytool = function(){
return {
method: function(){}
}
}();
Then it works.
Javascript has function scope. A variable is in scope within the function it was declared in, which also includes any functions you may define within that function.
function () {
var x;
function () {
// x is in scope here
x = 42;
y = 'foo';
}
// x is in scope here
}
// x is out of scope here
// y is in scope here
When declaring a variable, you use the var keyword.
If you don't use the var keyword, Javascript will traverse up the scope chain, expecting to find the variable declared somewhere in a higher function. That's why the x = 42 assignment above assigns to the x that was declared with var x one level higher.
If you did not declare the variable at all before, Javascript will traverse all the way to the global object and make that variable there for you. The y variable above got attached to the global object as window.y and is therefore in scope outside the function is was declared in.
This is bad and you need to avoid it. Properly declare variables in the right scope, using var.
The code you have doesn't show enough to demonstrate the problem. var makes the variable being defined 'local' so it will only be available within the same function (javascript has function level scoping). Not using var makes it global, this is almost always not what you want. You might need to rearrange your code to fix the scope issues.
I take it that you're using this inside of some function:
function setup_mytool() {
var mytool = function(){
return {
method: function(){}
}
}();
}
This creates the variable mytool in the function's scope; when the function setup_mytool exits, the variable is destroyed.
Saying window.mytool or window.my_global_collection.mytool will leave the variable mytool intact when the function exits.
Or:
var mytool;
function setup_mytool() {
mytool = function(){
return {
method: function(){}
}
}();
}
will also do what I think it is you're intending.
The reason why you are getting variable undefined errors is because when you use var, the declared variable is scoped to the surrounding context, meaning that the variable's lifetime is limited to the lifetime of the surrounding context (function, block, whathaveyou).
If you don't use var however, you are effectively declaring a variable tied to global scope. (Usually a Very Bad Idea).
So, in your code, the reason why you are able to access the mytool variable somewhere else in your template is because you tied it to global scope, where in the case of using var, the variable went out of scope because it must have been declared within a function.

Categories