Description
这个issue试图阐述JavaScript这门语言的3个难点:声明提升、作用域(链)和this
。
1. 声明提升
大部分编程语言都是先声明变量再使用,但在JS中,事情有些不一样:
console.log(a); // undefined
var a = 1;
上面是合法的JS代码,正常输出undefined
而不是报错Uncaught ReferenceError: a is not defined
。为什么?就是因为声明提升(hoisting)。
1.1 变量声明
参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
语法:
var varname1 [= value1 [, varname2 [, varname3 ... [, varnameN]]]];
变量名可以是任意合法标识符;值可以是任意合法表达式。
重点:
- 变量声明,不管在哪里发生(声明),都会在任意代码执行前处理。(Variable declarations, wherever they occur, are processed before any code is executed. )。
- 以
var
声明的变量的作用域就是当前执行上下文(execution context),即某个函数,或者全局作用域(声明在函数外)。 - 赋值给未声明的变量,当执行时会隐式创建全局变量(成为global的属性)。
声明变量和未声明变量的区别:
- 声明变量通常是局部的,未声明变量通常全局的。
- 声明变量在任意代码执行前创建,未声明变量直到赋值时才存在。
- 声明变量是execution context(function/global)的non-configurable 属性,未声明变量则是configurable。
在es5 strict mode
,赋值给未声明的变量将报错。
1.2 定义函数(Defining functions)
定义一个函数有两种方式:函数声明(function definition/declaration/statement)和函数表达式( function expression)。
1.2.1 function definition
语法:function name(arguments) {}
对参数而言,primitive parameter是传值,对象是传引用。
1.2.2 function expression
语法:var fun = function (arguments) {}
函数表达式中函数可以不需要名字,即匿名函数。
1.2.3 其它
还可以用 Function
构造函数来创建函数。
在函数内部引用函数本身有3种方式。比如var foo = function bar(){};
- 函数名字,即
bar()
arguments.callee()
foo()
1.3 声明提升
1.1
提到,var
声明的变量会在任意代码执行前处理,这意味着在任意地方声明变量都等同于在顶部声明——即声明提升。1.2
特意强调了函数定义,因为声明提升中,需要综合考虑一般变量和函数。
在JavaScript中,一个变量名进入作用域的方式有 4 种:
- Language-defined:所有的作用域默认都会给出
this
和arguments
两个变量名(global没有arguments
); - Formal parameters(形参):函数有形参,形参会添加到函数的作用域中;
- Function declarations(函数声明):如
function foo() {}
; - Variable declarations(变量声明):如
var foo
,包括_函数表达式_。
函数声明和变量声明总是会被移动(即hoist)到它们所在的作用域的顶部(这对你是透明的)。
而变量的解析顺序(优先级),与变量进入作用域的4种方式的顺序一致。
一个详细的例子:
function testOrder(arg) {
console.log(arg); // arg是形参,不会被重新定义
console.log(a); // 因为函数声明比变量声明优先级高,所以这里a是函数
var arg = 'hello'; // var arg;变量声明被忽略, arg = 'hello'被执行
var a = 10; // var a;被忽视; a = 10被执行,a变成number
function a() {
console.log('fun');
} // 被提升到作用域顶部
console.log(a); // 输出10
console.log(arg); // 输出hello
};
testOrder('hi');
/* 输出:
hi
function a() {
console.log('fun');
}
10
hello
*/