Как стать автором
Обновить

Комментарии 11

Самое главное забыли - в ряде случаев (когда они появились - практически во всех браузерах так было) у стрелочной функции нет name, `const fn = () => false`, fn.name undefined|''. То есть в стек-трейсе будет anonymous, и если код минифицирован, найти причину бага очень сложно. Но в современных браузерах name назначается по имени константы. Это все еще приходится держать в голове. В определенных случаях и современные браузеры не назначат - `const fn = (() => () => { return false })()`.

Также стрелочные функции, назначенные как константы, нельзя использовать выше их объявления, в отличие от именованных. То есть в ряде случаев придется жонглировать местами объявления.

Еще натыкался на то, что стрелочные методы классов в определенных версиях транспиляторов не декорируются, так как на выходе код совершенно другой, чем у нестрелочного метода.

Фича в целом отличная, но об ограничениях нужно помнить

Спасибо за уточнение! Действительно пропустил эти моменты.

Ну вообщето ссылаться на имя константы можно и до объявления стрелочной функции, в теле другой функции, если эта функция будет вызвана уже после объявления стрелочной. Temporary dead zone.

Спасибо за статью! Освежил в памяти эти моменты!

Надеюсь, теперь фраза «контекст this классической функции связан с местом вызова» стала понятна, а еще стало понятно, почему в ES6 было принято решение внедрить инструмент, который мог бы избавить разработчиков от необходимости использовать bind или присваивание нужного this в переменную. Один из минусов метода bind заключается в том, что он создает копию функции, где подменяет this, а стрелочная функция должна была оптимизировать расход памяти на копирование.

потом читаю

Стрелочную функцию лучше не использовать как метод в объектах и классах

Простите, но я не понял. Именно в классах стрелки избавляют от необходимости bind или self, но лучше стрелки не использовать в классах?

Здравствуйте!

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

Спасибо за статью, про методы класса и стрелочную функцию нигде раньше не встречал информации

Лучше не использовать стрелочную функцию при объявлении метода или не лучше. Когда нужен контекст объекта/класса, используем стрелочную, не нужен — не используем.

Как можно давать общую рекомендацию?!

В данном случае this равен undefined, потому что это происходит внутри класса, в котором строгий режим включен автоматически.

Не автоматически. Class Definition работает только в strict mode. Нет ни единой возможности использовать Class Definition вне strict mode.

 

Давайте попробуем решить эту проблему так, как она решалась до выхода ES6. this, который мы хотим использовать в анонимной функции, нужно записать в переменную и использовать ее вместо this

Еще можно было бы вспомнить о том, что в Array.prototype.forEach можно передавать this явным образом.

 

Контекст this в классических функциях вычисляется в момент вызова.

Правильнее сказать, что this устанавливается только в одном случае - когда вычисление MemberExpression приводит к Property Accessors вычисление которого приводит к Reference Record где [[Base]] будет установлен в обьект, а [[ReferencedName]] в identifier name нашей функции. Что позднее приведет к связыванию this с [[Base]]

Говоря простым языком - существует только один случай неявного связывания this - это случай вызова regular function в дот нотации: obj.someFunc() или obj['someFunc'] где this будет связан с obj (той ссылкой что идет перед точкой)

В случае arrow function - вне зависимости от способов вызова с this никаких операций не будет произведено вообще

В случае вызова regular function не в дот нотации - this будет установлен либо в global object либо в undefined в зависимости от текущего strict mode.

 

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

Тут какая то каша. Если Вы про расход ресурсов на создание bind function то bind создает exotic object с минимумом property-s. Такая JSBoundFunction в V8 на 4 байта меньше чем ArrowFunction. Какой расход памяти должна была оптимизировать ArrowFunction я не вполне понял. ArrowFunction глубоко по барабану что твориться вокруг нее.

 

В стрелочных функциях this устроен иначе. 

В стрелочных функциях this никак не устроен. Если происходит evaluation arrow function то согласно спецификации вычисление this завершается на второй строчке:
If thisMode is lexical, return unused // thisMode === lexical это признак стрелочной функции.

 

Еще можно сформулировать так — в стрелочных функциях this сохраняет значение this окружающего контекста в момент создания.

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

 

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

Нет не был. И ни к чему он не был привязан. ArrowFunction - это обычная Closure в теле которой this является обычным идентификатором, вычисление которого происходит ровно точно так же как и все прочие идентификаторы. Вы просто создаете банальное замыкание.

   

Стрелочную функцию лучше не использовать как метод в объектах и классах
Начнем с объекта. Если мы напишем следующий код, то он не будет работать:
[...]
В консоли мы получим ошибку:
[...]
А если мы напишем метод, объявленный стандартным способом, то все будет работать исправно.
[...]

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

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

Вы несете чушь. Вы используете латеральную форму связывания обьекта, и сравниваете ее с классом, в котором выполняете констуртор. Вам никто не мешает с обьектом поступить ровно точно так же, то есть использовать конструктор для связывания обьекта.

function myObj(name) {
  this.name = name;
  this.sayName = () => {
       console.log(`Hi! Me name is ${this.name}`)
   }  
}
const john = new myObj('John')
john.sayName();

Догадываетесь почему вдруг метод sayName заявленный как arrowFunction стал прекрасно работать с this? Потому, что благодаря конструктору, мы создали банальное замыкание в результате чего this стал прекрасно доступным в arrow.

   

Дело в том, что у класса, в отличие от объекта в JS, есть собственный контекст,

Классы и обьекты с точки зрения их формирования - абсолютно идентичны. Нет никакой разницы между ними. Нет никаких собственных контекстов и прочих выдумок.

 

Но использовать стрелочную функцию как метод все же не стоит по другой причине. Когда вы создаете метод обычным способом, то он записывается в прототип класса, и когда вы создаете новый экземпляр, то он содержит ссылку на метод родителя, что экономит ресурсы. А если вы решили использовать стрелочную функцию, то она не будет записана в прототип, и будет копироваться каждый раз заново. Давайте убедимся в этом:

Я предлагаю Вам выполнить домашнее задание: найти минимум три способа определение метода при помощи arrow function таким образом, чтобы этот метод стал частью прототипа, а не частью самого созданного класса/обьекта.

Здравствуйте!

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

var a = 5;

class Obj {
  constructor() {
    this.a = 10;
  }
  
  method() {
    return () => console.log(this.a);
  }
}

const obj = new Obj()

const obj2 = {
  a: 20,
  method: obj.method
}

obj2.method()(); // 20

Никуда контекст стрелочной функции не привязывается, а вычисляется по цепочке лексических окружений, зафиксированной при объявлении. При вызове obj2.method() метод получит в контекст объект obj2, а стрелочная функция ссылаясь на внешнюю область видимости найдет этот контекст

Зарегистрируйтесь на Хабре, чтобы оставить комментарий