Javascript как вывести содержимое объекта

Содержание
  1. Объекты в JavaScript
  2. Неупорядоченность объектов
  3. Вывод всего объекта
  4. Строковые ключи
  5. Ограничение на строковые ключи
  6. Альтернативный синтаксис
  7. Практика
  8. Заполнение
  9. JavaScript Отображение объекта
  10. Как отобразить объекты JavaScript?
  11. Пример
  12. Отображение свойств объекта
  13. Пример
  14. Отображение объекта в цикле
  15. Пример
  16. Использование Object.values()
  17. Пример
  18. Использование JSON.stringify()
  19. Пример
  20. Строковые даты
  21. Пример
  22. Строковые функции
  23. Пример
  24. Пример
  25. Строковые массивы
  26. Пример
  27. Упражнения
  28. Тесты
  29. КАК СДЕЛАТЬ
  30. ПОДЕЛИТЬСЯ
  31. СЕРТИФИКАТЫ
  32. Сообщить об ошибке
  33. Ваше предложение:
  34. Спасибо, за вашу помощь!
  35. Топ Учебники
  36. Топ Справочники
  37. Топ Примеры
  38. Веб Сертификаты
  39. Веб-заметки и Веб-подсказки
  40. JavaScript: Отладка — функция вывода объекта
  41. 8 комментариев на «JavaScript: Отладка — функция вывода объекта»
  42. Работа с объектами в JavaScript: теория и практика
  43. Объекты в JavaScript
  44. Работа по ссылке
  45. Примитивные значения
  46. Создание и использование объектов
  47. Конструктор
  48. Прототип
  49. Наследование
  50. Задача на звездочку
  51. Примеси
  52. Upd: Замыкания и приватные свойства
  53. Что теперь со всем этим делать

Объекты в JavaScript

Рассмотрим следующий массив:

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

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

К примеру, если мы хотим вывести на экран название первого дня недели ( ‘пн’ ), то должны написать в квадратных скобках цифру 0 , а не 1 .

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

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

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

Читайте также:  Чем отмыть щетку от пылесоса

Давайте сделаем так, чтобы понедельник имел ключ 1 , а не ноль, как было раньше, и всем остальным ключам дней прибавим единицу:

Теперь можно обратиться к понедельнику уже по ключу 1 , а не 0 . Сделаем это:

Создайте объект с ключами 1 , 2 и 3 и значениями ‘a’ , ‘b’ и ‘c’ . Выведите на экран все его элементы.

Неупорядоченность объектов

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

В объектах мы сами назначаем ключи, поэтому порядок следования элементов не имеет значения. То есть массивы являются упорядоченными списками, а объекты — нет.

Для примера рассмотрим вот такой объект:

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

Кроме того, числовые ключи не обязательно должны иметь все значения без дырок, подобно массиву. У нас могут быть произвольные числа и это не будет приводить ни к каким проблемам (подобно разреженности у массивов):

Вывод всего объекта

Содержимое всего объекта нельзя нормально посмотреть через функцию alert :

Конечно же, алертом можно вывести каждый отдельный элемент объекта. Но если нужно посмотреть содержимое всего объекта — выводите его в консоль:

Строковые ключи

В объектах, в отличие от массивов, ключи могут быть не только числовыми, но и строковыми. При этом эти ключи-строки, в отличие от значений-строк, в кавычки брать не нужно:

А вот, чтобы обратиться к элементу со строковым ключом, в квадратных скобках его уже нужно брать в кавычки:

Создайте объект с ключами a , b и c и значениями 1 , 2 и 3 . Найдите сумму его элементов.

Ограничение на строковые ключи

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

Такие строки на самом деле являются допустимыми ключами, но только если их взять в кавычки:

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

Создайте объект с ключами 1a , 2b и с-с и значениями 1 , 2 и 3 . Найдите сумму его элементов.

Для каких ключей данного объекта кавычки обязательны, а для каких нет?

Альтернативный синтаксис

Давайте рассмотрим следующий объект:

Выведем на экран значение какого-нибудь его элемента, например, с ключом key1 :

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

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

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

