Есть ли псевдоним для «этого» в машинописном?

голоса
72

Я пытался написать класс в машинописном, который имеет определенный метод, который действует в качестве обработчика события обратного вызова события 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.
    }
}

В обработчик события onFocusIn, машинопись видит «это», как быть «это» класса. Тем не менее, JQuery перекрывает эту ссылку и устанавливает его на объект DOM, связанный с событием.

Один из вариантов заключается в определении лямбда внутри конструктора в качестве обработчика события, в этом случае машинопись создает своего рода замыканию со скрытым _this псевдонимом.

class Editor {
    textarea: JQuery;

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

Мой вопрос, есть другой способ, чтобы получить доступ к этой ссылке в рамках методы на основе обработчика событий с помощью машинописи, чтобы преодолеть это поведение JQuery?

Задан 06/10/2012 в 04:26
источник пользователем
На других языках...                            


12 ответов

голоса
97

Объем thisсохраняются при использовании синтаксиса функции стрелки () => { ... }- вот пример взят из машинописи Для JavaScript программистов .

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

Обратите внимание , что this.textдает вам , Text from outer functionпотому что синтаксис функции стрелки сохраняет «лексическую область».

Ответил 06/10/2012 в 15:25
источник пользователем

голоса
21

Так как указано нет механизма Машинописи для обеспечения метода всегда связан с его thisуказателем (и это не только вопрос JQuery.) Это не означает , что не существует довольно простой способ решения этой проблемы. Что вам нужно , чтобы создать прокси - сервер для вашего метода , который восстанавливает thisуказатель перед вызовом обратного вызова. Затем нужно обернуть обратный вызов с этим прокси перед передачей его в событие. JQuery имеет встроенный механизм для этого называется jQuery.proxy(). Вот пример вашей выше кода , используя этот метод (обратите внимание на добавленный $.proxy()вызов.)

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

Это разумное решение , но я лично нашел , что разработчики часто забывают включить прокси - вызов , поэтому я придумать на основе альтернативного машинопись решения этой проблемы. Используя, то HasCallbacksкласс ниже все , что вам нужно сделать , это получить свой класс от HasCallbacksи затем любых методов с префиксом 'cb_'будут иметь их thisуказатель постоянно связан. Вы просто не можете вызвать этот метод с другим thisуказателем , который в большинстве случаев является предпочтительным. Либо механизм работает так , его просто в зависимости от того вы найдете проще в использовании.

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({});
Ответил 06/10/2012 в 06:28
источник пользователем

голоса
20

Как покрыты некоторые другие ответы, используя синтаксис со стрелкой , чтобы определить функцию вызывает ссылки thisвсегда относятся к классу вмещающей.

Таким образом, чтобы ответить на ваш вопрос, здесь два простых обходные пути.

Ссылочный метод с использованием синтаксиса со стрелкой

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

Определение метода с использованием синтаксиса со стрелкой

onFocusIn = (e: JQueryEventObject) => {
    var height = this.textarea.css('height');
}
Ответил 23/12/2013 в 05:27
источник пользователем

голоса
6

Вы можете привязать функцию члена к экземпляру его в конструкторе.

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

С другой стороны, просто привязать его при добавлении обработчика.

        this.textarea.focusin(onFocusIn.bind(this));
Ответил 20/05/2014 в 15:47
источник пользователем

голоса
3

Решение Стивена Ickman является удобным, но неполным. Дэнни Бекет и ответы Сэма короче и более ручные, и не в том же самом общем случае наличия обратного вызова, которая нуждается как динамичное и лексический контекстное «это» в то же самое время. Перейти к моему коду, если мое объяснение ниже TL; DR ...

Мне нужно сохранить «это» для динамического определения объема для использования с библиотекой обратных вызовов, и мне нужно иметь «это» с лексической областью видимости для экземпляра класса. Я считаю , что это самый элегантный передать экземпляр в генератор обратного вызова, эффективно позволяя закрыть параметр над экземпляром класса. Компилятор говорит вам , если вы пропустили это делать. Я использую условность вызова лексический контекстный параметр «outerThis», а «я» или другое название может быть лучше.

Использование «это» ключевое слова украдено из OO мира, и когда машинопись приняла его (из ECMAScript 6 спецификации я полагаю), они сплавлены с лексический контекстной концепцией и динамически контекстной концепция, всякий раз, когда метод вызывается другим объект , Я немного обижен на это; Я предпочел бы «само» ключевое слово в машинописном, так что я могу передать экземпляр лексический контекстный объект отходящему от него. С другой стороны, JS может быть пересмотрена, чтобы требовать явного первого-позиционный параметр «вызывающего абонента», когда это необходимо (и, таким образом, разорвать все веб-страницы в одном махом).

Вот мое решение (вырезано из большого класса). Взглянуть в частности, на то, как называются методы, и тело «dragmoveLambda», в частности:

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

}
Ответил 21/03/2014 в 20:33
источник пользователем

голоса
3

Попробуй это

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
    }

}
Ответил 24/05/2013 в 02:47
источник пользователем

голоса
2

Вы можете использовать функцию Eval JS: var realThis = eval('this');

Ответил 20/09/2014 в 13:13
источник пользователем

голоса
2

Машинопись не дает какой - либо дополнительный пути (за регулярным JavaScript средства) , чтобы вернуться к «реальной» thisссылке, кроме thisпереназначения удобства , представленного в синтаксисе жира стрелок лямбды (что допустимо из дублирующего Compat точки зрения , так как не существующего кода JS может быть с помощью =>выражения).

Вы можете оставить предложение на сайте CodePlex, но с точки зрения дизайна языка там, наверное , не так много , что может произойти здесь, так как любой здравомыслящий ключевого слова компилятор может обеспечить , возможно , уже используется на сохранившемся JavaScript кода.

Ответил 06/10/2012 в 04:35
источник пользователем

голоса
1

Я сталкивался с подобной проблемой. Я думаю , что вы можете использовать .each()во многих случаях , чтобы сохранить thisкак другое значение для последующих событий.

Способ JavaScript:

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

Машинописи путь:

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

Я надеюсь, что это помогает кому-то.

Ответил 16/06/2014 в 09:17
источник пользователем

голоса
0

Существует гораздо более простое решение, чем все выше ответы. В основном мы попадаем обратно в JavaScript, используя функцию клавиши слова вместо «=>» построить который будет переводить «это» в классе «это»

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'); 
    }
}
Ответил 11/05/2015 в 18:30
источник пользователем

голоса
0

Проверьте это сообщение в блоге http://lumpofcode.blogspot.com/2012/10/typescript-dart-google-web-toolkit-and.html , она имеет подробное обсуждение методов для организации звонков внутри и между классами машинописи.

Ответил 06/12/2014 в 01:49
источник пользователем

голоса
0

Вы можете хранить ссылку на thisдругом переменном .. selfвозможно, и получить доступ к ссылке , что путь. Я не использую машинопись, но это метод , который был успешным для меня с ванильным JavaScript в прошлом.

Ответил 06/10/2012 в 04:30
источник пользователем

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