1. typeof
可以使用 typeof 运算符来判断一个值的类型。typeof 运算符返回一个表示值类型的字符串,可能的值包括:
"undefined":表示未定义的值;"boolean":表示布尔值;"number":表示数值;"string":表示字符串;"symbol":表示 ES6 引入的 Symbol 类型;"bigint":表示 ES10 引入的 BigInt 类型;"object":表示对象(包括数组、函数和 null);"function":表示函数。
typeof undefined; // "undefined"
typeof true; // "boolean"
typeof 123; // "number"
typeof "hello"; // "string"
typeof Symbol(); // "symbol"
typeof 123n; // "bigint"
typeof null; // "object"
typeof {}; // "object"
typeof []; // "object"
typeof function() { /* ... */ }; // "function"
缺点:
-
typeof null返回的是"object"-
在 JavaScript 的早期版本中,它使用 32 位的内存来表示一个值,其中低 3 位用于标识类型信息。这 3 位的值分别为:
000:表示对象类型(包括函数)010:表示浮点数类型;100:表示字符串类型;110:表示布尔类型;1:表示整数类型,即除了上面 4 种类型之外的其他类型。
注意,这个类型系统是非常简单的,只有 5 种类型,而且都是由低 3 位标识的。
在这个类型系统中,对象类型的标识是
000,但是由于null被当作一个空对象指针,它的二进制表示全是 0,因此它被错误地识别为对象类型,而非整数类型。后来随着 JavaScript 的发展,内存模型变得更加复杂,同时也引入了更多的类型,但是
typeof null返回"object"这个错误的结果已经成为了 JavaScript 的一种行为,为了保持向后兼容性,这个行为就一直被保留了下来。因此,虽然null不是对象,但是typeof null仍然返回"object"。
-
-
不能区分不同对象类型之间的差异。比如无法区分数组和普通对象,因为它们的
typeof都是"object"。
2. instanceof
- 使用
用于判断对象是否为某个类的实例
其语法如下:
object instanceof constructor
其中 object 是要检测的对象,constructor 是构造函数或类。如果 object 是 constructor 的实例或者是 constructor 的子类的实例,返回 true,否则返回 false。
console.log(true instanceof Boolean) // false
console.log(123 instanceof Number) // false
console.log("hello" instanceof String) // false
console.log(Symbol() instanceof Symbol) // false
console.log(123n instanceof BigInt) // false
console.log(([]) instanceof Array); //true
console.log(({}) instanceof Object); //true
console.log((function() { /* ... */ }) instanceof Function); //true
可以看到,基本数据类型使用instanceof判断会出错。因此,不能使用instanceof判断基本数据类型。
- 原理
instanceof 的原理是通过判断 object 的原型链中是否存在 constructor.prototype。如果存在,则返回 true,否则返回 false。
在js中,使用instanceof会调用原型上的内置函数Symbol.hasInstance,举例如下
class Bar {}
class Baz extends Bar {
static [Symbol.hasInstance]() {
return false
}
}
let b = new Baz()
console.log(Bar[Symbol.hasInstance](b)) // true
console.log(b instanceof Bar) // true
console.log(Baz[Symbol.hasInstance](b)) // false
console.log(b instanceof Baz) // false
- instanceof 代码实现
function myInstanceOf(obj, constructor) {
let prototype = Object.getPrototypeOf(obj);
while (prototype) {
if (prototype === constructor.prototype) {
return true;
}
prototype = Object.getPrototypeOf(prototype);
}
return false;
}
-
缺点
-
无法正确判断基本数据类型。
-
只能判断对象是否为某个类的实例,不能判断对象的具体数据类型,比如一个数组是无法使用
instanceof判断的。 -
如果对象的原型链太长,
instanceof的效率可能会比较低。
-
3. constructor
constructor是对象对应原型上的属性,指向构造函数。
- 使用
console.log(new Number(123).constructor)
//ƒ Number() { [native code] }
可以看到它指向了Number的构造函数,因此,可以使用num.constructor==Number来判断一个变量是不是Number类型的
true.constructor === Boolean; // true
(123).constructor === Number; // true
('hello').constructor === String; // true
Symbol().constructor === Symbol; // true
(123n).constructor === BigInt; // true
({}).constructor === Object; // true
([]).constructor === Array; // true
(function() { /* ... */ }).constructor === Function; // true
undefined.constructor === undefined; // TypeError: Cannot read property 'constructor' of undefined
null.constructor === null; // TypeError: Cannot read property 'constructor' of null
可以看到,除了undefined和null报错之外,其他类型都可以通过constructor属性来判断类型。
- 缺点
- 无法判断undefined和null
- 不稳定,主要体现在自定义对象上,当开发者重写prototype后,原有的constructor会丢失,constructor会默认为Object。
4. Object.prototype.toString
toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型, 返回的类型格式为[object,xxx],xxx是具体的数据类型。
那为什么要用Object原型上的toString方法呢?大部分的对象都实现了自身的toString方法,这样就可能会导致Object的toString被终止查找,因此可以call来强制执行Object的toString方法。
- 使用
Object.prototype.toString.call(undefined) // [object Undefined]
Object.prototype.toString.call(true) // [object Boolean]
Object.prototype.toString.call(123) // [object Number]
Object.prototype.toString.call("hello") // [object String]
Object.prototype.toString.call(Symbol()) // [object Symbol]
Object.prototype.toString.call(123n) // [object BigInt]
Object.prototype.toString.call(null) // [object Null]
Object.prototype.toString.call({}) // [object Object]
Object.prototype.toString.call([]) // [object Array]
Object.prototype.toString.call(function() { /* ... */ }) // [object Function]
可封装成函数
function getType (val) {
return Object.prototype.toString.call(val).slice(8, -1).toLowerCase()
}
getType(true) // boolean
- 原理
使用 Symbol.toStringTag 拼接,举例如下。
class Bar {}
let bar = new Bar()
// 修改前
console.log(bar.toString()) // [object Object]
bar[Symbol.toStringTag] = 'Bar'
// 修改后
console.log(bar.toString()) // [object Bar]