Hello,我是冬青,一个具有产品 sense 的前端。
今天由我来带大家一起探索 JS 类型的世界。
Hello, types!
相信大家都有所了解,JS 是一种 动态 / 弱类型 语言。那么什么是动态语言,什么是弱类型语言呢?
动态语言
根据维基百科的描述,动态语言是 一类在运行时可以改变其结构的语言。也就是说,我们在写 JS 代码的时候,无需提前声明变量的类型,它的类型是在运行时被自动确定的,并且我们也可以使用同一个变量保存不同类型的值。
下面我用一段代码来演示一下:
let notSure = 'hello';
console.log(typeof notSure); // "string"
notSure = 123;
console.log(typeof notSure); // "number"
notSure = true;
console.log(typeof notSure); // "boolean"
复制代码我们首先声明了一个变量 notSure,并初始化为 "hello",注意,这里并没有规定变量 notSure 的类型。
然后我们使用 typeof 操作符查询变量 notSure 的数据类型,打印的结果为 "string"。也就是说,变量 notSure 在代码运行过程中自动拥有了一个 String 类型,并且通过修改变量的值,变量的类型也会随着改变(我们可以称之为 运行时类型)。
弱类型语言
关于弱类型语言,维基百科也没有给出准确的定义,这里我给出自己的理解,弱类型语言,一般具有 隐式类型转换 的特性。
话不多说,上代码:
const str = 'hello';
const num = 11;
console.log(str + num); // "hello11"
复制代码是的,你没有看错,String 类型的变量 str 和 Number 类型的变量 num 居然可以相加(准确来说是字符串拼接运算,而不是算数运算),打印的结果为 String 类型的 "hello11"。
这是因为变量 num 发生了隐式类型转换,类型由原来的 Number 转为了 String,然后和 String 类型的变量 str 发生了字符串拼接。
实际上,隐式类型转换在 JS 中是十分常见的,比如 ==、&&、||、!、+、? :、if...else 等等,有人喜欢它的 灵活 和 包容,有人讨厌它的 未知 和 不可控。
Types in JS
简单了解了动态语言和弱类型语言的基本概念之后,我们继续探索一下,JS 中有哪些数据类型。
原始类型
原始类型,又称 基本类型。JS 中有 7 种原始类型:
- Undefined
- Null
- Boolean
- Number
- String
- BigInt
- Symbol
那么什么是原始类型呢?—— 所有原始类型的值都是 不可改变的。
需要特别注意的是,重新赋值不等同于改变了之前的原始值。
我们可以使用 typeof 运算符检查原始值的数据类型(Null 除外):
console.log(typeof undefined); // "undefined"
console.log(typeof true); // "boolean"
console.log(typeof 11); // "number"
console.log(typeof 'hello'); // "string"
console.log(typeof 9007199254740992n); // "bigint"
console.log(typeof Symbol()); // "symbol"
复制代码为什么 Null 除外呢?接着往下看:
console.log(typeof null); // "object"
复制代码什么,typeof null === "object"?
是的,在 JS 设计之初,值是由一个 类型标签 和 实际数据值 表示的。
对象的类型标签是 0,而 Null 代表的是空指针(大多数平台下值为 0x00),它的类型标签也是 0,因此 typeof null === "object",这是一个历史遗留的问题。
不过我们依然可以使用 typeof 去判断一个值是否为 Null 类型,在写代码之前,先插入一个很重要的概念 —— falsy。
我们知道,false 是 Boolean 类型的两个值之一,表示假的。而 falsy 作为它的名词形式出现,表示转换为 Boolean 类型之后为 false 的值。
比较常见的 falsy 有:
- 0
- NaN
- ""(注意这里是空字符串,不是空格字符串)
- null
- undefined
console.log(Boolean(0)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
复制代码那么重点来了 ——
null 是 JS 世界中唯一一个 typeof 运算符返回 "object" 的同时,自身为 falsy 的值。
所以我们可以封装一个用于判断值是否为 Null 类型的通用函数:
const isNull = value => typeof value === 'object' && !value ? true : false;
复制代码Object 类型
Object 类型,也称 引用类型,几乎所有通过 new 创建的东西,都为 Object 类型:
const obj = { name: '冬青', age: 24 };
console.log(typeof obj); // "object"
const arr = [1, 2, 3];
console.log(typeof arr); // "object"
const date = new Date();
console.log(typeof date); // "object"
const reg = /abc/;
console.log(typeof reg); // "object"
const boolObj = new Boolean(true);
console.log(typeof boolObj); // "object"
const numObj = new Number(11);
console.log(typeof numObj); // "object"
const strObj = new String('hello');
console.log(typeof strObj); // "object"
复制代码关于 boolObj、numObj、strObj,这里补充几个比较重要的概念 —— 装箱转换、拆箱转换、临时装箱。
let str = 'hello';
console.log(typeof str); // "string"
str = new String(str); // 装箱转换
console.log(typeof str); // "object"
复制代码装箱转换之前,变量 str 存储的只是一个 String 类型的 原始值;装箱转换之后,变量 str 变成了 Object 类型的、通过 new 一个构造函数 String 创建的 实例对象。变量 str 同时也拥有了构造函数 String 对应原型对象上的属性和方法。
当然,我们也可以通过拆箱转换,获取到一个实例对象的原始值:
let str = new String('hello');
str = str.valueOf();
console.log(str); // "hello"
复制代码相信大家有疑问了,String 类型的原始值,也可以直接使用构造函数 String 对应原型对象上的属性和方法呀,这就涉及到了临时装箱(auto-boxing)的过程,JS 主动帮我们做了。
Function 类型
有很多人把 Function 归为 Object 类型,因为 Function 也是派生自 Object,它也有自己的属性:
const fn = (a, b) => {};
console.log(fn.name); // "fn"
console.log(fn.length); // 2
复制代码但是由于它在 JS 世界中一等公民的地位,在设计时,typeof 运算符返回 "function":
console.log(typeof function () {}); // "function"
复制代码What type?
在 JS 中,如何判断数据的类型呢?下面我来为大家列举最常见的 3 种方法。
typeof
返回一个字符串,表示未经计算的操作数的类型。
关于 typeof 运算符,我们已经见识过了,它可以准确地判断出除了 Null 以外的所有原始类型,以及 Function 类型,但是遇到 Object 类型的数据,它就无能为力了,只能返回 "object"。
如果我们想获取更准确的类型,typeof 显然无法满足我们的需求。
instanceof
用于检测构造函数的
prototype属性是否出现在某个实例对象的原型链上。
这句话如何理解呢?上代码:
const arr = [1, 2, 3];
console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // true
复制代码因为构造函数 Array 的 prototype 属性出现在 arr 的原型链上,而 Array 派生自 Object,所以构造函数 Object 的 prototype 属性也出现在 arr 的原型链上,两次打印结果都为 true。
instanceof 同样适用于自定义的类或者构造函数:
class List extends Array {
  // ...
}
const list = new List();
console.log(list instanceof List); // true
console.log(list instanceof Array); // true
console.log(list instanceof Object); // true
复制代码关于原型链,大家可以先简单了解一下,后续我会单独出一篇关于原型链的文章。
使用 instanceof 判断数据类型,最直观的好处是可以判断自定义的类或者构造函数,但是  instanceof 也有它的不足之处,比如它无法判断原始类型,且使用场景更适合对已有的猜测进行确认。
Object.prototype.toString.call()
Object.prototype.toString.call() 返回
"[object Type]",其中Type是对象的类型。
或许这是判断数据类型的终极方案了,上代码:
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
console.log(Object.prototype.toString.call(null)); // "[object Null]"
console.log(Object.prototype.toString.call(true)); // "[object Boolean]"
console.log(Object.prototype.toString.call(123)); // "[object Number]"
console.log(Object.prototype.toString.call('hello')); // "[object String]"
console.log(Object.prototype.toString.call(9007199254740992n)); // "[object BigInt]"
console.log(Object.prototype.toString.call(Symbol())); // "[object Symbol]"
console.log(Object.prototype.toString.call({})); // "[object Object]"
console.log(Object.prototype.toString.call([])); // "[object Array]"
console.log(Object.prototype.toString.call(new Date())); // "[object Date]"
console.log(Object.prototype.toString.call(/abc/)); // "[object RegExp]"
console.log(Object.prototype.toString.call(() => {})); // "[object Function]"
复制代码可见 Object.prototype.toString.call() 真的非常强大,它几乎可以帮我们判断所有的数据类型,但是返回结果为 "[object Type]" 的形式,感觉怪怪的,不过我们可以手动做一层封装,让它变得更好用。
封装
学习了上面 3 种判断数据类型的方法,我们可以试着自己封装一个好用的方法:
const typeOf = value => {
  if (typeof value !== 'object') {
    return typeof value;
  } else {
    if (!value) {
      return 'null';
    } else {
      const str = Object.prototype.toString.call(value);
      return str.slice(8, -1).toLowerCase();
    }
  }
};
const arr = [1, 2, 3];
console.log(typeOf(arr)); // "array"
复制代码Bye, types!
关于 JS 的数据类型,就先聊到这吧,写困了,睡觉,晚安。
























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)
