为了账号安全,请及时绑定邮箱和手机立即绑定

JS中的OOPS - 最终版

🔥连接: https://www.subham.online

🔥Twitter: https://twitter.com/TheSubhamMaity


▶️ 介绍

⭐ 什么是面向对象编程(OOPs)?

面向对象编程是一种通过创建对象来解决问题的方法。

⭐ 四大面向对象原则

面向对象编程术语

  1. Abstraction - 隐藏内部细节(仅显示重要信息!)
  2. Encapsulation - 将各种组件封装在一起(放在胶囊里)
  3. Inheritance - 从现有事物中派生出新事物
  4. Polymorphism - 一个实体,多种形态

js

▶️原型和 proto

JavaScript 的对象有一个特殊的属性叫做 prototype,它要么是 null,要么引用另一个对象。

当我们尝试从一个对象读取属性,而该属性缺失时,JavaScript 会自动从原型中获取该属性。这被称为原型继承。

⭐ 设置原型

我们可以通过设置__proto__来设置原型。如果我们从一个对象中读取一个该对象中不存在但原型中存在属性,JavaScript 将从原型中获取该属性。如果我们对象中有方法,将从对象中调用该方法。如果对象中不存在但原型中存在该方法,将从原型中调用该方法。

⭐ 示例:

    //它将正常工作
    let p = {
        run : () => {
            console.log("运行")
        }
    }

    p.run()//输出: 运行

    //让我们定义另一个属性
    let a = {
        name : " subham"
    }

    a.run() //TypeError: a.run is not a function

    //现在使用 proto
    let b = {
        name : " subham"
    }
    b.__proto__ = p
    b.run() //输出: 运行

进入全屏模式 退出全屏模式

简单来说,你可以在另一个对象中继承某个对象的原型。这被称为原型继承。

    //它会正常工作
    let p = {
        run : () => {
            console.log("p run")
        }
    }

    p.run()//输出: p run

    //现在使用 proto
    let b = {
        run : () => {
            console.log("b run")
        }
    }
    b.__proto__ = p
    b.run() //输出: b run

进入全屏模式 退出全屏模式

如果对象中已经存在某个属性或方法,JavaScript 将使用该属性或方法。如果对象中不存在该属性或方法,但在原型中存在,则 JavaScript 将从原型中获取。在这个例子中,由于run方法已经在b对象中存在,因此将打印 'b run'。

▶️ 类和对象
  • 在面向对象编程中,类是一个特定类型对象的方法和变量的模板定义
  • 在面向对象编程中,对象是类(或结构体)在内存中分配的一个具体实例

⭐ 示例:

    //类
    class GoogleForm {
        submit() {
           console.log(this.name + " " + this.roll + " 您的表单已提交")
        }
        cancel() {
            console.log(this.name + " " + this.roll +" 您的表单已取消")
        }
        fill(given_name , roll) {
            this.name = given_name
            this.roll = roll
        }
    }

    //对象
    const student1Form = new GoogleForm()

    student1Form.fill("Rahul" , 24)

    const student2Form = new GoogleForm()

    student2Form.fill("Raj" , 25)

    student2Form.cancel()

    student1Form.submit()

    student2Form.submit()

进入全屏模式 退出全屏模式

▶️ 构造函数

在 JavaScript 中,构造函数是一种 特殊函数,用于创建和初始化对象,设置它们的初始状态和属性。

假设他们忘记填写表格就点击了提交按钮,将会抛出 undefined!

    class 表单 {

        提交() {
            console.log(this.name + ": 您的表单已提交,车次号为: " + this.trainno)
        }

        取消() {
            console.log(this.name + ": 您的表单已取消,车次号为: " + this.trainno)
            this.trainno = 0
        }
        填写(姓名, 车次号) {
            this.name = 姓名
            this.trainno = 车次号
        }

    }

    let myForm1 = new 表单()

    let myForm2 = new 表单()
    //
    // myForm1.填写("Gaurav", 1234)
    //
    // myForm2.填写("Rahul", 5678)

    myForm1.提交()

    myForm2.提交()

    myForm2.取消()

    // 输出: undefined: 您的表单已提交,车次号为: undefined
    // 输出: undefined: 您的表单已提交,车次号为: undefined
    // 输出: undefined: 您的表单已取消,车次号为: undefined

进入全屏模式 退出全屏模式

现在创建构造函数,

    class 表单 {

        constructor() {
            this.姓名 = "Gaurav"
            this.车次号 = 0
        }

        提交() {
            console.log(this.姓名 + ": 您的表单已提交,车次号为: " + this.车次号)
        }

        取消() {
            console.log(this.姓名 + ": 您的表单已取消,车次号为: " + this.车次号)
            this.车次号 = 0
        }
        填写(给定姓名, 车次号) {
            this.姓名 = 给定姓名
            this.车次号 = 车次号
        }

    }

    let 我的表单1 = new 表单()

    let 我的表单2 = new 表单()

    // 我的表单1.填写("Gaurav", 1234)
    //
    // 我的表单2.填写("Rahul", 5678)

    我的表单1.提交()

    我的表单2.提交()

    我的表单2.取消()

    // 输出: Gaurav: 您的表单已提交,车次号为: 0
    // 输出: Gaurav: 您的表单已提交,车次号为: 0
    // 输出: Gaurav: 您的表单已取消,车次号为: 0

进入全屏模式 退出全屏模式

⭐ 构造函数的类型

  1. 无参构造函数 : 不带任何参数的构造函数。
        class 示例 {
            constructor() {
                this.属性 = "默认值";
            }
        }

进入全屏模式 退出全屏模式

  1. 带参数的构造函数 : 接受参数的构造函数。
        class 示例 {
            constructor(value) {
                this.属性 = value;
            }
        }

进入全屏模式 退出全屏模式

  1. 复制构造函数:JavaScript 没有像 C++ 或 Java 那样的内置复制构造函数。但是,你可以创建一个方法来复制对象。
        class 示例 {
            constructor(value) {
                this.属性 = value;
            }

            复制() {
                return new 示例(this.属性);
            }
        }

        const 原始 = new 示例("原始值");
        const 复制 = 原始.复制();

进入全屏模式 退出全屏模式

与 C++ 不同,JavaScript 没有析构函数。相反,JavaScript 依赖于一个高效的垃圾回收器,该垃圾回收器会自动释放内存。

▶️ 继承

类可以从另一个类派生属性和特征的能力称为继承。

⭐ 为什么?

如果你不知道什么是继承

    class 动物 {
        constructor(名字, 颜色, 年龄) {
            this.名字 = 名字
            this.颜色 = 颜色
            this.年龄 = 年龄
        }
        跑步() {
            console.log(this.名字 + '正在跑步')
        }

        大声喊叫() {

            console.log(this.名字 + '正在大声喊叫')
        }

        睡觉() {
            console.log(this.名字 + '正在睡觉')
        }
    }

    //如果你是一个新手开发者,你会这样做
    class 猴子 {
        constructor(名字, 颜色) {
            this.名字 = 名字
            this.颜色 = 颜色
        }
        跑步() {
            console.log(this.名字 + '正在跑步')
        }

        大声喊叫() {

            console.log(this.名字 + '正在大声喊叫')
        }

        睡觉() {
            console.log(this.名字 + '正在睡觉')
        }

        吃香蕉() {
            console.log(this.名字 + '正在吃香蕉')
        }
    }

    const 动物_1 = new 猴子('Simba 猴子', '棕色', 2)

    const 动物_2 = new 动物('驴', '白色', 3)

    动物_1.吃香蕉()

    动物_2.大声喊叫()

进入全屏模式 退出全屏模式

如果你知道

    //父类 - 基类
    class Animal {
        constructor(name, color, age) {
            this.name = name
            this.color = color
            this.age = age
        }
        run() {
            console.log(this.name + '正在奔跑')
        }

        shout() {
            console.log(this.name + '正在喊叫')
        }

        sleep() {
            console.log(this.name + '正在睡觉')
        }
    }

    //子类 - 派生类
    class Monkey extends Animal{
        eatBanana() {
            console.log(this.name + '正在吃香蕉')
        }
        //也可以添加新的方法
        hide() {
            console.log(this.name + '正在躲藏')
        }
    }

    const animal_1 = new Monkey('Simba monkey', 'Brown', 2)

    const animal_2 = new Animal('Donkey', 'White', 3)

    animal_1.eatBanana()
    animal_1.run()
    animal_1.hide()

    animal_2.shout()

进入全屏模式 退出全屏模式

⭐ 继承的类型

  1. 当一个类继承另一个类时,这被称为单继承。
    class Shape {
      area() {
        console.log("显示形状的面积");
      }
    }

    class Triangle extends Shape {
      area(h, b) {
        console.log((1/2) * b * h);
      }
    }

    const triangle = new Triangle();
    triangle.area(10, 5); // 输出: 25

进入全屏模式 退出全屏模式

  1. 层次继承被定义为从一个基类派生出多个类的过程。
    class Shape {
      area() {
        console.log("显示形状的面积");
      }
    }

    class Triangle extends Shape {
      area(h, b) {
        console.log((1/2) * b * h);
      }
    }

    class Circle extends Shape {
      area(r) {
        console.log(3.14 * r * r);
      }
    }

    const triangle = new Triangle();
    triangle.area(10, 5); // 输出: 25

    const circle = new Circle();
    circle.area(7); // 输出: 153.86

进入全屏模式 退出全屏模式

  1. 多级继承是指从另一个派生类派生出一个类的过程。
    class Shape {
      area() {
        console.log("显示形状的面积");
      }
    }

    class Triangle extends Shape {
      area(h, b) {
        console.log((1/2) * b * h);
      }
    }

    class EquilateralTriangle extends Triangle {
      constructor(side) {
        super();
        this.side = side;
      }

      area() {
        console.log((Math.sqrt(3) / 4) * this.side * this.side);
      }
    }

    const equilateralTriangle = new EquilateralTriangle(5);
    equilateralTriangle.area(); // 输出: 10.825317547305486

进入全屏模式 退出全屏模式

  1. 混合继承是简单继承、多重继承和层次继承的结合。JavaScript 不直接支持多重继承,但我们可以通过混入(mixins)实现类似的行为。
    class Shape {
      area() {
        console.log("显示形状的面积");
      }
    }

    class Triangle extends Shape {
      area(h, b) {
        console.log((1/2) * b * h);
      }
    }

    class Circle extends Shape {
      area(r) {
        console.log(3.14 * r * r);
      }
    }

    const mixin = (Base) => class extends Base {
      perimeter() {
        console.log("计算周长");
      }
    };

    class EquilateralTriangle extends mixin(Triangle) {
      constructor(side) {
        super();
        this.side = side;
      }

      area() {
        console.log((Math.sqrt(3) / 4) * this.side * this.side);
      }
    }

    const equilateralTriangle = new EquilateralTriangle(5);
    equilateralTriangle.area(); // 输出: 10.825317547305486
    equilateralTriangle.perimeter(); // 输出: 计算周长

进入全屏模式 退出全屏模式

▶️ 方法重写

如果在父类和子类中都定义了同一个方法,则子类中的方法会覆盖父类中的方法。

  • 通用
    class 人类 {
        constructor(名字 , 年龄 , 身材) {
            this.名字 = 名字
            this.年龄 = 年龄
            this.身材 = 身材
        }

        获取名字() {
            console.log("人类的名字是 : ", this.名字)
        }

        获取年龄() {

            console.log("人类的年龄是 :", this.年龄)
        }

        获取身材() {
            console.log("人类的身材是 :", this.身材)
        }
    }

    class 学生 extends 人类 {}

    const 学生_1 = new 学生("Subham" , 24 , "Thin")

    学生_1.获取年龄() //人类的年龄是 : 24

进入全屏模式 退出全屏模式

⭐ Super 关键字 - 类型

super 关键字用于调用父类的构造函数,以访问其属性和方法。

覆盖构造函数

    class 人类 {
        constructor(名字, 年龄, 身体类型) {
            this.名字 = 名字;
            this.年龄 = 年龄;
            this.身体类型 = 身体类型;
        }

        获取名字() {
            console.log("人类的名字是:", this.名字);
        }

        获取年龄() {
            console.log("人类的年龄是:", this.年龄);
        }

        获取身体类型() {
            console.log("人类的身体类型是:", this.身体类型);
        }
    }

    class 学生 extends 人类 {
        constructor() {
            super("Rahul", 80, "Fat");
        }
    }

    const 学生1 = new 学生();

    学生1.获取名字(); // 人类的名字是: Rahul

进入全屏模式 退出全屏模式

