Вы наверняка где-то читали, готовясь к техническому интервью: «в JavaScript все является объектом! Даже классы! Даже Функции!».
JavaScript — один из (если не «самый») самый популярный язык, используемый для веб-приложений и серверных приложений; это часть стандарта ECMAScript, и каждая новая спецификация вводит дополнительные функции, поддерживает ее в актуальном состоянии и добавляет новые интересные функции — однако рутинная работа, жесткая основа, которая делает JavaScript таким универсальным, правда ли, что это просто созданные объекты?
Истина и проста, и запутана. По своей сути JavaScript разделяет значения на два типа: примитивные значения и объекты. Хотя JavaScript часто кажется местом, где все значения действуют как объекты, существуют существенные различия и конкретные ситуации, когда эти различия имеют значение.
Во-первых, давайте сделаем примитивы и объект действительно четкими.
Примитивы или примитивные значения — это основные типы данных в JavaScript, которые не являются объектами и не имеют методов. JavaScript распознает семь примитивных типов данных: String
, Number
, BigInt
, Boolean
, Undefined
, Null
и Symbol
. Если вы используете TypeScript, вы знаете, о чем я говорю (или даже PropTypes в React).
Хотя эти примитивные типы по своей сути не обладают свойствами или методами, JavaScript включает эквиваленты объектов — String
, Number
, BigInt
, Boolean
и Symbol
— которые обертывают их соответствующие примитивные аналоги. Эта концепция называется «бокс».
let str = "Hello, World!"; // this is a string type console.log(str.length); // 13 - this is a number type
В приведенном выше фрагменте кода str
является примитивом строки, и когда мы ссылаемся на str.length
, JavaScript автоматически "упаковывает" str
во временный объект String
, обращается к свойству length
, а затем отбрасывает временный объект, создавая иллюзия примитива, действующего как объект. Но на самом деле это не так!
В отличие от примитивов, объекты в JavaScript представляют наборы свойств, каждое из которых является связью между ключом и значением.
А вот это мы все знаем. Значение может быть любым значением JavaScript, кроме null
и undefined
, и объект может представлять различные структуры данных, такие как массивы, функции и регулярные выражения, среди прочего (да, они все объекты!).
Объекты можно создавать с помощью:
- литералов объектов;
- конструкторов,
- метода Object.create
.
Они очень похожи, но в чем тогда разница?
Наиболее важным отличием является то, что примитивы в JavaScript неизменяемы: хотя JavaScript позволяет вам обращаться к свойствам и методам примитивов, как если бы они были объектами, вы не можете изменять существующие свойства или добавлять новые к примитивам. Вот пример:
let str = "Hello, World!"; // initiate a string str.property = "test"; // trying to add a property, as we would do in an obj console.log(str.property); // undefined - not allowed, but no error
Здесь JavaScript не выдает ошибку, когда мы пытаемся присвоить новое свойство строковому примитиву str
! Однако, поскольку примитивы неизменяемы, присваивание не вступает в силу, а последующий доступ к свойству возвращает undefined
, так что эффект «я вас слышу, но нет, я этого делать не буду».
Все еще не уверены? Позвольте мне попытаться воздействовать на эти строки другим способом, притворившись, что они являются массивом (то есть объектом).
let greetings = "hello John"; greetings[0] = "H"; // Attempt to change the first letter to 'H' console.log(greetings); // "hello", not "Hello"
Другая уникальная характеристика примитивов заключается в том, что они сравниваются по значению, а не по ссылке, как это происходит с объектами. Это может быть более интуитивно понятно, если я спрошу вас «эта кружка такая же, как эта», вы проверите две вещи на моей руке и сравните их. Это интуитивно, верно? Однако в JavaScript, если вы сделаете то же самое, сравнивая два свойства, как в следующем примере, ответ будет false
!
let person1 = { age: 18}; let person2 = { age: 18}; console.log(person1 === person2); // false
Они идентичны! Однако мы сравниваем ссылку на объект (ячейки памяти) вместо его свойств, и вот в чем хитрость. Однако сравнение двух примитивов таким же образом приведет к другому результату.
let myAge = 22; // I wish! let yourAge= 22; console.log(myAge === yourAge); // true
Это означает, что когда вы сравниваете два примитива с помощью ===
, JavaScript проверяет, имеют ли они одинаковое значение.
Время делать выводы
Итак, резюмируя:
- Примитивы являются неизменяемыми и имеют простые типы данных, в то время как объекты изменяемы и могут быть более сложными.
- Примитивы сравниваются по значению, а объекты сравниваются по ссылке.
- У примитивов нет свойств или методов, в отличие от объектов. Однако JavaScript позволяет примитивам получать доступ к методам их объектов-оболочек, создавая иллюзию того, что у примитивов есть методы (упаковка).
И теперь вы знаете, на что ответить в следующий раз, когда на собеседовании вам зададут этот каверзный и интересный вопрос 😉