关于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,存储在一个根据名字来管理的模块列表中。

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

时间: 2024-12-07 05:18:22

关于requirejs中的define的原理理解的相关文章

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, doAnothe

通过源码理解Spring中@Scheduled的实现原理并且实现调度任务动态装载

前提 最近的新项目和数据同步相关,有定时调度的需求.之前一直有使用过Quartz.XXL-Job.Easy Scheduler等调度框架,后来越发觉得这些框架太重量级了,于是想到了Spring内置的Scheduling模块.而原生的Scheduling模块只是内存态的调度模块,不支持任务的持久化或者配置(配置任务通过@Scheduled注解进行硬编码,不能抽离到类之外),因此考虑理解Scheduling模块的底层原理,并且基于此造一个简单的轮子,使之支持调度任务配置:通过配置文件或者JDBC数据

Caffe 中卷积运算的原理与实现

caffe中卷积运算设计的很巧妙,今天就来讨论一下caffe中卷积运算的原理,最后会给出一个自己的实现版本,便于初学者理解. Caffe中卷积运算的原理 俗话说,一图胜千言,首先先给出原理示意图,为了方便,这里以二维核为例 滑动窗口在图像中每滑动一个地方,将图像中该滑动窗口图像展开为一列,所有列组成图中的滑动窗口矩阵,这里假设pad=1,stride=1,K=3,则滑动窗口矩阵每行大小为W*H,一共K*K行. 每个核展开为一行,N个核形成的核矩阵大小为N*K*K. 最后将核矩阵和滑动窗口矩阵相乘

POJ1523(求连用分量数目,tarjan算法原理理解)

SPF Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 7406   Accepted: 3363 Description Consider the two networks shown below. Assuming that data moves around these networks only between directly connected nodes on a peer-to-peer basis, a

iOS中xib与storyboard原理,与Android界面布局的异同

用文本标记语言来进行布局,用的最多的应该是HTML语言.HTML可以理解为有一组特殊标记的XML语言. 一.iOS中xib与storyboard显示原理 在iOS中主要的布置界面的方式有3种:代码,xib,storyboard. 1. 代码 代码布置界面是万能的,但通常很复杂.布置一个简单的界面可能需要很多行代码,因此十分繁琐. 下面为创建一个按钮的代码,最少也要3行: UIButton *btn = [UIButton buttonWithType:UIButtonTypeContactAdd

iOS中的应用启动原理

iOS中的应用启动原理 来源: http://m.warting.com/program/201601/127355.html 一.UIApplication  1. 简单介绍 (1)UIApplication对象是应用程序的象征,一个UIApplication对象就代表一个应用程序. (2)每一个应用都有自己的UIApplication对象,而且是单例的,如果试图在程序中新建一个UIApplication对象,那么将报错提示. (3)通过[UIApplicationsharedApplicat

网站数据统计分析中的日志收集原理及其实现

> 网站数据统计分析工具是网站站长和运营人员经常使用的一种工具,比较常用的有谷歌分析.百度统计 和 腾讯分析等等.所有这些统计分析工具的第一步都是网站访问数据的收集.目前主流的数据收集方式基本都是基于javascript的.本文将简要分析这种数据收集的原理,并一步一步实际搭建一个实际的数据收集系统. 1.数据收集原理分析 简单来说,网站统计分析工具需要收集到用户浏览目标网站的行为(如打开某网页.点击某按钮.将商品加入购物车等)及行为附加数据(如某下单行为产生的订单金额等).早期的网站统计往往只收

jQuery 中 data 方法的实现原理

前言:jQuery 作为前端使用最多最广泛的 JS 库,其源码每个 JSer 都应该研究一下.早就打算看却一直被各种事拖着,上次某公司面试时被问到 jQuery 中 data 方法是如何实现的,结果答不上来懊悔不已.现在终于下决心开始看 jQuery 的源码,就从 data 方法开始.本人也是前端新手,如果文章中有理解不当或者错误之处,欢迎留言指出,3Q~ jQuery 版本为 1.8.2 data() 的使用方法 // 向一个 dom 元素绑定数据 $("#header").data

网站统计中的数据收集原理及实现

转载自:http://blog.sina.com.cn/s/blog_62b832910102w5mx.html Avinash Kaushik将点击流数据的获取方式分为4种:log files.web beacons.JavaScript tags和packet sniffers,其中包嗅探器(packet sniffers)比较不常见,最传统的获取方式是通过WEB日志文件(log files):而beacons和JavaScript是目前较为流行的方式,Google Analytics目前就