上一课没有讲到创建注入器的方法createInjector。

此方法,会创建两种不同的注入器:第一种叫做providerInjector,第二种叫做instanceInjector。providerInjector是用来创建provider的,instanceInjector是用来创建一个对象实例的。

我们可以在js代码中直接使用注入器:

var myModule = angular.module("myModule", []);

myModule.factory("person", function(){    //定义了一个服务person

  return {

    name:"chaojidan"

  }

});

myModule.controller("myController", ["$scope", "$injector",     //myController依赖于服务person,但是它不声明依赖于person,而是通过注入器$injector来获得服务person的实例对象。

  function($scope, $injector){

    $injector.invoke(function(person){   //通过注入器$injector的invoke方法,把服务person的实例注入到函数function中。

      console.log(person.name);

    });

  }

])

注入器$injector的annotate方法的作用:它主要分析函数的参数签名。比如:

$injector.annotate(function(arg1,arg2){})会得到[arg1, arg2]。

在前面的操作中,我们经常使用函数参数声明的方式来注入一个服务(实例对象),其实angular就是通过annotate方法得到参数的名字,然后通过注入器实例化这些对象,最后注入到函数中。

function annotate(fn) {
var $inject,
fnText,
argDecl,
last; if (typeof fn == 'function') {
if (!($inject = fn.$inject)) {
$inject = [];
if (fn.length) {
fnText = fn.toString().replace(STRIP_COMMENTS, '');
argDecl = fnText.match(FN_ARGS);
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
arg.replace(FN_ARG, function(all, underscore, name){
$inject.push(name);
});
});
}
fn.$inject = $inject;
}
} else if (isArray(fn)) {
last = fn.length - 1;
assertArgFn(fn[last], 'fn');
$inject = fn.slice(0, last);
} else {
assertArgFn(fn, 'fn', true);
}
return $inject;
}

它是怎么获得函数的参数声明的呢?其实它是通过函数的toString方法得到整个函数的描述,然后通过正则表达式得到函数的参数。

在angular中,所有的provider都可以用来进行注入。我们创建provider有以下几种方式:

provider/factory/service/constant/value。

我们创建好provider之后,注入到哪里去呢?我们有以下几种方式来注入provider:

controller/directive/filter/service/factory等。

举个例子:

var myModule = angular.module("myModule", []);

myModule.provider("helloAngular", function(){      //通过provider方法创建一个服务提供者helloAngular

  return {

    $get : function(){    //provider方法来定义服务提供者的话,必须定义$get方法。

      var name = "chaojidan";

      function getName(){

        return name;

      }

      return {

        getName: getName

      }

    }

  }

});

myModule.controller("myController", ["$scope", "helloAngular" ,     //通过controller方法注入helloAngular服务,也就是注入一个helloAngular服务的实例对象

  function($scope, helloAngular){

    $scope.name = helloAngular.getName();     //使用helloAngular服务的实例对象

  }

])

第二个例子:

var myModule = angular.module("myModule", []);

myModule.factory("helloAngular", function(){      //通过factory方法创建一个服务提供者helloAngular

  var name = "chaojidan";

  function getName(){

    return name;

  }

  return {

    getName:getName

  }

});

myModule.controller("myController", ["$scope", "helloAngular" ,     //通过controller方法注入helloAngular服务,也就是注入一个helloAngular服务的实例对象

  function($scope, helloAngular){

    $scope.name = helloAngular.getName();     //使用helloAngular服务的实例对象

  }

])

第三个例子:

var myModule = angular.module("myModule", []);

myModule.service("helloAngular", function(){      //通过service方法创建一个服务提供者helloAngular

  this.name = "chaojidan";

  this.getName = function(){

    return this.name;

  }

});

myModule.controller("myController", ["$scope", "helloAngular" ,     //通过controller方法注入helloAngular服务,也就是注入一个helloAngular服务的实例对象

  function($scope, helloAngular){

    $scope.name = helloAngular.getName();     //使用helloAngular服务的实例对象

  }

])

其实从angular源码可以知道,创建provider的这几种方式:provider/factory/service/constant/value,其中,

provider方法是基础,其他都是调用provider方法实现的,只是参数不同。从左到右,灵活性越差。

function provider(name, provider_) {
assertNotHasOwnProperty(name, 'service');
if (isFunction(provider_) || isArray(provider_)) {
provider_ = providerInjector.instantiate(provider_);
}
if (!provider_.$get) {
throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
}
return providerCache[name + providerSuffix] = provider_;
} function factory(name, factoryFn) {     return provider(name, { $get: factoryFn });
}
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
function value(name, val) {   return factory(name, valueFn(val));
}
function constant(name, value) {
assertNotHasOwnProperty(name, 'constant');
providerCache[name] = value;
instanceCache[name] = value;
}

