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

mhu*_*lse 16 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');

});
Run Code Online (Sandbox Code Playgroud)

可在此处找到完整的演示页面(jsbin.com).

注意:正如您所看到的,我已经用数字(#XX)"标记"了注释以便于参考.


观察1:

标记(#1)

BILLY! THIS: undefined | DOG: jQuery(p#dog)
Run Code Online (Sandbox Code Playgroud)

修辞问题:为什么$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)
Run Code Online (Sandbox Code Playgroud)

相同的逻辑适用于标记(#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);
Run Code Online (Sandbox Code Playgroud)

题:

上面的解决方案是如何避免$this碰撞的吗?如果没有,你最喜欢的技术是什么?


观察2:

标记(#9)

THIS: undefined | DOG: jQuery(p#dog)
Run Code Online (Sandbox Code Playgroud)

... $this由于上述原因未定义,但是:

THIS: jQuery(div#foo) | DOG: jQuery(p#dog)
Run Code Online (Sandbox Code Playgroud)

...... $this现在$('#foo')!

问题(S):

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

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

(嗯,我觉得我需要谷歌"javascript中的垃圾收集".)

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


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

Ali*_*guy 8

您实际上遇到了变量提升问题:

billy = function() {

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

    var $this = $(this);

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

};
Run Code Online (Sandbox Code Playgroud)

实际上在运行时解释如下:

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);

};
Run Code Online (Sandbox Code Playgroud)

JavaScript将变量和函数声明提升到作用域块的顶部,以便在手动声明它们之前引用它们.这会让人们在从作用域块外部引用相同名称的变量时遇到麻烦,正如您在示例中可以清楚地看到的那样.

  • 吊装.哇,我很高兴我在这个上寻求帮助......我从来不知道JS是如何处理事情的.惊人.非常感谢您的回复和代码示例以及澄清.+1 (2认同)

Exp*_*lls 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);
        });
    });
}
Run Code Online (Sandbox Code Playgroud)