在 JavaScript 中,原始值(原始数据类型)是一种既非对象也无方法或属性的数据。有 7 种原始数据类型:
除了 Object 以外,所有的类型都定义了不可变的、在语言最底层直接表示的值。我们将这些类型的值称为原始值;
除了 null 以外,所有的原始类型都可以使用 typeof 运算符进行测试。typeof null 返回 "object",因此必须使用 === null 来测试 null。
除了 null 和 undefined 以外,所有的原始类型都有对应的对象包装类型,它们为处理原始值提供了有用的方法。例如,Number 对象提供了像 toExponential() 这样的方法。当在原始值上访问属性时,JavaScript 会自动将值包装成对应的包装对象,并访问对象上的属性。
string number bigint boolean undefined symbol null
外加一个非原始值 Object
typeof
| 类型 | 结果 |
|---|---|
| Undefined | "undefined" |
| Null | "object" |
| Boolean | "boolean" |
| Number | "number" |
| BigInt | "bigint" |
| String | "string" |
| Symbol | "symbol" |
| Function(在 ECMA-262 中实现 [[Call]];classes也是函数) | "function" |
| 其他任何对象 | "object" |
Object.prototype.toString(Symbol.toStringTag)
将基本的 Object.prototype.toString() 用于重写过 toStirng 的对象, 返回 "[object Type]",这里的 Type 是对象的类型。如果对象有 Symbol.toStringTag 属性,其值是一个字符串,则它的值将被用作 Type。许多内置的对象,包括 Map 和 Symbol,都有 Symbol.toStringTag。一些早于 ES6 的对象没有 Symbol.toStringTag,但仍然有一个特殊的标签。它们包括(标签与下面给出的类型名相同):
- Array
- Function(它的 typeof 返回 "function")
- Error
- Boolean
- Number
- String
- Date
- RegExp
arguments 对象返回 "[object Arguments]"。其他所有内容,包括用户自定义的类,除非有一个自定义的 Symbol.toStringTag,否则都将返回 "[object Object]"。
在 null 和 undefined 上调用 Object.prototype.toString() 分别返回 [object Null] 和 [object Undefined]。
const toString = Object.prototype.toString;
console.log(toString.call(123)); // "[object Number]"
console.log(toString.call("abc")); // "[object String]"
console.log(toString.call(true)); // "[object Boolean]"
console.log(toString.call(null)); // "[object Null]"
console.log(toString.call(undefined)); // "[object Undefined]"
console.log(toString.call([])); // "[object Array]"
console.log(toString.call({})); // "[object Object]"
console.log(toString.call(new Date())); // "[object Date]"
console.log(toString.call(/regex/)); // "[object RegExp]"
console.log(toString.call(() => {})); // "[object Function]"
console.log(toString.call(Symbol())); // "[object Symbol]"
// 直接使用 Symbol.toStingTag
// 原始值没法用
const map = new Map();
const type = map[Symbol.toStringTag]; // 'Map'
instanceof
object instanceof constructor
instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log(new Date() instanceof Date); // true
console.log(123 instanceof Number); // false (基本类型不适用)
console.log("abc" instanceof String); // false
function myInstanceof(left, right) {
// 基本类型直接返回 false
if (
left === null ||
(typeof left !== "object" && typeof left !== "function")
) {
return false;
}
// 获取对象的原型
let proto = Object.getPrototypeOf(left);
while (true) {
// 到达原型链末端
if (proto === null) return false;
// 找到匹配的原型对象
if (proto === right.prototype) return true;
// 继续向上遍历原型链
proto = Object.getPrototypeOf(proto);
}
}`
总结对比
| 方法 | 优点 | 缺点 |
|---|---|---|
| typeof | 简单快速,适合基本类型 | 无法区分 null、数组、对象等 |
| instanceof | 可检测自定义对象 | 不适用于基本类型,跨框架失效 |
| Object.prototype.toString | 精准识别所有类型(推荐) | 代码稍长 |
| 特定方法(如Array.isArray) | 针对性强 | 适用范围有限 |
最后
function getType(v) {
return Object.prototype.toString.call(v).slice(8, -1).toLowerCase();
}
console.log(getType(123)); // "nubmer"
console.log(getType("abc")); // "string"
console.log(getType(true)); // "boolean"
console.log(getType([])); // "array"
console.log(getType(null)); // "null"
console.log(getType(Map)); // "function"
console.log(getType(new Map())); // "map"