C'è un alias per 'questo' a macchina?

voti
72

Ho cercato di scrivere una classe a macchina che ha un metodo definito che agisce come un callback gestore di eventi per un evento di jQuery.

class Editor {
    textarea: JQuery;

    constructor(public id: string) {
        this.textarea = $(id);
        this.textarea.focusin(onFocusIn);
    }

    onFocusIn(e: JQueryEventObject) {
        var height = this.textarea.css('height'); // <-- This is not good.
    }
}

All'interno del gestore di eventi onFocusIn, dattiloscritto vede 'questo' come il 'presente' della classe. Tuttavia, jQuery sostituisce il riferimento this e imposta all'oggetto DOM associato all'evento.

Un'alternativa è quella di definire un lambda all'interno del costruttore, come il gestore di eventi, nel qual caso dattiloscritto crea una sorta di chiusura con un alias _this nascosta.

class Editor {
    textarea: JQuery;

    constructor(public id: string) {
        this.textarea = $(id);
        this.textarea.focusin((e) => {
            var height = this.textarea.css('height'); // <-- This is good.
        });
    }
}

La mia domanda è, c'è un altro modo per accedere a questo riferimento all'interno del gestore di eventi metodo-based utilizzando dattiloscritto, per superare questo comportamento jQuery?

È pubblicato 06/10/2012 alle 04:26
fonte dall'utente
In altre lingue...                            


12 risposte

voti
97

Il campo di applicazione thisè conservata quando si utilizza la sintassi della funzione freccia () => { ... }- qui è un esempio tratto dalla tipografico per i programmatori JavaScript .

var ScopeExample = { 
  text: "Text from outer function", 
  run: function() { 
    setTimeout( () => { 
      alert(this.text); 
    }, 1000); 
  } 
};

Si noti che this.textsi dà Text from outer functionperché la sintassi della funzione freccia conserva la "scope lessicale".

Risposto il 06/10/2012 a 15:25
fonte dall'utente

voti
21

Quindi, come detto non esiste un meccanismo dattiloscritto per garantire un metodo è sempre legata alla sua thispuntatore (e questo non è solo un problema di jQuery.) Ciò non significa che non ci sia un modo abbastanza semplice per affrontare questo problema. Quello che vi serve è quello di generare un proxy per il metodo che consente di ripristinare il thispuntatore prima di chiamare il callback. È quindi necessario per avvolgere la callback con quella delega prima di passarlo nell'evento. jQuery ha un meccanismo integrato per questa chiamata jQuery.proxy(). Ecco un esempio del codice di cui sopra utilizza lo stesso metodo (notare l'aggiunta $.proxy()di chiamata.)

class Editor { 
    textarea: JQuery; 

    constructor(public id: string) { 
        this.textarea = $(id); 
        this.textarea.focusin($.proxy(onFocusIn, this)); 
    } 

    onFocusIn(e: JQueryEventObject) { 
        var height = this.textarea.css('height'); // <-- This is not good. 
    } 
} 

Questa è una soluzione ragionevole ma ho personalmente trovato che gli sviluppatori spesso si dimentica di includere la chiamata proxy in modo che è venuta in mente una soluzione basata dattiloscritto alternativa a questo problema. Utilizzando la HasCallbacksclasse di sotto tutto quello che dovete fare è derivare la classe da HasCallbackse quindi qualsiasi metodo con prefisso 'cb_'avranno il loro thispuntatore legato in modo permanente. Semplicemente non si può chiamare tale metodo con un diverso thispuntatore che nella maggior parte dei casi è preferibile. O meccanismo funziona così il suo solo a seconda di quale si trova più facile da usare.

class HasCallbacks {
    constructor() {
        var _this = this, _constructor = (<any>this).constructor;
        if (!_constructor.__cb__) {
            _constructor.__cb__ = {};
            for (var m in this) {
                var fn = this[m];
                if (typeof fn === 'function' && m.indexOf('cb_') == 0) {
                    _constructor.__cb__[m] = fn;                    
                }
            }
        }
        for (var m in _constructor.__cb__) {
            (function (m, fn) {
                _this[m] = function () {
                    return fn.apply(_this, Array.prototype.slice.call(arguments));                      
                };
            })(m, _constructor.__cb__[m]);
        }
    }
}

class Foo extends HasCallbacks  {
    private label = 'test';

    constructor() {
        super();

    }

    public cb_Bar() {
        alert(this.label);
    }
}

var x = new Foo();
x.cb_Bar.call({});
Risposto il 06/10/2012 a 06:28
fonte dall'utente

voti
20

Come coperto da alcune delle altre risposte, utilizzando la sintassi freccia per definire una funzione fa sì che i riferimenti a thisfare riferimento allo classe contenitrice.

Quindi, per rispondere alla tua domanda, qui ci sono due soluzioni semplici.

Fare riferimento al metodo che utilizza la sintassi freccia

constructor(public id: string) {
    this.textarea = $(id);
    this.textarea.focusin(e => this.onFocusIn(e));
}

Definire il metodo che utilizza la sintassi freccia

onFocusIn = (e: JQueryEventObject) => {
    var height = this.textarea.css('height');
}
Risposto il 23/12/2013 a 05:27
fonte dall'utente

voti
6

È possibile associare una funzione membro al suo esempio nel costruttore.

class Editor {
    textarea: JQuery;

    constructor(public id: string) {
        this.textarea = $(id);
        this.textarea.focusin(onFocusIn);
        this.onFocusIn = this.onFocusIn.bind(this); // <-- Bind to 'this' forever
    }

    onFocusIn(e: JQueryEventObject) {
        var height = this.textarea.css('height');   // <-- This is now fine
    }
}

In alternativa, basta associarlo quando si aggiunge il gestore.

        this.textarea.focusin(onFocusIn.bind(this));
Risposto il 20/05/2014 a 15:47
fonte dall'utente

voti
3

La soluzione di Steven Ickman è a portata di mano, ma incompleta. Danny Becket e le risposte di Sam sono più brevi e più manuali, e non riescono nello stesso caso generale di avere un callback che ha bisogno sia dinamico e con scope lessicale "questo" allo stesso tempo. Passa al mio codice, se la mia spiegazione che segue è TL; DR ...

Ho bisogno di conservare "questo" per scoping dinamico per l'utilizzo con i callback biblioteca, e ho bisogno di avere un "presente" con scoping lessicale per l'istanza della classe. Io sostengo che è più elegante di passare l'istanza in un generatore di callback, lasciando che in modo efficace la chiusura dei parametri sopra l'istanza di classe. Il compilatore ti dice se vi siete persi farlo. Io uso una convenzione di chiamare il parametro "outerThis" scope lessicale, ma "io" o un altro nome potrebbe essere migliore.

L'uso del "questo" parola chiave è stato rubato dal mondo OO, e quando dattiloscritto ha adottato (da ECMAScript 6 spec presumo), hanno conflated un concetto con scope lessicale e di un concetto ambito in modo dinamico, ogni volta che un metodo viene chiamato da un soggetto diverso . Sono un po 'seccata a questo; Io preferirei una parola chiave "sé" a macchina in modo che possa consegnare l'istanza di oggetto con scope lessicale fuori di esso. In alternativa, JS potrebbe essere ridefinito per richiedere una prima posizione di parametro "chiamante" esplicito quando è necessario (e quindi rompere tutte le pagine web in un colpo solo).

Ecco la mia soluzione (asportato da una vasta classe). Date un'occhiata in particolare il modo in cui i metodi vengono chiamati, e il corpo di "dragmoveLambda", in particolare:

export class OntologyMappingOverview {

initGraph(){
...
// Using D3, have to provide a container of mouse-drag behavior functions
// to a force layout graph
this.nodeDragBehavior = d3.behavior.drag()
        .on("dragstart", this.dragstartLambda(this))
        .on("drag", this.dragmoveLambda(this))
        .on("dragend", this.dragendLambda(this));

...
}

dragmoveLambda(outerThis: OntologyMappingOverview): {(d: any, i: number): void} {
    console.log("redefine this for dragmove");

    return function(d, i){
        console.log("dragmove");
        d.px += d3.event.dx;
        d.py += d3.event.dy;
        d.x += d3.event.dx;
        d.y += d3.event.dy; 

        // Referring to "this" in dynamic scoping context
        d3.select(this).attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

        outerThis.vis.selectAll("line")
            .filter(function(e, i){ return e.source == d || e.target == d; })
            .attr("x1", function(e) { return e.source.x; })
            .attr("y1", function(e) { return e.source.y; })
            .attr("x2", function(e) { return e.target.x; })
            .attr("y2", function(e) { return e.target.y; });

    }
}

dragging: boolean  =false;
// *Call* these callback Lambda methods rather than passing directly to the callback caller.
 dragstartLambda(outerThis: OntologyMappingOverview): {(d: any, i: number): void} {
        console.log("redefine this for dragstart");

        return function(d, i) {
            console.log("dragstart");
            outerThis.dragging = true;

            outerThis.forceLayout.stop();
        }
    }

dragendLambda(outerThis: OntologyMappingOverview): {(d: any, i: number): void}  {
        console.log("redefine this for dragend");

        return function(d, i) {
            console.log("dragend");
            outerThis.dragging = false;
            d.fixed = true;
        }
    }

}
Risposto il 21/03/2014 a 20:33
fonte dall'utente

voti
3

Prova questo

class Editor 
{

    textarea: JQuery;
    constructor(public id: string) {
        this.textarea = $(id);
        this.textarea.focusin((e)=> { this.onFocusIn(e); });
    }

    onFocusIn(e: JQueryEventObject) {
        var height = this.textarea.css('height'); // <-- This will work
    }

}
Risposto il 24/05/2013 a 02:47
fonte dall'utente

voti
2

È possibile utilizzare la funzione eval js: var realThis = eval('this');

Risposto il 20/09/2014 a 13:13
fonte dall'utente

voti
2

Dattiloscritto non fornisce alcun modo supplementare (al di là significa che la regolare JavaScript) per tornare alla 'vera' thisdi riferimento diverso thisconvenienza rimappatura previsto nella sintassi grasso freccia lambda (che è ammissibile dal punto di vista di back-compat dal momento che nessun codice JS esistente potrebbe essere utilizzando =>un'espressione).

Si potrebbe inviare un suggerimento al sito CodePlex, ma dal punto di vista di design lingua probabilmente non è tanto che può accadere qui, dal momento che una qualsiasi parola chiave sano di mente il compilatore potrebbe fornire potrebbe essere già in uso da esistente codice JavaScript.

Risposto il 06/10/2012 a 04:35
fonte dall'utente

voti
1

Ho affrontato un problema simile. Penso che si possa utilizzare .each()in molti casi per mantenere thiscome un valore diverso per gli eventi successivi.

Il modo in JavaScript:

$(':input').on('focus', function() {
  $(this).css('background-color', 'green');
}).on('blur', function() {
  $(this).css('background-color', 'transparent');
});

Il modo in cui dattiloscritto:

$(':input').each((i, input) => {
  var $input = $(input);
  $input.on('focus', () => {
    $input.css('background-color', 'green');
  }).on('blur', () => {
    $input.css('background-color', 'transparent');
  });
});

Spero che questo aiuta qualcuno.

Risposto il 16/06/2014 a 09:17
fonte dall'utente

voti
0

C'è una soluzione molto più semplice di tutte le risposte di cui sopra. Fondamentalmente ricadiamo a JavaScript utilizzando la funzione di parola chiave invece di utilizzare '=>' costruire che tradurrà il 'questo' in classe 'questo'

class Editor {
    textarea: JQuery;

    constructor(public id: string) {
        var self = this;                      // <-- This is save the reference
        this.textarea = $(id);
        this.textarea.focusin(function() {   // <-- using  javascript function semantics here
             self.onFocusIn(this);          //  <-- 'this' is as same as in javascript
        });
    }

    onFocusIn(jqueryObject : JQuery) {
        var height = jqueryObject.css('height'); 
    }
}
Risposto il 11/05/2015 a 18:30
fonte dall'utente

voti
0

Dai un'occhiata a questo post sul blog http://lumpofcode.blogspot.com/2012/10/typescript-dart-google-web-toolkit-and.html , ha una discussione dettagliata delle tecniche per l'organizzazione di chiamate all'interno e tra le classi dattiloscritto.

Risposto il 06/12/2014 a 01:49
fonte dall'utente

voti
0

Si potrebbe memorizzare il riferimento a thisin un'altra variabile .. selfforse, e accedere il riferimento in questo modo. Io non uso dattiloscritto, ma questo è un metodo che è stato il successo per me con javascript vaniglia in passato.

Risposto il 06/10/2012 a 04:30
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more