Web前端入门第 66 问:JavaScript 作用域应用场景(闭包)
什么是作用域?
就像孙悟空给唐僧画个圈圈一样,这个圈圈就可以称之为作用域,这个比喻可能不太形象。
作用域和孙悟空的圈圈还是有点区别,作用域内部可以获得作用域外部的变量,而内部的变量无法逃逸到作用域外面,如果逃逸出去了,那就造成内存泄漏了,程序将会出现崩溃!
全局作用域
可以理解为就是放在 JS 最外层的那部分内容,比如:变量、函数、对象等等。凡是定义在最外层的内容,都是属于全局作用域,在全局作用域下的任意函数都可访问到这部分内容。
var wechat = '前端路引';
(function () {
function test1 () {
console.log(wechat);
}
test1(); // 输出 '前端路引'
})()
以上代码用到了自执行函数 (function () {})(),作用就是为了创建一个局部作用域,避免变量污染全局作用域,在很多优秀的插件中都能看到它的影子。
上面代码中的 wechat 变量,就是全局作用域下的变量,test1 函数定义在全局作用域内部,所以对于 test1 函数来说,全局作用域中的变量它都是可以访问的。
函数作用域
也可称之为 局部作用域,生效范围在函数内部,在函数外面无法访问。
function test2 () {
var wechat = '前端路引';
console.log(wechat);
}
test2();
console.log(wechat); // 报错:wechat is not defined
wechat 变量定义在函数内部,便是函数作用域,在函数外面无法访问,这就是局部作用域的特性。
块级作用域
ES6 新增的玩法,一对花括号圈出来的区域,就称之为块级作用域。需注意 var 声明的变量是不存在块级作用域的,只有 let 和 const 才存在块级作用域。
{
var wechat1 = '前端路引';
let wechat2 = '前端路引';
const wechat3 = '前端路引';
}
console.log(wechat1); // 输出:前端路引
console.log(wechat2); // 报错:wechat2 is not defined
console.log(wechat3); // 报错:wechat3 is not defined
或者是像 if 条件判断的花括号一样也存在块级作用域:
if (true) {
var wechat1 = '前端路引';
let wechat2 = '前端路引';
const wechat3 = '前端路引';
}
console.log(wechat1); // 输出:前端路引
console.log(wechat2); // 报错:wechat2 is not defined
console.log(wechat3); // 报错:wechat3 is not defined
当然其他 while、for、do 等循环语句也存在块级作用域。
作用域链
作用域链总是从内部开始,一圈一圈往外部查找,比如:
let globalVal = '全局';
function outer() {
let outerVal = '外部';
function inner() {
let innerVal = '内部';
console.log(innerVal); // '内部'(当前作用域)
console.log(outerVal); // '外部'(外层作用域)
console.log(globalVal); // '全局'(全局作用域)
console.log(wechat); // 报错:ReferenceError: wechat is not defined
}
inner();
}
outer();
当内部找不到的时候,就往外一层查找,外层找不到就在全局作用域找,如果全局作用域也找不到,就会报错 ReferenceError。
闭包使用
基于作用域的特性,就有前辈们发现了 闭包 的用法,闭包这个东东,用得好呢可以说是一把利剑,用得不好那就要反噬主人了。
闭包 的用处就是搭建函数内部和外部的桥梁,使函数外部可以访问到函数内部的变量。