Translate

Поиск по этому блогу

четверг, 20 сентября 2018 г.

ES6:Наследование (X).

Наследование в ES6

Создание объекта на основе класса является одной из сторон объектно-ориентированного программирования . Другая сторона это наследование . То есть способность объектов наследовать свойства и методы от других классов.



Все материалы по ES6


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

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

Для начала, в папке src я создам файл inheritance.js , что в переводе с английского означает наследование.

Далее, я запущу Babel, который будет следить за файлами и при их изменении сгенерирует свежие версии .

Открою папку в командной строке и наберу команду:

npm run watch.


Последние версии браузеров поддерживает новый синтаксис без транспиляции и мой браузер входит в число, но тем не менее в конце мы посмотрим на транспонированный код.

Также, необходимо указать этот файл в index.html

<script src="src/inheritance.js"></script>

Продолжая тему задач, я создам класс Task и в конструкторе выведу сообщение - “создание задачи”.


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



Далее, создадим ещё один класс, который будет представлять из себя подзадачу.

Класс SubTask - подзадачи является задачей, но также будет иметь родительскую задачу . Для того чтобы указать что SubTask является подклассом, используется ключевое слово extends которое по-русски означает "расширяет".

После названия, я ставлю пробел и напишу extends и далее название класса который мы расширяем.

Это всё что нужно для того чтобы определить наследование между двумя классами.


class SubTask extends Task {
 
}



После классов я создам объект, который будет представителем класса Task и Объект, который будет представителем класса SubTask.


let task = new Task();
let subtask = new SubTask();



Давайте посмотрим на оба объекта в консоли браузера и используя оператор instanceof проверим - является ли объект subtask представителем обоих классов?


console.log(subtask instanceof Task);
console.log(subtask instanceof SubTask);



Откроем файл в браузере и посмотрим в консоли.



Мы видим два сообщения о создании задачи.

Далее мы видим два объекта и два сообщения true

Тот факт, что мы видим два сообщения о создании задачи означает, что SubTask не имея конструктора, использует конструктор родителя.

Таким образом получается:
Если у подкласса нет конструктора то он будет тебя использовать родительский конструктор!


А сообщение true означает, что subtask. является представителем класса SubTask.

и также subtask является представителем класса Task.

Наследование свойств

Как мы знаем из прошлого поста ES6:Классы , классы могут иметь свойства.

Ограничимся двумя title - заголовок и done - выполнена задача или нет.

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

При создании объектов укажем заголовок задачи

В качестве задачи отправим -”Изучить JavaScript” , а подзадачи отправим - “ Изучить ES6”

Далее, конструктор будет принимать параметры title, которому мы присвоим свойство this.title . И инициализируем свойство this.done со значением false То есть задача по умолчанию будет не выполнена.

Заметьте, что мы инициализировали свойство в конструкторе у класса Task, но при этом в SubTask, мы отправляем заголовок.

и пока конструктора у класса SubTask нет.


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

class SubTask extends Task {

}


let task = new Task('Изучить JavaScript');
let subtask = new SubTask('Изучить ES6');

console.log(task);
console.log(subtask);

console.log(subtask instanceof Task);
console.log(subtask instanceof SubTask);



Давайте посмотрим что произойдет.



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

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


Тот, в свою очередь, правильно инициализирует свойства.

Но это не значит что мы не можем определить классу SubTask свой конструктор.

Давайте это сделаем. Пока не будем указывать параметры и просто посмотрим что произойдет?


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

class SubTask extends Task {
 constructor() {
  
 }

}


let task = new Task('Изучить JavaScript');
let subtask = new SubTask('Изучить ES6');

console.log(task);
console.log(subtask);

console.log(subtask instanceof Task);
console.log(subtask instanceof SubTask);



Посмотрим в консоли и :



И мы увидим сообщение об ошибке.

Это происходит потому что
если подклассу мы указываем конструктор, то этот конструктор должен вызвать конструктор родительского класса .


Мы можем это сделать используя ещё одно ключевое слово которое называется super.

Я просто напишу superи поставлю круглые скобки super (); и супер вызовет конструктор родительского класса.

Давайте посмотрим.



Вроде бы всё сработало но заметьте, что у subtask заголовок исеет значение - undefined.

Это происходит потому, что конструктор родительского класса ожидает title в качестве параметра.

Но в данном случае, мы его не отправляет для этого в конструкторе

Я укажу параметр title в конструкторе.

И в качестве аргументов super() Я тоже отправлю title.


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

class SubTask extends Task {
 constructor( title ) {
  super( title );

 }

}


let task = new Task('Изучить JavaScript');
let subtask = new SubTask('Изучить ES6');

console.log(task);
console.log(subtask);

console.log(subtask instanceof Task);
console.log(subtask instanceof SubTask);



Давайте посмотрим, что у нас получилось.



Теперь мы видим, что всё работает правильно

И вот эти строчки я удалю они нам больше не нужны


console.log(subtask instanceof Task);
console.log(subtask instanceof SubTask);



Для того чтобы убедиться, что SubTask использует свой конструктор, давайте выведем сообщение похожие на сообщение родительского класса, только задачу изменим на создание подзадачи.


class SubTask extends Task {
 constructor(title) {
  super(title);
    console.log('Создание подзадачи');
 }

}



Посмотрим. И теперь мы видим 3 сообщения - создание задачи, создание задачи и создание подзадачи.

Первое сообщение выводится при создании задачи Task

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

Дальше возвращаемся конструктор SubTask и будет сообщение о создание подзадачи.

Именно поэтому видим сообщения 3 раза.

Давайте посмотрим что будет если я конструктор родительского класса как-будто удалил (закомментирую) и вызов родительского класса я тоже пока удалю. И создание объекта task тоже удалю.

И соответственно вывод в консоли тоже удалю.


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

class SubTask extends Task {
 constructor(title) {
  // super(title);
    console.log('Создание подзадачи');
 }

}


// let task = new Task('Изучить JavaScript');
let subtask = new SubTask('Изучить ES6');

console.log(task);
console.log(subtask);



Посмотрим, сможет ли SubTask правильно инициализироваться без конструктора у Task?



И мы видимся ту же ошибку.

Это означает что:
если мы указываем для подкласса конструктор, то этот конструктор должен вызывать конструктор родительского класса.
Этот вызов используют ключевое слово super и должен быть первой строчкой до того как мы приступим к присвоению каких-либо свойств.


И в качестве примера, давайте присвоим классу SubTask свойство parent или родитель, в котором мы укажем родительскую задачу.


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

class SubTask extends Task {
 constructor( title, parent ) {
  super(title);
  this.parent = parent;
    console.log('Создание подзадачи');
 }

}


let task = new Task('Изучить JavaScript');
let subtask = new SubTask('Изучить ES6', task);

console.log(task);
console.log(subtask);



Давайте посмотрим что случилось.



и мы видим? что у объекта subtask появилась свойство - parent, которое содержит в себе задачу.

И это заголовок - “Изучить JavaScript “



Помимо наследования свойств, классы могут наследовать и поведение, то есть - методы.

Наследование методов.

Давайте добавим классу Task метод complete, который мы будем использовать для того, чтобы завершить задачу. И в теле методa мы укажем свойства this.done со назначением true.
И в консоли выведен сообщение о том что задача с таким заголовком выполнена.
Для этого я воспользуюсь шаблонной строкой и напишу “задача выполнена” используя местозаполнитель покажу. title - заголовок задачи.


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



В коде, после создания объектов, давайте завершим задачу и также завершим подзадачу:


task.complete();
subtask.complete();



Посмотрим в консоль.



Мы видим сообщение, что задачи выполнены.

Это означает что SubTask унаследовал методы complete. То есть - он ему доступен.

Таким же образом мы можем унаследовать свойства get, set и статические свойства.

То есть все члены класса.

Но что делать если нас не устраивает родительский метод?

Допустим мы хотим его полностью поменять или немного изменить?

Мы можем его перезаписать!

Перезапись родительского метода

Для этого в подклассе, мы объявляем метод с таким же названием как у родительского, в теле которого мы можем сделать всё что угодно.

Я пометил, что задача выполнена.

И в консоли выведу сообщение -

console.log(`Подзадача : "${this.title}" выполнена! `);

Код полностью:

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() {
  this.done = true;
  console.log(`Подзадача : "${this.title}" выполнена! `);
 }

}


let task = new Task('Изучить JavaScript');
let subtask = new SubTask('Изучить ES6', task);

task.complete();
subtask.complete();

console.log(task);
console.log(subtask);



Давайте посмотрим, что произошло.



И теперь вместо “задача” мы видим “подзадача”.

Это означает, что SubTask теперь использует свой метод complete(), а не родительский. Но заметьте, что нам пришлось указать this.done в двух местах - у родительского класса и у подкласса.

Конечно, мы повторили лишь одну строчку, но тем не менее в настоящем классе методы обычно выполняет чуть больше работы и для того чтобы не нарушать принципы D.R.Y (Don’t Repeat Yourself - не повторяйся) в унаследованном методе, мы можем вызвать метод родительского класса, а потом как либо его дополнить.

this.done = true; у подкласса я удалю использую ключевое слово super далее ставлю точку и выбираю метод родительского класса - в данном случае - super.complete();

Таким образом, как только вызовите метод complete(); у объекта subtask мы “перепрыгнем” в родительский класс пометим this.done = true;

Далее, мы выведем сообщение “задача изучить ЕS6 выполнено”. После чего мы “перепрыгнем” обратно и выведем сообщение “ подзадача изучить ES6 выполнено!”

Давайте посмотрим.



И действительно, мы видим:
  1. - сообщение “задача изучить ЕS6 выполнила”.
  2. - видим сообщение “Подзадача : "Изучить ES6" выполнена! “
  3. - видим что свойства done - имеет значение true.


То есть - первое, мы успешно перезаписали родительский метод и второе - в этом перезаписанных методе мы использовали метод родительского класса.

Теперь для полной картины, я добавлю к классу Task геттер и сеттер для свойства title.

Также добавлю статическое свойство и статический метод.

Начнем с геттера и сеттера.

Геттер и сеттер.

Для начала названия в title я добавлю нижнее подчеркивание.

После конструктора напишу:


 get title() {
  return this._title;
 }
 set title( value ) {
  this._title = value;
 }
}



get title(){} и в теле метода мы просто вернем. this._title

и set title, который примет параметр value.

Для простоты я не буду проверять параметр и просто присвоили его.

Далее я создам статический метод static getDefaultTitle(), который просто вернет заголовок задачи по умолчанию.


 static getDefaultTitle() {
  return 'Задача';
 }


И после класса Task я добавлю статическое свойство, которое будет содержать в себе общее количество созданных задач.

Task.count = 0;

Инициализируем со значением 0. и при создании задачи добавим свойству единицу -

Task.count +=1;

далее, посмотрим как класс Task унаследовал эти методы и свойства.


console.log(SubTask.getDefaultTitle()); - статический метод у класса Task
console.log(SubTask.count); - статическое сво-во.



а геттер и сеттер мы сможем увидеть сразу в консоли.

Давайте посмотрим на результат



Мы видим:
  1. - Задача - это вызов статического метода у SubTask.
  2. -видим цифру 2 - это статическое свойство SubTask.count.
  3. - у субкласса есть свойство title помимо свойства _title.


Таким образом мы унаследовали свойства, методы, статические свойства, статические методы ,и геттеры, и сеттеры.

Файл inheritance.js полностью.

class Task {
 constructor(title) {
  this._title = title;
  this.done = false;
  Task.count +=1;
  console.log('Создание задачи');
 }
 complete() {
  this.done = true;
  console.log(`Задача : "${this.title}" выполнена! `);
 }
 get title() {
  return this._title;
 }
 set title( value ) {
  this._title = value;
 }
 static getDefaultTitle() {
  return 'Задача';
 }
}

Task.count = 0;

