从入门到清晰,聊聊 8 种数据类型和 4 种类型检测方法

328次阅读  |  发布于2年以前

1、JS中的8种数据类型

目前阶段JavaScript中的数据类型,一共8种,主要分类两大类型:基本类型引用类型

1.1、两大类型的区别

基本类型又叫做简单类型或者值类型,引用类型又叫做复杂类型;

基本类型(按值访问):在存储时变量中存储的是值本身,因此也叫做值类型;

引用类型(按引用访问):在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型;

介绍一下堆和栈:

(stack) :用来保存简单的数据字段

(heap):用来保存栈中简单数据字段对指针的引用

区别:

1、栈(操作系统):

由操作系统自动分配释放存放函数的参数值、局部变量的值等,其操作方式类似于数据结构中的栈;

简单数据类型直接存放到栈里面。

2、堆(操作系统):

存储复杂类型,一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收;

复杂数据类型引用放在栈里面,实际数据存放到堆里面。

为啥会导致上述区别,是因为:

1、基本类型的数据简单,所占用空间比较小,内存由系统自动分配;

2、引用类型数据比较复杂,复杂程度是动态的,计算机为了较少反复的创建和回收引用类型数据所带来的损耗, 就先为其开辟另外一部分空间——即堆内存,以便于这些占用空间较大的数据重复利用;

3、堆内存中的数据不会随着方法的结束立即销毁,有可能该对象会被其它方法所引用, 直到系统的垃圾回收机制检索到该对象没有被任何方法所引用的时候才会对其进行回收。

具体点,举个小栗子看看实际的影响:

// 基本数据类型
let  a = 1;
let  b = a;
b = 2;
console.log(a, b) // 1, 2
复制代码
// 引用数据类型
let  obj1 = {a: 1, b: 2};
let  obj2 = obj1;
obj2.a = 20;
console.log(obj1.a, obj2.a) // 20, 20
复制代码

1.2、两大类型的具体细分:

由于篇幅原因,前面五个老生常谈的基本类型就略过了... , 看看下面三个:

1、Symbol

Symbol 本质上是一种唯一标识符,可用作对象的唯一属性名,这样其他人就不会改写或覆盖你设置的属性值。声明方法:

let id = Symbol("id");
复制代码

Symbol 数据类型的特点是唯一性,即使是用同一个变量生成的值也不相等。

let id1 = Symbol('id');
let id2 = Symbol('id');
console.log(id1 == id2);  //false
复制代码

Symbol 数据类型的另一特点是隐藏性,for···inobject.keys() 不能访问

 let id = Symbol("id");
 let obj = {
  [id]:'symbol'
 };
 for(let option in obj){
     console.log(obj[option]); //空
 }
复制代码

但是也有能够访问的方法:Object.getOwnPropertySymbols, 该方法会返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。

let id = Symbol("id");
let obj = {
 [id]:'symbol'
};
let array = Object.getOwnPropertySymbols(obj);
console.log(array); //[Symbol(id)]
console.log(obj[array[0]]);  //'symbol'
复制代码

虽然这样保证了Symbol的唯一性,但我们不排除希望能够多次使用同一个symbol值的情况。为此,官方提供了全局注册并登记的方法:Symbol.for()

 let name1 = Symbol.for('name'); //检测到未创建后新建
 let name2 = Symbol.for('name'); //检测到已创建后返回
 console.log(name1 === name2); // true
复制代码

通过这种方法就可以通过参数值获取到全局的symbol对象了,反之,能不能通过symbol对象获取到参数值呢?是可以的 ,通过Symbol.keyFor()

 let name1 = Symbol.for('name');
 let name2 = Symbol.for('name');
 console.log(Symbol.keyFor(name1));  // 'name'
 console.log(Symbol.keyFor(name2)); // 'name'
复制代码

最后,提醒大家一点,在创建symbol类型数据 时的参数只是作为标识使用,所以 Symbol() 也是可以的。

2、BigInt

2.1、它是什么

BigInt数据类型提供了一种方法来表示大于2^53-1的整数。BigInt可以表示任意大的整数

2.2、解决了什么问题

