关于requirejs中的define的原理理解

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

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。
接下来咱们执行这样一段代码:app

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'));

看一下执行结果:enter description here函数

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

impl.apply(impl,deps)

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

也就至关于这样:this

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

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

但这里,咱们第二个参数是一个空数组,在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();

看一下其执行结果:enter description hereblog

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

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

以上内容,纯属我的理解,若是有不对之处,还请你们热心指正。

相关文章
相关标签/搜索