Translate

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

суббота, 22 сентября 2018 г.

ES6: Стрелочные функции (XI).

В ES6 есть новый способ создания функций - С помощью оператора Стрелка => . Такие функции называются стрелочные. Они предлагают более компактный синтаксис. Они не имеют имени и они по-своему работают с this.





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


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

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

npm run watch


И нажать Enter

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

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

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

Давайте напишем функцию, которая складывает два числа и возвращает их сумму. Назовем функцию add.


function add ( x, y ) {
 return x + y;
}

console.log ( add (3, 6 ));



В консоли мы увидим результат - 9

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

Уберем слово function, уберем имя функции и уберем фигурные скобки, и слово - return. После параметров поставим стрелку.


let add = ( x, y ) => x + y;

console.log ( add (4, 6 ));



Если посмтреть на тип переменной add используя оператор typeof:

console.log(typeof(add));

То мы увидим в консоли function

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


"use strict";

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var add = function add(x, y) {
  return x + y;
};
console.log(add(4, 6));
console.log(typeof add === "undefined" ? "undefined" : _typeof(add));



Мы видим, что Babel превратил наш код в простое выражение функцией.

Давайте напишем простую функцию которая будет возводить заданное число в квадрат.


let add = ( x, y ) => x + y;
console.log ( add (4, 6 ));
console.log(typeof(add));

let square = function( a ) {
 return a * a;
}
console.log( square ( 4 ));



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



Стрелочная функция будет выглядеть вот так:


let square = x => x * x;



Если стрелочная функция принимает только один параметр, то нет необходимости заключать его в скобки!
Давайте напишем функцию которая вообще не принимает параметров.


function givNumer () {
 return 33;
}

console.log( givNumer ());



Эта функция просто выводит в консоль число 33. Стрелочная:


let givNumer = () => 33;
console.log( givNumer ());



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


let log = function () {
 console.log( 'Hello World!' );
};
log();



Стрелочная:


let log = () => console.log('Hello World!!!');
log();



Создадим функцию тело которой будет состоять из двух строк.

Функция будет принимать два параметра. Теле функции создадим переменную. После этого вернём результат.


let mult = function ( a, b ) {
 let result = a * b;
 return result;
}

console.log( mult ( 4, 5 ));



Если в стрелочной функции несколько строк, то фигурные скобки - {} обязательны! И обязательно определить то, что возвращает эта функция, используя ключевое слово return


Стрелочная:


let mult = ( a, b ) => {
 let result = a * b;
 return result;
}
console.log( mult (4, 5 ));



Теперь создадим функцию, которая возвращает литерал объекта:


let literal = function () {
 return { name : 'John'};
}

console.log ( literal () );



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



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

Следует помнить, что если стрелочная функция возвращает литерал объекта, то нужны круглые скобки - ()


Стрелочная функция возвращающая литерал объекта:


let literal = () => ( { name : 'John'} );

console.log ( literal () );



Теперь попробуем использовать стрелочную функцию в качестве IIFE - Immediately-invoked function expression

Если сказать коротко, то это функция, которая выполняется сразу после объявления

Это выглядит вот так:


( function () {
 console.log('IIFE');
})();



Стрелочная IIFE - функция будет выглядеть вот так:


( () => console.log('IIFE'))();



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


Её нельзя просто так взять и спустить на строку ниже. Выдаст ошибку!



Практическое применение стрелочных функций.

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

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

Давайте вы считаем сумму всех переменных массива. Для этого я объявлю еще одну переменную - let sum = 0;

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


let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

let sum = 0;

numbers.forEach( function( num ) {
 sum += num;
});

console.log ( sum );



В консоли мы увидим 55. Давайте превратим эту функцию в стрелочную:

numbers.forEach( num => sum += num );

console.log(sum);



Таким образом, то что у нас ранее занимало три строки, теперь занимает одну.

Также мы можем возвести в квадрат каждый элемент массива.


let squared = numbers.map( n => n * n );

console.log ( squared );





Далее посмотрим как стрелочные функции работают с объектами.

Стрелочные функции и this.

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

У объекта person будет свойство name со значением ‘Bob’ и свойства greet - поприветствовать.В консоль выведем приветствие также посмотрим назначение this.


let person = {
 name: 'Bob',
 greet: function () {
  console.log( 'Hello! My name is ' + this.name );
  console.log( this );
 }
};

person.greet();



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



Теперь мы заменим функцию на стрелочную и посмотрим, что произойдет с this.

Убрали слово function и добавили =>.


let person = {
 name: 'Bob',
 greet: () => {
  console.log( 'Hello! My name is ' + this.name );
  console.log( this );
 }
};

