本帖最后由 Yiero 于 2023-9-30 14:13 编辑
如何判断一个值的类型
本章内容:
- 使用
typeof
判断基本类型
- 使用
Object.prototype.toString.call()
判断对象类型
- 通过类属性
Symbol.toStringTag
, 让自定义类也能输出类型
- 改变原生对象的类型
1. 基础类型判断
Js 提供了一个类型判断关键字 typeof
, 使用该关键字可以输出基本类型的类型字符串, 比如 typeof 'hello'
会输出字符串 'string'
.
这个关键字只能判断 js 中的八个数据类型还有函数: number
, string
, boolean
, undefined
, symbol
, bigint
, object
, null
和 function
.
需要注意的是 typeof null
输出的是 'object'
而不是 null
, 如果要判断 null
需要使用后面第三节的内容.
/* 数字类型 */
console.log('数字类型: ', typeof 1);
// -> 数字类型: number
/* 字符串类型 */
console.log('字符串类型: ', typeof 'hello');
// -> 字符串类型: string
/* 布尔类型 */
console.log('布尔类型: ', typeof true);
// -> 布尔类型: boolean
/* undefined类型 */
console.log('undefined类型: ', typeof undefined);
// -> undefined类型: undefined
/* Symbol类型 */
console.log('Symbol类型: ', typeof Symbol("hello"));
// -> Symbol类型: symbol
/* BigInt类型 */
console.log('BigInt类型: ' ,typeof BigInt(123));
// -> BigInt类型: bigint
/* Object类型 */
console.log('Object类型: ',typeof {});
// -> Object类型: object
/* null类型 */
console.log('null类型: ', typeof null);
// -> null类型: object
/* function类型 */
console.log( 'function类型: ', typeof function () {} );
// -> function类型: function
2. typeof
的弊端
typeof
只能判断 原始值 , 也就是上文中提到的除了 Object
和 null
以外的类型, 因为除了原始值, 其它的任何类型 (引用类型) 都被判断为 'object'
.
/* Object类型 */
console.log('Object类型: ',typeof {});
// -> Object类型: object
/* null类型 */
console.log('null类型: ', typeof null);
// -> null类型: object
/* Array类型 */
console.log('Array类型: ',typeof []);
// -> Array类型: object
/* Set类型 */
console.log('Set类型: ',typeof new Set());
// -> Set类型: object
/* Date类型 */
console.log('Date类型: ',typeof new Date());
// -> Date类型: object
3. Array.isArray()
的原理 - Object.prototype.toString.call()
除了 typeof
, js 中的某些类型还为我们提供了一些静态方法用于检测特定类型, 比如 Array.isArray()
这个静态方法可以检测传入的参数是否是一个数字, 那么这是基于什么原呢?
在 Object
的原型上有一个 toString()
方法: Object.prototype.toString()
. Object.prototype.toString()
返回 "[object Type]"
, 这里的 Type
是对象的类型.
如果直接调用 Object.prototype.toString()
, 则会输出 "[object Object]"
.
console.log( Object.prototype.toString() );
// -> [object Object]
那么我们该如何使用 Object.prototype.toString()
来判断引用类型的类型呢? 我们可以通过 Function.prototype.call()
改变这个方法的 this
指向, 让其指向我们需要判断的值的类型, 就可以判断引用类型的类型了.
比如如果将 Object.prototype.toString()
的 this
指向一个数组, 那么就会输出 "[object Array]"
.
console.log( Object.prototype.toString.call( [] ) );
// -> [object Array]
基于这一点, 那么我们就自己可以实现 Array.isArray()
:
function isArray(value) {
return Object.prototype.toString.call(value) === '[object Array]';
}
再拓展一点, 像是 isMap()
, isMath()
这些类似的类型判断函数, 也是同样的实现原理:
function isMap(value) {
return Object.prototype.toString.call(value) === '[object Map]';
}
function isMath(value) {
return Object.prototype.toString.call(value) === '[object Math]';
}
4. 通用的类型判断函数
在第三节中, 我们了解到了如何判断一个值的类型, 那么其实我们可以基于 Object.prototype.toString()
会返回 "[object Type]"
的原理, 写一个通用的类型判断函数, 获取其中的这个 Type
.
/**
* 判断 value 的类型是否是给定的 type
*
* @param {string} type - 预期想要的类型.
* @param {any} value - 需要判断的值.
* @returns {boolean} - 如果传入的 value 是需要的类型, 则输出 true, 否则输出 false
*/
function judgeType( type, value ) {
return Object.prototype.toString.call( value ).slice( 8, -1 ).toLowerCase() === type.toLowerCase();
}
基于这个通用函数, 我们可以很方便地再次封装具体的类型判断函数:
const isArray = judgeType.bind( null, 'array' );
const isSet = judgeType.bind( null, 'set' );
const isMap = judgeType.bind( null, 'map' );
const isMath = judgeType.bind( null, 'math' );
const isDate = judgeType.bind( null, 'date' );
5. 自定义类判断类型
如果我们自己定义了一个类, 直接使用 Object.prototype.toString.call()
只会输出 [object Object]
:
class Type {}
console.log( Object.prototype.toString.call( new Type() ) );
// -> [object Object]
这里需要定义一个类属性 Symbol.toStringTag
, Object.prototype.toString()
获取值类型的本质就是就是获取这个属性的值.
class Type {
[Symbol.toStringTag] = 'Type';
}
console.log( Object.prototype.toString.call( new Type() ) );
// -> [object Type]
同理, 由于 Object.prototype.toString()
获取的是 Symbol.toStringTag
的值, 所以原生的一些类型的值也是可能更改的:
Array.prototype[Symbol.toStringTag] = 'NotArray';
console.log( Object.prototype.toString.call( [ 1, 2, 3 ] ) );
// -> [object NotArray]