# Синтаксический сахар в ES6. *Синтаксический сахар* - ряд усовершенствований в синтаксисе языка, не несущих семантического смысла. Обычно синтаксический сахар легко заменяется более пространным и менее прозрачным синтаксисом, и при этом код работает так же. Отличия между **ES5** и **ES6** в-основном в сахаре и заключаются. ## Стрелочные функции. Синтаксически это `=>`, параметры до этой пары символов, выражение-результат функции - после: ```javascript var sqr = x => x*x //один параметр, скобки не нужны alert(sqr(2)) var mul = (x,y) => x*y //два параметра - нужны скобки alert(mul(2,5)) var objectCreator = (x,y) => ({x, y}) //создает объект {x: x, y: y}. Если забыть скобки - то JS воспримет {x,y} как блок кода, и без return вернет undefined console.log(objectCreator(4,5)) var longFunc = (x,y) => { //почти обычная функция let result = prompt(x,y) return result; } longFunc('check', 'text') ({arrowFuncMaker: function(){ return () => {console.log(this), this.oldSchoolSklerotikMaker()()} }, oldSchoolSklerotikMaker: function(){ return function(){ console.log(this) } } }).arrowFuncMaker()() //стрелочные функции помнят this на момент создания. ``` Свойства стрелочных функций: - Они анонимные. Синтаксис не предусматривает имени функции. - Они помнят `this`. В целом стрелочные функции могут быть заменены следующим синтаксисом **ES5**: ```javascript var func = (function(x, y, z){ console.log(this) return x + y + z }).bind(this) //bind прибивает this при объявлении функции ``` ## Параметры по умолчанию. Знакомы некоторым по другим языкам: ```javascript var pow = sqr = (x, power = 2) => Math.pow(x, power) console.log(pow(2,5)) console.log(sqr(2)) //квадрат по умолчанию ``` В **ES5** для этого обычно использовали проверку на `undefined`: ```javascript var pow = sqr = function(x, power){ return Math.pow(x, power || 2) } console.log(pow(2,5)) console.log(sqr(2)) //квадрат по умолчанию ``` ## rest/spread `...` Теперь можно, при вызове указать массив в качестве параметров, или же, наоборот, получить параметры как массив: ```javascript var func = function(a,b,...rest){ console.log(a,b,rest,arguments) } func(1,2,3) func(...[1,2,3,4,5]) ``` Подобные возможности в **ES5** реализуются через `arguments` и `apply` ## Литералы объектов ```javascript var name = 'John' var age = Math.round(Math.random()*100) var obj = { name, //name: name ["a" + 'ge']: age, //выражение в ключе, greet() { alert(this.name + ', ' + this.age) } } obj.greet() ``` ## Деструктуризация **Деструктуризация** позволяет доставать значения полей объектов и массивов по нескольку за одну операцию и избавляет вас от заведения временной переменной для хранения массива или объекта: ```javascript var [a,b,c] = [1,2,3] //создается три переменных из массива var {rand1: x, rand2: y, rand3: z} = {rand1: Math.random(), rand2: Math.random(), rand3: Math.random()} ``` ## Восьмеричные и двоичные литералы ```javascript console.log(0b1010) console.log(0777) console.log(0o555) ``` ## `for .. of` Работает как `for in` (почти), но перебирает *значения*, а не *ключи*: ```javascript for (let word of ['foo','bar']){ console.log(word) } ``` ## Шаблонизация строк `` ` Символ `` ` (*backtick*, обратная кавычка, находится на `Ё`) позволяет вставить выражение в строку без использования конкатенации: ```javascript alert(`Привет, ${prompt('Имя?')}`) ``` ## ES6 ООП. В синтаксисе **ES6** появились *классы* как их привыкли видеть в других языках: ```javascript class Parent { constructor(name){ console.log(`parent constructor: ${name}`) this.name = name; } someMethod(){ console.log('Parent method') } } class Child extends Parent { constructor(name){ super(name) //обязательный вызов родительского констуктора console.log(`child constructor: ${name}`) } someMethod(){ console.log(`Child method ${this.name}`) super.someMethod() //обращение к родительскому методу } } let child = new Child('Vasya') child.someMethod() ``` Методы в классе попадают в прототип, `constructor` - аналог функции конструктора из **ES5**: ```javascript function Parent(name){ console.log(`parent constructor: ${name}`) this.name = name; } Parent.prototype.someMethod = function(){ console.log('Parent method') } function Child(name){ Parent.apply(this, arguments) console.log(`child constructor: ${name}`) } Child.prototype = Object.create(Parent.prototype) Child.prototype.someMethod = function(){ console.log(`Child method ${this.name}`) this.__proto__.__proto__.someMethod.call(this) } let child = new Child('Vasya') child.someMethod() ``` Синтаксис **ES6** нагляднее, однако скрывает внутреннюю реализацию. Что в ES5, что в ES6 классов нет на уровне интерпретатора, ООП все еще остается прототипным. **Отличия**: - В **ES6** нельзя вызвать класс без `new`, в отличие от функции-конструктора. - Имя класса объявлено через `let`. - Методы лежат в прототипы - Методы работают в `strict` режиме. - Методы не перечислимы, `for in` их не видит. - `super` используется для вызова конструктора (`super()`) или метода (`super.someMethod`) предка. ### Class Expression Как и с анонимными функциями, класс может быть анонимный: ```javascript function classCreator(methodName, method){ let myClass = class { constructor(name){ this._name = name; } get name(){ return this._name; } set name(newName){ this._name = newName; } } myClass.prototype[methodName] = method; return myClass } var Random = classCreator('random', Math.random) var Counter = classCreator('inc', function(){ if (!this.counter){ this.counter = 0; } return this.counter++ }) var rand = new Random('random') console.log(rand.name) console.log(rand.random()) var counter = new Counter('counter') console.log(counter.name) counter.name = 'looks like string key in object, but it\'s a set method call'; console.log(counter.name) console.log(counter.inc()) console.log(counter.inc()) console.log(counter.inc()) ``` Пример выше реализует идею мета-класса (или метаконструктора) - функция-конструктор создает **не объект** а **класс**. В данном случае создается класс с методом-примесью, переданной в метаконструктор. Если класс не создавать внутри, а передавать снаружи, можно сделать удобные функции, которые добавляют интерфейс (или примесь) к другому классу. Так же обратите внимание на **геттер** (свойство) `name`. При использовании объекта это выглядит как обычные данные в объекте, а по факту при чтении или записи в такое свойство вызываются методы `get name()` или `set name()` объекта класса. ### Статические методы и свойства Класс так же может обладать **статическими** (т. е. общими для всего *класса*, а не для каждого объекта) методами. Свойства не предусмотрены, однако легко решаются через статический геттер или сеттер. На самом деле методы в прототипе тоже находятся в единственном экземпляре, однако за счет работы с `this` кажется что они привязаны к объекту. ```javascript class Singleton { static get instance(){ if (!Singleton._instance){ Singleton._instance = new Singleton() } return Singleton._instance; } } console.log(Singleton.instance); ``` Выше вы видите реализацию паттерна **Singleton**, которая заключается в том, что в классе существует только один экземпляр (объект). Данный объект лежит (фактически) в `_instance`, однако доступен по статическому геттеру `instance`. Последний при надобности создает экземпляр (при первом обращении).