class SubTask extends Task {
 constructor( title, parent ) {
  super(title);
  this.parent = parent;
    console.log('Создание подзадачи');
 }
 complete() {
  super.complete();
  console.log(`Подзадача : "${this.title}" выполнена! `);
 }

}


let task = new Task('Изучить JavaScript');
let subtask = new SubTask('Изучить ES6', task);

console.log(SubTask.getDefaultTitle());
console.log(SubTask.count);


task.complete();
subtask.complete();

console.log(task);
console.log(subtask);

И напоследок давайте взглянем на то, что сгенерировал Babel:


'use strict';

var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Task = function () {
 function Task(title) {
  _classCallCheck(this, Task);

  this._title = title;
  this.done = false;
  Task.count += 1;
  console.log('Создание задачи');
 }

 _createClass(Task, [{
  key: 'complete',
  value: function complete() {
   this.done = true;
   console.log('\u0417\u0430\u0434\u0430\u0447\u0430 : "' + this.title + '" \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0430! ');
  }
 }, {
  key: 'title',
  get: function get() {
   return this._title;
  },
  set: function set(value) {
   this._title = value;
  }
 }], [{
  key: 'getDefaultTitle',
  value: function getDefaultTitle() {
   return 'Задача';
  }
 }]);

 return Task;
}();

Task.count = 0;

var SubTask = function (_Task) {
 _inherits(SubTask, _Task);

 function SubTask(title, parent) {
  _classCallCheck(this, SubTask);

  var _this = _possibleConstructorReturn(this, (SubTask.__proto__ || Object.getPrototypeOf(SubTask)).call(this, title));

  _this.parent = parent;
  console.log('Создание подзадачи');
  return _this;
 }

 _createClass(SubTask, [{
  key: 'complete',
  value: function complete() {
   _get(SubTask.prototype.__proto__ || Object.getPrototypeOf(SubTask.prototype), 'complete', this).call(this);
   console.log('\u041F\u043E\u0434\u0437\u0430\u0434\u0430\u0447\u0430 : "' + this.title + '" \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0430! ');
  }
 }]);

 return SubTask;
}(Task);

var task = new Task('Изучить JavaScript');
var subtask = new SubTask('Изучить ES6', task);

console.log(SubTask.getDefaultTitle());
console.log(SubTask.count);

task.complete();
subtask.complete();

console.log(task);
console.log(subtask);



Кода опять много, но тем не менее можно разобрать название класса Task , конструктор, методы геттеры и сеттеры, подкласс ... Ну и так далее . Таким образом, мы теперь разобрались как работают классы и наследование .

В следующем посте мы сравним объектно-ориентированное программирование, как это было в ES5 и как это есть в ES6.


UPDATE


Основные отличия ES6 от ES5.

  1. Функции в ES6 не поднимаются. То есть нельзя вызвать функцию до её объявления в коде.
  2. В ES6 переменные “не загрязняют” глобальное пространство имен.


В ES5 все переменные являются производными глобального объекта window и мы всегда можем это проверить простым выводом в консоль:


console.log(window.name_var === name_var);

// true



В ES6 это не так.

Объявление функций и классов.

Функции в джаваскрипт можно Объявлять двумя способами:

Объявление функции - function declaration


function имя_функции() {
 что-то делает.
}


Выражение функций - function expression.

Для того чтобы объявить функцию нужно в начале объявить переменную и в качестве значения присвоить функцию. Функция может быть безымянной или иметь название. Если указать название то это поможет при отладке.


let Task = function Task() {

} 



Далее мы можем создать новый объект используя функцию Task:


let task = new Task();



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

Class Declaration - объявление класса.

Это самый простой и знакомый нам способ.


class Task {
 constructor(){
  console.log(‘Creating a Task);
}
};



Class Expression - выражение класса.

Для этого создадим переменную и в качестве значения присвоим класс. Этот класс может иметь имя или быть безымянным. Если мы указываем имя то это поможет при дальнейшей отладке. В теле класса укажем конструктор.


let Task = class Task {
 constructor(){
  console.log(‘Creating a Task’);
}
};

let task = new Task();



Если посмотреть этот код в консоли браузера, то мы увидим :

Creating a Task

Даже если мы уберем имя класса Task, то в браузере мы увидим то же самое сообщение.

Теперь давайте попробуем создать наследование.

Создадим переменную, которой присвоим в качестве значения - безымянных класс, который является расширением родительского класса.

В теле класса укажем конструктор, который будет вызывать родительский класс (super()) и выведем сообщение.


let SybTask = class extends Task {
 constructor(){
  super();
  console.log(‘Creating a SubTask’);
}
};

let subtask = new SubTask();



Если посмотреть результат в браузере, то мы увидим два сообщения о создании задачи и одно сообщение о создании подзадачи.





                                                                                                                                                             

среда, 19 сентября 2018 г.

ES6: Классы (IX).

Классы в ES6

Классы являются основой объектно-ориентированное программирование но не в JavaScript.



Все материалы по ES6


В объектно-ориентированное программирование наследование основано на прототипах объектов, а не на классах.

Если вы когда-нибудь писали код на Java Script имитируя классы, ты уверен согласитесь синтаксис не самый элегантный и простой.

С появлением es6 JavaScript наконец-то добавлена поддержка классов. Новый синтаксис не вводит новую объектно-ориентированная модель наследования, внутри используется всё те же прототипы.

Синтаксис классов это так называемый syntactic Sugar или синтаксический сахар, который призван немного подсластить, то есть упростить работу с классами и наследованием в JavaScript.

Сегодня мы рассмотрим новый синтаксис классов, а в следующем посте - наследование.

Первое, что мы сделаем, это запустим скрипт Babel, который будет следить за файлами и при их изменении создавать свежие версии.

Откроем папку проекта в командной строке (КС). Вводим команду:

npm run watch


И нажать Enter

В папке src создадим файл classes.js и сразу укажу его в файле index.html

<script src="src/classes.js"></script>

HTML последние версии браузеров поддерживает класс без транспиляции, и мой браузер входит в их число, но тем не менее в конце, мы посмотрим на транспилированный код.

Начнем сначала а именно создание классов.

Создание классов

Класс это формы или шаблон по которому создаются объекты. Классы в JavaScript определяют свойства, то есть какой будет объект и методы - что этот объект будет делать. При создание классов используется ключевое слово class, после которого указывается имя класса. В качестве примера я создам класс названием Task - или задача. Тело класса находится в фигурных скобках и в нём будут находиться члены класса, методы и свойства. А в консоли мы посмотрим на класс используя оператор typeof.


class Task {

};

console.log( typeof Task );





В консоли мы видим function. Это означает, что классы это функции, которые создают объекты. Теперь перейдем к созданию объектов.

Объекты

Объект - это экземпляр или представитель класса.

То есть объект созданный по шаблону с определёнными свойствами и методами указанными в классе. Для создания объектов мы используем ключевое слово new.

После класса мы объявим переменную task с маленькой буквы и в качестве значения укажу new Task То есть мы вызываем функцию Task передний ней ключевое слово - new.

Теперь, давайте в консоли посмотрим на тип переменной task и также проверим является ли task, представителем класса Task -с большой буквы.

Мы можем это проверить используя оператор instanceof.


class Task {

};

let task = new Task();

console.log( typeof task );
console.log( task instanceof Task );





Мы видим, что переменная тоски является объектом или точнее, содержит ссылку на объект, и этот объект является представителем класса.

Конструктор.

Конструктор - это особый метод, который вызывается в момент создания объекта.

То есть когда мы используем ключевое слово new Он создаёт свойства и инициализирует. То есть подготавливает объект к использованию.

В классе может быть только один конструктор!

Если мы укажем несколько, то получим ошибку.

Если же мне укажем конструктор вообще-то JavaScript создаст пустой конструктор.

Для того чтобы объявить конструктор в теле класса пишем constructor() {} Внутри конструктора я выведу сообщение о том что происходит создание задачи.


class Task {
 constructor() {
  console.log('Происходит создание задачи');
 }
};

let task = new Task();

console.log( typeof task );
console.log( task instanceof Task );



Давайте посмотрим на результат браузере.



и мы увидим сообщение о создании задачи

тем самым мы увидели, что конструктор сработал.

Свойства

Свойства - это характеристики объекта.

Они описывают какой объект или что у него имеется.

Свойство указывается в конструкторе. Таким образом каждый представитель класса имеет эти свойства.

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

Например у класса Task будет свойства title или заголовок, который будет содержать заголовок задачи.

Для того, чтобы указать свойство я напишу слово this. Дальше названия свойства title и в принципе всё, но мы можем указать значение. И в качестве значения давайте укажем

"Выучить JavaScript"


class Task {
 constructor() {
  this.title = "Выучить JavaScript";
  console.log('Происходит создание задачи');
 }
};

let task = new Task();

console.log( typeof task );
console.log( task.title );



и в консоли давайте выведем значение title.



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

let task = new Task("Выучить JavaScript");

Далее, в конструкторе нам необходимо указать параметр title.

И давайте присвоим ему значение по умолчанию пустую строку.

И свойству при присвоении - значение параметра title.


class Task {
 constructor( title = '' ) {
  this.title = title;
  console.log('Происходит создание задачи');
 }
};

let task = new Task("Выучить JavaScript");

console.log( typeof task );
console.log( task.title );



И в консоли мы видим тот же самый результат.

Теперь мы можем создать вторую задачу и в качестве заголовка указать что-нибудь другое - номер "выучить ES6".


class Task {
 constructor( title = '' ) {
  this.title = title;
  console.log('Происходит создание задачи');
 }
};

let task = new Task("Выучить JavaScript");
let task2 = new Task("Выучить ES6");

console.log( typeof task );
console.log( task.title );
console.log( task2.title );





Давайте добавим классу Task ещё одно свойство которое будет называться done, в котором будет указываться выполнена задача или нет.

По умолчанию мы поставим false. То есть задачи по умолчанию будет не выполнена.

При создании объекта принимать аргумент done мы не будем.

Одно маленькое замечание - свойства указывается только в конструкторе! Если у вас есть опыт работы с другими языками и такими как Java, C++ или C# в которых свойство указывает отдельно от конструктора, а в конструкторе инициализируется, то в джаваскрипт свойство указываются исключительно в конструкторе!




Так как на фото выше мы сделать не можем!

Или убрать слово let и var или const всё это не будет работать!

Насколько я знаю это исправят в следующей версии JvaScript.

Далее мы рассмотрим создание методов методы.

Методы.

Методы это то что объект умеет делать, то есть его возможности.

По сути это функции, которые в классе называются методами.

Давайте добавим классу Task метод complete(){}, с помощью которого мы укажем что задача выполнена.

Чтобы указать метод мы пишем названия метода, после чего пара круглых скобок за ними пару фигурных скобок. В теле метода мы напишем this.done = true;

Заметьте что между методами в классах не ставится, если я поставлю то мы получим ошибку


И для того чтобы видеть результат - console.log(`Задача "${this.title}" выполнена!`);


class Task {
 constructor( title = '' ) {
  this.title = title;
                this.done = false;
  console.log('Происходит создание задачи');
 }
  complete() {
  this.done = true;
  console.log(`Задача "${this.title}" выполнена!`);
 }
};

let task = new Task("Выучить JavaScript");
let task2 = new Task("Выучить ES6");

console.log( typeof task );
console.log( task.title );
console.log( task2.title );

task2.complete();





В самом сообщении воспользуюсь шаблоны строкой.

Далее, придём к статическим свойствам и методам

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



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



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

Статические свойства часто используются для хранения вспомогательной информации и в отличие от многих других языков, например Java, C++ или C#, мы не можем Объявлять статические свойства используют ключевое слово static.

Если мы попробую написать будет ошибка.



Так что же делать если статическая переменная мы не можем объявить о самом классе?

В самом деле это делается очень просто.

После объявления класса, мы пишем название класса и далее, так как функции в JavaScript являются такими же объектами, как и всё остальное, мы можем просто присвоить свойство.

И сразу укажем значение.

И в конструкторе при создании задачи мы давали Task.count += 1;

И после того как мы создали задачу...

