JS中数据类型检测专题

151 阅读4分钟

数据类型检测

主要包括集中常用的数据类型检测方法和JQuery中的数据类型检测

1. typeof

  1. 返回结果都是字符串
  2. 字符串中包含了对应的数据类型"number"/"string"/"boolean"/"undefined"/"symbol"/"object"/"function"
  3. 在判断基本数据类型的时候,可以使用.
  4. 局限性
    • typeof null => "object"
    • typeof array => "object"
    • null不是对象,它是空对象指针。而这也正是使用typeof操作 符检测null值时会返回"object"的原因。
    • 检测数组或者正则等特殊的对象,返回结果都是"object",所以无法基于typeof判断是数据还是正则
console.log(typeof(1));  //number
console.log(typeof('hello'));  //string
console.log(typeof(true));  //boolean
console.log(typeof(undefined));  //undefined
console.log(typeof({}));  //object
console.log(typeof(function() {}));  //function

console.log(typeof(null));  //object
console.log(typeof []); //=>"object"
console.log(typeof typeof []); //=>"string"、
//多个typeof检测,结果肯定是string
console.log(typeof(a)); // undefined // 未定义的变量
console.log(typeof("s")); // string
console.log(typeof(1)); // number
console.log(typeof(true)); // boolean
console.log(typeof(new String("s"))); // object
console.log(typeof(new Number(1))); // object
console.log(typeof(new Boolean(true))); // object
console.log(typeof(null)); // object // 在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 "object"。
console.log(typeof(undefined)); // undefined
console.log(typeof(Symbol())); // symbol
console.log(typeof(Object(Symbol()))); // object
console.log(typeof(1n)); // bigint // ES10(ES2019)新增基本数据类型
console.log(typeof(Object(BigInt(1n)))); // object
console.log(typeof(function() {})); // function
console.log(typeof([])); // object
console.log(typeof(new Date())); // object
console.log(typeof(/regex/)); // object
console.log(typeof({})); // object

2. instanceof

  1. instanceof 是用来判断A是否为B的实例,表达式为:A instanceof B,如果A是B的实例,则返回true,否则返回false。
  2. 检测某实例是否属于这个
  3. 他检测的底层机制:所有出现在其原型链上的类,检测结果都是TRUE
  4. 不能检测基本数据类型,在原型链上的结果未必准确
  5. 不能检测null,undefined
  6. 使用instanceof时对于字面量声明的StringNumberBooleanSymbolBigInt都会返回false
  7. instanceof主要用来弥补typeof不能检测具体属于哪个对象的局限性
  8. 局限性
  • 由于可以基于__proto__或者prototype改动原型链的动向,所以基于instanceof检测出来的结果并不一定是准确的
  • 基本数据类型的值,连对象都不是,更没有__proto__,虽说也是所属类的实例,在JS中也可以调取所属类原型上的方法,但是instanceof是不认的
  1. 检测不了基本属性类型,检测引用类型虽然不准,但是可以做一个简单的参考。
  let arr = [1,2,3];
  let reg = /\w/;
  console.log(arr instanceof Array);  //true
  console.log(arr instanceof Object);  //true
  console.log(reg instanceof RegExp);  //true
  console.log(reg instanceof Object);  //true
function Fn() {}
Fn.prototype.__proto__ = Array.prototype;
let f = new Fn();
//=>原型链:f -> Fn.prototype -> Array.prototype -> Object.prototype
console.log(12 instanceof Number); //=>false
console.log('qwe' instanceof String) //=>false
// instanceof判断一个对象是否是另一个对象的实例,而数字12是基本数据类型,不是对象, string也不是对象

格式是:object instanceof constructor

var a = new Number(1);// 是通过包装类Number把数字1转换成对象,你可以用typeof a,和typeof 1,看看他们返回的值
console.log(new Number(12) instanceof Number); //=>true
console.log([] instanceof Array); //=>true
console.log([] instanceof Object); //=>true
/^$/ instanceof RegExp //true 检测正则
console.log("s" instanceof String); // false
console.log(1 instanceof Number); // false
console.log(true instanceof Boolean); // false

console.log(new String("s") instanceof String); // true
console.log(new Number(1) instanceof Number); // true
console.log(new Boolean(true) instanceof Boolean); // true

console.log(null instanceof Object); // false 
console.log(undefined instanceof Object); // false

console.log(Symbol() instanceof Symbol); // false
console.log(Object(Symbol()) instanceof Symbol); // true
console.log(1n instanceof BigInt); // false
console.log(Object(1n) instanceof BigInt); // true
console.log(Symbol() instanceof Symbol); // false
console.log((function() {}) instanceof Function); // true
console.log([] instanceof Array); // true
console.log(new Date() instanceof Date); // true
console.log(/regex/ instanceof RegExp); // true
console.log({} instanceof Object); // true

7. 手写instanceof

  • a instanceof b
function myInstanceOf(a,b){
    let left = a.__proto__;
    let right = b.prototype;
    while(true){
        if(left == null){ //找到最顶层,还不相等
            return false
        }
        if(left === right){ //严格相等
            return true
        }
        left = left.__proto__ //没找到继续向上一层原型链查找
    }
}

//instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。
function myInstanceof(left, right) {
    let proto = Object.getPrototypeOf(left), // 获取对象的原型
    prototype = right.prototype; // 获取构造函数的 prototype 对象
    // 判断构造函数的 prototype 对象是否在对象的原型链上
    while (true) {
        if (!proto) return false;
        if (proto === prototype) return true;
        proto = Object.getPrototypeOf(proto);
    }
}

3. constructor

  1. 作用和instanceof非常相似。
  2. 要注意一点的是,当直接用(对象字面量或原始数据).constructor时,最好加上()
// 基本类型可以用,引用类型不准确
let arr =[]
arr.constructor
arr.constructor === Array //true
12.constructor === Number;    //报错,Invalid or unexceped token
(12).constructor
let n =12
n.constructor === Number //true
{}.constructor === Number;    //报错,Invalid or unexceped token
({}).constructor === Number;  //true


  1. 局限性:我们可以把类的原型进行重写,在重写的过程中很可能把之前constructor给覆盖了,这样检测出来的结果就是不准确的
Array.property.constructor = null
arr.constructor //null
arr.constructor ===Array //false

4. Object.prototype.toString

  • Object.prototype.toString.call([value])
  1. 只有Object上的原型的toString()才可以检测数据类型。
  2. Object.prototype.toString的作用是返回当前方法的执行主体(方法中的this)所属类的详细信息,是最全面也是最常用的检测数据类型的方式。
 Number/String/Boolean/Symbol他们的原型(.propertype)上都有:
	   =>toString:转化为字符串
	   =>valueOf:返回原始值
 Array/RegExp/Function等内置类的原型上都有
	   =>toString:转化为字符串
 Object的原型上
           =>toString:返回当前实例所属类的信息
	   =>valueOf:返回原始值
  1. 不是用来转换为字符串的,而是返回当前实例所属类的信息.
格式:"[object 所属类信息]" 
"[object Object/Array/RegExp/Date/Function/Null/Undefined/Number/String/Boolean/Symbol...]"
  1. 这种方式基本上没有什么局限性,是检测数据类型最准确的方式!!!
  2. 使用方法
Object.prototype.toString.call([value]) 
// 或者
({}).toString.call([value])
  1. 举例
({}).toString.call(12) // "[object Number]"
({}).toString.call('12') // "[object String]"
({}).toString.call(true) // "[object Boolean]"
({}).toString.call(null) // "[object Null]"
({}).toString.call(undefined) // "[object Undefined]"
({}).toString.call(Symbol()) // "[object Symbol]"
({}).toString.call([]) // "[object Array]"
({}).toString.call(function (){}) // "[object Function]"
({}).toString.call(/^$/) // "[object RegExp]"
console.log(Object.prototype.toString.call("s")); // [object String]
console.log(Object.prototype.toString.call(1)); // [object Number]
console.log(Object.prototype.toString.call(true)); // [object Boolean]
console.log(Object.prototype.toString.call(new String("s"))); // [object String]
console.log(Object.prototype.toString.call(new Number(1))); // [object Number]
console.log(Object.prototype.toString.call(new Boolean(true))); // [object Boolean]
console.log(Object.prototype.toString.call(null)); // [object Null] 
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call(Symbol())); // [object Symbol]
console.log(Object.prototype.toString.call(Object(Symbol()))); // [object Symbol]
console.log(Object.prototype.toString.call(1n)); // [object BigInt]
console.log(Object.prototype.toString.call(Object(BigInt(1n)))); // [object BigInt]
console.log(Object.prototype.toString.call(function() {})); // [object Function]
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call(new Date())); // [object Date]
console.log(Object.prototype.toString.call(/regex/)); // [object RegExp]
console.log(Object.prototype.toString.call({})); // [object Object]

改变原型链,不能影响检测结果

function Fn(){};
let f = new Fn();
({}).toString.call(f)  // "[object Object]" 实例是一个对象

function Fn(){};
Fn.prototype = Array.prototype
let f = new Fn();
({}).toString.call(f) // "[object Object]" f还是obj,不会因为原型被修改而改变

为什么用Object.prototype.toString.call(obj)检测对象类型?

JQuery中的数据类型检测

console.log(null == undefined) // true
console.log(null === undefined) // false
  1. 检测数据类型
var class2type = {};  
var toString = class2type.toString; //=>Ob ject.prototype.toString
var hasOwn = class2type.hasOwnProperty; //=>Object.prototype.hasOwnProperty
var fnToString = hasOwn.toString; //=>Function.prototype.toString
var ObjectFunctionString = fnToString.call(Object); //=>Object.toString() =>"function Object() { [native code] }"

"Boolean Number String Function Array Date RegExp Object Error Symbol".split(" ").forEach(function anonymous(item) {
        class2type["[object " + item + "]"] = item.toLowerCase();
}); 
console.log(class2type);
                // ******
function toType(obj) {
        //=>obj may be null / undefined
        //=>return "null"/"undefined"
        if (obj == null) {
                return obj + "";
        }

        return typeof obj === "object" || typeof obj === "function" ? class2type[toString.call(obj)] || "object" :
                typeof obj; 
}
// jQuery.type =  toType;
  1. 检测是否为空对象(重要)
var isEmptyObject = function isEmptyObject(obj) {
        var name;
        for (name in obj) {
                return false; //只要能循环,就是false,非空
        }
        return true;
};
  1. 检测是否为数组或类数组(重要)
var isArrayLike = function isArrayLike(obj) {
    var length = (!!obj)&& "length" in obj && obj.length, // obj传东西了(不是null undefined),且有length的属性,有的话获取length属性
            type = toType(obj); //判断类型
    if ( isFunction(obj) || isWindow(obj)) { // 如果传进来是function或者window (window也有length,值为0)
            return false;
    }
    return type === "array" || length === 0 || (typeof length === "number" && length > 0 && (length - 1) in obj); //type为array || 有length属性 ||  length不是0,且是个数字,且Length > 0, length -1 :最大索引对象是否是数组的元素
};
  1. 检测是否为函数
var isFunction = function isFunction(obj) {
       return typeof obj === "function" && typeof obj.nodeType !== "number";
};
  1. 检测是否为window对象
// window.window===window
		var isWindow = function isWindow(obj) {
			return obj != null && obj === obj.window;
		};

  1. 检测是否是纯粹的对象
var isPlainObject = function isPlainObject(obj) {
    var proto, Ctor;
    if (!obj || toString.call(obj) !== "[object Object]") {
            return false;
    }
    //=>getPrototypeOf获取当前对象的原型
    proto = Object.getPrototypeOf(obj);
    // Objects with no prototype (`Object.create( null )`)
    if (!proto) {
            return true;
    }
    // Objects with prototype are plain iff they were constructed by a global Object function
    // 看原型上有没有constructor属性(constructor指向当前类)
    Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
    return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
    //ObjectFunctionString = fnToString.call(Object); //=>Object.toString() =>"function Object() { [native code] }"
};