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

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

ただいまの
回答率

87.60%

constな配列を操作することは一般的に許されていないか

受付中

回答 8

投稿

  • 評価
  • クリップ 0
  • VIEW 748

score 66

以下のコードでは、定数として宣言した配列を操作しています。

const arr = []
arr.push(1)

「この場所以降では決して配列を変更しない」ということを示したいという思いでこのように記述しているのですが、やはりあまり好かれない書き方でしょうか?

もしバッドプラクティスとして一般的に認知されているのでしたら、こういった記述の仕方は控えようと思います。世間がどのように感じるかを知りたいです。回答よろしくお願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 8

+7

べき論

回答で「べき論」と「可否論」が混在している気がします。

「この場所以降では決して配列を変更しない」ということを示したいという思いでこのように記述しているのですが、やはりあまり好かれない書き方でしょうか?
もしバッドプラクティスとして一般的に認知されているのでしたら、こういった記述の仕方は控えようと思います。世間がどのように感じるかを知りたいです。回答よろしくお願いします。

これはべき論ですが、そもそも、プログラミングに一般的にこうというルールはないので、気にしないで下さい。
コーディング規約の話なら、有名どころのコーディング規約を複数読んで比較検討すればよい話ですが、「一般的」という論調になると

  • varは古いので使うべきでは有りません。let,constを使いましょう。
  • アロー関数こそが現在の流行です。関数宣言は捨てましょう。

「一般的」にはこれらと同じにおいを感じます。

  • varは関数スコープで宣言できるのでまだ使い道があります
  • アロー関数だけを使ってイベントハンドラ関数でthis値がとれないと嘆いている人を見たことがあります

結局、古いのが問題なのではなく、古い機能が現在書いているコードにマッチするかが重要で、新しくても古くてもそれは同じです。
同じように「一般的だから使う」のではなくて「その機能が要件にマッチしているから使う」のだと思うのです。

const と Object.freeze()

const は再代入を禁止する機能を持ちますが、プロパティの書き換えを禁止するわけではありません。

const a = [];
a.push(1);
a = 1;  // TypeError: Assignment to constant variable.

Object.freeze はプロパティの書き換えを禁止しますが、再代入を禁止するわけではありません。

let a2 = [];
a2 = [];
Object.freeze(a2);
a2.push(1); // TypeError: Cannot add property 0, object is not extensible

両方を使えば、再代入もプロパティ書き換えも禁止されます。

const a3 = Object.freeze([]);
a3.push(1); // TypeError: Cannot add property 0, object is not extensible

これは一般的だから使うわけでは有りません。
「再代入禁止」と「プロパティ書き換え禁止」の要件がある時に使うのです。

各々の機能を把握すれば、使うべき場面は自ずと判断できると思います。

Re: toshiyan さん

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+5

constな配列

というのが、const arr = []のことなら、constなのは変数arrであって、配列は別にconstという概念は無いでしょう。

配列を変更しないのなら、Object.freeze()でしょう。

そんなことより、;を付けてないことの方が気になります。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/10/03 01:59

    セミコロンレスで書く人多いですよね。
    でも何も知らないうちに、省略できるんだ!って感じではやるべきではない、と思います。
    (ASIを理解してやっているならまだ別ですが)

    キャンセル

  • 2020/10/03 11:12

    回答ありがとうございます!

    なるほど…配列の中身を変更したくない時は、明示的にフリーズするといいのですね。参考になります。

    セミコロンを使っていない理由はシンプルで、今参加しているプロジェクトがセミコロンを使わない文化だからですね。

    キャンセル

  • 2020/10/03 21:32 編集

    文化、ですか・・・。
    その文化にしてる理由などは、確認したほうがいいかもしれませんね。
    JavaScriptは、そもそも仕様としてセミコロンが必要で、その補填をする機能がある、というだけなので。
    (それ故に、書き方を間違えると、その補填機能のASIが下手に効いてエラーになる)
    ASIがどういう挙動をするかを認識した上で、記法をルール化してるなら、問題ないと思います。

    キャンセル

+5

もしバッドプラクティスとして一般的に認知されているのでしたら、こういった記述の仕方は控えようと思います。世間がどのように感じるかを知りたいです。回答よろしくお願いします。

そんなことはありません。
むしろ、arrを再代入させたくないのであれば、むしろ、constで宣言すべきでしょう。

ただし、JavaScriptでは、このような仕様(定数宣言したオブジェクトにも、プロパティの再代入は可能という仕様)ですが、
他の言語では、定数定義をしたオブジェクトには、pushのようなこともできない言語の方が多いように思うので、注意が必要です。

