Object.prototype.toString.call()作为安全的类型检测方法

3,306 阅读3分钟

基本的类型检测用typeof,引用类型检测用instanceof,还有专门用于检查是不是数组的Array.isArray()

let a = "b" 
let b = true 
let c = '123'
let d = 123 
function e(){} 
let f = undefined
let g = null
let h = /[a-z]/
let i = {}
let k = [1,2,3]

typeof 运行结果

typeof a    'string'
typeof b    'boolean'
typeof c    'string'
typeof d    'number'
typeof e    'function'
typeof f    'undefined'
typeof g    'object'
typeof h    'object'
typeof i    'object'
typeof k    'object'

instanceof

e instanceof Function        true
h instanceof RegExp          true
i instanceof Object          true
k instanceof Array           true
Array.isArray(k)             true
Array.isArray(i)             false

Object.prototype.toString.call()

Object.prototype.toString.call(a)   '[object String]'
Object.prototype.toString.call(b)   '[object Boolean]'
Object.prototype.toString.call(c)   '[object String]'
Object.prototype.toString.call(d)   '[object Number]'
Object.prototype.toString.call(e)   '[object Function]'
Object.prototype.toString.call(f)   '[object Undefined]'
Object.prototype.toString.call(g)   '[object Null]'
Object.prototype.toString.call(h)   '[object RegExp]'
Object.prototype.toString.call(i)   '[object Object]'
Object.prototype.toString.call(k)   '[object Array]'

去除object

const _toString = function(val){
    return Object.prototype.toString.call(val).slice(8,-1)
}

_toString (a)   'String'
_toString (b)   'Boolean'
_toString (c)   'String'
_toString (d)   'Number'
_toString (e)   'Function'
_toString (f)   'Undefined'
_toString (g)   'Null'
_toString (h)   'RegExp'
_toString (i)   'Object'
_toString (k)   'Array'

相较于typeof、instanceof、Array.isArray,Object.prototype.toString.call更可靠,甚至null,它都检测出来了

webpack源码更青睐typeof,instanceof,和Array.isArray,比如截取的这段代码,用来解析module.rules中某个规则的test、include和exclude字段,这3个字段的值可以是字符串、函数、正则表达式和数组。

image.png

Vue源码做类型检测时,也很喜欢typeof,instanceof 和Array.isArray()。只是在做严格的类型检测时,会使用到Object.prototype.toString。

function isPrimitive(value){
  return (
    typeof value === "string" ||
    typeof value === "number" ||
    typeof value === "symbol" ||
    typeof value === "boolean"
    );
}
function isPromise(val){
  return (
      isDef(val) &&
      typeof val.then === "function" &&
      typeof val.catch === "function"
    )
}
function isObject(obj){
  return obj!==null && typeof obj === "object";
}
 
//作严格的类型检测时,会用到Object.prototype.toString
var _toString = Object.prototype.toString;
function toRawType(value){
  return _toString.call(value).slice(8,-1);
}
function isPlainObject(obj){
  return _toString.call(obj) === '[object Object]';
}
function isRegExp(v){
  return _toString.call(v) === '[object RegExp]';
}

日常开发中,typeof,instanceof 和Array.isArray()能够满足所需,Object.prototype.toString.call更像是锦上添花,但是,Object.prototype.toString.call有原生的限制,它拿非原生构造函数没啥办法。

class Point{
	constructor(x,y){
		this.x = x;
		this.y = y;
	}
	toValue(){
		return this.x+this.y;
	}
}
let p = new Point(1,2);
Object.prototype.toString.call(p); //返回"[object Object]"
p instanceof Point;                //返回true
注意:Object.prototype.toString方法还可能被覆写。

类型检测方法有 typeof | instanceof | Array.isArray | Object.prototype.toString.call, “安全”的类型检测方法Object.prototype.toString.call

实现一个函数clone,可以对js中的5种主要的数据类型(包括number string object array Boolean)进行值复制

因为toString为Object的原型方法,而Array,function等类型作为Object的实例,都重写了toString方法。不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串......),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其对象类型,只能将obj转化为字符串类型;因此,在想要得到对象的具体类型时,应该调用Object上原型toString方法。我们可以验证一下,将数组的toString方法删除

var arr=[1,1213];
console.log(Array.prototype.hasOwnProperty("toString"));//true
console.log(arr.toString());//1,2,3
delete Array.prototype.toString;//delete操作符可以删除实例属性
console.log(Array.prototype.hasOwnProperty("toString"));//false
console.log(arr.toString());//"[object Array]"

demo

function getDataType(data){  
    var getType=Object.prototype.toString;  
    var myType=getType.call(data);//调用call方法判断类型,结果返回形如[object Function]  
    var typeName=myType.slice(8,-1);// [object Function],即取除了“[object ”的字符串。  ]"
    var copyInfo='';//复制后的数据  
    //console.log(data+" is "+myType);  
    switch(typeName){  
        case 'Number': copyInfo=data-0;  
                break;  
        case 'String': copyInfo="'"+data+"'";  
                break;  
        case 'Function': copyInfo=data;  
                break;  
        case 'Null': copyInfo=null;  
                break;  
        case 'Undefined': copyInfo="Undefined";  
                break;  
        case 'Array':   
                    copyInfo=[];//先将copyInfo变为空数组  
                    for(var i=0;i<data.length;i++){  
                        copyInfo[i]=data[i];//将data数组数据逐个写入copyInfo  
                    }  
                break;  
        case 'Object':   
                    copyInfo={};//先将copyInfo变为空对象  
                    for(var x in data){  
                        copyInfo[x]=data[x];  
                    }  
                break;  
        case 'Boolean': copyInfo=data;  
                break;  
        default : copyInfo=data;  
                break;  
    }  
    return copyInfo;  
}