Контакти

Оператор одержання типу typeof. JavaScript, typeof, типи і класи 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 \u003d new Add (); Sum s \u003d 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 \u003d new Add (); Sum s \u003d new Sum (); // Виконуємо приведення типів a \u003d s as Add; if (a! \u003d null) Console.WriteLine ( "Перетворення пройшло успішно"); else Console.WriteLine ( "Помилка при перетворенні"); Console.ReadLine ();)))

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

a \u003d (b\u003e 0) && (c + 1! \u003d d); flag \u003d! (status \u003d 0);

Таблиця 14.5. Логічні оператори

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

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

&& І (логічне множення)

|| АБО (логічне додавання)

Таблиця 14.6. Результати виконання операторів І і АБО

операнд 1

операнд 2

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

Оператор одержання типу typeof

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

s \u003d 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 \u003d 11;

b \u003d "12"; c \u003d a + b;

d \u003d "JavaScript"; e \u003d 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 \u003d (b + c) * 10;

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

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

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

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

1. Додавання b і c.

2. Множення отриманої суми на 10.

3. Віднімання d з твору.

4. Розподіл різниці на 2.

5. Додаток 9 до приватного.

Якщо видалити дужки:

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

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

1. Множення c і 10.

2. Розподіл d на 2.

3. Складання b і твори c і 10.

4. Віднімання з отриманої суми частки від розподілуd на 2.

5. Додаток 9 до отриманої різниці.

Виходить зовсім інший результат, чи не так?

JavaScript або JS (Скорочено) не простий мову і початківці розробники дізнаються про це не відразу. Спочатку вони дізнаються ази і все здається барвистим і прекрасним. Заходячи трохи глибше, з'являються JavaScript масиви, об'єкти, callback'і і все подібне, що часто виносить мозок.

В JavaScript важливо правильно перевіряти тип змінної. Припустимо ви хочете дізнатися чи є змінна масивом або об'єктом? Як це правильно перевірити? У цьому конкретному випадку, є хитрощі під час перевірки і саме про них буде ця запис. Давайте відразу приступимо.

Перевірка типу змінної

Наприклад вам потрібно перевірити є змінна об'єктом, масивом, рядком або числом. Для цього можна використовувати typeof, але вона не завжди видасть правду і в прикладі нижче я покажу чому.

Цей приклад я написав, щоб наочно показати, чому typeof не завжди правильний вибір.

Var _comparison \u003d (string: "рядок", int: 99, float: 13.555, object: (hello: "привіт"), array: new Array (1, 2, 3)); // поверне масив з ключами об'єкта var _objKeys \u003d Object.keys (_comparison); for (var i \u003d 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 \u003d 9.22; var _notFloatNumber \u003d 9; console.log (isFloat (_floatNumber)); console.log (isFloat (_notFloatNumber)); console.log (isFloat ( "")); function isFloat (n) (return Number (n) \u003d\u003d\u003d n && n% 1! \u003d\u003d 0;)

Функція isFloat () виконує перевірку всіх значень на числа з плаваючою крапкою. Спочатку перевіряється дорівнює змінна n числу (Number (n) \u003d\u003d\u003d n) і якщо так, то робиться ще одна перевірка на розподіл із залишком і якщо залишок є, то повертається булевої ( true або false) Результат (n% 1! \u003d\u003d 0).

В наведеному вище прикладі вона повертає true, false і false. Перше значення має floatтип, друге немає - це звичайне число і останнім всього лише порожній рядок, яка не підходить під правила.

Друга проблема: масив визначився як об'єкт

У найпершому прикладі, масив відобразився як об'єкт і це не дуже добре, тому що іноді вам потрібно використовуватися саме цей тип і нічого більше.

Є кілька способів для перевірки змінної на тип масиву.

Перший варіант (хороший варіант). Перевіряємо приналежність data до масиву за допомогою instanceof ().

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

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

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

Третій варіант (найкращий, але довгий). Для зручності, ви можете зробити цей спосіб функцією. Використовуючи Object, ми робимо. Якщо результат Object.prototype.toString.call (data) НЕ дорівнює значить змінна не масив ().

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

Останній результат у вигляді зручної функції:

Function isArray (data) (return Object.prototype.toString.call (data) \u003d\u003d "")

Тепер ви можете викликати функції isArray () і як аргумент задати масив або щось інше і подивитися результат.

Післямова

Запис вийшла досить великою, ніж спочатку замислювалася. Але я їй задоволений, тому що вона досить коротко і чітко описує труднощі при перевірці змінних в JavaScript і як їх обійти.

Якщо у вас залишилися які-небудь питання - пишіть їх нижче до цього запису. Я буду радий допомогти.

  • Undefined: «undefined»
  • Null: «object»
  • Boolean: «boolean»
  • Number: «number»
  • String: «string»
  • Function: «function»
  • Все інше: «object»

До цієї таблиці слід додати наступні зауваження:

1. typeof null \u003d\u003d\u003d "object".

Теоретично тут тонкий момент. У мовах зі статичної типізацією змінна об'єктного типу може не містити об'єкта (NULL, nil, нульовий покажчик).

Практично - в JavaScript це незручно. Тому розробники ES 5.1 збираються зробити більш інтуїтивно зрозумілу річ: typeof null \u003d\u003d\u003d "null".

Але так як у нас поки кругом ES3, не помиліться, наприклад, на такому:

/ * Функція шукає якийсь об'єкт і повертає його або null якщо нічого не знайдено * / function search () () var obj \u003d search (); if (typeof obj \u003d\u003d\u003d "object") (// чи дійсно ми знайшли об'єкт (FAIL) obj.method ();)

2. Не забуваємо про об'єкти-обгортки (typeof new Number (5) \u003d\u003d\u003d "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 \u003d\u003d\u003d "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 \u003d\u003d\u003d Number; (Number .NaN) .constructor \u003d\u003d\u003d Number; (True) .constructor \u003d\u003d\u003d Boolean; ( "String") .constructor \u003d\u003d\u003d String;

(5) .constructor \u003d\u003d\u003d Number; (Number.NaN) .constructor \u003d\u003d\u003d Number; (True) .constructor \u003d\u003d\u003d Boolean; ( "String"). Constructor \u003d\u003d\u003d 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 \u003d\u003d\u003d Function); // true

console.log ((function () ()) instanceof Function); // true console.log ((function () ()). Constructor \u003d\u003d\u003d 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 \u003d\u003d\u003d "Number".

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

висновки

Жоден з представлених способів не дає стовідсоткового визначення типу / класу значення у всіх браузерах. Однак, в сукупності вони дозволяють це зробити.

Найближчим часом спробую зібрати всі дані в таблички і привести приклад функції-определялкі.



Сподобалася стаття? поділіться їй