JavaScript, typeof, типы и классы. Как правильно проверить тип переменной в JavaScript Проверка типа переменной

JavaScript или JS (сокращенно) не простой язык и начинающие разработчики узнают об этом не сразу. По началу они узнают азы и все кажется красочным и прекрасным. Заходя чуть глубже, появляются JavaScript массивы, объекты, callback’и и все подобное, что часто выносит мозг.

В JavaScript важно правильно проверять тип переменной. Допустим вы хотите узнать является ли переменная массивом или объектом? Как это правильно проверить? В этом конкретном случае, есть хитрости во время проверки и именно о них будет эта запись. Давайте сразу приступим.

Проверка типа переменной

Например вам нужно проверить является переменная объектом, массивом, строкой или числом. Для этого можно использовать typeof , но она не всегда выдаст правду и в примере ниже я покажу почему.

Этот пример я написал, чтобы наглядно показать, почему typeof не всегда правильный выбор.

Var _comparison = { string: "строка", int: 99, float: 13.555, object: {hello: "привет"}, array: new Array(1, 2, 3) }; // Вернет массив с ключами объекта var _objKeys = Object.keys(_comparison); for(var i = 0; i <= _objKeys.length - 1; i++) { // выведем в консоль тип каждой переменной console.log(typeof _comparson[_objKeys[i]]); }

Результат выполнения кода:

String number number object object

Верно? — Нет, конечно. Есть две проблемы. Каждая из них будет подробно описана и предложено решение.

Первая проблема: float число, выводится как number

Comparison.float не является числом и вместо number должно быть float (число с плавающей точкой).Чтобы это исправить, можно создать функцию с проверкой как в коде ниже.

Var _floatNumber = 9.22; var _notFloatNumber = 9; console.log(isFloat(_floatNumber)); console.log(isFloat(_notFloatNumber)); console.log(isFloat("")); function isFloat(n){ return Number(n) === n && n % 1 !== 0; }

Функция isFloat() выполняет проверку всех значений на числа с плавающей точкой. Сначала проверяется равна ли переменная n числу (Number(n) === n) и если да, то делается еще одна проверка на деление с остатком и если остаток есть, то возвращается булевой (true или false ) результат (n % 1 !== 0).

В примере выше она возвращает true , false и false . Первое значение имеет float тип, второе нет — это обычное число и последнее всего лишь пустая строка, которая не подходит под правила.

Вторая проблема: массив определился как объект

В самом первом примере, массив отобразился как объект и это не очень хорошо, так как иногда вам нужно использоваться именно этот тип и ничего больше.

Есть несколько способов для проверки переменной на тип массива.

Первый вариант (хороший вариант). Проверяем принадлежность data к массиву с помощью instanceof ().

Var data = new Array("hello", "world"); var isArr = data instanceof Array;

Второй вариант (хороший вариант). Метод Array.isArray() возвращает булевое значение, которе будет зависеть от того является ли переменная массивом или нет ().

Var data = new Array("hello", "world"); var isArr = Array.isArray(data);

Третий вариант (самый лучший, но длинный). Для удобности, вы можете сделать этот способ функцией. Используя Object, мы делаем . Если результат Object.prototype.toString.call(data) не равен значит переменная не массив ().

Var data = new Array("hello", "world"); var isArr = Object.prototype.toString.call(data) == ""; console.log(isArr);

Последний результат в виде удобной функции:

Function isArray(data) { return Object.prototype.toString.call(data) == "" }

Теперь вы можете вызвать функции isArray() и как аргумент задать массив или что-то иное и посмотреть результат.

Послесловие

Запись получилась достаточно большой, чем изначально задумывалась. Но я ей доволен, потому что она достаточно кратко и четко описывает затруднения при проверке переменных в JavaScript и как их обойти.

Если у вас остались какие-либо вопросы — пишите их ниже к этому записи. Я буду рад помочь.

Динамическая идентификация типов

Динамическая идентификация типов (RTTI) позволяет определить тип объекта во время выполнения программы. Она оказывается полезной по целому ряду причин. В частности, по ссылке на базовый класс можно довольно точно определить тип объекта, доступного по этой ссылке. Динамическая идентификация типов позволяет также проверить заранее, насколько удачным будет исход приведения типов, предотвращая исключительную ситуацию в связи с неправильным приведением типов. Кроме того, динамическая идентификация типов является главной составляющей рефлексии.

Для поддержки динамической идентификации типов в C# предусмотрены три ключевых слова: is, as и typeof. Каждое из этих ключевых слов рассматривается далее по очереди.

Оператор is

Конкретный тип объекта можно определить с помощью оператора is. Ниже приведена его общая форма:

выражение is тип

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

