作为面向对象编程中实现控制反转(Inversion of Control,下文称IoC)最常见的技术手段之一,依赖注入(Dependency Injection,下文称DI)可谓在OOP编程中大行其道经久不衰。比如在J2EE中,就有大名鼎鼎的执牛耳者Spring。Javascript社区中自然也不乏一些积极的尝试,广为人知的AngularJS很大程度上就是基于DI实现的。遗憾的是,作为一款缺少反射机制、不支持Annotation语法的动态语言,Javascript长期以来都没有属于自己的Spring框架。当然,伴随着ECMAScript草案进入快速迭代期的春风,Javascript社区中的各种方言、框架可谓群雄并起,方兴未艾。可以预见到,优秀的JavascriptDI框架的出现只是早晚的事。
本文总结了Javascript中常见的依赖注入方式,并以inversify.js为例,介绍了方言社区对于Javascript中DI框架的尝试和初步成果。文章分为四节:
一. 基于Injector、Cache和函数参数名的依赖注入
二. AngularJS中基于双Injector的依赖注入
三. TypeScript中基于装饰器和反射的依赖注入
四. inversify.js——Javascript技术栈中的IoC容器
一. 基于Injector、Cache和函数参数名的依赖注入
尽管Javascript中不原生支持反射(Reflection)语法,但是Function.prototype上的toString方法却为我们另辟蹊径,使得在运行时窥探某个函数的内部构造成为可能:toString方法会以字符串的形式返回包含function关键字在内的整个函数定义。从这个完整的函数定义出发,我们可以利用正则表达式提取出该函数所需要的参数,从而在某种程度上得知该函数的运行依赖。
比如Student类上write方法的函数签名write(notebook, pencil)就说明它的执行依赖于notebook和pencil对象。因此,我们可以首先把notebook和pencil对象存放到某个cache中,再通过injector(注入器、注射器)向write方法提供它所需要的依赖:
var cache = {};// 通过解析Function.prototype.toString()取得参数名function getParamNames(func) { // 正则表达式出自http://krasimirtsonev.com/blog/article/Dependency-injection-in-JavaScript var paramNames = func.toString().match(/^function/s*[^/(]*/(/s*([^/)]*)/)/m)[1]; paramNames = paramNames.replace(/ /g, ''); paramNames = paramNames.split(','); return paramNames;}var injector = { // 将func作用域中的this关键字绑定到bind对象上,bind对象可以为空 resolve: function (func, bind) { // 取得参数名 var paramNames = getParamNames(func); var params = []; for (var i = 0; i < paramNames.length; i++) { // 通过参数名在cache中取出相应的依赖 params.push(cache[paramNames[i]]); } // 注入依赖并执行函数 func.apply(bind, params); }}; function Notebook() {}Notebook.prototype.printName = function () { console.log('this is a notebook');}; function Pencil() {}Pencil.prototype.printName = function () { console.log('this is a pencil');}; function Student() {}Student.prototype.write = function (notebook, pencil) { if (!notebook || !pencil) { throw new Error('Dependencies not provided!'); } console.log('writing...');};// 提供notebook依赖cache['notebook'] = new Notebook();// 提供pencil依赖cache['pencil'] = new Pencil();var student = new Student();injector.resolve(student.write, student); // writing...
新闻热点
疑难解答
图片精选