JavaScript变量提升

10 阅读2分钟

变量提升

首先是用var定义一个变量的时候, 例如: var a = 10; 大部分的编程语言都是先声明变量再使用, 但是javascript有所不同, 上面的代码, 实际相当于这样执行:

var a;
a = 10;

因此有了下面这段代码的执行结果:

console.log(a); // 声明,先给一个默认值undefined;
var a = 10; // 赋值,对变量a赋值了10
console.log(a); // 10

函数声明提升

定义函数也有两种方法:

● 函数声明: function foo () {};

● 函数表达式: var foo = function () {}.

类型一:

console.log(f1) // function f1(){}
function f1() {} // 函数声明
console.log(f2) // undefined
var f2 = function() {} // 函数表达式

可以看到, 使用函数声明的函数会将整个函数都提升到作用域(后面会介绍到)的最顶部, 因此打印出来的是整个函数; 而使用函数表达式声明则类似于变量声明提升, 将var f2提升到了顶部并赋值undefined.

类型二:

console.log(f1) // function f1(){...}
f1(); // 1
function f1() { // 函数声明
	console.log('1')
}
console.log(f2) // undefined
f2(); // 报错: Uncaught TypeError: f2 is not a function
var f2 = function() { // 函数表达式
	console.log('2')
}

虽然f1()在function f1 () {...}之前,但是却可以正常执行; 而f2()却会报错, 原因:f2还只是undifined并没有被赋值为一个函数, 因此会报错.

类型三:

当遇到函数和变量同名且都会被提升的情况时, 函数声明的优先级是要大于变量声明的.

console.log(f1); // f f1() {...}
var f1 = "10";
function f1() {
	console.log('我是函数')
}

小试牛刀

function test(arg) {
		console.log(arg);
		var arg = 10;
		function arg() {
				console.log('函数')
		}
		console.log(arg)
}
test('LinDaiDai');
  1. 函数里的形参arg被后面函数声明的arg给覆盖了, 所以第一个打印出的是函数;
  2. 当执行到var arg = 10的时候, arg又被赋值了10, 所以第二个打印出10.

执行上下文栈的变化

var scope = "global";
function checkScope () {
	var scope = "local";
	function fn () {
		return scope;
	}
	return fn();
}
checkScope();

栈的变化是这样的

ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();
var scope = "global"
function checkScope () {
	var scope = "local"
	function fn () {
		return scope
	}
	return fn;
}
checkScope()();

栈的变化是这样的

ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();

由于checkscope是先推入栈中且先执行的, 所以在fn被执行前就被推出了.