Home  >  Article  >  Web Front-end  >  JavaScript Advanced Series—Scope and Namespace

JavaScript Advanced Series—Scope and Namespace

黄舟
黄舟Original
2017-02-08 09:35:361306browse
  • Implicit global variables

  • Local variables

  • Variable declaration hoisting

  • Name resolution order

  • Namespace

  • Conclusion

Although JavaScript supports code snippets created by a pair of curly braces, it does not support block-level scope; only function scope is supported.

function test() { // 一个作用域
    for(var i = 0; i < 10; i++) { // 不是一个作用域
        // count
    }
    console.log(i); // 10
}

Translator's Note: If the left bracket of the return object and return are not on the same line, an error will occur.

(Note: If it is not in an assignment statement, but in a return expression or function parameter, {...} will be parsed as a code segment, not as the literal syntax of the object . If automatic semicolon insertion is taken into account, this may lead to some subtle errors)

// 译者注:下面输出 undefined
function add(a, b) {
    return 
        a + b;
}
console.log(add(1, 2));

There is no explicit namespace definition in JavaScript, which means that all objects are defined in a Under the globally shared namespace.

Every time a variable is referenced, JavaScript will traverse the entire scope upward until it finds the variable. If the global scope is reached but the variable is still not found, a ReferenceError exception is thrown.

Implicit global variables

// 脚本 A
foo = &#39;42&#39;;

// 脚本 B
var foo = &#39;42&#39;

The above two scripts have different effects. Script A defines variable foo in the global scope, while script B defines variable foo in the current scope.

Again, the above effect is completely different. Not using var to declare variables will result in the generation of implicit global variables.

// 全局作用域
var foo = 42;
function test() {
    // 局部作用域
    foo = 21;
}
test();
foo; // 21

Declaring the foo variable without using the var keyword in the function test will overwrite the external variable with the same name. This may not seem like a big problem at first, but when you have thousands of lines of code, declaring variables without using var can lead to bugs that are hard to track down.

// 全局作用域
var items = [/* 数组 */];
for(var i = 0; i < 10; i++) {
    subLoop();
}

function subLoop() {
    // subLoop 函数作用域
    for(i = 0; i < 10; i++) { // 没有使用 var 声明变量
        // 干活
    }
}

The outer loop will terminate after the first call to subLoop because subLoop overwrites the global variable i. This error can be avoided by declaring the variable using var in the second for loop. Never omit the var keyword when declaring a variable unless this is the desired behavior affecting the outer scope.

Local variables

Local variables in JavaScript can only be declared in two ways, one is as a function parameter, and the other is declared through the var keyword.

// 全局变量
var foo = 1;
var bar = 2;
var i = 2;

function test(i) {
    // 函数 test 内的局部作用域
    i = 5;

    var foo = 3;
    bar = 4;
}
test(10);

foo and i are local variables within the function test, and the assignment to bar will overwrite the variable with the same name in the global scope.

Variable declaration hoisting (Hoisting)

JavaScript will hoist variable declarations. This means that both var expressions and function declarations will be hoisted to the top of the current scope.

bar();
var bar = function() {};
var someValue = 42;

test();
function test(data) {
    if (false) {
        goo = 1;

    } else {
        var goo = 2;
    }
    for(var i = 0; i < 100; i++) {
        var e = data[i];
    }
}

The above code will be converted before running. JavaScript will hoist var expressions and function declarations to the top of the current scope.

// var 表达式被移动到这里
var bar, someValue; // 缺省值是 &#39;undefined&#39;

// 函数声明也会提升
function test(data) {
    var goo, i, e; // 没有块级作用域,这些变量被移动到函数顶部
    if (false) {
        goo = 1;

    } else {
        goo = 2;
    }
    for(i = 0; i < 100; i++) {
        e = data[i];
    }
}

bar(); // 出错:TypeError,因为 bar 依然是 &#39;undefined&#39;
someValue = 42; // 赋值语句不会被提升规则(hoisting)影响
bar = function() {};

test();

The lack of block-level scope not only causes var expressions to be moved from inside the loop to the outside, but also makes some if expressions harder to read.

In the original code, the if expression seems to modify the global variable goo, but in fact it modifies the local variable after the promotion rule is applied.

Without knowledge of hoisting, the following code appears to throw a ReferenceError exception.

// 检查 SomeImportantThing 是否已经被初始化
if (!SomeImportantThing) {
    var SomeImportantThing = {};
}

Actually, the above code works fine because the var expression is hoisted to the top of the global scope.

var SomeImportantThing;

// 其它一些代码,可能会初始化 SomeImportantThing,也可能不会

// 检查是否已经被初始化
if (!SomeImportantThing) {
    SomeImportantThing = {};
}

Translator's Note: There is an article introducing hoisting on the Nettuts+ website, and the code in it is very enlightening.

// 译者注:来自 Nettuts+ 的一段代码,生动的阐述了 JavaScript 中变量声明提升规则
var myvar = &#39;my value&#39;;  

(function() {  
    alert(myvar); // undefined  
    var myvar = &#39;local value&#39;;  
})();

Name resolution order


All scopes in JavaScript, including the global scope, have a special name this pointing to the current object . There is also a default variable arguments in the function scope, which contains the parameters passed to the function. For example, when accessing the foo variable within a function, JavaScript will search in the following order:

  1. Whether there is a definition of var foo in the current scope.

  2. Whether the function formal parameters use the foo name.

  3. Whether the function itself is called foo.

  4. Go back to the previous scope and start again from #1.

Namespace

A common mistake caused by having only one global scope is naming conflicts. In JavaScript, this can be easily solved with anonymous wrappers.

(Note: Custom arguments parameters will prevent the creation of native arguments objects.)

(function() {
    // 函数创建一个命名空间

    window.foo = function() {
        // 对外公开的函数,创建了闭包
    };

})(); // 立即执行此匿名函数

Anonymous functions are considered expressions; therefore for callability, they are executed first .

( // 小括号内的函数首先被执行
function() {}
) // 并且返回函数对象
() // 调用上面的执行结果,也就是函数对象

There are some other ways to call function expressions. For example, the following two methods have different syntax, but the effect is exactly the same.

// 另外两种方式
+function(){}();
(function(){}());

Conclusion


It is recommended to use an anonymous wrapper (Translator's Note: that is, a self-executing anonymous function) to create a namespace. This not only prevents naming conflicts, but also facilitates the modularization of the program.

Also, using global variables is considered a bad habit. Such code is error-prone and costly to maintain.

The above is the content of JavaScript advanced series - scope and namespace. For more related content, please pay attention to the PHP Chinese website (www.php.cn)!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn