JS :: Types Inside. Часть 1. Теория [Draft]

Всем привет.

Коллеги, недавно заново открыл для себя иерархию типов и внутреннюю структуру данных JavaScript. До этого момента был уверен в своих знаниях. Но после изучения исходного кода движка JavaScript (написанного на C), стало понятно, что заблуждался. На самом деле всё немного по другому. Собственно, решил с вами поделиться своими открытиями. Спасибо ребятам с DOU, которые ткнули носом, разрушили иллюзии и заставили своим напором, всё таки углубиться, в до селе казавшуюся понятной и понимание которой не вызывало у меня сомнений, иерархию типов и внутреннюю структуру данных JavaScript. Это ещё раз говорит (мне, надеюсь и другим, кто учится на чужом опыте, в данном случае моём) - что не надо быть черезчур самоуверенным и лучше лишний раз открыть мануал или посмотреть в source code, вместо того, что бы опираться на свои догадки и умозаключения. Давайте отбросим иллюзии и будем опираться на факты !

Хочется отметить, что данная статья - заслуга многих людей, помогавших мне своими советами и критикой. За что им огромное спасибо !

Часть 1. Теория


Двоичная система счисления

Двоичная система счисления - это позиционная система исчисления с основанием 2. Что значит "с основанием 2" ? А это значит что количество цифр этой системы счисления равно двум: 0 и 1. Для примера в десятичной системе счисления их 10 (цифр): 0, 1, 2, ..., 9. 
Каждое число в двоичной ситеме счисления - это последовательность разрядов, в каждом из которых: нуль или еденица.
Каждый разряд в двоичной системе счисления называется битом.
Биты могут группироваться в байты. 1 байт, это последовательность из 8-ми бит.
Байты в КиБайты, МиБайты, ГиБайты и т.д.

Память компьютера

Физически

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

Возможность чтения/записи

  • ROM - Read Only Memory. Память, содержание которой не предназначено для частых изменений, а возможно и лишено впринципе такой возможности (одни наз в такую память что-то записали на заводе и изменить без специального оборудования это её содержание не возможно). Как правило прошивка устройства, BIOS и другие системные программы или данные
  • RAM - Randon Access Memory. Память, произвольного доступа. Тоесть данный вид памяти позволяет как записывать в неё данные так и читать из из неё

Кратковременное/долговременное хранение

  • Оперативная память которая сохраняет своё содержимое, до тех пор, покаместь на неё подаётся ток (покаместь компьютер включен)
  • HDD или SSD. Хранит своё состояние после прекращения питания
  • Внешние носители, такие как: оптические, гибкие и другие диски, флеш память и так далее. Носители данного вида памяти долговременно хранят своё состояние (нескольо лет или десятков лет) и позволяют переносить информацию между компбютерами
Но подробно, в данной статье, мы здесь остановимя на памяти оперативной.

Физически, оперативная память компьютера - это микросхема или набор микросхем построенных на базе TTL или подобной логике.
Как уже было сказано, основное предназначением памяти - это хранение информации. Это касается и памяти компьютера.
Соответственно, каждая микросхема может хранить информацию, однако достаточно специфическую. 
Внутри микросхему памяти можно разбить на ряд блоков транзисторнрй или другой логики, каждому их которых можно задать - будет ли он проводить ток или нет (запись). 
Так же можно считать, проводит ли конкретный блок ток или нет (чтение).
Таким образом, у нас выкристализовались следующие термины:
Ячейка памяти: набор транзисторной или другой логики, которому можно задать, будет ли он проводить ток или нет. И считать, проводит ли данный блок ток или нет.
Значение ячейки памяти: как уже было сказано, фактически у каждой ячейки может быть два заначения: проводит она ток или нет.
Операция над ячейкой памяти: тоже нектороый набор транзисторной логики, управляемый одним из контактов микросхемы. Управление происходит посредством подачи на этот контакт напряжения или его отсутвтия. И соответственно определяет или записи в ячейку или чтение из неё.
Поскольку микросхемы были придуманы, чля того что бы компактно поместить в небольшом объёме большие эллектрический цепи, то легко догадаться, что в микросхеме находится большое количество цепей транзисторной логики и соответственно ячеек памяти.
Поэтому введём ещё один термин: адрес ячейки памяти.
Адрес ячейки памяти: уникальная конфигурация напряжений (только два значеиня: есть напрядения или нет) на проводах - так называемой шине адреса. Представьте что у микросхемы есть к примеру два контакта, на каждый из которых мы можем либо подать напряжение либо нет.
И если мы имеем 2 провода, то, мы можем получить 4 уникальных конфигурации напряжения на этих проводах: 
  1. На обоих проводах нет напряжения
  2. На первом проводе есть напряжение, на втором нет
  3. На первом проводе нет напряжения, на втором есть
  4. На обоих проводах есть напряжение