class Task {
 constructor( title = '' ) {
  this.title = title;
  this.done = false;
  Task.count += 1;
  console.log('Происходит создание задачи');
 }
  complete() {
  this.done = true;
  console.log(`Задача "${this.title}" выполнена!`);
 }
};

Task.count = 0; 

let task = new Task("Выучить JavaScript");
let task2 = new Task("Выучить ES6");


console.log( task.title );
console.log( task2.title );

console.log( Task.count );
task2.complete();



Давайте вывезем значение в консоль. Оно должно равняться двум.



И действительно, мы видим 2.

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

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

Статические методы часто используются для создания вспомогательных функций.

Давайте добавим Task статический метод.

Для того чтобы создать статический метод убежим ключевое слово static и дальше название метода.

Статический метод getDefaultTitle() с помощью которого мы сможем получить значение заголовка по умолчанию если тот не был передан. Поэтому в теле метода мы просто вернем слово "Задача".


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



теперь в конструкторе мы можем указать ->


class Task {
 constructor( title = Task.getDefaultTitle() ) {
  this.title = title;
  this.done = false;
  Task.count += 1;
  console.log('Происходит создание задачи');
 }
 get done() {
  return this.done === true ? 'Выполнено' : 'Не выполнено';
 }
 complete() {
  this.done = true;
  console.log(`Задача "${this.title}" выполнена!`);
 }
 static getDefaultTitle() {
   return "Задача";
 }
};

Task.count = 0; 

let task = new Task("Выучить JavaScript");
let task2 = new Task("Выучить ES6");
let task3 = new Task();

console.log( task.title );
console.log( task2.title );
console.log( task3.title );

console.log( Task.count );
task2.complete();



Давайте создадим задачу 3 без аргументов посмотрим на результат.



И мы видим что заголовок задачи 3 просто задача.

Если мы попытаемся вызвать метод .getDefaultTitle() у образца объекта которые мы сохранили в переменной task или task2 - не важно, то мы получим ошибку!

Давайте попробуем так - task.getDefaultTitle();

Мы получим ошибку Uncaught TypeError: task.getDefaultTitle is not a function.То есть у объектов task этого метода нет!

Единственный способ получить к нему доступ - использовать название самого класса!

У классов, помимо свойство методов можно добавить особые свойства get и set.

Особые свойства get и set.

Особые свойства get и set еще называются геттер и сеттер которые внутри класса выглядит ведут себя как методы а снаружи выглядит и ведут себя как свойства.

эти свойства - методы позволяют получить доступ и присвоить значение "настоящим свойствам объекта". То есть выступать в роли фильтра.

Давайте добавим свойства get и set классу - Task.

Для начала я немного почищу код. Удалю все созданные объекты и их выводы, кроме одного - task.

Итак начнем.

Get или гетер.
Если вывести в консоль значения console.log(task.done); то мы увидим false.



И если задача была бы выполнена тогда true.

Эти значения не совсем понятны простым пользователям. Было бы неплохо если бы при обращении к свойствам появлялось понятное сообщение.

Эту проблему можем решить используя свойство Get.

Давайте добавим классу Task это свойство.

Для этого в классе после конструктора я напишу слова get далее - название done, пару кругу скобок и пару фигуры скобок. Очень похоже на метод.

Но на самом деле это и есть метод.

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

В теле метода мы вернём строку в соответствии со значением свойства done.

Если done будет равняться true, то мы вернем одно значение если false то другое.

Давайте посмотрим на результат и мы увидим ошибку.



Которая говорит нам о том что не удалось указать свойство get .

Дело в том что название свойств Get и Set не должны совпадать с названием основных свойств объекта!

В нашем примере объявляя свойство get done() мы перезаписываемый значение done которое изначально указанно в конструкторе.

Есть несколько способов решить эту проблему и все они сводятся к переименование названий.

В других языках, например C#, названия личных свойств, там они называются полями, пишется с маленькой буквы, а публичных свойств с большой.

В JavaScript не принято называть свойства или метода заглавные буквы.

Также JavaScript свойства и методы в классах не делятся на личные и публичные.

Они всегда публичные!

И чтобы решить эту проблему, перед названием свойства методов которые мы хотим скрыть, часто ставят нижнее подчеркивание.

Давайте перед свойством done поставить нижнее подчеркивание.

this._done = false;

Это не означает что мы скрыли свойство или метод. К ним также легко получить доступ, но теперь хотя бы понятно что эти свойства личные.


class Task {
 constructor( title = Task.getDefaultTitle() ) {
  this.title = title;
  this._done = false;
  Task.count += 1;
  console.log('Происходит создание задачи');
 }
 get done() {
  return this._done === true ? 'Выполнено' : 'Не выполнено';
 }
  complete() {
  this.done = true;
  console.log(`Задача "${this.title}" выполнена!`);
 }
  static getDefaultTitle() {
   return "Задача";
 }
};

Task.count = 0; 

let task = new Task("Выучить JavaScript");
console.log(task.done, task._done);





Давайте попробуем выполнить задачу


task.complete();
console.log(task.done, task._done);



И ещё раз посмотрим.

И мы получили ошибку!



Это произошло потому что мы объявили свойство get но в методе .complete() мы присваиваем this.done значение true, но при этом свойства set мы не указали.


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



Давайте добавим классу Task свойство set.

Свойство set
. Для этого после get я напишу set дальше название свойства.

Отличие set от get заключается в том что set принимает параметр ,который мы можем присвоить свойству, предварительно проверив этот параметр.

Вы можете назвать его как угодно но чаще всего его называют value.

И в теле метода мы проверим наличие значения value и Является ли это значение типом boolean. Если мы прошли проверку это свойство мы присвоим this._done.

Если же мы проверку не прошли то в консоли мы выдадим ошибку.