重写方法

    class 人类 {
        constructor(名字, 年龄, 身体类型) {
            this.名字 = 名字;
            this.年龄 = 年龄;
            this.身体类型 = 身体类型;
        }

        获取名字() {
            console.log("人类的名字是:", this.名字);
        }

        获取年龄() {
            console.log("人类的年龄是:", this.年龄);
        }

        获取身体类型() {
            console.log("人类的身体类型是:", this.身体类型);
        }
    }

    class 学生 extends 人类 {
        constructor() {
            super("Rahul", 80, "Fat");
        }

        // 使用 super 关键字在子类中重写方法
        获取年龄() {
            super.获取年龄();
            console.log("学生的年龄是:", 20);
        }
    }

    const 学生1 = new 学生();

    学生1.获取年龄(); // 人类的年龄是: 80
                      // 学生的年龄是: 20

进入全屏模式 退出全屏模式

⭐ 方法重写的关键点

  1. 相同的方法名:子类中的方法必须与父类中的方法名相同。

  2. 相同的参数:子类中的方法必须与父类中的方法具有相同的参数列表。

  3. 是-一种关系:方法重写只发生在具有 IS-A 关系(继承)的两个类之间。

  4. 访问修饰符:重写的方法可以有较少限制的访问修饰符,但不能有更多限制的访问修饰符。

  5. Super 关键字 : 您可以使用 super 关键字来调用父类中被覆盖的方法。

⭐ 额外说明

注意事项 1

    class 人类 {
        constructor() {
            console.log("人类类构造函数")
        }
        吃饭() {
            console.log("人类可以吃饭")
        }
    }
    class 学生 extends 人类 {
    }
    const 学生_1 = new 学生()
    学生_1.吃饭()

    // 人类类构造函数
    // 人类可以吃饭

进入全屏模式 退出全屏模式

如果你在子类中没有显式地定义构造函数,JavaScript 会自动为你创建一个构造函数,并使用 super() 调用父类的构造函数。
就像这样

    class 人类 {
        constructor() {
            console.log("人类类构造函数")
        }
        吃饭() {
            console.log("人类可以吃饭")
        }
    }
    class 学生 extends 人类 {
        constructor(...arg) {
            super(...arg);
        }
    }
    const 学生_1 = new 学生()
    学生_1.吃饭()

进入全屏模式 退出全屏模式

注意 2

    class 人类 {
        constructor() {
            console.log("人类类构造函数")
        }
        吃饭() {
            console.log("人类可以吃饭")
        }
    }
    class 学生 extends 人类 {
        constructor() {
            console.log("这是学生类构造函数")
            super() // 必须在派生类中调用 super 构造函数
        }
    }
    const 学生_1 = new 学生()
    学生_1.吃饭()

    // console.log("这是学生类构造函数")
    //ReferenceError: 必须在派生类中调用 super 构造函数,才能访问 'this' 或从派生构造函数返回

进入全屏模式 退出全屏模式

你必须像这样使用 super 关键字

    class 人类 {
        constructor() {
            console.log("人类类构造函数")
        }
        吃饭() {
            console.log("人类可以吃饭")
        }
    }
    class 学生 extends 人类 {
        constructor() {
            super()
            console.log("这是学生类构造函数")
        }
    }
    const 学生_1 = new 学生()
    学生_1.吃饭()

进入全屏模式 退出全屏模式

注意 3

    class 人类 {
        constructor(名字) {
            console.log("人类类构造函数" , 名字)
            this.name = 名字
        }
        吃饭() {
            console.log("人类可以吃饭")
        }
    }
    class 学生 extends 人类 {
        constructor(名字) {
            this.name = 名字 //不允许
            super()
            console.log("学生类构造函数" , 名字)

        }
    }
    const 学生_1 = new 学生("subham")
    学生_1.吃饭()

    // this.name = 名字
    // 参考错误:派生类中必须在访问 'this' 或从派生构造函数返回之前调用超类构造函数

进入全屏模式 退出全屏模式

在 super 关键字之后,你可以使用 this

    class 人类 {
        constructor(name) {
            console.log("人类类构造函数" , name)
            this.name = name
        }
        吃饭() {
            console.log("人类可以吃饭")
        }
    }
    class 学生 extends 人类 {
        constructor(name) {
            super()
            this.name = name
            console.log("学生类构造函数" , name)

        }
    }
    const 学生_1 = new 学生("subham")
    学生_1.吃饭()

    // 人类类构造函数 undefined
    // 学生类构造函数 subham
    // 人类可以吃饭

进入全屏模式 退出全屏模式

▶️ 方法重载

在一个类中有两个或多个名称相同但参数不同的方法(或函数)

⭐ 我们能在JavaScript中重载函数吗?

在 JavaScript 中,并不像其他一些语言(如 Java)那样原生支持方法重载。这意味着你不能在同一个类中定义多个名称相同但参数不同的方法。然而,你可以通过在单个方法中检查参数的数量和类型等技术来实现类似的功能。

你不能在JS中这样做

    class 计算器 {
        加法(a, b) {
            return a + b;
        }

        加法(a, b, c) {
            return a + b + c;
        }
    }

    const calc = new 计算器();
    console.log(calc.加法(1, 2)); // 这将抛出错误,因为第一个加法方法被覆盖了

进入全屏模式 退出全屏模式

如果你愿意,你可以通过这种方式实现

    class 计算器 {
        add(...args) {
            if (args.length === 2) {
                return args[0] + args[1];
            } else if (args.length === 3) {
                return args[0] + args[1] + args[2];
            } else {
                throw new Error("参数数量无效");
            }
        }
    }

    const calc = new 计算器();

    console.log(calc.add(1, 2)); // 输出: 3
    console.log(calc.add(1, 2, 3)); // 输出: 6

进入全屏模式 退出全屏模式

▶️ 访问修饰符

访问修饰符是一个关键字,用于设置类成员的可访问性。

⭐ 访问修饰符的类型

  1. Public : 声明为 public 的成员可以从任何其他类中访问。
  2. Protected : 声明为 protected 的成员可以在同一类中访问,并且可以通过派生类实例访问。
  3. Private : 声明为 private 的成员只能在同一类中访问。

⭐ 可访问性表格

修改符 父类 子类 外部类
Public ✔️ ✔️ ✔️
Protected ✔️ ✔️
Private ✔️

⭐ 示例

1. 公有成员

公共成员可以从任何地方访问。

    class 父类 {
        publicProperty = "我是公共属性";

        publicMethod() {
            return "这是一个公共方法";
        }
    }

    class 子类 extends 父类 {
        使用父类公共方法() {
            console.log(this.publicProperty);
            console.log(this.publicMethod());
        }
    }

    const 父类实例 = new 父类();
    const 子类实例 = new 子类();

    console.log(父类实例.publicProperty);  // 输出: 我是公共属性
    console.log(父类实例.publicMethod());  // 输出: 这是一个公共方法
    子类实例.使用父类公共方法();
    // 输出: 
    // 我是公共属性
    // 这是一个公共方法

进入全屏模式 退出全屏模式

在这个例子中,publicPropertypublicMethod 可以从以下位置访问:

  • 在父类内部
  • 在子类内部
  • 在任何类外部

2. 受保护的成员(模拟)

在 JavaScript 中,我们约定使用下划线前缀来表示受保护的成员。它们在技术上仍然是公共的,但开发者们同意不在类或其子类之外直接访问它们。

    class Parent {
        _protectedProperty = "我是受保护的属性";

        _protectedMethod() {
            return "这是一个受保护的方法";
        }
    }

    class Child extends Parent {
        useParentProtected() {
            console.log(this._protectedProperty);
            console.log(this._protectedMethod());
        }
    }

    const parent = new Parent();
    const child = new Child();

    child.useParentProtected();
    // 输出:
    // 我是受保护的属性
    // 这是一个受保护的方法

    // 这些代码可以运行,但违反了约定:
    console.log(parent._protectedProperty);
    console.log(parent._protectedMethod());

进入全屏模式 退出全屏模式

在这个场景中:

  • _protectedProperty_protectedMethod 在父类中可以访问
  • 它们在子类中也可以访问(继承)
  • 它们在技术上也可以在外部访问,但这违反了约定

3. 私有成员

私有成员确实是私有的,只能在定义它们的类内部访问。

    class 父类 {
        #私有属性 = "我是私有的";

        #私有方法() {
            return "这是一个私有方法";
        }

        使用私有() {
            console.log(this.#私有属性);
            console.log(this.#私有方法());
        }
    }

    class 子类 extends 父类 {
        尝试使用父类私有() {
            // 解除注释会报错:
            // console.log(this.#私有属性);
            // console.log(this.#私有方法());
        }
    }

    const 父类实例 = new 父类();
    const 子类实例 = new 子类();

    父类实例.使用私有();
    // 输出:
    // 我是私有的
    // 这是一个私有方法

    // 解除注释会报错:
    // console.log(父类实例.#私有属性);
    // console.log(父类实例.#私有方法());
    // 子类实例.尝试使用父类私有();

进入全屏模式 退出全屏模式

在这种情况下:

  • #privateProperty#privateMethod 只能在 Parent 类内部访问
  • 即便 Child 继承了 Parent,也无法在 Child 中访问它们
  • 它们在类外部完全不可访问

主要收获

  1. 公有成员(默认)可以在任何地方访问。
  2. 受保护的成员(使用 _ 命名约定)可以在类及其子类中访问,但不应在类外部访问(尽管从技术上讲可以访问)。
  3. 私有成员(使用 #)只能在定义该成员的类中访问,不能在子类或类外部访问。
  4. 使用受保护成员时,它们在可访问性方面表现得像公有成员,但开发者同意将其视为受保护的成员。
  5. 真正的隐私和封装只能通过使用 # 语法的私有成员实现。
▶️ 静态

static 关键字定义了类的静态方法或静态字段。

静态方法是属于类本身的方法,而不是属于类的任何特定实例。

    class 动物 {
        constructor(name) {
            this.name = 动物.capitalize(name);
        }

        static capitalize(name) {
            return name.charAt(0).toUpperCase() + name.slice(1);
        }

        行走() {
            console.log(`动物 ${this.name} 正在行走`);
        }
    }

    const 动物实例 = new 动物("狮子");
    动物实例.行走(); // 输出: 动物 Lion 正在行走

    console.log(动物.capitalize("大象")); // 输出: Elephant

进入全屏模式 退出全屏模式

关键点:

  1. capitalize 方法使用 static 关键字声明为静态方法。
  2. 它是通过类 (Animal.capitalize) 调用的,而不是通过实例调用。
  3. 它可以在构造函数或其他方法中通过类名来使用。

⭐ 继承和静态方法

静态方法会被子类继承:

    class Human extends Animal {
        static greet() {
            console.log("你好!");
        }
    }

    const human = new Human("john");
    human.walk(); // 输出:动物 John 正在行走

    console.log(Human.capitalize("sarah")); // 输出:Sarah
    Human.greet(); // 输出:你好!

进入全屏模式 退出全屏模式

注意:

  1. Human 类继承了 Animal 类中的静态 capitalize 方法。
  2. Human 类还可以定义自己的静态方法,例如 greet

⭐ 从非静态方法中调用静态方法

你可以从非静态方法中调用静态方法,但需要使用类名:

    class 计算器 {
        static 加法(a, b) {
            return a + b;
        }

        乘法(a, b) {
            // 在非静态方法中使用静态方法
            return 计算器.加法(a, 0) * b;
        }
    }

    const calc = new 计算器();
    console.log(calc.乘法(3, 4)); // 输出: 12
    console.log(计算器.加法(5, 6)); // 输出: 11

进入全屏模式 退出全屏模式

⭐ 静态方法与实例方法的区别

这里做一个对比来说明区别:

    class MyClass {
        static 静态方法() {
            return "我是静态方法";
        }

        实例方法() {
            return "我是实例方法";
        }
    }

    console.log(MyClass.静态方法()); // 输出: 我是静态方法

    const obj = new MyClass();
    console.log(obj.实例方法()); // 输出: 我是实例方法

    // 这会抛出错误:
    // console.log(MyClass.实例方法());

    // 这也会抛出错误:
    // console.log(obj.静态方法());

进入全屏模式 退出全屏模式

⭐ 静态方法的使用场景

  1. 工具函数 : 不需要对象状态的方法。
  2. 工厂方法 : 创建具有特殊属性的实例。
  3. 缓存或固定配置 : 存储所有实例共享的数据。

工厂方法示例:

    class User {
        constructor(name, role) {
            this.name = name;
            this.role = role;
        }

        static createAdmin(name) {
            return new User(name, "admin");
        }
    }

    const admin = User.createAdmin("Alice");
    console.log(admin.role); // 输出: admin

进入全屏模式 退出全屏模式

⭐ 关键要点

  1. 静态方法是在类上定义的,而不是在实例上定义的。
  2. 使用类名调用它们:ClassName.methodName()
  3. 它们可以被子类继承。
  4. 它们不能直接访问实例属性或方法。
  5. 它们对于工具函数、工厂方法和管理类级别数据很有用。
  6. 你不能在实例上调用静态方法,也不能在类上调用实例方法。
▶️ 获取器 & 设置器

获取器和设置器分别是允许你获取和设置对象值的函数。

    //getter setter
    class 人类 {
        constructor(name, age) {
            this._name = name;
        }
        get getName() {
            return this._name;
        }
        set setName(name) {
            this._name = name;
        }
    }

    const 人 = new 人类("", 0);
    人.setName = "Raj";
    人.setAge = 25;

    console.log(人.getName);
    console.log(人.getAge);

    //Raj
    //25

进入全屏模式 退出全屏模式

▶️ instanceOf 操作符

检查一个对象是否是某个类、子类或接口的实例

    // getter 和 setter
    class 人类 {
        constructor(name, age) {
            this.name = name;
            this.age = age;
        }
        get getName() {
            return this.name;
        }
        set setName(name) {
            this.name = name;
        }
        get getAge() {
            return this.age;
        }
        set setAge(age) {
            this.age = age;
        }
    }

    const 人 = new 人类("", 0);
    人.setName = "Raj";
    人.setAge = 25;

    console.log(人.getName);
    console.log(人.getAge);

    const 人1 = "Subham"

    console.log(人 instanceof 人类)//true
    console.log(人1 instanceof 人类)//false

进入全屏模式 退出全屏模式

它也为子类返回 true

    // getter 和 setter
    class human {
        constructor(name, age) {
            this.name = name;
            this.age = age;
        }
        get getName() {
            return this.name;
        }
        set setName(name) {
            this.name = name;
        }
        get getAge() {
            return this.age;
        }
        set setAge(age) {
            this.age = age;
        }
    }

    class Coder extends human {
        constructor(name, age, language) {
            super(name, age);
            this.language = language;
        }
    }

    const person = new human("", 0);
    const subham = new Coder("subham", 22, "java");
    person.setName = "Raj";
    person.setAge = 25;

    console.log(person instanceof human)
    console.log(subham instanceof human)

进入全屏模式 退出全屏模式

▶️ 封装

封装是一种限制直接访问对象某些组件的方式。

    class 银行账户 {
        #余额; // 私有字段

        constructor(初始余额) {
            this.#余额 = 初始余额;
        }

        存款(金额) {
            if (金额 > 0) {
                this.#余额 += 金额;
            }
        }

        获取余额() {
            return this.#余额;
        }
    }

    const 账户 = new 银行账户(1000);
    账户.存款(500);
    console.log(账户.获取余额()); // 1500
    // console.log(账户.#余额); // 语法错误:私有字段

进入全屏模式 退出全屏模式

    // 封装
    const user = {
        firstName: "John",
        lastName: "Doe",
        age: 25,
        getAgeYear: function() {
            return new Date().getFullYear() - this.age;
        }
    }
    console.log(user.getAgeYear());

进入全屏模式 退出全屏模式

▶️ 多态

多态意味着“多种形态”,当多个类通过继承相互关联时,就会发生多态。

    // 父类
    class Animal {
      makeSound() {
        console.log("动物发出声音");
      }
    }

    // 子类
    class Dog extends Animal {
      makeSound() {
        console.log("狗在叫");
      }
    }

    class Cat extends Animal {
      makeSound() {
        console.log("猫在叫");
      }
    }

    // 用于演示多态的函数
    function animalSound(animal) {
      animal.makeSound();
    }

    // 使用示例
    const animal = new Animal();
    const dog = new Dog();
    const cat = new Cat();

    animalSound(animal); // 输出: 动物发出声音
    animalSound(dog); // 输出: 狗在叫
    animalSound(cat); // 输出: 猫在叫

进入全屏模式 退出全屏模式

▶️ 抽象化

抽象是指隐藏复杂的实现细节,只展示对象必要的特性。

    // 抽象:隐藏复杂的实现细节,只展示对象必要的特性。

    // 抽象类
    class Vehicle {
        constructor(brand) {
            this.brand = brand;
        }

        // 抽象方法
        start() {
            throw new Error("方法 'start()' 必须被实现。");
        }

        getBrand() {
            return this.brand;
        }
    }

    // 具体类
    class Car extends Vehicle {
        start() {
            return `${this.brand} 车正在启动...`;
        }
    }

    // 使用
    const myCar = new Car("丰田");
    console.log(myCar.getBrand()); // 输出:丰田
    console.log(myCar.start());    // 输出:丰田车正在启动...

进入全屏模式 退出全屏模式

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消