这是我参与更文挑战的第 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
自定义对象被该方法调用的返回值。
都看到这了,如果文章对你有帮助,点个赞呗~