Skip to content

Instantly share code, notes, and snippets.

@mikaelbalin
Last active July 20, 2020 05:29
Show Gist options
  • Save mikaelbalin/ec2457ae64dfecba41b380ebbb66fd44 to your computer and use it in GitHub Desktop.
Save mikaelbalin/ec2457ae64dfecba41b380ebbb66fd44 to your computer and use it in GitHub Desktop.

JS Help

Types:

  1. String
  2. Boolean
  3. Number
  4. BigInt
  5. Symbol
  6. null
  7. undefined
  8. object

Engine: byte stream stream decoder 👉 tokens parser 👉 Abstract Syntax Tree interpreter 👉 byte code optimizing compiler

Event Loop: callstack queue event loop

map, filter and slice return a new array find returns an element reduce returns a reduced value push returns length

TypeErrors get thrown when a value is not of the expected type. SyntaxErrors get thrown when you've written something that isn't valid JavaScript, for example when you've written the word return as retrun. ReferenceErrors get thrown when JavaScript isn't able to find a reference to a value that you're trying to access.

bind() only returns a copy of the bound function call() executes the bound function immediately

Falsy values:

  1. undefined
  2. null
  3. NaN
  4. 0, -0
  5. '' (empty string)
  6. false

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

abstract - создается класс, в нем методы которые работают с абстракными данными, и их надо переназначить в потомке readonly - нельзя изменять у экземпляров

static - нет у инстансов

Keywords

  • infer - returns a type from a generic

Для вставки специальных знаков в строки, нужно записывать / перед ними, называется escaping:

"He said \"Hi!\""; // He said Hi!
'He said "Hi!"'; // He said Hi!
`He said \'Hi!\'`; // Wrong punctuatin, but just for example

Список таких знаков (escape sequences) вместе с символами ****\u *hhhh* в кодировке Unicode с шестнадцатеричным (hexadecimal) кодом:

Unicode character value Escape sequence Meaning Category
\u0008 \b Backspace
\u000C \f Form feed White space
\u000A \n Line feed (new line) Line terminator
\u000D \r Carriage return Line terminator
\u0009 \t Tab White space
\u000B \v Vertical tab White space
\u0027 ' Single quotation mark (')
\u0022 " Double quotation mark (")
\u005C \ Backslash
\u0020 Space White space
\u00A0 Nonbreaking space White space
\u2028 Line separator Line terminator
\u2029 Paragraph separator Line terminator
\uFEFF Byte order mark White space

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

let x = 4;
x += 2; // x equals 6

let y = 4;
y -= 2; // y equals 2

let z = 4;
z *= 2; // z equals 8

Инкремент и декремент

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

Инкремент ++ увеличивает на 1:

let i = 2;
i++;      // более короткая запись для i = i + 1.
console.log(i); // 3

Декремент -- уменьшает на 1:

let i = 2;
i--;      // более короткая запись для i = i - 1.
console.log(i); // 1

Вызывать эти операторы можно не только после переменной i++ (постфиксная форма), но и перед переменной ++i (префиксная форма).

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

let i = 1;
let a = ++i;

console.log(a);
// -> 2

Вызов ++i увеличит переменную, а затем вернёт ее значение в a. Так что в a попадёт значение i после увеличения.

Постфиксная форма i++ отличается от префиксной ++i тем, что возвращает старое значение, бывшее до увеличения. В примере ниже в a попадёт старое значение i, равное 1:

let i = 1;
let a = i++;

console.log(a);
// -> 1

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

let i = 0;
i++;
++i;
console.log(i);
// -> 2

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

let i = 1;
console.log(2 * ++i);
// -> 4

let i = 1;
console.log( 2 * i++);
// -> 2,  выполнился раньше но значение вернул старое

console.log(i);
// -> 2

console.log(2 * i++);
// -> 4

console.log(i);
// -> 3

Экспоненциальный оператор

Новый в ES7 оператор ** позволяет возводить в степень:

const square = (x) => x**2;
square(2);
// -> 4

Выражения, утверждения и среда

К примеру если выражение (expression - то, что производит значение) соотносится с обычным предложением, как его фрагмент, то утверждение (statement) соотносится с целым предложением. Программа представляет собой набор утверждений.

Самый простой пример утверждения это выражение с ; после него:

1;
!false;

Точку запятой можно ставить не всегда, но сложно отследить когда она нужна, по-этому лучше ставить всегда.

Среда (environment) - это набор переменных, которые определены. Системы JavaScript всегда помещают ряд полезных стандартных переменных в вашу среду.

Свойства

Instance (экземпляр объекта) - это отдельный случай (или объект) типа данных. Каждый экземпляр, например такой как строка Hello, содержит дополнительную информацию.

Почти все значения в JavaScript имеют свойства. Исключением является null и undefined:

null.length;
// -> TypeError: null has no properties

Можно вычислить количество символов в строке при помощи свойства .length:

console.log('Hello'.length);

А так же .log является свойством в выражении console.log.

Два типичных способа доступа к свойствам в JavaScript - это точка и квадратные скобки. Оба value.x и value[x] получают доступ к свойству value, но не обязательно к одному и тому же. Разница заключается в том, как они интерпретируется. При использовании точки, слово после точки является буквальным именем свойства. При использовании квадратных скобок выражение между скобками оценивается (evaluats) для получения имени свойства.

Таким образом, если название свойства name, нужно писать value.name. Если нужно извлечь свойство названное по имени значения заложенное в переменной i, нужно писать value[i]. Свойством может быть любая строка, но нотация с точкой (dot notation) работает только с правильными именами переменных. Таким образом, если нужно получить доступ к свойству, названному “2” или “John Doe”, нужно использовать квадратные скобки: value[2] или значение ["John Doe"].

На March 2, 2018 список всех свойств JavaScript задокументированных на MDN насчитывает 127 страниц.

Библиотеки

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

Библиотека [Math](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math) является одной из таких коллекций, содержащая математические методы. Можно испольховать метод .random(), чтобы сгенерировать случайное число:

console.log(Math.random()); // random number between 0 and 1

Можно задать число в пределах которого должна производится генерация просто умножив на него:

Math.random() * 50;

В этом случае ответ будет скорее всего десятичным числом. Для того чтобы округлить число, есть метод Math.floor():

Math.floor(Math.random() * 50);

Остальные методы библиотеки можно найти тут.

Curring

Процесс конвертации функции, которая принимает несколько аргументов в фукцию, которая принимает их одновременно:

const multiply = (a, b) => a * b;
multiply(3, 4);
// -> 12

const curriedMultiply = (a) => (b) => a * b; // стрелки это функции

// старая версия
var curriedMultiply = function curriedMultiply(a) {
  return function (b) {
    return a * b;
  };
};

// тоже самое, что и closure
curriedMultiply(3)(4);
// -> 12

const multiplyBy5 = curriedMultiply(5);
multiplyBy5(5)
// -> 25

Таким образом первая функция принимает параметр a и вызывает вторую функция, которая перемножает a и b.

Compose

Слияние двух функций воедино, для формирования третьей функции, в которой результат одной функции является аргументом для другой:

const compose = (f, g) => (a) => f(g(a));

const sum = (num) => num + 1;

compose(sum, sum)(5);

// 1. const compose = (f, g) => (a) => f(g(5));
// 2. const compose = (f, g) => (a) => f(6);
// 3. const compose = (f, g) => (a) => 7;

// старая версия, f & g становятся функциями
var compose = function compose(f, g) {
  return function (a) {
    return f(g(a));
  }
};

var sum = function sum(num) {
  return num + 1
};

compose(sum, sum)(5);

Generators

Generators are a special kind of function with the ability to pause itself, and resume later, allowing other code to run in the meantime.

The code decides that it has to wait, so it lets other code “in the queue” to run, and keeps the right to resume its operations “when the thing it’s waiting for” is done.

All this is done with a single, simple keyword: yield. When a generator contains that keyword, the execution is halted.

A generator can contain many yield keywords, thus halting itself multiple times, and it’s identified by the *function keyword, which is not to be confused with the pointer dereference operator used in lower level programming languages such as C, C++ or Go.

Generators enable whole new paradigms of programming in JavaScript, allowing:

  • 2-way communication while a generator is running
  • long-lived while loops which do not freeze your program

Here is an example of a generator which explains how it all works.

function *calculator(input) {
    var doubleThat = 2 * (yield (input / 2))
    var another = yield (doubleThat)
    return (input * doubleThat * another)
}

We initialize it with

const calc = calculator(10)

Then we start the iterator on our generator:

calc.next()

This first iteration starts the iterator. The code returns this object:

{
  done: false
  value: 5
}

What happens is: the code runs the function, with input = 10 as it was passed in the generator constructor. It runs until it reaches the yield, and returns the content of yield: input / 2 = 5. So we got a value of 5, and the indication that the iteration is not done (the function is just paused).

In the second iteration we pass the value 7:

calc.next(7)

and what we got back is:

{
  done: false
  value: 14
}
7 was placed as the value of doubleThat. Important: you might read like input / 2 was the argument, but that’s just the return value of the first iteration. We now skip that, and use the new input value, 7, and multiply it by 2.

We then reach the second yield, and that returns doubleThat, so the returned value is 14. In the next, and last, iteration, we pass in 100

calc.next(100)

and in return we got

{
  done: true
  value: 14000
}

As the iteration is done (no more yield keywords found) and we just return (input * doubleThat * another) which amounts to 10 * 14 * 100.

Объекты

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

Можно создать объект с парами ключ-значение (key-value), используя следующий синтаксис:

let restaurant = {
  name: 'Italian Bistro',
  seatingCapacity: 120,
  hasDineInSpecial: true,
  entrees: ['Penne alla Bolognese', 'Chicken Cacciatore', 'Linguine Pesto'],
  open: function() { // это и есть метод
    console.log('${this.name} is open!');
  }
};

Описание:

  1. let restaurant создает переменную restaurant, которая хранит объект.
  2. Объект создается в фигурных скобках {}.
  3. name, seatingCapacity, hasDineInSpecial, и entrees это все ключи.
  4. Каждый ключ разделен с его соответствующим значением двоеточием :.
  5. Каждая пара разделяется запятой ,.

Доступ к свойствам объекта

Наиболее распространенным способом доступа к значению ключа является использование точечной нотации (dot notation).

let restaurant = {
  name: 'Italian Bistro',
  'seating capacity': 120,
  hasDineInSpecial: true,
  entrees: ['Penne alla Bolognese', 'Chicken Cacciatore', 'Linguine pesto']
};

console.log(restaurant.entrees);
// -> ['Penne alla Bolognese', 'Chicken Cacciatore', 'Linguine pesto']

Другой способ доступа к значению ключа - это обозначение в виде скобок.

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

let restaurant = {
  name: 'Italian Bistro',
  seatingCapacity: 120,
  hasDineInSpecial: true,
  entrees: ['Penne alla Bolognese', 'Chicken Cacciatore', 'Linguine pesto']
};

console.log(restaurant['entrees']);
// -> ['Penne alla Bolognese', 'Chicken Cacciatore', 'Linguine pesto']

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

let meal = 'none';
let time = 12;
// We'll use military time for this example, counting hours 0-23.

const restaurantSpecials = {
 breakfast: 'The breakfast special is 20% off freshly squeezed orange juice',
 lunch: 'The lunch special is 10% off appetizers',
 none: 'There are no specials currently'
};

if (time < 11) { // 11 am
  meal = 'breakfast';
} else if (time < 17) { // 5 pm
  meal = 'lunch';
}

console.log(restaurantSpecials[meal]);
// -> The lunch special is 10% off appetizers

Добавление и измерение свойств

Объекты считаются изменчивыми (mutable), что означает, их можно изменить после создания. Даже если объект сохранен в переменной const, все равно можно добавлять и редактировать пары ключ-значение внутри него, не вызывая ошибки.

Пример:

const restaurant = {
  name: 'Italian Bistro',
  seatingCapacity: 120,
  hasDineInSpecial: true,
  entrees: ['Penne alla Bolognese', 'Chicken Cacciatore', 'Linguine pesto']
}

Можно добавить ключи appetizers и desserts следующим образом:

restaurant['appetizers'] = ['Fried Calamari', 'Bruschetta'];
restaurant.desserts = ['Homemade Tiramisu', 'Cannoli'];

Можно заменить значения, назначенные для appetizers и desserts новыми массивами:

restaurant['appetizers'] = ['Fried Calamari', 'Bruschetta', 'Caprese Salad'];
restaurant.desserts = ['Homemade Tiramisu', 'Canolli', 'Cheesecake'];

Методы

Когда объекты имеют пары ключ-функция, функция называется методом (method):

const restaurant = {
  name: 'Italian Bistro',
  seatingCapacity: 120,
  hasDineInSpecial: true,
  entrees: ['Penne alla Bolognese', 'Chicken Cacciatore', 'Linguine pesto'],
  openRestaurant: () => {
    return 'Unlock the door, flip the open sign. We are open for business!';
  },
  closeRestaurant: () => {
    return 'Lock the door, flip the open sign. We are closed.'
  }
};

console.log(restaurant.openRestaurant());
// -> Unlock the door, flip the open sign. We are open for business!

console.log(restaurant.closeRestaurant());
// -> Lock the door, flip the open sign. We are closed.

В примере созданы два метода .openRestaurant() и .closeRestaurant(), при помощи синтаксиса со стрелкой в объекте restaurant.

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

const restaurant = {
  name: 'Italian Bistro',
  seatingCapacity: 120,
  hasDineInSpecial: true,
  entrees: ['Penne alla Bolognese', 'Chicken Cacciatore', 'Linguine pesto'],

  openRestaurant() {
    return 'Unlock the door, flip the open sign. We are open for business!';
  },
  closeRestaurant() {
    return 'Lock the door, flip the open sign. We are closed.'
  }

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

const obj =  {
  player: 'bobby',
  experience: 100,
  wizardLevel: false
}

const player = obj.player;
const experience = obj.experience;
let wizardLevel = obj.wizardLevel;

Можно использовать новый синтаксис (destructuring):

const {player, experience} = obj;
let {wizardLevel} = obj;

player
// -> bobby

Так же можно задавать динамические свойства (dynamic properties), например:

const name = 'john snow';
const property = 'Property';

const obj = {
  [name]: 'Bob',
  [1 + 3]: 'result',
  [`get${property}`]() {
    return this[name];
  }
}

obj[name]
obj[`get${property}`]()
// -> "Bob"

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

const a = "Simon";
const b = true;
const c = {};

const obj = {
  a: a,
  b: b,
  c: c,
}

Теперь можно делать это так:

const obj = {a, b, c}

Дескриптор this

Можно создать методы, которые работают с данными внутри одного и того же объекта, например свойство hasDineInSpecial в методе .openRestaurant():

const restaurant = {
  name: 'Italian Bistro',
  seatingCapacity: 120,
  hasDineInSpecial: true,
  entrees: ['Penne alla Bolognese', 'Chicken Cacciatore', 'Linguine pesto'],
  openRestaurant() {
    if (hasDineInSpecial) {
      return 'Unlock the door, post the special on the board, then flip the open sign.';
    } else {
      return 'Unlock the door, then flip the open sign.';
    }
  }
};

console.log(restaurant.openRestaurant());
// -> ReferenceError: hasDineInSpecial is not defined

Ошибка происходит из-за того, что hasDineInSpecial вне области действия .openRestaurant().

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

const restaurant = {
  name: 'Italian Bistro',
  seatingCapacity: 120,
  hasDineInSpecial: true,
  entrees: ['Penne alla Bolognese', 'Chicken Cacciatore', 'Linguine pesto'],
  openRestaurant: function() {
    if (this.hasDineInSpecial) {
      return 'Unlock the door, post the special on the board, then flip the open sign.'
    } else {
      return 'Unlock the door, then flip the open sign.'
    }
  }
}

console.log(restaurant.openRestaurant());
// -> Unlock the door, post the special on the board, then flip the open sign.

this.hasDineInSpecial внутри объекта это тоже самое, что обращаться к restaurant.hasDineInSpecial вне объекта.


Значение this, при использовании в объекте, является сам объект. В Javascript this относится к объекту, который мы вызываем внутри.

Например:

let myObj = {
  name: 'Miti',
  sayHello() {
    return `${this.name} says hello!`;
  }
};

Это можно изменить, поменяв объект:

let yourObj = {
  name: 'Timer'
};

yourObj.sayHello = myObj.sayHello;
// Sets the sayHello method on yourObj to be the sayHello method on yourObj

Если вызвать yourObj.sayHello(), он вернет 'Timer says hello!'. this в примере вызван в объекте yourObj, который ограничен областью действия свойств внутри yourObj.

Область действия this с функциями со стрелкой наследуется от контекста. В обычных функциях this всегда относится к ближайшей функции, тогда как в функция со стрелкой эта проблема решена и больше не нужно писать var that = this.

Операторы delete и in

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

let anObject = {left: 1, right: 2};
console.log(anObject.left);
// → 1

delete anObject.left;
console.log(anObject.left);
// → undefined

console.log("left" in anObject);
// → false

console.log("right" in anObject);
// → true

Унарный оператор in сообщает есть ли у объекта то или иное свойство.

Методы объектов

Чтобы перечислить все свойства объекта можно использовать функцию Object.keys:

let obj = {
  a: 'Santa',
  b: 'Rudolf',
  c: 'Mr. Grinch'
}

console.log(Object.keys(obj));
// -> ["a", "b", "c"]

Object.keys(obj).forEach((key, index) => {
  console.log(key, obj[key]);
})

Функция Object.assign копирует все свойства из одного объкта в другой:

let objectA = {a: 1, b: 2};
Object.assign(objectA, {b: 3, c: 4});

console.log(objectA);
// -> {a: 1, b: 3, c: 4}

Новый синтаксис ES8 Новая функция Object.values позволяет получить значения свойств объекта:

let obj = {
  a: 'Santa',
  b: 'Rudolf',
  c: 'Mr. Grinch'
}

Object.values(obj).forEach(value => {
  console.log(value);
})

// ->
// Santa 
// Rudolf 
// Mr. Grinch 

C помощью Object.entries можно получить массивы, состоящих из пар ключ-значение свойств объкта:

let obj = {
  a: 'Santa',
  b: 'Rudolf',
  c: 'Mr. Grinch'
}

Object.entries(obj).forEach(value => {
  console.log(value);
})

// ->
// ["a", "Santa"]
// ["b", "Rudolf"]
// ["c", "Mr. Grinch"]

Getters и Setters

Методы getter и setter получают и устанавливают свойства внутри объекта. Существует несколько преимуществ использования этих методов для получения и установки свойств напрямую:

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

Setters Например:

let restaurant = {
  name: 'Italian Bistro',
  seatingCapacity: 120,
  hasDineInSpecial: true,
  entrees: ['Penne alla Bolognese', 'Chicken Cacciatore', 'Linguine Pesto']
}

Ключ seatingCapacity содержит значение 120. Если он изменится, нужно проверить его годность. Например, метод должен проверить, является ли поле seatingCapacity числом, равным 150, а не строкой 'one hundred fifty``'. Можно записать это в метод setter следующим образом:

let restaurant = {
  _name: 'Italian Bistro',
  _seatingCapacity: 120,
  _hasDineInSpecial: true,
  _entrees: ['Penne alla Bolognese', 'Chicken Cacciatore', 'Linguine pesto'],

  set seatingCapacity(newCapacity) {
    if (typeof newCapacity === 'number') {
      this._seatingCapacity = newCapacity;
      console.log(`${newCapacity} is valid input.`);
    } else {
      console.log(`Change ${newCapacity} to a number.`)
    }
  }
}

Описание:

  1. Нужно добавить имена свойств с символами подчеркивания. _ используется до названия свойства, чтобы обозначить, что его значение не должно быть изменено напрямую с помощью другого кода. Рекомендуется добавлять подчеркивания для всех свойств и создавать setters для всех атрибутов, к которым нужно получить доступ позже в своем коде.
  2. Метод setter set seatingCapacity() принимает newCapacity как переменную. Переменная newCapacity содержит новое значение, которое будет хранится в _seatingCapacity.
  3. Внутри setter .seatingCapacity() используется условный оператор (conditional statement), чтобы проверить, что переменная newCapacity с новым значением - это число.
  4. Если переменная число (правельный ввод), используется this._seatingCapacity для изменения значения, присвоенного _seatingCapacity.

Методы setter вызываются так же, как обычные свойства:

// Sets the _seatingCapacity value to 150
restaurant.seatingCapacity = 150;
// -> 150 is valid input.

Getters Getters используются для получения значений свойств внутри объекта:

let restaurant = {
  _name: 'Italian Bistro',
  _seatingCapacity: 120,
  _hasDineInSpecial: true,
  _entrees: ['Penne alla Bolognese', 'Chicken Cacciatore', 'Linguine pesto'],

  // setters получают значение и могут например проверять новое значение
  set seatingCapacity(newCapacity) {
    if (typeof newCapacity === 'number') {
      this._seatingCapacity = newCapacity;
    } else {
      console.log(`Change ${newCapacity} to a number.`)
    }
  },

  // getters служат для вывода значения в преобразованном виде
  get seatingCapacity() {
    console.log(`There are ${this._seatingCapacity} seats at Italian Bistro.`);
    return this._seatingCapacity;
  }
}

Метод getter вызывается так же, как свойство без метода:

restaurant.seatingCapacity = 150;
const seats = restaurant.seatingCapacity;

Классы

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

Constructor

Синтаксис классов и объектов схож, но есть один отличительный метод - конструктор (constructor). JavaScript вызывает constructor() каждый раз когда создается новый экземпляр объекта (instance) класса с использованием new:

class Dog {
  constructor(name) {
    this.name = name;
    this.behavior = 0;
  }
}

Описание:

  • Dog это название класса.
  • JavaScript будет вызывать (invoke) метод constructor() каждый раз когда создается новый экземпляр объекта класса Dog.
  • Метод constructor() принимает один аргумент, name.
  • Внутри constructor() используется дескриптор this. В контексте класса, this относится к экземпляру объекта этого класса.
  • После this.name, создается свойтво behavior, которое будет отслеживать количество раз, когда собака плохо себя ведет. Свойство behavior всегда инициализируется нулем.

Instance

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

class Dog {
  constructor(name) {
    this.name = name;
    this.behavior = 0;
  } 
}

const halley = new Dog('Halley'); // создает новый экземпляр Dog

console.log(halley.name); // выводит значение имени, сохраненное в halley
// -> "Halley"

console.log(halley instanceof Dog); // проверить является ли halley представителем класса Dog
// -> true

Описание:

  • Новая переменная halley будет содержать экземпляр класса Dog.
  • После класса Dog, используется дескриптор new чтобы создать новый экземпляр класса Dog. new вызывает constructor(), запускает код внутри него и затем возвращает новый экземпляр.
  • Строка 'Halley' передаеся в конструктор Dog, который устанавливает имя 'Halley' для свойства name.
  • И наконец значение сохраненное в ключе name в объекте halley выводит 'Halley' в консоль.

Методы

Синтаксис классов методов, setters и getters такой же как для объектов, за исключением того, что нельзя включать запятые между методами:

class Dog {
  constructor(name) {
    this._name = name;
    this._behavior = 0;
  }

  get name() {
    return this._name;
  }

  get behavior() {
    return this._behavior;
  }

  incrementBehavior() {
    this._behavior++;
  }
}

Вызов методов

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

В примере созданы два экземпляра, nikko and bradford:

let nikko = new Dog('Nikko'); // создать собаку Nikko

nikko.incrementBehavior(); // добавить 1 к поведению экзепляра nikko

let bradford = new Dog('Bradford'); // создать собаку Bradford

console.log(nikko.behavior); // -> 1
console.log(bradford.behavior); // -> 0

Наследование

Для примера сделан новый класс Cat. Он разделяет пару свойств (_name и _behavior) и метод .incrementBehavior() с классом Dog. Так же у класса Cat есть дополнительное свойство _usesLitter, которое содержит логическое значение, чтобы указать, может ли кошка использовать свой ящик для мусора 🐱:

class Cat {
  constructor(name, usesLitter) {
    this._name = name;
    this._usesLitter = usesLitter;
    this._behavior = 0;
  }

  get name() {
    return this._name;
  }

  get usesLitter() {
    return this._usesLitter;
  }

  get behavior() {
    return this._behavior;
  }

  incrementBehavior() {
    this._behavior++;
  }
}

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

С наследованием можно создать родительский (parent) класс, также известный как супер класс (superclass), со свойствами и методами, которые разделяют несколько дочерних (child) классов, также называемых подклассами (subclasses). Дочерние классы наследуют свойства и методы из своего родительского класса.

class Animal {
  constructor(name) {
    this._name = name;
    this._behavior = 0;
  }

  get name() {
    return this._name;
  }

  get behavior() {
    return this._behavior;
  }   

  incrementBehavior() {
    this._behavior++;
  }
  
  hello() {
    return 'Hello, I am ' + this._name + '.'
  }
}

Используя общие свойства и методы в родительском классе Animal, можно распространить их на подкласс Cat:

class Cat extends Animal {
  constructor(name, usesLitter) {
    super(name);
    this._usesLitter = usesLitter;
  }
}

Описание:

  • Дескриптор extends делает методы класса Animal доступными в классе cat.
  • Конструктор вызывается, когда создается новый объект Cat, который принимает два аргумента - name и usesLitter.
  • Дескриптор super вызывает конструктор родительского класса. В этом случае, super(name) передает аргумент имени класса Cat в конструктор класса Animal. Когда выполняется конструктор Animal, он устанавливает this._name = name; для новых экзепляров Cat.
  • Свойство _usesLitter уникально для класса Cat, и устанавливается только для конструктора Cat.

📝 Примечание: super вызывается в первой строке constructor(), а затем устанавливается свойство usesLitter во второй строке. В constructor() всегда надо вызывать метод super, прежде чем использовать this - иначе JavaScript выдаст ошибку (reference error).

Ниже создан новый экземпляр Cat и вызвано его имя, таким же образом, как и для класса Dog:

const bryceCat = new Cat('Bryce', false);

console.log(bryceCat._name); // -> Bryce

В результате класс Cat имеет доступ к Animal getters и методу .incrementBehavior().

Пример наследования:

bryceCat.incrementBehavior();
console.log(bryceCat.behavior); // -> 1

Описание:

  • Класс Cat наследует свойство _behavior, getter behavior, и метод .incrementBehavior() класса Animal.
  • Когда был создан экземпляр bryceCat, конструктор Animal установил значение 0 свойству _behavior.
  • Первая строчка вызывает наследуемый метод .incrementBehavior(), который увеличивает свойство _behavior в bryceCat на 1.
  • На второй строчке вызывается getter behavior.

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

Ниже добавлен getter usesLitter. Синтаксис создания getters, setters и методов такой же, как и в любом другом классе:

class Cat extends Animal {
  constructor(name, usesLitter) {
    super(name);
    this._usesLitter = usesLitter;
  }

  get usesLitter() {
    return this._usesLitter;
  }
  
  hello() {
    return super.hello() + ' I am a cat.'
  }
}

Добавился getter usesLitter в классе Cat, который возвращает значение, сохраненное в _usesLitter.

Для того, чтобы сделать дополнительный подкласс Dog:

class Dog extends Animal {
  constructor(name) {
    super(name);
  }
}

Данный класс Dog имеет те же свойства, getters, setters, и методы, что и класс Dog, который был сделан без наследования, и на четверть меньше.

Статические методы

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

Например класс Date — можно создать экземпляр Date чтобы представить любую дату, и вызвать статический (static) метод, такой как Date.now(), который возвращает текущую дату прямо из класса. Метод .now() статический, таким образом его можно вызвать прямо из класса, но не из его экземпляра.

Используя дескриптор static, можно создать статический метод generateName в классе Animal, который возвращает случайное имя при вызове:

class Animal {
  constructor(name) {
    this._name = name;
    this._behavior = 0;
  }

  static generateName() {
    const names = ['Angel', 'Spike', 'Buffy', 'Willow', 'Tara'];
    const randomNumber = Math.floor(Math.random()*5);
    return names[randomNumber];
  }
}

Из-за ключевого слова static, можно получить доступ к .generateName() добавив его только к классу Animal используя следующий синтаксис:

console.log(Animal.generateName()); // returns a name

Нельзя получить доступ к .generateName() из экзепляра класса Animal или экзепляров его подклассов:

const tyson = new Animal('Tyson'); 
tyson.generateName(); // TypeError

DOM

На самом базовом уровне веб-сайт состоит из HTML-документа. Браузер, используемый для просмотра веб-сайта, представляет собой программу, которая интерпретирует HTML и CSS и отображает стили, содержимое и структуру на странице, которую вы видите.

Помимо синтаксического анализа (parsing) стилей и структуры HTML и CSS, браузер создает представление документа, известного как объектная модель документа (Document Object Model). Эта модель предоставляет JavaScript доступ к текстовому содержимому и элементам документа веб-сайта в виде объектов.

Объект **document** Объект document это встроенный объект у которого есть много свойств (properties) и методов (methods), к которым можно получить доступ для изменения сайтов.

Разница между DOM и HTML-кодом Есть несколько различий между созданной браузером DOM и исходным кодом HTML:

  • DOM изменяется на стороне клиента JavaScript
  • Браузер автоматически исправляет ошибки в исходном коде

Манипуляция DOM

Селекторы элементов DOM структурирован как дерево объектов, называемых узлами (nodes), и узлы могут быть текстом, комментариями или элементами.

Берет Синтаксис селекторов Метод
ID #demo getElementById()
Class .demo getElementsByClassName()
Tag demo getElementsByTagName()
Первый найденный элемент querySelector()
Все элементы querySelectorAll()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment