在这篇文章中,我们将带领您了解pythonbuilt-indecorators的全貌,同时,我们还将为您介绍有关@babel/plugin-proposal-decorators错误Decorator
在这篇文章中,我们将带领您了解python built-in decorators的全貌,同时,我们还将为您介绍有关@babel/plugin-proposal-decorators 错误 Decorators transform is necessary、Angular 2 Decorators - 1、Angular 2 Decorators - 2、Angular 2 Decorators - 3的知识,以帮助您更好地理解这个主题。
本文目录一览:- python built-in decorators
- @babel/plugin-proposal-decorators 错误 Decorators transform is necessary
- Angular 2 Decorators - 1
- Angular 2 Decorators - 2
- Angular 2 Decorators - 3
python built-in decorators
Built-in Decorators
Python has two built-in decorators.
-
@staticmethod
-
The
staticmethod
decorator modifies a method function so that it does not use theself
variable. The method function will not have access to a specific instance of the class.For an example of a static method, see the section called “Static Methods and Class Method”.
-
@classmethod
-
The
classmethod
decorator modifies a method function so that it receives the class object as the first parameter instead of an instance of the class. This method function wil have access to the class object itself.
The @classmethod
decorator is used to create singleton classes. This is a Python technique for defining an object which is also a unique class. The class definition is also the one and only instance. This gives us a very handy, easy-to-read way to segregate attributes into a separate part of a class declaration. This is a technique used heavily by Python frameworks.
Generally, a function decorated with @classmethod
is used for introspection of a class. An introspection method looks at the structure or features of the class, not the values of the specific instance.
Here''s a contrived example of using introspection to display some features of a object''s class.
Example 26.1. introspection.py
import types
class SelfDocumenting( object ):
@classmethod
def getMethods( aClass ):
return [ (n,v.__doc__) for n,v in aClass.__dict__.items()
if type(v) == types.FunctionType ]
def help( self ):
"""Part of the self-documenting framework"""
print self.getMethods()
class SomeClass( SelfDocumenting ):
attr= "Some class Value"
def __init__( self ):
"""Create a new Instance"""
self.instVar= "some instance value"
def __str__( self ):
"""Display an instance"""
return "%s %s" % ( self.attr, self.instVar )
We import the |
|
We define a superclass that includes two methods. The classmethod, |
|
We use the |
|
Every subclass of |
Here''s an example of creating a class and calling the help method we defined. The result of the getMethods
method function is a list of tuples with method function names and docstrings.
>>>
ac= SomeClass()
>>>
ac.help()
[(''__str__'', ''Display an instance''), (''__init__'', ''Create a new Instance'')]
Difference between @staticmethod and @classmethod decorators
http://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python
A staticmethod is a method that knows nothing about the class or instance it was called on. It just gets the arguments that were passed, no implicit first argument. It is basically useless in Python -- you can just use a module function instead of a staticmethod.
A classmethod, on the other hand, is a method that gets passed the class it was called on, or the class of the instance it was called on, as first argument. This is useful when you want the method to be a factory for the class: since it gets the actual class it was called on as first argument, you can always instantiate the right class, even when subclasses are involved.
Maybe a bit of example code will help: Notice the difference in the call signatures of foo
, class_foo
and static_foo
:
class A(object): def foo(self,x): print "executing foo(%s,%s)"%(self,x) @classmethod def class_foo(cls,x): print "executing class_foo(%s,%s)"%(cls,x) @staticmethod def static_foo(x): print "executing static_foo(%s)"%x
a=A()
Below is the usual way an object instance calls a method. The object instance, a
, is implicitly passed as the first argument.
a.foo(1) # executing foo(<__main__.A object at 0xb7dbef0c>,1)
With classmethods, the class of the object instance is implicitly passed as the first argument instead of self
.
a.class_foo(1) # executing class_foo(<class ''__main__.A''>,1)
You can also call class_foo
using the class. In fact, if you define something to be a classmethod, it is probably because you intend to call it from the class rather than from a class instance. A.foo(1)
would have raised a TypeError, but A.class_foo(1)
works just fine:
A.class_foo(1) # executing class_foo(<class ''__main__.A''>,1)
One use people have found for class methods is to create inheritable alternative constructors.
With staticmethods, neither self
(the object instance) nor cls
(the class) is implicitly passed as the first argument.
a.static_foo(1) # executing static_foo(1)
foo is just a function, but when you call a.foo you don''t just get the function, you get a "curried" version of the function with the object instance a
bound as the first argument to the function. foo
expects 2 arguments, while a.foo
only expects 1 argument.
a
is bound to foo
. That is what is meant by the term "bound" below:
print(a.foo) # <bound method A.foo of <__main__.A object at 0xb7d52f0c>>
With a.class_foo
, a
is not bound to foo
, rather the class A
is bound to foo
.
print(a.class_foo) # <bound method type.class_foo of <class ''__main__.A''>>
Here, with a staticmethod, even though it is a method, a.static_foo
just returns a good ''ole function with no arguments bound. static_foo
expects 1 argument, anda.static_foo
expects 1 argument too.
print(a.static_foo) # <function static_foo at 0xb7d479cc>
@babel/plugin-proposal-decorators 错误 Decorators transform is necessary
react-mobx,mobx,注解依赖于 @babel/plugin-proposal-decorators。
7.0 - 7.1.x 阶段,@babel/plugin-proposal-decorators 的声明必须优先于 @babel/plugin-proposal-class-properties,类似如下:
options: {
presets: [''@babel/preset-env'', ''@babel/preset-react''],
plugins: [
[''@babel/plugin-proposal-decorators'', {
"legacy": true
}],
[''@babel/plugin-proposal-class-properties'', {
"loose": true
}],
''@babel/plugin-transform-modules-commonjs'',
''@babel/plugin-transform-block-scoping'',
''@babel/plugin-transform-computed-properties'',
''@babel/plugin-proposal-object-rest-spread'',
''@babel/plugin-transform-async-to-generator'',
''@babel/plugin-transform-regenerator''
]
}
此时的 babel package 环境如下:
{
"@babel/core": "^7.1.6",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-proposal-decorators": "^7.2.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/plugin-transform-async-to-generator": "^7.1.0",
"@babel/plugin-transform-block-scoping": "^7.1.5",
"@babel/plugin-transform-computed-properties": "^7.0.0",
"@babel/plugin-transform-modules-commonjs": "^7.1.0",
"@babel/plugin-transform-regenerator": "^7.0.0",
"@babel/polyfill": "^7.0.0",
"@babel/preset-env": "^7.1.6",
"@babel/preset-react": "^7.0.0",
}
babel 升级到 7.2,任意一个 babel 的 plugin,特别是 @babel/plugin-proposal-decorators 或 @babel/plugin-proposal-class-properties 中的任意一个,只要升级了(后安装),就要全部都升级到 7.2 。
否则就是就会出现针对注解的代码,提示如下错误(即 7.0 - 7.1 @babel/core 调用 7.2 的 plugins):
SyntaxError: [文件].js: Decorators transform is necessary.
头疼,比后端依赖库管理还复杂。
package 各插件依赖信息如下:
{
"@babel/core": "^7.2.2",
"@babel/plugin-proposal-class-properties": "^7.2.3",
"@babel/plugin-proposal-decorators": "^7.2.3",
"@babel/plugin-proposal-object-rest-spread": "^7.2.0",
"@babel/plugin-transform-async-to-generator": "^7.2.0",
"@babel/plugin-transform-block-scoping": "^7.2.0",
"@babel/plugin-transform-computed-properties": "^7.2.0",
"@babel/plugin-transform-modules-commonjs": "^7.2.0",
"@babel/plugin-transform-regenerator": "^7.0.0",
"@babel/polyfill": "^7.0.0",
"@babel/preset-env": "^7.1.6",
"@babel/preset-react": "^7.0.0",
}
该影响只关联 babel 的插件,不关联 preset 和 polyfill runtime 系列。
Angular 2 Decorators - 1
在我们深入了解 Angular 2 中 @NgModule、@Component、@Injectable 等常见的装饰器之前,我们要先了解 TypeScript 中的装饰器。装饰器是一个非常酷的特性,最早出现在 Google 的 AtScript 中,它出现的目的是为了让开发者,开发出更容易维护、更容易理解的 Angular 代码。令人兴奋的是,在2015年 Angular 团队跟 MicroSoft 的 TypeScript 团队经过数月的的交流,最终决定采用 TypeScript 来重写 Angular 2 项目 。
装饰器是什么
它是一个表达式
该表达式被执行后,返回一个函数
函数的入参分别为 targe、name 和 descriptor
执行该函数后,可能返回 descriptor 对象,用于配置 target 对象
装饰器的分类
类装饰器 (Class decorators)
属性装饰器 (Property decorators)
方法装饰器 (Method decorators)
参数装饰器 (Parameter decorators)
TypeScript 类装饰器
类装饰器声明:
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void
类装饰器顾名思义,就是用来装饰类的。它接收一个参数:
target: TFunction - 被装饰的类
看完第一眼后,是不是感觉都不好了。没事,我们马上来个例子:
function Greeter(target: Function): void {
target.prototype.greet = function (): void {
console.log(''Hello!'');
}
}
@Greeter
class Greeting {
constructor() {
// 内部实现
}
}
let myGreeting = new Greeting();
myGreeting.greet(); // console output: ''Hello!'';
上面的例子中,我们定义了 Greeter 类装饰器,同时我们使用了 @Greeter 新的语法,来使用装饰器。
(备注:读者可以直接复制上面的代码,在 TypeScript Playground 中运行查看结果)。
有的读者可能想问,例子中总是输出 Hello! ,能自定义输出的问候语么 ?这个问题很好,答案是可以的。具体实现如下:
function Greeter(greeting: string) {
return function(target: Function) {
target.prototype.greet = function(): void {
console.log(greeting);
}
}
}
@Greeter(''您好'')
class Greeting {
constructor() {
// 内部实现
}
}
let myGreeting = new Greeting();
myGreeting.greet(); // console output: ''您好!'';
TypeScript 属性装饰器
属性装饰器声明:
declare type PropertyDecorator = (target:Object, propertyKey: string | symbol ) => void;
属性装饰器顾名思义,用来装饰类的属性。它接收两个参数:
target: Object - 被装饰的类
propertyKey:string | symbol - 被装饰类的属性名
趁热打铁,马上来个例子热热身:
function LogChanges(target: Object, key: string) {
var propertyValue: string = this[key];
if(delete this[key]) {
Object.defineProperty(target, key, {
get: function () {
return propertyValue;
},
set: function(newValue) {
propertyValue = newValue;
console.log(`${key} is now ${propertyValue}`);
}
});
}
}
class Fruit {
@LogChanges
name: string;
}
let fruit = new Fruit();
fruit.name = ''apple''; // console output: ''name is now apple''
fruit.name = ''banana''; // console output: ''name is now banana''
那么问题来了,如果用户想在属性变化的时候,自动刷新页面,而不是简单地在控制台输出消息,那要怎么办?我们能不能参照类装饰器自定义问候语的方式,来实现监测属性变化的功能。具体实现如下:
function LogChanges(callbackObject: any) {
return function(target: Object, key: string): void {
var propertyValue: string = this[key];
if(delete this[key]) {
Object.defineProperty(target, key, {
get: function () {
return propertyValue;
},
set: function(newValue) {
propertyValue = newValue;
callbackObject.onchange.call(this, propertyValue);
}
});
}
}
}
class Fruit {
@LogChanges({
onchange: function(newValue: string): void {
console.log(`The fruit is ${newValue} now`);
}
})
name: string;
}
let fruit = new Fruit();
fruit.name = ''apple''; // console output: ''The fruit is apple now''
fruit.name = ''banana''; // console output: ''The fruit is banana now''
TypeScript 方法装饰器
方法装饰器声明:
declare type MethodDecorator = <T>(target:Object, propertyKey: string | symbol, descriptor: TypePropertyDescript<T>) => TypedPropertyDescriptor<T> | void;
方法装饰器顾名思义,用来装饰类的属性。它接收三个参数:
target: Object - 被装饰的类
propertyKey: string | symbol - 方法名
descriptor: TypePropertyDescript - 属性描述符
废话不多说,直接上例子:
function LogOutput(tarage: Function, key: string, descriptor: any) {
var originalMethod = descriptor.value;
var newMethod = function(...args: any[]): any {
var result: any = originalMethod.apply(this, args);
if(!this.loggedOutput) {
this.loggedOutput = new Array<any>();
}
this.loggedOutput.push({
method: key,
parameters: args,
output: result,
timestamp: new Date()
});
return result;
};
descriptor.value = newMethod;
}
class Calculator {
@LogOutput
double (num: number): number {
return num * 2;
}
}
let calc = new Calculator();
calc.double(11);
// console ouput: [{method: "double", output: 22, ...}]
console.log(calc.loggedOutput);
最后我们来看一下参数装饰器:
TypeScript 参数装饰器
参数装饰器声明:
declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number ) => void
参数装饰器顾名思义,是用来装饰函数参数,它接收三个参数:
target: Object - 被装饰的类
propertyKey: string | symbol - 方法名
parameterIndex: number - 方法中参数的索引值
function Log(target: Function, key: string, parameterIndex: number) {
var functionLogged = key || target.prototype.constructor.name;
console.log(`The parameter in position ${parameterIndex} at ${functionLogged} has
been decorated`);
}
class Greeter {
greeting: string;
constructor(@Log phrase: string) {
this.greeting = phrase;
}
}
// console output: The parameter in position 0 at Greeter has
// been decorated
我有话说
1.Object.defineProperty() 方法有什么用 ?
Object.defineProperty 用于在一个对象上定义一个新的属性或者修改一个已存在的属性,并返回这个对象。 方法的签名:Object.defineProperty(obj, prop, descriptor) ,参数说明如下:
obj 需要定义的属性对象
prop 需被定义或修改的属性名
descriptor 需被定义或修改的属性的描述符
对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个拥有可写或不可写值的属性。存取描述符是由一对 getter-setter 函数功能来描述的属性。描述符必须是两种形式之一,不能同时是两者。
数据描述符和存取描述符均具有以下可选键值:
configurable
当且仅当该属性的 configurable 为 true 时,该属性才能够被改变,也能够被删除。默认为 false。enumerable
当且仅当该属性的 enumerable 为 true 时,该属性才能够出现在对象的枚举属性中。默认为 false。
数据描述符同时具有以下可选键值:
value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。writable
当且仅当仅当该属性的writable为 true 时,该属性才能被赋值运算符改变。默认为 false。
存取描述符同时具有以下可选键值:
get
一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。该方法返回值被用作属性值。默认为undefined。set
一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。该方法将接受唯一参数,并将该参数的新值分配给该属性。默认为undefined。
使用示例:
var o = {}; // 创建一个新对象
Object.defineProperty(o, "a", {value : 37, writable : true, enumerable : true,
configurable : true});
总结
本文主要介绍了 TypeScript 中的四种装饰器,了解装饰器的基本分类和实现原理,为我们下一篇深入 Angular 2 的 @NgModule、@Component、@Injectable 等常用装饰器做好铺垫。
Angular 2 Decorators - 2
在 Angular 2 Decorators - part 1 文章中,我们介绍了 TypeScript 中的四种装饰器。本文的主要目的是介绍 Angular 2 中常见的内置装饰器。Angular 2 内置装饰器分类:
-
类装饰器
@Component、@NgModule、@Pipe、@Injectable
-
属性装饰器
@Input、@Output、@ContentChild、@ContentChildren、@ViewChild、@ViewChildren
-
方法装饰器
@HostListener
-
参数装饰器
@Inject、@Optional、@Self、@SkipSelf、@Host
Angular 2 类装饰器示例:
import { NgModule, Component } from ''@angular/core'';
@Component({
selector: ''example-component'',
template: ''<div>Woo a component!</div>''
})
export class ExampleComponent {
constructor() {
console.log(''Hey I am a component!'');
}
}
Angular 2 属性装饰器示例:
import { Component, Input } from ''@angular/core'';
@Component({
selector: ''example-component'',
template: ''<div>Woo a component!</div>''
})
export class ExampleComponent {
@Input()
exampleProperty: string;
}
Angular 2 方法装饰器示例:
import { Component, HostListener } from ''@angular/core'';
@Component({
selector: ''example-component'',
template: ''<div>Woo a component!</div>''
})
export class ExampleComponent {
@HostListener(''click'', [''$event''])
onHostClick(event: Event) {
// clicked, `event` available
}
}
Angular 2 参数装饰器示例:
import { Component, Inject } from ''@angular/core'';
import { MyService } from ''./my-service'';
@Component({
selector: ''example-component'',
template: ''<div>Woo a component!</div>''
})
export class ExampleComponent {
constructor(@Inject(MyService) myService) { // 与myService: MyService等价
console.log(myService);
}
}
下面我们就着重分析一下最常用的类装饰器 @Component ,其它的装饰器读者有兴趣的话,可以参考 Component 的分析思路自行分析。
import { Component } from ''@angular/core'';
@Component({
selector: ''my-app'',
template: `<h1>Hello {{name}}</h1>`,
})
export class AppComponent {
name = ''Angular'';
}
首先从最简单的例子入手,我们都知道采用 TypeScript 开发,为了保证兼容性最终都会转换成标准的 ES 5代码。上面的例子转成如下的代码:
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
...
};
define(["require", "exports", "@angular/core"], function (require, exports, core_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var AppComponent = (function () {
function AppComponent() {
this.name = ''Angular'';
}
return AppComponent;
}());
AppComponent = __decorate([
core_1.Component({ // (1)
selector: ''my-app'',
template: "<h1>Hello {{name}}</h1>",
})
], AppComponent);
exports.AppComponent = AppComponent;
});
通过 Angular 2 Decorators - part 1 文章,我们知道 TypeScript 类装饰器的声明:
declare type ClassDecorator = <TFunction extends Function>(target: TFunction)
=> TFunction | void;
而转换后 ES5 代码中 __decorate 函数的方法签名是 function (decorators, target, key, desc) 。因此我们可以推断,core_1.Component 是一个函数,该函数调用后返回一个 ClassDecorator 。类似于 Angular 2 Decorators - part 1 文章中的 Greeter 装饰器:
function Greeter(greeting: string) {
return function(target: Function) {
target.prototype.greet = function(): void {
console.log(greeting);
}
}
}
@Greeter(''您好'')
class Greeting {
constructor() {
// 内部实现
}
}
let myGreeting = new Greeting();
myGreeting.greet(); // console output: ''您好!'';
那我们来看一下 @angular/core 模块中导出的 Component 函数:
/**
* Component decorator and metadata.
*/
export const Component: ComponentDecorator = <ComponentDecorator>makeDecorator(
''Component'', {
selector: undefined, // 用于定义组件在HTML代码中匹配的标签
inputs: undefined, // 组件的输入属性
outputs: undefined, // 组件的输出属性
host: undefined, // 绑定宿主的属性、事件等
exportAs: undefined, // 导出指令,使得可以在模板中调用
moduleId: undefined, // 包含该组件模块的id,它被用于解析模板和样式的相对路径
providers: undefined, // 设置组件及其子组件可以用的服务
viewProviders: undefined, // 设置组件及其子组件(不含ContentChildren)可以用的服务
changeDetection: ChangeDetectionStrategy.Default, // 指定组件使用的变化检测策略
queries: undefined, // 设置组件的查询条件
templateUrl: undefined, // 为组件指定一个外部模板的URL地址
template: undefined, // 为组件指定一个内联的模板
styleUrls: undefined, // 为组件指定一系列用于该组件的样式表文件
styles: undefined, // 为组件指定内联样式
animations: undefined, // 设置组件相关动画
encapsulation: undefined, // 设置组件视图包装选项
interpolation: undefined, // 设置默认的插值运算符,默认是"{{"和"}}"
entryComponents: undefined // 设置需要被提前编译的组件
},
Directive);
让我们继续来看一下 makeDecorator 这个函数:
// @angular/core/src/util/decorators.ts
/**
* const Component: ComponentDecorator = <ComponentDecorator>makeDecorator(
* ''Component'', {...}, Directive);
*/
function makeDecorator(name, props, parentClass, chainFn) {
// name: ''Component'', props: {...}, parentClass: Directive
if (chainFn === void 0) { chainFn = null; }
// 创建Metadata构造函数
var metaCtor = makeMetadataCtor([props]);
// objOrType: { selector: ''my-app'', template: "<h1>Hello {{name}}</h1>" }
function DecoratorFactory(objOrType) {
// 确保已经引入了Reflect库
if (!(Reflect && Reflect.getMetadata)) {
throw ''reflect-metadata shim is required when using class decorators'';
}
// 判断this对象是否为DecoratorFactory的实例,若是则合并metadata信息
if (this instanceof DecoratorFactory) {
metaCtor.call(this, objOrType);
return this;
}
var annotationInstance = new DecoratorFactory(objOrType);
var chainAnnotation = typeof this === ''function'' &&
Array.isArray(this.annotations) ? this.annotations : [];
chainAnnotation.push(annotationInstance);
// 定义类装饰器,参数即要装饰的类
var TypeDecorator = function TypeDecorator(cls) {
// 首先先获取装饰类关联的annotations信息,若不存在则创建
// 保存上面创建的annotationInstance实例,并调用保存更新后的annotations信息
var annotations = Reflect.getOwnMetadata(''annotations'', cls) || [];
annotations.push(annotationInstance);
Reflect.defineMetadata(''annotations'', annotations, cls);
return cls;
};
TypeDecorator.annotations = chainAnnotation;
TypeDecorator.Class = Class;
if (chainFn) chainFn(TypeDecorator);
return TypeDecorator;
}
if (parentClass) {
DecoratorFactory.prototype = Object.create(parentClass.prototype);
}
DecoratorFactory.prototype.toString = function () { return ("@" + name); };
DecoratorFactory.annotationCls = DecoratorFactory;
return DecoratorFactory;
}
// 生成Metadata构造函数
function makeMetadataCtor(props: ([string, any] | {[key: string]: any})[]): any {
// args: [{ selector: ''my-app'', template: "<h1>Hello {{name}}</h1>" }]
return function ctor(...args: any[]) {
props.forEach((prop, i) => {
// argVal: { selector: ''my-app'', template: "<h1>Hello {{name}}</h1>" }
const argVal = args[i];
if (Array.isArray(prop)) {
this[prop[0]] = argVal === undefined ? prop[1] : argVal;
} else {
// propName: ''selector'' | ''template''
for (const propName in prop) {
this[propName] =
argVal && argVal.hasOwnProperty(propName) ?
argVal[propName] : prop[propName];
}
}
});
};
}
通过阅读以上的源码,我们发现当调用 makeDecorator(''Component'', {..}, Directive) 方法时,返回的是
DecoratorFactory 函数,该函数只接收一个参数,当调用该工厂函数时,则返回 TypeDecorator 函数即类装饰器。回到最早的例子,当我们调用 core_1.Component({ selector: ''my-app'', template: "..." }) 创建的 annotationInstance 实例,内部结构如下:
{
selector: ''my-app'',
inputs: undefined,
outputs: undefined,
host: undefined,
exportAs: undefined,
moduleId: undefined,
providers: undefined,
viewProviders: undefined,
changeDetection: ChangeDetectionStrategy.Default,
queries: undefined,
templateUrl: undefined,
template: "<h1>Hello {{name}}</h1>",
styleUrls: undefined,
styles: undefined,
animations: undefined,
encapsulation: undefined,
interpolation: undefined,
entryComponents: undefined
}
现在我们来梳理一下整个流程,系统初始化的时候,会调用 makeDecorator(''Component'', {..}, Directive) 方法,创建 ComponentDecorator 工厂 。我们编写的 @Component 组件转换成 ES 5 代码后,会使用用户自定义的 metadata 信息作为参数,自动调用 ComponentDecorator 工厂函数,该函数内部实现的主要功能就是创建 annotationInstance 对象,最后返回 TypeDecorator 类装饰器。该类装饰器会被 __decorate([...], AppComponent) 函数调用,参数 traget 就是我们要装饰的类 。
我有话说
因为一个类可以应用多个装饰器,所以 var annotations = Reflect.getOwnMetadata(''annotations'', cls) || [] 语句中,annotations 的值是数组。在 Angular 2 中,应用多个装饰器的情形是使用 @Optional 、@Inject()、@Host 等参数装饰器,描述构造函数中需要注入的依赖对象。
通过 Reflect.defineMetadata API 定义的 metadata 信息,是保存在 window[''__core-js_shared__''] 对象的 metadata 属性中。感兴趣的话,大家可以直接在 Console 控制台,输入 window[''__core-js_shared__''] 查看该对象内部保存的信息。
@Component 中 @ 符号的作用是为了告诉 TypeScript 编译器,@ 后面的是装饰器函数或装饰器工厂,需要特殊处理。假设在 @Component({...}) 中去掉 @ 符号,那么变成了普通的函数调用,这样马上就会报错,因为我们并没有定义 Component 函数。通过观察转换后的代码,我们发现 @Component({...}) 被转换成 core_1.Component ,它就是从 @angular/core 导入的装饰器函数。
总结
本文介绍了 Angular 2 中最常用的 ComponentDecorator 装饰器,并通过简单的例子,一步步分析该装饰器的内部工作流程。不过我们只介绍了 Angular 2 框架内部如何解析、创建及保存 metadata 信息,还未涉及到组件初始化的过程中,如何读取、应用组件对应的 metadata 信息。另外在后续的 Angular 2 DI 文章中,我们还会继续分析其它装饰器的工作原理。
Angular 2 Decorators - 3
在 Angular 2 Decorators part -1 和 part -2 文章中,我们介绍了 Decorator 的分类和 Angular 2 常见的内置装饰器,并且我们深入分析了 ComponentDecorator 内部工作原理。此外,我们还发现在 TypeDecorator 类装饰器内部,使用了 Reflect 对象提供的 getOwnMetadata 和 defineMetadata 方法,实现 metadata 信息的读取和保存。具体可参照下图:
Angular 2 metadata 类型分为:
annotations
design:paramtypes
propMetadata
parameters
(备注:其中 design:paramtypes 和 parameters metadata 类型是用于依赖注入)
接下来我们来看一下具体示例:
以上代码成功运行后,在浏览器控制台中,输入 window[''__core-js_shared__''] 即可查看 AppComponent 相关连的 metadata 信息:
示例代码
import { Component, Inject, ViewChild, HostListener, ElementRef } from ''@angular/core'';
@Component({
selector: ''my-app'',
template: `<h1 #greet> Hello {{ name }} </h1>`,
})
export class AppComponent {
name = ''Angular'';
@ViewChild(''greet'')
private greetDiv: ElementRef;
@HostListener(''click'', [''$event''])
onClick($event: any) {
console.dir($event);
}
constructor(public appService: AppService,
@Inject(CONFIG) config: any) {
}
}
编译后的 ES 5 代码片段:
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {...};
var __metadata = (this && this.__metadata) || function (k, v) {...};
var __param = (this && this.__param) || function (paramIndex, decorator) {...};
var AppComponent = (function () {
// AppComponent构造函数
function AppComponent(appService, config) {
this.appService = appService;
this.name = ''Angular'';
}
AppComponent.prototype.onClick = function (event) {
console.dir(event);
};
__decorate([
core_1.ViewChild(''greet''),
__metadata(''design:type'', core_1.ElementRef) // 标识greetDiv属性类型
], AppComponent.prototype, "greetDiv", void 0);
__decorate([
core_1.HostListener(''click'', [''$event'']),
__metadata(''design:type'', Function), // 标识onClick类型
__metadata(''design:paramtypes'', [Object]), // 标识onClick参数类型
__metadata(''design:returntype'', void 0) // 标识返回值类型
], AppComponent.prototype, "onClick", null);
AppComponent = __decorate([
core_1.Component({ // 调用ComponentDecoratorFactory返回TypeDecorator
selector: ''my-app'',
template: "<h1 #greet> Hello {{ name }} </h1>",
}),
__param(1, core_1.Inject(config_1.CONFIG)),
__metadata(''design:paramtypes'', [app_service_1.AppService, Object])
], AppComponent);
return AppComponent;
}());
exports.AppComponent = AppComponent;
总结
本文主要介绍了 angular 2 中 metadata 分类,并通过一个实际的案例,阐述了 Angular 2 内部装饰器与 metadata 之间的映射关系。window[''__core-js_shared__''] 对象内保存的 metadata 信息,是 Angular 2 依赖注入的基础,也为我们揭开了 Angular 2 依赖注入神秘的面纱。
我们今天的关于python built-in decorators的分享已经告一段落,感谢您的关注,如果您想了解更多关于@babel/plugin-proposal-decorators 错误 Decorators transform is necessary、Angular 2 Decorators - 1、Angular 2 Decorators - 2、Angular 2 Decorators - 3的相关信息,请在本站查询。
本文标签: