由 JS基本数据类型 衍生的小知识

263 阅读5分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

相信JS的基本数据类型是每一个前端er入行第一天所接触到的东西,当我们背下5种原始类型Undefined、Null、Boolean、Number 、 String和引用类型对象时,是否了解过ES6和TS里的数据类型和JS有何不同呢?原始类型和引用类型的区别是什么呢?怎么检测一个变量是什么类型呢?你知道多少种检测方式?

今天我们来一一解答这些问题。

1.数据类型有哪些?

我们常说的是JS的5种原始类型和1种引用类型,原始类型即 Undefined、Null、Boolean、Number 和 String引用类型即 对象Object,那ES6和TS中,新增了哪些数据类型呢?内容整理如下:

image.png

既然类型分为原始类型和引用类型,那这两种类型的区别是什么呢?又怎么检测这么多种类型呢?

2. 原始类型和引用类型的区别?

从存储结构上区分,原始数据类型在内存中是栈存储,引用类型是栈存储+堆存储。对应使用上的区别就是拷贝的时候要注意是深拷贝还是浅拷贝

(1)原始类型:栈存储

它的存储过程是这样的

直接用 = 赋值的时候就是深拷贝,当修改第二个变量的值时,不会影响第一个变量的值,举个栗子:

var a = 10
var b = a
b = 20
console.log(a)  // 10

(2)引用类型:栈存储 + 堆存储

它的存储过程是这样的,引用类型在栈存储里面存的只是一个引用,这是一个引用地址,指向堆内存的一块区域,实际的数据存在堆内存中。

引用类型直接用 = 赋值的时候就是浅拷贝,拷贝的只是栈内存里的引用地址,两个引用变量指向的是同一块堆内存区域,所以当修改第二个变量的值时,会影响第一个变量的值,举个栗子:

var obj1 = new Object();
var obj2 = obj1;
obj2.name = "我有名字了";
console.log(obj1.name); // 我有名字了

3. 数据类型的检测方式有几种

先说答案,我整理的一共有4种,分别是typeofObject.prototype.toString.call()instance ofconstructor

(1)typeof

typeof检测后输出的结果有6种:number,string,boolean,undefined,object,function(都是小写)

举个栗子:

console.log(typeof ""); //string
console.log(typeof 1); // number
console.log(typeof true); // boolean
console.log(typeof null); // object
console.log(typeof undefined); // undefined
console.log(typeof []); // object
console.log(typeof function(){}); // function
console.log(typeof {}); // object
console.log(typeof {}.toString); // function
console.log(typeof {}.constructor); // function

typeof的缺陷null,[],{}, 正则,日期,这几个的检测结果都是object,我们无法进一步准确的判断是什么类型。如果想准确检测这几种的类型,我们需要用到Object.prototype.toString.call()

(2)Object.prototype.toString.call()

对于 Object.prototype.toString.call() 方法,会返回一个形如 '[object XXX]' 的字符串。(XXX首字母大写),举个栗子:

console.log(Object.prototype.toString.call("") === '[object String]'); // true
console.log(Object.prototype.toString.call(1) === '[object Number]'); // true
console.log(Object.prototype.toString.call(true) === '[object Boolean]'); // true
console.log(Object.prototype.toString.call(null) === '[object Null]'); // true
console.log(Object.prototype.toString.call(undefined) === '[object Undefined]'); // true
console.log(Object.prototype.toString.call([]) === '[object Array]'); // true
console.log(Object.prototype.toString.call({}) === '[object Object]'); // true
console.log(Object.prototype.toString.call(/[hbc]at/gi) === '[object RegExp]'); // true

还有日期和函数

// 日期类型
var date = new Date();
console.log(Object.prototype.toString.call(date) === '[object Date]'); // true

// 函数类型
Function fn(){
  console.log(“test”);
}
console.log(Object.prototype.toString.call(fn) === '[object Function]'); // true

到这里,看起来Object.prototype.toString.call() 已经覆盖了大部分类型的检测, 功能已经很强大, 但是有一种场景是它无能为力的,就是通过自定义构造函数实例化出来的对象。

function Person(name, age) {
    this.name = name;
    this.age = age;
}
var person = new Person("Rose", 18);
console.log(Object.prototype.toString.call(person)); // "[object Object]"

很明显,它只能检测出来是一个对象,但是不能判断person是Person的实例,因此第三种方法instanceof应运而生。

(3)instanceof

instanceof用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。

还是上面这个栗子,我们改造一下,就能解决Object.prototype.toString.call() 的痛点:

function Person(name, age) {
    this.name = name;
    this.age = age;
}
var person = new Person("Rose", 18);
console.log(person instanceof Person); // true

虽然说instanceof解决了自定义构造函数实例化对象检测上的痛点,但是,在一些基础类型的检测上,它却掉链子了:

console.log("1" instanceof String); // false
console.log(1 instanceof Number); // false
console.log(true instanceof Boolean); // false
console.log(new String("1") instanceof String); //true 
console.log(new Number(1) instanceof Number); //true
console.log(new Boolean(false) instanceof Boolean); //true
console.log([] instanceof Array); //true
console.log(function(){} instanceof Function); //true
console.log({} instanceof Object); //true

我们可以看到,普通的原始数据类型instanceof是无法正确检测的,只有当原始类型string、number、boolean是由new关键字创建的实例化对象时,instanceof才能正确检测。另外,[], {}, function这些引用类型,instanceof是可以正确检测的。

但是,原始类型里的null、undefined是无法检测的,会直接报错。因为instanceof用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。而Null和Undefined本身就是一个不可实例化的对象,所以会直接报错。

image.png

(4)constructor

构造函数实例化对象的constructor属性是指向构造函数的,我们利用这个特性也可以做类型的检测

// 检测原理
function Fn(){}; 
var f=new Fn(); 
console.log(f.constructor===Fn); // true

我们来检测一下所有的数据类型:

console.log(("1").constructor === String); // true
console.log((1).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true
console.log((null).constructor === Null); // 报错:Uncaught TypeError: Cannot read properties of null (reading 'constructor')
console.log((undefined).constructor === Undefined); // 报错:Uncaught TypeError: Cannot read properties of undefined (reading 'constructor')

大体上看,constructor的检测效果和instanceof的检测效果是相似的,他们都不能检测null和undefined。其他数据类型都能正确检测。

(5)总结

没有一种检测方式能准确检测所有的类型,所以我们要在不同的场景下使用对应的检测方式,我总结成了一张表,可以点赞收藏哦,可以当做工作中的自查表:

image.png

原创文章,希望对你有帮助,也为社区贡献自己的一份力量!喜欢请点赞、收藏、关注三连哦~

作者:前端小小梦

主页:了解更多,点击个人主页