Что такое протособытия – обзор движка, механизмов времени выполнения, стека вызовов / RUVDS.com corporate blog / Habr

Содержание

Есть пробитие — что значит мем и откуда он взялся

Есть пробитие — словесный мем, пришедший из лексикона геймеров и любителей игры World of Tanks. Используется для иллюстрации ситуаций, связанных с сексом, крушениями или падением людей.

Происхождение

Фраза «Есть пробитие» пришла из многопользовательской онлайн-игры World of Tanks (WoT). Она звучит каждый раз, когда снаряд наносит максимально возможный урон и пробивает броню противника.

Изначально выражение «есть пробитие» было локальным мемом, распространенным среди пользователей World of tanks. Начиная примерно с 2013 года, на YouTube появлялись любительские ролики с моментами из игры и эпичными кадрами.

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

Популяризации мема способствовали топовые стримеры, которые использовали фразу в своих трансляциях. Например, эту фразу произносили Братишкин и Глад Валакас.

Значение

В широком смысле мем «есть пробитие» иллюстрирует любую ситуацию, связанную с пробитием чего-либо. Часто можно встретить картинки с поврежденными в аварии машинами, подписанные таким образом.

Кроме того, мем приобрел и эротический характер, ведь под пробитием также подразумевают проникновение в сексе. Так, в 2014 году на сайте «Пикабу» появилась короткая история, связанная с сексом и World of Tanks.

Галерея

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

