jQuery和“这个”管理?如何避免变量碰撞?

时间:2013-02-14 21:26:42

标签: javascript jquery

当您编写复杂的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');

});

A full demo page can be found here (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(即使 IS 已定义)。

避免$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')

问题(S):

究竟为什么会发生这种情况?

是因为$this是通过标记(#10)重新定义的吗?

(嗯,我觉得我需要谷歌“javascript中的垃圾收集”。)

同样,在编写复杂的jquery / javascript时,避免这种类型的变量冲突的最佳方法是什么?


我希望这些不是可怕的问题。提前感谢您抽出宝贵时间帮助我。 :)

5 个答案:

答案 0 :(得分: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将变量和函数声明提升到作用域块的顶部,以便在手动声明它们之前引用它们。这会让人们在范围块的 outside 中引用同名的vars时会遇到麻烦,正如您在示例中可以清楚看到的那样。

答案 1 :(得分:4)

您刚刚遇到了范围和JavaScript的乐趣。很多人喜欢做的一件事是,如果你需要在回调中访问当前作用域,请将其定义为self,如下所示:

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

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

请注意,当调用'connect'时,“this”的范围将重置为回调的范围而不是click-handler,这就是您将范围存储在可访问变量中的原因。

答案 2 :(得分:3)

在内部范围内重新声明var $this时,您看到的行为称为hoisting。这就是为什么你应该在每个范围的开头都有一个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);
        });
    });
}

答案 3 :(得分:2)

其他人已经指出了你在例子中遇到的悬挂问题。我仍然认为值得一提的是jQuery的代理功能。

它超级方便!您不必执行var self = this;之类的操作,这会在嵌套多个范围时给您带来麻烦。

jQuery proxy documentation

使用代理,您可以将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 proxy usage

如果你真的需要在一个函数中并排使用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
  });
});

答案 4 :(得分:1)

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

this.on('change', function() {
 //this is now the scope outside the callback function.
}, this)