从零开始学_JavaScript_系列(19)——js系列<6>闭包
来源:互联网 发布:电视家直播软件怎么样 编辑:程序博客网 时间:2024/06/05 00:39
(18)闭包
①函数内部的子函数,用到了父函数的变量,就叫做闭包。
②可以为函数保存其执行状态,
③其原理大概是:
首先,假如B函数在A函数的内部,则A为外部函数,B为内部函数,B可以访问A的变量(变量的作用域规定的);
然后,假如在函数内部,如果有一个return,那么在return结束前,这个函数中的变量,其值是维持不变的;
另外需要注意,不能让A函数(外部函数)执行完毕,否则状态无法保留(因为闭包的前提是A函数没有执行完毕),因此不能直接调用A函数,而是需要生成A函数return部分的一个实例。
因为有了这两个前提,于是两个办法:
方法一:我们可以把B函数放在A函数的return之中。return的内容是一个对象{},然后通过new生成这样一个对象,B函数成为这个对象的一个方法;
如代码:
var func = function () { var i = 0; return { getI: function () { return i; }, add: function () { i++; } }}var p = new func();p.add();console.log(p.getI());
此时,p.add()会导致i的值增加,此时i的值变为了1。
方法二:让A函数的return部分,成为一个函数,
但不能直接操纵A函数,因为调用A函数必然会导致A函数的return执行完毕,(因为函数会执行到return结束)
因此,需要将A函数的return部分赋值给另外一个变量C,此时C是A函数return部分(要记得,这个return部分是一个函数)的一个实例(相当于一个构造函数生成一个实例)。
而B函数是这个return部分的构造函数的一个方法,因此这个生成的实例也拥有了B函数这个方法。
如代码:
var func = function () { var i = 0; return function(){ i++; return i; }}var p = new func();console.log(p());console.log(p());
这时,p的调用会导致i加一,因此调用p,输出的值分别为1和2。
ps:无论是哪种方法,假如我们再new一个实例
var q = new func();
其并不会导致i的值在p和q之间共享。
④优点:
按照网易云课堂的说法,闭包可以减少内存使用,提高效率。(个人经过实测,认为没有说服性)
但是经过我实测,大部分方法和闭包差不多,比闭包慢的一个方法我后面列出。
如:
var func = function () { var i = 0; return function () { i++; return i; }}var starttime = new Date();var p = new func();for(var i=0;i<1000000;i++){ p();}console.log(p())console.log(new Date()-starttime);
计算一百万次,消耗时间大概10毫秒左右。
这是我看网易云课堂给的一个示例(我自己略有修改);
function sum(i) { var add = function (i) { i++; return i; } return add(i);}var starttime = new Date();for (var i = 0; i < 1000000; i++) { sum(1);}console.log(sum(1));console.log(new Date() - starttime);
其时间耗时为30毫秒左右。
但这个例子事实上返回了2次,所以我觉得不能证明闭包更快。例如,常规写法:
function sum(i) { i++; return i;}
其耗时就和闭包差不多。我把计算量增加10倍(到一千万次),其时间和上面那个所说的比较慢的方法是差不多的(30ms左右)。
因此个人认为,应该是双重return所导致了耗时增多。
⑤闭包的利用:
假设我们有N个按钮,想点击第x按钮时,输出x。
var m = function () { function cli(i){ return function(){ console.log(i); } } for (var i = 1; i < 3; i++) { document.getElementById("test" + i).onclick = cli(i); }}m();
初始时,不输出任何变量。点击id="test1",输出1,点击id="test2",输出2。
假如逐个绑定,那么是不能达成这种目的的。例如:
for (var i = 1; i < 3; i++) { $("#test" + i).click(function () { console.log(i) });}
并不能如我们所愿那样,点击第一个按钮便输出1,事实上,无论点哪个,输出都是3。
- 从零开始学_JavaScript_系列(19)——js系列<6>闭包
- 从零开始学_JavaScript_系列(15)——js系列<3>(转为字符串,截取字符串)
- 从零开始学_JavaScript_系列(16)——js系列<5>(正则表达式)
- 从零开始学_JavaScript_系列(30)——NodeList
- 从零开始学_JavaScript_系列(32)——事件广播
- 从零开始学_JavaScript_系列(43)——Symbol简述
- 从零开始学_JavaScript_系列(47)——Reflect
- 从零开始学_JavaScript_系列(58)——Thunk函数
- 从零开始学_JavaScript_系列(59)——async函数
- 从零开始学_JavaScript_系列(42)——简述js的八种继承方式
- 从零开始学_JavaScript_系列(17)——dojo(6)(声明一个类declare)
- 从零开始学_JavaScript_系列(八)——js系列<2>(事件触发顺序、文本读取、js编写ajax、输入验证、下拉菜单)
- 从零开始学_JavaScript_系列(三)——CSS相关(基础、选择器、position、div)
- 从零开始学_JavaScript_系列(14)——dojo(7)(饼图,BorderContainer,hashchange,弹窗)
- 从零开始学_JavaScript_系列(18)——dojo(7)(dojo中类的继承)
- 从零开始学_JavaScript_系列(46)——Proxy代理(给对象加壳)
- 从零开始学_JavaScript_系列(23)——css<5>滚动条,Tab,spellcheck,img放置
- 从零开始学_JavaScript_系列(24)——查看对象属性,合并数组
- HTML协议
- 第9课:Spark Streaming源码解读之Receiver在Driver的精妙实现全生命周期彻底研究和思考
- Groovy基础
- Java IO - ByteArrayInputStream&ByteArrayOutputStream
- 3、Spring IOC&DI使用
- 从零开始学_JavaScript_系列(19)——js系列<6>闭包
- 训练自己haar-like特征分类器并识别物体(1)
- Go语言实现将[]string转化为[]byte
- CodingNet - Learning - 4
- Qt学习笔记之数据库的访问
- Java性能优化权威指南-第一章 策略、方法和方法论
- UVa10082(常量数组的妙用)
- poj 2406
- Python入门:函数及使用函数进行面向过程封装