JavaScript核心基础-对象

🥽 对象

🌊 对象简介

  • 为什么需要对象数据类型
    • 原始值(数值 Number、大整数 BigInt、字符串 String、布尔值 Boolean等)只能用来表示一些简单的数据,原始值只能表示独立的数据,不能表示复杂数据。比如:需要在程序中表示一个人的信息。
  • 对象:对象是JS中的一种复合数据类型,它相当于一个容器,在对象中可以存储各种不同类型数据
  • 对象中可以存储多个各种类型的数据,对象中存储的数据,我们称为属性。

🌊 对象的创建与对象属性的增删改查

💦 创建对象

1 // 创建一个空对象
2 let obj = new Object()
3 let obj = Object() // 在使用Object创建对象时new关键字可以省略
4 
5 console.log(obj)

💦 添加属性

语法:

1 对象.属性名 = 属性值
1 obj.name= "孙悟空"
2 obj.age = 18
3 obj.gender = "男"
4 
5 console.log(obj)

💦 读取属性

语法:

1 对象.属性名
  • 如果读取的是一个对象中没有的属性不会报错而是返回undefined
1 console.log(obj.name)
2 console.log(obj.hobby)

💦 修改属性

语法:

1 对象.属性名 = 新属性值
1 obj.name = "Tom"
2 console.log(obj.name)
3 console.log(obj)

💦 删除属性

语法:

1 delete 对象.属性名
1 deleteobj.name
2 console.log(obj.name)
3 console.log(obj)

🌊 对象的属性

  • 属性名
    • 通常属性名就是一个字符串,所以属性名可以是任何值,没有什么特殊要求。但是如果你的属性名太特殊了,不能直接使用,需要使用[]来设置,虽然如此,但是我们还是强烈建议属性名也按照标识符的规范命名
1 let obj = Object()
2 obj.name = "孙悟空"
3 obj.if = "哈哈" // 关键字为属性名不建议
4 obj.let = "嘻嘻"// 不建议
5 obj["1231312@#@!#!#!"] = "呵呵"// 不建议
6 console.log(obj)

    • 也可以使用符号(symbol)作为属性名,来添加属性。获取这种属性时,也必须使用symbol,且该symbol必须为之前添加属性时使用的symbol。使用symbol添加的属性,通常是那些不希望被外界访问的属性。
    • 使用[]去操作属性时,可以使用变量。使用.的形式添加属性时,不能使用变量。
1 let mySymbol= Symbol()
2 console.log(mySymbol)
3 // 使用symbol作为属性名
4 obj[mySymbol] = "通过symbol添加的属性"
5 console.log(obj)
6 console.log(obj[mySymbol])
7 let newSymbol = Symbol()
8 console.log(obj[newSymbol])

  • 属性值
    • 对象的属性值可以是任意的数据类型,也可以是一个对象
1 obj.a = 123
2 obj.b = 'hello'
3 obj.c = true
4 obj.d = 123n
5 obj.f = Object()
6 obj.f.name = "猪八戒"
7 obj.f.age = 28
8 console.log(obj)

  • 查看对象中是否具有某个属性
    • in 运算符:用来检查对象中是否含有某个属性,语法:属性名 in obj,如果有返回true,没有返回false// obj对象中是否具有name属性
1 // obj对象中是否具有name属性
2 console.log("name" in obj)
3 // obj对象中是否具有hobby属性
4 console.log("hobby" in obj)

🌊 对象字面量

  • 对象字面量其实就是创建对象的另一种方式
  • 可以直接使用{}来创建对象
  • 使用{}所创建的对象,可以直接向对象中添加属性
  • 语法:
1 {
2     属性名:属性值,
3     [属性名]:属性值, // 用于添加属性名特殊的属性
4     ......
5 }
 1 let mySymbol = Symbol()
 2 let obj2 = {
 3     name:"孙悟空", 
 4     age:18,
 5     "hobby": 123, // 属性名其实是一个字符串
 6     ["gender"]:"男", // 属性名特殊的属性
 7     [mySymbol]:"特殊的属性", // 属性名特殊的属性
 8     hello:{ // 对象的属性值可以是一个对象
 9         a:1,
10         b:true
11     }
12 }
13 console.log(obj2)

🌊 枚举对象中的属性(for-in语句)

枚举属性,指将对象中的所有的属性全部获取
枚举属性使用 for-in 语句
语法:
for(let propName in 对象){
语句...
}
1
2
3
for-in 的循环体会执行多次,有几个属性就会执行几次,每次执行时,都会将一个属性名赋值给我们所定义的变量
注意:并不是所有的属性都可以枚举,比如:使用symbol(符号)添加的属性,符号添加的属性一般不希望被外界所访问,符号添加的属性不可枚举。
let obj = {
name:'孙悟空',
age:18,
gender:"男",
address:"花果山",
[Symbol()]:"测试的属性" // 符号添加的属性是不能枚举
}

for(let propName in obj){
console.log(propName, obj[propName])
}
1
2
3
4
5
6
7
8
9
10
11


🌊 可变类型
原始值都属于不可变类型,一旦创建就无法修改,在内存中不会创建重复的原始值


对象属于可变类型,对象创建完成后,可以任意的添加删除修改对象中的属性。

注意:
当对两个对象进行相等或全等比较时,比较的是对象的内存地址
如果有两个变量同时指向一个对象,通过一个变量修改对象时,对另外一个变量也会产生影响。当修改一个对象时,所有指向该对象的变量都会受到影响
🌊 对象的修改与变量的修改
修改对象:
修改对象时,如果有其他变量指向该对象, 则所有指向该对象的变量都会受到影响。如果我们修改存储对象的变量的指向(修改变量),即变量指向为不同的对象,此时通过变量修改对象不会影响其他变量所指向的对象。
修改变量:
修改变量时,只会影响当前的变量,不会影响其他变量。
在使用变量存储对象时,很容易因为改变变量指向的对象,提高代码的复杂度,所以通常情况下,声明存储对象的变量时会使用const。
注意:const只是禁止变量被重新赋值,对对象的修改没有任何影响。对于指向对象的变量来说,存储的为对象的地址,只要其指向没变(存储的地址没变)相当于该变量没有修改,修改对象的属性不会影响变量的指向。
🌊 方法(method)
当一个对象的属性指向一个函数,那么我们就称这个函数是该对象的方法,调用函数就称为调用对象的方法

let obj = {}
obj.name = "孙悟空"
obj.age = 18
// 函数也可以成为一个对象的属性,对象的方法
obj.sayHello = function(){
console.log("hello")
}
console.log(obj)
obj.sayHello() // 调用对象的方法 对象.方法()
1
2
3
4
5
6
7
8
9


🥽 函数
函数(Function):函数也是一个对象,它具有其他对象所有的功能,函数中可以存储代码,且可以在需要时调用这些代码

🌊 函数的创建
语法:

function 函数名(){
语句...
}
1
2
3
// 创建一个函数对象
function fn(){
console.log("你好!")
console.log("Hello!")
}
// 使用typeof检查函数对象时会返回function
console.log(typeof fn)
1
2
3
4
5
6
7


🌊 函数的调用
调用函数就是执行函数中存储的代码,语法:函数名()。对于函数,调用几次,函数中的代码就会执行几次。

fn()
fn()
fn()
1
2
3


🌊 函数的创建方式
函数的创建方式主要有三种:

函数声明
function 函数名(){
语句...
}
1
2
3
函数表达式
const 变量 = function(){
语句...
}
1
2
3
箭头函数
const 变量 = () => {
语句...
}
// 如果箭头函数的函数体只有一句语句可以简写
const 变量 = () => 语句
1
2
3
4
5
function fn(){
console.log("函数声明所定义的函数~")
}

const fn2 = function(){
console.log("函数表达式")
}

const fn3 = () => {
console.log("箭头函数")
}

const fn4 = () => console.log("箭头函数")

console.log(typeof fn)
console.log(typeof fn2)
console.log(typeof fn3)
console.log(typeof fn4)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18


🌊 参数
💦 函数的参数
形式参数

在定义函数时,可以在函数中指定数量不等的形式参数(形参)
在函数中定义形参,就相当于在函数内部声明了对应的变量但是没有赋值。函数形参的赋值,发生在函数调用时实参的传递。
函数定义形参语法:

函数声明
function 函数名([参数]){
语句...
}
1
2
3
函数表达式
const 变量 = function([参数]){
语句...
}
1
2
3
箭头函数
const 变量 = ([参数]) => {
语句...
}
1
2
3
实际参数

在调用函数时,可以在函数的()传递数量不等的实参
实参会赋值给其对应的形参
实参:
如果实参和形参数量相同,则对应的实参赋值给对应的形参
如果实参多余形参,则多余的实参不会使用
如果形参多余实参,则多余的形参为undefined
实参的类型
JS中不会检查参数的类型,可以传递任何类型的值作为参数
function fn(a, b){
console.log("a =", a, "\tb =", b)
}

fn(1, 2)
fn(1, 2, 3)
fn(1)
fn(true, "hello")
fn(null, 11n)
fn({name:"孙悟空"},"hello")
1
2
3
4
5
6
7
8
9
10


箭头函数的参数

当箭头函数中只有一个参数时,可以省略()
const fn2 = a => {
console.log("a =", a);
}
1
2
3
💦 函数参数的默认值
定义函数的参数时,可以为参数指定默认值。函数参数的默认值,会在形参没有对应实参传递时生效。

const fn3 = (a=10, b=20, c=30) => {
console.log("a =", a);
console.log("b =", b);
console.log("c =", c); // 由于在调用函数时形参c没有实参传递,所以形参c的值为默认值30
}

fn3(1, 2)
1
2
3
4
5
6
7


💦 对象类型数据作为函数实参
对象类型的数据作为函数的实参,实际上是将保存对象地址变量中的对象地址作为函数的实参传递给函数形参,所以函数的形参得到的为对象的地址,即对象类型的数据作为函数的实参,函数的形参也会指向该对象。

对象类型数据作为函数实参,在函数中对对象的属性进行修改会对对象的属性造成影响。

function f(a) {
console.log('f函数中输出: ', a)
a.name = 'Tom'
console.log('f函数中输出: ', a)
}

let obj = {
name: 'Mary'
}

f(obj)

console.log('f函数外输出: ', obj)
1
2
3
4
5
6
7
8
9
10
11
12
13


💦 函数作为参数
在JS中,函数也是一个对象(函数被称为一等函数或一等对象,别的对象能做的事情,函数也可以)

function fn(a) {
console.log(a)
if (typeof a === 'function') {
// 因为函数为对象,函数作为实参,a指向函数对象,a可以当前函数调用
a() // 如果传递的实参为函数对象则调用函数
}
}

function fn2(){
console.log("我是fn2")
}

fn(fn2)
fn(function(){
console.log("我是匿名函数~")
})
fn(()=>console.log("我是箭头函数"))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


🌊 返回值
💦 函数的返回值
在函数中,可以通过return关键字来指定函数的返回值,返回值就是函数的执行结果,函数调用完毕返回值便会作为结果返回

function sum(a, b) {
// console.log(a + b)
// 计算完成后,将计算的结果返回而不是直接打印
return a + b
}

let numSum = sum(12, 13)
console.log(numSum)
1
2
3
4
5
6
7
8


任何值都可以作为返回值使用(包括对象和函数之类),如果return后不跟任何值,则相当于返回undefined,如果不写return,那么函数的返回值依然是undefined

function fn1() {
return 'hello'
}
function fn2() {
return 123
}
function fn3() {
return {name: 'Tom'}
}
function fn4() {
function fnInner() {
console.log('fn4 fnInner')
}
return fnInner
}
function fn5() {
return
}
function fn6() {}

console.log('fn1', fn1()) // 打印调用函数后的返回值
console.log('fn2', fn2())
console.log('fn3', fn3())
console.log('fn4', fn4())
console.log('fn5', fn5())
console.log('fn6', fn6())

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26


return一执行函数立即结束,return后面的代码不会继续执行

function fn(a, b) {
let sum = a + b
return sum
console.log('hello world')
console.log('hello world')
console.log('hello world')
console.log('hello world')
}
let sum = fn(11, 12)
console.log(sum)
1
2
3
4
5
6
7
8
9
10


