(一)词法作用域,动态作用域的区别
因为 JavaScript 采用的是词法作用域,函数的作用域在函数定义的时候就决定了。
而与词法作用域相对的是动态作用域,函数的作用域是在函数调用的时候才决定的。
function foo() {
console.log( a );
}
function bar() {
var a = 3;
//2 该处是函数调用,词法作用域会进入函数foo中,foo的上级作用域为全局作用域,因此为2;
//javascript采用词法作用域,故此处输出为2,若是采用动态作用域,此处则应该为3.
foo();
(function foo2() {
console.log( a ); //3 该处是函数表达式,词法作用域会进入函数foo2中,foo2的上级作用域为bar(),因此为3;
})();
}
var a = 2;
bar();
词法作用域查找只会查找一级标识符,比如 a、b 和 c。如果代码中引用了 foo.bar.baz, 词法作用域查找只会试图查找 foo标识符,找到这个变量后,对象属性访问规则会分别接 管对 bar 和 baz 属性的访问,因此存在重名变量是相当不安全的。
注意:
//由于浏览器演进的历史遗留问题,在创建带有 id 属性
//的 DOM 元素时也会创建同名的全局变量。例如:页面上有
<div id="foo"></div>
if (typeof foo == "undefined") {//foo已经被Dom声明为全局变量了
foo = 42; // 永远也不会运行
}
console.log( foo ); // HTML元素
//你可能认为只有 JavaScript 代码才能创建全局变量,并且习惯使用 typeof 或 .. in window来检测全局变量。
//但是如上例所示,HTML 页面中的内容也会产生全局变量,并且稍不注意就很容易让全局变量检查错误百出。
(二)函数作用域,块级作用域
javascript中function中才有函数作用域,if与for语句是没有块级作用域的。
块作用域只有在let,try…catch, const, with(不推荐使用)中。(’{}'进行块级显式操作,例如let a=1; => {let a=1;})
- if与for代码串是没有私有作用域的
"use strict";
//if语句
var foo = true, baz = 10;
if (foo) {
var bar = 3;
}
if (baz > bar) {//10 > 3
console.log( baz );//10
}
//for循环同样会污染全局变量
for (var i=0; i<10; i++) {
console.log( i );//1 2 ... 9
}
console.log( i );//10
- 函数表达式与函数声明
区分函数声明和表达式最简单的方法是看 function 关键字出现在声明中的位置(不仅仅是一行代码,而是整个声明中的位置)。
如果 function 是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。
var a = 2;
(function IIFE() {//立即执行函数表达式
var a = 3;
console.log( a ); // 3
})();//IIFE的另一种写法(function IIFE(){}()); 依据个人习惯即可
console.log( a ); // 2
- 块级作用域
{
let a = 2;
console.log( a );
}
console.log( a ); // ReferenceError
//try..catch语句在目前javascript环境中效率十分低下,日后有望提升执行效率
try{
throw 2;
}catch(a){
console.log( a ); // 2
}
console.log( a ); // ReferenceError
(三)提升
- 函数优先
函数声明和变量声明都会被提升。但是一个值得注意的细节是函数会首先被提升,然后才是变量。 - 函数声明会被提升,但是函数表达式却不会被提升
foo(); // 1
var foo;
function foo() {
console.log( 1 );
}
foo = function() {
console.log( 2 );
};
foo();//2
- 重复声明同名函数,会以后声明的为准
foo(); // 3
function foo() {
console.log( 1 );
}
var foo = function() {
console.log( 2 );
};
function foo() {
console.log( 3 );
}