JavaScript温故而知新——执行环境和做用域

1、全局对象

咱们都知道JavaScript中有一类很是重要的对象——全局对象(global object),它的属性是全局定义的符号,编写JavaScript代码时咱们能够直接对这些属性进行使用。当JavaScript解释器启动时(浏览器加载新页面的时候),它将建立一个新的全局对象,而且定义一组初始的属性:
浏览器

  • 全局属性,如undefinedInfinityNaN
  • 全局函数,如isNaN()parseInt()eval()
  • 构造函数,如Date()RegExp()String()Object()Array()
  • 全局对象,如MathJSON

在代码的最顶级——不在任何函数内的JavaScript代码中,能够使用this来引用全局对象:bash

var global = this;  // 定义一个引用全局对象的全局变量
复制代码

在浏览器中,Window对象充当了全局对象,它的window属性引用其自身,能够代替this来引用全局对象。Window对象还针对Web浏览器和客户端JavaScript定义了额外的全局属性。另外,当咱们在代码中声明了一个全局变量,这个全局变量就是全局对象的一个属性。闭包

2、执行环境

执行环境能够说是JavaScript中最为重要的一个概念。app

执行环境定义了变量或函数有权访问的其余数据,决定了它们各自的行为。函数

能够这么理解,每一个执行环境都有一个与之关联的变量对象,咱们在环境中定义的全部变量和函数都会保存在这个对象中,不过这个对象咱们是没法访问的,它只供解析器在处理数据时在后台使用它。
post

全局执行环境——最外围的一个执行环境,在浏览器中,全局执行环境即全局对象window,所以全部全局变量和函数均可以做为window对象的属性和方法;
函数的执行环境——每个函数都本身的执行环境,当代码执行到一个函数时,这个函数的环境就会被推入到一个环境栈中,函数执行完毕后,环境栈会将它的执行环境弹出,并将控制权返回给以前的执行环境。JavaScript代码的执行顺序即是由这一机制所控制着。
垃圾收集——当环境栈将一个执行环境弹出后,该环境便会被销毁,保存在其中的全部变量和函数定义也会随之销毁进而释放内存,这即是JavaScript的自动垃圾收集机制。而全局执行环境只有在程序退出,例如关闭网页时才会被销毁,所以对于全局变量或者全局对象的属性,一旦咱们再也不须要用到它们的时候,能够手动将其值设置为null来解除其引用,一旦引用被解除,它们便会脱离执行环境,以便垃圾收集器运行时将其回收,这样作能起到很好的优化内存占用的效果。优化

3、做用域

1.变量做用域

一个变量的做用域指在代码中定义这个变量的区域,全局变量拥有全局做用域,在任何地方都有定义。
而在函数内声明的变量只在函数体内有定义,它们是局部变量,做用域是局部性的。函数参数也是局部变量,只在函数体内有定义。
在函数体内,局部变量优先级高于全局变量,若是在函数内声明的变量或函数参数中带有的变量和全局变量重名,则全局变量会被局部变量所覆盖。
声明局部变量必须使用var语句ui

scope = "global";
function checkscope () {
    scope = "local";    // 修改了全局变量
    myscope = "local";  // 显示的声明了新的全局变量
    return [scope, myscope];
}
checkscope();   // ["local", "local"]
console.log(scope, myscope)     // "local","local"
复制代码

2.函数做用域

在其余相似于C之类的语言中,花括号封闭的代码块都有本身的做用域,至关于JS中的执行环境。而JS中是没有块级做用域的,JS取而代之使用的是函数做用域,即变量在声明它们的函数体以及这个函数体内嵌套的任意函数体内都是有定义的。this

function test(o) {
    if (o) {
        var i = o;                      // i在函数体内是有定义的,不只是在if这个代码段内
        for(var j = 0; j < 10; j++) {   // j在函数体内是有定义的,不只是在for循环内
            console.log(j);             // 输出0~9
        }
        console.log(j);                 // j已经定义了,输出10
    }
    console.log(i);                     // i已经定义了,但可能没有初始化
}
复制代码

3.声明提早

咱们知道了在JavaScript的函数做用域下,函数内声明的全部变量在函数体内是始终可见的,因为这一特性咱们可能会出现一些误解。先看一段代码:spa

var scope = "global";
function f() {
    console.log(scope);     // 输出"undefined",而不是"global"
    var scope = "local";    // 变量在这里赋初始值,但变量自己在函数体内任何地方都是有定义的
    console.log(scope);     // 输出"local"
}
复制代码

你可能会误觉得函数中第一行输出global,但结果并不是如此,这就是容易形成咱们误解的地方。因为函数做用域的特性,局部变量在整个函数体始终是有定义的,这意味着变量在声明以前就能够使用了。
这个特性被称做:"声明提早"——即函数内的变量声明会被“提早”到函数体顶部,同时变量初始化留在原来的位置。
所以上面函数的代码实际上等价于:

function f() {
    var scope;              // 在函数顶部声明了局部变量
    console.log(scope);     // 变量存在,但其值是"undefined"
    scope = "local";        // 到这里才对变量进行初始化并赋值
    console.log(scope);
}
复制代码

注意点

  • "声明提早"是在JavaScript引擎的"预编译"阶段进行的,即代码开始运行以前。
  • 因为JavaScript没有块级做用域,所以一般将变量声明放在函数体顶部,这使得咱们的代码能更加清晰的反映出真实的变量做用域。

结尾

系列文章:

相关文章
相关标签/搜索