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;
Related
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.
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.
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.
I am really confused as to why sometimes vars will not work when "var" is declared in front of them when used in a namespaced object. Isn't adding "var" in front of every variable the correct thing to do to keep it outside the global namespace?
Or would creating any var without declaring "var" first in my namespaced object, ensure of this, so I don't eed to worry about "var"?
Here's an example of my code:
MYNAME.DoStuff = {
initialize: function() {
var var1 = 'name'; //1
var2 = 'name'; //2
this.var3 = 'name'; //3
var $var4 = $('#' + name); //4
$var5 = $('#' + name); //5
this.$var6 = $('#' + name); //6
},
linkStuff: function() {
// then use the vars from the init above in here
}
}
MYNAME.DoStuff.initialize();
Can someone tell me which number (1, 2, or 3) is correct? Are there cases where I would use more than one or all? How about when I need to do DOM references with jQuery? Which way is correct (4, 5, or 6)?
The var statement will bind the variable to the scope of the current function, in your example var1 is accessible only within initialize.
Your second example, var2 = 'name'; is simply an assignment made to a -possibly- undeclared identifier.
This is an anti-pattern, and should be avoided. If the identifier is not resolvable higher in the scope chain, var2 will end up being a property of the global object -an implied global, since most of the time you actually try to avoid globals-.
Moreover, the new ECMAScript 5 Strict Mode, completely disallows this kind of assignments, please avoid them...
The third example, this.var3 will create a property in the current object, in your example the this value of initialize will refer to MYNAME.DoStuff because you invoke the initialize method by MYNAME.DoStuff.initialize();.
In this third example you can access this kind of properties on your linkStuff function, just by this.var3 - if you invoke that linkStuff function properly, e.g. MYNAME.DoStuff.linkStuff();-.
Based on what it looks like you're trying to do in the linkStuff method, you might try this:
var DoStuff = {
firstVar: null,
secondVar: null,
initialize: function() {
// set initial values
this.firstVar = 'name';
this.secondVar = 'name2';
},
linkStuff: function() {
alert(this.firstVar); // 'name'
}
};
var myname = DoStuff;
myname.initialize();
myname.linkStuff();
As #CMS pointed out, var sets up a variable in the current scope. That's why you do var variablename outside of functions to create globals and var variablename inside functions to create locals.
But in the context of objects, there's more going on. Javascript has a feature called closures, which enables very powerful tricks like private, public and privileged methods (I don't normally like linking to Crockford because he's a bit of an arrogant knob, but sometimes he's a smart knob).
The other thing to watch out for is that Javascript objects are actually very different from objects in most other languages. In Javascript, you don't define them, you build them. If you do something like this:
function stuff() {
// Do init code here
this.linkStuff = function() {
// Do linkstuff code here
}
}
... you can do myname.dostuff = new stuff() and your initialization code will run automatically. That is also the point to fetch a jQuery object and assign it to a variable for later use.
Or more specific to what I need:
If I call a function from within another function, is it going to pull the variable from within the calling function, or from the level above? Ex:
myVar=0;
function runMe(){
myVar = 10;
callMe();
}
function callMe(){
addMe = myVar+10;
}
What does myVar end up being if callMe() is called through runMe()?
Jeff is right. Note that this is not actually a good test of static scoping (which JS does have). A better one would be:
myVar=0;
function runMe(){
var myVar = 10;
callMe();
}
function callMe(){
addMe = myVar+10;
}
runMe();
alert(addMe);
alert(myVar);
In a statically scoped language (like JS), that alerts 10, and 0. The var myVar (local variable) in runMe shadows the global myVar in that function. However, it has no effect in callMe, so callMe uses the global myVar which is still at 0.
In a dynamically scoped language (unlike JS), callMe would inherit scope from runMe, so addMe would become 20. Note that myVar would still be 0 at the alert, because the alert does not inherit scope from either function.
if your next line is callMe();, then addMe will be 10, and myVar will be 0.
if your next line is runMe();, then addMe will be 20, and myVar will be 10.
Forgive me for asking - what does this have to do with static/dynamic binding? Isn't myVar simply a global variable, and won't the procedural code (unwrap everything onto the call stack) determine the values?
Variables are statically scoped in JavaScript (dynamic scoping is really a pretty messy business: you can read more about it on Wikipedia).
In your case though, you're using a global variable, so all functions will access that same variable. Matthew Flaschen's reply shows how you can change it so the second myVar is actually a different variable.
This Page explains how to declare global vs. local variables in JavaScript, in case you're not too familiar with it. It's different from the way most scripting languages do it. (In summary: the "var" keyword makes a variable local if declared inside a function, otherwise the variable is global.)
Unless you use the keyword var to define your variables, everything ends up being a property on the window object. So your code would be equivalent to the following:
window.myVar=0;
function runMe(){
window.myVar = 10;
window.callMe();
}
function callMe(){
window.addMe = window.myVar+10;
}
If you keep this in mind, it should always be clear what is happening.
I would like to add that lambda expressions are also statically scoped at the location that the expression is defined. For example,
var myVar = 0;
function foo() {
var myVar = 10;
return { bar: function() { addMe = myVar + 10; }}
}
var myObj = foo();
var addMe = 6;
alert(addMe);
myVar = 42;
myObj.bar();
alert(addMe);
This will display 6 and 20.
As far as I understand, any variable without the var keyword is treated global, with it, its local scoped, so:
// This is a local scoped variable.
var local_var = "something";
// This is a global scoped variable.
global_var = "something_else";
As a good JS practice, it is recommended to ALWAYS add the var keyword.
myVar=0;
function runMe(){
myVar = 10;
callMe();
}
function callMe(){
addMe = myVar+10;
}
As far as the output is concerned myVar and addMe both will be global variable in this case , as in javascript if you don't declare a variable with var then it implicitly declares it as global hence when you call runMe() then myVar will have the value 10 and addMe will have 20 .