javascript中的闭包、模块与模块加载
一、前言
闭包是基于词法作用域( 和动态作用域对应,词法作用域是由你写代码时,将变量写在哪里来决定的,因此当词法分析器处理代码时,会保持作用)书写代码时所产生的自然结果,甚至不需要为了利用闭包而有意地创建闭包。闭包的创建和使用在动态语言的代码中随处可见。你缺少的只是识别,拥抱和使用闭包的思维。
当函数可以记住并访问所在的词法作用域,即使函数在当前词法作用域之外执行。就产生了闭包。
一般情况下,当函数执行完毕,垃圾回收机制会期待函数的整个内部作用域被销毁,但当闭包存在时,会阻止这件事情的发生,事实上内部作用域依旧存在,此时内部函数依旧持有对外部函数作用域的引用,这个引用就叫做闭包。无论通过何种方式将内部函数传递到所在的词法作用域之外,他都会持有对 原始定义作用域的引用,无论在何处执行这个函数都会使用闭包。
所以说,在javascript,python这种动态语言中,因为函数是一级对象,无论何时何地,只要将函数当做第一级的值类型到底传递,都会看到闭包的运用,可以说,闭包无处不在。
二、循环与闭包
关于闭包,一段很经典的代码是:
var i=;
for(i=;i<;i++){
setTimeout(function(){
console.log(i);
},);
}
var result=[];
for(var i=;i<;i++){
result[i]=function(){
console.log(i);
}
}
for(var j=;j<;j++){
result[j]();
}
上面的代码不会如预期分别输出1-5,而是输出4次5;
首先解释下5是怎么来的,循环终止的条件是i<5;因此条件成立时候i的值等于5;
事实上这种情况是必然的,setTimeout,以及result中的函数都是在循环结束的时候才会执行,哪怕是setTimeout(,0).
我们试图假设循环中的每个迭代在运行时,都会给自己“捕获一个i的副本”。但是根据作用域的工作原理,实际情况是尽管循环中的5个函数实在各个迭代中分别定义的,但是它们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。
再考虑下面的代码:
var i;
for(i=;i<;i++){
setTimeout(function(i){
console.log(i);
},);
}
上面代码的执行结果是输出5个undefined,同样的道理,实际上延迟函数是在循环结束之后才执行的,执行的时候,并没有为其传入参数,因此执行的结果只能是undefined.
如果想要上面的代码如预期执行,需要在循环中为每个迭代都创建一个闭包作用域。
var i;
for(i=;i<;i++){
(function(){
var j=i;//得到一个副本
setTimeout(function(){
console.log(j);
},);
})();
} //或者进一步简化
//形参传递本质上是传递只的一个副本;
//作用域冒泡到ITFE,找到了参数j,就不再向上冒泡
for(i=;i<;i++){
(function(j){
//此时形参命名为j,还是叫i都无所谓
setTimeout(function(){
console.log(j);
},);
})(i);
}
三、模块
模块是一个利用闭包的典型案例:
模块模式至少具备两个条件:
1)必须有外部的封闭包装函数来创建内部作用域,该函数至少被调用一次(每次调用都会创建一个新的模块作用域),如果是ITFE调用就只产生一个实例(单例模式?);
2)封闭函数返回至少一个内部函数的引用(可以直接返回该内部函数,如jQuery;也可以返回一个对象,该对象至少包含一个属性,指向内部函数的引用),这样内部函数才能在私有作用域形成闭包,而且可以访问或者修改私有的状态;
比如模块的一个很常见的应用就是返回作为公共API返回的对象:
var foo=(function(){
function change(){
console.log('change');
}
function identify(){
console.log('identify');
}
return{
change:change,
identify:identify,
}
})();
四、模块加载器/管理器
模块管理器本质上并没有任何的“魔力”,本质上就是讲模块定义封装进一个友好的API。下面是一个简单的模块加载器的实现:
// 尝试实现简单的模块加载器,这种方法只考虑了模块的本地加载,事实上require.js等加载器,还需要动态创建script标签来远程加载模块
var MyModules=(function(){
var modules=[]
//参数name:模块名称
//参数deps:依赖的模块名称
//impl:名为name的模块实现
var define=function define(name,deps,impl){
var i=;
for(i;i<deps.length;i++){
deps[i]=modules[deps[i]];
}
modules[name]=impl.apply(impl,deps);
};
var get =function(name){
return modules[name];
};
return {
define:define,
get:get
}
})(); //调用
MyModules.define('bar',[],function(){
function hello(){
return 'hello';
}
return {
hello:hello,
};
});
MyModules.define('foo',['bar'],function(){
function awesome(){
console.log('foo'+bar.hello());
}
return{
awesome:awesome,
}
})
var bar=MyModules.get('bar');
var foo=MyModules.get('foo');
foo.awesome();
javascript中的闭包、模块与模块加载的更多相关文章
- Linux中实现在系统启动时自动加载模块
下面是以前学习Linux时写的,后来仔细研究rc.sysinit后发现,只需要修改下列地方就可以了,不必这么麻烦的: rc.sysinit中有这样的一段代码: # Load other user-de ...
- 使用angular.bootstrap() 完成模块的手动加载
之前我们看到使用ng-app指令,可以实现模块的自动加载.现在我们看下,angular中如何手动加载模块.需要使用到angular.bootstrap这个函数. <html> <he ...
- 让你分分钟学会Javascript中的闭包
Javascript中的闭包 前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它 ...
- 模块“XXX.dll”加载失败
具体问题:模块“XXX.dll”加载失败 请确保该二进制存储在指定的路径中,或者调试它以检查该二进制或相关的.DLL文件是否有问题 找不到指定的模块. 1.在安装C++软件的时候,有时候安装失败提示 ...
- 基于python的opcode优化和模块按需加载机制研究(学习与个人思路)(原创)
基于python的opcode优化和模块按需加载机制研究(学习与思考) 姓名:XXX 学校信息:XXX 主用编程语言:python3.5 个人技术博客:http://www.cnblogs.com/M ...
- Javascript中的闭包(转载)
前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它.下面是作者从作用域链慢慢讲到 ...
- 狗日的Javascript中的闭包
前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它.下面是作者从作用域链慢慢讲到 ...
- javaScript中的闭包原理 (译)
这篇文章通过javaScript代码解释了闭包的原理,来让编程人员理解闭包.它不是写给大牛或使用功能性语言进行编程的程序员的.一旦意会了其核心概念,闭包理解起来并不难.然而,你不可能通过阅读任何有关闭 ...
- 0x02 Python logging模块利用配置加载logger
目录 logging模块利用配置加载logger 方式一模板:logging.config.dictConfig(config_dict) logging模块利用配置加载logger logging. ...
随机推荐
- Redis到底该如何利用(二)?
上一篇文章里我简述了使用Keys作为Redis搜索的方式,确实感受到了社区的力量,写文章好处多.首先谢谢各位前辈的指导,我知道了拿Redis作为搜索是个错误的方向.本来这篇文章我觉得确实没必要发了,但 ...
- HDU 5769 Substring 后缀数组
Substring Problem Description ?? is practicing his program skill, and now he is given a string, he h ...
- Node.js学习之简介
1.简单的说Node.js就是运行在服务端的javaScript: 2.Node.js是一个基于Chrome javaScript运行时建立的一个平台: 3.Node.js是一个事件驱动I/O服务端J ...
- Java 根据两个经纬度坐标计算距离
public class Distance{ private static final double EARTH_RADIUS = 6378137; private static double ...
- D3.js部署node环境开发
总结一段D3.js部署node环境的安装过程 准备阶段: 首先电脑上要安装node环境,这个阶段过滤掉,如果node环境都不会装,那就别玩基于node环境搞的其他东西了. 搭建环境: 我在自己的F:系 ...
- /usr文件系统
/usr文件系统 /usr 文件系统经常很大,因为所有程序安装在这里. /usr 里的所有文件一般来自Linux distribution:本地安装的程序和其他东西在/usr/local 下.这样可 ...
- SpringMVC核心分发器DispatcherServlet分析[附带源码分析]
目录 前言 DispatcherServlet初始化过程 DispatcherServlet处理请求过程 总结 参考资料 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不 ...
- 启动mysql服务 报1067 错误
启动mysql 报1067 错误 一般报1067错误,先看一下data/my.ini配置文件 中的路径 datadir ,log-bin ,log-error 报1067错误原因 多种 ...
- C#_实用
1.获取当前浏览器信息HttpContext.Current.Request.Browser string browser = HttpContext.Current.Request.Browser. ...
- zeromq中两个dealer 通过一个router进行通信
发现有童鞋不是很清楚ZMQ中的“请求-回复”模式中的ROUTER怎么用,所以简单介绍一下“请求-回复”模式的使用(最后付代码). 一.讲一讲 1.要使用zmq 通过一个router进行通信,你首先需要 ...