一、对象

对象是JS中的一种复合数据类型,它相当于一个容器,在对象中可以存储各种不同类型数据

1、创建对象

1
2
3
4
5
6
7
//利用Object()方法创建
let obj1 = Object()
//通过字面量法创建对象
let obj2 ={
name: "张三",
age: 18
}

2、对象的属性

1
2
3
4
5
//续1.1
//添加
obj1.name = "李四"
//读取
console.log(obj2.name) //打印 "张三"

3、遍历对象中的属性

通过for-in语句可以获取对象中的属性

注:并不是所有属性都可以被枚举

1
2
3
4
//续1.1
for(let i in obj){
console.log(i, obj2[i])
}

4、改变量与改变对象

  • 修改对象时,如果有其他变量指向该对象则所有指向该对象的变量都会受到影响

  • 修改变量修改变量时,只会影响当前的变量

在使用变量存储对象时,很容易因为改变变量指向的对象,提高代码的复杂度。所以通常情况下,声明存储对象的变量时会使用const

注意: const只是禁止变量被重新赋值,对对象的修改没有任何影响

1
2
3
4
5
const obj = {name:"张三"}
const obj2 = obj
obj2 = null //此时obj不会改变
//修改对象
obj2.name = "李四" //此时obj也会跟着改变

5、方法

当一个对象的属性指向一个函数,那么我们就称这个函数是该对象的方法,调用函数就称为调用对象的方法

1
2
3
4
5
6
7
8
9
let obj = {
name: "张三",
age:18,
sayHello = function(){
alert("Hello")
}
}
//调用
obj.sayHello()

2、面向对象编程

一个事物抽象到程序中后就变成了对象,在程序的世界中,一切皆对象。面向对象的编程指,程序中的所有操作都是通过对象来完成。做任何事情之前都需要先找到它的对象,然后通过对象来完成各种操作。

三大特性:封装(安全性)、继承(扩展性)、多态(灵活性)

​ 一个事物通常由两部分组成:数据和功能
​ 一个对象由两部分组成:属性和方法
​ 事物的数据到了对象中,体现为属性;事物的功能到了对象中,体现为方法

1、创建类

类是对象模板,可以将对象中的属性和方法直接定义在类中定义后,就可以直接通过类来批量创建对象。

通过同一个类创建的对象,我们称为同类对象。可以使用instanceof来检查一个对象是否是由某个类创建如果某个对象是由某个类所创建,则我们称该对象是这个类的实例

1
2
3
4
5
6
//创建Person类
class Peoson{

}
//调用构造函数创建对象
const p1 = new Person()

2、构造函数

在类中可以添加一个特殊方法constructor,这个方法我们称为构造函数,它会在我们调用类创建对象时执行

1
2
3
4
5
6
7
8
9
10
11
class Person{
constructor(name, age, gender){
// console.log("构造函数执行了~", name, age, gender)
// 可以在构造函数中,为实例属性进行赋值
// 在构造函数中,this表示当前所创建的对象
this.name = name
this.age = age
this.gender = gender
}
}
const p1 = new Person("张三",18,"男")

3、封装

  • 对象不仅存储属性,还要负责数据的安全,直接添加到对象中的属性并不安全,因为他们可以被随意修改。这时我们就要通过封装确保数据的安全,这样数据就只能在类内部使用

  • 实现方式:1、属性私有化 前加# 2、通过getter和setter方法来操作属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Person{
#name //首先我们哟啊声明一个私有属性
#age
#gender
constructor(name, age, gender) {
this.#name = name
this.#age = age
this.#gender = gender
}
// getter方法,用来读取属性
getName(){
return this.#name
}
//setter方法,用来设置属性
setName(name){
this.#name = name
}
}
const p1 = new Person("张三",18,"男")
p1.setName(12) //设置
p1.getName() //读取

4、多态

多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果

5、继承

可以通过extends关键来完成继承
​ - 当一个类继承另一个类时,就相当于将另一个类中的代码复制到了当前类中(简单理解)
​ - 继承发生时,被继承的类称为 父类(超类),继承的类称为 子类
​ - 通过继承可以减少重复的代码,并且可以在不修改一个类的前提对其进行扩展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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() //输出:动物在叫

6、原型对象

在JavaScript中,我们创建一个函数A(就是声明一个函数), 那么浏览器就会在内存中创建一个对象B,而且每个函数都默认会有一个属性 prototype 指向了这个对象( 即:prototype的属性的值是这个对象 )。这个对象B就是函数A的原型对象,简称函数的原型。这个原型对象B 默认会有一个属性 constructor 指向了这个函数A ( 意思就是说:constructor属性的值是函数A )。

  • 会添加到原型对象中的情况:
    ​ 1.在类中通过xxx(){}方式添加的方法,位于原型中
    ​ 2.主动向原型中添加的属性或方法

  • 注意:原型对象也有原型,这样就构成了一条原型链,根据对象复杂程度不同,原型链的长度也不同

  • 原型链:读取对象属性时,会优先对象自身属性, 如果对象中有,则使用,没有则去对象的原型中寻找。如果原型中有,则使用,没有则去原型的原型中寻找,直到找到Object对象的原型(Object的原型没有原型(为null))。 如果依然没有找到,则返回undefined。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const obj = {}
    class Person{
    name = "张三"
    age = 18
    sayHello(){
    console.log("Hello,我是",this.name)
    }
    }
    const p1 = new Person()
    //访问一个对象的原型对象 对象.__proto__ 或 Object.getPrototypeOf(对象)
    console.log(p1.__proto__)
    console.log(p1.__proto__.__proto__)
  • 原型链:读取对象属性时,会优先对象自身属性, 如果对象中有,则使用,没有则去对象的原型中寻找。如果原型中有,则使用,没有则去原型的原型中寻找,直到找到Object对象的原型(Object的原型没有原型(为null))。 如果依然没有找到,则返回undefined。
    ​ 例: p对象的原型链:p对象 –> 原型 –> 原型 –> nul ; obj对象的原型链:obj对象 –> 原型 –> null

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Animal{ 
    }
    class Cat extends Animal{
    }
    class TomCat extends Cat{
    }
    //原型链
    // TomCat --> cat --> Animal实例 --> object --> Object原型 --> null
    // cat --> Animal实例 --> object --> Object原型 --> null
    6.2、修改原型
    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
    class Person {
    name = "孙悟空"
    age = 18
    sayHello() {
    console.log("Hello,我是", this.name)
    }
    }
    Person.prototype.fly = () => {
    console.log("我在飞!")
    } //这段代码将为构造函数的 prototype 属性添加一个新的方法:
    const p = new Person()
    const p2 = new Person()
    p.fly()
    p2.fly()
    /*
    大部分情况下,我们是不需要修改原型对象
    注意:
    千万不要通过类的实例去修改原型
    1. 通过一个对象影响所有同类对象,这么做不合适
    2. 修改原型先得创建实例,麻烦
    3. 危险
    除了通过__proto__能访问对象的原型外,还可以通过类的prototype属性,来访问实例的原型
    修改原型时,最好通过通过类去修改
    好处:
    1. 一修改就是修改所有实例的原型
    2. 无需创建实例即可完成对类的修改
    原则:
    1. 原型尽量不要手动改
    2. 要改也不要通过实例对象去改
    3. 通过 类.prototype 属性去修改
    4. 最好不要直接给prototype去赋值
    */