The “const” Deception

2024-06-04  本文已影响0人  涅槃快乐是金

The const keyword in JavaScript is used to declare a constant. Constants are often thought of as “variables that can't change”:

<pre style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; color: rgb(10, 12, 16); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">

JS

<pre aria-hidden="true" style="box-sizing: inherit; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; white-space: pre-wrap; word-break: keep-all; overflow-wrap: break-word; padding: 10px; font-size: inherit; border: 0px; background: none; display: inherit; font-family: inherit; font-style: inherit; font-variant-ligatures: inherit; font-weight: inherit; letter-spacing: inherit; tab-size: inherit; text-indent: inherit; text-rendering: inherit; text-transform: inherit; position: relative; pointer-events: none;">

const hi = 5;

hi = 10;

// 🛑 Uncaught TypeError: Assignment to constant variable.

console.log(hi);

// -> 5

</pre>

</pre>

Curiously, though, when I create an object using const, I'm free to change it:

<pre style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; color: rgb(10, 12, 16); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">

JS

<pre aria-hidden="true" style="box-sizing: inherit; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; white-space: pre-wrap; word-break: keep-all; overflow-wrap: break-word; padding: 10px; font-size: inherit; border: 0px; background: none; display: inherit; font-family: inherit; font-style: inherit; font-variant-ligatures: inherit; font-weight: inherit; letter-spacing: inherit; tab-size: inherit; text-indent: inherit; text-rendering: inherit; text-transform: inherit; position: relative; pointer-events: none;">

const person = {

name: 'Hassan',

};

person.name = 'Sujata';

// Seems to work?? 🤔

console.log(person);

// -> { name: 'Sujata' }

</pre>

</pre>

How come I was able to change the person variable? I used const!

To make sense of this apparent contradiction, we need to learn about the difference between assignment and mutation. This is a core concept in JavaScript, and so many things make more sense when you have a clear understanding of this distinction.

Let's go!

Intended audience

This blog post is intended for beginner/intermediate JavaScript developers. I do assume that you've been working with JavaScript for at least a few weeks and are comfortable with the basic syntax.

Link to this heading Variable names as labels

So, here's a perfectly-valid JavaScript program:

<pre style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; color: rgb(10, 12, 16); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">

JS

<pre aria-hidden="true" style="box-sizing: inherit; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; white-space: pre-wrap; word-break: keep-all; overflow-wrap: break-word; padding: 10px; font-size: inherit; border: 0px; background: none; display: inherit; font-family: inherit; font-style: inherit; font-variant-ligatures: inherit; font-weight: inherit; letter-spacing: inherit; tab-size: inherit; text-indent: inherit; text-rendering: inherit; text-transform: inherit; position: relative; pointer-events: none;">

5;

</pre>

</pre>

Here's another one:

<pre style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; color: rgb(10, 12, 16); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">

JS

<pre aria-hidden="true" style="box-sizing: inherit; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; white-space: pre-wrap; word-break: keep-all; overflow-wrap: break-word; padding: 10px; font-size: inherit; border: 0px; background: none; display: inherit; font-family: inherit; font-style: inherit; font-variant-ligatures: inherit; font-weight: inherit; letter-spacing: inherit; tab-size: inherit; text-indent: inherit; text-rendering: inherit; text-transform: inherit; position: relative; pointer-events: none;">

['apple', 'banana', 'cherry'];

</pre>

</pre>

In both of these examples, I'm creating some stuff. A number, and an array. When the code runs, this data will be created and stored in the computer's memory.

These programs aren't terribly useful, however. I'm creating some data, but I have no way of accessing it!

Variables allow us to stick a label on the stuff we create, so that we can reference it later:

<pre style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; color: rgb(10, 12, 16); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">

JS

<pre aria-hidden="true" style="box-sizing: inherit; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; white-space: pre-wrap; word-break: keep-all; overflow-wrap: break-word; padding: 10px; font-size: inherit; border: 0px; background: none; display: inherit; font-family: inherit; font-style: inherit; font-variant-ligatures: inherit; font-weight: inherit; letter-spacing: inherit; tab-size: inherit; text-indent: inherit; text-rendering: inherit; text-transform: inherit; position: relative; pointer-events: none;">

// Create it now...

const fruits = ['apple', 'banana', 'cherry'];

// ...and access it later:

console.log(fruits);

// -> ['apple', 'banana', 'cherry']

</pre>

</pre>

When I was first learning to program, I thought that the code was executed from left to right: first we create a fruits variable, like an empty box, and then we assemble our array within that box.

