Open breakinferno opened 6 years ago
首先搞清楚几个概念:依赖、DI、IoC
依赖: 通俗的讲,一个对象需要完成某个动作或者功能,需要依靠另外一个对象提供功能或服务,则就是依赖
控制反转:(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度。通俗的将,控制反转就是将创建依赖的对象的实例的权限交给第三方,而当前对象不关心创建的过程及逻辑,只需指定我们需要的依赖即可。
依赖注入:(Dependency Injection,简称DI)是IOC的一种实现方式,对象在指定了依赖之后,根据指定的依赖标志获取具体的依赖实例,注入到对象中即可,以此来达到控制范围的目的。
依赖注入的本质是设计模式的依赖倒置原则(DIP)。
我们知道一般来说,一个对象只能通过三种方法来得到它的依赖项目:
constructor(iB){this.b = iB}
contructor(){this.b = new B()}
可以看出第三种就是依赖注入了。依赖注入的调用者不必知道依赖的创建细节,只需能够获取依赖并且能够使用该依赖即可。其他两种方式都会引起其他困难的挑战,例如污染全局作用域以及使隔离变得几乎不可能。
总的来说有三种方式,angular的controller、service、factory、filter等都是通过依赖注入方式进行注入。
1 查询推断注入:通过函数签名中的参数列表注入依赖
myModule.controller('MyCtrl', function($scope) { // doSomething });
2 内联数组注入的方式: 通过数组指定依赖
myModule.controller('MyCtrl', ['$scope', function($scope) { // doSomething });
3 $inject属性注入: 通过指定函数的$inject来指定依赖
var MyCtrl = function($scope) { // doSomething }; MyCtrl.$inject = ['$scope']; myModule.controller('MyCtrl', MyCtrl);
三种依赖注入的方式比较:
目前第二种方式最为常见,既避免压缩导致的错误,用于也无需刻意指定$inject属性,因此依赖过程更为透明。
var MyController = function($scope,$http){ $scope.test = 1; $http.get(''); } var inject = { dependencies: {}, register: function(key, value) { this.dependencies[key] = value; }, resolve: function(deps, func, scope) { var arr = []; for (var i = 0 ; i < deps.length ; i++) { if (this.dependencies.hasOwnProperty(deps[i])) { arr.push(this.dependencies[deps[i]]) } } return function(){ func.apply(scope || {}, arr); } } } inject.register('$http', {'get':function(){console.log('get')}}); inject.register('$scope', {'test':''}); inject.register('$location', {'hash':function(){console.log('hash')}});
存在一些缺陷,比如参数位置不同就不行。
var ARROW_ARG = /^([^(]+?)=>/; var FN_ARGS = /^[^(]*\(\s*([^)]*)\)/m; var FN_ARG_SPLIT = /,/; var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; var $injectorMinErr = minErr('$injector'); function stringifyFn(fn) { return Function.prototype.toString.call(fn); } // 得到依赖项 function extractArgs(fn) { var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''), args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS); return args; } function anonFn(fn) { // For anonymous functions, showing at the very least the function signature can help in // debugging. var args = extractArgs(fn); if (args) { return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')'; } return 'fn'; } function annotate(fn, strictDi, name) { var $inject, argDecl, last; if (typeof fn === 'function') { if (!($inject = fn.$inject)) { $inject = []; if (fn.length) { if (strictDi) { if (!isString(name) || !name) { name = fn.name || anonFn(fn); } throw $injectorMinErr('strictdi', '{0} is not using explicit annotation and cannot be invoked in strict mode', name); } argDecl = extractArgs(fn); 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; } function createInternalInjector(cache, factory) { // 查找依赖项所对应的对象 function getService(serviceName, caller) { if (cache.hasOwnProperty(serviceName)) { if (cache[serviceName] === INSTANTIATING) { throw $injectorMinErr('cdep', 'Circular dependency found: {0}', serviceName + ' <- ' + path.join(' <- ')); } return cache[serviceName]; } else { try { path.unshift(serviceName); cache[serviceName] = INSTANTIATING; cache[serviceName] = factory(serviceName, caller); return cache[serviceName]; } catch (err) { if (cache[serviceName] === INSTANTIATING) { delete cache[serviceName]; } throw err; } finally { path.shift(); } } } // 执行时注入 function injectionArgs(fn, locals, serviceName) { var args = [], $inject = createInjector.$$annotate(fn, strictDi, serviceName); for (var i = 0, length = $inject.length; i < length; i++) { var 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, serviceName)); } return args; } function isClass(func) { // Support: IE 9-11 only // IE 9-11 do not support classes and IE9 leaks with the code below. if (msie || typeof func !== 'function') { return false; } var result = func.$$ngIsClass; if (!isBoolean(result)) { result = func.$$ngIsClass = /^class\b/.test(stringifyFn(func)); } return result; } // 调用 function invoke(fn, self, locals, serviceName) { if (typeof locals === 'string') { serviceName = locals; locals = null; } var args = injectionArgs(fn, locals, serviceName); if (isArray(fn)) { fn = fn[fn.length - 1]; } if (!isClass(fn)) { // http://jsperf.com/angularjs-invoke-apply-vs-switch // #5388 return fn.apply(self, args); } else { args.unshift(null); return new (Function.prototype.bind.apply(fn, args))(); } } function instantiate(Type, locals, serviceName) { // 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) {}]); var ctor = (isArray(Type) ? Type[Type.length - 1] : Type); var args = injectionArgs(Type, locals, serviceName); // Empty object at position 0 is ignored for invocation with `new`, but required. args.unshift(null); return new (Function.prototype.bind.apply(ctor, args))(); } return { invoke: invoke, instantiate: instantiate, get: getService, annotate: createInjector.$$annotate, has: function(name) { return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); } }; }
更多的看这里
DI的核心就是将依赖对象的创建交给第三方,带来的好处如下:
依赖注入
什么是依赖注入
首先搞清楚几个概念:依赖、DI、IoC
依赖: 通俗的讲,一个对象需要完成某个动作或者功能,需要依靠另外一个对象提供功能或服务,则就是依赖
控制反转:(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度。通俗的将,控制反转就是将创建依赖的对象的实例的权限交给第三方,而当前对象不关心创建的过程及逻辑,只需指定我们需要的依赖即可。
依赖注入:(Dependency Injection,简称DI)是IOC的一种实现方式,对象在指定了依赖之后,根据指定的依赖标志获取具体的依赖实例,注入到对象中即可,以此来达到控制范围的目的。
依赖注入的本质是设计模式的依赖倒置原则(DIP)。
我们知道一般来说,一个对象只能通过三种方法来得到它的依赖项目:
constructor(iB){this.b = iB}
而非contructor(){this.b = new B()}
可以看出第三种就是依赖注入了。依赖注入的调用者不必知道依赖的创建细节,只需能够获取依赖并且能够使用该依赖即可。其他两种方式都会引起其他困难的挑战,例如污染全局作用域以及使隔离变得几乎不可能。
Angular依赖注入方式
总的来说有三种方式,angular的controller、service、factory、filter等都是通过依赖注入方式进行注入。
1 查询推断注入:通过函数签名中的参数列表注入依赖
2 内联数组注入的方式: 通过数组指定依赖
3 $inject属性注入: 通过指定函数的$inject来指定依赖
三种依赖注入的方式比较:
目前第二种方式最为常见,既避免压缩导致的错误,用于也无需刻意指定$inject属性,因此依赖过程更为透明。
实现
自己实现
存在一些缺陷,比如参数位置不同就不行。
angular实现
更多的看这里
好处
DI的核心就是将依赖对象的创建交给第三方,带来的好处如下: