a humble software engineer
117,7
рейтинг
1 января в 13:35

Разработка → Обзор возможностей современного JavaScript

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);
Александр Мышов @Myshov
карма
91,2
рейтинг 117,7
a humble software engineer
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

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

  • +9
    Навскидку:

    let sayHappyNewYear = year => 
          `Happy New ${year} Year!`;    
    
    sayHappyNewYear(2016);
    
    • +5
      Зашёл сюда увидеть этот комментарий, потому что блогозапись с упоминанием ECMAScript 2015 и без использования строк-шаблонов напрашивалася на этот комментарий.
    • +3
      Все же есть разница между функциями и функциональными выражениями. Конкретно в этом случае — это должна быть обычная функция, а использование стрелочных функций в этом контексте существенно снижает читабельность.

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

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

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


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

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

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

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

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


            Говоря об удобстве… Я предпочитаю описывать функции в порядке убывания их значимости. То есть, функция, ради которой пишется весь модуль объявлена выше всех. А всякие helpers/wrappers болтаются в самом низу, работу таких можно предсказать и по названию функции. Ну, если использовать любую IDE, становится все равно, в каком месте определена функция, был бы к ней доступ. Не системные же утилиты пишете! Вроде бы, почти не осталось мамонтов, использующих vim/emacs для кодинга на javascript.
            • 0
              Есть и более-менее современные редакторы, которые при этом и не совсем IDE и не умеют так умно прыгать по коду, как WebStorm, например. Лично я пользуюсь Atom, в котором вроде бы как и можно эту штуку добавить отдельным плагином, но он работает чуть хуже, чем в WebStorm. Кроме того, иногда приходится код читать онлайн, на гитхабе, к примеру, и тогда хорошо, когда код легко читается сам по себе.
          • 0
            Бывало тоже приходилось писать подобный код, но я все-таки предпочел тот вариант, который вы назвали не очень удобным. Я согласен, что это и правда не самый удобный вариант, однако, мне такой вариант показаля более очевидным. Думаю больше дело вкусов и привычек.
  • –1
    Ради всего святого прочтите статью Your coding conventions are hurting you.
    • +4
      Carlo, несомненно, умный мужик. Но в данном случае greeter более чем подходящее название для игрушечного примера несмотря на этот злополучный суффикс -er. В любом случае очередные примеры с Animal, Dog или Shape, Square были бы скучнее.
  • +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.
    • 0
      В следующий раз когда мне понадобится использовать некую абстрактную сущность, которая всех поздравляет, я назову ее GreetingMachine. Хотя, если быть честным Greeter здесь не хуже какого-нибудь Animal. Разве не так?
      • +4
        В следующий раз когда я буду писать нечто подобное я назову класс ВеселыйРождественскийЭльфКоторыйЖиветВКомпьютереИПечатаетНаЭкранеВеселыеПоздравленияСНовымГодом.
  • 0
    Расскажите пожалуйста про принцип: «меньше больше — больше меньше»
    • +1
      Эту фразу как-то сказал мой очень хороший знакомый. Я понимаю ее так:

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

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

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


      С обычной функцией без замыкания, такое было бы невозможно.
      • +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
          Ну, всем не угодить. С подобным примером я бы получил комментарии про оверинжениринг.
  • +5
    Вы это реально называете композицией и используете?

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

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

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

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

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

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

    kangax.github.io/compat-table/es6
    • 0
      Вот что праздники с людьми делают. ES6 это ES5++
      • –1
        Не знаю что там и с кем сделали праздники, но нативную поддержку ES6 во всех броузерах они пока еще не сделали.
        • +2
          на сегодняшний день поддержка ES6 в последних версиях браузеров нативно и с флагами — 80% от стандарта. Что как мне кажется весьма неплохо. (статус на 31-ое декабря)

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

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

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

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

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

          p.s. В статье кто-то кого-то агитирует переходить с какого-то языка на какой-то?
        • +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
            К вопросу о 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-решения!
    • +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. Множество трансляторов генерирующих качественный код
  • 0
    Можно, конечно, вынести все, что связано с форматированием, в отдельную функцию formatMessage() и пропустить через нее поздравление во всех функциях. Но давайте для начала попробуем отразить предметную область с помощью ООП

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

      Но все-таки искать какую-то серьезную идеалогоческую базу в этом игрушечном примере не надо (комментарий об этом есть в статье). Основная цель статьи заключалась в том, чтобы показать как одна и та же задача решается в ES5 и ES2015. То что пришлось лезть в дебри «composition vs. inheritance» объясняется тем, что мне показалось интересным сделать небольшой экскурс, чтобы назначение Object.assign() было более очевидным.
  • 0
    '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?
    • 0
      Greeter#doGreeting() объявлен с параметром.
      NewYearGreeter наследуется от Greeter и является дочерним по отношению к нему.
      NewYearGreeter#doGreeting() объявлен без параметров.

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

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

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

            #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;
            }
            
            • 0
              Если верно понимаю, в спп можно будет и два метода сразу прописать и call() и call(const int &year) и это будут два разных метода, и в зависимости от того будет лит передан параметр или нет, выполнится тот или иной метод. В JS аналогично?
              • –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
          В JS любой «метод»(функция) может принимать любое число параметров. Лишние игнорируются, недостающие считаются undefined.
      • 0
        NewYearGreeter#doGreeting(msg) тоже небось доступен? Тогда это не override, а overload…
        • 0
          Он доступен, но не в виде перегруженной функции, если вызывать так:
          newYearGreeter.doGreeting(123)
          

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

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

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