Вычисляемое свойство - свойство, которое можно посчитать.
Как мы обращались к свойствам объекта раньше:
const person = {
name: 'Vlad'
};
person.name; // Vlad
Вычисляемое свойство:
const person = {
name: 'Vlad',
};
person['na' + 'me']; // Vlad
Цикл for...in проходит по всем перечисляемым свойствам объекта.
Перечисляемым является свойство, значение поля enumerable дескриптора которого установлено в true.
Синтаксис цикла for...in.
for(let наименование_свойства in объект){
действие_1
...
действие_N
}
Для каждой итерации цикла значением блочной переменной наименование_свойство устанавливается последующее наименование ключа объекта.
const person = {
name: 'Ivan',
lastName: 'Ivanov',
age: 30
};
for (const key in person) {
console.log('key: ', key);
};
// Результат
key: name
key: lastName
key: age
Пример:
let person = {
firstName: 'Jake',
lastName: 'Mitchel',
birthDate: '04.09.2001'
};
for(let property in person)
console.log(`Property: ${property}, value: ${person[property]}`);
/*
Результат в консоли:
Property: firstName, value: Jake
Property: lastName, value: Mitchel
Property: birthDate, value: 04.09.2001
*/
Использование for...in на примере массива
let arr = ['Hello', 'world', '!'];
for(let property in arr)
console.log(property);
// Результат в консоли: 0 1 2
Поскольку ключами в обычном массиве являются индексы, то значением переменной property поочередно будут соответственно 0, 1 и 2.
Для простого прохождения по элементам массива используется цикл for...of.
В процессе выполнения цикла не предоставляется никакой информации об индексе текущего элемента.
Синтаксис цикла for...of:
for(let элемент_массива of массив){
действие_1
...
действие_N
}
Для каждой итерации массива объявляется переменная элемент_массива с блочной областью видимости в пределах этого массива.
let arr = [1, 2, 3];
for(let item of arr)
console.log(item);
// Результат в консоли: 1 2
const arr = [1,2,3,4];
for(let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
В JavaScript при работе с циклами возможно использование двух конструкций: continue и break.
Ключевое слово continue прерывает выполнение текущей итерации цикла и запускает следующую.
Рассмотрим на примере.
let arr = [2, 0, 4, 8];
for(let item of arr){
if(item === 0)
continue;
console.log(item / 2);
}
// Результат в консоли: 1 2 4
alert
Эта функция показывает сообщение и ждёт, пока пользователь нажмёт кнопку «ОК».
Например:
alert("Hello");
Это небольшое окно с сообщением называется модальным окном.
Понятие модальное означает, что пользователь не может взаимодействовать с интерфейсом остальной части страницы, нажимать на другие кнопки и т.д. до тех пор, пока взаимодействует с окном.
В данном случае – пока не будет нажата кнопка «OK».
Функция prompt принимает два аргумента:
const result = prompt(title, [default]);
Этот код отобразит модальное окно с текстом, полем для ввода текста и кнопками OK/Отмена.
- title.
Текст для отображения в окне.
- default
Необязательный второй параметр, который устанавливает начальное значение в поле для текста в окне.
Квадратные скобки вокруг default в описанном выше синтаксисе означают, что параметр факультативный, необязательный.
Вызов prompt возвращает текст, указанный в поле для ввода, или null, если ввод отменён пользователем.
let age = prompt('Сколько тебе лет?', 100);
alert(`Тебе ${age} лет!`); // Тебе 100 лет!
const result = confirm(question);
Функция confirm отображает модальное окно с текстом вопроса question и двумя кнопками: OK и Отмена.
Результат – true, если нажата кнопка OK. В других случаях – false.
let isBoss = confirm("Ты здесь главный?");
alert( isBoss ); // true, если нажата OK
Давайте напишем функцию, которая будет обрабатывать возраст юзера.
Для создания нового объекта Date нужно вызвать конструктор new Date()
Для создания нового объекта Date нужно вызвать конструктор new Date() с одним из следующих аргументов:
- new Date()
Текущая дата и время
let now = new Date();
alert( now ); // показывает текущие дату и время
- new Date(milliseconds)
Создать объект Date с временем, равным количеству миллисекунд (тысячная доля секунды), прошедших с 1 января 1970 года UTC+0.
// 0 соответствует 01.01.1970 UTC+0
let Jan01_1970 = new Date(0);
alert( Jan01_1970 );
// теперь добавим 24 часа и получим 02.01.1970 UTC+0
let Jan02_1970 = new Date(24 * 3600 * 1000);
alert( Jan02_1970 );
- new Date(datestring)
Если аргумент всего один, и это строка, то из неё «прочитывается» дата
let date = new Date("2017-01-26");
alert(date);
// Время не указано, поэтому оно ставится в полночь по Гринвичу и
// меняется в соответствии с часовым поясом места выполнения кода
// Так что в результате можно получить
// Thu Jan 26 2017 11:00:00 GMT+1100 (восточно-австралийское время)
// или
// Wed Jan 25 2017 16:00:00 GMT-0800 (тихоокеанское время)
- new Date(year, month, date, hours, minutes, seconds, ms)
Создать объект Date с заданными компонентами в местном часовом поясе. Обязательны только первые два аргумента.
- year должен состоять из четырёх цифр: значение 2013 корректно, 98 – нет.
- month начинается с 0 (январь) по 11 (декабрь).
- Параметр date здесь представляет собой день месяца. Если параметр не задан, то принимается значение 1.
- Если параметры hours/minutes/seconds/ms отсутствуют, их значением становится 0.
new Date(2011, 0, 1, 0, 0, 0, 0); // // 1 Jan 2011, 00:00:00
new Date(2011, 0, 1); // то же самое, так как часы и проч. равны 0
- date.getDate()
Возвращает день месяца (1-31) указанной даты по местному времени.
- date.getDay()
Возвращает день недели (0-6) указанной даты по местному времени.
- date.getFullYear()
Возвращает год (4 цифры для 4-х значного года) указанной даты по местному времени.
- date.getHours()
Возвращает часы (0-23) указанной даты по местному времени.
- date.getMinutes()
Возвращает минуты (0-59) указанной даты по местному времени.
- date.getMonth()
Возвращает месяц (0-11) указанной даты по местному времени.
- date.getSeconds()
Возвращает секунды (0-59) указанной даты по местному времени.
- date.getTimezoneOffset()
Возвращает смещение часового пояса в минутах для текущей локали.
Функция должна удовлетворять двум условиям, чтобы считаться «чистой»:
— Каждый раз функция возвращает одинаковый результат, когда она вызывается с тем же набором аргументов
— Нет побочных эффектов
Рассмотрим подробнее.
- Одинаковый вход => Одинаковый выход
Сравните это:
const add = (x, y) => x + y;
add(2, 4); // 6
С этим:
let x = 2;
const add = (y) => {
x += y;
};
add(4); // x === 6
В первом случае значение возвращается на основании заданных параметров, независимо от того, где/когда вы его вызываете.
Если вы сложите 2 и 4, всегда получите 6.
Ничего не влияет на результат.
Нечистые функции = непостоянные результаты
Второй пример ничего не возвращает. Он полагается на общее состояние для выполнения своей работы путем увеличения переменной за пределами своей области.
Эта модель кошмар для разработчиков.
Разделяемое состояние вводит зависимость от времени. Вы получаете разные результаты в зависимости от того, когда вы вызвали функцию. В первый раз результат 6, в следующий раз 10 и так далее.
Примеры побочных эффектов:
- Видоизменение входных параметров
- console.log
- HTTP вызовы (AJAX/fetch)
- Изменение в файловой системе
- Запросы DOM
По сути, любая работа, выполняемая функцией, не связана с вычислением конечного результата.
Вот “нечистая” функция с побочным эффектом.
const impureDouble = (x) => {
console.log('doubling', x);
return x * 2;
};
const result = impureDouble(4);
console.log({ result });
console.log здесь это побочный эффект, но он не повредит. Мы все равно получим те же результаты, учитывая те же данные.
"Нечистое" изменение объекта
const objMutation = (key, value, object) => {
object[key] = value;
};
const person = {
name: 'Bobo'
};
const result = objMutation('shoeSize', 400, person);
console.log({
person,
result
});
Переменная person была изменена навсегда, потому что функция была объявлена через оператор присваивания.
Мы можем очистить objMutation, просто вернув новый объект с желаемыми свойствами.
const objMutation = (key, value, object) => ({
...object,
[key]: value
});
const person = {
name: 'Bobo'
};
const result = objMutation('shoeSize', 400, person);
console.log({
person,
result
});
Это функции, которые работают с другими функциями, либо принимая их в виде параметров, либо возвращая их.
Проще говоря, функцией высшего порядка называется такая функция, которая принимает функцию как аргумент или возвращает функцию в виде выходного значения.
Например, встроенные функции JavaScript Array.prototype.map, Array.prototype.filter и Array.prototype.reduce являются функциями высшего порядка.
Пример №1:
Предположим, у нас имеется массив чисел, и мы хотим создать новый массив, который содержит результаты умножения этих чисел на 2.
Рассмотрим способы решения этой задачи с использованием функций высшего порядка и без них.
- Решение задачи без использования функций высшего порядка
const arr1 = [1, 2, 3];
const arr2 = [];
for(let i = 0; i < arr1.length; i++) {
arr2.push(arr1[i] * 2);
}
// выводит [ 2, 4, 6 ]
console.log(arr2);
- Решение задачи с помощью функции высшего порядка map
const arr1 = [1, 2, 3];
const arr2 = arr1.map(function(item) {
return item * 2;
});
console.log(arr2);
Пример №2:
Предположим, у нас имеется массив, содержащий год рождения неких людей, и нам надо создать массив, в который попадёт их возраст в 2018 году.
Рассмотрим, как и прежде, решение этой задачи в двух вариантах.
- Решение задачи без использования функций высшего порядка
const birthYear = [1975, 1997, 2002, 1995, 1985];
const ages = [];
for(let i = 0; i < birthYear.length; i++) {
let age = 2018 - birthYear[i];
ages.push(age);
}
// выводит [ 43, 21, 16, 23, 33 ]
console.log(ages)
- Решение задачи с помощью функции высшего порядка map
const birthYear = [1975, 1997, 2002, 1995, 1985];
const ages = birthYear.map(year => 2018 - year);
// выводит [ 43, 21, 16, 23, 33 ]
console.log(ages);
Продвинутая техника для работы с функциями. Она используется не только в JavaScript, но и в других языках.
Каррирование – это трансформация функций таким образом, чтобы они принимали аргументы не как f(a, b, c), а как f(a)(b)(c)
Каррирование не вызывает функцию. Оно просто трансформирует её.
Наше первое карри:
let greetCurried = function(greeting) {
return function(name) {
console.log(greeting + ", " + name);
};
};
let greetHello = greetCurried("Hello");
greetHello("Heidi"); //"Hello, Heidi"
greetHello("Eddie"); //"Hello, Eddie"
Пример 1:
Без каррирования:
function multiply(a, b, c) {
return a * b * c;
}
multiply(1,2,3); // 6
С каррированием:
function multiply(a) {
return (b) => {
return (c) => {
return a * b * c
}
}
}
log(multiply(1)(2)(3)) // 6
function curry(f) { // curry(f) выполняет каррирование
return function(a) {
return function(b) {
return f(a, b);
};
};
}
// использование
function sum(a, b) {
return a + b;
}
let carriedSum = curry(sum);
alert( carriedSum(1)(2) ); // 3
Как вы видите, реализация довольна проста: это две обёртки
- Результат curry(func) – обёртка function(a).
- Когда она вызывается как sum(1), аргумент сохраняется в лексическом окружении и возвращается новая обёртка function(b).
- Далее уже эта обёртка вызывается с аргументом 2 и передаёт вызов к оригинальной функции sum.
Контекст выполнения функции — это одно из фундаментальных понятий в JavaScript.
Контекстом еще часто называют значение переменной this внутри функции.
- Переменная this:
Значение переменной this чаще всего определяется тем, как вызывается функция.
Когда функция вызывается как метод объекта, переменная this приобретает значение ссылки на объект, который вызывает этот метод:
const user = {
name: 'John Smith',
getName: function() {
console.log(this.name);
}
};
user.getName(); // John Smith
Итак, мы знаем, что this – это текущий объект при вызове «через точку»
В JavaScript ключевое слово «this» ведёт себя иначе, чем в большинстве других языков программирования.
Оно может использоваться в любой функции.
function getName() {
console.log(this.name);
};
const person = {
name: 'Vlad'
};
const animal = {
name: 'Bobik'
};
person.testMethod = getName;
animal.testMethod= getName;
person.testMethod();
animal.testMethod();
Передача метода отдельно от объекта
let user = {
name: "Джон",
hi: function () {
alert(this.name);
}
};
// разделим получение метода объекта и его вызов в разных строках
let hi = user.hi;
hi(); // Ошибка, потому что значением this является undefined
Как это исправить?
Стрелочные функции особенные:
у них нет своего «собственного» this.
Если мы используем this внутри стрелочной функции, то его значение берётся из внешней «нормальной» функции.
Например, здесь arrow() использует значение this из внешнего метода user.sayHi():
let user = {
firstName: "Илья",
sayHi() {
let arrow = () => alert(this.firstName);
arrow();
}
};
user.sayHi(); // Илья
Как этот this переопределить?
- Метод call
Синтаксис метода call:
func.call(context, arg1, arg2, ...)
При этом вызывается функция func, первый аргумент call становится её this, а остальные передаются «как есть».
Вызов func.call(context, a, b...) – то же, что обычный вызов func(a, b...), но с явно указанным this(=context).
const test = {
first: 'a',
getA: function() {
console.log(this.first);
}
};
const test2 = {
first: 'b'
};
test.getA.call(test2);
Так же работает с функциями:
function test() {
console.log(this);
console.log(this.arg);
}
const obj = {
arg: 'argument'
};
test.call(obj);
Если нам неизвестно, с каким количеством аргументов понадобится вызвать функцию, можно использовать более мощный метод: apply.
Вызов функции при помощи func.apply работает аналогично func.call, но принимает массив аргументов вместо списка.
func.call(context, arg1, arg2);
// идентичен вызову
func.apply(context, [arg1, arg2]);
В частности, эти две строчки сработают одинаково:
showFullName.call(user, 'firstName', 'surname');
showFullName.apply(user, ['firstName', 'surname']);
HomeWork:
- Получить от юзера число.
Получить сумму квадаров всех чисел от 1 до числа, которое ввел юзер. Пример:
- Юзер ввел 4
- (1 * 1) + (2 * 2) + (3 * 3) + (4 * 4) = 30
- Вывести в консоль результат
Привести во вторую степерь можно через оператор **. 3 ** 2 = 9
- Есть массив [3, 5, 12, 9, 23, 93, 17]
Отфильтровать его так, чтобы остались только те числа, которые больше 2 и меньше 20. И потом получить их сумму.
-
Дан массив [[1, 6, 3, '6'], [10, 15, 13, '10']]. Найти сумму элементов, которые являются числами и которые кратны двум
-
Написать функцию, которая устанавливает новые свойства в объект.
Функция принимает в себя 3 аргумента - key, value, obj
key - свойство, которое хотим добавить. Принимаем это от юзера.
value - значение свойства. Принимаем это от юзера.
obj - объект, в который хотим добавить новое свойство.
Если юзер ввел ключ, который уже есть в объекте, то выводим сообщение - "Уже есть", если ключа нет, то устанавливаем его в объект.