AngularJS之依賴注入類比實現_AngularJS

來源:互聯網
上載者:User

一、概述 

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

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

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

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

如下: 

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

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

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

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

哈哈,下面,我們就來一起類比一下這些依賴注入方式,從而瞭解它們。 

二、搭建基本骨架 

依賴注入的擷取過程就是,通過欄位對應,從而擷取到相應的方法。 

故而,要實現一個基本的依賴注入,我們需要一個儲存空間(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()方法,我們可以將函數轉換成字串,從而通過Regex獲得參數名,即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

以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援雲棲社區。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.