Домой / Осваиваем ПК / Javascript проверить тип переменной. Оператор получения типа typeof

Javascript проверить тип переменной. Оператор получения типа typeof

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 и как их обойти.

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

a = (b > 0) && (c + 1 != d); flag = !(status = 0);

Таблица 14.5. Логические операторы

Оператор Описание

! НЕ (логическая инверсия)

&& И (логическое умножение)

|| ИЛИ (логическое сложение)

Таблица 14.6. Результаты выполнения операторов И и ИЛИ

Операнд 1

Операнд 2

Таблица 14.7. Результаты выполнения оператора НЕ

Оператор получения типа typeof

Оператор получения типа typeof возвращает строку, описывающую тип данных операнда. Операнд, тип которого нужно узнать, помещают после этого оператора и заключают в круглые скобки:

s = typeof("str");

В результате выполнения этого выражения в переменной s окажется строка "string" , обозначающая строковый тип.

Все значения, которые может вернуть оператор typeof , перечислены в табл. 14.8.

Таблица 14.8. Значения, возвращаемые оператором typeof

Тип данных

Возвращаемая строка

Строковый

Числовой

Таблица 14.8 (окончание)

Тип данных

Возвращаемая строка

Логический

Совместимость и преобразование типов данных

Настала пора рассмотреть еще два важных вопроса: совместимость типов данных и преобразование одного типа к другому.

Что получится, если сложить два числовых значения? Правильно - еще одно числовое значение. А если сложить число и строку? Трудно сказать... Тут JavaScript сталкивается с проблемой несовместимости типов данных и пытается сделать эти типы совместимыми, преобразуя один из них к другому. Сначала он пытается преобразовать строку в число и, если это удается, выполняет сложение. В случае неудачи число будет преобразовано в строку, и две полученные строки будут объединены. Например, в результате выполнения Web-сценария из листинга 14.6 значение переменной b при сложении с переменной a будет преобразовано в числовой тип; таким образом, переменная c будет содержать значение 23.

Листинг 14.6

var a, b, c, d, e, f; a = 11;

b = "12"; c = a + b;

d = "JavaScript"; e = 2;

Но поскольку значение переменной d нельзя преобразовать в число, значение e будет преобразовано в строку, и результат - значение f - станет равным

Логические величины преобразуются либо в числовые, либо в строковые, в зависимости от конкретного случая. Значение true будет преобразовано в число 1 или строку "1" , а значение false - в 0 или "0" . И наоборот, число 1 будет преобразовано в значение true , а число 0 - в значение false . Также в false будут преобразова-

ны значения null и undefined .

Часть III. Поведение Web-страниц. Web-сценарии

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

Приоритет операторов

Последний вопрос, который мы здесь разберем, - приоритет операторов. Как мы помним, приоритет влияет на порядок, в котором выполняются операторы в выражении.

Пусть имеется следующее выражение:

В этом случае сначала к значению переменной b будет прибавлено значение c , а потом из суммы будет вычтено 10. Операторы этого выражения имеют одинаковый приоритет и поэтому выполняются строго слева направо.

Теперь рассмотрим такое выражение:

Здесь сначала будет выполнено умножение значения c на 10, а уже потом к полученному произведению будет прибавлено значение b . Оператор умножения имеет больший приоритет, чем оператор сложения, поэтому порядок "строго слева направо" будет нарушен.

Самый низкий приоритет у операторов присваивания. Вот почему сначала вычисляется само выражение, а потом его результат присваивается переменной.

В общем, основной принцип выполнения всех операторов таков: сначала выполняются операторы с более высоким приоритетом, а уже потом - операторы с более низким. Операторы с одинаковым приоритетом выполняются в порядке их следования (слева направо).

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

Таблица 14.9. Приоритет операторов (в порядке убывания)

Операторы

Описание

++ -- - ~ ! typeof

Инкремент, декремент, смена знака, логическое НЕ, определение типа

Умножение, деление, взятие остатка

Сложение и объединение строк, вычитание

Операторы сравнения

Логическое И

Глава 14. Введение в Web-программирование. Язык JavaScript

Таблица 14.9 (окончание)

Операторы

Описание

Логическое ИЛИ

Условный оператор (см. ниже)

= <оператор>=

Присваивание, простое и сложное

В НИМАНИЕ!

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

Но что делать, если нам нужно нарушить обычный порядок выполнения операторов? Воспользуемся скобками. При такой записи заключенные в скобки операторы выполняются первыми:

a = (b + c) * 10;

Здесь сначала будет выполнено сложение значений переменных b и c , а потом получившаяся сумма будет умножена на 10.

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

a = ((b + c) * 10 - d) / 2 + 9;

Здесь операторы будут выполнены в такой последовательности:

1. Сложение b и c .

2. Умножение полученной суммы на 10.

3. Вычитание d из произведения.

4. Деление разности на 2.

5. Прибавление 9 к частному.

Если удалить скобки:

a = b + c * 10 - d / 2 + 9;

то порядок выполнения операторов будет таким:

1. Умножение c и 10.

2. Деление d на 2.

3. Сложение b и произведения c и 10.

4. Вычитание из полученной суммы частного от деления d на 2.

5. Прибавление 9 к полученной разности.

Получается совсем другой результат, не так ли?

Оператор 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-объекты являются объектами, но не функциями. Например.

  • 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 «.

Выводы

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

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