04. Объекты
Контрольные вопросы и задания
-
Какое значение получит переменная x. Ответ объясните.
var ob = {}; for (var i=0; i<5; i++) ob["t"+2*i] = i-1; var t = ob.t8/3; var x = ob.t4+ob["t"+(t+5-0)];
ОтветОтвет: 3. В результате работы цикла получен объект:
ob = {t0:-1,t2:0,t4:1,t6:2,t8:3}Вычисляем t и x:
t = ob.t8/3 = 3/3 = 1
ob.t4+ob["t"+(t+5-0)] = 1+ob["t"+6] = 1+ob["t6"] = 1+2 = 3 -
Напишите функцию, которая получает объект и возвращает строку с перечислением всех свойств объекта и их значений.
function getProperties(object) { } var ob = {x:10,y:"Роботландия",z:true}; var n = getProperties(ob); // Равно строке "x:10, y:Роботландия, z:true" // Заметьте, запятой после последней записи нет
Ответfunction getProperties(object) { var str = ""; for(var x in object) str += x + ":" + object[x] + ","; if (str) str = str.substring(0, str.length-1); return str; } var ob = {x:10,y:"Роботландия",z:true}; var n = getProperties(ob); // Равно строке "x:10, y:Роботландия, z:true" // Заметьте, запятой после последней записи нет
-
Какое значение получит переменная x. Ответ объясните.
var ob = {x:2,y:3}; ob.f = function (x) { return this.x + x; } var x = ob["y"] + ob.f(5);
ОтветОтвет: число 10.
ob["y"] = ob.y = 3
ob.f(5) = this.x + 5 = 2+5 = 7Пояснение: this внутри функции ссылается на объект в контексте которого функция вызывается.
-
Какие значения получат переменные x, y, z? Ответ объясните.
var k = 10; var p = {x:k--/5,y:k/3}; var c = {p:p, color:"red"}; var ob = {"p":p,"c":c}; var x = ob.p.x; var y = ob.c.p.y; var z = ob.c.color.substring(2).concat(ob.p.y);
Ответx равно 2
y равно 3
z равно "d3"Пояснения
Для начала перепишем объект p со свойствами-константами:
p равно {x:k--/5,y:k/3} равно {x:2,y:3}.
Оператор «запятая» выполняется слева направо, значит, сначала вычисляем x:k--/5. Выражение k-- выполняется так: сначала k участвует в выражении, затем уменьшается на 1. После вычисления x:k--/5 получим x:2, а k станет равным 9. Теперь вычисляем y:k/3, получаем y:3. Итак, p равно {x:2,y:3}.
Оператор «точка» выполняется слева направо.
Для x:
ob.p; // Указывает на {x:2,y:3} ob.p.x; // Указывает на 2 var x = ob.p.x; // Копирует 2 в переменную x
Для y:
ob.c; // Указывает на {p:p, color:"red"} // которое есть {p:{x:2,y:3}, color:"red"} ob.c.p; // Указывает на {x:2,y:3} ob.c.p.y; // Указывает на 3 var y = ob.c.p.y; // Копирует 3 в переменную y
Для z:
ob.c // Указывает на {p:{x:2,y:3}, color:"red"} ob.c.color // Указывает на "red" ob.c.color.substring(2) // Равно "d" ob.c.color.substring(2).concat(ob.p.y) // Равно "d".concat(3) равно "d3" var z = ob.c.color.substring(2).concat(ob.p.y); // Копирует "d3" в z
Дополнительное пояснение
Обратите внимание, запись вида ob.p есть операция доступа к свойству объекта. Оператор точка — двухместный. Слева должен быть объект или выражение, значением которого является ссылка на объект, а справа должен располагаться идентификатор (выражение не допускается) — точное имя свойства объекта.
Результат выполнения операции точка — ссылка на значение этого свойства (неважно, какого типа это свойство, элементарного или объектного).
Оператор точка имеет самый высокий приоритет (среди всех других операторов) и выполняется слева направо. Так:
ob.p // Указывает на объект {x:2,y:3} ob.p.x; // Указывает на число 2, эквивалентно (ob.p).x
А вот инструкция
var x = ob.p.x;
копирует число 2 в переменную x. Естественно, копируется само число, а не ссылка. Если теперь изменить свойство x:
ob.p.x = 3;
то это не изменит значение переменной x, оно по-прежнему будет равно 2.
Если бы объект ob.p имел, например, вид {x:[2],y:3}, то по инструкции
var x = ob.p.x; // ссылка на массив [2]
в переменную x был бы скопирован не массив [2], а ссылка на него. И если теперь изменить свойство:
ob.p.x[0] = 3;
то это будет заметно и через переменную x — ссылка останется прежней, но она будет относится к массиву [3].
Рассмотрим для примера ещё раз цепочку вычислений
ob.c.color.substring(2).concat(ob.p.y)
Вычисляем «точку» слева направо:
ob.c // Указывает на свойство c, которое есть // объект {p:{x:2,y:3}, color:"red"} // Свойство с, конечно, содержит не сам объект, а ссылку на него, // но это не важно сейчас (пока нет копирования, передачи в // функцию или сравнения). // То есть ob.c указывает на объект {p:{x:2,y:3}, color:"red"} ob.c.color // Указывает на "red", эквивалентно (ob.c).color ob.c.color.substring(2) // Равно "d", эквивалентно // (((ob.c).color).substring)(2) // В игру вступил новый оператор () -- вызов // функции (метода, в данном случае). // Функция вернула "d", и эта строка стала // теперь значением всего выражения // ob.c.color.substring(2) ob.c.color.substring(2).concat(ob.p.y)
Последняя строка в нашем случае эквивалентна:
"d".concat(ob.p.y) // а это эквивалентно ("d".concat)(ob.p.y) // то есть сначала вычисляется ("d".concat), // результат -- ссылка на метод, а затем // вызывается сам метод при помощи оператора ()
Результат вычисления последнего выражения — строка "d3". Заметим попутно, что при выполнении операции "d".concat для строки "d" создаётся объект-обёртка класса String, которая после выполнения операции уничтожается.
-
Создайте конструктор объекта Complex для работы с комплексными числами вида x+yi. После определения конструктора должен правильно работать следующий код:
var n = new Complex(1,3); // Создали объект для 1+3i var m = new Complex(3); // Создали объект для 3+0i var p = n.add(m); // Получили объект для 4+3i var s = p.toString(); // Получили строку "4+3i" var m = p.valueOf(); // Получили число 5 (модуль комплексного числа)
Что нужно знать о комплексных числах:
(x1+y1i) + (x2+y2i) = (x1+x2) + (y1+y2)i модуль числа x+yi равен квадратному корню от (x*x + y*y)
Ответ// Конструктор объекта "комплексное число x+yi" function Complex(x,y) { // Свойства this.x = x; this.y = y || 0; // Методы this.add = function (t) { var z = new Complex(this.x,this.y); z.x += t.x; z.y += t.y; return z; }; this.toString = function () { return this.x + "+" + this.y + "i"; }; this.valueOf = function () { return Math.sqrt(this.x*this.x + this.y*this.y); }; } var n = new Complex(1,3); // Создали объект для 1+3i var m = new Complex(3); // Создали объект для 3+0i var p = n.add(m); // Получили объект для 4+3i var s = p.toString(); // Получили строку "4+3i" var m = p.valueOf(); // Получили число 5 (модуль комплексного числа)
Пояснения
В функции add есть «подозрительная» инструкция:
var z = new Complex(this.x,this.y);
Как это так? Внутри конструктора Complex есть ссылка на сам конструктор Complex! Похоже на бесконечную рекурсию. Будет ли это работать?
Давайте разбираться. Дело в том, что описание функции — это просто текст, содержащий код. И этот код начинает работать только тогда, когда функция вызывается.
Посмотрим по шагам.
Шаг 1
var n = new Complex(1,3); // Создали объект для 1+3i
- Оператор new создает пустой объект (где-то в памяти) и передает ссылку на этот объект через ключевое слово this конструктору Complex.
-
Конструктор Complex делает следующее:
- По инструкции this.x = x; создает в объекте свойство x и копирует в него 1.
-
По инструкции
создает в объекте свойство add и копирует в него ссылку на текст кода функции, которую он размещает где-то в памяти.this.add = function (t) { var z = new Complex(this.x,this.y); z.x += t.x; z.y += t.y; return z; };
-
Инструкция
не приводит к созданию нового свойства. Свойство toString в объекте уже есть, оно наследуется от Object. Но старое значение toString стирается и записывается новое — ссылка на текст кода функции, который размещается где-то в памяти.this.toString = function () { return this.x + "+" + this.y + "i"; };
-
Инструкция
не приводит к созданию нового свойства. Свойство valueOf в объекте уже есть, оно наследуется от Object. Но старое значение valueOf стирается и записывается новое — ссылка на текст кода функции, который размещается где-то в памяти.this.valueOf = function () { return Math.sqrt(this.x*this.x + this.y*this.y); };
- Ссылка на созданный (где-то в памяти) объект записывается в переменную n.
Как видите, никакой рекурсии не возникло. Коды объявленных методов скопированы в созданный объект, но они не запускались.
Шаг 2
var m = new Complex(3); // Создали объект для 3+0i
Аналогично создается новый объект Complex и ссылка на него помещается в переменную m.
Шаг 3
И вот оно, самое интересное:
var p = n.add(m); // Получили объект для 4+3i
В объекте n запускается код функции add:
this.add = function (t) { var z = new Complex(this.x,this.y); z.x += t.x; z.y += t.y; return z; };
-
Работает инструкция
Она создает (где-то в памяти) новый объект Complex (как подробно было описано ранее) и записывает ссылку на него в локальную переменную z.var z = new Complex(this.x,this.y);
-
Выполняются действия:
z.x += t.x; z.y += t.y;
- Возвращается ссылку на объект и уничтожается локальная переменная z (сам объект, конечно, остается в памяти).
Переменная p получает ссылку на объект Complex, полученный из метода n.add.