Translate

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

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

ES6: Создание обещаний (promises)(XIV-2).

Создание обещаний (promises).

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



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


ES6 для начинающих (2)

В редакторе откроем новый файл- promises.js. Подключим его в нашем файле

index.html

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

В этом файле создадим функцию.


function applyForVisa( document ) {
    console.log('Обработка заявления... ');
};

applyForVisa({});



Она называется applyForVisa(). Это функция принимает документы на визу.

В консоли браузера мы выведем - Обработка заявления... . После чего мы в эту функцию вызываем. и в качестве объекта документа отправляем пустой объект - applyForVisa({});

Процесс получения визы займет всего 2 секунды. Эту задержку стимулируем с помощью метода setTimeout ();.

Он принимает функцию и количество миллисекунд.

А что именно произойдет через эти две секунды, мы напишем функции.

Создадим переменную виза. В качестве значения присвоим - пустой объект let visa = {};.

А вот что делать дальше?


function applyForVisa( document ) {
    console.log('Обработка заявления... ');
    setTimeout( function () {
        let visa = {};
       // ??
    }, 2000);
};

applyForVisa({});



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

Если мы напишем return visa; то мы вернем его из функции которые отправляем в

setTimeout, но при этом пользователь визу не получил!

Таким образом вернуть визу мы не можем!

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


applyForVisa({},function( visa ) {
	console.info('Виза получена!');
});



Данная функция получит visa, в качестве параметра. И в консоль браузера мы выедем сообщение - "Виза получена!".

Для вывода в консоль мы будем использовать метод - console.info();.

Теперь функция applyForVisa принимает не только документы ,но и функцию.

Назовем её resolve - то есть разрешить. После того как, мы создали объект виза, вызовем функцию resolve и в качестве аргумента отправим объект visa.


function applyForVisa( document, resolve ) {
    console.log('Обработка заявления... ');
    setTimeout( function () {
        let visa = {};
        resolve( visa );
    }, 2000);
};

applyForVisa( {}, function ( visa ) {
    console.info('Виза получена!');
});



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



Мы увидим сообщение- Обработка заявления и через 2 секунды, мы видим сообщение Виза получена.

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

Данная функция в качестве параметра примет причину - reson, почему нам отказали в визе. Давайте выведем эту причину в консоле браузера Используя метод console/error( reson )

Теперь функция applyForVisa принимает документы, функцию на случай если Виза одобряется, и функцию на случай если в визе отказывают. Назовем эту функцию reject - отказать.

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

Для этого воспользуемся Math.random();, чтобы получить случайное число. Если это число будет больше 0,5 то мы вызовем функцию resolve в качестве аргумента отправим пустой объект.

Представим, что это будет виза. В противном случае мы вызовем функцию reject и в качестве аргумента напишем - "В визе отказано: не хватило документов...".

Вызов функции resolve( visa ); мы уберем. Кстати, заметьте, функцию resolve я могу вызывать столько раз сколько я хочу.

Функцию reject я могу звать столько раз сколько я захочу и это может быть очень серьезной проблемой, если я использую какую-то стороннюю библиотеку, потому что этим кодом Я не руковожу!

Решить эту проблему нам помогут обещания! Но об этом чуть позже.


function applyForVisa( document, resolve, reject ) {
    console.log('Обработка заявления... ');
    setTimeout( function () {
        Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
       
    }, 2000);
};

applyForVisa( {}, function ( visa ) {
    console.info('Виза получена!');
},
function ( reson ) {
    console.error( reson );
});



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

При перезагрузке страницы мы будем получать два вида сообщений:



Добавим еще две функции.

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

Логика у них будет очень похоже на функцию applyForVisa. Поэтому просто оставим их пустыми.

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

Благо, что мы её уже получили и поэтому можем отправить.

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

в качестве аргумента отправим reservation. И давайте отправим функцию на случае ошибки.

Ой какой читабельный код получается!!!

На случай Если всё хорошо, и мы зарезервировали номер в отеле, давайте купим билеты.

Для покупки билета нам тоже нужно отправить номер резервации.

И наподобие кода выше: функцию на случае успеха и функцию на случай неудачи.

Если всё правильно записать, то получится достаточно много кода.

Это и есть как раз то, что называют Callback Hell.


function applyForVisa( document, resolve, reject ) {
    console.log('Обработка заявления... ');
    setTimeout( function () {
        Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
       
    }, 2000);
};
function bookHotel () {};

function buyTickets () {};

applyVisa({}, function( visa ){
	console.info('Виза получена!');
	bookHotel( visa, function (reservation){
		buyTikkets(reservation, function () {}, function () {});
	}, function (error) {})
},
function( reson ) {
	console.error( reson );

});



Красным отмечен полный кошмар вложений из функций обратного вызова. Их может быть очень очень много и это сделает код полностью нечитабельным!

Callback Hell - когда у одной функции обратного вызова есть еще функция обратного вызова и у той тоже функция обратного вызова, и так далее.

Таким образом есть две проблемы.
  1. - функции resolve reject могут быть вызваны один, два или три раза, а могут быть не вызваны ниразу. Мы предполагаем, что они будут вызваны один раз, но гарантировать это мы не можем.
  2. - это нечетабельный Callback Hell .
Всё это мы ррешим с помощью обещаний.


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           
        }, 2000);
        
    });
    return promise;
};

function bookHotel () {};

function buyTickets () {};

applyForVisa({})
    .then(
            function ( visa ) {
            console.info('Виза получена!');
            bookHotel();
            },
            function ( reson ) {
            console.error( reson );
         });



Код должен работать также.

В консоли мы увидим такие же сообщения при перезагрузке страницы.

Перепишем с использованием стрелочных функций:


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           
        }, 2000);
        
    });
    return promise;
};

function bookHotel () {};

function buyTickets () {};

applyForVisa({})
    .then( visa => console.info('Виза получена!'),
          reson => console.error( reson ));



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

На случай, если обещание будет выполнено нам надо отправить туда одну функцию.

Таким способом мы отправим туда функцию бронирования отеля - boorHotel() и вместо того, чтобы на каждом этапе определять функцию на случай ошибки, мы можем определить одну для всей операции в целом.

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

Для этого, после .then(bookHotel мы сделаем еще один метод . Туда отправим buyTickets, а далее - используя метод .catch у обещания, мы выведем ошибку, которая нам будет передана. При этом, обработчик ошибки у первого метода мы можем убрать!

И для проверки в функцию bookHotel добавим вывод сообщения в консоль - Бронируем отель.


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           
        }, 2000);
        
    })
    return promise;
}

function bookHotel () {
    console.log('Бронируем отель.')
};

function buyTickets () {};

applyForVisa({})
    .then( visa => console.info('Виза получена!'))
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ));



В результате в консоли мы увидим, что при одобрении визы у нас появится пункт - Бронируем отель.



А если виза не одобрена, то вывод как и в предыдущих случаях .

Можно добавить Покупаем билеты в buyTickets



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

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

Заметьте метод .then мы используем три раза.



Мы используем одно обещание или несколько?

Как минимум одно обещание в методе applyForVisa мы создаем. Далее, мы его возвращаем.

Используем метод .then выводим сообщение в консоль браузера.

Далее, мы используем .then( bookHotel ).

Это одно и то же обещание или нет?

Обещание выполняется или не выполняется только один раз!

То есть обещание может находиться только в одном из состояний.
  1. Оно может быть Panding - Ожидание.
  2. Оно может быть resolved - Выполнена
  3. или rejected - Отказано или не выполнено.
То есть, если я выполнил обещание используя метод resolved, состояние этого обещания я уже не могу поменять на rejected, используя метод reject. Все последующие попытки изменить состояние обещания будут игнорироваться.

Это легко проверить если после Math.random() обещание ещё раз выполним, а потом отменим а потом выполним.


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           resolve();
           reject();
           resolve();
        }, 2000);
        
    })
    return promise;
}

function bookHotel () {
    console.log('Бронируем отель.');
};

function buyTickets () {
    console.log('Покупаем билеты.');
};

applyForVisa({})
    .then( visa => console.info('Виза получена!'))
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ));
    


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

Заметьте, мы получили не 6 сообщений или 9, все те же 3.

То есть обещание было выполнено один раз.

Тоже самое с ошибкой. Мы получили только одно сообщение об ошибке.

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

Благо, это делает автоматически!

То есть, вот здесь (на фото ниже показано стрелкой), после того как выполняется console.info('Виза получена!')), создается новое обещание, которое передается дальше!



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

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

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

Новое обещание мы можем использовать а можем не использовать.

Более того, после метода .catch();, мы можем использовать еще раз .then().


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           resolve();
           reject();
           resolve();
        }, 2000);
        
    })
    return promise;
}

function bookHotel () {
    console.log('Бронируем отель.');
};

function buyTickets () {
    console.log('Покупаем билеты.');
};

applyForVisa({})
    .then( visa => console.info('Виза получена!'))
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ))
    .then(() => console.log('Я всё еще тут.'));



И это сообщение мы увидим в любом случае!

Для того чтобы забронировать отель, нам необходимо предоставить визу, поэтому функция bookHotel () будет принимать в качестве параметра visa.

и для того чтобы нам не отказывали в визе, можно поставить 0 в условном выражении Math.random() > 0 ? .

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



Давайте разберемся почему?

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

Так вот, обещание в котором мы отправляем функцию bookHotel () ничего про объект visa не знает. Для этого нам необходимо объект visa из первого обещания вернуть.

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



function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           resolve();
           reject();
           resolve();
        }, 2000);
        
    })
    return promise;
}

function bookHotel ( visa ) {
    console.log('Бронируем отель.');
    console.log('visa =' + visa);
};

function buyTickets () {
    console.log('Покупаем билеты.');
};

applyForVisa({})
    .then( visa => {
        console.info('Виза получена!');
        return visa; 
    })
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ));
    



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



И теперь мы видим visa =[object Object]. То есть нам удалось передать visa из одного обещания в другое.

Создадим функцию получения визы и в нее поместим код:


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           resolve();
           reject();
           resolve();
        }, 2000);
        
    })
    return promise;
}
function getVisa ( visa ) {
    console.info('Виза получена!');
    return visa; 
}

function bookHotel ( visa ) {
    console.log('Бронируем отель.');
    console.log('visa =' + visa);
};

function buyTickets () {
    console.log('Покупаем билеты.');
};

applyForVisa({})
    .then( getVisa ) 
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ));
    


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

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

Далее в функции buyTickets мы принимаем параметр booking - так назвали его. И посмотрим на него в консоли.

function buyTickets ( booking ) {
    console.log('Покупаем билеты.');
    console.log('Бронь', booking);
};


И в консоли мы увидим объект Бронь.



Давайте полагаться на JavaScript не будем и с функции getVisa() сами вернём обещание.

В теле просто вызовем resolve( visa ) с параметром - visa.


function getVisa ( visa ) {
    console.info('Виза получена!');
    return new Peomise( function ( resolve, reject ) {
        resolve();
    }); 
}

В консоли мы получим тот же самый результат. Тоже самое сделаем в функции bookHotel Для разнообразия в нее передадим функцию reject('Нет мест!');

function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > .5 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           resolve();
           reject();
           resolve();
        }, 2000);
        
    })
    return promise;
}
function getVisa ( visa ) {
    console.info('Виза получена!');
    return new Promise( function ( resolve, reject ) {
        resolve();
    }); 
}

function bookHotel ( visa ) {
    console.log('Бронируем отель.');
    console.log('visa =' + visa);
    return new Promise ( function (resolve, reject ) {
        reject('Нет мест!');
    });
};

function buyTickets ( booking ) {
    console.log('Покупаем билеты.');
    console.log('Бронь', booking);
};

applyForVisa({})
    .then( getVisa ) 
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ));
    



В консоли увидим.



До функции -купить билеты мы не дошли.

Если поменять reject('Нет мест!') на resolve({}), то мы пройдем через все функции.

То есть и получим визу и отель и билеты...

В функциях getVisa bookHotel мы сразу выполняем обещания. И если это так, то нет необходимости использовать конструктор.

Например так:


function bookHotel ( visa ) {
    console.log('Бронируем отель.');
    console.log('visa =' + visa);
    return Promise.resolve( visa );
};



Если мы хотим сразу отклонить обещание, то можно сделать сразу reject так - return Promise.reject( 'Some Text' );

В данном случае мы моментально отклоняем обещание.

В функции getVisa мы используем конструктор, но в теле функции мы сразу выполняем обещание.

Давайте это исправим и превратим эту функцию в асинхронную.

для этого воспользуемся методом setTimeout и стрелочной функцией, которой вызовем resolve(visa) и подождем 2 сек.

Таким образом все процедура должна занять 4 сек.


function applyForVisa( documents ) {
    console.log('Обработка заявления... ');
    let promise = new Promise( function ( resolve, reject ) {
        setTimeout( function () {
            Math.random() > 0 ? resolve ({}) : reject ('В визе отказано: не хватило документов...')
           resolve();
           reject();
           resolve();
        }, 2000);
        
    })
    return promise;
}
function getVisa ( visa ) {
    console.info('Виза получена!');
    return new Promise( function ( resolve, reject ) {
        setTimeout( () => resolve( visa ), 2000);
    }); 
}

function bookHotel ( visa ) {
    console.log('Бронируем отель.');
    console.log('visa =' + visa);
    return Promise.resolve( visa );
};

function buyTickets ( booking ) {
    console.log('Покупаем билеты.');
    console.log('Бронь', booking);
};

applyForVisa({})
    .then( getVisa ) 
    .then( bookHotel )
    .then( buyTickets )
    .catch( error => console.error( error ));
    


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

Первое - Обработка заявления...
2 сек
Виза получена! и через 2 сек все остальное.

Думаю, что вам стало более понятно, как работают обещания.

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





                                                                                                                                                             

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

ES6: Обещания - Promise (XIV-1).

Обещания - Promise

Сегодня мы рассмотрим обещания в ES6 или как они называются по-английски Promise.



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


ES6 для начинающих (2)

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

Давайте разберемся Зачем вообще нужны эти обещания?

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

Допустим, мы кликаем на кнопку и появляется сообщение.

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



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

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

При этом мы не хотим сидеть и ждать, когда все эти операции закончится.

Приложение должно работать дальше. Если использовать функции обратного вызова для каждой из этих операций то код может превратиться в так называемый callback hell - когда у функции обратного вызова есть своя функция обратного вызова, у которой есть своя функция обратного вызова и вы вbдите как всё это выглядит.



Для маленьких приложений это не такая большая проблема, но для больших всё это может превратится в очень большую головную боль, особенно при отладке.

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



Заметьте насколько он компактен и легко читается.

Давайте наглядно посмотрим как работают обещание.

Допустим вы хотите поехать отдыхать в другую сторону.

Для этого вам необходимо:
  1. - сначала получить визу
  2. - забронировать номер в отеле,
  3. - и в конце купить билет на самолет.


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

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

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

Пока обещание выполняется оно находится в состоянии Pending ( ожидание ). Далее обещание может быть сдержанным, в таком случае она будет в состоянии Resolved, или несдержанно. В таком случае мы получим ошибку Error и обещание будет находиться в состоянии Rejected.



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

Если какой-то из шагов не выполняется, то вы полностью отменяете отпуск.

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

Допустим имеется функция applyForVisa(); - подать документы на визу.

Эта функция асинхронная и поэтому она вернет обратно - обещание.

let promise = applyForVisa();

Это обещание мы сохраним в переменной promise.

У обещания есть метод .then() что по-русски можно перевести как далее.

Данный метод принимает два аргумента.


let promise = applyForVisa();

promise.then( resolve, reject );



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

В зависимости от результата выполнения функции applyForVisa(); сработает либо функция resolve или функция reject.

В нашем случае в качестве функции resolve мы отправляем функцию bookHotel или забронировать отель. В качестве функции reject мы отправляем cancelVacation, то есть отменить отпуск.



Как я уже сказал, результатом applyForVisa(); является обещание, но нам не обязательно хранить его в переменной, поэтому сразу после вызова функции applyForVisa(); мы можем использовать метод .then() и в качестве аргументов отправить соответствующие функции.



В функцию .then() не обязательно отправлять два аргумента. Мы можем отправить только один - bookHotel на тот случай если обещание будет выполнено.

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

Он называется .catch().


applyForVisa()
 .then( bookHotel )
 .catch( cancelVacation );



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

В данном случае cancelVacation.

Метод .catch() имеет смысл использовать если у нас имеется несколько шагов.


applyForVisa()
 .then( bookHotel )
 .then( buyTickeys )
 .catch( cancelVacation );



То есть мы бронируем отель - bookHotel.
Далее Мы покупаем билеты buyTickeys
и если вдруг на каком-то из шагов происходит ошибка, то мы и ловим (.catch) функцией cancelVacation.

Заметьте как просто читается код.
  • Подать заявление на визу.
  • Далее - забронировать отель.
  • Далее - купить билеты.
  • В случае если что-то не получится, то - отменить отпуск.
Асинхронный код выглядит так, как будто он синхронный.

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

Отличается то, как мы пишем код.Его структура.Её гораздо проще понимать.

Теперь, перейдем к созданию собственных обещаний.



                                                                                                                                                             

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

ES6: Деструктивное присваивание объектов (XIII).

Деструктивное присваивание объектов.





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


Лучше всего это рассмотреть на примере.

Представьте. что у нас есть некий объект и нам нужно получить доступ к его свойствам. В ES5 мы бы сделали просто - присвоили свойства объекта переменным вот таким образом:


var person = {
 firstName : 'John',
 lastName : 'Gray'
};

var firstName = person.firstName;
var lastName = person.lastName;

console.log( firstName, lastName );



В ES6 все это можно уместить в одну строку применив деструктивное присваивание объектов.

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

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


let person = {
 firstName : 'John',
 lastName : 'Gray'
};

let { firstName, lastName } = person;

console.log( firstName, lastName );



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


let person = {
 firstName : 'John',
 lastName : 'Gray'
};

let { firstName : first , lastName : last } = person;

console.log( first, last );



Мы просто указали новые названия переменных после двоеточия.

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

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



let { firstName : first , lastName : last } = { firstName : 'John',lastName : 'Gray'};

console.log( first, last );



В данном случае мы также в консоли получим John Gray.



Значения по умолчанию.

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


let { firstName : first , lastName : last, age = 35 } = { firstName : 'John',lastName : 'Gray'};

console.log( first, last, age );





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

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


let { ['first' + 'Name'] : first , lastName : last, age = 35 } = { firstName : 'John',lastName : 'Gray'};

console.log( first, last, age );



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

Извлечение значений свойств вложенных объектов.

Мы можем извлечь свойства объектов, являющихся свойствами других объектов.

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


let user = { 
 firstName : 'John',
 lastName : 'Gray',
 social : {
  facebook: 'GrayJohn-Facebook',
  twitter: 'J-G-Twitter'
 }
}



Таким образом, чтобы получить доступ к свойству фейсбук, нам необходимо -


