关于requirejs中的define的原理理解

 

我们已经了解到模块模式是为单例创建私有变量和特权方法的。
一个最基本的例子:

var foo=(function(){
var something='cool',
var another=[1,2,3];
function dosomething(){
console.log(something);
}
function doAnother(){
console.log(another.join('!'));
}
return {
doSomething:doSomething,
doAnother:doAnother
}
})();
foo.doSomething(); //cool
foo.doAnother(); //1!2!3

我们将模块函数转换成了立即执行函数,立即调用这个函数并将返回值直接赋值给单例的模块实例标识符。在函数内部定义了私有变量,并通过函数留下了接口,除了通过接口访问其内部的私有变量的值,在函数外部是无法访问它的值的,这也就保证了不会引起命名冲突,修改数据等问题,同时,模块还可以复用,大大提高了开发效率。
其实,大多数的模块倚赖加载器/管理器本质上都是将这种模块定义封装进一个友好的API(接口)。我们知道,在requirejs中,使用define([],function(){})来定一个模块的,来看下面一段原理性代码。

  var MyModules=(function Manager(){
var moudules={}; //对象字面量
function define(name,deps,impl){
for(var i=0;i<deps.length;i++){
deps[i]=moudules[deps[i]];
}
moudules[name]=impl.apply(impl,deps);
console.log(moudules[name]);
} function get(name){
return moudules[name];
}
return {
define:define,
get:get
};
})();

这里我们定义了一个匿名函数,并让他立即执行,然后将返回值传给了MyModules。
接下来我们执行这样一段代码:

MyModules.define('bar',[],function(){
function hello(who){
return 'Let me introduce:'+who;
}
return {
hello:hello
}
});
var bar=MyModules.get('bar');
console.log(bar.hello('nihao'));

看一下执行结果:

大致分析一下执行过程:
通过MyModules的公共接口define,同时,传给define的第二个参数是一个空数组,所以说,for循环会跳过,这时,最关键的一句是

 impl.apply(impl,deps)

它会将impl也就是第三个匿名函数参数的this值绑定到函数自身的作用域并同时将第二个参数deps作为函数的参数传进去。
这时,modules对象中就多了一个bar属性,其大致的结构跟我们开篇讲的第一个例子,并没有什么两样,我们看一下,其结构:

也就相当于这样:

var bar=function(){
function hello(who){
return 'Let me introduce':+who;
}
return {
hello:hello
}
};

这时,我们再调用get函数获取到其属性,然后调用其hello()方法,结果也就一目了然了。

但这里,我们第二个参数是一个空数组,在requirejs中,也就相当于不倚赖任何模块,那当他需要依赖其他模块时,会是什么样的情况呢,他又是如何完成其他模块的加载的呢?
我们在代码的基础上,继续增加如下代码:

MyModules.define('foo',['bar'],function(bar){
var hungry='hippo';
function awesome(){
console.log(bar.hello(hungry).toUpperCase());
}
return {
awesome:awesome
}
});
var foo=MyModules.get('foo');
foo.awesome();

看一下其执行结果:

是按照我们预想的那样输出的。
我们再来分析一下执行过程:
其关键的步骤就是第二个参数是有一个数组值的数组,代码将执行遍历此数组操作,然后,将该数组的第一个值赋值为此前module对象中的bar属性对象,所以说该数组中的值也就也就指向了moudule对象中的bar属性对象(注意引用类型的复制问题)。
其也就当做参数传递到了modules对象的另外一个属性foo之中,其实,这也就完成了requirejs中加载模块的功能。这时,当我们引用bar模块中的公共接口的时候,其结果也就在情理之中了。

其实,这段代码的核心就是modules[name]=impl.apply(impl,deps)。为了模块的定义引入了包装函数(可以传入任何依赖),并将其返回值,也就是模块的API,存储在一个根据名字来管理的模块列表中。

以上内容,纯属个人理解,如果有不对之处,还请大家热心指正。

