Yiero 发表于 2023-9-29 21:56:34

[JavaScript基础] 如何判断一个值的类型

本帖最后由 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` 需要使用后面第三节的内容.

```js
/* 数字类型 */
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` 只能判断 [原始值](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Data_structures#%E5%8E%9F%E5%A7%8B%E5%80%BC) , 也就是上文中提到的除了 `Object` 和 `null` 以外的类型, 因为除了原始值, 其它的任何类型 (引用类型) 都被判断为 `'object'`.

```js
/* 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()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/toString). `Object.prototype.toString()` 返回 `""`, 这里的 `Type` 是对象的类型.

如果直接调用 `Object.prototype.toString()`, 则会输出 `""`.

```js
console.log( Object.prototype.toString() );
// ->
```



---

那么我们该如何使用 `Object.prototype.toString()` 来判断引用类型的类型呢? 我们可以通过 [`Function.prototype.call()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call) 改变这个方法的 `this` 指向, 让其指向我们需要判断的值的类型, 就可以判断引用类型的类型了.

比如如果将 `Object.prototype.toString()` 的 `this` 指向一个数组, 那么就会输出 `""`.

```js
console.log( Object.prototype.toString.call( [] ) );
// ->
```



基于这一点, 那么我们就自己可以实现 `Array.isArray()`:

```js
function isArray(value) {
    return Object.prototype.toString.call(value) === '';
}
```



再拓展一点, 像是 `isMap()`, `isMath()` 这些类似的类型判断函数, 也是同样的实现原理:

```js
function isMap(value) {
    return Object.prototype.toString.call(value) === '';
}
```

```js
function isMath(value) {
    return Object.prototype.toString.call(value) === '';
}
```



## 4. 通用的类型判断函数

在第三节中, 我们了解到了如何判断一个值的类型, 那么其实我们可以基于 `Object.prototype.toString()` 会返回 `""` 的原理, 写一个通用的类型判断函数, 获取其中的这个 `Type` .

```js
/**
* 判断 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();
}
```



---

基于这个通用函数, 我们可以很方便地再次封装具体的类型判断函数:

```js
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()` 只会输出 ``:

```js
class Type {}

console.log( Object.prototype.toString.call( new Type() ) );
// ->
```

---

这里需要定义一个类属性 `Symbol.toStringTag` , `Object.prototype.toString()` 获取值类型的本质就是就是获取这个属性的值.

```js
class Type {
        = 'Type';
}

console.log( Object.prototype.toString.call( new Type() ) );
// ->
```

---

同理, 由于 `Object.prototype.toString()` 获取的是 `Symbol.toStringTag` 的值, 所以原生的一些类型的值也是可能更改的:

```js
Array.prototype = 'NotArray';

console.log( Object.prototype.toString.call( [ 1, 2, 3 ] ) );
// ->
```



steven026 发表于 2023-9-30 12:11:04

!(data/attachment/forum/202309/30/120922o4znlbuuaoci6tol.png)
typeoffunction\arrow function\class 都会返回function

另外typeof 永远不会返回'null'

Yiero 发表于 2023-9-30 14:09:31

steven026 发表于 2023-9-30 12:11
!(data/attachment/forum/202309/30/120922o4znlbuuaoci6tol.png)
typeoffunction\arrow...

写漏了, 我改一下(
页: [1]
查看完整版本: [JavaScript基础] 如何判断一个值的类型