又快年底了,你是不是快忘了js类型判断了?
前言
- 在开始本篇讲解之前,你可能需要一些原型的前置知识,如果还不是很懂,可以通过 原型链 这篇文章进行学习或者巩固。我们先来了解一下JavaScript的数据结构,我们很快就会被用到。
JavaScript 7种内置类型
- 空值(
null)、未定义(undefined)、布尔值(boolean)、数字(number)、字符串(string)、对象(object)、符号(symbol)。
JavaScript 7种原始类型
- 空值(
null)、未定义(undefined)、布尔值(boolean)、数字(number)、字符串(string)、 任意精度整数(Bigint)、符号(symbol)。
什么是 typeof
typeof用于判断一个变量的是什么类型,通过typeof判断可以返回number、string、object,boolean,function、undefined、symbol这七种基本类型- 其基本语法是以下这样的形式:
// operand 表示要返回类型的对象或基本类型的表达式
typeof operand
typeof (operand)
typeof 基本用法
- 接下来我们使用
typeof运算符来查看值的类型,它返回的是类型的字符串值。有意思的是,这七种类型和它们的字符串值并不一一对应:
console.log(typeof 777); // number
console.log(typeof 3.14); // number
console.log(typeof 0); // number
console.log(typeof Infinity); // number
console.log(typeof Number("moment")); // number
console.log(typeof 77n); // bigint
console.log(typeof "1"); // string
console.log(typeof typeof 1); // string typeof 返回一个字符串
console.log(typeof String(777)); // string
console.log(typeof true); // boolean
console.log(typeof false); // boolean
console.log(typeof Boolean(5)); // boolean // Boolean() 会基于参数是真值还是虚值进行转换
typeof !!1 === "boolean"; // 两次调用 !(逻辑非)运算符相当于 Boolean()
console.log(typeof Symbol()); // symbol
console.log(typeof Symbol("foo")); // symbol
console.log(typeof Symbol.iterator); // symbol
console.log(typeof { a: 1 }); // object
console.log(typeof [1, 2, 4]); // object
console.log(typeof new Date()); // object
console.log(typeof /regex/); // object
console.log(typeof null); // object
console.log(typeof function () {}); // function
console.log(typeof class T {}); // function
- 你可能注意到
null类型比较特殊,typeof null==='object',正确的返回应该是'null',但是这个bug由来已久,在JavaScript中已经存在了将近二十年,也许永远不会修复。
在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于
null代表的是空指针(大多数平台下值为 0x00),因此,null 的类型标签是 0,typeof null也因此返回"object"。
console.log(typeof function () {}); // function
- 在上面的代码中,输出结果为
'function',说明function也是JavaScript的一个内置类型。然而规范里,它实际上是object的一个子类型。具体来说,函数是可调用对象,它有一个内部属性[[call]],该属性使其可以被调用。
function f() {}
console.log(f.__proto__.constructor === Function); // true
console.log(f.__proto__.__proto__.constructor === Object); // true
// 函数不仅是对象,还可以拥有属性
console.log(f.name); // f
console.log(f.arguments); // 因为没有传参数,所以是一个 null
- 通过原型的方法去判别,函数确实是 Object的子类。
- 再来看看数组,
JavaScript支持数组,数组也是对象,确切涞水,它也是object的一个子类型,数组的元素按数字顺序来进行索引,其length属性是元素的个数。
const foo = [];
console.log(foo.__proto__.constructor === Array); // true
console.log(foo.__proto__.__proto__.constructor === Object); // true
new 操作符
- 所有使用
new调用的构造函数都将返回非基本类型,返回的不是object类型,就是function。大多数返回对象,但值得注意的例外是 Function,它返回一个函数。
const str = new String("777");
const num = new Number(777);
const func = new Function();
console.log(typeof str); // object
console.log(typeof num); // object
console.log(typeof func); // function
undefined 和 undeclared
- 变量在未持有值的时候为
undefined。此时typeof返回undefined:
var a;
console.log(tyoeof a); // undefined
- 大多数的开发者倾向于将
undefined等同于undeclared(未声明),但在JavaScript中它们完全是两回事。在作用域中声明但是还没有赋值的变量,是undefined。相反,还没有在作用域中声明过的变量,是undeclared的。

- 在上列中,
bar is not defined容易让人误以为是bar is undefined。但是undefined和is undefined是两码事,但是typeof处理undeclared返回的结果竟然是undefined,例如:
var foo;
console.log(typeof foo); // undefined
console.log(typeof bar); // undefined
- 它们两个原样返回
"undefined",并且typeof bar并没有报错,这是因为typeof有一个特殊的安全防范机制。
内部属性[[class]]
- 在前面的例子中,使用
typeof进行判断,无论是null、Object、Array等类型,都返回的是"object",那么是否有一种机制可以判断它具体为什么类型的值呢?答案是有的。 - 所有
typeof返回值为object的对象(如数组)都包含一个内部属性[[class]] ,我们可以把它看作一个内部的分类,而非传统的面向对象意义上的类。这个属性无法直接访问,一般通过Object.prototype.toString(...)来查看。例如:
console.log(Object.prototype.toString.call([1, 2, 3])); // [object Array]
console.log(Object.prototype.toString.call(1)); // [object Number]
console.log(Object.prototype.toString.call("moment")); //[object String]
console.log(Object.prototype.toString.call(true)); //[object Boolean]
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call(function f() {})); // [object Function]
console.log(Object.prototype.toString.call(class C {})); // [object Function]
console.log(Object.prototype.toString.call(new Date())); // [object Date]
console.log(Object.prototype.toString.call(Symbol())); // [object Symbol]
console.log(Object.prototype.toString.call(new Boolean(1))); // [object Boolean]
console.log(Object.prototype.toString.call(new RegExp())); // [object RegExp]
- 上例中,数组内部[[class]]属性值是
Array,正则表达式的值是RegExp。多数情况下,对象的内部[[class]]属性和创建该对象的内建原生构造函数相对应,但并不是所有的情况都是这样,例如一些基本类型,例如null和undefined,虽然Null()和undefined()这样的原生构造函数并不存在,但是内部[[class]]属性值仍然是Null和Undefined。 - 其他基本类型,例如 字符串、数值和布尔值 的情况有所不同,由于基本类型值没有
.length和.toString()这样的属性和方法,需要通过封装对象才能访问,此时JavaScript会自动为基本类型封装为一个对象,例如var foo = 'moment';,实际上进行的是var foo =new String('moment');,使其变成一个对象,让其拥有自己的属性和方法,如果想要得到封装对象中的基本类型值,可以使用valueOf()函数,例如:
var foo = new String("moment");
console.log(foo); // [String: 'moment']
console.log(foo.valueOf()); // moment
console.log(typeof foo.valueOf()); // string
手写 typeof
typeof是非常有用的,但它不像需要的那样万能。例如,typeof []是"object",以及typeof new Date()、typeof /abc/等。- 为了明确地检查类型,
mdn上提供了一个自定义的type(value)函数,它主要模仿typeof的行为,但对于非基本类型(即对象和函数),它在可能的情况下返回更详细的类型名。
function type(value) {
// 如果传入的值是 null ,则返回 null
if (value === null) {
return "null";
}
const baseType = typeof value;
// 如果是基本类型
if (!["object", "function"].includes(baseType)) {
return baseType;
}
// Symbol.toStringTag 通常指定对象类的“display name”
const tag = value[Symbol.toStringTag];
if (typeof tag === "string") {
return tag;
}
// 如果他是一个函数,其源代码以 class 关键字开头的
if (
baseType === "function" &&
Function.prototype.toString.call(value).startsWith("class")
) {
return "class";
}
// 构造函数的名称;例如 `Array`、`GeneratorFunction`、`Number`、`String`、`Boolean` 或 `MyCustomClass`
const className = value.constructor.name;
if (typeof className === "string" && className !== "") {
return className;
}
// 没有合适的方法来获取值的类型,直接返回
return baseType;
}
参考文献
- 书籍
你不知道的JavaScript - mdn文档
转载自:https://juejin.cn/post/7168082770181226527