Фундаментальные основы JavaScript

Содержание

Типы данных

Примитивные:

  • String (“Hello world”, ‘Text’, `Template string`);
  • Number (2020, 4.3);
  • Boolean (true, false);
  • Null (null);
  • Undefined (undefined);
  • Symbol ES6 (Symbol());

Составные:

  • Object ({age:50});
    • Function (function Test() {});
    • Array ([1,2,3,4,5]).
    • Date (Thu Jan 01 1970 02:00:02 GMT+0200 (Финляндия (зима)));

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

Когда JavaScript встречает в коде переменную, он смотрит, чему она равна, и вместо переменной использует её значение.

Переменные могут меняться. В одну переменную могут записываться разные данные.

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

Переменные. Правила именования и объявления.

  1. Объявляются переменные с помощью ключевого слова var, let или const.
  2. Имя может состоять из: букв, цифр, символов $ и _ .
  3. Первый символ не должен быть цифрой.
  4. Обычная переменная начинается с маленькой буквы (можно называть и с большой но по этому поводу есть определенные соглашения с которыми мы познакомимся позже).
  5. Регистр букв имеет значение easycode и EasyCode это разные переменные.
  6. В имени переменной используем строго английские буквы.
  7. Именоваться переменная должна строго по смыслу (сообщение -> message или msg).
  8. Существует список зарезервированных слов, которые нельзя использовать для переменных, так как они используются самим языком, например: var, class, return, export и др.
  9. Используют несколько стилей именования переменных это либо camelCase или через _ например: let userName; или let user_name; Выбрав один стиль его и придерживайтесь.
  10. Константы именуются все в верхнем регистре, например: const PI; но если константа это не какое то число которое не должно меняться то может именоваться с маленькой буквы, например const array = [1, 2, 3];

Переменные. Ключевое слово var, let, const.

var user = "Andrews";
let year = 2020;
year = 2019; // перезапись переменной
let new_year = year; // копирование переменной
// теперь new_year равна 2019
const default_string = "default string";
const arr = [1, 2, 3];
const string_array = ["one", "two"];
let month; // Если переменная ничему не равна, то она равна undefined.

let a = 1,
     b = 10,
     c = 20,
     arr = [1, 2, 3],
     string_array = ["one", "two"];

Вне зависимости от места объявления переменной, она стремится “всплыть” вверх, действительно для переменных var.

Переменные. ECMAScript 6.

let

  • всплытие (hoisting) не работает;
  • блочная видимость;
  • не может быть двух одинаковых let.

const

  • все то же самое что и у let + невозможность изменить;
  • нельзя создать пустую const.

Полезные ссылки:

Преобразование типов

Преобразование типов это процесс конвертации значения из одного типа в другой (как например, строки в число, объекта к булевому значению и т. д.).

Явное и неявное преобразование

Преобразование типов может происходить явно и неявно.

Когда разработчик хочет намеренно произвести преобразование типов, написав, к примеру Number(value), это называется явным преобразованием типов (или type casting).

Так как JavaScript это слабо типизированный язык, преобразование между разными типами может происходить автоматически, и это называется неявным преобразованием типов. Чаще всего это происходит когда вы применяете операторы к значениям разных типов, таких как 1 == null, 2 / `5`, 1 + “” и т.д.

Преобразование примитивов

Есть 3 типа преобразования:

Строковое:

  1. Функции String(2018), (2018).toString();
  2. Сложение со строкой 1 + “” // “1”

Числовое:

  1. арифметические операции (кроме +)
  2. нестрогое сравнение разных типов: 12 == ‘12’, if (5) {…}
  3. функция Number: Number(‘18’)
  4. унарный оператор +: +’58’, +true

true → 1, false → 0, undefined → NaN, null → 0, “text” → NaN, “” → 0

Логические преобразования:

  1. “”, null, undefined, NaN → false
  2. 2016, ‘string’, {}, [] → true
  3. функция Boolean: Boolean(‘test me’) → true
  4. оператор !!: !!’string’ → true
  5. внутри if (Boolean) {}

При сравнении примитивов (кроме !== и ===) сначала всё приводится к числу, а потом сравнивается:

'12' > 2; // true ('12' > '2' - false)
true < 2 // true (1 < 2)
false == 0 // true (0 == 0)

НО!

undefined == false/0 // FALSE
null == false/0 // FALSE

Преобразование сложных типов:

[] + [] // “” - пустая строка
[] + [1] // “1”
1 - [1] // 0
[1] + [1] // “11”
{} + {} //  "[object Object][object Object]"
{} - {} // NaN

Полезные ссылки:

Числа

