Javascript имеет 8 типов данных:
null
undefined
string
number
boolean
bigint
symbol
object

Из этих 8 типов данных первые 7 являются примитивными. Что значит примитив? Примитивные типы данных определяются на самом низком уровне языка. Это означает, что они не могут быть изменены после их создания.

const name = "Pouria";

Переменная name содержит значение «Пурия», и изменить его невозможно:

name[0] = "H";
console.log(name); // Pouria

С другой стороны, объекты — это структурированные данные, которые могут состоять из данных других типов и даже из других объектов. Мы можем изменить значение всех этих подпеременных, также называемых свойствами. Действие по изменению содержимого объекта называется «мутация».

const person = {
  name: "Pouria",
  job: "developer",
  hobbys: ["reading", "walking", "knitting"],
};
person.job = "writer";
console.log(person.job); // writer

Примечание. Важно знать, что хотя объекты и изменяемы, их свойства могут быть примитивными. Итак, здесь person изменяемо, person.hobbys также изменяемо, потому что массивы — это частный случай объектов. Но person.name и person.hobbys[0] являются строковыми примитивами.

Примечание. Не путайте "мутацию" с "переназначением":

let name = "Pouria";
name = "John"; // This is reassignment

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

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

Теперь давайте посмотрим краткое введение в каждый из этих примитивных типов данных:

нулевой

Стандарт Ecmascript определяет его как примитивное значение, которое представляет собой преднамеренное отсутствие какого-либо значения object. Однако позже нулевая переменная может быть заполнена примитивом. Важной частью определения является то, что null представляет намеренное отсутствие значения.

const value = null;
console.log(typeof null); // object

Помните, что причина, по которой оператор typeof возвращает object вместо null, является исторической ошибкой в ​​Javascript. Поэтому никогда не полагайтесь на него, чтобы проверить, является ли переменная объектом или нет.

неопределенный

В отличие от null, которое является встроенным значением в Javascript (например, true или false), undefined является свойством глобального объекта (который в браузерах равен window).

typeof window.undefined === 'undefined'; // true

Это значение по умолчанию для переменной, которая создана, но еще не инициализирована:

let variable; // here, variable is undefined
variable = "Hello";

Чтобы проверить, является ли значение undefined, у вас есть несколько вариантов, но два наиболее распространенных:

// Pay attention that here, undefined is the property of global
// object
console.log(variable === undefined); // true
// Pay attention that here, 'undefined' is a string. 
// The "typeof" operator always returns string.
console.log(typeof variable === 'undefined'); // true

undefined отличается от null тем, что подразумевает отсутствие самой переменной. Это особенно важно при передаче параметров функциям с параметрами по умолчанию:

function sayHiTo(name = "Pouria") {
  console.log("Hi " + name);
}
sayHiTo(null); // Hi null
sayHiTo(undefined); // Hi Pouria

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

Нить

const variable = "Hello";
// OR
const variable = 'Hello';
// OR
const variable = `Hello`;
// OR (With a built-in function that returns strings)
const variable = String("Hello");
typeof variable === "string"; // true

Строковые переменные — это ноль или более символов, заключенных в одинарные или двойные кавычки или обратные кавычки. Одинарные и двойные кавычки в JS абсолютно одинаковы, но обратные кавычки или «шаблонные литералы» имеют больше возможностей, таких как прием многострочных строк и переменных (внутри ${}):

const name = "Pouria";
const variable = `My Name
is ${name}`;
console.log(variable);
// result:
// My Name
// is Pouria

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

Число

const number = 10;
// OR
const number = Infinity;
// OR
const number = NaN;
// OR
const number = 1e10; // the number after "e" represents the number of zeros
// OR
const number = 123.456;
// OR (With a built-in function that returns Numbers)
const number = Number(123.456);
typeof number === "number"; // true

Тип number в Javascript может быть любым десятичным числом. Javascript не различает целые и десятичные числа.

12.0 === 12; // true

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

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

NaN === NaN; // false
// To check if the value is NaN, use:
Number.isNaN(NaN); // true
// To compare to NaN values, use:
Object.is(NaN, NaN); // true

Значения больше примерно 1,8E308 (18 с 307 нулями после него) отображаются как Infinity.

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

логический

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

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

BigInt

Наибольшее значение, которое может безопасно хранить javascript, — это Number.MAX_SAFE_INTEGER, то есть 9007199254740991. Числа, превышающие это значение, небезопасно хранить, а это означает, что выполнение арифметических операций над ними обычно ошибочно.

Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2; // true

Чтобы хранить числа больше Number.MAX_SAFE_INTEGER, нам нужно использовать Big Integer или BigInt. BigInt — это просто целое число, за которым следует n.

typeof 12n === 'bigint'; // true
9007199254740991n + 1n === 9007199254740991n + 1n; // true
9007199254740991n + 1n === 9007199254740991n + 2n; // false

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

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

Символ

const variable = Symbol();
console.log(typeof variable); // symbol

Самое важное использование символов — это уникальные ключи объекта. Представьте, что вы пишете библиотеку и экспортируете этот объект:

const person = {
  name: "Pouria",
  sayHi: () => {
    console.log("Hi!");
  },
};

и представьте, что внутри вашего кода вы полагаетесь на метод person.sayHi. Но что, если пользователь библиотеки изменит ваш объект person? Тогда весь ваш код сломается.

До символов у нас было три способа предотвратить это:

1- Попросите пользователей не переназначать person.sayHi! Не хорошая идея :)

2- Использование длинного уникального идентификатора в качестве ключа:

const SAYHI_KEY = "3rfgneDEUFNtrg3n4";
const person = {
  name: "Pouria",
  [SAYHI_KEY]: () => {
    console.log("Hi!");
  },
};

Однозначно не чисто!

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

А вот и наш герой: symbol.

const SAYHI_KEY = Symbol();
const person = {
  name: "Pouria",
  [SAYHI_KEY]: () => {
    console.log("Hi!");
  },
};

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

Но самое классное в символах то, что их можно зарегистрировать в глобальном реестре символов и нам не нужно будет везде экспортировать и импортировать этот символ. Использование Symbol.for("key") ищет глобальный реестр символов с этим ключом; если он не существует, он создаст и вернет его, а если он существует, он вернет этот символ. (В отличие от вызова функции Symbol(), которая каждый раз создает новый символ).

A.js

const obj = {
  [Symbol.for("sayHi")]: () => {
    console.log("Hi!");
  }
};
export default obj;

B.js

import obj from "./A";
obj[Symbol.for("sayHi")](); // Hi!

Примечание. В функцию Symbol() можно передать необязательный строковый параметр. Это не ключ к этому символу, это просто описание, и оно не очень полезно. Если вы хотите зарегистрировать свой символ в глобальном реестре, используйте вместо него Symbol.for("keyName").

Подведение итогов

Мы говорили о 7 примитивных типах данных Javascript и о том, что их содержимое нельзя изменить (что означает, что они неизменяемы).

Это важные строительные блоки Javascript, но для большей гибкости и контроля над программой нам нужен способ их группировки. Здесь в игру вступает понятие «объекты».

Что дальше?

В этой статье мы увидели, что есть несколько встроенных функций, таких как String, Number, Boolean и…; но есть причина, по которой эти функции существуют, и именно так Javascript обрабатывает наследование и позволяет вам обращаться к таким методам, как (123).toFixed(2) и (“hello").toUpperCase(). Чтобы немного углубиться в объекты Javascript, как они строятся и как наследуют друг друга, вы можете прочитать эту статью.

Надеюсь, вам было интересно прочитать статью :)