JS中的变量与作用域

在JavaScript中,是没有块状作用域的,比如我们可以这样写,而不报错:

var age = 20;
if(age > 10){
	var name = "张三";
}
console.log(name);// 张三

如果是Java里面,这样写是编译不过的,name变量必须要在外面申明,否则下面引用name就会报未申明的变量错误。

这里这样写不会报错的原因,是因为这段代码相当于这样:

var age = 20;
var name;
if(age > 10){
	name = "张三";
}
console.log(name);

也就是说,在JS中,最终运行时会把变量申明提前。

其实函数也是这样,这也是为什么在JS中上部的代码能引用下面的function的原因,比如:

console.log(foo());//1

function foo(){
	return 1;
}

在JS中,变量的引用,程序会自动在所在的环境里一级一级往上检索,直到找到对应的变量为止。

function foo(){
	var name = "tom";
	var age = 20;
	function bar(){
		console.log(name);//tom
		console.log(age);//20
	}
	bar();
}
foo();

再看下面这段:

function foo(){
	var name = "tom";
	var age = 20;
	function bar(){
		console.log(name); //undefined
		console.log(age); //undefined
		var name = "lily";
		var age = 18;
	}
	bar();
}
foo();

之所以这里会变成undefined,是因为这段代码等价于:

function foo(){
	var name = "tom";
	var age = 20;
	function bar(){
		var name,age;
		console.log(name); //undefined
		console.log(age); //undefined
		name = "lily";
		age = 18;
	}
	bar();
}
foo();

就是前面提到的,变量申明会被提到所在域的最前面。因此bar函数里的name,age不再是foo里的了。

这是JS的灵活只处,但这有时也会带来很多麻烦,比如变量污染。

由于可以在任何时候申明和使用,所以我们有时在反复使用某一变量时发现与预期结果不一致,又不会报错,这时就会比较难找到错误所在。

为此,在ES6版本中,我们通常都会使用let关键字替换var来申明变量的原因。

let与var的区别是,let申明变量时变量不会被程序把变量拿到最前面申明,而且重复申明会报错。

比如:

var age = 20;
console.log(age); // 20
var age = 18;
console.log(age);// 18

这是没问题的。

但是用let就不能这样:

let age = 20;
console.log(age); // 20
let age = 18; // Identify 'age' already declared.
console.log(age);
Table of Contents