СоХабр закрыт.

С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.

TypeOf — проверка типа сущности в JavaScript в черновиках

TypeOf javascript library

Начиная какой-то более-менее сложный и комплексный проект с использованием JavaScript, перед разработчиком может встать выбор о том, какую библиотеку\фреймворк использовать. Если речь идет о веб-разработке, то jQuery — это, априори, библиотека №1 — лаконичная, простая, удобная. Но за слепой любовью к ней кроется немало, если и не устаревших, то уж точно таких вещей, которые можно сделать лучше.

Ответвимся к проверкам типов


Вышеупомянутая библиотека имеет статический метод для проверки типов — это jQuery.type. Окунемся в тонкости его работы и, для этого, взглянем на исходный код. Увидим что изначально пришедший аргумент проверяется на null и, при положительном результате, тип аргумента приводится к строке и возвращается. В иных случаях идет проверка средствами языковых конструкций и выполняется сравнительно точная типизация, но минус такого подхода — медлительность из-за вызова метода toString и неточное определение типа, в следствии чего приходится писать дополнительные конструкции, позволяющие «уточнить» тип сущности.

Теперь расскажу о «синей таблетке» — том, что предлагаю я — TypeOf


Основная идея TypeOf — это проверка по конструктору. В исходном коде библиотеки не выполняется никаких методов, а только сверяется конструктор с объектами или же, в нескольких особых случаях, выполняется дополнительная проверка по свойству length для определения массивов, деление по модулю — для float либо нативная языковая функция isNaN — для проверки случаев, когда не удается привести тип к числу.

Например, вы можете делать так:

TypeOf(document.getElementsByTagName('script')).is('node'); // true
TypeOf(document.getElementsByTagName('script')).is('array'); // true

node — это подтип объекта, тип которого — array. Звучит немного запутанно, но JavaScript — язык в котором все — это объект.

Попробую немного прояснить ситуацию и приведу еще один блок кода:

jQuery.type(document.getElementById('demo')).is('node'); // true
jQuery.type(document.getElementById('demo')).is('object'); // true

Разница вышеуказанных примеров заключается в том, что в первом мы получаем массив объектов типа NodeObject, а во втором — лишь один объект этого же типа. Все просто и jQuery так не умеет :)

Самая занятная ситуация касается проверки типа так называемого plainObject. Далее я приведу примеры того, как это проверить с помощью jQuery и TypeOf.

// jQuery
jQuery.isPlainObject({}); // true

// TypeOf
TypeOf({}).is('plain'); // true

А теперь взгляните на то, как проверяется plainObject у jQuery и как это делает TypeOf. Думаю слова излишни.

Подробнее о функции TypeOf и возвращаемом ею объекте


Функция всегда будет возвращать объект, содержащий два свойства: type, subtype и метод: is. По названиям, думаю, не мудрено догадаться о том, что на них возложено. По умолчанию, свойство subtype — это булево значение которое равняется false. Измениться оно может лишь в случаях, когда TypeOf принимает в качестве аргумента число с плавающей точкой, так называемый not-a-number и объект, будь он простым либо содержащим NodeObject объекты.

Методом is вы можете проверить тип, передав ему один из следующих аргументов: NaN, null, bool, node, plain, float, array, string, number, object, function, undefined. В тех случаях, когда проверка будет осуществляться на NaN, float, node или plain метод будет сверять аргумент с подтипом. Важно заметить, что параметр метода — это строка и результатом выполнения, например, следующего кода:

TypeOf(null).is(null);

будет false.

Поддержка браузеров и производительность


TypeOf с уверенностью будет работать везде, где есть JavaScript, т.е. абсолютно в любом браузере. Также, на основе проведенных тестов, с уверенностью могу заявить что TypeOf, в среднем, отрабатывает в полтора-два раза быстрее.

Демонстрация и репозиторий


  • https://github.com/BR0kEN-/TypeOf — тут располагается git-репозиторий проекта и в его описании вы сможете подробно узнать об объекте, возвращаемом функцией TypeOf.
  • http://br0ken-.github.io/TypeOf/ — демонстрация. При попытке открыть страницу в устаревших браузерах будут проблемы из-за отсутствия функционала, используемого только для демонстрации. Чтобы тестировать саму библиотеку в экстремальных условиях, вам необходимо работать с ее исходным кодом напрямую.



С наилучшими пожеланиями,
Сергей Бондаренко, Web Developer в Propeople Ukraine.

комментарии (9)

+7
swwwfactory ,  

Чем instanceof не угодил?

Кстати, как у этого метода насчет сходства с результатами проверки через instanceof?

Ссылка с демонстрацией не работает.

0
TheShock ,  
Кстати, как у этого метода насчет сходства с результатами проверки через instanceof?

instanceof проверяет всю цепочку прототипов, т.е. если какой-то класс унаследовался от Array, то instanceof будет правдив и на сам класс и на его родителя.

Чем instanceof не угодил?

Что у метода из топика (проверка конструктора), что у инстансоф есть существенньій недостаток — они оба паршиво работают при множестве разньіх window. К примеру в нашей игре можно писать плагиньі (greasemonkey/tampermonkey) и где-то внутри кода elem instanceof Array сработает для массива из игрьі, но вернет ложь для самого настоящего массива из плагина.
+6
RubaXa ,  

Какая-то неправильная утилита, больше похожа на баг:

TypeOf(/Ну-ну/).is('node'); // true   WAT?!

function WAT (length) {
   this.length = length;
}
WAT.prototype = {
    constructor: WAT,
    length: 0
};
TypeOf(new WAT).is("object"); // true
TypeOf(new WAT(15)).is("array"); // true    WAT?!

TypeOf(document.getElementsByTagName('script')).is('array')
— это именно HTMLCollection, а не массив, общее у них только свойство length и всё.
+2
Sombressoul ,   * (был изменён)

На входе:

TypeOf( document.querySelectorAll('body') );

На выходе:
type: "array"
subtype: "node"

— всё гуд (по логике функции).

На входе:
TypeOf( [ document.querySelector('body') ] );

На выходе:
type: "array"
subtype: false

— wat?

Что я делаю не так?
+4
jet-black ,  

Порой мне кажется, что люди тратят больше времени на оформление readme файла, чем на написание самой библиотеки.

+2
SowingSadness ,  

Я хочу сделать ваш день:

function TypeOf(value) { return Object.prototype.toString.call(value).split(' ')[1].split(']')[0].toLowerCase() }

+1
MuLLtiQ ,  

Можно чуть проще :)

function TypeOf(value) { return Object.prototype.toString.call(value).slice(8, -1).toLowerCase() }
0
Sombressoul ,   * (был изменён)

А можно чуть сложней… Рекурсивный разборщик на типы:

function TypeOf(v){
    var type    = ({}).toString.call(v).toLowerCase().match(/\s([a-z]+)/)[1];
    
    var map_arr = function( arr ){
        var tmp = [];
        for( var i = 0, l = arr.length; i<l; i++ ){ 
            tmp.push( TypeOf( arr[i] ) ); 
        }; 
        return ( tmp.length ? tmp : false ); 
    };
    
    var map_obj = function( obj ){ 
        var tmp = {}; 
        for( var key in obj ){ 
            if ( obj.hasOwnProperty( key ) ) tmp[ key ] = TypeOf( obj[ key ] ); 
        }; 
        return ( Object.keys( tmp ).length ? tmp : false ); 
    };
    
    return {
        type    : type ,
        subtype :
            (
                type === 'array' 
                ? map_arr( v )
                :
                    (
                        type === 'object' 
                        ? map_obj( v )
                        : false
                    )
            )
    };
}

// test: TypeOf( {a:[],b:false,c:{},d:[1,undefined,null,{}],e:"bugaga"} )
0
xGromMx ,  

Как по мне такой подход довольно не плох

({}).toString.call(whatever)

({}).toString.call(10) => [object Number]
({}).toString.call([]) => [object Array]