JavaScript类型检测之typeof 和 instanceof 的缺陷与优化
typeof的缺陷
在JavaScript中,typeof
操作符用于检测一个变量或表达式的数据类型,返回一个字符串常量。
typeof variable
typeof
操作符返回的结果只有一下几种:
- "undefined": 如果变量没有被赋值或者值为
undefined
- "boolean": 如果变量是布尔类型
- "number": 如果变量是数值类型
- "string": 如果变量是字符串类型
- "object": 如果变量是对象或者null
- "symbol":如果变量是ES6中新增的Symbol类型
typeof
操作符看上去非常方便,但是在一下几种情况下,它会有一些缺陷:
无法准确地检测变量类型
typeof null // "object"
当我们使用typeof
来检测null
时,返回的结果是"object",而不是"null"。这是由JavaScript中的历史原因造成的,也是一个已知的BUG。
无法区分不同类型的对象
typeof {name: "John", age: 18} // "object"
typeof [1, 2, 3] // "object"
当我们使用typeof
来检测一个对象或者数组时,返回的结果都是"object",无法区分不同类型的对象。
无法检测函数类型
typeof function(){} // "function"
函数是JavaScript中的一种特殊类型,但是使用typeof
检测函数时,返回的结果是"object",而不是"function"。这也是JavaScript中的一个已知BUG。
instanceof的缺陷
在JavaScript中,instanceof
操作符用于检测一个对象是否是某个类的实例。
object instanceof constructor
instanceof
操作符返回一个布尔值,如果object
是constructor
的实例,则返回true
,否则返回false
。
虽然instanceof
操作符看上去比typeof
更加准确,但是它也存在以下几个缺陷:
无法检测基本数据类型
var value = 123;
value instanceof Number // false
在JavaScript中,number
、boolean
、string
等基本数据类型与对应的包装类型有明显的区别,但是使用instanceof
检测基本数据类型时,结果总是为false
。
无法跨域检测对象
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
var iframeArray = iframeDocument.createElement('iframe');
console.log(iframeArray instanceof window.parent.Array) // false
console.log(iframeArray instanceof iframe.contentWindow.Array) // true
如果我们想要在不同的窗口或者不同的iframe中检测对象类型时,使用instanceof
会出现问题。
优化思路
虽然typeof
和instanceof
存在一些缺陷,但是我们依然可以利用它们进行类型检测,只需要对它们的缺陷进行一些规避和优化。
规避typeof的缺陷:
var isType = function (type) {
return function(obj) {
return Object.prototype.toString.call(obj) === '[object ' + type + ']';
}
}
var isObject = isType('Object');
var isArray = isType('Array');
var isFunction = isType('Function');
var isString = isType('String');
var isNumber = isType('Number');
var isBoolean = isType('Boolean');
var isNull = isType('Null');
var isUndefined = isType('Undefined');
通过自定义一个isType
方法,来避免typeof
无法准确检测对象类型的问题。这个方法内部通过Object.prototype.toString.call
来获取精确的类型信息,并返回一个布尔值。
规避instanceof的缺陷:
function _instanceof(left, right) {
// 获取 right 的 prototype 属性值
var prototype = right.prototype;
// 获取 left 的原型对象
left = left.__proto__;
// 遍历 left 的原型链
while (true) {
// 如果 left 的原型链等于 null,则说明原型链遍历完毕
if (left === null) {
return false;
}
// 如果 left 的原型链等于 right 的 prototype,则说明 left 是 right 的实例
if (prototype === left) {
return true;
}
// 继续遍历原型链
left = left.__proto__;
}
}
自定义_instanceof
方法,通过获取左侧对象的原型链,与右侧构造函数的prototype属性进行匹配,从而规避了instanceof
不能跨域检测对象的问题。
示例说明
示例1:检测对象类型
function Person(name, age) {
this.name = name;
this.age = age;
}
var john = new Person("John", 18);
console.log(isObject(john)); // true
console.log(isFunction(john)); // false
console.log(isArray(john)); // false
在这个示例中,我们定义了一个Person
类,并创建了一个john
对象。通过使用自定义的isObject
、isFunction
和isArray
方法来检测john
对象的类型,可以得到准确的结果。
示例2:跨域检测对象
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
var iframeArray = iframeDocument.createElement('iframe');
console.log(_instanceof(iframeArray, window.parent.Array)); // true
在这个示例中,我们创建了一个新的iframe
,并在这个iframe
中创建了一个iframeArray
对象。使用自定义的_instanceof
方法来检测iframeArray
对象是否是window.parent.Array
类型时,可以得到准确的结果。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript类型检测之typeof 和 instanceof 的缺陷与优化 - Python技术站