Node.js variable declaration and scope
It doesn't work in Node when using var
because testContext
is a local of the current module. You should reference it directly: console.log(testContext);
.
When you don't type var
, what happens is that testContext
is now a global var in the entire Node process.
In Chrome (or any other browser - well, I'm unsure about oldIE...), it doesn't matter if you use var
or not in your example, testContext
will go to the global context, which is window
.
By the way, the "global context" is the default this
of function calls in JS.
What is the scope of variables in JavaScript?
TLDR
JavaScript has lexical (also called static) scoping and closures. This means you can tell the scope of an identifier by looking at the source code.
The four scopes are:
- Global - visible by everything
- Function - visible within a function (and its sub-functions and blocks)
- Block - visible within a block (and its sub-blocks)
- Module - visible within a module
Outside of the special cases of global and module scope, variables are declared using var
(function scope), let
(block scope), and const
(block scope). Most other forms of identifier declaration have block scope in strict mode.
Overview
Scope is the region of the codebase over which an identifier is valid.
A lexical environment is a mapping between identifier names and the values associated with them.
Scope is formed of a linked nesting of lexical environments, with each level in the nesting corresponding to a lexical environment of an ancestor execution context.
These linked lexical environments form a scope "chain". Identifier resolution is the process of searching along this chain for a matching identifier.
Identifier resolution only occurs in one direction: outwards. In this way, outer lexical environments cannot "see" into inner lexical environments.
There are three pertinent factors in deciding the scope of an identifier in JavaScript:
- How an identifier was declared
- Where an identifier was declared
- Whether you are in strict mode or non-strict mode
Some of the ways identifiers can be declared:
var
,let
andconst
- Function parameters
- Catch block parameter
- Function declarations
- Named function expressions
- Implicitly defined properties on the global object (i.e., missing out
var
in non-strict mode) import
statementseval
Some of the locations identifiers can be declared:
- Global context
- Function body
- Ordinary block
- The top of a control structure (e.g., loop, if, while, etc.)
- Control structure body
- Modules
Declaration Styles
var
Identifiers declared using var
have function scope, apart from when they are declared directly in the global context, in which case they are added as properties on the global object and have global scope. There are separate rules for their use in eval
functions.
let and const
Identifiers declared using let
and const
have block scope, apart from when they are declared directly in the global context, in which case they have global scope.
Note: let
, const
and var
are all hoisted. This means that their logical position of definition is the top of their enclosing scope (block or function). However, variables declared using let
and const
cannot be read or assigned to until control has passed the point of declaration in the source code. The interim period is known as the temporal dead zone.
function f() {
function g() {
console.log(x)
}
let x = 1
g()
}
f() // 1 because x is hoisted even though declared with `let`!
javascript running in nodejs v.s. web browser, about variable scope
nodejs encapsulates scope per file, sort of like ruby. If you want to share variables you should export
them:
file1.js:
exports.a = 5;
file2.js:
exports.b = 6;
main.js:
var file1 = require('./file1.js');
var file2 = require('./file2.js');
console.log(file1.a); // 5
console.log(file2.b); // 6
Anything you export in a file by assigning to export.variablename = yourObject;
will be accessible when you include the file from elsewhere: var exportedStuff = require('./file.js')
would let you access yourObject
at exportedStuff.variablename
.
The reason for all of this is to force you to be more organized about how you write your code. As opposed to just slapping global variables around everywhere, it forces you to organize your code into modules, and also gives you the ability to emulate private scoping in an easier way than on the web.
In the web when you omit var
, and just have varname = 5
then when variable does not already exist, it's the same as saying window.varname
. This is NOT the case in nodejs. In Node if you want to use global you must do global.varname = 5
In what scope are module variables stored in node.js?
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
});
javascript function declarations and differences in scope
There are four ways to create a function in JavaScript.
Function declaration
This will create a variable foo
in the current scope and assign a named function to it.
function foo () {
}
Function declarations are hoisted so it doesn't matter where, in the applicable scope, you put them. It is considered good coding practise to define them before you use them though.
Anonymous function expression
This will create a function without a name and use it in an expression. In this example it is assigned to the variable something
.
something = function () {
};
Named function expression
This is the same as an anonymous function expression except that it has a name, creates a variable of that name in the scope of itself and is horribly broken in older versions of Internet Explorer.
something = function foo () {
};
Function constructor
Do not use function constructors. They are eval
by another name. You can read about them on MDN if you're interested.
scope of variable in javascript with inner/nested functions when redeclared
Variables in JavaScript (and function declarations) get hoisted, i.e. their declaration moves at compile time, up to the start of their scope (either the function their in, or module).
But the assignment does not get hoisted. That is why the first function prints undefined - the local variable replaces the outer one, but it’s undefined until after the log is called.
How can I dynamically create a variable dynamically in the current scope in node.js?
IIUC, whether or not there are better ways to achieve your goal, if you just remove 'var '
on the dynamic Function
, it will operate on the outer (global) scope.
Change:
new Function('value', k+' = value')(vars[k])
to:
new Function('value', k+' = value')(vars[k])
So:
var vars = {a: {}, b:{}}
for(var k in vars) {
eval('var '+k) // create the variable dynamically
new Function('value', k+' = value')(vars[k]) // attempt to set the value
}
a.b = 5
console.log(vars.a.b)
You don't want to declare a new variable with local scope inside the function, you want to operate on the outer scope.
Update to address new question
Your first loop does work. Try introspecting on a
or b
; they are as should be expected, 0 and 1, respectively.
Update 2 based on info this is for Node.js
Per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function , Function
always works on the global scope. In Node, this is on global
and var
variables are not the global scope but are the module's scope. To fix this for Node, you can do the following and omit your eval
var
declaration (which was overriding for the module the global scope which you have access to inside Function
):
var vars = {a: {}, b:{}}
for(var k in vars) {
new Function('value', k +' = value')(vars[k]) // attempt to set the value
}
a.b = 5
console.log(vars.a.b) // 5
In other words, the inner function sets variables which automatically become accessible via global
, so your module code, in the absence of any module-scoped var
declarations of a
overwriting the global, you can set the global a
properties as with the line a.b = 5
.
Update 3
Since I had just been addressing your issue in understanding Function
, I gave the above information. As per your follow-up comment (and again, without speaking to its suitability for your specific use case), you can operate on the object via eval
as follows:
var vars = {a: {}, b:{}}
for(var k in vars) {
eval('var '+k+' = vars["'+k+'"]')
}
a.b = 5
console.log(vars.a.b) // 5
But to reiterate the warnings made by others--using eval
is generally not a good idea and can be dangerous when the variables referenced include arbitrary user data...
Getting All Variables In Scope
No. "In scope" variables are determined by the "scope chain", which is not accessible programmatically.
For detail (quite a lot of it), check out the ECMAScript (JavaScript) specification. Here's a link to the official page where you can download the canonical spec (a PDF), and here's one to the official, linkable HTML version.
Update based on your comment to Camsoft
The variables in scope for your event function are determined by where you define your event function, not how they call it. But, you may find useful information about what's available to your function via this
and arguments by doing something along the lines of what KennyTM pointed out (for (var propName in ____)
) since that will tell you what's available on various objects provided to you (this
and arguments; if you're not sure what arguments they give you, you can find out via the arguments
variable that's implicitly defined for every function).
So in addition to whatever's in-scope because of where you define your function, you can find out what else is available by other means by doing:
var n, arg, name;
alert("typeof this = " + typeof this);
for (name in this) {
alert("this[" + name + "]=" + this[name]);
}
for (n = 0; n < arguments.length; ++n) {
arg = arguments[n];
alert("typeof arguments[" + n + "] = " + typeof arg);
for (name in arg) {
alert("arguments[" + n + "][" + name + "]=" + arg[name]);
}
}
(You can expand on that to get more useful information.)
Instead of that, though, I'd probably use a debugger like Chrome's dev tools (even if you don't normally use Chrome for development) or Firebug (even if you don't normally use Firefox for development), or Dragonfly on Opera, or "F12 Developer Tools" on IE. And read through whatever JavaScript files they provide you. And beat them over the head for proper docs. :-)
Related Topics
Get Index of Clicked Element Using Pure JavaScript
Compare 2 Arrays Which Returns Difference
Getting a Jquery Selector for an Element
Converting 24 Hour Time to 12 Hour Time W/ Am & Pm Using JavaScript
Call JavaScript Function from Url/Address Bar
Get Selected Text Position and Place an Element Next to It
Does Awaiting a Non-Promise Have Any Detectable Effect
How to Remove Emoji Code Using JavaScript
Convert Nan to 0 in JavaScript
Stop Cursor from Jumping to End of Input Field in JavaScript Replace
Reason Behind This Self Invoking Anonymous Function Variant
How to Create a Custom Error in JavaScript
Change Route Params Without Reloading in Angular 2
How to Create a Session Using JavaScript
Can't Access Cookies from Document.Cookie in Js, But Browser Shows Cookies Exist