Explain JavaScript scopes and variable declarations promote "translation"

Source: Internet
Author: User
Tags anonymous function definition

Do you know the value of alert following JavaScript execution?

var foo = 1;
function Bar () {
if (!foo) {
var foo =;
alert (foo);
}
Bar ();


If you're surprised by the result of "10," you might want to take a good look at this article:

var a = 1;
Function B () {
a = ten;
return;
function A () {}
}
B ();
alert (a);


What about this one? The browser alert is "1", so why are these results so? It does seem strange, dangerous, and confusing, but it's just a description of JavaScript as a powerful and expressive language. I don't know the standard name for this feature, but I'm used to calling it "ascension," and this article will try to illustrate this mechanism, but first we need to understand the scope of JavaScript.


The scope of JavaScript

Many beginners are most likely to confuse the "scope", in fact, it is not only easy to confuse beginners, I have seen many experienced JavaScript programmers do not fully understand the scope. The reason why JavaScript is not clear to the scope is because it is understood as the scope of Class C language. Consider the following C program:

#include <stdio.h>
int main () {
int x = 1;
printf ("%d,", x);        1
if (1) {
int x = 2;
printf ("%d,", x); 2
}
printf ("%d\n", x);//1
}


The output of this program is 1,2,1. This is because the C, and Class C languages have block-level scopes. When the code executes within a block, such as a pair of curly braces, the new variable is declared within the scope and does not affect the outside of the curly braces. This is different from JavaScript. Under Firebug, try the following code:

var x = 1;
Console.log (x);    1
if (true) {
var x = 2;
Console.log (x); 2
}
console.log (x);//2


In this case, Firebug displays the 1,2,2. This is because JavaScript is a function scope, which is completely different from the block level of Class C, such as within curly braces, which does not create new scopes. Only in functions.

The block-level scopes that many languages use, such as c,c++,c#, and Java, so it's easy to make JavaScript programmers incomprehensible, and thankfully, JavaScript's function definition is very flexible, and if the inverse needs to create a temporary scope within a function, You can do this:

function foo () {
var x = 1;
if (x) {
(function () {
var x = 2;
Some other Code
} ());
}
//x is still 1.
}


declaring variables and promoting

In JavaScript, there are four basic ways in which a variable enters the scope:

Language definition: Global scope, by default, there are variables this and arguments.
Formal parameters: A function can be a physical parameter, and its scope is within the entire function.
function declaration: For example this form function foo () {}
Variable declaration: For example this form var foo;

function declarations and variable declarations are internally "elevated" in parsing JavaScript programs, and formal parameters and global variables already exist, meaning that 3, 42 variable types are promoted, meaning that the code is parsed like this:

function foo () {
bar ();
var x = 1;
}
The explanation will actually look like this: 6
function foo () {
var x;
Bar ();
x = 1;
}


It turns out that it exists regardless of whether or not it contains a variable declaration. The following two functions are equivalent:

function foo () {
if (false) {
var x = 1;
} return
;
var y = 1;
}
function foo () {
var x, y;
if (false) {
x = 1;
} return
;
y = 1;
}


Note: When a function is defined as a value of a variable, the declaration is not promoted, and only the variable name is raised, which causes the function name to ascend, but the function body is not elevated, but remember that the function declaration has two forms, consider the following javascript:

function test () {
foo ();//TypeError ' Foo is ' not a function '
bar ();//"This'll run!"
var foo = function () {//function expression assigned to local variable ' foo '
alert ("This won ' t run!");    
}
function bar () {//function declaration, given the name ' bar '
alert (' This'll run! ');
}
test ();


In this case, only the function declaration containing the body of the function is promoted to the top, and the variable "foo" is promoted, but its body is on the right and is not assigned until the statement is executed.

This is the basic concept of ascension, so it is not so complicated and confusing. Of course, when it comes to writing JavaScript, it's a little more complicated to have some special situations.


Variable Name resolution ORDER

The most important thing is to remember the order of the variable name parsing in special cases. They have four ways to enter the namespace, this order is based on the list from the top to the next, the order list is listed below, and in general, if a name is already defined, it is not overwritten by another name, which means that a function declaration takes precedence over the variable declaration. This does not mean assigning a new name to be invalid, except that the declaration will be masked. They also have some exceptions:

Built-in arguments, it looks like the formal parameter has been declared before the function declaration, which means that a formal parameter name arguments will take precedence over the built-in arguments, even if it is not defined, which is a bad feature. Do not use arguments as a formal parameter.
If you define this name in some places, it can cause syntax errors. This is a good feature.
If multiple formal parameters have the same name, then the last name in the formal parameter will be given precedence, even if it is not defined.

Function-Named expression

You can assign a function definition to a function name using a function expression, which is like a function definition, but unlike a function declaration, the name does not enter the scope, and the function body does not ascend. Here are some examples of code to illustrate the implications:

foo (); // typeerror  "foo is not a " Function "Bar ();  // valid Baz (); // typeerror " baz is not a  function "spam (); // referenceerror " spam is not defined "var foo =  function  ()  {}; // anonymous function expression  (' foo '  gets  hoisted) Function bar ()  {}; // function declaration  (' Bar '  and  the function body get hoisted) Var baz = function spam ()  {};   named function expression  (only  ' Baz '  gets hoisted) foo (); //  Valid bar ();  // valid Baz ();  // valid spam (); // referenceerror  "Spam is  not defined "


How to use this knowledge to write code

Now that you understand scope and name elevation, but how do you write JavaScript code? A very important thing is to use Var when declaring variables. I strongly recommend that you define variables in the head of each scope using VAR, and if you always do, you will not be confused by the characteristics of ascension. However, it is difficult to distinguish between variables that are actually declared under the current scope and variables that are already in use. I recommend using the JSLint onevar option to perform these. If you are prepared to do so, your code should look like this:
/*jslint onevar:true [...] *
function foo (A, B, c) {
var x = 1,
Bar
Baz = "something";
}

What does the spec say?

I find it very useful to consult ECMAScript Starndard frequently to understand how these features work. Here's what it describes variable declarations and scopes (the latest version of 12.2.2)

If the variable is declared inside the function declaration, then the variable is defined within the current function scope, as described in section 10.1.3. In addition, they are defined in the global scope (that is, they create a member of the global object, as described in 10.1.3), and have attributes of the attribute. A variable is created within the scope of the current execution, a block does not produce a new space, only the program and function declarations create a new scope of action. When a variable is initialized, it is created as a undefined. A variable is really initialized by assigning an existing value to a variable using an expression. is not when the variable is created.


Original author


Http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html





JavaScript Scoping and hoisting





Do you know what value would be alerted if the following is executed as a JavaScript program?





var foo = 1;


function Bar () {


if (!foo) {


var foo = 10;


}


Alert (foo);


}


Bar ();





If It surprises you to the answer is "ten", then this one would probably really throw your for a loop:





var a = 1;


Function B () {


A = 10;


Return


function A () {}


}


b ();


alert (a);





Here, of course, the browser would alert "1". So what ' s going in here? While it might seem strange, dangerous, and confusing, this is actually a powerful and expressive of the feature. I don ' t know if there is a standard name for this specific behavior, but I ' ve come to like the term "hoisting". This article would try to shed some light on this mechanism, but the a lets take the necessary of a to detour understand T ' s scoping.


Scoping in JavaScript





One of the sources of most confusion for JavaScript beginners is scoping. Actually, it ' s not just beginners. I ' ve met a lot of experienced JavaScript programmers who don ' t fully understand scoping. The reason scoping is so confusing in JavaScript are because it looks like a c-family language. Consider the following C program:





#include &lt;stdio.h&gt;


int main () {


int x = 1;


printf ("%d,", x); 1


if (1) {


int x = 2;


printf ("%d,", x); 2


}


printf ("%d\n", X); 1


}





The output from this program would be 1, 2, 1. This is because C, and the rest of the C family, has block-level scope. When control enters a block, such as the IF statement, new variables can is declared within that scope, without affecting The outer scope. This isn't the case in JavaScript. Try the following in Firebug:





var x = 1;


Console.log (x); 1


if (true) {


var x = 2;


Console.log (x); 2


}


Console.log (x); 2





In this case, Firebug'll show 1, 2, 2. This is because JavaScript has function-level scope. This is radically different from the C family. Blocks, such as if statements, does not create a new scope. Only functions create a new scope.





To a lot of the programmers who are used to languages like C, C + +, C #, or Java, this is unexpected and unwelcome. Luckily, because of the flexibility of JavaScript functions, there is a workaround. If you are must create temporary scopes within a function, do the following:





function foo () {


var x = 1;


if (x) {


(function () {


var x = 2;


Some other code


}());


}


X is still 1.


}





This is actually quite flexible, and can being used anywhere you need a temporary scope, not just within block stateme Nts. However, I strongly recommend that you take the time to really understand and appreciate JavaScript scoping. It ' s quite powerful, and one of my favorite features of the language. If you understand scoping, hoisting'll make a lot more sense to you.


Declarations, Names, and hoisting





In JavaScript, a name enters a scope in one of four basic ways:





Language-defined:all scopes are, by default, given the names this and arguments.


Formal parameters:functions can have named formal parameters, which are to the "body of" that function.


function Declarations:these are of the form function foo () {}.


Variable Declarations:these take the form var foo;.





Function declarations and variable declarations are always moved ("hoisted") invisibly to the top of their containing SCOP E by the JavaScript interpreter. Function parameters and language-defined names are, obviously, already there. This means the code like this:





function foo () {


Bar ();


var x = 1;


}





is actually interpreted like this:





function foo () {


var x;


Bar ();


x = 1;


}





It turns out this it doesn ' t matter whether the line that contains the declaration would is ever. The following two functions are equivalent:





function foo () {


if (false) {


var x = 1;


}


Return


var y = 1;


}


function foo () {


var x, y;


if (false) {


x = 1;


}


Return


y = 1;


}





Notice that the assignment portion of the declarations were not hoisted. Only the ' name is hoisted. This isn't the case with a function declarations, where the entire function body would be hoisted as. But Remember that there are two normal ways to declare functions. Consider the following JavaScript:





function Test () {


Foo (); TypeError "Foo is not a function"


Bar (); "This'll run!"


var foo = function () {//function expression assigned to local variable ' foo '


Alert ("This won ' t run!");


}


function bar () {//function declaration, given the name ' bar '


Alert ("This'll run!");


}


}


Test ();





In this case, only the function declaration has its body hoisted to the top. The name ' foo ' is hoisted, but the "is" left behind and to be assigned during execution.





That covers the basics of hoisting, which are not as complex or confusing as it seems. Of course, this being JavaScript, there are a little more complexity in certain special cases.


Name resolution Order





The most important special case to keep into mind is name resolution order. Remember that there are four ways for names to enter a given scope. The order I listed them above are the order they are resolved in. In general, if a name has already been defined, it's never overridden by another property of the same name. This means the A function declaration takes priority over a variable declaration. This does is not mean that a assignment to that name won't work just that the declaration portion would be ignored. There are a few exceptions:





The built-in name arguments behaves oddly. It seems to be declared following the formal parameters, but before function declarations. This means so a formal parameter with the name arguments would take over the precedence and built-in if it is even . This is a bad feature. Don ' t use the name arguments as a formal parameter.


Trying to use the name this as a identifier anywhere would cause a syntaxerror. This is a good feature.


If multiple formal parameters have the same name, the one occurring latest in the list would take precedence, even if it is Undefined.





Named Function Expressions





You can give names to functions defined in function expressions, with the syntax like a function declaration. This does isn't make it a function declaration, and the name isn't brought into scope, nor are the body hoisted. Here's some code to illustrate what I mean:





Foo (); TypeError "Foo is not a function"


Bar (); Valid


Baz (); TypeError "Baz is not a function"


Spam (); Referenceerror "spam is not defined"





var foo = function () {}; anonymous function expression (' foo ' gets hoisted)


function bar () {}; function declaration (' Bar ' and the function body get hoisted)


var baz = function spam () {}; Named function expression (only ' Baz ' gets hoisted)





Foo (); Valid


Bar (); Valid


Baz (); Valid


Spam (); Referenceerror "spam is not defined"





How to Code and this knowledge





Now this you understand scoping and hoisting, what does that mean to coding in JavaScript? The most important thing is to always declare your and variables with a var statement. I strongly recommend that your have exactly one var statement per scope, and that it is at the top. If you are force yourself to does this and you'll never have hoisting-related confusion. However, doing this can make it hard to keep track of which variables, have actually been, declared in the current scope. I recommend using JSLint with the Onevar option to enforce this. If you have done all of this, your the code should look something like this:





/*jslint onevar:true [...] *


function foo (A, B, c) {


var x = 1,


Bar


Baz = "something";


}





What the Standard Says





I find that it's often useful to just consult the ECMAScript Standard (pdf) directly to understand how this things. Here's what it has to say about variable declarations and scope (section 12.2.2 in the older version):





IF The variable statement occurs inside a functiondeclaration, the variables are defined with function-local scope in function, as described in section 10.1.3. Otherwise, they are defined with global scope (which, they are, as members of the global object, as created in Section 10.1.3) using the property attributes {Dontdelete}. Variables are created the execution scope is entered. A blocks does not define a new execution scope. Only the program and Functiondeclaration produce a new scope. Variables are initialised to undefined when created. A variable with A Initialiser is assigned the value of it assignmentexpression when the variablestatement is executed, n OT when the variable is created.





I hope this article has shed some light on one of the most common of sources to JavaScript confusion. I have tried to being as thorough as possible, to avoid creating more confusion. If I have made any mistakes or have large omissions, please let me know.



Add a JavaScript variable declaration elevation (hoisting)

The JavaScript variable declaration has a hoisting mechanism that, when executed, promotes the declaration of all variables to the front of the current scope.

Read a piece of code first

var v = "Hello";
(function () {
console.log (v);
var v = "World";
}) ();


What is the result of this code running?
The answer is: undefined
This code illustrates two questions,
First, the variable v in the function scope covers the upper scope variable v. Code to make less changes

var v = "Hello";
if (true) {
console.log (v);
var v = "World";
}


The output is "Hello", which indicates that JavaScript is not a block-level scope. A function is the only structure in JavaScript that has its own scope.

Second, in the function scope, the declaration of variable v is promoted. So the initial code is equivalent to:

var v = "Hello";
(function () {
var V;//declaration hoisting
Console.log (v);
v = "World";
}) ();


Declaration, definition, and initialization

The declaration declares the existence of a name, and the definition assigns storage space to the name, while initialization assigns the initial value to the storage space allocated by the name.


Use C + + to express these three concepts

extern int i;//This is a declaration indicating that the name I already exists somewhere
int i;//This is declaring and defining name I, allocating storage space for I
i = 0;//This is the initialization name I, which is assigned an initial value of 0

That's the way it is in JavaScript.

var v;//declaration Variable V
v = "Hello";//(define and) Initialize variable V

Because JavaScript is a dynamic language, its variables do not have a fixed type, and their storage size varies with initialization and assignment, so the definition of the variable is not the same as the traditional static language, and its definition does not matter.

Claim elevation

Declarations within the current scope are elevated to the front of the scope, including declarations of variables and functions

(function () {
var a = "1";
var f = function () {};
var B = "2";
var c = "3";
}) ();


The declaration of the variable A,F,B,C is promoted to the front of the function scope, similar to the following:

(function () {
var a,f,b,c;
A = "1";
f = function () {};
b = "2";
c = "3";
}) ();


Note that the function expression is not elevated, which is also the difference between a function expression and a function declaration. Look further at the difference between the two:

(function () {
//var f1,function F2 () {};//hoisting, implicitly promoted declaration
F1 ();//referenceerror:f1 is not defined< c15/> F2 ();
var f1 = function () {};
function F2 () {}
}) ();


The function declaration F2 is promoted in the above code, so it is no problem to call F2 before. Although the variable F1 is also promoted, the F1 value after ascension is undefined, and its true initial value is given at the execution of the function expression. So only the declaration is promoted.

Name resolution Order

A first name (name) in JavaScript enters scope (scope) in four ways, with the following order of precedence:
1, language built-in: All scopes have this and arguments keywords
2, formal parameters: function parameters in the scope of the function is valid
3. function declaration: Shape like function foo () {}
4, variable declaration: like Var bar;

The precedence of a name declaration is shown above, that is, if the name of a variable is the same as the name of the function, then the name of the function overrides the name of the variable regardless of its order in the code. But the names are initialized in the order in which they are written in the code, and are not affected by the above priority. Look at the code:

(function () {
var foo;
Console.log (typeof foo);    function
function foo () {}
foo = "foo";
Console.log (typeof foo); String
}) ();

If there is more than one variable with the same name in the formal argument, the last parameter with the same name overrides other parameters with the same name, even if the last parameter with the same name is not defined.

There are exceptions to the above name resolution precedence, such as the arguments name that can be overridden in a language.

named function expression

You can specify a name for a function expression like a function declaration, but that does not make the function expression a function declaration. The name of a named function expression does not enter the namespace and is not promoted.

f ();//typeerror:f is not a function
foo ();//referenceerror:foo are not defined
var f = function foo () {Console.log (typeof foo);};
f ();//function
foo ();//referenceerror:foo is not defined


The name of a named function expression is only valid within the scope of the function.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.