位置: 编程技术 - 正文

Javascript技术栈中的四种依赖注入小结(javaweb技术栈是什么)

编辑:rootadmin

推荐整理分享Javascript技术栈中的四种依赖注入小结(javaweb技术栈是什么),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:javaweb技术栈是什么,javascript 栈,技术栈说明,javascript 栈,java技术栈是什么意思,java技术栈是什么意思,javaweb技术栈是什么,javaweb技术栈是什么,内容如对您有帮助,希望把文章链接给更多的朋友!

作为面向对象编程中实现控制反转(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方法提供它所需要的依赖:

有时候为了保证良好的封装性,也不一定要把cache对象暴露给外界作用域,更多的时候是以闭包变量或者私有属性的形式存在的:

比如现在要执行Student类上的另一个方法function draw(notebook, pencil, eraser),因为injector的cache中已经有了notebook和pencil对象,我们只需要将额外的eraser也存放到cache中:

通过依赖注入,函数的执行和其所依赖对象的创建逻辑就被解耦开来了。当然,随着grunt/gulp/fis等前端工程化工具的普及,越来越多的项目在上线之前都经过了代码混淆(uglify),因而通过参数名去判断依赖并不总是可靠,有时候也会通过为function添加额外属性的方式来明确地说明其依赖:

二. AngularJS中基于双Injector的依赖注入

熟悉AngularJS的同学很快就能联想到,在injector注入之前,我们在定义module时还可以调用config方法来配置随后会被注入的对象。典型的例子就是在使用路由时对$routeProvider的配置。也就是说,不同于上一小节中直接将现成对象(比如new Notebook())存入cache的做法,AngularJS中的依赖注入应该还有一个”实例化”或者”调用工厂方法”的过程。这就是providerInjector、instanceInjector以及他们各自所拥有的providerCache和instanceCache的由来。在AngularJS中,我们能够通过依赖注入获取到的injector通常是instanceInjector,而providerInjector则是以闭包中变量的形式存在的。每当我们需要AngularJS提供依赖注入服务时,比如想要获取notebook,instanceInjector会首先查询instanceCache上是存在notebook属性,如果存在,则直接注入;如果不存在,则将这个任务转交给providerInjector;providerInjector会将”Provider”字符串拼接到”notebook”字符串的后面,组成一个新的键名”notebookProvider”,再到providerCache中查询是否有notebookProvider这个属性,如有没有,则抛出异常Unknown Provider异常:

如果有,则将这个provider返回给instanceInjector;instanceInjector拿到notebookProvider后,会调用notebookProvider上的工厂方法$get,获取返回值notebook对象,将该对象放到instanceCache中以备将来使用,同时也注入到一开始声明这个依赖的函数中。

需要注意的是,AngularJS中的依赖注入方式也是有缺陷的:利用一个instanceInjector单例服务全局的副作用就是无法单独跟踪和控制某一条依赖链条,即使在没有交叉依赖的情况下,不同module中的同名provider也会产生覆盖,这里就不详细展开了。

另外,对于习惯于Java和C#等语言中高级IoC容器的同学来说,看到这里可能觉得有些别扭,毕竟在OOP中,我们通常不会将依赖以参数的形式传递给方法,而是作为属性通过constructor或者setters传递给实例,以实现封装。的确如此,一、二节中的依赖注入方式没有体现出足够的面向对象特性,毕竟这种方式在Javascript已经存在多年了,甚至都不需要ES5的语法支持。希望了解Javascript社区中最近一两年关于依赖注入的研究和成果的同学,可以继续往下阅读。

Javascript技术栈中的四种依赖注入小结(javaweb技术栈是什么)

三. TypeScript中基于装饰器和反射的依赖注入

博主本身对于Javascript的各种方言的学习并不是特别热情,尤其是现在EMCAScript提案、草案更新很快,很多时候借助于polyfill和babel的各种preset就能满足需求了。但是TypeScript是一个例外(当然现在Decorator也已经是提案了,虽然阶段还比较早,但是确实已经有polyfill可以使用)。上文提到,Javascript社区中迟迟没有出现一款优秀的IoC容器和自身的语言特性有关,那就依赖注入这个话题而言,TypeScript给我们带来了什么不同呢?至少有下面这几点:* TypeScript增加了编译时类型检查,使Javascript具备了一定的静态语言特性* TypeScript支持装饰器(Decorator)语法,和传统的注解(Annotation)颇为相似* TypeScript支持元信息(Metadata)反射,不再需要调用Function.prototype.toString方法下面我们就尝试利用TypeScript带来的新语法来规范和简化依赖注入。这次我们不再向函数或方法中注入依赖了,而是向类的构造函数中注入。TypeScript支持对类、方法、属性和函数参数进行装饰,这里需要用到的是对类的装饰。继续上面小节中用到的例子,利用TypeScript对代码进行一些重构:

下面是injector和装饰器Inject的实现。injector的resolve方法在接收到传入的构造函数时,会通过name属性取出该构造函数的名字,比如class Student,它的name属性就是字符串”Student”。再将Student作为key,到dependenciesMap中去取出Student的依赖,至于dependenciesMap中是何时存入的依赖关系,这是装饰器Inject的逻辑,后面会谈到。Student的依赖取出后,由于这些依赖已经是构造函数的引用而非简单的字符串了(比如Notebook、Pencil的构造函数),因此直接使用new语句即可获取这些对象。获取到Student类所依赖的对象之后,如何把这些依赖作为构造函数的参数传入到Student中呢?最简单的莫过于ES6的spread操作符。在不能使用ES6的环境下,我们也可以通过伪造一个构造函数来完成上述逻辑。注意为了使instanceof操作符不失效,这个伪造的构造函数的prototype属性应该指向原构造函数的prototype属性。

injector和装饰器Inject的逻辑完成后,就可以用来装饰class Student并享受依赖注入带来的乐趣了:

利用装饰器,我们还可以实现一种比较激进的依赖注入,下文称之为RadicalInject。RadicalInject对原代码的侵入性比较强,不一定适合具体的业务,这里也一并介绍一下。要理解RadicalInject,需要对TypeScript装饰器的原理和Array.prototype上的reduce方法理解比较到位。

使用RadicalInject,原构造函数实质上已经被一个新的函数代理了,使用上也更为简单,甚至都不需要再有injector的实现:

由于class Student的constructor方法需要接收三个参数,直接无参调用new Student()会造成TypeScript编译器报错。当然这里只是分享一种思路,大家可以暂时忽略这个错误。有兴趣的同学也可以使用类似的思路尝试代理一个工厂方法,而非直接代理构造函数,以避免这类错误,这里不再展开。

AngularJS2团队为了获得更好的装饰器和反射语法的支持,一度准备另起炉灶,基于AtScript(AtScript中的”A”指的就是Annotation)来进行新框架的开发。但最终却选择拥抱TypeScript,于是便有了微软和谷歌的奇妙组合。

当然,需要说明的是,在缺少相关标准和浏览器厂商支持的情况下,TypeScript在运行时只是纯粹的Javascript,下节中出现的例子会印证这一点。

四. inversify.js——Javascript技术栈中的IoC容器

其实从Javascript出现各种支持高级语言特性的方言就可以预见到,IoC容器的出现只是早晚的事情。比如博主今天要介绍的基于TypeScript的inversify.js,就是其中的先行者之一。inversity.js比上节中博主实现的例子还要进步很多,它最初设计的目的就是为了前端工程师同学们能在Javascript中写出符合SOLID原则的代码,立意可谓非常之高。表现在代码中,就是处处有接口,将”Depend upon Abstractions. Do not depend upon concretions.”(依赖于抽象,而非依赖于具体)表现地淋漓尽致。继续使用上面的例子,但是由于inversity.js是面向接口的,上面的代码需要进一步重构:

由于使用了inversity框架,这次我们就不用自己实现injector和Inject装饰器啦,只需要从inversify模块中引用相关对象:

这样就行了吗?还记得上节中提到TypeScript中各种概念只是语法糖吗?不同于上一节中直接将constructor引用传递给Inject的例子,由于inversify.js是面向接口的,而诸如NotebookInterface、PencilInterface之类的接口只是由TypeScript提供的语法糖,在运行时并不存在,因此我们在装饰器中声明依赖时只能使用字符串形式而非引用形式。不过不用担心,inversify.js为我们提供了bind机制,在接口的字符串形式和具体的构造函数之间搭建了桥梁:

注意这步需要从inversify模块中引入TypeBinding和Kernel,并且为了保证返回值类型以及整个编译时静态类型检查能够顺利通过,泛型语法也被使用了起来。说到这里,要理解new TypeBinding(“NotebookInterface”, Notebook)也就很自然了:为依赖于”NotebookInterface”字符串的类提供Notebook类的实例,返回值向上溯型到NotebookInterface。完成了这些步骤,使用起来也还算顺手:

最后,顺带提一下ECMAScript中相关提案的现状和进展。Google的AtScript团队曾经有过Annotation的提案,但是AtScript胎死腹中,这个提案自然不了了之了。目前比较有希望成为es7标准的是一个关于装饰器的提案:

javascript正则表达式总结 工具Regexpal是一个在线Javascript正则表达式处理器,地址是:

javascript中FOREACH数组方法使用示例 Array.prototype.forEach()方法让数组的每一项都执行一次给定的函数。—MDN假设有这么一个场景,你拿到了这么一个数组[{symbol:"XFX",price:.,volume:},{symbol

JavaScript的React框架中的JSX语法学习入门教程 什么是JSX?在用React写组件的时候,通常会用到JSX语法,粗看上去,像是在Javascript代码里直接写起了XML标签,实质上这只是一个语法糖,每一个XML标签

标签: javaweb技术栈是什么

本文链接地址:https://www.jiuchutong.com/biancheng/369980.html 转载请保留说明!

上一篇:javascript的 {} 语句块详解(javascript的语法在哪一项中规定)

下一篇:javascript正则表达式总结(js的正则表达式)

  • 组成计税价格是销售额吗
  • 为什么开发成本资本化
  • 小规模纳税人运输费计入什么科目
  • 金蝶软件数量金额式怎样输入数据
  • 当月有进项无销项月末怎么处理
  • 临时设施摊销方案怎么写
  • 农产品收购发票可以跨区域开吗
  • 固定资产做错了怎么办
  • 土地结转会计分录是什么
  • 财务报表有哪些局限性
  • 收回赔款会计分录
  • 出纳去银行
  • 公司委托其他公司为员工代缴社保公积金
  • 开给个人的通讯费发票能下账吗
  • 个人所得税计算器2023
  • 进项票没开过来可以先开销项票吗
  • 留抵税额怎么计算
  • 支付给供应商的现金属于什么活动
  • 金融企业不良资产批量收购处置业务
  • 购进农产品再销售
  • boss直聘收费怎么这么贵
  • 清产核资的步骤
  • 所得税税前扣除税金有哪些
  • 固定资产弃置费怎么算
  • 总分类账是什么的依据
  • windows10安装应用怎么到桌面
  • 工程暂估收入入账的会计分录
  • php零基础教程
  • 无线路由器wifi指示灯不亮
  • Laravel 5.5官方推荐的Nginx配置学习教程
  • php编辑器哪个好
  • 瑞吉外卖项目简历
  • vue全家桶教程
  • vue :data="data"
  • 微软和梅赛德斯奔驰宣布合作
  • 建设工程施工合
  • 工程物资发生报废损毁
  • 企业退税要用税吗
  • 对公账户原路退回
  • 免征增值税的项目可以开专票吗
  • 原材料入库单价20000出库单价800000
  • sql server的go
  • sqlserver2012开发版
  • 利润分配的账务处理视频
  • 跨月管理费用多计怎么处理
  • 企业期末预收账款怎么算
  • 金融企业有啥
  • 已经确认收入的商品发生销售折让
  • 如何发放银行贷款利息
  • 劳务费如何计提
  • 公路工程投标保证金
  • 附表1是什么意思
  • 人力资源公司可以开培训费发票吗
  • win7出现问题
  • win8老是自动安装软件
  • win10用着怎么样
  • win10快速访问在哪
  • 电脑win7一开机就弹出各种广告窗口怎么办
  • win8全屏快捷键
  • win7安全防护怎么关闭
  • NGUI带位置图标的拖动例子
  • javascript屏蔽元素
  • 爱家保障行动
  • react all in js
  • TNet Tasharen Networking 学习总结
  • python编程入门指南
  • linux使用文本编辑器
  • Linux 中的time函数
  • shell脚本对比两个文件夹内的内容
  • dos下重命名
  • 使用jquery实现的项目
  • Android中dip、dp、sp、pt和px的区别
  • js super方法
  • auto.js获取剪切板内容
  • 基于JAVASCRIPT实现的可视化工具是
  • 纳税申报表如何看销售额
  • 构建税务系统纵向到底
  • 发票怎样上传?
  • 房子契税发票没给怎么办
  • 新疆国税网
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设