JavaScript. Подробное руководство, 6-е издание, стр. 197

14.8.3. JavaScript во взаимодействующих окнах

Для каждого окна или фрейма имеется свой собственный объект

<b>Window</b>
, определяющий независимый контекст выполнения JavaScript-кода. Но если сценарий в одном окне или фрейме может сослаться на другое окно или фрейм (и если политика общего происхождения не препятствует этому), сценарий в одном окне или фрейме может взаимодействовать со сценарием в другом окне или фрейме.

Представим себе веб-страницу с двумя элементами

<b>&lt;iframe&gt;</b>
, с именами «А» и «В», и предположим, что эти фреймы содержат документы, полученные с одного и того же сервера, и эти документы содержат взаимодействующие сценарии. Сценарий во фрейме А определяет переменную і:

<b>var і = 3;</b>

Это переменная представляет собой свойство глобального объекта, т. е. свойство объекта

<b>Window</b>
. Сценарий во фрейме А может явно сослаться на эту переменную как на свойство с помощью объекта
<b>window</b>
:

<b>window.і</b>

Благодаря тому что сценарий во фрейме В может ссылаться на объект

<b>Window</b>
во фрейме А, он также может ссылаться на свойства этого объекта окна:

<b>parent.А.і = 4; // Изменит значение переменной во фрейме А</b>

Напомню, что ключевое слово

<b>function</b>
, определяющее функцию, объявляет переменную так же, как ключевое слово
<b>var</b>
. Если JavaScript-код во фрейме В объявляет функцию
<b>f</b>
, эта функция станет глобальной переменной во фрейме В, и сценарий во фрейме В сможет вызывать функцию
<b>f</b>
как
<b>f()</b>
. Однако сценарий во фрейме А должен ссылаться на
<b>f</b>
как на свойство объекта
<b>Window</b>
во фрейме В:

<b> parent.В.f();</b>

Если сценарий во фрейме А часто вызывает эту функцию, ее можно присвоить переменной во фрейме А, чтобы было удобнее ссылаться на функцию:

<b>var f = parent.В.f;</b>

Теперь сценарий во фрейме А сможет вызывать функцию как

<b>f()</b>
точно так же, как сценарий во фрейме В.

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

<b>f</b>
ссылается на глобальные переменные, поиск этих переменных выполняется в свойствах фрейма В, даже когда функция вызывается из фрейма А.

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

<b>Set</b>
из примера 9.6.

Сценарии в окне верхнего уровня смогут создавать новые объекты

<b>Set</b>
, как показано ниже:

<b>var s = new Set();</b>

Но сценарии в обоих фреймах должны явно ссылаться на конструктор

<b>Set()</b>
, как на свойство родительского окна:

<b>var s = new parent.Set();</b>

В качестве альтернативы сценарий в любом фрейме может определить собственные переменные для более удобного обращения к функции-конструктору:

<b>var Set = top.Set(); </b>

<b>var s = new Set();</b>

В отличие от пользовательских классов, предопределенные классы, такие как

<b>Set</b>
,
<b>Date</b>
и
<b>RegExp</b>
, оказываются автоматически определенными во всех окнах. Однако следует заметить, что каждое окно имеет независимую копию конструктора и независимую копию объекта-прототипа. Например, каждое окно имеет собственную копию конструктора
<b>String()</b>
и объекта
<b>String.prototype</b>
. Поэтому, если вы создадите новый метод для работы с JavaScript-строками и сделаете его методом класса
<b>String</b>
, присвоив его объекту
<b>String.prototype</b>
в текущем окне, все строки в этом окне смогут использовать новый метод, однако этот новый метод будет недоступен строкам, определенным в других окнах.

Тот факт, что каждый объект

<b>Window</b>
имеет собственные объекты-прототипы, означает, что оператор
<b>instanceof</b>
не будет работать с объектами в разных окнах. Например, оператор
<b>instanceof</b>
будет возвращать
<b>false</b>
при сопоставлении строки из фрейма В с конструктором
<b>String()</b>
из фрейма А. В разделе 7.10 описываются похожие сложности с определением типов массивов в разных окнах.

Объект WindowProxy

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

<b>Window</b>
, представляющий фрейм или окно, даже если в этот фрейм или окно загружается новый документ.

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

<b>Window</b>
, в действительности не является глобальным объектом - это промежуточный объект. Всякий раз, когда сценарий читает или изменяет значение свойства объекта
<b>Window</b>
, этот объект запрашивает или изменяет свойство с тем же именем глобального объекта окна или фрейма. В спецификации HTML5 этот промежуточный объект называется объектом
<b>WindowProxy</b>
, но далее в этой книге мы продолжим использовать имя
<b>Window</b>
.