我对JS世界比较陌生。
我习惯用QT编写UI(构建UI的好工具!)。使用QT我正在为每个元素做一个类:如果我有一个带有一些元素的表,我有一个每个元素的类和一个表的类(也许也用于行)。每个类都包含数据和操作其“DOM”的方法。
现在在html中我正在使用一些在div上包含空骨架的ejs文件,而我正在编写一个用于通过jquery操作它的类。
例如:
userslist.ejs:
<script type="text/javascript">
function UsersList(path) {
this.path = path;
$('#userList > tbody').empty();
this.loadAjax();
}
UsersList.prototype.loadAjax = function() {
var that = this;
$.getJSON(this.path, function(users) {
that.fillUsers(users);
});
};
UsersList.prototype.fillUsers = function(users) {
var that = this;
users.forEach(function(user){
var tr = that.buildTr(user);
$('#userList > tbody:last').append(tr);
});
};
UsersList.prototype.buildTr = function(user) {
...
};
</script>
<h1>Users list</h1>
<table id="userList">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Control</th>
</tr>
</thead>
<tbody></tbody>
</table>
使用JavaScript编码的回调方式(以及一般的jquery dom操作)我很容易丢失this
关键字来引用类方法和类属性(即使我在嵌套回调中获得了一些控制权)。而且我发现自己真的滥用var that = this;
的东西......我几乎在课堂上的每个方法中都有它。
我该如何避免这种情况?我喜欢OOP,但我很想探索这个新的时尚js,具有一些功能特性和无处不在的asynch。
在JS中处理这个问题的正确方法是什么?每次使用回调时,如何避免在每种方法中编写var that = this;
?
答案 0 :(得分:2)
您可以使用.bind
来保留上下文。较新版本的JavaScript(ES6)使用新箭头表示法解决此问题。此外,一些函数接受上下文参数。
以下是ES5解决方案:
UsersList.prototype.fillUsers = function(users) {
users.forEach(function(user){
var tr = this.buildTr(user);
$('#userList > tbody:last').append(tr);
}, this); // note the context parameter
};
这是ES6解决方案:
UsersList.prototype.fillUsers = function(users) {
users.forEach((user) => { // arrow notation
var tr = this.buildTr(user);
$('#userList > tbody:last').append(tr);
});
};
当然,ES3的方式是使用普通for循环而不是forEach。
至于$.getJSON
jQuery的$.ajax
也接受上下文参数:
$.ajax({
url: this.path,
context: this
}).done(function(users) {
this.fillUsers(users);
});
或者,使用ES6箭头:
$.getJSON(this.path).done((users) => this.fillUsers(users));
正如您可能已经发现的那样,“经典”oop在这样的JavaScript中看起来并不太好,您可能希望分发代码与传统语言不同的地方。
答案 1 :(得分:2)
当您在javascript(events,ajax)中使用异步编程时,上下文通常是事件的上下文,因此您将丢失对象的上下文。
var that = this
模式,您将更频繁地将其视为var self = this
或var $this = this
允许在方法中添加闭包以重新建立上下文。
这是另一种方法,它将在调用它时强制上下文在object方法中:
UsersList.prototype.loadAjax = function() {
var that = this;
$.getJSON(this.path, this.fillUsers.bind(this));
};
答案 2 :(得分:1)
您可以创建一个将上下文作为参数传递的装饰器(ala Python),以便您可以在嵌套范围内访问它。而且,您可以return this
进行链接:
function fluent(f) {
return function() {
f.apply(this, [this].concat([].slice.call(arguments)))
return this
}
}
function Ary(xs) {
this.xs = xs
}
Ary.prototype.method = fluent(function(self, a, b) {
self.xs.forEach(function(x) {
console.log(self.xs, x + a + b)
})
})
var a = new Ary([1,2,3])
a.method(1, 2)
//[1, 2, 3] 4
//[1, 2, 3] 5
//[1, 2, 3] 6
答案 3 :(得分:1)
其他答案的另一种选择是使用jQuery&#39; .proxy
。一个例子:
UsersList.prototype.loadAjax = function() {
$.getJSON(this.path, $.proxy(function(users) {
//jQuery.proxy ensures that 'this' referring to the object you intend to refer to, i.e. the object passed in to the second argument of .proxy
this.fillUsers(users);
}), this); //Note we pass 'this' to .proxy
};
我认为这个类似的问题有助于说明:$(this) inside of AJAX success not working。
答案 4 :(得分:1)
老实说,我不会试图避免这种做法。也许您可以使用.bind(...)
,.call(...)
或.apply(...)
,或者过度设计代码以摆脱编码var that = this;
。
但是,毕竟var that = this
的效果优于其他解决方案,因为您使用 clousures 并且只需访问this
就没有额外的开销表示当前对象的引用。
最佳论点是假设 JavaScript以这种方式运作,我非常确定这将是您项目中不那么重要的问题。
你说:
在JS中处理这个问题的正确方法是什么?我怎么能避免 写var = this;每次使用回调时,每种方法都有用吗?
答案是你以正确的方式做事。无论如何,我会查看bind
,call
和apply
函数,因为在某些情况下,它们的工作效果优于var that = this;
。