2015-03-31 5 views
8

Я использую класс ES6 для объединения некоторых функций вместе в узле. Вот (в основном), как это выглядит:Методы ES6 получают нулевое значение «this» и переменные класса недоступны

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, как назначить общественные переменные были просто ссылаться this и способ для инициализации этих переменных с помощью конструктора, как именно он показывает в моем определении класса.

Вот как я звоню класс (в отдельном файле):

var Processor = require('./processDocs'); 

var pr = new Processor(id); 
var docs; 
pr.process(); 

Вот вопрос, когда я console.log из this из конструктора, я получаю мое { id: id } значение как предсказывал; однако, всякий раз, когда я выхожу из this в getDocs, когда process запущен, он равен нулю. НО, когда я выхожу из this в process() прямо перед водопадом, я получаю свой первоначальный объект.

Есть ли причина для этого?

Btw, я использую узел: v0.10.33 и babel-node 4.6.6, и я запускаю babel-узел с флагом --harmony. Прежде чем кто-нибудь спросит, я не могу обновить версию более нового узла из-за большой зависимости, которая застряла на v0.10.x.

EDIT Мне удалось создать обходное решение, но оно не очень похоже на es6. Проблема, похоже, связана с async.waterfall. Я должен был использовать .bind исправить:

async.waterfall([ 
     this.getDocs.bind(this), 
     this.alterDocs.bind(this), 
     this.reindexSearch.bind(this) 
    ]); 
+0

Я не понимаю, что вы имеете в виду под «не очень ES6 типа»? Методы не были, не являются и не будут связаны с экземпляром сами по себе. Кстати, если вы хотите настоящий код ES6, а затем отмените 'async' и используйте обещания. – Bergi

+0

Я говорю не очень 'es6-like', потому что мне пришлось использовать' .bind (this) ', когда мне не нужно. Так вы говорите, что если я вызову метод из класса, 'this' будет потерян? Потому что это не имеет смысла для меня. Когда я использовал 'pr.process()', 'this' был прав, и когда я вызывал' this.getDocs' непосредственно из 'this.process()', он также сохранил свой 'this'. Кажется, это проблема 'async'. Кроме того, если бы я хотел быть очень похожим на ES6, я бы использовал генераторы :) Обещания - это ES5 и все равно будут приводить к обратному ад, который я пытаюсь предотвратить, используя 'async'. – antjanus

+0

Почему, по-вашему, вам не нужно использовать '.bind()'? Ваши передающие ссылки на функцию в другом месте. Вы не вызываете их * из своего класса! Посылы - это правильное решение здесь (они очень много ES6! Генераторы - это не асинхронная функция!): 'Process() {return this.getDocs(). Then (docs => this.alterDocs (docs)), затем (alteredDocs => his.redindexSearch (измененныйDocs)); } ' – Bergi

ответ

3

ES6 НЕ ИЗМЕНЯЕТ, как работает «это», поэтому это зависит от контекста «где вы его называете», а не «всегда иметь одно и то же» значение. Это совершенно неинтуитивно, и это не распространено на других языках.

Рассмотрим пример

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», потому что вы не называли эту функцию напрямую из своего класса, поэтому контекст отличается.

Вы можете прочитать об этом, например here

+0

занял у меня месяцы, чтобы получить то, что вы говорите, но теперь я получил его. Спасибо за ответ! – antjanus

0

Я создал себе следующие функции.

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']) 
+0

Мне нужно изучить этот более похожий на es6 способ делать вещи с обещает Берги говорить. –

0

Вы можете использовать функции курсора внутри класса, как они авто свяжите это. Вы можете написать свои методы класса, как:

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: «Функции стрелки захватить это значение контекста объемлющего»

+0

Я недавно видел, что этот шаблон становится обычным явлением. Я задавался вопросом, почему. Благодаря! – antjanus

0

Рассмотрим обновление тела process() к этому:

process() { 
    // console.log(this) returns { id: id } 
    async.waterfall([ 
    (cb)=>{this.getDocs(cb);}, 
    (documents,cb)=>{this.alterDocs(documents,cb);}, 
    (cb)=>{this.reindexSearch(cb);} 
    ]); 
} 

Использование функций стрелок гарантирует, что функции-члены класса будут вызваны с правильным контекстом.

Смежные вопросы