質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.81%

JavaScript: const の配列の値が変えられるのはなぜ?

解決済

回答 8

投稿

  • 評価
  • クリップ 7
  • VIEW 21K+

score 894

const の配列の値が変えられるのはなぜでしょうか?

const list = [];
list.push(3);
list.push(44);
list[0] += 1;
console.log(list);

こうすると結果は[ 4, 44 ]となります。
Constと定義しているのになぜ配列の中身が変更されるのでしょうか?
また、そもそもなぜPushできるのでしょうか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 8

+16

const 宣言は、値への読み取り専用の参照を作ります。その値が不変ということではなく、その変数識別子が再代入できないというだけです。たとえば、定数がオブジェクトのコンテンツの場合、オブジェクトのコンテンツ(例 その引数)自体は変更可能です。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/const

 Object.freeze()

Object.freeze()を使えば、配列やオブジェクトを不変に出来ますが、以下のデモコードの通り、ネストされている場合は可変のままになります。

 配列

// #1
const list1 = []
list1.push(1);
console.log(list1); //[ 1 ]

// #2
const list2 = [];
Object.freeze(list2);
//list2.push(2); //TypeError: Can't add property 0, object is not extensible


// #3
const list3 = [[]];
Object.freeze(list3);
list3[0].push(3);
console.log(list3); // [ [ 3 ] ]

 オブジェクト

// #1
const obj1 = {
    id: 1
}

obj1.id = 2;
console.log(obj1.id); //2

// #2
const obj2 = {
    id: 10
}

Object.freeze(obj2);
obj2.id = 20;
console.log(obj2.id); //10

// #3
const obj3 = {
    id: 100,
    items: {
        id: 1000
    }
}

Object.freeze(obj3);
obj3.items.id = 2000;
console.log(obj3.items.id); //2000

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/10 13:26

    エラーにしたい場合はどうすりゃいいんでしょうね? Proxyでラップしてsetされたらthrow Errorとかかな

    キャンセル

  • 2018/04/10 13:27

    それですね!まさにその方法を紹介している記事を以前読んだことがあります。

    キャンセル

+8

それは配列の中身を変えているからですよ. もちろん, 配列そのものを交換する(変数listへの代入)はエラーとなります.

const list = [];
list = [];//エラー

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+8

JavaScript > リファレンス > 文と宣言 > const

const 宣言は、値への読み取り専用の参照を作ります。その値が不変ということではなく、その変数識別子が再代入できないというだけです。たとえば、定数がオブジェクトのコンテンツの場合、オブジェクトのコンテンツ(例 その引数)自体は変更可能です。

ES6のconstを使い倒すレシピ1 - 前提共有編 〜 JSおくのほそ道 #034

constの盲点
ではconst化すれば、あらゆる値の状態変化がないことを保証できるかというと、そうではありません。
const宣言の挙動としては「再代入を許容しない」ことにあります。再代入以外は許容します。

とのことです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

+6

 const は再代入不可

既出の回答通り、const は再代入不可にするだけであり、改変は可能です。
詳細は出尽くしている為、割愛します。

 Object.freeze()

こちらが本題。
HayatoKamono さんの回答に対し、defghi1977 さんが次のコメントを残しています。

エラーにしたい場合はどうすりゃいいんでしょうね? Proxyでラップしてsetされたらthrow Errorとかかな

プロパティの変更はエラーになりませんが、追加はエラーになるので再帰的にオブジェクトを freeze する事で影響範囲を広げる事は出来ます。

しかし、Object.freeze はプロパティ変更に対しては不変であってもエラーを発生させません。
ここで、[[Prototype]] 上のプロパティと直属のプロパティが別管轄である事に注目します。

var object = Object.create({a:{b:{c:{d:{e:true}}}}});

deepFreezeFromObject(Object.getPrototypeOf(object));  // [[Prototype]] を freeze
deepFreezeFromObject(object); // 直属を freeze
object.a = 1; // TypeError: Cannot assign to read only property 'a' of object '#<Object>'

object.a は存在しないのでプロパティ追加扱いになるというロジックです。
が、[[Prototype]] を辿ってプロパティ改変を試みれば、TypeErrorにならずに不変である事象に変わりはないので、ネタにしかならないですね…。

Object.getPrototypeOf(object).a = 2;  // エラーにならない
console.log(object.a);  // {b:{c:{d:{e:true}}}}

Re: Nippun さん

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/11 04:57

    プロトタイプチェーンの思考題材として良い記事

    キャンセル

+6

constはあくまで、再代入が不可能になるだけです。オブジェクトの中の各要素までは保護されていません。
このページの例にあるように、配列にpushすることも普通にできます。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/const

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+5

Array オブジェクトはミュータブル(変更可能)だからです。

var a = 1;
var b = a;
a = 2;
a === b; // false

var a = [1];
var b = a;
a[0] = 2;
a === b; // true


ミュータブルな型とイミュータブルな型の相違を知ろう - Qiita

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+3

javascriptのconstは、再代入が出来ない(一旦値が入れられたら、後で変えることができない)という意味です。
数字や文字列のようなリテラルだけでなく、質問のコードのように配列を代入することも可能です。

質問のコードで、listの値は、1つの配列(配列への参照)から変わってはいません。

そして、listから参照されているのは配列なので、値を変更したりpushやpopなど、配列で出来る事は何でもできます(配列なのですから)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+3

JavaScriptはオブジェクトについて参照渡しだなんて、信じない

今回の質問はこれが分かりやすいと感じました。
JavaScriptはオブジェクトや、それから派生して作られた配列等は
メモリ空間上に配列がポコンと作られ、list変数にメモリ空間上のアドレス値が格納されます。

console.log([] === []) // false

この結果がfalseになるのは、2つの配列をメモリ空間上に生成してアドレス値同士で比較したからです。
だから違うものとしてfalseが帰ってきます。

constが見張り続けるのはこのメモリ空間上のアドレス値です。
その中にあるメモリ空間内の実体に関しては知らないというスタンスです。

でもまぁ、list.push(hoge)みたいなので変更しまくる場合、心情的にはletを使いたくなりますね。
eslint等ではlist.pushを使いまくったからといって再代入はしていない為、不要なletを許してくれず、constで宣言しろと怒られます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.81%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る