Итак, каждая такая уникальная конфигурация - адрес ячейки памяти. А контакты (провода) на которых мы можем задать эту конфигурацию физически - шина адреса.

Подведём небольшой итог

Микросхема памяти - это массив ячеек памяти, каждая из которых имеет свой уникалный адрес и состояние, которое может быть или измененно на одно из следующих: проводит ток или нет. Либо считано, в зависисомти от текущей операции над ячейкой памяти.
Как следствие иммет следующие контакты:
  1. Контакты ардесации конкретной ячейки памяти. Путём задания либо наличия либо отсутствия напряжения на них, мы задаём конкретную ячейку внутри микросхемы над которой будет произведена одна из операций: запись или чтение
  2. Контакт данных. Контакт либо урпавляющий тем какой значение запишется в ячейку памяти, в случае операции записи. Либо сигнализирующий о проводимости адресуемой ячейки памяти (на этом контактне как в либо будет напряжение (если сооветствующая ячейка проводит ток) либо нет (в противном случае)), в случае операции чтения. В зависимости от операции, либо записывет (меняет состояние ячейки) - будет ли данная ячейка проводить ток или нет. Или наоборот, считывает (читает состояние ячейки) - проводит ли данная ячейка в данный момент ток или нет
  3. Контакт операции над ячейкой памяти. Отсутствие или наличие напряжения на нём, сигнализирует о текущей операции - запись в ячейку памяти или чтение из неё.

Логически

Как мы уже говорили, адрес ячейки памяти - это конфигурация напряжений (есть или нет) на контактах микросхемы, отвечающих за адресацию ячеек памяти. 
Давайте теперь свяжем полученные знания с двоичной системой исчисления. Это делается для того, что бы в дальшейшем оперировать двоичными числами, вместо токов и напряжений. Что более удобно и так же даёт ряд других приемуществ.
Итак, если каждый контакт из последовательности контактов, задающих адрес ячейки памяти, проассоццировать с соответствующим своему номеру разрядом двоичного числа - битом. 
А наличие или отсутствие напряжения на контакте с двоичным значением нуля или еденицы в соответствющем контаку разряде.
То адрес ячейки памяти можно записать в виде двоичного числа, где каждый разряд (бит) - соответствует контакту из последовательности контактов формирующих адрес, а значение в этом разряде - наличию или отсутствию напряжения на этом контакте.
Так же, мы поступим и с содержимым ячейки: если транзисторная цепь не проводит ток, то поставим этому состоянию цепи в соответсвие двоичный нуль, в противном же случае, если она проводит ток - еденицу.

Исходя из этого, становится очевидным, что логически, оперативую память компьютера можно предствить в виде таблицы из двух колонок. 
В первой колонке находится адрес ячейки памяти. А во второй её значение.

Адрес                                   Значение
(В данном конкретном случае 8 бит)      (В данном конкретном случае 1 бит)
--------------------------------------------------------------------------
00000001                                1
00000010                                0
00000011                                0
00000101                                1
........ .

Кодирование / декодирование данных

Как мы уже говорили, данные - это стрктурированная информация.
А исходя их описанного выше, двоичного устройсва памяти компьюетра, очевидно, что для того что бы сохранить данные, отличные от двоичного числа, в памяти компьютера, их необходимо преобразовать из исходного формата в двоичный - закодировать. 
То есть например число 10 нужно как-то преобразовать - закодировать в двоичное, предствить в виде последовательности нулей и едениц, которая потом с лёгкостью может быть помещена в память компьюетра. 
Это касается любых данных, строки "Hello World", числа с плавающей точкой, даты и так далее. Все они должны быть преобразованы каким то образовм в двоичное предсвавление, и тогда их можно сохранить в памяти компьютера.
Как вы уже успели догадаться, одни и те же данные могу быть представлены по разному. Например иноформаци о курсе валют может быть представлена в виде таблицы колонками: дата, покупка, продажа. Так и в виде графика. Но фактически это одна и та же информация, только по разному представленная.
То же касается и хранения данных в памяти компьютера. А именно для того что бы их там сохранить, их нужно представить в таком виде, в котором они с лёгкостью могут быть помещены в память компьютера, а именно в последовательности нулей и едениц, которые соотествуют проводимости/не проводимости транзисторных цепей. Перейдя на логический уровень - в виде двоичных чисел.
Итак, давадим определение. 
Двоичное кодирование - это преобразование данных в двоичное представление.
Обратная операция, преобразование закодированных данных из двоичного представления в их изначальный формат - называется операцией декодирования данных.
Одно и тоже двоичное число - последовательность из 8-ми бит (одного байта) 0100 1100 является десятичным числом 76, если её интепретировать как десятичное число или символом "L", если её интерпретировать как ASCII символ. 
Соответственно, что бы правильно интерпертирвать (декодировать), двоичное число - содержимое некоторой области памяти копьютера, нужно знать, покрайней мере, какого типа значение закодировано в двоичный формат и записано в эту область. Зная это мы можем декодировать - совершить обратное преобразование из двоичного числа, в изначальный формат. данных (тот формат из которого они были преобразованы в двоичное число). 

Типы данных

Итак, что же такое в принципе, без привязки к какому либо конкретному языку программирования, тип данных. Тип данных - это описатель, который сообщает о том, что именно из себя представляют данные. Это целове число со знаком или без, это строка или символ  и так далее.
Давайте теперь попытаемя применить это понятие к JavaScript, . Давайте последовательно, шаг за шагом, разберёмся в том какие на самом деле существуют типы данных непо

Данные

Данные - это информация, как абстрактная (число 10), так и конкретная ("У меня етсь 10 ябок"). В обоих случаях мы имеем дело с числом 10. Но в первом случае, это просто некторое абстрактное число, которое не привязано ни как какому смысловому контексту. И не имеет отражения в физической реальности. Во втором случае - это тоже число 10, но которое привязано к некоторому факту из физической реальности. Такому, как наличие у меня в данный момент 10 яблок. 
На самом разницы в обработке и хранении абстрактной и конкретной информации компьютером не существует. Число 10 в обоих случаях будет храниться и обрабатываться одним и тем же образом. Поэтому мы опустим данное различие и дадми простые определения информации и данным.
Очевидно что информация - это то, чем оперирует наше сознание. И либо имеющая (конкретная, как в случае с фактом о наличие у меня 10-ти яблок) либо не имеющая (абстрактная, как в случае числа 10 без контекста) отражение в физической реальности. В любом случае информация о объете не является самим объектом. Это образ объекта, или его описание, но не лн сам. 
А, данные в свою очередь - это информация, хранящаяся в памяти компьютера, как оперативной, так и посятонной. 
Надеюсь это понятно.

Данные внутри компьютра

Но, внутри, "под капотом", как и в любом другом высркруровневом языке программирования, происходят преобразования из числовы, строковых, объектных и других - понятных человеку данных, в их внутренне представление - в двоичные - в последовательности нулей и едениц.

Днные в JavaScript

Как и в любом высокоуровневом языке программирования - данные в JavaScipt могут быть "простых" числовымых или символьных типов. Так же эти данные "простых" типов могут группироваться в объекты.


Переменная в JavaScript

Переменная - это ассоциация группы бит (внутреннего представления данных, таких как целое число, строка и других) и некоторой символьной строки - имени переменной, понятной человеку. Внутреннее представление данных native типов в JavaScript - это 32 бита информации.
Согласитесь, запомнить: 01001101 01001111 01101001 111011100 намного сложнее чем строку "myNumber".
Собственно код ниже "под капотом" проводит ассоциацию между некоторым внутренним представлением целого со знаком числа 10 и именем переменной "myNumber".

  var myNumber = 10; // Internal representaion: 00000000 00000000 00000000 00001101

В отличие от языков, компилируемых в машинный код, таких как C/C++, где некоторому имени (имени переменной) ставится в соответствие некоторый адрес в памяти, в JavaScript дело обстоит немного по другому. Каждая функция имеет свой variables object который содержит key-value пары, где key - имя переменной, а value - её данные. Т.е

var number = 1;

создаст в этом variable object новое свойство с именем "number" и проассоциирует с этим именем некие 32 бита - данные переменной, которые эквивалентны числу 1.

 Я здесь сознательно пишу "данные переменной", а не "значение переменной". Потому что в отличие от C / C++ / C# / Java и многих других языков, в JavaScript с именем переменной ассоциируется не только её значение, но ещё и тип, что и есть данные переменной. 

Если в C написать:

int number = 1;

то в стек запишется (в случает если в используемом компиляторе C под данную платформу, длина int равна 32-ум битам) двоичное:


Что является эквивалентом целго десятичного числа 1.

В JavaScript всё обстоит немного по другому. Давайте посмотрим, что произойдет "под капотом", если написать:

var number = 1;

В текущем variable object создастся cвойство с именем "number" которому проассоциируются следующие 32 бита двоичных данных: 


Как мы видим,эти данные нельзя рассматривать, как эквивалент дестятичного целого числа равного еденице, так как это десятичное целое число 3. Но, как мы уже сказали, JavaScript ассоциирует с переменной (или именем свойства объекта), не только значение, но и тип. Поэтому я и пишу "данные" переменной/свойства, а не "значение". В данном случае, первый младший бит, равный еденице, говтоит, что все отсальные биты (31 бит) - есть целое со знаком: 


Как мы видим в JavaScript целое со знаком имеет не 32, а 31 бит под хранения значения.

Внутреннее представление данных в JavaScript

Теперь давайте посмотрим более детально, что из себя представляют данные JavaScript.
Все данные можно разделить на две группы: это данные native типов и данные объектных типов JavaScript.
Native типы - это те базовые, нативные типы, которые "зашиты" в ядро движка JavaScript и которые являются фундаментом для всех остальных типов в том числе объектных. 
На них мы сейчас и остановимся. Давайте подробно рассмотрим их внутреннюю структуру.  
Для начала, данные native типов - это всегда группа из 32-ух бит (4 байта). 



Рассмотрим, как данные различных native типов, кодируются этими 32-мя битами.

Undefined. Если старшие 2 бита выставлены в единицы, а все остальные 30 бит выставлены в 0 (эквивалент -2 x 2^30, что выходит за пределы int31) то эти данные характеризуются типом undefined (его внутренне название в Си-шных исходниках JavaScript движка: JSVAL_VOID).



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



boolean. Если младший бит выставлен в ноль, а второй и третий биты в единицу - полезное значение данного типа есть значение true или false (0 или 1).



string. Строка, точнее указатель на первый символ Null terminated C string.



dobule. Если первый и третий младшие биты данных выставлены в ноль, а второй младший бит в единицу, то эти данные содержат значение числа с плавающей точкой двойной точности.



object. И наконец, если все три младших бита выставлены в нули - остальные биты этих данных содержат ни что иное как ссылку на объект. Объектные типы будут рассмотрены ниже.



Или специальное значение null (все оставшиеся 29 бит выставлены в ноль). Которое сообщает о том, что значение данных ссылочного типа - ссылка, никуда не ссылается.



Опять же, хочется внести ясность. И показать различие между данными undefined типа и ссылочного типа object со значением null. 
Во первых, это в принципе разные типы. Посмотрите на дескрипторы этих типов - они разные. У undefined это первые два старших бита единицы, а все остальные биты нули. А у object - это первые три младших бита нули, а все остальные биты - это адрес - ссылка не объект.
Во вторых, у данных undefined типа, вообще нет никакого значения внутри - только дескриптор типа . А данные ссылочного типа могу либо содержать значение - ссылку на объект либо не содержать (все биты занчения-ссылки выставлены в ноль, что и есть по сути null-ом). В этом разница. И это надо понять и хорошо запомнить.

Две парадигмы типов JavaScript

Native / Object парадигма

Native типы - как уже было сказано ранее это типы задающие формат простых, базовых данных. Таких как булеан, целое число, число с плавающей точкой, ссылка на первый символ Null terminated C string и ссылка на объект. Как правило полезные значения этих типов хранятся непосредственно в данных типа, но не всегда как в случае string и object.

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

Value / Reference парадигма

Value - это типы, значения которых хранятся непосредственно в самих данных переменной или значения свойства объекта. Как правило это типы малой сложности и соответственно размера. Значение которых умещаются в несколько байт, от 1 до 4-ёх. И это почти все native типы данных (за исключением string и object - данные которых содержат не сами значения этих типов а ссылки на них).

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

Cистема типов JavaScript

Итак, давайте совершим погружение в систему типов JavaScript.

Условно типы JavaScript можно разделить на следующие группы и подгруппы:
  • Undefined - тип, данные которого не содержит в себе никакого полезного значения. Ни значение value типа данных ни ссылку на объект в куче. Аналог void в C / C++
  • Native types
    • Value - типы, данные которых содержат непосредственно значение
    • Reference - типы, данные которых хранят ссылку на объект в куче (на первый символ Null terminated C string в куче, или на первый байт экземпляра объекта объектного типа в куче)
  • Object types  - типы сложной структуры, и абсолютно все ссылочного типа. Их экземпляры всегда хранятся в куче

Карта типов


Таблицы типов
Давайте перечислим все типы принадлежащие каждой из групп.

Undfined

Название типаОписаниеtypeofinstanceof[[Class]]
undefined Не может содержать внутри себя ничего: ни значение простого типа ни ссылку на объект в куче'undefined'--

Native types

Имя типаОписаниеtypeofinstanceof[[Class]]
booleanThe predefined values: true / false'boolean'--
int31A signed 31 bit integer number'number'--
doubleA signed float point number with a double precision'number'--
stringThe Null terminated C string
Really reference to the first character of the string in the heap
'string'--
objectReference to the object in the heap'object'--

Как мы видим оператор typeof JavaScript, для данных обоих числовых типов:
  • целочисленного 
  • с плавающей точкой двойной точности
возвращает одно и тоже значение - 'number'. Хотя дескриптор типа у данных этих типов различный. Это одна из особенностей типов данных JavaScript, которую надо понять и запомнить.

Object Types (объектные типы)

Имя типаОписанияtypeofinstanceof[[Class]]
BooleanThe boxing object type for the native type: boolean'object'+'Boolean'
NumberThe boxing object type for the native types: int31 and double'object'+'Number'
StringThe boxing object type for the native type: string'object'+'String'
DateThe internal JavaScript object type that represent a Date'object'+'Date'
ArrayThe internal JavaScript object type that represent an Array'object'+'Array'
ArgumentsThe internal JavaScript object type that represent an Arguments'object'+'Arguments'
RegExpThe internal JavaScript object type that represent a RegExp'object'+'RegExp'
MathThe internal JavaScript object type that represent a Math'object'+'Math'
ObjectThe  internal JavaScript object type that represent an Object'object'+'Object'
FunctionThe internal JavaScript object type that represent a Function'function'+'Function'
DOM objectsThe internal JavaScript set of the object types for the DOM manipulations. In different browsers - there are different types (but with the same interfaces). Browser related'object'+Browser related
Примечание. Данные native object типа могут как содержать ссылку на экземпляр объекта одного из перечисленных объектных типов, так и пустую ссылку, никуда не указывающую (null или zero reference). Однако null не является одним из внутренних типов, это возможное значение внутри данных native object типа. Значение ссылки равное нулю (все биты reference равны нулю), что означает что ссылка никуда не ссылаетсяЭто надо чётко понимать во избежание недоразумений. Существует заблуждение, что null - это отдельный тип JavaScript. На самом деле как сказано выше - это не так, не попадайтесь на эту "удочку". Это заблуждение связано с незнанием внутренней организации типов данных JavaScript, в частности native object типа.

Глядя не приведённые таблицы, можно увидеть следующие закономерности:
  1. typeof возвращает значение отличное от 'object' только для переменных: типа undefined, native типов: boolean, int31, double, string и особого объектного типа - Function. Во всех остальных cлучаях, включая null, возвращаемое значение - 'object'. Что есть вполне закономерно и предсказуемо, так как все оставшиеся типы являются объектными. 
  2. instnaceof работает только для объектных типов
  3. Внутреннее свойство [[Class]] (о нём речь пойдёт ниже), есть только у экземпляров объектных типов и его значение соответствует имени соответствующего объектного типа JavaScript (String, RegExp и так далее)

Boxing & Unboxing

Начнём из далека. У создателя языка JavaScipt была цель создать прототипно-ориентированный (стиль объектно ориентированногосценарный язык программирования. Коим JavaScript и является.
Так вот, одним из принципов ООП есть инкапсуляцияИнкапсуляция - это принцип состоящий в том, что объект должен содержать в себе (инкапсулировать) как данные, так и методы оперирующие этими данными. Так же он должен скрывать всё своё содержимое и давать доступ только к публичным членам. Давайте остановимся на первом: инкапсуляции данных и методов оперирующие этими данными. Вернёмся к native type в частности string. Как мы видим это просто ссылка на набор символов в куче, заканчивающихся ASCII 0 символом. Ни о каких методах работы со строкой речи не идёт. Так вот, для этого и были введены Boolean, Number и String объектные типы, которые внутри, в одном из своих свойств или внутренней переменной хранят ссылку на native type данные. А так же инкапсулируют методы и свойства для работы с данными этого типа. В данном примере c данными native string (методы и свойства экземпляра типа String: substr, substring, length...).
Механизм "оборачивания" данных native типа в соответствующий ему объектный - называется boxing. И соответственно, распаковка объекта, извлечение данных native типа из объектного: unboxing.

Давайте разберём, что происходит за кулисами этого кода:
  var myString = 'This is a my string',
      mySubstring = myString.substr(5);
1. В кучу записывается последовательность символов 'This is a my string' с нулевым окончанием
2. В variables object создаётся свойтво с именем "myString" с типом native string (младшие три бита XXX...100), в остальные биты которой записывается адрес в куче на первый символ созданной на шаге 1 Null terminated C string
3. Происходит boxing, то есть создаётся объект типа String в котором создаётся свойство с типом данных native string и со ссылкой на первый символ, скопированной из переменной myString
4. У созданного экземпляра объекта типа String, происходит вызов метод substr cо значением первого параметра: 5 в контексте созданного экземпляра объекта типа String
5. Результатом выполнения метода substr являются native string данные (не имеющая имени, а просто 32 бита, описывающих содержание) с адресом первого символа полученной подстроки. С которыми и ассоциируется в результате имя "mySubstring" в variables object.

Хочется обратить внимание, что оба: int32 и double value types "обёрнуты" одним и тем же ссылочным типом: Number. 

Таблица ассоциаций native types и boxing Object types
Value typeBoxing reference type
booleanBoolean
int31Number
doubleNumber
stringString

[[Class]]

Каждый экземпляр одного из объектных типов JavaScript, содержит свойство [[Class]] в котором находится имя внутреннего типа JavaScript, экземпляром которого является данный объект. Просьба не путать native тип данных и тип объекта.
Native тип данных - это набор бит описанных ранее и сообщающих о том какое значение содержится в данных, а так же сами данные или ссылка на них. 
А тип объекта - это сведения о внутренней структуре самого объекта (его  конструкторе, свойствах, методах и так далее).
Целочисленные данные типа int31 - это 32 бита, младший бит которых- это описатель типа (xxx...1), говорящий о том, что тип значения внутри этих данных - int31, и остальные 31 бит содержащие значение - само число.
Таким образом переменная - это 32 бита, содержащие тип значения и само значение (или ссылку на него).
В тоже время само понятие объекта подразумевает наличие класса (или типа), экземпляром которого он, этот объект, является. Например объекты могут быть внутренних объектных: String, Number. RegEx и других типов. Так вот данные этих объектов хранятся в куче (свойства, методы и так далее), а в с теке (или свойстве другого объекта) хранятся данные типа native object, которые хранят ссылку (адрес) на этот объект внутри себя в виде значения reference. 
Разновидностью внутреннего объектного типа так же является тип Object. Который позволяет создавать экземпляры заданной разработчиком структуры. При помощи заданного разработчиком метода - конструктора.
Пример:

// Create a new method inside a global context with a "MyClassConstructor" name
function MyClassConstructor()
{
  // _this - the Internal variable. 
  // this - a reference to the current context.
  var _this = this; 
  // Add a custom property with the name "numberPropertyto the current instance.
  this.numberProperty = 10; 
  // Add a the custom method with the name "sqr" to the current instance.
  this.sqr = function()
  {
   return _this.numberProperty * _this.numberProperty;
  }
}
// Next line of the code do the next:
// Create an instance of the Object internal type
// Internally set "constructor" Function type property of the created instance to the reference to the "MyClassConstructor" method
// Internally copy the MyClassConstructor.prototype to the "_proto_" Object type property of the created instance
// Call the MyClassConstructor method with a context as the created instance. (This means that 'this' keyword inside a "MyClassConstructor" method called with a new keyword, will reference to the created instance).
var myClassInstance = new MyClassConstructor(); 
В данном случае тип переменной myClassInstance - native object. Которая содержит ссылку на адрес в куче - экземпляр внутреннего объектного типа Object, созданного движком во время интерпретации ключевого слова JavaScript - "new".

Получить имя класса, содержащееся во внутреннем свойстве объекта [[Class]], можно следующим образом: 
  var className = Object.prototype.toString.call(obj);



NaN

Часто слышу недовольство: "почему же они не сделали NaN отдельным типом". Отчасти могу понять это недовольство, но такова реальность и тут уже ничего не поделаешь.
Итак, давайте для себя проясним, что такое на самом деле NaN.
Для этого выполним этот код в браузере:
  var nanType = Object.prototype.toString.call(NaN);
  alert(nanType);
получим результат - '[object Number]'. Что говорит о том что NaN, не что иное как объект внутреннего объектного типа JavaScript - Number. Как правило Number является обёрткой над native int31 или native double данными, но не в этом случае. NaN переводится как Not a Number и возвращается кодом в том случае, если данная операция должна была вернуть данные одного из числовых типов, но по тем или иным причинам, результатом оказалось не число (например деление на 0, не может вернуть число, так как с математической точки зрения - это не допустимо).
То есть обычно, Number является "обёрткой" int31 или dobule, но в данном случае, внутри нет никакого числового значения. А есть некий внутренний признак, что это Not a Number (NaN), а не число. Объект такого типа можно определить методом глобального контекста: isNaN, который вернёт true для NaN, а в противном случае false.

Function

Этот внутренний объектный тип является особенным по отношению к другим объектным типам. И особенность его заключается в том, что он инкапсулирует информацию о некотором выполняемом коде. Коде который может быть выполнен внутри среды выполнения JavaScript. B cозданный с использованием ключевого слова function. Или созданием экземпляра внутреннего объектного типа Function.

Итоги

Хочется подвести итог сказанному. Целью данной статьи, было сформировать у читателя понимание таких фундаментальных понятий JavaScript как типы данных. Дать правильную иерархию типов. Насколько это удалось судить не мне. В любом случае буду рад любой конструктивной критике и советам по улучшению, упрощению материала, для более ясного его понимания и быстрого усвоения. Все отзывы можно присылать на e-mail: rostislav.nikitin@gmail.com.

Так же в процессе исследования системы типов и переменных в JavaScript, мной была осознана проблема излишней сложности для понимании данной тематики разработчиками, не изучавшими указатели, ссылочные типы и другие низкоуровневые принципы и походы применяемые при низкоуровневом программировании. Поэтому пришла идея написать эту статью. А так же реализовать некоторый универсальный подход, в виде JavaScript библиотеки. Упрощающей работу с типами за счёт ясной архитектуры самой библиотеки, скрывающей всю витиеватость внутренней реализации системы типов JavaScript. В тоже время дающую мощное средство, предоставляющее возможность получения максимальной информации о типах данных переменных и свойств объектов. В следующей части будет описана данная библиотека, её внутренний дизайн и примеры использования. Надеюсь Вам было не скучно.

Дальше будет...
Комментарии закрыты

Page List

Calendar

<<  Сентябрь 2019  >>
ПнВтСрЧтПтСбВс
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

Большой календарь

Month List