05. Переменные
В этой заметке — разговор о переменных, ибо данные без связи с переменными имеют в программе мало смысла.
Описание переменных
Переменные в JavaScript описываются при помощи нетипизированной инструкции var:
var x = 1; // x равно 1
var y; // y равно undefined
var s="", d; // s равно пустой строке, d равно undefined
При описании переменной может быть присвоено значение, в противном случае она получает значение undefined.
В языке С запись int x = 1; есть не просто описание переменной (с присвоением значения), но и описание типа: int — целое число. После такого описания переменной x нельзя присваивать значения другого типа. Язык JavaScript — нетипизированный, переменная во время исполнения кода может многократно менять тип своего значения:
var x = 1; // x -- число
x = true; // теперь x -- логическое значение
x = ""; // теперь x -- строка
x = function (t) { .... } // теперь x -- функция
Повторное описание не меняет значение переменной, если только не сопровождается повторной инициализацией:
var x = 1; // x равно 1
var x; // x равно 1 (а не undefined)
var x = 2; // x равно 2
Локальные и глобальные переменные
Переменные бывают локальные и глобальные.
Локальные переменные — это переменные, описанные в теле функции. Они видны (доступны) только внутри своей функции и перекрывают глобальные переменные с теми же именами (если такие есть).
Глобальные переменные — это переменные, описанные вне функций. Они видны (доступны) всюду: как вне всех функций, так и внутри любой из них (если только не перекрываются локальными переменными).
Говорят об области видимости переменных. Область видимости глобальных переменных — весь код. Область видимости локальных переменных — та функция, в которой они описаны.
var x = 1; // Глобальная переменная
function f()
{
var x = 2; // Локальная переменная
return x;
}
var y = f(); // Глобальная переменная y получает значение 2
x == y; // Равно false (глобальная переменная x не изменилась)
Если неописанной переменной присваивается значение, она описывается неявно, автоматически, и становится глобальной, независимо от того, где появляется: внутри функции или вне её:
function f()
{
x = 2; // Глобальная переменная
}
f();
y = x; // Глобальной переменной y присваивается 2 -- значение
// глобальной переменной x
Рекомендация. Обязательно описывайте внутренние переменные функции, иначе они станут глобальными. На самом деле, лучше всегда описывать переменные явно до их использования.
Обращение к неописанной переменной приводит к ошибке времени исполнения.
var x = 1 + z; // Приводит к ошибке, если z не описано (явно или неявно)
Ошибка возникнет и при выполнении такого кода (подумайте, почему):
function f()
{
x = 2;
}
y = x;
Аргументы функции — это локальные переменные
Аргументы функции внутри её тела становятся локальными переменными, которые инициализируются передаваемыми в функцию значениями.
var x=0, y=0, z=0;
function f(x, y, z)
{
x *= 2; // x -- локальная переменная функции
y *= 2; // y -- локальная переменная функции
z *= 2; // z -- локальная переменная функции
return x + y + z;
}
var t = f(1, 2, 3); // Равно 12 (1*2+2*2+3*2)
var k = x + y + z; // Равно 0 (0+0+0)
Глобальные переменные — свойства глобального объекта
Глобальные переменные становятся свойствами глобального объекта window (объект, описывающий окно браузера). Следующие записи эквивалентны:
var x = 1;
x = 1;
window.x = 1;
Работа переменных по ссылке и по значению
Переменные содержат:
- значения для элементарных типов данных и
- ссылки для объектов.
Пример 1
var x = 1;
var y = x; // Копирование значения:
y = y + 1; // x содержит 1, а y содержит 2
Пример 2
var x = {t:1};
var y = x; // Копирование ссылки на объект (а не самого объекта)
y.t = 2; // y.t как и x.t теперь содержит 2, ибо и x и у ссылаются
// на один объект
Пример 3
var x = "1";
var y = x; // Копирование значения
z = y.concat(2); // y содержит "1", а z содержит "12"
Последний пример некорректен в смысле доказательства хранения строк в переменных по значению. Даже если бы переменные хранили не сами строки, а ссылки на них (а так оно и есть!), результат не изменился бы.
В самом деле, пусть инструкция var y = x; копирует в переменную y не строку, а ссылку на нее. Выражение z = y.concat(2) создает новую строку (не меняя исходной y), и ссылка на новую строку записывается в z.
Так как ни одна операция со строкой не меняет исходную строку (в том числе ни один метод объекта-обёртки String), то невозможно проверить, как хранятся строки в переменных, в виде значений или ссылок.
Удобно считать, что строки хранятся по значению (как и другие данные элементарного типа). Но, реально, конечно, строки должны храниться по ссылкам ради эффективности работы JavaScript!
Манипулирование данными по значению и по ссылке
Поговорим подробнее о двух возможных способах работы с данными:
- по значению
- по ссылке
Эти два варианта манипулирования данными характерны не только для JavaScript, но и для всех других языков программирования.
Над данными можно выполнять три операции:
- Копировать
- Передавать в функцию
- Сравнивать
Если с данными работать по значению, то:
- Копируются сами данные
- Передаются в функцию сами данные
- Сравниваются сами данные
Если с данными работать по ссылке, то:
- Копируются ссылки (а не данные)
- Передаются в функцию ссылки (а не данные)
- Сравниваются ссылки (а не данные)
Примеры работы с данными по значению
Пример 1
var x = 1;
var y = x; // В y копируется единица из x
Пример 2
var x = 1;
function f(t)
{
t = t+1;
}
f(x); // В функцию передаётся копия значения переменной x.
// Изменение этой копии не повлияет на оригинал:
x == 1; // Равно true.
Пример 3
var x = 1;
var y = 1;
x==y; // Равно true так как сравниваются равные значения
Примеры работы с данными по ссылке
Пример 1
var x = {p:1}; // Переменная x содержит не сам объект, а ссылку на него
var y = x; // В y копируется ссылка на объект x (а не сам объект)
y.p = 2; // Меняется свойство объекта (на который ссылаются и x, и y)
x.p == 2; // Равно true
Пример 2
var x = {p:1}; // Переменная x содержит не сам объект, а ссылку на него
function f(t)
{ // t -- локальная переменная, она содержит ссылку на объект
t.p = 2; // t.p -- доступ к свойству глобального объекта по ссылке
}
f(x); // Функция внутри себя меняет свойство глобального объекта x
x.p == 2; // Равно true
Пример 3
var x = {p:1}; // Переменная x содержит ссылку на объект
var y = {p:1}; // Переменная y содержит другую ссылку на другой объект
x == y; // Равно false, так как ссылки разные
// (хотя сами объекты идентичны)
x.p == y.p; // Равно true, так как сравниваются числа (по значению)
В следующей таблице кратко изложены принципы работы с данными по значению и по ссылке.
По значению | По ссылке | |
---|---|---|
Копирование | Образуются две независимые копии. | Копируется не значение, а ссылка. Если значение меняется с помощью копии ссылки, то изменения видны и по оригинальной ссылке. |
Передача | В функцию передаётся копия значения. Изменение этой копии не влияет на оригинал. | В функцию передаётся ссылка на значение. Если значение меняется с помощью полученной ссылки, то эти изменения будут видны и за пределами функции. |
Сравнение | Сравниваются два разных значения, чтобы определить, совпадают ли они. | Сравниваются две ссылки. Они равны, если ссылаются на одно и то же значение, и не равны, если ссылаются на разные значения, даже если эти значения совершенно идентичны. |
В разделе задания собраны контрольные вопросы (с ответами).