Использование jQuery $ (this) с функциями стрелок ES6 (лексическая привязка this)

129

Прекрасно использовать стрелочные функции ES6 с лексической thisпривязкой.

Однако минуту назад я столкнулся с проблемой, используя его с типичной привязкой кликов jQuery:

class Game {
  foo() {
    self = this;
    this._pads.on('click', function() {
      if (self.go) { $(this).addClass('active'); }
    });
  }
}

Вместо этого используйте стрелочную функцию:

class Game {
  foo() {
    this._pads.on('click', () => {
      if (this.go) { $(this).addClass('active'); }
    });
  }
}

А затем $(this)преобразуется в закрытие типа ES5 (self = this).

Можно ли заставить Traceur игнорировать "$ (this)" для лексической привязки?

JRodl3r
источник
это кажется прекрасным примером того, когда не следует использовать стрелочную функцию, поскольку .on()она действительно имеет thisполезное для вас значение. Для меня гораздо яснее thisссылаться на цель события, чем передавать событие и находить цель вручную. Я не очень много играл со стрелочными функциями, но кажется, что переходить туда и обратно с анонимными функциями было бы непросто.
robisrob

Ответы:

194

Это не имеет ничего общего с Traceur и отключением чего-либо, просто так работает ES6. Это конкретная функциональность, которую вы просите использовать =>вместо function () { }.

Если вы хотите написать ES6, вам нужно писать ES6 все время, вы не можете включать и выключать его в определенных строках кода, и вы определенно не можете подавить или изменить способ =>работы. Даже если бы вы могли, вы бы просто столкнулись с какой-то причудливой версией JavaScript, которую понимаете только вы и которая никогда не будет работать правильно за пределами вашего настроенного Traceur, что определенно не является целью Traceur.

Способ решения этой конкретной проблемы - не использовать thisдля получения доступа к элементу, по которому щелкнули мышью, а вместо этого использовать event.currentTarget:

Class Game {
  foo(){
    this._pads.on('click', (event) => {
      if(this.go) {
        $(event.currentTarget).addClass('active');
      }
    });
  }
}

jQuery обеспечивает event.currentTargetименно это, потому что даже до ES6 jQuery не всегда может наложить a thisна функцию обратного вызова (т. е. если она была привязана к другому контексту через bind.

Meagar
источник
4
Обратите внимание: хотя он расходится только для делегированных .onвызовов, thisна самом деле event.currentTarget.
loganfsmyth
Это нормально? Как отметил loganfsmyth, this== event.currentTarget. Нет?
Леон Пеллетье
3
@ LéonPelletier No. thisи event.currentTargetявляются такими же, если только они thisне привязаны к охватывающей области, как с функцией стрелки ES6.
meagar
8
если вы хотите сохранить код, вы также можете его деструктурировать: ({currentTarget}) => {$ (currentTarget) .addClass ('active')}
Фрэнк
55

Связывание событий

$button.on('click', (e) => {
    var $this = $(e.currentTarget);
    // ... deal with $this
});

петля

Array.prototype.forEach.call($items, (el, index, obj) => {
    var $this = $(el);
    // ... deal with $this
});
Райан Хуанг
источник
Ищете $jQueryElements.each()? jQuery фактически отправляет некоторые аргументы каждому объекту, поэтому вам это вообще не нужно => Это$(...).each((index, el) => console.log(el))
jave.web
53

Другой случай

Главный ответ правильный, и я проголосовал за него.

Однако есть еще один случай:

$('jquery-selector').each(() => {
    $(this).click();
})

Может быть исправлено как:

$('jquery-selector').each((index, element) => {
    $(element).click();
})

Это историческая ошибка в jQuery, которая ставит индекс вместо элемента в качестве первого аргумента:

.each (функция)


Тип функции : Function( Integer index, Element element )
функция, выполняемая для каждого совпадающего элемента.

См. Https://api.jquery.com/each/#each-function

Тайлер Лонг
источник
1
То же самое для таких функций, как$('jquery-selector').map
Николас
Это было очень полезно для меня в таких вещах, как $ ('jquery-selector'). Filter и т. Д. ... спасибо, что сделали это так ясно.
Р Джонс,
8

(Это ответ, который я написал для другой версии этого вопроса, до того, как узнал, что это был дубликат этого вопроса. Я думаю, что ответ объединяет информацию довольно четко, поэтому я решил добавить его как вики сообщества, хотя он в основном отличается формулировка других ответов.)

Вы не можете. Это половина функций стрелок, они закрываются, thisвместо того, чтобы иметь свои собственные, которые задаются способом их вызова. Для случая использования в вопросе, если вы хотите thisустановить jQuery при вызове обработчика, обработчик должен быть functionфункцией.

Но если у вас есть причина для использования стрелки (возможно, вы хотите использовать thisто, что она означает за пределами стрелки), вы можете использовать e.currentTargetвместо нее, thisесли хотите:

class Game {
  foo(){
    this._pads.on('click', e => {                   // Note the `e` argument
      if(this.go) {
        $(e.currentTarget).addClass('active');      // Using it
      }
    });
  }
}

Объект currentTargetсобытия совпадает с тем, что устанавливает jQuery thisпри вызове обработчика.

TJ Crowder
источник
5

Как сказал Мигер в своем ответе на тот же вопрос, если вы хотите писать ES6, вам нужно писать ES6 все время ,

поэтому, если вы используете стрелочную функцию ES6 : (event)=>{}, тогда вы должны использовать $(event.currentTarget)вместо $(this).

вы также можете использовать более приятный и чистый способ использования currentTarget как ({currentTarget})=>{},

Class Game {
  foo(){
    this._pads.on('click', ({currentTarget}) => {
      if(this.go) {
        $(currentTarget).addClass('active');
      }
    });
  }
}

первоначально эта идея была прокомментирована Рицци Фрэнком в ответе @ Meager , и я счел это полезным, и я думаю, что не все люди прочитают этот комментарий, поэтому я написал его как еще один ответ.

Haritsinh Gohil
источник