💦 箭头函数的返回值
对于箭头函数,如果箭头函数的函数体只有一句语句且返回值为该语句的计算值,则返回值可以直接写在箭头后,如果直接在箭头后设置对象字面量为返回值时,对象字面量必须使用()括起来,否则该对象字面量会被认为是函数体

let add = (a, b) => a + b
let fn = () => ({name:"孙悟空"})

let sum = add(12, 23)
let result = fn()

console.log(sum)
console.log(result)
1
2
3
4
5
6
7
8


🌊 作用域
💦 作用域简介
作用域指的是一个变量的可见区域
作用域有两种:
全局作用域
全局作用域在网页运行时创建,在网页关闭时销毁
所有直接编写到script标签中的代码都位于全局作用域中
全局作用域中的变量是全局变量,可以在任意位置访问
局部作用域
块作用域
块作用域是一种局部作用域
块作用域在代码块执行时创建,代码块执行完毕它就销毁
在块作用域中声明的变量是局部变量,只能在块内部访问,外部无法访问
函数作用域
函数作用域也是一种局部作用域
函数作用域在函数调用时产生,调用结束后销毁
函数每次调用都会产生一个全新的函数作用域
在函数中定义的变量是局部变量,只能在函数内部访问,外部无法访问
💦 作用域链
当我们使用一个变量时,JS解释器会优先在当前作用域中寻找变量,如果找到了则直接使用;如果没找到,则去上一层作用域中寻找,找到了则使用
如果没找到,则继续去上一层寻找,以此类推;如果一直到全局作用域都没找到,则报错 xxx is not defined。

作用域链,就是就近原则,会寻找距离使用位置最近的作用域的变量。

let b = 33
function fn(){
function f1(){
let b = 55
console.log(b) // 当前作用域就有b,所有输出为当前作用域的b
}
f1()
console.log(b) // 当前作用域无b,会向外查找
}
fn()
1
2
3
4
5
6
7
8
9
10


🌊 window对象
在浏览器中,浏览器为我们提供了一个window对象,可以直接访问
window对象代表的是浏览器窗口,通过该对象可以对浏览器窗口进行各种操作,除此之外window对象还负责存储JS中的内置对象(如Number、String)和浏览器的宿主对象(如document、console)
window对象的属性可以通过window对象访问,也可以直接访问
console.log("hello")
window.console.log("world")
1
2

函数就可以认为是window对象的方法
function fn(){
console.log('我是fn')
}
window.fn()
fn()
1
2
3
4
5

向window对象中添加的属性会自动成为全局变量
window.a = 10 // 向window对象中添加的属性会自动成为全局变量
console.log(window.a)
console.log(a)
1
2
3

var 用来声明变量,作用和let相同,但是var不具有块作用域
在全局中使用var声明的变量,都会作为window对象的属性保存,等价于window.变量
使用function声明的函数,都会作为window的方法保存
使用let声明的变量不会存储在window对象中,而存在一个秘密的小地方(无法访问)
var虽然没有块作用域,但有函数作用域
var b = 20 // 等价于window.b = 20
console.log(window.b)
console.log(b)
console.log('------------------')

function fn(){
console.log('我是fn')
}
window.fn()
fn()
console.log('------------------')

let c = 33
window.c = 44
console.log(c) // window与存放let变量的位置都有相同的变量,优先let声明的变量
console.log(window.c)
console.log('------------------')

function fn2(){
var d = 10 // var虽然没有块作用域,但有函数作用域
e = 200 // 在局部作用域中,如果没有使用var或let声明变量,则变量会自动成为window对象的属性 也就是全局变量

}
fn2()
console.log('e:', e)
console.log('d:', d)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

🌊 提升
💦 变量的提升
使用var声明的变量,它会在所有代码执行前被声明,所以我们可以在变量声明前就访问变量,但是没什么意义,一般都在变量声明赋值后进行变量的访问。

console.log(a)
var a = 10

// 相当于

var a
console.log(a) // undefine
a = 10
1
2
3
4
5
6
7
8
let声明的变量实际也会提升,但是在赋值之前解释器禁止对该变量的访问

💦 函数的提升
使用function函数声明创建的函数,会在其他代码执行前被创建,所以我们可以在函数声明前调用函数

fn()
function fn(){
alert("我是fn函数~")
}

// 相当于

function fn() {
alert("我是fn函数~")
}
fn()
1
2
3
4
5
6
7
8
9
10
11
// 会报错
fn2()
var fn2 = function(){}

// 相当于

var fn2 // 此时fn2为undefine
fn2()
fn2 = function(){}
1
2
3
4
5
6
7
8
9
💦 提升的目的
将函数的声明定于与变量的声明进行提升,可以提前为函数和变量预留内存空间,预留比实际所需略大的内存空间,减少内存重新分配的次数,从而提高性能。

🌊 立即执行函数(IIFE)
在开发中应该尽量减少直接在全局作用域中编写代码,在全局作用域声明的变量容易被修改或覆盖,所以我们的代码要尽量编写的局部作用域。

如果使用let声明的变量,可以使用{}来创建块作用域,不同块级作用域的同名变量不会造成干扰,var声明的变量不具有块级作用域,使用var关键字声明的不同块级作用域下的同名变量会互相影响。如果使用var声明的变量,可以使用函数作用域

创建一个只执行一次的匿名函数,立即执行函数(IIFE),立即执行函数是一个匿名的函数,并它只会调用一次,可以利用IIFE来创建一个一次性的函数作用域,避免变量冲突的问题

// 括号内的函数虽然为使用function创建的函数,但是没有函数名
// 所以该函数不会被提升

// 以括号开头的函数不会被提升
(function(){
let a = 10
console.log(111)
}());
// 如果没有添加分号会两个立即执行函数会被解释器认为(...)(...)
// Js解析器自动添加的分号会在第二个立即执行函数后面 (...)(...);
// 为了避免报错,可以手动添加分号
// (intermediate value)(intermediate value)(...) is not a function

// 调用立即执行函数的括号可以写在外面也可以写在里面
(function(){
let a = 20
console.log(222)
})()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18