createInjector方法里面,其实是通过createInternalInjector方法来创建注入器的。

function createInternalInjector(cache, factory) {
function getService(serviceName) { //注入器可以用来获取一个服务的实例
if (cache.hasOwnProperty(serviceName)) {
if (cache[serviceName] === INSTANTIATING) {
throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- '));
}
return cache[serviceName];
} else {
try {
path.unshift(serviceName);
cache[serviceName] = INSTANTIATING;
return cache[serviceName] = factory(serviceName);
} catch (err) {
if (cache[serviceName] === INSTANTIATING) {
delete cache[serviceName];
}
throw err;
} finally {
path.shift();
}
}
} function invoke(fn, self, locals){ //可以用来调用一个方法
var args = [],
$inject = annotate(fn),
length, i,
key; for(i = 0, length = $inject.length; i < length; i++) {
key = $inject[i];
if (typeof key !== 'string') {
throw $injectorMinErr('itkn',
'Incorrect injection token! Expected service name as string, got {0}', key);
}
args.push(
locals && locals.hasOwnProperty(key)
? locals[key]
: getService(key)
);
}
if (!fn.$inject) {
// this means that we must be an array.
fn = fn[length];
} // http://jsperf.com/angularjs-invoke-apply-vs-switch
// #5388
return fn.apply(self, args);
} function instantiate(Type, locals) { //可以用来实例化一个对象
var Constructor = function() {},
instance, returnedValue; // Check if Type is annotated and use just the given function at n-1 as parameter
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
instance = new Constructor();
returnedValue = invoke(Type, instance, locals); return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
} return { //返回的对象,其实就是注入器
invoke: invoke,
instantiate: instantiate,
get: getService,
annotate: annotate, //可以用来分析一个函数的签名
has: function(name) {
return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
}
};
}

注入器总共有invoke,instantiate,get,annotate,has五个方法,其中,annotate用来分析一个函数的签名,也就是函数的参数,invoke用来调用一个函数,get用来获得一个服务的实例对象,instantiate用来实例化一个对象。

angularJS在初始化启动时,注册了一些内置的provider。在publishExternalAPI方法中:

$provide.provider({
$anchorScroll: $AnchorScrollProvider,
$animate: $AnimateProvider,
$browser: $BrowserProvider,
$cacheFactory: $CacheFactoryProvider,
$controller: $ControllerProvider,
$document: $DocumentProvider,
$exceptionHandler: $ExceptionHandlerProvider,
$filter: $FilterProvider,
$interpolate: $InterpolateProvider,
$interval: $IntervalProvider,
$http: $HttpProvider,
$httpBackend: $HttpBackendProvider,
$location: $LocationProvider,
$log: $LogProvider,
$parse: $ParseProvider,
$rootScope: $RootScopeProvider,
$q: $QProvider,
$sce: $SceProvider,
$sceDelegate: $SceDelegateProvider,
$sniffer: $SnifferProvider,
$templateCache: $TemplateCacheProvider,
$timeout: $TimeoutProvider,
$window: $WindowProvider
});

我们以$controller: $ControllerProvider,为例子,来看下内置的provider是如何定义的?

    function $ControllerProvider() {
var controllers = {},
CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
this.register = function(name, constructor) {
assertNotHasOwnProperty(name, 'controller');
if (isObject(name)) {
extend(controllers, name);
} else {
controllers[name] = constructor;
}
};
this.$get = ['$injector', '$window', function($injector, $window) { //provider必须有$get方法
return function(expression, locals) {
var instance, match, constructor, identifier;
if(isString(expression)) {
match = expression.match(CNTRL_REG),
constructor = match[1],
identifier = match[3];
expression = controllers.hasOwnProperty(constructor)
? controllers[constructor]
: getter(locals.$scope, constructor, true) || getter($window, constructor, true);
assertArgFn(expression, constructor, true);
}
instance = $injector.instantiate(expression, locals); //当你想去拿控制器,也就是controller实例时,实际上是注入器帮你实例化的。
if (identifier) {
if (!(locals && typeof locals.$scope == 'object')) {
throw minErr('$controller')('noscp',
"Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
constructor || expression.name, identifier);
}
locals.$scope[identifier] = instance;
}
return instance;
};
}];
}

当你的应用中,需要控制器实例对象时,也就是需要controller这个服务时,实际上是注入器在ControllerProvider(控制器服务提供者)中实例化一个控制器实例对象,然后给应用的(注入进去)。