It turns out that this isn't really the right mental model. It's more accurate to say that the array gets created first, and then we point our fruits label at it.

“Variables”?

In this tutorial, I'm going to use the term “variable” to refer to any names given to data, regardless of whether we use let or const:

<pre style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased;">

JS

<pre aria-hidden="true" style="box-sizing: inherit; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; white-space: pre-wrap; word-break: keep-all; overflow-wrap: break-word; padding: 10px; font-size: inherit; border: 0px; background: none; display: inherit; font-family: inherit; font-style: inherit; font-variant-ligatures: inherit; font-weight: inherit; letter-spacing: inherit; tab-size: inherit; text-indent: inherit; text-rendering: inherit; text-transform: inherit; position: relative; pointer-events: none;">

// This is a variable:

let age = 26;

// This is a variable too:

const favouriteColor = 'hotpink';

</pre>

</pre>

I recognize that this is confusing; the term “variable” implies that something can vary! Unfortunately, “variable” is the established umbrella term in JavaScript, including both let and const (as well as the deprecated var keyword).

Link to this heading Reassigning our labels

When we use the let keyword to create a variable, we're able to change which “thing” that label refers to.

For example, we can point our fruits label at a new value:

letfruits

<label for="choice-:Rcaj2m:-0" class="sc-5dc6815f-5 ijPnSE" style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; position: relative; color: black; border-radius: 4px; padding: 14px 18px; font-family: var(--font-family-mono); white-space: pre-wrap;">['apple', 'banana', 'cherry']

<input type="radio" name="reassignable-variable-:Rcaj2m:" id="choice-:Rcaj2m:-0" class="sc-5dc6815f-9 bJehnA" checked="" style="box-sizing: border-box; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; font-style: inherit; font-variant: inherit; font-weight: var(--font-weight-light); font-stretch: inherit; font-size: inherit; font-family: var(--font-family); position: absolute; inset: 0px; appearance: none; cursor: pointer; border-radius: inherit;"></label> <label for="choice-:Rcaj2m:-1" class="sc-5dc6815f-5 ijPnSE" style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; position: relative; color: black; border-radius: 4px; padding: 14px 18px; font-family: var(--font-family-mono); white-space: pre-wrap;">['durian', 'eggplant', 'fig']

<input type="radio" name="reassignable-variable-:Rcaj2m:" id="choice-:Rcaj2m:-1" class="sc-5dc6815f-9 bJehnA" style="box-sizing: border-box; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; font-style: inherit; font-variant: inherit; font-weight: var(--font-weight-light); font-stretch: inherit; font-size: inherit; font-family: var(--font-family); position: absolute; inset: 0px; appearance: none; cursor: pointer; border-radius: inherit;"></label> <label for="choice-:Rcaj2m:-2" class="sc-5dc6815f-5 ijPnSE" style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; position: relative; color: black; border-radius: 4px; padding: 14px 18px; font-family: var(--font-family-mono); white-space: pre-wrap;">['honeydew']

<input type="radio" name="reassignable-variable-:Rcaj2m:" id="choice-:Rcaj2m:-2" class="sc-5dc6815f-9 bJehnA" style="box-sizing: border-box; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; font-style: inherit; font-variant: inherit; font-weight: var(--font-weight-light); font-stretch: inherit; font-size: inherit; font-family: var(--font-family); position: absolute; inset: 0px; appearance: none; cursor: pointer; border-radius: inherit;"></label> <label for="choice-:Rcaj2m:-3" class="sc-5dc6815f-5 ijPnSE" style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; position: relative; color: black; border-radius: 4px; padding: 14px 18px; font-family: var(--font-family-mono); white-space: pre-wrap;">27

<input type="radio" name="reassignable-variable-:Rcaj2m:" id="choice-:Rcaj2m:-3" class="sc-5dc6815f-9 bJehnA" style="box-sizing: border-box; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; font-style: inherit; font-variant: inherit; font-weight: var(--font-weight-light); font-stretch: inherit; font-size: inherit; font-family: var(--font-family); position: absolute; inset: 0px; appearance: none; cursor: pointer; border-radius: inherit;"></label> <label for="choice-:Rcaj2m:-4" class="sc-5dc6815f-5 ijPnSE" style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; position: relative; color: black; border-radius: 4px; padding: 14px 18px; font-family: var(--font-family-mono); white-space: pre-wrap;">null

