2015-03-31 15 views
8

Sto utilizzando una classe ES6 per raggruppare alcune funzionalità insieme nel nodo. Ecco (in pratica) che cosa assomiglia:I metodi ES6 ottengono null "questo" e le variabili di classe non sono accessibili

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; 

ho pensato che con le classi ES6, il modo di assegnare variabili pubbliche è stato quello di semplice riferimento this e il modo per inizializzare le variabili tramite un costruttore è esattamente come si presenta nella mia definizione di classe.

Ecco come sto chiamando la classe (in un file separato):

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

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

Ecco il problema, quando ho console.log fuori this dal costruttore, ottengo il mio valore { id: id } come previsto; tuttavia, ogni volta che logout this in getDocs quando process è in esecuzione, è null. MA, quando registro this in process() proprio prima della cascata, ottengo il mio oggetto originale.

C'è qualche motivo per questo?

Btw, sto usando il nodo: v0.10.33 e il nodo babel 4.6.6 ed eseguo babel-node con il flag --harmony. Prima che qualcuno chieda, non posso aggiornare a una versione più recente del nodo a causa di una dipendenza importante che è bloccata a v0.10.x.

EDIT Sono stato in grado di creare una soluzione alternativa, ma non è molto simile a ES6. Il problema sembra essere con async.waterfall. Ho dovuto usare un .bind per risolvere il problema:

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

Non capisco cosa si intende per "non molto ES6-like"? I metodi non erano, non lo sono e non saranno vincolati all'istanza da soli. A proposito, se si desidera un vero codice ES6, quindi rottamare 'async' e usare le promesse. – Bergi

+0

Sto dicendo che non è molto "es6-like" perché dovevo usare '.bind (this)' quando non dovevo. Quindi stai dicendo che se invoco un metodo all'interno di una classe, 'this' andrà perso? Perché non ha senso per me. Quando ho usato 'pr.process()', 'this' era corretto e quando ho richiamato' this.getDocs' direttamente da 'this.process()', ha mantenuto anche il suo 'this'. Questo sembra essere un problema 'async'. Inoltre, se volessi essere molto simile ad ES6, userei i generatori :) Le promesse sono ES5 e causerebbero comunque un inferno di callback che è quello che sto cercando di prevenire usando 'async'. – antjanus

+0

Perché pensi di non aver bisogno di usare '.bind()'? I tuoi riferimenti di passaggio per funzionare altrove. Tu * non li stai invocando * dalla tua classe! Le promesse sono la soluzione giusta qui (sono molto ES6! I generatori non sono una funzione asincrona!): 'Process() {return this.getDocs(). Then (docs => this.alterDocs (docs)). Then (alteredDocs => his.redindexSearch (alteredDocs)); } ' – Bergi

risposta

3

L'ES6 NON ha modificato il modo in cui "questo" funziona, quindi dipende dal contesto di "dove lo si chiama" piuttosto che "ha sempre lo stesso valore". Non è intuitivo e non è comune in altre lingue.

Si consideri l'esempio

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(){}); 

L'uscita è

processDocs {id: 10} 
processDocs {id: 10} 
processDocs {id: 10} 
undefined 

Come si può vedere, l'ultimo è non-definisce, che si chiama "docsAsFunction", perché non hai chiamato direttamente che la funzione dalla sua classe, quindi il contesto è diverso.

Si può leggere su di esso, ad esempio here

+0

mi ci sono voluti mesi per ottenere quello che stai dicendo, ma ora ho capito. Grazie per la risposta! – antjanus

0

io ho creato le seguenti funzioni.

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); 
    } 
} 

E poi, quando ne ho bisogno, ho utilizzare questo comando nei miei costruttori

fixThisPointer(this, foo) 

O questo comando

fixThisPointer2(this, ['foo', 'foo2', 'foo3']) 
+0

Dovrò cercare in questo modo più es6 di fare le cose con promette che Bergi abbia parlato. –

0

È possibile utilizzare le funzioni di direzione all'interno della vostra classe come si auto legare questo È possibile scrivere i metodi di classe come:

getDocs = (cb) => { 
    // console.log(this) will not returns null 
    docs 
     .query(qb => { 
     qb.where('id', this.id); 
     }) 
     .fetch() 
     .then(function(documents) { 
     cb(null, documents); 
     }) 
    ; 
} 

vedere this MDN article: "Funzioni freccia catturare l'questo valore del contesto racchiude"

+0

Ho visto di recente questo schema diventare comune. Mi sono chiesto perché. Grazie! – antjanus

0

consideri l'aggiornamento del corpo di process() a questo:

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

L'utilizzo delle funzioni di freccia garantisce che le funzioni membro della classe vengano richiamate con il contesto corretto.

Problemi correlati