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 属性,如果具备这个属性(方法),则把方法执行
  • 只要是支持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 // …

  1. <a name="nrwBC"></a>
  2. #### 重写instanceof
  3. ```javascript
  4. var instance_of = function instance_of(obj, Ctor) {
  5. // 确保右侧是标准的构造函数
  6. if (typeof Ctor !== 'function') throw new TypeError('Right-hand side of instanceof is not callable')
  7. var proto = Ctor.prototype
  8. if (!proto) throw new TypeError('Function has non-object prototype undefined in instanceof check')
  9. // 如果左边是原始值,则直接返回false
  10. if (obj === null || !/^(object|function)$/.test(typeof obj)) return false
  11. // 如果构造函数具备 Symbol.hasInstance ,则直接基于其处理即可
  12. if (typeof Symbol !== 'undefined') {
  13. var hasInstance = Ctor[Symbol.hasInstance]
  14. if (hasInstance) {
  15. return hasInstance.call(Ctor, obj)
  16. }
  17. }
  18. // 如果不支持,才基于原型链进行查找匹配
  19. return proto.isPrototypeOf(obj)
  20. //一般直接用isPrototypeOf进行查找 不会有下面步骤
  21. /* var o = Object.getPrototypeOf(obj) // obj.__proto__
  22. while (o) {
  23. // o:一轮轮查找,在obj原型链上出现的原型对象
  24. if (o === proto) return true
  25. o = Object.getPrototypeOf(o)
  26. }
  27. return false */
  28. }

constructor

  • 构造函数「在类默认的原型对象上都具备 constructor 属性,值是当前构造函数本身」
  • 实例对象.constructor => 获取其隶属的构造函数

检测数据类型的思路:

  • 获取实例对象的constructor
  • 验证是不是我们要检测类型的构造函数
  • 如果是,则是这个类型的
  • 如果不是,则不是这个类型的

答案仅供参考!!

  1. let arr = []
  2. console.log(arr.constructor === Array) //如果结果是true,则说明其是一个数组
  3. console.log(arr.constructor === RegExp) //false
  4. console.log(arr.constructor === Object) //false

相比较于 instanceof 来讲,原始值其也可以处理

  1. let num = 1
  2. console.log(num.constructor === Number) //true

Object.prototype.toString

在Number/Array/Function…的原型对象上,都有toString这个方法,但是都是用来转换为字符串的,只有 Object.prototype.toString 是用来检测数据类型的!!

只要把 Object.prototype.toString 方法执行,让this指向谁,就是在检测谁的数据类型

  1. const toString = Object.prototype.toString
  2. 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