<input type="radio" name="reassignable-variable-:Rcaj2m:" id="choice-:Rcaj2m:-4" class="sc-5dc6815f-9 bJehnA" style="box-sizing: border-box; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; font-style: inherit; font-variant: inherit; font-weight: var(--font-weight-light); font-stretch: inherit; font-size: inherit; font-family: var(--font-family); position: absolute; inset: 0px; appearance: none; cursor: pointer; border-radius: inherit;"></label>

This is known as re-assignment. We're saying that actually, the fruits label should refer to an entirely different value:

JS

<pre aria-hidden="true" style="box-sizing: inherit; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; white-space: pre-wrap; word-break: keep-all; overflow-wrap: break-word; border: 0px; background: none; display: inherit; font-family: inherit; font-size: inherit; font-style: inherit; font-variant-ligatures: inherit; font-weight: inherit; letter-spacing: inherit; tab-size: inherit; text-indent: inherit; text-rendering: inherit; text-transform: inherit; position: relative; pointer-events: none; padding: 10px;">

// We start with a labeled array:

let fruits = ['apple', 'banana', 'cherry'];

// ⚠️⚠️⚠️⚠️

// Pick a different option from the list above

// to see how it translates in code!

</pre>

We're not modifying the data, we're modifying the label. We're detaching it from that original array, and connecting it to a new one.

By contrast, variables created with const cannot be reassigned:

constfruits

<label for="choice-:Rdqj2m:-0" disabled="" class="sc-c2b3f2e4-6 hfOlIo" style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; position: relative; color: black; border-radius: 4px; padding: 14px 18px; font-family: var(--font-family-mono); white-space: pre-wrap;">['apple', 'banana', 'cherry']

<input disabled="" type="radio" name="reassignable-variable-:Rdqj2m:" id="choice-:Rdqj2m:-0" class="sc-c2b3f2e4-9 eRlyvW" checked="" style="box-sizing: border-box; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; font-style: inherit; font-variant: inherit; font-weight: var(--font-weight-light); font-stretch: inherit; font-size: inherit; font-family: var(--font-family); position: absolute; inset: 0px; appearance: none; cursor: not-allowed; border-radius: inherit;"></label> <label for="choice-:Rdqj2m:-1" disabled="" class="sc-c2b3f2e4-6 hfOlIo" style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; position: relative; color: black; border-radius: 4px; padding: 14px 18px; font-family: var(--font-family-mono); white-space: pre-wrap;">['durian', 'eggplant', 'fig']

<input disabled="" type="radio" name="reassignable-variable-:Rdqj2m:" id="choice-:Rdqj2m:-1" class="sc-c2b3f2e4-9 eRlyvW" style="box-sizing: border-box; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; font-style: inherit; font-variant: inherit; font-weight: var(--font-weight-light); font-stretch: inherit; font-size: inherit; font-family: var(--font-family); position: absolute; inset: 0px; appearance: none; cursor: not-allowed; border-radius: inherit;"></label> <label for="choice-:Rdqj2m:-2" disabled="" class="sc-c2b3f2e4-6 hfOlIo" style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; position: relative; color: black; border-radius: 4px; padding: 14px 18px; font-family: var(--font-family-mono); white-space: pre-wrap;">null

<input disabled="" type="radio" name="reassignable-variable-:Rdqj2m:" id="choice-:Rdqj2m:-2" class="sc-c2b3f2e4-9 eRlyvW" style="box-sizing: border-box; margin: 0px; line-height: inherit; -webkit-font-smoothing: antialiased; font-style: inherit; font-variant: inherit; font-weight: var(--font-weight-light); font-stretch: inherit; font-size: inherit; font-family: var(--font-family); position: absolute; inset: 0px; appearance: none; cursor: not-allowed; border-radius: inherit;"></label>

This is the fundamental difference between let and const. When we use const, we create an indestructible link between a variable name and a piece of data.

Here's the thing, though: we're still allowed to modify the data itself! As long as the label remains intact.

For example, with an array, we can add/remove items from that array without issue. The fruits variable is still connected to the same array:

constfruits

<label for="choice-:Rfqj2m:" class="sc-2eb06e2b-8 gIOrcB" style="box-sizing: border-box; margin: 0px; line-height: calc(1em + 0.725rem); -webkit-font-smoothing: antialiased; position: relative; color: black; border-radius: 4px; padding: 16px; font-family: var(--font-family-mono);">Add Item Remove Item [ apple,]</label>

In code, this would look something like this:

上一篇下一篇

猜你喜欢

热点阅读