Объекты

Примитивными типами в JavaScript являются: true, false, числа, строки, null и undefined. Все остальные значения представляют собой объект.

В JavaScript объекты содержат пару «имя: значение».

Создание

В JavaScript существует два способа создать объект:

1. литеральный

var object = {};
// Да, просто пара фигурных скобок!

Это рекомендованный метод.

2. объектно-ориентированный

var object = new Object();

Почти как в Java.

Свойства

Свойство объекта представляет собой пару «свойство: значение», где имя свойства может быть только строкой. Если это не строка, то преобразуется в строку. Вы можете указать свойства объекта при его создании или позднее. Допустимо несколько свойств, разделённых запятыми или ни одного свойства.

var language = {
  name: 'JavaScript',
  isSupportedByBrowsers: true,
  createdIn: 1995,
  author:{
    firstName: 'Brendan',
    lastName: 'Eich'
  },
  // Да, объекты могут быть вложенными!
  getAuthorFullName: function(){
    return this.author.firstName + " " + this.author.lastName; 
  }
  // Да, функции тоже могут быть значениями!
};

Следующий код демонстрирует как получить значение свойства.

var variable = language.name;
// variable теперь содержит строку "JavaScript".
variable = language['name'];
// Строка выше делает то же самое. Отличие в том, что этот метод позволяет использовать 
// буквально любую строку в качестве значения свойства, но читается хуже. 
variable = language.newProperty; 
// variable теперь undefined, потому что мы ещё не определили это свойство.

В следующем примере показано, как добавить новое свойство или изменить существующее.

language.newProperty = 'новое значение';
// Теперь объект содержит новое свойство. Если оно уже существует, значение будет заменено.
language['newProperty'] = 'изменённое значение';
// Ещё раз — вы можете получить доступ к свойству двумя методами. Рекомендуется первый (с точкой).

Изменчивость

Разница между объектами и значениями примитивов в том, что мы можем изменить объекты, в то время как значения примитивов остаются неизменными.

var myPrimitive = "начальное значение";
myPrimitive = "другое значение";
// myPrimitive теперь указывает на другую строку.
var myObject = { key: "начальное значение"};
myObject.key = "другое значение";
// myObject указывает на тот же объект.

Указатели

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

// Представьте, что у меня была пицца
var myPizza = {slices: 5};
// И я поделился ею с тобой
var yourPizza = myPizza;
// Я ем другой кусок
myPizza.slices = myPizza.slices - 1;
var numberOfSlicesLeft = yourPizza.slices;
// Теперь у нас есть 4 куска, потому что myPizza и yourPizza
// указывают на один объект.
var a = {}, b = {}, c = {};
// a, b и c указывают на разные пустые объекты
a = b = c = {};
// a, b и c указывают на один пустой объект

Прототип

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

Все объекты, созданные как литеральные ({}), автоматически связаны с Object.prototype, который является стандартным объектом JavaScript.

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

var adult = {age: 26},
retrievedProperty = adult.age;

Вначале интерпретатор просматривает каждое свойство, которое есть у объекта. Например, adult содержит только одно свойство — age. Но кроме него на самом деле свойств несколько больше, потому что они наследуются из Object.prototype.

var stringRepresentation = adult.toString();
// переменная содержит значение [object Object]

toString является унаследованным свойством Object.prototype. Оно содержит значение функции, которое возвращает строку представляющую собой объект. Если вы хотите вернуть строку в более понятном виде, то можете переопределить её. Просто добавьте новое свойство в объект adult.

adult.toString = function(){
  return "Мне " + this.age;
}

Если вы сейчас вызовите функцию toString интерпретатор найдёт новое свойство в самом объекте и остановится.

Таким образом интерпретатор извлекает первое свойство которое будет найдено в самом объекте и дальше через него в его прототипе.

Чтобы установить свой собственный объект в качестве прототипа, а не Object.prototype по умолчанию, вы можете вызвать Object.create следующим образом:

var child = Object.create(adult);
/*
  Этот способ создания объектов позволяет нам легко заменить Object.prototype 
  по умолчанию на желаемый. В этом случае прототипом child является объект adult.
 */
child.age = 8;
/*
  Ранее у child не было своего свойства age и интерпретатор должен был смотреть дальше 
  на прототип child, чтобы его найти.
  Теперь, когда мы создали свойство age у child, интерпретатору не надо далеко ходить.
  Примечание: значение age у adult по-прежнему 26.
 */
var stringRepresentation = child.toString();
// Значение: «Мне 8».
/* 
  Примечание: мы не переопределяем свойство toString у child, так что 
  будет вызван метод у adult. Если у adult нет свойства toString, тогда будет 
  вызван метод toString у Object.prototype и мы получим [object Object] вместо «Мне 8» 
 */

Прототипом child является adult, у которого прототип Object.prototype. Такую последовательность прототипов называют цепочкой прототипов.

Удаление

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

var adult = {age:26},
    child = Object.create(adult);
    child.age = 8;
  
delete child.age;
/* Удаление свойства age из объекта child открывает age прототипа, 
   потому что оно не было переопределено. */
var prototypeAge = child.age;
// 26, потому что у child нет своего свойства age.

Перечисление

Выражение for in может перебрать все имена свойств в объекте. Перечисление будет включать функции и свойства прототипа.

var fruit = {
  apple: 2,
  orange: 5,
  pear: 1
},
sentence = 'I have ',
quantity;
for (kind in fruit){
  quantity = fruit[kind];
  sentence += quantity+' '+kind+
              (quantity===1?'':'s')+
              ', ';
}
// Следующая строка удаляет последнюю запятую.
sentence = sentence.substr(0,sentence.length-2)+'.';
// Результат: «I have 2 apples, 5 oranges, 1 pear».

Глобальное воздействие

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

Предположим, что мы разрабатываем модуль счётчика:

var myCounter = {
  number : 0,
  plusPlus : function(){
    this.number = this.number + 1;
  },
  isGreaterThanTen : function(){
    return this.number > 10;
  }
}

Этот приём часто применяется с замыканиями, чтобы внутреннее состояние не менялось извне.

У модуля теперь есть только одно имя переменной — myCounter. Если любой другой модуль на странице использует такие же имена, как number или isGreaterThanTen, то это совершенно безопасно, потому что модуль не заменит другие значения.

Автор: Сами Песси
Последнее изменение: 25.08.2015
Редакторы: Клим Щербаков