instanceof
本意是检测某个对象是否是指定构造函数的实例
- 用其检测数据类型,主要是弥补 typeof 不能细分对象的缺陷的
- 只要 instanceof 左边是原始值,则结果都是 false
- 理解1:instanceof不能用来检测原始值类型
- 理解2:实例都是对象类型的,所以原始值不可能是任何类的实例
- instanceof 的右边必须是一个函数
否则报错:TypeError: Right-hand side of ‘instanceof’ is not callable
- 而且函数必须具备 prototype 属性 「否则报错:TypeError: Function has non-object prototype ‘undefined’ in instanceof check」
- 虽然Symbol/BigInt不能被new执行,但是他们确实是构造函数,可以出现在 instanceof 的右侧
检测机制(原理):
- 首先看 构造函数 是否具备 Symbol.hasInstance 属性,如果具备这个属性(方法),则把方法执行
- 构造函数Symbol.hasInstance -> true/false
- 只要是支持ES6的浏览器,函数必然具备Symbol.hasInstance属性,因为这个属性在 Function.prototype
没有这个属性,再看 构造函数.prototype 是否出现在 对象 的原型链上
局限性:
无法检测原始值
- 无法区分是否为“标准普通对象(纯粹对象)”「所有对象都是Object的实例,基于 instanceof 检测都是true」
- 检测结果仅供参考「因为我们可以重构实例对象的原型链」 ```javascript function Fn() { } Fn.prototype = Object.create(Array.prototype) Fn.prototype.constructor = Fn let f = new Fn
class Fn extends Array { constructor() { super() // Array.call(this) } } let f = new Fn console.log(f instanceof Array) //true
let obj = {} obj.proto = Array.prototype console.log(obj instanceof Array) //true
let arr = [] console.log(arr instanceof Array) //true // ArraySymbol.hasInstance console.log(arr instanceof RegExp) //false // RegExpSymbol.hasInstance console.log(arr instanceof Object) //true // …
<a name="nrwBC"></a>
#### 重写instanceof
```javascript
var instance_of = function instance_of(obj, Ctor) {
// 确保右侧是标准的构造函数
if (typeof Ctor !== 'function') throw new TypeError('Right-hand side of instanceof is not callable')
var proto = Ctor.prototype
if (!proto) throw new TypeError('Function has non-object prototype undefined in instanceof check')
// 如果左边是原始值,则直接返回false
if (obj === null || !/^(object|function)$/.test(typeof obj)) return false
// 如果构造函数具备 Symbol.hasInstance ,则直接基于其处理即可
if (typeof Symbol !== 'undefined') {
var hasInstance = Ctor[Symbol.hasInstance]
if (hasInstance) {
return hasInstance.call(Ctor, obj)
}
}
// 如果不支持,才基于原型链进行查找匹配
return proto.isPrototypeOf(obj)
//一般直接用isPrototypeOf进行查找 不会有下面步骤
/* var o = Object.getPrototypeOf(obj) // obj.__proto__
while (o) {
// o:一轮轮查找,在obj原型链上出现的原型对象
if (o === proto) return true
o = Object.getPrototypeOf(o)
}
return false */
}
constructor
- 构造函数「在类默认的原型对象上都具备 constructor 属性,值是当前构造函数本身」
- 实例对象.constructor => 获取其隶属的构造函数
检测数据类型的思路:
- 获取实例对象的constructor
- 验证是不是我们要检测类型的构造函数
- 如果是,则是这个类型的
- 如果不是,则不是这个类型的
答案仅供参考!!
let arr = []
console.log(arr.constructor === Array) //如果结果是true,则说明其是一个数组
console.log(arr.constructor === RegExp) //false
console.log(arr.constructor === Object) //false
相比较于 instanceof 来讲,原始值其也可以处理
let num = 1
console.log(num.constructor === Number) //true
Object.prototype.toString
在Number/Array/Function…的原型对象上,都有toString这个方法,但是都是用来转换为字符串的,只有 Object.prototype.toString 是用来检测数据类型的!!
只要把 Object.prototype.toString 方法执行,让this指向谁,就是在检测谁的数据类型
const toString = Object.prototype.toString
toString.call([value])
特点:
此方案没有任何的局限性,可以检测所有值(内置类型)的类型,而且非常的精准
- 返回结果是一个字符串,具备统一格式 “[object ?]”
- “?” 会是啥?
- 一般都是检测值自己所属的构造函数「只针对内置类型值」
- 但是其内部是这样处理的:首先看要检测的值 [value] 是否具备 Symbol.toStringTag 这个属性
- 如果有,则属性值是啥,“?”部分就是啥
- 例如:
Promise.prototype[Symbol.toStringTag] -> ‘Promise’
Math[Symbol.toStringTag] -> ‘Math’
…(大部分都没有Symbol.toStringTag这个属性)
- 如果没有,则按照刚才说的一般情况处理 ```javascript const toString = Object.prototype.toString
function Fn() { } let f = new Fn console.log(toString.call(f)) //“[object Object]”
需求:如何让其检测结果是 “[object Fn]”? function Fn() { } Fn.prototype[Symbol.toStringTag] = ‘Fn’ let f = new Fn console.log(toString.call(f))//“[object Fn]” ```
补充
除了以上四种检测类型的方式外,还有一些辅助函数,可以快速检测是不是某个类型值
- Array.isArray 检测是否为数组
- isNaN 检测是否为有效数字
- …
真实的项目中
- 检测除 null 之外的原始值,基于 typeof
- 检测null -> 就看值是否为null即可,无需基于其它的方法 if([value]===null){….}
- 精准检测对象 toString.call([value])
- 笼统检测对象 typeof
- 检测函数 typeof
- …