Pull to refresh

Comments 67

Навскидку:

let sayHappyNewYear = year => 
      `Happy New ${year} Year!`;    

sayHappyNewYear(2016);
Зашёл сюда увидеть этот комментарий, потому что блогозапись с упоминанием ECMAScript 2015 и без использования строк-шаблонов напрашивалася на этот комментарий.
Все же есть разница между функциями и функциональными выражениями. Конкретно в этом случае — это должна быть обычная функция, а использование стрелочных функций в этом контексте существенно снижает читабельность.

// функция суммы элементов - обычная функция
function sum(arr) {
    
    // а вот тут arrow-functions упрощают код.
    return arr.reduce((sum, item) => sum + item, 0);
}
Заранее извиняюсь за занудство.
На самом деле, это не очень хороший пример, который хотелось бы демонстрировать новичкам JS. Нативные шаблоны — это гуд.
А вот укороченные стрелочные функции надо использовать осторожно, по крайней мере, в обычных незамысловатых ситуациях.
У них есть несколько ограничений:
  • Во-первых, стрелочная функция запускается всегда с контекстом того места, где она описана. То есть, .apply() и .call() использовать с такими функциями бесполезно.
  • Во-вторых, стрелочные функции не создают объекта arguments, который можно было бы использовать в теле стрелочной функции
  • В-третьих, несмотря на явное название функции sayHappyNewYear, она всегда остается анонимной.
  • В-четвертых, (не самое главное) определив функцию обычным образом function sayHappyNewYear(year) { ... }, например, в глобальном окружении, функцию можно использовать где угодно, хоть после определения, хоть до определения. Переменные, определенные через let, доступны для использования лишь после определения.
Ну так это в статье нужно писать, а не в комментариях.
+ Такие выражения нельзя использовать в качестве конструктора поскольку отсутствует у них prototype и [[Construct]]:

let foo = new (() => {}); // TypeError
let foo = () => { super() }; // SyntaxError
let foo = () => { new.target }; // SyntaxError
Вы правы. Но, слава богу, для конструкторов есть class и constructor.
Если функция в дальнейшем будет использоваться как «чистая» почему бы не использовать толстую стрелку после return? Если нужен контекст, то да надо вернуть function.
В качестве примера сие имеет место быть.
А на деле использую стрелочные функции только в аргументах при вызове функций.
Еще иногда в return. Например,
function sayHappyNewYear(year) {
   return cb => cb(null, `Happy New ${year} Year!`);
}
В остальных случаях использую что-либо отличное от стрелочных функций.
2-е легко решается оператором распространения (spread operator):

myFunction(...iterableObj);
оно то решается — вопрос зачем.
Во-вторых, стрелочные функции не создают объекта arguments, который можно было бы использовать в теле стрелочной функции
И не надо его использовать, если вы не библиотеку пишете.
В-третьих, несмотря на явное название функции sayHappyNewYear, она всегда остается анонимной.
Ваше замечание некорректно. У функции в примере нет названия. Она присваивается именованной переменной.
У функции в примере нет названия. Она присваивается именованной переменной.


Вы правы, именно это явление можно выяснить взглянув на код или в инспектор/отладчик. А если это не ваш код? Часто, чтобы избежать разногласий, пишут:
let a = (function a () {
   ...
});
console.log(a.name); // => a

Может быть, для браузеров код выглядит необычно, но, например, разбираться в чужих middleware, когда ~30% из них анонимные функции, похоже на ад. — И в итоге, все равно, нужно прочесть и разобраться во 100% кода приложения.
В-четвертых, (не самое главное) определив функцию обычным образом function sayHappyNewYear(year) {… }, например, в глобальном окружении, функцию можно использовать где угодно, хоть после определения, хоть до определения. Переменные, определенные через let, доступны для использования лишь после определения.

Я бы сказал, что такое ограничение очень даже полезно, потому что, как по мне, такой код читать и использовать удобнее, когда есть определенный порядок. Если же функция может быть определена где-то ниже, то, иногда, это не очень удобно.
Часто бывает так, что функции вызывают друг друга, образуя неявную рекурсию. И, чтобы одна функция не была определена раньше другой, пришлось бы сначала объявить их всех через let, а ниже делать им описания, — что не очень удобно.
let a, b;

