typeof
typeof 对于判断简单数据类型简单有效,包装类型的简单数据类型除外
基本数据类型的判断(OK)
// 数值
typeof 37 === 'number';
typeof 3.14 === 'number';
typeof(42) === 'number';
typeof Math.LN2 === 'number';
typeof Infinity === 'number';
typeof NaN === 'number'; // 尽管它是 "Not-A-Number" (非数值) 的缩写
typeof Number(1) === 'number'; // Number 会尝试把参数解析成数值
typeof 42n === 'bigint';
// 字符串
typeof '' === 'string';
typeof 'bla' === 'string';
typeof `template literal` === 'string';
typeof '1' === 'string'; // 注意内容为数字的字符串仍是字符串
typeof (typeof 1) === 'string'; // typeof 总是返回一个字符串
typeof String(1) === 'string'; // String 将任意值转换为字符串,比 toString 更安全
// 布尔值
typeof true === 'boolean';
typeof false === 'boolean';
typeof Boolean(1) === 'boolean'; // Boolean() 会基于参数是真值还是虚值进行转换
typeof !!(1) === 'boolean'; // 两次调用 ! (逻辑非) 操作符相当于 Boolean()
// Symbols
typeof Symbol() === 'symbol';
typeof Symbol('foo') === 'symbol';
typeof Symbol.iterator === 'symbol';
// Undefined
typeof undefined === 'undefined';
typeof declaredButUndefinedVariable === 'undefined';
typeof undeclaredVariable === 'undefined';
// 对象
typeof {a: 1} === 'object';
引用数据类型的判断
但是对引用数据类型的判断作用不大,除 function外,其他都返回 object,业务中我们通常需要知道它具体的类型
typeof {a: 1} === 'object';
typeof [1, 2, 4] === 'object';
typeof new Date() === 'object';
typeof /regex/ === 'object';
typeof function() {} === 'function';
typeof class C {} === 'function'
typeof Math.sin === 'function';
包装类型的基本数据类型
迷惑吧!
typeof new Boolean(true) === 'object';
typeof new Number(1) === 'object';
typeof new String('abc') === 'object';
在lodash中 在判断基础数据类型前 会判断是否是包装数据类型
function isObjectLike(value) {
return typeof value === 'object' && value !== null
}
typeof null
typeof null === 'object';
在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于 null 代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null 也因此返回 “object”。
instanceof
对比实例原型链上的基类
实例.proto == 基类.prototype
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}
const auto = new Car('Honda', 'Accord', 1998);
console.log(auto instanceof Car);
// expected output: true
auto.__proto__ == Car.prototype
console.log(auto instanceof Object);
// expected output: true
auto.__proto__ == Object.prototype
验证原理
验证一下确实是对比的proto 而不是构造方法里的prototype 有人说是这样的
const auto1 = new Car('Honda', 'Accord', 1998);
const auto2 = new Car('Honda', 'Accord', 1998);
auto1 instanceof Car
true
auto2 instanceof Car
true
删掉
auto1.__proto__ = null
null
auto2.constructor = null
null
结果
auto1 instanceof Car
false
auto2 instanceof Car
true
结论
instanceof原理
实例.proto == 基类.prototype
prototype给instanceof带来的风险
auto2 instanceof Car
true
auto2.__proto__ == Car.prototype
true
操作
Car.prototype = null
auto2.__proto__ == Car.prototype
false
auto2 instanceof Car
VM1487:1 Uncaught TypeError: Function has non-object prototype 'null' in instanceof check
at Function.[Symbol.hasInstance] (<anonymous>)
at <anonymous>:1:7
由于基类的prototype 可改变,所以 instanceof这种对比 是不安全的
Object.prototype.toString()
每个对象都有一个 toString() 方法,默认情况下,toString() 方法被每个 Object 对象继承。如果此方法在自定义对象中未被覆盖,toString() 返回一个字符串 “[object type]”,其中 type 是对象的类型。
var o = new Object();
o.toString(); // [object Object]
为什么不能直接使用toString()
内置对象或者自定义类型的对象可能会实现自己的 toString 方法或者根本没有定义该方法,导致返回的不再是 “[object type]” 或者直接报错
var str = new String('hello')
str.toString() // "hello"
123..toString() // "123"
null.toString() // Uncaught TypeError: Cannot read property 'toString' of null
undefined.toString() // Uncaught TypeError: Cannot read property 'toString' of undefined
所以需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用(不陌生吧,改变 this 指向)
var toString = Object.prototype.toString;
toString.call(new String); // "[object String]"
toString.call('hello'); // "[object String]"
toString.call(Math); // "[object Math]"
toString.call(123); // "[object Number]"
toString.call(true); // "[object Boolean]"
toString.call(123n); // "[object BigInt]"
toString.call(new Date); // "[object Date]"
toString.call(Symbol()) // "[object Symbol]"
toString.call(() => {}) // "[object Function]"
toString.call([1, 2]); // "[object Array]"
toString.call(new Map) // "[object Map]"
toString.call(function* () {}); // "[object GeneratorFunction]"
toString.call(Promise.resolve()); // "[object Promise]"
// ES5 中才实现下面的功能
toString.call(undefined); // "[object Undefined]"
toString.call(null); // "[object Null]"
ECMAScript 内置的针对某个类型的判断方法
Array.isArray()
Array.isArray([1]); // true
Array.isArray(new Array('a', 'b', 'c', 'd')); // true
总结
基本数据类型的判断使用 typeof 足够,instanceof由于判断的是对象的prototype,具有可变性,所以相对不安全。
引用数据类型使用Object.prototype.tosString.call(obj) 最为安全可靠