ECMAScript 6 — что нового

Содержание

let

объявление переменной (аналог var), локальной для блока (внутри if, switch и т.п.).

var a = 5;
var b = 10;

if (a === 5) {
  let a = 4; // видна только внутри if
  var b = 1; // видна везде внутри функции

  console.log(a);  // 4
  console.log(b);  // 1
} 

console.log(a); // 5
console.log(b); // 1

Ссылки:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/let,
http://wiki.ecmascript.org/doku.php?id=harmony:let

const

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

const a = 15;
console.log(a); // 15
a = 1;
console.log(a); // 15

const b;
console.log(b); // undefined
b = 1;
console.log(b); // undefined

Ссылки:
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/const,
http://wiki.ecmascript.org/doku.php?id=harmony:const

Блочные функции

функции, видимые только внутри блока (по аналогии с let).

// обычная функция
[1, 2, 3].map(function (x) { return x
 
// блочная функция
[1, 2, 3].map {|x| x * x} // [1, 4, 9] 

http://wiki.ecmascript.org/doku.php?id=harmony:block_functions,
http://www.slideshare.net/dmitrysoshnikov/falsyvalues-dmitry-soshnikov-ecmascript-6

Выражение деструктурирования

«Разворачивает» объект или массив в последовательность переменных. При этом при разворачивании объекта нужно указать только ключи, которые нужны.
Аналоги: в Python это называется распаковыванием, в php для этого используется функция list.

var a, b, c = [1, 2];
[a, b] = c;
console.log(a, b); // 1, 2

var pt = {x:2, y:-5, z: 1};
var {x, y1, z} = pt;
console.log(x, y1, z); // 2, undefined, 1
console.log(y); // ReferenceError: y is not defined
var {x: n1, y1: n2, z: n3} = pt;
console.log(n1, n2, n3); // 2, undefined, 1

https://developer.mozilla.org/en-US/docs/JavaScript/New_in_JavaScript/1.7#Destructuring_assignment_%28Merge_into_own_page.2Fsection%29

Значения по умолчанию для аргументов функций

function multiply(a, b = 1) {
  return a*b;
}
 
multiply(5); // 5

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/default_parameters

Остальные параметры вызова функции

Конструкция позволяет получить все параметры вызова функции, для которых не определены аргументы, в виде массива (замена arguments)

function fun1(a, b, ...theArgs) {
  console.log(theArgs.length);
}

fun1(); // 0
fun1(5); // 0
fun1(5, 6, 7); // 1

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/rest_parameters

Spread

Распаковывание массива в параметры вызова функции (аналог распаковки кортежа в параметры вызова функции в Python)

let a = [0,1,2,3], o = func(...a);

https://developer.mozilla.org/en-US/docs/Syntax/Spread_operator (на момент написания ссылка ведёт на пустую статью).

Proxy

Кто-то называет это мета-объектами, но, к сожалению, очень это похоже на классы. Лично я надеюсь, что широкого распространения эта функциональность не получит. (Возможно тоже вдохновлено Python-овскими метаклассами, но всё-таки мета-объекты — это же классы).
В примере заменяется метод get объекта, чтобы для несуществующего свойства возвращать 37, а не undefined:

var handler = {
    get: function(target, name){
        return name in target?
            target[name] :
            37;
    }
}
 
var p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;
 
console.log(p.a, p.b); // 1, undefined
console.log('c' in p, p.c); // false, 37

Подробнее: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Proxy,
http://wiki.ecmascript.org/doku.php?id=harmony:virtual_object_api.

WeakMap

Объект, ключами которого могут быть объекты. Вроде как не боится циклических ссылок внутри себя.
Имеет методы get, set, has, delete.

var wm1 = new WeakMap(),
    wm2 = new WeakMap();
var o1 = {},
    o2 = function(){},
    o3 = window;
 
wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // любое значение, включая функции и объекты
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // ключами и значениями могут быть любые объекты, даже WeakMaps
 
wm1.get(o2); // "azerty"
wm2.get(o2); // undefined, т.к. нет такого ключа
wm2.get(o3); // undefined, т.к. там хранится именно это значение
 
wm1.has(o2); // true
wm2.has(o2); // false
wm2.has(o3); // true (несмотря на значение "undefined")
 
wm1.has(o1);   // true
wm1.delete(o1);
wm1.has(o1);   // false

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/WeakMap

Итераторы

Объект, который знает, в каком порядке нужно обходить его свойства, и хранит текущую позицию в обходе. Сводится к методу next.

var lang = { name: 'JavaScript', birthYear: 1995 };
var it = Iterator(lang);
// однажды инициализированный, позволяет получить последовательный доступ к парам ключ-значение объекта
var pair = it.next(); // пара ["name", "JavaScript"]
pair = it.next(); // пара ["birthYear", 1995]
pair = it.next(); // Возбуждается исключение StopIteration
// удобно использовать для обхода объекта
var it = Iterator(lang);
for (var pair in it)
  console.log(pair); // каждый раз выводит пару [key, value]

https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Iterators_and_Generators.
Частные случаи итераторов — генераторы и прокси-объекты (могут быть).

Генераторы

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

function fibonacci() { 
    let [prev, curr] = [0, 1]; 
    while (true) { // бесконечный цикл
        [prev, curr] = [curr, prev + curr]; 
        yield curr; 
        // здесь будет продолжено выполнение после следующего вызова метода next()
    } 
} 
 
for (let n in fibonacci()) { 
    // ограничим выборку несколькими значениями 
    if (n > 1000) {
        break; 
    }
    console.log(n); // 1, 2, 3, 5, 8 ... 987
} 
console.log('------------');
for (let n in fibonacci()) { 
    // ограничим выборку несколькими значениями 
    if (n > 2000) {
        break; 
    }
    console.log(n); // 1, 2, 3, 5, 8 ... 1597
}
var fib = fibonacci();
console.log('------------');
console.log(fib.next()); // 1
console.log(fib.next()); // 2
console.log(fib.next()); // 3
console.log(fib.next()); // 5

https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Iterators_and_Generators

Comprehensions

Выражения для создания генераторов на основе других генераторов.
Выражения для работы с массивами:

var ar_a = [0, 1, 2, 3],
     ar_b = [0, 1, 2, 3],
     ar_c;

ar_c = [a + b for (a in ar_a) for (b in ar_b)];
console.log(ar_c); // ["00", "01", "02", "03", "10", "11", "12", "13", "20", "21", "22", "23", "30", "31", "32", "33"]

Выражения-генераторы:

var obj = {a: 1, b: 2, c: 3};

let generator = (x for (x in obj) if (obj[x] >= 2));

for (let i in generator) {
    console.log(i);
}

https://developer.mozilla.org/en-US/docs/JavaScript/New_in_JavaScript/1.8#Generator_expressions_(Merge_into_Generator_expressions)

for…of

перебирает элементы коллекции (массивы, итераторы, генераторы).
Работа с массивами:

let arr = [ 3, 5, 7 ];
arr.foo = "hello";
 
for (let i in arr) {
   console.log(i); // "0", "1", "2", "foo"
}
 
for (let i of arr) {
   console.log(i); // "3", "5", "7"
}

Работа с генераторами:

function fibonacci() { // функция-генератор
    let [prev, curr] = [0, 1];
    for (;;) {
        [prev, curr] = [curr, prev + curr];
        yield curr;
    }
}
 
for (let n of fibonacci()) {
    // прерываемся после 10
    if (n > 10) {
        break;
    }
    console.log(n); // 1, 2, 3, 5, 8
}

https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/for…of

StructType

const Pixel = new StructType({x:uint32, y:uint32, color:Color}); // Color тоже должен быть StructType
new ArrayType(Pixel, 3);

https://developer.mozilla.org/en-US/docs/Mozilla/js-ctypes/Using_js-ctypes/Declaring_types

Классы (class)

Синтаксический сахар для функций-конструкторов (с возможным наследованием)

class Animal {
    constructor(name) {
        this.name = name;
    }

    sayName() {
        console.log(this.name);
    }
}

class Dog extends Animal {
    constructor(name) {
        super(name);
    }

    bark() {
        console.log("Woof!");
    }
}

// аналог в ES3

function Animal(name) {
    this.name = name;
}

Animal.prototype.sayName = function() {
    console.log(this.name);
};

function Dog(name) {
    Animal.call(this, name);
}

Dog.prototype = new Animal(null);
Dog.prototype.bark = function() {
    console.log("Woof!");
};

Более сложный пример:

class Point extends Base {
  constructor(x,y) {
    super();
    this.px = x, this.py = y;
    this.r = function() { return Math.sqrt(x*x + y*y); }
  }
  get x() { return this.px; }
  get y() { return this.py; }
  proto_r() { return Math.sqrt(this.px * this.px +
      this.py * this.py); }
  equals(p) { return this.px === p.px &&
      this.py === p.py; }
}

http://wiki.ecmascript.org/doku.php?id=strawman:maximally_minimal_classes,
http://www.nczonline.net/blog/2012/10/16/does-javascript-need-classes/

Модули

// вот модуль
module DBLayer { 
    export function query(s) { ... } 
    export function connection(...args) { ... } 
} 

// использование модуля
import DBLayer.*; // импортировать все функции из модуля
 
import DBLayer.{query, connection: attachTo}; // импортировать только нужные функции (причём с возможностью назвать функцию при импорте по-другому)
 
query(“SELECT * FROM books”).format(“escape | split”); 
 
attachTo(“/books/store”, { 
    onSuccess: function (response) { ... } 
}); 

Работа с внешними модулями:

// загрузка из файловой системы
module $ = require(“./library/selector.js”); 
 
// загрузка модуля из интернета
module CanvasLib = require(“http:// ... /js-modules/canvas.js”); 
 
// можно использовать напрямую
let rect = new CanvasLib.Rectangle({width: 30, height: 40, shadow: true}); 
 
// или импортировать только нужный функционал
import CanvasLib.{Triangle, rotate}; 
 
rotate(-30, new Triangle($.query(...params))); 

http://wiki.ecmascript.org/doku.php?id=harmony:modules,
http://wiki.ecmascript.org/doku.php?id=harmony:modules_examples,
http://wiki.ecmascript.org/doku.php?id=harmony:module_loaders.

Строки-шаблоны

Используются для замены входящих в них плэйсхолдеров на значения локальных переменных.
Преследуют 4 цели: «многострочные» строки, форматирование строк, HTML-экранирование и локализация.

Синтаксис:

tag`literal${substitution}literal`

Замена:

var sub = 1;
var text = `Some ${sub}First line
Second line`;
// получим "Some 1First line
// Second line"

var total = 30,
    msg = `The total is ${total} (${total*1.05} with tax)`;
    
console.log(msg);       // "The total is 30 (31.5 with tax)"

Если переменная для замены не определена, будет возбуждено исключение.
В качестве tag можно использовать произвольную функцию.

http://www.nczonline.net/blog/2012/08/01/a-critical-review-of-ecmascript-6-quasi-literals/ (хорошая подробная статья с примерами и рассуждениями об удобстве использования).

LEAVE A COMMENT