AngularJS 浅谈DI-依赖注入 $injector的神奇之处

来源:互联网 发布:非线性最优化试题 编辑:程序博客网 时间:2024/04/29 14:48

前言

依赖注入(DI)和控制反转(IOC)都是java Spring里经典的面向对象编程的法则来削减计算机程序的耦合问题的解决方案。
在Angular中,引入了DI的思想,DI是一种工具思想,而不是一种目的,它的目的是为了降低程序代码之间的耦合。

Angular的启动方式

先看一下angular究竟是怎么启动的。
在等待DOM解析完毕后,触发DOMContentLoaded事件时,angular会尝试找到ng-app指令,从而来确定程序的边界,而angular总是以模块开始启动的。

一个简单的angular程序,大概类似于这样

angular.module("myApp",[])            .controller('myController', ['$scope', function($scope){                console.log("hello world");            }]);

上述代码比较简单我这里就不再赘述了。

然而,有的时候我们并不想显示的通过ng-app来启动angular,因此我们可以手动的调用angular.bootstrap方法来进行一个启动的操作,到这里我们首先必须明白,angular是一个单例对象,被绑定在window这个全局对象上。

在启动angular程序之前,我们必须明白的是我们必须等待浏览器解析DOM树完毕后我们才能启动angular来遍历DOM树,由于angular内部封装有轻量级的jquery,因此我们可以这样写

let app = angular.module("myApp",[]);angular.element(document).ready = () => {            angular.bootstrap(document,["myApp"]);        }

这样我们就手动的启动了一个angular程序。

三种注入方式

在调用angular.bootstrap时,在bootstrap方法内部会创建一个$injector对象,这个对象是个注入器,用于完成依赖注入的核心对象,$injector也是一个单例对象,同时它也是一个service。

现在我们大致的来看一下注入的三种方式。

1.推断注入法

let app = angular.module('myApp',[]);let myController = $scope => {    $scope.name = 'zhangsan';}app.controller('myController',myController);

这种注入方式会使得injector去寻找到与参数名相同的服务来注入,它的缺点很明显,当我们用grunt或者其他打包工具来进行代码混淆的时候,它就可能会注入失败,因为它注入的依据仅仅是参数名这个字符串而已,改变了过后就找不到了。

2.声明式注入法

let app = angular.module('myApp',[]);let myController = $scope111 => {    $scope111.name = 'zhangsan';}myController.$inject = ['$scope'];app.controller('myController',myController);

可以看到上面的myController 的参数名已经被改变了,按理说是找不到$scope111这个服务的,但是下面我们用该函数$injector属性来指向了一个数组,注入器会根据这个数组,依次传入函数的参数列表中,因此,就算代码被混淆了,也不会有任何问题,那这样做的缺点是,书写太过麻烦,每次都要去写一个$inject 来进行声明。

内联式注入法

下面介绍一种比较好的注入方式
正如介绍启动方式的时候

angular.module("myApp",[])            .controller('myController', ['$scope', function($scope){                console.log("hello world");            }]);

控制器方法的第二个参数接受一个数组,数组里声明的是需要被注入的服务名称,数组的最后一个元素必须为一个函数,这个的函数的参数列表会根据数组的前几个元素找到的对应的服务进行依次注入,这种写法其实就是第二种注入方式的简写。

浅谈$injector

$injector是一个angular内置的注入器,我们可以根据需要获取到注入器的对象。
比如

angular.injector();

这样的话我们就能拿到一个injector对象。
既然,$injector也是一个service,那么,我们可以直接通过注入器将它自己注入进来,听起来很绕?不过确实如此。

    .controller('myController', ['$scope','$injector' function($scope,$injector){                console.log("hello world");            }]);

之后我们可以调用$injector的get方法,来注入其他的服务。

angular.module("myApp",[])            .factory('nameObj',() => {                return {                    name:'zhangsan'                }            })            .controller('myController',['$scope','$injector',($scope,$injector) => {                let nameObj = $injector.get("nameObj");                console.log(nameObj.name);            }]);

可以看到控制台清楚的打印了’zhangsan’

可能我们想知道$injector是怎样知道我们想要的service的,这里就不得不说$injector的annotate方法,这个方法可以返回一个函数的参数列表,只要拿到参数列表,我们就可以去找到对应的服务来进行注入了。
比如 :

angular.injector().annotate((aaaa,bbb,cc,d) => {});

这样,我们可以清楚的看到控制台打印的参数列表

output:["aaaa", "bbb", "cc", "d"]

这里不得不顺带的提一下service,service是factory的一层包装,灵活性较低,实际上在service函数内部,angular去调用了$injector的instantiate方法来把传入的构造函数进行实例化。

从这里看来,$injector确实给angular带来了许多的活力,很多时候,我们并不关心我们怎样得到一个东西,我们只需要关系我们要得到一个什么样的东西,中间的过程都由$injector做了,这就是依赖注入(DI)

0 0
原创粉丝点击