person.greet();




Теперь мы не получили значение имени и в качестве значения this - window!

Но почему? Дело в том, что значение this берется из контекста в котором функция объявлена.! Независимо от того, где эта функция будет выполнена. Это можно увидеть на картинке:

У нас есть программа.

В ней пока что кроме объекта window ничего нет. Добавили объект person. Заметьте, что у метода мы используем стрелочную функцию. Как мы и говорили - значение this будет браться из контекста. Контекст это окружение. В данном случае окружением объекта person, всех его свойств и методов, будет являться объект window. И если значение this будет браться из контекста, то this будет ссылаться на объект window.



Если мы рассмотрим обычную функцию, то мы знаем, что this ссылается на сам объект person. Вы можете спросить, почему значение this в стрелочных функциях берется из контекста? А ответ очень простой - их так сделали! :-) Дело в том, что стрелочные функции были созданы для решения проблем в другой ситуации. Давайте посмотрим на примере. Для того, чтобы увидеть проблему мы вернемся к нашей стрелочной функции.


let person = {
 name: 'Bob',
 greet: function () {
  console.log( 'Hello! My name is ' + this.name );
  console.log( this );
 }
};



Представим, что наш Bob достаточно занятой и ему нужно пара секунд, чтобы завершить свою работу. Ожидание в 2 сек. мы симулируем с помощью функции setTimeout();.В качестве первого параметра эта функция принимает функцию и вторым параметром - количество миллисекунд, которые необходимо подождать.


let person = {
 name: 'Bob',
 greet: function () {
         setTimeout( function () {
        console.log( 'Hello! My name is ' + this.name );
        console.log( this );
  }, 2000);
 }
};

person.greet();



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



Но почему? Если посмотреть наш код, то логично предположить. что this ссылается на объект person, так как мы используем обычную функцию. Дело в том, что setTimeout() принадлежит объекту window. Если написать так: window.setTimeout(), то как вы думаете на что ссылается thus? И в консоли мы получим тот же самый результат! В ES5 есть несколько способов решить эту проблему. Мы рассмотрим самый распространенный: Перед setTimeout() я объявлю еще одну переменную that и в качестве значения присвоим this. И теперь, в теле функции вместо this мы укажем that.


let person = {
 name: 'Bob',
 greet: function () {
  let that = this;
  setTimeout( function () {
       console.log( 'Hello! My name is ' + that.name );
       console.log( that );
  }, 2000);
 }
};

person.greet();



Теперь благодаря замыканию Функция которую мы отправляем в setTimeout() будет иметь доступ к переменной that, значением которой будет this, то есть, в данном случае, объект person.



Можно для наглядности посмотреть на то, что ссылаются наши that и this.


let person = {
 name: 'Bob',
 greet: function () {
  let that = this;
  setTimeout( function () {
     console.log( 'Hello! My name is ' + that.name );
        console.log( 'It is my That = ' + that );
        console.log( 'It is my This = ' + this );

  }, 2000);
 }
};

person.greet();



В консоли мы увидим подтверждение:



Мы видим, что this будет объектом окна - This = [object Window], а that будет объектом нашего person - That = [object Object].

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


let person = {
 name: 'Bob',
 greet: function () {
  setTimeout( () => {
      console.log( 'Hello! My name is ' + this.name );
      console.log( 'It is my This = ' + this );
  }, 2000);
 }
};

person.greet();



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



В графическом примере для стрелочной функции контекстом будет служить объект person, а не объект window. именно поэтому this будет ссылаться на person.



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

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


var person = {
 name: 'Bob',
 greet: function greet() {
  var _this = this;

  setTimeout(function () {
   console.log('Hello! My name is ' + _this.name);
   console.log('It is my This = ' + _this);
  }, 2000);
 }
};

person.greet();

Babel использовал тот же самый метод, что и мы в ES5. Вся разница в том, что мы называли переменную that, а Babel назвал - _this. Благодаря замыканию, функция которую мы отправляем в setTimeout, будет иметь доступ к переменной _this и как следствие - к объекту person.

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

Еще некоторые особенности стрелочных функций:
  1. Стрелочные функции нельзя использовать как конструкторы объектов. То есть с ними нельзя использовать оператор new.




  2. Получим ошибку:



  3. Со стрелочными функциями нельзя использовать методы:
    1. bind()
    2. call()
    3. apply()
    4. Это происходит потому, что мы не можем изменить значение this. Оно автоматически берется из контекста.
Еще информацию по ES6 и стрелочным функциям вы можете посмотреть в моем посте ES6 для начинающих. (1)                                                                                                                                                              

Комментариев нет:

Отправить комментарий



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