class Task {
 constructor( title = Task.getDefaultTitle() ) {
  this.title = title;
  this._done = false;
  Task.count += 1;
  console.log('Происходит создание задачи');
 }
 get done() {
  return this._done === true ? 'Выполнено' : 'Не выполнено';
 }
 set done(value) {
  if(value!== undefined && typeof value === 'boolean') {
   this._done = value;
  } else {
   console.error("Ошибка! Укажите значение true или false.");
  }
 }
  complete() {
  this.done = true;
  console.log(`Задача "${this.title}" выполнена!`);
 }
  static getDefaultTitle() {
   return "Задача";
 }
};

Task.count = 0; 

let task = new Task("Выучить JavaScript");
console.log(task.done, task._done);
task.complete();
console.log(task.done, task._done);



Давайте посмотрим на результат и мы видим что задача успешно выполнена!



Давайте попробуем присвоить, допустим Один.


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



И мы видим сообщение об ошибке.

О том что мы указали неверные значения.



Свойств можем указать сколько угодно.

В нашем примере мы можем переделать свойств title для того чтобы она использовала get и set. нам даже не обязательно иметь внутреннее свойство для того чтобы воспользоваться get и set.

Тем самым мы получим так называемая псевдо-свойства.

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

У нас в конце-концов получился вполне себе полноценный класс.

И напоследок Давайте взглянем на то что сгенерировал Babel.


'use strict';

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Task = function () {
 function Task() {
  var title = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Task.getDefaultTitle();

  _classCallCheck(this, Task);

  this.title = title;
  this._done = false;
  Task.count += 1;
  console.log('Происходит создание задачи');
 }

 _createClass(Task, [{
  key: 'complete',
  value: function complete() {
   this.done = 1;
   console.log('\u0417\u0430\u0434\u0430\u0447\u0430 "' + this.title + '" \u0432\u044B\u043F\u043E\u043B\u043D\u0435\u043D\u0430!');
  }
 }, {
  key: 'done',
  get: function get() {
   return this._done === true ? 'Выполнено' : 'Не выполнено';
  },
  set: function set(value) {
   if (value !== undefined && typeof value === 'boolean') {
    this._done = value;
   } else {
    console.error("Ошибка! Укажите значение true или false.");
   }
  }
 }], [{
  key: 'getDefaultTitle',
  value: function getDefaultTitle() {
   return "Задача";
  }
 }]);

 return Task;
}();

;

Task.count = 0;

var task = new Task("Выучить JavaScript");
console.log(task.done, task._done);
task.complete();
console.log(task.done, task._done);



Здесь очень много сгенерированного кода но в целом, в принципе, можно различить название класса Task свойства и пр.

Если выписали объектно-ориентированный JavaScript используя ES5, то вы писали, скорее всего, что-то похожее на то что сгенерировал Babel. Не совсем конечно, но близко.

Лично я очень рад что в JavaScript наконец-то появились классы.

Но многие программисты могут со мной не согласится, так как JavaScriptэто больше функциональный язык нежели чем классический.

А прелесть JavaScript заключается в том, что если выходите пользоваться классами, то можете пользоваться классами если хотите писать функциональной код Можете писать функциональный код. Здесь каждый найдёт что-то по душе!

Далее рассмотрим наследование.



                                                                                                                                                             

вторник, 18 сентября 2018 г.

Работа с REPL. Основные команды.

REPL - read eval print loop это интерактивный компонент, который поставляется вместе с Node.js




При работе в Node.js часто приходится запускать целые файлы или части кода для тестирования. Для удобства можно использовать REPL, просто набрав в КС (здесь и далее - командная строка):

node


Нажать Enter.

Без ключевого слова var будет возвращать введенные выражения, а с var - undefined



Так происходит потому что в результате операции присваивания значения переменной не возвращает результат.

Вот интересный пример:



Ожидали увидеть true?

Не забывайте, что интерпретатор пойдет слева направо и вначале сравнит 3 и 2. Получит true. И только потом сравнит true и 1 и получит false

3>2==1 будет true, потому что при не строгом сравнении true==1

Можете проверить!

Многострочный и сложный JS код

JS код можно набирать в REPL точно так же как в обычном файле, включая необходимые инструкции для импорта модулей.

Пример с модулем Query String:



Переменная qs без var и нажать Enter. Пока мы работаем без var, то сразу видим результат. В данном случае это интерфейс объекта querystring.