Найдите сумму его элементов. Обращайтесь к элементам с помощью изученного синтаксиса.

К каким элементам этого объекта допустимо обращение через свойство, а к каким — нет?

Практика

Создайте объект user с ключами name , surname , patronymic и какими-то произвольными значениями. Выведите на экран фамилию, имя и отчество через пробел.

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

Заполнение

Объекты заполняются так же, как и массивы:

Можно также использовать альтернативный синтаксис:

Создайте объект с ключами a , b и c и элементами 1 , 2 и 3 .

Источник

JavaScript Отображение объекта

Как отобразить объекты JavaScript?

Отображение объекта JavaScript приведет к выводу [объект Object].

Пример

Вот некоторые распространенные решения для отображения объектов JavaScript:

Some common solutions to display JavaScript objects are:

  • Отображение свойств объекта по имени
  • Отображение свойств объекта в цикле
  • Отображение объекта с помощью Object.values()
  • Отображение объекта с помощью JSON.stringify()

Отображение свойств объекта

Свойства объекта можно отобразить в виде строки:

Пример

document.getElementById(«demo»).innerHTML =
person.name + «,» + person.age + «,» + person.city;

Отображение объекта в цикле

Свойства объекта можно собрать в цикле:

Пример

for (x in person) <
txt += person[x] + » «;
>;

Вы должны использовать в цикле person[x].

person.x person.x не будет работать (потому что x — переменная).

Использование Object.values()

Любой объект JavaScript можно преобразовать в массив, используя Object.values() :

var myArray = Object.values(person);

myArray теперь представляет собой массив JavaScript, готовый к отображению:

Пример

var myArray = Object.values(person);
document.getElementById(«demo»).innerHTML = myArray;

Object.values() поддерживается во всех основных браузерах с 2016 года.

54 (2016) 14 (2016) 47 (2016) 10 (2016) 41 (2016)

Использование JSON.stringify()

Любой объект JavaScript можно преобразовать в строку (преобразовать в строку) с помощью функции JavaScript JSON.stringify() :

var myString = JSON.stringify(person);

myString теперь является строкой JavaScript, готовой к отображению:

Пример

var myString = JSON.stringify(person);
document.getElementById(«demo»).innerHTML = myString;

Результатом будет строка в формате JSON:

JSON.stringify() включен в JavaScript и поддерживается всеми основными браузерами.

Строковые даты

JSON.stringify преобразует даты в строки:

Пример

var myString = JSON.stringify(person);
document.getElementById(«demo»).innerHTML = myString;

Строковые функции

JSON.stringify не будет связывать функции:

Пример

var myString = JSON.stringify(person);
document.getElementById(«demo»).innerHTML = myString;

Это можно «исправить», если перед преобразованием функций в строки вы преобразовываете их в строки.

Пример

var myString = JSON.stringify(person);
document.getElementById(«demo»).innerHTML = myString;

Строковые массивы

Также возможно структурировать массивы JavaScript:

Пример

var arr = [«John», «Peter», «Sally», «Jane»];

var myString = JSON.stringify(arr);
document.getElementById(«demo»).innerHTML = myString;

Результатом будет строка в формате JSON:

Упражнения

Тесты

КАК СДЕЛАТЬ

ПОДЕЛИТЬСЯ

СЕРТИФИКАТЫ

Сообщить об ошибке

Если вы хотите сообщить об ошибке или сделать предложение, не стесняйтесь, присылайте нам электронное письмо:

Ваше предложение:

Спасибо, за вашу помощь!

Ваше сообщение было отправлено в SchoolsW3.

Топ Учебники

Топ Справочники

Топ Примеры

Веб Сертификаты

SchoolsW3 оптимизирован для обучения, тестирования и тренировки. Примеры упрощают и улучшают чтение и базовое понимание. Учебники, справочники, примеры постоянно пересматриваются, для того, чтобы избежать ошибки, невозможно гарантировать правильность всего содержимого. Используя данный сайт, вы соглашаетесь прочитать и принять условия использования, cookie и Политика конфиденциальности. Авторское право 1999 — 2021 Все права защищены.
Работает на W3.CSS.

Источник

Веб-заметки и Веб-подсказки

JavaScript: Отладка — функция вывода объекта

Очень часто при отладке javascript-приложений необходимо просматривать содержимое объектов.

Если стандартная функция вывода сообщений alert() умеет выводить массивы, то с объектами она справляется намного хуже.
Конечно, можно выводить сложные структуры данных в консоль, используя выражение: console.log(), но на данный момент не у всех веб-браузеров есть этот консоль.

В общем, я написал для дебагинга небольшую функцию под названием alertObj(). Как можно догадаться, название произошло от 2 слов: alert и object, то-есть — выдать сообщение с содержимым объекта. Она преобразовывает объект в строку понятную человеку и выводит её стандартным диалоговым сообщением. Ничего сложного.
Кому удобно — используйте на здоровье…

function alertObj(obj) <
var str = «»;
for(k in obj) <
str += k+»: «+ obj[k]+»\r\n»;
>
alert(str);
>

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

javascript: function alertObj(o)alert(s);>

8 комментариев на «JavaScript: Отладка — функция вывода объекта»

Часто объекты многомерны для этого:
function strObj(obj,prefix,depth) <
var str = «\r\n\r\n\r\n\r\n\r\n\r\n»;
for(k in obj) <
str += prefix+» «+k+»: «+ obj[k]+»\r\n»;
if(obj[k] && ‘object’ === typeof obj[k] && prefix.length Igor :

Я тебя обожаю, неделю искал выход, никак не мог редактировать объект, ни html ни text взять, в итоге увидел что outerText пашет и взял его, ОГРОМНОЕ СПАСИБО!

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

alertObj(obj) работает но все равно выдает вместе с данными [object Object]

А что за ‘object’ у Ивана? На что заменить ‘’?

Прикольно 😉 Зачем изобретать велосипед, если в JavaScript уже все есть?
alert(«JSON.stringify = » + JSON.stringify(obj, «», 4));

классная дискуссия, а как же быть если внутри объекта имеется функция? Упс надо изобретать велосипед…

Источник

Работа с объектами в JavaScript: теория и практика

В этой статье я хочу по возможности полно и последовательно рассказать о том, что такое объект в JavaScript, каковы его возможности, какие взаимоотношения могут строиться между объектами и какие способы «родного» наследования из этого вытекают, как это все влияет на производительность и что вообще со всем этим делать 🙂

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

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

Объекты в JavaScript

Во многих статьях встречается фраза «В JavaScript — всё объект». Технически это не совсем верно, однако производит должное впечатление на новичков 🙂

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

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

Итак, в JavaScript есть 6 базовых типов данных — это Undefined (обозначающий отсутствие значения), Null, Boolean (булев тип), String (строка), Number (число) и Object (объект).
При этом первые 5 являются примитивными типами данных, а Object — нет. Кроме того, условно можно считать, что у типа Object есть «подтипы»: массив (Array), функция (Function), регулярное выражение (RegExp) и другие.
Это несколько упрощенное описание, но на практике обычно достаточное.

Кроме того, примитивные типы String, Number и Boolean определенным образом связаны с не-примитивными «подтипами» Object: String, Number и Boolean соответственно.
Это означает, что строку ‘Hello, world’, например, можно создать и как примитивное значение, и как объект типа String.
Если вкратце, то это сделано для того, чтобы программист мог и в работе с примитивными значениями использовать методы и свойства, как будто это объекты. А подробнее об этом можно будет прочитать в соответствующем разделе данной статьи.

Работа по ссылке

Ссылка — это средство доступа к объекту под различными именами. Работа с любыми объектами ведется исключительно по ссылке.
Продемонстрируем это на примере:

test= function () //Создадим функцию (а функция, как мы помним, является полноправным объектом) и сделаем переменную test ссылкой на нее
test_link=test; //test_link теперь тоже ссылается на нашу функцию
test(); //Hello!
test_link(); //Hello!
* This source code was highlighted with Source Code Highlighter .

Как мы видим, и первая ссылка, и вторая дают один и тот же результат.
Необходимо осознать, что у нас нет никакой функции с именем test, и что переменная test не является какой-то «главной» или «основной» ссылкой, а «test_link» — второстепенной.

Наша функция, как и любой другой объект — просто область в памяти, и все ссылки на эту область абсолютно равнозначны. Более того, объект может вообще не иметь ссылок — в таком случае он называется анонимным, и может быть использован только непосредственно сразу после создания (например, передан в функцию), иначе доступ к нему получить будет невозможно и в скором времени он будет уничтожен сборщиком мусора (garbage collection), который и занимается тем, что удаляет объекты без ссылок.

Посмотрим, почему так важно это понимать:

test= //Создаем объект со свойством prop
test_link=test; //Создаем еще одну ссылку на этот объект

alert(test.prop); //sometext
alert(test_link.prop); //sometext

//Изменяем свойство объекта
test_link.prop= ‘newtext’ ;

alert(test.prop); //newtext
alert(test_link.prop); //newtext
/*Можно было бы сказать, что свойство изменилось и там и тут — но это не так.
Объект-то один. Так что свойство изменилось в нем один раз, а ссылки просто продолжают указывать туда, куда и указывают. */

//Добавляем новое свойство и удаляем старое
test.new_prop= ‘hello’ ;
delete test.prop;

alert(test_link.prop); //undefined — такого свойства больше нет
alert(test_link.new_prop); //hello — что и следовало ожидать

//Удаляем ссылку
delete test;
alert(test.new_prop);
/*В этом месте скрипт выкинет ошибку, потому что test уже не существует, и test.new_prop не существует тем более */
alert(test_link.new_prop); //hello
/* а вот тут все в порядке, ведь мы удалили не сам объект, а лишь ссылку на него. Теперь на наш объект указывает единственная ссылка test_link */

//Создаем новый объект
test=test_link; //Сперва снова создадим ссылку test
test_link= //А вот и новый объект

alert(test_link.prop); //sometext
alert(test.prop); //undefined
/* Cоздание нового объекта разрывает ссылочную связь, и теперь test и test_link указывают на разные объекты.
Фактически, это равносильно удалению ссылки test_link и созданию ее заново, но уже указывающей на другой объект */
alert(test.new_prop); //hello — теперь test содержит ссылку на наш самый первый объект

* This source code was highlighted with Source Code Highlighter .

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

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

Примитивные значения

obj= new String( ‘hello’ ); //Создаем строку как объект
simple= ‘hello’ ; //Создаем примитивное значение

alert(obj); //hello
alert(simple); //hello — пока все предсказуемо

alert(obj.length); //6 — у объекта типа String есть свойство length, хранящее длину строки
alert(simple.length); //6
/* Хотя simple — не объект, мы можем обращаться к тому же набору свойств, что и у объекта типа String. Это довольно удобно */

obj.prop= ‘text’ ;
simple.prop= ‘text’ ;

alert(obj.prop); //text — раз obj обычный объект, то мы можем запросто придать ему еще одно свойство
alert(simple.prop); //undefined — а вот simple не объект, и этот номер у нас не пройдет

* This source code was highlighted with Source Code Highlighter .

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

Не стоит путать использование примитивных значений с использованием литералов — например, независимо от того, создаем мы массив как «test=new Array()» или как «test=[]», в результате все равно будет один и тот же объект. Никаких примитивных значений мы не получим.

Создание и использование объектов

test= <
simple_property: ‘Hello’ ,
object_property: <
user_1: ‘Петя’ ,
user_2: ‘Вася’
>,
function_property: function (user) <
alert( this .simple_property + ‘, ‘ + this .object_property[user]);
>
>

test.function_property( ‘user_1’ ); //Hello, Петя.

* This source code was highlighted with Source Code Highlighter .

Перед нами объект test, имеющий 3 свойства, названия которых, как я надеюсь, говорят сами за себя. Больше всего нас в нем интересует свойство function_property, содержащее функцию. Такую функцию можно назвать методом объекта.

В нашей функции дважды используется ключевое слово this, которое является указателем (т.е. ссылкой) на объект, из которого вызывается функция. Таким образом, this.simple_property=test.simple_property=’Hello’, а this.object_property[user]=test.object_property[user]=’Петя’.

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

test.function_property( ‘user_1’ ); //Hello, Петя.

test2= new Object(); //Еще одна форма создания нового объекта, аналогичная test2=<>

test.function_property.call(test2, ‘user_1’ ); //ошибка
/* Метод call позволяет вызвать функцию от имени другого объекта. В данном случае, мы вызываем метод function_property объекта test, и его this указывает уже не на объект test, а на объект test2. А т.к. в нем нет свойства object_property, то при попытке получить this.object_property[user]скрипт выдаст ошибку */

//попробуем исправить ситуацию
test2.simple_property= ‘Good day’ ;
test2.object_property=test.object_property; //В данном случае воспользуемся указанием объекта по ссылке, чтобы не дублировать код

test.function_property.call(test2, ‘user_1’ ); //Good day, Петя.

* This source code was highlighted with Source Code Highlighter .

Из примера также должно быть видно, что нет четких этапов создания и использования объекта. Объект может быть как угодно модифицирован в любое время — до, после и даже во время использования. Это тоже важное отличие от «традиционного» ООП.

Конструктор

В примере выше мы создавали 2 объекта, обладающих некой схожестью. И там и там имелись свойства simple_property и object_property. Очевидно, что при написании реального кода также нередко встает задача создания одинаковых или просто похожих объектов. И разумеется, мы не должны каждый такой объект создавать вручную.

На помощь нам придет конструктор. Конструктор в JavaScript — это не часть класса (потому что здесь нет классов), а просто самостоятельная функция. Самая обычная функция.

make_me= function (_name) <
alert( ‘меня запустили’ );
this .name=_name;
this .show_name= function ()
>

child= new make_me( ‘Вася’ ); //меня запустили
/* Давайте разберемся, что здесь происходит. Интерпретатор видит оператор new и проверяет, что находится справа от него. Т.к. make_me — это функция, и она может быть использована в качестве контруктора, то создается новый объект в памяти и запускается на выполнение функция make_me, причем ее this указывает как раз на этот новый объект. Далее этому объекту добавляется свойство name, которому присваивается значение из аргумента _name, и метод show_name. Также (не знаю в какой именно момент, но это и не важно) переменная child начинает указывать на наш новенький, только что рожденный объект */

alert(child.name); //Вася
child.show_name(); //Вася

child2= new make_me( ‘Петя’ );
child2.show_name(); //Петя

child2.show_name= function () //Не забываем, что можем изменять наши объекты в любой момент
child2.show_name(); //Не буду говорить свое имя

child.show_name(); //Вася — дети никак не влияют друг на друга

* This source code was highlighted with Source Code Highlighter .

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

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

Прототип

make_me= function (_name) <
alert( ‘меня запустили’ );
this .name=_name;
this .show_name= function ()
>
/*
Видя ключевое слово function, интерпретатор проверяет код справа от него, и т.к. все ок — создает новый объект в памяти, который одновременно является нашей функцией. Затем, автоматически (без участия программиста) для этой функции создается свойство prototype, ссылающееся на пустой объект. Если бы мы это делали вручную, это выглядело бы как make_me.prototype=new Object();

Затем, данному объекту (на который указывает свойство prototype) также автоматически добавляется свойство constructor, указывающее обратно на функцию. Получается такая вот циклическая ссылка.

Теперь этот объект, который можно описать как — и есть прототип функции.
*/

alert( typeof make_me.prototype); //Object — действительно, объект
alert( typeof make_me.prototype.constructor); //Function — это наша функция
alert(make_me.prototype.constructor === make_me); //true

make_me.prototype.set_name= function (_name) < this .name=_name;>//Добавляем в прототип функции make_me новый метод

child= new make_me( ‘Вася’ ); //меня запустили
/* Теперь помимо всего того, что описано в предыдущем примере, дополнительно в объекте child создается скрытое свойство [[Prototype]], которое указывает на тот же объект, что и make_me.prototype. Т.к. свойство скрыто, мы не можем ни просмотреть его значение, ни изменить его — однако оно играет важную роль в дальнейшей работе */

alert(child.name); //Вася
child.show_name(); //Вася

child.set_name( ‘Коля’ );
/* Сначала, интерпретатор ищет метод set_name в объекте child. Так как его там нет, он продолжает поиск в свойстве child.[[Prototype]], находит его там и запускает. */
child.show_name(); //Коля — теперь Васю зовут Коля 🙂

make_me.prototype.show_name2= function () //Т.к. прототип — это обычный объект, мы точно также можем его менять на лету

child2= new make_me( ‘Петя’ );
child2.show_name2(); //Привет, Петя
child.show_name2(); //Привет, Коля — изменения в прототипе влияют не только на вновь созданные объекты, но и на все старые

child2.show_name2= function () //Мы по прежнему можем изменить сам объект, при этом новый метод show_name2 в данном объекте (и только в нем) как бы «затрет» старый метод из прототипа
child2.show_name2(); //Не буду говорить свое имя — т.к. у нас теперь есть собственный метод show_name2, то он и вызывается, и поиск в прототипе не происходит

child.show_name2(); //Привет, Коля — здесь все по прежнему

make_me.prototype= //Попробуем пересоздать прототип заново

alert(child.prop); //undefined
child.show_name2(); //Привет, Коля
/* Если вспомнить, что такое работа по ссылке, то все понятно. Пересоздание прототипа рвет связь, и теперь свойство [[Prototype]] у объектов child и child2 указывают на один объект (который раньше был прототипом функции make_me), а свойство make_me.prototype — на другой объект, который является новым прототипом функции make_me */

child3= new make_me( ‘Олег’ );
alert(child3.prop); //hello — что и следовало ожидать

* This source code was highlighted with Source Code Highlighter .

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

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

make_me= function (_name) <
alert( ‘меня запустили’ );
this .name=_name;
this .show_name= function ()
>

make_me.prototype.set_name= function (_name) < this .name=_name;>
child= new make_me( ‘Вася’ );

alert( typeof make_me.prototype); //object — у функции есть свойство prototype
alert( typeof child.prototype); //undefined — у созданного объекта НЕТ свойства prototype
alert(child.constructor.prototype === make_me.prototype); //true — зато у объекта есть свойство constructor, которое указывает на функцию-конструктор make_me, у которой, в свою очередь, есть свойство prototype

* This source code was highlighted with Source Code Highlighter .

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

Необходимо всегда четко понимать, что если речь идет о прототипе конструктора — то это всегда свойство prototype, а если о прототипе созданного объекта — то это скрытое свойство [[Prototype]].

Наследование

bird= function () <> //Это конструктор птички
bird.prototype.cry= function () //Птичка умеет кричать
bird.prototype.fly= function () //и летать

duck= function () <>
duck.prototype= new bird();
duck.prototype.cry= function () //Утка кричит по другому
duck.prototype.constructor=duck; //Принудительно устанавливаем свойство prototype.constructor в duck, т.к. иначе оно будет ссылаться на bird

billy = new duck(); //Билли — это наша утка
billy.fly(); //Я лечу! — Билли может летать, потому что он птица
billy.cry(); //Кря-кря! — Билли кричит кря-кря, потому что он утка

* This source code was highlighted with Source Code Highlighter .

Так можно реализовывать иерархию любого уровня вложенности.

Задача на звездочку

make_me= function () <>
child= new make_me();
alert(child.toString()); //выводит [object]

* This source code was highlighted with Source Code Highlighter .

В первой строке мы создаем новую функцию и переменную make_me, которая указывает на эту функцию. При этом создается прототип функции, make_me.prototype, в котором содержится свойство constructor, указывающее на make_me.
Но это далеко не все 🙂
Т.к. функция make_me — это тоже объект, то он в свою очередь имеет папу и маму, т.е. конструктор и прототип. Его конструктор — это родная функция языка Function(), а прототип — объект, содержащий в себе методы call, apply и т.д. — именно благодаря этому прототипу мы и можем пользоваться этими методами в любой функции. Таким образом, у функции make_me появляется свойство [[Prototype]], указывающее на Function.prototype.

В свою очередь, прототип конструктора Function — тоже объект, конструктором которого является (сюрприз!) Object (т.е. Function.prototype.[[Prototype]].constructor===Object), а прототипом — объект, содержащий стандартные свойства и методы объекта, такие как toString, hasOwnProperty и другие (другими словами — Function.prototype.[[Prototype]][‘hasOwnProperty’] — это как раз тот самый метод, которым мы можем пользоваться во всех производных объектах — причем это именно собственной метод данного объекта, а не наследованный). Вот таким вот интересным образом мы обнаруживаем, что все виды объектов являются производными от Object.

Можем ли мы продолжить дальше? Оказывается, нет. Object.prototype именно потому и содержит базовые свойства объекта, что не имеет собственного прототипа. Object.prototype.[[Prototype]]=null; В этом месте путешествие по цепочке прототипов в поиске свойства или метода прекращается.

Еще один интересный факт — конструктором Object является Function. Т.е. Object.[[Prototype]].constructor===Function.
Налицо еще одна циклическая ссылка — конструктор Object это Function, а конструктор Function.prototype — это Object.

Вернемся к нашему примеру. Как создается функция мы уже поняли, теперь перейдем ко второй строке. Там мы создаем объект child, конструктором которого является функция make_me, а прототипом — make_me.prototype.

Ну и в третей строчке мы видим, как интепретатор поднимается по цепочке, от child к child.[[Prototype]] (он же make_me.prototype), затем к child.[[Prototype]].[[Prototype]] (он же Object.prototype), и уже там находит метод toString, который и запускает на выполнение.

Примеси

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

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

//Это конструктор человека
man= function () <
this .live= function () //Человек умеет жить
this .walk= function () //Человек умеет ходить
>

//Это конструктор поэта
poet= function () <
this .kill= function () //Поэт может убить человека
this .live= function () //От этого человек умрет
>

vladimir= new man(); //Владимир — человек
vladimir.live(); //Я живу — он жив
vladimir.walk(); //Я иду — он ходит

poet.call(vladimir); //Выполняем конструктор poet для объекта vladimir
vladimir.kill(); //Поэт убил человека
vladimir.live(); //Я мертв

//А теперь фокус
man.call(vladimir);
vladimir.live(); //Я живу

* This source code was highlighted with Source Code Highlighter .

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

Upd: Замыкания и приватные свойства

Чтобы не раздувать эту и без того немаленькую статью, даю ссылку на пост Замыкания в JavaScript, где про это довольно подробно написано.

Что теперь со всем этим делать

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

Причем вопрос о цене довольно нетривиален, особенно если мы говорим о разработке под браузер Internet Explorer 6 и 7 версий.
1. Память — тут все просто. Во всех браузерах наследование на прототипах отнимает в разы меньше памяти, чем при создании методов через конструкторы. Причем, чем больше методов и свойств у нас есть, тем больше разница. Однако, стоит помнить, что если у нас не тысяча одинаковых объектов а всего лишь один, то расходы памяти в любом случае будут небольшими, т.к. здесь стоит учитывать другие факторы.
2. Процессорное время — здесь основные тонкости связанны именно с браузерами от Microsoft.
С одной стороны, объекты, где методы и свойства создаются через конструктор — могут создаваться в разы (в некоторых случаях в десятки и сотни раз) медленнее, чем через прототип. Чем больше методов — тем медленнее. Так что если у вас в IE замирает на несколько секунд во время инициализации скрипта — есть повод копать в эту сторону.

С другой стороны, собственные методы объекта (созданные через конструктор) могут выполняется немного быстрее, чем прототипные. В случае, если позарез необходимо ускорить именно выполнение какого-то метода в этом браузере, то нужно это учесть. Имейте ввиду, ускоряется именно вызов метода (т.е. поиск его в объекте), а не его выполнение. Так что если сам метод у вас выполняется секунду, то особого увеличения быстродействия вы не заметите.

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

Источник

Оцените статью