JavaScript 的函数作用域是指在函数内声明的所有变量在函数体内是始终可见的;有意思的是这意味着变量在声明之前甚至就可以使用,JavaScript 的这个特性被非正式的称为“声明提前(hoisting)”。先看下面这段代码:
var demo=1;
!function () {
console.log(demo); // undefined
if(typeof demo ==="undefined"){
var demo=2;
}
console.log(demo); // 2
}()
要清楚这一段代码这样执行的结果,首先要非常熟悉 JavaScript 的 hoisting。
Javascript Hoisting:In javascript, every variable declaration is hoisted to the top of its declaration context.
在 Javascript 语言中,变量的声明(注意不包含变量赋值)会被提升(置顶)到声明所在作用域的最顶端。在上面的哪个例子当中,自运行函数外面声明的那个 demo
相当于一个全局变量,函数体内部有一个 var demo=2
,虽然还没执行到这一步,但是 JavaScript 在编译的时候会将函数体内部的 demo 提升至当前作用域的最顶部初始化(不赋值),局部变量优先级高于全局变量。因此第一个是 undefined,然后执行到条件中,给 demo 赋值 2,因此第二个的结果是 2。上面的例子等同于这样:
var demo=1;
!function () {
var demo;
console.log(demo); // undefined
if(typeof demo ==="undefined"){
demo=2;
}
console.log(demo); // 2
}()
我们在编写 JavaScript 脚本的时候应当注意一个习惯,在当前作用域的最顶端声明需要的变量,后面直接赋值。
JavaScript 的"hoisting"不仅仅有变量,针对函数也是有类似的效果,来看看 JavaScript 的函数提升部分先。
Javascript Function 有两种类型:
函数声明(Function Declaration);
// 函数声明
function demo(n){
return n;
}
函数表达式(Function Expression)
// 函数表达式
var demo2=function (m) {
return m
}
上面的代码看起来很类似,感觉也没什么太大差别。但实际上,Javascript 函数上的一个“陷阱”就体现在 Javascript 两种类型的函数定义上。
console.log(demo(2)); // 2
function demo(n) {
return n;
}
console.log(demo2(3)) // Uncaught TypeError: demo2 is not a function
var demo2=function (m) {
return m
}
用函数声明创建的函数 demo 可以在 demo 定义之前就进行调用;而用函数表达式创建的 demo1 函数不能在 demo1 被赋值之前进行调用。
为什么差距这么大呢,用函数声明创建的函数可以在 函数解析后调用 (解析时进行等逻辑处理)。而用函数表达式创建的函数是在运行时进行赋值,且要等到表达式赋值完成后才能调用。
不过函数的提升,并不能超过 {}
的作用域,请看下面的示例:
var demo;
console.log(typeof demo1) // undefined
console.log(typeof demo); //undefined
if(true){
function demo1() {
console.log("demo1");
}
demo = function() {
console.log("demo2");
}
}
console.log(typeof demo1) // function
console.log(typeof demo); //function
demo1(); // demo1
demo(); //demo2
关于 hoisting 的介绍就先到这里,在写 JavaScript 脚本时,养成良好的代码习惯,会有意想不到的收获。