函数是JS常见的作用域单元。本质上,声明在一个函数内部的变量或者函数所在的处的作用域中“隐藏”起来,这是有意为之的良好软件的设计原则。
块作用域指的是变量和函数不仅可以属于所处的作用域,也可以属于某个代码块(通常指 {...} 内部)。
函数作用域
(function foo(a) {
function doSomething(a) {
return a - 1;
}
var b;
b = a + doSomething(a * 2);
console.log(b * 3); // 15
})(2);
函数表达式形式函数 foo 是立即执行函数(IIFE)也是一个具名函数,将 doSomething 放在函数内部是规避冲突,实现“私有”内容。
以 function 关键字是声明中的第一个词,那么就是函数声明。否则就是函数表达式。
匿名函数的缺点
- 在栈中不会显示出有意义的函数名,使得调试很困难。
- 如果没有函数名,当函数调用自身时只能使用已经过期的 arguments.callee 引用。
- 代码可读性/可理解性差
块作用域
for (var i = 0; i< 3; i++) {
console.log(i);
}
形式上带有块作用域风格,但是 i 还是全局作用域,可以使用 ES6 中的 let 形成真正的块作用域。
此外还有 if , try...catch(err){}, with 对象
var foo = true;
if (foo) {
let bar = foo * 2;
bar = something(bar);
console.log(bar);
}
console.log(bar); // ReferenceError
const 类似;
try {
undefined();
}
catch (err) {
console.log(err); // 正常执行
}
console.log(err) // ReferenceError: err not found
catch 也会形成一个块作用域
垃圾收集
function process(data) {...}
var some = {...}
process(some);
var btn = document.getElmentById("myButton");
btn.addEventLister("click", function click(evt){
console.log("btn click");
}, false);
click 函数并没有用到 some 变量,理论上 process 执行后,可以被垃圾回收,但是由于 click 函数形成一个覆盖整个作用域的闭包,JS引擎kennel依然保存这个结构,但是修改成:
{
let some = {...};
process(some);
}
就可以在完事之后被销毁!