16

複雑なjQuery/javascriptを作成している場合、以前に定義された変数をthis再定義せずに、どのように使用を管理しますか?this変数に名前を付けるための経験則または個人的な好みはありますthisか(ネストが深くなるにつれて)?

より高いスコープの変数をネストされた関数/コールバックで使用できるようにしたい場合もありますが、クリーンなスレート/スコープが必要な場合もあります。変数の衝突を心配せずに関数/コールバックを呼び出す良い方法はありますか?もしそうなら、あなたはどのようなテクニックを使っていますか?


いくつかの非常にばかげたテストコード:

$(document).ready(function() {

    console.warn('start');

    var $this = $(this),

    $dog = $('#dog'),

    billy = function() {

        console.log('BILLY!', 'THIS:', $this, ' | ', 'DOG:', $dog);

        var $this = $(this);

        console.log('BILLY!', 'THIS:', $this, ' | ', 'DOG:', $dog);

    };

             // (#1)
    billy(); // BILLY! THIS: undefined | DOG: jQuery(p#dog)
             // BILLY! THIS: jQuery(Window /demos/this/) | DOG: jQuery(p#dog)

    console.log('THIS:', $this, ' | ', 'DOG:', $dog); // THIS: jQuery(Document /demos/this/) | DOG: jQuery(p#dog)

             // (#2)
    billy(); // BILLY! THIS: undefined | DOG: jQuery(p#dog)
             // BILLY! THIS:  jQuery(Window /demos/this/) | DOG: jQuery(p#dog)

    $('#foo').slideUp(function() {

                                                          // (#3)
        console.log('THIS:', $this, ' | ', 'DOG:', $dog); // BILLY! THIS: undefined | DOG: jQuery(p#dog)

        var $this = $(this); // (#10)

                                                          // (#4)
        console.log('THIS:', $this, ' | ', 'DOG:', $dog); // BILLY! THIS: jQuery(Window /demos/this/) | DOG: jQuery(p#dog)

    });

    $('#clickme').click(function() {

                                                          // (#5)
        console.log('THIS:', $this, ' | ', 'DOG:', $dog); // THIS: undefined | DOG: jQuery(p#dog)

        var $this = $(this);

                                                          // (#6)
        console.log('THIS:', $this, ' | ', 'DOG:', $dog); // THIS: jQuery(button#clickme) | DOG: jQuery(p#dog)

        $('#what').animate({
            opacity : 0.25,
            left    : '+=50',
            height  : 'toggle'
        }, 500, function() {

                                                              // (#7)
            console.log('THIS:', $this, ' | ', 'DOG:', $dog); // THIS: undefined | DOG: jQuery(p#dog)

            var $this = $(this);

                                                              // (#8)
            console.log('THIS:', $this, ' | ', 'DOG:', $dog); // THIS: jQuery(div#what) | DOG: jQuery(p#dog)

        });

    });

             // (#9)
    billy(); // THIS: undefined | DOG: jQuery(p#dog)
             // THIS: jQuery(div#foo) | DOG: jQuery(p#dog)

    console.warn('finish');

});

完全なデモページはここ(jsbin.com)にあります。

