Dlaczego mogę zmienić wartość stałej w javascript

102

Wiem, że ES6 nie jest jeszcze ustandaryzowane, ale wiele przeglądarek obecnie obsługuje const słowo kluczowe w JS.

W specyfikacji jest napisane, że:

Wartość stałej nie może ulec zmianie przez ponowne przypisanie, a stałej nie można ponownie zadeklarować. Z tego powodu, chociaż można zadeklarować stałą bez jej inicjalizacji, byłoby to bezcelowe.

a kiedy robię coś takiego:

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

Widzę, że wszystko jest w porządku i xxxnadal jest .6yyy[]

Ale jeśli to zrobię yyy.push(6); yyy.push(1);, moja stała tablica została zmieniona. W tej chwili tak jest [6, 1]i przy okazji nadal nie mogę tego zmienić yyy = 1;.

To jest błąd, czy coś mi brakuje? Wypróbowałem to w najnowszym chrome i FF29

Salvador Dali
źródło
1
Czy możesz po prostu utworzyć klasę, zadeklarować zmienną i przypisać jej wartość wewnątrz klasy. Następnie utwórz GETTER dla tej zmiennej; i nie wdrażaj setera. Powinien zaimplementować stałą ...
Andrew
8
@ Andrew dzięki, ale nie pytam, jak mam to zrobić. Jestem ciekaw, dlaczego słowo kluczowe const zachowuje się w ten sposób.
Salvador Dali

Odpowiedzi:

174

Dokumentacja stwierdza:

... stała nie może ulec zmianie przez ponowne przypisanie
... stałej nie można ponownie zadeklarować

Kiedy dodajesz do tablicy lub obiektu, którego nie przypisujesz ponownie ani nie deklarujesz ponownie, stała jest już zadeklarowana i przypisana, po prostu dodajesz do „listy”, na którą ta stała wskazuje.

Więc to działa dobrze:

const x = {};

x.foo = 'bar';

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

x.foo = 'bar2';

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

i to:

const y = [];

y.push('foo');

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

y.unshift("foo2");

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

y.pop();

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

ale żaden z tych:

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
adeneo
źródło
4
więc masz na myśli, że to nie jest błąd, ale powinien działać w ten sposób? Ponieważ myślałem, że idea stałej jest taka, że ​​nie można jej zmienić. Zasadniczo programista ma zaufanie, że bez względu na to, co się stanie, nic nie może zmienić wartości mojej stałej.
Salvador Dali
2
Myślę, że nie jest to takie proste, w tym przypadku wartością stałej jest tablica konkretnych elementów. Zmiana czegokolwiek oznacza zmianę wartości .
veritas
6
Tak, to powinno działać w ten sposób, nie przypisujesz ponownie stałej, to wciąż to samo odniesienie, po prostu dodajesz do tablicy stałe referencje, a tablice i obiekty są jak "listy", modyfikowanie ich robi nie zmieniać odniesienia ani ponownie deklarować stałej.
adeneo
27
@SalvadorDali: stały i tylko do odczytu to dwie różne rzeczy. Twoja zmienna jest stała , ale tablica, na którą wskazuje, nie jest tylko
Matt Burland
46

Dzieje się tak, ponieważ twoja stała faktycznie przechowuje odniesienie do tablicy. Kiedy dołączasz coś do swojej tablicy, nie modyfikujesz stałej wartości, ale tablicę, na którą wskazuje. To samo stanie się, jeśli przypiszesz obiekt do stałej i spróbujesz zmodyfikować jakąkolwiek jego właściwość.

Jeśli chcesz zablokować tablicę lub obiekt, aby nie można go było zmodyfikować, możesz użyć Object.freezemetody, która jest już częścią ECMAScript 5.

