0x00、引言
为了对JavaScript的作用域和作用域链有一个更好的理解,我们就需要了解JS这门语言的一个机制,即声明前置。
0x01、从变量的声明前置说起
我比较喜欢从一些小的demo来说起和加深自己对JavaScript这门语言机制的理解,那么声明前置这个也不另外
- demo1
console.log(a) // undefined var a = 1 console.log(a) //1
第二个console.log(a) 打印出1我们好理解,那么上面为什么第一个console会打印出undefined?
这里就不得不提一直说的声明前置,其实var a = 1可以拆解为var a
和a = 1
这样两个部分了,var a
是对变量a的声明,a = 1是对变量a的赋值,所谓声明前置就JS引擎在解析这段代码的时候,会将var a
声明放置在最前面,即可以改写为以下代码
-
demo1改写
var a //变量a声明前置了
console.log(a) // undefined
a = 1
console.log(a) //1
下面我们再来看一个例子
- demo2
console.log(a) // undefined
if (false){
var a
}
这里我们将var a放置在if 语句中,还会声明被前置么,结果是当然,因为在[深入之作用域](http://www.jianshu.com/p/636c5eb548e2)中我们以及知道在JS这门语言中只有全局作用域和函数作用域,无块级作用域(ES6有新增),**因而var a 即使在if/for里面,也会被前置的**
##### 0x02、关于函数的声明前置
我们知道函数有多种的调用方式,但我们这里主要讨论函数的声明前置,所以当用到函数的调用就以最简单的一种函数调用模式为例
- demo3
fn(2,3); //5
function fn(a,b) {
return a+b;
var c = 1
}
在这个简单的函数调用中,在JS中将定义的函数进行一个声明前置,确保它在被调用前以及被定义了,下面我们可以改写为以下形式
- demo3改写
function fn(a,b) { //全局作用域下定义的函数fn声明前置
var c //函数作用域内声明变量c前置
return a+b;
c = 1
}
fn(2,3); //5
#### 0x03、下面举出几个复杂一点的例子
> 1.若不报错,下面代码输出什么?
var y = 10;
if (!(x in window)) {
var x = 10;
} else {
++y;
}
alert(x); //undefined,var a 即使在if/for里面,也会被前置的,
//但a=10的赋值还会停留在if(){...}中
alert(y); //11
> 2.重复声明两个函数会怎样?
var m= 1, j = k = 0;
function add(n) {
return n = n+1;
}
y = add(m); //4,由声明前置我们可以知道后一个函数将在y调用前声明并覆盖掉前一个函数的声明(同名)
function add(n) {
return n = n + 3;
}
z = add(m); //4,z和y的指针其实指向的其实是同一个函数
> 3.对下面进行声明前置的改写
a = 2
b()
var a
function c(){
console.log(a)
}
function b(){
a = 1
c()
var a
}
/------改写------/
var a //全局作用域内的变量a提升
function c(){ //声明函数的变量提升
console.log(a) //重点 ,此时的a指的是全局变量的a
}
function b(){ //声明函数的变量提升
var a //函数作用域内的变量a提升
a = 1
c() //c()函数被调用
}
a = 2
b() //2