ES6 方法得到一个 null "this" 并且 class 变量不可访问
ES6 methods get a null "this" and class variables are inaccessible
我正在使用 ES6 class 将 Node 中的一些功能捆绑在一起。这是(基本上)它的样子:
class processDocs {
constructor(id) {
this.id = id;
// console.log(this) returns { id: id }
}
getDocs(cb) {
// console.log(this) returns null
docs
.query(qb => {
qb.where('id', this.id);
})
.fetch()
.then(function(documents) {
cb(null, documents);
})
;
}
alterDocs(documents, cb) {
//some logic
}
reindexSearch(cb) {
//some logic
}
process() {
// console.log(this) returns { id: id }
async.waterfall([
this.getDocs,
this.alterDocs,
this.reindexSearch
]);
}
}
export default processDocs;
我认为在 ES6 classes 中,分配 public 变量的方式是简单地引用 this
并且通过构造函数初始化这些变量的方式正是它的方式出现在我的 class 定义中。
以下是我如何调用 class(在单独的文件中):
var Processor = require('./processDocs');
var pr = new Processor(id);
var docs;
pr.process();
这是问题所在,当我从构造函数中 console.log
输出 this
时,我得到了预测的 { id: id }
值;但是,当 process
为 运行ning 时,每当我在 getDocs
中注销 this
时,它为空。但是,当我在瀑布前 process()
注销 this
时,我得到了我原来的对象。
这有什么原因吗?
顺便说一句,我正在使用 node: v0.10.33
和 babel-node 4.6.6
并且我 运行 带有 --harmony
标志的 babel-node。在有人问之前,我无法更新到较新的 Node 版本,因为主要依赖项停留在 v0.10.x
.
EDIT 我能够创建一个解决方法,但它不是很像 es6。问题似乎与 async.waterfall
有关。我不得不使用 .bind
来修复它:
async.waterfall([
this.getDocs.bind(this),
this.alterDocs.bind(this),
this.reindexSearch.bind(this)
]);
我自己创建了以下函数。
let bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; //from coffeescript's => operator
//use in a class's constructor to make the this pointer always refer to the object in which a function resides
function fixThisPointer(_this, func){
_this[func.name] = bind(_this[func.name], _this);
}
function fixThisPointer2(_this, funcNameArray){
for (name of funcNameArray){
_this[name] = bind(_this[name], _this);
}
}
然后,当我需要它时,我在我的构造函数中使用这个命令
fixThisPointer(this, foo)
或者这个命令
fixThisPointer2(this, ['foo', 'foo2', 'foo3'])
您可以在 class 中使用箭头函数,因为它们会自动绑定它。您可以将 class 方法编写为:
getDocs = (cb) => {
// console.log(this) will not returns null
docs
.query(qb => {
qb.where('id', this.id);
})
.fetch()
.then(function(documents) {
cb(null, documents);
})
;
}
参见this MDN article:"Arrow functions capture the this value of the enclosing context"
考虑将 process()
的正文更新为:
process() {
// console.log(this) returns { id: id }
async.waterfall([
(cb)=>{this.getDocs(cb);},
(documents,cb)=>{this.alterDocs(documents,cb);},
(cb)=>{this.reindexSearch(cb);}
]);
}
使用箭头函数确保 class 的成员函数在正确的上下文中被调用。
ES6 没有改变 "this" 的工作方式,因此它取决于 "where you call it" 的上下文而不是 "always have same" 值。这很不直观,在其他语言中也不常见。
考虑这个例子
class processDocs {
constructor(id) {
this.id = id;
console.log(this)
}
getDocs(cb) {
console.log(this)
}
alterDocs(documents, cb) {
//some logic
}
reindexSearch(cb) {
//some logic
}
process() {
console.log(this)
}
}
var process = new processDocs(10);
var docs = process.getDocs(function(){});
var processInstance = process.process();
var docsAsFunction = process.getDocs;
docsAsFunction(function(){});
输出是
processDocs {id: 10}
processDocs {id: 10}
processDocs {id: 10}
undefined
如你所见,最后一个是undefines,它是调用"docsAsFunction",因为你没有直接从它的class调用那个函数,所以context是不同的
你可以阅读它,例如 here
我正在使用 ES6 class 将 Node 中的一些功能捆绑在一起。这是(基本上)它的样子:
class processDocs {
constructor(id) {
this.id = id;
// console.log(this) returns { id: id }
}
getDocs(cb) {
// console.log(this) returns null
docs
.query(qb => {
qb.where('id', this.id);
})
.fetch()
.then(function(documents) {
cb(null, documents);
})
;
}
alterDocs(documents, cb) {
//some logic
}
reindexSearch(cb) {
//some logic
}
process() {
// console.log(this) returns { id: id }
async.waterfall([
this.getDocs,
this.alterDocs,
this.reindexSearch
]);
}
}
export default processDocs;
我认为在 ES6 classes 中,分配 public 变量的方式是简单地引用 this
并且通过构造函数初始化这些变量的方式正是它的方式出现在我的 class 定义中。
以下是我如何调用 class(在单独的文件中):
var Processor = require('./processDocs');
var pr = new Processor(id);
var docs;
pr.process();
这是问题所在,当我从构造函数中 console.log
输出 this
时,我得到了预测的 { id: id }
值;但是,当 process
为 运行ning 时,每当我在 getDocs
中注销 this
时,它为空。但是,当我在瀑布前 process()
注销 this
时,我得到了我原来的对象。
这有什么原因吗?
顺便说一句,我正在使用 node: v0.10.33
和 babel-node 4.6.6
并且我 运行 带有 --harmony
标志的 babel-node。在有人问之前,我无法更新到较新的 Node 版本,因为主要依赖项停留在 v0.10.x
.
EDIT 我能够创建一个解决方法,但它不是很像 es6。问题似乎与 async.waterfall
有关。我不得不使用 .bind
来修复它:
async.waterfall([
this.getDocs.bind(this),
this.alterDocs.bind(this),
this.reindexSearch.bind(this)
]);
我自己创建了以下函数。
let bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; //from coffeescript's => operator
//use in a class's constructor to make the this pointer always refer to the object in which a function resides
function fixThisPointer(_this, func){
_this[func.name] = bind(_this[func.name], _this);
}
function fixThisPointer2(_this, funcNameArray){
for (name of funcNameArray){
_this[name] = bind(_this[name], _this);
}
}
然后,当我需要它时,我在我的构造函数中使用这个命令
fixThisPointer(this, foo)
或者这个命令
fixThisPointer2(this, ['foo', 'foo2', 'foo3'])
您可以在 class 中使用箭头函数,因为它们会自动绑定它。您可以将 class 方法编写为:
getDocs = (cb) => {
// console.log(this) will not returns null
docs
.query(qb => {
qb.where('id', this.id);
})
.fetch()
.then(function(documents) {
cb(null, documents);
})
;
}
参见this MDN article:"Arrow functions capture the this value of the enclosing context"
考虑将 process()
的正文更新为:
process() {
// console.log(this) returns { id: id }
async.waterfall([
(cb)=>{this.getDocs(cb);},
(documents,cb)=>{this.alterDocs(documents,cb);},
(cb)=>{this.reindexSearch(cb);}
]);
}
使用箭头函数确保 class 的成员函数在正确的上下文中被调用。
ES6 没有改变 "this" 的工作方式,因此它取决于 "where you call it" 的上下文而不是 "always have same" 值。这很不直观,在其他语言中也不常见。
考虑这个例子
class processDocs {
constructor(id) {
this.id = id;
console.log(this)
}
getDocs(cb) {
console.log(this)
}
alterDocs(documents, cb) {
//some logic
}
reindexSearch(cb) {
//some logic
}
process() {
console.log(this)
}
}
var process = new processDocs(10);
var docs = process.getDocs(function(){});
var processInstance = process.process();
var docsAsFunction = process.getDocs;
docsAsFunction(function(){});
输出是
processDocs {id: 10}
processDocs {id: 10}
processDocs {id: 10}
undefined
如你所见,最后一个是undefines,它是调用"docsAsFunction",因为你没有直接从它的class调用那个函数,所以context是不同的
你可以阅读它,例如 here