Кто знает о JS больше, чем один из его «отцов»? На HolyJS 2017 Piter приедет легендарный Дуглас Крокфорд, создатель JSON и автор множества инструментов JavaScript. В преддверии его выступлений в Петербурге публикуем перевод его выступления на .concat() 2015: The Better Parts — о том как использовать существующие языки программирования более эффективно и каким будет язык программирования будущего. Хотя с момента выступления прошло более года, доклад коснулся ряда «вечных» вопросов программирования, которые, мы уверены, будут актуальны и через 3-5 лет.



Путь к совершенству


Этот человек — известный авиатор и писатель Антуан де Сент-Экзюпери. Большинство его помнит за то, что он является автором замечательной детской книги «Маленький принц» (хотя книга эта не совсем детская). Помимо нее он написал много других книг, и в одной из них есть удивительная фраза: «Как видно, совершенство достигается не тогда, когда уже нечего прибавить, но когда уже ничего нельзя отнять».

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

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

Я думаю, что это относится и к языкам программирования.

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

Это удивительно противоречивое заявление. Есть множество людей, не желающих использовать лучший вариант. И происходит это из-за непонимания одной вещи: нам не платят за использование каждой функции языка. В конце проекта нет менеджера с портфелем, проверяющего: «А использовали ли вы двойное равенство»? Никто не заботится об этом. Нам платят за то, чтобы мы писали программы, которые хорошо работают и не содержат ошибок.

Когда «отсутствие ошибок» стало частью сделки? Оно всегда была ее частью. Просто мы очень редко достигаем этого. Легко забыть, что на самом деле отсутствие ошибок было первым требованием. Поэтому хороший язык программирования должен научить вас этому.

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

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

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

Я написал книгу о своем опыте использования JavaScript и кода JSLint — «JavaScript — сильные стороны». Возможно, вы слышали о ней. Она по-прежнему бестселлер, что редкость для книг по программному обеспечению. Большинство книг по программному обеспечению устаревают еще до выпуска, а эта по-прежнему актуальна. Все потому, что «сильные стороны» по-прежнему остаются сильными сторонами. Что хорошо, язык не изменился вообще.

Контраргументы


Теперь аргументы против использования «сильных сторон». Я бы хотел изложить их для вас:

  • Первый звучит так: «что хорошо, а что плохо — это просто вопрос мнения». Это неправда. В рамках поддержки JSLint я получаю сообщения об ошибках от людей со всего мира. Недавно я получил письмо от одной компании, которая провела две недели, пытаясь решить некую проблему. Выяснилось, что в одном месте была случайно добавлена точка перед знаком равенства. Никто не видел ее, потому что она выглядела корректно, но результат был плохим. Поэтому они спросили меня, могу ли я модифицировать JSLint, чтобы никто больше не страдал от подобной проблемы. Почему нет? Я делаю это уже много лет. Так что если вы используете JSLint, вы никогда не потратите впустую две недели на проблему такого рода. И это не мнение, это факт.

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

  • Я имею право использовать каждую функцию. Тема разговора изменилась. Неужели мы больше не говорим о том, как лучше всего писать программы. Мы сейчас говорим о наших правах? На этом аргументация, в конечном итоге, заканчивается. «Я имею право писать дерьмо», правда? Это не важно. Важно, что мы обязаны писать очень хорошо.

  • Мне нужна свобода самовыражения или «я художник и я самовыражаюсь, ставя точку с запятой в начале оператора, а не в конце». Соображения те же.

  • Мне нужно уменьшить количество нажатий клавиш. Мы предполагаем, что большую часть времени печатаем. И если бы мы могли найти способ компоновки программ с помощью нескольких нажатий клавиш, это сделало бы нас намного эффективнее. Но дело обстоит с точностью до наоборот. Главный растратчик времени — не набор символов, а взгляд в бездну с возгласом: «мой бог, что я сделал, почему это не работает». Вот куда мы тратим большую часть времени. Если бы я мог предложить вам схему, согласно которой увеличение количества нажатий клавиш в 10 раз могло бы сократить ошибки вдвое, это было бы огромной победой (к сожалению, у меня нет такой схемы).

  • «Это оскорбление предполагать, что я когда-либо ошибусь, используя опасную функцию». «Я знаю, что можно ошибиться, но я настолько искусен, что это спасает меня. Думать иначе — оскорбление для меня, личная травма». Тут все понятно.

  • «Есть причина, почему эти функции были добавлены в язык». Это абсолютно неверно. Существует множество причин, почему вещи попадают в язык, но большинство из них не являются вескими.

    Например, JavaScript был разработан и реализован всего за 10 дней. Это удивительно. И блестящий человек, который это сделал, Брендан Айк, за эти 10 дней совершил несколько ошибок. Одна из них — оператор двойного равенства, который он заставил делать преобразование типа, прежде чем сравнивать элементы. Это вызывает ложные срабатывания, которые путают. Брендан Айк признал, что оператор был реализован неправильно. В то время многие другие языки (например, PHP) имели ту же ошибку. Брендан хотел исправить это. Поэтому, когда началась работа над стандартом, он решил, что сейчас самое время внести исправления. Он пошел к ECMA и сказал: «Такое поведение неверно, давайте сделаем правильно». Но в ECMA отказали, хотя и предложили компромисс: ввести оператор тройного равенства, который будет работать правильно. При этом двойное равенство осталось в языке для людей, которые уже начали использовать неправильную конструкцию. В итоге мы имеем оператор без веских на то оснований.

    Брендан называет подобные особенности «выстрелом в ногу» (footgun — в дословном переводе пистолет для выстрела в ногу, — прим. ред.). И он непреднамеренно поместил их в JavaScript. Их использовать не рекомендуется.

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

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

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

