在 TypeScript 中,const 和 readonly 有何区别?

128

Typescript中的常量与只读变量

将一个变量声明为readonly,即使它们是公共属性,也不能进行覆盖。

const的行为:

const SOME_VARIABLE:number = 10;

如果我覆盖它的值,它会如何工作?


3
请参见《TypeScript 深入解析》一书中的 readonly 与 const 的区别 章节。 - Paolo
9
更新的书籍链接:https://basarat.gitbook.io/typescript/type-system/readonly#difference-from-const这个页面解释了 TypeScript 中 "readonly" 关键字和 "const" 关键字之间的区别。"readonly" 关键字用于表示属性不可更改,而 "const" 关键字用于表示变量不可重新赋值。 - rtpHarry
2
这本书链接非常珍贵,它用一半的篇幅就解释清楚了这些答案所涉及到的差异。 - rinogo
7个回答

155

const变量不能被重新赋值,就像readonly属性一样。

实际上,在定义属性时,您可以使用readonly来防止重新赋值。这只是编译时检查。

当您定义const变量(并针对更高版本的JavaScript以保留输出中的const),该检查也会在运行时进行。

因此,它们实际上都执行相同的操作,但一个是针对变量,另一个是针对属性。

const x = 5;

// Not allowed
x = 7;


class Example {
    public readonly y = 6;
}

var e = new Example();

// Not allowed
e.y = 4;

重要提示... "不能重新分配"并不等同于不可变性。

const myArr = [1, 2, 3];

// Not allowed
myArr = [4, 5, 6]

// Perfectly fine
myArr.push(4);

// Perfectly fine
myArr[0] = 9;

3
我对这个**// Perfectly fine myArr [0] = 9;**感到困惑。你怎么能将它称为常量呢? - Aravind
79
在JS/TS中,数组是可变对象。因此,myArr是对可变对象的常量引用。你不能将其指向其他数组,但可以更改数组的内容。你(myArr)拿起了一个盒子([1, 2, 3])并将其黏在手上(const)。黏合剂意味着你不能放开它并拿起另一个盒子(myArr=[4, 5, 6])或其他任何东西 (myArr='something else')。但是黏合剂并不妨碍别人往盒子里装东西(myArr.push(4))或替换其中的一些内容(myArr[0]=9)。 - jcalz
同样的情况也发生在Object上...我们有责任使它们变成不可变的 - 它们本身并不是不可变的。 - Pedro Ferreira
4
补充一下,在TypeScript中,可以使用ReadonlyArray<T>来防止push/pop/shift等操作。这只在编译时有效,但对于公共库的API非常有用。 - Jay
1
总结:它们是相同的,但const用于变量,readonly用于类属性。 - Safa Alai
显示剩余3条评论

40

const和readonly之间的关键区别在于它们与数组的使用方式不同(除了已经提到的区别)。你必须使用:

readonly Array<T> 

在使用数组时,其中T是泛型类型(请搜索了解更多)。

当您将任何数组声明为const时,您可以对数组执行可能更改数组元素的操作。例如:

const Arr = [1,2,3];

Arr[0] = 10;   //OK
Arr.push(12); // OK
Arr.pop(); //Ok

//But
Arr = [4,5,6] // ERROR

但是,在只读数组的情况下,您无法像上面那样更改数组。

arr1 : readonly Array<number> = [10,11,12];

arr1.pop();    //ERROR
arr1.push(15); //ERROR
arr1[0] = 1;   //ERROR

20
我不认为这与手头的问题(关键字“readonly”与关键字“const”)实际上有关,尽管我不知道ReadonlyArray的存在。 - SnailCoil
4
答案与readonly和const关键字的实际行为相符。使用readonly时,无法以任何方式修改对象/数组及其属性/元素。此关键字通常用于状态管理系统中,其中我们只想从纯函数集合中修改值,而不是其他地方。 - Alex
5
虽然这与所提出的问题没有直接关系,但是感谢提供额外信息。 - Piyush

19

两种类型:

  • 都可以被改变(例如如果是数组,可以使用.push()

const:

  • 不能被重新赋值
  • 用于变量

readonly:

  • 只能在构造函数内部被重新赋值
  • 用于属性(类成员)

10
readonly 关键字不允许在数组上进行 .push() 操作:Property 'push' does not exist on type 'readonly any[]'. ts(2339) - Samathingamajig
1
有关“readonly”和构造函数的注释是其他答案中遗漏的关键要素。谢谢! - rinogo
官方文档链接是此答案和其他答案中遗漏的关键内容。 - lasec0203
as const 不允许在数组上使用 .push(),且会报错:属性“push”在类型上不存在 - mihaa123

12
  1. 我认为被接受的答案没有强调足够多的是const应该用于变量,而readonly应该用于类/接口属性。

  2. readonly仅在类型检查(编译时)期间进行检查,而const则在运行时检查。

  3. 将属性声明为readonly并不意味着其值不能被更改:这意味着该属性不能被重新赋值,例如:


interface Person {
    readonly info: { name: string; age: number };
}

//create a new person
// ...

person.info.age += 1; // this is valid
person.info = { name: "Johnny", age: 15 }; // this is invalid!
  1. 自 TypeScript 3.4 版本以来,可以使用常量断言来定义类型

// Type 'readonly [10, 20]'
let y = [10, 20] as const;

// Type '{ readonly text: "hello" }'
let z = { text: "hello" } as const;

更多信息,请参见文档


8

1
我认为两个术语的原因可能是它比替代方案更容易实现。
例如,在C++中,类可以具有常量成员。但是,在运行构造函数之前,C++具有特殊的语法来初始化常量,因此实际上从未真正进行任何对常量的“赋值”,如下所示:
class TestClass {
    const int _x;
    TestClass(int x) : _x(x) {}
}

_x(x)在构造函数被调用之前初始化变量_x。
在TypeScript中,如果他们想要允许成员声明为const并且认真对待变量的const性质,他们需要添加类似的语法。
"readonly"并不完全等同于const。我认为它意味着略微不同的东西。它的意思是“允许在构造函数内进行赋值,但之后不再允许”。

-1

const

  1. 在编译时是常量
  2. 必须在声明时初始化
  3. 可以在方法中被声明

readonly

  1. 在执行时是常量
  2. 不需要在声明时初始化
  3. 不能在方法中声明,只能在构造函数或自我声明中

readonly 在执行期间不是常量,而 const 是。两者在编译时也都是常量。 - Ontonator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接