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

В ушедшем году, вышел стандарт ECMAScript 2015 (неформально ES6), который сильно изменил, то к чему мы привыкли. Появилась масса новых возможностей, которые по сути представляют собой современное надмножество языка, пытающегося решить существующие проблемы. Class, let, const, стрелочные функции… разработчик, который ранее не видел код, написанный на ES6, не сразу догадается, что перед ним, по сути, старый добрый JS.

Есть масса прекрасных статей, посвященных современному стандарту. В этом же посте я хочу показать, что нам может предложить современный JS, когда необходимо решить насущную задачу. Например, поздравить всех c Новым Годом.

Императивный подход


Самый простое решение, сделать все императивно. Да, действительно, если следовать принципу «меньше больше — больше меньше», то это будет самое прагматичное решение:

function sayHappyNewYear(year){
    console.log('Happy New ' + year + ' Year!');
}
    
sayHappyNewYear(2016);


Решение простое, и оно будет работать во всех средах, где есть реализация объекта console, но в нем есть изъян: у нас есть не только Новый Год, но и множество других праздников. Создавать для каждого праздника свою отдельную функцию не очень разумное занятие. Если нам необходимо отформатировать сообщения однообразно, то при изменении формата сообщения необходимо будет изменить соответствующий код во всех функциях (да, примеры в статье получились немного искусственными). Можно, конечно, вынести все, что связано с форматированием, в отдельную функцию formatMessage() и пропустить через нее поздравление во всех функциях. Но давайте для начала попробуем отразить предметную область с помощью ООП и посмотреть, чем нам может помочь JavaScript в этой ситуации.

Объектно-ориентированный подход


Как всем вам хорошо известно в JS можно писать в объектно-ориентированном стиле с наследованием на базе прототипов:

function Greeter() {}

Greeter.prototype.doGreeting = function(msg) {
    console.log(msg);
}

function NewYearGreeter(currentYear) {
    this.currentYear = currentYear;
}

NewYearGreeter.prototype = Object.create(Greeter.prototype);
NewYearGreeter.prototype.constructor = NewYearGreeter;

NewYearGreeter.prototype.doGreeting = function() {
    var year = this.currentYear + 1;
    var newYearMsg = 'Happy New ' + year + ' Year!';
    Greeter.prototype.doGreeting.call(this, newYearMsg);
}

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


Получилось довольно многословное решение, плюс которого в том, что этот код будет работать во всех современных runtime-средах (браузеры, Node.js). Именно из-за многословности реализации объектно-ориентированного подхода в ES6 появились классы, которые знакомы любому программисту, владеющему C++, Java, C#, Python etc. Вот таким образом будет выглядеть пример выше, если использовать классы ES6:

'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();


Выглядит уже симпатичнее. Но как вы наверное знаете было много споров вокруг классов в JS. Были ярые противники этого новшества. В целом их позиция была ясна — ООП в перспективе порождает проблему «гориллы и банана»:

Проблема объектно-ориентированных языков в том, что они тащат с собой всё своё неявное окружение. Вам нужен был банан – а вы получаете гориллу с бананом, и целые джунгли впридачу.
Джо Армстронг «Coders at Work»

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

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

Предпочитайте объектную композицию наследованию классов
Гамма, Хелм, Джонсон, Влиссидс «Приёмы объектно-ориентированного проектирования. Паттерны проектирования.»

В JS, композицию можно реализовать разными способами. Вот одно из решений, которое будет работать во всех современных runtime-средах:

function extend(destination, source) {
    for (var key in source) {
        if (source.hasOwnProperty(key)) {
            destination[key] = source[key];
        }
    }
}

function Greeter() {
    this.doGreeting = function(msg) {
        console.log(msg);
    }
}

function NewYearGreeter(year) {
    this.year = year;

    this.doNewYearGreeting = function() {
        var newYearMsg = 'Happy New ' + this.year + ' Year!';
        this.doGreeting(newYearMsg);
    }
}

var greeter = new Greeter;
var newYearGreeter = new NewYearGreeter(2016);
extend(newYearGreeter, greeter);

newYearGreeter.doNewYearGreeting();


В этом примере с помощью конструкторов создаются объекты, свойства которых (методы) компонуются в одной сущности (объект newYearGreeter) с помощью служебной функции extend. Современный стандарт позволяет упростить реализацию композиции с помощью Object.assign():

'use strict';
let greeter = {
    doGreeting(msg) {
        console.log(msg);
    }
};

let newYearGreeter = {
    setYear(year) {
        this.year = year;
    },

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

Object.assign(newYearGreeter, greeter);
newYearGreeter.setYear(2016);

newYearGreeter.doNewYearGreeting();


Object.assign() по сути делает тоже самое, что и extend, за тем исключением, что его можно использовать «из коробки» при этом компоновать можно любое количество объектов.

Это объектная сторона вопроса. Но JS также предоставляет средства для программирования в функциональном стиле.

Функциональный стиль


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

function createGreeter(msg) {
    return function(param) {
        return msg.replace(/%.*%/, param);
    }
}

var greetWithNewYear = createGreeter('Happy New %year% Year!');
console.log(greetWithNewYear(2016));


В ES6 из средств для упрощения программирования в функциональном стиле, самое заметное нововведение — стрелочные функции. Стрелочная функция — это анонимная функция (например, функция сразу после return из примера выше) или лямбда-выражение, как говорит народ из функционального лагеря. Вот таким образом преобразуется наш пример, если мы будем использовать «толстую стрелку»:

function createGreeter(msg) {
    return param => msg.replace(/%.*%/, param);
}

var greetWithNewYear = createGreeter('Happy New %year% Year!');
console.log(greetWithNewYear(2016));


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

Что почитать / посмотреть?


Блог Акселя Раушмайера:

Серия статей ES6 in Depth на mozilla.org:

Youtube-канал Маттиаса Йохонсона:


Итоги


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

(_ => ++_)(2015);

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


  1. monolithed
    01.01.2016 14:53
    +9

    Навскидку:

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


    1. Mithgol
      01.01.2016 23:12
      +5

      Зашёл сюда увидеть этот комментарий, потому что блогозапись с упоминанием ECMAScript 2015 и без использования строк-шаблонов напрашивалася на этот комментарий.


    1. Fesor
      01.01.2016 23:29
      +3

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

      // функция суммы элементов - обычная функция
      function sum(arr) {
          
          // а вот тут arrow-functions упрощают код.
          return arr.reduce((sum, item) => sum + item, 0);
      }
      


    1. ckr
      01.01.2016 23:38
      +1

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

      • Во-первых, стрелочная функция запускается всегда с контекстом того места, где она описана. То есть, .apply() и .call() использовать с такими функциями бесполезно.
      • Во-вторых, стрелочные функции не создают объекта arguments, который можно было бы использовать в теле стрелочной функции
      • В-третьих, несмотря на явное название функции sayHappyNewYear, она всегда остается анонимной.
      • В-четвертых, (не самое главное) определив функцию обычным образом function sayHappyNewYear(year) { ... }, например, в глобальном окружении, функцию можно использовать где угодно, хоть после определения, хоть до определения. Переменные, определенные через let, доступны для использования лишь после определения.


      1. monolithed
        02.01.2016 00:04
        +1

        Ну так это в статье нужно писать, а не в комментариях.


        1. monolithed
          02.01.2016 01:11
          +1

          + Такие выражения нельзя использовать в качестве конструктора поскольку отсутствует у них prototype и [[Construct]]:

          let foo = new (() => {}); // TypeError
          let foo = () => { super() }; // SyntaxError
          let foo = () => { new.target }; // SyntaxError
          


          1. ckr
            02.01.2016 01:31

            Вы правы. Но, слава богу, для конструкторов есть class и constructor.


      1. Myshov
        02.01.2016 04:53

        Если функция в дальнейшем будет использоваться как «чистая» почему бы не использовать толстую стрелку после return? Если нужен контекст, то да надо вернуть function.


        1. ckr
          02.01.2016 07:21

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

          function sayHappyNewYear(year) {
             return cb => cb(null, `Happy New ${year} Year!`);
          }
          
          В остальных случаях использую что-либо отличное от стрелочных функций.


      1. Serator
        02.01.2016 16:24
        +1

        2-е легко решается оператором распространения (spread operator):

        myFunction(...iterableObj);
        


        1. Fesor
          02.01.2016 17:56
          +1

          оно то решается — вопрос зачем.


      1. Ohar
        03.01.2016 01:10

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


        1. ckr
          03.01.2016 08:21
          +1

          У функции в примере нет названия. Она присваивается именованной переменной.


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

          Может быть, для браузеров код выглядит необычно, но, например, разбираться в чужих middleware, когда ~30% из них анонимные функции, похоже на ад. — И в итоге, все равно, нужно прочесть и разобраться во 100% кода приложения.


      1. bogus92
        03.01.2016 05:39

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

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


        1. ckr
          03.01.2016 08:05
          -1

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

          let a, b;
          
          a = function () {
             b ();
          }
          
          b = function() {
             a ();
          }
          

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


          1. ckr
            03.01.2016 08:37

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


            Говоря об удобстве… Я предпочитаю описывать функции в порядке убывания их значимости. То есть, функция, ради которой пишется весь модуль объявлена выше всех. А всякие helpers/wrappers болтаются в самом низу, работу таких можно предсказать и по названию функции. Ну, если использовать любую IDE, становится все равно, в каком месте определена функция, был бы к ней доступ. Не системные же утилиты пишете! Вроде бы, почти не осталось мамонтов, использующих vim/emacs для кодинга на javascript.


            1. bogus92
              04.01.2016 02:26

              Есть и более-менее современные редакторы, которые при этом и не совсем IDE и не умеют так умно прыгать по коду, как WebStorm, например. Лично я пользуюсь Atom, в котором вроде бы как и можно эту штуку добавить отдельным плагином, но он работает чуть хуже, чем в WebStorm. Кроме того, иногда приходится код читать онлайн, на гитхабе, к примеру, и тогда хорошо, когда код легко читается сам по себе.


          1. bogus92
            04.01.2016 02:23

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


  1. zahardzhan
    01.01.2016 15:07
    -1

    Ради всего святого прочтите статью Your coding conventions are hurting you.


    1. Myshov
      01.01.2016 15:55
      +4

      Carlo, несомненно, умный мужик. Но в данном случае greeter более чем подходящее название для игрушечного примера несмотря на этот злополучный суффикс -er. В любом случае очередные примеры с Animal, Dog или Shape, Square были бы скучнее.


  1. zahardzhan
    01.01.2016 16:26
    +1

    Может быть так оно и есть, но если заглянуть в толковый словарь и сопоставить значения слов со здравым смыслом:
    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.


    1. Myshov
      01.01.2016 16:31

      В следующий раз когда мне понадобится использовать некую абстрактную сущность, которая всех поздравляет, я назову ее GreetingMachine. Хотя, если быть честным Greeter здесь не хуже какого-нибудь Animal. Разве не так?


      1. zahardzhan
        01.01.2016 16:39
        +4

        В следующий раз когда я буду писать нечто подобное я назову класс ВеселыйРождественскийЭльфКоторыйЖиветВКомпьютереИПечатаетНаЭкранеВеселыеПоздравленияСНовымГодом.


  1. mtt
    01.01.2016 18:43

    Расскажите пожалуйста про принцип: «меньше больше — больше меньше»


    1. Myshov
      01.01.2016 19:12
      +1

      Эту фразу как-то сказал мой очень хороший знакомый. Я понимаю ее так:

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

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


      1. Nashev
        03.01.2016 01:31
        +2

        То есть, меньше программа — больше подпрограммы, больше программа — меньше подпрограммы


        1. Fesor
          03.01.2016 03:16

          Less is more — The notion that simplicity and clarity lead to good design.


    1. Fesor
      01.01.2016 21:38

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


    1. Fesor
      01.01.2016 22:14

      По ссылке, так сказать, история этого устойчивого выражения с объяснением смысла. Less is more


  1. stalkerg
    01.01.2016 18:43
    +4

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


    1. Myshov
      01.01.2016 19:35

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

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


      С обычной функцией без замыкания, такое было бы невозможно.


      1. monolithed
        01.01.2016 20:18
        +1

        Выбрали бы тогда хоть пример поинтересней:

        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 });
        


        1. Myshov
          02.01.2016 05:08
          +1

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


  1. Viacheslav01
    02.01.2016 01:23
    +5

    Вы это реально называете композицией и используете?

    var greeter = new Greeter;
    var newYearGreeter = new NewYearGreeter(2016);
    extend(newYearGreeter, greeter);
    


    1. Myshov
      02.01.2016 05:19
      -1

      Я ведь писал в статье про то, что пример получился искусственный. Именно поэтому дал ссылку на видео Маттиаса.


  1. Fesor
    02.01.2016 01:38
    +1

    Статья несет несколько провокационный и популистский характер. И при этом практически никакой информации о ES6 и его возможностях (кроме того о чем пишут уже пол года все кому не лень). Только очередной повод разогреть холивар «неправильный ООП» vs «функциональная чистота».

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

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

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

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


    1. Myshov
      02.01.2016 05:04
      -1

      На самом деле никакого злого умысла в эту статью я не вкладывал.
      Где я обидел python не ясно.

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


  1. PerlPower
    02.01.2016 03:50
    -6

    ES6 — еще один язык программирования имеющий транслятор в ES5, который поддерживается всеми основными броузерами.

    kangax.github.io/compat-table/es6


    1. Fesor
      02.01.2016 13:15

      Вот что праздники с людьми делают. ES6 это ES5++


      1. PerlPower
        02.01.2016 14:35
        -1

        Не знаю что там и с кем сделали праздники, но нативную поддержку ES6 во всех броузерах они пока еще не сделали.


        1. Fesor
          02.01.2016 14:53
          +2

          на сегодняшний день поддержка ES6 в последних версиях браузеров нативно и с флагами — 80% от стандарта. Что как мне кажется весьма неплохо. (статус на 31-ое декабря)

          Да и есть же babel и core-js. В подавляющем большинстве случаев профит от использования подобных вещей, в виде скорости разработки, читабельности кода и как следствие большей поддерживаемости, перекрывает риски. Есть конечно задачи где риски слишком велики, но как я уже сказал — это очень небольшая доля задач и туда новичков не пускают обычно.


    1. Apathetic
      02.01.2016 19:24
      -4

      Извините, может это глупый вопрос, но что такое «броузер»?


  1. kozyabka
    02.01.2016 18:58
    -7

    Просто-напросто для реализации web-клиентов НЕТ альтернатив. Важно осознавать это, и не воспринимать эту технологию как нечто грандиозное или действительно крутое лишь потому, что нет аналогов.
    Возможно, стоит провести опрос, как много людей пишущих на нормальных ЯП с радостью перешли на JS в связи со всеми его возможностями… Я не знаю ни одного.


    1. Fesor
      02.01.2016 19:27
      +2

      как много людей пишущих на нормальных ЯП с радостью перешли на JS

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

      Вообще нынче модно иметь транспайлер с «нормального» в «ненормальный» языки. Scala.js, ClojureScript, Dart и куча других. Да и если присмотретья то ES6/7 — это уже нормальный JS. Ну и Web Assembly может еще чуть поменяет ситуацию.


      1. kozyabka
        02.01.2016 20:15
        -3

        С/C#/С++/Java/Python/PHP
        И речь не о том, что для каждой задачи свой ЯП, а о том, сколько людей пишущих на этих языках с радостью перешли на JS из-за таких ошеломляющих возможностей и перспектив. (Хотя на нем теперь и сервера и десктоп пишут помимо веб-апп).
        Кстати, товарищи минусовщики, был бы рад услышать с чем именно вы не согласны? С тем что для веб больше не существует альтернатив, или с тем что все сишники вдруг не перешли на js?


        1. Fesor
          02.01.2016 21:03
          +4

          Понятие «нормальный язык» весьма субъективное.

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

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

          p.s. В статье кто-то кого-то агитирует переходить с какого-то языка на какой-то?


        1. ckr
          02.01.2016 22:51
          +3

          Я начал свои первые шаги в программировании на Си в 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, — совсем меньшинство.


          1. ckr
            03.01.2016 00:26
            +1

            К вопросу о 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-решения!


    1. monolithed
      03.01.2016 01:26
      +2

      Последние пару лет с большой радостью переписываю многие библиотеки с С++ и 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. Множество трансляторов генерирующих качественный код


  1. zayko
    03.01.2016 07:55

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

    Объясните пожалуйста, какую предметную область вы хотите отразить? Не совсем понятно, какую идеалогическую базу вы здесь вообще используете? На основе каких суждений вы ВНЕЗАПНО перевели повествование все в плоскость ООП?


    1. Myshov
      03.01.2016 08:36
      +2

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

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


  1. kozyabka
    03.01.2016 08:44

    '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?


    1. ckr
      03.01.2016 08:54

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

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

      Еще что непонятно?


      1. kozyabka
        03.01.2016 09:02

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

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


        1. ckr
          03.01.2016 09:15

          В JS еще и не такое возможно. У NewYearGreeter#doGreeting() здесь нет параметров, но мог бы быть, например, один, но совсем другого назначения, либо даже два или, вообще, переменное множество.


          1. kozyabka
            03.01.2016 09:21

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


            1. ckr
              03.01.2016 09:34

              В JS с этим есть кое-какая свобода. Нет интерфейсов (как в Java), которые необходимо соблюдать. Ровно как и типов данных при определении классов или функций, если критичен тип значения, проверяется вручную в теле функции.


            1. Myshov
              03.01.2016 10:04

              В С++ подобное тоже можно делать, только там это называется не overriding, а hiding. В Java, насколько я знаю, нельзя менять сигнатуру.


              1. kozyabka
                04.01.2016 06:30

                (ниже опередили)


          1. monolithed
            04.01.2016 01:00

            Этот же пример на С++. Ничего фантастического.

            #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;
            }
            


            1. kozyabka
              04.01.2016 06:35

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


              1. ckr
                04.01.2016 22:01
                -3

                Две версии 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 верны.


        1. VolCh
          04.01.2016 21:37
          +1

          В JS любой «метод»(функция) может принимать любое число параметров. Лишние игнорируются, недостающие считаются undefined.


      1. Nashev
        03.01.2016 11:55

        NewYearGreeter#doGreeting(msg) тоже небось доступен? Тогда это не override, а overload…


        1. ckr
          03.01.2016 12:11

          Он доступен, но не в виде перегруженной функции, если вызывать так:

          newYearGreeter.doGreeting(123)
          

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

          Проверять некогда, улетаю.


  1. w1ld
    04.01.2016 16:06

    А как там дела с модульностью в новом ES6? Будет/поддерживает ли CommonJS или что-то такое? Или нужно все еще использовать RequireJS? Спасибо за обзор


    1. Myshov
      04.01.2016 16:38

      С модульностью все ок. Реализация модульности в es6 напоминает модульность в python (import… from '...') hacks.mozilla.org/2015/08/es6-in-depth-modules. Поддержка в браузерах на данный момент (январь 2016) на эксперементальном уровне, но можно использовать babel для транспиляции, в своих production проектах мы так и делаем.