const x = Object.freeze(['a'])
x.push('b')
console.log(x) // ["a"]
Guilherme Sehn
źródło
1
Zgodnie z tą samą logiką, stała fiveustawiona na 5 w rzeczywistości nie ma wartości 5, jest po prostu odniesieniem do liczby 5. Więc jeśli to zrobię five++, nie zmieniam stałej, tylko liczbę, na którą wskazuje.
Anthony,
3
@Anthony rzeczą odniesienia działa tylko dla tablic i obiektów, wartości nie prymitywnych
Guilherme SEHN
1
@Anthony W twoim przykładzie zmieniasz liczbę, na którą fivewskazuje zmienna (zmienna była fivekiedyś etykietą dla liczby 5, teraz wskazuje inną liczbę: 6). W przykładzie w pytaniu (i tej odpowiedzi) xzawsze wskazuje na tę samą listę; jeśli xjest stała, nie możesz wskazać innej listy. Jedyną różnicą jest to, że ta sama lista może się powiększać lub zmniejszać; jest to możliwe tylko dla tablic i obiektów, a nie dla prymitywów.
ShreevatsaR
9

Jest to spójne zachowanie w każdym języku programowania, o którym przychodzi mi do głowy.

Rozważmy C - tablice to tylko gloryfikowane wskaźniki. Stała tablica oznacza tylko, że wartość wskaźnika się nie zmieni - ale w rzeczywistości dane zawarte pod tym adresem są wolne.

W javascript możesz wywoływać metody obiektów stałych (oczywiście - w przeciwnym razie obiekty stałe nie służyłyby zbyt wiele!) Te metody mogą mieć efekt uboczny modyfikowania obiektu. Ponieważ tablice w javascript są obiektami, to zachowanie dotyczy również ich.

Zapewniamy tylko, że stała będzie zawsze wskazywać na ten sam obiekt. Właściwości samego obiektu można dowolnie zmieniać.

czwartek
źródło
4

Deklaracja const tworzy odwołanie tylko do odczytu do wartości. Nie oznacza to, że wartość, którą posiada, jest niezmienna, po prostu nie można ponownie przypisać identyfikatora zmiennej. Na przykład w przypadku, gdy treść jest obiektem, oznacza to, że zawartość obiektu (np. Jego parametry) może zostać zmieniona.

Dodatkowo ważna uwaga:

Stałe globalne nie stają się właściwościami obiektu okna ...

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const

Benjamin West
źródło
3

Myślę, że dałoby to większą jasność w tej kwestii: https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0 .

Zasadniczo sprowadza się to do constciągłego wskazywania w pamięci tego samego adresu. Możesz zmienić wartość przechowywaną w tym adresie, ale nie możesz zmienić również adresu, który constwskazuje.

Wspomniana definicja constbędzie aktualna, gdy wskaże constadres, który ma wartość pierwotną. Dzieje się tak, ponieważ nie możesz przypisać do tego wartości constbez zmiany jego adresu (ponieważ tak działa przypisywanie wartości pierwotnych), a zmiana adresu a constjest niedozwolona.

Tam, gdzie tak, jakby constwskazywał na wartość inną niż pierwotna, istnieje możliwość edycji wartości adresu.

Zyxmn
źródło
2

Przeszedłem przez ten artykuł, szukając, dlaczego udało mi się zaktualizować obiekt, nawet po zdefiniowaniu go jako const. Chodzi o to, że to nie obiekt bezpośrednio, ale atrybuty, które zawiera, mogą być aktualizowane.

Na przykład mój obiekt wygląda następująco:

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

Powyższe odpowiedzi poprawnie wskazały, że to Object jest stałą, a nie jego atrybutem. W związku z tym będę mógł zaktualizować identyfikator lub nazwę, wykonując:

number.name = 'John';

Ale nie będę mógł zaktualizować samego obiektu, jak:

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

TypeError: Assignment to constant variable.
Atul O Holic
źródło
1
Twój przykład jest praktyczny i poprawny
Ebrahim
0

Ponieważ w const można zmienić wartości obiektu, więc obiekt w rzeczywistości nie przechowuje danych przypisania, ale zamiast tego wskazuje na to. więc istnieje różnica między prymitywami a obiektami w JavaScript.


źródło