这是我参与更文挑战的第 23 天,活动详情查看:更文挑战
目前JavaScript 总共有八种数据类型,其中七种属于基本数据类型,也称为原始类型,分别是number、bigint、string、boolean、null、undefined、symbol。还有一种复杂数据类型是object。
typeof 运算符
typeof 运算符用于返回参数的类型,有两种使用方式:typeOf x 和 typeOf(x),这两种使用方式得到的结果是一样的,都以字符串的形式返回数据的类型。
typeof undefined // "undefined"
typeof 0 // "number"
typeof 10n // "bigint"
typeof true // "boolean"
typeof "foo" // "string"
typeof Symbol("id") // "symbol"
typeof alert // "function"
typeof null // "object"
typeof Math // "object"
typeof [] // "object"
用typeof 运算符可以准确识别 6 种基本类型和function类型,但是无法识别null类型。事实上function是object类型的子类型,而null跟object类型没有关系,typeof的这个行为错误属于JavaScript语言的早期错误,考虑到兼容性而保留了下来。
根据类型转换相关知识,null 跟 undefined的判断(即判断数据为空时)通常使用非严格相等(==)运算符,如下所示:
let a = null;
let b = undefined;
a == null; // true
a == undefined; // true
b == null; // true
b == undefined; // true
而在实际项目中,我们常常需要判断数据是否是特殊的对象类型,比如数组、正则表达式、日期等等,就需要使用Object.prototype.toString方法了。
Object.prototype.toString
根据规范20.1.3.6,Object.prototype.toString的逻辑如下所示:
- If the this value is undefined, return "[object Undefined]".
- If the this value is null, return "[object Null]".
- Let O be ! ToObject(this value).
- Let isArray be ? IsArray(O).
- If isArray is true, let builtinTag be "Array".
- Else if O has a [[ParameterMap]] internal slot, let builtinTag be "Arguments".
- Else if O has a [[Call]] internal method, let builtinTag be "Function".
- Else if O has an [[ErrorData]] internal slot, let builtinTag be "Error".
- Else if O has a [[BooleanData]] internal slot, let builtinTag be "Boolean".
- Else if O has a [[NumberData]] internal slot, let builtinTag be "Number".
- Else if O has a [[StringData]] internal slot, let builtinTag be "String".
- Else if O has a [[DateValue]] internal slot, let builtinTag be "Date".
- Else if O has a [[RegExpMatcher]] internal slot, let builtinTag be "RegExp".
- Else, let builtinTag be "Object".
- Let tag be ? Get(O, @@toStringTag).
- If Type(tag) is not String, set tag to builtinTag.
- Return the string-concatenation of "[object ", tag, and "]".
简单来说就是Object.prototype.toString可以判断以下几种类型:
let toString = Object.prototype.toString;
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]
toString.call([1,2,3]); // [object Array]
function func() {
console.log(toString.call(arguments)); // [object Arguments]
}
func();
toString.call(func); // [object Function]
toString.call(new Error()); // [object Error]
toString.call(true); // [object Boolean]
toString.call(123); // [object Number]
toString.call('LvLin'); // [object String]
toString.call(new Date()); // [object Date]
toString.call(/LvLin/g); // [object RegExp]
// 如果不是以上几种类型,就返回 [object Object]
toString.call({}); // [object Object]
Object.prototype.toString在遇到所述几种类型时,会返回相应的值 ([object xxx]),如果不是那几种类型,就返回[object Object]。
但是看一下下面这个:
let map = new Map()
toString.call(map); // [object Map]
为什么不是[object Object]呢?这就需要看规范 15~17 描述的规则,这里稍微进行一下解释:
tag = O[Symbol.toStringTag]- 如果
tag不是String类型(如果对象O没定义Symbol.toStringTag,得到的是undefined),那tag = builtinTag(builtinTag参考自规则 1 ~ 14)- 返回
[object tag]
即如果判断的对象定义了Symbol.toStringTag,并且是字符串类型,就以这个值为结果,否则就让tag = builtinTag,以这个值为结果,最后返回[object tag]。
console.log(map[Symbol.toStringTag]); // "Map"
let name = {
[Symbol.toStringTag]: 'LvLin'
}
console.log(toString.call(name)) // [object LvLin]
name[Symbol.toStringTag] = {};
console.log(toString.call(name)) // [object Object]
最后再看几个类型:
toString.call(Math) // [object Math]
toString.call(JSON) // [object JSON]
toString.call(new Set()) // [object Set]
let a = BigInt(123);
Object.prototype.toString.call(a); // [object BigInt]
a[Symbol.toStringTag]; // "BigInt"
总结
当前(2021 年)JavaScript数据类型总共有 8 种,通过使用typeof运算符和Object.prototype.toString方法进行类型判断。
typeof 能够确定number、bigint、string、boolean、undefined、symbol、function七种类型,其余返回object(包括null)。
Object.prototype.toString方法能够判断更加细致的类型,优先返回的是对象定义的Symbol.toStringTag值,如果该值不是字符串类型,返回相关规则对应的值。可以通过Symbol.toStringTag自定义对象被该方法调用的返回值。
都看到这了,如果文章对你有帮助,点个赞呗~