ПРОБИТИЕ — это… Что такое ПРОБИТИЕ?

  • пробитие — пробивание, просаживание, проколачивание, проламывание Словарь русских синонимов. пробитие сущ., кол во синонимов: 4 • пробивание (16) • …   Словарь синонимов

  • пробитие — 3.10 пробитие: По ГОСТ Р 51221. Источник: ГОСТ Р 51136 2008: Стекла защитные многослойные. Общие технические условия …   Словарь-справочник терминов нормативно-технической документации

  • Пробитие — ср. устар. процесс действия по гл. пробить Толковый словарь Ефремовой. Т. Ф. Ефремова. 2000 …   Современный толковый словарь русского языка Ефремовой

  • пробитие — пробитие, пробития, пробития, пробитий, пробитию, пробитиям, пробитие, пробития, пробитием, пробитиями, пробитии, пробитиях (Источник: «Полная акцентуированная парадигма по А. А. Зализняку») …   Формы слов

  • Пробитие образца для испытаний банковского защитного средства — характеристика пулестойкости образца для испытаний банковского защитного средства, определяющая его кондиционность. Пробитие характеризуется выходом пули или осколков пуль за пределы банковской защитной преграды. См. также: Пулестойкость… …   Финансовый словарь

  • пробитие (образца для испытаний банковского защитного средства) — Характеристика пулестойкости образца для испытаний банковского защитного средства, определяющая его кондиционность. Примечание Пробитие характеризуется выходом пули или осколков пуль за пределы банковской защитной преграды. [ГОСТ Р 51221 98]… …   Справочник технического переводчика

  • пробитие (образца для испытаний банковского защитного средства) — 56 пробитие (образца для испытаний банковского защитного средства): Характеристика пулестойкости образца для испытаний банковского защитного средства, определяющая его кондиционность. Примечание Пробитие характеризуется выходом пули или осколков… …   Словарь-справочник терминов нормативно-технической документации

  • АК-74 — Автоматы Калашникова АК74, АКС74, АК74М АК74 Тип: Автомат Страна …   Википедия

  • АК74 — АК 74 Тип: Автомат Страна …   Википедия

  • АКС74У — Тип: Автомат Страна …   Википедия

  • Что такое Javascript.

    Текстовое содержание видео:

    Что же такое Javascript? Давайте попробуем сейчас разобраться в этом вопросе.

    1. Javascript — это язык программирования.

    Первое, что нужно понимать это то, что Javascript — это язык программирования.

    Это означает то, что с его помощью вы можете пользоваться всеми основными возможностями языков программирования: вы можете применять условия (если — сделай «то», иначе — сделай «это»), циклы, перебирать какие-то значения, и.т.д.

    В общем, работать с данными, обрабатывать их и выполнять какие-то действия по автоматизации.

    2. Javascript — это клиентский язык программирования.

    Что значит клиентский?

    Это означает то, что он работает на стороне клиента. Когда мы переходим на какой-либо сайт в сети Интернет, мы делаем это по протоколу HTTP. Со своего домашнего компьютера мы отправляем запрос на удаленный сервер, на котором располагается сайт. И удаленный сервер нам уже присылает ответ (html-страницу, которая будет отображена на домашнем компьютере в браузере). 

    Получается клиент-серверная структура.

    Клиент — это наш локальный компьютер, с которого мы работаем.

    Сервер — это тот удаленный компьютер на котором располагается какой-либо сайт.

    То, что нужно понимать — Javascript работает на клиенте.

    Что является клиентом для протокола http?

    Клиентом для протокола http является браузер. Обычный браузер, с помощью которого вы заходите на какие-либо сайты по сети Интернет. Это может быть Google Chrome, Firefox, Яндекс Браузер и.т.д.

    Javascript — это тот язык программирования, который работает в браузерах.

    Можно упрощенно сказать, что язык программирования Javascript встроен в возможности браузера. При установке браузера вы уже имеете возможности для работы с языком Javascript.

    Отсюда, самый главный плюс языка Javascript — это то, что для того, чтобы им пользоваться не нужно устанавливать какое-либо дополнительное программное обеспечение.

    Все встроено в браузер и имея его, вы уже можете работать с языком Javascript.

    Отсюда же вытекает минус. Я нас появляются трудности с переходом на новые версии языка Javascript.

    Когда язык будет обновляться и будут появляться новые возможности, могут возникнуть некоторые трудности с поддержкой этих возможностей для всех посетителей какого-либо сайта. 

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

    Но, на самом деле есть возможности как можно обойти этот минус, как можно сделать возможность поддержки новых возможностей Javascript для всех посетителей сайта и клиентов, но это тема не этого видео. 

    Давайте вернемся к нашей теме, для чего же нам нужен язык программирования Javascript. Для чего он нужен в браузере?

    Его главная задача — внести возможности автоматизации, на веб-страницы сайтов. 

    Что мы можем делать с помощью Javascript?

    В первую очередь мы можем создавать какие-то интерактивные элементы, с которыми пользователь может взаимодействовать. Примером этого могут быть различные калькуляторы для сайтов (см. видео). Пользователь может взаимодействовать с элементами на этом калькуляторе и с помощью Javascript рассчитываются значения, пересчитываются какие-то поля. Вы можете взаимодействовать с этими элементами и получать какой-то обработанный результат.

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

    В общем, такие интерактивные элементы позволяет создавать именно Javascript.

    Подводя итог, когда мы создаем веб-страницы, HTML позволяет размечать веб-страницы (т.е. говорить чем является тот или иной элемент). 

    С помощью технологии CSS можно придавать для веб-страницы элемент оформления. Каким размером, цветом, положением должны быть элементы.

    И, Javascript — это автоматизация и логика веб-страницы. 

    Javascript позволяет создавать интерактивные элементы на веб-странице, обрабатывать события с этими элементами, создавать автоматизированные программы и.т.д.

    Надеюсь, что вам стало более или менее понятно что такое Javascript. Если будут вопросы, пишите в комментариях.

    Друзья, владеть языком программирования в современной веб-разработке — это это просто необходимость сейчас. Этот язык приобретает все большую популярность и распространение. Предлагаю вашему вниманию серию уроков по основам языка Javascript.

    Давайте будем осваивать эту технологию вместе и я предлагаю вам первое практическое задание. По большей степени, это задание для того, чтобы мы с Вами познакомились.

    Напишите в комментариях:

    1) Для решения каких задач вы планируете использовать язык Javascript?

    2) Если изучали язык Javascript раньше, с какими трудностями сталкивались? Что мешало освоить этот инструмент полноценно?

    3) Есть какие-нибудь ожидания от этого курса? Что бы хотелось в нем увидеть?

    4) Где будете хранить данные из этого курса (блокнот, компьютерная программа или еще где-то)? Что является вашей «Базой знаний»? Поделитесь этим в комментариях.

    Как это организую я, можно посмотреть здесь

    Как поджечь танк в WoT? Что такое пробитие? Основные понятия стрельбы

    Одной из основных особенностей World of Tanks является то, что здесь присутствует достаточно большое количество различных снарядов, каждый из которых отличается своими особенностями, преимуществами и недостатками, предельно приближенными к реалиям Второй мировой войны. В связи с этим далеко не все знают о том, как поджечь танк в WoT, а также какие снаряды использовать для достижения различных целей.

    Что дают разные снаряды?

    как поджечь танк в wot

    Конечно, большое разнообразие снарядов делает игру более сложной, однако в то же время открывает игрокам большое количество возможностей. Таким образом, те люди, которые знают, как поджечь танк в WoT, а также какие снаряды использовать в различных ситуациях, в конечном итоге имеют весомое преимущество перед теми, кто еще не успел ознакомиться с нюансами боевой механики. Именно по этой причине, если вы хотите добиться действительно высоких показателей боевой эффективности, азы стрельбы – это первое, что вам следует изучить.

    Обычные и «премиумные»

    пробитие танков в wot

    Сразу стоит отметить тот факт, что в этой игре существует всего два основных типа заряда – это стандартные и «премиумные». Первые покупаются исключительно за серебро, в то время как вторые приобретаются за золото или же очень большое количество серебра. Таким образом, если обыкновенный снаряд к топовой пушке будет иметь стоимость приблизительно 255 серебра, то более серьезный, премиумный снаряд имеет практически в 20 раз большую стоимость – 4400 серебра, однако и эффективность их заметно выше.

    Бронебойные снаряды

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

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

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

    Осколочно-фугасные снаряды

    игра танки wot

    Идеальный вариант для тех людей, которые ищут, как поджечь танк в WoT, однако в связи с невероятно низкими показателями пробития брони такие снаряды в преимущественном большинстве случаев используются только машинами типа САУ. При этом стоит отметить тот факт, что существует большое количество различных танков, предусматривающих возможность использовать орудий с достаточно большим стволом, предназначенным именно под такие снаряды. Однако нужно понимать, что при использовании стандартных бронебойных снарядов пробитие танков в WoT падает достаточно сильно.

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

    Премиумные фугасные снаряды – одно из самых универсальных оружий, которые предоставила нам игра. Танки WoT 9-го и 10-го уровней от Британии могут использовать такие снаряды, что является одной из основных их особенностей, благодаря которой они и получили такое широкое распространение.

    Кумулятивные

    Кумулятивные снаряды представляют собой снаряды с крайне высоким пробитием, и при этом с огромной дальностью боя. За счет использования таких снарядов танк получает огромное преимущество в бою и свободно может в лоб атаковать даже те танки, которые превышают его по уровню. Среди недостатков таких снарядов стоит отметить то, что они могут рикошетить, а показатель их пробития очень сильно снижается в том случае, если снаряд попадает в экран вражеского танка. Также у них нет нормализации, то есть стрелять необходимо только под нужным углом.

    Отдельное внимание стоит уделить тому, что вам стоит обратить внимание на эти снаряды, если вы думаете, как поджечь танк в WoT с большого расстояния.

    Подкалиберные

    wot пробитие

    Главным преимуществом подкалиберных снарядов является то, что в WoT пробитие у них самое большое, в связи с чем средние танки могут максимально эффективно истреблять даже самые тяжелые машины противника. Стоит отметить, что для всех типов танков такие снаряды голдовые, в то время как для средних танков 10-го уровня они стандартные.

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

    Таким образом, игрок самостоятельно может выбирать, какие снаряды ему лучше использовать в тех или иных ситуациях.

    Прототипное программирование — Википедия

    Прототипное программирование — стиль объектно-ориентированного программирования, при котором отсутствует понятие класса, а наследование производится путём клонирования существующего экземпляра объекта — прототипа.

    Каноническим примером прототип-ориентированного языка является язык Self. В дальнейшем этот стиль программирования начал обретать популярность и был положен в основу таких языков программирования, как JavaScript, Lua, Io, REBOL и др.

    В языках, основанных на понятии «класс», все объекты разделены на два основных типа — классы и экземпляры. Класс определяет структуру и функциональность (поведение

    ), одинаковую для всех экземпляров данного класса. Экземпляр является носителем данных — то есть обладает состоянием, меняющимся в соответствии с поведением, заданным классом.

    Сторонники прототипного программирования часто утверждают, что языки, основанные на классах, приводят к излишней концентрации на таксономии классов и на отношениях между ними. В противоположность этому, прототипирование заостряет внимание на поведении некоторого (небольшого) количества «образцов», которые затем классифицируются как «базовые» объекты и используются для создания других объектов. Многие прототип-ориентированные системы поддерживают изменение прототипов во время выполнения программы, тогда как лишь небольшая часть класс-ориентированных систем (например, Smalltalk, Ruby) позволяет динамически изменять классы.

    Хотя подавляющее большинство прототип-ориентированных систем основаны на интерпретируемых языках с динамической типизацией, технически возможно добавить прототипирование и в языки со статической проверкой типов. Язык Omega является одним из примеров такой системы.

    В класс-ориентированных языках новый экземпляр создаётся через вызов конструктора класса (возможно, с набором параметров). Получившийся экземпляр имеет структуру и поведение, жёстко заданные его классом.

    В прототип-ориентированных системах предоставляется два метода создания нового объекта: клонирование существующего объекта, либо создание объекта «с нуля». Для создания объекта с нуля программисту предоставляются синтаксические средства добавления свойств и методов в объект. В дальнейшем, из получившегося объекта может быть получена полная его копия — клон. В процессе клонирования копия наследует все характеристики своего прототипа, но с этого момента она становится самостоятельной и может быть изменена. В некоторых реализациях копии хранят ссылки на объекты-прототипы, делегируя им часть своей функциональности; при этом изменение прототипа может затронуть все его копии. В других реализациях новые объекты полностью независимы от своих прототипов. Ниже рассмотрены оба этих случая.

    //Пример наследования в прототипном программировании 
    //на примере языка JavaScript
    
    //Создание нового объекта
    var foo = {name: "foo", one: 1, two: 2};
    
    //Создание еще одного нового объекта
    var bar = {two: "two", three: 3};
    
    bar.__proto__ = foo; // foo теперь является прототипом для bar
    
    //Если теперь мы попробуем получить доступ к полям foo из bar
    //то все получится
    bar.one // Равно 1
    
    //Свои поля тоже доступны
    bar.three // Равно 3
    
    //Собственные поля выше по приоритету полей прототипов
    bar.two; // Равняется "two"
    

    Делегирование[править | править код]

    В прототип-ориентированных языках, использующих делегирование, среда исполнения способна выполнять диспетчеризацию вызовов методов (или поиск нужных данных) просто следуя по цепочке делегирующих указателей (от объекта к его прототипу), до совпадения. В отличие от отношения «класс — экземпляр», отношение «прототип — потомки» не требует, чтобы объекты-потомки сохраняли структурное подобие со своим прототипом. С течением времени они могут адаптироваться и улучшаться, но при этом нет нужды переделывать прототип. Важно, что добавлять/удалять/модифицировать можно не только данные, но и функции, при этом функции тоже оказываются

    объектами первого уровня. Вследствие этого большинство прототип-ориентированных языков называют данные и методы объекта «слотами» (ячейками).

    Каскадирование[править | править код]

    При «чистом» прототипировании — именуемом также каскадным и представленном в Kevo — клонированные объекты не хранят ссылок на свои прототипы. Прототип копируется один-в-один, со всеми методами и атрибутами, и копии присваивается новое имя (ссылка). Это напоминает митоз биологических клеток.

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

    В число недостатков можно включить трудности с распространением изменений в системе: модификация прототипа не влечёт за собой немедленное и автоматическое изменение всех его потомков. Тем не менее, Kevo предоставляет дополнительные средства для публикации изменений среди множества объектов, причём на основании их подобия («семейного сходства»), а не по наличию общего предка, что типично для моделей с делегированием.

    Другой недостаток в том, что простейшие реализации этой модели приводят к увеличенному (по сравнению с моделью делегирования) расходу памяти, так как каждый клон, пока он не изменён, будет содержать копию данных своего прототипа. Однако эта проблема разрешима оптимальным разделением неизменённых данных и применением «ленивого копирования» — что и было использовано в Kevo.

    Сторонников класс-ориентированных объектных моделей, критикующих прототипный подход, часто беспокоят те же проблемы, какими обеспокоены сторонники статической типизации в отношении к языкам с динамической типизацией. В частности, обсуждения вращаются вокруг таких тем, как правильность, безопасность, предсказуемость и эффективность программы.

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

    В части эффективности, объявление классов значительно упрощает компилятору задачу оптимизации, делая более эффективными как методы, так и поиск атрибутов в экземплярах. В случае языка Self немалая часть времени была потрачена на разработку таких техник компиляции и интерпретации, которые позволили бы приблизить производительность прототип-ориентированных систем к их класс-ориентированным конкурентам. Дальнейшая работа в этом направлении, а также прогресс в теории JIT-компиляторов привели к тому, что в настоящее время различие между класс- и прототип-ориентированными подходами мало влияет на эффективность результирующего кода. В частности, прототип-ориентированный Lua является одним из самых быстрых интерпретируемых языков и напрямую соперничает со многими компилируемыми,

    [1] а транслятор языка Lisaac генерирует код ANSI C, практически не уступающий нативному.[2]

    Наконец, возможно наиболее общим местом критики против прототипного программирования является то, что сообщество разработчиков программного обеспечения недостаточно хорошо знакомо с ним, несмотря на популярность и распространённость JavaScript. Вдобавок, так как прототип-ориентированные системы сравнительно новы и всё ещё немногочисленны и редки, приёмы разработки с их использованием до сих пор не получили большого распространения.

    • Иан Грэхем. Объектно-ориентированные методы. Принципы и практика = Object-Oriented Methods: Principles & Practice. — 3-е изд. — М.: «Вильямс», 2004. — С. 880. — ISBN 0-201-61913-X.

    Понимание ООП в JavaScript [Часть 1] / Enterra corporate blog / Habr

    — Прототипное наследование — это прекрасно
    JavaScript — это объектно-ориентированный (ОО) язык, уходящий корнями в язык Self, несмотря на то, что внешне он выглядит как Java. Это обстоятельство делает язык действительно мощным благодаря некоторым приятным особенностям.

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

    К счастью, в ECMAScript 5 появилось множество вещей, которые позволили поставить язык на правильный путь (некоторые из них раскрыты в этой статье). Также будет рассказано о недостатках дизайна JavaScript и будет произведено небольшое сравнение с классической моделью прототипного ОО (включая его достоинства и недостатки).


    Статья предполагает, что вы уже знакомы с основами JavaScript, имеете представление о функциях (включая концепты замыкания и функций первого класса), примитивных значениях, операторах и т.д.

    1. Объекты

    Объект в JavaScript — это просто коллекция пар ключ-значение (и иногда немного внутренней магии).

    Однако, в JavaScript нет концепции класса. К примеру, объект с свойствами {name: Linda, age: 21} не является экземпляром какого-либо класса или класса Object. И Object, и Linda являются экземплярами самих себя. Они определяются непосредственно собственным поведением. Тут нет слоя мета-данных (т.е. классов), которые говорили бы этим объектам как нужно себя вести.

    Вы можете спросить: «Да как так?», особенно если вы пришли из мира классических объектно-ориентированных языков (таких как Java или C#). «Но если каждый объект обладает собственным поведением (вместо того чтобы наследовать его от общего класса), то если у меня 100 объектов, то им соответствует 100 разных методов? Разве это не опасно? А как мне узнать, что, например, объект действительно является Array-ем?»

    Чтобы ответить на все эти вопросы необходимо забыть о классическом ОО-подходе и начать всё с нуля. Поверьте, оно того стоит.

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

    1.1. А что такое объекты?

    Ранее упоминалось, что объекты — это просто пары уникальных ключей с соответствующими значениями — такие пары называются свойства. К примеру, вы хотите описать несколько аспектов своего старого друга (назовём его Мишей, он же Mikhail), таких как возраст, имя и пол:

    Объект в JavaScript создаётся с помощью функции Object.create. Эта функция из родителя и опционального набора свойств создаёт новую сущность. Пока что мы не будем беспокоиться о параметрах.

    Пустой объект — это объект без родителя, без свойств. Посмотрим на синтакс создания такого объекта в JavaScript:

    var mikhail = Object.create(null)
    
    1.2. Создание свойств

    Так, значит у нас уже есть объект, но у него пока нет свойств — мы должны исправить эту ситуацию для описания нашего объекта Mikhail.

    Свойства в JavaScript являются динамическими. Это означает, что мы их можем создавать или удалять в любое время. Свойства уникальны в том смысле, что ключ свойства внутри объекта соответствует ровно одному значению.

    Создадим новые свойства через функцию Object.defineProperty, которая в качестве аргументов использует объект, имя свойства для создания и дескриптор, описывающий семантику свойства.

    Object.defineProperty(mikhail, 'name', { value:        'Mikhail'
                                           , writable:     true
                                           , configurable: true
                                           , enumerable:   true })
    
    Object.defineProperty(mikhail, 'age', { value:        19
                                          , writable:     true
                                          , configurable: true
                                          , enumerable:   true })
    
    Object.defineProperty(mikhail, 'gender', { value:        'Male'
                                             , writable:     true
                                             , configurable: true
                                             , enumerable:   true })
    

    Функция Object.defineProperty создаёт новое свойство, если свойство с данным ключём ранее не существовало (в противном случае произойдёт обновление семантики и значения существующего свойства).

    Кстати, вы также можете использовать Object.defineProperties когда необходимо добавить больше одного свойства в объект:

    Object.defineProperties(mikhail, { name:   { value:        'Mikhail'
                                               , writable:     true
                                               , configurable: true
                                               , enumerable:   true }
    
                                     , age:    { value:        19
                                               , writable:     true
                                               , configurable: true
                                               , enumerable:   true }
    
                                     , gender: { value:        'Male'
                                               , writable:     true
                                               , configurable: true
                                               , enumerable:   true }})
    

    Очевидно, что оба вызова аналогичны, они вполне конфигурируемы, но не предназначены для конечного пользователя кода. Лучше создать уровень абстракции над ними.
    1.3. Дескрипторы

    Маленькие объекты, которые содержат в себе семантику, называются дескрипторами (мы их использовали при вызове Object.defineProperty). Дескрипторы бывают одного из двух типов — дескрипторы данных и дескрипторы доступа.

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

    Рассмотрим некоторые флаги:

    • writable — значение свойства может быть изменено, используется только для дескрипторов данных.
    • configurable — тип свойства может быть изменён или свойство может быть удалено.
    • enumerable — свойство используется в общем перечислении.
      Дескрипторы данных таковы, что определяют конкретное значение, которое соответствует дополнительному value-параметру, описывающему конкретные данные, привязанные к свойству:
    • value — значение свойства

    Дескрипторы доступа определяют доступ к конкретному значению через getter-ы и setter-ы функций. Если не установлены, то по умолчанию равны undefined.

    • get() — функция вызывается без аргументов, когда происходит запрос к значению свойства.
    • set(new_value) — функция вызывается с аргументом — новым значением для свойства, когда пользователь пытается

    модифицировать значение свойства.
    1.4. Стремимся к лаконичности

    К счастью, дескрипторы свойств — это не единственный путь работать со свойствами в JavaScript — их можно создавать более лаконично.

    JavaScript также понимает ссылки на свойства, используя так называемую скобочную запись. Основное правило записывается следующим образом:

    <bracket-access> ::= <identifier> "[" <expression> "]"
    

    Тут identifier — это переменная, которая хранит объект, содержащий свойство, значение которого мы хотим установить, а expression — любое валидное JavaScript-выражение, определяющее имя свойства. Нет ограничений на то, какое имя может иметь свойство, всё позволяется.

    Таким образом, мы можем переписать предыдущий пример:

    mikhail['name']   = 'Mikhail'
    mikhail['age']    = 19
    mikhail['gender'] = 'Male'
    

    На заметку: все имена свойств в конечном счёте конвертируются в строку, т.е. записи object[1], object[[1]], object[‘1’] и object[variable] (где значение variable равно 1) эквивалентны.

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

    Общее правило для точечной записи:

    <dot-access> ::= <identifier> "." <identifier-name>
    

    Таким образом, предыдущий пример стал ещё более красивым:
    mikhail.name   = 'Mikhail'
    mikhail.age    = 19
    mikhail.gender = 'Male'
    

    Оба варианта синтаксиса выполняют эквивалентный процесс создания свойств, с выставлением семантических флагов в значение true.
    1.5. Доступ к свойствам

    Очень просто получить значение, хранящиеся в заданном свойстве — синтаксис очень похож на создание свойства с той лишь разницей, что в нём нет присваивания.
    Например, если мы хотим узнать возраст Миши, то мы напишем:
    mikhail['age']
    // => 19
    

    Но если мы попробуем получить значение свойства, которого не существует в нашем объекте, то мы получим undefined:
    mikhail['address']
    // => undefined
    
    1.6. Удаление свойств

    Для удаления свойства из объекта в JavaSCript предусмотрен оператор delete. К примеру, если вы хотите удалить свойство gender из нашего объекта mikhail:
    delete mikhail['gender']
    // => true
    
    mikhail['gender']
    // => undefined
    

    Оператор delete вернёт true, если свойство удалено, и falseв противном случае. Не будем углубляться в то, как работает этот оператор. Но если вам всё-таки интересно, то вы можете почитать самую прекрасную статью о том как работает delete.
    1.6. Getter-ы и setter-ы

    Getter-ы и setter-ы обычно используются в классических объектно-ориентированных языках для обеспечения инкапсуляции. Они не особо нужны в JavaScript, но, у нас динамический язык, и я против этой функциональности.

    Но, с любой точки зрения, они позволяет обеспечить proxy для запросов на чтение и запись свойств. Например, у нас были отдельные слоты для имени и фамилии, но мы хотим иметь удобный способ читать и устанавливать их.

    Для начала, создадим имя и фамилию нашего друга, описав соответствующие свойства:

    Object.defineProperty(mikhail, 'first_name', { value:    'Mikhail'
                                                 , writable: true })
    
    Object.defineProperty(mikhail, 'last_name', { value:    'Weiß'
                                                , writable: true })
    

    Затем мы опишем общий способ получения и установки сразу двух свойств за один раз — назовём их объединение name:
    // () → String
    // Returns the full name of object.
    function get_full_name() {
        return this.first_name + ' ' + this.last_name
    }
    
    // (new_name:String) → undefined
    // Sets the name components of the object, from a full name.
    function set_full_name(new_name) { var names
        names = new_name.trim().split(/\s+/)
        this.first_name = names[⁣'0'] || ''
        this.last_name  = names['1'] || ''
    }
    
    Object.defineProperty(mikhail, 'name', { get: get_full_name
                                           , set: set_full_name
                                           , configurable: true
                                           , enumerable:   true })
    

    Теперь, каждый раз когда мы попытаемся узнать значение свойства name нашего друга на самом деле вызовется функция get_full_name:
    mikhail.name
    // => 'Mikhail Weiß'
    
    mikhail.first_name
    // => 'Mikhail'
    
    mikhail.last_name
    // => 'Weiß'
    
    mikhail.last_name = 'White'
    mikhail.name
    // => 'Mikhail White'
    

    Мы также можем установить name объекта, обратившись к соответствующему свойству, но на самом деле вызов set_full_name выполнит всю грязную работу:
    mikhail.name = 'Michael White'
    
    mikhail.name
    // => 'Michael White'
    
    mikhail.first_name
    // => 'Michael'
    
    mikhail.last_name
    // => 'White'
    

    Есть сценарии, в которых действительно удобно так делать, но стоит помнить, что такой механизм работает очень медленно.
    Кроме того, следует учитывать что getter-ы и setter-ы обычно используются в других языках для инкапсуляции, а в ECMAScript 5 вы всё ещё не можете так делать — все свойства объекта являются публичными.
    1.8. Перечисление свойств

    Ввиду того, что свойства являются динамическими, JavaScript обеспечивает функционал по проверке набора свойств объекта. Существует два способа перечисления всех свойств объекта, зависящих от того, какой вид свойств вас интересует.

    Первый способ заключается в вызове функции Object.getOwnPropertyNames, которая вернёт вам Array, содержащий имена всех свойств, установленных для данного объекта — мы будет называть эти свойства собственными. Например, посмотрим, что мы знаем о Мише:

    Object.getOwnPropertyNames(mikhail)
    // => [ 'name', 'age', 'gender', 'first_name', 'last_name' ]
    

    Второй способ заключается в использовании Object.keys, который вернёт список собственных свойств, которые помечены флагом enumerable :
    Object.keys(mikhail)
    // => [ 'name', 'age', 'gender' ]
    
    1.9. Литералы

    Простой способ создать объект заключается в использовании литерального синтаксиса JavaScript. Литеральный объект определяет новый объект, родитель которого Object.prototype (о родителях поговорим немного позже).

    В любом случае, синтаксис литеральных объектов позволяет определять простые объекты и инициализировать их свойства. Перепишем пример создания объекта Mikhail:

    var mikhail = { first_name: 'Mikhail'
                  , last_name:  'Weiß'
                  , age:        19
                  , gender:     'Male'
    
                  // () → String
                  // Returns the full name of object.
                  , get name() {
                        return this.first_name + ' ' + this.last_name }
    
                  // (new_name:String) → undefined
                  // Sets the name components of the object,
                  // from a full name.
                  , set name(new_name) { var names
                        names = new_name.trim().split(/\s+/)
                        this.first_name = names['0'] || ''
                        this.last_name  = names['1'] || '' }
                  }
    

    Невалидные имена свойств могут быть заключены в кавычки. Учитывайте, что запись для getter/setter в литеральном виде определяется анонимными функциями. Если вы хотите связать ранее объявленную функцию с getter/setter, то вы должны использовать метод Object.defineProperty.

    Посмотрим на общее правила литерального синтаксиса:

    <object-literal>  ::= "{" <property-list> "}"
                        ;
    <property-list>   ::= <property> ["," <property>]*
                        ;
    <property>        ::= <data-property>
                        | <getter-property>
                        | <setter-property>
                        ;
    <data-property>   ::= <property-name> ":" <expression>
                        ;
    <getter-property> ::= "get" <identifier>
                        :       <function-parameters>
                        :       <function-block>
                        ;
    <setter-property> ::= "set" <identifier>
                        :       <function-parameters>
                        :       <function-block>
                        ;
    <property-name>   ::= <identifier>
                        | <quoted-identifier>
                        ;
    

    Литеральные объекты могут появляться внутри выражений в JavaScript. Из-за некоторой неоднозначности новички иногда путаются:
    // This is a block statement, with a label:
    { foo: 'bar' }
    // => 'bar'
    
    // This is a syntax error (labels can't be quoted):
    { "foo": 'bar' }
    // => SyntaxError: Invalid label
    
    // This is an object literal (note the parenthesis to force
    // parsing the contents as an expression):
    ({ "foo": 'bar' })
    // => { foo: 'bar' }
    
    // Where the parser is already expecting expressions,
    // object literals don't need to be forced. E.g.:
    var x = { foo: 'bar' }
    fn({foo: 'bar'})
    return { foo: 'bar' }
    1, { foo:
    
    2. Методы

    До сих пор объект Mikhail имел только слоты для хранения данных (ну, за исключением getter/setter для свойства name). Описание действий, которые можно делать с объектом делается в JavaScript очень просто. Просто — потому что в JavaScript нет разницы между манипулированием такими вещами, как Function, Number, Object. Всё делается одинаково (не забываем, что функции в JavaScript являются сущностями первого класса).

    Опишем действие над данным объектом, просто установив функцию, как значение нашего свойства. К примеру, мы хотим, чтобы Миша мог приветствовать других людей:

    // (person:String) → String
    // Greets a random person
    mikhail.greet = function(person) {
        return this.name + ': Why, hello there, ' + person + '.'
    }
    

    После выставления значения свойства, мы можем использовать аналогичный способ для выставления конкретных данных, связанных с объектом. Таким образом, доступ к свойствам будет возвращать ссылку на функцию, хранящуюся в нём, которую мы можем вызвать:
    mikhail.greet('you')
    // => 'Michael White: Why, hello there, you.'
    
    mikhail.greet('Kristin')
    // => 'Michael White: Why, hello there, Kristin.'
    
    2.1. Динамический this

    Следует учитывать одну вещь при описании функции greet — эта функция должна обращаться к getter/setter свойства name, а для этого она использует магическую переменную this.

    Она хранит в себе ссылку на объект, которому принадлежит исполняющаяся функция. Это не обязательно означает, что this всегда равно объекту, в котором функция хранится. Нет, JavaScript не настолько эгоистичен.

    Функции являются generic-ами. Т.е. в JavaScript переменная this определяет динамическую ссылку, которая разрешается в момент исполнения функции.

    Процесс динамического разрешения this обеспечивает невероятно мощный механизм для динамизации объектной ориентированности JavaScript и компенсирует отсутствие строгого соответствия заданным структурам (т.е. классам). Это означает, что можно применить функцию к любому объекту, который отвечает требованиям запуска, независимо от того, как устроен объект (как и в CLOS).

    2.2. Разрешение this

    Существует четыре различных способа разрешения this в функции, зависящие от того, как функция вызывается: непосредственно, как метод, явно применяется, как конструктор. Мы посмотрим первые три, а к конструкторам вернёмся позже.

    Для следующих примеров вы примем:

    // Returns the sum of the object's value with the given Number
    function add(other, yet_another) {
        return this.value + other + (yet_another || 0)
    }
    
    var one = { value: 1, add: add }
    var two = { value: 2, add: add }
    
    2.2.1 Вызов как метод

    Если функция вызывается, как метод объекта, то this внутри функции ссылается на сам объект. Т.е. когда мы явно указываем какой объект выполняет действие, то объект и будет значением this в нашей функции.

    Это произойдёт, когда мы вызовем mikhail.greet(). Эта запись говорит JavaScript-у, что мы хотим применить действие greet к объекту mikhail.

    one.add(two.value) // this === one
    // => 3
    
    two.add(3)         // this === two
    // => 5
    
    one['add'](two.value) // brackets are cool too
    // => 3
    
    2.2.2 Непосредственный вызов

    Когда функция вызывается непосредственно, то this разрешается в глобальный объект движка (window в браузере, global в Node.js)
    add(two.value)  // this === global
    // => NaN
    
    // The global object still has no `value' property, let's fix that.
    value = 2
    add(two.value)  // this === global
    // => 4
    
    2.2.3. Явное применение

    В заключении, функция может быть явно применена к любому объекту, несмотря на то, есть ли в объекте соответствующее свойство или нет. Эта функциональность достигается с помощью методов call или apply.

    Различие между двумя методами заключается в параметрах передаваемых в функцию и времени исполнения — apply работает примерно в 55 раз медленнее, чем непосредственный вызов, а вот call обычно не особо хуже. Всё очень зависит от текущего движка, так что используйте Perf test, чтобы быть уверенными — не оптимизируйте код раньше времени.

    В любом случае, call ожидает объект, как первый параметр функции, за которым следуют обычные аргументы исходной функции:

    add.call(two, 2, 2)      // this === two
    // => 6
    
    add.call(window, 4)      // this === global
    // => 6
    
    add.call(one, one.value) // this === one
    // => 2
    

    С другой стороны, apply позволяет описывать вторым параметром массив параметров исходной функции:
    add.apply(two, [2, 2])       // equivalent to two.add(2, 2)
    // => 6
    
    add.apply(window, [ 4 ])       // equivalent to add(4)
    // => 6
    
    add.apply(one, [one.value])  // equivalent to one.add(one.value)
    // => 2
    

    На заметку. Учтите, что разрешение this в null или undefined зависит от семантики используемого движка. Обычно результат бывает таким же, как и применение функции к глобальному объекту. Но если движок работает в strict mode, то this будет разрешено как и ожидается — ровно в ту вещь, к которой применяется:
    window.value = 2
    add.call(undefined, 1) // this === window
    // => 3
    
    void function() {
      "use strict"
      add.call(undefined, 1) // this === undefined
      // => NaN
      // Since primitives can't hold properties.
    }()
    
    2.3. Связывание методов

    Отвлечёмся от динамической сущности функций в JavaScript, пойдём по пути создания функций, связывая их с определёнными объектами, так чтобы this внутри функции всегда указывал на данный объект, несмотря на то, как он вызывается — как метод объекта или непосредственно.

    Функция обеспечивает функциональность, называемую bind: берётся объект и дополнительный параметр (очень похоже на вызов call) и возвращается новая функция, которая будет применять параметры к исходной функции при вызове:

    var one_add = add.bind(one)
    
    one_add(2) // this === one
    // => 3
    
    two.one_adder = one_add
    two.one_adder(2) // this === one
    // => 3
    
    one_add.call(two) // this === one
    // => 3
    
    3. Наследование

    До сих пор мы видели, как объекты могут определять своё поведение и как мы можем использовать их действия на других объектах, но, мы до сих пор не увидели нормального пути для повторного использования кода и его расширяемости.

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

    Модель прототипирования идёт дальше. Хоть она и поддерживает такие технологии, как «selective extensibility» и «behaviour sharing», но мы их не будем особо изучать. Печальная вещь: конкретные модели прототипного ОО, реализованные в JavaScript несколько ограниченны. Мы можем обойти эти ограничения, но накладные расходы будут велики.

    3.1. Прототипы

    Наследование в JavaScript осуществляется через клонирование поведения объекта и расширение его специализированным поведением. Объект, поведение которого клонируют, называется прототипом.

    Прототип — это обычный объект, который делится своим поведением с другими объектами — в этом случае он выступает в качестве родителя.

    Концепт клонирования поведения не означает, что вы будете иметь две различные копии одной и той же функции или данных. На самом деле JavaScript реализует наследование через делегирование, т.е. все свойства хранятся в родителе, а доступ к ним расширен через ребёнка.

    Как упоминалось ранее, родитель (или [[Prototype]]) объекта определяется вызовом Object.create с первым аргументом, ссылающимся на объект-родитель.

    Вернёмся к примеру с Мишей. Выделим его имя и способность приветствовать людей в отдельный объект, который поделится с Мишей своим поведением. Вот как будет выглядеть наша модель:

    Реализуем её на JavaScript:

    var person = Object.create(null)
    
    // Here we are reusing the previous getter/setter functions
    Object.defineProperty(person, 'name', { get: get_full_name
                                          , set: set_full_name
                                          , configurable: true
                                          , enumerable:   true })
    
    // And adding the `greet' function
    person.greet = function (person) {
        return this.name + ': Why, hello there, ' + person + '.'
    }
    
    // Then we can share those behaviours with Mikhail
    // By creating a new object that has it's [[Prototype]] property
    // pointing to `person'.
    var mikhail = Object.create(person)
    mikhail.first_name = 'Mikhail'
    mikhail.last_name  = 'Weiß'
    mikhail.age        = 19
    mikhail.gender     = 'Male'
    
    // And we can test whether things are actually working.
    // First, `name' should be looked on `person'
    mikhail.name
    // => 'Mikhail Weiß'
    
    // Setting `name' should trigger the setter
    mikhail.name = 'Michael White'
    
    // Such that `first_name' and `last_name' now reflect the
    // previously name setting.
    mikhail.first_name
    // => 'Michael'
    mikhail.last_name
    // => 'White'
    
    // `greet' is also inherited from `person'.
    mikhail.greet('you')
    // => 'Michael White: Why, hello there, you.'
    
    // And just to be sure, we can check which properties actually
    // belong to `mikhail'
    Object.keys(mikhail)
    // => [ 'first_name', 'last_name', 'age', 'gender' ]
    
    3.2 Но как же [⁣[Prototype]⁣] работает?

    Как вы видели в прошлом примере, ни одно из свойств, определённых в Person мы не определяли явно в Mikhail, но всё же смогли получить к ним доступ. Это произошло благодаря тому, что JavaScript реализует делегирование доступа к свойствам, т.е. свойство ищется через всех родителей объекта.

    Эта цепь родителей определяется скрытым слотом в каждом объекте, который называется [⁣[Prototype]⁣]. Вы не можете изменить его непосредственно, существует только один способ задать ему значение — при создании нового объекта.

    Когда свойство запрашивается из объекта, движок сначала пытается получить свойство из целевого объекта. Если свойство не найдено, то рассматривается непосредственный родитель объекта, затем родитель родителя и т.д.

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

    // (person:String) → String
    // Greets the given person
    person.greet = function(person) {
        return this.name + ': Harro, ' + person + '.'
    }
    
    mikhail.greet('you')
    // => 'Michael White: Harro, you.'
    
    3.3. Перегрузка свойств

    Так, прототипирование (т.е. наследование) используется для того, чтобы можно было поделиться данными с другими объектами. Причём этот способ работает быстро и экономичен по отношению к памяти, т.к. мы всегда имеем только один экземпляр используемых данных.

    Но что если мы хотим добавить специализированное поведение, которым можно было бы делиться с другим объектами? Мы уже видели как объекты определяют своё поведение с помощью свойств, специализированное поведение определяется аналогичным образом — вы просто устанавливаете значение нужному свойству.

    Для лучшей демонстрации положим, что Person реализует только обобщённое приветствие, а каждый наследник Person будет реализовывать своё уникальное приветствие. Также добавим новую персону в наш сценарий, чтобы лучше показать как расширяется объект:

    Учтите, что и mikhail, и kristin определяют собственную версию метода greet. В этом случае мы вызовем метод greet из собственной версии поведения объекта, а не обобщённый метод greet, унаследованный от Person:

    // Here we set up the greeting for a generic person
    
    // (person:String) → String
    // Greets the given person, formally
    person.greet = function(person) {
        return this.name + ': Hello, ' + (person || 'you')
    }
    
    // And a greeting for our protagonist, Mikhail
    
    // (person:String) → String
    // Greets the given person, like a bro
    mikhail.greet = function(person) {
        return this.name + ': \'sup, ' + (person || 'dude')
    }
    
    // And define our new protagonist, Kristin
    var kristin = Object.create(person)
    kristin.first_name = 'Kristin'
    kristin.last_name  = 'Weiß'
    kristin.age        = 19
    kristin.gender     = 'Female'
    
    // Alongside with her specific greeting manners
    
    // (person:String) → String
    // Greets the given person, sweetly
    kristin.greet = function(person) {
        return this.name + ': \'ello, ' + (person || 'sweetie')
    }
    
    // Finally, we test if everything works according to the expected
    
    mikhail.greet(kristin.first_name)
    // => 'Michael White: \'sup, Kristin'
    
    mikhail.greet()
    // => 'Michael White: \'sup, dude'
    
    kristin.greet(mikhail.first_name)
    // => 'Kristin Weiß: \'ello, Michael'
    
    // And just so we check how cool this [[Prototype]] thing is,
    // let's get Kristin back to the generic behaviour
    
    delete kristin.greet
    // => true
    
    kristin.greet(mikhail.first_name)
    // => 'Kristin Weiß: Hello, Michael'
    

    Продолжение следует…

    JavaScript парадигма объектов и прототипов. Простое объяснение.

    Многим новым разработчикам, особенно тем, кто привык работать с традиционным ООП, работать в мире JavaScript может показаться не удобно и не привычно. Для них код на JavaScript может выглядеть грязным и запутанным. Данная статья это попытка объяснить, максимально простым языком, что такое объекты в JavaScript и рассказать о механизме наследования на основе прототипов.

    Хочу отметить, что я читал, множество статей, пытающихся объяснить что такое объекты в JavaScript, но я, никогда не читал те, которые объясняли бы их полностью или четко. Они часто сосредотачиваются исключительно на наследовании, оставляя без внимания другие важные моменты или не приводят достаточного количества примеров.

    Что такое объекты?

    Объекты на самом базовом уровне можно представить как список пар ключ / значение, причем ключ всегда является строкой, а значение… ну что-нибудь еще. Это похоже на то, что вы можете назвать «картой» или «словарем» на других языках. Все, базовые сущности которые вы обычно создаете в JavaScript, и которое не является примитивами, является объектами. Объекты облегчают упаковку и перемещение данных, а создание новых объектов в JavaScript более тривиально, чем в других объектно-ориентированных, таких как Java / C #.

    Когда говорят об объектах часто упоминают термин “свойство”. Этот термин означает определенную пару ключ/значение. Чтобы дать вам представление о том, как выглядят объекты, мы начнем с простого примера объекта с двумя свойствами: age и weight.

    var Dog = {
        age: 8,
        weight: 65
    }

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

    Функции являются объектами

    Как было замечено раньше, в Javascript все что не примитивы являются объектами, включая функции… Я знаю это может показаться странным. Сложно думать о функции как о группе пар ключ/значение. Так как функции объекты, их часто еще называют как объекты-фукнции. Это специальные группа пар ключ/значение с особыми свойствами для выполнения кода и передачи значений. Мы рассмотрим эти свойства в следующем разделе. В начале давайте поговорим почему функции так важны.

    Можно сказать, что объекты-функции имеют две основные цели. Если мы хотим создать блок логики, который выполняется, мы можем использовать объект-функцию: точно так же, как «методы» в любом другом языке программирования. Другой целью является то, что если мы захотим создать объекты со значениями и методами, и возможно, с некоторой логикой для установки этих значений, мы также будем использовать объекты-функции. Здесь вы можете думать о объектах-функциях как о «классах», которые ведут себя как объектно-ориентированные языки (то есть Java / C #).

    В JavaScript вы часто встречаете термин “метод” . Термин метод относится к объекту-функции которая является свойством другого объекта.

    В стандартном случае функции в JavaScript выглядят как функции на любом другом языке; они выполняют логику для выполнения конкретной задачи.

    function bark() {
        console.log('woof woof')
    }
    bark() => 'woof woof'

    Если мы хотим упаковать небольшую группу данных, как, например, два наших свойства в объекте Dog, то достаточно простой список пар ключ / значение. Но что если мы захотим создать несколько объектов Dog? Может быть, некоторые значения должны быть статическими, а другие динамическими. В данном случае нам понадобятся объекты-функции. Когда мы вызываем функцию с помощью new, объект (он же экземпляр-объект) вернется со свойствами установленными с помощью ключевого слова this внутри функции.

    function Dog(age, weight) {
        this.species = 'Canis Familiaris'
        this.age = age
        this.weight = weight
        this.bark = bark <-- bark() from prev snippet
    }
    // Spot and Bingo are 'instance-objects' of Dog
    var Spot = new Dog(8, 65)
    var Bingo = new Dog(10, 70)
    Spot.species => 'Canis Familiaris'
    // bark is a 'method' of Dog
    Bingo.bark() => 'woof woof'
    Объекты против Прототипов

    Теперь давайте поговорим о прототипах. Вы часто слышали, что JavaScript – это язык на основе прототипов. Значит ли это, что объекты и прототипы – это одно и то же? Ну, не совсем. Прототипы являются особым типом объекта и существуют как свойство для объектов-функций. Когда мы пытаемся получить доступ к ключу объекта-функции, JavaScript проверяет его свойство prototype, чтобы увидеть, есть ли оно там. Если нет, он пойдет вверх по цепочке прототипов, чтобы попытаться найти его. Чтобы понять цепочку прототипов, нам нужно узнать о функциях и наследовании.

    Функции и наследование

    Всякий раз, когда экземпляр объекта возвращается из вызова функции с использованием new, ему присваивается свойство с ключом __proto__. Значение этого свойства является свойство prototype функции, которая его создала.

    Bingo.__proto__ === Dog.prototype
    Spot.__proto__ === Dog.prototype

    Если мы попытаемся получить доступ к свойству объекта-экземпляра, а его там нет, JavaScript сначала перейдет к __proto__ и проверит, находится ли оно в прототипе родительской функции. Чтобы увидеть это в действии, давайте установим свойство в одном из атрибутов prototype нашего объекта Dog, и когда мы вызовем Spot[‘whatever the key name is’] или Bingo[‘whatever the key name is’], мы получим то же значение. Это будет работать даже после того, как будут созданы оба объекта-экземпляра dog.

    Dog.prototype.bark = function() {
        console.log('woof woof')
    }
    Spot.bark() // => 'woof woof'
    Bingo.bark() // => 'woof woof'

    Создание методов таким способом (в отличие от использования this внутри функций) особенно полезна, потому что создание метода будет происходить только один раз, а не каждый раз, когда вызывается new. Это будет экономить память и увеличит производительность.

    Рассмотрим наследование немного глубже

    В основе JavaScript-наследования лежит ключевое слово Object, которое является объектом-функцией. Все экземпляры-объекты наследуются от него. Да же когда мы литерально создаем объект в действительности вызывается new Object(). Свойство нового объекта __proto__ будет указывать на прототип родительского объекта (Object().__proto__) . Таким образом, все объекты, создание из литералов объектов, на самом деле являются объектами экземпляра Object. Это предоставляет нам множество полезных родительских методов. Например hasOwnProperty, которое может сообщить нам, существует ли определенное свойство у объекта. Если мы попытаемся получить доступ к свойству непосредственно у объекта-функции, JavaScript сначала будет смотреть на prototype, а затем перемещаться вверх по цепочке, используя __proto__ у родительского prototype.

    Давайте рассмотрим несколько примеров JavaScript, движущихся вверх по цепочке прототипов для доступа к hasOwnProperty у некоторых объектов.

    Литеральный объект (Object-literal):

    var insect = {legs: 6}
    // insect.__proto__ === Object.prototype
    // insect.hasOwnProperty === Object.prototype.hasOwnProperty
    insect.hasOwnProperty('legs') // => true

    Объект Экземпляр (Instance-Object):

    var Bingo = new Dog()
    // Bingo.__proto__ === Dog.prototype
    // Dog.prototype.__proto__ === Object.prototype
    Bingo.hasOwnProperty('weight') // => true

    Объект Функция (Function-object):

    function Foo() {
        this.something = 'blah'
    }
    // Foo.prototype.__proto__ === Object.prototype
    Foo.hasOwnProperty('name') // => true
    Foo.hasOwnProperty('something') // => false, set on instance-object not on the function
    А как насчет __proto__ у объектов-функций?

    Как уже говорилось, __proto__ помогает связать объекты с прототипами, от которых они наследуются. А как насчет вызова __proto__ непосредственно для объектов-функций? JavaScript действительно имеет встроенный объект-функцию под названием Function. Свойство __proto__ каждой функции указывает на Function.prototype, который является функцией, но не имеет свойства prototype и возвращает неопределенное значение. Function.prototype определяет поведение по умолчанию, от которого наследуются все функции. Как и все свойства прототипов функциональных объектов, он по-прежнему имеет __proto__, который указывает на Object.prototype.

    Dog.__proto__ === Function.prototype       
    Object.__proto__ === Function.prototype    
    Function.__proto__ === Function.prototype  
    Function.prototype.__proto__ === Object.prototype

    Выше сказанное можно продемонстрировать следующей картинкой. Обратите внимание, что Object.prototype – это то, откуда все происходит.

    Многоуровневое наследование

    Когда мы говорим о наследовании, мы обычно думаем об объектах экземпляра, возвращаемых из функций. С прототипом вы также можете создать несколько уровней наследования и иметь объекты-функции, наследуемые от других объектов-функций. Все, что вам нужно сделать, это установить прототип дочернего объекта-функции в другой экземпляр прототипа родительского объекта-функции. Тогда все свойства родителя будут скопированы. Если родительская функция получает аргументы, такие как age и weight у Dog, используйте .call, чтобы установить свойство this дочернего объекта.

    Labrador наследуется от Dog:

    function Labrador(furColor, age, weight) {
        this.furColor = furColor
        this.breed = 'labrador'
        Dog.call(this, age, weight)
    }
    Labrador.prototype = Object.create(Dog.prototype)
    var Fido = new Labrador('white', 4, 41)
    Fido.bark()
    Классы

    Классы в JavaScript, созданные в ES6, являются просто синтаксическим сахаром над объектами-функциями. Вместо того, чтобы набирать прототип снова и снова, чтобы определить методы для функций, с ключевым словом class мы можем просто определить группу методов внутри класса. С помощью ключевого слова extends классы могут наследоваться от других классов без необходимости использовать Object.create и Object.call. Лично мне больше нравится использовать классы, но имейте в виду, что старые браузеры могут их не поддерживать. Для решения этой проблемы есть такие инструменты, как Babel.

    Использование объектов функций:

    function Dog(age, weight) {
        this.age = age
        this.weight = weight
    }
    Dog.prototype.bark = function() {console.log('woof woof')}
    
    function Labrador(furColor, age, weight) {
        this.furColor = furColor
        this.breed = 'labrador'
        Dog.call(this, age, weight)
    }
    Labrador.prototype = Object.create(Dog.prototype)

    Аналогичный код но с использованием классов:

    class Dog {
        constructor(age, weight) {
            this.age = age
            this.weight = weight
        }
        bark() {
            console.log('woof woof')
        }
    }
    class Labrador extends Dog {
        constructor(furColor, age, weight) {
            super(age, weight)
            this.furColor = furColor
            this.breed = 'labrador'
        }
    }
    Объекты против примитивов

    Код JavaScript по сути сводится к двум основным типам: примитивам и объектам. В JavaScript есть 5 примитивов: boolean, number, string, null и undefined. Примитивы – это просто простые значения без свойств. Три примитива: boolean, number и string имеют дубликаты объектов, которые JavaScript использует как оболочку во время определенных операций. Например, “some string”.length вызовет new String() и вернет объект-экземпляр, обернутый вокруг строкового примитива, чтобы можно было получить доступ к свойству length. Как уже упоминалось, все объекты-экземпляры наследуются от Object. Так что со строкой вы можете использовать методы родительского объекта, например тот же hasOwnProperty.

    // String.prototype.__proto__ === Object.prototype
    String.hasOwnProperty('length')  // => true

    Оригинал

    Была ли вам полезна эта статья?

    [0 / 0]

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *