3.1 函数中的作用域
每声明一个函数都会为其自身创建作用域
函数作用域是指:属于这个函数的全部变量可以在整个函数的范围内使用
3.2 函数内部实现
将变量和函数包裹到一个函数的作用域中,用这个作用域来“隐藏”它们
规避冲突
-
全局命名空间
程序加载多个第三方库,如果没有妥善隐藏私有变量或函数,就会引发冲突
这些库会在全局作用域声明足够独特的变量,被用作库的命名空间
-
模块管理
通过依赖管理器机制将库的标识符显示导入到另一个特定的作用域
3.3 函数作用域
如果函数不需要函数名(或者至少函数名可以不污染所在作用域),并且会自动执行,这将会更理想
var a = 2
(function foo() {
var a = 3
console.log(a) // 3
})()
console.log(a) // 2
包装函数以(function...
开头,会被当做函数表达式而不是一个标准的函数声明来处理
(function foo(){...})
作为函数表达式意味着foo
只能在...所代表的位置中被访问,外部作用域不行;不会污染外部作用域
3.3.1 匿名和具名
函数表达式可以是匿名的,但函数声明不行
setTimeout(function () {
console.log('sss')
}, 1000)
很多库和工具都倾向于这种风格,但是缺点很明显
-
在栈追踪中不会显示有意义的函数名,调试困难
-
自身引用,只能通过
arguments.callee
引用(arguments.callee
在哪一个函数中运行,它就代表哪一个函数)(function(n){ if(n > 1) return n* arguments.callee(n-1); return n; })(10);
- 省略了代码可读性/可理解性
3.3.2 立即执行函数表达式(IIFE Immediately Invoked Function Expression
)
(function foo(){...})()
,第一个()将函数变为表达式,第二个()执行这个函数
-
IIFE
进阶用法将它们当做函数调用并传递参数进去
var a = 2
(function foo(global) {
var a = 3
console.log(a) // 3
console.log(global.a) // 2
})(window)
console.log(a) // 2
- 另一种用法倒置代码的运行顺序,将需要运行的函数放在第二位,在
IIFE
执行之后当做参数传递出去
var a = 2
(function foo(def) {
def(window)
})(function def(global) {
var a = 3
console.log(a) // 3
console.log(global.a) // 2
})
3.4 块作用域
for (var a = 2; a < 10; a++) {
console.log(a)
}
此时的a已被绑定到外部作用域中
块作用域:变量的声明距离使用的地方越近越好
var foo = true
if (foo) {
var bar = foo * 2
console.log(bar)
}
当使用var时,最终都会属于外部作用域;这段代码只是伪装出形式上的块作用域
3.4.1 with
块作用域,用with从对象中创建出的作用域仅在with声明中而非外部作用域有效
3.4.2 try/catch
catch会创建一个块作用域
多个catch用同样的标识符声明错误变量时,静态检查工具还是会发出警告
3.4.3 let
let为其声明的变量隐式地劫持了所在的块作用域
let进行声明的变量不会在块作用域中进行提升,声明的代码被运行之前,声明并不存在
-
垃圾收集
//(function(){ function process(data) {} var someData = {...} // 执行后 someData变量可以被垃圾回收了 click函数的点击回调不需要此变量 process(someData) // 由于click函数形成一个覆盖整个作用域闭包 JavaScript引擎可能依然保存这个变量 var btn = document.getElementById('my_buttn') btn.addEventListener('click', function click(evt) { console.log('clicked') }) // })()
此处的代码应该在某个函数中,点击click函数可能会调用外层函数作用域中的变量,所以形成了闭包,导致可能不会回收
// 使用块作用域解决 { let someData = {...} process(someData) }
-
let循环
for (let i = 0; i < 10; i++) { console.log(i) } console.log(i) // ReferenceError
i绑定在let循环的块中,事实上将其重新绑定在循环的每一个迭代中
ES6
同样引入了const
创建块作用域变量
网友评论