基本数据类型
基本数据类型(有时候又称为简单数据类型和值类型)是程序设计中最基础的、不可再分的数据类型,也称为原始数据类型。在大多数编程语言中,基本数据类型包括6种
-
"undefined":未定义类型,声明后没有赋值 -
"boolean":布尔类型,返回值是true或false -
"number":数值类型,包括整数和浮点数,(也包括NAN) -
"string":字符串类型 -
"bigint":大整数类型, -
"symbol":Symbol 类型(ECMAScript 6新增)
补充:---Symbol表示独一无二的标识符,用于对象属性的键名。与字符串作为键名相比,使用 Symbol 作为键名具有以下优点: 不会出现键名冲突的问题,因为每个 Symbol 的值都是唯一的。 -
- 避免了对象属性被不小心覆盖的风险。由于
Symbol值是唯一的,所以使用 Symbol 作为键名的属性不会被其它代码意外修改。
- 避免了对象属性被不小心覆盖的风险。由于
-
- Symbol 类型的值不能与其它值进行计算或转换,也不能直接访问。可以使用
Object.getOwnPropertySymbols()方法获取对象中所有的 Symbol 属性。
BigInt是 ECMAScript 2020(ES10)新增的一种基本数据类型,用于表示任意精度的整数。与 JavaScript 中的number类型不同,BigInt 可以安全地表示超出Number.MAX_SAFE_INTEGER(2^53-1)范围内的整数,BigInt 类型的值可以使用后缀n表示,例如10n。
- Symbol 类型的值不能与其它值进行计算或转换,也不能直接访问。可以使用
使用 BigInt 时需要注意以下几点:
BigInt类型只能用于整数运算,不能进行浮点数运算。BigInt类型和number类型不能混合运算,必须进行显式转换。BigInt类型不能与其它数据类型直接比较(除了==和!=),必须进行显式转换。
由于 BigInt 是 ES10 才新增的数据类型,因此在某些旧版本的浏览器(如 IE11)中可能不被支持。
引用数据类型
引用数据类型是指在程序中表示对象的数据类型。与基本数据类型不同,引用数据类型的值是一个对象。在大多数编程语言中,引用数据类型包括:
"Object":对象"Array":数组"Function":函数"Date":日期对象"RegExp":正则表达式(regular expression)"Error":错误信息
在 JavaScript 中,除了基本数据类型以外的所有数据类型都属于引用数据类型。
存储方式
栈存储和堆存储
看他们的存储方式之前,我们先来看一看栈存储和堆存储的优缺点:
栈(stack)和堆(heap)是两种不同的内存分配方式,它们各有优缺点。
栈存储和堆存储各有优缺点,适用于不同的场景。
栈存储的优点:
- 存取速度快:由于栈内存是连续分配的,因此访问速度非常快,可以直接通过指针进行访问。
- 管理方便:由于栈内存的分配和回收是自动完成的,不需要手动管理内存,减少了内存泄漏和空间碎片等问题。
- 数据安全性高:栈内存具有先进后出(Last-In-First-Out)的特性,保证了数据的安全性,不容易被其它程序误操作或破坏。
栈存储的缺点:
- 大小受限:由于栈内存是固定大小的,一旦超过了栈的最大容量,就会引起栈溢出的问题。
- 生命周期短:栈变量的生命周期是在函数执行结束后就会被销毁,无法保留其状态,不适合存储长期存在的数据。
堆存储的优点:
- 大小灵活:由于堆内存可以动态分配,大小没有限制,可以存储任意大小和形状的数据。
- 生命周期长:堆内存中的数据可以被多个程序或线程共享,可以长期保留其状态。
堆存储的缺点:
- 存取速度慢:由于堆内存是随机分配的,访问速度相对栈内存较慢,需要通过指针进行间接访问。
- 管理复杂:由于堆内存的分配和回收需要手动控制,容易出现内存泄漏、空间碎片等问题。
- 数据安全性低:堆内存中的数据可以被其它程序误操作或破坏,需要进行额外的保护措施。
基本数据类型和引用数据类型的存储方式
基本数据类型的值通常存储在栈空间中;
引用数据类型的值存储在堆(heap)内存中,地址存储在栈内存中,通过栈内存中的引用地址(reference)来访问堆内存中的值;
存储数据时,栈内存是向下增长;堆内存向上增长,在达到到某一个临界点后,他们会发生重叠;
实际上,栈和堆内存的存储方式只是一种抽象概念,在具体实现中可能会有所不同;也有可能出现栈和堆内存的存储方式都是向下增长。栈内存中的数据是按照函数调用的顺序依次压入栈中,在内存地址较低的位置开始存储,而栈指针向地址较高的方向移动;当函数执行结束时,栈指针会向地址较低的方向回移,以弹出栈中的数据(先进先出);
堆内存的分配和释放是动态进行的,并没有固定的分配顺序。堆内存中的数据通常是从相对较高的地址开始分配,随着内存的使用逐渐向地址较低的方向移动。由于堆内存的大小是可变的,因此在堆内存中可能会有多个数据区域重叠的情况发生。
基本数据类型和引用数据类型存储的方式有所不同,主要表现在以下几个方面:
- 存储位置:基本数据类型的值直接存储在栈(stack)内存中,而引用数据类型的引用地址存储在堆内存中,实际的对象或数组等数据存储在堆(heap)内存中。
- 复制方式:基本数据类型的值在进行赋值操作时会直接复制一份新的值,互不影响。而引用数据类型的变量在进行赋值操作时,只是复制了一个指向堆内存中实际数据的引用地址,并没有复制数据本身,因此多个变量可能会引用同一个对象或数组等数据。
- 访问方式:基本数据类型的值可以直接访问,在进行运算等操作时也可以直接使用。而引用数据类型的数据必须通过其变量对应的引用进行访问,需要先获取引用指向的数据,然后才能进行操作。
由于基本数据类型的值较小且固定,存储和复制比引用数据类型更高效,但无法表示复杂的数据结构。而引用数据类型可以表示任意大小和形状的数据,但由于涉及到堆内存的分配和回收等复杂操作,因此在处理大量数据时可能会导致性能问题。
检测方式
为了能更好的看到检测的结果,利用console.log()后台打印,仅是辅助代码,在此处检测数据类型中没有实质性意义,
typeof
// // --->基本数据类型
let a //声明不赋值为undefined,需要let声明
const u=null
const s='abc'
const n=100
const b=true
const sy=Symbol('s')
// --->引用类型
const obj={
a:"lll",
b:88
}
function fn1(params) {
return 111
}
console.log(typeof a )//undefined
console.log(typeof u) //object 不能检测null
console.log(Object.prototype.toString.call(u))//[object Null]
console.log(typeof n) //number
console.log(typeof b)//boolean
console.log(typeof s)//string
console.log(typeof sy) //symbol
console.log(typeof obj)//object
console.log(typeof fn1)//function
console.log(typeof [1,2,3])//object 只能是被为object
</script>
typeof 运算符是用来检测一个值的数据类型的,它返回一个表示值类型的字符串, 适用类型
- 除
null以外的基本数据类型 - object,function
缺点区分不了除object和其他引用数据类型(除function外), 会把null认为是一个对象
instanceof
console.log([1, 2, 3] instanceof Array); //true
console.log(function () {} instanceof Function); //true
console.log({} instanceof Object);//true
console.log(/re/ instanceof RegExp)//true
console.log(new Date() instanceof Date)//true
instanceof 运算符是 JavaScript 中用于检测实例对象的运算符。一个对象是否是另一个对象的实例,并返回一个布尔值。instanceof 本质上是检测构造函数.prototype(原型对象) 是否存在于实例对象的原型链上-->通过实列对象的__proto__原型链向上查找原型对象,再通过原型对象的属性constructor找到Promise(构造函数),确定实列对象是否是构造函数的通过new创建的,
适用类型: instanceof 只适用于引用数据类型,而不适用于简单类型。 缺点:
- 在复杂类型的情况下,对象必须是直接或间接继承自构造函数的实例,才能被判断为该类型的实例对象。也就是说,如果一个对象是从其它构造函数原型上继承来的,那么
instanceof会返回false。 - 在多个框架或窗口(cross-frame)模型的环境中,相同类型的对象经过不同的构造函数创建,
instanceof的检测结果可能会产生意想不到的结果。
通常情况下,当你需要检查对象是否是特定类型的实例时,推荐用instanceof
constructor
// 引用数据型
console.log([1, 2, 3].constructor===Array); //true
console.log(function () {}.constructor=== Function); //true
console.log({}.constructor=== Object);//true
console.log(/re/.constructor === RegExp)//true
console.log(new Date().constructor === Date)//true
//简单数据类型
const a='1'
console.log(a.constructor=== Sring);//Sring is not defined
利用了原型链的原理,一个对象是否是另一个对象的实例对象,每一个实例对象都可以通过constructor找到他的构造函数,返回一个布尔值
值得注意的是:这里的constructor其实是原型对象上的.需要通过__proto__原型链向上先找到他的原型对象,再由原型对象上的constructor找到构造函数; 适用类型:引用数据类型 缺点:这种方法可能会出现问题,因为对象的 constructor 属性是可以被修改的。会造成检测的结果不准确
===>instanceof和isisPrototypeOf以及constructor都有异曲同工之妙,都是利用原型链,向上查找其实例对象的构造函数.
Object.prototype.toString.call()
检测数据类型最完美的方式
console.log(Object.prototype.toString.call(123));//[object Number]
console.log(Object.prototype.toString.call("123"));//[object String]
console.log(Object.prototype.toString.call(null));//[object Null]
console.log(Object.prototype.toString.call([12, 3]));//[object Array]
console.log(Object.prototype.toString.call(undefined))//[object Undefined]
console.log(Object.prototype.toString.call(function(){}));//[object Function]
console.log(Object.prototype.toString.call({ age: 123 }));//[object Object]
console.log(Object.prototype.toString.call(new Date())); //[object Date]
console.log(Object.prototype.toString.call(true));//[object Boolean]
console.log(Object.prototype.toString.call(/RegExp/))//[object RegExp]
Object.prototype.toString.call() 是一种通用的方法,可以用来获取一个值的类型字符串。它利用了 JavaScript 中内置的 Object.prototype.toString() 将任何对象转换为字符串形式的方法。再利用.call方法 适用类型:所有的数据类型 缺点:由于它的实现利用了 JavaScript 函数上下文和内置函数 toString(),因此它会带来一些额外的开销和不必要的复杂性。此外,由于该方法在检测对象类型时可能会被覆盖或修改,因此不能保证其始终有效。