当您编写复杂的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)
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
碰撞?如果没有,你最喜欢的技术是什么?
标记(#9)
THIS: undefined | DOG: jQuery(p#dog)
由于上述原因, ... $this
未定义,但是:
THIS: jQuery(div#foo) | DOG: jQuery(p#dog)
... $this
现在是$('#foo')
!
究竟为什么会发生这种情况?
是因为$this
是通过标记(#10)重新定义的吗?
(嗯,我觉得我需要谷歌“javascript中的垃圾收集”。)
同样,在编写复杂的jquery / javascript时,避免这种类型的变量冲突的最佳方法是什么?
我希望这些不是可怕的问题。提前感谢您抽出宝贵时间帮助我。 :)
答案 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;
之类的操作,这会在嵌套多个范围时给您带来麻烦。
使用代理,您可以将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)