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

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

ただいまの
回答率

87.80%

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

解決済

回答 6

投稿

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

score 710

変数宣言の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をつけたら関数ローカルになり、明らかに違いが出ますので比較の対象外です。
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 6

+6

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

【追記】
MDN:varによると、
違いは、宣言した変数はグローバルオブジェクトの設定変更不可プロパティになります、宣言していない変数は設定変更可能です。
とありますので、差異は質問で既に述べられている事ぐらいしか無いようです。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/08/20 23:05

    もともとの意図をなんとなく汲み取ると、「varありは変数、varなしはプロパティとする」としたかったのかなと感じます。

    しかしグローバルに限ると、変数がプロパティのようにもアクセスできるようになっているため、変数とプロパティの差が(ほぼ)無くなってしまう。だから存在意義がないから将来削除しましょうということなんでしょうかね。

    ありがとうございました

    キャンセル

checkベストアンサー

+3

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/25 22:04

    なるほど。ES3からES5への変更があったのですね。ChromeはES3のまま、IE, FirefoxはES5対応済みということですね。仕様の点についてはほぼ把握できたと思います。ありがとうございました。

    私の最初の質問に戻りますと、書籍「JavaScriptパターン」中で、『varをつけるかつけないかで「グローバル変数」と「グローバルオブジェクトのプロパティ」と違いが出ますよ』という内容が書かれていたが、どう違うのか分からなかったので質問をしました。(質問文が悪く、十分に伝わっていなかったかもしれませんが)

    この点については、確かに「グローバル変数」と「グローバルオブジェクトのプロパティ」と違いはあるものの、実質的に差はないも同じ、そこを意識して使い分けをすることなどまずない、ということでしょうかね。

    自分の中ではほぼそうなんだろうと、思っていますが。その点についてもし何かありましたら、お手数ですが再度コメントよろしくお願いします。

    キャンセル

  • 2015/08/25 22:48

    仰る通り、「グローバル変数」と「グローバルオブジェクトのプロパティ」は等価です。
    Webブラウザで使う場合はグローバルオブジェクトは window となるわけですが、「グローバルコードで var hoge = 'hogehoge';」と「window.hoge = 'hogehoge';」は [[Configurable]] の違いが出る実装を除けば、同一の動作となります。
    あえていうとすれば、プロパティであるからこそ、Object.defineProperty 等の使えるメソッドがあることでしょうか。

    他に注意するとすれば、「グローバルオブジェクトの名前」です。
    グローバルオブジェクトの名前は ES 仕様で策定されていない為、実装によって異なります。
    例えば、Webブラウザでは window であり、Node.js では global になります。
    この差異を吸収する(コードの移植性を重視する)為、グローバルオブジェクトの名前に依存しないコードを書く方法があります。
    グローバルコードで this を参照する、Function#call でグローバルオブジェクトを渡す、Function('return this'); を実行する等。

    キャンセル

  • 2015/08/26 01:33

    >「グローバル変数」と「グローバルオブジェクトのプロパティ」は等価です。

    等価ですか!私は「等価」とまでは思っていなかったのですが…。でもまぁそうですね、違うものだけれども、実質的に違いは無いということで

    「グローバル変数」===「グローバルオブジェクトのプロパティ」 // false
    「グローバル変数」==「グローバルオブジェクトのプロパティ」 // true

    というように表現できそうですね。

    ありがとうございました

    キャンセル

  • 2015/08/26 07:52

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

    キャンセル

+1

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

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

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

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

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

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/08/20 22:56

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

    キャンセル

0

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


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

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

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/08/25 04:21 編集

    んー。あまり詳しくないのですが確か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(グローバル)オブジェクトのプロパティとして定義されたことになるわけです。

    と、いうことでもちろん専門家でないのでよくわかりませんが、一般的な大多数のプログラマの中での解釈は「グローバル変数としてある変数を定義することは、グローバルオブジェクトのプロパティとしてそれを宣言することと同値である」となっています。

    キャンセル

  • 2015/08/25 10:36 編集

    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を開示した方が良いと思います。

    キャンセル

  • 2015/08/25 17:21 編集

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

    キャンセル

0

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

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

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/08/23 03:27

    ありがとうございました

    キャンセル

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 < a1*2 ; ii++){
       g1 += ii* 2 ;
    }
  return g1 ;
}

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

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

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/08/25 21:34

    質問の仕方が悪かったのかもしれませんね。すみません。
    一応、質問文にも明記しており繰り返しになりますが、関数内の話はわかりきったことで対象外です。

    『「グローバル変数」と「グローバルオブジェクトのプロパティ」の違いや使い分けを教えてください』とでも書いた方がよかったかもしれません。

    もしこの点について何かご存知であればよろしくお願いします。

    キャンセル

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

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

関連した質問

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