3

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>

コメントされた最初の行は私には適切なようです(thisshowLabel関数でキーワードを使用)。代わりにobjを使用する方法がわかりません。objは、実行時にobjが定義されていないため、その関数内でグローバル変数を参照する必要があります。私は2つのオブジェクトを作成しているので、この場合は両方が正常に表示されるだけでなく、objのコンテンツの古い値が適切に保存および参照されます。しかし、どのように?2番目のコメントのコメントを外すと、コメントが壊れて理由がわかります。ローカル変数について話しているのに、ローカル変数がないことをjsに明示的に伝えています。

4

4 に答える 4

6

クロージャの世界へようこそ。あなたは、それがグローバルであるが完全にグローバルではないかのように振る舞うと感じるものを感じるのは正しいです。これがクロージャの動作です。

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

技術的にはメカニズムが異なり、一部の人々はそれをそのように説明しようとし、他の多くの人々を混乱させることになります。私にとって、クロージャは一種の「プライベート」グローバル変数であり、グローバルと同様に、関数間で共有されますが、グローバルスコープでは宣言されません。これは、この機能に遭遇したときの気持ちを説明するのと同じです。

これが私が読む価値があると私が信じるjavascriptクロージャに関連するstackoverflowに関する私の他の答えのいくつかです:

JavaScriptの隠された機能?

ループでのJavaScriptクロージャの使用について説明してください

または、「javascriptclosure」というフレーズをグーグルで検索して主題を調べることもできます。


追加の答え。

コードで機能する理由の説明についてはthis(* cough *がコードを修正しようとしてthis、修正されていないバージョンの* cough *で機能する場合でも機能するようにするのとは対照的です;-):

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場合でも機能する理由です。

于 2010-12-10T16:21:13.340 に答える
2

obj関数内で機能する理由showLabelは、objがローカル変数であるためです。関数は、createaddressが呼び出されるたびに宣言されます。JavaScriptでは、これをクロージャと呼びます。

一般的に言えば、このファクトリパターンよりもプロトタイプオブジェクトの作成が優先されます。

次に、このプロトタイプの例を見てください。

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);
}

新しいキーワードで新しいアドレスを作成すると、次のようになります。

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

コードは期待どおりに動作します。ただし、コンストラクター内でnewキーワードを使用しない場合、this実際にはwindowオブジェクトになります。

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

これで少し問題が解決することを願っています。

于 2010-12-10T15:56:56.347 に答える
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();
于 2010-12-10T16:04:26.317 に答える
0

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

期待どおりに「this」を使用するには、新しい演算子を使用する必要があります。

var foo = new createAddress(..。

そして、メンバー変数を'this'に割り当てます。

newが使用されていないこの場合、「this」はグローバルオブジェクトです。

于 2010-12-10T16:02:13.743 に答える