Number类型只能安全的支持-9007199254740991(-(2^53-1))9007199254740991(2^53-1)之间的整数,任何超过这个范围的数值都会失去精度;而BigInt可以解决这个问题

console.log(9007199254740999) //9007199254741000
console.log(9007199254740993===9007199254740992) //true
12
复制代码

上图,当数值超过Number数据类型支持的安全范围值时,将会被四舍五入,从而导致精度缺失的问题

2.3、如何使用BigInt

1、方式一:在整数的末尾追加n

console.log(9007199254740999n); //9007199254740999
复制代码

2、方式二:调用BigInt()构造函数

let bigInt = BigInt("9007199254740999"); //传递给BigInt()的参数将自动转换为BigInt
console.log(bigInt); //9007199254740999n    
复制代码

2.4、注意事项

1、BigInt除了不能使用一元加号运算符外,其他的运算符都可以使用

 console.log(+1n); // Uncaught TypeError: Cannot convert a BigInt value to a number
 console.log(-1n); //ok
复制代码

2、BigIntNumber之间不能进行混合操作

console.log(1n+5)
复制代码

如果希望使用BigIntNumber执行算术计算,首先需要确定应该在哪个类型中执行该操作。为此,只需通过调用Number()BigInt()来转换操作数:

BigInt(10) + 10n;    // → 20n
// 或者(在同一环境中操作)
10 + Number(10n);    // → 20
复制代码

2.5、总结:

1、BigInt数据类型提供了一种方法来表示大于2^53-1或者小于-2^53-1的整数,BigInt可以表示任意大的整数

2、不能使用NumberBigInt操作数的混合执行算术运算,需要通过显式转换其中的一种类型,使得两者在同一环境中操作;

3、此外,出于兼容性原因,不允许在BigInt上使用一元加号(+)运算符。

3、Object(万物皆对象)

Js中常用的引用类型有:ObjectArrayFunctionDateRegExp

3.1、Object类型

带有属性和方法的特殊数据类型;

创建Object实例的方式有两种。第一种是使用new操作符后跟Object构造函数,例如;

let person = new Object();
person.name = "Nicholas";
person.age = 29;
console.log(person instanceof Object); // true
复制代码

另一种方式是使用对象字面量表示法。例如:

let person = {
  name : "Nicholas",
  age ; 29
}
console.log(person instanceof Object); // true
复制代码

注意:在通过对象字面量定义对象时,实际上不会调用Object构造函数。

3.2、Array类型

是使用单独的变量名来存储一系列的值;

创建数组的基本方式有两种。第一种是使用Array构造函数,例如:

let colors = new Array();
console.log(colors instanceof Array);  // true
复制代码

第二种基本方式是使用数组字面量表示法。数组字面量由一对包含数组项的方括号表示,多个数组项之间以逗号隔开,例如:

let colors = ["red","blue","green"];
console.log(colors instanceof Array);  // true
复制代码

3.3、Function类型

函数类型在JavaScript中也是对象

函数实际上是对象,函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。函数通常是使用函数声明语法定义的:(函数声明提升)

function sum (sum1,sum2) {
  return sum1 + sum2;
}
console.log(sum instanceof Function); // true
复制代码

还有一种方式,使用函数表达式定义函数:

let sum = function(sum1,sum2) {
  return sum1 +sum2 ;
};
console.log(sum instanceof Function); // true
复制代码

3.4、其他

其他的引用类型具体细节可以看看这篇文章,内容较多就不一一列举了:

juejin.cn/post/691441…[1]

2、数据类型的4种判断方法

2.1、typeof

先看一下用法:

// 基本类型
console.log(typeof ""); 
console.log(typeof 1); 
console.log(typeof true); 
console.log(typeof undefined); 
console.log(typeof null); // object---有点儿特殊,见下
console.log(typeof Symbol('id'))
console.log(typeof 9007199254740999n) 
console.log(typeof BigInt(9007199254740999)) 

// 引用类型
console.log(typeof []); 
console.log(typeof function(){});
console.log(typeof {}); 
复制代码

结果:

图片.png

关于基础类型null,使用typeof返回的是object的说明,来个官方文档贴图:

图片.png

小结

通过上面的无脑console.log,可以看出typeof可以用于检测基本类型(除了null);

不同的数据类型在底层都是通过二进制表示的,二进制前三位为000则会被判断为object类型,而null底层的二进制全都是0,那前三位肯定也是000,所以被判断为object;

但碰到引用类型均返回为object,无法精准判定。

2.2、instanceof

用法:

// 基本类型
console.log('1' instanceof String) 
console.log(1 instanceof Number) 
console.log(true instanceof Boolean) 

// console.log(undefined instanceof undefined)
      // Uncaught TypeError: Right-hand side of 'instanceof' is not an object
// console.log(null instanceof null)
      // Uncaught TypeError: Right-hand side of 'instanceof' is not an object

console.log(typeof Symbol('id') instanceof Symbol) 
console.log(typeof 9007199254740999n instanceof BigInt) 
console.log(typeof BigInt(9007199254740999) instanceof BigInt) 

// 引用类型
console.log([] instanceof Array) 
console.log(function () {} instanceof Function) 
console.log({} instanceof Object) 
复制代码

结果:

图片.png

小结

不难看出,instanceof可以用于引用类型的检测,但对于基本类型是不生效的;

另外,不能用于检测nullundefined, 会抛错。

2.3、constructor

先看一下用法:

// 基本类型
console.log('1'.constructor === String)
console.log((1).constructor === Number)
console.log(true.constructor === Boolean)
// console.log(undefined.constructor === Boolean)
      // Uncaught TypeError: Cannot read properties of undefined (reading                 'constructor')
// console.log(null.constructor === Boolean)
      // Uncaught TypeError: Cannot read properties of undefined (reading                 'constructor')
console.log(Symbol('id').constructor === Boolean)
console.log(9007199254740999n.constructor === Boolean)
console.log(BigInt(9007199254740999).constructor === Boolean)

// 引用类型
console.log([].constructor === Array)
console.log(function () {}.constructor === Function)
console.log({}.constructor === Object)
复制代码

结果:

图片.png

撇去null、undefined、Symbol、BigInt,似乎说constructor能用于检测js的基本类型和引用类型

但当涉及到原型和继承的时候,便出现了问题,如下:

function fun() {};

fun.prototype = new Array();

let f = new fun();

console.log(f.constructor===fun); // false
console.log(f.constructor===Array); // true
复制代码

在这里,我们先是定义了一个函数fun,并将该函数的原型指向了数组,同时,声明了一个f为fun的类型,然后利用constructor进行检测时,会发现并不符合预期

小结

撇去null、undefined、Symbol、BigIntconstructor能用于检测js的基本类型和引用类型,但当对象的原型更改之后,constructor便失效了。

2.4、Object.prototype.toString.call()

用法:

let test = Object.prototype.toString

// 基本类型
console.log(test.call('str'))
console.log(test.call(1))
console.log(test.call(true))
console.log(test.call(null))
console.log(test.call(undefined))

console.log(test.call(Symbol('id')))
console.log(test.call(9007199254740999n))
console.log(test.call(BigInt(9007199254740999)))

// 引用类型
console.log(test.call([]))
console.log(test.call(function () {}))
console.log(test.call({}))
复制代码

结果:

图片.png

得到结果之后, 你可以通过:xxx.slice(8, \-1).toLowerCase(),就可拿来正常使用咯;

这样一看,似乎能满足js的所有数据类型,那我们看下继承之后是否能检测出来:

function fun() {};

fun.prototype = new Array();

let f = new fun();

console.log(Object.prototype.toString.call(fun))
console.log(Object.prototype.toString.call(f))
复制代码

结果:

图片.png

小结

可以看出,Object.prototype.toString.call()可用于检测js所有的数据类型,完美~

2.5、总结:

文字看的真是难受?来个表吧:

图片.png

图片.png

参考文档链接:

developer.mozilla.org/zh-CN/docs/…[2]

es6.ruanyifeng.com/#docs/symbo…[3]

juejin.cn/post/684490…[4]

juejin.cn/post/691441…[5]

blog.csdn.net/yiyueqinghu…[6]

blog.csdn.net/m0\_50914413…[7]

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8