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

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

То, что уже пугало меня, стало еще более запутанным из-за некоторых вещей, таких как оператор typeof, возвращающий «object» для переменных, назначенных массивам.

Если все в javascript было объектом, значит ли это, что массивы являются чем-то более чистым объектами, чем другие типы данных, которые предположительно также были объектами на самом высоком уровне?

Я также заметил, что не могу получить доступ к методам объекта, таким как freeze() или entries(), для строк, функций, илимассивов (из которых Я понял, что это как-то более чисто объекты, чем другие типы данных)!

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

Итак, давайте снова вернемся к тому утверждению, которое мы так часто слышим:

«Все в javascript является объектом».

Во-первых, это просто неправда. Не все в javascript является объектом, хотя он может вести себя как объект. Итак, что же значит быть объектом и что значит вести себя как объект?

Быть объектом…

На очень фундаментальном уровне все языки программирования состоят из структур данных и типов, которые могут относиться к определенному способу организации, обработки, извлечения и хранения данных в памяти. Если читатель еще не знаком с такими понятиями, я бы рекомендовал дополнить этот пост в блоге кратким введением в типы данных и структуры. В Javascript у нас есть доступ к следующим необъектным типам данных:

Эти типы данных, наряду с реальными объектами, являются тем, на что смотрит оператор typeof при определении типа данных конкретного значения. Еще раз хочу подчеркнуть, что ни один из вышеперечисленных типов данных не является объектом. Когда мы объявляем переменную как строку, она сохраняется в памяти как строковый тип данных. Не как предмет. Однако когда мы сохраняем эту строку, мы просто сохраняем само значение без каких-либо методов или свойств, таких как .length или .concat(), к которым мы так привыкли иметь доступ всякий раз, когда мы работаем со строками. Основным преимуществом этого является то, что гораздо дешевле хранить только само значение каждый раз, когда нам нужно иметь дело со строкой, чем хранить как строку, так и все связанные с ней свойства и методы для каждой отдельной строки в нашем программа. Как же тогда, спросите вы, у нас есть доступ к этим свойствам и методам? Что ж, именно здесь возникает идея вести себя как объект, и поэтому фраза «все в javascript является объектом» часто используется в разговорной речи.

Вести себя как объект…

Когда мы вызываем string.concat(), наш строковый примитив ведет себя только как объект, потому что JavaScript автоматически упаковывает значение в объект-оболочку, когда для него вызывается метод. Нам не нужно знать технические подробности работы автобокса, чтобы понять, что в такие моменты объект-обертка наследует все методы и свойства String.prototype. С точки зрения дизайна это выгодно, поскольку связывание объекта-оболочки с уже существующим прототипом, содержащим эти методы, обходится намного дешевле, чем создание и хранение самого объекта. После запуска метода Javascript удаляет оболочку, и строка снова становится простым строковым примитивом, хранящимся в памяти.

Но как насчет массивов?

Вы могли заметить, что массивы не включены в приведенный выше список примитивных типов данных в Javascript. Вы также можете вспомнить из примера с typeof, что массивы были единственным литералом, не являющимся объектом, который мы пытались использовать, который на самом деле давал нам тип «object». Возможно, вы также помните, что я говорил, что оператор typeof возвращает любой из указанных выше типов данных или объект. Это связано с тем, что, в отличие от других типов данных, массив фактически является объектом в Javascript, где ключ для каждого значения в массиве представлен его индексом. Когда мы обращаемся к элементу в массиве по его индексу, то, что происходит за кулисами, по сути эквивалентно доступу к значению в литерале объекта по его ключу/свойству.

Ура! Теперь у нас есть прочная концептуальная основа того, как то, что мы называем примитивными типами данных, принципиально отличается от таких структур данных, как массивы, наборы и карты, причем первые ведут себя как объекты только тогда, когда к ним вызываются методы, связанные с их прототипом, и последние являются реальными объектами, хранящимися в памяти как объекты.

Однако на этом этапе моего понимания у меня все еще оставалось одно особое непрекращающееся сомнение. Если массивы действительно являются объектами в javascript, почему они не наследуют такие методы, как замораживание () или записи (), которые можно вызывать для литералов объекта?

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

Давайте посмотрим здесь поближе. Если мы проверим Object.prototype, мы фактически не увидим вышеупомянутых методов. Однако у нас есть несколько доступных нам методов, таких как hasOwnProperty(). Оставив в стороне вопрос о том, где расположены функции freeze() и entry() и как и чем к ним можно получить доступ, мы знаем, что если мы правильно понимаем наследование прототипов, мы должны иметь возможность вызывать такие методы, как hasOwnProperty(), для всего, что наследует из Object.prototype.

Давайте проверим это, попробовав вызвать метод hasOwnProperty для массива.

Эй, это работает! Мы вызываем метод для массива, который был определен и унаследован от Object.prototype, а не от Array.prototype. Вроде аккуратно.

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

Этот метод должен работать и со строкой, так как когда мы вызываем метод для строки, он получает оболочку объекта, связанную с прототипом String, который наследуется от прототипа Object. Как мы узнали ранее, несмотря на то, что строка хранится как примитивное строковое значение, она упаковывается как объект, когда мы хотим вызвать для нее метод. Давай посмотрим что происходит…

И это тоже работает!

Так почему же такие методы, как entry() и freeze(), не работают так, как мы могли бы ожидать?

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

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

Источники: https://developer.mozilla.org/en-US/docs/Glossary/Primitive https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures https:/ /developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor