Трудно за простото
Последния път се опитахме да се справим със следните неща:
- 1. Въпреки общоприетото мнение „всичко в JS е обект” не е така, установихме, че от 6 типа данни, с които разполага програмистът, цели 5 са примитивни и само един представлява обектен тип.
- 2. Научихме за обектите, че това е такава структура от данни, която съдържа двойки ключ-стойност. Стойността може да бъде всеки от типовете данни (и това ще бъде свойство на обекта) или функция (и това ще бъде методът на обекта).
- 3. Но примитивите не са обекти. Въпреки че можете да работите с тях като с обект (и това причинява погрешното схващане, че примитивът е обект), но ...
- 4. Променливите могат да бъдат декларирани както прости (буквално) (var a = 'str'), така и чрез функция конструктор (wrapper) (var a = new String('str')). Във втория случай вече няма да получим примитив, а обект, създаден от конструктора String(). (какво е новият магически оператор и какво е конструкторска функция, ще научим по-нататък).
- 5. Научихме, че можете да работите с него като с обект, като създадете обвивка върху примитивния елемент (нов String('str')). Това е тази обвивка, която интерпретаторът създава около примитива, когато се опитваме да работим с него като обект, но след извършване на операцията той се унищожава (следователно примитивът никога няма да може да запомни свойството, което му присвояваме a.test = 'test' - свойството test ще изчезне с обвивката).
- 6. Научихме, че обектите имат метод toString(), който връща низово представяне на обекта (за типа number valueOf() той ще върне числова стойност).
- 7. Разбрах, че при извършване на конкатенация или математически операцииоперации, примитивите могат да предефинират своя тип до желания. За да направят това, те използват функции за обвиване от техния тип, но без новия оператор (str = String(str)).(каква е разликата и как работи, ще говорим по-нататък)
- 8. И накрая, научихме, че typeof взема стойности от твърдо кодирана таблица (оттук идва друго погрешно схващане, базирано на typeof null //object).
Функциите на конструктора са като обекти, прототип.
Да започнем по ред. Помислете за привидно прост ред код.
3. Можете също така да кажете, че това е декларация на функция (DF) и така нататък, но останалото все още не е важно.

По подразбиране прототипният обект е "празен" (е, почти празен, но повече за това по-долу). По-горе казах, че всичко, което се намира в този обект, ще влезе в инстанцията и ще бъде достъпно и за потомците. Тоест по подразбиране (ако нищо не е добавено към прототипа), тогава екземплярът на „нищо“ няма да премине от функцията на конструктора „A“. Тоест при изпълнение на кода:
ще получим "нормален" (доколкото е възможно в JS) обект "a". JS вече има много вградени конструкторски функции. Това е например Number(), String() и т.н. Нека се откъснем от примера за момент и да поговорим за вградените функции на конструктора и за обектите като цяло.
Обекти (__proto__).

Нека да разгледаме пример, за да изясним:
конструктор.
аз винагипоставете думата (празно) в кавички, когато говорите ("празен" прототип). Например, когато създаваме конструктор функция функция A()<>, тогава се създава свойство на прототип с препратка към "празен" обект на прототип. Не точно. Все още има нещо в прототипа. Първо, тъй като, както казах, прототипът е „прост“ обект, тогава има свойството __proto__ с връзка към прототипа на функцията конструктор Object() (тя е тази, която създава всички „прости“, най-елементарни обекти), и второ, има свойството конструктор. Свойството конструктор се добавя там от интерпретатора, когато разбере, че се създава функция конструктор, а не просто обект.Като начало, нека допълним нашата първа фигура, като имаме предвид тези два факта.

Всичко, което е нарисувано в сиво, всъщност не ни трябва точно сега - това е за по-пълна картина. Нека се съсредоточим върху свойството конструктор. Както може да се види от фигурата, конструкторът сочи към самата функция на конструктора, за която първоначално е създадено това „съхранение“, този обект. Тоест появява се цикличност между свойството прототип на функцията конструктор и свойството конструктор на обекта прототип – те сочат един към друг обектите. Чрез свойството конструктор (ако то все още сочи към конструктор и свойството прототип на конструктора от своя страна все още сочи към оригиналния прототип), можете индиректно да получите препратка към прототипа на обекта: a.constructor.prototype.x. И можете да получите връзка към самата функция на конструктора и нейните свойства, които са присвоени не на прототипния обект, а конкретно на него. Например:
=<> — като функция конструктор (new Object()).
Страхотно, всичко изглежда на мястото си. Има „споделено хранилище“, родителят и детето имат връзки към това хранилище, ако свойството не е в самия екземпляр, тогава интерпретаторът ще отиде навръзката ще го търси в "споделено хранилище". Каква е уловката?? Да видим Пример2:
Изглежда, че всичко трябва да работи. Създадохме функция конструктор, зададохме свойството „споделено хранилище“ (прототип (чрез връзка)) (x), създадохме екземпляр, има свойство (x) - всичко е наред. След това напълно отменихме свойството на прототипа на родителя, като добавихме свойствата (x) и (y), за да посочим правилния конструктор. Всичко трябва да работи в „споделеното хранилище“ и двете от тези свойства, но не, (y) интерпретаторът не намира. wtf

Черен цвят - това е, което не се е променило след B.prototype = ; Червено - какво е премахнато Зелено - какво е добавено
Можете също да добавите малко за instanceof. Колкото и да е странно, но в този пример b1 ще принадлежи на функцията конструктор B, но b не. Всичко е много просто. Факт е, че instanceof търси да бъде изпълнено следното условие - така че обектът, посочен от връзката __proto__ (на което и да е ниво на веригата) (окръжност с числото 1) да е равен на обекта, към който се отнася прототипното свойство на желания родител (окръжност с цифрата 2) (сравнете черното и зеленото на фигурата). При черното това условие вече не е изпълнено, но при зеленото е. В нашия случай екземплярът (b) е прекъснал тази връзка, тъй като новото свойство на прототипа на желания родител (B) вече препраща към нов обект, а не както преди. Но за примера (b1), както виждаме, всичко е наред с това.
Няма да навлизам в дълбочина за това в тялото на функцията конструктор - повече за това в следващата статия. Единственото нещо, което ще кажа е, че това, когато се извиква функция като конструктор (чрез new), ще сочи към създадения екземпляр, а когато се извиква като функция, към глобален обект. Нека да разгледаме един пример:
Как знаете как е извикана функцията? Чрез нов или не? Това се прави многоПросто:
Приблизително по този начин се реализира механизмът за леене на типове. Когато изпълнява 1+'1' например, интерпретаторът третира + като конкатенация на низ и се опитва да прехвърли числото 1 в низа. Това се прави с имплицитно извикване на String(1) (без new). И в конструктора String е написана приблизително същата конструкция, както по-горе. Тоест, ако извикването е станало без new, просто върнете низа (имплицитно извикване на метода toString()). По този начин, без да се създават обекти, се извършва преобразуване на типа. Също така искам да добавя следното, за да добавите свойство към функция (а именно към функция, а не към прототип), трябва да се обърнете към него като към обект. Например
Това свойство няма да бъде достъпно за детето, тъй като не се намира в прототипа и детето има достъп само там. Но както се казва, „ако наистина го искаш, можеш“. Това е мястото, където свойството на прототипния обект, конструктор, идва на помощ. Както си спомняме, това се отнася до самата функция (освен ако, разбира се, това не е променено специално). След това, за да получите променливата val, трябва да получите достъп до нея по следния начин:
И тук можете да получите грант за тестов период на Yandex.Cloud. Необходимо е само да въведете "Habr" в полето "секретна парола".