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

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

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

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

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

var і = 3;

Это переменная представляет собой свойство глобального объекта, т. е. свойство объекта Window. Сценарий во фрейме А может явно сослаться на эту переменную как на свойство с помощью объекта window:

window.і

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

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

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

 parent.В.f();

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

var f = parent.В.f;

Теперь сценарий во фрейме А сможет вызывать функцию как f() точно так же, как сценарий во фрейме В.

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

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

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

var s = new Set();

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

var s = new parent.Set();

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

var Set = top.Set();
var s = new Set();

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

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

Объект WindowProxy

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

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

Этот промежуточный объект ведет себя как настоящий глобальный объект, за исключением того, что живет гораздо дольше. Если бы вы могли сравнить эти два объекта, вы едва ли смогли бы отличить их. Однако на самом деле нет никакой возможности сослаться на настоящий клиентский глобальный объект. Глобальный объект находится на вершине цепочки областей видимости, но свойства window, self, top, parent и frames ссылаются на промежуточные объекты. Метод window.open() возвращает промежуточный объект. Даже ключевое слово this в функциях верхнего уровня ссылается на промежуточный объект, а не на настоящий глобальный объект.[33]1

****************************************

Оглавление книги


Генерация: 1.562. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз