TypeScript: Как использовать как жирную стрелку, так и это?
Я использую чрезвычайно полезную локальную жирную стрелку, чтобы сохранить this
контекст в обратных вызовах. Однако иногда мне нужно получить доступ к значению, которое this
имело бы, если бы я не использовал жирную стрелу.
Одним из примеров являются обратные вызовы событий, где this
имеет значение элемента, в котором произошло событие (я знаю, что в этом конкретном примере вы можете использовать event.currentTarget
, но предположим, что вы не можете для примера):
function callback() {
// How to access the button that was clicked?
}
$('.button').click(() => { callback() });
Примечание. Я столкнулся с этим вопросом, который касается именно этой же проблемы, но в CoffeeScript.
Ответы
Ответ 1
Вы можете написать функцию декоратора, которая обертывает функцию fat-arrow внутри другой функции, которая позволяет получить доступ к обычному this
и передает это значение функции fat-arrow в качестве дополнительного аргумента:
function thisAsThat (callback) {
return function () {
return callback.apply(null, [this].concat(arguments));
}
}
Поэтому, когда вы вызываете thisAsThat
с thisAsThat
fat-arrow, это в основном возвращает другую функцию обратного вызова, которая при вызове вызывает функцию fat-arrow со всеми аргументами, но добавляет this
как аргумент в начале. Поскольку вы не можете связывать функции стрелки, вы можете просто вызвать bind
и apply
на нем, не беспокоясь о потере значения.
Затем вы можете использовать его следующим образом:
element.addEventListener('click', thisAsThat((that, evt) => console.log(this, that, evt)));
Это регистрирует this
из текущей области (в соответствии с правилами жира стрелки), то this
функции обратного вызова в that
, that
(указывающем на элемент для обработчиков событий), а сам (но в основном, все аргументы по - прежнему передаются на событие).
Ответ 2
Для этой цели вам не нужно использовать функции стрелок.
Вы можете просто использовать bind
Function для изменения области действия:
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>
Ответ 3
Ответ @poke - это правильная идея, но есть пара вопросов:
function thisAsThat (callback) {
return function () {
return callback.apply(null, [this].concat(arguments));
}
}
Во-первых, arguments
не являются истинным массивом, поэтому вызов concat()
как показано выше, приведет к вложенному массиву:
[0] = this
[1] = [ [0] = param1, [1] = param2, ... ]
Чтобы исправить это, используйте slice() для преобразования arguments
в истинный массив:
return callback.apply(null, [this].concat(Array.prototype.slice.call(arguments)));
Результат:
[0] = this
[1] = param1
[2] = param2
...
Во-вторых, передача null
для первого параметра apply() не работает для меня; Я должен был пройти класс Foo this
явно. Вот полный пример:
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)));
}
}
}
При таком подходе обратный вызов onCheckboxClick()
имеет доступ как к классу this
и к элементу checkbox (в качестве первого параметра), который был бы this
в типичном onCheckboxClick()
события click
. Недостатком использования captureThis()
является то, что вы теряете безопасность типа TypeScript для обратного вызова, поэтому TypScript не может помешать вам испортить подпись функции обратного вызова.