今天在开发1050的时候,遇到了这么一个有意思的问题:
我有类SongModel
和SongView
:
var SongModel = Backbone.Model.extend({
...
});
var SongView = Backbone.View.extend({
...
render: function() {
// render to Dom
},
events: {
'click xxx': 'delete'
},
delete: function(e) {
var tThis = this;
this.model.destroy({
wait: true,
$btn: $(e.currentTarget),
success: function() {
tThis.remove();
}
});
}
});
然后:
var songModel = new SongModel(someObj);
var songView = new SongView({model: songModel});
页面进来的时候,执行代码,然后songView
正常的显示在页面上。
当我点击删除
的时候,songView
的delete
事件被触发,然后发送一个Http Delete
异步请求,后台执行删除,成功以后执行tThis.remove();
。
有意思的是,我为了统一代码的交互,用到了一个utils.js
来执行一些同样的工作,如,每次发起异步请求before
的时候,显示加载中...
的进度条。
具体的实现方式,是通过jQuery
的ajaxSetup
以及ajaxSend
, ajaxComplete
, ajaxSuccess
, ajaxError
来实现的:
var utils = {
setGlobalAjaxSettings: function() {
var tThis = this;
...
$(document).ajaxSuccess(function(event, jqxhr, settings) {
if(jqxhr.responseJSON&&!jqxhr.responseJSON.error) {
tThis.success(event, jqxhr, settings);
} else {
tThis.error(event, jqxhr, settings);
}
});
},
success: function(event, jqxhr, settings) {
if(settings.$btn) {
var $alert = $(this.getAlertHtml('alert-success', '操作成功'));
var $target = settings.$btn.closest('.row').find('*[tag="alert"]');
this.renderAlert($target, $alert);
}
}
}
所以,当我发起的Http Delete
请求成功之后,会先执行songView
里面的success
回调的代码,然后执行utils
里面通用的success
回调的代码。
然后,有意思的事情就来了,我们注意到,前面发请求的时候,传入了一个$btn: $(e.currentTarget)
,当回调成功以后,会执行tThis.remove();
;
这时,按我之前错误的理解,在通用的回调里面,settings.$btn
应该是undefined
,而当我单步调试的时候发现,他竟然还是当前点击按钮的那个jQuery
元素!
而且,它能一直parent()
追溯到songView
的el
,再往上就是undefined
了。
我顿时就来劲了,仔细想了一下,原来是这样:
如图所示,当我们执行var songView = new SongView({model: songModel});
的时候,内存1被创建,它的内容存我们实例化的jQuery
对象;
同时,内存2被创建,它的内容存1的地址,如图红色线所示。
而当我们render
的时候,实际上是把2的地址存到DOM
的内容中,如图绿色线所示。
接着,让我们执行请求的时候,传入了一个$btn: $(e.currentTarget)
,我们知道,在JavaScript中,参数的传递,不管是基本类型还是引用类型,都是传值;
所以,这时,内存4被创建,它的内容也存1的地址,如图蓝色线所示。
然而,当请求成功之后,执行:tThis.remove();
,这时,内存2被销毁。红色线和绿色线同时也不存在了。
但此时,蓝色线还是存在的,所以,这就解释了我之前单步调试的时候遇到的现象。
那如果我们不执行tThis.remove();
呢?,这时,我们执行var $target = settings.$btn.closest('.row').find('*[tag="alert"]');
就能搜索到DOM
树中其它的父元素。
因为内存2的地址被存在DOM
树中,同时内存2的内容又指向内存1的地址,所以DOM
树中就会显示内存1的内容,当内存4不断执行parent()
的时候,实际上也是在DOM
树中往上查找,最终也会找到window
元素。
由此可见,DOM
, View
, e.target
之间真是纠缠不清,也难怪我会亲切的称呼它们为三贱客了。