REPL предоставляет возможность введения многострочного кода после любой открывающей скобки { или (. При вложенности, просто увеличивается кол-во точек перед введенным кодом.

Здесь можно также вставлять блоки кода - правая кнопка мыши и выбрать "Вставить".

Основной объект Node.js global на официальном сайте Node.js документирован довольно слабо. Чтобы это испавить, вы можете посмотреть всё информацию об этом объекте просто набрав:

console.log(global);


Или:

gl = global;


Для перехода по уже набранным вами командам, можно воспользоваться стрелками "вверх" - "вниз" - . Выбрать нужную и нажать Enter.

Управление REPL с помощью команд.
  • Ctrl + C - Завершает выполнение текущей команды. Повторное нажатие - выход из REPL.
  • Ctrl + D или написать в консоли .exit - Выход из REPL.
  • Tab - Автоматическое завершение имени глобальной или локальной переменной.
  • - Переход вверх по списку введенных команд.
  • - Переход вниз по списку введенных команд.
  • _ -(нижнее подчеркивание) - ссылка на результат вычисления после выражения.


Ниже приведу список команд для ввода в поле REPL:

  • .break - Если вы запутались с введением многострочного кода, эта команда вернет вас к самому началу, но весь многострочный код будет потерян.
  • .clear - Перезапуск объекта контента и очистка любого многострочного выражения. Эта команда по сути запускает сеанс с самого начала.
  • .exit - Выход из REPL
  • .help - Вывод всех доступных команд.
  • .save - Сохранение текущего REPL сеанса в файле.
  • .load путь/к/файлу.js - Загрузка файла в текущий сеанс (


Команда .save путь/к/файлу/для/сохранения.js сохраняет в файле все, что было введено в текущий объект контента. Пока не будет специально создан новый объект контента или использована команда .clear. Контент включает в себя все, что было введено в текущий REPL сеанс.

Node компонент REPL является удобным интерактивным средством упрощающим разработку. REPL позволяет не только проверять код перед его включением в файлы, но и создавать приложения в интерактивном режиме, сохраняя результаты после завершения.

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

Если вы собираетесь использовать REPL для разработки, то не забывайте чаще сохранять свою работу!



                                                                                                                                                             

вторник, 11 сентября 2018 г.

ES6: Объекты (VIII).

Объекты в ES6

Рассмотрим нововведения, которые помогут нам работе с объектами в ES6.



Все материалы по ES6


В отличии от других языков, в JavaScript мы очень часто работаем с литералами объектов. То есть с простыми объектами, которые не являются представителями какого-либо класса, а представляют из себя набор свойств и значений.

В ES6 было добавленно несколько нововведений, которые немного упростили синтаксис при работе с простыми объектами.

Первое, что мы сделаем, это запустим скрипт Babel, который будет следить за файлами и при их изменении создавать свежие версии.

Откроем папку проекта в командной строке (КС). Вводим команду:

npm run watch


И нажать Enter

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

В папке src создадим файл objects.js и сразу укажу его в файле index.html

<script src="src/objects.js"></script>

Начнем со свойств.

Допустим, имеется три переменные - имя, фамилия и электронная почта.


let firstName = "Bill",
    lastName = "Smit",
    email = "bill@gmail.com";



Далее, создадим объект в котором в качестве свойств мы укажем названия переменных:


let firstName = "Bill",
    lastName = "Smit",
    email = "bill@gmail.com";

let person = {
 firstName: firstName,
 lastName: lastName,
 email: email
};

console.log( person );



И в конце мы вывели значения объекта person в консоли браузера.



В ES6, если свойство совпадает с названием переменной, то мы можем указать только свойство. Значение автоматически вставится из переменной с таким же названием.

Объекст person мы можем записать так:


let person = {
 firstName,
 lastName,
 email
};



Сохраняем, и видим тотже результат, что и на фото выше.

Давайте посмотрим на то, что транспилировал Babel

dist/object.js


"use strict";

var firstName = "Bill",
    lastName = "Smit",
    email = "bill@gmail.com";

var person = {
 firstName: firstName,
 lastName: lastName,
 email: email
};

console.log(person);



Он сделал тоже самое, что у нас было изначально!

Теперь перейдем к методам

Создание методов тоже стало немного проще.

Давайте добавим нашему объекту метод.


let firstName = "Bill",
   lastName = "Smit",
   email = "bill@gmail.com";

let person = {
 firstName,
 lastName,
 email,
 say: function() {
  console.log(`Hello, my name is ${firstName} ${lastName}`);
 }
};

console.log( person );
person.say();





В ES6 мы можем немного упростить написание метода, а именно - убрать слово function и двоеточие, оставив только круглые скобки ()


 say() {
  console.log(`Hello, my name is ${firstName} ${lastName}`);
 }


В консоле мы увидим тотже самый результат.

Если посмотреть на транспилированный код, то можно увидеть то, что Babel сделал все ,как и было изначально:

dist/object.js


"use strict";

var firstName = "Bill",
    lastName = "Smit",
    email = "bill@gmail.com";

var person = {
 firstName: firstName,
 lastName: lastName,
 email: email,
 say: function say() {
  console.log("Hello, my name is " + firstName + " " + lastName);
 }
};

console.log(person);
person.say();



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

Добраться до свойства firstName объекта person мы можем используя точку - person.firstName или квадратные скобки - person['firstName'].

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

Создадим новую переменную в которой в качестве значения укажем название свойства.

let property = 'firstName';

Далее в квадратных скобках мы можем указать название переменной

person[property]; Это будет тоже самое, что и person['firstName'];

Теперь, если поменять значение переменной на lastName

let property = 'lastName';

Так мы получим доступ к свойству lastName

Так можно сделать только в том случае если объект уже существует!!!


Но в ES6 мы можем использовать квадратные скобки прями внутри объекта. Тем самым динамически указывать названия сво-ва.

Например:


let property = 'lastName';
person[property]; // person['firstName']
person = {
    [property]: 'Bill'
};


В данном случае person будет присвоен объект, в котором сво-ва мы указываем динамически из переменной property.

Для того, чтобы указать firstName нам нужно указать в параменной let property - 'firstName'.

Вот так:

let property = 'lastName';

и так далее. Мы можем динамически менять значение переменной.

Для более практичного примера я создам функцию createCar, которая будет принимать название свойства - property и значение свойства - value.

В функции мы создадим простой объект с одним свойством и вернем его.

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

Вначале создаем объект - var car ={}

Далее в квадратных скобках указываем его свойства car[property]

и присваеваем ему значение value

car[property] = value

И возвращаем объект - return car

Чтобы посмотреть на результат console.log(createCar('vin', 1));


function createCar( property, value ) {
 var car = {};
 car[property] = value;
 return car;
};

console.log(createCar('vin', 1));





В консоле мы увидим объект со свойством vin и значением - 1.

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

Поэтому мы сразу вернем объект - return

и в свойствахв квадратных скобках мы указываем переменную [property] со значением value.

[property]:value


function createCar( property, value ) {
 return{
  [property]: value
  };
};
console.log(createCar('vin', 1));



В консоле мы получим тотже самый результат.

И теперь давайте посмотри как это все сгенерировал Babel

dist/object.js

function _defineProperty(obj, key, value) { 
if (key in obj){ 
Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });
 } 
else { 
obj[key] = value; } return obj;
 }

function createCar(property, value) {
 return _defineProperty({}, property, value);
};
console.log(createCar('vin', 1));



Функция _defineProperty обращается к глобальному объекту, чтобы создать свойства.

Благодаря новому синтаксису нам нет в этом необходимости и записи стали проще.

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

Мы можем теперь добавить нижнее подчеркивание к названию совойства ['_' + property]: value


function createCar( property, value ) {
 return{
  [property]: value,
  ['_' + property]: value
  };
};
console.log(createCar('vin', 1));



У объекта появилось новое свойство - _vin



Или мы можем использовать метод toUpperCase()


function createCar( property, value ) {
 return{
  [property]: value,
  ['_' + property]: value,
  [property.toUpperCase()]: value
  };
};
console.log(createCar('vin', 1));



Результат - новое свойство VIN:



Можно динамически указать название метода:

function createCar( property, value ) {
 return{
  [property]: value,
  ['_' + property]: value,
  [property.toUpperCase()]: value,
  ['get' + property]() {
       return this[property];
      }
  };
};
console.log(createCar('vin', 1));



Теперь у обекта появился динамически заданный метод getvin



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

Эти суппер-свойства называются get и set или геттер и сеттер.

Давайте вернемся к объекту person и добавим ему оба этих метода.

Вначале добавим их в стиле ES5:

Чтобы добаить свойство мы воспользуемся объектом Objcts методом .defineProperty()

Первым параметром будет объект которому создаются свойства - person, а вторым параметром название свойства 'fullName', а далее принимается объект в котором это свойство описаывается.

В этом объестк первым свойством будет get - функция, которая ничего не принимает, но возвращает (this.firstName + ' ' + this.lastName).

Перед тем, как посмотреть результат я закоментирую код, который не относится к объекту person


Object.defineProperty(person, 'fullName', {
    get: function() {
        return this.firstName + ' ' + this.lastName;
    }

});



Сохраняем. Смотрим в консоле.

Теперь у нашего объекта person появилось новое свойство - fullName (на фото ниже оно подчеркнуто.

Теперь если мы обратимся к нему через точечную нотацию в консоле - person.fullName , то получим "Bill Smit", как на фото ниже в рамке.



Обратите внимание что get является функцие, но при этом мы не используем круглые скобки.

Внутри объекта - это метод, а снаружи это свойства!

Особого смысла метод set, в данном случае, не имеет, но для полноты картины, мы его определим. Его отличие от get в том. что set получает значение. Его можно назвать как угодно - это просто переменная. В нашем случае - value. В теле метода мы можем что-либо сделать - напрмер присвоить свойство this.firstName = value;


let person = {
    firstName,
    lastName,
    email,
    sayHello() {
        console.log(`Hi my name is ${this.firstName} ${this.lastName}`);
    },
    get fullName() {
        return this.firstName + ' ' + this.lastName;
    },
    set fullName(value) {
        this.firstName = value;
    }
};



Сохраним и посмотрим в консоле:



  1. - мы увидим, что у нашего объекта появились два метода (геттер и сеттер).
  2. - назначим новое значение свойству fillName - > person.fullName = "John";
  3. - вывдеме значение этого свойства - person.fullName


Для того, чтобы переписать код на ES6, все что нам нужно сделать это добавить к нашему объекту person


let person = {
    firstName,
    lastName,
    email,
    sayHello() {
        console.log(`Hi my name is ${this.firstName} ${this.lastName}`);
    },
    get fullName() {
        return this.firstName + ' ' + this.lastName;
    },
    set fullName(value) {
        this.firstName = value;
    }
};



Результат в консоли будет тот же.



Чаще всего свойства get и set не присваиваются простым объектам. Они присваиваются, как правило, объектам - представителям какого-нибудь класса.

В ES6 добавили поддержку классов. О них мы поговорим позже.



                                                                                                                                                             

понедельник, 10 сентября 2018 г.

ES6: Цикл for...of (VII).

Цикл for...of

Сегодня мы рассмотрим цикл for...of, благодаря которому мы можем итерировать массивы и другие структуры.



Все материалы по ES6


Первое, что мы сделаем это запустим скрипт Babel, который будет следить за файлами и при их изменении создавать свежие версии.

Откроем папку проекта в командной строке (КС). Вводим команду:

npm run watch


И нажать Enter

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

В папке src создадим файл for.js и сразу укажу его в файле index.html

<script src="src/for.js"></script>

Последние версии браузеров поддерживают цикл for...of без транспиляции.

Цикл for...of очень похож на цикл for...in.

Разница в том, что for...of перебирает значения, в то время как for...in - ключи.

Если вы забыли как работает цикл for...in, то можно посмотреть это на примере:

Создадим массив с некоторыми значениями и попробуем вывести значения в консоль, используя цикл for...in и выведем сам массив.


let animals = ['cat', 'dog', 'hamster', 'fish', 'goat'];
for( animal in animals ) {
 console.log(animal);
};
console.log(animals);



В консоли мы увидим:



Вывели циклом for...in ключи массива, а нам нужно значения.

Конечно, имея ключ мы можем сделать так:


let animals = ['cat', 'dog', 'hamster', 'fish', 'goat'];
for( let animal in animals ) {
 console.log(animal);
};

for( let index in animals ) {
 console.log( animals[index]);
};

console.log(animals);





Мы так же можем воcпользоваться методом for...of


let animals = ['cat', 'dog', 'hamster', 'fish', 'goat'];
for( let animal in animals ) {
 console.log(animal);
};

for( let index in animals ) {
 console.log( animals[index]);
}

for( let animal of animals ) {
 console.log( animal );
}

console.log(animals);





Таким образом мы сможем легко вывоить значения циклом без использования кллючей.

Давайте посмотрим на то, что сгенерировал Babel


'use strict';

var animals = ['cat', 'dog', 'hamster', 'fish', 'goat'];
for (var animal in animals) {
 console.log(animal);
};

for (var index in animals) {
 console.log(animals[index]);
}

var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;

try {
 for (var _iterator = animals[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
  var _animal = _step.value;

  console.log(_animal);
 }
} catch (err) {
 _didIteratorError = true;
 _iteratorError = err;
} finally {
 try {
  if (!_iteratorNormalCompletion && _iterator.return) {
   _iterator.return();
  }
 } finally {
  if (_didIteratorError) {
   throw _iteratorError;
  }
 }
}

console.log(animals);



Если честно, то совсем нет желания разбираться с тем, что там происходит. :-)

Меня вполне устраивает цикл for...of!

Помимо массивов цикл for...of позволет итеррировать и новые структуры, которые будут добавлены в ES6 - это объекты set и map.

О них чуть позже.



                                                                                                                                                             


Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.Пишите мне - kolesnikovy70 почта gmail.com