在 中,有 5 种基本数据类型和 1 种复杂数据类型。 基本数据类型有: 、 Null、 、 和; 复杂的数据类型中还细分为很多具体的类型,如:Array、Date等。 今天我们将讨论如何确定变量的类型。
在解释各种方法之前,我们首先定义几个测试变量,看看后续的方法如何解析变量的类型。 下面的变量几乎包含了我们在实际编码中常用的类型。
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. 使用测试
我们平时用的最多的就是检测变量类型。 这次,我们还使用以下方法检测变量的类型:
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
从输出结果来看,arr、json、nul、date、reg、error均被检测为类型,其他变量均能正确检测。 当需要判断一个变量是否是 , , , , , json 类型时,可以用它来判断。 无法确定其他变量的类型,包括 null。
而且,无法区分 array 和 json 类型。 因为使用这个变量时,输出的是array和json类型。
2. 使用情况检测
在 中,运算符经常用于确定变量的类型。 使用运算符时,使用引用类型存储值时会出现问题。 无论引用什么类型的对象,都会返回“”。 Java 引入了另一个运算符来解决这个问题。 运算符与运算符类似,用于标识正在处理的对象的类型。 与方法不同,方法要求开发人员显式确认对象属于特定类型。 例如:
function Person(){ } var Tom = new Person(); console.log(Tom instanceof Person); // true
让我们再看一下下面的例子:
function Person(){ } function Student(){ } Student.prototype = new Person(); var John = new Student(); console.log(John instanceof Student); // true console.log(John instancdof Person); // true
它还可以检测多级继承关系。
好吧,我们用上面的变量来检测一下:
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
从上面的运行结果可以看出,没有检测到num、str、bool的类型,但是当我们使用下面的方法创建num时,可以检测到类型:
var num = new Number(123); var str = new String('abcdef'); var boolean = new Boolean(true);
同时我们还要看到und和nul是检测到的类型,所以输出true,因为js中不存在Null这样的全局类型。 und 和 nul 都是类型,所以输出 true。
3. 使用情况检测
使用变量类型检测时,我们无法检测“”、bool 的类型。 因此,我们需要寻找另一种方法来解决这个问题。
原本是原型对象上的一个属性,指向构造函数。 但是,根据实例对象查找属性的顺序,如果实例对象上没有实例属性或方法,则会在原型链上查找。 因此,实例对象也可以使用属性。
我们先输出num.的内容,也就是数值类型变量的构造函数是什么样的:
() {[代码]}
我们可以看到它指向了构造函数,所以我们可以使用num.==来判断num是否是类型,其他变量类似:
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
从输出结果可以看出,除了for和null之外,其他类型的变量都可以用来判断类型。
但使用起来并不安全,因为属性可以被修改,这会导致检测结果不正确,例如:
function Person(){ } function Student(){ } Student.prototype = new Person(); var John = new Student(); console.log(John.constructor==Student); // false console.log(John.constructor==Person); // true
上面的例子中,原型被修改为指向,导致无法检测到实例对象John的真正构造函数。
同时使用and时,要判断的数组必须在当前页面声明! 例如,一个页面(父页面)有一个框架,框架中引用了一个页面(子页面)。 在子页面中声明一个数组,并将其分配给父页面的变量。 这时候判断变量,Array == .; 将返回 false; 原因:
1.数组是参考数据。 传输过程中,只是参考地址的传输。
2、每个页面的Array原生对象引用的地址不同。 子页面上声明的数组对应的构造函数就是子页面的Array对象; 父页面判断,使用的Array不等于子页面的Array; 记住,否则很难追查问题!
4. 使用...调用
不管这是什么,我们先看看它是如何检测变量类型的:
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]'
从输出结果来看,...call()输出的是一个字符串,字符串里有一个数组。 第一个参数是,第二个参数是变量的类型,所有变量的类型都已经检测出来了,我们只需要取出第二个参数即可。 或者可以使用...call(arr)=="Array"来检测变量arr是否是数组。
现在让我们看一下 ECMA 中如何定义...call:
复制代码代码如下:
..( ) 当 为 时,执行以下步骤:
1. 获取此 的 [[Class]]。
2. 由三个“[”、(1)、“]”组成的值。
3.(2)
上面的规范定义了...的行为:首先获取对象的一个内部属性[[Class]],然后根据这个属性返回一个类似“[Array]”的字符串作为结果(看过的人ECMA标准应该众所周知,[[]]用于表示语言内部使用的、不能从外部直接访问的属性,称为“内部属性”)。 利用该方法,结合call,我们可以获取任意对象的内部属性[[Class]],然后将类型检测转化为字符串比较,从而达到我们的目的。
5.$.type的实现
提供了$.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
当你看到输出时,是不是感觉很熟悉? 是的,就是上面使用...call()输出的结果的第二个参数。
我们先来对比一下以上所有方法的检测结果。 横排是使用的检测方法,纵排是各个变量:
类型判断
。称呼
$.type
编号
错误的
真的
[ ]
斯特
错误的
真的
[ ]
布尔值
错误的
真的
[ ]
到达
真的
真的
[大批]
大批
json
真的
真的
[ ]
功能
真的
真的
[ ]
和
错误的
[ ]
无
错误的
[无效的]
无效的
日期
真的
真的
[日期]
日期
注册
真的
真的
[ ]
错误
真的
真的
[错误]
错误
优势
使用方便,可直接输出结果
检测复杂类型的能力
基本上可以检测所有类型
检测所有类型
缺点
检测到的类型太少
基本类型无法检测且无法交叉
不可交叉且易修改
在IE6下,null是
通过这样的比较,你可以更好地看出方法之间的差异,而且……call和$type的输出结果确实很相似。 我们看一下$.type方法内部是如何实现的(2.1.2版本):
// 实例对象是能直接使用原型链上的方法的 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(); });
我们先看一下.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: 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; }
当 obj === "" || 时 obj === "", 返回 [ .call(obj). 至此,我们应该明白为什么...call和$.type如此相似了。 事实上,它是使用...call来实现的,它将'[]'类型转换为''类型并返回。 如果未存储该变量的类型,则返回“”。
除了“”和“”类型之外,还使用其他类型进行检测。 也就是说,可以使用 , 类型的变量。
«
»