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";
Комментарии к статье