In JavaScript, there are 5 basic data types and 1 complex data type. The basic data types are: Undefined, Null, Boolean, Number and String; the complex data type is Object, and Object is also subdivided into many specific types. Type, such as: Array, Function, Date, etc. Today we will discuss how to determine the type of a variable.
Before explaining various methods, we first define a few test variables to see how the subsequent methods can parse the types of variables. The following variables almost include the types we commonly use in actual coding. .
var num = 123; var str = 'abcdef'; var bool = true; var arr = [1, 2, 3, 4]; var json = {name:'wenzi', age:25}; var func = function(){ console.log('this is function'); } var und = undefined; var nul = null; var date = new Date(); var reg = /^[a-zA-Z]{5,20}$/; var error= new Error();
1. Use typeof detection
What we usually use most is to use typeof to detect variable types. This time, we also use typeof to detect the type of the variable:
console.log( typeof num, typeof str, typeof bool, typeof arr, typeof json, typeof func, typeof und, typeof nul, typeof date, typeof reg, typeof error ); // number string boolean object object function undefined object object object object
Judging from the output results, arr, json, nul, date, reg, error are all detected as object types, and other variables can be detected correctly. When you need to determine whether a variable is of type number, string, boolean, function, undefined, or json, you can use typeof to determine. The type of other variables cannot be determined, including null.
Also, typeof cannot distinguish between array and json types. Because when using the typeof variable, both array and json types output objects.
2. Use instance detection
In JavaScript, the typeof operator is often used to determine the type of a variable. When using the typeof operator, a problem arises when using reference types to store values. No matter what type of object is referenced, it returns "object" ". ECMAScript introduces another Java operator instanceof to solve this problem. The instanceof operator is similar to the typeof operator and is used to identify the type of object being processed. Unlike the typeof method, the instanceof method requires the developer to explicitly confirm that the object is of a specific type. For example:
function Person(){ } var Tom = new Person(); console.log(Tom instanceof Person); // true
Let’s look at the following example again:
function Person(){ } function Student(){ } Student.prototype = new Person(); var John = new Student(); console.log(John instanceof Student); // true console.log(John instancdof Person); // true
instanceof can also detect multi-level inheritance relationships.
Okay, let’s use instanceof to detect the variables above:
console.log( num instanceof Number, str instanceof String, bool instanceof Boolean, arr instanceof Array, json instanceof Object, func instanceof Function, und instanceof Object, nul instanceof Object, date instanceof Date, reg instanceof RegExp, error instanceof Error ) // num : false // str : false // bool : false // arr : true // json : true // func : true // und : false // nul : false // date : true // reg : true // error : true
From the above running results, we can see that the type of num, str and bool has not been detected, but when we create num using the following method, the type can be detected:
var num = new Number(123); var str = new String('abcdef'); var boolean = new Boolean(true);
At the same time, we also need to see that und and nul are detected Object types, so they output true, because there are no global types of Undefined and Null in js. Both und and nul belong to the Object type, so they are output. true.
3. Use constructor to detect
When using instanceof to detect variable types, we cannot detect the types of number, ‘string’, and bool. Therefore, we need to find another way to solve this problem.
constructor is originally a property on the prototype object, pointing to the constructor. However, according to the order in which the instance object searches for attributes, if there is no instance attribute or method on the instance object, it will be searched on the prototype chain. Therefore, the instance object can also use the constructor attribute.
Let’s first output the content of num.constructor, that is, what the constructor of a numeric type variable looks like:
function Number() { [native code] }
We can see that it points to the constructor of Number. Therefore, we can use num.constructor==Number to determine whether num is of type Number. Other variables are similar:
function Person(){ } var Tom = new Person(); // undefined和null没有constructor属性 console.log( Tom.constructor==Person, num.constructor==Number, str.constructor==String, bool.constructor==Boolean, arr.constructor==Array, json.constructor==Object, func.constructor==Function, date.constructor==Date, reg.constructor==RegExp, error.constructor==Error ); // 所有结果均为true
From the output results, we can see that except for undefined and null, the type of other types of variables can be determined using the constructor.
However, using constructor is not safe, because the constructor attribute can be modified, which will cause the detected results to be incorrect, for example:
function Person(){ } function Student(){ } Student.prototype = new Person(); var John = new Student(); console.log(John.constructor==Student); // false console.log(John.constructor==Person); // true
In the above example, the constructor in the Student prototype is modified to point to Person, resulting in the fact that the real constructor of the instance object John cannot be detected.
At the same time, when using instanceof and construcor, the array being judged must be declared on the current page! For example, a page (parent page) has a frame, and a page (child page) is referenced in the frame. An array is declared in the child page and assigned to a variable of the parent page. At this time, the variable is judged, Array == object.constructor; will return false; Reason:
1. Array is reference data. During the transfer process, it is only the transfer of the reference address.
2. The address referenced by the Array native object of each page is different. The corresponding constructor of the array declared on the sub-page is the Array object of the sub-page; the parent page makes the judgment, and the Array used is not equal to Array of subpages; remember, otherwise it will be difficult to track down the problem!
4. Use Object.prototype.toString.call
Let’s not care about what this is, let’s first take a look at how it detects variable types:
console.log( Object.prototype.toString.call(num), Object.prototype.toString.call(str), Object.prototype.toString.call(bool), Object.prototype.toString.call(arr), Object.prototype.toString.call(json), Object.prototype.toString.call(func), Object.prototype.toString.call(und), Object.prototype.toString.call(nul), Object.prototype.toString.call(date), Object.prototype.toString.call(reg), Object.prototype.toString.call(error) ); // '[object Number]' '[object String]' '[object Boolean]' '[object Array]' '[object Object]' // '[object Function]' '[object Undefined]' '[object Null]' '[object Date]' '[object RegExp]' '[object Error]'
从输出的结果来看,Object.prototype.toString.call(变量)输出的是一个字符串,字符串里有一个数组,第一个参数是Object,第二个参数就是这个变量的类型,而且,所有变量的类型都检测出来了,我们只需要取出第二个参数即可。或者可以使用Object.prototype.toString.call(arr)=="object Array"来检测变量arr是不是数组。
我们现在再来看看ECMA里是是怎么定义Object.prototype.toString.call的:
上面的规范定义了Object.prototype.toString的行为:首先,取得对象的一个内部属性[[Class]],然后依据这个属性,返回一个类似于”[object Array]”的字符串作为结果(看过ECMA标准的应该都知道,[[]]用来表示语言内部用到的、外部不可直接访问的属性,称为“内部属性”)。利用这个方法,再配合call,我们可以取得任何对象的内部属性[[Class]],然后把类型检测转化为字符串比较,以达到我们的目的。
5. jquery中$.type的实现
在jquery中提供了一个$.type的接口,来让我们检测变量的类型:
console.log( $.type(num), $.type(str), $.type(bool), $.type(arr), $.type(json), $.type(func), $.type(und), $.type(nul), $.type(date), $.type(reg), $.type(error) ); // number string boolean array object function undefined null date regexp error
Does it feel familiar when you see the output? Yes, it is the second parameter of the result output using Object.prototype.toString.call (variable) above.
Let’s first compare the detection results of all the above methods. The horizontal row is the detection method used, and the vertical row is each variable:
类型判断 | typeof | instanceof | constructor | toString.call | $.type |
num | number | false | true | [object Number] | number |
str | string | false | true | [object String] | string |
bool | boolean | false | true | [object Boolean] | boolean |
arr | object | true | true | [object Array] | array |
json | object | true | true | [object Object] | object |
func | function | true | true | [object Function] | function |
und | undefined | false | - | [object Undefined] | undefined |
nul | object | false | - | [object Null] | null |
date | object | true | true | [object Date] | date |
reg | object | true | true | [object RegExp] | regexp |
error | object | true | true | [object Error] | error |
Advantages | Easy to use and can output results directly | Can detect complex types | Basically able to detect all types | Detect all types | - |
Disadvantages | Too few types detected | Basic types cannot be detected and cannot cross iframes | Cannot cross iframes, and the constructor is easily modified | Under IE6, undefined and null are both Object | - |
这样对比一下,就更能看到各个方法之间的区别了,而且Object.prototype.toString.call和$type输出的结果真的很像。我们来看看jquery(2.1.2版本)内部是怎么实现$.type方法的:
// 实例对象是能直接使用原型链上的方法的 var class2type = {}; var toString = class2type.toString; // 省略部分代码... type: function( obj ) { if ( obj == null ) { return obj + ""; } // Support: Android<4.0, iOS<6 (functionish RegExp) return (typeof obj === "object" || typeof obj === "function") ? (class2type[ toString.call(obj) ] || "object") : typeof obj; }, // 省略部分代码... // Populate the class2type map jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); });
我们先来看看jQuery.each的这部分:
// Populate the class2type map jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); }); //循环之后,`class2type`的值是: class2type = { '[object Boolean]' : 'boolean', '[object Number]' : 'number', '[object String]' : 'string', '[object Function]': 'function', '[object Array]' : 'array', '[object Date]' : 'date', '[object RegExp]' : 'regExp', '[object Object]' : 'object', '[object Error]' : 'error' }
再来看看type方法:
// type的实现 type: function( obj ) { // 若传入的是null或undefined,则直接返回这个对象的字符串 // 即若传入的对象obj是undefined,则返回"undefined" if ( obj == null ) { return obj + ""; } // Support: Android<4.0, iOS<6 (functionish RegExp) // 低版本regExp返回function类型;高版本已修正,返回object类型 // 若使用typeof检测出的obj类型是object或function,则返回class2type的值,否则返回typeof检测的类型 return (typeof obj === "object" || typeof obj === "function") ? (class2type[ toString.call(obj) ] || "object") : typeof obj; }
当typeof obj === "object" || typeof obj === "function"时,就返回class2type[ toString.call(obj)。到这儿,我们就应该明白为什么Object.prototype.toString.call和$.type那么像了吧,其实jquery中就是用Object.prototype.toString.call实现的,把'[object Boolean]'类型转成'boolean'类型并返回。若class2type存储的没有这个变量的类型,那就返回”object”。
除了”object”和”function”类型,其他的类型则使用typeof进行检测。即number, string, boolean类型的变量,使用typeof即可。