《JavaScript设计模式与开发》笔记 4.闭包
Posted SmarTom
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《JavaScript设计模式与开发》笔记 4.闭包相关的知识,希望对你有一定的参考价值。
- 1.变量的作用域
- 2.变量的生存周期
- 3.闭包更多作用
- 1.封装变量
- 2.延续局部变量寿命
- 4.闭包和面向对象设计
- 5.闭包和内存管理
1.变量的作用域
var func = function(){ var a = 1; } func(); console.log(a); //输出undefined var a = 1; fun1 = function(){ var b = 2; fun2 = function(){ var c = 3; console.log(b); //输出 2 console.log(a); //输出 1 } fun2(); console.log(c); //输出undefined } fun1();
2.变量的生存周期
var func = function(){ var a = 1; return function(){ a++; console.log(a); } } var f = func(); f(); // 输出1 f(); // 输出2 f(); // 输出3 f(); // 输出4
当退出函数后,局部变量a并没有消失,而是似乎一致在某个地方存活着。这是因为当执行var f = func();
时,f返回了一个匿名函数的引用
,它可以访问到func()被调用时产生的环境,而局部变量a一致处于这个环境里。既然局部变量所在的环境还能被外界,这个局部变量就有了不被销毁的理由。这里产生了一个闭包结构,局部变量的声明看起来被延续了。
既然f返回了一个匿名的函数引用,那么下面的也符合闭包
var func = (function(){ var a = 1; return function(){ a++; console.log(a); } })(); f(); // 输出1 f(); // 输出2 f(); // 输出3 f(); // 输出4
再次变种为引用对象的方式
var func = { a :0, call:function(){ this.a++; console.log(this.a); } } func.call(); //输出1 func.call(); //输出2 func.call(); //输出3
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div>1</div> <div>2</div> <div>3</div> <div>4</div> <div>5</div> <script> var nodes = document.getElementsByTagName(‘div‘); for(var i =0;i<nodes.length;i++){ nodes[i].onclick=function(){ alert(i); } } </script> </body> </html>
测试这段代码就会发现,无论点击哪个div,最后弹出的结构都是5,。这是因为div节点onclick事件是被异步出发的,当事件出发的时候,for循环早已结束,此时变量i的值已经是5,所以在div的onclick事件函数中顺着作用域链从内到外查找变量i时,查找到的值总是5。
解决方法是在闭包作用的帮助下,把每次循环的i值都封装起来。(就是把每个点击事件都独立放在内存里面。)
var nodes = document.getElementsByTagName(‘div‘); for(var i =0;i<nodes.length;i++){ (function(i){ nodes[i].onclick=function(){ alert(i); } })(i); }
3.闭包更多作用
1.封装变量
闭包可以帮组我们封装私有变量”
var mult = function(){ var a = 1; for(var i =0;i <arguments.length;i++){ a = a*arguments[i]; } return a; } console.log(mult(1,2,5));
mult函数接受一些number类型的函数,并返回这些参数的乘积。现在我们觉得对于这些那些参数来说,每次都进行计算是一种浪费,我们可以加入缓存机制来提高这个函数的性能。
var cache={}; var mult = function(){ var args = Array.prototype.join.call(arguments,‘,‘); //将参数对象变成一个字符串 args 1,2,3 if(cache[args]){ console.log(‘执行缓存‘); return cache[args]; } var a = 1; for(var i=0;i<arguments.length;i++){ a = a*arguments[i]; } return cache[args] = a; } console.log(mult(1,2,3)); //进行计算 console.log(mult(1,2,3)); //执行缓存,不用计算
继续封装减少页面全局变量
var mult = (function(){ var cache={}; //私有变量 return function(){ var args = Array.prototype.join.call(arguments,‘,‘); if(cache[args]){ return cache[args]; } var a = 1; for(var i=0;i<arguments.length;i++){ a = a*arguments[i]; } return cache[args] = a; } })() console.log(mult(1,2,3)); //进行计算 console.log(mult(1,2,3)); //执行缓存,不用计算
提炼代码,独立模块
var mult = (function(){ var cache={}; //私有变量 var cala = function(){ var a = 1; for(var i=0;i<arguments.length;i++){ a = a*arguments[i]; } return a; } return function(){ var args = Array.prototype.join.call(arguments,‘,‘); if(cache[args]){ return cache[args]; } return cache[args] = cala.apply(null,arguments); } })(); console.log(mult(1,2,3)); //进行计算 console.log(mult(1,2,3)); //执行缓存,不用计算
2.延续局部变量的寿命
var report = function(src){ var img = new Image(); img.src = src; }
利用闭包来延续img的使用
var report = (function(){ var imgs = []; //img就留在了内存里面 return function(src){ var img = new Image(); imgs.push(img); img.src = src; } })();
4.闭包和面向对象
闭包:
var extent = function(){ var value =0; return { call:function(){ value++; console.log(value); } } } var extent = extent(); extent.call(); //输出1 extent.call(); //输出2 extent.call(); //输出3
如果换成面向对象的写法就是
var extent = { value :0, call:function(){ this.value++; console.log(this.value); } } extent.call(); //输出1 extent.call(); //输出2 extent.call(); //输出3
5.闭包和内存管理
闭包是一个非常强大的特性,但人民对其他诸多误解,一种耸人听闻的说法就是闭包会造成内存泄漏。如果在将来需要回收这些变量,我们可以手动吧这些变量设置为null。
以上是关于《JavaScript设计模式与开发》笔记 4.闭包的主要内容,如果未能解决你的问题,请参考以下文章
JavaScript设计模式与开发实践---读书笔记 策略模式
《JavaScript设计模式与开发》笔记 2.this指针
javascript设计模式与开发实践阅读笔记—— this,闭包与高阶函数