С год назад мы тут разбирались с определением типов в 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"; } // ... } |
Такие дела…