Javascript作用域及变量提升

October 25th, 2016 Leave a comment Go to comments

因为javascript没有块级作用域(即if, for包裹的区域)的概念,在函数和变量的定义和使用中,可能会出现令人迷惑的结果。本文就js作用域和变量提升这两个话题,做一番讲解。
PS: ES6标准引入了let关键字,用它声明的变量,具有块级作用域。并且在ES6中,花括号{}内部就是一个块级作用域。

在Javascript中,名称(变量或函数)进入作用域的基本方式有以下4种:
1. 所有作用域内,都存在 thisarguments 这两个变量
2. 给函数指定的参数,参数名会自动包含在函数的作用域内
3. 函数的声明。如 function X() {}
4. 变量的声明。如 var Y;

重点来了,这4种类型的名称,会按照上述的优先级,被javascript引擎移动到它们所在的作用域(如函数内)的顶端。这就是所谓的“变量提升”。
下面让我们用实例讲解上面这段话的含义。

首先是变量声明。下面的函数A()其实会被js引擎解释为函数B()的形式:

由这个特性我们可以得出一个推论:给变量声明+赋值的代码即使在逻辑上执行不到,也不会妨碍变量被声明,只不过无法赋值而已。
比如下面这俩函数是一样的:

也就是说,即使在函数A()里,x和y这两个变量也是存在的,只不过因为if和return的缘故,无法被赋值为1罢了。

接着是函数声明。和变量声明不同,函数声明的提升,是连同函数体一起提升了。当然,仅限于function X() {}这种写法的函数。而var X = function() {}这种,本质上是声明了一个变量,并把一个匿名函数给这个变量赋值,所以只会把变量名提升。如下示例:

上述的4个优先级还会造成一个现象:因为一旦一个名称被声明,后面再重复声明是无效的,所以同一个作用域内的同名变量和函数,函数的声明永远会覆盖掉变量的声明。结合变量提升的原理,你应该知道是怎么回事了吧?当然,只是声明部分被覆盖,赋值部分还是会被执行。下面的例子可以清楚地看到这一点:

不过也有特殊情况。其一,就比如作用域里默认生成的那个arguments变量。虽然把它放在优先级1里,但是实际上它真正的声明位置是在函数的参数之后,即优先级2和3之间的位置。因此如果在定义函数时,把函数的某个参数命名为arguments,那么这个参数就会覆盖掉默认的arguments,例如:

其二,this也不能作为标识符使用,如作为函数的参数及函数名,否则会报SyntaxError,因此可以说this是这些名称里面优先级最高的。其三,如果一个函数拥有多个同名参数,那么最后那个参数会进入函数的内部作用域。这与在函数内反复声明变量时的结果正好相反。例如:

最后一个要点,是关于用命名函数来给变量赋值,那个命名函数是否会被提升的问题。答案是NO。
如下面的例子:

可以看到,X()不仅没有被提升,它根本没有进入作用域!

Reference:
[1] http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html

本文为悠然居(https://wordpress.youran.me/)的原创文章,转载请注明出处!

声明: 本文采用 BY-NC-SA 协议进行授权. 转载请注明转自: Javascript作用域及变量提升
  1. No comments yet.