JavaScript関数名が衝突するのはなぜですか?


97

関数が割り当てられている変数と関数に名前が衝突したときに何が起こるかを確認するために、次のスクリプトを作成しました。

var f = function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

私が得ている出力は "Me original"です。他の関数が呼び出されなかったのはなぜですか?

また、元の割り当てをに変更するとvar f = new function() {、 "Me original"が表示され、その後にTypeErrorと表示されobject is not a functionます。誰か説明していただけますか?


26
@ Dean.DePue — JavaScriptの部分に混乱はありません。それらを処理するためのルールは非常に明確です(そして、Benjaminが彼の回答で説明しています)。
クエンティン2014年

4
好奇心、それでも言語について学ぶ最良の方法。:-D
セルブルス2014年

2
また、「JavaScript」のように重要でないものを混乱させたり(または、感情について
言えば

2
なぜ2番目の例では巻き上げによって順序が逆になるのでしょうか。
セルブルス2014年

5
JavaScriptの知識を深めるための手順:1)「use strict」を使用する2)常にjslintまたはjshintのいずれかを使用する3)jslintまたはjshintが不満を言うことを調べる4)すすぎ、繰り返す
steve-er-rino

回答:


170

関数宣言はJavaScriptで巻き上げられます(最上部に移動)。解析順序の点では正しくありませんが、関数宣言が巻き上げられるため、コードは意味的に次のコードと同じです。

function f() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

これは、関数名を除いて、次と同じです。

var f = function() {
    console.log("Me duplicate.");
}
var f = function() {
    console.log("Me original.");
}


f();

これは、巻き上げが可変であるため、次と同じです。

var f;
f = function() {
    console.log("Me duplicate.");
}
f = function() {
    console.log("Me original.");
}

f();

これは、何を取得しているかを説明するものであり、関数をオーバーライドしています。より一般的にvarは、JavaScript では複数の宣言が許可されます-var x = 3; var x = 5完全に合法です。新しいECMAScript 6標準では、letステートメントがこれを禁止しています。

@kangaxによるこの記事は、JavaScriptの関数をわかりやすく説明するのに非常に役立ちます。


2
あなたは本当に簡素化することができますfunction f()var f = function()そのくらいですか?巻き上げと関数名が本当に唯一の違いですか?
djechlin 2014年

6
この質問の文脈での@djechlin-はい。一般に、それはより微妙です-stackoverflow.com/questions/336859/…を参照してください。コンパイラの観点からは、それらは異なりますが、プログラマの観点からは、それを主張するのに十分近づいています。そのため、「解析順序の点では正しくありませんが、コードは「と同じ」ではなく、意味的には同じ」と長く追加しました。いい視点ね。
Benjamin Gruenbaum 2014年

1
@dotslash元の質問を編集して変更しないでください。これはマナーが悪いと見なされます。また、複数の質問を1つに混在させることもマナーが悪いと見なされます。代わりに新しい質問をすることもできますが、あまりにも軽微だと思われる場合は、コメントで説明を求めてください(とにかくそれを説明します)。上記のコードでは、両方のバージョンのfホイストがあり、"Me Original"バージョンは後でホイストされるだけで、それぞれが先頭に移動されますが、順序は同じです。私はそれを一般的に追加したいのですが、いくつかの関数に同じ方法で名前を付けないでください:)
Benjamin Gruenbaum

5
厳密モードvarでは、同じスコープ内で同じ名前を2回使用することはできません。
ホフマン2014年

4
「それは明白なはずです」- おそらくあなたには、しかし私にはある時点では明白ではなく、OPがそれを尋ねたときも明白ではなく、ネーミング、そしてより一般的には、字句環境がJavaScriptでどのように管理されるかが1つでした。最初にJavaScriptを学ぶときに把握するのが最も難しいことの1つです。私はそれを理解していない人々を侮辱するのはそれほど速くありません。
Benjamin Gruenbaum 2014年

10

フォローアップの質問に誰かが回答したように見えない場合は、ここで回答しますが、通常は別の質問としてフォローアップの質問をする必要があります。

あなたはなぜこれを尋ねました:

var f = new function() {
    console.log("Me original.");
}

function f() {
    console.log("Me duplicate.");
}

f();

「私のオリジナル」を出力します。そしてエラー。

ここで起こっているのは、newが関数をコンストラクターとして使用することです。したがって、これは次と同等です。

function myConstructor() {
    console.log("Me original.");
}
var f = new myConstructor();

function f() {
    console.log("Me duplicate.");
}

f();

そしてベンジャミンが説明した関数の巻き上げのおかげで、上記は基本的にこれと同等です:

var myConstructor = function() {
    console.log("Me original.");
};
var f = function() {
    console.log("Me duplicate.");
};

f = new myConstructor();

f();

この表現:

var f = new function() {
    console.log("Me original.");
}

f匿名関数をコンストラクタとして使用して、新しいオブジェクトが作成され、に割り当てられます。「私オリジナル。」コンストラクターの実行時に出力されます。しかし、構築されるオブジェクト自体は関数ではないため、これが最終的に実行されると、次のようになります。

f();

fは関数ではないため、エラーが発生します。


ああ、素晴らしい!わざわざ答えてくれてありがとう!:) :)
ankush981 2014年

2

これがポイントを追加するアプローチの間違った方法であるかどうか私を許してください。私はずっとここにいるわけではなく、建設的な方向性や批判を歓迎します。

ベンジャミンの答えはOPの質問にうまく対応していますが、巻上げとその奇妙さの完全なツアーを提供する1つの微調整を追加したいと思います。

元のコードをの呼び出しで開始するとf、次のようになります。

f();

var f = function() {
   console.log("Me original.");
};

function f() {
   console.log("Me duplicate.");
}

f();

出力は次のようになります。

Me duplicate.
Me original.

その理由は、そのことvarfunctionステートメントがわずかに異なる方法で持ち上げられているためです。

以下のために宣言 *現在のスコープの上部に移動し、任意れる割当var掲揚されていません。宣言された変数の値に関する限り、元の割り当て行に到達するまでは未定義です。

以下のためfunctionの文、宣言の両方定義が掲揚されています。関数、で使用されるvar f = function() {...構文では巻き上げられません。

したがって、巻き上げ後の実行は、コードが次のようになります。

var f; // declares var f, but does not assign it.

// name and define function f, shadowing the variable
function f() { 
  console.log("Me duplicate.");
}

// call the currently defined function f
f(); 

// assigns the result of a function expression to the var f,
// which shadows the hoisted function definition once past this point lexically
f = function() { 
  console.log("Me original."); 
}

// calls the function referenced by the var f
f();

*すべてのJavaScriptスコープは字句スコープ、または関数スコープですが、その時点でfワードを使用すると混乱するように見えました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.