Одна из вещей, усложняющих управление разработкой программного обеспечения, — это сложность планирования. Есть два времени, о которых вы должны знать. Время A — это время, необходимое для написания кода. Мы действительно плохо оцениваем его — у нас нет науки, которая ответила бы на вопрос, как оценивать время A. Но еще хуже обстоит дело с оценкой времени B — времени, необходимого на то, чтобы заставить код работать правильно.

Время B должно быть равно 0, верно? Вы пишете код — он должен работать. Но время В иногда становится больше времени А. Иногда оно бесконечно. Подобное происходит, когда у вас есть проект по разработке ПО, который заканчивается, но затем от ПО отказываются, прежде чем оно начинает работать.

Все, что вы делаете в рамках времени A, увеличивающее время B, неправильно. Вы должны пытаться сократить время B до нуля.

Новые сильные стороны ES6


Есть ряд дополнений к JavaScript, которые будут отправлены на ECMA General Assembly в июне этого года (эти нововведения действительно прошли через процедуру сертификации и стали частью новой спецификации — ECMAScript 2015 Language Specification, — прим. ред.).

Рад сообщить, что там есть некоторые новые сильные стороны. И сегодня я бы хотел поделиться ими с вами.

Хвостовая рекурсия


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

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

Многоточие


Также мы получили оператор многоточия. Если мы поместим его в список параметров или аргументов, сможем иметь дело с переменным количеством аргументов, что действительно приятно.

Здесь у нас есть две версии функции curry. Первая — в соответствии с ES6:

function curry(func, ...first) {
          return function (...second) {
                    return func(...first, ...second);
          };
}

Вторая — как это реализовывалось ранее:

function curry(func) {
          var slice = Array.prototype.slice, args = slice.call(arguments, 1);
          return function () {
                    return func.apply(null, args.concat(slice.call(arguments, 0)));
          };
}

Я не буду объяснять, что именно происходит во второй версии, потому что все это просто ужасно. Первая, наоборот, выглядит довольно разумно: всюду мы видим точки (здесь у вас может быть столько аргументов, сколько вы захотите). И это замечательно.

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

Модули


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

Теперь мы можем сделать это правильно. Если вам не кажется, что это здорово, попробуйте реализовать то же самое в соответствии с предыдущим стандартом. Но с ES6 мы наконец можем делать это правильно на полностью асинхронном языке, без блокировок.

Константы


У нас появилось два новых способа определения переменных: let и const, что решает проблему области видимости блока. Оказывается, в хорошей программе вам не нужна область видимости блока. Но синтаксис JavaScript ранее выглядел так, будто область видимости блока есть (с его синтаксической конструкцией var), хотя это было не так, что смущало людей. Всякий раз из-за путанницы возникали ошибки. Let и Const позволяют этого избежать.

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

Деструктуризация


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

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

let {that, other} = some_object;
let that = some_object.that, other = some_object.other;

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

WeakMaps


У нас появился WeakMap. WeakMap работает так, как должны были бы работать объекты. В объектах JavaScript ключи — это строки, что является ошибкой. Было бы лучше, если бы вместо них использовались какие-то значения. Но, тем не менее, это строки. WeakMap решает эту проблему. Здесь можно взять любое значение и использовать его в качестве ключа.

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

С помощью WeakMap вы можете писать программы, которые невозможно было создать на языке ранее.

Шаблонные строки


И наконец мы нечто, что я назвал мегастроковым литералом (в языке это называется шаблонными строками, но мне не нравится это имя; но ранее их называли квази-литералами, что еще больше сбивало с толку).

Это регулярные выражения, которое соответствует нескольким строкам в ES6. Объявление этого регулярного выражения просто ужасно; я надеюсь, когда-нибудь мы сделаем что-нибудь получше. Но в ES6 у нас нет лучшей альтернативы.

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

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

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

Стрелочные функции


Еще одна новая функция, которую мы получили а ES6, это анонимные стрелочные функции. Хорошей мотивацией для их появления стало то, что некоторые люди жаловались на большой объем текста при наборе названий функций. Поэтому я добавил такую вещь. По сути это более короткое обозначение для функции. В результате я получаю функцию, которая будет принимать аргумент и возвращать объект, у которого указанное свойство имеет это значение.

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

Недостатки ES6


Помимо сильных сторон, у ES6 есть и откровенно плохие элементы.

Классы


Худшее нововведение — это классы. Класс был самой востребованной новой функцией, причем большая часть запросов поступала от Java-программистов, которые теперь должны писать на JavaScript и они действительно возмущены этим. Хочется писать на Java, но задачи и деньги есть в JavaScript, поэтому им приходится это делать. Переучиваться они не хотят, поэтому новый синтаксис упростит их жизнь.

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

Object.Create


Я пересматриваю то, что было написано в «Сильных сторонах», учитывая новые знания и необходимость отражения нововведений ES6. Когда я писал «сильные стороны», я рекомендовал вместо классов использовать object.create.

Фактически, именно мне удалось добавить object.create к языку, чтобы именно я мог его использовать. Получилось неплохо. Но как же я был удивлен, когда заметил, что прекратил использовать object.create. Я добавил его туда для себя, но даже я его не использую. И причина в том, что я прекратил использовать this.

This


Я перестал использовать this из-за того, что в 2007 году сделал проект под названием ADSafe. В то время существовало несколько исследовательских групп, таких как fbjs в Facebook, Caja Google, Web Sandbox в Microsoft, кроме того, был мой собственный проект — ADSafe и другие. Все мы пытались выяснить, как сделать JavaScript безопасным языком, чтобы можно было добавить сторонний код в приложение и быть уверенным, что он не породит проблем с безопасностью. И в JavaScript это оказалось очень сложной задачей.

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

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

Мой подход в ADSafe был намного проще. Я просто сделал это незаконным, побудив отказаться от программ, где это используется. Это работает. Единственная проблема в том, что отказаться приходится от львиной доли программ, потому что все использовали this.

Однако моя гипотеза заключалась в том, что если удалить this из языка, мы все равно останемся с функциональным языком программирования, которого достаточно для написания хороших программ. Чтобы проверить ее, я начал писать также (без this) в свободном от ограничений JavaScript. И я был очень удивлен, обнаружив, что стал писать лучшие программы с меньшими усилиями, не имея this. Очень мило. Что и говорить, если наличие this в языке само по себе вызывает сложности: общаясь на английском, я не могу заранее знать, имеется в виду конструкция языка или местоимение. Это довольно сложно.

Null / undefined


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

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

Неопределенное значение порождает целый круг проблем: совершенно неправильный тип отсутствующего объекта. Мы надеялись, что это будет исправлено в ES6, но этого не произошло. Наверное, так будет всегда. Но если вы не используете null, то не столкнетесь с этой проблемой. И я перестал использовать ошибочные значения, решив, что само их существование в JavaScript — плохая идея.

Изначально эта идея появилась из C, который использует 0 для представления no, false и другие аналогичные значения. JavaScript попытался сделать то же самое. Но это сбивает с толку.

For


Я перестал использовать for. В ES5 мы ввели новый набор методов работы с массивами — for each, map и другие, поэтому теперь я использую эти инструменты. Если у меня есть массив и мне нужно его обработать, я использую один из этих методов. И я могу объединить их вместе удобным образом. Это действительно выразительно.

For Each


Я не использую for each. В ES5 мы получили object .keys(object). Эта конструкция дает вам хороший массив строк. И он не включает в себя вещи из цепочки прототипов, поэтому вам не нужно фильтровать результаты. Очень приятно, что я могу взять массив и выполнить for each таким образом, это действительно выразительный способ.

Ранее я уже упоминал, что в ES6 появились правильная хвостовая рекурсия. И сейчас пора перестать использовать циклы вовсе (в частности, оператор while) — время использовать только рекурсивные функции.

Например, это рекурсивная функция. Вы вызываете эту функцию до тех пор, пока она не вернет undefined. Первая версия кода написана с использованием цикла while.

function repeat(func) {
          while (func() !== undefined) {
          }
}

Вторая версия написана с использованием хвостовой рекурсии.

function repeat(func) {
          if (func() !== undefined) {
                     return repeat(func);
          }
}

В ES6 эти два способа должны работать с одинаковой скоростью, потребляя ровно столько же памяти. Таким образом, у циклов перед рекурсией больше нет преимущества. Это здорово.

Язык следующего поколения


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

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

В одной вещи я уверен: когда он, наконец, появится, мы отвергнем его. И причина этого в том, что программисты — эмоциональные и иррациональные ненормальные люди.

Мы считаем, что это не так. Мы думаем, что являемся ультра-рациональными, потому что исполняем роль послов людей в мире компьютеров, а компьютеры вполне рациональны. Соответственно, мы предполагаем, что и сами также рациональны. Многие из нас лишились социальных навыков, но выходит так, что иррациональность и эмоциональность никуда не делась. Давайте посмотрим на доказательства в поддержку этого тезиса:

  • Потребовалось поколение, чтобы признать языки высокого уровня хорошей идеей. Когда среди множества ассемблерных языков появился Fortran, разработчики (на тот момент они все были ассемблерными программистами) отказались на него переходить. Потому что Fortran отнимал у них контроль взаимодействия с машиной, чего они не могли допустить. Переход сделал бы их жизнь намного лучше, но они отказались от него.

  • Потребовалось целое поколение, чтобы согласиться с тем, что go to был плохой идеей. Десятилетиями мы вели эмоциональные споры о том, следует ли нам использовать go to или нет. И приводились как глубоко продуманные аргументы от очень умных людей, так и заявления в стиле: «Это мой способ самовыражения», «Мне нужна эффективность», «Умру, но не отдам». Оказалось, что все эти доводы были совершенно неправильными.

  • Потребовалось поколение, чтобы согласиться, что объекты были хорошей идеей. В 1967 году в Норвегии появился язык, названный Simula. Насколько я могу судить, только один человек в мире — Алана Кей из Университета штата Юта (США) — признал, что этот язык содержал важные идеи. Все остальные пропустили это. Он думал, что объекты, которые появились в Simula были настолько выразительными, что он мог бы разработать язык программирования для детей. И дети могли бы писать удивительные программы, используя эту парадигму. Поэтому он начал изучать этот язык и потратил почти десять лет на разработку и совершенствование собственного. В 1980 он наконец был опубликован. И это был лучший из разработанных языков программирования в истории — С++. Алан Кей взял идеи Simula, не совсем понял их, но перевел на C.

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

  • И, наконец, потребовалось два поколения, чтобы согласиться с тем, что лямбды были хорошей идеей. Лямбды появились в языке под названием Squeak в MIT в начале семидесятых. И индустрия вообще не обратила на это внимания. Потребовалось даже не два, а четыре поколения, чтобы наконец лямбды добрались до мейнстрима. На это потребовалось так много времени, что некоторые считали это доказательством несостоятельности идеи. Но оказалось, что функциональное программирование с лямбдами очень эффективно при работе  с асинхронностью и распределенными системами, с которыми мы имеем дело сегодня. Первый язык, принявший эту идею, — JavaScript. После этого лямбды появились в Python и Ruby, и, в конечном итоге, C#. Не так давно наконец в Java. Но JavaScript был первым.

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

Я помню, когда появился go to. Спор вокруг него не утихал годами, а потом вдруг стало тихо. Можем ли мы избавиться от него сейчас? Да, мы уже просто бросили его. Разве кто-нибудь страдает от отсутствия go to? Все споры оказались бессмысленным, произошел сдвиг парадигмы.

Людям действительно сложно сдвинуть эту парадигму. История с go to — это просто избавление от одной конструкции и изменение способа, структурирования программы. Оказалось, что отсутствие go to облегчает программирование.

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

О классификации языков


Подумайте о том, как мы классифицируем языки программирования. Я делю языки на два основных набора: системные и прикладные.

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

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

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

Прикладные языки также можно разделить на две группы: классическую школу (к которой относятся почти все языки) и группу языков для прототипного программирования, которая включает JavaScript.

И я думаю, JavaScript правильно выбрал направление. Вызывая много критики, JavaScript содержит инновации.

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

Все это не является обязательной частью объектно-ориентированного программирования. Это просто классы. Если вы займетесь объектно-ориентированным программированием без классов, вам не придется ничего делать. Поэтому в JavaScript, если вы просто делаете прототипы (и делаете их правильно), вам не нужно делать таксономию и выполнять рефакторинг. Это намного проще.

Я постоянно пытаюсь объяснить это Java-разработчикам. Но они так и не осознают, что таксономию делать не надо. Здесь нужен сдвиг парадигмы — они просто не могут осознать, что таксономия делает их несчастными, не могут представить другого способа.

Раньше я был сторонником прототипического наследования. Его основное преимущество заключается в сохранении памяти. В этом случае мы копируем объект, запоминая только реальное различие между оригиналом и копией, т.е. памяти выделяем меньше. И, возможно, это было самым важным фактором в 1995 году. Но не сегодня. Объем доступной памяти постоянно растет в соответствии с Законом Мура. Теперь у вас есть гигабайты оперативной памяти буквально в вашем кармане. Поэтому беспокоиться о том, сколько битов выделяется на каждый объект сегодня — просто потеря времени. Но мы по-прежнему делаем это.

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

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

Поэтому, хотя я и был сторонником прототипического наследования, теперь я придерживаюсь бесклассового объектно-ориентированного программирования. Я думаю, такой подход — дар JavaScript человечеству.

Позвольте объяснить, что я имею в виду. Это пример области видимости блока. Вы можете создать внутренний блок, который видит переменные внешнего блока. При этом другой блок не может видеть переменные во внутреннем блоке.

{
          let a;
          {
                     let b;
                     ... a …
                     ... b …
          }
          ... a ...
}

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

function green() {
          let a;
          function yellow() {
                    let b;
                    ... a …
                    ... b …
          }    
          ... a ...
}

Множество переменных, которые видит внешняя функция, включается в множество переменных, видимых для внутренней функции (поэтому мы такие взаимоотношения называем замкнутыми — это понятие из теории множеств).

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

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

С учетом всего этого вот, как я создаю объекты, в соответствии с ES6. Итак, у меня есть функция constructor:

function constructor(spec) {
          let {member} = spec,
                     {other} = other_constructor(spec),
                     method = function () {
                              // member, other, method
                     };
          return Object.freeze({
                    method,
                    other
          });
}

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

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

Но вернемся к нашему примеру. Я использую деструктуризацию, чтобы получить значения из объекта и инициализировать локальные переменные.

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

После этого я выполняю return. В ES6 у нас есть еще одна сокращенная нотация: вместо вызова метода можно просто написать method. Что приятно, это больше похоже на объявление, чем на литерал.

Далее я собираюсь его заморозить. Теперь это объект, который нельзя изменить, что правильно определяет его локальное состояние. Это лучший способ реализовать подобное на JavaScript. И я думаю, что это действительно важное свойство для объектов. В начале объектно-ориентированного программирования мы стартовали с идеи, реализованной в Pascal, согласно которой у нас есть запись, а затем мы связываем с этой записью функции, которые являются методами, действующими на элементы записи. И ранее я не мог выйти за эти рамки. Теперь же у меня теперь есть два совершенно разных набора объектов. У меня есть замороженные объекты, которые содержат только функции, и у меня есть значимые объекты, которые просто содержат данные. И я не смешиваю их. Я использую их друг с другом, чтобы защитить данные и обеспечить соблюдение дисциплины.

Единственный численный тип


История одной ошибки


У меня есть еще немного времени, чтобы рассказать вам историю одной ошибки. В 2001 году я писал на Java один из первых парсеров JSON. И он содержал конструкцию this. Я создавал локальную переменную с именем index, она была int. Переменная измеряла, сколько символов было в файле или потоке, без парсинга. При обнаружении синтаксической ошибки переменная могла сообщить вам, в каком именно месте она произошла ошибка.

Напомню, я написал это в 2001 году. Я хорошо подумал, что int дает мне примерно 2 миллиарда байт — это два гигабайта. В то время это был довольно большой диск. Я не мог себе представить, что текст JSON окажется больше. Самый большой текстовый файл из тех, что я создавал, был пару килобайт.

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

Я ненавижу int. У int есть ужасное свойство — он переполняется без предупреждения. Что должно произойти, если у вас есть число, которое слишком велико для помещения в int? Считается, что программа не должна вылетать. В итоге значимые биты выкидываются, а вы получаете неверные результаты. Лучше было бы бросить эксепшн, который скажет, что «что-то не так».

Альтернативный вариант — замена: когда вы попробуете получить значение, вам сообщат, что его больше здесь нет. Но существующий вариант — худшее, что можно было придумать; это путь максимизировать ошибки.  Более того, эти ошибки никогда не выявляются в тестах, поскольку тесты изначально предполагают, что данные соответствуют диапазонам.

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

DEC64


Причина всех проблем с типами данных родом из пятидесятых годов, когда компьютеры были ламповыми. Чем больше ламп, тем больше требуется энергии и косвенных затрат (например, тем быстрее выгорают лампы).

Кроме того, задействовать дополнительную память было действительно страшно. У машины могла быть только пара килобайт. К примеру, память Atari 2600 была всего 128 байтов. И поэтому не хотелось выделять 64 бита на то, что могло бы быть представлено 4 битами.

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

Аналогичная ситуация сохраняется на других языках. Например, в Java у вас есть byte, char, short, int, long, flot, double и т.п. Каждый раз, когда вам нужно создать параметр, переменную или элемент, вы должны спросить себя, к какому типу будет относится новая структура. Значение сэкономленной при этом памяти настолько мало, что говорить о нем нет смысла. Нет никакой пользы в том, чтобы сделать правильный выбор. Фактически время, которое вы потратили на размышления, стоит бесконечно больше, чем сэкономленный ресурс. Но если вы ошибетесь, все перестанет работать, и тесты это не выявят. Это плохо.

С другой стороны, JavaScript имеет только один численный тип. Здесь вы не можете ошибиться. Единственная проблема с JavaScript заключается в том, что это неправильный тип. При его использовании 0.1 + 0.2 не равно 0.3. Это фундаментальная вещь. Проблема в двоичной плавающей запятой — это снова нечто, что имело смысл в пятидесятые годы.

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

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

По сей день мы продолжаем это делать. И это неправильно, поскольку вызывает проблемы. Это было так же неправильно и в пятидесятые годы: бизнесмены возражали, что не могут пользоваться такими вычислениями. Вместо этого они использовали BCD (двоично-десятичный код). Таким образом языки программирования разделились. Вы могли использовать Fortran с плавающей запятой или COBOL с BCD. В конце концов, ситуация стала совсем запутанной. Java стала наследником COBOL. Но Java не была предназначена для ведения бизнес-процессов. Так что этот функционал здесь просто не используется.

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



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

Я написал реализацию DEC64 на ассемблере Intel x64 — используйте ее, если хотите опробовать тип в аппаратной реализации (dec64.com, GitHub).

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

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

У меня есть немного опыта в сфере убеждения всего мира в том, как поступать правильно. Пример — мой любимый формат обмена данными JSON.



Это статистика Google, которая показывает относительную популярность XML (красным) и JSON (синим). Вы можете видеть, что мир неуклонно теряет интерес к XML с 2005 года, и он очень медленно растет интерес к JSON.

Стоит отметить, что на рост популярности JSON не повлияла никакая отрасль. Нет крупных корпораций, которые продают большие инструменты JSON, потому что вам не нужны большие инструменты для работы с JSON. Вот почему вам он нравится. Это просто работает.

Похоже, скоро будет пересечение. Я не могу предсказать, когда это произойдет. Но Google может. Согласно прогнозам Google Trends это произойдет в этом году (2015 — прим. ред.). JSON наконец станет официально более интересным, чем XML.



Если вам интересна оригинальная видеозапись доклада на английском, с радостью ее предоставляем:




На грядущем HolyJS Piter вы сможете прослушать два доклада Дугласа Крокфорда:


Кроме них в течение двух дней вы услышите еще более 20 докладов о JavaScript: от новых фронтенд фреймворков до разбора серверного перфоманса и десктопной разработки. Будут доклады от Lea Verou, Martin Splitt, Anjana Vakil, Claudia Hernandez и много кого еще. Детали смотрите на сайте конференции.
Поделиться с друзьями
-->

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


  1. MikailBag
    25.04.2017 17:09
    +1

    Я правильно понял, что это обычный float, но вместо 2 используется 10?
    Если да, то это шило на мыло.


    1. int33h
      25.04.2017 17:35
      +1

      Нет тут вы не правы. Замена основания экспоненты с 2 на 10 действительно поможет исправить часть проблем связанных с операциями над вещественными числами, да и вывод их существенно упроститься… Но автор, к сожалению,? не учел, что делать с такими обыкновенными дробями, как 1/3. Так что мне кажется, что будущее будет за типом fraction(дробь)


      1. MikailBag
        25.04.2017 17:43

        fraction не спасет от корней, логарифмов и проч.
        А так неплохо, да.
        Но лучше бы просто длинную арифметику добавили.


        1. int33h
          25.04.2017 17:51

          Почему не спасет? Я уверен, что в любых вычислениях мы заранее можем задать требуемую точность(никому ведь, кроме совсем неадекватных, не потребуется вычислить 1 млн или сразу бесконечность знаков числа pi), а если можно задать точность, то становятся применимы и стандартные алгоритмы нахождения корней, логарифмов и т.д. Единственное, что от этого пострадает — это скорость вычислений, но как говорится за все нужно платить…


          1. MikailBag
            25.04.2017 17:53

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


            1. int33h
              25.04.2017 17:58

              Ну а я о чем… С таким подходом 0.1 + 0.2 будет теперь равно 0.3, но (1/3) * 6 будет по прежнему выдавать не тот результат.


              1. shockable
                26.04.2017 13:49

                В пайтоне давно уже есть и decimal и fraction. В реальном коде, конечно, не так уж и часто встречается, но как инструмент доступен.


                1. MikailBag
                  26.04.2017 19:34

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


      1. nullc0de
        25.04.2017 17:52

        Да как бы он ничего нового не изобрел такие же методы давно используются в научных вычислениях, где важна высокая точность особенно с большими числами. В многих книгах по алгоритмам поднимается этот вопрос. Я помню сам писал что-то подобное, когда учился. А насчет fraction это хотели в dart реализовать, но вроде потом от идеи отказались. Вообще куда сейчас движется JS так это в сторону Dart, даже ES6 часть конструкций слизал с dart. Все проблемы с JS, что разработчики dart озвучивали еще в 2011 году, сейчас озвучиваются на JS конференциях, и к этому приходят сами разработчики, натыкаясь в работе на ограничения. Я не удивлюсь если, через 10 лет JS разработчики откроют для себя, что пишут код на Dart… Многие вещи, что выдают за изобретения и инновация уже давно изобретены кем-то другим…


        1. Nakosika
          26.04.2017 10:18

          Fraction есть в большом количестве функциональных языков, например Clojure.


  1. int33h
    25.04.2017 17:46

    Вообще, я впервые вижу, чтобы автор поднимал в своей статье те же проблемы, что появились в виде вопросов и у меня в голове пару месяцев назад. Правда жаль, что между нами более 20 лет разницы…


  1. DmitryKoterov
    25.04.2017 18:28
    +1

    Хвостовая оптимизация в языке — это, безусловно, хорошо. Но у нее есть один недостаток, затрудряющий отладку: потеря фреймов стека, в результате чего в цепочке вызовов функций A->B->C->D при возникновении исключения в D мы видим в стектрейсе только A и D. Так что мы не имеем никакого понятия, откуда именно была вызвана D.


  1. copal
    25.04.2017 18:44
    +1

    Когда делаешь интерфейсы с сотней items в массиве, то может показаться забавным, что алгоритмы по изменению массивов с данными о пикселях с применением цикла for могут дать ну очень существенный прирост. Так к примеру на минимальном разрешении 1024х768 массив имеет длину в 786432 элементов, а итераций для изменения может достигать тоже очень много. поэтому заявления что for это ошибка, ошибочно! Ну а использовать рекурсию вместо циклов, если даже рекурсию сделают по скорости выполнения равной циклам, то лично мне было ну очень сложно начать программировать с использованием рекурсий. К слову, кто-нибудь замерял скорость хвостовых рекурсий vs циклы?

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


    1. int33h
      25.04.2017 19:36

      Он не говорит, что for — это ошибка. Он утверждает, что for для JS перешел в разряд deprecated… Да и в С++, использование for является вынужденной мерой, т.к. конструкции for_each и (…: ...) появились в нем совсем недавно и не позваляют разгуляться в полной мере…


      1. copal
        25.04.2017 21:04

        Это получается что учить js друзьям не советовать? Если такие разговоры пошли, то лет через пять, после внедрения wasm js просто уйдет в небытия, ведь на любом языке можно будет писать программы для браузера и самое главное, что это будет легально и более того, знать нативный js, как в случаи с препроцессорами, уже не нужно. Кто тогда лидирует, c#?


        1. int33h
          26.04.2017 00:11

          Я думаю, что небольшие страницы по прежнему будут писать на JS, но когда дело будет доходить до крупных сайтов с тысячами посетителей в день, там уже будет использоваться другие языки(хотя мне до сих пор не понятно, как они собираются взамодействовать с html). Это как сравнивать python и С… У каждого есть свои преимущества и недостатки…


    1. nullc0de
      25.04.2017 19:46

      Может не дать прироста на последних версиях VM. Сейчас во многих виртуальных машинах JIT оптимизирует код нереально круто и прощает ошибки. А вот в старых браузерах и виртуальных машинах может быть реальный затык… Приходится делать сложные бенчмарки, чтобы jit не оптимизировал и делал реальный тест, вместо гоняния воздуха туда-сюда, и мерить производительность на критичных участках программы…
      Самый большой прирост дают дают typed arrays… while будет быстрее for, если запуск цикла один, а потом jit оптимизирует for…


  1. megapixar
    25.04.2017 18:44

    Но оказывается, что писать хорошие программы на JavaScript не только невозможно, но и не нужно.

    Одна из сильнейших сторон javascript.


    1. nullc0de
      25.04.2017 19:55
      -1

      Писать можно. Но по сравнению с нормальными языками типа C++, Java, C#, python, приходится много писать костылей, чтобы реализовать банальные вещи, которые делаются в других языках очень просто и легко. Ну и многие действия бьют сильно по производительности кода. JS — язык написания костылей… Имхо изначально архитектура языка не совсем удачная и все развитие языка идет к прикручиванию костылей к языку… Преимущество языка — он простой и легкий вход в него, по сравнению например с dart, который больше подходит для крупных проектов…


      1. int33h
        25.04.2017 20:05

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


  1. worldmind
    25.04.2017 21:35

    Реально здраво мыслящий человек, жаль такие редко проектируют языки.


  1. amakhrov
    25.04.2017 22:52

    А где ссылка на оригинал?


    У меня есть подозрение, что


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

    это не совсем то, что хотел сказать автор.


    По существу:
    Крокфорд, конечно, голова — но в данном докладе он кое-где гонит :)


    • Разве Алан Кей придумал C++, а не Smalltalk?
    • Pascal сюда зачем-то сюда приплел в контексте "начала объектно-ориентированного программирования". Почему не тот же Smaltalk? Да даже C++, если верить википедии, был создан раньше, чем Object Pascal (не путаем с оригинальным Pascal, в котором объектов не было
    • Консервативность в принятии новых языков разработчиками. Имхо вопрос больше в аппаратных возможностях. Высокие абстракции, как правило, требуют больше аппаратных ресурсов. Когда производительность стала позволять, разработчики стали переключаться на более высокоуровневые инструменты.


  1. iShatokhin
    25.04.2017 22:54

    Я не использую for each. В ES5 мы получили object .keys(object). Эта конструкция дает вам хороший массив строк. И он не включает в себя вещи из цепочки прототипов, поэтому вам не нужно фильтровать результаты. Очень приятно, что я могу взять массив и выполнить for each таким образом, это действительно выразительный способ.

    Douglas Crockford в видео ясно говорит "for each", но судя по контексту, он имел ввиду, что не использует "for in". Думаю, он оговорился.


    Пассаж про WeakMap тоже непонятен, т.к. есть еще Map, без "слабого" названия. Что означает Weak в названии он не объяснил, только запутав слушателей.


    P.S. это не претензии к переводу, просто мысли в слух.


  1. irezvov
    26.04.2017 02:29
    +1

    В 1980 он наконец был опубликован. И это был лучший из разработанных языков программирования в истории — С++. Алан Кей взял идеи Simula, не совсем понял их, но перевел на C.


    Что за бред? Речь явно про Smalltalk, но какое отношение он имеет к С?


    1. boolive
      26.04.2017 16:13

      Да, Алан Кей взял идеи Simula для Smalltalk, творил десять лет до 1980. Страуструп сделал C++ под влиянием Simula и немного Smalltalk и других в 1983


  1. ACPrikh
    26.04.2017 08:01

    Религиозная ажиотация зашкаливает. Так подпитываются холивары.


  1. potan
    26.04.2017 17:26

    «Лямбды появились в языке под названием Squeak в MIT в начале семидесятых.»
    Lisp? Нет, не слышал…


    1. ACPrikh
      27.04.2017 08:34
      +1

      Да там все эти «потребовалось поколение» — притянуты за уши. Это всё живо напоминает выступление Кашпировского на стадионе.


  1. paratagas
    28.04.2017 15:56

    Похоже, скоро будет пересечение. Я не могу предсказать, когда это произойдет. Но Google может. Согласно прогнозам Google Trends это произойдет в этом году (2015 — прим. ред.). JSON наконец станет официально более интересным, чем XML.
    Совсем немного ошибся: пересечение двух технологий и перевес JSON зафиксированы в апреле 2016 г.


  1. sv_kozlov
    28.04.2017 16:35

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

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


  1. keslo
    02.05.2017 11:02

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

    Плиз, знающие люди, объясните этот момент подробнее. Можно примером.


    1. iShatokhin
      02.05.2017 12:53

      Чтобы понять, запустите в консоли браузера.


      const obj = {}; 
      obj['foo'] = function () { 
          console.log(this === obj);
          console.log(this === window);
      }; 
      obj.foo(); // => true, false
      
      const fn = obj.foo;
      fn(); // => false, true


      1. keslo
        04.05.2017 13:36

        Получается, что

        const fn = obj.foo;
        fn();
        

        равноценно
        const window.fn = obj.foo;
        window.fn();
        

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


        1. Azoh
          04.05.2017 16:28

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


          (function() {
              const localFoo = obj.foo;
              localFoo(); // => false, true
          })();
          console.log(window.localFoo); // => undefined

          С this все плохо, оно определяется способом вызова (про связанные функции не будем вспоминать)


          1. keslo
            04.05.2017 16:36

            Хм… для

            localFoo(); // => false, true
            
            контекстом будет window. Но после вызова
            (function() {
                ...
            })();
            
            информация о localFoo исчезает из области window?


            1. Azoh
              04.05.2017 21:35

              Тут все еще несколько хитрее. Глобальная область видимости — это поля глобального объекта, в данном случае window. Однако же localFoo не принадлежит window, это просто локально объявленная константа. Вот пример того, как мы радостно дали эту функцию другому объекту (это ровно одна и та же функция, нужно заметить), а this снова поменялся.


              const obj2 = { otherFoo: obj.foo };
              obj2.otherFoo(); // => false, false

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


              Т.е. если мы вызываем функцию как метод (obj.foo()), то this будет определен как объект, содержащий поле. Но если мы вызываем функцию как функцию, то такого объекта, очевидно, нет. Но javascript в данном случае нам просто радостно подставляет глобальный объект. Это особенно раздражает в случае если передавать метод как callback. Но напрямую это сделать нельзя (this потеряется), его нужно каким-то образом привязать к правильному объекту.


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


  1. iShatokhin
    02.05.2017 12:52

    ошибся уровнем