加油!

angular核心原理解析2:注入器的创建和使用的更多相关文章

  1. angular核心原理解析1:angular自启动过程

    angularJS的源代码整体上来说是一个自执行函数,在angularJS加载完成后,就会自动执行了. angular源代码中: angular = window.angular || (window ...

  2. angular核心原理解析3:指令的执行过程

    指令的执行过程分析. 我们知道指令的执行分两个阶段,一个是compile,一个是link. 我们可以在指令中自定义compile和link. 首先,我们来讲解如何自定义link函数 举个例子: < ...

  3. 【算法】(查找你附近的人) GeoHash核心原理解析及代码实现

    本文地址 原文地址 分享提纲: 0. 引子 1. 感性认识GeoHash 2. GeoHash算法的步骤 3. GeoHash Base32编码长度与精度 4. GeoHash算法 5. 使用注意点( ...

  4. Java并发包JUC核心原理解析

    CS-LogN思维导图:记录CS基础 面试题 开源地址:https://github.com/FISHers6/CS-LogN JUC 分类 线程管理 线程池相关类 Executor.Executor ...

  5. 「进阶篇」Vue Router 核心原理解析

    前言 此篇为进阶篇,希望读者有 Vue.js,Vue Router 的使用经验,并对 Vue.js 核心原理有简单了解: 不会大篇幅手撕源码,会贴最核心的源码,对应的官方仓库源码地址会放到超上,可以配 ...

  6. ibatis 核心原理解析!

    关注下方公众号,可以在公众号后台回复“博客园”,免费获得作者 Java 知识体系/面试必看资料. 最近查找一个生产问题的原因,需要深入研究 ibatis 框架的源码.虽然最后证明问题的原因与 ibat ...

  7. ibatis 核心原理解析

    最近查找一个生产问题的原因,需要深入研究 ibatis 框架的源码.虽然最后证明问题的原因与 ibatis 无关,但是这个过程加深了对 ibatis 框架原理的理解. 这篇文章主要就来讲讲 ibati ...

  8. Promise核心原理解析

    作者: HerryLo 本文永久有效链接: https://github.com/AttemptWeb...... Promises对象被用于表示一个异步操作的最终完成 (或失败), 及其结果值.主要 ...

  9. NameServer 核心原理解析

    在之前的文章中,已经把 Broker.Producer 和 Conusmer 的部分源码和核心的机制介绍的差不多了,但是其实 RocketMQ 中还有一个比较关键但是我们平时很容易忽略的组件--Nam ...

随机推荐

  1. JS nodeJs 的日期计算

    目录[-] date-utils Static Methods 静态方法 Instance Methods 接口方法 date-utils 前端引用 <script type="tex ...

  2. Element DatePicker日期范围选择

    前7天后7天 <el-date-picker v-model="value1" type="date" :picker-options="pic ...

  3. Java常用的输出调试技巧

    --------siwuxie095                 Eclipse 开发中常用的输出调试技巧:     先在左侧的 Package Explorer,右键->New->J ...

  4. exe怎么找main函数

    先说DEBUG版本的命令行EXE main函数有两种,第一种是int main(),第二种是int main(int argc,char* argv[]) 不管哪种只要查找j___p___argc就能 ...

  5. Golang之一个简单的聊天机器人

    翠花,上代码 package main import ( "bufio" "fmt" "os" "strings" ) ...

  6. 【Centos linux系统】命令行(静默)安装oracle 11gR2

    一.安装前准备 1.内存及swap要求 至于swap如何添加,后文将提到 1 2 grep MemTotal /proc/meminfo grep SwapTotal /proc/meminfo 2. ...

  7. # 20155327 2016-2017-4 《Java程序设计》第9周学习总结

    20155327 2016-2017-4 <Java程序设计>第9周学习总结 20155327 2016-2017-4 <Java程序设计>第9周学习总结 教材学习内容总结 了 ...

  8. RocketMQ 运维指令

    1.1. 控制台使用 RocketMQ 提供有控制台及一系列控制台命令,用于管理员对主题,集群,broker 等信息的管理 登录控制台 首先进入RocketMQ 工程,进入/RocketMQ/bin ...

  9. mongodb-win32-i386-3.0.6 使用

    一.下载地址 https://fastdl.mongodb.org/win32/mongodb-win32-i386-3.0.6.zip 二.安装 1. systeminfo OS 名称: Micro ...

  10. SQL编程:group by合并结果字符串 ---> group_concat函数就能行

    1.表结构 create table tt(id int,v varchar(30));              insert into tt values(1,'a'),(1,'b'),(2,'b ...