作用域
一个变量的作用域是程序源代码中定义这个变量的区域。
而在ES5中只分为全局作用域和函数作用域,也就是说for,if,while等语句是不会创建作用域的。ES6(let,const)除外。
1 | //全局作用域 |
声明提前
JavaScript函数里声明的所有变量(但不涉及赋值)都被“提升”至函数体的顶部,在代码开始运行之前。这个特性被称为声明提前。
var a = "g";
function f() {
console.log(a); //输出undefined
var a = "l";
console.log(a); //输出"l"
}
由于函数作用域的特性,局部变量在整个函数体始终是有定义的,也就是说,函数体的局部变量覆盖了同名全局变量。在函数体内,变量a被“提前”了,提前至函数体的顶部,所以第一次输出的是undefined,那时候还没赋值,但代码执行到var语句时候,局部变量才会被赋值。因此第二次输出则是“l”。此代码过程如下:
1 | var a = "g"; |
因此一些程序员特意将变量声明放在函数体的顶部,而不是将声明靠近放在使用变量之处。
作用域链
先看一段简单代码,代码如下:
1 | var name = "wythe"; |
看到代码可知,name是在全局作用域中声明的全局变量,而firend则是在函数作用域中声明的局部变量。在执行时候你会发现函数作用域能够访问到在全局作用域中name这个变量,而全局作用域却不能访问到函数作用域的friend的变量,原因是作用域链!
作用域链的规则:
外部不能访问内部变量,内部可以访问外部变量
为什么会有这样规则?因为是执行环境所规定的。
执行环境定义了变量或函数有权访问其他数据,决定了它们的行为。每个执行环境都有一个与之关联的变量对象(variable object),环境中定义的所有变量和函数都保存在这个对象中。
全局执行环境是最外围的一个执行环境。在Web浏览器中,全局执行环境被认为是window对象。某个执行环境中所有所有代码执行完毕后,该环境被销毁,保存在其中的所有的变量和函数定义也随之销毁。
补充说明:需要了解一些概念,变量对象(Variable Object)、活动对象(Activation Object)、函数的属性[[scope]].
变量对象指的是变量对象(缩写为VO)是一个与执行上下文相关的特殊对象,它存储着在上下文中声明的内容有:变量 (var, 变量声明)、函数声明和函数的形参。
执行上下文(执行环境):每次当控制器转到ECMAScript可执行代码的时候,即会进入到一个执行上下文。执行上下文(简称-EC)是ECMA-262标准里的一个抽象概念,用于同可执行代码(executable code)概念进行区分。
活动对象指的是由函数的运行期上下文(代码执行前)创建,在运行时可变,初始时只有 arguments 属性,通过变量的初始化,包含了局部变量、命名参数、 this 等。