TL; DR: я пытаюсь глубже понять JavaScript. Я думал, что сделаю это, прочитав серию книг You Don’t Know JS (YDKJS). Я размещаю здесь свои выводы из третьей книги Это и прототипы объектов. Дополнительную информацию можно найти в моей предыдущей публикации.
Вторник, 29 августа | 20:45
Это сайт вызова и стек вызовов
В книге this
определяется как «привязка для каждого вызова функции, полностью основанная на ее месте вызова… месте в коде, где функция вызывается (а не там, где она объявлена)».
В конце главы 2 книга дает действительно отличный обзор четырех правил, которые могут применяться для определения this
на основе сайта вызова функции.
Если вы плохо разбираетесь в this
JavaScript, я рекомендую прочитать эту главу.
Вкратце, это правила:
- Если функция вызывается с ключевым словом
new
, результатом будетthis
. - Если функция вызывается с _6 _, _ 7_ или
.bind()
,this
будет объектом, который передается в - Если функция вызывается для объекта,
obj.foo()
,this
будет объектом, для которого она была вызвана. - Если функция вызывается сама по себе,
foo()
,this
будет либо неопределенным (если в строгом режиме), либо глобальным объектом (если в нестрогом режиме)
Самый простой способ найти сайт вызова - посмотреть на стек вызовов в инструментах разработчика Chrome. Сайт вызова и, следовательно, точка, в которой был привязан this
, всегда находится в вызове непосредственно перед выполняющейся в данный момент функцией.
.связывать()
В книге даются действительно интересные подробности о том, что .bind()
делает под капотом.
Чего я не знал, так это того, что .bind()
«возвращает функцию, которая жестко запрограммирована для вызова исходной функции с this
заданным вами контекстом».
Стрелочная функция ES6 и это
Мы постоянно используем стрелочную функцию ES6 на работе. Мы даже будем использовать его в пользу .bind()
, поскольку он рассматривается как более современный подход.
Книга имеет несколько иную точку зрения на функцию стрелки.
В нем говорится, что вместо использования четырех стандартных this
правил, как описано выше, стрелочная функция по существу «отключает традиционный this
механизм в пользу более широко понимаемой лексической области видимости».
В нем говорится, что использование стрелочной функции похоже на запись _this = this
в функции. При этом вы фиксируете все, что this
функции во время ее вызова.
В нем говорится, что стрелочная функция появилась потому, что люди не совсем понимают или боятся использовать .bind().
Он говорит, что «по сути убегает от this
вместо того, чтобы понять и принять его».
Среда, 30 августа | 8:00 утра
Основные типы
Знаете ли вы, что в JavaScript существует 6 основных типов (и только один из них является объектом, поэтому все в JavaScript не является объектом)? Они называются простыми примитивами. Они есть:
- нить
- количество
- логический
- нулевой
- неопределенный
- объект
Подтипы объектов
Всего существует 9 подтипов объектов. Их называют сложными примитивами. Они есть:
- Нить
- Число
- Логический
- Объект
- Функция
- Множество
- Дата
- RegExp
- Ошибка
Все это встроенные функции JavaScript, которые можно использовать как конструктор, то есть с ключевым словом new
. Мне очень нравится, как в книге определены конструкторы. В нем говорится, что конструктор - это просто вызов функции с оператором new
, например. new String()
, в результате чего создается новый объект указанного подтипа.
Четверг, 31 августа | 7:50 утра
var x = “hello”
против новой строки («привет»)
Почему-то весь этот раздел о первичных типах и подтипах объектов действительно поразил меня. Я потратил целую вечность, пытаясь понять, что происходит, особенно разницу между использованием примитивной формы и конструктора. Вот суть, которую я понял на данный момент:
Вторник, 5 сентября | 7:45 утра
Массивы - это объекты
Как видно из приведенного выше Gist, массивы являются объектами. В книге я прочитал одну интересную вещь: вы можете добавить свойство в массив! Не то чтобы вам когда-нибудь это действительно понадобилось, но, тем не менее, это доказало, что массивы являются объектами.
Object.assign ()
В книге подробно рассказывается о сложностях, связанных с дублированием объектов, и о разнице между мелкой или глубокой копией.
Чтобы понять неглубокую копию, вы должны сначала понять, что когда вы добавляете массив или функцию к свойству в объекте, это не создает копию массива или функции. Он просто создает ссылку на массив или функцию.
Таким образом, неглубокая копия объекта будет копироваться через любые значения, на которые нет ссылок, например {a: 22 }
и копируйте любые ссылки на функции или массивы (но не копируйте фактическую функцию или массив).
Вот что делает Object.assign()
в ES6:
Записываемый, перечисляемый и настраиваемый
Итак, свойства объекта содержат не только свои значения! Дескриптор свойства содержит другие характеристики, включая возможность записи, перечисления и настройки свойства.
- Возможность записи означает, можете ли вы изменить значение
- Перечислимый означает, можно ли перебирать свойство
- Настраиваемый означает, можете ли вы изменить дескриптор свойства (поэтому, как только вы установите для параметра configurable значение false, вы никогда не сможете снова изменить какие-либо из доступных для записи, перечисляемых или настраиваемых параметров)
Object.preventExtensions (), Object.seal () и Object.freeze ()
Опять же, я понятия не имел о существовании этих трех методов. Ну, я раньше видел Object.freeze()
, но толком не понимал, что он делает. Все эти методы можно использовать, чтобы сделать ваш объект неизменным (неизменяемым) в той или иной форме.
Object.preventExtensions()
означает, что к вашему объекту нельзя добавлять новые свойства.Object.seal()
вызываетObject.preventExtensions()
, но также устанавливает для каждого свойства объекта configurable значение false. Это предотвращает добавление новых свойств, а также их изменение или удаление. Однако вы все равно можете изменить их значения.Object.freeze()
вызываетObject.seal()
, но также устанавливает доступ для записи в false для каждого свойства объекта. Это высший уровень неизменности.
Среда, 6 сентября | 8:30 утра
Получить и положить
Знаете ли вы, что когда вы вызываете obj.a
для доступа к свойству a
, на самом деле выполняется get()
операция над объектом? Точно так же, когда вы добавляете свойство к объекту obj.b = 2
, выполняется put()
операция.
Получить
Get
сначала проверяет объект на предмет свойства с запрошенным именем. Если он его найдет, он вернет значение. Если он не найдет его, он будет обходить цепочку прототипов (все объекты, связанные с этим), пока либо не найдет свойство с запрошенным именем и не вернет его значение, либо не сможет найти свойство и не вернет undefined
.
Положить
Если свойство уже присутствует в указанном объекте, операция размещения начнется с проверки двух вещей:
Во-первых, он проверит, является ли свойство «дескриптором доступа», что означает, что у него есть собственное определение получателя / установщика (операции get и put по умолчанию могут быть переопределены с помощью методов получения и установки). Если это так, он вызовет установщик.
Во-вторых, он проверит, является ли свойство «дескриптором данных», то есть имеет только определения по умолчанию для получения и установки, и установлено ли для него writable
значение false. Если writable
ложно, он либо завершится ошибкой (в нестрогом режиме), либо выдаст ошибку (в строгом режиме), потому что значение не может быть изменено. Если writable
не является ложным, он установит значение свойства как обычно.
Если свойство не уже присутствует в указанном объекте, оно будет проходить по цепочке прототипов (все объекты, связанные с этим). Если свойство с тем же именем не найдено где-либо в цепочке, оно будет добавлено непосредственно к указанному объекту с указанным значением. Если свойство находится где-то выше по цепочке - в зависимости от обстоятельств - оно либо:
- Быть добавленным к указанному объекту, в результате чего получается «затененное свойство». Это означает, что свойство с тем же именем будет существовать в двух или более местах в цепочке прототипов. Этот сценарий происходит, когда свойство с тем же именем существует выше по цепочке прототипов и для
writable
установлено значение true. - Никакое свойство не будет добавлено, и значение не будет установлено вообще. Этот сценарий происходит, когда свойство с тем же именем обнаруживается выше в цепочке прототипов, но для
writable
установлено значение false. - Будет вызван сеттер. К указанному объекту не будет добавлено никакого значения, и определение установщика не изменится. Этот сценарий происходит, когда сеттер обнаружен на объекте выше по цепочке и имеет то же имя, что и указанное свойство.
Если вам интересно, см. Ссылку «в зависимости от обстоятельств» выше, чтобы получить более подробную информацию об установке значений свойств в главе 5 книги.
Четверг, 7 сентября | 8:00 утра
в vs hasOwnPropertyOf ()
in
будет проверять, находится ли свойство на указанном объекте или где-нибудь выше по цепочке прототипов, а hasOwnPropertyOf
проверяет только указанный объект. Подробнее см. Здесь.
Цикл for .. of
Я не знал, что в ES6 появился цикл for of
! Его можно использовать для перебора значений в массиве. Но его также можно использовать для перебора значений в объекте - при условии, что объект определяет свой собственный настраиваемый итератор.
Наследование классов и полиморфизм
Глава 4 дает действительно хороший обзор ООП, классов и наследования. Автор приводит доводы в пользу того, что классы и наследование не существуют в JavaScript. Он подробно рассказывает о полиморфизме. Полиморфизм - одно из тех причудливых словечек, которые могут устрашить начинающих программистов. Это то, чем можно заниматься каждый день, но не знать названия. То, что вы изучаете перед собеседованием. Для меня это было слово, о котором я забыл со времен моей первой работы в C # и .net. Так что мне очень понравилось напоминание о том, что это такое.
Один из аспектов полиморфизма, который обсуждал автор, заключался в том, как имя метода может иметь несколько определений на разных уровнях в цепочке наследования. Скажем, например, у нас есть класс с именем Vehicle
и класс с именем Car
, который наследуется от Vehicle
. Метод с именем drive
может быть определен для обоих классов. «Определение метода…. полиморфы (изменения) в зависимости от того, на какой класс (уровень наследования) вы ссылаетесь на экземпляр ».
Пятница, 8 сентября | 7:50 утра
Классы "подделки" на JS
Большой кусок главы 4 посвящен тому, что в JavaScript есть объекты, а не классы. В традиционном объектно-ориентированном смысле классы выполняют поведение копирования при наследовании или создании экземпляра. В JavaScript объекты не копируются в другие объекты, они связываются вместе. В нем говорится, что такие утилиты, как .extend()
или .mixin()
, обычно пытаются имитировать поведение отсутствующей копии. Он объясняет два типа миксинов - явные и неявные.
Явные миксины
- Если вы явно копируете неперекрывающееся содержимое одного объекта на другой, например. В
Car
смешано содержимоеVehicle
s. В книге есть отличный пример кода. - Объекты кажутся отдельными, хотя они могут влиять друг на друга. например они оба могут использовать ссылку на общий объект, такой как массив или функция.
«Даже ручное копирование функций (также называемых миксинами) из одного объекта в другой не фактически имитирует реальное дублирование от класса к экземпляру, которое происходит в классо-ориентированных языках.
Функции JavaScript на самом деле не могут быть дублированы (стандартным и надежным способом), поэтому вместо этого вы получаете дублированную ссылку на один и тот же объект общей функции ... Если вы изменили один из объектов общей функции ... путем добавления свойств кроме того, например, и
Vehicle
, иCar
будут «затронуты» через общую ссылку ».
Неявные миксины
- Если вы заимствуете функцию, например
Something.cool()
и вызовите его в контексте другого объекта. Таким образом, у вас может бытьSomething.cool.call(this)
внутри другого объекта. Любые назначения, которые делаетSomething.cool
, применяются к другому объекту, а не к объектуSomething
. - Посмотрите пример кода в книге.
Книга подводит итог разделу о миксинах, говоря, что использование синтаксиса типа Something.cool.call(this)
«некрасиво и хрупко» и может привести к коду, который «труднее понять и трудно поддерживать».
Воскресенье, 17 сентября | 17:25
Прототип
JavaScript объекты имеют внутреннее свойство [[Prototype]]
, которое является ссылкой на другой объект.
Всякий раз, когда ищется свойство объекта, например myObject.a
, а свойство отсутствует на указанном объекте, выполняется переход по ссылке [[Prototype]]
.
Объекты связаны вместе с помощью Object.create,
, например. var mySecondObject = Object.create(myObject)
. В этом примере mySecondObject
теперь связан с myObject
, и если a
не был на myObject
, будет запрашиваться mySecondObject
.
Поиск значения свойства будет остановлен либо при нахождении значения, либо при завершении цепочки. Вершиной цепочки является Object.prototype
, от которого происходит почти каждый объект JS (или с которым он связан обратно). По этой причине Object.prototype
включает в себя обычные утилиты JS, такие как toString
, hasOwnProperty
и isPrototypeOf
.
Функции и прототип
Здесь это немного сбивает с толку ...
Функции по умолчанию получают свойство с именем prototype
. См. Y.prototype
ниже. Каждый объект, созданный из Y
путем вызова new
(см. z
ниже), в конечном итоге будет [[prototype]]
связанным с Y.prototype
.
В книге говорится, что использование слова new
пытается имитировать поведение класса в других языках, но это просто приводит к связыванию объектов:
В итоге мы получаем два объекта, связанных друг с другом. Вот и все. Мы не создавали экземпляр класса. Мы определенно не копировали поведение из «класса» в конкретный объект. Мы просто связали два объекта друг с другом.
В нем говорится, что более прямой способ достичь того, чего мы хотим, - это Object.create()
.
Четверг, 28 сентября | 9:10 утра
Вернувшись из Сан-Франциско
Я наконец возвращаюсь к своей книге после недели, проведенной в Сан-Франциско с Твиттером, а через неделю после этого восстанавливаюсь после смены часовых поясов. Я не могу дождаться, когда закончу это.
Вызов конструктора
В книге утверждается, что в JavaScript на самом деле нет конструкторов, а есть вызовы конструкторов.
В приведенном выше примере OrdinaryFunction
- это просто старая функция. Даже не обязательно начинать с заглавной буквы. В книге утверждается, что называть OrdinaryFunction
конструктором технически некорректно, потому что в этом нет ничего особенного. Использование ключевого слова new
, которое «захватывает» OrdinaryFunction
и вызывает его таким образом, что создается объект в дополнение к тому, что еще делает OrdinaryFunction
.
Когда мы регистрируем x.constructor
, оказывается, что x был построен OrdinaryFunction
. Но в следующей строке мы видим, что свойство конструктора на самом деле не принадлежит x
. Он принадлежит OrdinaryFunction.prototype
.
[[prototype]]
x
связан с OrdinaryFunction.prototype.
. Когда мы делаем x.constructor
, мы не можем найти constructor
свойство на x,
, поэтому ищем цепочку прототипов, пока не найдем его на OrdinaryFunction.prototype
.
OrdinaryFunction.prototype
имеет свойство constructor
, потому что:
Объект [
OrdinaryFunction.prototype]
по умолчанию получает общедоступное неперечислимое свойство с именем.constructor
, и это свойство является обратной ссылкой на функцию [OrdinaryFunction
в данном случае], с которой связан объект.
Понедельник, 2 октября | 8:00 утра
.constructor "небезопасен, ненадежен"
Раздел о конструкторах завершается тем, что .constructor
крайне ненадежен и небезопасен.
Например, если .prototype
установлен на пустой объект после объявления функции (по существу удаляя его .constructor
), когда ключевое слово new
позже используется для создания нового объекта, вызов .constructor
для нового объекта не будет указывать на начальную функцию, поскольку ожидал. Он будет указывать на объект в верхней части цепочки прототипов, на котором есть .constructor
.
Вторник, 3 октября | 8:20 утра
Используйте Object.create () поверх new
Обратной стороной использования ключевого слова new
для связывания объектов является то, что любые побочные эффекты возникнут во время связывания, потому что мы используем вызов конструктора var x = new Foo().
Лучше всего связать один объект с другим с помощью Object.create()
(или версии ES6 Object.setPrototypeOf()
).
Object.create()
создает новый объект, связанный с указанным нами объектом, что дает нам всю мощь (делегирование) механизма[[Prototype]]
, но без ненужного усложненияnew
функций, действующих как классы и вызовы конструкторов, запутывающих ссылки.prototype
и.constructor
, или любой из этих лишних вещей. - Глава 5
Среда, 4 октября | 8:30 утра
Прозрачное делегирование
В конце главы 5 книги говорится об использовании цепочки прототипов при построении API прозрачным, а не волшебным образом.
Вторник, 10 октября | 8:30 утра
Поведение делегирования
Глава 6 пытается заставить читателя задуматься в терминах шаблона проектирования делегирование поведения, а не классов.
Делегирование поведения означает: позволить некоторому объекту (
XYZ
) предоставить делегирование (Task
) для ссылок на свойства или методы, если они не найдены на объекте (XYZ
).
Вышеупомянутая цитата относится к этому сценарию кода: var XYZ = Object.create(Task)
.
В книге говорится:
В JavaScript механизм
[[Prototype]]
связывает объекты с другими объектами. Не существует абстрактных механизмов, подобных «классам», как бы вы ни пытались убедить себя в обратном. Это похоже на греблю на каноэ против течения: вы можете сделать это, но вы выбираете идти против естественного течения, поэтому, очевидно, будет труднее добраться туда, где вы '' Собираемся.
Вторник, 10 октября | 8:45 утра
Законченный
Наконец-то! Должен признать, эту книгу было намного труднее понять, чем первые две. Оказывается, объекты JavaScript - это гораздо больше, чем я думал.
Осталось три, осталось три в серии You Don’t Know JS. Далее я буду читать (и вести блог) Типы и грамматика.