模擬AngularJS之依賴注入


一、概述

AngularJS有一經典之處就是依賴注入,對於什么是依賴注入,熟悉spring的同學應該都非常了解了,但,對於前端而言,還是比較新穎的。

依賴注入,簡而言之,就是解除硬編碼,達到解偶的目的。

下面,我們看看AngularJS中常用的實現方式。

方法一:推斷式注入聲明,假定參數名稱就是依賴的名稱。因此,它會在內部調用函數對象的toString()方法,分析並提取出函數參數列表,然后通過$injector將這些參數注入進對象實例。

如下:

//方法一:推斷式注入聲明,假定參數名稱就是依賴的名稱。
//因此,它會在內部調用函數對象的toString()方法,分析並提取出函數參數列表,
//然后通過$injector將這些參數注入進對象實例
injector.invoke(function($http, $timeout){
    //TODO
});

方法二:行內注入聲明,允許我們在函數定義時,直接傳入一個參數數組,數組包含了字符串和函數,其中,字符串代表依賴名,函數代表目標函數對象。

如下:

//方法二:行內注入聲明,允許我們在函數定義時,直接傳入一個參數數組,
//數組包含了字符串和函數,其中,字符串代表依賴名,函數代表目標函數對象。
module.controller('name', ['$http', '$timeout', function($http, $timeout){
    //TODO
}]);

看了上述代碼,心中有一疑問,這些是怎么實現的呢?

哈哈,下面,我們就來一起模擬一下這些依賴注入方式,從而了解它們。

該篇博客原文地址:http://www.cnblogs.com/giggle/p/5769169.html

二、搭建基本骨架

依賴注入的獲取過程就是,通過字段映射,從而獲取到相應的方法。

故而,要實現一個基本的依賴注入,我們需要一個存儲空間(dependencies),存儲所需鍵值(key/value);一個注冊方法(register),用於新增鍵值對到存儲空間中;還有一個就是核心實現方法(resolve),通過相關參數,到存儲空間中獲得對應的映射結果。

So,基本骨架如下:

var injector = {
    dependencies: {},
    register: function(key, value){
        this.dependencies[key] = value;
        return this;
    },
    resolve: function(){
                            
    }
};
三、完善核心方法resolve

從我們搭建的基本骨架中,可以發現,重點其實resolve方法,用於實現我們具體形式的依賴注入需求。

首先,我們來實現,如下形式的依賴注入:推斷式注入聲明。

如下:

injector.resolve(function(Monkey, Dorie){
    Monkey();
    Dorie();
});

要實現上述效果,我們可以利用函數的toString()方法,我們可以將函數轉換成字符串,從而通過正則表達式獲得參數名,即key值。 然后通過key值,在存儲空間dependencies找value值,沒找到對應值,則報錯。

實現如下:

var injector = {
    dependencies: {},
    register: function(key, value){
        this.dependencies[key] = value;
        return this;
    },
    resolve: function(){
        var func, deps, args = [], scope = null;
        func = arguments[0];
        //獲取函數的參數名
        deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, '').split(',');
        scope = arguments[1] || {};
        for(var i = 0, len = deps.length; i < len, d = deps[i]; i++){
            if(this.dependencies[d]){
                args.push(this.dependencies[d]);
            }else{
                throw new Error('Can\'t find ' + d);
            }
        }
        func.apply(scope, args);                    
    }
};

測試代碼,如下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body>
        <script>
            var injector = {
                dependencies: {},
                register: function(key, value){
                    this.dependencies[key] = value;
                    return this;
                },
                resolve: function(){
                    var func, deps, args = [], scope = null;
                    func = arguments[0];
                    //獲取函數的參數名
                    deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, '').split(',');
                    scope = arguments[1] || {};
                    for(var i = 0, len = deps.length; i < len, d = deps[i]; i++){
                        if(this.dependencies[d]){
                            args.push(this.dependencies[d]);
                        }else{
                            throw new Error('Can\'t find ' + d);
                        }
                    }
                    func.apply(scope, args);                    
                }
            };
            //測試代碼
            injector.register('Monkey', function(){
                console.log('Monkey');
            }).register('Dorie', function(){
                console.log('Dorie');
            });
            injector.resolve(function(Monkey, Dorie){
                Monkey();
                Dorie();
                console.log('-.-');
            });        
        </script>
    </body>
</html>
代碼稍長,請自行打開

推斷式注入聲明,有個缺點,就是不能利用壓縮工具壓縮,因為我們是通過函數的參數作為依賴的,當我們壓縮時,會將參數名改掉,參數名都變了,那肯定撲街咯。

那么下面,我們就看看行內注入聲明,它可以彌補這缺點。

實現行內注入聲明,如下:

injector.resolve(['Monkey', 'Dorie', function(M, D){
    M();
    D();
}]);

利用typeof判斷arguments[0]的類型可以辨別並獲得依賴參數和函數。

實現如下:

var injector = {
    dependencies: {},
    register: function(key, value){
        this.dependencies[key] = value;
        return this;
    },
    resolve: function(){
        var firstParams, func, deps = [], scope = null, args = [];
        firstParams = arguments[0];
        scope = arguments[1] || {};
        //獲得依賴參數
        for(var i = 0, len = firstParams.length; i < len; i++){
            var val = firstParams[i],
                type = typeof val;
            if(type === 'string'){
                deps.push(val);
            }else if(type === 'function'){
                func = val;
            }
        }
        //通過依賴參數,找到關聯值
        for(i = 0, len = deps.length; i < len, d = deps[i]; i++){
            if(this.dependencies[d]){
                args.push(this.dependencies[d]);
            }else{
                throw new Error('Can\'t find ' + d);
            }
        }
        func.apply(scope || {}, args);                    
    }
};
代碼稍長,請自行打開

測試代碼,如下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body>
        <script>
            var injector = {
                dependencies: {},
                register: function(key, value){
                    this.dependencies[key] = value;
                    return this;
                },
                resolve: function(){
                    var firstParams, func, deps = [], scope = null, args = [];
                    firstParams = arguments[0];
                    scope = arguments[1] || {};
                    //獲得依賴參數
                    for(var i = 0, len = firstParams.length; i < len; i++){
                        var val = firstParams[i],
                            type = typeof val;
                        if(type === 'string'){
                            deps.push(val);
                        }else if(type === 'function'){
                            func = val;
                        }
                    }
                    //通過依賴參數,找到關聯值
                    for(i = 0, len = deps.length; i < len, d = deps[i]; i++){
                        if(this.dependencies[d]){
                            args.push(this.dependencies[d]);
                        }else{
                            throw new Error('Can\'t find ' + d);
                        }
                    }
                    func.apply(scope || {}, args);                    
                }
            };
            //測試代碼
            injector.register('Monkey', function(){
                console.log('Monkey');
            }).register('Dorie', function(){
                console.log('Dorie');
            });
            injector.resolve(['Monkey','Dorie',function(M, D){
                M();
                D();
                console.log('-.-');
            }]);    
        </script>
    </body>
</html>
代碼稍長,請自行打開

因為行內注入聲明,是通過字符串的形式作為依賴參數,so,壓縮也不怕咯。

最后,我們將上面實現的兩種方法,整合到一起,就可以為所欲為啦。

那,就合並下吧,如下:

var injector = {
    dependencies: {},
    register: function(key, value){
        this.dependencies[key] = value;
        return this;
    },
    resolve: function(){
        var firstParams, func, deps = [], scope = null, args = [];
        firstParams = arguments[0];
        scope = arguments[1] || {};
        //判斷哪種形式的注入
        if(typeof firstParams === 'function'){
            func = firstParams;
            deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, '').split(',');
        }else{
            for(var i = 0, len = firstParams.length; i < len; i++){
                var val = firstParams[i],
                    type = typeof val;
                if(type === 'string'){
                    deps.push(val);
                }else if(type === 'function'){
                    func = val;
                }
            }    
        }
        //通過依賴參數,找到關聯值
        for(i = 0, len = deps.length; i < len, d = deps[i]; i++){
            if(this.dependencies[d]){
                args.push(this.dependencies[d]);
            }else{
                throw new Error('Can\'t find ' + d);
            }
        }
        func.apply(scope || {}, args);                    
    }
};
代碼稍長,請自行打開
四、花絮—RequireJS之依賴注入

依賴注入並非在AngularJS中有,倘若你使用過RequireJS,那么下面這種形式,不會陌生吧:

require(['Monkey', 'Dorie'], function(M, D){
    //TODO    
});

通過,上面我們一步步的模擬AngularJS依賴注入的實現,想必,看到這,你自己也會豁然開朗,換湯不換葯嘛。

模擬實現如下:

var injector = {
    dependencies: {},
    register: function(key, value){
        this.dependencies[key] = value;
        return this;
    },
    resolve: function(deps, func, scope){
        var args = [];
        for(var i = 0, len = deps.length; i < len, d = deps[i]; i++){
            if(this.dependencies[d]){
                args.push(this.dependencies[d]);
            }else{
                throw new Error('Can\'t resolve ' + d);
            }
        }
        func.apply(scope || {}, args);
    }
};

測試代碼如下:

<!DOCTYPE html>
    <head>
        <meta charset="utf-8"/>
    </head>
    <body>
        <script>
            var injector = {
                dependencies: {},
                register: function(key, value){
                    this.dependencies[key] = value;
                    return this;
                },
                resolve: function(deps, func, scope){
                    var args = [];
                    for(var i = 0, len = deps.length; i < len, d = deps[i]; i++){
                        if(this.dependencies[d]){
                            args.push(this.dependencies[d]);
                        }else{
                            throw new Error('Can\'t resolve ' + d);
                        }
                    }
                    func.apply(scope || {}, args);
                }
            };
            //測試代碼            
            injector.register('Monkey', function(){
                console.log('Monkey');
            }).register('Dorie', function(){
                console.log('Dorie');
            });
            injector.resolve(['Monkey', 'Dorie'], function(M, D){
                M();
                D();
                console.log('-.-');
            });        
        </script>
    </body>
</html>
代碼稍長,請自行打開
五、參考

1、AngularJS應用開發思維之3:依賴注入

2、Dependency injection in JavaScript

 


免責聲明!

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



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