ECMAScript 6+
Коротка історія JavaScript / ECMAScript
JavaScript - товарний знак, що належить компанії Oracle Corporation в США.
ECMAScript належить організації ECMA (European Computer Manufacturers Association) - міжнародній організації, що займається регулюванням та стандартизацією у сферах IT та комунікації.
Початкова розробка була здійснена в компанії Netscape, за прототип було взято скриптову мову Cmm (C--, ScriptEase), яка так і не здобула популярності. Змінивши декілька назв (Mocha, LiveScript/LiveWire) в 1995 році мова отримала назву JavaScript (під ліцензією Sun Microsystems, яку в 2010 році викупила Oracle).
Microsoft в 1996 році випустив аналогічну скриптову мову JScript, вбудував його в IE3.
Організація ECMA стандартизувала мови JavaScript, JScript і ScriptEase, прийнявши ECMAScript. Ці три скриптові мови є взаємосумісними, проте були ньюанси, варіанти реалізації BOM і DOM (дивись далі), що призводили до проблем крос-браузерності.
Між першою та другою версіями було багато мінорних версій: 1.1, 1.2 ... 1.8
ECMAScript 2 вийшов в червні 1998 року.
ECMAScript 3 вийшов у грудні 1999 року.
Робота над четвертою версією заглохла на пів-дорозі, вона так і не вийшла через сварки в консорціумі розробників, патенти, конкуренцію та лобіювання власних інтересів. Проте, технології, декларовані в цій версії, були реалізовані у різних браузерах (неофіційно).
ECMAScript 5 вийшов у грудні 2009 року (переназвана версія 3.1, яку розробили на заміну 4-ї версії, відкинувши всі серйозні нововведення і зосередившись на оптимізації).
Разом з впровадженням HTML5 та CSS3, виникненням нових концепцій веб-застосунків, прискоренням розробки браузерів (від 0,2-1 версії на рік до майже щомісячного реліза) ECMAScript також прискорився, впровадив багато нових можливостей, взяв курс на зближення з іншими мовами програмування.
ECMAScript 6 / 2015 — березень 2015 року.
Браузери не встигали завершити свій життєвий цикл, багато користувачів з різних причин сиділи на старих браузерах, що не підтримували нові можливості. Тому набули популярність транспайлери (конвертори нового JS в старий), наприклад, Babel, Traceur.
ECMAScript 7 / 2016.
ECMAScript 8 / 2017.
ECMAScript 9 / 2018.
Таким чином, JavaScript створюють та супроводжують декілька компаній: ECMA, Oracle, Microsoft, Mozilla. На деяких етапах історії також долучалися Adobe, Opera, Yahoo та інші компанії.
Загальний огляд JavaScript
JavaScript складається з трьох компонентів:
- ядро (ECMAScript);
- об'єктна модель браузера (BOM);
- об'єктна модель документа (DOM).
Таким чином, якщо JS використовується у вбудованій техніці, робототехніці, як мова сценаріїв (макросів) у програмах, іграх, то використовується лише ядро і додаткові інтерфейси для роботи з портами вводу/виводу, файловою системою і т.д., без BOM і DOM. Ядро і віртуальна машина доволі компактні і дозволяють вшити їх в ROM, CMOS-чіпи невеликого об'єму та розміру.
BOM та DOM не стандартизуються організацією ECMA, специфікації розробляють спільноти WHATWG і W3C, а доки немає специфікацій - кожна з компаній-розробників ліпить свої варіанти реалізацій, що якраз і веде до проблем кросбраузерності.
Нові можливості ECMAScript 6 / 2015
Похилим шрифтом в коді показані класичні реалізації
Оголошення констант const та локальних змінних let
Специфікація радить відмовитися від використання var.
const COLUMNS_COUNT = 12;
for (let n = 1; n <= COLUMNS_COUNT; n++) {
setTimeout(showMessage, n * 1000);
}
if (test) {
let str = 'hello';
...
}
Змінні n та str будуть існувати лише в межах фігурних дужок
Змінна n буде доступна в асинхронній функції showMessage зі значеннями 1, 2, 3...
Оголошені змінні через let будуть доступні лише нижче по коду, в той час як змінні, оголошені через var будуть мати значення undefined вище по коду.
Відмова від IIFE (immediately invoked function expression) - як наслідок з області видимості let, const:
(function (){
var x = 42;
})();
{
let x = 42;
}
Дефолтні значення аргументів функції
function fun(a, b = 6, c = false) { ... }
function fun(a, b, c) {
if (b === undefined) b = 6;
if (c === undefined) c = false;
}
Іменовані аргументи функцій
function fun1({ x = 10, y }) { ... }
function fun2(s, b, { x, y }) { ... }
fun1({ y: 30 });
fun1({ x: 5, y: 42 });
fun1({ y: 42, x: 5 });
fun2('Вася', false, { x: 30, y: 0 });
Деструктуризація об'єктів (іменовані аргументи функцій якраз і використовують деструктуризацію).
let arr = [40, 20, 0];
let [x, y, z] = arr;
let [a, , c] = arr;
let { name: n, age: a } = { name: 'Вася', age: 22 };
n === 'Вася', a === 22.
Скорочений варіант з іменами змінних, що співпадають з іменами властивостей об'єкта (використовується розпаковування об'єкта):
let { name, age } = { name: 'Вася', age: 22 };
name === 'Вася', age === 22.
Корисна річ, зменшує писанину, покращує читаємість коду:
let name = 'Masha';
let age = 17;
var obj = { name: name, age: age, x: 4 };
let obj = { name, age, x: 4 };
var x = obj.x;
let {x} = obj;
console.log(x); // 4
Порівняйте два старих способа і один новий:
function padding(){
var top = '10px', right = '20px', bottom = '30px', left = '20px';
return [ top, right, bottom, left ];
}
var pad = padding();
var left = pad[3], right = pad[1];
function padding(){
var top = '10px', right = '20px', bottom = '30px', left = '20px';
return { top: top, right: right, bottom: bottom, left: left };
}
var pad = padding();
var left = pad.left, right = pad.right;
function padding(){
let top = '10px', right = '20px', bottom = '30px', left = '20px';
return { top, right, bottom, left };
}
let { left, right } = padding();
Дозволяє досить просто обмінятися значеннями:
let a = 10, b = 25;
var tmp = a;
a = b;
b = tmp;
[a, b] = [b, a];
Spreads - зручне розпаковування масивів:
function fun(a, b, c, d) { alert(a + b + c + d); }
const arr = [3, 4, 5, 6];
fun(...arr);
const brr = [1, 2, ...arr, 7, 8];
alert('Max: ' + Math.max(...arr));
arr1 = arr1.concat(arr2, arr3);
arr1 = [...arr1, ...arr2, ...arr3];
А ще ці три крапки дозволяють скопіювати об'єкт:
let obj1 = { name: 'Вася', age: 22 };
let obj2 = { ...obj1 };
Стрілочні функції
Стрілочні функції завжди анонімні, не мають arguments та super, не можуть бути конструктором, не мають власного this, а використовують this з контексту вище.
() => { ... }
function (){ ... }
() => window.innerWidth / 2;
function (){ return window.innerWidth / 2; }
() => alert('hello');
function (){ return alert('hello'); }
(a, b, c) => { ... }
function (a, b, c){ ... }
(a, b, c) => a + b * c;
function (a, b, c){ return a + b * c; }
a => { ... }
a => a * a;
let obj = { sqr: a => a * a; }
obj.sqr(5); // 25
var obj = {
sqr: function(a){ return a * a; }
}
val => ({ str: val });
function(val){ return { str: val }; }
(a, b, ...c) => { ... }
(a = 5, b, c = 'hey') => { ... }
let f = ([a, b] = [1, 2], {x: c} = {x: a + b}) => a + b + c;
f(); // 6
function Person(){
this.age = 0;
setInterval(() => { this.age++; }, 1000);
}
let p = new Person();
function Person(){
this.age = 0;
var that = this;
setInterval(function(){ that.age++; }, 1000);
}
var p = new Person();
Класи (синтаксичний цукор поверх прототипів)
JS рухається в бік повноцінного ООП.
class User {
constructor(name, age){
this.name = name;
this.age = age;
}
hello(){
alert('Hello ' + this.name);
}
}
let user = new User('John', 25);
user.hello();
function User(name, age){
this.name = name;
this.age = age;
}
User.prototype.hello = function(){
alert('Hello ' + this.name);
};
var user = new User('John', 25);
user.hello();
let User = class {
sayHi() { alert('Привет!'); }
};
new User().sayHi();
// наслідування:
class Customer extends User {
constructor(name, age, gender){
super.constructor(name);
this.gender = gender;
}
hello(){
super.hello();
console.log('gender: ' + this.gender);
}
}
User, оголошений через class, неможна викликати без new (буде помилка).
Оголошення class веде себе як let: клас буде доступний лише в поточному блоці і лише нижче по коду.
Методи класа не злічені, вони не будуть доступні в конструкції for..in.
Можна створювати статичні методи (ці методи не мають доступу до this і можуть викликатися без створення об'єкта). Приклад вам вже відомий, метод now статичний:
let ms = Date.now();
let today = new Date();
class User {
constructor(name, role){
this.name = name;
this.role = role;
}
static anon(){
return new User('anon', 0);
}
}
let admin = new User('admin', 7);
let anon = User.anon();
Покращення об'єктних літералів
let offsetX = '70px';
let n = 42;
let obj = {
offsetX,
['item-' + n]: 'Sorok dva',
hello42(){ alert(this['item-42']); }
};
var obj = {
offsetX: offsetX,
hello42: function(){ alert(this['item-42']); }
};
obj['item-' + n] = 'Sorok dva';
Модулі - продвинутий варіант бібліотеки функцій.
По-старому:
var Math = {
PI: 3.14159,
sin: function (angle){ return ... ; },
cos: function (angle){ return ... ; },
__internalFunction: function (){ ... }
}
По-новому:
module Math {
export const PI = 3.14159,
export let step = 0,
export function sin(angle){ return ... ; }
export function cos(angle){ return ... ; }
internalFunction() { ... }
}
При цьому службова функція internalFunction ззовні буде недоступна.
Функції і змінні модуля можна імпортувати для більш зручного використання, область видомості імпорта обмежена конструкціями в фігурних дужках. Імпорт схожий на оператор with (не рекомендований до застосування):
let angleSin = Math.sin(angle * Math.PI / 180);
import Math.{sin, PI};
let angleSin = sin(angle * PI / 180);
import Math.*;
let angleSin = sin(angle * PI / 180);
let angleCos = cos(angle * PI / 180);
step++;
internalFunction(); // тут буде помилка
Цикл for-of
Зручний та очевидний перебір елементів масива:
for (var i = 0; i < arr.length; i++) { arr[i] - кожен елемент масива }
arr.forEach(function (e){ e - кожен елемент масива });
for (let e of arr) { e - кожен елемент масива }
Шаблонні літерали
Тепер для виокремлення строк можна використовувати не тільки одинарні та подвійні лапки, а й апостроф, що знаходиться під тільдою.
Строка в цих апострофах може містити шаблони зі змінними, виразами, викликами функцій:
const s = 'Masha';
alert('Hello ' + s + '!');
alert(`Hello ${s}!`);
alert(`Hello ${Math.sin(s.length) + 42}!`);
Багатострочні строки
var s1 = 'lorem ipsum dolor ' +
'sit amet, consectetur ' +
'adipisicing elit.';
var s2 = 'lorem ipsum dolor ';
s2 += 'sit amet, consectetur ';
s2 += 'adipisicing elit.';
let s = `lorem ipsum dolor
sit amet, consectetur
adipisicing elit.`;
Генератори yield
Добавлені функції-генератори (TODO: дописати).
Проміси, Fetch (TODO: дописати).
Нові можливості ECMAScript 7 / 2016
Array.prototype.includes(n) - перевіряє наявність елемента n в масиві, повертає true чи false.
Оператор піднесення до степеня
// 7 5 =
Math.pow(7, 5);
7**5
Нові можливості ECMAScript 8 / 2017
Асинхронні функції:
async function fun(){ ... }
const fun = async function (){ ... };
const fun = async () => { ... };
let obj = { async methodName(){ ... } };
Асинхронні функції повертають проміс, що виконається асинхронно, проте саме тіло функції виконується синхронно:
async function fun(){
console.log('test');
return 'Hello!';
}
console.log('start');
fun().then(function (n){ console.log(n); });
console.log('finish');
// результат:
start
test
finish
Hello!
Object.entries() - повертає масив з підмасивів, кожен підмасив - пара "ключ", "значення".
Object.values() - повертає масив значень ключів об'єкта.
Висячі коми після останнього елемента масива, об'єкта, аргумента функції.