🌊 this
函数在执行时,JS解析器每次都会传递进一个隐含的参数,这个参数就叫做 this,this会指向一个对象,this所指向的对象会根据函数调用方式的不同而不同。

以函数形式调用时,this指向的是window;以方法的形式调用时,this指向的是调用方法的对象

function fn() {
console.log(this === window)
console.log("fn打印", this)
}

const obj = { name: "孙悟空" }
obj.test = fn
const obj2 = { name: "猪八戒", test: fn }

fn()
// 方法中的this为调用方法的对象
// function声明的函数为window对象的方法,直接调用函数相当于window.fn()
// 相当于调用window上的方法,所以以函数形式调用时,this指向的是window
window.fn()
obj.test() // {name:"孙悟空"}
obj2.test() // {name:"猪八戒", test:fn}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16


通过this可以在方法中引用调用方法的对象,从而在方法中可以使用对象的属性或方法。

const obj3 = {
name: "沙和尚",
sayHello: function () {
console.log(this.name)
},
}
const obj4 = {
name: "唐僧",
sayHello: function(){
console.log(this.name)
}
}

// 为两个对象添加一个方法,可以打印自己的名字
obj3.sayHello()
obj4.sayHello()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16


🌊 箭头函数的this
箭头函数的语法:
([参数]) => 返回值
无参箭头函数:() => 返回值
一个参数的:a => 返回值
多个参数的:(a, b) => 返回值
只有一个语句的函数:() => 返回值
只返回一个对象的函数:() => ({...})
有多行语句的函数:
() => {
....
return 返回值
}
1
2
3
4
箭头函数没有自己的this,它的this有外层作用域决定
箭头函数的this和它的调用方式无关
function fn() {
console.log("fn -->", this)
}
// 箭头函数没有自己的this,它的this有外层作用域决定
// 由于该箭头函数声明在全局中,所以该箭头函数的this总是window
const fn2 = () => {
console.log("fn2 -->", this)
}
fn() // window
fn2() // window
const obj = {
name:"孙悟空",
fn, // fn:fn 属性名和属性值一样可以简写
fn2, // 箭头函数fn2声明在全局作用域中,this指向window
sayHello(){
console.log(this.name)
function t(){
console.log("t -->", this)
}
t() // 以函数形式调用时,this指向的是window
// 当前箭头函数声明在对象的sayHello方法中
// 当前作用域的this为调用该方法的对象
const t2 = () => {
console.log("t2 -->", this)
}
t2()
}
}
obj.fn() // obj
obj.fn2() // window
obj.sayHello()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31


🥽 严格模式
JS运行代码的模式有两种:
正常模式
默认情况下代码都运行在正常模式中,在正常模式,语法检查并不严格
它的原则是:能不报错的地方尽量不报错
这种处理方式导致代码的运行性能较差
严格模式
在严格模式下,语法检查变得严格
禁止一些语法
更容易报错,有些正常模式下不报错的在严格模式下会报错,如a = 10即直接给变量a赋值没有声明,在正常模式下不报错,但是在严格模式下会报错
提升了性能
在开发中,应该尽量使用严格模式,这样可以将一些隐藏的问题消灭在萌芽阶段,同时也能提升代码的运行性能
开启严格模式
全局严格模式,在js代码的开头(js代码第一行),写如下代码开启全局严格模式,"use strict"
"use strict" // 全局的严格模式
a = 10
1
2

只在函数中开启严格模式
function fn(){
"use strict" // 函数的严格的模式
...
}
1
2
3
4
🥽 面向对象
🌊 面向对象概述
面向对象编程(OOP)
程序是干嘛的?
程序就是对现实世界的抽象
对象是干嘛的?
一个事物抽象到程序中后就变成了对象
在程序的世界中,一切皆对象
面向对象的编程
面向对象的编程指,程序中的所有操作都是通过对象来完成
做任何事情之前都需要先找到它的对象,然后通过对象来完成各种操作
一个事物通常由两部分组成:数据和功能
一个对象由两部分组成:属性和方法
事物的数据到了对象中,体现为属性
事物的功能到了对象中,体现为方法
const five = {
// 添加属性
name:"王老五",
age:48,
height:180,
weight:100,

// 添加方法
sleep(){
console.log(this.name + "睡觉了~")
},

eat(){
console.log(this.name + "吃饭了~")
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
🌊 类
使用Object或对象字面量{}创建对象的问题:
无法区分出不同类型的对象
不方便批量创建对象
在JS中可以通过类(class)来解决这个问题:
类是对象模板,可以将对象中的属性和方法直接定义在类中,定义后,就可以直接通过类来创建对象。类好比汽车制作的图纸,每个汽车为汽车这个类对应的汽车对象。
通过同一个类创建的对象,我们称为同类对象,可以使用instanceof来检查一个对象是否是由某个类创建,如果某个对象是由某个类所创建,则我们称该对象是这个类的实例。
类是创建对象的模板,要创建对象第一件事就是定义类,定义类的语法:
class 类名 {} // 类名要使用大驼峰命名
const 类名 = class {}
1
2
通过类创建对象
new 类()
1
// Person类专门用来创建人的对象
class Person{

}
// Dog类式专门用来创建狗的对象
class Dog{

}

// 使用类创建对象,便于批量创建对象
const p1 = new Person() // 调用类的构造函数创建对象
const p2 = new Person()
console.log(p1, p2) // 使用类创建对象,可以区分出不同类型的对象

const d1 = new Dog()
const d2 = new Dog()
console.log(d1, d2)

console.log(p1 instanceof Person) // true
console.log(d1 instanceof Person) // false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20


🌊 属性
类构造出来的对象,传统的属性添加方法

class Person {}

const p1 = new Person()
p1.name = 'Tom'
console.log(p1)
1
2
3
4
5


类的代码块,默认就是严格模式,类的代码块是用来设置对象的属性和方法的,不是什么代码都能写

class Person {
// Person的实例属性
// 在类中定义了类的实例属性,通过该类创建出来的对象都会具有一个属于自己的该属性
// 实例属性只能通过实例访问
// 调用实例属性 对象名.实例属性
name = "孙悟空" // Person的实例属性name,并附初始值,也可以不赋初始值,则默认初始值为undefined
age = 18

// 使用static声明的属性,是静态属性(类属性)
// 静态属性只能通过类去访问
// 调用静态属性 类名.静态属性
static hh = "静态属性"
}

const p1 = new Person()
p1.name = 'Tom' // 访问p1的实例属性name
console.log(p1)
console.log(Person.hh) // 访问Person的类属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18


🌊 方法
class Person {
// 实例属性
name = "Tom"
// 方法
// 方法其实也是属性,只是方法的值为函数
// 添加方法的方式一
sayHi = function() { // 实例方法
console.log('hi world')
}
// 添加方法的方式二
// 添加方法可以直接写 `方法名+参数列表+方法体`
sayHello() { // 实例方法
console.log('hello world')
}
// 实例方法的调用 对象.方法()
// 实例方法中的this为调用方法的实例对象
printThis() {
console.log('实例方法的this: ', this)
}
// 静态方法(类方法)
// 通过类来调用 类.方法()
// 静态方法中this指向的是当前类
static printStaticThis() {
console.log('静态方法的this: ', this)
}
}

const p1 = new Person()
console.log(p1)
p1.sayHi()
p1.sayHello()
p1.printThis()
Person.printStaticThis()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33


🌊 构造函数
在类中可以添加一个特殊的方法constructor,该方法我们称为构造函数(构造方法),构造函数会在我们调用类创建对象时执行,我们可以在构造函数中,为实例属性进行赋值。

在构造函数中,this表示当前所创建的对象。

class Person {
constructor(name, age, gender) {
console.log("构造函数执行了~", name, age, gender)
// this指向当前创建出来的对象
// 向创建出来的对象中添加属性name,其值为通过参数传递过来的name
// 对象的属性可以动态添加
this.name = name
this.age = age
this.gender = gender
}
}

const p1 = new Person('孙悟空', 18, '男')
const p2 = new Person('猪八戒', 28, '男')
const p3 = new Person('沙和尚', 38, '男')

console.log(p1)
console.log(p2)
console.log(p3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


🌊 面向对象的三大特性
面向对象的三大特性:
封装 —— 安全性
继承 —— 扩展性
多态 —— 灵活性
💦 封装
对象就是一个用来存储不同属性的容器
对象不仅存储属性,还要负责数据的安全,即还要保证数据的合法性
直接添加到对象中的属性,并不安全,因为它们可以被任意的修改
如何确保数据的安全:
私有化数据
将需要保护的数据设置为私有,只能在类内部使用
实例属性使用#开头就变成了私有属性,私有属性只能在类内部访问
提供setter和getter方法来开放对数据的操作
getter方法,用来读取属性
setter方法,用来设置属性
属性设置私有,通过getter setter方法操作属性带来的好处
可以控制属性的读写权限
可以在方法中对属性的值进行验证
封装主要用来保证数据的安全
实现封装的方式:
属性私有化加#
通过getter和setter方法来操作属性
// getter与setter使用下面的写法
// 调用属性和修改属性可以使用 对象.属性
// 但是在调用属性和修改属性时会自动调用相应的getter和setter方法
get 属性名(){
return this.#属性
}

set 属性名(参数){
this.#属性 = 参数
}
1
2
3
4
5
6
7
8
9
10
class Person {
// 实例使用#开头就变成了私有属性,私有属性只能在类内部访问
#name
#age
#gender

constructor(name, age, gender) {
this.#name = name
this.#age = age
this.#gender = gender
}

sayHello() {
console.log(this.#name)
}

// getter方法,用来读取属性
getName(){
return this.#name
}
// setter方法,用来设置属性
setName(name){
this.#name = name
}
getAge(){
return this.#age
}
setAge(age){
// 校验数据是否合法
if(age >= 0){
this.#age = age
}
}

// getter与setter使用下面的写法
// 调用属性和修改属性可以使用 对象.属性
// 但是在调用属性和修改属性时会自动调用相应的getter和setter方法
get gender(){
return this.#gender
}
set gender(gender){
this.#gender = gender
}
}

const p1 = new Person("孙悟空", 18, "男")
console.log(p1.getName())
// -11 修改的数据不合法,在setter方法中判断不合法不会继续修改,保证了数据的合法
p1.setAge(-11)
p1.gender = "女" // getter与setter使用 `get 属性` `set 属性` 写法的调用
console.log(p1.gender)
console.log(p1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52


💦 多态
多态,在JS中不会检查参数的类型,所以这就意味着任何数据都可以作为参数传递,要调用某个函数,无需指定的类型,只要对象满足某些条件即可。即调用某个函数不关心函数参数的类型,关心函数的参数是否具有某些特点。

多态为我们提供了灵活性。

class Person{
constructor(name){
this.name = name
}
}
class Dog{
constructor(name){
this.name = name
}
}
class Test{

}

const dog = new Dog('旺财')
const person = new Person("孙悟空")
const test = new Test()

function sayHello(obj){
if(obj.hasOwnProperty('name')){ // 判断对象是否具有name属性
console.log("Hello,"+obj.name)
}
}

sayHello(dog)
sayHello(person)
sayHello(test)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


💦 继承
可以通过extends关键来完成继承
当一个类继承另一个类时,就相当于将另一个类中的代码复制到了当前类中(简单理解)
class Animal{
constructor(name){
this.name = name
}
sayHello(){
console.log("动物在叫~")
}
}
class Dog extends Animal{

}
class Cat extends Animal{

}

const dog = new Dog("旺财")
const cat = new Cat("汤姆")
dog.sayHello()
cat.sayHello()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

继承发生时,被继承的类称为 父类(超类),继承的类称为 子类
通过继承可以减少重复的代码,并且可以在不修改一个类的前提对其进行扩展
在子类中,可以通过创建同名方法来重写父类的方法
class Dog extends Animal{
// 在子类中,可以通过创建同名方法来重写父类的方法
sayHello(){
console.log("汪汪汪")
}
}
const dog = new Dog("旺财")
const cat = new Cat("汤姆")
dog.sayHello()
cat.sayHello()
1
2
3
4
5
6
7
8
9
10

重写构造函数时,构造函数的第一行代码必须为super(),super表示父类,super()调用父类的构造函数
class Cat extends Animal{
// 重写构造函数
constructor(name, age){
// 重写构造函数时,构造函数的第一行代码必须为super()
super(name) // 调用父类的构造函数
this.age = age
}
sayHello(){
// 调用一下父类的sayHello
super.sayHello() // 在方法中可以使用super来引用父类的方法
console.log("喵喵喵")
}
}
const dog = new Dog("旺财")
const cat = new Cat("汤姆", 3)
dog.sayHello()
cat.sayHello()
console.log(dog)
console.log(cat)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

通过继承可以在不修改一个类的情况下对其进行扩展
OCP 开闭原则,程序应该对修改关闭,对扩展开放
🌊 对象的结构
对象中存储属性的区域实际有两个:
对象自身
直接通过对象所添加的属性,位于对象自身中
在类中通过 x = y 的形式添加的属性,位于对象自身中
class Person {
name = "孙悟空" // 在类中通过 x = y 的形式添加的属性
age = 18 // 在类中通过 x = y 的形式添加的属性
constructor(){
this.gender = "男" // 通过对象所添加的属性
}
}
const p = new Person()
p.address = "花果山" // 通过对象所添加的属性
1
2
3
4
5
6
7
8
9
原型对象(prototype)
对象中还有一些内容,会存储到其他的对象里(原型对象)
在对象中会有一个属性用来存储原型对象,这个属性叫做__proto__

原型对象也负责为对象存储属性,当我们访问对象中的属性时,会优先访问对象自身的属性,对象自身不包含该属性时,才会去原型对象中寻找
会添加到原型对象中的情况:
在类中通过xxx(){}方式添加的方法,位于原型中
主动向原型中添加的属性或方法
🌊 原型对象
💦 访问一个对象的原型对象
// 方式一
对象.__proto__ // 不推荐
// 方式二
Object.getPrototypeOf(对象)
1
2
3
4
class Person {
name = "孙悟空"
age = 18
sayHello() {
console.log("Hello,我是", this.name)
}
}
const p = new Person()
console.log(p)
// 一般以下划线开头的属性都是不希望直接进行访问的属性
console.log(p.__proto__)
1
2
3
4
5
6
7
8
9
10
11


const p = new Person()
console.log(p)
// 一般以下划线开头的属性都是不希望直接进行访问的属性
console.log(p.__proto__)
console.log(Object.getPrototypeOf(p)) // 获取某个对象的原型对象
1
2
3
4
5


由结果可以看出,原型对象中的数据包含:1. 对象中的数据(属性、方法等)2. constructor (对象的构造函数,其实就是对象对应的类)

console.log(p.__proto__.constructor)
console.log(p.constructor)
1
2


💦 原型链
原型对象也有原型,这样就构成了一条原型链,根据对象的复杂程度不同,原型链的长度也不同,p对象的原型链:p对象 --> 原型 --> 原型 --> null,obj对象的原型链:obj对象 --> 原型 --> null。
读取对象属性时,会优先对象自身属性,如果对象中有,则使用,没有则去对象的原型中寻找,如果原型中有,则使用,没有则去原型的原型中寻找,直到找到Object对象的原型(Object的原型没有原型(为null)),如果依然没有找到,则返回undefined
作用域链,是找变量的链,找不到会报错
原型链,是找属性的链,找不到会返回undefined
💦 原型的作用
所有的同类型对象它们的原型对象都是同一个,也就意味着,同类型对象的原型链是一样的。

class Person {
name = "孙悟空"
age = 18
sayHello() {
console.log("Hello,我是", this.name)
}
}
class Dog {}

const p = new Person()
const p2 = new Person()
const d = new Dog()
const d2 = new Dog()
console.log(p === p2)
console.log(p.__proto__ === p2.__proto__)
console.log(d.__proto__ === d2.__proto__)
console.log(p.__proto__ === d.__proto__)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

 

原型就相当于是一个公共的区域,可以被所有该类实例访问,可以将该类实例中,所有的公共属性(方法)统一存储到原型中,这样我们只需要创建一个属性,即可被所有实例访问。

在对象中有些值是对象独有的,像属性(name,age,gender)每个对象都应该有自己值,对于这些放在对象上;但是有些值对于每个对象来说都是一样的,像各种方法,对于一样的值没必要重复的创建,所以放在对象的原型对象中

const p = new Person()
const p2 = new Person()
console.log(p.__proto__)
console.log(p2.__proto__)
console.log(p.sayHello === p2.sayHello)
1
2
3
4
5


JS中继承就是通过原型来实现的,当继承时,子类的原型就是一个父类的实例。

class Animal{}
class Cat extends Animal{}
const cat = new Cat()
// cat --> Animal实例 --> object --> Object原型 --> null
console.log(cat.__proto__)
console.log(cat.__proto__.__proto__)
console.log(cat.__proto__.__proto__.__proto__)
console.log(cat.__proto__.__proto__.__proto__.__proto__)
1
2
3
4
5
6
7
8


function fn() {}
console.log(fn.__proto__)
console.log(fn.__proto__.__proto__)
console.log(fn.__proto__.__proto__.__proto__)
1
2
3
4


💦 修改原型
大部分情况下,我们是不需要修改原型对象
注意:千万不要通过类的实例去修改原型
通过一个对象影响所有同类对象,这么做不合适
修改原型先得创建实例,麻烦
危险,如果将对象的原型对象进行整个替换,该操作很危险
除了通过__proto__能访问对象的原型外,还可以通过类的prototype属性,来访问实例的原型
class Person {
name = "孙悟空"
age = 18
sayHello() {
console.log("Hello,我是", this.name)
}
}
const p = new Person()
console.log(p.__proto__)
console.log(Person.prototype)
console.log(Person.prototype === p.__proto__)
1
2
3
4
5
6
7
8
9
10
11

修改原型时,最好通过通过类去修改
好处:
一修改就是修改所有实例的原型
无需创建实例即可完成对类的修改
原则:
原型尽量不要手动改
要改也不要通过实例对象去改
通过 类.prototype 属性去修改,类.prototype.属性 = 值
最好不要直接给prototype去赋值,即类.prototype = 值
💦 instanceof
instanceof 用来检查一个对象是否是一个类的实例
instanceof检查的是对象的原型链上是否有该类实例,只要原型链上有该类实例,就会返回true
Object是所有对象的原型,所以任何和对象和Object进行instanceof运算都会返回true
class Animal {}
class Dog extends Animal {}
const dog = new Dog()
// dog -> Animal的实例 -> Object实例 -> Object原型
console.log(dog instanceof Dog) // true
console.log(dog instanceof Animal) // true
console.log(dog instanceof Object) // true
1
2
3
4
5
6
7


💦 判断对象是否具有指定属性
in
使用in运算符检查属性时,无论属性在对象自身还是在原型中,都会返回true
对象.hasOwnProperty(属性名) (不推荐使用)
用来检查一个对象的自身是否含有某个属性,该方法位于object原型中
Object.hasOwn(对象, 属性名)
用来检查一个对象的自身是否含有某个属性
class Person {
name = "孙悟空"
age = 18
sayHello() {
console.log("Hello,我是", this.name)
}
}
const p = new Person()
cconsole.log("'name' in p", 'name' in p)
console.log("'address' in p", 'address' in p)
console.log('"sayHello" in p', "sayHello" in p)
console.log('-----------------------------------')
console.log('p.hasOwnProperty("name")', p.hasOwnProperty("name"))
console.log('p.hasOwnProperty("sayHello")', p.hasOwnProperty("sayHello"))
console.log('-----------------------------------')
console.log('Object.hasOwn(p, "name")', Object.hasOwn(p, "name"))
console.log('Object.hasOwn(p, "sayHello")', Object.hasOwn(p, "sayHello"))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


🌊 旧类(早期JS中定义类)
早期JS中,直接通过函数来定义类,一个函数如果直接调用 xxx() 那么这个函数就是一个普通函数,一个函数如果通过new调用 new xxx() 那么这个函数就是一个构造函数

// 立即执行函数,防止类的代码还未完成书写就使用类进行对象的创建
var Person = (function () {
// 相当于class中的constructor构造函数
function Person(name, age) {
// 在构造函数中,this表示新建的对象
this.name = name
this.age = age
// 采用该方式添加方法会造成每个对象都有自己的方法,无法实现方法的共享
// this.sayHello = function(){
// console.log(this.name)
// }
}
// 向原型中添加属性(方法)
Person.prototype.sayHello = function () {
console.log(this.name)
}
// 类添加静态属性
Person.staticProperty = "xxx"
// 类添加静态方法
Person.staticMethod = function () {}
// 返回类
return Person
})()
const p = new Person("孙悟空", 18)
console.log(p)

var Animal = (function(){
function Animal(){

}
return Animal
})()
var Cat = (function(){
function Cat(){

}
// 继承Animal
// 继承之后,该类创建出来的对象的原型对象为父类的实例对象
Cat.prototype = new Animal()
return Cat
})()
var cat = new Cat()
console.log(cat)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43


🌊 new运算符
new运算符是创建对象时要使用的运算符
使用new时,到底发生了哪些事情:【https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new】
当使用new去调用一个函数时,这个函数将会作为构造函数调用,类创建对象必须使用new关键字,使用new调用函数时,将会发生这些事:
创建一个普通的JS对象(Object对象 {}), 为了方便,称其为新对象
将构造函数的prototype属性设置为新对象的原型
使用实参来执行构造函数,并且将新对象设置为函数中的this
如果构造函数返回的是一个非原始值,则该值会作为new运算的返回值返回(千万不要这么做),如果构造函数的返回值是一个原始值或者没有指定返回值,则新的对象将会作为返回值返回,通常不会为构造函数指定返回值
function MyClass(){
// 1. 创建一个普通的JS对象(Object对象 {}), 为了方便,称其为新对象
var newInstance = {}
// 2. 将构造函数的prototype属性设置为新对象的原型
newInstance.__proto__ = MyClass.prototype
// 3. 使用实参来执行构造函数,并且将新对象设置为函数中的this
this = newInstance
// 4.
// 如果构造函数返回的是一个非原始值,则该值会作为new运算的返回值返回
// return {name: 'Tom'} // 则新创建的对象为{name: 'Tom'}
// 如果构造函数的返回值是一个原始值或者没有指定返回值,则新的对象将会作为返回值返回
return 1 // 新创建的对象为newInstance
}
var mc = new MyClass()
console.log(mc)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


🥽 数组(Array)
🌊 简介
数组也是一种复合数据类型,在数组可以存储多个不同类型的数据
数组中存储的是有序的数据,数组中的每个数据都有一个唯一的索引,可以通过索引来操作获取数据,索引(index)是一组大于0的整数
数组中存储的数据叫做元素
创建数组
通过Array()来创建数组,也可以通过[]来创建数组
const arr = new Array()
const arr2 = [1, 2, 3, 4, 5] // 数组字面量

console.log(arr)
console.log(arr2)
1
2
3
4
5

向数组中添加元素
语法:数组[索引] = 元素
const arr = new Array()
console.log(arr)
arr[0] = 10 // 向数组的第一个位置添加一个数据
arr[1] = 22 // 向数组的第二个位置添加一个数据
arr[2] = 44 // 向数组的第三个位置添加一个数据
arr[3] = 88
arr[4] = 99
console.log(arr)
1
2
3
4
5
6
7
8

读取数组中的元素
语法:数组[索引]
如果读取了一个不存在的元素,不会报错而是返回undefined
const arr = [11, 22, 33, 44, 55]
console.log(arr[0])
console.log(arr[1])
console.log(arr[10])
1
2
3
4

length
获取数组的长度
获取的实际值就是数组的最大索引 + 1
向数组最后添加元素:数组[数组.length] = 元素
length是可以修改的
const arr = [11, 22, 33, 44, 55]
console.log(arr)
console.log(arr.length) // 获取数组的长度, 数组的最大索引 + 1
arr[arr.length] = 66 // 向数组最后添加元素
console.log(arr)
console.log(arr.length)
arr.length = 12 // 修改length
console.log(arr)
console.log(arr.length)
1
2
3
4
5
6
7
8
9

任何类型的值都可以成为数组中的元素
// 任何类型的值都可以成为数组中的元素
let arr = [1, "hello", true, null, { name: "孙悟空" }, () => {}]
console.log(arr)
1
2
3

🌊 数组的遍历
遍历数组简单理解,就是获取到数组中的每一个元素

💦 for循环
//任何类型的值都可以成为数组中的元素
let arr = [1, "hello", true, null, { name: "孙悟空" }, () => {}]
console.log(arr)

// 遍历数组
for (let index = 0; index < arr.length; index++) {
const element = arr[index];
console.log('第' + (index+1) + '个数组元素: ', element)
}
1
2
3
4
5
6
7
8
9


💦 for-of语句
for-of语句可以用来遍历可迭代对象
语法:
for(变量 of 可迭代的对象){
语句...
}
1
2
3
执行流程:for-of的循环体会执行多次,数组中有几个元素就会执行几次,每次执行时都会将一个元素赋值给变量
const arr = ["孙悟空", "猪八戒", "沙和尚", "唐僧"]
// 每次执行时都会将一个元素赋值给变量
for(let value of arr){
console.log(value)
}
// for-of语句可以用来遍历可迭代对象
for(let value of "hello"){
console.log(value)
}
1
2
3
4
5
6
7
8
9


🥽 Map
Map用来存储键值对结构的数据(key-value)
Object中存储的数据就可以认为是一种键值对结构
Map和Object的主要区别:
Object中的属性名只能是字符串或符号,如果传递了一个其他类型的属性名,JS解释器会自动将其转换为字符串
const obj = {
"name":"孙悟空",
'age':18,
[Symbol()]:"哈哈",
[obj2]:"嘻嘻" // JS解释器会自动将其转换为字符串 object Object
}
console.log(obj)
1
2
3
4
5
6
7

Map中任何类型的值都可以成为数据的key
🌊 创建Map
语法:new Map()

// 创建一个Map
const map = new Map()
console.log(map)
// Map中任何类型的值都可以成为数据的key
// map.set(key, value) 向map中添加键值对
map.set("name", "孙悟空")
const obj2 = {}
map.set(obj2, "呵呵")
map.set(NaN, "哈哈哈")
console.log(map)
1
2
3
4
5
6
7
8
9
10


🌊 Map的属性和方法
💦 map.set(key, value) 向map中添加键值对
// 创建一个Map
const map = new Map()
console.log(map)
// Map中任何类型的值都可以成为数据的key
// map.set(key, value) 向map中添加键值对
map.set("name", "孙悟空")
const obj2 = {}
map.set(obj2, "呵呵")
map.set(NaN, "哈哈哈")
console.log(map)
1
2
3
4
5
6
7
8
9
10


💦 map.get(key) 根据key获取值
console.log(map.get("name"))
console.log(map.get(NaN))
console.log(map.get(obj2))
1
2
3


💦 map.size 获取map中键值对的数量
console.log(map)
console.log(map.size)
1
2


💦 map.delete(key) 删除指定数据
console.log(map)
console.log(map.size)
map.delete(NaN)
console.log(map)
console.log(map.size)
1
2
3
4
5


💦 map.has(key) 检查map中是否包含指定键
console.log(map.has("name"))
map.delete(NaN)
console.log(map.has(NaN))
1
2
3


💦 map.clear() 删除全部的键值对
console.log(map)
console.log(map.size)
map.clear()
console.log(map)
console.log(map.size)
1
2
3
4
5


🌊 map转换为数组
💦 Array.form()
const map = new Map()
map.set("name", "孙悟空")
map.set("age", 18)
// 将map转换为数组
// 转换的结果为二维数组
// Map的每个键值对的键和值组成一个数组作为转换结果数组的元素
const arr = Array.from(map) // [["name","孙悟空"],["age",18]]
console.log(map)
console.log(arr)
1
2
3
4
5
6
7
8
9


💦 扩展转换
const map = new Map()
map.set("name", "孙悟空")
map.set("age", 18)
// 将map转换为数组
// const arr = Array.from(map) // [["name","孙悟空"],["age",18]]
// 转换的结果为二维数组
// Map的每个键值对的键和值组成一个数组作为转换结果数组的元素
const arr = [...map]
console.log(map)
console.log(arr)
1
2
3
4
5
6
7
8
9
10


🌊 通过二维数组创建Map
const map2 = new Map([
["name", "猪八戒"],
["age", 18],
[{}, () => {}],
])
// 数组中的每个一维数组为一个键值对
// 每个一维数组的第一个元素为键,第二个元素为值
console.log(map2)
1
2
3
4
5
6
7
8


🌊 遍历map
💦 for-of
// 遍历map
for (const entry of map) {
// 获取的entry为map中的每个键值对
// entry是一个一维数组
// entry中第一个元素为键值对的键,第二个元素为键值对的值
console.log(entry)
const [key, value] = entry // 对entry解构赋值
console.log(key, value)
}
console.log('------------------------------------')
// 在获取到map的每个键值对时直接进行解构赋值
for (const [key, value] of map) {
console.log(key, value)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14


💦 Map.forEach()
// Map.forEach()遍历map
// 需要传递一个回调函数
// map中有几个键值对就会执行几次回调函数
// 回调函数的第一个参数为键值对的键,第二个参数为键值对的值
// 回调函数的第三个参数为map本身
map.forEach((key, value, map)=>{
console.log(key, value, map)
})
1
2
3
4
5
6
7
8


💦 map.keys() 获取map的所有的key
console.log(map.keys()) // 获取map的所有的key
for (const key of map.keys()) {
console.log(key)
}
1
2
3
4


💦 map.values() 获取map的所有的value
console.log(map.values()) // 获取map的所有的value
for (const value of map.values()) {
console.log(value)
}
1
2
3
4


💦 map.entries() 获取map的所有的键值对
console.log(map.entries()) // 获取map的所有的键值对
for (const entry of map.entries()) {
console.log(entry)
}
1
2
3
4


🥽🌊💦💧

posted @ 2023-02-11 11:44  雨潇潇兮  阅读(14)  评论(0编辑  收藏  举报