let x = 10;
let y = x + 10;
let z = 3.14;
parseInt(“200.5px”) // 200;
parseFloat(“20.5%”) // 20.5;
parseFloat(“is20.5%”) // NaN

Infinity - 1/0, 1e+309
NaN - 1 - ‘string’, 2*’10px’, 0/0
isNaN() - isNaN(‘string’) // true
isFinite() - isFinite(123) // true
         isFinite(‘string’) // false

Округление и объект Math

Math — встроенный инструмент для работы с числам

Math.round(5.1) // 5
Math.round(5.5) // 6
Math.floor(20.1) // 20
Math.floor(20.5) // 20
Math.floor(20.99) // 20
Math.ceil(20.1) // 21
Math.ceil(20.5) // 21

(100.123).toFixed(2) // ‘100.12’

Полезные ссылки:

Строки

Методы

let str = “Hello world”;

str.length;
str.charAt(1); (устаревший)
str[1];
str.toLowerCase();
str.toUpperCase();
str.indexOf(‘is’); // lastIndexOf
str.substr(5 [, 2])
str.substring(5 [, 10]);
str.slice(5 [, 10]);
str.includes(substring);
str + ‘another string’

 

Шаблонные строки

let name = “Andrew”;
let age = 44;
let str = `Hello! my name is ${name} I’m ${age} years old`; // Hello! my name is Andrew I’m 44 years old

Полезные ссылки:

Объекты

Объект — такой тип данных, который может в сочетать в себе несколько значений (примитивных или составных).

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

let user = {
  name: ‘Kostya’,
  age: 38,
  gender: ‘Male’,
  work: ‘EasyCode’
};

Объект — это всегда пара ключ: значение, помещенные внутрь фигурных скобок {}, несколько пар разделяются запятыми.

Объекты. Чтение. Запись.

et obj = {name: ‘Katniss’, age: 16}, obj2 = {}, obj3 = {};

Получить (прочитать) свойство объекта можно двумя способами:

  1. с помощью точки — объект.имяСвойства: obj.name // ‘Katniss’
  2. с помощью квадратных скобок и имени ключа в кавычках: obj[‘name’] // ‘Katniss’

Добавить новое свойство объекту или перезаписать значение существующего поля можно этими же двумя способами:

  1. obj3.name = ‘Snow’; 
  2. obj2[‘name’] = ‘Gale’;

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

let person = {
  name: ‘Nikolay’,
  age: 25
};

 

let property = ‘name’;
person[property]; // ‘Nikolay’
property = ‘age’;
person[property]; // 25

При вычислении переменной property JavaScript подставит значение этой переменной: person[‘Nikolay’];

Объекты а внутри другие объекты

let complexObj = {
  name: ‘Peeta’,
  age: 16,
  info: {
    married: false,
    country: ‘District 12’
  }
};


complexObj.info.married // false
complexObj[‘info’][‘married’] // false
complexObj[‘info’].married // false

Массивы. Тоже объекты.

Массив — это набор данных (переменных), связанных одним смыслом и помещенных внутрь квадратных скобок [ ]. Несколько переменных внутри массива разделяются запятыми.

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

let names = [‘Vasya’, ‘Petya’, ‘Slava’];
let products = [ {title: ‘Колбаса’, price: 25}, {title: ‘Хлеб’, price: 10} ];
let fibonachi = [1, 2, 3, 5, 8, 13, 21];

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

names[0] // ‘Vasya’
names[2] // ‘Slava’

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

names.length // 3

Оператор typeof

Оператор typeof возвращает строку, указывающую тип операнда, не производя конечных вычислений

typeof ‘string’ || typeof(‘string’) // “string”
typeof 5 || typeof(5) // “number”
typeof false // "boolean"
typeof undefined // "undefined"
typeof null // ooops!.. "object" → an old js mistake
typeof {name: ’Haymitch’} // "object"
typeof function foo(){} // “function”

Полезные ссылки:

Операторы сравнения

Выражения

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

Оператор — это символ или ключевое слово, которое производит вычисление над некоторыми значениями. Эти значения называют операндами.

Оператор с одним операндом называется унарным, с двумя — бинарным, с тремя — тернарным.

Унарный: 10, +’10’

Бинарный: 1 4, year = 2018

Тернарный: 10 > 9 ? true : false

Приоритеты

  • Арифметика: */% > +-
  • Логика: && > ||
  • Оператор запятая: нулевой приоритет
  • Арифметики > логика > присваивание > оператор запятая

Присвоение. Сокращения. Арифметические операторы.

Присвоение:

let x = 5;
let y = 5 + 5;
let z = x + y;

x = x +10;
x += 10;

y = y * 15;
y *= 15;

let a = b = 5;

Арифметические операторы:

  1. Инкремент: x++, ++x
  2. Декремент: —x, x— 
  3. Взятие остатка: 
4 % 2 // 0 
100 % 60 // 40 
10 % 3 // 1

Операторы сравнения

10 < 20 // true
10 > 20 // false
5 <= 10 // true
10 >= 8 // true
“text” == “text” // true
5 != 5 // false
12 === 12 // true
12 !== 12 // false

Нестрогое (проверяется только значение)

12 == ‘12’ // true
0 == false // true
undefined == null // true
12 != ‘12’ // false

Строгое (проверяется тип данных)

12 === ‘12’ // false
0 === false // false
undefined === null // false
12 !== ‘12’ // true

Логические операторы. ||, &&

Оператор “Или” ||

let x = true || false; // true
let x = ‘’ || ‘string’; // “string”
let x = ‘’ || 0 || ‘string’; // “string”

let user = { name: “Denis”, age: 29 };
let userName = user.name || “User”;

Оператор “И” &&

let x = true && false; // false
let x = ‘’ && ‘string’; // “”
let x = ‘string’ && 0 && ‘’; // 0

Логические операторы. Итого.

  1. В JavaScript есть логические значения true (истина) и false (ложь). Операторы сравнения возвращают их.
  2. Строки сравниваются побуквенно.
  3. Значения разных типов приводятся к числу при сравнении, за исключением строгого равенства === (!==).
  4. Значения null и undefined равны == друг другу и не равны ничему другому.

Будьте осторожны при использовании сравнений типа > или < с переменными, которые иногда могут быть null/undefined. Сделать отдельную проверку для null/undefined – хорошая идея.

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

Условные операторы. if else. Синтаксис.

if (условие) {
    инструкции которые выполняются если условие правдиво
}

if (условие) {
    инструкции которые выполняются если условие правдиво
} else
    инструкции которые выполняться если условие не правдиво
}


if (условие) {
    инструкции которые выполняются если условие правдиво
} else if (условие) {
    инструкции которые выполняются если условие правдиво
} else
    инструкции которые выполняться если условие не правдиво
}

Полезные ссылки:

Тернарный оператор

условие ? выражение если условие true : выражение если условие не true;

оператор : — обязательный;

let x = 10;

if (x === 10) { 
    console.log(x);
} else 
    console.log(‘else’);
}

x === 10 ? console.log(x) : console.log(‘else’);

 

let y = 1;

if (y === 1) {
    y = 2;
} else {
    y = 10;
}

y = y === 1 ? 2 : 10;
let z = 10;


if (z > 0) { 
    console.log(z);
} else if (z < 0) {
    console.log(‘else if’);
} else  {
    console.log(‘else’);
}

z > 0 ? console.log(z) 
     : z < 0 ? console.log(‘else if’)
: console.log(‘else’);

Switch case

let value = 2;
switch (value) {
    case 1: console.log(value);
    case 2: console.log(value);
    case 3: console.log(value);
    default: console.log('without any changes');
}

Всегда используйте в конце case блока ключевое слово break!

let value = 2;
switch (value) {
    case 1: console.log(value); break;
    case 2: console.log(value); break;
    case 3: console.log(value); break;
    default: console.log('without any changes');
}

 

let userType = ‘admin’;
switch (userType) {
    case ‘admin’
        console.log(userType); break;
    case ‘user’
    case ‘guest’: 
        console.log(userType); break;
    default
        console.log('without any changes');
}

Полезные ссылки:

 

Циклы while и for

Циклы

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

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

Для многократного повторения одного участка кода – предусмотрены циклы.

Цикл while и do while

while (условие) {…}

Пока условие приводится к true цикл будет работать.

let i = 0;
while (i < 10) {
     console.log(i);
     i++;
} 

// 0..9


let i = 0;
while (i++ < 10) {
    console.log(i);
}

// 1..10
let i = 0;
while (++i < 10) {
    console.log(i);
}

// 1..9
let i = 10;
while (i--) {
    console.log(i);
}

// 9..0

do {…} while (условие)

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

let index;
while (index) {
   console.log(index);
}

do {
    console.log(index);
} while (index)

Цикл for

for ([initialization]; [condition]; [iterator]) {… }

break — останавливает цикл, continue — прерывает текущую итерацию и переходит к следующей

for (let i = 0; i < 10; i++) {
    console.log(i);
} // 0..9

поток выполнения цикла: начало → (если условиетелошаг) → (если условиетелошаг) → … и так далее, пока верно условие.

let i объявленная при инициализации цикла недоступна за его пределами.

let i = 0;
for ( ; i < 10; i++) {
    console.log(i);
} // 0..9

let i = 10;
for ( ; i--; ) {
    console.log(i);
} // 9..0

Цикл for in

Для перебора всех свойств из объекта используется цикл по свойствам for..in .

let list  = { 
   one: ‘text’,
   two: ‘two text’,
}
for (let item in list) {
  console.log(item) // one, two …
  console.log(list[item]) // text, two text ...
}

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

Цикл for of

Новый цикл for .. of предназначен для итерации по элементам коллекций (итерируемые объекты), но в отличие от цикла for .. in при итерациях используется значение, а не ключ.

let myArray = [1, 2, 3, 4];

for (let value of myArray) {
    console.log(value); // 1, 2, 3, 4 ...
}

for-of не только для массивов. Он также работает с большинством массивоподобных объектов, вроде списков NodeList в DOM и со строками.

Полезные ссылки:

Функции

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

Предположим, мы имеем несколько выражений, которые планируются повторяться во многих местах нашего кода:

let a = 2;
let b = a +2;
console.log(‘Result is ’ + b); // ‘Result is 4’

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

Функции являются основными «строительными блоками» программы.

Примеры встроенных функций вы уже видели – это alert(message), prompt(message, default) и confirm(question). Но можно создавать и свои.

Функции. Объявление.

Чтобы указать javascript, что мы хотим объединить наши выражения в функцию, нужно использовать ключевое слово function.

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

Кроме того, после имени функции мы должны указать круглые скобки (в них могут быть аргументы) function name().

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

Тело функции заключается в фигурные скобки function name() { /* function body */ }.

function foo () {
  let a = 2;
  let b = a +2;
  console.log(‘Result is ’ + b); // ‘Result is 4’
}

Функции. Вызов.

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

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

function foo () {
  let a = 2;
  let b = a +2;
  console.log(‘Result is ’ + b);
}

foo(); // ‘Result is 4’

Функции. Возвращаемое значение. Return.

Функция всегда производит какое-то действие. Любое действие имеет результат.

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

Слово return определяет конечный результат выполнения функции.

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

Если после return указано выражение или переменная — функция вернет результат этого выражения или переменную.

function foo() {} // результат - undefined
function foo() { return; } // результат - undefined
function foo() { return 25; } // результат - 25
function foo() { return 5 + 10; } // результат - 15

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

function foo() { let a = 5; return a * 2; }
let result = foo(); // в переменную result присваивается значение 10
15 + foo(); // 15 + 10 → 25
‘В этой комнате ’ + foo() + ‘ человек’
// "В этой комнате 10 человек"

Функции. Методы объявления.

Function Declaration:

function foo() {}
test(); // 18
function test() { return 18 }

Нельзя объявлять функции внутри блоков при ‘use strict’!

if (a) {
    function goo() {return ‘works!’}
}

Function Expression:

var foo = function () {}
test(); // Error: test is not a function
var test = function (){ return 18 }
if (a) {
     var goo = function () {return ‘works!’};
}

Не сработает

if (a) {
     let goo = function () {return ‘works!’};
}

if (goo) goo();

Функции. Self invoked function.

(function () {
    console.log(‘self invoked function’);
})(); // self invoked function


(function () {
    console.log(‘self invoked function’);
}()); // self invoked function


(function (value) {
    console.log(value);
})(2016); // 2016

(function () {
    создали функцию
})(); // и тут же (на месте) её вызвали



(function () {
    создали функцию
}()); // и тут же (на месте) её вызвали


(function (value) {
   функция принимает аргумент value
})(2016); // 2016

Функции. Область видимости.

Область видимости — то место, где видна переменная.

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

Если переменная объявлена внутри функции (локальная переменная), то она видна только внутри этой функции, а также для внутренних функций.

Локальные переменные не объявленные через ключевые слова let, var или const “перезаписывают” глобальные переменные с таким же именем.

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

var a = ‘global variable’,
      b = 2016;

function foo() {
    var a = ‘local variable’;
    console.log(a, b)
}


foo() // “local variable”, 2016
console.log(a) // “global variable”
var cat = “I’m a global cat!” // 3. потом ищет в глобальном окружении

function findCat(cat) { // 2. потом она ищет внутри аргументов
    var cat = ‘I am a local cat!’; // 1. сначала функция ищет переменную внутри себя

    return cat;
}

Функции. Аргументы.

Обычно функции дают возможность выполнять один и тот же код, но с разными значениями — то есть вместо конкретных (определенных)

значений используются переменные.

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

Чтобы что-то передать внутрь функции, мы используем аргументы.

Аргументы передаются внутри круглых скобок.

Аргументы — это обычные переменные, которые еще не определены (undefined); их особенность в том, что они становятся чем-то определенным во время вызова функции.

Рассмотрим функцию:

function greeting() {
    let name = “Andrew”;
    console.log(`Hello ${name}`);
}

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

Изменим нашу функцию так, чтобы вместо name = “Andrew” мы могли использовать name, равное любому числу.

Для этого нам нужно name вынести в аргумент, а функцию вызывать как greeting(“Maks”) или greeting(“Andrew”) и тд.

function greeting() {
    let name = “Andrew”;
    console.log(`Hello ${name}`);
}
function greeting(name) {
    console.log(`Hello ${name}`);
}


greeting(“Andrew”); // Hello Andrew
greeting(“Maks”) // Hello Maks

Аргументов может быть больше одного и они разделяются запятой:

function calc(a, b) {
    return a + b;
}
calc(2, 3); // 5
calc(23, 42) // 65

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

calc(2) // 2 + undefined -> NaN

Функции. Аргументы. Проверка.

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

 

function calc(a, b) {
    if (typeof a !== “number” || isNaN(a)) return console.log(“error”);
    if (typeof b !== “number” || isNaN(b)) return console.log(“error”);
 
    return a + b;
}

calc(); // error
calc(23) // error

Функции. Аргументы. Значения по умолчанию.

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

function calc(a = 0, b = 0) {
    if (typeof a !== “number” || isNaN(a)) return console.log(“error”);
    if (typeof a !== “number” || isNaN(a)) return console.log(“error”);
   
    return a + b;
}

calc(); // 0
calc(23) // 23

Функции. Аргументы. Arguments.

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

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

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

function foo() { return arguments; }
foo(); // []
foo(1, 2, 3); // [1, 2, 3]
foo(‘Hello’, ‘By-by’); // [‘Hello’, ‘By-by’]
foo( {car: ‘Lexus’, price: 64000} ); // [ {car: ‘Lexus’, price: 64000} ]

Полезные ссылки:

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

 

Полезные ссылки:

Функции высшего порядка

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

Функция принимает другую функцию в качестве аргумента:

function highFunction( func ) {
    func();
}
highFunction( function () { console.log(123) } ) // 123

 

Функция возвращает другую функцию в качестве результата:

function anotherHighFunction() {
    return function () { return 123 }
}

В этом случае anotherHighFunction вернет другую функцию:

anotherHighFunction() → function () { return 123}

Так как возвращаемый результат — функция, её тоже можно вызывать:

anotherHighFunction()() → ( function () { return 123} )()

 

function processString(string, handler) {
    let newString = ‘’;


    for (let i = 0; i < string.length; i++) {
        newString += handler(string[i]);
    }

    return newString;
}
processString(‘Hello, easycode!’,
function (symbol) {
    return symbol.toUpperCase();
});
processString(‘Hello, easycode!’,
function (symbol) {
    return symbol + ‘_’;
});
processString(‘Hello, easycode!’,
function (symbol) {
    return symbol.charCodeAt();
});

Полезные ссылки:

This

Методом объекта будем называть ключ объекта, который содержит функцию:

const furniture = {
    type: 'chair',
    transform: true,
    price: 9,
    getPrice: function () { return furniture.price}, // object method
    getType: function () { return furniture.type} // object method
}
furniture.getPrice(); // 9
furniture.getType(); // 'chair'

JavaScript работает внутри браузера, то есть браузер является как бы окружением для языка.
Любая JS функция также запущена в некотором окружении, вот некоторые из них:

функция запущена как есть в глобальном потоке:

    function foo() {};
    foo();

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

функция запущена как метод объекта:

const user = {
    age: 45,
    getAge: function() { return user.age}
}
user.getAge();


В этом случае функция запущена как метод объекта — то есть объект как бы окружает функцию.

Окружение, в котором выполняется функция, называется контекстом вызова функции (функция вызвана в контексте некоторого окружения). 

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

Внутри любой функции можно получить доступ к объекту, который вызвал функцию.
Специальное ключевое слово this является ссылкой на этот объект (this ещё называют объектом контекста).

Можно сказать, что this — это ссылка на объект, который вызывает код в данный момент.
this — всегда некоторый объект (или null).

В строгом режиме работы функции (‘use strict’) при запуске функции в глобальном окружении this равно undefined (а не window).

Не важно, как была создана функция — this всегда определяется во время вызова функции.

const product = {
    price: '15.2 $',
    getPrice: getPrice
}

function getPrice() { return parseFloat(this.price) }

product.getPrice(); → this в данном случае — ссылка на product

getPrice(); → this в данном случае — ссылка на глобальный объект window

Приём chaining или цепочка — это когда можно вызывать методы объекта через точку, строя цепочку вызовов:
someObject.method1().method2().method3().method1().method3()…

[3,1,2].sort().join();

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

const myObject = {
    foo: function () { /*...*/ return this;},
    boo: function () { /*...*/ return this;}
}

myObject .foo(); // вернёт this ---> myObject: { foo: function..., boo: func... }
myObject .foo().boo().foo().boo()...

Итак, функцию можно вызвать:

  • в глобальном контексте foo();
  • как метод объекта object.foo();

Существует третий способ вызвать функцию — принудительно указать функции, какой использовать контекст (передать this внутрь функции).

This. Call.

Метод call() вызывает функцию с указанным значением this и индивидуально предоставленными аргументами.

const info = { price: 10 };
function getPrice() {return this.price;}

getPrice.call(info); // 10

function getPriceWithDiscount(discount) {
    return this.price * (100 - discount) / 100;
}
getPriceWithDiscount.call(info, 10); // 9

This. Apply.

Метод apply() вызывает функцию с указанным значением this и
аргументами, предоставленными в виде массива.

const info = {price: 10};

function getPriceWithDiscount(discount, currency) {
    return this.price * (100 - discount) / 100 + currency;
}

getPriceWithDiscount.apply(info, [10, '$']); // 90$

This. Потеря контекста.

Потеря контекста - ситуация, когда функция вызывается с другим
контекстом, отличным от ожидаемого:

const obj = { test: 111, foo: function () { return this.test } };
obj.foo(); // 111

const boo = obj.foo;
// boo = function () { return this.test }

boo(); // this = window, так как функция запущена в глобальном окружении

Метод bind() создаёт новую функцию, которая при вызове устанавливает в качестве контекста выполнения this предоставленное значение. Bind также позволяет привязывать к функции аргументы

func.bind(context, arg1, arg2...)
let user = {uName: 'John', getName: function () {return this.uName}};
user.getName(); // 'John'
let getName = user.getName;

getName(); // undefined
getName = user.getName.bind(user);
getName(); // 'John'

Полезные ссылки:

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

Функции-стрелки (стрелочные функции, функции-ракеты) — новый синтаксис для создания функциональных выражений.

Особенности arrow function:

  1. более лаконичный синтаксис;
  2. отсутствие псевдомассива аргументов arguments;
  3. лексическое определение this;
  4. не могут использоваться в качестве конструкторов (с оператором new);
  5. не могут использоваться для создания генераторов.

Arrow function всегда создается с помощью function expression и не имеет имени (анонимная). Для создания arrow functions не используется ключевое слово function и вместо этого используется fat arrow (жирная стрелка) =>

Объявление стрелочной функции

const foo= () => { return 2 + 3; } → 
// function foo() { return 2 + 3; }

const boo = () => { 
  const number = Math.random();  return number; 
} → 
/* function boo() { 
  const number = Math.random(); return number; 
} */

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

const test = () => 2 + 3; 
// function test() { return 2 + 3; }

Однако если в функции более одной строки, то следует использовать традиционный синтаксис:
const test = () => { 
  const pi = Math.PI;
  return pi * 2; 
}

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

const getModule = (number) => { return Math.abs(number); }

const getModule = number => { return Math.abs(number); }

const getModule = number => Math.abs(number); 

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

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

const getTextInfo = (text) => {
  return { length: text.length, isEven: !(text.length % 2) };
}
getTextInfo('ola!'); // {length: 4, isEven: true}

const getTextInfo = text => ({ length: text.length, isEven: !(text.length % 2) });
getTextInfo('hello'); // {length: 5, isEven: false}

Функции стрелки как callback

Функции-стрелки очень удобно использовать в качестве коллбэков, например, при переборе массива. Сравните:

[0,1,2,3,4,5,6,7].filter(function (number) { return number % 2});
// [1, 3, 5, 7]


[0,1,2,3,4,5,6,7].filter(number => number % 2);
// [1, 3, 5, 7]


[0,1,2,3].map(number => ({ digit: number}));
// [ {digit: 0}, {digit: 1}, {digit: 2} ...]

[0,1,2,3,4,5,6,7].sort((prev, next) => prev - next);

document.body.addEventListener(‘click’, (e) => {// some actions});

Функции стрелки. Arguments.

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

Проблему отсутствия arguments в стрелочных функциях можно решить путём сочетания функций-стрелок с rest оператором:

const printParams = (...props) => console.log(props);
printParams('test', 123); // ["test", 123]


В данном случае переменная props будет истинным массивом, так как rest оператор всегда возвращает нативный массив (не псевдомассив).

Функции стрелки. this.

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

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

const user = { age: 45,
                            getAge: () => { return this.age; }
                      };
user.getAge(); // undefined


В данном случае объект user создавался в глобальном пространстве, следовательно, в момент создания объекта this являлся глобальным объектом (Window).

Полезные ссылки:

Перебирающие методы массивов

Метод forEach() выполняет указанную функцию один раз для каждого элемента в массиве.

Метод filter() создаёт новый массив со всеми элементами, прошедшими проверку, задаваемую в передаваемой функции.

Метод every() проверяет, удовлетворяют ли все элементы массива условию, заданному в передаваемой функции.

Метод some() проверяет, удовлетворяет ли хоть какой-нибудь элемент массива условию, заданному в передаваемой функции.

Метод map() создаёт новый массив с результатом вызова указанной функции для каждого элемента массива.

Метод reduce() применяет функцию к аккумулятору и каждому значению массива (слева- направо), сводя его к одному значению.

Метод Sort

По умолчанию сортирует элементы массива как строки:

['d', 'a', 'b', 'c'].sort() // ["a", "b", "c", "d"]
[10, 2, 15].sort() // [10, 15, 2] → т.к ‘10’ < ‘15’ < ’2’

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

[…].sort( function (prev, next) { return …} )

Функция в методе sort будет выполнена для каждой пары элементов внутри массива (текущий-следующий или предыдущий-следующий). Функция, передаваемая в метод sort может вернуть три возможных значения:

  • отрицательное — в этом случае prev будет сдвинут в начало массива, а next станет следующим после prev
  • положительное — в этом случае prev будет сдвинут правее (prev и next поменяются местам)
  • ноль — оставить элементы без изменений

Для сортировки по числовому значению следующий код можно упростить:

[10, 2, 15].sort(function (previous, next) {
    if (previous < next) return -1;
    if (previous > next) return 1;
}) // [2, 10, 15]

[10, 2, 15].sort(function (previous, next) {
    return previous - next;
}) // [2, 10, 15]

Полезные ссылки:

Замыкание

Замыкания — это функции, ссылающиеся на независимые (свободные) переменные. Другими словами, функция, определённая в замыкании, «запоминает» окружение, в котором она была создана.

Существует три типа кода в ECMAScript:

  • глобальный код ( window и все его свойства и методы );
  • код функции ( function foo() { …some code… } );
  • eval код ( eval(‘1 + 1’) ).

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

Замыкания. Контекст исполнения.

Контекст исполнения — это абстрактная сущность, к которой относятся все доступные переменные функции, её область видимости и “владелец” функции (this)

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

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

Лексическое окружение (lexical environment) — скрытый объект в программе, где хранятся все переменные (моё вольное определение термина).

Стек вызовов — это структура данных, устроенная по принципу LIFO (Last In, First Out — последним вошёл, первым вышел). Новые элементы можно помещать только в верхнюю часть стека, и только из неё же элементы можно изымать.

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

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

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

Лексическое окружение содержит два компонента:

  1.  Запись окружения (environment record) — место, где хранятся объявления переменных и функций.
  2. Ссылка на внешнее окружение (reference to the outer environment) — ссылка, позволяющая обращаться к внешнему (родительскому) лексическому окружению. Это — самый важный компонент, с которым нужно разобраться для того, чтобы понять замыкания.

Замыкания. Примеры.

function makeCounter() {
    let counter = 0;
    return function () {
        return ++counter;
    }
}

let counterInstance1 = makeCounter(),
counterInstance2 = makeCounter();

counterInstance1(); // 1
counterInstance1(); // 2
counterInstance2(); // 1
counterInstance2(); // 2
const getHello = function (text) {
     text = text || 'Hello, ';
     return function (name) {
         var greeting = text + name;
         return greeting;
     }
}
const hello = getHello('Здрасьте, ');


hello('Easycode'); "Здрасьте, Easycode"
hello('дамы и господа'); "Здраствуйте, дамы и господа"

Полезные ссылки:

Модули

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

Пример примитивного модуля:

const counter = { i: 0, inc: function () {return ++i;} }
counter.i; // 0
counter.inc(); // 1

Однако в данном примере имеется доступная снаружи переменная (ключ) i.
Что если мы не хотим предоставлять доступ к переменной i ?

const counter = { i: 0, inc: function () {return ++i;} }
counter.i; // 0
counter.inc(); // 1

const counter = (function () {
    let i = 0;
    return function () { return ++i; }
})();


counter; // function () { return ++i; }

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

const price = (function () {
    let discount = 0, price = 0;
    

    function setDiscount(value) { discount = parseInt(value);}
    

    function getPriceWithDiscount() {
       const discountFromPrice = price*discount /100;
       return price - discountFromPrice;
    }


    function setPrice(value) {price = value;}
    function getPrice() {return price;}


    return {
            getPriceWithDiscount: getPriceWithDiscount,
            setDiscount: setDiscount,
            setPrice: setPrice,
            getPrice: getPrice
    };
}());
--


Методы объектов про которые часто забывают

Деструктуризация

Деструктурирующее присваивание. Массивы.

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

const [ name1, name2 ] = [ 'John', 'Mike', 'Abraham', 'Piter' ];
console.log(name1, name2); //  John Mike

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

const [ , name2, name3 ] = [ 'John', 'Mike', 'Abraham', 'Piter' ];
console.log(name2, name3);  // Mike Abraham


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

Деструктурирующее присваивание. Массивы.

Если воспользуемся rest оператором, можем получить остаток массива в переменную:

const [ name1, name2, ...other ] = [ 'John', 'Mike', 'Abraham', 'Piter' ];
console.log(name1, name2); // John Mike
console.log(other); //["Abraham", "Piter"]


rest оператор в деструктурирующем приравнивании может быть только в конце выражения (перед закрывающей квадратной скобкой).

Если в деструктурирующем присваивании больше переменных, чем элементов в массиве, то последним переменным, которым “не хватило” элементов из массива, будет присвоено undefined:

const [ name1, name2, name3 ] = [ ‘John’, ‘Mike’ ];
console.log(name1, name2, name3); //  John Mike undefined
Однако этого можно избежать, используя значения по умолчанию:

const [ name1, name2, name3 = «Unknown» ] = [ ‘John’, ‘Mike’ ];
console.log(name1, name2, name3); // John Mike Unknown

Деструктурирующее присваивание. Объекты.

Работа с деструктурирующим присваиванием на объектах производится аналогично работе с массивами.
Для извлечения данных из объекта путем деструктуризации используются фигурные скобки {} и имена переменных, соответствующие полям объекта:

const figure = { width: 10, height: 12, type: 'square' };
const { width, height, type } = figure;

console.log(width, height, type); // 10 12 "square"

При необходимости использовать другие имена переменных в деструктуризации используется двоеточие:
const figure = { width: 10, height: 12, type: 'square' };
const { width: w, height: h, type: t } = figure;
console.log(w, h, t); // 10 12 "square"

Destructuring также позволяет использовать свойства по умолчанию:

const info = { type: 'html' };
const { type, size = 0} = info;
console.log(type, size) // 'html' 0

 

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

function getUser() {
  return { name: 'John', age: 15 };
}
const { name, age } = getUser();
console.log(name, age); // John 15

Destructuring позволяет присваивать и вложенные объекты, для этого используется вложенные фигурные скобки:

const element = {
  name: 'div',
  attributes: { className: 'box', title: 'info' }
};


const { name, attributes: { className, title } } = element;
console.log(name, className, title);
// “div” “box” “info”

Destructuring позволяет комбинировать присваивание объектов, внутри которых вложены массивы:

const element = {
  name: 'div',
  children: [ { name: 'span' }, { name: 'em' } ]
};
const { name: parentEl, children: [ child1 ] } = element;
console.log(parentEl, child1); → 
// “div” Object {name: "span"}

 

Можно деструктурировать сущности любой сложности:

const element = {
  name: 'div',
  children: [ { name: 'span' }, { name: 'em' } ]
};
const { children: [ { name } ] } = element;
console.log(name); // "span"

Деструктурирующее присваивание. Функции.

Destructuring можно использовать внутри функции при работе с аргументами:

const user = {  name: 'Louis', surname: 'Watkins', gender: 'Male', age: 50 };

function getBaseInfo( { name, age } ) {
  return `name - ${name}; age - ${age}`;
}
getBaseInfo(user); // "name - Louis; age - 50"

 

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

function getBaseInfo( { name = 'John', age = 18} ) {
  return `name - ${name}; age - ${age}`;
}
getBaseInfo({}); // "name - John; age - 18"

Однако вызов без аргумента приведёт к ошибке:
getBaseInfo(); → 
Uncaught TypeError: Cannot destructure property `name` of 'undefined' or 'null'

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

function getBaseInfo( { name = 'John', age = 18} = {} ) {
  return `name - ${name}; age - ${age}`;
}
getBaseInfo(); // "name - John; age - 18"

 

Полезные ссылки:

 

Object descriptor

 

Полезные ссылки: