Синтаксический сахар - ряд усовершенствований в синтаксисе языка, не несущих семантического смысла. Обычно синтаксический сахар легко заменяется более пространным и менее прозрачным синтаксисом, и при этом код работает так же. Отличия между ES5 и ES6 в-основном в сахаре и заключаются.
Синтаксически это =>
, параметры до этой пары символов, выражение-результат функции - после:
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:
var func = (function(x, y, z){
console.log(this)
return x + y + z
}).bind(this) //bind прибивает this при объявлении функции
Знакомы некоторым по другим языкам:
var pow = sqr = (x, power = 2) => Math.pow(x, power)
console.log(pow(2,5))
console.log(sqr(2)) //квадрат по умолчанию
В ES5 для этого обычно использовали проверку на undefined
:
var pow = sqr = function(x, power){
return Math.pow(x, power || 2)
}
console.log(pow(2,5))
console.log(sqr(2)) //квадрат по умолчанию
...
Теперь можно, при вызове указать массив в качестве параметров, или же, наоборот, получить параметры как массив:
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
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()
Деструктуризация позволяет доставать значения полей объектов и массивов по нескольку за одну операцию и избавляет вас от заведения временной переменной для хранения массива или объекта:
var [a,b,c] = [1,2,3] //создается три переменных из массива
var {rand1: x, rand2: y, rand3: z} = {rand1: Math.random(), rand2: Math.random(), rand3: Math.random()}
console.log(0b1010)
console.log(0777)
console.log(0o555)
for .. of
Работает как for in
(почти), но перебирает значения, а не ключи:
for (let word of ['foo','bar']){
console.log(word)
}
Символ ` (backtick, обратная кавычка, находится на Ё
) позволяет вставить выражение в строку без использования конкатенации:
alert(`Привет, ${prompt('Имя?')}`)
В синтаксисе ES6 появились классы как их привыкли видеть в других языках:
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:
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 классов нет на уровне интерпретатора, ООП все еще остается прототипным.
Отличия:
new
, в отличие от функции-конструктора.let
.strict
режиме.for in
их не видит.super
используется для вызова конструктора (super()
) или метода (super.someMethod
) предка.Как и с анонимными функциями, класс может быть анонимный:
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
кажется что они привязаны к объекту.
class Singleton {
static get instance(){
if (!Singleton._instance){
Singleton._instance = new Singleton()
}
return Singleton._instance;
}
}
console.log(Singleton.instance);
Выше вы видите реализацию паттерна Singleton, которая заключается в том, что в классе существует только один экземпляр (объект). Данный объект
лежит (фактически) в _instance
, однако доступен по статическому геттеру instance
. Последний при надобности создает экземпляр (при первом обращении).