2010-12-10 14 views
3

javascriptでオブジェクトを作成するためのファクトリパターンを示すチュートリアルに従っています。次のコードは、なぜそれがうまく動作するかについて私は困惑しています。JavaScriptファクトリ変数の変数スコープ

<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>6-2.htm</title> 
</head> 
<body> 
<script type="text/javascript"> 
function createAddress(street, city, state, zip) { 
    var obj = new Object(); 
    obj.street = street; 
    obj.city = city; 
    obj.state = state; 
    obj.zip = zip; 
    obj.showLabel = function() { 
    //alert(this.street + "\n" + this.city + ", " + this.state + " " + this.zip); 
    //var obj; 
    alert(obj.street + "\n" + obj.city + ", " + obj.state + " " + obj.zip); 
    }; 
    return obj; 
}; 

var JohnAddr = createAddress("12 A St.", "Johnson City", "TN", 37614); 
var JoeAddr = createAddress("10061 Bristol Park", "Pensacola", "FL", 32503); 

JohnAddr.showLabel(); 
JoeAddr.showLabel(); 
</script> 
</body> 
</html> 

最初のコメント行は、(showLabel機能でthisキーワードを使用して)私には、適切と思われます。私はその場所でobjを使用することがどのように機能しているのかよくわかりません。 objはグローバル変数をどこかで参照する必要があります。なぜなら、その関数内で実行されるobjが定義されていないからです。私は2つのオブジェクトを作っているので、この場合は両方ともうまく表示されるだけでなく、objの内容の古い値が正しく格納され、参照されます。しかしどのように?私は2番目のコメントのコメントを外した場合、それは壊れ、私はなぜ今、私はローカル変数について話していることを明示的にjsに伝え、何もありません。

答えて

6

ようこそ。あなたがあることを感じるものを感知する権利ですあたかもグローバルではあるが、まったくグローバルではないように思える。それがクロージャがどのように動作するかです。

基本的に関数が返ってくると、すべてのローカル変数が必ずJavaやCのようにガベージコレクション/解放されます。その変数への参照がある場合、その変数は定義されている関数のスコープ内で存続します。

技術的には機構が異なり、そのように説明して、多くの人を混乱させる人もいます。私にとっては、クロージャーはグローバルのような「プライベート」グローバル変数の一種であり、関数間で共有されていますが、グローバルスコープでは宣言されていません。これは、この機能に遭遇したときの感覚を表現するようなものです。

は、ここで私は信じているjavascriptの閉鎖に関連したstackoverflowの上ここに私の他の回答のいくつかは読む価値ありです:

Hidden Features of JavaScript?

Please explain the use of JavaScript closures in loops

それともあなただけのフレーズをグーグルできる「JavaScriptのクロージャ」主題を探検する


その他の回答。対照的に、あなたのコードでは、なぜthis作品の説明については

(咳*それが訂正されていないバージョンで動作していてもthisがうまくいくようにコードを修正しようとする*咳* ;-) *に:

Javascriptにレイトバインディングがあります。非常に遅く、非常に遅い。 thisはコンパイル時にバインドされないだけでなく、実行時にバインドされません。実行時にバインドされます。つまり、関数が呼び出されるまで、実際に何が指しているのかわからないことがあります。呼び出し元は、基本的にthisが使用されている関数ではなく、thisの値が何であるかを決定します。

いくつかのファンキーなjavascriptの遅延バインディング演習:あなたのコードで

function foo() { 
    alert(this.bar); 
} 

var bar = "hello"; 
var obj = { 
    foo : foo, 
    bar : "hi" 
}; 
var second_obj = { 
    bar : "bye" 
}; 

foo(); // says hello, 'this' refers to the global object and this.bar 
     // refers to the global variable bar. 

obj.foo(); // says hi, 'this' refers to the first thing before the last dot 
      // ie, the object foo belongs to 

// now this is where it gets weird, an object can borrow/steal methods of 
// another object and have its 'this' re-bound to it 

obj.foo.call(second_obj); // says bye because call and apply allows 'this' 
          // to be re-bound to a foreign object. In this case 
          // this refers to second_obj 

thisは便利それはおそらくを使用して、あなたが明らかにないであっても働く理由はその方法としての機能を呼び出すオブジェクトを参照します正しいコンストラクター構文。

+0

WHOAこれはすべて私が信じるように配線してきたことには全く反対です(でも面白いですが)。私は本当にあなたの入力を感謝します!私が最も援助したと思うことは、括弧が(変数を渡す)場合、私はそれを期待どおりに振る舞いますが、私がそれらを見ないと、私はより詳しく見ていなければなりません。 – Ankur

2

objshowLabel関数内で動作する理由は、objがローカル変数であるためです。この関数は、create addressが呼び出されるたびに宣言されます。 JavaScriptでは、これをクロージャーと呼びます。

一般に、プロトタイプオブジェクトの作成は、このファクトリパターンよりも優先されます。あなたが期待するようなコードが正確に動作します

// create new address 
var address = new Address('1', '2', '3', '4'); 
address.showLabel(); // alert 

:私は新しいキーワードで新しいアドレスを作成するときに今

var Address = function(street, city, state, zip){ 
    this.street = street; 
    this.city = city; 
    this.state = state; 
    this.zip= zip; 
}; 

Address.prototype.showLabel = function(){ 
    alert(this.street + "\n" + this.city + ", " + this.state + " " + this.zip); 
} 

は今、このプロトタイプの例を見てみましょう。しかし、新しいキーワードthisをコンストラクタ内で使用しないと、実際にはwindowオブジェクトです。

// create new address 
var address = Address('1', '2', '3', '4'); // address == undefined 
window.showLabel(); // address was added to window 

これで少しクリアされることを願っています。

+0

プロトタイプのパターンはよりエレガントです。私は同意します。私は学術的POVから来ていたので、私はなぜ工場のパターンが働いたのかを知りたがっていました。ありがとう! – Ankur

0

これはjsの奇妙さの一部です。showLabelはクロージャであり、作成時にスコープ内にあるためobjにアクセスできます。つまり、createAddressが呼び出されるたびに新しいクロージャが作成されます。

するvar FOO =新しいcreateAddress(...

とメンバ変数を割り当てることが:

「これは」のように、あなたがそうnew演算子を使用する必要があるだろう期待している使用するには「これ」に。使用されていない新しいこの場合

「これは」グローバルオブジェクトである。クロージャの世界へ

+0

大丈夫、私はそれを受け入れることができますが、最初のコメント行を使用するのはなぜですか? (他の2行は意味しません)。 this.streetが実際にwindow.streetを割り当てる場合、showLabelへの2回目の呼び出しで別の値が返され、上書きによって古い値が失われないのはなぜですか? – Ankur

+0

私は次のように答えることで私の質問に答えることができます: 'this'は特別な変数で、クロージャにもかかわらず、関数がメンバであるオブジェクトを参照します。または私は完全にこれを得ていないのですか? – Ankur

+0

@Ankur:私の答えを見てください。要するにあなたが言っていることは正しいです。 – slebetman

1
function Address(street, city, state, zip) { 

    this.street = street; 
    this.city = city; 
    this.state = state; 
    this.zip = zip; 
    this.showLabel = function() { 
    //alert(this.street + "\n" + this.city + ", " + this.state + " " + this.zip); 
    //var obj; 
    alert(this.street + "\n" + this.city + ", " + this.state + " " + this.zip); 
    }; 
}; 

var JohnAddr = new Address(...); 
JohnAddr.showLabel(); 
+0

+1は範囲についての彼の質問に答えることはできませんが、これはすばらしい優雅な解決策です。 – Zevan