JavaScript: типы, классы и фреймы

С год назад мы тут разбирались с определением типов в JavaScript.

Рассмотрим ещё один аспект: фреймы. Из фрейма во фрейм можно передавать данные.

Например, у нас есть функция, которая обрабатывает массив немного по другому, чем объект:

function func(value) {
    if (value instanceof Array) {
        // массив
    } else {
        // объект
    }
}

И на странице есть iframe, в котором следующее:

window.parent.func([1, 2, 3]);

Это будет работать некорректно. Так как мы сравниваем значение с нашим Array из нашего основного окна. А значение создано во фрейме, в котором свой window и свой window.Array и именно от него производится этот массив.

Попробуем теперь определить тип и «класс» значения с учётом того, что оно может прийти не из нашего окна.

Как определить, что значение пришло из фрейма

Все нескалярные значения наследуются от Object, так что для них можно проверить так:

value instanceof Object; // value наследуется от Object нашего окна, значит оно также из него.

typeof и сравнение со значением

typeof работает, как обычно, и для «не наших» значений:

function isBoolean(value) {
    return typeof value === "boolean";
}
 
function isNumber(value) {
    return typeof value === "number"; // не для new Number()
}
 
function isString(value) {
    return typeof value === "string"; // не для new String()
}

typeof null, по-прежнему «object», но его лучше определять сравнением:

function isNull(value) {
    return value === null;
}
 
function isUndefined(value) {
    return value === undefined;
}

В обозримом будущем мы, надеюсь, наконец, сможем с помощью typeof определять и function, но пока здесь есть нюансы:

typeof alert === "object"; // Host-функции в IE < 9
typeof /./ === "function"; // В недавних версиях Chrome
typeof HTMLCollection === "function"; // В некоторых версиях Safari

Object.prototype.toString

Также работает одинаково для «наших» значений и значений из фрейма. С его помощью можно точно и во всех браузерах определить следующие «классы»: array, regexp, Error, Date а также String, Number и Boolean созданные через конструкторы (new Number(1)):

function isDate(value) {
    return Object.prototype.toString.call(value) === "Date";
}
function isError(value) {
    return Object.prototype.toString.call(value) === "Error";
}
 
// ...

Для массива ещё можно вспомнить, что у нас уже почти повсемесно ES5:

function isArray(value) {
    if (Array.isArray) {
        return Array.isArray(value); // сработает даже, если Array и value из разных фреймов
    }
    return Object.prototype.toString.call(value); // для старых IE
}

DOM для нормальных браузеров

Для нормальных браузеров нам осталось определить HTMLCollection и DOM-элементы.

function isHTMLCollection(value) {
    var repr = Object.prototype.toString.call(value);
    return (
        (repr === "[object HTMLCollection]")        // Firefox, IE 9+
        && (repr === "[object NodeList]")           // Chrome, Opera, Safari
        && (repr === "[object HTMLAllCollection]")  // Chrome для document.all
        )
    );
}

Все DOM-элементы имеют строковое представление вида «[object HTML<тег>Element]»:

function isElement(value) {
    return (Object.prototype.toString.call(value).indexOf("[object HTML") === 0);
}

Интересный момент:

element instanceof window.HTMLElement;

В Firefox это срабатывает для DOM-элементов всегда, вне зависимости из каких фреймов этот элемент или HTMLElement. При том, что element instanceof Object работает только для одного фрейма.

В других браузерах это срабатывает только для своего фрейма, ну а в IE < 9 HTMLElement вообще нет (зато в IE8 есть Element).

IE < 9

Для старых IE мы не можем таким образом определить ни DOM-элементы, ни host-функции, ни даже arguments.

Здесь, видимо, остаётся только проверять «контрольные свойства»:

if (!value.constructor) { // нет конструктора - хост-объект в IE < 9
    if (value.nodeType === 1) {
        return "element";
    }
    // ...
}

Такие дела…

Комментариев нет »

Комментариев ещё нет

Leave a comment