6

背景:クロージャーコンパイラーが詳細オプションで警告を生成しないように、ライブラリー(私は作成しませんでした)を書き直したいと思います。この質問ごとに、JavaScriptの「this」キーワードとクロージャコンパイラの警告に対する答えは、クロージャを使用してコードを書き直すことでした。目的は、キーワードthis(コンパイラー警告を生成する)の使用を避けることです。

ライブラリには多くの関数があるので、新しいクロージャがオブジェクトリテラルを返すのが最善だと思います。これがどのように機能し、考えられる影響について理解したいと思います。したがって、私は次の(意味のない)例を学習の練習として書きました(ここでも:jsFiddle):

  var CurrencyObject = function(Amount) {
        var money = Amount;
        return {
              "toCents": function() {
                    money *= 100;
                    return money;
              },
              "toDollars": function() {
                    money /= 100;
                    return money;
              },
              "currentValue": money  // currentValue is always value of Amount
        };
  }; // end currencyObject

  var c1 = CurrencyObject(1.99); // what's the difference if the syntax includes `new`?

  alert('cents = ' + c1.toCents() + '\ncurrent money = ' + c1.currentValue + '\ndollars = ' + c1.toDollars() + '\ncurrent money = ' + c1.currentValue);

  var c2 = CurrencyObject(2.99);

  alert('cents = ' + c2.toCents() + '\ncurrent money = ' + c2.currentValue + '\ndollars = ' + c2.toDollars() + '\ncurrent money = ' + c2.currentValue);

  alert('cents = ' + c1.toCents() + '\ncurrent money = ' + c1.currentValue + '\ndollars = ' + c1.makeDollars() + '\ncurrent money = ' + c1.currentValue);

Q1:toCentsの呼び出し後にcurrentValueが更新されないなぜですか?( currentValueは、CurrencyObjectが最初に実行されたときに初期化されるリテラル(?)であるためだと推測します。その場合、プロパティcurrentValueも返すための構文は何ですか?)

Q2:この構文(とnewvar c1 = new CurrencyObject(1.99);は、私が検出できる方法でコードの動作を変更しませんが、違いがあると思います。それは何ですか?

Q3c2がインスタンス化されるとき、作成されている関数のコピーですか、それともc1c2は同じ(関数)コードを共有しますか?(関数のコピーが作成されている場合、それを回避するためにどのような変更を加えますか?)

TIA

ところで:誰かが疑問に思っている場合は、Closure-Compilerが名前を変更しないように、オブジェクトリテラルの記号が引用符で囲まれています。

4

2 に答える 2

3

Q1:の別々のコピーを含む2つの変数を作成しましたAmount。1つはmoney、クロージャーでキャプチャされた変数にあります。もう1つのコピーは、currentValue返されたオブジェクトのプロパティにあります。currentValueこれらは、の初期値が変数の値で初期化されることを除いて、相互に接続されていない別個の変数moneyです。

この問題を回避するには、保管する場所を1つだけにcurrentValueして、そこで参照する必要があります。moneyクロージャーで使用できるのはローカル変数のみですが、そのためには、プロパティを直接データプロパティではなくcurrentValue、ゲッター関数(値を取得する)に変更する必要があります。money

または、moneyクロージャ変数を削除して、currentValueプロパティのみを使用することもできます。そのためには、 andメソッドthisでそのプロパティを取得するためにを使用する必要があります。私の意見では、これを行うためのよりクリーンな方法は後者です(オブジェクト自体のプロパティを参照するために使用します)。なぜ閉鎖があなたにそうさせたくないのか私には分かりません。toCentstoDollarsthis

Q2:コンストラクターからオブジェクトを明示的に返す場合、コンストラクターが関数として直接呼び出されるか、new演算子を使用して呼び出されるかは問題ではなくなりました。ご覧のとおり、どちらも同じ結果になります。

Q3:関数は匿名であるため、新しいインスタンス化ごとに新しい匿名関数オブジェクトが作成されます。それがどれほど効率的か非効率的かは、javascriptの実装によって異なります。関数をローカルの名前付き関数として宣言し、その名前を参照することで、関数を1つだけにすることができます。次に、新しい関数が作成されていないことを保証できます。

于 2012-10-29T21:45:16.057 に答える
1

更新:
はあなたのフィドルを分岐し、すべてのインスタンスが実際に共有するいくつかのメソッドを追加しました(つまり、インスタンスごとに新しい関数オブジェクトは作成されません):たとえば、金額をユーロまたはポンドに変換するメソッド。money変数は必要ないので、省略しました。可能な限り使用する必要thisがないように、返されたオブジェクトをクロージャスコープ内の変数に割り当てました。これにより、すべてのメソッドがに依存するのではなく、その変数名を介して親オブジェクトを参照できるようになりますthis
ただし、共有メソッドは「より高い」スコープでthis宣言されており、returnObject変数、単にスコープに存在しないため。変数宣言を解除してreturnObjectも、疑問に思った場合の解決策にはなりません。通貨オブジェクトのインスタンスを複数作成できないことがすぐにわかるからです。最後に、 「コンストラクター」
の名前を小文字から始めるように変更しました。技術的には、関数はもはやコンストラクターではないため、大文字で始めることはできません...ここで説明したこと、または提案された変更のいずれかがまだ不明な場合はお知らせください。

次のようにして変数の値currentValueを変更したため、は更新されません。このステートメントは値を乗算し、それを同じ変数に割り当てます。これはプリミティブであり、この値の独自のコピーを持っているため、リンクされていません。提案:値を一定に保つために使用します。現在のところ、メソッドを2回呼び出すことは、元の金額に10,000を掛けることと同じです。呼び出しごとにonを更新/設定するには、次を追加します。これは少し危険です。または、名前付き参照を使用してクロージャーに独自のリテラルへのアクセスを許可します(より安全ですが、もう少し冗長です)。 :moneymoney *= 100;moneycurrentValue
return money/100;moneytoCentscurrentValuethis.currentValue = money*100;

var someClosure = function (amount)
{
    var money = amount;//redundant: you can use amount, it's preserved in this scope, too
    var me = {currentValue:amount,
              toCents: function()
              {
                  me.currentValue = money*100;//<-- use me to refer to the object itself
                  return amount/100;
              }
      };
      return me;
}

newキーワードを使用する理由はありません。この「コンストラクター」によって作成されるオブジェクトはオブジェクトリテラルであり、プロトタイプは1つだけです(Object.prototype、明示的なコンストラクター名はありません)。追加newするとthis、関数自体の新しいオブジェクトを指すようになりますが、それを使用しておらず、関数が別のオブジェクトを返すため、オブジェクトが返されることはありません。

厳密に言えば、一部のJSエンジンは、新しいインスタンスごとに新しい関数オブジェクトを作成します。最近のオブジェクトの中にはこれを最適化するものがあり、実際には1つの関数オブジェクトしか作成しません。安全のために、コードを実行するエンジンに関係なく、関数が1つしかないことを確認するために、別のクロージャーをラップすることができます。

var someObjectGenerator = (function()
{
    var oneOffFunction = function()
    {
        console.log('this function will only be created once');
    }
    return function(amount)
    {
        return {    foo:oneOffFunction,
                    toCents: function()
                    {
                        return amoutn/100;
                    }
                };
    };
};

もちろん、moneyまたはamount変数を持つスコープより上のスコープで作成された関数はその変数にアクセスできないため、その場合は新しい関数を作成する必要があります...しかしJSオブジェクトは非常に安価なので、そんなに心配しないでください
繰り返しますが、ほとんどのエンジンはこれをかなりうまく処理します。

于 2012-10-29T21:51:22.190 に答える