JS判断数据类型有哪几种方式,他们的使用限制什么

257 阅读5分钟

1、判断数据类型有一下方法

  1. typeof
  2. instanceof
  3. constructor
  4. Object.prototype.toString.call()

2、各个方法的使用方式

(1)typeof

typeof "";  //"string"
typeof 1;   //"number"
typeof false; //"boolean"
typeof undefined; //"undefined"
typeof function(){}; //"function"
typeof {}; //"object"
typeof Symbol(); //"symbol"
typeof null; //"object"
typeof []; //"object"
typeof new Date(); //"object"
typeof new RegExp(); //"object"
typeof new Number(33) //"object"
typeof Null //"undefined"

总结:

  1. typeof 对基本类型做出准确的判断(除了null返回的数据类型numberstringundefinedsymbolobjectfunction。但都是小写。
  2. null 返回的是object。因为不同对象在底层都是用二进制来表示的,在**js中二进制的前三位是 000 的话,表示的是 object 类型的数据,null 的二进制全都是 0 **,所以typeof的时候是'object'。

(2) instanceof

var a= new Number(22);
a instanceof Number // true
a instanceof number // 报错!number is not defined

let num = 1
num instanceof Number // false

[] instanceof Array; // true
[] instanceof Object; // true

null instanceof Object //false
undefined instanceof Object //false

总结:

  1. instanceof 判断 A 是否是 B 的实例instanceof左侧必须是对象,如果不是直接返回false。
  2. instanceof 只能判断两个对象是否属于实例关系,不能具体判断一个对象实例属于哪种类型。

(3) constructor

当一个函数 F 被定义时,JS 引擎会为F 添加 prototype 原型,然后再在 prototype 上添加一个 constructor 属性,并让其指向自身。

var f=new F()
f.constructor===F // true

F.prototype={a:"XXXX"}
var ff=new F()
ff.constructor===F  // false
ff.constructor  // ƒ Object() { [native code] }

总结:

  1. 函数的 constructor 是不稳定的,这个主要体现在自定义对象上,当开发者重写 prototype 后,原有的 constructor 引用会丢失,constructor 会默认为 Object

(4) Object.prototype.toString.call()

toString()Object 的原型方法,调用该方法,默认返回当前对象的 [[Class]]。这是一个内部属性,其格式为 [object Xxx],其中Xxx就是对象的类型。

Object.prototype.toString.call("a")
// "[object String]"
Object.prototype.toString.call(undefined)
// "[object Undefined]"
Object.prototype.toString.call(null)
// "[object Null]"
Object.prototype.toString.call(new Date())
// "[object Date]"

那为什么不直接用obj.toString()呢?

console.log("Merlin".toString());//Merlin
console.log((1).toString());//1 
console.log([1,2].toString());//1,2 
console.log(new Date().toString());//Wed Dec 21 2016 20:35:48 GMT+0800 (中国标准时间) 
console.log(function(){}.toString());//function (){} 
console.log(null.toString());//报错
console.log(undefined.toString());//报错

这是因为toStringObject的原型方法,而Arrayfunction等类型作为Object的实例,都重写了toString方法。

不同的对象类型调用toString方法时,根据原型链的知识,调用的是对应的重写之后的toString方法。(function类型返回内容为函数体的字符串,Array类型返回元素组成的字符串.....),而不会去调用Object上原型toString方法(返回对象的具体类型),所以采用obj.toString()不能得到其对象类型,只能将obj转换为字符串类型。

因此,在想要得到对象的具体类型时,应该调用Object上原型toString方法

现将数组的toString方法删除

var arr=[1,2,3]; 
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]"

3、深入理解Object.prototype.toString方法

ECMAScript 3

toString方法被调用时,会执行下面的操作步骤:

  1. 获取this对象的[[Class]]属性的值.
  2. 计算出三个字符串[object ", 第一步的操作结果Result(1), 以及 "]连接后的新字符串.
  3. 返回第二步的操作结果Result(2).

[[Class]]是一个内部属性,所有的对象(原生对象和宿主对象)都拥有该属性。 [[Class]] 一个字符串值,表明了该对象的类型。 所有内置对象的[[Class]]属性的值是由本规范定义的. 所有宿主对象的[[Class]]属性的值可以是任意值,甚至可以是内置对象使用过的[[Class]]属性的值. [[Class]]属性的值可以用来判断一个原生对象属于哪种内置类型.需要注意的是,除了通过Object.prototype.toString方法之外,本规范没有提供任何其他方式来让程序访问该属性的值。

ECMAScript 5

ECMAScript 5中,Object.prototype.toString()被调用时,会进行如下步骤:

  1. 如果 thisundefined ,返回 [object Undefined]
  2. 如果 thisnull , 返回 [object Null]
  3. O 为以 this 作为参数调用 ToObject 的结果;
  4. classO 的内部属性 [[Class]] 的值;
  5. 返回三个字符串 "["object, class, 以及"] 拼接而成的字符串。

[[Class]]是一个内部属性,值为一个类型字符串,可以用来判断值的类型。

本规范的每种内置对象都定义了 [[Class]] 内部属性的值。宿主对象的 [[Class]] 内部属性的值可以是除了 "Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String" 的任何字符串。[[Class]] 内部属性的值用于内部区分对象的种类。

ECMAScript 6

在ES6,调用 Object.prototype.toString 时,会进行如下步骤:

  1. 如果 thisundefined ,返回 [object Undefined]; (函数直接返回分支)
  2. 如果 thisnull , 返回 [object Null] ;(函数直接返回分支)
  3. O 为以 this 作为参数调用 ToObject 的结果;(调用ToObject函数获取结果O)
  4. isArrayIsArray(O) ;(调用IsArray(O) 函数获取结果赋值给isArray
  5. ReturnIfAbrupt(isArray) (如果 isArray 不是一个正常值,比如抛出一个错误,中断执行);
  6. 如果 isArraytrue , 令 builtinTagArray ;
  7. else ,如果 O is an exotic String object , 令 builtinTagString
  8. else ,如果 O 含有 [[ParameterMap]] internal slot , 令 builtinTag Arguments
  9. else ,如果 O 含有 [[Call]] internal method , 令 builtinTagFunction
  10. else ,如果 O 含有 [[ErrorData]] internal slot , 令 builtinTagError
  11. else ,如果 O 含有 [[BooleanData]] internal slot , 令 builtinTagBoolean
  12. else ,如果 O 含有 [[NumberData]] internal slot , 令 builtinTagNumber
  13. else ,如果 O 含有 [[DateValue]] internal slot , 令 builtinTag 为 Date ;
  14. else ,如果 O 含有 [[RegExpMatcher]] internal slot , 令 builtinTagRegExp
  15. else , 令 builtinTagObject
  16. tagGet(O, @@toStringTag) 的返回值( Get(O, @@toStringTag) 方法,既是在 O 是一个对象,并且具有@@toStringTag属性时,返回 O[Symbol.toStringTag] );
  17. ReturnIfAbrupt(tag) ,如果 tag 是正常值,继续执行下一步;
  18. 如果 Type(tag) 不是一个字符串,let tag be builtinTag ;
  19. 返回由三个字符串 “[object”, tag, “]”拼接而成的一个字符串。 参考文章

toStringTag

  • Symbol.toStringTag 是一个内置 symbol,它通常作为对象的属性键使用,对应的属性值应该为字符串类型,这个字符串用来表示该对象的自定义类型标签
  • 通常只有内置的 Object.prototype.toString() 方法会去读取这个标签并把它包含在自己的返回值里。
let obj = {};
console.log(Object.prototype.toString.call(obj));// [object Object]

let myExports = {};
// 在这里可以自定义object类型的数据
Object.defineProperty(myExports, Symbol.toStringTag, { value: "Module" });
console.log(Object.prototype.toString.call(myExports)); //[object Module]

4、如何实现instanceOf

function _instanceOf(left, right){
    left = left.__proto__;
    right = right.prototype;
    while(true){
        if(left === null){
            return false;
        }
        if(left === right){
            return true;
        }
        left = left.__proto__;
    }
}