一、闭包 closure
1. 复习作用域
作用域是可访问变量的有效范围。
- 全局作用域
作用于所有代码执行的环境 (整个script标签内部) 或独立的js文件。
- 局部作用域(函数作用域)
作用于函数内的代码环境,就是局部作用域。
- 全局变量
在全局作用域下(函数外部)声明的变量叫做全局变量。
- 网页中所有脚本和函数均可使用全局变量
- 如果变量在函数内没有声明(没有使用 var 关键字),该变量为全局变量
- 局部变量
在局部作用域下(函数内部)声明的变量叫做局部变量。
- 局部变量只能在该函数内部使用。
- 局部变量在函数开始执行时创建,函数执行完后局部变量会自动销毁。
- 因为局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。
- 函数参数只在函数内起作用,是局部变量。
- 全局变量/函数,可以覆盖window对象的变量/函数。
// 全局变量/函数,可以覆盖window对象的变量/函数。
window.variable = 'window对象的变量';
var variable = '全局变量';
console.log(variable); // 返回:全局变量
- 局部变量/函数,可以覆盖window对象的变量和全局变量/window对象的函数和全局变量的函数。
// 局部变量/函数,可以覆盖window对象的变量和全局变量/window对象的函数和全局变量的函数。
function test4() {
var variable = '局部变量';
console.log(variable); // 返回:局部变量
}
test4();
2. 外部怎么读取函数内的局部变量?
出于种种原因,我们有时候需要得到函数内的局部变量。
但是,前面已经说过了,正常情况下,这是办不到的,
只有通过变通方法才能实现:那就是在函数的内部,再定义一个函数。
function fn1() {
var num = 10;
function fn2() {
console.log(num); // 10
}
}
在上面的代码中,函数fn2就被包括在函数fn1内部。这时fn1内部的所有局部变量,对fn2都是可见的。但是反过来就不行,fn2内部的局部变量,对fn1就是不可见的。
这就是Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然fn2可以读取fn1中的局部变量,那么只要把fn2作为返回值,我们不就可以在fn1外部读取它的内部变量了嘛。
function fn1() {
// fn1函数就是闭包
var num = 10;
function fn2() {
return num;
}
return fn2; // fn2没有调用不执行,fn1的返回值是fn2函数
}
var f = fn1(); // 调用fn1,得到返回值--fn2函数,把它保存到变量f中
f(); // 此时f保存的是fn2函数,调用该函数,就能得到fn2的返回值--num
console.log(f()); // 10
3. 闭包的概念
闭包
就是能够读取其他函数内部变量的函数。(变量所在的函数就是闭包函数)
例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成——“定义在一个函数内部的函数“。
在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
注意:
-
闭包不是函数套函数
是因为需要局部变量,所以才把 num 放在一个函数里,如果不把 num 放在一个函数里,num 就是一个全局变量了,达不到使用闭包的目的——隐藏变量
-
闭包中的函数是可以没有return语句
因为如果不 return,你就无法使用这个闭包。把 return fn2 改成 window.fn2 = fn2 也是一样的,只要让外面可以访问到这个 fn2 函数就行了。
所以 return fn2 只是为了 fn2 能被使用,也跟闭包无关。
4. 闭包的作用
闭包可以用在许多地方。它的最大用处有两个:
- 可以读取函数内部的变量(延伸变量的作用范围,但仅能读取访问,不能修改)
- 让这些变量的值始终保持在内存中
5. 闭包的使用案例
① 点击 li 输出当前 li 的索引号
<ul class="nav">
<li>项目1</li>
<li>项目2</li>
<li>项目3</li>
<li>项目4</li>
</ul>