typescript:为什么我能在构造函数中修改`readonly`属性?

时间:2017-02-13 09:48:24

标签: typescript

我有以下打字稿class

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中的错误还是故意的。有人可以解释一下吗?

3 个答案:

答案 0 :(得分:3)

文档只说

  

必须在声明或构造函数中初始化Readonly属性。 (Source

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

  

只读属性可能具有初始值设定项,并且可能在同一类声明中的构造函数中分配,但是不允许对只读属性进行赋值。 (Source

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

正如您所看到的那样,初始(readonly)属性在编译时没有来自TS的任何警告时被覆盖。我想这是故意的,因为语言的行为始终相同,边缘情况也较少。

编辑:还有一个"糖化"使用TypeScript中的属性初始化class的方法(我想你知道但是无论如何):

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

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

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

如果让TypeScript编译上面的代码,它实际上按预期工作(或者至少是我期望的)。还有另一个Link to the Playground

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

您可以启用noImplicitAnystrictNullChecks来避免此问题(或至少让TypeScript尖叫)。这样,TypeScript会通知您,您的类属性可能是undefined

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

答案 1 :(得分:0)

从TypeScript文档:

  

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

More about the readonly keyword

答案 2 :(得分:0)

声明为readonly的成员变量可以使用其声明或构造函数进行分配。 C#对此有一定的了解。

示例1

这里,readonly成员变量是直接分配的,如果您知道某个值是什么,并且它永远不会改变,这将非常有用。

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;
})();

不变性怎么办?

只有TypeScript及其编译器支持

readonly; JavaScript 中没有readonly(嗯,这并不完全正确,只是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);
    }
}