数据类型检测的方法
- typeOf
- instanceOf
- constructor
- Object.Prototype.toString.call([value])
typeOf
typeOf(value) => 返回结果首先是个字符串,包含对应的数据类型
-
局限性:
-
typeof null -> "object" ,不能正确的检测null,
-
typeof 检测对象类型值,除了可执行对象{函数}可以检测出来是“function”,其余都是“object”
-
基于typeof检测一个未被声明的变量,结果是“undefined”,而不会报错「基于let在后面声明则会报错」
-
typeof 检测原始值对应的对象类型的值,结果是“object”
typeOf检测数据类型的原理: typeof检测数据类型是,按照计算机底层存储的二进制值,来进行检测的,它认为以“000”开始的都是对象,而null全是零,
-
-
也不能说typeOf不好,typeOf使用起来简单,而且性能好
instanceOf
[value] instanceof [Ctor] => true/false
- instanceOf临时用来检测数据类型,本意是检测当前实例是否属于这个类
- 优点:
- 好处:细分对象数据类型值「但是不能因为结果是TRUE,就说他是标准普通对象」
- 弊端:
- 不能检测原始值类型的值「但是原始值对应的对象格式实例则可以检测」;但是因为原型链指向是可以肆意改动的,所以最后检测的结果不一定准确;
原理:按照原型链检测的;只要当前检测的构造函数(它的原型对象),出现在实例的原型链上,则检测结果就是TRUE;如果找到Object.prototype都没有找到,则结果是FALSE;
几个小例子来加深一下影响吧
// 我们这个方法支持原始值类型
const instance_of = function instance_of(obj, Ctor) {
if (Ctor == null) throw new TypeError('Right-hand side of instanceof is not an object');
let type = typeof Ctor;
if (!/^(object|function)$/i.test(type)) throw new TypeError('Right-hand side of instanceof is not an object');
if (!/^function$/i.test(type)) 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');
let proto = Object.getPrototypeOf(obj);
while (proto) {
if (proto === Ctor.prototype) return true;
proto = Object.getPrototypeOf(proto);
}
return false;
};
console.log(instance_of([], Array)); //=>true
console.log(instance_of([], RegExp)); //=>false
console.log(instance_of(1, Number)); //=>true
console.log(instance_of(Symbol(), Symbol)); //=>true
console.log(instance_of([], () => {})); //报错 */
constroctor 可以弥补instanceOf的不足
/* let arr = [],
n = 10,
m = new Number(10);
console.log(arr.constructor === Array); //=>true
console.log(arr.constructor === RegExp); //=>false
console.log(arr.constructor === Object); //=>false
// 如果CTOR结果和Object相等,说明当前可能是标准普通对象
console.log(n.constructor === Number); //=>true
console.log(m.constructor === Number); //=>true */
/* function Fn() {}
let f = new Fn;
console.log(f.constructor === Fn); //=>true
console.log(f.constructor === Object); //=>false */
/* function Fn() {}
Fn.prototype = {}; //重定向后丢失了constructor
let f = new Fn;
console.log(f.constructor === Fn); //=>false
console.log(f.constructor === Object); //=>true */
Object.prototype.toString.call([value])
这是JS中唯一一个检测数据类型没有任何瑕疵的「最准确的,除了性能比typeof略微少那么一点点,写起来略微麻烦那么一点点」
- 大部分内置类的原型上都有toString方法,一般都是转换为字符串的,但是Object.prototype.toString是检测数据类型的,返回值中包含自己所属的构造函数信息...
- 为什么要用call呢?
- ([]).toString() 调用的是Array.prototype上的toString,是转换为字符串
- ({}).toString() 调用的是Object.prototype上的toString,是检测数据类型的「方法中的this是谁,就是检测谁的类型」 -> 我们需要把Object.prototype.toString执行,而且还要改变其中的this -> ({}).toString.call(10) =>“[object Number]”
- 2.检测返回值遵循啥规则?
-
一般都是返回当前实例所属的构造函数信息
-
但是如果实例对象拥有 Symbol.toStringTag 属性,属性值是啥,最后返回的就是啥,例如:Math[Symbol.toStringTag]="Math" =>Object.prototype.toString.call(Math) “[object Math]”
-
class Fn {
// constructor() {
// this[Symbol.toStringTag] = 'Fn';
// }
[Symbol.toStringTag] = 'Fn';
}
let f = new Fn;
// console.log(Object.prototype.toString.call(Fn)); //“[object Function]”
console.log(Object.prototype.toString.call(f)); //“[object Fn]”