typeof 、instance of
很多时候,回头望,理解会更深刻,也希望能帮助一些初学的同学理解。
先聊聊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 无法准确检测的情况:
- 区分对象类型:typeof 将任何对象类型都返回为 “object”,不论它是一个普通的对象字面量、数组、正则表达式还是其他内置对象。
typeof []; // "object",但实际上是 Array typeof {}; // "object",普通对象 typeof /regex/; // "object",在非标准的浏览器中可能返回 "function" typeof new Date(); // "object",但实际上是 Date
- 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"
- 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() 方式来进行类型的判断。