【代码】js闭包
闭包
-
闭包的英文是?
closure
-
函数为什么要有闭包特征呢?
-
如果 A 函数中 声明了 B函数, B函数使用了 A函数作用域的属性
常规操作: A函数执行结束后,会自动销毁其
函数作用域对象
, 导致 x 被销毁, B函数会无法使用所以 B函数需要把 A函数的作用域保存在自身的 scopes 属性里
B.scopes = [ 0: Closure A {x: 10} ]
即 A函数作用域 是 B函数的闭包
内层
函数, 保存外层函数
作为自己的闭包
-
-
利用闭包 我们可以做什么?
-
防止全局污染
-
把函数使用的变量 不在全局中声明, 而是在父级函数中声明, 存储在函数作用域
固定格式:
var 函数名 = (function(){ var 变量 = 值 return function(){ } })()
-
-
闭包的缺点?
消耗内存
- 常规: 函数执行完毕后, 会自动销毁开辟的
函数作用域
对象 - 但是: 因为闭包的存在, 导致对象无法被销毁, 会一直存在于内存中
- 常规: 函数执行完毕后, 会自动销毁开辟的
-
难以理解??
- 在群众的呼声中:
2015
年出版的 ES6 中, 提供了 let/const 搭配 块级/脚本 两个新生作用域, 比闭包使用更加方便快捷, 用于代替闭包 - 但是: 你开发的软件为了兼容 2015年之前的浏览器, 例如 IE8, 就无法使用新特性, 只能用闭包
- 在群众的呼声中:
<!DOCTYPE html>
<html lang="en">
<body>
<!--
函数作用域:函数在 调用时 `临时` 生成的对象, 用于存储函数中生成的变量
函数作用域 在函数运行完毕后, 会自动销毁 来节省内存
-->
<script>
// 函数对象中有一个属性: scopes -- 作用域们
// 以 b 函数为例:
// -- 第一个作用域: a函数作用域 -- 闭包
// -- 第二个作用域: 全局作用域
// 总结: 函数运行时会产生临时的函数作用域, 如果这个函数作用域被 子函数保存了, 则称为闭包
// 具体: a函数作用域, 被 b函数保存在 scopes 属性里, 就说: a函数作用域是b的闭包
// 为什么: b函数使用了 a 函数作用域中的变量, 假设b函数没有存, 则 a函数执行结束后会自动释放, 导致 b 函数中的 abb 无法使用
function a() {
var abb = 'ABB' // 此变量存储在 a函数作用域中
function b() {
// 根据上午提到的作用域链: b函数中没有变量 abb,
// 所以查找到 上级a函数作用域的abb
console.log(abb);
}
return b
}
// b存储的是 a函数的返回值, 即 b 函数
var b = a() // 调用函数a, 生成一个函数作用域
b()
console.dir(b) // dir: 直接输出函数对象, 与log不同
// b函数使用了 abb 变量, 此变量存储在 a函数的作用域里, a函数执行完毕后, 则会自动释放函数作用域
// 问: b() 调用时, 还能打印出 abb 变量么??
var a = 5 //把 5 存放在变量 a 中
var scopes = {}
scopes.c = 10 //把10存储在 scopes对象的属性c里
// 赋值操作 : 动名词结构
var a = 10 //代表把 10 存储在 变量 a 中
// a 中存储了 10 : 描述
// 例如:
// 涛哥想健身, 去健身房办卡
// 过了几天后: 健身房倒闭了 -- 办卡无效
// 闭包:
// 涛哥想健身, 去健身房买了一套设备, 带回家
// 健身房倒闭, 也没关系
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<body>
<script>
// 1. 函数 调用时, 会形成临时的作用域对象, 在函数运行结束后会销毁, 节省内存
function a() {
var x = 10
function b() {
console.log(x)
}
return b
}
var b = a() //调用a函数, 生成函数作用域,
// 函数执行完毕后 变量x 就会销毁
b() // a()已经执行完毕, 理论上其生成的变量x 会销毁
// b() 调用时, 还能读取到 x 吗?
console.dir(b);
// b函数会把 用到的变量所在的作用域保存在自身的 Scopes 属性里
// 为了后期 调用时能够正常使用, 不怕销毁
// a函数作用域销毁了吗?
// 没有. 以为被b函数保存了
// JS垃圾回收机制: 没用的东西会自动销毁
console.log(window);
</script>
</body>
</html>
闭包应用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>闭包的应用 14:48</title>
</head>
<body>
<script>
// ES6之前的 旧时代, 有哪些作用域? 就两种
// 全局 + 局部(函数)
// 自定义变量存储在 全局里, 会造成全局变量污染
// 那存放在哪里合适?? 放局部 -- 即函数作用域
// 记录函数调用的次数
// 变量n 专为函数 a 而生, 存储在全局区就不合理 -- 公有的不安全 也 污染全局
// 所以: 不能放全局作用
// 就剩下局部作用域能放 -- 函数触发后生成
// 思路: 利用 匿名函数自调用 快速形成函数作用域
// (function(){})() -- 下课去回顾 亮亮的 匿名函数自调用
var n = 0
function a() {
n++
console.log('n:', n);
}
a()
a()
a()
// 声明函数的多种方案:
// 1. 最普通
function c() { }
// 2. 匿名函数赋值给变量
var c = function () { }
// 3. 通过触发函数, 返回一个函数
// d函数触发后, 返回一个函数交给变量 c
var d = function () {
// var x = function () { }
// return x
return function () { }
}
var c = d()
// 4. 匿名函数自调用格式 (匿名函数)()
var c = (function () {
return function () { }
})()
// 4. 利用匿名函数 快速完成
// 改写成匿名函数自调用
var c = (function () {
// var: 在哪个作用域中执行, 变量就提升到哪个作用域
// 在 script 中用var, 就是全局window里
// 在 函数{} 中用var, 就是函数作用域的
var n = 0
return function () {
n++
console.log('c的n:', n);
}
})()
// (匿名函数)()
// 思考: n是记录函数c调用次数的, 他是存在全局吗?
c()
c()
c()
c()
c()
console.dir(c)
</script>
</body>
</html>