4

次のことがわかりません。

var x = function() {
    this.foo="foo";
    return function() {
        this.bar = "bar";
        return foo+bar;
    };
}(); // returns inner

alert(x()); // 'foobar', so both 'this' variables are set
alert(x.bar); // undefined - but wasn't it used correctly?
alert(new x().bar); // ok, works

私の仮定では、デフォルトの「this」スコープ/変数マップが最初に生成されて使用され、次に「new」が呼び出されると、新しい「this」を持つ新しいオブジェクト (関数?) が送信されて返されます。それとも、x は適切なオブジェクトではないのでしょうか? しかし、では、'this' はどのように設定され、'foobar' を作成するために使用されるのでしょうか?

これを理解するには何を知る必要がありますか?

4

4 に答える 4

9

最初に、JavaScript のいくつかの細かい点を見てみましょう。それから、あなたの例を扱うことができます。

関数のコンテキスト

誤解の 1 つのポイントはコンテキストです。すべての関数は、キーワードを使用して利用できるコンテキストで呼び出されますthis。コンテキストを検査するために使用できる関数を書きましょう。

var probe = function(){
  // if the context doesn't have a name, let's name it
  if(!this.name){
    this.name = "lumberjack";
  }
  // print the name of my context
  console.log(this.name);
};

どうぞ:

name = "global!";

// when we call a function normally it still have a context:
// the global context
probe(); // prints: global!

var ctx = {name: "ctx"};

// we can set a context explicitly using call()
probe.call(ctx); // prints: ctx

// we can set a context explicitly using apply()
probe.apply(ctx); // prints: ctx

// it is set implicitly, if we call a function as a member
ctx.fun = probe;
ctx.fun(); // prints: ctx

// or we can create a brand new object and set it as a context:
// that's what "new" does
var t = new probe(); // prints: lumberjack

// let's sum it up:
console.log(name);     // prints: global!
console.log(ctx.name); // prints: ctx
console.log(t.name);   // prints: lumberjack

そのため、混乱してうっかりグローバル コンテキストに陥ってしまうのは非常に簡単です。

コンストラクタで値を返す

コンストラクターが値を返すのを見ると、多くの人が混乱します。合法です。コンストラクターは、オブジェクト、関数、または配列を返すことができます。この値はインスタンスとして使用されます。古いインスタンスは破棄されます。

var myClass = function(){
  // if it is called as a constructor, "this" will be a new instance
  // let's fill it up:
  this.a = 42;
  this.b = "Ford";
  this.c = function(){ return "Perfect"; };
  // done? let's discard it completely!
  // and now for something completely different...
  return {
    owner: "Monty Python",
    establishment: "Flying Circus"
  };
};
var t = new myClass();
alert(t.owner + "'s " + t.establishment);

さすが「モンティ・パイソンズ・フライング・サーカス」。

コンストラクターが何か他のもの (数値、文字列、null、未定義な​​ど) を返す場合、返された結果は破棄され、古いインスタンスが使用されます。

あなたの例は、主にそれが書かれた方法のために理解するのが難しいです. 簡単に書き直してみましょう。

まず、次のものを扱いましょうx

var x = function() {
  this.foo = "foo";
  return function() {
    this.bar = "bar";
    return foo + bar;
  };
}(); // returns inner

ご覧のとおり、無名関数 ( 1番目 function) はすぐに実行されるため、インライン化できます。

// next assignment can be simplified because
// top "this" is window or the global scope
//this.foo = "foo"; =>
foo = "foo";
x = function() {
  this.bar = "bar"; // this line depends on its context, or "this"
  return foo + bar; // this line uses global "foo" and "bar"
};

最後に、(foo文字列) とx(関数) の 2 つのグローバル変数があります。

それでは、最初のアラートを見てみましょう。

alert(x()); // 'foobar', so both 'this' variables are set

繰り返しますが、インライン化しましょうx():

// next assignment can be simplified because
// top "this" is window or the global scope
//this.bar = "bar"; =>
bar = "bar";
// at this moment both global "foo" and "bar" are set
alert(foo + bar); // => "foo" + "bar" => "foobar"

2番目のアラートも同様に単純です。

alert(x.bar); // undefined - but wasn't it used correctly?

あまり書き直す必要はありません。xは関数であり、プロパティを追加していないためx.bar未定義です。追加すると、結果が表示されます。

x.bar = "bar2";
alert(x.bar); // bar2

3番目のアラートは、JavaScript の OOP の動作を示しています。

alert(new x().bar); // ok, works

(補足: 最初に実行した場合にのみ機能します。それ以外の場合は未定義x()であるために爆発します)。bar

次のように書き直してみましょう。

var t = new x();
alert(t.bar); // bar

それでは、コンストラクターを分析しましょう。これには、割り当てと戻りの 2 つのステートメントがあります。後者は文字列を返すため無視されます。したがって、次のように書き直すことができます。

x = function(){
  this.bar = "bar";
};
var t = new x();
alert(t.bar); // bar

すべてが簡単に見えることを願っています。

于 2009-08-16T09:16:55.710 に答える
4

それが新しいオペレーターの主な不満です...

その演算子は、オペランド コンストラクター関数のプロトタイプから継承する新しいオブジェクトを作成し、関数を呼び出して、新しいオブジェクトを に割り当てますthis

コンストラクター関数を呼び出すときに new 演算子を使用するのを忘れると、代わりに通常の関数呼び出しが行われ、新しいオブジェクトではなくthisグローバル オブジェクト ( ) にバインドされます。window

this関数は、独自のインスタンスを初期化しようとするたびにグローバル変数を追加します。

あなたの例では、グローバルオブジェクトは2つの新しい変数で終わります:

window.foo
window.bar

そのため、疑似古典的なアプローチではなく、プロトタイプの継承を好む人もいます。

于 2009-08-16T07:18:34.370 に答える
3

Shog9 が言ったように、これは Java などで見られるような通常のスコーピングと同じではありません。

私の知る限り、Javascript は、Common LISP/eLisp のように動的スコープを使用しますが、Scheme/Lisp-1 のようなレキシカル スコープは使用しません。

于 2009-08-16T07:08:46.890 に答える
3
于 2009-08-16T07:02:31.913 に答える