Ниже приведен пример применения оператора is:

Using System; namespace ConsoleApplication1 { class Add { } class Sum: Add { } class Program { static void Main() { Add a = new Add(); Sum s = new Sum(); if (a is Add) Console.WriteLine("Переменная a имеет тип Add"); if (s is Sum) Console.WriteLine("Тип переменной s унаследован от класса Add"); Console.ReadLine(); } } }

Оператор as

Иногда преобразование типов требуется произвести во время выполнения, но не генерировать исключение, если исход этого преобразования окажется неудачным, что вполне возможно при приведении типов. Для этой цели служит оператор as, имеющий следующую общую форму:

выражение as тип

где выражение обозначает отдельное выражение, преобразуемое в указанный тип.

Если исход такого преобразования оказывается удачным, то возвращается ссылка на тип, а иначе - пустая ссылка. Оператор as может использоваться только для преобразования ссылок, идентичности, упаковки, распаковки. В некоторых случаях оператор as может служить удобной альтернативой оператору is. В качестве примера рассмотрим следующую программу:

Using System; namespace ConsoleApplication1 { class Add { } class Sum: Add { } class Program { static void Main() { Add a = new Add(); Sum s = new Sum(); // Выполняем приведение типов a = s as Add; if (a != null) Console.WriteLine("Преобразование прошло успешно"); else Console.WriteLine("Ошибка при преобразовании"); Console.ReadLine(); } } }

Результатом выполнения данной программы будет успешное преобразование.

  • Undefined: «undefined»
  • Null: «object»
  • Boolean: «boolean»
  • Number: «number»
  • String: «string»
  • Function: «function»
  • Всё остальное: «object»

К этой таблице следует добавить следующие замечания:

1. typeof null === "object" .

Теоретически здесь тонкий момент. В языках со статической типизацией переменная объектного типа может не содержать объекта (NULL, nil, нулевой указатель).

Практически — в JavaScript это неудобно. Поэтому разработчики ES 5.1 собираются сделать более интуитивно понятную вещь: typeof null === "null" .

Но так как у нас пока кругом ES3, не ошибитесь, например, на таком:

/* Функция ищет какой-то объект и возвращает его или null если ничего не найдено */ function search() {} var obj = search(); if (typeof obj === "object") { // действительно ли мы нашли объект (FAIL) obj.method(); }

2. Не забываем про объекты-обёртки (typeof new Number(5) === "object").

3. И не забываем на право браузеров творить что угодно с host-объектами.

Не удивляйтесь тому, что Safari упорно считает HTMLCollection типом function , а IE ранее 9-й версии держат нашу любимую функцию alert() за object . Также Chrome раньше считал RegExp за function , но теперь, кажется образумился и отвечает на неё object .

toString()

Пытаться узнать тип значения по результату его метода toString() бессмысленно. Во всех «классах» этот метод переопределён на свой.

Для вывода отладочной информации метод хорош, но типа переменной по нему не определить.

Object.prototype.toString()

Хотя toString внутри конкретных «классов» переопределён, у нас всё равно есть его изначальная реализация из Object. Попробуем воспользоваться ей:

console.log ( Object .prototype .toString .call (value) ) ;

console.log(Object.prototype.toString.call(value));


Клинтон разбавляет эту тягомотину

Как ни странно, метод этот работает на удивление хорошо.

Для скалярных типов возвращает , , , .

Самое смешное, что даже new Number(5) на котором засбоил typeof здесь возвращает .

На null и undefined метод давать сбои. Разные браузеры возвращают, то ожидаемые и , то , то вообще . Впрочем определить тип этих двух значений легко можно и без этого.

Интересное начинается, когда мы подходим к объектам (тем, у которых typeof === "object").

built-in объекты отрабатывают, практически, на ура:

  • {} —
  • Date —
  • Error —
  • RegExp —

Единственно, выпадает из списка arguments , который то , то .
С host-объектами опять всё хуже.

В IE DOM-объекты стали становиться «нормальными» объектами только с 8-й версии и то не совсем до конца. Поэтому в IE 6-8 все эти объекты (HTMLCOllection, DOMElement, TextNode , а заодно document и window) приводятся просто к .

Во всех остальных браузерах (включая IE9) с результатом toString уже можно что-то делать. Хотя тоже всё непросто: HTMLCollection там , то . window — то , то , то . Но из этого уже можно попытаться что-то выудить.

Сложнее с DOMElement: он выводится в виде , — свой формат для каждого тега. Но и здесь регулярка нам поможет.

С другими host-объектами (в тестах location и navigator) примерно таже история. Везде, кроме IE, их можно идентифицировать по строке.

Из минусов использования Object.prototype.toString():

1. Возможность сия не освящена стандартом. И мы здесь должны скорее радоваться, что всё так удачно работает, а не сокрушаться по поводу некоторых изъянов.

2. Определение типа по синтаксическому разбору строки, возвращаемой методом, который вообще не для определения типа, да и ещё вызывается на объекте к которому не относится, оставляет некоторый осадок на душе.

3. В старых IE, как видно, host-объекты нормально не идентифицировать.

Однако же, это вполне рабочая штука при использовании совместно с другими средствами.


Конструкторы

Ну и, наконец, конструкторы. Кто может лучше сказать о «классе» объекта в JS, если не его конструктор?

У null и undefined нет ни объектов-обёрток, ни конструкторов.

У остальных скалярных типов обёртки есть, соответственно, можно получить и конструктор:

(5 ) .constructor === Number ; (Number .NaN ) .constructor === Number ; (true ) .constructor === Boolean ; ("string" ) .constructor === String ;

(5).constructor === Number; (Number.NaN).constructor === Number; (true).constructor === Boolean; ("string").constructor === String;

А вот instanceof здесь не пройдёт:

5 instanceof Number ; // false Number .NaN instanceof Number ; // false true instanceof Boolean ; // false "string" instanceof String ; // false

5 instanceof Number; // false Number.NaN instanceof Number; // false true instanceof Boolean; // false "string" instanceof String; // false

(instanceof сработает для многострадального new Number(5))

С функциями (которые к тому же объекты) пройдёт и instanceof:

console.log ( (function () { } ) instanceof Function ) ; // true console.log ( (function () { } ) .constructor === Function ) ; // true

console.log((function () {}) instanceof Function); // true console.log((function () {}).constructor === Function); // true

Все объекты встроенных классов также легко идентифицируются по конструкторам: Array , Date , RegExp , Error .

Одна проблема возникает здесь с arguments , конструктор которого Object .

А вторая с самим Object , вернее как отнести к нему объект, созданный через пользовательский конструктор.

Так можно определить только базовый объект:

obj instanceof Object;

Как один из вариантов определения — перебрать все остальные возможные типы (Array , Error …) и если ни под один не подпал — «object».

Конструкторы и host-объекты

С host-объектами всё хуже.

Начнём с того, что IE до 7-й версии включительно их вообще за нормальные объекты не считает. У них там просто нет конструкторов и прототипов (во всяком случае программисту до них не достучаться).

В других браузерах дела получше. Конструкторы есть и по ним можно определить класс значения. Только называются они в разных браузерах по разному. Например для HTMLCollection конструктор будет или HTMLCollection или NodeList , а то и вовсе NodeListConstructor .

Также следует определить базовый конструктор для DOMElement. В FF, это, например, HTMLElement , от которого уже наследуются HTMLDivElement и другие.

Подлянку подкидывают FireFox ниже 10-й версии и Opera ниже 11. Там конструктор коллекции — Object .

constructor.name

Ещё у конструкторов есть свойство name , которое может быть полезно.

Оно содержит имя функции-конструктора, например, (5).constructor.name === "Number" .

Однако:
1. В IE его нет вообще, даже в 9-м.
2. В Host-объекты браузеры опять лепят каждый что горазд (а зачастую те вообще не имеют этого свойства). В Opera’е у DOMElement имя конструктора вообще Function.prototype .
3. arguments вновь « object «.

Выводы

Ни один из представленных способов не даёт стопроцентного определения типа/класса значения во всех браузерах. Однако, в совокупности они позволяют это сделать.

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

Оператор typeof возвращает строку, указывающую тип операнда.

Синтаксис

Операнд следует за оператором typeof:

Typeof operand

Параметры

operand является выражением, представляющим объект или примитив , тип которого должен быть возвращен.

Описание

В следующей таблице приведены возможные возвращаемые значения typeof . Дополнительная информация о типах и примитивах находится на странице .

Примеры

// Числа typeof 37 === "number"; typeof 3.14 === "number"; typeof(42) === "number"; typeof Math.LN2 === "number"; typeof Infinity === "number"; typeof NaN === "number"; // несмотря на то, что это "Not-A-Number" (не число) typeof Number(1) === "number"; // никогда не используйте эту запись! // Строки typeof "" === "string"; typeof "bla" === "string"; typeof "1" === "string"; // обратите внимание, что число внутри строки всё равно имеет тип строки typeof (typeof 1) === "string"; // typeof всегда вернёт в этом случае строку typeof String("abc") === "string"; // никогда не используйте эту запись! // Booleans typeof true === "boolean"; typeof false === "boolean"; typeof Boolean(true) === "boolean"; // никогда не используйте эту запись! // Символы typeof Symbol() === "symbol" typeof Symbol("foo") === "symbol" typeof Symbol.iterator === "symbol" // Undefined typeof undefined === "undefined"; typeof declaredButUndefinedVariable === "undefined"; typeof undeclaredVariable === "undefined"; // Объекты typeof {a: 1} === "object"; // используйте Array.isArray или Object.prototype.toString.call // чтобы различить обычные объекты и массивы typeof === "object"; typeof new Date() === "object"; // То что ниже приводит к ошибкам и проблемам. Не используйте! typeof new Boolean(true) === "object"; typeof new Number(1) === "object"; typeof new String("abc") === "object"; // Функции typeof function() {} === "function"; typeof class C {} === "function"; typeof Math.sin === "function";

null

// Это было определено с рождения JavaScript typeof null === "object";

В первой реализации JavaScript значения были представлены парой тип тега и значение. Тип тега для объектов равнялся 0. null был представлен как нулевой указатель (0x00 в большинстве платформ). Следовательно, тип тега для null равнялся нулю, поэтому возвращаемое значение typeof является фиктивным. ()

Исправление было предложено в ECMAScript (через отключение), но было отклонено . Это привело бы к тому, что typeof null === "null" .

Использование оператора new

// Все функции-конструкторы, созданные с помощью "new", будут иметь тип "object" var str = new String("String"); var num = new Number(100); typeof str; // Вернёт "object" typeof num; // Вернёт "object" // Но существует исключение для конструктора Function var func = new Function(); typeof func; // Вернёт "function"

Регулярные выражения

Вызываемые регулярные выражения были нестандартным дополнением в некоторых браузерах.

Typeof /s/ === "function"; // Chrome 1-12 Не соответствует ECMAScript 5.1 typeof /s/ === "object"; // Firefox 5+ Соответствует ECMAScript 5.1

Ошибки, связанные с временными мёртвыми зонами

До ECMAScript 2015, гарантировалось, что оператор typeof вернёт строку для любого операнда, с которым он был вызван. Это изменилось после добавления неподнимающихся объявлений let and const с блочной областью видимости. Теперь, если переменные объявлены с помощью let и const , и для них вызывается typeof в блоке объявления переменных, но до объявления, то выбрасывается ReferenceError . Поведение отличается от необъявленных переменных, для которых typeof вернёт "undefined". Переменные с блочной областью видимости находятся в "временной мёртвой зоне ", которая длится от начала блока до момента объявления переменных. В этой зоне попытка доступа к переменным выбрасывает исключение.

Typeof undeclaredVariable === "undefined"; typeof newLetVariable; let newLetVariable; // ReferenceError typeof newConstVariable; const newConstVariable = "hello"; // ReferenceError

Исключения

Во всех текущих браузерах существует нестандартный host-объект document.all , который имеет тип Undefined.

Typeof document.all === "undefined";

Хотя спецификация разрешает собственные имена типов для нестандартных экзотических объектов, требуется чтобы эти имена отличались от предопределённых. Ситуация, когда document.all имеет тип undefined должна рассматриваться как исключительное нарушение правил.

Спецификации

Спецификация Статус Комментарии
ECMAScript Latest Draft (ECMA-262)
Черновик
ECMAScript 2015 (6th Edition, ECMA-262)
Определение "The typeof Operator" в этой спецификации.
Стандарт
ECMAScript 5.1 (ECMA-262)
Определение "The typeof Operator" в этой спецификации.
Стандарт
ECMAScript 3rd Edition (ECMA-262)
Определение "The typeof Operator" в этой спецификации.
Стандарт
ECMAScript 1st Edition (ECMA-262)
Определение "The typeof Operator" в этой спецификации.
Стандарт Изначальное определение. Реализовано в JavaScript 1.1

Совместимость с браузерами

Update compatibility data on GitHub

Компьютеры Мобильные Server
Chrome Edge Firefox Internet Explorer Opera Safari Android webview Chrome для Android Firefox для Android Opera для Android Safari on iOS Samsung Internet Node.js
typeof Chrome Полная поддержка 1 Edge Полная поддержка 12 Firefox Полная поддержка 1 IE Полная поддержка 3 Opera Полная поддержка Да Safari Полная поддержка Да WebView Android Полная поддержка 1 Chrome Android Полная поддержка 18 Firefox Android Полная поддержка 4 Opera Android Полная поддержка Да Safari iOS Полная поддержка Да Samsung Internet Android Полная поддержка 1.0 nodejs Полная поддержка Да

Легенда

Полная поддержка Полная поддержка

IE-специфичные замечания

В IE 6, 7 и 8 многие host-объекты являются объектами, но не функциями. Например.

Тонкости