为什么我可以在javascript中更改常量的值


102

我知道ES6尚未标准化,但是目前许多浏览器都支持 const JS中的关键字。

规范中写道:

常量的值不能通过重新分配而更改,并且常量也不能重新声明。因此,尽管可以在不初始化的情况下声明常量,但这样做是没有用的。

当我做这样的事情:

const xxx = 6;
xxx = 999;
xxx++;
const yyy = [];
yyy = 'string';
yyy = [15, 'a'];

我看到一切正常xxx6yyy[]

但是,如果这样做yyy.push(6); yyy.push(1);,我的常量数组已更改。现在是[6, 1]这样,顺便说一句,我仍然无法用更改它yyy = 1;

我是一个错误,还是我错过了什么?我在最新的chrome和FF29中尝试过


1
您可以只创建一个类,声明变量并在类内分配其值吗?然后,为该变量创建一个GETTER;并且不实施setter。它应该实现一个不变的……
2014年

8
@Andrew,谢谢,但是我没有问我该怎么做。我很好奇const关键字为什么会这样表现。
萨尔瓦多·达利

Answers:


174

该文档指出:

...常量不能通过重新分配进行更改
...常量不能重新声明

当您添加到数组或对象时,您无需重新分配或重新声明该常数,它已经被声明并分配了,您只是将其添加到该常数指向的“列表”中。

所以这很好用:

const x = {};

x.foo = 'bar';

console.log(x); // {foo : 'bar'}

x.foo = 'bar2';

console.log(x); // {foo : 'bar2'}  

还有这个:

const y = [];

y.push('foo');

console.log(y); // ['foo']

y.unshift("foo2");

console.log(y); // ['foo2', 'foo']

y.pop();

console.log(y); // ['foo2']

但这些都不是:

const x = {};
x = {foo: 'bar'}; // error - re-assigning

const y = ['foo'];
const y = ['bar']; // error - re-declaring

const foo = 'bar'; 
foo = 'bar2';       // error - can not re-assign
var foo = 'bar3';   // error - already declared
function foo() {};  // error - already declared

4
所以您的意思是这不是错误,但是应该可以这样工作吗?因为我认为常量的想法是不能更改。基本上,程序员相信无论发生什么事情,任何事情都不能改变我常量内部的值。
萨尔瓦多·达利

2
我认为这并不容易,在这种情况下,常量的值是特定元素的数组。更改任何内容都意味着您更改了
veritas 2014年

6
是的,应该以这种方式工作,您不必重新分配常量,它仍然是相同的引用,您只是将常量引用添加到数组中,而数组和对象就像“列表”,对其进行修改请勿更改引用或重新声明常量。
2014年

27
@SalvadorDali:常量只读是两个不同的东西。您的变量是恒定的,但它指向数组是不是只读
马特Burland

46

发生这种情况是因为您的常量实际上存储了对该数组的引用。当您将某些东西连接到数组中时,您不是在修改常量值,而是修改它指向的数组。如果将对象分配给常量并尝试修改其任何属性,也会发生同样的情况。

如果要冻结数组或对象以使其无法修改,则可以使用该Object.freeze方法,该方法已经是ECMAScript 5的一部分。

const x = Object.freeze(['a'])
x.push('b')
console.log(x) // ["a"]

1
按照同样的逻辑,five设置为5 的常数实际上没有值5,它只是对数字5的引用。因此,如果我five++不更改常数,则仅更改它所指向的数字。
安东尼

3
@Anthony参考对象仅适用于数组和对象,不适用于原始值
Guilherme Sehn '18

1
@Anthony在您的示例中,您正在更改变量five指向的数字(该变量five曾经是数字5的标签,现在它指向另一个数字:6)。在问题(和此答案)的示例中,x始终指向同一列表;如果x是const,则不能使其指向其他列表。唯一的区别是同一列表可以增长或缩小。这仅适用于数组和对象,不适用于原语。
ShreevatsaR

9

这是我能想到的每种编程语言的一致行为。

考虑一下C-数组只是荣耀的指针。常量数组仅意味着指针的值不会改变-但实际上该地址中包含的数据是可以自由更改的。

在javascript中,允许您调用常量对象的方法(当然-否则常量对象将无济于事!)这些方法可能具有修改对象的副作用。由于javascript中的数组是对象,因此此行为也适用于它们。

您可以放心的是,常量将始终指向同一对象。对象本身的属性可以自由更改。



3

我认为这可以使您更清楚地了解这个问题:https : //codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0

基本上,它可以归结为const始终指向内存中的同一地址。您可以更改存储在该地址中的值,但也不能更改其const指向的地址。

constconst指向具有原始值的地址时,您提到的定义将成立。这是因为您不能在const不更改其地址的情况下为其分配值(因为这是分配原始值的工作方式),并且const不允许更改a的地址。

如果好像const指向非原始值,则可以编辑地址的值。


2

在搜索为什么即使定义为以下对象后仍能够更新对象时,仔细阅读了本文 const。因此,这里的要点是它不是直接的Object而是它包含的可以更新的属性。

例如,我的对象看起来像:

const number = {
    id:5,
    name:'Bob'
};

上面的答案正确指出,是const而不是其属性的是Object。因此,我将能够通过执行以下操作来更新ID或名称:

number.name = 'John';

但是,我将无法像这样更新对象本身:

number = {
    id:5,
    name:'John'
  };

TypeError: Assignment to constant variable.

1
您的示例是一个实用且正确的说明
Ebrahim

0

因为在const中您可以更改对象的值,所以该对象实际上并不存储分配数据,而是指向它。因此Javascript中的原语和对象之间存在差异。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.