Node.Js Variable Declaration and Scope

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:

  1. Global - visible by everything
  2. Function - visible within a function (and its sub-functions and blocks)
  3. Block - visible within a block (and its sub-blocks)
  4. 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:

  1. How an identifier was declared
  2. Where an identifier was declared
  3. Whether you are in strict mode or non-strict mode

Some of the ways identifiers can be declared:

  1. var, let and const
  2. Function parameters
  3. Catch block parameter
  4. Function declarations
  5. Named function expressions
  6. Implicitly defined properties on the global object (i.e., missing out var in non-strict mode)
  7. import statements
  8. eval

Some of the locations identifiers can be declared:

  1. Global context
  2. Function body
  3. Ordinary block
  4. The top of a control structure (e.g., loop, if, while, etc.)
  5. Control structure body
  6. 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



Leave a reply



Submit