[jsexpert] Понятный JavaScript (Middle) - Part 1

ФУНКЦИИ И ИХ ВИДЫ

Определение и синтаксис

Функция – конструкция для повторного вызова одной и той же части кода, выполнения одних и тех же действий, в разных частях программы.
Одним из принципов программирования является принцип DRY (don’t repeat yourself) – в программе следует избегать дублирования кода.

function functionName([argument1], [argument2]) {
    statements
    [return]
}

function – ключевое слово для объявления функции;
functionName – имя функции для дальнейшего обращения к ней;
argument – аргументы передаваемые в функцию (необязательно);
statements – код, который является телом функции (код выполняемый после вызова функции);
return – ключевое слово после которого указывается возвращаемое значение (необязательно).

function sayHello(name) {
    let upperCaseName = name.toUpperCase();
    return "Wellcome Mr. " + name;
}

let yourName = "Johnson";
sayHello(yourName); // "Wellcome Mr. JOHNSON"
// some code
sayHello(yourName); // "Wellcome Mr. JOHNSON"
// some code
sayHello("Smith"); // "Wellcome Mr. SMITH"

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

getElementById();
loadData();
bindEvents();
generateUID();

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

Функция без аргументов

function getCurrentYear() {
    let date = new Date();
    return date.getFullYear();
}
const currentYear = getCurrentYear();
console.log(currentYear); // текущий год

Функция без return

function noReturn() {
    var sum = 0;
    for(var i = 0; i < 10; i++) {
        sum += i;
    }
    console.log(sum);
}
noReturn(); // undefined

Если в функции отсутствует ключевое слово return, то она вернет специальное значение undefined.
Код, после возвращения значения из функции, то есть после return, не выполняется.

function checkReturn() {
    console.log("Before return");
    return true;
    console.log("After return");
}
checkReturn(); // "Before return"

Function declaration и expression

Существует несколько способов объявления функции, это Function Declaration и Function Expression
Синтаксис function declaration

function funcDeclaration() {
    console.log("Function declaration");
}

Синтаксис function expression

var funcExpression = function () {
    console.log("Function expression");
}

Отличия function declaration и function expression:

  • • функциональные выражения (function expression) используются когда тело функции надо присвоить в переменную, например, для передачи функции как параметра в другую функцию;
var firstFunction = function () {
    console.log("Run firstFunction.");
};
function runFunction(param) {
    console.log("Run runFunction");
    param();
};

runFunction(firstFunction);
  • • или сделать ее свойством объекта;
var showFullName = function (firstName, secondName) {
    return firstName + " " + secondName;
}
var obj = {
    fullName: showFullName
}
obj.fullName("Keanu", "Reeves");
  • • function declaration можно вызывать как до, так и после ее объявления;
showFullName ("John", "Karter"); // "John Karter"
function showFullName(firstName, secondName){
    console.log(firstName + " " + secondName);
}
showFullName ("Garry", "Miller"); // "Garry Miller"
  • • function expression доступна для вызова после того как объявлена;
showFullName ("John", "Karter"); // ошибка!
var showFullName = function(firstName, secondName){
    console.log(firstName + " " + secondName);
}
showFullName ("Garry", "Miller"); // "Garry Miller"

Анонимная функция

Анонимная функция – функциональное выражение, которое не записывается в переменную, у которого отсутствует имя.

setTimeout(function(){
    console.log("Anonymous function.");
}, 2000);

Анонимная функция используется когда отсутствует необходимость вызвать функцию по имени где-то в коде, то есть она объявляется в месте ее использования.

Самовызывающаяся функция (IIFE)

Самовызывающаяся функция (Immediately Invoked Function Expression(IIFE)) – синтаксическая конструкция с помощью которой можно вызвать функцию в месте ее определения.

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

В такую функцию можно так же передавать параметры.

(function sum(a, b) {
    console.log(a + b); // 5
})(2, 3);

Стрелочная функция (arrow function)

Стрелочные функции (arrow function) – это более короткая синтаксическая запись обычных функций. Базовый синтаксис:

var arrowFunction = (argument) => statements;

arrowFunction – имя функции;
argument – аргументы передаваемые в функцию (необязательно);
statements – выполняемый код или возвращаемое значение.
Если функция короткая, ее можно записать в одну строку, без return и фигурных скобок:

let getSum = (a, b) => a+b;

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

let getSum = a => a + 2;

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

let getSum = (a, b) => { return a+b; }

Эта же функция записанная как function expression:

let getSum = function(a, b) { return a+b; }

Функцию без параметров записывают с пустыми фигурными скобками.

let getYear = () => {
    return new Date().getFullYear();
}

Стрелочная функция как анонимная функция.
Самый распространненый вариант использование анонимных стрелочных функций, это использование в функциях (методах) итераторах:

[1,2,3,4].map( item => item * 2 ); // [2, 4, 6, 8]

item => item * 2 – анонимная стрелочная функция переданная методу map.
Дательнее некоторые из них будут рассмотрены далее в курсе.

ОБЛАСТЬ ВИДИМОСТИ И ЗАМЫКАНИЕ

Область видимости (Scope)

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

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

Глобальная область видимости (Global Scope)

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

var language = "JavaScript"; // по умолчанию, область видимости - глобальная

Переменные внутри глобальной области видимости доступны в любой части кода и другой области видимости.
Глобальная область видимости «живет» столько, сколько «живет» приложение.

var language = "JavaScript";
console.log(language); // "JavaScript"
function show() {
    console.log(language); // здесь есть доступ к переменной "language"
}
show(); // "JavaScript"

Локальная область видимости (Local Scope)

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

// Глобальная область видимости
function someFunction() {
    // Локальная область видимости №1
    function someOtherFunction() {
        // Локальная область видимости №2
    }
}

Локальная область видимости «живет» только с момента вызова функции и до конца ее выполнения.

Контекст (Context)

Контекст используется для ссылки на значение this в конкретной части кода.
Область видимости ссылается на видимость переменных, а контекст на значение this в этой же самой области видимости. То есть this – это в контексте чего вызывается функция.
В глобальной области видимости контекстом всегда является объект Window .

console.log(this); // Window { ... }

function functionThis() {
    console.log(this);
}

functionThis(); // так же Window { ... }, поскольку функция не является свойством объекта (object)

Если вызывать функцию в строгом режиме, то контекстом будет undefined .

'use strict'
function strictThis() {
    console.log(this);
}

strictThis(); // undefined

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

var object = {
    language: "JavaScript",
    returnLanguage: function() {
        console.log(this.language);
    }
};
object.returnLanguage(); // в this подставляется object, по этому - "JavaScript"

То есть в this подставляется объект в контексте которого выполняется метод.

Контекст исполнения (Execution Context)

Контекст исполнения – ссылается на область видимости, а не на контекст.
Контекст исполнения – понятие, которое связывает воедино все рассмотренные нами прежде понятия. Это некая сущность, которая содержит все необходимое для запуска каждой отдельно взятой функции (переменные, аргументы функции, области видимости, this).
Когда интерпретатор JavaScript начинает выполнять код, контекст (область видимости) по умолчанию является глобальным. То есть контекст this – Window (в use strict – undefined), область видимости – глобальная. Изначально контекст исполнения для всего кода – глобальный контекст, он автоматически «прикрепляется» к контексту исполнения при начале выполнения кода.
После этого, когда функция вызывается и исполняется, она может «прикрепить» собственный контекст к контексту исполнения. То же самое происходит, когда одна функция вызывается внутри другой функции или где-либо еще.
Каждая функция создает свой собственный контекст исполнения. Глобальный контекст может быть только один, но контекстов функции может быть сколько угодно. Во время создания функциями своих контекстов, все они попадают в стек исполнения () по правилу последний созданный помещается наверх и первым же выполняется. После выполнения он убирается из стека и выполняется следующий.

Контекст исполнения имеет две фазы: создания и выполнение кода .
Фаза создания это первая фаза, которая начинается во время вызова функции, но ее код еще не выполняется.
На этом этапе происходят три ключевые вещи:

  • • создание объекта переменных (объект активации) (Variable (Activation) Object);
  • • создание цепочки областей видимости (Scope Chain);
  • • присваивание значение контексту (value of the context).

Объект переменных (Variable Object) содержит все переменные, функции и другие декларации (объявленные конструкции), определенные в конкретном контексте исполнения. Когда функция вызывается, интерпретатор сканирует код на декларации, принадлежащие этой функции. Все это помещается в один объект, который и называется объектом переменных.

"variableObject": {
    // содержит аргументы функции, внутренние переменные и function declarations
}

Цепочка областей видимости (Scope Chain) создается после создания объекта переменных и содержит его в себе. Другими словами, содержит в себе свой объект переменных и объекты родительских функций.

"scopeChain": {
    // содержит собственный объект переменных и другие объекты переменных из родительских контекстов исполнения
}

Цепочка областей видимости, грубо говоря, используется для поиска переменных. Помните, что переменные определенные внутри функции, снаружи не видны, мы не можем получить их значение, но получить доступ к переменным, которые объявлены вне функции, уровнем выше, мы можем. Все это благодаря цепочке областей видимости. Например, есть две объявленные функции, одна вложена в другую. В родительской функции есть переменная «а», но обращаемся мы к этой переменной во вложенной функции.

// Глобальная область видимости
console.log(a); // Uncaught ReferenceError: a is not defined
function outer(param) {
    // область видимости функции outer
    var a = param;
    function nested() {
        // область видимости функции nested
        console.log(a);
    }
    nested();
}
outer(5); // 5

Здесь происходит следующее, мы обращаемся к переменной «а» во вложенной функции «nested», интерпретатор не находит ее в локальной области видимости этой функции и благодаря цепочки областей видимости, начинает подниматься вверх, в родительские области видимости, и ищет необходимую переменную. Как только переменная будет найдена, поиск прекращается, и мы получаем значение этой переменной. Если же переменная не найдена, то вернется значение undefined. Поиск по цепочке областей видимости может продолжаться до глобальной области видимости, это конечная точка поиска. В случае, если переменная находится в этой же функции, то поиск по цепочке областей видимости производится не будет. Это позволяет определять переменные с одинаковыми именами, не боясь переопределить такую же переменную в глобальной области видимости.
Поиск по цепочке областей видимости возможен благодаря тому, что Scope Chain содержит в себе не только собственный объект переменных, но и родительские объекты переменных.
Присваивание значение контексту означает, что в this подставляется нужный контекст.
Как результат первой фазы, создания контекста исполнения, создается объект контекста исполнения (Execution Context Object) , который можно представить, как абстрактный объект:

executionContextObject = {
    "scopeChain": {},
    "variableObject": {},
    "this": valueOfThis
}

Фаза выполнения кода означает выполнение JavaScript кода.

Лексическая область видимости

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

function grandfather() {
    var name = "John";
    // likes не доступно здесь
    function parent() {
        // name доступно здесь
        // likes не доступно здесь
        function child() {
            // name доступно здесь
            var likes = "JavaScript";
        }
    }
}

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

Замыкание (Closure)

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

function greet() {
    var name = "John";
    return function () {
        console.log("Hi " + name + "!");
    }
}

greet(); // ничего не произойдет
// возвращенная функция из greet() сохранена в greetLetter
var greetLetter = greet();
// вызов greetLetter вызовет возвращенную функцию из функции greet()
greetLetter(); // "Hi John!"

Следует запомнить, что функция «greetLetter» может получить доступ к переменной «name», которая принадлежит функции «great», даже функция «great» была выполнена. Можно сказать, что переменная «name» осталась в замыкании.
Возвращаемую функцию можно вызвать и без присвоения в переменную используя конструкцию из двухразового указания пары круглых скобок «()()».

greet()(); // "Hi John!"

Блочные конструкции (Block Statements)

Блочные конструкции, такие как «if», «for» и т.п. (кроме функций), не создают новую область видимости. Переменные, объявленные внутри блочной конструкции при помощи ключевого слова «var», видны везде.
Но, переменные объявленные при помощи ключевого слова «let» или «const», внутри таких блочных конструкций, не будут видны вне них. Такие переменные «создают» свою блочную область видимости.

if (true) {
    var name = "John";
    let language = "JavaScript";
    const framework = "React";
}

console.log(name); // "John"
console.log(likes); // Uncaught ReferenceError: language is not defined
console.log(skills); // Uncaught ReferenceError: framework is not defined

Операторы «let» и «const» позволяет объявить локальную переменную с областью видимости, ограниченной текущим блоком кода.

РАСШИРЕННЫЕ ВОЗМОЖНОСТИ ФУНКЦИИ

Аргументы

Параметры функции играют роль локальных переменных в теле функции. При указании параметров функции ключевое слово var или let использовать не нужно, JavaScript объявляет их в качестве локальных переменные автоматически.

function func (a, b, c) { ... }   // a, b, c – параметры функции

Значения, которые передаются при вызове функции называются аргументами .
При вызове функции ей передаётся список аргументов, эти аргументы присваиваются параметрам функции в том порядке, в каком они указаны: первый аргумент присваивается первому параметру, второй аргумент – второму параметру и т. д.
Аргументы, измененные внутри функции, снаружи остаются прежними (если аргументы примитивы):

function sayHello(name) {
    var prefix = "Hi Mr. ";
    name = "Bart";
    return prefix + name;
}

var name = "Smith";
console.log(name); // "Smith"
console.log(sayHello(name)); //"Hi Mr. Bart"
console.log(name); // "Smith"

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

function sayHello(param) {
    var prefix = "Hi Mr. ";
    param.name = "Bart";
    return prefix + param.name;
}

var obj = { name: "Smith" };
console.log(obj.name); //"Smith"
console.log(sayHello(obj)); //"Hi Mr. Bart"
console.log(obj.name); //"Bart"

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

Детальнее об этом рассказано в теме про объекты.

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

function sayHello (firstName, secondName) {
    let prefix = "Hi Mr. ";
    return prefix + firstName + " " + secondName;
}

sayHello("John", "Connor"); // "Hi Mr. John Connor"
sayHello("John"); // "Hi Mr. John undefined"

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

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

function sayHello (firstName, secondName) {
    let prefix = "Hi Mr. ";
    return prefix + firstName + (secondName || "Nobody");
}

sayHello("John"); // "Hi Mr. John Nobody"

Новый способ (ES6)
Параметр по умолчанию:

let sayHello = function(firstName = "Anakin", secondName = "Skywalker") {
    const prefix = "Hi Mr. ";
    return prefix + firstName + " " + secondName ;
}
sayHello(); // "Hi Mr. Anakin Skywalker"
sayHello("Lyke"); // "Hi Mr. Lyke Skywalker"
sayHello(null, "John"); // "Hi Mr. null John"
sayHello(undefined, undefined); // "Hi Mr. Anakin Skywalker"

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

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

function testArguments(firstName, secondName = "Skywalker") {
    console.log(firstName === arguments[0]);
    console.log(secondName === arguments[1]);
}
testArguments("Anakin");   
// true
// false

Так как в функцию передаётся только один аргумент, arguments[1] имеет значение undefined, поэтому сравнение console.log(secondName === arguments[1]); в результате даёт false.

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

function testArguments(arg1 = 1) {
    "use strict";   // Ошибка
}

Значением по умолчанию может быть как простое, так и сложное выражение:

function testArguments(arg1 = 1, arg2 = 2 + 2) {
    console.log(arg1, arg2);
}
 
testArguments(1);   // 1 4

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

function testArguments(arg1, arg2 = arg1) {
    console.log(arg1, arg2);
}
 
testArguments("John");   // John John

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

function testArguments(arg1 = arg2, arg2) {
    console.log(arg1, arg2);
}
 
testArguments(undefined, "John");   // Ошибка

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

Псевдо-массив arguments

Arguments – локальная переменная, которая доступна внутри каждой функции.
Она содержит информацию про количество и значение всех параметров, переданных в функцию, во время ее вызова.

function sayHello() {
    console.log(arguments);
}
sayHello("John", "Connor"); // (2) ["John", "Connor"]

В функции, не принимающей аргументы на вход, все равно можно получить переданные в нее аргументы.
Arguments отсутствует в arrow function.
При помощи arguments можно обратиться к каждому аргументу отдельно.
Нумерация начинается с нуля.

function sayHello(greating, name) {
    console.log(arguments[0]);
    console.log(arguments[1]);
    console.log(arguments[2]);
    console.log(greating + name + arguments[2] + arguments[3]);
}
sayHello("Hi, ", "John", " Connor", "!"); // "Hi, John Connor!"

У arguments есть единственное свойство length. С его помощю, например, можно вывести все переданные параметры при
помощи обычного цикла for.

function sayHello(greatings, name) {
    for(var i = 0; arguments.length > i; i++) {
        console.log(arguments[i]);
    }
}
sayHello("Hi, ", "John", " Connor", "!");

В соответствии с typeof arguments является объектом. Но его можно превратить в настоящий массив.

function checkType() {
    console.log(typeof arguments);
    console.log(Array.isArray(arguments));
    let arrayArguments = Array.prototype.slice.call(arguments);
    console.log(Array.isArray(arrayArguments));
}
checkType();
// object
// false
// true

Детальнее о массивах мы поговорим далее в нашем курсе. В соответствующей теме.

Rest оператор

Оператор … (rest) используется вместо arguments.
Аргументы полученные с помощью оператора ,являются настоящим массивом, со всеми его методами и свойствами.

function rest(...args) {
    console.log(args);
    console.log(Array.isArray(args));
}
rest(1, 2, 3);
// (3) [1, 2, 3]
// true

Если в функцию передали больше параметров, чем она ожидает, оператор … (rest) «аккумулирует» в себе все «лишние» аргументы.

function rest(person, ...args) {
    console.log(person);
    console.log(...args);
}
rest("John", "Bob", "Carl");
// "John"
// "Bob Carl"

ГЛОБАЛЬНЫЕ ФУНКЦИИ

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

  • • isNaN
  • • isFinite
  • • parseInt
  • • parseFloat
  • • encodeURI
  • • encodeURIComponent
  • • decodeURI
  • • decodeURIComponent

isNaN и isFinite

Функция isNaN() проверяет, является ли переданный параметр числом или нет. Функция вернет true, если параметр не является числом и false, если является.
Как она работает: переданный параметр преобразуется к числу. Если это не число (строка, массив и т.п.), то он преобразуется в NaN и уже результат этого преобразования будет проверятся на равенство с NaN.
Стоит учесть, что, к примеру true преобразуется не к NaN, а к числу 1. Подробнее о преобразовании типов данных в соответствующей теме курса.

isNaN('NaN'); //true
isNaN(NaN); //true
isNaN(0 / 0); //true
isNaN(true); // false
isNaN(null); // false
isNaN(10); // false

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

isNaN('NaN'); // true
Number.isNaN('NaN'); // false
isNaN(undefined); // true
Number.isNaN(undefined); // false
Number('NaN'); // NaN
Number(undefined); // NaN

Поскольку isNaN() преобразовывает значение сначала в число, то строка «NaN» и undefined преобразуются в значение NaN.
В Number.isNaN() преобразование отсутствует, по этому результат более корректный.

isFinite() – определяет, является ли переданное в функцию значение конечным числом (то есть не строкой, массивом и т.п. и не плюс или минус бесконечностью).
Возвращает false если значение равно NaN или Infinity, в других случаях true.

Number.isFinite(Infinity); // false
Number.isFinite(NaN); // false
Number.isFinite(0); // true
Number.isFinite(444); // true

Как и в случае с isNaN() существует более точная функция Number.isFinite() .
При передаче значения в isFinite(), оно также принудительно будет преобразовано в число и уже после этого проводится сравнение.

Number.isFinite() не преобразовывает значение в число, по этому результат более корректный.

isFinite('0'); // true
Number.isFinite('0'); // false

В случае с isFinite() строковое значение «0» преобразовывается в число, по этой причине мы получаем true.

Number.isFinite() не преобразовывает строковое значение «0» в число, по этому результат будет false, так как строка не может быть конечным числом.

parseInt и parseFloat

Функция parseInt() – выполняет преобразование строки в число посимвольно.
Эта функция часто используется для получения значений типа ’10px’ — когда вначале стоит число, а потом единицы измерения. Если применить функцию parseInt к ’10px’, то результатом получится число 10 (и это будет действительно число, а не строка).

parseInt('10px', 10); // 10

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

parseInt("15*3", 10); // 15
parseInt("1.3", 10); // 1

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

parseInt('my17px'); // NaN

Преобразование произойдет, если только целое число стоит в начале строки, иначе будет выведено NaN.

Кроме глобально функции parseInt() есть функция Number.parseInt() .
Они абсолютно идентичны.

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

parseInt('3.14'); // 3
parseFloat('3.14'); // 3.14

encodeURI и encodeURIComponent

URI (Uniform Resource Identifier) — унифицированный (единообразный) идентификатор ресурса.
Это символьная строка, позволяющая идентифицировать какой-либо ресурс: документ, изображение, файл, службу, ящик электронной почты и т. д.
URL (Uniform Resource Locator) — единообразный локатор ресурса.
URL — это URI, который, помимо идентификации ресурса, предоставляет ещё и информацию о местонахождении этого ресурса.

encodeURI() – кодирует универсальный идентификатор ресурса (URI).

Будет закодирована URL строка но не параметры или хеш.

Не кодируются символы:
; , / ? : @ & = + $ #

Есть символы, которые никогда не кодируются (так называемые не экранируемые символы):
латинские буквы, десятичные цифры, — _ . ! ~ * ‘ ( )

encodeURI("http://www.google.com/results with spaces#some-anchor");
// http://www.google.com/results%20with%20spaces#some-anchor

encodeURIComponent() – кодирует универсальный идентификатор ресурса.
Будет закодирована URL строка включая специальные символы, которые пропускаются при encodeURI() (кроме не экранированных).

encodeURIComponent("http://www.google.com/results with spaces#some-anchor");
// http%3A%2F%2Fwww.google.com%2Fresults%20with%20spaces%23some-anchor

Таким образом encodeURIComponent() лучше использовать для кодирования параметров URL, а не всего URL полностью.

const param = encodeURIComponent("#some-anchor");
const url = 'http://www.google.com/results/' + param;
console.log(url); // http://www.google.com/results/%23some-anchor

encodeURI() и encodeURIComponent() кодируют специальные символы в URL адресе для их безопасного использования в HTTP запросах.

decodeURI и decodeURIComponent

decodeURI() и decodeURIComponent() используются для соответственного декодирования URL.

decodeURI('http://site.com/%D0%BF%D0%BE%D0%BD%D1%8F%D1%82%D0%BD%D1%8B%D0%B9_javascript');
// http://site.com/понятный_javascript

decodeURIComponent('https%3A%2F%2Fdrive.google.com%2Fdrive%2Fmy-drive');
// https://drive.google.com/drive/my-drive

ИНСТРУКЦИЯ ПО ВЫПОЛНЕНИЮ ДОМАШНЕГО ЗАДАНИЯ

Основное домашнее задание №1:

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

  1. Создать главную самозапускающуюся функцию run() в которой будет выполняться основной код (цикл)
    Также эта функция должна содержать в себе вызовы всех остальных функций.

  2. Сделать функцию для получения случайных чисел (getRndNumber).
    Значение каждой переменной, в которую мы записываем, какая выпала кость получать с помощью вызова этой функции

  3. Сделать одну функцию которая будет склеивать все строки в одну (setResult). Она должна принимать только один аргумент. Строку текста, которую надо склеить.
    (если вы выводите данные не только в div с id result, а возможно еще в какой то другой div, тогда функция должна принимать 2 аргумента: id и Строку)

  4. Сделать функцию для определения совпадений. (isNumbersEqual). Она должна содержать в себе проверку на совпадение и внутри себя вызывать функцию для сохранения данных в общую строку (setResult).

  5. Сделать функцию для определения разницы. (isBigDifference). Она должна содержать в себе соответствующую проверку и внутри себя вызывать функцию для сохранения данных в общую строку (setResult).

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

  7. Сделать функцию, которая напечатает полученные с помощью функции setResult данные в HTML (printResult).

Основное домашнее задание №2: «Камень ножницы бумага»

Цель задания: научиться использовать функции, реализовывать алгоритмы похожие на реальные задачи.
Названия всех функций даны для справки. Вы можете придумать свои.

Заготовка для ДЗ:
Архив с заготовкой

Условие.
У вас есть 2 игрока которые играют в игру. У каждого может выпасть камень, ножницы или бумага.
На самом деле у вас есть функция (getPlayerResult) которая возвращает случайные числа от 1 до 3
1 — камень
2 — ножницы
3 — бумага
В заготовке реализован следующий функционал.
По нажатии на кнопку получаем случайное число и выводим его в соответствующий div элемент.

  1. Вместо того чтоб выводить на экран случайное число как в примере вам необходимо
    добавить функцию (getNameById) которая будет принимать это число и возвращать слово «камень», «ножницы», или «бумага», согласно словарю указанному выше.

  2. На экран вывести полученную текстовую строку для каждого из игроков.

  3. Написать функцию (determineWinner), которая будет принимать два числа, предварительно полученные в функции getPlayerResult и принимать решение, кто из игроков выиграл.

  4. Результатом выполнения функции determineWinner должно быть число, номер игрока, который выиграл.
    То есть эта функция должна возвращать номер игрока который выиграл

  5. Функция printResult должна принять номер игрока, который выиграл и напечатать в div Id result текстовое сообщение типа: «выиграл первый игрок» номер игрока надо вывести словами.

ЗАДАЧИ

1. Создайте функцию, возвращающую слово «ворона» в правильной форме в зависимости от переданого числа n. Например: На ветке сидит 1 ворона; На ветке сидит 4 вороны; На ветке сидит 26 ворон.

2. Напишите функцию, которая принимает строку и выполняет следующее преобразование:
если принимаемая строка больше или равна 15 символов, то остаток строки обрезается и вставляется символ … (троеточие). Для решения этой задачи используйте строковый метод String.substring().

3. Напишите функцию, определяющую, является ли данный год високосным по григорианскому календарю. Функция должна принимать число и выводить true если год является високосным и false если не високосным. Функция должна содержать проверку полученного аргумента на число, если полученный аргумент не число, выполнять преобразование к числу, иначе выводить сообщение об ошибке.
Математическую формулу можно вывести прочитав статью на википедии
Можете воспользоваться заготовкой

ОБЪЕКТЫ

Определение и использование

Объекты – это составной тип данных в JavaScript, который содержит коллекцию свойств, каждое из которых имеет пару ключ-значение.
Каждое свойство состоит из пары ключ-значение. Ключ – это имя свойства, по которому можно получить его значение (каждое свойство должно иметь уникальное имя, которое может быть строкой или числом).
Значениями могут быть числа, строки, булевые значения, функции или же другие объекты.
Свойства, у которых в значениях указаны функции называются методами.

Создание объектов

Объекты можно создавать с помощью литералов объектов, ключевого слова new или функции Object.create().

let object = {};
let newObject = new Object();
let createObject = Object.create();

Создание объекта с помощью литерала (конструкция {}) или с помощью ключевого слова new создаст одинаковые пустые объекты. Создание с помощью Object.create() имеет свои особенности и как правило не используется для создания простых, пустых объектов, этот способ создания будет рассмотрен в последующих темах. Таким образом, чаще всего, для создания объектов используется литеральная форма.

Определение свойств объекта

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

let user = {
    firstName: 'John',
    secondName: 'Connor'
};

Так же можно добавить свойство в уже объявленный объект при помощи точки (.) или квадратных скобок ([]). При присвоении значения через квадратные скобки, ключ необходимо указывать как строку.

let user = {};

user.firstName = 'John';
user['secondName'] = 'Connor';

Получение и изменение свойств

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

let user = {
    firstName: 'John',
    secondName: 'Connor',
    login: 'T2',
    password: 'doomsday',
    age: 25
};

// Получение значения
let name = user.firstName; // 'John'
let surname = user['secondName']; // 'Connor'

// Изменение значения
user.password; // 'doomsday'
user.password = 'judgement_day';
user.password; // 'judgement_day'

При попытке обратиться к значению, которое отсутствует, получим undefined.

let user = {
    firstName: 'John',
    secondName: 'Connor'
};

user.age; // undefined

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

с помощью оператора delete можно удалить свойство из объекта.

let user = {
    firstName: 'John',
    secondName: 'Connor',
    login: 'T2',
    password: 'doomsday',
    age: 25
};

user.age; // 25
delete user.age;
user.age; // undefined

user['secondName']; // 'Connor'
delete user['secondName'];
user['secondName']; // undefined 

Как видно, оба синтаксиса обращения к свойству объекта (точка (.) или квадратные скобки ([])) можно использовать во всех случаях. На практике же чаще используется синтаксис с точкой.
Синтаксис с квадратными скобками используется в тех случаях, когда заранее не известно ключ свойства или происходит динамическое вычисление имени ключа. Например, при переборе объекта в цикле.

let user = {
    firstName: 'John',
    secondName: 'Connor',
    login: 'T2',
    password: 'doomsday',
    age: 25
};

let key;

for (key in user) {
    console.log(user[key]);
}

// John
// Connor
// T2
// doomsday
// 25

В примере выше, в цикле, мы указываем, что «искать» ключ (key) в объекте (здесь это объект user). Перебирая все свойства объекта, при каждой новой итерации, в переменную key по очереди подставляется каждый ключ. То есть, при каждой новой итерации, динамически вычисляется значение ключа в объекте. Интерпретатор наперед не знает какие ключи содержит в себе объект.
Таким образом, выводя в консоль, в примере выше, user[key], мы буквально пишем user[‘firstName’], user[‘secondName’] и т.д. На каждой итерации, по очереди, подставляются все ключи.
Если же указать в цикле user.key, то интерпретатор поймет это буквально как обращение к свойству с ключом key, а так как свойства с таким ключом несуществует, то мы получим undefined. При чем такой результат будет выведен столько раз, сколько свойств содержит объект, ведь при каждой итерации будет происходить попытка обратиться к свойству с ключом key.

let user = {
    firstName: 'John',
    secondName: 'Connor',
    login: 'T2',
    password: 'doomsday',
    age: 25
};

let key;

for (key in user) {
    console.log(user.key);
}

// undefined
// undefined
// undefined
// undefined
// undefined

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

let user = {
    firstName: 'John',
    secondName: 'Connor',
    login: 'T2',
    password: 'doomsday',
    age: 25
};

let key;

for (key in user) {
    console.log(key);
}

// firstName
// secondName
// login
// password
// age

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

Если значение свойства объекта является функцией, то такое свойство носит название «метод», во всех остальных случаях, оно так и называется «свойство». Обратиться к методу можно с помощью того же синтаксиса что и к свойству.

let user = {
    age: 25,
    login: 'T2',
    authenticated: true,
    greating: function(name) {
        return console.log('Hello ' + name);
    }
}

user.greating('John'); // 'Hello John'

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

user.age; // свойство
user.login; // свойство
user.authenticated; // свойство
user.greating(); // метод

Объект может иметь более сложную структуру, содержать в себе еще один объект как значение свойства.

let user = {
    firstName: 'John',
    age: 25,
    info: {
        birthDay: '12/12/1985',
        language: 'English'
    },
    showMessage: function (msg) {
        console.log(msg);
    }
}

user.info.language; // 'English'

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

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

let age = {
    18: 'Teenager'
}

age.18; // Uncaught SyntaxError: Unexpected number
age['18']; // 'Teenager'

Концепция передачи по ссылке и по значению

Если вы хотите скопировать одну переменную в другую (если значение переменной примитив (число, строка, булевое значение, null или undefined)), то при копировании создастся новая копия переменной. При этом, меняя значение одной переменной, значение второй останется неизменным.

let jackPot = 777;
let bingo = jackPot;

jackPot; // 777
bingo; // 777

jackPot = null;

jackPot; // null
bingo; // 777

Создав переменную с присвоенным ей значением (jackPot) и передав ее как значение в другую переменную (bingo), во вторую переменную (bingo) попадает значение из первой (то есть 777). Значение из одной переменной (777 из jackPot) скопировалось в другую (bingo). При таком копировании создаются независимые переменные. По этой причине, дальнейшие изменения в какой-либо из переменных никак не влияют на другую.
Такой механизм называется передача по значению .
Если же в переменной хранится объект, то в ней хранится не сам объект (как в случае с примитивными значениями), а всего лишь ссылка на него, другими словами адрес на место в памяти где он хранится. Другими словами, объект содержится где-то в памяти программы, а переменная содержит всего лишь ссылку на его размещение, чтобы иметь возможность получать значения свойств этого объекта.
Таким образом, если мы захотим скопировать переменную с объектом в другую переменную, то во второй переменной мы получим ту же ссылку на объект, что была в первой переменной.

var original = {
    number: '777'
};

var linked = original;

original.number = '888';
console.log(linked.number); // '888'

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

Копирование объектов

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

let userA = {
    age: 25,
    sex: 'male'
}

let userB = {
    age: 25,
    sex: 'male'
}

console.log(userA == userB); // false
console.log(userA === userB); // false

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

let userA = {
    age: 25,
    sex: 'male'
}

let userB = {
    age: 25,
    sex: 'male'
}

let userC = userA;

console.log(userA === userB); // false
console.log(userA === userC); // true

Хотя по своему наполнению объекты userA и userB одинаковы, они не равны друг другу, но объекты userA и userC при сравнении будут равны, так как имеют одинаковую ссылку на объект.
Таким образом, невозможно скопировать один объект в другой с помощью присваивания его в другую переменную, так как в результате получим лишь копию ссылки на объект и любые изменения в одном из объектов повлекут изменения в другом. В ряде случаев возникает необходимость в создании независимой копии, клона объекта.
Одним из способов является копирование свойств объекта с помощью цикла.

let products = {
    fruit: 'banana',
    vegetable: 'carrot',
    juice: 'orange'
};

let key;
let newProducts = {}

for (key in products) {
    newProducts[key] = products[key];
}

newProducts.fruit = 'apple';

products.fruit; // banana
newProducts.fruit; // 'apple'
products.fruit; // banana

После такого копирования объект newProducts является независимой копией объекта products и изменения в одном из них никак не отобразятся в другом.
Стандарт ECMAScript 2015 ввел в спецификацию новый способ копирования, метод Object.assign().
Object.assign() – копирует переданные объекты в указанный целевой объект и возвращает новую копию объекта со всеми свойствами. Первым параметром в метод передается целевой объект, объект в который будут скопированы все значения. Второй и последующие параметры – это объекты, которые необходимо скопировать.

let products = {
    fruit: 'banana',
    vegetable: 'carrot'
};

let newProduct = {
    juice: 'orange'
}

let allProducts = Object.assign({}, products, newProduct);

allProducts; // {fruit: "banana", vegetable: "carrot", juice: "orange"}

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

let products = {
    fruit: 'banana',
    vegetable: 'carrot'
};

let newProduct = {
    juice: 'orange'
}

let allProducts = Object.assign(products, newProduct);

products; // {fruit: "banana", vegetable: "carrot", juice: "orange"}
allProducts; // {fruit: "banana", vegetable: "carrot", juice: "orange"}

Если в объектах попадаются свойства с одинаковыми именами, то они перезаписываются в порядке слева на право.

let products = {
    fruit: 'banana',
    vegetable: 'carrot'
};

let newProduct = {
    fruit: 'orange'
}

let allProducts = Object.assign({}, products, newProduct);

allProducts; // {fruit: 'orange', vegetable: "carrot"}

Итераторы объектов

for … in – проходится по каждому отдельному элементу (свойству) объекта.

let products = {
    juice: 'orange',
    fruit: 'banana',
    vegetable: 'carrot'
};
  
for (let item in products) {
    console.log('Key: ' + item + ', value: ' + products[item]);
}

// Key: juice, value: orange
// Key: fruit, value: banana
// Key: vegetable, value: carrot

Переменная item может иметь любое имя. Как уже говорилось ранее, в переменную item, попадает, при каждой итерации, имя свойства объекта.

Object.keys() – метод, который возвращает массив имен свойств объекта. Такой массив будет содержать имена свойств объект в виде строк.

let products = {
    juice: 'orange',
    fruit: 'banana',
    vegetable: 'carrot'
};
  
let productsName = Object.keys(products);
console.log(productsName); // ["juice", "fruit", "vegetable"]

Spread оператор и деструктуризация объекта

Spread оператор (оператор разворота) – позволяет разворачивать один объект в другой. Синтаксис spread оператора – три точки (…). Другими словами, spread оператор, как и Object.assign(), копирует все свойства из объектов в новый объект.

const user = { name: 'David', age: 30 };
const tools = { system: 'Windows', editor: 'VS Code' };

const object = {...user, ...tools};
// { name: "David", age: 30, system: "Windows", editor: "VS Code" }

Результат от использования spread оператора и Object.assign() одинаковый. В случае с использованием spread оператора нет необходимости указывать промежуточный объект, куда все будет копироваться, он копирует все новые свойства непосредственно в определенную переменную.

В случае совпадения имен свойств, сработает тоже правило, что и при использовании Object.assign(), последнее полученное значение перетрет существующее.

const programmer = { name: 'David', age: 30, editor: 'VS Code' };
const user = { editor: 'Sublime text'  };

const newUser = {...programmer, ...user};
// {name: "David", age: 30, editor: "Sublime text"}

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

let products = {
    juice: 'orange',
    fruit: 'banana',
    vegetable: 'carrot'
};

const juice = products.juice;
const fruit = products.fruit;

С использованием spread оператора все становится намного проще.

let products = {
    juice: 'orange',
    fruit: 'banana',
    vegetable: 'carrot'
};

const {juice, fruit, vegetable} = products;

juice; // 'orange'
fruit; // 'banana'
vegetable; // 'carrot'

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

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

let products = {
    juice: 'orange',
    fruit: 'banana',
    vegetable: 'carrot'
};

const {juice: j, fruit: f, vegetable: v} = products;

j; // 'orange'
f; // 'banana'
v; // 'carrot'

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

let products = {
    juice: 'orange',
    fruit: 'banana'
};

const {juice: j, fruit: f, vegetable = 'carrot' } = products;

В случае, если свойств в объекте больше чем переменных, то можно указать специальную переменную с помощью rest оператора (оператор остатка), синтаксис которого совпадает с spread оператором.

let products = {
    juice: 'orange',
    fruit: 'banana',
    vegetable: 'carrot'
};

const {juice, ...restProducts } = products;

juice; // 'orange'
restProducts; // {fruit: "banana", vegetable: "carrot"}

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

ВСТРОЕНЫЕ ОБЪЕКТЫ В JAVASCRIPT

String

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

const user = 'John';
const system = new String('Windows');

На практике используется первый способ.
Если есть необходимость как-то отформатировать строку, то для этого есть специальные символы. Наиболее часто используемый это символ \n для переноса на новую строку.

const text = 'Life is good,\n life is great';
console.log(text);
// Life is good,
// life is great

На созданной строке можно вызывать свойства и методы объекта String. Это возможно благодаря тому, что JavaScript автоматически преобразовывает литерал строки во временный String объект, вызывает метод, и затем уничтожает временный String объект. Это же относится и к String.length свойству, примененному к литералу строки.

Свойство length

length – отображает длину строки.

const system = 'Windows OS';
console.log(system.length); // 10

Пробел считается символом. Нумерация начинается с 1.

Методы toLowerCase() и toUpperCase()

toLowerCase() – преобразовывает все символы строки в нижний регистр.
toUpperCase() – преобразовывает все символы строки в верхний регистр.

const language = 'JavaScript';
const lower = language.toLowerCase(); // 'javascript'
const upper = language.toUpperCase(); // 'JAVASCRIPT'

Методы indexOf() и lastIndexOf()

indexOf() – позволяет найти и вернуть номер позиции искомого в строке текста. Другими словами, помогает найти в тексте какой-либо символ, слово и т.п.

const text = 'Life is good, life is great';
const key = 'life';
const firstPos = text.indexOf(key);
firstPos; // 14

Как видите, регистр имеет значение, заглавные и прописные буквы – это разные символы.
Нумерация начинается с 0, заглавная “L” имеет индекс 0. Пробел считается символом, поэтому так же учитывается при подсчете индекса.
Если же искомая строка не найдена, то метод вернет результат -1.

const text = 'Life is good, life is great';
const key = 'bad';
const firstPos = text.indexOf(key);
firstPos; // -1
 

lastIndexOf() имеет ту же функциональность, только поиск начинается с конца строки.

const text = 'Life is good, life is great';
const key = 'is';
const position = text.lastIndexOf(key);
position; // 19

Оба эти метода имеют второй, необязательные параметр, указывающий с какого индекса следует искать строку. Так как по умолчанию этот параметр установлен в 0, то без его указания поиск всегда производится с нулевого индекса.

const text = 'Life is good, life is great';
const key = 'g';
const position = text.indexOf(key, 10);
position; // 22

Метод trim()

trim() – удаляет пробелы с начала и конца строки и возвращает строку с вырезанными пробелами.

const language = '  JavaScript  ';
language.length; // 14

const trimLanguage = language.trim();
trimLanguage.length; // 10

Методы substr(), substring() и slice()

substr() – возвращает строку начиная с указанной позиции и указанной длины.
Как первый аргумент метод принимает стартовую позицию для извлечения строки, второй аргумент, длина извлекаемой строки. То есть с какого символа начинать извлечение и какая длина извлекаемой строки. Нумерация начинается с нуля.
Если не указать второй аргумент, то вернет строку от начальной позиции и до конца строки.

const str = 'Life is good';
const subString = str.substr(8, 4);
const longString = str.substr(5);

console.log(subString); // 'good'
console.log(longString); // 'is good'

substring() – возвращает строку начиная с и до указанных позиции.
Как первый аргумент метод принимает стартовую позицию для извлечения строки, второй аргумент, до какого индекса (не включая его) извлечь строку. Нумерация начинается с нуля.
Если не указать второй аргумент, то вернет строку от начальной позиции и до конца строки.

const str = 'Life is good';
const subString = str.substring(0, 5);
const longString = str.substring(5);

console.log(subString); // 'Life '
console.log(longString); // 'is good'

slice() – возвращает строку начиная с и до указанной позиции. Работает аналогично с substring().
Главное отличие slice() от substring() в работе с отрицательными числами.
В substring() если число отрицательное, то оно будет интерпретировано в 0. Если же стартовая позиция больше конечной, то аргументы меняются местами.

const str = 'Life is good';
const longString = str.substring(-2);
// -2 интерпретируется в 0 и выполняется как str.substring(0)
const subString = str.substring(5, -1); 
// 5 > -1, аргументы меняются местами, -1 интерпретируется как 0 и выполняется как str.substring(0, 5)

console.log(longString); // 'Life is good'
console.log(subString); // 'Life '

Метод slice() работает более прозрачно, отрицательные значения означают отсчет с конца строки.

const str = 'Life is good';
const longString = str.slice(-2);
// от 2 индекса с конца
const subString = str.slice(5, -1); 
// с 5 индекса до 1 с конца

console.log(longString); // 'od'
console.log(subString); // 'is goo'

Метод split()

split() – разделяет строку на массив подстрок по указанному разделителю.

const text = 'Life is good, life is great';

const separated = text.split(','); // ["Life is good", " life is great"]
const words = text.split(' '); // ["Life", "is", "good,", "life", "is", "great"]

Метод concat()

concat() – объединяет несколько строк в одну и возвращает новую.

const good = 'Life is good';
const great = 'life is great';
const song = 'Song: ';

const text = song.concat(good, ', ', great);
text; // 'Song: Life is good, life is great'

Результат метода concat() аналогичен конкатенации строк с помощью оператора +.

const good = 'Life is good';
const great = 'life is great';

const text = good + ', ' + great;
text; // 'Life is good, life is great'

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

Шаблонные строки – способ создания сТроковых литералов.

const name = 'John';

`Hello ${name}!`;
// Hello John!

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

const name = 'Life is good, life is great';

function songName(words) {
    const song = 'Song:';
    return `${song} ${words}`;
}

`Now you are listening ${songName(name)}`;
// Now you are listening Song: Life is good, life is great

Обратите внимание на синтаксис. Шаблонные строки заключаются в обратные кавычки . Если в шаблонную строку записывается обычная строка (в примере это Now you are listening), то никаких особенностей нету. Указывая в шаблонной строке переменные или какие-то вычисляемые значения (в примере выше это функция songName), то они заключаются в конструкцию ${ }. Кроме того, если в обычных строках для переноса строки нужно указывать специальный символ, то можно начать строку с новой линии, просто явно перенеся ее на новую.

const a = 10;
const b = 5;

`Add: a + b = ${a + b}
Multiply: a * b = ${a * b}`;

// Add: a + b = 15
// Multiply: a * b = 50

Number и Boolean

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

const num = 25;
const bigNumber = new Number(5555);

Если в объект Number передать не число, то оно будет преобразовано к числу, а в случае невозможности такой трансформации, результатом будет NaN.

new Number('777'); // 777
new Number('number'); // NaN

В отличии от методов объекта String, где можно вызвать метод на самой строке (например, someString.toUpperCase()), то при использовании объекта Number все его методы надо вызывать на самом объекте (например, Number.isNan(5)).
Основные методы для работы с числами были рассмотрены ранее, поэтому просто перечислим их:

  • • Number.isNaN() – определяет, является ли переданное в функцию значение NaN;
  • • Number.isFinite() – определяет, является ли переданное в функцию значение конечным числом;
  • • Number.parseInt() – преобразовывает строку в число посимвольно;
  • • Number.parseFloat() – преобразовывает строку в десятичное число (число с плавающей точкой).

Кроме того, существует метод Number.isInteger() .
Number.isInteger() – определяет является ли переданное значение целым числом и возвращает true либо false, в зависимости от результата.

Number.isInteger(0); // true
Number.isInteger(25); // true
Number.isInteger('25'); // false
Number.isInteger(0.1); // false
Number.isInteger(NaN); // false

Напомним, NaN – это специальное свойство, которое возвращается как результат операции, когда значение невозможно интерпретировать в число или невозможно выполнить математическую операцию.
Кроме NaN, существует другое глобальное свойство Infinity которое является специальным числовым значением. Это числовое значение больше любого другого числа. Его можно получить при делении числа на 0.

const inf = 25 / 0;

console.log(inf); // Infinity

console.log(inf > 777); // true

Boolean
Создание примитивного значения булевого типа с помощью присвоения одного из его значений (true или false) в переменную и с помощью объекта Boolean имеют отличие. Тут следует сказать про общую особенность использования объектов для создания примитивных типов.

const str = 'Some text';
const objStr = new String('Some text');

console.log(typeof str); // string
console.log(typeof objStr); // object

const num = 25;
const objNum = new Number(25);

console.log(typeof num); // number
console.log(typeof objNum); // object

const bool = false;
const objBool = new Boolean(false);

console.log(typeof bool); // boolean
console.log(typeof objBool); // object

Как видно, примитивы, созданные с помощью объектов, при определении их типа данных, показываю не совсем то, что вы скорее всего ожидали, – это объекты. Это одна из причин, почему не следует использовать такой способ создания примитивов.
Объекты String, Number или Boolean лучше всего использовать без ключевого слова new, в таком случае они будут просто приводить переданные в них значения в соответственные типы – строка, число или булевый. Об этом шла речь в теме о преобразовании типов.
Вернемся к Boolean.
Таким образом использование объекта Boolean в результате даст вам преобразование переданного в него значения в булевый тип. Напомним, пустая строка, 0, null, false, NaN, undefined преобразовываются в false, все остальные значения в true.
Присвоение же в переменную значение true или false это просто создание переменной с соответствующим значением.

const bool = false;
const objBool = new Boolean(0);

И вроде как 0 должен преобразоваться к false.

const objBool = new Boolean(0);
objBool === false; // false

Как видите, результат сравнения false. Почему? Давайте выведем в консоль objBool.


Как видите, это объект, у которого просто есть значение false. Поэтому при сравнении выше, сравнивается не примитивы boolean и boolean, а object и boolean.
При нестрогом равенстве результат сравнения будет true.

const objBool = new Boolean(0);
objBool == false; // true

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

const bool = new Boolean(false);
if (bool) {
  console.log('WHAT!?') // выполнится
}

Условие выполнится так как в переменной bool находится не false, как можно было ожидать, а объект. Объект всегда true, даже если он пустой.

Date

Date – объект который используется для работы с датой и временем.
В объектах Date время сохраняется как число миллисекунд от 1 января 1970 года.
Вызов new Date() без аргументов вернет текущий день в таком формате: Sat Dec 09 2017 16:45:10 GMT+0200 (Eastern Europe Standard Time).

const currentDate = new Date();

Из даты можно получить отдельные компоненты даты: getFullYear(), getMonth(), getDate(), getHours(), getMinutes(), getSeconds(), getMilliseconds().
Все они по названиям интуитивно понятны, лишь следует учесть несколько моментов:

  • • getFullYear – возвращает год в формате четырех цифр;
  • • getMonth – возращает месяц, нумерация начинается с 0, так что 0 – это первый месяц, а 11 – последний;
  • • getDate – число месяца от 1 до 31;
  • • getHours – возвращает часы от 0 до 23;
  • • getMinutes – возвращает минуты от 0 до 59;
  • • getSeconds – возвращает секунды от 0 до 59;
  • • getMilliseconds – возвращает миллисекунды от 0 до 999.
const currentDate = new Date();

currentDate.getFullYear(); // вернет текущий год, например 2017

с помощью Data можно и задать необходимую дату. Этот объект может принять множество параметров: new Date(year, month, date, hours, minutes, seconds, ms). В данной ситуации минимальным количеством параметров, которые передаются в Date, являются первые два: year (состоит из 4 чисел) и month (нумерация начинается с 0). Все остальные параметры являются опциональными.

new Date(1995, 11, 17);
// Sun Dec 17 1995 00:00:00 GMT+0200 (Eastern Europe Standard Time)

new Date(1995, 11, 17, 3, 24, 0);
// Sun Dec 17 1995 03:24:00 GMT+0200 (Eastern Europe Standard Time)

Кроме того, в полученной дате можно менять отдельные компоненты даты. Делается это с помощью методов: setFullYear(), setMonth(), setDate(), setHours(), setMinutes(), setSeconds(), setMilliseconds(), setTime().
В эти методы передаются значения в формате описанным выше для методов получения даты (год – четыре цифры, месяц – начиная от нуля и т.д.).
При установке даты с помощью этих методов, кроме изменения самой даты, методы возвращают количество миллисекунд от новой установленной даты.

const currentDate = new Date();

currentDate; // Sat Dec 09 2017 17:23:27 GMT+0200 (Eastern Europe Standard Time)
currentDate.getTime(); // 1512833007138

const newDate = currentDate.setHours(0, 0, 0, 0);
newDate; // 1512770400000
currentDate; // Sat Dec 09 2017 00:00:00 GMT+0200 (Eastern Europe Standard Time)

с помощью использованного выше метода getTime() можно получить текущее время в миллисекундах от 1 января 1970 года.

Math

Math – объект, который содержит в себе множество математических констант и функций.
Рассмотрим несколько из них.
Math.floor() – округляет число вниз.
Math.ceil() – округляет число вверх.
Math.round() – округляет число до ближайшего целого.

Math.floor(2.9); // 2
Math.ceil(5.1); // 6
Math.round(5.5); // 6

Math.max() – возвращает максимальное число из переданных.
Math.min() – возвращает минимальное число из переданных.

Math.max(11, 3243, 532, 5, 4); // 3243 
Math.min(11, 3243, 532, 5, 4); // 4 

МАССИВЫ

Определение и использование

Массив – это упорядоченная коллекция значений.
Каждый элемент массива характеризуется числовой позицией в массиве, которая называется индексом. То есть, значения в массиве хранятся не под заранее заданными именами (ключами), а под числовыми индексами. Индексация массивов начинается с нуля .
Элементы массива могут иметь любой тип, причем разные элементы одного и того же массива могут иметь разные типы, кроме примитивов, это могут так же быть объекты или другие массивы.
Если применить к массиву оператор typeof, то мы получим object . Таким образом массивы – это специальный тип объектов.

Создание массивов

Создать массив можно двумя способами: при помощи литерала (конструкция []) или ключевого слова new.

const array = [];
const newArray = new Array();
console.log(typeof array); // object

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

Присвоение значений

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

const array = ['HTML', 'CSS'];
console.log(array); // ['HTML', 'CSS']

array[2] = 'JavaScript';
console.log(array); // ['HTML', 'CSS', 'JavaScript']

Мы создали массив с двумя строковыми значениями. Поскольку нумерация значений в массивах автоматически начинается с 0, то строка HTML помещена в массив под индексом 0, строка CSS под индексом 1. Далее мы добавили к следующему индексу строку JavaScript. Что бы убедиться, что значения в массиве действительно получают числовые ключи (индексы), посмотрим на массив, из примера выше, в консоли.

Как видите, индекс соответствует значению. Кроме того, в консоли видно свойство length , которое указывает на длину массива. Несмотря на то, что индексация в массиве начинается с 0, длина все равно соответствует количеству элементов в массиве. Длина массива – это всегда индекс последнего элемента + 1.
При создании массива со значениями с помощью new Array(), необходимо учитывать следующую особенность. Попробуем сделать такие же присвоения что и в примере выше.

const newArray = new Array('HTML', 'CSS');
console.log(newArray); // ['HTML', 'CSS']

newArray[2] = 'JavaScript';
console.log(newArray); // ['HTML', 'CSS', 'JavaScript']

Результат тот же.
При создании массива передадим в него как значение только одно число.

const newArray = new Array(5);

console.log(newArray); // [empty × 5]
console.log(newArray.length); // 5

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

const array = [5];

console.log(array); // [5]
console.log(array.length); // 1

Получение и изменение свойств

Получить или изменить значения массива можно с помощью соответствующих числовых индексов.

const array = ['JavaScript', 4, { name: 'John' }];

console.log(array[0]); // 'JavaScript'
console.log(array[1]); // 4
console.log(array[2]); // {name: 'John'}

array[1] = 25;

console.log(array[1]); // 25

console.log(array[3]); // undefined

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

const array = ['JavaScript', 4, { name: 'John' }];
array[6] = 25;

console.log(array); // ["JavaScript", 4, {…}, empty × 3, 25]
console.log(array.length); // 7

В консоли это выглядит так.


Как и было сказано выше, длина массива – это всегда последний индекс массива + 1. Ожидая длину массива в 4 элемента, получаем 7.
Стоит упомянуть также и о том, что так как массив – это тип объекта, то его значения, так же передаются по ссылке, а не по значению.

let array = [1, 2, 3, 4];
let newArray = array;

array; // [1, 2, 3, 4]
newArray; // [1, 2, 3, 4]

array[2] = 777;

array; // [1, 2, 777, 4]
newArray; // [1, 2, 777, 4]

Методы массивов

push() – добавляет один или несколько новых элементов в конец массива и возвращает его новую длину.

const array = ['HTML'];

array.push('CSS', 'JavaScript');

console.log(array); // ['HTML', 'CSS', 'JavaScript']

pop() – удаляет последний элемент массива, уменьшает длину массива. Возвращает удаленное им значение.

const array = ['HTML', 'CSS', 'JavaScript'];

const deleted = array.pop();

console.log(array); // ['HTML', 'CSS']
console.log(deleted); // 'JavaScript'

unshift() – добавляет элемент или элементы в начало массива.

const array = ['CSS', 'JavaScript'];

array.unshift('HTML');

console.log(array); // ['HTML', 'CSS', 'JavaScript']

shift() – удаляет и возвращает первый элемент массива.

const array = ['HTML', 'CSS', 'JavaScript'];

const deleted = array.shift();

console.log(array); // ['CSS', 'JavaScript']
console.log(deleted); // 'HTML'

join() – преобразовывает массив в строку с заданным разделителем. Если разделитель не указать, то массив будет разделен через запятую.

const array = ['HTML', 'CSS', 'JavaScript'];

const joined = array.join();
const dashJoined = array.join('--');

console.log(joined); // 'HTML,CSS,JavaScript'
console.log(dashJoined); // 'HTML--CSS--JavaScript'

split() – разделяет строку на массив по переданному разделителю.

const names = 'John, Dave, Bill';

const array = names.split(', ');

console.log(array); // ['John', 'Dave', 'Bill']

slice() – копирует часть массива от и до (не включая это значение).
Указав только один аргумент, возвращает элементы от начальной позиции до конца массива. Если один из аргументов имеет отрицательное значение, он определяет индекс элемента относительно конца массива. Так, аргументу -1 соответствует последний элемент массива, а аргументу -3 – третий элемент массива с конца.

let array = ['Paul', 'John', 'Bill', 'Joe', 'Mark'];

array.slice(0, 3); // ['Paul', 'John', 'Bill']
array.slice(3); // ['Joe', 'Mark']
array.slice(1, -1); // ['John', 'Bill', 'Joe']
array.slice(-3, -2); // ['Bill']

splice() – универсальный метод, выполняющий вставку или удаление элементов массива.
Первый аргумент определяет позицию в массиве, начиная с которой будет выполняться вставка и/или удаление. Второй аргумент определяет количество элементов, которые должны быть удалены (вырезаны) из массива. Если второй аргумент опущен, удаляются все элементы массива от указанного до конца массива.
В случае, если метод удаляет какие-то элементы, то он вернет массив, содержащий их, если же удаления не было, то вернется пустой массив.
Если после первых двух аргументов в методе указать дополнительные аргументы (любое количество), то этим самым определяются элементы, которые будут вставлены в массив, начиная с позиции, указанной в первом аргументе.

const list = ['one', 'two', 'three', 'four', 'five'];
const line = list.splice(2, 1);

list; // ['one', 'two', 'four', 'five']
line; // ['three']

const list = ['one', 'two', 'three', 'four', 'five'];
const line = list.splice(2, 2, 'new', 'elements');

list; // ['one', 'two', 'new', 'elements', 'five']
line; // ['three', 'four']

const list = ['one', 'two', 'three', 'four', 'five'];
const line = list.splice(3, 0, 'new');

list; // ['one', 'two', 'three', 'new', 'four', 'five']
line; // []

reverse() – сортирует массив в обратном порядке.

const list = ['one', 'two', 'three'];

const reversed = list.reverse();

console.log(reversed); // ['three', 'two', 'one']

concat() – создает и возвращает новый массив, содержащий элементы исходного массива, и значения всех переданных аргументов. Такая манипуляция с массивом называется конкатенация.

const list = ['one', 'two', 'three'];

const concatString = list.concat('four', 'five');
const concatArray = list.concat(['four', 'five', 'six']);

console.log(concatString); // ['one', 'two', 'three', 'four', 'five']
console.log(concatArray); // ['one', 'two', 'three', 'four', 'five', 'six']

indexOf() – возвращает номер искомого элемента в массиве. Если элемент не найден, то вернет -1.

const list = ['one', 'two', 'three'];
console.log(list.indexOf('two')); // 1
console.log(list.indexOf('four')); // -1

lastIndexOf() – выполняет то же что и indexOf(), но поиск начинается с конца массива.

Spread оператор и деструктуризация массива

При помощи spread оператора можно как конкатенировать, так и копировать массив, при этом создается именно копия, а не передача массива по ссылке.

// Конкатенация

let array = ['Angular'];
let newArray = ['React', ...array, 'Vue'];

console.log(newArray); // ['React', 'Angular', 'Vue']

// Копирование
let array = ['React', 'Angular', 'Vue'];
let newArray = [...array];

console.log(newArray); // ['React', 'Angular', 'Vue']

Деструктуризация работает так же, как и в объектах.

let array = ['one', 'two', 'three'];
let [one, two, three] = array;

console.log(one); // 'one'
console.log(two); // 'two'
console.log(three); // 'three'

let one, two, rest;

[one, two] = [1, 2];
console.log(one); // 1
console.log(two); // 2

[one, two, ...rest] = [1, 2, 3, 4, 5];
console.log(one); // 1
console.log(two); // 2
console.log(rest); // [3, 4, 5]

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

let one, two;

[one=1, two='five'] = ['one'];
console.log(one); // 'one'
console.log(two); // 'five'

ИТЕРАТОРЫ МАССИВОВ

for и for … of

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

const list = ['firstItem', 'secondItem', 'thirdItem'];

for (let i = 0; i < list.length; i++) {
    console.log(list[i]);
}

// firstItem
// secondItem
// thirdItem

С помощью свойства массива length определяем длину массива и таким образом определяем количество итераций для массива. Таким образом, в примере выше list.length равен 3-м и будет выполнено три итерации, пока условие, что переменная-счетчик (i) будет меньше длины массива.
Перебор составных типов (объекта, массива) очень популярная операция. Во время перебора можно выполнять различные вычисления и операции с элементами. Кроме того, например, можно заполнить пустой массив значениями.

const list = [];

for (let i = 0; i < 5; i++) {
    list[i] = `value ${i}`;
}

console.log(list); // ["value 0", "value 1", "value 2", "value 3", "value 4"]

for … of как и for предназначен для перебора элементов массива. Этот способ был введен в стандарте ECMAScript 2015 (ES6).

const list = ['firstItem', 'secondItem', 'thirdItem'];

for (let value of list) {
    console.log(value);
}

// firstItem
// secondItem
// thirdItem

При каждой итерации переменной value присваивается значение нового элемента коллекции из массива.
В конструкции for … of можно использовать операторы continue и break. Напомним, continue прерывает выполнение текущей итерации и сразу запускает следующую. Оператор break прекратит дальнейшие итерации, перебор массива будет остановлен. К слову, эти операторы так же используются и в конструкции for.

const list = ['one', 2, 'three', 'four', null, 6];

for (let value of list) {
    if (typeof value === 'string') { 
        continue;
    }

    if (value === null) {
        break;
    }

    console.log(value);
}
// 2

forEach

forEach() – это метод, который перебирает все элементы массива и для каждого вызывает callback функцию. То есть, в этот метод передается callback функция (функция обратного вызова), которая может принять три аргумента (текущий элемента массива, индекс текущего элемента массива, массив, который перебирается).

let list = ['firstItem', 'secondItem', 'thirdItem'];

list.forEach(function(item){
    console.log(item);
});
// firstItem
// secondItem
// thirdItem

list.forEach(function(item, index){
    console.log(`index: ${index}, item: ${item}`);
});
// index: 0, item: firstItem
// index: 1, item: secondItem
// index: 2, item: thirdItem

Метод forEach вызывается на массиве, который необходимо перебрать (list.forEach()). Сам метод принимает обязательную callback функцию, которая применяется к каждому элементу массива, то есть, внутри этой функции с элементом можно выполнять любые операции.
Для того, чтобы работать с текущим элементом в callback функции, функция, как первый аргумент, принимает сам элемент. В примере выше, в аргумент item, по одному, подставляются каждый из элементов массива. Вторым аргументом функция принимает индекс текущего элемента массива. Все эти аргументы автоматически передаются в callback функцию, благодаря внутреннему механизму метода forEach.
Третий аргумент, который принимает функция, является сам массив, который перебирается.
Кроме анонимной function declaration, в метод forEach можно передать и стрелочную функцию, что в современном коде используется повсеместно.

list.forEach((item, index, array) => {
    console.log(`index: ${index}, item: ${item}`);
    console.log(array[index]);
});
// index: 0, item: firstItem
// firstItem
// index: 1, item: secondItem
// secondItem
// index: 2, item: thirdItem
// thirdItem

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

let numbers = [1, 2, 3];

numbers.forEach(function(number) {
    console.log(number * 2);
});
// 2
// 4
// 6

console.log(numbers); // [1, 2, 3]

В данном методе не работают операторы continue и break . Метод forEach предназначен именно для перебора и обработки каждого элемента массива, для выполнения проверки условий для какого-то элемента или необходимости вернуть какие-то значения, существуют специально созданные для этого методы.

map и filter

map() – этот метод создает новый массив, который состоит из результатов вызовов callback функции.
Данный метод работает аналогично методу forEach (метод также принимает callback функцию, в которую так же можно передать три аргумента). Отличительной чертой map от forEach является то, что первый возвращает новый массив. Поэтому в теле callback функции следует обязательно указывать, что именно возвращается с помощью ключевого слова return.
При этом, метод map так же не изменяет исходный массив.

let numbers = [1, 2, 3];

let doubleNumbers = numbers.map(function(number) {
    return number * 2;
});

console.log(doubleNumbers); // [2, 4, 6]
console.log(numbers); // [1, 2, 3]

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

let numbers = [1, 2, 3];

let doubleNumbers = numbers.map(number => number * 2);

console.log(doubleNumbers); // [2, 4, 6]
console.log(numbers); // [1, 2, 3]

filter() – этот метод создает новый массив с отфильтрованными, с помощью callback функции, значениями.
Метод работает аналогично методу map, с одним небольшим отличием – в новый массив попадут только те значения, которые прошли проверку в callback функции.

let list = ['one', 2, 'three', 'four', null, 6];

let newList = list.filter(item => typeof item === 'string');

console.log(newList); // ["one", "three", "four"]

В примере выше в массив попадут только те элементы массива, тип которых равен string.
С помощью этого метода можно не просто сравнивать, но и предварительно сделать какие-то операции.

let numbers = [14, 2, 22, 78, 114];

let newNumbers = numbers.filter(number => {
    let newNumber = number * 5;
    return newNumber > 75;
});

console.log(newNumbers); // [22, 78, 114]

Здесь сначала элемент из массива умножается на 5 и лишь потом проверяется, является ли полученный результат больше 75. Заметьте, в новый массив будут возвращены именно старые, а не новые, умноженные на 5, элементы массива.

every и some

Методы some и every похожи друг на друга – возвращают true или false.
some() – вернет true в том случае, если хотя бы один из элементов массива, при выполнении условия в callback функции, вернет true.
every() – возвращает true, когда все элементы массива, при выполнении условия внутри callback функции, вернут true.
Callback функции этих методов принимают те же аргументы, что и в рассмотренных выше методах.

let list = [12, 25, 5, 7, 25, 9];

let everyResult = list.every(function(item){
    return item > 10;
});
console.log(everyResult); // false

let someResult = list.some(item => item > 10);
console.log(someResult); // true

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

reduce и reduceRight

reduce() – метод который обрабатывает, с помощью callback функции, каждый элемент массива, но при этом результат каждой такой итерации записывается в промежуточное значение.
Это наиболее сложный метод для работы с массивом.
Метод reduce, кроме callback функции, принимает еще один аргумент – начальное значение, в которое и будут записываться промежуточные значения. Сама callback функция также принимает больше аргументов, чем предыдущие методы: промежуточное значение, элемент массива, индекс элемента и сам массив.

let numbers = [1, 2, 3, 4, 5];

let sum = numbers.reduce(function(result, number) {
    return result + number;
}, 0);

console.log(sum); // 15

В примере выше, как и остальные рассмотренные методы, метод reduce первым параметром принимает функцию, второй параметр – начальное значение – 0. Это начальное значение, при каждой итерации, передается первым аргументом в callback функцию.
Таким образом, при каждой итерации, результат выражения result + number подставляется на место начального значения (вместо 0), и далее передается первым аргументом в функцию (подставляется в result).

Сначала начальное значение передается первым аргументом в callback функцию (1), после этого происходят вычисления (не обязательно использовать result в них), результаты которых опять же помещаются в начальное значение (2) и так до последней итерации.
Метод reduce возвращает аккумулированный результат, который является последним значением, которое попало в параметр «начальное значение».
reduceRight() – работает аналогичным образом, только с конца массива.

ДОМАШНЕЕ ЗАДАНИЕ

Цель: потренироваться в работе с массивами и объектами.
ЧИТАТЬ ВНИМАТЕЛЬНО, МНОГО ДЕТАЛЕЙ! ВНИЗУ ЕСТЬ ПРИМЕР ТОГО, ЧТО У ВАС ДОЛЖНО В РЕЗУЛЬТАТЕ ПОЛУЧИТЬСЯ!
Вам необходимо из массива данных, который находится в файле data.js создать новый выполнив серию преобразований.
Новый массив объектов необходимо вывести в консоль.
Заготовку сможете найти здесь. Напишите ваш функционал в main.js.
Файл data.js уже подключен, по этому просто пишите код вашей программы, массив должен быть доступен.
Внимание: если какое то преобразование сделать не удается. Пропускаете его и и двигайтесь дальше.

  1. С помощью функции splice необходимо вырезать 6-й элемент массива. В результате ваш массив должен стать короче на один элемент.

  2. Используйте функцию forEach.
    Внутри цикла создайте новый массив объектов.
    В процессе создания нового массива объектов, избавьтесь от ключа id.
    То есть в вашем новом массиве не должно быть id в каждом конкретном объекте.
    Подсказка:

var newArr = [];
data.forEach(function(item, index){
      newArr.push({
        name: item.name
        ////.....
      })
})
  1. По новому массиву объектов, полученному с помощью функции forEach пройдитесь методом map()

  2. На каждой итерации цикла мы получаем один объект из массива объектов. Берем этот объект и преобразоваем его поля по следующим правилам.
    Вам пригодится документация по дате и по строкам.

  3. Для поля Name: с помощью функций работы со стрингами делаете первую букву большой, остальные маленькие (ДЖИП -> Джип)

  4. Для поля url: добавить перед ним «http://»

  5. Для поля Description: с помощью функций работы со стрингами делаете обрезание до 15 символов. После добавляем многоточие (…) Остальное отбрасываете.

  6. Для поля date: создать объект даты из заданных миллисекунд и потом отобразить в виде «2015/07/02 14:15»
    Для этого надо открыть документацию по дате.
    https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Date
    Пример как преобразовывать дату. Можно использовать и другой подход. Главное чтоб он не был более громоздким и сложным.

var date = 1422153200637;
var newDate = function(date){
    var tmpDate = new Date(date);
    return tmpDate.getFullYear() + "/" +
           tmpDate.getMonth() + "/" +
           tmpDate.getDate() + " " +
           tmpDate.getHours() + ":" +
           tmpDate.getMinutes();
};

9*.(дополнительное задание)
Более предпочтительно работать с датой с помощью библиотеки moment.js
Постарайтесь разобраться как она работает и использовать ее вместо примера выше. Если очень тяжело — используйте подход выше.

  1. Для поля params: из значений ключей сформировать строку типа «true=>80». Для выполнения задания можно обращаться к полям объект params напрямую.
    То есть params.status и params.progress

  2. Создать новое поле isVisible. Переложить в это поле значение поля params.status.

  3. После всех преобразований функция map вернет вам новый массив. Теперь с помощью функции filter вам необходимо выбрать только те элементы у которых isVisible == true. Пример работы функции filter есть в презентации.

  4. Полученный результат печатаем в консоль.
    Для печати используем отдельную функцию как в прошлых заданиях. То есть вынесете console.log в отдельную функцию.

  5. Пример результата (количество элементов в результате должно быть не два а сколько укажете в переменной):

var data = [{
    url: "http://desktopwallpapers.org.ua/mini/201507/40069.jpg",
    name: "Chevrolet",
    params : "true=>80",
    isVisible : true,
    description : "be conveyed to ...",
    date : "2015/01/25 14:15"
},{
    url: "http://desktopwallpapers.org.ua/mini/201507/40068.jpg",
    name: "Dewoo",
    params : "true=>88",
    isVisible : true,
    description : "sing color to a...",
    date : "2015/12/18 15:35"
}]

ЗАДАЧИ

1. Из данного массива удалить значение «technics». Все остальное превратить в строку формата «foods, fruits…» преобразование в строку выполнить с помощью одного метода.

goods = ['foods', 'fruits', 'technics', 'phones', 'computers']

2. Преобразовать текущую дату и время в понятный человеку формат: 08:05 01/01/2018. Используя шаблонные строки.

3. Напишите функцию, которая возвращает расширение файла. Например, getExt(«/home/user/project/script.js») вернет “js”. Функция должна принимать строку

4. Напишите функцию, которая удаляет дубликаты из массива. Например, входной массив: [1, 2, 2, 4, 5, 4, 7, 8, 7, 3, 6], массив который возвращает функция [1, 2, 4, 5, 7, 8, 3, 6]

2 Likes