動手實現你的依賴注入

JS每日一題發表於2017-09-20

首先我們用大白話來理解一次,依賴注入就是我有一個東西,我平時不用,我把這個東西放在你那裡,在我用的時候你拿給我。

這個時候我們已經初步有了概念,我們在用程式碼來消化這個概念

首先我們來先建立一個inject的物件,物件下有個三個屬性分別為

  • dependencies這個為一個陣列,用來存放我們所有的依賴
  • register 這個為一個函式,用來註冊我們的依賴
  • resolve 這個用來注入(提取)我們的依賴
    const inject = {
    dependencies: {},
    register: function(key, value) {
      this.dependencies[key] = value;
    },
    resolve: function(deps, func, scope){
      let arr = [];
      for (let i = 0 ; i < deps.length ; i++) {
           if (this.dependencies.hasOwnProperty(deps[i])) {
              arr.push(this.dependencies[deps[i]])
           }
      }
      return function(){
        func.apply(scope||{},arr);
      }
    },
    }複製程式碼
    嗯, 大概就是這個,即然已經把初步的想法實現,我們現在就來試試到底可不可以注入

首先實現我有一個東西,並且把這個東西存在你那,那麼我們用register方法來註冊兩個

inject.register('$http', {
  'get': function() {
    return '我是依賴注入的$http下的一個函式';
  }
});
inject.register('$scope', {
  'test': ''
});複製程式碼

這個時候我需要用到我前面存放在你那裡東西,你得拿給我

let MyController = function($scope,$http){
  console.log(`MyController-result:${$http.get()}`)
}
MyController = inject.resolve(['$scope','$http'],MyController)
MyController(); // MyController-result:我是依賴注入的$http下的一個函式複製程式碼

嗯,完美,這樣就簡單的實現我們的注入,但我們仔細一下,如果這個時候我們把這個依賴注入的順序變換一下是報錯的,因為他們是一一對應的,所以我們還得來改造一下resolve這個方法,

resolve: function(func, scope){
  let arr = [];
  // 這些正則是在angular的原始碼裡找來的
  // 首先把這個函式toString,然後FN_ARGS匹配出來函式的引數,這個引數就是我們依賴的表
  // 那麼這個時候我們已經拿到依賴的表了,我們按照這個表裡取出相對應的函式體給他就完了
  let FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
  let STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
  let fnText = func.toString().replace(STRIP_COMMENTS, '');
  let argDecl = fnText.match(FN_ARGS);
  let deps = argDecl[1].split(',');
  for (let i = 0 ; i < deps.length ; i++) {
       if (this.dependencies.hasOwnProperty(deps[i])) {
          arr.push(this.dependencies[deps[i]])
       }
  }
  return function(){
    func.apply(scope||{},arr);
  }
}複製程式碼

當然使用的時候也得改一下

let MyController = function($scope,$http){
  console.log(`MyController-result:${$http.get()}`)
}
MyController = inject.resolve(MyController)
MyController(); // MyController-result:我是依賴注入的$http下的一個函式複製程式碼

看上去有點像模樣了,這時候我覺得應該有人會提出問題,嗯,對,就是還有問題,我們知道上線前我們一般會把專案靜態資源打包,function($scope,$http)會打成類似於function(a,b)這種,那麼這個時候還怎麼去打到對應的函式體呢 ? 我們再來改寫一下resolve方法

resolve: function(func, scope) {
  let deps;
  // 如果傳入的函式是一個陣列,大概長這樣[a,b,function(a,b){}],那麼我們刪掉function(){}這部分,只留下我們的依賴部分,這樣就解決了打包壓縮時的問題
  if (isArray(func)) {
    let last = func.length - 1;
    deps = func.slice(0, last);
    func = func[last]
  } else {
    let FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
    let STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
    let fnText = func.toString().replace(STRIP_COMMENTS, '');
    let argDecl = fnText.match(FN_ARGS);
    deps = argDecl[1].split(',');
  }
  let arr = [];
  for (let i = 0; i < deps.length; i++) {
    if (this.dependencies.hasOwnProperty(deps[i])) {
      arr.push(this.dependencies[deps[i]])
    }
  }
  return function() {
    func.apply(scope || {}, arr);
  }
}複製程式碼

當然使用的時候還是得改改

let MyController = ['$scope', '$http', function($scope, $http) {
  console.log(`MyController-result:${$http.get()}`)
}];
MyController = inject.resolve(MyController)
MyController(); // MyController-result:我是依賴注入的$http下的一個函式複製程式碼

這樣我們一個簡易的dependencies inject就完成了,angular的實現思路也是如此

相關文章