繁体   English   中英

TypeScript:为什么我可以在构造函数中修改 `readonly` 属性?

[英]TypeScript: Why am I able to modify `readonly` property in constructor?

我有以下打字稿片段:

class Jedi { 
  private readonly name: string = 'Skywalker';
  constructor(name: string) { 
    this.name = name; //Why I am able to modify name.
  }
  toString():string {
    return this.name;
  }
}

var jedi = new Jedi('Kenobi');

console.log(jedi.toString()); //Kenobi

正如您在代码中看到的,我已将name属性声明为readonly 据我所知,将属性声明为常量,我们在 TypeScript 中使用readonly 一旦我们用初始化声明它,我们就不能修改它。

但是正如您在constructor看到的那样,它正在被修改。 此外,TypeScript 编译器也不会对此发出警告。 不确定这是 TypeScript 中的错误还是有意为之。 谁能解释一下?

文档只说

只读属性必须在其声明或构造函数中初始化。 来源

但正如 Romain 指出的,TypeScript 2.0 发行说明中有更多信息:

只读属性可能有初始值设定项,并且可以在同一个类声明中的构造函数中赋值,但不允许对只读属性赋值。 来源


让我困惑的是编译后的输出。 请参阅TS Playground上的示例。

正如您所看到的,初始(只读)属性在编译时被覆盖,而没有来自 TS 的任何警告。 我猜这是故意的,因为语言的行为一直都一样,而且边缘情况更少。

编辑:还有一种“糖化”的方式来用 TypeScript 中的属性初始化一个class (我想你知道但无论如何):

public / private添加到构造函数签名的参数会将这些参数初始化为类属性。 所以在你的例子中:

class Jedi { 
    constructor(
        private readonly name: string = 'Skywalker'
    ) {}

    toString():string {
        return this.name;
    }
}

如果让 TypeScript 编译上面的代码,它实际上会按预期工作(或者至少是我期望的那样)。 还有另一个指向操场的链接

tl;博士; 如果没有给出,则通过public / private在构造函数签名中初始化类属性将设置默认值。 如果您“手动”设置类属性( this.name = name ),这将不起作用。 后者可能仍然不是错误而是有意的行为,因为您显式设置了类属性。

你可以通过启用noImplicitAnystrictNullChecks来避免这个问题(或者至少让 TypeScript 向你尖叫)。 这样 TypeScript 会让你知道你的类属性可能是undefined


如果您想拥有不变性,我建议不要使用readonly属性。 不仅上面提到的有点奇怪的行为,一些经验不足的开发人员还可能认为readonly数组/对象在它不是的地方真的完全冻结/只读。 而是使用类似ImmutableJS 的东西。

从打字稿文档:

只读属性可能有初始值设定项,并且可以在同一个类声明中的构造函数中赋值,但不允许对只读属性赋值。

关于readonly关键字的更多信息

声明为readonly成员变量可以通过它们的声明进行赋值,也可以从构造函数中赋值。 对此有一定的 C# 熟悉。

示例 1

这里,只读成员变量是直接赋值的,如果你知道一个值是什么并且它永远不会改变,这很有用。

class Foo {
    public readonly bar: number = 123;
}

示例 2

这里声明了 readonly 成员变量,然后从构造函数赋值,如果你还不知道最终值,这很有用,但一些构造函数逻辑会为你解决这个问题。

class Foo {
    public readonly bar: number;

    constructor(bar: number) {
        this.bar = 123 * bar / 100;
    }
}

示例 3

这里的 readonly 成员变量使用了 TypeScript 的简写成员变量声明,全局成员变量可以从构造函数中赋值,如果你希望用户输入一个永远不会改变的值,这很有用。 在此特定示例中, bar可以由用户设置,也可以默认为123

class Foo {
    constructor(public readonly: bar: number = 123) {
    }
}

示例 4

这是您不想做的事情,因为发出的 JavaScript 会导致对同一变量进行两次赋值。 我只是添加这个,以便未来的开发人员知道他们可能不应该这样做。

class Foo {
    public readonly bar: number = 123;

    constructor(bar: number) {
        this.bar = bar;
    }
}

发出的 JavaScript 看起来像这样......

var Foo = (function () {
    function Foo(bar) {
        this.bar = 123; // :)
        this.bar = bar; // :(
    }

    return Foo;
})();

不变性呢?

readonly仅由 TypeScript 及其编译器支持; JavaScript 中没有只读(嗯,这并不完全正确,只是 TypeScript 不会发出真正的只读值) 对此有几种解决方法(是的,如果 TypeScript 可以为我们发出这些,那就太好了!)

创建不可变成员变量

class Foo {
    // TypeScript respects "readonly"
    public readonly bar: number;

    constructor(bar: number) {
        // JavaScript respects property that isn't writable.
        Object.defineProperty(this, "bar", {
            value: bar,
            enumerable: true,
            configurable: false,
            writable: false
        });
    }
}

创建一个不可变的类

class Foo {
    constructor(public readonly bar: number) {
        // Nothing in the object is allowed to be reassigned.
        Object.freeze(this);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM