前言
在JavaScript中,可以分成两种类型:
- 基本类型
- 引用类型
两种类型的区别: 存储位置不同
基本类型
- String
- Number
- Boolean
- Undefined
- null
- symbol
String
字符串可以使用双引号(”)、单引号(’)或反引号(`)标示1
2
3let firstName = "John";
let lastName = 'Jacob';
let lastName = `Jingleheimerschmidt`
字符串是不可变的,意思是一旦创建,它们的值就不能变了1
2let lang = "Java";
lang = lang + "Script"; // 先销毁再创建
Number
数值最常见的整数类型
格式则为十进制,还可以设置八进制(零开头)、十六进制(0x开头)
1 | let intNum = 55 // 10进制的55 |
浮点类型
则在数值汇总必须包含小数点,还可通过科学计数法表示1
2
3
4let floatNum1 = 1.1;
let floatNum2 = 0.1;
let floatNum3 = .1; // 有效,但不推荐
let floatNum = 3.125e7; // 等于 31250000
在数值类型中,存在一个特殊数值NaN
,意为“不是数值”,用于表示本来要返回数值的操作失败了(而不是抛出错误)1
2console.log(0/0); // NaN
console.log(-0/+0); // NaN
Boolean
Boolean(布尔值)类型有两个字面值: true 和false
通过Boolean可以将其他类型的数据转化成布尔值
规则如下:
1 | 数据类型 转换为 true 的值 转换为 false 的值 |
Undefined
Undefined 类型只有一个值,就是特殊值 undefined。当使用 var或 let声明了变量但没有初始化时,就相当于给变量赋予了 undefined值1
2let message;
console.log(message == undefined); // true
包含undefined 值的变量跟未定义变量是有区别的1
2
3
4let message; // 这个变量被声明了,只是值为 undefined
console.log(message); // "undefined"
console.log(age); // 没有声明过这个变量,报错
Null
Null类型同样只有一个值,即特殊值 null
逻辑上讲, null 值表示一个空对象指针,这也是给typeof传一个 null 会返回 “object” 的原因1
2let car = null;
console.log(typeof car); // "object"
undefined 值是由 null值派生而来1
console.log(null == undefined); // true
只要变量要保存对象,而当时又没有那个对象可保存,就可用 null来填充该变量
Symbol
Symbol (符号)是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险
1 | let genericSymbol = Symbol(); |
引用类型
- Object
- Array
- Function
Object
创建object常用方式为对象字面量表示法,属性名可以是字符串或数值1
2
3
4
5let person = {
name: "Nicholas",
"age": 29,
5: true
};
Array
JavaScript数组是一组有序的数据,但跟其他语言不同的是,数组中每个槽位可以存储任意类型的数据。并且,数组也是动态大小的,会随着数据添加而自动增长1
2let colors = ["red", 2, {age: 20 }]
colors.push(2)
Function
函数实际上是对象,每个函数都是 Function类型的实例,而 Function也有属性和方法,跟其他引用类型一样
函数存在三种常见的表达方式:
- 函数声明
1 | // 函数声明 |
函数表达式
1
2
3let sum = function(num1, num2) {
return num1 + num2;
};箭头函数
函数声明和函数表达式两种方式1
2
3let sum = (num1, num2) => {
return num1 + num2;
};
其他引用类型
Date 类型保存的是自 1970 年1 月 1 日午夜至今所经过的毫秒数,可以精确表示 1970 年 1 月1 日之后 285616 年的日期
1 | let now = new Date(); |
格式化日期
toDateString()
显示日期中的周几、月、日、年(格式特定于实现)toTimeString()
显示日期中的时、分、秒和时区(格式特定于实现)toLocaleDateString()
显示日期中的周几、月、日、年(格式特定于实现和地区)toLocaleTimeString()
显示日期中的时、分、秒(格式特定于实现和地区)toUTCString()
显示完整的UTC日期(格式特定于实现)
这些方法的输出与toLocaleString()
和toString()
一样,会因浏览器而异。因此不能用于在用户界面上一致地显示日期。1
2
3
4
5
6let date = new Date() // Wed May 17 2023 14:10:19 GMT+0800 (中国标准时间)
console.log(date.toDateString()) //'Wed May 17 2023'
console.log(date.toTimeString())// '14:10:19 GMT+0800 (中国标准时间)'
console.log(date.toLocaleDateString()) //'2023/5/17'
console.log(date.toLocaleTimeString()) //'14:10:19'
console.log(date.toUTCString()) //'Wed, 17 May 2023 06:10:19 GMT'
Date.now()
Date.now()方法,返回执行时的日期时间毫秒数
1 | // 起始时间 |
Date.parse()
Date.parse()
支持以下日期格式:
- 月/日/年,例如:5/20/2020
- 月名 日,年,例如:May 20 2020
- 周几 月名 日 年 时:分:秒 时区,如Tue May 20 202000:00:00 GMT-0700
- ISO 8601扩展格式 YYYY-MM-DDTHH:mm:ss.sssZ,如2020-05-20T00:00:00(只适用于兼容ES5的实现)
如果把以上标识日期的字符串直接传给 Date 函数,省略了Date.parse(),那么 Date 会在后台默认调用,也就是说,可以省略Date.parse()方法。
1 | let loveDate = new Date(Date.parse("May 20, 2020")); |
Date.UTC()
Date.UTC()接受的参数格式是:年、零起点月数(1月是0,2月是1,以此类推)、日(1-31)、时(0-23)、分、秒和毫秒。这些参数中,只有前两个(年和月)是必需的。示例:
1 | // GMT时间2000年1月1日零点 |
与Date.parse()一样,Date.UTC()也会被 Date 构造函数隐式调用,但有一个区别:这种情况下创建的是本地日期,不是 GMT 日期。
RegExp
可参考:https://youle.zhipin.com/articles/1f2eb19c13828defqxBy2d-5.html
Map
Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值
构造函数
语法:new Map([iterable])
参数: iterable 可以是一个数组或者其他 iterable 对象,其元素或为键值对,或为两个元素的数组;每个键值对都会添加到新的 Map,null 会被当做 undefined
1 | let arr = [1,2,3]; |
Map实例方法
.set()
:myMap.set(key, value).get()
:myMap.get(key).has()
:myMap.has(key).delete()
:myMap.delete(key).clear()
:myMap.clear()方法会移除Map对象中的所有元素.entries()
:myMap.entries()返回一个新的包含[key, value]对的Iterator对象,返回的迭代器的迭代顺序与Map对象的插入顺序相同
1 | let myMap = new Map(); |
.keys()
:myMap.keys()1
2
3
4
5
6
7
8
9let myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");
let mapIter = myMap.keys();
console.log(mapIter.next().value); // "0"
console.log(mapIter.next().value); // 1
console.log(mapIter.next().value); // Object.values()
:myMap.values()1
2
3
4
5
6
7
8
9let myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");
let mapIter = myMap.values();
console.log(mapIter.next().value); // "foo"
console.log(mapIter.next().value); // "bar"
console.log(mapIter.next().value); // "baz".forEach()
:myMap.forEach(callback[, thisArg])
1 | // callback 必要,每个元素所要执行的函数 |
Set
Set 对象允许你存储任何类型的唯一值
,无论是原始值或者是对象引用。
构造函数
语法:new Set([iterable])
参数: iterable Set接收一个可选的Iterator对象,所有元素将按照顺序不重复地添加到Set中,传递null或者undefined将返回一个空Set
1 | const set = new Set(); |
Set实例方法
.add()
:myMap.add( value).has()
:myMap.has(1).delete()
:myMap.delete(1).clear()
:myMap.clear()方法会移除Map对for...of
1
2
3
4
5
6
7
8
9const set = new Set(undefined);
set.add("string").add("string");
for (const v of set.entries()) {
console.log(v);
}
// ['string', 'string']forEach
1
2
3
4
5
6
7
8const set = new Set(undefined);
set.add("string").add("string");
set.forEach(function(value) {
console.log(value);
});
// string
Set使用场景
Set和数组相互转化
1
2
3const array = [1,2];
const set = new Set(array); // 数组转化为set
const newArray = [...set]; // set转化为数组去除字符串重复字符
1
2
3
4
5const s = 'aabbcc';
const set = new Set(s);
const newString = [...set].join('');
console.log(newString); // abc数组去重
1 | const list = [1,2,3,1,2,3]; |
并集
1
2
3const set = new Set([1,2,3]);
const set2 = new Set([1,2,3,4]);
const set3 = new Set([...set], [...set2]); // [1, 2, 3]交集
1
2
3const set = new Set([1,2,3]);
const set2 = new Set([1,2,3,4]);
const set3 = new Set([...set].filter(item => set2.has(item))); // [1, 2, 3]差集
1
2
3const set = new Set([1,2,3]);
const set2 = new Set([1,2,3,4]);
const set3 = new Set([...set2].filter(item => !set.has(item))); // [4], 注意set2和set的顺序
数据类型的唯一性判定使用示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const set = new Set(undefined);
set.
add("string").add("string"). //srting
add(1).add(1). // 1
add(true).add(true). // true
add(null).add(null). // null
add(undefined).add(undefined) // undefined
.add(NaN).add(NaN) // NaN
.add({}).add({}) // {}, {}
.add([]).add([]) // [], []
.add(function () { }).add(function () { }); // [Function], [Function]
// 可以看到基本类型只会存储一个值,引用类型会存入多个
存储区别
基本数据类型和引用数据类型存储在内存中的位置不同:
- 基本数据类型存储在
栈
中 - 引用类型的对象存储于
堆
中
当我们把变量赋值给一个变量时,解析器首先要确认的就是这个值是基本类型值还是引用类型值
举个🌰:
基本类型
1 | let a = 10; |
a的值为一个基本类型,是存储在栈中,将a的值赋给b,虽然两个变量的值相等,但是两个变量保存了两个不同的内存地址
引用类型
1 | var obj1 = {} |
引用类型数据存放在堆中,每个堆内存对象都有对应的引用地址指向它,引用地址存放在栈中。
obj1是一个引用类型,在赋值操作过程汇总,实际是将堆内存对象在栈内存的引用地址复制了一份给了obj2,实际上他们共同指向了同一个堆内存对象,所以更改obj2会对obj1产生影响
总结
- 声明变量时不同的内存地址分配:
简单类型的值存放在栈中,在栈中存放的是对应的值
引用类型对应的值存储在堆中,在栈中存放的是指向堆内存的地址 - 不同的类型数据导致赋值变量时的不同:
简单类型赋值,是生成相同的值,两个对象对应不同的地址
复杂类型赋值,是将保存对象的内存地址赋值给另一个变量。也就是两个变量指向堆内存中同一个对象
总结:大功告成✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️
参考链接: