【有书共读08】JS核心技术开发解密读书笔记04
JS核心技术开发解密第六章读书笔记
第六章 闭包
一、概念
1、什么是闭包
闭包由两部分组成-执行上下文A和在该执行上下文中创建的函数B,当B执行时,如果访问了A中的变量对象的值就会产生闭包。我们遵从Chrome的命名方式,将A叫做闭包。
//闭包 function foo() { var a = 20; var b = 30; function bar () { return a + b; } return bar; } var bar = foo();//先执行上下文foo,返回函数bar bar();//执行bar时会引用foo中的变量a、b从而产生闭包1
二、闭包与垃圾回收机制
当浏览器采用标记法清除垃圾时,闭包中保存的内容不会被回收,可能会存在内存泄漏。因此使用闭包一定要慎重,不能滥用。
三、闭包与作用域链
作用域规则是在编译阶段就完成,虽然是在执行阶段才生成,但是它的规则并不会被改变,因此闭包不会改变作用域链。
var fn = null; function foo () { var a=2; function innerFoo(){ console.log(a); } fn=innerFoo;//将innerFoo的引用赋值给fn } function bar(){ fn();//此处仍然是innerFoo的引用 } foo(); bar();//2
四、应用闭包
1、循环、setTimeOut与闭包
问题:改写下列函数,使之每秒打印1、2、3、4、5
//原函数 for(var i=1;i<=5;i++){ setTimeOut(function() { console.log(i); },1000*i); }
上面函数不能依次打印出1、2、3、4、5的原因是:setTimeOut()会在循环后的下一个事件循环被调用,因此这时的i值已经是6,所以每次都会打印6;
改进:要想setTimeOut()函数每次都记住i的值可以利用闭包中被引用的变量不会被回收的特性,改进例子如下:
//改进函数一 for(var i=1;i<=5;i++){ (function(i){ setTimeOut(function() { console.log(i); },1000*i); })(i)//利用子函数产生闭包 } //改进函数二 for(var i=1;i<=5;i++){ setTimeOut(function(i) { return function(){ console.log(i); } },1000*i); }
2、单例模式与闭包
单例模式只有一个实例
①简单的单例模式
对象字面量是最简单的单例模式,但是它的属性能被外部修改。
②有私有方法/属性的单例模式
利用匿名函数为对象字面量创建一个单独的作用域即可实现私有的属性和方法:
var per=(function(){ var name='jake'; return{ getName: function(){ return name; } } })();//利用闭包,保存A中的属性
③调用时才初始化的单例模式
②中的实例在一开始的时候就会被生成,当只需要在执行时被初始化就需要一个中间变量进行判断:
var per=(function(){ var name='jake'; var instace=null;//保存实例 function initial(){ return{ getName: function(){ return name; } } return { getInstance: function (){ if(!instance) instance=initial(); return instance; } } } })(); var p1=per.getInstance();//只在使用时初始化实例
3、模块化与闭包
模块化是建立在单例模式上面的,而且模块化与闭包息息相关。
1)每一个单例就是一个模块
2)每一个模块要与其他模块交互,则必须有获取其他模块的能力,比如requirejs中的require和ES6 modules中的import;
require var $ =require('jquery'); //ES6 modules import $ from 'jquery';
3)每一个模块都有一个对外接口,以保证与其他模块交互的能力。
var module_test = (function() { ... return { testfn1: function(){}, testfn1: function(){}, } })();
总结一下js模块化开发的流程(实现一个body背景色随着数字的递增在固定的三种颜色之间切换的效果)
(1)首先创建一个管理全局状态的模块,这个模块有自己的方法和属性,并且提供一个对外接口来保证外部访问内部私有属性和方法。
var module_status = (function() { var status = { number: 0, color: null } var get = function(prop) { return status[prop]; } var set =function(prop, value){ status [prop] =value; } return { get: get, set: set } })();
(2)创建一个模块,这个模块专门负责满足需求的实现(负责body背景色的改变)。
var module_color = (function() { **//假she用这种方式执行第二步引入模块, //类似于import state from 'module-status'** var state = module_status; var colors = ['orange', '#ccc', 'pink']; function render() { var color = colors[state.get ('number') % 3]; document.body.style.backgroundColor = color; } return { render: render } })();
(3)创建另外一个模块,负责显示当前的number值,用于参考和对比
var module_context = (function() { var state = module_status; function render() { document.body.innerHTML ='this Number is '+ state.get ('number '); } return { render: render } })();
(4)这些功能模块都创建完毕后,最后我们只需要穿创建一个主模块,这个主模块的目的是借助这些功能莫魁岸,来实现我想要实现的效果
var module_main = (function() { var state = module_status; var color = module_color; var context = module_context; setInterval(function() { var newNumber = state.get ('number') + 1; state.set('number', newNumber); color.render(); context.render(); }, 1000); })();
完整代码(初步学习和实现js模块化):
<script> var module_status = (function() { var status = { number: 0, color: null } var get = function(prop) { return status[prop]; } var set =function(prop, value){ status [prop] =value; } return { get: get, set: set } })(); var module_color = (function() { **//假she用这种方式执行第二步引入模块, //类似于import state from 'module-status'** var state = module_status; var colors = ['orange', '#ccc', 'pink']; function render() { var color = colors[state.get ('number') % 3]; document.body.style.backgroundColor = color; } return { render: render } })(); var module_context = (function() { var state = module_status; function render() { document.body.innerHTML ='this Number is '+ state.get ('number '); } return { render: render } })(); var module_main = (function() { var state = module_status; var color = module_color; var context = module_context; setInterval(function() { var newNumber = state.get ('number') + 1; state.set('number', newNumber); color.render(); context.render(); }, 1000); })(); </script>#前端#