typeof、prototype、instanceof、constructor方法比较
typeof
我们最常见的是typeof,通常我们会看到1
console.log(typeof('mamba')) //string
这种写法,但是typeof可是一个一元运算符,就跟加减乘除一样1
console.log(typeof 'mamba') //string
《JavaScript权威指南》中对typeof的介绍:
typeof 是一元操作符,放在其单个操作数的前面,操作数可以是任意类型。返回值为表示操作数类型的一个字符串。
在 ES6 前,JavaScript 共六种数据类型,分别是:
1 | Undefined、Null、Boolean、Number、String、Object |
然而当我们使用 typeof 对这些数据类型的值进行操作的时候,返回的结果却不是一一对应,分别是:1
undefined、object、boolean、number、string、object
注意以上都是小写的字符串。Null 和 Object 类型都返回了 object 字符串。
尽管不能一一对应,但是typeof却能检测出函数类型:1
2function a() {}
console.log(typeof a) //function
所以 typeof 能检测出六种类型的值,但是,除此之外 Object 下还有很多细分的类型呐,如 Array、Function、Date、RegExp、Error 等。
例如:1
2
3
4var date = new Date();
var error = new Error();
console.log(typeof date); // object
console.log(typeof error); // object
返回的都是 object ,所以就要用Object.prototype.toString来检测了
Object.prototype.toString(通用但很繁琐)
当 toString 方法被调用的时候,下面的步骤会被执行:
- 如果 this 值是 undefined,就返回 [object Undefined]
- 如果 this 的值是 null,就返回 [object Null]
- 让 O 成为 ToObject(this) 的结果
- 让 class 成为 O 的内部属性 [[Class]] 的值
- 最后返回由 “[object “ 和 class 和 “]” 三个部分组成的字符串
通过规范,我们至少知道了调用 Object.prototype.toString 会返回一个由 “[object “ 和 class 和 “]” 组成的字符串,而 class 是要判断的对象的内部属性。
让我们写个 demo:1
2
3
4
5console.log(Object.prototype.toString.call(undefined)) // [object Undefined]
console.log(Object.prototype.toString.call(null)) // [object Null]
var date = new Date();
console.log(Object.prototype.toString.call(date)) // [object Date]
1 | var number = 1; // [object Number] |
除以上11种还有
1 | console.log(Object.prototype.toString.call(Math)); // [object Math] |
既然有了 Object.prototype.toString 这个神器!那就让我们写个 type 函数帮助我们以后识别各种类型的值吧!
写一个 type 函数能检测各种类型的值,如果是基本类型,就使用 typeof,引用类型就使用 toString。此外鉴于 typeof 的结果是小写,我也希望所有的结果都是小写。
考虑兼容性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16var class2type = {};
// 生成class2type映射
"Boolean Number String Function Array Date RegExp Object Error".split(" ").map(function(item, index) {
class2type["[object " + item + "]"] = item.toLowerCase();
})
function type(obj) {
// 一箭双雕
if (obj == null) {
return obj + "";
}
return typeof obj === "object" || typeof obj === "function" ?
class2type[Object.prototype.toString.call(obj)] || "object" :
typeof obj;
}不考虑兼容性
1
2
3function type(val){
return Object.prototype.toString.call(val).split(' ')[1].split(']')[0].toLowerCase();
}
isFunction
有了 type 函数后,我们可以对常用的判断直接封装,比如 isFunction:1
2
3function isFunction(obj) {
return type(obj) === "function";
}
数组
jQuery 判断数组类型,旧版本是通过判断 Array.isArray 方法是否存在,如果存在就使用该方法,不存在就使用 type 函数。1
2
3var isArray = Array.isArray || function( obj ) {
return type(obj) === "array";
}
待续···
1 | var a = "iamstring."; |
判断已知对象类型的方法:instanceof
1 | alert(c instanceof Array) ---------------> true |
注意:instanceof后面一定要是
对象类型,并且大小写不能错,该方法适合一些条件选择或分支。根据对象的constructor判断:constructor
1
2
3alert(c.constructor === Array) ----------> true
alert(d.constructor === Date) -----------> true
alert(e.constructor === Function) -------> true注意:
constructor 在类继承时会出错
例子:
function A(){};
function B(){};
A.prototype = new B(); //A继承自B
var aObj = new A();
alert(aobj.constructor === B) -----------> true;
alert(aobj.constructor === A) -----------> false;
而instanceof方法不会出现该问题,对象直接继承和间接继承的都会报true:
alert(aobj instanceof B) ----------------> true;
alert(aobj instanceof B) ----------------> true;
言归正传,解决construtor的问题通常是让对象的constructor手动指向自己:
aobj.constructor = A; //将自己的类赋值给对象的constructor属性
alert(aobj.constructor === A) -----------> true;
alert(aobj.constructor === B) -----------> false; //基类不会报true了;