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

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

新規登録して質問してみよう
ただいま回答率
85.37%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

6回答

32126閲覧

変数宣言のvarアリとナシの違いや使い分け

miu_ras

総合スコア902

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

7クリップ

投稿2015/08/19 20:37

変数宣言のvarアリとナシの違いや使い分けについて教えてください。

一応自分で分かっている範囲のことを書きますと、
varアリだとconfigurableがfalseになる。つまりdeleteできない(Chromeの場合。IEとFirefoxでは削除できる)。
varナシはtrueでdeleteできる。

var v1 = 1; v2 = 2; console.log(Object.keys(this).filter(function (v) {return /^v.$/.test(v)})); console.log(Object.getOwnPropertyDescriptor(this, "v1")["configurable"]); console.log(Object.getOwnPropertyDescriptor(this, "v2")["configurable"]); // Chromeでの結果。IE, Firefoxではどちらもtrue。 // ["v1", "v2"] // false // true

言い換えると

varアリは「グローバル変数」、
varナシは「グローバルオブジェクトのプロパティ」

であるということ(書籍「JavaScriptパターン」にそう書いてあった。でもそれ以上の説明はなく、本質的には違いが分かっていません)。

自分の分かる範囲では、ここだけです。もしかしたら他にも差があるかもしれませんが、ここだけだとしても、違いがあるということは意味があって差をもうけていて、何かしらの使い分けをするケースがあるのではないかと思うのですが…

だとすると「あとでdeleteしたいときにvarをつけない」とするということでしょうか?
その他、シンボルの解決に影響するとかあるのでしょうか?

質問
・上記以外に差はありますか?
・どのような使い分けをするものなのでしょうか?

よろしくお願いします。

※言うまでもないことだと思いますが、関数内でvarをつけたら関数ローカルになり、明らかに違いが出ますので比較の対象外です。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

細かい差異までは分かりませんが、最近の傾向として、
"use strict";を用いて厳格(strict)モードで書くのが推奨されているようで、
厳格モードではvarナシはエラーとなり、そもそも使用できません。
JavaScriptとしては、varナシは互換性のために残している古い書き方で、
基本使ってはいけない扱いのようです。
厳格モードにしてvarナシは使用しない、がいいのではないでしょうか?

【追記】
MDN:varによると、

違いは、宣言した変数はグローバルオブジェクトの設定変更不可プロパティになります、宣言していない変数は設定変更可能です。

とありますので、差異は質問で既に述べられている事ぐらいしか無いようです。

投稿2015/08/19 21:47

編集2015/08/19 21:53
raccy

総合スコア21737

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miu_ras

2015/08/20 14:05

もともとの意図をなんとなく汲み取ると、「varありは変数、varなしはプロパティとする」としたかったのかなと感じます。 しかしグローバルに限ると、変数がプロパティのようにもアクセスできるようになっているため、変数とプロパティの差が(ほぼ)無くなってしまう。だから存在意義がないから将来削除しましょうということなんでしょうかね。 ありがとうございました
guest

0

ベストアンサー

ES3 の VariableStatement には DontDelete 属性という内部属性があり、var 付きで宣言した変数は delete 演算子で削除できませんでした。
(ES3 の仕様に関しては JavaScript の変数と delete 演算子: Days on the Moon が詳しい)
ES5.1 の VariableStatement ではこの規定は削除され、delete 演算子で削除できるようになりました。
(私の読む範囲ではそうですが、間違いがあったらご指摘下さい)

varアリだとconfigurableがfalseになる。つまりdeleteできない(Chromeの場合。IEとFirefoxでは削除できる)。

IE, Firefox の挙動が ES5.1 に準拠していると思われます。

・上記以外に差はありますか?

var なしで変数宣言すると Strict Mode で ReferenceError を返します。

・どのような使い分けをするものなのでしょうか?

私なら configurable の値が何でも良いなら var を使い、configurable の値を指定するなら Object.defineProperty を使用します。
実装の挙動差異もさることながら、configurable の値を操作するなら Object.defineProperty を使うのが順当だと思います。

投稿2015/08/23 22:56

think49

総合スコア18189

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miu_ras

2015/08/25 13:04

なるほど。ES3からES5への変更があったのですね。ChromeはES3のまま、IE, FirefoxはES5対応済みということですね。仕様の点についてはほぼ把握できたと思います。ありがとうございました。 私の最初の質問に戻りますと、書籍「JavaScriptパターン」中で、『varをつけるかつけないかで「グローバル変数」と「グローバルオブジェクトのプロパティ」と違いが出ますよ』という内容が書かれていたが、どう違うのか分からなかったので質問をしました。(質問文が悪く、十分に伝わっていなかったかもしれませんが) この点については、確かに「グローバル変数」と「グローバルオブジェクトのプロパティ」と違いはあるものの、実質的に差はないも同じ、そこを意識して使い分けをすることなどまずない、ということでしょうかね。 自分の中ではほぼそうなんだろうと、思っていますが。その点についてもし何かありましたら、お手数ですが再度コメントよろしくお願いします。
think49

2015/08/25 13:48

仰る通り、「グローバル変数」と「グローバルオブジェクトのプロパティ」は等価です。 Webブラウザで使う場合はグローバルオブジェクトは window となるわけですが、「グローバルコードで var hoge = 'hogehoge';」と「window.hoge = 'hogehoge';」は [[Configurable]] の違いが出る実装を除けば、同一の動作となります。 あえていうとすれば、プロパティであるからこそ、Object.defineProperty 等の使えるメソッドがあることでしょうか。 他に注意するとすれば、「グローバルオブジェクトの名前」です。 グローバルオブジェクトの名前は ES 仕様で策定されていない為、実装によって異なります。 例えば、Webブラウザでは window であり、Node.js では global になります。 この差異を吸収する(コードの移植性を重視する)為、グローバルオブジェクトの名前に依存しないコードを書く方法があります。 グローバルコードで this を参照する、Function#call でグローバルオブジェクトを渡す、Function('return this'); を実行する等。
miu_ras

2015/08/25 16:33

>「グローバル変数」と「グローバルオブジェクトのプロパティ」は等価です。 等価ですか!私は「等価」とまでは思っていなかったのですが…。でもまぁそうですね、違うものだけれども、実質的に違いは無いということで 「グローバル変数」===「グローバルオブジェクトのプロパティ」 // false 「グローバル変数」==「グローバルオブジェクトのプロパティ」 // true というように表現できそうですね。 ありがとうございました
think49

2015/08/25 22:52

なかなか説明が難しいですね。 実体は同じという意味でWebブラウザにおける window === window.window を意図していました。
guest

0

今EcmaScriptの仕様書をさらっと見てきましたがその「違い」についての言明は見つけられませんでした。言い換えればそもそも「varのない場合」についての言明がみられません。多分。要するにvarがないのはおそらく(EcmaScriptにおける)変数宣言文としてはただの文法違反です。要するに、本来実行されるべきでない「意味を持たないー意味論上解釈不能な」文です。
更に換言すればその「違い」は完全に処理系に委託されており、その違いを以ってコーディングするなどはまともなコーダーのすべきことではなかろうと思われます。

と断言してしまいましたが私は英語が苦手なのでそこまでしっかり数百ページに及ぶ仕様書を読んだわけではありません。ただ、おそらくEcma仕様外です。

んで、多分var無しのほうがconfigure==trueとなってるのはそもそもecma5以前はconfigurableという属性がそもそも存在しなかったからです。私自身ecma3でjsを勉強しましたからこのconfigurableという属性を知りませんでした。これまでは書き込みの可否、列挙の可否、再定義の可否の3種類です。
ちなみにconfigurableの規定値はfalseであり、すなわちvarなしの方もそうあるべきなのでしょうが、おそらくecma前に無茶して書かれたコードを生かそうとした結果だと思われます。
私はおいラリーの本でJSを勉強しました。厚くてお勧めです。座布団にもなります。

投稿2015/08/21 16:38

henpon

総合スコア40

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

raccy

2015/08/22 06:34

ECMAScript 2015の仕様書を読み直したところ、 http://www.ecma-international.org/ecma-262/6.0/#sec-strict-mode-of-ecmascript の4番目に厳密モードで宣言されていない変数名に代入したとき話が書いてありました。実際の処理は、 http://www.ecma-international.org/ecma-262/6.0/#sec-putvalue にある5番目の処理の通りで、厳密モードで無いときは、globalObjにその変数名でセットするとなっているようです。宣言の構文ではなく、"a = 1;"のような代入文で、aが事前に宣言されていない時の例外的な処理扱いが真相のようです。
miu_ras

2015/08/22 18:40

raccyさん、有用情報ありがとうございます。 > 宣言の構文ではなく、"a = 1;"のような代入文で、 > aが事前に宣言されていない時の例外的な処理扱いが真相のようです。 ただ、ここは「例外的」と判断してしまうのは主観的過ぎるように思います。単に「仕様で定められている」「仕様どおりの動き」と事実だけを受け止めるべきだと思います。
miu_ras

2015/08/22 18:41

henponさんも、ありがとうございました。
henpon

2015/08/23 19:30

私が読んだ1998年のそれに比べて随分現代っぽく複雑な感じになってますね。まあここまで広く使われてるわけですから当然っちゃ当然なのかもしれないですが。 やっぱり英語が苦手なので指摘の該当部分はよくわかんなかったですがともかくも「Variable Statement」には注釈なく以下のようにありますのでやっぱり「プログラミング言語」としての仕様範囲においてはvarが絶対なんですよ。きっと。 もちろんこの言語がここまで広く使われていて、まあvarがないというのは現代的な超高級言語に慣れていればやりがちですから結果的にあくまで「動作上の」仕様、というか統一的な最低限の指針として、定められているんでしょう。たぶん。 13.3.2 Variable Statement NOTE A var statement declares variables that are scoped to the running execution context’s VariableEnvironment. Var variables are created when their containing Lexical Environment is instantiated and are initialized to undefined when created. Within the scope of any VariableEnvironment a common BindingIdentifier may appear in more than one VariableDeclaration but those declarations collective define only one variable. A variable defined by a VariableDeclaration with an Initializer is assigned the value of its Initializer’s AssignmentExpression when the VariableDeclaration is executed, not when the variable is created. Syntax(※シンタックス。「シンタックス」と書いてあるんです。 VariableStatement[Yield] : var VariableDeclarationList[In, ?Yield] ; (※[var]じゃないですよvarですよ。つまりシンタックスとしては不可欠なんです。うん。 VariableDeclarationList[In, Yield] : VariableDeclaration[?In, ?Yield] VariableDeclarationList[?In, ?Yield] , VariableDeclaration[?In, ?Yield] VariableDeclaration[In, Yield] : BindingIdentifier[?Yield] Initializer[?In, ?Yield]opt BindingPattern[?Yield] Initializer[?In, ?Yield]
miu_ras

2015/08/24 15:53

henponさん確認させてください。オライリー書籍「JavaScriptパターン」に # varアリは「グローバル変数」、 # varナシは「グローバルオブジェクトのプロパティ」になる と書いてあるのですが、これは前提としていただいていますか? それともこの書籍が間違っているということをおっしゃろうとしているのですか?
henpon

2015/08/24 20:47 編集

んー。あまり詳しくないのですが確かJSにおける「変数スコープー名前空間」というのはそもそも「スコープ(オブジェクト)チェイン」という概念によってなっているんですね。んでこれを言い換えれば一般に「ある変数スコープ階層ー名前空間において変数を定義すること」は、「そのスコープ範囲ー名前空間を表現する「オブジェクト」の「そのプロパティ」としてその変数を定義すること」と同定される。という事になるんです。 要するに、例えばトップレベルで function Outer_Funciton(){ function Inner_Function(){;}; } などと定義するとこれは 1,window.Outer_Function=function(){function Inner_Function(){;};}; 2,Outer_Function実行時:新たな名前空間Xを表現する特殊なオブジェクトXについて X.Inner_Function=function(){;}; と定義することと全く同定されるわけです。 (「クロージャー」はご存知でしょうが、この仕組みによってそうなっています。つまり、window.Outer_Funcion呼び出し時に当然新たな「(ローカル)スコープ」が生じますが、これは換言すればそのスコープを表現する「オブジェクト」が新たに生成され(因みに同時にスコープチェーンの先頭に追加されます)、これへのアクセスが不可能にならない限りはこれはメモリ上に保存されます。したがって、オブジェクトもとい「スコープ」は保存され、これを以ってクロージャが成っています。また、例えばOuter_Funcion中で名前解決の必要性が生じた時、すなわちまず上述のオブジェクトXを参照しますが、これにて解決ができなかった時には、そのスコープチェーンの「1つ前」のオブジェクトを参照するわけです。上述の例では例えばInner_Function中で変数vを解決する場合まずInnner_Function呼び出し時に生成される特殊なオブジェクトYを参照し、そこで解決できなければスコープチェーン中の次のオブジェクト、すなわちここではオブジェクトXを参照し、そこでも解決できなければ最後、スコープチェーンの末端に常にある「グローバルオブジェクト」を参照します。 この「Y->X->グローバルオブジェクト」という名前解決の流れを見ればそれが「スコープ(を表現するオブジェクト)」の「チェイン」をなしていることがよくわかると思います。) つまり、私の昔読んだ本による記憶では「JSにおける名前空間とその解決」は「オブジェクトそのもの」によって実現されているんだーという事になるわけです。 上のコードを見ていただいてわかる通り、トップレベルで変数を定義すれば(つまり「グローバル」変数として変数を定義すれば)、それは特殊な、常にスコープチェーンの先頭にある「グローバル・オブジェクト」の「プロパティ」としてそれを定義することと事実上同じなわけです。var aとやれば当然window.aでそれを参照できます。ドット演算子はプロパティ参照式をなしますから、要するにaはwindow(グローバル)オブジェクトのプロパティとして定義されたことになるわけです。 と、いうことでもちろん専門家でないのでよくわかりませんが、一般的な大多数のプログラマの中での解釈は「グローバル変数としてある変数を定義することは、グローバルオブジェクトのプロパティとしてそれを宣言することと同値である」となっています。
think49

2015/08/25 01:37 編集

To: henponさん > んで、多分var無しのほうがconfigure==trueとなってるのはそもそもecma5以前はconfigurableという属性がそもそも存在しなかったからです。 ES3 には [[Configurable]] は存在しませんが、DontDelete 属性が存在します(私の回答でも触れています)。 http://www2u.biglobe.ne.jp/~oz-07ams/2002/ecma262r3/12_Statements.html#section-12.2 > JSにおける「変数スコープー名前空間」というのはそもそも「スコープ(オブジェクト)チェイン」という概念によってなっているんですね。 scope-chain は変数を実体化した後の処理になるので、グローバル変数がグローバルオブジェクトのプロパティになる仕組みには関連しないと思いますが、いかがでしょうか。 ES3 の "12.2 変数文 (Variable statement)" では次のように説明されています。 --- FunctionDeclaration 内部で変数文が発生するならば、その変数は、セクション 10.1.3 にあるように、その関数内の関数ローカルスコープで定義される。そうでないならば、それらはプロパティ属性 { DontDelete } のグローバルスコープで定義される(つまり、セクション10.1.3 にあるグローバルオブジェクトのメンバとして作成される)。 http://www2u.biglobe.ne.jp/~oz-07ams/2002/ecma262r3/12_Statements.html#section-12.2 --- 仕様に言及するのであれば関連記述を引用するか、URLを開示した方が良いと思います。
henpon

2015/08/25 11:40 編集

おっと。詳しい方がありましたか。私の理解は随分昔に読んだ本でのごく概念的なものですので、これ以上はお任せ致します。
guest

0

使い分けというより、やはりvar付きで使うようにするほうが良いです。
スコープの違いがあることは理解されているようなので、それが理由なんですが、たとえば

  • グローバルに変数hogeがある
  • 関数fの中だけで使う変数hogeを用意する

という場合に、var宣言しないと、関数fが実行されたときにグローバル変数を変更してしまい、予期しない状況を引き起こします。

実際には単にグローバルというだけでなく、var無しだとスコープの外側の変数を参照/変更してしまいます。

さらに理解を深めるために、変数のホスティング(巻き上げ)やスコープ、クロージャーなんかを調べてみると良いと思います。

投稿2015/08/20 00:49

makoto

総合スコア36

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miu_ras

2015/08/20 13:56

繰り返しになりますが、関数内の話は対象外です。 ありがとうございました
guest

0

var 付きは局所変数。var 無しは大域変数ですが。
関数の内部で 外部と同じ変数を他の変数として用いる場合にvar をつけます
例えば
g1=20 ;
for( ii=0 ; ii<g1 ; ii++ ){
b1 = func1( ii ) ;
}

function f1( a1 ){
var g1 = 0 ;
for( var ii=a1 ; ii < a12 ; ii++){
g1 += ii
2 ;
}
return g1 ;
}

と、しておくと、関数の内部で関数の外部と同じ名前の変数を他の変数として使えるわけです。

それと、関数の内部の変数を外部に出したくない場合に用います

投稿2015/08/25 04:45

yoshidatakeshi0

総合スコア30

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miu_ras

2015/08/25 12:34

質問の仕方が悪かったのかもしれませんね。すみません。 一応、質問文にも明記しており繰り返しになりますが、関数内の話はわかりきったことで対象外です。 『「グローバル変数」と「グローバルオブジェクトのプロパティ」の違いや使い分けを教えてください』とでも書いた方がよかったかもしれません。 もしこの点について何かご存知であればよろしくお願いします。
guest

0

可読性、保守性の観点から考えて変数宣言であるvar無しで使用することはまずないと考えます。
仮にあるとすればコードゴルフのような1文字でも削るという目標がある場合でしょう。

後から削除したいグローバル変数であればwindow.userNameというような形でグローバルオブジェクトのプロパティとして宣言し、deleteを用いてそれを削除するという方法が考えられます。

投稿2015/08/21 19:09

Cf_cwd

総合スコア730

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miu_ras

2015/08/22 18:27

ありがとうございました
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問