Czy jest aliasem „to” na maszynie?

głosy
72

Mam próbował napisać klasę na maszynie, która ma zdefiniowaną metodę, która działa jako callback obsługi zdarzeń na zdarzenie 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.
    }
}

W ramach obsługi zdarzeń onFocusIn, maszynopis widzi „to” jako do „tego” klasy. Jednak jQuery nadpisuje to odwołanie i ustawia go do obiektu DOM związanej ze zdarzeniem.

Jedną z alternatyw jest zdefiniowanie lambda w konstruktorze jako obsługi zdarzeń, w którym to przypadku maszynopis tworzy pewnego rodzaju zamknięcia z ukrytym _this aliasu.

class Editor {
    textarea: JQuery;

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

Moje pytanie brzmi, czy istnieje inny sposób, aby uzyskać dostęp do tego odniesienia w ramach obsługi zdarzeń przy użyciu metody opartej na maszynopis, aby przezwyciężyć ten problem jQuery?

Utwórz 06/10/2012 o 04:26
źródło użytkownik
W innych językach...                            


12 odpowiedzi

głosy
97

Zakres thisjest zachowany przy użyciu składni funkcji strzałki () => { ... }- to jest przykład wzięty z maszynopis dla programistów JavaScript .

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

Zauważ, że this.textdaje Text from outer functionbo składnia funkcji strzałka zachowuje „Zakres leksykalny”.

Odpowiedział 06/10/2012 o 15:25
źródło użytkownik

głosy
21

Tak jak wspomniano nie ma mechanizmu maszynopis do zapewnienia metody jest zawsze związany jest thiswskaźnik (i to nie jest tylko kwestia jQuery). To nie znaczy, że nie jest dość prosta droga do rozwiązania tego problemu. Co potrzebne jest aby wygenerować proxy dla metody, która przywraca thiswskaźnik przed wywołaniem zwrotnego. Następnie należy owinąć zwrotnego z tym proxy przed przekazaniem go do zdarzenia. jQuery ma wbudowany mechanizm ten nazywa jQuery.proxy(). Oto przykład z Twojego powyższego kodu przy użyciu tej metody (zawiadomienie dodaną $.proxy()połączenia).

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

To rozsądne rozwiązanie, ale ja osobiście uważam, że deweloperzy często zapominają to połączenie proxy więc mam wymyślić alternatywną maszynopis oparciu o rozwiązania tego problemu. Korzystanie The HasCallbacksklasa poniżej wszystko, co trzeba zrobić, to czerpią swoją klasę z HasCallbacksczym wszelkie metody poprzedzone 'cb_'będą mieli thiswskaźnik trwale związany. Po prostu nie można nazwać tę metodę z innym thiswskaźnikiem, który w większości przypadków jest korzystne. Mechanizm działa więc albo jej tylko w zależności od tego, który jest łatwiejszy w użyciu.

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({});
Odpowiedział 06/10/2012 o 06:28
źródło użytkownik

głosy
20

Objętych niektórych innych odpowiedzi, używając składni strzałki, aby zdefiniować funkcję powoduje odniesień do thiszawsze odnoszą się do klasy okalającego.

Tak, aby odpowiedzieć na pytanie, tu są dwa proste rozwiązania.

Odwołać się do metody z użyciem składni strzałki

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

Zdefiniować metodę używając składni strzałki

onFocusIn = (e: JQueryEventObject) => {
    var height = this.textarea.css('height');
}
Odpowiedział 23/12/2013 o 05:27
źródło użytkownik

głosy
6

Można powiązać funkcję członka do jej przykład w konstruktorze.

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

Alternatywnie, po prostu związać go po dodaniu obsługi.

        this.textarea.focusin(onFocusIn.bind(this));
Odpowiedział 20/05/2014 o 15:47
źródło użytkownik

głosy
3

Steven Ickman rozwiązanie jest przydatne, ale niekompletne. Danny Becket i odpowiedzi Sama są krótsze i bardziej ręczne, a nie w tym samym ogólnym przypadku konieczności wywołania zwrotnego, który wymaga zarówno dynamiczne i leksykalnie scoped „to” w tym samym czasie. Przejdź do mojego kodu, jeśli moje wyjaśnienie poniżej TL; DR ...

Muszę zachować „to” dla dynamicznego rozsyłania do użytku z callbacków bibliotecznych, i muszę mieć „to” z leksykalnego zakresu celem instancji klasy. Uważam, że jest to najbardziej elegancki przekazać instancję do generatora wywołania zwrotnego, pozwalając skutecznie zamknięcie parametru nad instancji klasy. Kompilator informuje, jeśli brakowało robić. Używam konwencję wywołanie leksykalnie zawężona parametr „outerThis”, ale „ja” lub inna nazwa może być lepiej.

Korzystanie z „tego” hasła został skradziony ze świata oo, a gdy maszynopis przyjęła go (z ECMAScript 6 specyfikacje przypuszczam), że pomylili się leksykalnie scoped koncepcję i dynamicznie scoped pojęcie, gdy metoda jest wywoływana przez inny podmiot , Jestem trochę dotknięty w tym; Wolałbym „ja” słowo kluczowe w maszynie tak, że mogę przekazać zawężona leksykalnie instancji obiektu z dala od niego. Alternatywnie, JS może być na nowo, aby wymagać pierwszej pozycji parametru explicit „rozmówcy”, kiedy jest to konieczne (a więc złamać wszystkie strony internetowe w jednym zamachem).

Oto moje rozwiązanie (wycięto z dużej klasy). Popatrzeć w szczególności w drodze metody są nazywane i ciało „dragmoveLambda” w szczególności:

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

}
Odpowiedział 21/03/2014 o 20:33
źródło użytkownik

głosy
3

Spróbuj tego

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
    }

}
Odpowiedział 24/05/2013 o 02:47
źródło użytkownik

głosy
2

Można użyć funkcji js eval: var realThis = eval('this');

Odpowiedział 20/09/2014 o 13:13
źródło użytkownik

głosy
2

Maszynopis nie przewiduje żadnych dodatkowych sposób (poza oznacza regularne JavaScript), aby wrócić do „prawdziwego” thisodniesienia innego niż thismanipulowanie wygody świadczonych w składni tłuszcz strzałka lambda (co jest dopuszczalne z punktu widzenia back-compat ponieważ nie istniejącego kodu JS może być za pomocą =>ekspresji).

Można zakładać sugestię do strony CodePlex, ale z językowego punktu widzenia projektowania tam chyba nie dużo, że może zdarzyć się tutaj, ponieważ każdy zdrowy hasła kompilator mógłby zapewnić może być już używany przez kod JavaScript obowiązują.

Odpowiedział 06/10/2012 o 04:35
źródło użytkownik

głosy
1

Mam do czynienia z podobnym problemem. Myślę, że można używać .each()w wielu przypadkach, aby zachować thisjak inną wartość dla późniejszych wydarzeń.

Sposób JavaScript:

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

Sposób maszynopis:

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

Mam nadzieję, że ktoś pomoże.

Odpowiedział 16/06/2014 o 09:17
źródło użytkownik

głosy
0

Jest o wiele prostsze rozwiązanie niż wszystkich powyższych odpowiedzi. Zasadniczo możemy spaść z powrotem do JavaScript za pomocą klawisza funkcyjnego słowo zamiast używać „=>” skonstruować co przełoży się to „” do klasy „to”

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'); 
    }
}
Odpowiedział 11/05/2015 o 18:30
źródło użytkownik

głosy
0

Sprawdź ten wpis na blogu http://lumpofcode.blogspot.com/2012/10/typescript-dart-google-web-toolkit-and.html , ma szczegółowe omówienie technik organizowania połączeń wewnątrz i pomiędzy klasami maszynopis.

Odpowiedział 06/12/2014 o 01:49
źródło użytkownik

głosy
0

Można przechowywać odniesienie do thisw innej zmiennej .. selfmoże i dostęp do odwołania w taki sposób. Nie używam maszynopis, ale to metoda, która był udany dla mnie z wanilii JavaScript w przeszłości.

Odpowiedział 06/10/2012 o 04:30
źródło użytkownik

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