摘录于《你不知道的js》(上)
1. 词法作用域
function foo(a) {
var b = a * 2;
function bar(c) {
console.log( a, b, c );
}
bar( b * 3 );
}
foo( 2 ); // 2, 4, 12
词法作用域 对比 动态作用域
function foo() {
console.log( a );
}
function bar() {
var a = 3;
foo();
}
var a = 2;
bar(); // 2
词法作用域让 foo() 中的 a 通过 RHS 引用到了全局作用域中的 a ,因此会输出 2 。
而动态作用域并不关心函数和作用域是如何声明以及在何处声明的,只关心它们从何处调用。换句话说,作用域链是基于调用栈的,而不是代码中的作用域嵌套,所以基于动态作用域,上面代码会输出 3
2. 欺骗作用域
function foo(str, a) {
eval( str );
console.log(a, b);
}
var b = 2;
foo('var b = 3;', 1);
严格模式下失效
3. 函数作用域
function fun() {
var a = 1;
}
fun();
console.log( a ) // ReferenceError: a is not defined
// fun()函数创造的作用域相对于全局作用域是内层的,查找变量的定义的规律是往外层定义域找,而不是向内层定义域找
函数作用域的含义是指,属于这个函数的全部变量都可以在整个函数的范围内使用及复用(事实上在嵌套的作用域中也可以使用)。无法从外部直接访问函数作用域,除非使用闭包
4. 隐藏内部实现
隐藏内部实现也就是私有化,为什么要私有化变量和函数?
- 权限控制
- 规避冲突
如何隐藏内部实现?不急,先介绍几个私有化的工具
- 通过函数作用域
var a = 2;
( function foo(){
var a = 3;
console.log( a ); // 3
})();
console.log( a ); // 2
上面代码使用了函数表达式,避免了 foo 变量污染全局。 (function foo(){ … }) 作为函数表达式意味着 foo 只能在 … 所代表的位置中被访问,外部作用域则不行。
注意函数表达式可以是匿名的,而函数声明则不可以省略函数名
setTimeout( function() {
console.log("I waited 1 second!");
}, 1000 )
但匿名函数表达式有一定的缺点,不太建议使用
- 匿名函数在栈追踪中不会显示出有意义的函数名,使得调试很困难
- 递归调用自身时会很麻烦
- 不利于代码的可读性
建议使用
setTimeout( function timeoutHandler() { // <-- 快看,我有名字了!
console.log( "I waited 1 second!" );
}, 1000 );
立即执行函数表达式 IIFE(Immediately Invoked Function Expression)有两