数据类型检测

常见数据类型检测方法

/*
      + JS中检测数据类型的办法
        + typeof([value]): 检测数据类型的运算符
          + @1 返回结果是一个字符串, 其次字符串中包含对应的数据类型,
            例如: "undefined" "number" "string" "boolean" "symbol" "bigint" "function" "object"
            typeof typeof typeof [1,2,3,4] => "string"
          + @2 弊端
            + typeof null   ==>  "object"
            + typeof 对象  ==> 除函数对象被识别为 "function", 其余的对象返回的都是 "object"
            + typeof 未被声明的变量  ==> 不会报错, 而是 "undefined"
            + 除了这些以外, 用 typeof 检测原始值类型(除了null以外)[或者函数类型]还是非常 方便 准确 的
          + @3 检测原理
            + 所有的数据类型值在计算机底层都是按照二进制来进行存储的, 而 typeof 检测类型, 也是按照二进制
              来进行检测的 [特点: 性能好]; 所有以 000 开始的, 都是对象类型, 再排查一下call, 实现的是函数
              对象, 没实现的就是其余的对象..., 而null这个值存储的二进制都是 0, 所以也被识别为 "object"
          + @4 不准确  
              typeof 10 ==> 'number'
              typeof new Number(10) ==> 'object'
        + instanceof: 检测当前实例是否属于这个类[临时拉来做数据类型检测]
          + @1 实例 instanceof 构造函数,  返回值 true/false
          + @2 可以做一些数据类型检测[实现对typeof的补充, 可以实现对象的细分]
          + 弊端
            + 无法基于instanceof检测是否为标准普通对象[纯粹对象: 直接是Object类的实例];
              因为所有对象都是Object类的实例, 基于 'xxx instanceof Object' 检测的时候
              返回的结果都是 true
            + 无法检测原始值类型; 因为基于 instanceof 检测的时候, 默认不会进行"装箱"操作
              例如: 10 instanceof Number --> false
            + 检测的结果不一定严谨: 因为, 原型链以及所属构造函数是谁, 用户自己是可以改变的
            + ...
          + @4 当我们基于 "[value] instanceof [Ctor]" 运算符进行数据类型的检测的时候
            + 首先调用 [Ctor][Symbol.hasInstanceof]这个函数, 如果存在这个函数, 则直接基于这个
              函数处理即可; 当代浏览器基本都有, 因为Symbol.hasInstanceof在Function.prototype中,
              每一个构造函数都是 Function 的实例, 都可以调用Function.prototype[Symbol.hasInstanceof]
              这个方法
            + 如果不存在这个函数, 浏览器会按照当前[value]原型链一层层向上查找, 直到找到Object.prototype为止,
              查看[Ctor].prototype是否出现在它的原型链中, 如果出现了, 则结果是true, 说明[value]是[Ctor]的
              实例, 反之则为false ...
          + @5 分析 instanceof 的优缺点 和 底层实现机制, 并重写 instanceof

        + constructor: 获取当前实例所属的构造函数
          + @1  [value].constructor 获取其构造函数, 验证是否为我们想检测的类
                例如 [value].constructor === Array
          + @2  相比较 instanceof 来讲
            + 可以检测原始值类型(排除 null 和 undefined)
            + 可以检测是否为标准普通对象  [value].constructor === Object
            + 和 instanceof 一样, 检测的结果仅供参考 [constructor这个值是可以被肆意修改的]

        + Object.prototype.toString.call([value]): 专门检测数据类型的办法
          +@1 调用 Object.prototype.toString 方法, 让方法中的this指向检测的值, 就是检测
              当前值的数据类型: 返回结果是一个字符串 "[object ?]"
              例如:  Object.prototype.toString.call(10)  ==> "[object Number]"
                    Object.prototype.toString.call(new Number(10))  ==> "[object Number]"
          +@2 基本上所有数据类型, 所属构造函数的原型上都有 toString 方法, 一般都是用来转换为字符串的, 
              只有 Object.prototype.toString是用来检测数据类型的
          +@3 它是所有检测数据类型的办法中, 最强大 最稳定 ... 的方式[除了写起来麻烦点]
          +@4 即使constructor值被修改 或者 基于Object.setPrototypeof(ary, Object.prototype)
              重定向实例的原型指向, 结果也是不变的!
          +@5 返回结果是 "[object ?]"; "?" 会是啥呢?
            + 首先获取[value][Symbol.toStringTag] 属性值, 如果存在这个属性, 则这个属性值是啥, "?"就是啥
            + 如果没有这个属性, 一般 "?" 是当前实例所属的构造函数
            + Symbol.prototype & BigInt.prototype & Math & GeneratorFunction.prototype & Promise.prototype
              & Set.prototype & Map.prototype ... 这些类的原型上, 都有 Symbol.toStringTag 这个属性
          +@6 Object.prototype.toString 这个方法是用来检测数据类型的, 而且这个方法内部规定: 方法中的 this是谁,
              我们就检测谁的类型, 所以我们基于call方法改变this指向

      --------------------
      Array.isArray([value]): 检测是否为数组
      isNaN([value]): 检测是否为有效数字
    */
    alert({
      name: 'xxxx'
    }) // ==> "[object Object]"      
    /* 
      + alert会把编写的值转换为字符串再输出, 而对象toString的时候, 调用的是Object.prototype.toString
        这个方法, 而这个方法时检测数据类型的
    */
    /* 
      + 真正转换标准普通对象为字符串 
        + JSON.stringify() 把对象变为JSON格式字符串  ---> JSON.parse()
        + Qs.stringify 依托Qs第三方库, 我们把对象变为 urlencoded
        + 前后端数据通信的时候, 我们经常需要把对象变为指定的字符串格式, 传递给服务器; 或者把服务器返回的
          指定格式字符串, 变为对象
    */


    // ---------------------- Object.prototype.toString.call
    let obj = {}
    let toString = obj.toString   // ---> Object.prototype.toString 
    /* 
      + 基本上所有数据类型, 所属构造函数的原型上都有 toString 方法, 一般都是用来转换为字符串的, 
        只有 Object.prototype.toString是用来检测数据类型的
    */
    // console.log(toString.call(10)) // "[object Number]"
    // console.log(toString.call(new Number(10))) // "[object Number]"
    // console.log(toString.call('zhufeng')) // "[object String]"
    // console.log(toString.call(true)) // "[object Boolean]"
    // console.log(toString.call(null)) // "[object Null]"
    // console.log(toString.call(undefined)) // "[object Undefined]"
    // console.log(toString.call(Symbol())) // "[object Symbol]"
    // console.log(toString.call(10n)) // "[object BigInt]"
    // console.log(toString.call({})) // "[object Object]"
    // console.log(toString.call([])) // "[object Array]"
    // console.log(toString.call(/^$/)) // "[object RegExp]"
    // console.log(toString.call(function () { })) // "[object Function]"
    // console.log(toString.call(new Date)) // "[object Date]"
    // console.log(toString.call(new Error)) // "[object Error]"
    // console.log(toString.call(Math)) // "[object Math]"
    // console.log(toString.call(function* () { })) // "[object GeneratorFunction]"
    // console.log(toString.call(Promise.resolve())) // "[object Promise]"
    /* 
      + 即使constructor值被修改 或者 基于Object.setPrototypeof(ary, Object.prototype)重定向
        实例的原型指向, 结果也是不变的!!
    */
    // let ary1 = [1, 2]
    // ary1.constructor = 0
    // console.log(ary1)
    // console.log(toString.call(ary1)) // "[object Array]"
    // let ary = [1, 2]
    // Object.setPrototypeOf(ary, Object.prototype)
    // console.log(ary)
    // console.log(toString.call(ary)) // "[object Array]"


    // const Fn = function () { }
    // let f = new Fn()
    // console.log(toString.call(f)) // "[object, Object]"
    /* 面试题
      + 需求: 期望自己写自定义构造函数, 所创建出来的实例在检测数据类型的时候,
        可以返回的是 [object 自己的构造函数]
    */
    const Fun = function () { }
    Fun.prototype[Symbol.toStringTag] = "Fun"
    let fun = new Fun()
    console.log(toString.call(fun)) // "[object, Fun]"


    //------------------------ constructor
    // let arr = []
    // let reg = /^$/
    // let num = 10
    // console.log(arr.constructor === Array) // true
    // console.log(reg.constructor === Object) // false
    // console.log(num.constructor === Array) // false
    // console.log(num.constructor === Number) // true


    // ----------------------- instanceof
    // let obj = {}
    // let arr = []
    // let reg = /^$/
    // let num = new Number(10)
    // console.log(arr instanceof Array) // true 只有arr是一个数组
    // console.log(obj instanceof Array) // false
    // console.log(reg instanceof Array) //false
    // console.log(num instanceof Array) // false

    // console.log(typeof num) // 'object'
    // console.log(num instanceof Number)  // true 说明num 是Number类的一个实例[原始值对应的对象类型结果]

    // console.log(arr instanceof Array) // true
    // console.log(arr instanceof Object) // true
    // console.log(reg instanceof Object) // true
    // console.log(num instanceof Object) // true

    // let n = 20
    // // n.toFixed(2) => 默认将n进行装箱 new Number(n) ==> new Number(n).toFixed(2)
    // console.log(n instanceof Number) // false


    // const Fn = function Fn () { }
    // Fn.prototype = Array.prototype
    // let f = new Fn()
    // console.log(f) // 从结构来看, f一定不是数组[数组的结构: 数字索引 逐级递增 length属性...]
    // console.log(f instanceof Array) // true



    // const Fn = function Fn () {
    //   this.name = 'zhufeng'
    // }
    // Fn.prototype.sayName = function () { }
    // Fn.xxx = 'xxx'
    // Fn[Symbol.hasInstance] = function () { // 这样设置是无效的
    //   console.log(1)
    //   return false
    // }
    // Fn.prototype[Symbol.hasInstance] = function () { // 这样设置也是无效的
    //   console.log(1)
    //   return false
    // }
    // let f = new Fn()
    // console.log(f instanceof Fn)



    // 重构Symbol.hasInstance
    // class Fn {
    //   name = 'zhufeng'
    //   sayName () { }
    //   /*
    //     + 当做对象设置静态私有属性方法 [这样设置是有用的, 所以重构"构造函数"的Symbol.hasInstance,
    //       只支持ES6中class创造的类, ES5中创建的构造函数不支持这样的重构]
    //   */
    //   static xxx = 'xxx'
    //   static [Symbol.hasInstance] (obj) {
    //     console.log(obj)
    //     return false
    //   }
    // }
    // let f = new Fn()
    // console.log(f instanceof Fn)

    /**
     * 检测value是否为Ctor的实例
     * value: 要检测的实例;
     * Ctor: 要检测的构造函数
    */
    // const instance_of = function instance_of (value, Ctor) {
    //   // 保证Ctor的格式也是正确的
    //   if (typeof Ctor !== 'function') throw new TypeError('Right-hand side of instanceof is not callable')
    //   if (!Ctor.prototype) throw new TypeError('Function has non-object prototype in instanceof check')

    //   // 不支持原始值类型检测
    //   if (value === null) return false
    //   if (!/^(object|function)$/.test(typeof value)) return false

    //   // 支持 Symbol.hasInstance 方法的直接按照这个处理
    //   if (typeof Ctor[Symbol.hasInstance] === 'function') return Ctor[Symbol.hasInstance](value)
    //   // 不支持的则按照原型链一层层的查找即可; Object.getPrototypeOf(value) 获取value所属构造函数的原型对象
    //   let proto = Object.getPrototypeOf(value)
    //   while (proto) {
    //     // Ctor.prototype出现了value的原型链上[value是Ctor的实例对象]: 直接返回true  &  结束查找
    //     if (proto === Ctor.prototype) return true
    //     proto = Object.getPrototypeOf(proto)
    //   }
    //   return false
    // }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值