forked from IT-xzy/WEB-NEW
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path1011-js-4.html
235 lines (224 loc) · 10.1 KB
/
1011-js-4.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>闭包是什么、用处如何</title>
<link rel="stylesheet" href="./css/reveal/reveal.css">
<!-- PPT主题,可以在/css/reveal/theme/中选择其他主题,目前暂时只能使用该模板 -->
<link rel="stylesheet" href="./css/reveal/theme/ptt.css">
<!-- syntax highlighting 代码高亮主题 -->
<link rel="stylesheet" href="./lib/reveal/css/zenburn.css">
<!-- 打印和PDF输出样式 -->
<script>
var link = document.createElement( 'link' );
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = window.location.search.match( /print-pdf/gi ) ? './css/reveal/print/pdf.css' : './css/reveal/print/paper.css';
document.getElementsByTagName( 'head' )[0].appendChild( link );
</script>
</head>
<body>
<img src="./img/demo/logo.png" alt="" usemap="#pttmap" class="base-logo">
<map name="pttmap">
<area shape="rect" coords="0,0,276,58" href="http://www.jnshu.com" alt="" target="_blank"/>
</map>
<div class="reveal">
<div class="slides">
<section>
<h2>【js-04】闭包是什么?用处如何?</h2>
<h3>【萌新】小课堂</h3>
<p style="text-align: center;">分享人:吴泽华</p>
</section>
<section>
<p>目录</p>
<p>1.背景介绍</p>
<p>2.知识剖析</p>
<p>3.常见问题</p>
<p>4.解决方案</p>
<p>5.编码实战</p>
<p>6.扩展思考</p>
<p>7.参考文献</p>
<p>8.更多讨论</p>
</section>
<section>
<h3>1.背景介绍</h3>
</section>
<section>
<p>闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。闭包是「函数」和「函数内部能访问到的变量」(也叫环境)的总和。</p>
</section>
<section>
<h3>2.知识剖析</h3>
</section>
<section>
<section>
<p>闭包可以用在许多地方。它的最大用处有两个:</p>
<p>1.可以读取函数内部的变量</p>
<p>2.让这些变量的值始终保存在内存中</p>
</section>
<section>
<p style="text-align: left">读取函数内部的变量的例子:</p>
<pre>
<code>
/*使用闭包读取函数内部的变量*/
function f1(){
n = 999;
function f2(){
alert(n);
}
return f2;
}
var result = f1();
result(); //999
</code>
</pre>
</section>
<section>
<p style="text-align: left;text-indent: 2rem">在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗</p>
</section>
<section>
<p style="text-align: left">变量的值始终保存在内存的例子:</p>
<pre>
<code>
/*使用闭包让函数内部的变量储存在内存中*/
function f1(){
n = 999;
nAdd = function(){
n+=1;
};
function f2(){
alert(n);
}
return f2;
}
var result = f1();
result();//999
nAdd();
result();//1000
</code>
</pre>
</section>
<section>
<p style="text-align: left;text-indent: 2rem">在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。因为f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被<a href="http://eatpockyboy.blog.163.com/blog/static/1167346402011321423929/" target="_blank">垃圾回收机制(garbage collection)</a>回收。这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个<a href="http://www.itxueyuan.org/view/6314.html" target="_blank">匿名函数(anonymous function)</a>,而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。</p>
</section>
</section>
<section>
<h3>3.常见问题</h3>
</section>
<section>
<pre>
<code>
window.onload = function(){
var el = document.getElementById("id");
el.onclick = function(){
alert(el.id);
}
}
</code>
</pre>
<p>
这段代码为什么会造成内存泄露?
</p>
</section>
<section>
<h3>4.解决方案</h3>
</section>
<section>
<p style="text-align: left">
内存泄漏的原因:执行这段代码的时候,将匿名函数对象赋值给el的onclick属性;然后匿名函数内部又引用了el对象,存在循环引用,所以不能被垃圾回收机制回收;
</p>
<p style="text-align: left">修改后:</p>
<pre>
<code>
window.onload = function(){
var el = document.getElementById("id");
var id = el.id; //解除循环引用
el.onclick = function(){
alert(id);
}
el = null; // 将闭包引用的外部函数中活动对象清除
}
</code>
</pre>
</section>
<section>
<h3>5.编码实战</h3>
</section>
<section>
<section>
<p>点击按钮会弹出相应的数字0、1、2、3、4</p>
<pre>
<code>
function init) {
var pAry = document.getElementsByTagName("button");
for( var i=0; i< pAry.length; i++ ) {
(function(arg){
pAry[i].onclick = function() {
alert(arg);
};
})(i);//调用时参数
}
}
</code>
</pre>
<p>
思路:加一层闭包,i以局部变量形式传递给内存函数,在js任务4中的杀人游戏选中的身份死亡有用到。
</p>
</section>
</section>
<section>
<h3>6.扩展思考</h3>
</section>
<section>
<p>在闭包中的this指向问题</p>
</section>
<section>
<h3>7.参考文献</h3>
</section>
<section>
<p style="text-align: left">参考一:<a href="http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html" target="_blank">阮一峰的网络日志:学习Javascript闭包</a></p>
<p style="text-align: left">参考二:<a href="https://zhuanlan.zhihu.com/p/22486908#!" target="_blank">逼乎专栏:JS 中的闭包是什么?</a></p>
<p style="text-align: left">参考三:<a href="https://segmentfault.com/a/1190000002778015" target="_blank">segmentfault:JS进阶之闭包</a></p>
<!--<p style="text-align: left"><a href="./demo/js-04-JSClosure/Closure.html" target="_blank">本次小课堂demo</a></p>-->
</section>
<section>
<h3>8.更多讨论</h3>
</section>
<section>
<p>垃圾回收机制(garbage collection)以及匿名函数</p>
</section>
<section>
<h4>感谢观看</h4>
<p><small>BY : 吴泽华 </small></p>
</section>
</div>
</div>
<script src="./lib/reveal/js/head.min.js"></script>
<script src="./lib/reveal/reveal.js"></script>
<script>
// 以下为常见配置属性的默认值
// {
// controls: true, // 是否在右下角展示控制条
// progress: true, // 是否显示演示的进度条
// slideNumber: false, // 是否显示当前幻灯片的页数编号,也可以使用代码slideNumber: 'c / t' ,表示当前页/总页数。
// history: false, // 是否将每个幻灯片改变加入到浏览器的历史记录中去
// keyboard: true, // 是否启用键盘快捷键来导航
// overview: true, // 是否启用幻灯片的概览模式,可使用"Esc"或"o"键来切换概览模式
// center: true, // 是否将幻灯片垂直居中
// touch: true, // 是否在触屏设备上启用触
Reveal.initialize({
history: true,
dependencies: [
{src: './plugin/markdown/marked.js'},
{src: './plugin/markdown/markdown.js'},
{src: './plugin/notes/notes.js', async: true},
{
src: './plugin/highlight/highlight.js', async: true, callback: function () {
hljs.initHighlightingOnLoad();
}
}
]
});
</script>
</body>
</html>