注:ご覧のとおり、簡単に参照できるように、コメントに番号(#XX)を付けています。


観察1:

マーカー(#1)

BILLY! THIS: undefined | DOG: jQuery(p#dog)

修辞的な質問:なぜ$this未定義なのに$dogアクセスできるのですか?

回答:varその範囲内で再定義されているため$this; $thisそのスコープ内で定義される前にログに記録しようとしているだけです。

コメントアウトするvar $this = $(this);と、マーカー(#1)は次のように戻ります。

BILLY! THIS: jQuery(Document index2.html) | DOG: jQuery(p#dog)
BILLY! THIS: jQuery(Document index2.html) | DOG: jQuery(p#dog)

同じロジックがマーカー(#2)、(#3)、(#4)、(#5)、(#6)、(#7)、および(#8)にも適用されます。

この観察に基づいて(そして私がここで間違っている場合は私を訂正してください)私はvar $this = $(this);関数の一番下に置くことができると仮定しています、そして現在のスコープは私が現在のスコープを使いたいと知っているでしょう$this(そうではありませんがまだ定義されています)、親のものではありません(定義$thisされている場合でも)。

競合を回避するための可能な解決策$this

他のクロージャ/関数/コールバックの外部/内部をキャッシュして$(this)衝突を回避したい場合は、次のような異なる変数ラベルを使用する必要があります(たとえば)。

var $$ = $(this);
var $this2 = $(this);
var $t = $(this);
var $that = $(this);

質問:

$this上記の解決策は、衝突を回避する方法ですか?そうでない場合、あなたが好むテクニックは何ですか?


観察2:

マーカー(#9)

THIS: undefined | DOG: jQuery(p#dog)

...$this上記の理由で定義されていませんが、:

THIS: jQuery(div#foo) | DOG: jQuery(p#dog)

...$this$('#foo')です!

質問:

正確になぜこれが起こったのですか?

マーカー(#10)$thisで再定義されたからですか?

(うーん、Googleの「javascriptのガベージコレクション」が必要な気がします。)

繰り返しますが、複雑なjquery / javascriptを作成する場合、このタイプの変数の衝突を回避するための最良の方法は何ですか?


これらが恐ろしい質問ではないことを願っています。お手数をおかけしますが、よろしくお願いいたします。:)

4

5 に答える 5

8

あなたは実際に可変巻き上げの問題に直面しています:

billy = function() {

    console.log('BILLY!', 'THIS:', $this, ' | ', 'DOG:', $dog);

    var $this = $(this);

    console.log('BILLY!', 'THIS:', $this, ' | ', 'DOG:', $dog);

};

実際には、実行時に次のように解釈されます。

billy = function() {

    var $this;

    console.log('BILLY!', 'THIS:', $this, ' | ', 'DOG:', $dog); // $this hasn't been set to anything yet...

    $this = $(this);

    console.log('BILLY!', 'THIS:', $this, ' | ', 'DOG:', $dog);

};

JavaScriptは、変数と関数の宣言をスコープブロックの先頭に引き上げるので、手動で宣言する前にそれらを参照できます。これは、例ではっきりとわかるように、スコープブロックの外側から同じ名前の変数を参照するときに問題を引き起こします。

于 2013-02-14T21:34:36.823 に答える
4

スコープとJavaScriptの楽しさに出会ったばかりです。多くの人がやりたいことの1つは、コールバックで現在のスコープにアクセスする必要がある場合、次のようにこれを自分自身として定義することです。

var $ = require('jquery')
  , database = require('database')

$.on('click', function(action) {
  var self = this;
  database.connect(function() {
    database.putSync(self.action.button);
  });
});

'connect'が呼び出されると、 "this"のスコープは、クリックハンドラーではなく、コールバックのスコープにリセットされることに注意してください。これが、スコープをアクセス可能な変数に格納した理由です。

于 2013-02-14T21:30:38.560 に答える
3

var $this内部スコープで再宣言するときに表示される動作は、巻き上げと呼ばれます。varこれが、各スコープの先頭に、そのスコープで必要な変数に対して常に1つのステートメントを含める必要がある理由の1つです。必要なわけではなく、同じことを実行し、変数名が簡単であるため、より明確になります。見つける。

深くネストされたスコープで変数を使用する必要がある場合、それは何かが間違っていることを示している可能性があります(ネストが多すぎる、単一責任など)。特定の構成を頻繁に使用する必要がある場合は、で終わる名前を付けるのではなく、オブジェクトのスコープに追加することを検討してください2。少なくとも、わかりやすい名前を付ける必要があります(私は通常$this = $(this)、単一のスコープ内でのみ使用します。これは、$()繰り返し呼び出す必要がないようにするためです)。 self多くの場合、独自のスコープ内の関数定義内で現在のオブジェクトを参照するために使用されます。

var Dog = function (boy, dog) {
    this.boy = boy;
    this.dog = dog;
    this.$clickme = $("#clickme");
}

Dog.prototype.bind = function () {
    var self = this;

    self.$clickme.on('click', function() {
        var $this = $(this);
        console.log('THIS:', $this, ' | ', 'DOG:', self.dog);

        $('#what').animate({
            opacity : 0.25,
            left    : '+=50',
            height  : 'toggle'
        }, 500, function() {
            var $this = $(this);
            console.log('CLICKME:', self.$clickme, ' | ', 'DOG:', self.dog);
        });
    });
}
于 2013-02-14T21:37:47.437 に答える
2

他の人は、あなたがあなたの例で経験した巻き上げの問題をすでに指摘しました。それでも、jQueryのプロキシ機能について言及する価値があると思います。

とても重宝します!のようなことをする必要はありませんvar self = this;。これは、複数のスコープをネストするときに問題を引き起こします。

jQueryプロキシドキュメント

プロキシを使用すると、関数に委任できthisます。これは、オブジェクト指向のJavaScriptをjQueryと組み合わせて使用​​している場合に非常に便利です。

$(document).ready(function () { 
  var $dog = $('#dog');
  // without proxy
  $('.billy').click(function () {
      // $(this) refers to the node that has been clicked 
      $(this).css('color', 'green');
  });

  // with proxy
  $('.billy').click($.proxy(function () {
     // $(this) refers to $dog
     $(this).css('backgroundColor', 'red');
  }, $dog));
});

ここでの作業例:jsFiddlejQueryプロキシの使用法

関数内で参照を並べて表示する必要がある場合は、this参照をすべて配列にロードしておくことをお勧めします。これにより、参照に名前を付ける必要がなくなり、ネストレベルのスコアも保持されます。

$(document).ready(function () { 
  var thisStack = [];
  thisStack.push($('#dog'));

  $('.billy').click(function () {
    thisStack.push($this);
    // thisStack[0] refers to $('#dog')
    // thisStack[1] refers to $(this)

    ... more code ...

    thisStack.pop(); // get rid of $(this), leaving scope
  });
});
于 2013-02-18T09:57:35.933 に答える
1

上記のように。一般的な方法の1つは、変数selfを'this'に割り当てることです。別の方法は、関数を含めるスコープを最後のパラメーターとして送信することです。

this.on('change', function() {
 //this is now the scope outside the callback function.
}, this)
于 2013-02-14T21:38:26.207 に答える