前言

在JavaScript中,可以分成两种类型:

  • 基本类型
  • 引用类型

两种类型的区别: 存储位置不同

基本类型

  • String
  • Number
  • Boolean
  • Undefined
  • null
  • symbol

String

字符串可以使用双引号(”)、单引号(’)或反引号(`)标示

1
2
3
let firstName = "John";
let lastName = 'Jacob';
let lastName = `Jingleheimerschmidt`

字符串是不可变的,意思是一旦创建,它们的值就不能变了

1
2
let lang = "Java";
lang = lang + "Script"; // 先销毁再创建

Number

数值最常见的整数类型格式则为十进制,还可以设置八进制(零开头)、十六进制(0x开头)

1
2
3
let intNum = 55 // 10进制的55
let num1 = 070 // 8进制的56
let hexNum1 = 0xA //16进制的10

浮点类型则在数值汇总必须包含小数点,还可通过科学计数法表示

1
2
3
4
let floatNum1 = 1.1;
let floatNum2 = 0.1;
let floatNum3 = .1; // 有效,但不推荐
let floatNum = 3.125e7; // 等于 31250000

在数值类型中,存在一个特殊数值NaN,意为“不是数值”,用于表示本来要返回数值的操作失败了(而不是抛出错误)

1
2
console.log(0/0); // NaN
console.log(-0/+0); // NaN

Boolean

Boolean(布尔值)类型有两个字面值: true 和false
通过Boolean可以将其他类型的数据转化成布尔值

规则如下:

1
2
3
4
5
数据类型      				转换为 true 的值      				转换为 false 的值
String 非空字符串 ""
Number 非零数值(包括无穷值) 0 、 NaN
Object 任意对象 null
Undefined N/A (不存在) undefined

Undefined

Undefined 类型只有一个值,就是特殊值 undefined。当使用 var或 let声明了变量但没有初始化时,就相当于给变量赋予了 undefined值

1
2
let message;
console.log(message == undefined); // true

包含undefined 值的变量跟未定义变量是有区别的

1
2
3
4
let message; // 这个变量被声明了,只是值为 undefined

console.log(message); // "undefined"
console.log(age); // 没有声明过这个变量,报错

Null

Null类型同样只有一个值,即特殊值 null

逻辑上讲, null 值表示一个空对象指针,这也是给typeof传一个 null 会返回 “object” 的原因

1
2
let car = null;
console.log(typeof car); // "object"

undefined 值是由 null值派生而来

1
console.log(null == undefined); // true

只要变量要保存对象,而当时又没有那个对象可保存,就可用 null来填充该变量

Symbol

Symbol (符号)是原始值,且符号实例是唯一、不可变的。符号的用途是确保对象属性使用唯一标识符,不会发生属性冲突的危险

1
2
3
4
5
6
7
let genericSymbol = Symbol();
let otherGenericSymbol = Symbol();
console.log(genericSymbol == otherGenericSymbol); // false

let fooSymbol = Symbol('foo');
let otherFooSymbol = Symbol('foo');
console.log(fooSymbol == otherFooSymbol); // false

引用类型

  • Object
  • Array
  • Function

Object

创建object常用方式为对象字面量表示法,属性名可以是字符串或数值

1
2
3
4
5
let person = {
name: "Nicholas",
"age": 29,
5: true
};

Array

JavaScript数组是一组有序的数据,但跟其他语言不同的是,数组中每个槽位可以存储任意类型的数据。并且,数组也是动态大小的,会随着数据添加而自动增长

1
2
let colors = ["red", 2, {age: 20 }]
colors.push(2)

Function

函数实际上是对象,每个函数都是 Function类型的实例,而 Function也有属性和方法,跟其他引用类型一样

函数存在三种常见的表达方式:

  • 函数声明
1
2
3
4
// 函数声明
function sum (num1, num2) {
return num1 + num2;
}
  • 函数表达式

    1
    2
    3
    let sum = function(num1, num2) {
    return num1 + num2;
    };
  • 箭头函数
    函数声明和函数表达式两种方式

    1
    2
    3
    let sum = (num1, num2) => {
    return num1 + num2;
    };

其他引用类型

  • Date
  • RegExp
  • Map
  • Set

    Date

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
6
let 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
2
3
4
5
6
7
8
9
10
// 起始时间
let start = Date.now();

// 调用函数
doSomething();

// 结束时间
let stop = Date.now();

result = stop - start;

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
2
3
let loveDate = new Date(Date.parse("May 20, 2020"));
// or
let loveDate = new Date("May 20, 2020");

Date.UTC()

Date.UTC()接受的参数格式是:年、零起点月数(1月是0,2月是1,以此类推)、日(1-31)、时(0-23)、分、秒和毫秒。这些参数中,只有前两个(年和月)是必需的。示例:

1
2
3
4
5
// GMT时间2000年1月1日零点
let y2k = new Date(Date.UTC(2000, 0));

// GMT时间2005年5月5日下午5点55分55秒
let allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55));

与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
2
3
let arr = [1,2,3];
let myMap = new Map(arr.entries());
console.log(myMap.get(0)); // 1

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
2
3
4
5
6
7
8
9
let myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");

let mapIter = myMap.entries();
console.log(mapIter.next().value); // ["0", "foo"]
console.log(mapIter.next().value); // [1, "bar"]
console.log(mapIter.next().value); // [Object, "baz"]
  • .keys():myMap.keys()

    1
    2
    3
    4
    5
    6
    7
    8
    9
    let 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
    9
    let 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
2
3
4
5
6
 // callback 必要,每个元素所要执行的函数
// thisArg 可选,callback 执行时其 this 的值
let myMap = new Map([["foo", 3], ["bar", {}], ["baz", undefined]]);
myMap.forEach((value,key,map) => {
console.log("key =",key,",value =",value); //key = foo ,value = 3
});

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
    9
    const 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
    8
    const set = new Set(undefined);

    set.add("string").add("string");

    set.forEach(function(value) {
    console.log(value);
    });
    // string

Set使用场景

  • Set和数组相互转化

    1
    2
    3
    const array = [1,2];
    const set = new Set(array); // 数组转化为set
    const newArray = [...set]; // set转化为数组
  • 去除字符串重复字符

    1
    2
    3
    4
    5
    const s = 'aabbcc';

    const set = new Set(s);
    const newString = [...set].join('');
    console.log(newString); // abc
  • 数组去重

1
2
3
const list = [1,2,3,1,2,3];
const set = new Set(list);
console.log([...set]); // [1,2,3]
  • 并集

    1
    2
    3
    const 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
    3
    const 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
    3
    const 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
2
3
4
let a = 10;
let b = a; // 赋值操作
b = 20;
console.log(a); // 10值

a的值为一个基本类型,是存储在栈中,将a的值赋给b,虽然两个变量的值相等,但是两个变量保存了两个不同的内存地址

截屏2023-05-12 上午10.42.57.png

引用类型

1
2
3
4
var obj1 = {}
var obj2 = obj1;
obj2.name = "Xxx";
console.log(obj1.name); // xxx

引用类型数据存放在堆中,每个堆内存对象都有对应的引用地址指向它,引用地址存放在栈中。

obj1是一个引用类型,在赋值操作过程汇总,实际是将堆内存对象在栈内存的引用地址复制了一份给了obj2,实际上他们共同指向了同一个堆内存对象,所以更改obj2会对obj1产生影响

截屏2023-05-12 上午10.42.57.png

总结

  • 声明变量时不同的内存地址分配:
    简单类型的值存放在栈中,在栈中存放的是对应的值
    引用类型对应的值存储在堆中,在栈中存放的是指向堆内存的地址
  • 不同的类型数据导致赋值变量时的不同:
    简单类型赋值,是生成相同的值,两个对象对应不同的地址
    复杂类型赋值,是将保存对象的内存地址赋值给另一个变量。也就是两个变量指向堆内存中同一个对象

总结:大功告成✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️✌️

参考链接: