2015-08-15 15 views
11

Sto utilizzando l'estremamente utile local fat arrow per preservare il contesto this nei callback. Tuttavia, a volte ho bisogno di accedere al valore che avrebbe avuto this se non avessi usato la freccia grassa.TypeScript: come utilizzare sia la fat fat che questa?

Un esempio sono i callback di eventi, in cui this ha il valore dell'elemento che l'evento è accaduto il (Sono consapevole che in questo particolare esempio si potrebbe usare event.currentTarget, ma lascia supporre non è possibile per il bene di un esempio):

function callback() { 
    // How to access the button that was clicked? 
} 

$('.button').click(() => { callback() }); 

Nota: ho incontrato this question che si occupa di questo esattamente lo stesso problema, ma in CoffeeScript.

+2

Utilizzare una funzione normale e memorizzare "questo" esterno in una variabile. – Oriol

+0

"... e associa lessicamente questo valore" https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Functions/Arrow_functions – coma

+0

Non dimenticare il buon ol ''event.currentTarget' a ottenere il target dell'evento indipendentemente dall'associazione 'this' (si noti che sarà un' HTMLElement', non un oggetto jQuery). –

risposta

2

Si potrebbe scrivere una funzione decoratore che avvolge una funzione grasso freccia all'interno di un'altra funzione che permette l'accesso alla consueta this e passa tale valore alla funzione grasso freccia come parametro aggiuntivo:

function thisAsThat (callback) { 
    return function() { 
     return callback.apply(null, [this].concat(arguments)); 
    } 
} 

Pertanto, quando si chiama thisAsThat con una funzione freccia avanzata, ciò restituisce fondamentalmente una funzione di callback diversa che, quando chiamata, chiama la funzione fat-arrow con tutti gli argomenti ma aggiunge this come argomento nella parte anteriore. Dal momento che non puoi vincolare le funzioni fat-arrow, puoi semplicemente chiamare bind e apply senza doversi preoccupare di perdere il valore.

È quindi possibile utilizzare in questo modo:

element.addEventListener('click', thisAsThat((that, evt) => console.log(this, that, evt))); 

Questo registrerà il this dell'ambito corrente (come da regolamento grasso-freccia), il this della funzione di callback come that (indicando l'elemento per i gestori di eventi) e l'evento stesso (ma in pratica tutti gli argomenti vengono ancora passati).

+1

Grazie - questa è un'idea così semplice e intelligente. Una domanda però - è davvero necessario '.bind'? Potrei (come vedo io) ottenere lo stesso effetto con '.apply' da solo, in questo modo:' function thisAsThat (callback) {return function() {return callback.apply (null, [this] .concat (argomenti)) ; }; } '. Qualche problema con questo approccio secondo te? – fstanis

+0

Sì, dovrebbe funzionare altrettanto bene. Ho appena usato 'bind' quindi non avrei dovuto giocare con' argomenti' (dal momento che non è un array ma solo "array-like"). Ma sì, usare "concat" mi sembra una buona idea. Aggiungerò invece la mia risposta per usarlo :) – poke

1

Non è necessario utilizzare le funzioni freccia per questo scopo.

Si può semplicemente utilizzare il bind funzione per modificare la portata della funzione:

function callback() { 
 
    // How to access the button that was clicked? 
 
    $("span").text(this.text()); 
 
} 
 

 
var b = $('.button'); // in Typescript you should be using let instead of var 
 
b.click(callback.bind(b));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> 
 
<button class='button'>Hello, world!</button> 
 
<span></span>


Per altri scenari complessi in cui si desidera utilizzare entrambe le funzioni di direzione e di chiamare altre funzioni in nello stesso contesto, è possibile utilizzare Function's callor apply:

// let's suppose your callback function expects a Date and a Number param 
$('.button').click(() => callback.call(this, new Date(), 23)); 
// or 
$('.button').click(() => callback.apply(this, [new Date(), 23])); 
+3

OP vuole accedere contemporaneamente a "this" (quello dell'ambito esterno e quello del callback) allo stesso tempo, idealmente solo da una funzione fat-arrow. Quello che stai facendo è solo rebinding 'this' per una funzione di callback" completa ". – poke

+0

Infatti, questa soluzione funziona * solo * se, nel momento in cui si registra il gestore di eventi, si ha accesso al valore che si desidera 'questo' avere quando viene attivato l'evento. Considerare il caso di eventi delegati, come '$ ('# button-container'). On ('click', '.button', callback)'.In tal caso, non si poteva sapere in anticipo quale pulsante è stato cliccato e non è possibile ".bind" su un argomento (infatti, "questo" avrebbe in realtà molti valori possibili, a seconda dei pulsanti esistenti). – fstanis

+0

Oh, scusa, ho frainteso la domanda. Grazie per il chiarimento! – Buzinas

0

@ risposta di Poke è l'idea giusta, ma ci sono un paio di problemi:

function thisAsThat (callback) { 
    return function() { 
     return callback.apply(null, [this].concat(arguments)); 
    } 
} 

First, arguments non è una vera e propria gamma, in modo da chiamare concat() come indicato sopra si tradurrà in un array nidificato:

[0] = this 
[1] = [ [0] = param1, [1] = param2, ... ] 

per risolvere questo problema, use slice() per convertire arguments ad una vera matrice:

 return callback.apply(null, [this].concat(Array.prototype.slice.call(arguments))); 

Risultato:

[0] = this 
[1] = param1 
[2] = param2 
... 

In secondo luogo, passando null per il primo parametro da applicare() non ha funzionato per me; Dovevo superare la classe Foo this in modo esplicito. Ecco un esempio completo:

class Foo { 

    public setClickHandler(checkbox: JQuery): void { 
     checkbox.click(this.captureThis(this.onCheckboxClick)); 
    } 

    protected onCheckboxClick(checkbox: HTMLInputElement, event: Event) { 
     // 'this' refers to class Foo 
    } 

    protected captureThis(callback) { 
     var self = this; 
     return function() { 
      return callback.apply(self, [this].concat(Array.prototype.slice.call(arguments))); 
     } 
    } 

} 

Con questo approccio, il callback onCheckboxClick() ha accesso sia alla classe di this e l'elemento casella di controllo (come il primo parametro) che sarebbe stato this in un tipico callback click evento. Lo svantaggio dell'uso di captureThis() è che si perde la sicurezza del tipo di TypeScript per il callback, quindi Typescript non può impedirti di incasinare la firma della funzione di callback.

+0

Sembra che qualcosa sia cambiato nell'implementazione, poiché ricordo che l'approccio di '[this] .concat (...)' funzionava in passato. Inoltre, suppongo che il passaggio di 'null' come primo argomento non abbia funzionato per te dal momento che il tuo approccio sostituisce del tutto le funzioni di freccia grassa (come stai praticamente replicando la loro funzionalità). Se si utilizzano le funzioni di freccia grossa, è possibile mantenere la sicurezza del tipo di esso. – fstanis

+0

Ma tu non puoi usare la freccia grassa perché riscrivere 'questo' in' [this] .concat (...) 'per riferirsi alla classe Foo invece della casella di controllo. – cbranch

Problemi correlati