javascript - 关于何时在 TypeScript 中调用装饰器的困惑

标签 javascript angular typescript decorator

我的印象是 TypeScript 中的装饰器是在类的构造函数之后调用的。但是,有人告诉我其他情况,例如,this 的最佳答案post 声称 Decorators 在声明类时被调用——而不是在实例化对象时调用。我参加的一门 Angular 类(class)的 Udemy 讲师还告诉我,Typescript 中的装饰器属性初始化之前运行。

但是,我在这个主题上的实验似乎表明情况并非如此。例如,这是一段带有属性绑定(bind)的简单 Angular 代码:

test.component.ts

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-test',
  template: '{{testString}}'  
})
export class TestComponent{    
  @Input() testString:string ="default string";    
  constructor() {
    console.log(this.testString);
   }       
}

app.component.html

<app-test testString="altered string"></app-test>

当我执行代码时,控制台记录“默认字符串”而不是“更改后的字符串”。这证明装饰器是在类的构造函数执行后调用的。

谁能给我一个关于何时调用装饰器的明确答案?因为我的在线研究与我所做的实验相矛盾。谢谢!

最佳答案

Decorators are called when the class is declared—not when an object is instantiated.

没错。

作为@H.B.已经说过,我们可以通过查看转译后的代码来证明这一点。

var TestComponent = /** @class */ (function () {
    function TestComponent() {
        this.testString = "default string";
        console.log(this.testString);
    }
    __decorate([
        core_1.Input(),
        __metadata("design:type", String)
    ], TestComponent.prototype, "testString", void 0);
    TestComponent = __decorate([
        core_1.Component({
            selector: 'app-test',
            template: '{{testString}}'
        }),
        __metadata("design:paramtypes", [])
    ], TestComponent);
    return TestComponent;
}());

现在,让我们通过后续步骤了解您错在哪里。

第一步,主要目的是提供元数据

When I execute the code, the console logs "default string" instead of "altered string". This proves that decorators are called after the constructor of a class executes.

只有知道 @Input() 装饰器的作用,您才能确定。

Angular @Input 装饰器只是用一些信息装饰 组件属性。

这只是元数据,即 storedTestComponent.__prop__metadata__ 属性中。

Object.defineProperty(constructor, PROP_METADATA, {value: {}})[PROP_METADATA]

enter image description here

第 2 步。Angular 编译器。

现在是 Angular 编译器收集有关组件的所有信息(包括 @Input 元数据)以生成组件工厂的时候了。准备好的元数据如下所示:

{
  "selector": "app-test",
  "changeDetection": 1,
  "inputs": [
    "testString"
  ],
  ...
  "outputs": [],
  "host": {},
  "queries": {},
  "template": "{{testString}}"
}

(注意:当 Angular TemplateParser 遍历模板时,它使用此元数据 to check 指令是否有名称为 testString 的输入)

基于元数据编译器constructs updateDirective 表达式:

if (dirAst.inputs.length || (flags & (NodeFlags.DoCheck | NodeFlags.OnInit)) > 0) {
  updateDirectiveExpressions =
      dirAst.inputs.map((input, bindingIndex) => this._preprocessUpdateExpression({
        nodeIndex,
        bindingIndex,
        sourceSpan: input.sourceSpan,
        context: COMP_VAR,
        value: input.value
      }));
}

将包含在生产工厂中:

enter image description here

我们可以注意到上面的更新表达式是在父 View (AppComponent)中生成的。

第 3 步。变更检测

在 Angular 生成所有工厂并初始化所有必要的对象后,它从顶 View 到所有 subview 循环运行变化检测。

在此过程中 Angular 调用checkAndUpdateView函数,它还调用了 updateDirectiveFn :

export function checkAndUpdateView(view: ViewData) {
  if (view.state & ViewState.BeforeFirstCheck) {
    view.state &= ~ViewState.BeforeFirstCheck;
    view.state |= ViewState.FirstCheck;
  } else {
    view.state &= ~ViewState.FirstCheck;
  }
  shiftInitState(view, ViewState.InitState_BeforeInit, ViewState.InitState_CallingOnInit);
  markProjectedViewsForCheck(view);
  Services.updateDirectives(view, CheckType.CheckAndUpdate);  <====

这是您的 @Input 属性 gets value 的第一个位置:

providerData.instance[propName] = value;
if (def.flags & NodeFlags.OnChanges) {
  changes = changes || {};
  const oldValue = WrappedValue.unwrap(view.oldValues[def.bindingIndex + bindingIdx]);
  const binding = def.bindings[bindingIdx];
  changes[binding.nonMinifiedName !] =
    new SimpleChange(oldValue, value, (view.state & ViewState.FirstCheck) !== 0);
}

如您所见,它发生在 ngOnChanges Hook 之前。

结论

Angular 在装饰器执行期间不会更新 @Input 属性值。变更检测机制负责此类事情。

关于javascript - 关于何时在 TypeScript 中调用装饰器的困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49461933/

相关文章:

javascript - 在 typescript 中返回实体数组的最简洁方法是什么?

javascript - 从javascript中的多维数组中删除列

javascript - Angular:如何配置 'ng serve' 命令来打开隐身模式浏览器?

angular - 如何更新 Angular 中提供者提供的值?

初始化前从 REST 服务的 Angular 加载路由

javascript - 当相机移动三个js时线消失

javascript - 如何获取所有具有固定起始字符串的html元素

javascript - 处理、镜像或翻转图像而不影响其他图像

javascript/jquery 使用 contenteditable 进行语法高亮显示

javascript - Angular 2路由在导航时隐藏选择器