Предлагаю читателям «Хабрахабра» вольный перевод статьи «Constant confusion: why I still use JavaScript function statements» от Билла Суро (Bill Sourour).

В далеких 90-х, когда я только изучал JavaScript, мы пытались писать «Hello World» с помощью оператора function. Примерно так:

function helloWorld() {
  return ‘Hello World!’;
}

В настоящее же время крутые ребята пишут функцию “Hello World” вот так:

const helloWorld = () => 'Hello World!';

Здесь используется стрелочная функция, добавленная в JavaScript в стандарте ES2015. Она выглядит чертовски прекрасно. Всё умещается в одну строку. Так кратко. Так замечательно.

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

Когда я впервые их увидел, я выглядел примерно как Фрай из Футурамы

image
Даже если учесть, что Babel бесплатен

Так вот, спустя 20 лет изучения JavaScript и после начала использования ES2015 в некоторых проектах, как думаете, каким же образом я напишу Hello World сегодня? Вот так:

function helloWord() {
  return ‘Hello World!’;
}

После того как я показал вам новый способ, вы едва ли станете даже смотреть на код “старой школы”, представленный выше.

Целых 3 строчки на такую маленькую простую функцию?! Зачем здесь столько лишних символов?

Я знаю о чем вы думаете:

image
Ни у кого нет времени на это!

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

Надеюсь из этой цитаты Мартина “Дяди Боба” станет понятно, зачем же я это делаю.

Отношение времени, потраченного на чтение кода, по отношению ко времени, потраченному на его написание, составляет 10 к 1. Мы постоянно читаем наш старый код во время работы над новым кодом.

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

Роберт Мартин: Чистый код: создание, анализ и рефакторинг

Оператор function имеет 2 явных преимущества по сравнению со стрелочными функциями.

Преимущество №1: Ясность намерения


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

Взгляните на это:

const maxNumberOfItemsInCart = ...;

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

const maxNumberOfItemsInCart = 100;

… так и:

const maxNumberOfItemsInCart = (statusPoints) => statusPoints * 10;

Если же вы используете оператор function, то никакой двусмысленности уже не будет.

Взгляните на:

const maxNumberOfItemsInCart = 100;

… против:

function maxNumberOfItemsInCart(statusPoints) {
  return statusPoints * 10;
}

Намерения программиста ясны с самого начала строки.

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

Я вас понимаю. Краткая запись все еще выглядит довольно привлекательно.

На самом деле, если бы это являлось моей единственной причиной, то я бы смог убедить себя, что читабельностью можно пожертвовать.

Но все же это не единственная моя причина.

Преимущество №2: Порядок объявления == Порядок выполнения


В идеальном случае, я хотел бы объявлять мой код примерно в том порядке, в котором я хочу, чтобы он выполнялся.

Но при использовании стрелочных функций это будет являться препятствием для меня: любое значение, объявленное через const, недоступно до того момента, пока до него не дойдет исполнение кода.

image
Приготовьтесь к куче тарабарщины, которая должна доказать (надеюсь), что я разбираюсь в том, о чем говорю

Единственная вещь, которую вы должны уяснить в том, что написано ниже, это то, что вы не можете использовать const до того, как вы объявите её.

Данный код вызовет ошибку:

sayHelloTo(‘Bill’);
const sayHelloTo = (name) => `Hello ${name}`;

Все потому что в тот момент, когда движок JavaScript считает код, то он сделает привязку для функции «sayHelloTo», но не проинициализирует её.

Все объявления в JavaScript привязываются на ранней стадии, но инициализируются они в разное время. Другими словами, JavaScript привязывает объявление константы «sayHelloTo» — считывает ее, перемещает наверх и выделяет место в памяти под ее хранение — но при этом не устанавливает ей какого-либо значения до того момента, пока не дойдет до нее в процессе выполнения.

Время между привязкой «sayHelloTo» и ее инициализацией называется «временная мертвая зона» (temporal dead zone — TDZ).

Если вы используете ES2015 прямо в браузере, не переводя код в ES5 с помощью Babel, представленный ниже пример также выдаст ошибку:

if(thing) { 
  console.log(thing);
}
const thing = 'awesome thing';

Если же заменить здесь const на var, то мы не получим ошибки. Дело в том, что переменные инициализируются со значением undefined сразу во время привязки, в отличие от констант. Но что-то я отвлекся…

Оператор function, в отличие от const, не страдает от проблемы TDZ. Данный код будет валидным:

sayHelloTo(‘Bill’);
function sayHelloTo(name) {
  return `Hello ${name}`;
}

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

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

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

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

По факту, разве не будет прекрасно, если мы предоставим краткую сводку небольшой части нашего API в начале нашего кода? С оператором function мы легко можем это сделать.

Посмотрите на этот (отчасти выдуманный) модуль для корзины товаров…

export {
  createCart,
  addItemToCart,
  removeItemFromCart,
  cartSubTotal,
  cartTotal,
  saveCart,
  clearCart,
}
function createCart(customerId) {...}
function isValidCustomer(customerId) {...}
function addItemToCart(item, cart) {...}
function isValidCart(cart) {...}
function isValidItem(item) {...}
...

Со стрелочными функциями он будет выглядеть как-то так:

const _isValidCustomer = (customerId) => ...
const _isValidCart = (cart) => ...
const _isValidItem = (item) => ...
const createCart = (customerId) => ...
const addItemToCart = (item, cart) => ...
...

export {
  createCart,
  addItemToCart,
  removeItemFromCart,
  cartSubTotal,
  cartTotal,
  saveCart,
  clearCart,
}

А теперь представьте, что это большой модуль с огромным количеством небольших внутренних функций. Что вы предпочтете в таком случае?

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

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

Так что же насчёт стрелочный функций?


Да. Они все еще прекрасны.

Я обычно использую стрелочные функции для передачи небольшой функции в качестве значения для функции уровнем выше. Я использую их с promise, с map, с filter, с reduce. Именно здесь они будут являться прекрасным выбором.

Вот некоторые примеры:

const goodSingers = singers.filter((singer) => singer.name !== 'Justin Bieber');
function tonyMontana() {
  return getTheMoney().then((money) => power)
                                     .then((power) => women);
}

На этом я пожалуй и закончу. Спасибо за чтение!
Поделиться с друзьями
-->

Комментарии (88)


  1. comerc
    24.08.2016 18:34
    -17

    CoffeeScript снимает вопрос. Но мода прошла, к сожалению.


    1. stardust_kid
      24.08.2016 19:54
      +19

      IE6 снимает вопрос еще более радикально. Но мода прошла, к сожалению.


      1. comerc
        24.08.2016 23:22
        -3

        ClojureScript еще более радикально снимает вопрос. И мода побоку.


        1. stardust_kid
          25.08.2016 00:09
          +7

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


          1. bentall
            25.08.2016 10:00
            +1

            ClojureScript устареет не раньше Clojure, и да, Clojure+ClojureScript это альтернативная Node.js реализация идеи «пишем клиент и сервер на одном языке»


            1. stardust_kid
              25.08.2016 11:57
              +2

              А откуда у вас уверенность, что Clojure не забросят в 2018? Я не хочу говорить ничего плохого про этот язык, но вдруг.
              На стороне чистого js — огромная экосистема, сообщество и монополия на выполнение в браузере. Я считаю, можно быть уверенным, что в 2018 ES продолжат активно развивать.


              1. seryh
                25.08.2016 16:44

                Проблема только в том что на чистом js мало кто пишет сложные приложения. А проблема того что в 2018 забросят сегодня популярный очередной фреймворк, актуальна для js в первую очередь.


                1. stardust_kid
                  25.08.2016 17:00
                  +2

                  Вы смешиваете язык и фреймворк. Приложения на React, Angular и т. д. как правило пишут на чистом js. Замена фреймворка — это намного меньший напряг, чем замена языка. Компании будет легче найти специалиста со знанием более мейнстримового языка.


                  1. seryh
                    25.08.2016 17:08
                    +1

                    В случае JavaScript к сожалению все несколько сложнее. Фреймворки для него отличаются идеологически. Чтоб далеко не ходить: React+redux — ФП идеология, Angular — ООП. Возможно субъективно, но для меня проще начать писать на coffeescript или TS чем сменить фреймворк который поставит с ног на голову все то к чему я привык.


                    1. megahertz
                      29.08.2016 06:23

                      ООП это скорее к Ember чем к Angular.


                  1. k12th
                    25.08.2016 17:22

                    Написанное Angular назвать чистым JS сложно (хотя бывают приятные исключения). React и вовсе на jsx пишут, однако, если есть flux/redux части, то там довольно чистенько.


                    1. stardust_kid
                      25.08.2016 17:30
                      -1

                      В Angular они переписали половину стандартной библиотеки, увлеклись, наверное. Но в нем можно теоретически писать и на чистом js.
                      В React на jsx стоит писать только вывод html.


                      1. k12th
                        25.08.2016 17:32
                        -1

                        Просто начинали-то писать во времена IE8, да и npm еще только зарождался, отсюда и всякие isUndefined и прочая ересь. Теоретически можно, но чаще всего пишут так, что лучше б на jQuery.


                        В React на jsx стоит писать только вывод html.

                        Бесспорно. Проблема в том, что «лучшие практики» это не «практики по умолчанию»:)


                  1. comerc
                    25.08.2016 17:35

                    Наоборот! Хочется конвертнуть CoffeeScript — JS? Легко! На ClojureScript вообще можно вытворять чудеса — готовим перевод статьи с практическим примером.


                    А вот конвертора Angular-React я что-то не припомню :)



  1. f0rk
    24.08.2016 19:23
    +5

    Еще один момент, который нужно учитывать:

    (() => { return this.foo }).call({foo:1})
    // => undefined
    (function () { return this.foo }).call({foo:1})
    // => 1
    


    1. sugadu
      24.08.2016 19:48
      +6

      Это фича, а не баг. Чтобы не было таких костылей: var self = this;


      1. andreysmind
        24.08.2016 19:58
        +21

        это даже by design — стрелочные функции не создают свой this как раз, чтобы не было все этих self, that, thi$, _this.


        1. f0rk
          25.08.2016 11:03
          +6

          Видимо кто-то меня не понял :) Я хотел обратить внимание на то, что стрелочные функции — это не сахар для function () {}.bind(this), как некоторые думают, а отдельные сущности с собственными static and runtime semantics.


          1. andreysmind
            25.08.2016 11:08
            +1

            Ну да, так и есть. Почему кто-то заминусовал — загадка.


      1. f0rk
        25.08.2016 10:53
        +3

        Я в курсе, что это фича, это не значит что такое поведение не нужно иметь в виду.


    1. mannaro
      25.08.2016 01:01

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


      1. f0rk
        25.08.2016 10:54

        Не выкинет в известных мне окружениях, в браузере например, в global scope this == window


        1. lxmarduk
          25.08.2016 16:45

          При «use strict» this не будет равен window.


          1. f0rk
            26.08.2016 07:57

            Ухты… неожиданно. Но стрелочных функций это не должно касаться


      1. f0rk
        25.08.2016 11:09
        +1

        http://www.ecma-international.org/ecma-262/6.0/#sec-lexical-environments

        A global environment is a Lexical Environment which does not have an outer environment. The global environment’s outer environment reference is null. A global environment’s EnvironmentRecord may be prepopulated with identifier bindings and includes an associated global object whose properties provide some of the global environment’s identifier bindings. This global object is the value of a global environment’s this binding. As ECMAScript code is executed, additional properties may be added to the global object and the initial properties may be modified.

        То есть this равный undefined по стандарту невозможен.


    1. Odrin
      25.08.2016 12:27
      +1

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


    1. lucky_libora
      25.08.2016 13:31

      какой смысл использовать call в ES2015?


      1. f0rk
        25.08.2016 14:28

        В большинстве случаев rest+spread+arrow functions позволяют избежать традиционных для es5 паттернов с bind, call и apply, но иногда нужно контекст передать.


  1. RubaXa
    24.08.2016 19:46
    +3

    Ну, порядок объявления, это очень спорно (да и большинство код-стайлов запрещают это) и пример странный, ведь можно просто:

    export const sum = (a, b) => a + b;
    

    (хотя сам объявляю через function, читается легче и подсветка, да)

    Ещё можно добавить пункт «Читабельность callstack'а при профайлинге/ошибке»


  1. andreysmind
    24.08.2016 19:52
    +7

    Прекрасно. Хипстеры в своем репертуаре — ломанулись использовать новые «крутые» штуки вместо «некрутых» старых, но забыли разобраться в том, что это на самом деле разные вещи.


  1. bingo347
    24.08.2016 20:07

    А мне вот лично не хватает именованных стрелочных функций
    Именованные функции очень упрощают отладку


    1. Apathetic
      24.08.2016 21:39

      Ни разу не сталкивался с ситуацией, когда имя у функции упрощало бы отладку. Можно пример?


      1. k12th
        24.08.2016 22:07

        В стектрейсе приятно видеть осмысленное имя функции.


        1. Apathetic
          24.08.2016 22:15

          Ясно. В хроме с этим проблем нет (месяца два примерно).
          image

          Еще пример:
          image

          Но, повторюсь, так только в хроме.


          1. RubaXa
            24.08.2016 22:57
            +1

            Не, проблема правильно выглядит так (и теперь даже хуже):

            Код курильщика и здорового человека
            <script>
            	(function no() {
            		const exec = (fn) => fn();
            		const error = () => {throw ':('};
            		const queue = (fn) => fn()
            		queue(() => exec(error));
            	})();
            </script>
            <script>
            	(function yes() {
            		const exec = (fn) => fn();
            		const error = () => {throw ':)'};
            		const queue = (fn) => fn()
            
            		queue(function worker() {
            			exec(error)
            		});
            	})();
            </script>


            1. Maxxon
              24.08.2016 23:32

              Ну так у вас анонимная функция же (стрелочная) в первом примере и не анонимная во втором. Причем даже в этом случае хром не пишет в стеке anonymous function, пишется имя функции по стеку выше.


              Теперь сравните с этим


              (function yes() {
                  const exec = (fn) => fn();
                  const error = () => {throw ':('};
                  const queue = (fn) => fn()
              
                  queue(function() {
                      exec(error)
                  });
              })();


              1. RubaXa
                24.08.2016 23:45

                Так мы об это и пишем, что во втором случае стек читается лучше, а в первом нет, даже хуже, вводит в заблуждение, раньше такого не было, например FF в этом же тесте напишет `anonymous`, что абсолютно правильно.


          1. k12th
            25.08.2016 12:18

            Скажем, не в хроме, а в стандарте так — когда кладем анонимную функцию в переменную, ее name становится таким же, как имя переменной. ЕМНИП, аналогично, когда ее кладем в свойство объекта. В V8 это реализовано, в остальных — не помню.


            Но когда мы пишем arr.map((val) => val.someProperty * 2), она так и остается анонимной.


  1. eme
    24.08.2016 20:12
    +31

    Почему я все еще использую молоток вместо отвертки:
    1. Ясность намерений. Когда смотришь на человека с молотком, абсолютно понятны его намерения. Не возникает вопросов, в какую же сторону он будет крутить.
    3. Нужен всего лишь один молоток для всех видов шлицов. Очень удобно.


  1. G-M-A-X
    24.08.2016 21:23
    -8

    +1
    Мне сразу это показалось черти-чем.

    Странная цель написать меньше строк кода в ущерб читаемости.
    А добавляется еще и бажность.


    1. bromzh
      24.08.2016 22:13
      +5

      А добавляется еще и бажность.

      Если не уметь пользоваться — то да.


      в ущерб читаемости.

      Спорно. Мне легче понять такое


      Rx.Observable.fromEvent(worker, 'message')
          .map(ev => ev.data * 10)
          .buffer(Rx.Observable.interval(500))
          .where(x => x.length > 0)
          .map(x => x.length);

      Чем такое


      Rx.Observable.fromEvent(worker, 'message')
          .map(function (ev) { return ev.data * 1; })
          .buffer(Rx.Observable.interval(500))
          .where(function (x) { return x.length > 0; })
          .map(function (x) { return x.length; });


      1. k12th
        24.08.2016 22:26
        +1

        Автор оригинала предлагает как раз первый вариант.


      1. G-M-A-X
        24.08.2016 23:20
        -6

        > Если не уметь пользоваться — то да.

        Я не пользовался. Сужу по статье.

        > Спорно. Мне легче понять такое

        Это какие-то анемичные функции.

        Потом, первый вариант затруднит рефакторинг: увеличение тела функции, выделение функции.


        1. oxidmod
          24.08.2016 23:29

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


          1. G-M-A-X
            25.08.2016 09:32
            -5

            >да вы унас просто человек «не читал, но осуждаю»

            Я как раз читал, о чем и указал…
            В статье показана «несовместимость» с обычными функциями.
            Это только добавляет сложности.
            Наплодили сущностей.

            П.С.
            Я например использую goto в PHP и ничего страшного.


      1. lucky_libora
        25.08.2016 13:37

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

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


  1. sugadu
    24.08.2016 21:38
    -1

    export {
    createCart,
    addItemToCart,
    removeItemFromCart,
    cartSubTotal,
    cartTotal,
    saveCart,
    clearCart,
    }

    Задачка из разряда «найдите лишнее слово». Подсказка: используйте принцип единой ответственности.


  1. Spiritschaser
    24.08.2016 22:19
    -10

    Спасибо за перевод!
    Школота прочитает, вдохновится, и JavaScript НЕ постигнет участь Perl!


  1. vdonich
    24.08.2016 23:38
    +10

    Данный код вызовет ошибку:

    > sayHelloTo(‘Bill’);
    > const sayHelloTo = (name) => `Hello ${name}

    (сдерживает себя, чтоб не начать ругаться матом)

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


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

    Мне кажется, что «всплывание» объявлений — это злобное зло, за которое надо бить линейкой по рукам, хотя бы за то, что вот такой код сработает:

    (function() {
      doFoo('bar');
      return;
      function doFoo(a) { console.log(a); }
    })();
    


    А представьте теперь, если у вас использование и объявление разнесено например, на 100 строк. И так несколько раз.


    1. vintage
      24.08.2016 23:43
      -5

      Рекомендую вам всё же не заниматься BDSM с канцелярскими предметами, а присмотреться к довольно неплохому принципу "сначала пишем, что делаем, а потом уже — как".


      1. babylon
        25.08.2016 02:33

        +1000. Я постоянно откладываю написания кода:) Потому, что правильные идеи не приходят следом одна за другой. Ну если только к гениям.


      1. impwx
        25.08.2016 11:19
        +1

        Вспомните школу. В любой задаче — сначала «дано», потом «решение», не наоборот.


        1. vintage
          25.08.2016 12:53
          -1

          Вы ещё детский сад вспомните. Открываем книгу — оглавление в начале, а не в конце. Открываем любой сайт — меню сверху или слева, а не снизу и справа.


          https://ru.wikipedia.org/wiki/Структурное_программирование#.D0.9C.D0.B5.D1.82.D0.BE.D0.B4_.C2.AB.D1.81.D0.B2.D0.B5.D1.80.D1.85.D1.83_.D0.B2.D0.BD.D0.B8.D0.B7.C2.BB


          1. impwx
            25.08.2016 13:41
            +2

            Метод «сверху вниз» говорит о порядке декомпозиции — от общего к частному, а не о порядке расположения кода в файле. Если же мы говорим о разработке настоящего приложения, то его код будет разбит на отдельные модули, для которых расположение «ниже» или «выше» относительно друг друга не имеет смысла. Однако, вы когда-нибудь видели, чтобы инструкции import или using были после кода?


            1. vintage
              25.08.2016 20:59

              Метод «сверху вниз» говорит о порядке декомпозиции — от общего к частному, а не о порядке расположения кода в файле.

              О порядке написания кода.


              код будет разбит на отдельные модули

              Каждую функцию в отдельный модуль?


              Однако, вы когда-нибудь видели, чтобы инструкции import или using были после кода?

              Я видел очень много кода, где функция А вызывает функцию Б, а функция Б, вызывает функцию А. В каком порядке их предложите объявлять?


              1. impwx
                25.08.2016 23:02
                +1

                Каждую функцию в отдельный модуль?

                Вы удивитесь, но так действительно многие делают.

                В каком порядке их предложите объявлять?

                Порядок объявлений не должен играть роли, в отличие от порядка инструкций. К сожалению, в JS разница между этими понятиями размыта, поэтому объявлять функции через const или var нужно с осторожностью.


    1. sugadu
      24.08.2016 23:44

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


    1. ganqqwerty
      24.08.2016 23:53
      +3

      Как же сложно любить людей, которые активно используют хойстинг в своих js-творениях. Правда ведь, это абсолютно флибустьерская фича, даже jshint на нее ругается.


      1. jsfun
        25.08.2016 00:12
        -1

        Погодите, ведь хойстинг это часть стандарта и игнорировать его — тоже неверно. Самый яркий пример — сносить стандартные методы, типа различных проверок isArray() и им подобных, удобнее вниз файла. Например у Вас есть длинный класс с кучей методов и, если снести такие проверки вниз, то файл становиться читабельнее, потому как есть методы и свойства, описывающее логику и предназначение класса, которые, будучи вынесены выше, помогут быстрее понять суть.


        1. vdonich
          25.08.2016 01:01
          +1

          Не все, что является частью стандарта, необходимо использовать.
          Наглядный пример — особое поведение == (https://dorey.github.io/JavaScript-Equality-Table/)
          Ввиду чего зачастую предпочитают использовать ===


          1. jsfun
            25.08.2016 01:33
            +1

            Я и не говорил, что его необходимо использовать, но необходимо понимать, как это работает и быть способным это применить к месту.

            Полностью согласен с примером о строгом сравнении.


            1. vintage
              25.08.2016 08:58
              +2

              А я вот не согласен, нестрого сравнивать с null — куда удобнее, чем строго сравнивать с undefined, а потом с null.


              1. stardust_kid
                25.08.2016 09:45
                +1

                В таком случае стоит использовать Boolean


                1. vintage
                  25.08.2016 10:43

                  И каким образом вам тут поможет Boolean?


                  1. stardust_kid
                    25.08.2016 17:03
                    +1

                    let a;
                    console.log(Boolean(a)) // false;
                    a = null;
                    console.log(Boolean(a)) // false;
                    


                    1. vintage
                      25.08.2016 20:59

                      a = 0
                      console.log(Boolean(a)) // false;


                      1. stardust_kid
                        25.08.2016 23:33

                        А разве это всегда плохо?


                        1. vintage
                          26.08.2016 05:04
                          +1

                          Это, очевидно, не то, что требуется.


              1. babylon
                25.08.2016 13:20

                undefined подставляет сам компилятор.


                1. vintage
                  25.08.2016 13:39

                  Вы это к чему?


    1. webkumo
      25.08.2016 13:12

      Имхо, для функций, которые являются «мясом» для данного кода — самое место внизу. Т.е. я предпочитаю структуру кода в виде блоков: объявления, внешний интерфейс (если есть), инициализация, рабочий код, внутреннее API.

      В итоге я сразу вижу с чем придётся работать, сразу вижу, что происходит (при правильном именовании функций это нетрудно), а уже если заинтересуют детали — тогда лезу в конкретную функцию, посмотреть что происходит в ней.

      PS В различных языках блоки могут сливаться или видоизменяться (в Java — объявления + инициализация переменных класса, объявления + инициализация переменных объекта, конструкторы, паблик API, наследуемое API, приватное API).


  1. boolive
    24.08.2016 23:53

    Вы правда изучали JavaScript в 90-х годах?


    1. jsfun
      25.08.2016 00:02

      Автор написал вначале статьи, что это лишь перевод


  1. Finom
    25.08.2016 01:53
    +6

    1. Читаемость кода всегда выжнее скорости и краткости, так что я согласен.
    2. А это — торба. Использование функций до объявления — антипаттерн. Это та фича языка, которая должна уйти следом за ``with``. Да, это было удобно когда мы говнокодили тысячи строк в одном файле, но сейчас другое время.


  1. bustEXZ
    25.08.2016 08:31
    -3

    eslint говорит что формат функций вида

    function fnName () {}
    
    слишком стар, надо использовать:
    const fnName = function () {}
    


    И я пока склоняюсь ко второму варианту.


    1. ganqqwerty
      25.08.2016 09:54

      объясните новичку, чем различаются эти два формата?

      function fn1 () {}
      var fn2 = function () {}
      


      Мое ограниченное понимание таково:
      1) у fn1 есть имя, а fn2 — анонимная. как это используется и на что влияет, я не очень понимаю
      2) fn1 можно вызвать до объявления, а fn2 — выдаст undefined is not a function (если с const — то и вовсе ошибку при обращении к переменной)


      1. andreysmind
        25.08.2016 10:00

        можно почитать например
        https://www.sitepoint.com/function-expressions-vs-declarations/
        там объясняется разница между такими видами объявлений.


      1. bustEXZ
        25.08.2016 11:05
        -1

        Я знаю разницу между ними. И т.к. я стараюсь разрабатывать соблюдая правила, у меня методы не вызываются до их определения. И я не новичек...(


        1. ganqqwerty
          25.08.2016 12:14

          нене, это я новичок и прошу чтобы объяснили мне


      1. f0rk
        25.08.2016 12:52
        +4

        у fn1 есть имя, а fn2 — анонимная.

        Не совсем, к ней можно обратиться по имени и у неё есть поле «name», так что не анонимная
        fn2.name // => "fn2"
        

        Разница в том, что первый вариант — это function declaration, когда код будет выполняться, сначала будут созданы все функции объявленные таким образом в текущем scope а потом уже будут выполняться выражения.
        Второй вариант — это function expression, в этом случае функция создается в момент выполнения выражения и является его значением.
        Эти варианты по разному разбираются парсером и имеют разную семантику.
        Чтоб прочувствовать разницу, поиграйтесь с этим кодом в консоли:
        !function t() { console.log('t') }() // immediate function invocation
        function t() { console.log('t') }() // throws a SyntaxError
        


        1. ganqqwerty
          25.08.2016 16:09

          про поле name вообще сюрприз — спасибо большое


  1. Herethic
    25.08.2016 09:58
    +1

    Я согласен с вашим пониманием читаемости кода, но не совсем согласен с вашим пониманием самого кода.
    В ваших примерах вы упорно сравниваете function declaraion (function a() {}) с function expression (var a = function() {}).
    Точно так же нельзя сделать так:

    a();
    var a = function() {
      console.log('Hello');
    }
    

    Я думаю, большинство итак знает почему, но, все же, поясню:
    перед запуском кода браузер добавит переменную «a» в область видимости, она станет доступна из любой части кода (да-да, в отличие от const), но значение ей не присвоит и мы получим TypeError: not a function. Вывод: делает невозможным вызов функции до описания отнюдь не es2015, а сам способ объявления.


  1. murzilka
    25.08.2016 13:04
    +1

    Взгляните на это:

    const maxNumberOfItemsInCart = ...;

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


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


  1. skyline405
    25.08.2016 16:43
    +2

    Стрелочные функции целесообразно использовать в конструкциях типа array.forEach( item => {} );
    Каждому инструменту своя область применения, иначе это уже похоже на «откручивание гайки молотком».


  1. Dromok
    02.09.2016 14:02
    -1

    По-моему стрелочная функция выглядит ужасно и не понимаю восторга автора по отношению к ней. Да, меньше кода, но зачем? Читаемость кода всегда была и будет на первом месте.


    1. bromzh
      02.09.2016 14:50

      У стрелочных функций другая семантика.


    1. Shannon
      02.09.2016 23:10

      У стрелочных функций сохраняется контекст this, то есть это не просто те же функции, только пишутся по другому
      Так же они сокращают код, когда надо всего лишь сделать

      [' aa ', ' bb '].map(a => a.trim()) // ['aa', 'bb']
      

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