为什么80%的码农都做不了架构师?>>>
声明,这篇文章是我在网上搜索并根据自己的理解写的,第一次发文章,还请多多指教。
什么是依赖注入呢,我的理解,简单点就是说我的东西我自己并不像来拿着,我想要我依赖的那个人来帮我拿着,当我需要的时候,他给我就行了。当然这只是简单的理解,还是用代码解释比较清楚一些。
这里有一个function,很简单。
var a = function(name){console.log(name);}
我们调用它:
a('abc');//abc
那么,就像我上面说的,我能不能自己不传参数呢,例如:
a();//undefined
如何才能实现让别人帮我们注入这个参数呢:
var inject = function(name,callback){return function(){callback(name);}}
像这样,我们在定义参数的时候这样传:
a = inject('abc',a)
我们再调用a方法:
a();//abc
这其实就是最简单的依赖注入了,当然这么简单是不行的,其实这是很无意义的,下面我们来看一下高深的angularjs:
var MyController = function($scope){$scope.test = 1;}
上面这段代码定义了angularjs的controller里面用到了scope,这样还看不出问题,在看下面:
var MyController = function($scope,$http){$scope.test = 1;$http.get('');}
上面这段代码在原来的基础上增加了http,那么问题就来了,angular在调用controller的时候怎么知道我需要scope还是http还是两个都需要呢,这就牵着到了angular里的依赖注入,那么我们来模拟一下。
假设没有angular的情况下,我们:
var MyController = function($scope,$http){$scope.test = 1;$http.get('');}MyController();//undefined
肯定会报错的,然后我们来修改下我们的inject:
var inject &#61; {dependencies: {},register: function(key, value) {this.dependencies[key] &#61; value;},resolve: function(deps, func, scope) {var arr &#61; [];for (var i &#61; 0 ; i < deps.length ; i&#43;&#43;) {if (this.dependencies.hasOwnProperty(deps[i])) {arr.push(this.dependencies[deps[i]])}}console.log(arr);return function(){func.apply(scope || {}, arr);}}}
这里解释一下&#xff0c;我们用了dependencies来存储所有的依赖&#xff0c;register来实现注册依赖&#xff0c;resolve方法来实现注入。
然后我们模仿angular来预先注册几个模块&#xff1a;
inject.register(&#39;$http&#39;, {&#39;get&#39;:function(){console.log(&#39;get&#39;)}});inject.register(&#39;$scope&#39;, {&#39;test&#39;:&#39;&#39;});inject.register(&#39;$location&#39;, {&#39;hash&#39;:function(){console.log(&#39;hash&#39;)}});
然后我们就可以注入了&#xff1a;
MyController &#61; inject.resolve([&#39;$http&#39;,&#39;$scope&#39;],MyController)&#xff1b;MyController();
我们只需要http和scope&#xff0c;所以我们只传了两个&#xff0c;虽然这样看似解决了依赖注入&#xff0c;但是还有很多问题&#xff0c;比如我要交换两个参数的位置就不行了。
于是翻看了angularjs的源码&#xff0c;找到了&#xff1a;
var FN_ARGS &#61; /^function\s*[^\(]*\(\s*([^\)]*)\)/m;var STRIP_COMMENTS &#61; /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;.....function annotate(fn) {.....fnText &#61; fn.toString().replace(STRIP_COMMENTS, &#39;&#39;);argDecl &#61; fnText.match(FN_ARGS);.....}
我们忽略掉一些细节代码&#xff0c;只看我们需要的。annotate方法和我们的resolve方法很像。它转换传递过去的func为字符串&#xff0c;删除掉注释代码&#xff0c;然后抽取其中的参数。让我们看下它的执行结果&#xff0c;修改一下resolve方法&#xff1a;
resolve: function(deps, func, scope) {var FN_ARGS &#61; /^function\s*[^\(]*\(\s*([^\)]*)\)/m;var STRIP_COMMENTS &#61; /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;var fnText &#61; func.toString().replace(STRIP_COMMENTS, &#39;&#39;);var argDecl &#61; fnText.match(FN_ARGS);console.log(argDecl);}
打印出argDecl&#xff1a;
["function ($scope,$http)", "$scope,$http", index: 0, input: "function ($scope,$http){↵ $scope.test &#61; 1;↵ $http.get(&#39;&#39;);↵ }"]
可以看到&#xff0c;这个数组拿到了func的参数&#xff0c;argDecl&#xff3b;1&#xff3d; &#61; “$scope,$http”;
根据这个&#xff0c;我们来修改resolve&#xff1a;
resolve: function(func, scope) {var FN_ARGS &#61; /^function\s*[^\(]*\(\s*([^\)]*)\)/m;var STRIP_COMMENTS &#61; /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;var fnText &#61; func.toString().replace(STRIP_COMMENTS, &#39;&#39;);var argDecl &#61; fnText.match(FN_ARGS);console.log(argDecl);var deps &#61; argDecl[1].split(&#39;,&#39;);var arr &#61; [];for (var i &#61; 0 ; i < deps.length ; i&#43;&#43;) {if (this.dependencies.hasOwnProperty(deps[i])) {arr.push(this.dependencies[deps[i]])}}return function(){func.apply(scope || {}, arr);}}
OK&#xff0c;这次我们不用在意参数的顺序了&#xff0c;但是angular远比我们要想的多&#xff0c;大多数情况下&#xff0c;我们的js都是要压缩的&#xff0c;所以function的实参会被替换&#xff0c;如果是那样的话&#xff0c;我们这个方法的argDecl&#xff3b;1&#xff3d; &#61; “$scope,$http”;就会是argDecl&#xff3b;1&#xff3d; &#61; “r,t”;类似这样的变量&#xff0c;那么又该怎么解决呢&#xff1f;
angular官方有这样的解释&#xff1a;
为了克服压缩引起的问题&#xff0c;只要在控制器函数里面给$inject属性赋值一个依赖服务标识符的数组&#xff0c;就像&#xff1a;
var MyController &#61; [&#39;$scope&#39;, &#39;$http&#39;, function($scope, $http) { }];
那么&#xff0c;用到我们这个方法里面又该怎么实现呢&#xff1f;那我们在看看angular的源码吧&#xff1a;
....} else if (isArray(fn)) {last &#61; fn.length - 1;assertArgFn(fn[last], &#39;fn&#39;)$inject &#61; fn.slice(0, last);} else {....
看到了吧&#xff0c;之所以用到数组也是有原因的&#xff0c;把需要的依赖写在方法的前面&#xff0c;于是&#xff0c;应用到我们的reslove方法&#xff1a;
resolve: function(func, scope) {if (isArray(func)) {var last &#61; func.length - 1;var deps &#61; func.slice(0, last);func &#61; func[last]} else {var FN_ARGS &#61; /^function\s*[^\(]*\(\s*([^\)]*)\)/m;var STRIP_COMMENTS &#61; /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;var fnText &#61; func.toString().replace(STRIP_COMMENTS, &#39;&#39;);var argDecl &#61; fnText.match(FN_ARGS);var deps &#61; argDecl[1].split(&#39;,&#39;);}var arr &#61; [];for (var i &#61; 0 ; i < deps.length ; i&#43;&#43;) {if (this.dependencies.hasOwnProperty(deps[i])) {arr.push(this.dependencies[deps[i]])}}return function(){func.apply(scope || {}, arr);}}
OK&#xff0c;到这里&#xff0c;便可以用我们的inject来模拟angular的依赖注入了&#xff0c;当然&#xff0c;真正angular的依赖注入还有很多东西&#xff0c;这里就不在详细描述了。