let user = { 
 firstName : 'John',
 lastName : 'Gray',
 social : {
  facebook: 'GrayJohn-Facebook',
  twitter: 'J-G-Twitter'
 }
}

let { firstName : first , lastName : last, social:{facebook},  age = 35 } = user;

console.log( first, last, facebook, age );





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

Если бы свойство facebook содержало бы в себе еще один объект, то мы сделали бы вот так:


let { firstName : first , lastName : last, social:{facebook : { имя_свойства } },  age = 35 } = user;



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


let { firstName : first , lastName : last, social:{ facebook : fb },  age = 35 } = user;

console.log( first, last, fb, age );



Результат в консоли не изменится.

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

Лучше всего рассмотреть на примере.

Создадим функцию, которая будет принимать два параметра, один из которых - config, будет объектом.

У этого объекта будет два свойства - data и cach.

Если бы мы писали код на ES5, то поступили бы так:


function post ( url, config ) {
 config.data;
 config.cach;
};



В ES6 мы сделаем это иначе.

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

let user = { 
 firstName : 'John',
 lastName : 'Gray',
 social : {
  facebook: 'GrayJohn-Facebook',
  twitter: 'J-G-Twitter'
 }
}

let result = post ( 'api/users', { data : user, cach : false });



И теперь используя деструктивное присваивание, мы можем вместо переменной (параметра функции) - congig, сразу передать свойства data и cach в фигурных скобках:


let user = { 
 firstName : 'John',
 lastName : 'Gray',
 social : {
  facebook: 'GrayJohn-Facebook',
  twitter: 'J-G-Twitter'
 }
}

let { firstName : first , lastName : last, social:{ facebook : fb },  age = 35 } = user;

console.log( first, last, fb, age );



function post ( url, { data, cach } ) {
 console.log( data, cach );
};

let result = post ( 'api/users', { data : user, cach : false });







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


function post ( url, { data : { firstName, lastName }, cach } ) {
 console.log( firstName, lastName, cach );
};

let result = post ( 'api/users', { data : user, cach : false });





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

Если попробовать вывести в консоли свойство data, то получим ошибку - Uncaught ReferenceError: data is not defined.

Деструктивное присваивание для возврата значений.

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


 function getUserInfo () {
  return  {
   firstName : 'John',
         lastName : 'Gray',
         social : {
  facebook: 'GrayJohn-Facebook',
  twitter: 'J-G-Twitter'
  }
 };
};

let { firstName, lastName, social:{ twitter}} = getUserInfo();
console.log(firstName, lastName, twitter);





Здесь все очень похоже на то, что было выше. Ничего сложного, если понимать, то как происходит деструктивное присваивание.


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


Удачного кодирования!                                                                                                                                                              

Телеграм канал - Full Stack JavaScript Developer
Помочь проекту (любая валюта). DONATE

воскресенье, 23 сентября 2018 г.

ES6: Деструктивное присваивание массивов (XII).

Деструктивное присваивание

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

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





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


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

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

npm run watch


И нажать Enter

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

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

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

Деструктивное присваивание массивов

Создадим массив. Для извлечения данных из этого массива в ES5:


var cats = ['cat1', 'cat2', 'cat3','cat4'];

var cat_1 = cats[0];
var cat_2 = cats[1];
var cat_3 = cats[2];
var cat_4 = cats[3];

console.log(cat_1, cat_2, cat_3, cat_4);



В ES6 это можно сделать проще.


let cat_1, cat_2, cat_3, cat_4; //объявили переменные
[ cat_1, cat_2, cat_3, cat_4 ] = cats; //присвоили им значения

console.log(cat_1, cat_2, cat_3, cat_4);



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

Еще немного упростим:


let [ cat_1, cat_2, cat_3, cat_4 ] = cats;

console.log(cat_1, cat_2, cat_3, cat_4);



Массив можно объявить сразу в выражении:


let [ cat_1, cat_2, cat_3, cat_4 ] = ['cat1', 'cat2', 'cat3','cat4'];

console.log(cat_1, cat_2, cat_3, cat_4);



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

Файл dist/destructor.js


'use strict';

// var cat_1 = cats[0];
// var cat_2 = cats[1];
// var cat_3 = cats[2];
// var cat_4 = cats[3];

var cat_1 = 'cat1',
    cat_2 = 'cat2',
    cat_3 = 'cat3',
    cat_4 = 'cat4';


console.log(cat_1, cat_2, cat_3, cat_4);



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

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

Таким образом мы получаем переменные со значениями массива.

Если переменных больше, чем значений массива.

Если переменных больше, чем значений массива, то значение переменной будет не определено -

undefined.


let items = [11, 55, 99];

let [ low, mid, height, best ] = items;

console.log( low, mid, height, best );



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



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

Например так:


let items = [11, 55, 99];

let [ low,, best ] = items;

console.log( low, best );



В консоли получим 11 и 99.

В массиве больше значений, чем переменных.

Для последней переменной можно использовать синтаксис оставшихся параметров - три точки ....

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


let items = [11, 55, 99];

let [ low,...rest] = items;

console.log( rest );



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



Мы можем использовать значение по умолчанию.

В массиве меньше значений, чем переменных.




let items = [11, 55];

let [ low, mid, height = 0 ] = items;

console.log( low, mid, height );



В консоли 11, 55 и 0.

Вложенный массаив.

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


let items = [11, 55, [ 2, 4] ];

let [ low, mid, height ] = items;

console.log( height );



Значением переменной height будет массив:



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


let items = [11, 55, [ 2, 4] ];

let [ low, mid, [ two, four ] ] = items;

console.log( low, mid, two, four );





Как параметр функции.

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

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


function compute ( [ tree, five ] ) {
 console.log("tree = " + tree);
 console.log("five = " + five)
}

compute([3, 5]);



Как и ожидалось, в консоли мы получим :



Возврат значений функцией.

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

Допустим у нас есть функция, которая возвращает массив из трех значений.

Далее объявим переменную в которую пометим вызов этой функции.


function getNumbers () {
 return [ 33, 44, 77];
}

let numbers = getNumbers ();

console.log( numbers );



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



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

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


function getNumbers () {
 return [ 33, 44, 77];
}

let numbers = getNumbers ();

console.log( numbers );

let [t_tree, f_four, s_seven] = getNumbers();

console.log( t_tree, f_four, s_seven );





Быстрый обмен значениям переменных.

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

Проще всего это увидеть на примере:


let yes = 'yes';
let no = 'no';

[ yes, no ] = [ no, yes];

console.log("yes = " + yes);
console.log("no = " + no);



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



Если поcмотреть на то, что сделал Babel dist/destructor.js


'use strict';

var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = 

false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n 

= true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } 

finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } 

return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) 

{ return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable 

instance"); } }; }();

// let items = [11, 55, [ 2, 4] ];

// let [ low, mid, [ two, four ] ] = items;

// console.log( low, mid, two, four );

// function compute ( [ tree, five ]) {
//  console.log("tree = " + tree);
//  console.log("five = " + five)
// }

// compute([3, 5]);

function getNumbers() {
 return [33, 44, 77];
}

var numbers = getNumbers();

console.log(numbers);

var _getNumbers = getNumbers(),
    _getNumbers2 = _slicedToArray(_getNumbers, 3),
    t_tree = _getNumbers2[0],
    f_four = _getNumbers2[1],
    s_seven = _getNumbers2[2];

console.log(t_tree, f_four, s_seven);

var yes = 'yes';
var no = 'no';

var _ref = [no, yes];
yes = _ref[0];
no = _ref[1];


console.log("yes = " + yes);
console.log("no = " + no);



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








                                                                                                                                                             

суббота, 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