¿Hay un alias de 'esto' a máquina de escribir?

votos
72

He intentado escribir una clase a máquina de escribir que tiene un método definido que actúa como un controlador de eventos de devolución de llamada a un evento 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.
    }
}

Dentro del controlador de eventos onfocusin, mecanografiado ve 'esto' como el 'esto' de la clase. Sin embargo, jQuery anula la esta referencia y lo establece en el objeto DOM asociado con el evento.

Una alternativa consiste en definir una lambda dentro del constructor como el controlador de eventos, en cuyo caso mecanografiado crea una especie de cierre con un alias de _Es oculto.

class Editor {
    textarea: JQuery;

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

Mi pregunta es, ¿hay otra manera de acceder al esta referencia dentro del controlador de eventos basados ​​en el método utilizando mecanografiado, para superar este comportamiento jQuery?

Publicado el 06/10/2012 a las 04:26
fuente por usuario
En otros idiomas...                            


12 respuestas

votos
97

El alcance de la thisque se conserva cuando se utiliza la sintaxis de la función de flecha () => { ... }- aquí es un ejemplo tomado de mecanografiado JavaScript programadores .

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

Tenga en cuenta que this.textse da Text from outer functiondebido a que la sintaxis de la función conserva la flecha "ámbito léxico".

Respondida el 06/10/2012 a las 15:25
fuente por usuario

votos
21

Así como se ha dicho no existe un mecanismo mecanografiado de asegurar un método siempre está obligado a que es thispuntero (y esto no es sólo una cuestión de jQuery.) Eso no significa que no hay una forma relativamente sencilla de abordar esta cuestión. Lo que necesita es generar un proxy para el método que restaura el thispuntero antes de llamar a su devolución de llamada. A continuación, deberá envolver su devolución de llamada con ese proxy antes de pasarlo al evento. jQuery tiene un mecanismo incorporado para esta llamada jQuery.proxy(). He aquí un ejemplo del código anterior utilizando ese método (notar el agregado $.proxy()llamada).

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. 
    } 
} 

Eso es una solución razonable, pero he encontrado personalmente que los desarrolladores a menudo se olvidan de incluir la llamada de proximidad por lo que he llegado con una solución basada en texto mecanografiado alternativa a este problema. El uso, la HasCallbacksclase más adelante todo lo que necesita hacer es derivar la clase de HasCallbacksy luego cualquier método con el prefijo 'cb_'tendrán su thispuntero ligado de forma permanente. Simplemente no se puede llamar a ese método con una diferente thispuntero que en la mayoría de los casos es preferible. De cualquier mecanismo funciona por lo que su justo la que resulte más fácil de usar.

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({});
Respondida el 06/10/2012 a las 06:28
fuente por usuario

votos
20

Como cubierto por algunas de las otras respuestas, utilizando la sintaxis de flecha para definir una función provoca referencias thisa conocer y utilizar la clase envolvente.

Así que para responder a su pregunta, aquí hay dos soluciones simples.

Referencia el método que utiliza la sintaxis flecha

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

Definir el método que utiliza la sintaxis flecha

onFocusIn = (e: JQueryEventObject) => {
    var height = this.textarea.css('height');
}
Respondida el 23/12/2013 a las 05:27
fuente por usuario

votos
6

Puede enlazar una función miembro a su instancia en el constructor.

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

Como alternativa, sólo se unen cuando se agrega el controlador.

        this.textarea.focusin(onFocusIn.bind(this));
Respondida el 20/05/2014 a las 15:47
fuente por usuario

votos
3

La solución de Steven Ickman es muy útil, pero incompleta. Danny Becket y respuestas de Sam son más cortos y más manual, y fallan en el mismo caso general de tener una devolución de llamada que necesita "este", al mismo tiempo dinámico y ámbito léxico. Saltar a mi código si mi explicación a continuación es TL; DR ...

Necesito para preservar "este" de ámbito dinámico para su uso con las devoluciones de llamada de la biblioteca, y tengo que tener un "presente" con ámbito léxico a la instancia de clase. Argumento que es más elegante para pasar la instancia en un generador de devolución de llamada, dejando que efectivamente el cierre de parámetros sobre la instancia de clase. El compilador le indica si se ha perdido hacerlo. Yo uso una convención de llamar a los parámetros "outerThis alcance léxico", pero "sí" o cualquier otro nombre podría ser mejor.

El uso de la palabra "this" es robado del mundo OO, y cuando mecanografiado lo adoptó (de ECMAScript 6 especificaciones supongo), que combinó un concepto ámbito léxico y un concepto de ámbito dinámico, cada vez que un método es llamado por una entidad diferente . Estoy un poco molesto en esto; Yo preferiría una palabra clave "sí" a máquina de escribir para que pueda entregar la instancia del objeto ámbito léxico fuera de ella. Alternativamente, JS podría redefinirse para requerir un parámetro explícito primera posición de "el que llama" cuando es necesario (y por lo tanto romper todas las páginas web de una sola vez).

Aquí está mi solución (escindido de una gran clase). Echar un vistazo en particular, por la forma en se llaman los métodos, y el cuerpo de "dragmoveLambda", en particular:

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

}
Respondida el 21/03/2014 a las 20:33
fuente por usuario

votos
3

Prueba esto

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
    }

}
Respondida el 24/05/2013 a las 02:47
fuente por usuario

votos
2

Puede utilizar la función eval JS: var realThis = eval('this');

Respondida el 20/09/2014 a las 13:13
fuente por usuario

votos
2

Mecanografiado no proporciona ninguna manera adicional (más allá de los medios de JavaScript regular) para volver a la 'real' thisde referencia distinto de thisconveniencia reasignación prevista en la sintaxis de grasa flecha lambda (que es permisible desde una perspectiva de back-compat ya que ningún código JS existentes podría ser usando una =>expresión).

Puede publicar una sugerencia para el sitio CodePlex, pero desde una perspectiva de diseño de lenguajes probablemente no hay mucho que puede suceder aquí, ya que cualquier palabra clave en su sano juicio podría proporcionar el compilador pueda estar ya en uso por existente código JavaScript.

Respondida el 06/10/2012 a las 04:35
fuente por usuario

votos
1

Me he enfrentado a un problema similar. Creo que se puede utilizar .each()en muchos casos para mantener thiscomo un valor diferente para eventos posteriores.

La forma de JavaScript:

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

La forma mecanografiada:

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

Espero que esto ayude a alguien.

Respondida el 16/06/2014 a las 09:17
fuente por usuario

votos
0

Hay solución mucho más simple que todas las respuestas anteriores. Básicamente volvemos a caer al JavaScript mediante el uso de la función de la palabra clave en lugar de utilizar '=>' construir lo que se traducirá el 'esto' clase en 'esto'

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'); 
    }
}
Respondida el 11/05/2015 a las 18:30
fuente por usuario

votos
0

Echa un vistazo a esta entrada del blog http://lumpofcode.blogspot.com/2012/10/typescript-dart-google-web-toolkit-and.html , tiene una discusión detallada de las técnicas para la organización de las llamadas dentro ya través de las clases de texto mecanografiado.

Respondida el 06/12/2014 a las 01:49
fuente por usuario

votos
0

Se podría almacenar su referencia a thisen otra variable .. selftal vez, y acceder a la referencia de esa manera. No consumo mecanografiado, pero eso es un método que ha sido un éxito para mí con javascript vainilla en el pasado.

Respondida el 06/10/2012 a las 04:30
fuente por usuario

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