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重新定义了吗?

(嗯,我觉得我需要谷歌“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 的乐趣。很多人喜欢做的一件事是,如果您需要在回调中访问当前范围,请将其定义为 self,如下所示:

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

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

请注意,当调用“连接”时,“this”的范围会重置为回调的范围,而不是点击处理程序的范围,这就是您将范围存储在可访问变量中的原因。

于 2013-02-14T21:30:38.560 回答
3

var $this您在内部范围内重新声明时看到的行为称为提升。这就是为什么你应该var在每个范围的开头为你在该范围内需要的任何变量始终使用一个语句的原因之一——并不是说它是必要的,它只是更清楚,因为它做同样的事情并且变量名更容易定位。

至于必须在深度嵌套的范围内使用变量..这可能表明出现问题(嵌套过多、单一职责等)。如果您需要大量使用某些构造,请考虑将它们添加到对象的范围内,而不是给它们以 . 结尾的名称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));
});

此处的工作示例:jsFiddle jQuery 代理使用

如果您确实需要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

如上所说。一种流行的方法是将变量 self 分配给 'this' 另一种方法是将您希望函数所在的范围作为最后一个参数发送:

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