a = function () {
   b ();
}

b = function() {
   a ();
}

Пример надуманный, не ругайтесь. Ситуация довольно часто проявляется при обработки иерархических данных, например, при работе с файловой системой. Куда логичнее и проще:
function a() {
   b ();
}

function b() {
   a ();
}

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


Говоря об удобстве… Я предпочитаю описывать функции в порядке убывания их значимости. То есть, функция, ради которой пишется весь модуль объявлена выше всех. А всякие helpers/wrappers болтаются в самом низу, работу таких можно предсказать и по названию функции. Ну, если использовать любую IDE, становится все равно, в каком месте определена функция, был бы к ней доступ. Не системные же утилиты пишете! Вроде бы, почти не осталось мамонтов, использующих vim/emacs для кодинга на javascript.
Есть и более-менее современные редакторы, которые при этом и не совсем IDE и не умеют так умно прыгать по коду, как WebStorm, например. Лично я пользуюсь Atom, в котором вроде бы как и можно эту штуку добавить отдельным плагином, но он работает чуть хуже, чем в WebStorm. Кроме того, иногда приходится код читать онлайн, на гитхабе, к примеру, и тогда хорошо, когда код легко читается сам по себе.
Бывало тоже приходилось писать подобный код, но я все-таки предпочел тот вариант, который вы назвали не очень удобным. Я согласен, что это и правда не самый удобный вариант, однако, мне такой вариант показаля более очевидным. Думаю больше дело вкусов и привычек.
Carlo, несомненно, умный мужик. Но в данном случае greeter более чем подходящее название для игрушечного примера несмотря на этот злополучный суффикс -er. В любом случае очередные примеры с Animal, Dog или Shape, Square были бы скучнее.
Может быть так оно и есть, но если заглянуть в толковый словарь и сопоставить значения слов со здравым смыслом:
greeter – определения имя существительное a person who greets people entering a store, church service, or other public place.
greeting – определения имя существительное a polite word or sign of welcome or recognition.
В следующий раз когда мне понадобится использовать некую абстрактную сущность, которая всех поздравляет, я назову ее GreetingMachine. Хотя, если быть честным Greeter здесь не хуже какого-нибудь Animal. Разве не так?
В следующий раз когда я буду писать нечто подобное я назову класс ВеселыйРождественскийЭльфКоторыйЖиветВКомпьютереИПечатаетНаЭкранеВеселыеПоздравленияСНовымГодом.
Расскажите пожалуйста про принцип: «меньше больше — больше меньше»
Эту фразу как-то сказал мой очень хороший знакомый. Я понимаю ее так:

Меньше больше:
Не имеет смысла в программе, решающей одну маленькую задачу, воплощать какие-то абстракции и паттерны. Лучше решить ее «в лоб». Пусть программа будет маленькой при этом лишние слои абстракции не будут стоять на пути понимания ее сути.

Больше меньше
Если программа растет, имеет смысл разбить ее на множество осмысленных взаимосвязанных кусков / слоев. Иначе сложность выйдет из-под контроля и дальнейшие изменения функций программы просто станет невозможным. Вот тут как раз на помощь приходят разные абстракции и паттерны.
То есть, меньше программа — больше подпрограммы, больше программа — меньше подпрограммы
Less is more — The notion that simplicity and clarity lead to good design.
Вообще — это принципы концепции минимализма в архитектуре, которые берут начало из минимализма в искусстве и старого доброго принципа разделяй и властвуй — этому принципу уже не одна сотня лет.
По ссылке, так сказать, история этого устойчивого выражения с объяснением смысла. Less is more
Мне одному кажется, что первый вариант самый компактный и удобный?
т.е. по сути почти всё, что писалось дальше, описывало реализацию объекта (с методом), вместо просто функции.
Я понимаю, что это только пример, но он совсем как то не иллюстрирует подходы которые раскрываются дальше.
Цель была проиллюстрировать не подходы как таковые, а новые возможности, которые предоставляет ES2015 при использовании того или иного подхода. Да я согласен, пример, довольно искусственный, но если взять последний пример, то без изменения кода основной функции можно создавать необходимые в данный момент функции:

var greetHabrauser = createGreeter('Hello, %habrauser%!');
console.log(greetHabrauser("stalkerg"));

> Hello, stalkerg!


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

let format = (strings, ...keys) =>
	(...values) => {
		let object = values[values.length - 1],
			result = [strings[0]];

	keys.forEach((value, index) => {
		result.push(object[value], strings[index + 1]);
	});

	return result.join('');
};

let year = 2016

format `Happy New ${'year'} Year!`({ year });
Ну, всем не угодить. С подобным примером я бы получил комментарии про оверинжениринг.
Вы это реально называете композицией и используете?

var greeter = new Greeter;
var newYearGreeter = new NewYearGreeter(2016);
extend(newYearGreeter, greeter);
Я ведь писал в статье про то, что пример получился искусственный. Именно поэтому дал ссылку на видео Маттиаса.
Статья несет несколько провокационный и популистский характер. И при этом практически никакой информации о ES6 и его возможностях (кроме того о чем пишут уже пол года все кому не лень). Только очередной повод разогреть холивар «неправильный ООП» vs «функциональная чистота».

Задача описанная вами решается не наследованием а композицией объектов, классов в JS как небыло и нет, это как назвать классотипы в хаскеле классами сравнивая оные с Java-скими.

Более того, это ребятам с Java/C# приходится плодить на такие вещи классы, банально потому что в этих языках нет отдельной сущности «функция». Ребята пишущие на Python например за такие наговоры обиделись бы. Вообще Python сообщество имеет все то же что в JS (модули, «классы» и т.д.) уже довольно давно. И стоит иногда присматриваться к тому как они комбинируют ОО подходы и функциональщину.

Да и мне кажется что вы как-то странно реализовали «композицию» функций, больше на каррирование похоже.

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

P.S. Статью немного исправил
UFO just landed and posted this here
Вот что праздники с людьми делают. ES6 это ES5++
UFO just landed and posted this here
на сегодняшний день поддержка ES6 в последних версиях браузеров нативно и с флагами — 80% от стандарта. Что как мне кажется весьма неплохо. (статус на 31-ое декабря)

Да и есть же babel и core-js. В подавляющем большинстве случаев профит от использования подобных вещей, в виде скорости разработки, читабельности кода и как следствие большей поддерживаемости, перекрывает риски. Есть конечно задачи где риски слишком велики, но как я уже сказал — это очень небольшая доля задач и туда новичков не пускают обычно.
Извините, может это глупый вопрос, но что такое «броузер»?
Просто-напросто для реализации web-клиентов НЕТ альтернатив. Важно осознавать это, и не воспринимать эту технологию как нечто грандиозное или действительно крутое лишь потому, что нет аналогов.
Возможно, стоит провести опрос, как много людей пишущих на нормальных ЯП с радостью перешли на JS в связи со всеми его возможностями… Я не знаю ни одного.
как много людей пишущих на нормальных ЯП с радостью перешли на JS

Нормальные это какие?

Вообще нынче модно иметь транспайлер с «нормального» в «ненормальный» языки. Scala.js, ClojureScript, Dart и куча других. Да и если присмотретья то ES6/7 — это уже нормальный JS. Ну и Web Assembly может еще чуть поменяет ситуацию.
С/C#/С++/Java/Python/PHP
И речь не о том, что для каждой задачи свой ЯП, а о том, сколько людей пишущих на этих языках с радостью перешли на JS из-за таких ошеломляющих возможностей и перспектив. (Хотя на нем теперь и сервера и десктоп пишут помимо веб-апп).
Кстати, товарищи минусовщики, был бы рад услышать с чем именно вы не согласны? С тем что для веб больше не существует альтернатив, или с тем что все сишники вдруг не перешли на js?
Понятие «нормальный язык» весьма субъективное.

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

К примеру возьмем PHP или Python и задачи связанные с обработкой потоков ввода/вывода. Да, в PHP и в Python есть корутины, да, есть lib-ev и lib-uv биндинги, но в JS это все из коробки. То есть мы берем node.js и получаем готовый фреймворк, все API которого имеет асинхронные функции. В Python ситуация не такая плохая как в PHP но так же не без проблем. Ну и Java так же, большинство не умеют работать с потоками и запорят все локами.

p.s. В статье кто-то кого-то агитирует переходить с какого-то языка на какой-то?
Я начал свои первые шаги в программировании на Си в 1997 за прошедшее время я успел поработать на всех языках из вашего списка, а также на многих, которые не вошли в ваш список.

С 2011 года фанат node.js. С 2014 фанат SpiderMonkey. Попробуйте меня переубедить, чем ваши языки более «нормальнее», чем JS.

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

(Хотя на нем теперь и сервера и десктоп пишут помимо веб-апп).


Вы, однако, проснулись! На JS сегодня реализуются не только сайты (серверная и браузерная часть), node.js используют везде где требуется обработка i/o-steaming. Еще на нем пишут и просто скрипты, и все самые крутые парсеры, >60% интерфейсов мобильных приложений. Чёрт возьми, у меня даже оболочка ОС Gnome-Shell написана 100% на JS. Да, думаю и в последних версиях Windows, во всяких плиточных интерфейсах, тоже где-то существуют js-виджеты.

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


Скажу по секрету, именно с этих ЯП С/C#/С++/Java/Python/PHP и переходят на JS. На JS переходят гораздо меньше людей, программирующих на perl, ruby, go, scala и проч. Но, последних по сравнению даже с аудиторией JS, — совсем меньшинство.
К вопросу о desktop-приложениях. Хочу оставить пару ссылок здесь (для всех сомневающихся):
https://developer.mozilla.org/en-US/docs/Web/API
http://www.roojs.com/seed/gir-1.2-gtk-3.0/gjs/

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

И в конце концов, обратите свое внимание на opensource-решения!
Последние пару лет с большой радостью переписываю многие библиотеки с С++ и Python на JavaScript.
Объяснений этому множество:

1. Лучший на сегодняшний день пакетный менеджер — `npm`
2. Огромное количество качественных пакетов практически под любую задачу
3. Шикарные таск-менеджеры типа Grunt, Gulp, Webpack
4. Отличные инструменты для всех видов тестирования (Karma, Mocha, ассерты типа chai, navit, движки типа Phantome, Electron, Slimer и пр.)
5. Возможность писать десктопные приложения (Node-Webkit)
6. Очень низкий порог вхождения
7. Простой и комфортный синтаксис (ES6)
8. Огромное комьюнити
9. Платформонезависимый код
10. Высокая скорость разработки приложений
11. Масса архитектурных фреймворков
12. Множество трансляторов генерирующих качественный код
Можно, конечно, вынести все, что связано с форматированием, в отдельную функцию formatMessage() и пропустить через нее поздравление во всех функциях. Но давайте для начала попробуем отразить предметную область с помощью ООП

Объясните пожалуйста, какую предметную область вы хотите отразить? Не совсем понятно, какую идеалогическую базу вы здесь вообще используете? На основе каких суждений вы ВНЕЗАПНО перевели повествование все в плоскость ООП?
Зайду издалека. Создателям больших фреймворков без физической предметной области, ничего не мешает использовать ООП для решения своих задач. ExtJS со своей коллекцией виджетов яркое этому подтверждение. Итого, у ExtJS предметная область UI, у меня — некие поздравляющие сущности.

Но все-таки искать какую-то серьезную идеалогоческую базу в этом игрушечном примере не надо (комментарий об этом есть в статье). Основная цель статьи заключалась в том, чтобы показать как одна и та же задача решается в ES5 и ES2015. То что пришлось лезть в дебри «composition vs. inheritance» объясняется тем, что мне показалось интересным сделать небольшой экскурс, чтобы назначение Object.assign() было более очевидным.
'use strict';

class Greeter {
    doGreeting(msg) {
        console.log(msg);
    }
}

class NewYearGreeter extends Greeter {
    constructor(currentYear) {
        super();
        this.currentYear = currentYear;
    }

    doGreeting() {
        let year = this.currentYear + 1;
        let newYearMsg = 'Happy New ' + year + ' Year!';
        super.doGreeting(newYearMsg);
    }
}

let newYearGreeter = new NewYearGreeter(2015);
newYearGreeter.doGreeting();


Поясните пожалуйста, как так получилось, что в классе NewYearGreeter метод doGreeting потерял параметр msg?
Greeter#doGreeting() объявлен с параметром.
NewYearGreeter наследуется от Greeter и является дочерним по отношению к нему.
NewYearGreeter#doGreeting() объявлен без параметров.

Внутри собственного кода NewYearGreeter#doGreeting() вызывает Greeter#doGreeting() вот этой строкой super.doGreeting(newYearMsg); — вызов с параметром.

Еще что непонятно?
NewYearGreeter#doGreeting() объявлен без параметров.

Вот это и странно, ведь в базовом классе параметр есть, а в классе наследнике параметра нет. Видимо у меня другое представление о наследовании, где класс наследник может либо иметь свои собственные методы, либо оверрайдить методы класса родителя. В данном случае, наследник NewYearGreeter, если оверрайдит метод doGreeting родителя, обязан принимать параметр msg.
В JS еще и не такое возможно. У NewYearGreeter#doGreeting() здесь нет параметров, но мог бы быть, например, один, но совсем другого назначения, либо даже два или, вообще, переменное множество.
Про переменное множество параметров мне известно, а про наследование, где можно оверрайдить родительские методы с необязательным повторением сигнатуры не знал.
В JS с этим есть кое-какая свобода. Нет интерфейсов (как в Java), которые необходимо соблюдать. Ровно как и типов данных при определении классов или функций, если критичен тип значения, проверяется вручную в теле функции.
В С++ подобное тоже можно делать, только там это называется не overriding, а hiding. В Java, насколько я знаю, нельзя менять сигнатуру.
Этот же пример на С++. Ничего фантастического.

#include <iostream>

class A {
	public:
   		int call (const int &year) {
   	  		return year;
   		}
};

class B : public A {
	public:
   		int call () {
   			return A::call(2015);
   		}
};


int main() {
	B b;
	
	std::cout << b.call();
	

	return 0;
}
Если верно понимаю, в спп можно будет и два метода сразу прописать и call() и call(const int &year) и это будут два разных метода, и в зависимости от того будет лит передан параметр или нет, выполнится тот или иной метод. В JS аналогично?
Две версии call в одном namespace сделать просто не получится, выдаст ошибку.
У JS нет как таковых типов данных, переменные могут менять типы данных по ходу выполнения программы.

Если мы можем создать несколько методов call в JAVA или C++:
void B::call(int year)
void B::call(String year)

в JS мы можем создать один метод или просто функцию
call(year);

Например для простоты эксперимента:
function call(year){
   if(typeof year == 'string') {
      year = parseInt(year);
   }
   return year;
}


Тогда:
call(2016); // вернет => 2016
call("2016"); // также => 2016
call(); // теперь => undefined
call(2005, 2016); // и опять => 2005

При этом все вызовы с точки зрения JS верны.
В JS любой «метод»(функция) может принимать любое число параметров. Лишние игнорируются, недостающие считаются undefined.
NewYearGreeter#doGreeting(msg) тоже небось доступен? Тогда это не override, а overload…
Он доступен, но не в виде перегруженной функции, если вызывать так:
newYearGreeter.doGreeting(123)

мы попадем в тот же самый метод, и сколько будет передано параметров, разницы нет никакой, т.к. в теле метода аргументы не обрабатываются.
По идее, метод c параметром доступен как-то так:
newYearGreeter.prototype.prototype.doGreeting.call(newYearGreeter, 2016);

Проверять некогда, улетаю.
А как там дела с модульностью в новом ES6? Будет/поддерживает ли CommonJS или что-то такое? Или нужно все еще использовать RequireJS? Спасибо за обзор
С модульностью все ок. Реализация модульности в es6 напоминает модульность в python (import… from '...') hacks.mozilla.org/2015/08/es6-in-depth-modules. Поддержка в браузерах на данный момент (январь 2016) на эксперементальном уровне, но можно использовать babel для транспиляции, в своих production проектах мы так и делаем.
Sign up to leave a comment.

Articles