requirejs中的define的更多相关文章

  1. 关于requirejs中的define的原理理解

    我们已经了解到模块模式是为单例创建私有变量和特权方法的. 一个最基本的例子: var foo=(function(){ var something='cool', var another=[1,2,3 ...

  2. RequireJS中的require如何返回模块

    requirejs中定义AMD模块规则如下: define(function(){ var ProductManager={ Create:function(){ console.log(" ...

  3. RequireJS中的require返回模块

    requirejs中定义AMD模块规则如下: define(function(){ var ProductManager={ Create:function(){ console.log(" ...

  4. requirejs中Shims使用说明

    RequireJS中如果使用AMD规范,在使用的过程中没有太多的问题,如果加载非AMD规范的JS文件,就需要使用Require中的shim. require.config({ paths:{ jque ...

  5. vs2010编译zapline-zapline.systemoptimization 注释工程中的//#define abs(value) (value >= 0 ? value : -(value))即可

    vs2010编译zapline-zapline.systemoptimization-8428e72c88e0.zip出错 1>d:\program files (x86)\microsoft ...

  6. requireJS中如何用r.js对js进行合并和压缩css文件

    我运行的环境是windows+node.js,首先是用npm安装requirejs(全局安装,即使用 'npm install requirejs -g',这样方便在各个目录调用),接着就是下载r.j ...

  7. oracle中SET DEFINE意思

    et define off关闭替代变量功能 在SQL*Plus中默认的"&"表示替代变量,也就是说,只要在命令中出现该符 号,SQL*Plus就会要你输入替代值.这就意味着 ...

  8. requirejs中 shim

    使用requireJS的shim参数,完成jquery插件的加载 时间 2014-10-31 13:59:10  CSDN博客 原文  http://blog.csdn.net/aitangyong/ ...

  9. PHP 中 const define 的区别

    在php中定义常量时,可用到const与define这两种方法,那他们到底有什么区别呢? 1.const用于类成员变量的定义,一经定义,不可修改.define不可用于类成员变量的定义,可用于全局常量. ...

随机推荐

  1. python基础学习笔记——time模块

    time模块 time翻译过来就是时间,有我们其实在之前编程的时候有用到过. #常用方法 1.time.sleep(secs) (线程)推迟指定的时间运行.单位为秒. 2.time.time() 获取 ...

  2. 修改JVM的参数、Jstat、Jstack、gclog

    ---恢复内容开始--- 1. jetty 修改JVM的参数 deploy/bin/env.sh 在上面的环境变量脚本中进行修改:如果分配给JVM的内存是4g 这个里面的JAVA_OPTS 的配置项就 ...

  3. 练习题,新建数据库anyun

    一.新建数据库,数据库名为anyun mysql> create database anyun; Query OK, 1 row affected (0.00 sec) 二.查看数据库 mysq ...

  4. Python第三方库之openpyxl(6)

    Python第三方库之openpyxl(6) 折线图 折线图允许在固定轴上绘制数据,它们类似于散列图,主要的区别在于,在折线图中,每个数据序列都是根据相同的值绘制的,不同的轴可以用于辅助轴,与条形图类 ...

  5. python3--__getattr__和__setattr__捕捉属性的一个引用

    __getattr__和__setattr__捕捉属性的一个引用 __getattr__方法是拦截属性点号运算.更确切地说,当通过对未定义(不存在)属性名称和实例进行点号运算时,就会用属性名称为字符串 ...

  6. Leetcode 329.矩阵中的最长递增路径

    矩阵中的最长递增路径 给定一个整数矩阵,找出最长递增路径的长度. 对于每个单元格,你可以往上,下,左,右四个方向移动. 你不能在对角线方向上移动或移动到边界外(即不允许环绕). 示例 1: 输入: n ...

  7. windows下在指定目录下打开命令行

    直接用cd的话比较麻烦,可以先进入制定目录后,按住shift键,鼠标右键可以选择“在此处打开命令窗口”

  8. 【C#】堆、栈和堆栈的区别

    导读:今天看视频,就看到了堆.栈这一块了.记得当年初相见(VB视频),劈头盖脸一阵蒙,什么都不知道,那时候师傅叫我挂起来,说我随着学习的进度,慢慢的就会懂了.现在,学到了这里,想着自己对自己从前的问题 ...

  9. tomcat404

    确认目录下有我的html文件,但仍然是404. 问题一: 指定目录下没有访问的文件,这是最常见的,也是最容易解决的.只需要将访问的文件(如:a.html)放到指定目录下即可. 问题二: 将访问的文件( ...

  10. UITableView加载几种不同的cell

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...