【Angular专题】——(2)【译】Angular中的ForwardRef
原文地址:https://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html
作者:Christoph Burgdorf
译者注:文章内容比较老,控制台信息等与新框架不完全一致,理解思路即可。
一. 问题点在哪里
先做一个小声明,我们现在拥有一个AppComponent
,并使用DI系统向其中注入了一个NameService
,因为我们使用的是Typescript
,所以需要做的工作就是在构造函数的参数中声明变量nameService
的类型为NameService
,这样做的目的是为了向Angular提供运行时解析依赖所需要的相关信息。
app.ts
import { Component } from '@angular/core';
import { NameService } from './name.service';@Component({selector: 'my-app',template: '<h1>Favourite framework: {{ name }}</h1>'
})
class AppComponent {name: string;constructor(nameService: NameService) {this.name = nameService.getName();}
}
nameService.ts
export class NameService {getName () {return "Angular";}
}
上述代码是可以正常工作的,如果我们将nameService.ts中的代码直接嵌入app.ts时,会产生哪些变化呢?别着急反对,先听听我希望声明的问题点。
import { Component } from '@angular/core';@Component({selector: 'my-app',template: '<h1>Favourite framework: {{ name }}</h1>'
})
class AppComponent {name: string;constructor(nameService: NameService) {this.name = nameService.getName();}
}class NameService {getName () {return "Angular";}
}
当我们试图运行上面的代码时,它并未能够正常工作。但是在控制台上却无法得到报错信息,我猜想是因为调试Typescript代码时使用了source map。无论如何,当我们在调试器中打开Pause on caught exceptions功能时,就会在Angular框架中捕获这个错误:
Cannot resolve all parameters for AppComponent(undefined). Make sure they all have valid type or annotations
错误信息显示,AppComponent
的构造函数在被调用时,同一个文件中声明的NameService
类型的变量是undefined。这个错误提示是合理的,因为我们在定义NameService
之前就在AppComponent
的构造函数中使用了它,但是另一方面来看,在普通的ES5代码中就不会出现报错,因为函数声明会被Js解释器提升至作用域头部,不是说ES6仅仅是ES5的语法糖么?
那如果我们将NameService
的定义代码进行提前,会出现什么情况呢:
import { Component } from '@angular/core';class NameService {getName () {return "Angular";}
}@Component({selector: 'my-app',template: '<h1>Favourite framework: {{ name }}</h1>'
})
class AppComponent {name: string;constructor(nameService: NameService) {this.name = nameService.getName();}
}
此时它似乎可以正常工作了。那么问题来了:
Javascript解释器进行这样的改动意义何在呢?
二. 不对Class定义进行提升的理由
先来理解一下Javascript语言的机制,Javascript解释器不进行类的提升,是因为变量提升会导致在使用extend
关键字实现继承时会导致错误,例如当被继承者是一个合法的函数表达式时。来看这样一段ES6代码:
class Dog extends Animal {}function Animal() {this.move = function () {alert(defaultMove);}
}var defaultMove = "moving";var dog = new Dog();
dog.move();
上述代码是能够正常工作的,因为Javascript解释器对其进行了提升重组,实际相当于如下代码:
var defaultMove, dog;function Animal() {this.move = function () {alert(defaultMove);}
}class Dog extends Animal {}defaultMove = "moving";dog = new Dog();
dog.move();
然而,如果将Animal
从一个函数声明改变成一个函数表达式时,它是不会被提升的。
//将函数声明改变为函数表达式
class Dog extends Animal {}var Animal = function Animal() {this.move = function () {alert(defaultMove);}
}var defaultMove = "moving";var dog = new Dog();
dog.move();
提升后的真实执行顺序如下,函数表达式并没有被提升:
var Animal, defaultMove, dog;class Dog extends Animal {}Animal = function Animal() {this.move = function () {alert(defaultMove);}
}defaultMove = "moving";dog = new Dog();
dog.move();
如果函数表达式也被提升,那么当Dog
类继承Animal
类时就会报错,因为它还没有被声明。从上面的示例中不难看出,如果Javascript解释器对class声明也进行提升处理,就容易在类继承时出现基类未定义的错误。
三. class在使用前必须声明吗?
我们理解了class为什么不适合被提升执行顺序,这对于之前的Angular的示例来说有什么指导意义呢?我们只能通过将NameService
移动到代码顶部的方式来解除之前的报错吗?
答案是我们可以使用另一种解决方案。我们使用@Inject
注解和forwardRef
函数来替代之前方式,也就是声明一个NameService
类型的参数nameService
,如下所示:
import { Component, Inject, forwardRef } from '@angular/core';@Component({selector:'my-app',template:'<h1>Favourite framework:{{ name }}</h1>'
})
class AppComponent{name: string;constructor(@Inject(forwardRef(()=> NameService)) nameService){this.name = nameService.getName();}
}class NameService{getName(){return "Angular"}
}
forwardRef
所做的工作,就是接收一个函数作为参数,然后返回一个class,因为这个函数并不是立即被调用的,而是在NameService
声明之后才会安全地返回NameService
,也就是说当()=>NameService
执行的时候,NameService
的值已经不是undefined了。
四. 小结
这个场景并不会经常出现,一般它只在当我们想要注入在同一个文件中声明的类时才会发生,大多数情况下我们在一个文件中只会声明一个类,并且会在文件的头部引入其他依赖的类,以此来保证不会被class不进行变量提升的特性造成困扰。
五.补充
以下内容摘录自Angular中文网:
在Typescript里面,类声明的顺序很重要,如果一个类尚未定义,就不能引用它。
这通常都没有问题的,特别是遵循一个文件一个类规则的时候。但有时候循环引用可能无法避免,当类A引用类B,同时B又引用A时,就会陷入困境:它们中的某一个必须先定义。
forwardRef( )
建立一个间接引用,供Angular随后解析。
来源:华为云社区 作者:大史不说话
【Angular专题】——(2)【译】Angular中的ForwardRef相关推荐
- [译] ES6+ 中的 JavaScript 工厂函数(第八部分)
本文讲的是[译] ES6+ 中的 JavaScript 工厂函数(第八部分), 原文地址:JavaScript Factory Functions with ES6+ 原文作者:Eric Elliot ...
- 破境Angular(三)Angular构件之模块
一.知识点 Angular模块核心知识点如下: 1.模块的作用. 2.模块各个元数据的含义和作用 3.模块有哪些分类,分类原则 4.模块的惰性加载机制 5.开发时如何对模块进行规划 二.模块作用 首先 ...
- Angular企业级开发(3)-Angular MVC实现
1.MVC介绍 Model-View-Controller 在20世纪80年代为程序语言Smalltalk发明的一种软件架构.MVC模式的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并 ...
- swagger 源代码_我们如何使用swagger代码生成器从Angular 4更新到Angular 5
swagger 源代码 by Mark Grichanik 马克·格里卡尼克(Mark Grichanik) 我们如何使用swagger代码生成器从Angular 4更新到Angular 5 (How ...
- angular技巧_提升Angular技能的5个技巧
angular技巧 This summer me and Roman started a series of tweets with helpful tips and tricks about Ang ...
- angular 代码生成器_使用Angular 10构建QR代码生成器
angular 代码生成器 In this tutorial, we'll learn how to build a QR Codes generator application using the ...
- angular html模板,使用Angular HTML 模板
Angular HTML 模板定义页面在 Web 应用程序中的布局.我们对Angular HTML 模板的支持包括验证和代码智能补全. 设置舞台 Angular HTML 模板提供了一种结构化的绑定数 ...
- Angular 服务端渲染 Angular Universal 实例
标准的 Angular 应用运行在浏览器中,它会在 DOM 中渲染页面,以响应用户的操作. 而Angular Universal 会在服务端运行,生成一些静态的应用页面,稍后再通过客户端进行启动. 这 ...
- [译] JavaScript 中的 CSS:基于组件的样式的未来
本文讲的是[译] JavaScript 中的 CSS:基于组件的样式的未来, 原文地址:CSS in JavaScript: The future of component-based styling ...
- cockroachdb mysql_CockroachDB学习笔记——[译]CockroachDB中的SQL:映射表中数据到键值存储...
CockroachDB学习笔记--[译]CockroachDB中的SQL:映射表中数据到键值存储 原文标题:SQL in CockroachDB: Mapping Table Data to Key- ...
最新文章
- 微信小程序开发实战(一)开发指南
- haproxy调度web案例
- 使用kubectl port-forward暴露minikube k8s service端口
- 226. Invert Binary Tree 1
- Android成长日记-Android监听事件的方法
- 【金融申请评分卡】数据准备 - 缺失值数据清洗
- Angularjs-项目搭建
- 使用C#调用Java带MIME附件WebService方法的初步设想
- 网络通信安全基础和OpenSSL
- 养殖环控程序 三菱plc可以带物联网模块,7寸触摸屏程序
- java 阴阳师个人脚本
- MyBatis源码的学习(9)---映射器mappers的的XML解析
- 【医学图像处理】X-ray 数字射线成像
- 基于云原生的大数据产品前端实践 | 第七期图文直播文字回放
- 机器学习11-聚类,孤立点判别
- 万字报告拆解:Web3 浪潮风靡,NFT 找到新增长点,AIGC 如火如荼
- 机器学习算法系列之K近邻算法
- 优质开源:共享图书小程序3.0 全新UI 免费下载
- xp运行linux软件下载,为你演示xp系统利用U盘直接运行Linux软件 的解决方案
- devexpress gridcontrol gridview小结