js中的四种数据类型检测方法

/* 
 * 数据类型检测
      + typeof  检测数据类型的运算符
        + 返回结果是一个字符串,字符串中包含了对应的数据类型
        + "number/string/boolean/symbol/bigint/object/function"
        + 多个typeof检测,结果都是字符串  typeof typeof xxx => 'string'
       【有BUG】
         + typeof null  => 'object'
         + 不能具体检测出对象类型  typeof arr  => 'object'

      + instanceof  检测当前实例是否属于某个类,并不是用来检测数据类型的
        + 用它来检测,一般是应用于普通对象/数组对象/正则对象/日期对象等的具体细分的
        + 结果返回的是boolean类型值(不是true就是false)
       【有BUG】
        + 不能证明 xxx instanceof Object 为true就说 xxx是普通对象
          arr instanceof Array  => true
          arr instaceof Object  => true
        + 不能检测字面量创建的基本类型值  10 instanceof Number  =>false

     + constructor  用来获取实例的构造函数
        + 比 instanceof 好用一点
        + 但是也不准确:constructor 也是可以被随意修改的

     + Object.prototype.toString.call([value])
        + 专门用来检测数据类型的
        + Number/String/Boolean/Symbol/BigInt/Function/Array/Date/RegExp/Object......的原型上都有toString,除了Object.prototype.toString不是用来转换为字符串的,其余的都是
        + 返回结果 "[object 对象[Symbol.toStringTag] || 对象.构造函数 || Object]"
*/

//--------typeof-----------------------

 function fn(){}
console.log(typeof fn);  // 'function'
console.log(typeof null);  // 'object'
let arr = [10,20];
console.log(typeof arr);  // 'object' 


//-------instanceof--------------------------
 let arr = [10,20];
console.log(arr instanceof Array);  // true
console.log(arr instanceof Object);  // true
let m = 10;
console.log(m instanceof Number);  // false
m = new Number(10);
console.log(m instanceof Number);  // true 

 function P(){}
P.prototype = Array.prototype;
let p = new P;
console.log(p instanceof Array);  // true

let arr = [10];
console.log(arr instanceof Array);  // true
console.log(Array[Symbol.hasInstance](arr)); // true
// + 基于 “实例 instanceof 类” 检测的时候,浏览器是这样处理的 “类[Symbol.hasInstance](实例)”
// + Function.prototype[Symbol.hasInstance] 值是一个函数
// + Symbol.hasInstance 方法执行原理
//   + 根据原型链(__proto__)查找机制,一层层查找该实例的原型链上是否存在这个类的原型(prototype):因为实例的__proto__都指向所属类的原型prototype
//   + arr.__proto__ === Array.prototype  => arr instanceof Array => ture
//   + arr.__proto__.__proto__ === Object.prototype  => arr instanceof Object => true

let obj = {};
// console.log(arr instanceof obj); // 报错
// 因为obj是一个对象,没办法调用 Function.prototype 上的方法(函数才可以调用) 


//--------constructor--------------------
 let arr = [10];
let obj = {};
// 在constructor不改的情况下,可以检测是是普通对象还是数组
console.log(arr.constructor === Array);  // true
console.log(arr.constructor === Object);  // false
console.log(obj.constructor === Object);  // true */

/* function Person(){}
Person.prototype = Array.prototype;  // 一旦原型重定向了,constructor也改了,也就不准确了
let p = new Person;
console.log(p.constructor === Array); // true

let m = 10;
console.log(m.constructor === Number); // true 


//------------Object.prototype.toString.call([value])-----------------
/* let valType = {},
toString = valType.toString;
console.log(toString.call([10]));  // '[object Array]'
console.log(toString.call(10));  // '[object Number]'
console.log(toString.call(30n));  // '[object BigInt]'
console.log(toString.call(null));  // '[object Null]'

function Person(){}
let p = new Person;
console.log(toString.call(p));  // [object Object] 
// 说明使用 对象.构造函数 只对内置类有效,对自己定义的类无效,想要实现检测p的类型是  "[object Person]",需要给 Person类中添加 [Symbol.toStringTag] 属性

class QQ{
  // 只要获取实例的 [Symbol.toStringTag] 属性值,则调用这个方法
  get [Symbol.toStringTag](){
    return 'QQ'
  }
}
let q = new QQ;
console.log(toString.call(q));  // '[object QQ]' */


// 重写 instanceof
function myInstanceof(obj, constructor) {
  // 先判断obj是不是基本数据类型值,如果是,直接返回false
  if(obj == null || !/^(object|function)$/i.test(typeof obj)) return false;
  // 如果检测的类不是函数,报错
  if(typeof constructor !== 'function') throw new TypeError('请传入一个函数');

  // obj.__proto__ === Object.getProtypeOf(obj)
  let proto = Object.getPrototypeOf(obj),
      prototype = constructor.prototype;
  while(1) {
    if(proto === null) return false;
    if(proto === prototype) return true;
    proto = Object.getPrototypeOf(proto)
  }
}
console.log(myInstanceof([], Array)); // true
console.log(myInstanceof([], Object)); // true
console.log(myInstanceof(10, Number));  // false
console.log(myInstanceof(new Number(10), Number));  // true
console.log(myInstanceof([], {})); // 报错 请传入一个函数