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

看一下執行結果:enter description here

大致分析一下執行過程:
通過MyModules的公共接口define,同時,傳給define的第二個參數是一個空數組,所以說,for循環會跳過,這時,最關鍵的一句是

 impl.apply(impl,deps)

它會將impl也就是第三個匿名函數參數的this值綁定到函數自身的作用域並同時將第二個參數deps作為函數的參數傳進去。
這時,modules對象中就多了一個bar屬性,其大致的結構跟我們開篇講的第一個例子,並沒有什么兩樣,我們看一下,其結構:
enter description here

也就相當於這樣:

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

看一下其執行結果:enter description here

是按照我們預想的那樣輸出的。
我們再來分析一下執行過程:
其關鍵的步驟就是第二個參數是有一個數組值的數組,代碼將執行遍歷此數組操作,然后,將該數組的第一個值賦值為此前module對象中的bar屬性對象,所以說該數組中的值也就也就指向了moudule對象中的bar屬性對象(注意引用類型的復制問題)。
其也就當做參數傳遞到了modules對象的另外一個屬性foo之中,其實,這也就完成了requirejs中加載模塊的功能。這時,當我們引用bar模塊中的公共接口的時候,其結果也就在情理之中了。

其實,這段代碼的核心就是modules[name]=impl.apply(impl,deps)。為了模塊的定義引入了包裝函數(可以傳入任何依賴),並將其返回值,也就是模塊的API,存儲在一個根據名字來管理的模塊列表中。

以上內容,純屬個人理解,如果有不對之處,還請大家熱心指正。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM