Открыть меню    

ECMAScript 2015, Основы ES6

https://babeljs.io/repl

Современные возможности ES-2015 от Кантора

let

let - используется для создания переменных с областью видимости в пределах блока (блок ограничивается {})

Javascript

let me = 'go'; //globally scoped
var i = 'able'; //globally scoped

Javascript

function ingWithinEstablishedParameters() {
    let terOfRecommendation = 'awesome worker!'; //function block scoped
    var sityCheerleading = 'go!'; //function block scoped
};

Javascript

function allyIlliterate() {
    //tuce is *not* visible out here

    for( let tuce = 0; tuce < 5; tuce++ ) {
        //tuce is only visible in here (and in the for() parentheses)
    };

    //tuce is *not* visible out here
};

function byE40() {
    //nish *is* visible out here

    for( var nish = 0; nish < 5; nish++ ) {
        //nish is visible to the whole function
    };

    //nish *is* visible out here
};

stackoverflow.com: let-keyword-vs-var-keyword

Деструктуризация в ES6:

--- деструктуризация для объекта:

{
            let me = {
                name: 'Kateryna',
                age: 20,
                gender: 'female',
                city: 'Kyiv'
            };

            let { name: firstName, city } = me;

            console.log(firstName); // Kateryna
            console.log(city); // Kyiv
}

Классы в ES6:

--- constructor

Метод конструктор (constructor) — специальный метод, необходимый для создания (вызывается при создании объекта) и инициализации объектов с помощью класса. В классе может быть только один конструктор. Конструктор может принимать параметры, которые мы используем при создании объекта.

--- extends

Ключевое слово extends используется для создания дочернего класса относительно другого класса.

--- super

  • super может использоваться в методе constructor класса для вызова родительского конструктора
  • super может использоваться внутри методов класса для ссылок на свойства (методы) базового класса

Как видите, super может использоваться лишь в методах объекта.

--- Статические свойства

Статические свойства принадлежат непосредственно классу, а не объектам на его основе. Часто используется для хранения вспомогательной информации. Мы не можем определять статические свойства через ключевое слово static. Но мы можем присвоить свойство самому классу:

Task.count = 0;

// используем в классе:
class Task {
    constructor(title = ""){
        this.title = title;
        Task.count += 1;
    }
}

--- Статические методы

Статические методы - это методы, которые принадлежат самому классу, а не объектам на его основе. Если вызвать статический метод у экземпляра класса, то будет ощибка.

class Task {
    constructor(title = Task.getDefaultTitle()){
        this.title = title;
        Task.count += 1;
    }

    static getDefaultTitle() {
        return "Задача "
    }
}

--- Свойства класса: get и set

get и set внутри класса выглядят как методы, а снаружи как свойства. Эти свойства-методы позволяют получить/присвоить значение "настоящим" свойствам объекта.

class Task {
    constructor(title = ""){
        this.title = title;
        this._done = false;
        Task.count += 1;
    }

    // Свойство get связывает свойство объекта с функцией,
    // которая будет вызываться при обращении к этому свойству

    // Названия свойств get и set не должны совпадать со свойствами объекта
    //(обычно перед названием свойств ставится  _)
    get done(){
        return this._done === true ? 'Выполнена' : 'Не выполнена';
    }

    // set принимает параметр
    set done(val){
        if(val !== undefined && typeof val === 'boolean') {
            this._done = val;
        } else {
            console.error(`Ошибка`);
        }
    }
}

--- Наследование, пример

1) Если у подкласса нет конструктора, то он будет использовать родительский конструктор.
2) Конструктор может принимать параметры, которые могут быть использованы при создании свойств.
3) Если подклассу мы указываем конструктор, то этот конструктор должен вызвать конструктор родительского класса. Это делается при момощи ключевого слова super.
4) super должен идти в конструкторе подкласса первой строкой
5) Методы родителя также наследуются подклассом
6) Чтобы переопределить метод в подклассе достаточно его просто перезаписать
7) В унаследованном методе мы можем вызвать метод родительского класса, а потом дополнить его
8) Статические свойства и методы также наследуются, как и get-ры и set-ры

class Task {
    constructor(title){
        this.title = title;
        this.done = false;
        console.log("Создание задачи");
    }

    complete(){
        this.done = true;
        console.log(`Задача ${this.title} выполнена`);
    }
}

class SubTask extends Task {
    constructor(title, parent) {
        super(title); // вызовет конструктор род-го класса
        this.parent = parent;
        console.log("Создание подзадачи");
    }

    complete(){ // SubTask использует свой метод complete, а не родит-й
        super.complete(); // вызовем родит-й метод
        console.log(`Подзадача ${this.title} выполнена`);
    }

}

let task = new Task('изучить js')
let subtask = new SubTask('изучить Node.js', task)

Пример II

function A(a) {
  this.a = a;
}

A.prototype.printA = function(){
  alert(this.a);
}

class B extends A {
  constructor(a, b){
    super(a);
    this.b = b;
  }

  printB(){
    alert(this.b);
  }

  static sayHello(){
    alert("hello");
  }
}

class C extends B {
  constructor(a, b, c){
    super(a, b);
    this.c = c;
  }

  printC(){
    alert(this.c);
  }

  printAll(){
    this.printC();
    super.printB();
    super.printA();
  }
}

var obj = new C(1, 2, 3);
obj.printAll();
C.sayHello();

//3
//2
//1
//"hello"

let animal = {
  walk() {
    alert("I'm walking");
  }
};

let rabbit = {
  __proto__: animal,
  walk() {
    alert(super.walk); // walk() { … }
    super.walk(); // I'm walking
  }
};

rabbit.walk();

Если свойство обозначить через function, то внутри него super работать не будет.

super по Кантору

const

const - используется для объявления переменных, которые нельзя изменить. Область видимости такая же как и у let.

Параметры по умолчанию для функций

Default_parameters

Javascript

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

multiply(5); // 5

В качестве параметра можно использовать и выражения

При передаче любого значения, кроме undefined, включая пустую строку, ноль или null, параметр считается переданным, и значение по умолчанию не используется.

Остаточный параметр для функций

Если параметр функции начинается с ... , то это остаточный параметр. Это означает, что все параметры переданные функции собираются в массив, и переменной, которая стоит перед ... присваивается этот массив. Например:

function testDots(...needs) {
    needs.forEach(function (val) {
        console.log(val);
    });
    return 1;
}
testDots("ка", "ра", "мба");

//ка
//ра
//мба
//1

Функции в блоке

Объявление функции Function Declaration, сделанное в блоке, видно только в этом блоке.

Javascript

'use strict';

if (true) {

  toDo(); // работает

  function toDo() {
    alert("to Do smthing");
  }

}
toDo(); // ошибка, функции не существует

Стрелочные функции =>

ES6 предоставляет новый синтаксиси для создания функций. Все стрелочные функции являются анономными.

Javascript

let summaFu = (a, b) => {
    let summa = a + b;
    return summa;
}

let result = summaFu(5,7);
alert(result); // 12

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

Стрелочные функции очень удобны в качестве callback функций, например:

Javascript

let arr = [4, 7, 3];

let sorted = arr.sort( (a,b) => a - b );

alert(sorted); // 3, 4, 7

Стрелочные функции имеют тот же this, что и снаружи.

Расширенный оператор: ...

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

Javascript

function f(x, y, z) { }
var args = [0, 1, 2];
f.apply(null, args);

Javascript

function f(x, y, z) { }
var args = [0, 1, 2];
f(...args);

Расширенный оператор имеет множество применение, помимо разбиения итерируемого объекта.

Spread_operator

Новые кавычки для строк ` `

1. В них разрешён перевод строки.

Javascript

alert(`Lorem ipsum dolor
sit amet, consectetur
adipisicing.`);

2. Можно вставлять выражения при помощи ${...}.

Javascript

let a = 2;
let b = 3;

alert(`${a}`+`${b}`); //23

alert(`${a+b}`); // 5

alert(a+b);           //5

Объект Object.assign

Метод Object.assign получает список объектов и копирует в первый target свойства из остальных.

При этом важно отметить, что последующие свойства перезаписывают предыдущие.

Javascript

let user = { name: "Вася" };
let visitor = { isAdmin: false, visits: true };
let admin = { isAdmin: true };

Object.assign(user, visitor, admin);

// user <- visitor <- admin
alert( JSON.stringify(user) ); // name: Вася, visits: true, isAdmin: true

пример взят с learn.javascript.ru

Методы объекта (новый синтаксис)

Чтобы задать метод нужно вместо "старого" синтаксиса prop: function() {...} использовать "новый":
prop() { … }

Javascript

let user = {
  name: "John",
  // вместо "sayHi: function() {" пишем "sayHi() {"
  sayHi() {
    alert(this.name);
  },
  otherProp: 666
};

user.sayHi(); // John

Также методами станут объявления геттеров get prop() и сеттеров set prop():

инструменты:

Profiler

console.profile()

console.profileEnd()

Для profiler ф-и использовать следующим образом $(".as").click(clickHandler);

Ручной замер

console.time("description");

console.timeEnd("description");

правильный код:

1. вынести обращения к DOM-дереву из циклов. 2. использовать нативный js 3. пункт 1 и результат обращений, то есть использовать += (относится также к добавлению элементов на страницу) 4. избавиться от дублирования обращений к DOM-дереву 5. использовать querySelector 6. Document Fragment: fragment = document.createDocumentFragment(); 7. там где возможно глобальные переменные следует перенести в локальную область видимости 8. jQuery селектор должен состоять из наименьшего кол-ва селекторов 9. Стоит использовать встроенные (find, но не children) методы jQuery для обращения к коллекциям (элементам)

Самый простой способ работы с асинхронным кодом это callback.

Promise

Абстракция для обещания асинхронных данных

Обещание можно выполнить, но можно и не сдержать

Создание и использование

       Fulfilled (выполнено)
Pending (состояние ожидания данных)
       Rejected (отклонено)

Такой promises выглядит как обертка над некоторой асинхронной логикой (пример):

Javascript

// Такой promises выгдялет как обертка над некоторой асинхронной логикой
var promise = new Promise(function(resolve,reject){
    var xhr = new XMLHttRequest();
    xhr.open(method, url);
    xhr.onload = function(){  // в момент, когда получаем данные подаем данные на код resolve
        resolve(xhr.response);
    }
    xhr.onerror = function(){ // если error подаем данные в rejecte
        reject(new Error('network request failed'));
    }
    xhr.send();
});

promise.then(function(response){
    // обработка результата
});
promise.catch(function(error){
    //обработка ошибки
})

Зачем использовать promise? Promise позволяют делать цепочки обработки из нескольких синхронных операций. См. след. Пример.

Методы .then() и .catch() возвращают Promise. Можно строить цепочки:

Javascript

//новый встроенный метод для ajax-запросов (возвращает promise)
fetch('https://api.github.com/users/user')
    .then(function(response){
        if(response.status === 200){
            return response;
        }
        throw new Error(response.status);
    })
    .then(function(response){
        return response.json();
    })
    .then(function(data){
        console.log(data.name);
    })
    .catch(function(error){
        console.log(error.stack);
    })

Обработка исключений

Promise не потеряет ни одного исключения!

Каждый раз, когда в promise встречается исключение создается promise в состояние отклонен.

Это позволяет структурировать код:

Javascript

// обработка исключений

// в конструкторе вызван reject()
var promise = new Promise(function(resolve, reject){
    //...
    reject(new Error('goodbye world')); // возможно в асинхронной лперации
    //...
});

// сгенерировано исключение в конструкторе,либо в обработчике
promise = promise.then(function(result){
    //...
    throw new Error('goodbye world');
    //...
});

В цепочке исключение обрабатывается в ближайшем .catch()

Javascript

// В цепочке исключение обрабатывается в ближайшем .catch()

.then(function(result){
    //...
    throw new Error('goodbye world');
})
.then(function(result){
    return process1(data);  // обработчик не будет выполнен
})
.catch(function(error){
    console.log(error.stack);
    return FALLBACK_DATA;   // восстановление после ошибки
})
.then(function(data){
    return process2(data);  // продолжаем
})

Это аналог try – catch в асинхронном виде.

Дополнительно

Promise.all([promise1, promise2, …])
Дожидаемся, когда все промисы будут выполнены

Promise.race([promise1, promise2, …])
Завершится как только любой (один) промис завершится

Есть возможность создавать промисы сразу resolve или reject:
Promise.resolve(value)
Promise.reject(error)

Модули (export, import)

Ранее модули определялись посредством: модули AMD (в стиле requireJS), модули CommonJS (в стиде nodejs), UMD (универсальная система).

Модуль - отдельный файл с кодом

export

Ключевое слово export используется для экспорта функций, объектов или примитивов из файла (или модуля).
developer.mozilla.org/ru: export

// экспорт переменных
export let name1, name2, …, nameN; // или var

// экспорт уже объявленных где-то переменных
export { name1, name2, …, nameN };

//при помощи as указываем, что переменная name1 экспортирована под именем default
export { name1 as default, … };

// здесь будет ошибка, так как для export обязательно нужно имя
export function() { alert("Error"); };

export default

Для ситуации, когда один модуль экспортирует одно значение, предусмотрено особое ключевое сочетание export default.

export default выражение;
export default function (…) { … } // или class, function*
export default function name1(…) { … } // или class, function*
export { name1 as default, … };

// module "my-module.js"
export default function cube(x) {
  return x * x * x;
}

// module "my-module.js"
import cube from 'my-module';
console.log(cube(3)); // 27

import

Современный js: import

Другие модули могут подключать экспортированные значения при помощи ключевого слова import.
developer.mozilla.org: import

import { member, member2 } from "module-name";

member, member2 – импортируемые переменные, которые должны быть обозначены в module-name ключевым словом export.

Импортировать значения можно и под другим именем, указав его в as:

import {member as alias } from "module-name";

*

Импортируем ВСЕ в виде объекта (import * as obj)

import * as members from "module-name";

// теперь экспортированные переменные - свойства members
alert( `${members.name_one} and ${members.name_two}` ); // Joe and John

bind

Контекст функции можно изменить пи помощи функций:

  • bind
  • call
  • apply

bind - возвращает новую функцию с измененным контекстом.

Принимает в качестве аргументов:

  • 1. новый контекст
  • 2. произвольной количество аргументов

Изменить контекст функции можно только один раз.

Имеет особенность накапливать аргументы.

a.f.bind(b);
function summ(a, b){
    return this.prop + a + b;
}
var a = {
    prop: 1
}
var func = summ.bind(a, 1, 1);
func(); //3
//Накапливание аргументов:
function summ(a, b){
    return this.prop + a + b;
}
var a = {
    prop: 1
}
var func = summ.bind(a, 1);
func = func.bind(a, 1);

func(); //3

EXPORT

// экспортируем одну переменную
export {varName};

// экспортируем несколько переменных
export {varName1, varName2};

// экспортируем  переменную под псевдонимом
export {varName3 as myVarName3};

// экспортируем несколько переменных под другими именами
export {varName4 as myVarName4, varName5 as myVarName5};

// используем псевдоним default
export {varName6 as default};

// например:
// import x, {x1, x2} импортируем переменные x1 и x2 и псевдоним default

// используем псевдоним default и экспортируем  переменную под псевдонимом
export {varName7 as default, varName1 as myVarName1};

// экспортируем выражение
export default function(){ console.log("Hello, man") };

// экспортируем переменные из подмодуля
export { varMod1, varMod2 } from "myAnotherModule";

// экспортируем все эскпортируемые переменные из подмодуля
export * from "myAnotherModule";

Особенности export:

  • Использовать можно в любом месте подмодуля
  • Модуль может содержать множество export
  • В условии экспортировать нельзя
  • Нельзя экспортировать одно и тоже

IMPORT

Оператор import состоит из двух частей - имен импортируемых переменных и относ-го пути до модуля.

jQuery or Javascript

// Импортируем псевдоним default
// Имя x является псевдонимом default (также псевдоним)
import x from "path_to_module";

// Импортируем перем. x
import {x} from "path_to_module";

// аналогично выше, но KU является псевдонимом x
import {x as KUKU} from "path_to_module";

// Импортируем перем. x и x2
import {x , x2} from "path_to_module";

// Импортируем перем. x и x2
// При этом KU является псевдонимом x2
import {x , x2 as KU} from "path_to_module";

// Импортируем перем. x и x2
// Имя y является псевдонимом default (также псевдоним)
import y, {x , x2} from "path_to_module";

// Импортируем модуль
import "path_to_module";

// Импортируем все в объект x, включая default
import * as x from "path_to_module";

// Импортируем все в объект x, включая default
// Псевдониму default присваивается псевдоним x1
import x1, * as x from "path_to_module";

Комментарии к статье