JS和OOP:滥用“that = this”模式
JS and OOP: abuse of `that = this` pattern
我是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;
的东西…我在类的几乎每个方法中都有它。
我怎样才能避免这种情况?我喜欢面向对象,但我很好奇探索这个新的流行的js,一些功能特性和异步无处不在。
在JS中处理这个问题的正确方法是什么?我怎么能避免写var that = this;
在每个方法每次我使用回调?
当你在javascript (events, ajax)中使用异步编程时,上下文通常是事件的上下文,所以你失去了对象的上下文。
var that = this
模式,你甚至更经常读为var self = this
或var $this = this
,允许在方法中添加闭包,以便重新建立上下文。
这里有另一个方法,它将在调用对象方法时强制上下文为this:
UsersList.prototype.loadAjax = function() {
var that = this;
$.getJSON(this.path, this.fillUsers.bind(this));
};
您可以使用.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中看起来不太好,并且您可能希望在代码与经典语言不同的地方分发。
您可以创建一个将上下文作为参数传递的装饰器(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
另一个答案是使用jQuery的.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)内部AJAX成功不工作
老实说,我不会试图避免这种方法。也许你可以使用.bind(...)
, .call(...)
或.apply(...)
,或者过度设计你的代码来摆脱var that = this;
的编码。
但是,毕竟,var that = this
比其他解决方案执行得更好,因为您使用云,并且没有额外的开销来访问代表当前对象的this
引用。
最好的论点是假设 JavaScript以这种方式工作,我非常确定这将是您项目中不太重要的问题。
你说:
在JS中处理这个问题的正确方法是什么?我怎样才能避免写var that = this;在每个方法中,每次我使用回调?
答案是你的做法是正确的。无论如何,我会看看bind
, call
和apply
函数,因为有些情况下它们比var that = this;
工作得更好。
- Javascript,访问一个主要对象模块模式中的每个对象
- $(this).prop('property') vs. this.property
- 是否有任何snippet或jQuery插件可以列出easylist.txt模式匹配的DOM中的所有元素
- 试图在引导模式内动态生成图表,得到offsetWidth错误
- Twitter Bootstrap typeahead:使用“this”获取上下文/调用元素
- 同位素库错误:未捕获错误无布局模式包装生产线8
- 在模式方法内部,“this”在猫鼬 4.4.12 中为空 {}
- 揭示模块模式:func.apply(null,arr) vs. func.apply(this,arr),其中 func
- 模块模式内的回调函数中的“this”指的是什么
- Javascript模式:引用"this"在私人和公共场合
- 在严格模式下使用this.inherited(arguments)时出现DOJO错误
- 理解构造函数调用模式中的“this”
- JS和OOP:滥用“that = this”模式
- $(this)是否需要与“标准”一起使用?jQuery的插件模式
- Function.prototype.call在严格模式之外改变this的类型;为什么
- 如何保存javascript "this"单例模式中的上下文
- Javascript“this"方法调用模式中的指针没有指向对象
- 如果不使用模块揭示模式,我是否可以访问"this"在匿名函数中
- Javascript链接模式返回该对象的等价项,而不是返回this
- JavaScript发布订阅模式:“this”上下文中发生了什么,为什么它丢失了