JavaScript 的数据类型
JavaScript 是一种动态、弱类型的语言。虽然 JavaScript 的类型系统不像静态类型语言那样严格,但它仍然有自己的类型系统,并通过不同的方式管理和操作这些类型。
JavaScript 的数据类型可以分为原始类型(Primitive Types) 和对象类型(Object Types)。
原始类型(基础类型)
- String :字符串,例如
"hello"
- Number :数字,包括整数和浮点数,例如
42
和3.14
- Boolean :布尔值,只有两个值:
true
和false
- BigInt :用于表示大整数,例如
123n
- Symbol :一种唯一且不可变的值,用作对象属性的标识符,例如
Symbol("description")
- Undefined:表示变量未定义或未赋值
- Null:表示空或不存在的值
对象类型
- Object:对象是引用类型,用于存储键值对和复杂数据结构。
- Function:JavaScript 中的函数也是对象,是一种特殊的对象类型。
- Array:数组是对象的一种,用于存储有序的值列表。
- 其他对象类型 :如
Date
、RegExp
等。
原始类型与对象类型的主要区别
-
存储方式:
- 原始类型直接存储在栈内存中,访问速度快。
- 对象类型存储在堆内存中,引用存储在栈内存中。
-
可变性:
- 原始类型是不可变的,一旦创建,值就不能改变。
- 对象类型是可变的,属性和内容可以动态改变。
-
传递方式:
- 原始类型按值传递,变量赋值时会创建副本。
- 对象类型按引用传递,变量赋值时会复制引用,多个变量可以引用同一个对象。
万物皆对象?
大家都知道,JavaScript同时也是一门面向对象的语言,我们尊崇"万物皆对象",咦?那既然如此,基础类型和他们所对应的对象类型又如何区分呢?为了不打起来,JavaScript做出了妥协,为部分基础类型系统中的"值类型"设定对应的包装类 ;然后通过包装类,将"值类型数据"作为对象来处理。
什么鬼东西?
先别急着刀人 我们来看个栗子
js
let str ='I love you'
console.log(str.length);
let num = 23
console.log(num.length)
虽然我们经常用到str.length
,但有没有想过,为什么str就有,而num就没有呢? 其实呢 是这样的
对于str
-
原始类型 :
str
是一个原始类型的字符串。 -
临时包装对象 :当你访问
str.length
时,JavaScript 引擎创建一个临时的String
对象来包装这个原始字符串。 -
属性查找 :JavaScript 在这个临时
String
对象的原型链上查找length
属性。由于String.prototype
上定义了length
属性,查找成功。 -
返回值 :
length
属性的值是字符串的长度,因此返回 10。 -
丢弃临时对象 :访问完成后,临时的
String
对象被丢弃。
而对于num
-
原始类型 :
num
是一个原始类型的数字。 -
临时包装对象 :当你访问
num.length
时,JavaScript 引擎创建一个临时的Number
对象来包装这个原始数字。 -
属性查找 :JavaScript 在这个临时
Number
对象的原型链上查找length
属性。由于Number.prototype
上没有length
属性,查找失败。 -
返回值 :由于临时的
Number
对象没有length
属性,因此返回undefined
。 -
丢弃临时对象 :访问完成后,临时的
Number
对象被丢弃。
相信大家都理解了吧,那么如果你看了你知道new会发生什么吗这篇文章,相信你可以理解下面的例子
js
let str = new String('Hello');
console.log(typeof str); // 输出 "object"
console.log(str.valueOf()); // 输出 "Hello"
let num = new Number(123.456);
console.log(typeof num); // 输出 "object"
console.log(num.valueOf()); // 输出 123.456
那么 对于上面的new和我们这段代码又有什么区别呢?
js
let num =123
let str='Hello'
当然有
js
console.log(str1 == str2); // 输出: true (因为==比较值)
console.log(str1 === str2); // 输出: false (因为===比较类型和值)
我们可以看见 两者的值都是一样的,但是由于类型不同而第二条输出false,所以说,由new创建的num和str都是和我们直接赋值一样都有一个值的,但是,前者是一个包装类。但是呢,包装类创建的对象占用更多内存,并且性能上相对较慢,因为每次创建对象时都需要分配内存和垃圾回收。基本类型更轻量,性能更高,因为它们直接在栈上操作,而不是在堆上。
最后 我们以一道面试题来结束
js
var str = 'abc'
str += 1
console.log(str);
var test = typeof (str)
if (test.length === 6) {
test.sign = 'typeOf的返回结果可能是String'
}
console.log(test.aign)//undefined
- 加号在这里是链接字符串 所以str-->'abc+1'
- 所以test-->'String'
- 所以if判断为真执行test.sign='typeOf的返回结果可能是String'
- 这里引擎会干这么几件事
- 创建一个String对象并且增加sign属性,new String(test).sign='typeOf的返回结果可能是String'
- 访问完毕,然后发现test为原始类型,丢弃该String对象。
- 所以最后的输出引擎找不到该属性而输出undefined 了解包装类有助于我们理解代码并运用一些隐式地转换简化我们的代码,更为透彻理解引擎的执行。