例えば、Swiftは、letが定数宣言なのですが、
letで宣言された配列変数には、要素を追加もできません。
(イミュータブルという)
対して、JavaScriptは、いかなる場合でも、
再宣言を除き、オブジェクトに関しては、常にミュータブルとなります。
(再宣言、再代入はできないが、プロパティなど、中身は再代入や追加、削除が可能)

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/10/03 11:02

    回答ありがとうございます!

    JavaScriptでは受け入れられている記述なのですね。他の言語経験者が見ると「ん?」と思う記述なので使うのが悩むところです。

    キャンセル

  • 2020/10/03 21:50

    JavaScriptに限らず、言語間ではこういった細かな仕様の違いがありますので、新しく学ぶ、使う言語は勉強が必要ですね。

    JavaScriptではむしろ、letで宣言することもやめよう的な書き方さえ存在します。
    いろいろと緩い言語なので、
    そういった、個人や現場間でいろいろな書き方や、考え方が存在するのかと思います。

    キャンセル

+3

TypeScriptではconstと別にreadonlyが存在します。

const readOnlyArr: readonly number[] = [1];

// コンパイルエラー
readOnlyArr.push(2);

const constArr: number[] = [3];

// こちらは問題なし
constArr.push(4);

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/10/03 07:51

    こうして併存していると、「constはあくまで『変数に入れたオブジェクトが入れ替わらない』という意味しか持たない」ということがはっきりする感じもします。

    キャンセル

  • 2020/10/03 11:00

    回答ありがとうございます!

    なるほど…。TypeScriptでは型宣言で変更不可にできるのですね。参考になります。

    キャンセル

+1

あまり好かれない書き方でしょうか?

破壊的変更を加えることは一般的に、あまり好かれない書き方だと思います。

以下のように新しい変数(定数)に代入すると良いです。

const arr = [1, 2, 3]
const newArr = arr.concat(4)
console.log(arr) // [1, 2, 3]
console.log(newArr) // [1, 2, 3, 4]

 

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/10/03 11:07

    回答ありがとうございます!

    今の議論の対象は「constな配列に破壊的操作をすること」であって「配列に破壊的操作をすること」ではないため、回答が少しずれている気がします。

    キャンセル

  • 2020/10/03 12:48 編集

    「この場所以降では決して配列を変更しないということを示したい」ということであれば、対象の配列をdeep freezeさせるしか、JavaScriptでは方法がないです。ちなみに、constな配列かconstな配列でないかに関わらず、破壊的変更は避けたいため、結論一緒です。コードレビュー時に質問にあるようなコードをみかけたら、指摘が入ると思うので、わざわざ、deep freezeによって、対象の配列に変更が加わらないことを示さなくても大丈夫かと。

    キャンセル

+1

個人的には、コードリードしているときに「const宣言された配列の要素は変更されない」とは思っていないです。なので、気になりません。
その他のオブジェクトも同様です。たとえば、DOMオブジェクトはユーザーの操作によってプロパティが変化しうると思いますが、これがconstで宣言されていても特段の違和感はありません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

0

constな配列を操作することは一般的に許されていないか

一般的にはしないと思います。
配列に限ればconstで定義したからには、以降の処理で要素に変化が無い方がコードが読み易い。直観的です。
だからdeveloper.mozilla.orgのサンプルコードでもletを使っているんじゃないかと思います。

操作の対象をオブジェクトにまで話を広げた場合は、const定義以降は中身を変更しないという基本的な考えは踏襲しつつも、constに対してappendするようなコードが信頼出来るドキュメント上で見つかればそれを真似ます。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

-2

const
上記URLにもあるけど

// 配列も同じ
const MY_ARRAY = [];
// 配列にアイテムをプッシュすることができる
MY_ARRAY.push('A'); // ["A"]
// しかし、新しい配列を代入するのはエラーになる - Uncaught TypeError: Assignment to constant variable.
MY_ARRAY = ['B'];

constの使い方は「この変数は定数です」(値が変化しない)と指し示す物だから。
初期値を設定する意味でのpushなら書き方的にOKだが
通常の変数の様に初期値から値を変更するのは書き方的にNG

もしpushがダメなら

const msec = 24 * 60 * 60 * 1000;


みたいな計算式自体NGになると思うんだけど

追記
例えば
「共通処理でinputの初期値を定数として持っておきたい
となった時、初期値なので定数constで指定となるが
配列の長さは画面によって可変し、また順番もあるので
[.push]での書き方のほうが指定しやすい。」
という事だと理解しています。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/10/03 08:47

    再代入と変更を区別せず説明しているように見えますが
    const arr = [1];
    arr[0] = 3;
    の場合も、書き方的にOKということでしょうか?

    キャンセル

  • 2020/10/03 11:09

    回答ありがとうございます!

    JavaScriptをよく知っている人であれば普通に受け入れられる記述みたいですね。

    キャンセル

  • 2020/10/03 12:04

    > const arr = [1]; // これはOK
    > arr[0] = 3; // これはNG (Firefoxで確認したら変数自体が消えました)

    現実的にJavascript自体が処理を受け入れている以上
    完全に「pushの書き方がいけない」とは言えないと私は思っています。
    ただ使い方として「定数」の概念から外れなければ良いという事です。

    キャンセル

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

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

関連した質問

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