作用域
在 JavaScript 中有两种作用域
- 全局作用域
- 局部作用域
当变量定义在一个函数中时,变量就在局部作用域中,而定义在函数之外的变量则从属于全局作用域。每个函数在调用的时候会创建一个新的作用域。
全局作用域
最外层函数定义的变量拥有全局作用域,全局作用域里的变量能够在其他作用域中被访问和修改。
1 | var name = "Tennant"; |
局部作用域
和全局作用域相反,局部作用域一般只在固定的代码片段内可访问到,而对于函数外部是无法访问的,最常见的例如函数内部
1 | function fn(){ |
需要注意的是,函数内部声明变量的时候,一定要使用var命令。如果不用的话,实际上声明了一个全局变量:
1 | function fn(){ |
再如以下代码,只要函数内定义了一个局部变量,函数在解析的时候都会将这个变量“提前声明” :
1 | var scope = "global"; |
1 | var scope = "global"; |
没有块级作用域
Javascript没有块级作用域,在其他类C的语言中,由花括号封闭的代码块都有自己的作用域,因此支持条件来定义变量。例如,以下代并不会得到想要的结果:
1 | if(true){ |
这里是在一个if语句中定义了变量name。如果是在C、C++或者Java中,name会在if语句执行完毕后被销毁。但在JavaScript中,if语句中的变量声明会将变量添加到当前的执行环境(在这里是全局环境)中。在使用for语句时要特别注意,例如:
1 | for (var i = 0; i < 10; i++){ |
对于有块级作用域的语言来说,for语句初始化变量的表达式所定义的变量,只会存在于循环的环境之中。而对于JavaScript来说,由for语句创建的变量i即使在for循环执行结束后,也依旧会存在于循环外部的执行环境中。
作用域链(Scope chain)
当代码在一个环境执行时,会创建变量对象的一个作用域链。作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问。
执行环境
执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。
全局执行环境是最外围的执行环境,全局执行环境被认为是window对象,因此所有的全局变量和函数都作为window对象的属性和方法创建的。
JavaScript的执行顺序是根据函数的调用来决定的,当一个函数被调用时,该函数环境的变量对象就被压入一个环境栈中。而在函数执行之后,栈将该函数的变量对象弹出,把控制权交给之前的执行环境变量对象。
举个例子:
1 | var color = "blue"; |
在以上例子中,函数changeColor()的作用域链包含两个对象:
它自己的变量对象(其中定义着arguments对象)和全局环境的变量对象。可以在函数内部访问变量color,就是因为可以在这个作用域链中找到它。
看如下代码:
1 | var a = 1 |
1 | var a = 1 |
以上查找方向为:
- 函数在执行的过程中,先从自己内部找变量(注意找的是变量的当前的状态)
- 如果找不到,再从创建当前函数所在的作用域去找, 以此往上
立即执行函数表达式是什么?有什么作用?
立即执行函数的作用是隔离作用域,使被执行的函数内部的变量不会污染到外部,即外部不能访问函数内部的变量。
写法:
1 | (function(){ / code / }()); |
立即执行函数表达式的作用:
1.不必为函数命名,避免了污染全局变量;
2.立即执行函数表达式内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量,隔离作用域。
写一个函数,返回参数的平方和
1 | function sumOfSquares(){ |
如下代码的输出?为什么?
1 | console.log(a); |
1 | 输出:undefined 报错("error" "ReferenceError: b is not defined ..."); |
1 | sayName('world'); |
1 | 输出: hello world |
1 | var x = 10 |
1 | 输出: 10 |
1 | var x = 10; |
1 | 输出: 30 |