I'm writing a Javascript library that I hope to be able to minify with the Closure Compiler's ADVANCED_OPTIMIZATIONS option. The library has maybe two dozen global variables which set lower and upper range limits, string literals, etc.
To make these variable accessible from other source files and to avoid dead code removal, I have to "export" them. See Advanced Compilation and Externs.
Therefore, rather than declare variables with this syntax:
var fooMinValue = 10;
I plan to use this syntax:
window['fooMinValue'] = 10;
I've tested this and it seems to work fine. My questions are, is there any downside to using this syntax and is it supported in all browsers released since IE 6? (Or should I be using a completely different technique altogether?)
Although both are properties of the global object, there is a difference: when you declare the variable with var, its [[Configurable]] internal attribute gets set to false. Therefore, it's not possible to change its attributes with Object.defineProperty (except for [[Value]]). The most notable effect is that such variables cannot be deleted:
var foo = 'bar';
window['bar'] = 'baz';
console.log(foo); // 'bar'
console.log(bar); // 'baz'
delete foo; // doesn't work, you can't delete vars
delete bar; // works, bar is an object property
console.log(foo); // 'bar'
console.log(bar); // ReferenceError
Also, when assigning a variable to a property, you make a COPY of the value instead of referencing the variable. This means external changes to the property don't affect the value of the variable.
(function() {
var foo = 'bar';
window['foo2'] = foo; //export foo
console.log(foo); // will output 'bar'
setTimeout(function() { console.log(foo) }, 1000); //will output 'bar'
})();
window['foo2'] = 'baz';
console.log(window['foo2']); // will output 'baz'
The above code will produce the following output:
'bar'
'baz'
'bar'
It is the same except that if your script is not running on a browser it is very probable that window will be undefined.
You are welcome!
It will work; it's perfectly valid syntax; and it's supported in IE6 and up.
Demo: http://ie6test.it/?url=http://jsbin.com/usafeg/2
Related
When I do this in my node.js module:
var abc = '123';
Where does it go? And by this I mean: in the browser it goes in window.abc (if not executed in a function or otherwise)
If I execute this:
abc = '123';
Then I can find it in global.abc, but that's not how I want it.
Unlike the browser, where variables are by default assigned to the global space (i.e. window), in Node variables are scoped to the module (the file) unless you explicitly assign them to module.exports.
In fact, when you run node myfile.js or require('somefile.js') the code in your file is wrapped as follow:
(function (exports, require, module, __filename, __dirname) {
// your code is here
});
All the other answers are 100% correct, but I thought I would add an expanded/definitive list of the scopes within a Node.js application in case anybody comes across this via Google while starting off learning Node.js or JavaScript:
Global Scope
Anything declared without the var keyword in any file will be accessible from anywhere running in the same instance of the Node server:
// foo.js
bar = 'baz';
// qux.js
console.log(bar); // prints 'baz'
Note that this is widely considered to be a bad idea, because it makes your app strongly 'coupled'– meaning that you'd have to open foo.js to work out why bar = 'baz' in qux.js
Module Scope
Anything declared with the var keyword at the top level (not inside a function or object, or any other block) of a node.js file is in module scope, and will be accessible from anywhere within the same file, but will not exist anywhere else:
// foo.js
var bar = 'baz';
console.log(bar); // prints 'baz'
// qux.js
console.log(bar); // prints 'undefined'
Function Scope
Anything declared using the var keyword within a function will only be accessible from within that function, and not from anywhere else:
// foo.js
function myFunction() {
var bar = 'baz';
console.log(bar); // prints 'baz'
}
function myOtherFunction() {
console.log(bar); // prints 'undefined'
}
// qux.js
console.log(bar); // prints 'undefined'
JavaScript is function scoped. Unlike other (block scoped) languages, variables declared in a block within a function are accessible from anywhere else in that parent function. For example, this means that if you declare a new variable inside inside a loop, it's accessible outside of that loop as well, as long as you're still inside the parent function:
function myFunction() {
while (thing === true) {
var bar = 'baz';
thing = false;
}
console.log(bar); // prints 'baz'
}
Shadowing
If you 'redeclare' an existing variable, e.g. use the var keyword with a variable name that has been used already, then the value associated with that variable name is overwritten within the scope of the new declaration:
var bar = 'foo';
console.log(bar) // prints 'foo'
function myFunction() {
var bar = 'baz';
console.log(bar);
}
myFunction(); // prints 'baz'
console.log(bar) // prints 'foo'
Node has a module scope, so var abc = '123' in a module will create a variable which is scoped to (and therefore, reachable only for code in) that module.
See also http://nodejs.org/api/globals.html#globals_global
Pretty old question, and if anyone is curious about ECMA specs about this question, here is the link
And there is no way for direct access for module variables (except for imported modules):
Lexical Environments and Environment Record values are purely specification mechanisms and need not correspond to any specific artefact of an ECMAScript implementation. It is impossible for an ECMAScript program to directly access or manipulate such values.
I'm reading "you don't know javascript" and I find some trouble with one example in the book "This & Object Prototypes".
When discussing the different rules for this, specifically in the "Implicit binding" paragraph, the author gives this example:
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // function reference/alias!
var a = "oops, global"; // `a` also property on global object
bar(); // "oops, global"
However when trying this on JSFiddle I get an undefined output in console instead of the "oops, global".
Conversely, if I define a without var or using window.a I get the output intended by the author regardless of strict mode.
Why is this happening? Did something in ES6 change the way global variables should be declared?
The default settings for JS Fiddle wrap the JS in a function and assign it as an load event handler.
Your tests are not in the global scope.
It's common to see CommonJS modules defined using the following idiom:
(function() {
var logThis = function() { console.log(this); }
module.exports = logThis;
}).call(this);
Underscore.js, for example, does this.
I just spend half an hour discussing with a colleague why they invoke the closure with call(this). This will cause the value of this inside the closure to be inherited from the caller, rather than being set to the global object. However, when I tested this in Node.js, the value of this inside the module was always the global object, even when I loaded and ran it like this:
var bar = {};
bar.foo = function() { var foo = require("./foo"); foo(); }
I was really expecting to see the bar object in the console, but actually I see the global object. It then occurred to me that this might be because modules like Underscore.js are also used in a web context. But in that case it would be loaded with a <script> tag so this will always be equal to the global object anyway.
What gives? I'm sure there is a reason for using this construct but I can't see the practical difference in this particular case whether the module is being used in Node.js or in a webpage.
Update: Just to clarify, I can think of a number of cases where this could make a difference. For example, if I say:
var bar = {}
var foo = require("./foo");
bar.foo = foo;
bar.foo();
(Thanks to #Pointy for correcting my original example.)
I would expect the closure in the module to be evaluated when require() is called, which means that the value of this inside it would be bound to the global object, which would be written to the console even though foo() is then invoked as a member of the "bar" object. However, I am seeing the "bar" object in the console even in this example. I guess that this is not being bound to the closure as I expected?
In a nutshell, I'm looking for one example where a module like Underscore.js will have different behavior due to being wrapped in a closure invoked with fn.call(this) instead of just fn(), either in Node.js or in a web page.
Your call to "foo" inside "bar.foo" is made without any context, so the global context is used. The fact that it's inside a function where this refers to "bar" is not relevant; that's just not how JavaScript works. The only thing that matters is how the function is invoked, not where it's invoked, in other words.
If "bar.foo" looked like this:
bar.foo = function() { require("./foo"); foo.call(this); }
then you'd see "bar" in the console. Or, you could do this:
var bar = {};
require("./foo");
bar.foo = foo;
Then calling bar.foo() would also log the "bar" object. (Does that really work in Node? That is, I thought require() returned an object, and that it didn't just leave things in the global scope. I'm a rank novice at Node however.)
edit — OK thanks for updating. Thus, my example would be corrected as follows. First, I think that your module should look like this:
(function() {
var logThis = function() { console.log(this); }
module.exports.logThis = logThis;
}).call(this);
That is, I think that you want to explort the "logThis" function, so it needs to be bound to the "exports" object as a named property.
Then:
var bar = {};
var foo = require("./foo");
// At this point, foo.logThis is the function
bar.foo = foo.logThis;
// Now the "foo" property of "bar" is a reference to the same function
bar.foo(); // logs the "bar" object
var fee = { fie: foo.logThis };
fee.fie(); // logs the "fee" object
I'm reading the Backbone.js documents and am seeing a lot of code that assigns attributes to the window object:
window.something = "whatever";
What's the difference between calling this code, and just assigning the variable and creating a global variable, like this:
something = "whatever";
I assume there is some kind of scope difference, and/or object ownership difference (window being the owner vs. not), but I am interested in the detail between the two and why I would use window vs. not use it.
No difference. They both have the same effect (In the browser, where window is the global context1).
window.foo = "bar" sets the property foo on window.
foo = "bar" indicates either a typo or intentionally global.
Since I have to double check whether it's a typo or not, I personally find it more readable to set window.foo directly.
Also, in ES5 strict mode, foo = "bar" is an illegal assignment because foo is not declared and will throw a Error.
Edit:
As noted in the comments, foo = "bar" will look all the way up the scope chain for the variable foo and re-assign it with "bar" if it's found. If it's not found, it will create a new global variable.
Also with window.foo = "bar" you're just assigning a property to an object, which can be deleted using delete window.foo.
In ES5 strict mode it is invalid to delete a variable.
1 In other environments, such as node.js and Web Workers, there may be another name for the global object and window may not exist at all. Node.js uses global and Web Workers use self.
They both kind of do the same thing. But by accessing a window property, you know for sure that you're accessing a global variable no matter what scope you're in.
For example:
globalVar = "smth";
function(){
var globalVar = 2;
alert(globalVar); // Points to the current scope globalVar
alert(window.globalVar); // Points to the original globalVar
}
In other words, If you want to work with globals, it's somewhat safer to access them via their container: window.variable
The key, as Raynos alluded to, is that it's set explicitly on the window object. In the browser, the global object is the same as the window object but in other environments (e.g., Node.js, or perhaps running in a web view of some sort on a mobile device), it may not.
The difference is that window.foo = bar; cannot be intercepted by refactoring done later.
Using foo = bar; means that if, at a later date, the code is moved into a closure where var foo has been defined, it will no longer set it on the global object.
Adding one more point:
If you refer an undeclared variable directly (without using - window or typeof) then you will get a variable is not defined error.
Examples:
// var unDecVariable
if (unDecVariable != null) // Error: unDecVariable is not defined
{
// do something
}
if (window.unDecVariable != null) // No Error
{
// do something
}
if (typeof unDecVariable != 'undefined' && unDecVariable != null) // Alternative way
{
// do something
}
I noticed some weird (and to my knowledge undefined behavior, by the ECMA 3.0 Spec at least), take the following snippet:
var foo = { bar: "1", baz: "2" };
alert(bar);
with(foo) {
alert(bar);
alert(bar);
}
alert(bar);
It crashes in both Firefox and Chrome, because "bar" doesn't exist in the first alert(); statement, this is as expected. But if you add a declaration of bar inside the with()-statement, so it looks like this:
var foo = { bar: "1", baz: "2" };
alert(bar);
with(foo) {
alert(bar);
var bar = "g2";
alert(bar);
}
alert(bar);
It will produce the following:
undefined, 1, g2, undefined
It seems as if you create a variable inside a with()-statement most browsers (tested on Chrome or Firefox) will make that variable exist outside that scope also, it's just set to undefined. Now from my perspective bar should only exist inside the with()-statement, and if you make the example even weirder:
var foo = { bar: "1", baz: "2" };
var zoo;
alert(bar);
with(foo) {
alert(bar);
var bar = "g2";
zoo = function() {
return bar;
}
alert(bar);
}
alert(bar);
alert(zoo());
It will produce this:
undefined, 1, g2, undefined, g2
So the bar inside the with()-statement does not exist outside of it, yet the runtime somehow "automagically" creates a variable named bar that is undefined in its top level scope (global or function) but this variable does not refer to the same one as inside the with()-statement, and that variable will only exist if a with()-statement has a variable named bar that is defined inside it.
Very weird, and inconsistent. Anyone have an explanation for this behavior? There is nothing in the ECMA Spec about this.
This follows from ECMA-262, 3rd edition, §12.2, as
Variables are created when the execution scope is entered. A Block does not define a new execution scope.
and
A variable with an Initialiser is assigned the value of its AssignmentExpression when the VariableStatement is executed, not when the variable is created.
This means you have to think of a declaration with initializer as two seperate steps: first, you create a new variable in the function-local (global) scope when entering the function (executing the script), but you don't assign a value until reaching the statement. In your examples, at this point the scope chain has changed due to with(), and the assignment sees the overloaded varriable, leaving the newly created one undefined.
This means
with({ foo : 42 }) {
var foo = 'bar';
}
is parsed as
with({ foo : 42 }) {
var foo;
foo = 'bar';
}
which is equivalent to
var foo;
({ foo : 42 }).foo = 'bar';
and therefore leaving foo uninitialized.
I think you're just misunderstanding the scoping a bit. Not that I blame you, if you're coming from most other languages it makes you go "WTF?". Javascript has what I call "FUBAR Scoping" (I'm sure there's a proper term here but I never bothered to learn it...knowing how it works > knowing what it's called, at least to me).
Let's make the example even simpler:
with({}) {
var foo = "test";
}
alert(foo); //alerts "test"
with() allows block level definition of a variable, for being passed into a scope (good answer demonstrating this here). But...it doesn't restrict the variable to this scope, it is available afterwards, here's a better example:
with({ bar: 1}) {
this.foo = "test";
alert(bar); //alerts: 1"1"
alert(this); //alerts: "DOMwindow"
}
alert(foo);
If with() was a closure to the element you're dealing with, then this would refer to the { bar: 1} object we passed into the with, but it doesn't :). this in this case is still the global context of window. Since you're still executing in this context, any variables you define there will be available in that context, and therefore available later in the function, after the with(), because they're defined much higher in scope than you would think. Intuition tells you it's defined in a more constrained scope: within the with()...but javascript is different, it all depends what context your closure is running in.
I tried to find a good description for this behavior that gives a lot more scoping examples, this is the best I could find for you. Overall this is more of a closure/scoping question than with() explicitly, this site covers the overall concept in more detail in case I did a terrible job of explaining it...I know it's a backwards concept in many respects, sorry!