プロが教えるわが家の防犯対策術!

幼稚な質問で恐縮です。

条件に該当した時のみ定数を宣言する事はできますか。
当然と言えば当然ですが、次のようにif内で宣言した定数をif外で使うとエラーになります。
このような使い方は想定されていないのでしょうか
function sub() {
if(1) {const x = 5}
console.log(x)
}
分岐によって使わない定数は宣言しないようにしようとしています。

A 回答 (8件)

一般的かどうかはわかりかねますが...



初期化によるデータ量や処理コストが大量ならば話は別ですが、
大した量でないならば単純な記述にしたほうが個人的には好みです。

理由
* 昔のプログラム言語は必ず事前宣言だった。現在でも踏襲する人は多い
* 分岐判断する箇所を少なくしたほうが可読性が良い
* 単純な宣言のほうがコンパイラ最適化による性能向上が見込める
    • good
    • 0
この回答へのお礼

ありがとうございます。

昔は必ず事前宣言だったのが、何らかの必要性が生じて都度宣言が解禁されたというところでしょうか。
複数の変数をコンマで区切って1行で書けるので私も事前に宣言するのが好きです。
都度宣言の利点をほんの少し調べてみましたが、進んで利用しようと思えるほどの情報は見つけられませんでした。
よって今後も踏襲し、スコープの先頭で一気に宣言する事とします。
可読性の重要性は痛いほど承知していますが、どうしても阿呆なこだわりが優先し、日を空けて読むと???なコードになっています。
速度は重要です。
変数宣言の前に毎回判断の処理を入れるより、判断なしで全てセットする方が断然速そうです。
また、関数スコープの定数が用意されていないことからも、私がやろうとしていた事は、どう見ても一般的でないとの見解が強まりました。

まもなく方針が決められそうです。

お礼日時:2023/03/16 18:02

> 関数の中で変数を宣言する必要性があるという前提



例えば、その前提がある、って以前に、「何故に無引数なのか」とか不明な点が多いのね。
フツーに計算目的なら引数で取って、それでまず計算をさせるんで充分じゃないか、って話もある。
いずれにせよ、イマイチ目的がハッキリせんし、状況によって変わるだろう以上、貴方自身が書いたコードを計測してみて、貴方が抱えてる問題での最適解を探すべきだろうねぇ。
プロファイラ、ってソフトがあって、それで計測が可能です。
JavaScriptだといくつか選択肢があって、

Microsoft Edge:
ランタイム パフォーマンスの分析を開始する: https://learn.microsoft.com/ja-jp/microsoft-edge …
メモリの問題を修正する: https://learn.microsoft.com/ja-jp/microsoft-edge …

Firefox:
Firebug: https://getfirebug.com/index.html

Chrome:
Chrome DevTools: https://developer.chrome.com/docs/devtools/

辺りを使って「自分が考えたコーディングが充分速いかどうか」チェックすることが出来ます。
    • good
    • 0
この回答へのお礼

ありがとうございます。

本件に関しては、どちらの書き方をしても、一瞬で処理が終わってしまう事と、JavaScriptで関数スコープの定数が用意されていない事から、いちいち要不要を判断せずに全て宣言する事にしました。

プロファイラでCPUの性能を削る事ができるのですね。
これで貧弱な私のPCをさらに貧弱にさせる方法が分かりました。

試しにCPU性能を削ってテストしてみると、表示が一瞬遅れます。
しかし、画面へのテキスト入力からして遅くなるなど私のコーディングとは無関係の動作も同じぐらい遅くなるので、これはユーザーのハードウエア性能の責任とします。

お礼日時:2023/03/19 10:28

> 先の例で関数内で使うx、y、zにセットする値が、関数を呼び出した時の状況で毎回変わる物だとしたら、どうでしょうか。



これでも答えは変わらんかな。貴方が「x、y、zにセットする値」って表現している以上、実は無意識に「x、y、zが既に(どっかに)ある」って発想になっている。
つまり、現時点では貴方が「関数内で変数を新しく宣言しなきゃいけない」必然性がある状況を実は思いついてない、って事を表してるんだ。
もう一回繰り返すけど、一般的なコーディングを気にする必要はない。けど、「必然性を思いつかないような」状況、って事は、そこには理がない、あるいは理が弱い、って事だよね。

ちょっと例を考えてみよう。
例えば、だ。
こういう関数を考えてみる。

function foo() {
 while (true) {
  let x = 1;
  console.log("hoge");
 }
}

これ、無限ループを行う関数なんで、実行して欲しくないんだけど(笑)、それはさておき。whileループ内で「x = 1」ってのを「生成」してる。
「生成してる」って事は字面は同じなんだけど、ループの1回目のxとループの2回目のxは実は「違うブツ」なんだよ。1回目に「生成された」xは2回目には「使われずに」、コーディング上のxに付いては新しくメモリ領域を割り当てられて新しく生成される。そして3回目のループでは2回目に生成されたメモリは「使われない」わけだ。
一般化するとn回目のループでのxではn-1回目のループで使われたxは「再利用されない」。再利用されない以上ガベージコレクタが回収していく。そして、whileがループを回す度にガベージコレクタが走って「非効率なプログラム」になるわけだ。
同じ書くなら

function foo() {
 let x = 1;
 while (true) {
  console.log("hoge");
 }
}

って書いた方が「マシ」だって事はわかるでしょう。こっちなら関数foo内でxを「生成」するのはたった1回、そしてfoo()が終了した時点でガベージコレクタが走る回数も1回だ。

つまり、通常、「関数の中で変数を宣言する」必要性が生じるのは、

1. 何らかの計算で計算式を書く時にそのまま書くと煩わしい為、エイリアスとして変数が必要な場合。あるいは、「再計算」をさせたくない場合。
2. クロージャを生成する必要性が生じた場合。

の2つのケースに集約されると思う。・・・これは「一般的なコーディングがどうの」って話じゃなくって、大体思いつく「必然性」はそれくらいなんじゃないか、って話だ。
例えば2番だと、有名なケースとしては

function foo() {
 let x = 1;
 return function bar(n) {
  return x += n;
 }
}

みたいに「環境を閉じ込める」場合に使われたりする。

js> f = foo()
(function bar(n) {
return x += n;
})
js> f(1)
2
js> f(2)
4
js> f(3)
7
js>

当然、「中に閉じ込められた」xは変更を受け付ける前提なんで、constantを使うよか(現状のEcmaScript仕様の推奨だと恐らく)letを使うべきケースになる。

いずれにせよ、貴方が挙げた例だと「いざと言う時に出てくるけど、常用を前提とされない」ケースの方になるんじゃないか。
そして「変数を生成する」のと「変数の値を変更する」のの、なんつーかな、体感的な意味の違いがまだ良く分かってないとは思う。
ただしそうは言っても、「仕様の許す使い方」で、色々と実験してみる事は良い事だと思いますよ。むしろ、色々と「思いついた」事は実行してみるべきだ。
じゃないと何が理で何がそうじゃないのか、ってのはなかなか分かってこないからね。

ちなみに、

> 昔は必ず事前宣言だったのが、何らかの必要性が生じて都度宣言が解禁されたというところでしょうか。

これは昔のコンパイラの都合。
昔のプログラミング言語だと「変数宣言部」ってのがファイルの冒頭にあるべき、ってルールがあった・・・と言うより、ALGOLって名前のプログラミング言語がそういう設計になってたの。

ALGOL:
https://ja.wikipedia.org/wiki/ALGOL

んでALGOL影響下のプログラミング言語がそういう設計を採用したのが多かったのね。
コンパイラがファイルをコンパイルする際に、一回ファイルを走査しただけで実行ファイルが作れるように「厳密にどこに何を置く」ってルールにした方が都合が良かったから、です。
ただ、今のモダンなプログラミング言語のコンパイラだと「ファイルを複数回走査して」実行ファイルを作る事がフツーになっちゃったんで、別にファイル冒頭で変数宣言せんでもエエや、ってのが多くなったのね(例えばC言語とか)。

> 都度宣言の利点をほんの少し調べてみましたが、進んで利用しようと思えるほどの情報は見つけられませんでした。

いや、これは単純に「変数を使う直前で宣言してた」方が分かりやすいでしょ。それだけ。
一々「あの変数名何だったっけ?」とかファイル冒頭に戻って確認、とかやってらんねぇじゃん(笑)。
「可読性」って意味では「新たに出て来た変数は使う直前に宣言してた」方が読みやすいです。
少なくとも、モダンな言語環境に慣れてるとそういう発想になります。
    • good
    • 0
この回答へのお礼

本件は、
何らかの計算で計算式を書く時にそのまま書くと煩わしい為、エイリアスとして変数が必要な場合。あるいは、「再計算」をさせたくない場合
に該当しますので、関数の中で変数を宣言する必要性があるという前提で、先の質問(2択)に回答していただけると嬉しいです。
つまり、条件によって使わない変数がある場合には、
その条件によってその変数を宣言(初期化)したり、しなかったりするか。
または、条件によっては使うのだから、無条件に宣言するか。

と、ここまで書いて思ったのですが、
>初期化によるデータ量や処理コストが大量ならば話は別ですが、
>大した量でないならば単純な記述にしたほうが個人的には好みです。
という回答がしっくりきました。
つまり状況によってはどちらの選択肢も有り得ると思うので、答えにくい質問をしてしまったと思いました。
今回のケースではどちらの方法でも一瞬で処理が終わるので、どちらでも良いのですが、よりコードが単純で、より速そうな後者にしようかなと思いました。

お礼日時:2023/03/17 02:35

> 先生はどちらの方法を取りますか。



先生じゃないんだけど(笑)、僕なら「どちらの方法も取らない」。
なぜなら貴方はこう書いている。

> 例えばa、b、cの3つのチェックボックスを操作するたびに呼び出される関数sub()

つまり、a、b、cは全部「チェックボックスとして存在し続けてる」って事でしょ。
こういう場合はa、b、c全部とも大域変数として定義するのが正解だと思う。
要するに「関数subの中には仕込まない」。

ガベージコレクタ、って分かる?「使わなくなったメモリ」を解放するシステムの事なんだけど。
つまり、関数subの中に仕込む、って事は、subの使用が終わった時に、その内部のスコープにとらわれている変数(つまりメモリだ)は解放されるわけ。
この「メモリを解放するシステム」、ガベージコレクタの「仕事」自体にコストがかかるわけよ。
要は、チェックボックスみたいに「ずーっとそこにある」のが前提のブツをsub内に仕込むと、スコープが解除される度にガベージコレクタが走り回るわけね。不必要にガベージコレクタが走る、って事はコストがかかる、つまり「実行速度が遅い」プログラムになっちまうんだ。

こういうケースは「変数をその関数内で生成する必要があるかどうか」考えないとならないわけだ。ずーっと固定で存在し続けるモノに対して、「関数内で生成する」ってのはあまり良い手段じゃあないね。
つまり、こういうケースが「書けて実行出来る」ので可、なのか、あるいは「効率性」を考慮すべきなのか、って判断になって、通常は後者を優先するんじゃないかな。
    • good
    • 0
この回答へのお礼

スミマセン、例えが悪かったです。
先の例で関数内で使うx、y、zにセットする値が、関数を呼び出した時の状況で毎回変わる物だとしたら、どうでしょうか。

このような場合には先生はどちらの方法を取りますか。
※他の呼び方が思いつかないので先生と呼ばせていただく事にしています。もちろん敬意の表れでもあります。

お礼日時:2023/03/16 03:53

> そもそもこのような書き方は一般的ではないのでは?という新たな疑問が発生した



う〜んとね。
極論、「仕様が許してるのなら一般的じゃなくても関係ない」ってのが答えかな。
重要なのはそこに「理」があるかどうか。
もちろん、会社でJavaScript使います、なんて場合はそこで行われている「コーディング規約」に従わなければならない事がある。
でも、自分でやる分には、「そうしなければならない/そうした方がいい」時には「やった方がいい」。一般的なコーディングを意識する、なんてのはあまり意味がないんだ。
特にJavaScriptみたいな「自由で強力なプログラミング言語」だと、一般的コーディングが必ずしも「最適解」ではなく、単に「冗長だ」ってのが良く起こると思う。

例えばね。#2で回答してるfujillinさん、とか、ここでのJavaScriptの質問に対する回答例をたくさん挙げてるんだけど、ぶっちゃけ「え?」って言うようなカッコいい回答を山ほどやってます。それは「一般的かどうか」って言うと多分一般的じゃあない。凄いCoolな解挙げてて、短いコーディングにたくさん「キレイな技」を詰め込んでる。おそらく「フツーにJavaScript書いてます」とか言う人だと理解出来ないようなカッコいい解ばっか書いてる。
つまり、一般的、ってのが「カッコいい」と必ずしも等価じゃないわけね。
どうせやるなら、fujillinさんみたいな「JavaScriptの仕様をキチンと読んで」一般的じゃなくても「カッコいい」書き方を学ぶべきじゃないかな、と思う。
一般的、ってのは目指す先としてはかなり凡庸なんですよ。そしてそれは「JavaScriptを使いこなす」とは真逆の方向なわけ。

と言うわけで、このお題でも「条件分岐で宣言したりしなかったりする」必要性があるなら、仕様が許してるならやっていいんじゃないのかな。
    • good
    • 0
この回答へのお礼

ありがとうございます。

なるほど必要性があるかという観点で考えてみました。

本件の場合どっちでも動くので必要性は無いと思いました。
つまり、条件によって宣言する変数や定数を変更するのと、
使う可能性のある変数を全て列挙するのと、どちらでも動きます。

ですから、どちらの書き方にしようか迷っています。

そこで両者の書き方にはどのようなメリット(デメリット)の違いがありますか。
また、先生はどちらの方法を取りますか。

例えばa、b、cの3つのチェックボックスを操作するたびに呼び出される関数sub()について、同じ結果を返す次の2通りの書き方では、どのようなメリットの違いがありますか。

function sub() {
if(a) var x = 3
if(b) var y = 4
if(c) var z = 5
return (a ? x : 0) + (b ? y : 0) + (c ? z : 0)
}

function sub() {
const x = 3, y = 4, z = 5
return (a ? x : 0) + (b ? y : 0) + (c ? z : 0)
}

お礼日時:2023/03/16 00:41

> このような使い方は想定されていないのでしょうか


> function sub() {
> if(1) {const x = 5}
> console.log(x)
> }

これはxが定数かどうかは関係ないんじゃないの?
#1氏が言ってる通り、単にifが形成するスコープから抜けるとxが参照出来なくなる、って話に見えるんだけど・・・・・・。

例えば、

function sub() {
 if (true) { let x = 5;}
 console.log(x);
}

と定数じゃなくってローカル変数としてxを宣言してもxは参照不能となる。
これはJavaScriptみたいな「ローカルスコープをキチンと実装してる言語」だと割に当たり前の動作で、xが定数かどうかは関係ない。スコープを抜けたらその設定は「消える」んだ。
    • good
    • 0
この回答へのお礼

質問が分かりにくくてスミマセン。

疑問が発生した時に扱っていたのがたまたま定数だったので、定数と記載してしまいましたが、当方変数に対しても同様の疑問を抱いていますので、質問文中の「定数」の所を「定数または変数」と読み替えていただくと幸いです。

既にvarを使えば、条件によって変数を宣言したり、しなかったりできる事が分かり、最初の疑問は解消できたのですが、そもそもこのような書き方は一般的ではないのでは?という新たな疑問が発生したので、回答者の皆様に新たな解説を求めている所であります。

つまり、いちいち条件によって変数を宣言したり、しなかったりするのではなく、使わない可能性のある変数も、とりあえず全て初期化しておくのが一般的な書き方なのかという、新しい疑問に回答いただきたく存じます。

お礼日時:2023/03/15 20:34

こんにちは



意図がイマイチわかりませんね。

>分岐によって使わない定数は宣言しないようにしようとしています。
その通りになっていませんか?
「使わない定数」を参照しようとするから、エラーになっているのでは?
参照する必要があるのなら、それは「使う定数」ということだと思いますけれど・・?
(「使わない定数」を使わなければエラーは発生しません )

>console.log(x)
「使わない定数」を参照した際の値として、何を期待しているのでしょうか?
undefinedなどを期待するのなら、No1様の回答にある通りでしょう。
(ただし、使っているので「使わない定数」とはなりませんけれど)
    • good
    • 0
この回答へのお礼

質問が分かりにくくてスミマセン。

例えば、次のように定数を使うなら宣言し、使わないなら宣言しないというのが、やりたかった事です。
function sub() {
if(x) {const a = 5}
console.log(x ? a : 9)
}

しかし私がやろうとしてるこのような書き方は普通はしないのでしょうか。
つまり、条件によっては使わない可能性のある変数も、とりあえず全て初期化しておくのが一般的なのでしょうか。

お礼日時:2023/03/15 15:02

const 宣言はブロックスコープを持つので if ブロック外から見えません。



代案1. var 宣言に変える
巻き上げにより、if ブロック内宣言でも関数スコープで有効になる
参考)
https://developer.mozilla.org/ja/docs/Glossary/H …

代案2. 初期化内容を分岐
定数の宣言自体は必ず行い、値を分岐させることで「使わない」を表現
const x = (...)? 5: undefined;
    • good
    • 1
この回答へのお礼

慣例というか最初に教わった書き方なので、いつも関数は最後にまとめて定義しているのですが、この書き方で動くのは巻き上げという機能なのですね。

var宣言で普通に関数スコープで使えるのでしたね。
var宣言は使っちゃいけない風習が漂っていたせいか、いつの間にかもっと広いスコープになるイメージになっていました。

しかし私がやろうとしてるこのような書き方は普通はしないのでしょうか。
つまり、条件によっては使わない可能性のある変数も、とりあえず全て初期化しておくのが一般的なのでしょうか。

お礼日時:2023/03/15 14:41

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!