我有以下Javascript类:
class App {
log_text(text) {
console.log(text)
}
process_response(response) {
this.log_text(response) // Uncaught TypeError: this.log_text is not a function
// self.log_text(response) // Uncaught TypeError: self.log_text is not a function
}
do_stuff() {
this.log_text('hello') // OK
}
fetch_data() {
jQuery.get('http://example.com/data/sample.txt', this.process_response, 'text')
}
}
调用方法do_stuff
时,我可以通过调用this.log_text
来访问log_text
。但是,方法 process_response
(用作jQuery.get
的回调处理程序)失败,因为this
表示该上下文中完全不同的对象。
同样,self.log_text
也会抛出 TypeError。
process_response
调用log_text
的可能(或正确)方法是什么,如本例所示?
正在发生的事情是你正在传递你的process_response函数,仅此而已,正如你已经看到这个变化的上下文。一种解决方法是使用箭头语法包装它,这将在 jQuery 触发回调时保留此值。
fetch_data() {
jQuery.get('http://example.com/data/sample.txt', (r)=> this.process_response(r), 'text')
}
Function.bind()
来设置process_response
函数的上下文
fetch_data() {
jQuery.get('http://example.com/data/sample.txt', this.process_response.bind(this), 'text')
}
箭头函数,该函数具有词法this
-
fetch_data() {
jQuery.get
( 'http://example.com/data/sample.txt'
, r => this.process_response(r)
, 'text'
)
}
或者使用将上下文(以及可选的一些参数)绑定到函数的Function#bind
-
fetch_data() {
jQuery.get
( 'http://example.com/data/sample.txt'
, this.process_response.bind(this)
, 'text'
)
}
或者像历史上所做的那样,用var保留上下文;现在,这比上述技术更不受欢迎 -
fetch_data() {
var ctx = this
jQuery.get
( 'http://example.com/data/sample.txt'
, function (r) { ctx.process_response(r) }
, 'text'
)
}
但是,新的JS功能将改善您的生活质量。考虑强制你的 jqXHR 遵守承诺,这样你就可以使用async
和await
-
const get = (opts = {}) =>
new Promise
( (resolve, reject) =>
$.get(opts)
.done((req, status, res) => resolve(res))
.fail((req, status, err) => reject(err))
)
结果是代码更扁平,许多无关的函数(如 fetch_data
和 process_response
)不再需要。更好的是,我们的思想从思考绑定功能和动态上下文中解放出来——
class App {
log_text(text) {
console.log(text)
}
async main () {
const res = await
get ({ url: '/data/sample.txt', dataType: 'text' })
this.log_text(res)
}
}
您甚至可以为get
包装器设置默认选项 -
const defaultOpts =
{ dataType: 'text' }
const get = (opts = {}) =>
new Promise
( (resolve, reject) =>
$.get({ ...defaultOpts, ...opts })
.done((req, status, res) => resolve(res))
.fail((req, status, err) => reject(err))
)
然后使用它——
async main () {
const res = await
get ({ url: '/data/sample.txt' })
this.log_text(res)
// ...
}