进阶理解——typeof 、instanceof


很多时候,回头望,理解会更深刻,也希望能帮助一些初学的同学理解。

先聊聊JavaScript基本类型

以下类型一定注意区别

数据类型

5种含值数据类型

在 JavaScript 中有 5 种不同的可以包含值的数据类型:

  • string
  • number
  • boolean
  • object
  • function

2种不含值类型

  • null
  • undefined

6种类型的对象

  • Object
  • Date
  • Array
  • String
  • Number
  • Boolean

typeof

您可以使用 typeof 运算符来确定 JavaScript 变量的数据类型返回对应数据类型字符串。
这是官方的一句话,细品,这说明什么?
说明:我们可以通过这个运算符判断出含值和不含值的数据类型
而类似于Array、Date等类型的对象,我们无法具体判断,只能判断出时一个object数据类型

typeof "Bill"                 // 返回 "string"
typeof 3.14                   // 返回 "number"
typeof NaN                    // 返回 "number"
typeof false                  // 返回 "boolean"
typeof [1,2,3,4]              // 返回 "object"
typeof {name:'Bill', age:19}  // 返回 "object"
typeof new Date()             // 返回 "object"
typeof function () {}         // 返回 "function"
typeof myCar                  // 返回 "undefined" *
typeof null                   // 返回 "object"

需要注意的是:

  • NaN 的数据类型是数字
  • 数组的数据类型是对象
  • 日期的数据类型是对象
  • null 的数据类型是 object
  • 未定义变量的数据类型为 undefined *
  • 未赋值的变量的数据类型也是 undefined *

看到这,心里可能会有疑惑,说好的可以判断数据类型,null为什么是object?
对咯。就他最特别,这实际上是语言设计上的一个错误,并且由于历史原因一直保留到了现在。所以他是特立独行的—一个bug。

原理是这样的,不同的对象在底层都表示为二进制,
在 JavaScript 中二进制前三位都为 0 的话会被判断为 object 类型,
null 的二进制表示是全 0,自然前三位也是 0,
所以执行 typeof 时会返回“object”。

想了解可以看看本链接
说到这里大家是不是明明白白了,除了一个bug null,typeof只能判断含值和不含值的数据类型
像Array、Data等等这些对象的类型就无法具体判断了。


那么还有别的办法吗?
答案是肯定的

instanceof

instanceof 是一个二元运算符,用于测试构造函数的 prototype 属性是否出现在对象的原型链上
如果对象是指定的构造函数创建的实例,或者继承自该构造函数的原型链,instanceof 运算符会返回 true

class Car {}
const myCar = new Car();

myCar instanceof Car // true
myCar instanceof Object // true(因为Car继承自Object)

[] instanceof Array // true
([]) instanceof Object // true(因为所有的数组都是对象)

function MyFunc() {}
const myFunc = new MyFunc();

myFunc instanceof MyFunc // true
myFunc instanceof Object // true(因为所有函数都是对象)

// 注意:字面量形式创建的基本类型不是任何构造函数的实例
'hello' instanceof String // false
42 instanceof Number // false
true instanceof Boolean // false

由于 instanceof 是基于原型链进行检查的,它能够用于检测复杂对象类型,比如自定义对象和内置对象实例(如 Array、Date 等)。然而,它不适用于基本数据类型。

总结

  • 使用 typeof 来检测基本数据类型,除了 null。
  • 使用 instanceof 来检查一个对象是否是某个特定类(或构造函数)的实例,或者说检测一个对象是否继承自某个原型
  • 要准确地判断一个变量是否为 null,应使用严格等于比较 (=== null)。
  • 对于其他更复杂的类型检查需求,可能需要使用其他方法,比如 Object.prototype.toString.call(value)。

关于第三个,这里说一下undefined 与 null 的区别
undefined 和 null 值相等但类型不同:

typeof undefined           // undefined
typeof null                // object

null === undefined         // false
null == undefined          // true

ps:如果有原型和原型链不太熟悉的,我会抽空补一篇,放下链接,还没有就说明还没写。。。

进一步扩展一下

具体讨论一下typeof局限性

typeof 运算符对于基本数据类型通常很有效,但在某些情况下,它并不能提供足够的信息来准确判断值的类型。以下是一些 typeof 无法准确检测的情况:

  1. 区分对象类型:typeof 将任何对象类型都返回为 “object”,不论它是一个普通的对象字面量、数组、正则表达式还是其他内置对象。
typeof [];            // "object",但实际上是 Array
typeof {};            // "object",普通对象
typeof /regex/;       // "object",在非标准的浏览器中可能返回 "function"
typeof new Date();    // "object",但实际上是 Date
  1. null 值:typeof null 返回 “object”,这实际上是一个长期存在的 JavaScript 错误。
typeof null;          // "object",应该是 `null`

3.区分数组和普通对象:如上所述,数组和普通对象都返回 “object”。

typeof [];            // "object",没有区分它是 Array

4.函数对象和普通对象:虽然 typeof 可以识别函数对象返回 “function”,但无法区分不同种类的函数(例如普通函数、箭头函数、异步函数、生成器函数等)。

typeof function(){};  // "function"
typeof (() => {});    // "function"
typeof async function(){}; // "function"
typeof function*(){}; // "function"

5.原始包装对象和原始值:对于字符串、数字和布尔值的原始包装对象,typeof 会将其视为 “object”,而非它们各自的原始类型。

typeof new String("string"); // "object"
typeof new Number(100);      // "object"
typeof new Boolean(true);    // "object"

6.特殊对象:typeof 对于特殊的对象,比如 Map、Set、WeakMap 和 WeakSet 等也只能返回 “object”。

typeof new Map();     // "object"
typeof new Set();     // "object"
  1. Undefined vs. undeclared:虽然 typeof 在处理未声明变量时不会抛出错误,并且会返回 “undefined”,但它无法区分一个变量是未定义还是未声明。
let x;
typeof x;             // "undefined"
typeof y;             // "undefined",即使 y 没有被声明

由于这些局限性,当需要更精确地判断复杂数据类型时,开发者通常会采用其他方法,如 instanceof 检查、Object.prototype.toString.call() 方法,或者在现代JavaScript框架和库中定义的自己的类型判断函数。

扩展判断方法

JavaScript 提供了 Array.isArray() 方法来特别判断一个值是否为数组。它比 typeof 更精确,因为 typeof 对于数组会返回 “object”。

对于其他类型的检查,虽然没有内置的类似 Array.isArray() 这样针对特定数据结构的方法,但我们可以使用 Object.prototype.toString.call() 来获取对象的类(class)信息:

let toString = Object.prototype.toString;

// 使用 toString 方法检测不同类型
toString.call([]); // "[object Array]"
toString.call({}); // "[object Object]"
toString.call(''); // "[object String]"
toString.call(new Date()); // "[object Date]"
toString.call(1); // "[object Number]"
toString.call(true); // "[object Boolean]"
toString.call(function(){}); // "[object Function]"
toString.call(/regex/); // "[object RegExp]"
toString.call(null); // "[object Null]"
toString.call(undefined); // "[object Undefined]"

通过上面的方法,你可以构建自己的类型检查函数,就像 Array.isArray() 那样。例如:

function isDate(value) {
    return toString.call(value) === '[object Date]';
}

此外,ES6 引入了几个新的全局对象,也带来了类似的静态方法用于确定值是否为特定类型的实例:

  • ArrayBuffer.isView(value): 检查一个值是否是类型化数组视图,比如 Int8Array 实例或其他类型化数组视图。
ArrayBuffer.isView(new Int8Array()); // true
ArrayBuffer.isView(new Float32Array()); // true
ArrayBuffer.isView([]); // false
  • Number.isNaN(value): 确定传入的值是否是 NaN,且更准确地与全局 isNaN() 函数区分开来。
Number.isNaN(NaN); // true
Number.isNaN('NaN'); // false
  • Number.isInteger(value): 判断给定的参数是否为整数。
Number.isInteger(1); // true
Number.isInteger(1.5); // false
  • Number.isFinite(value): 检查一个值是否为有限数。
Number.isFinite(Infinity); // false
Number.isFinite(-Infinity); // false
Number.isFinite(1); // true

这些方法为不同类型的检测提供了官方支持并保证了结果的准确性。但仍然没有像 Array.isArray() 那样的方法用于直接检测 Map, Set, WeakMap, 或 WeakSet 等数据类型。在这些情况下,通常需要使用上述 Object.prototype.toString.call() 方式来进行类型的判断。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值