• ES6-类--【面向对象编程思想】


    面向对象编程

    面向对象重点不就是对象吗?我们接下来会谈论到什么是对象,说到对象,在前言部分我想说说什么是抽象类?类的定义是什么?

    JavaScript 传统方式是通过构造函数来定义生成对象的,所以说 function 既是对象,对象既是 function ,ECMAScript2015 之前是没有 class 概念的,在ECMAScript 2015 之后的ES6中为我们提供了更加接近传统语法的写法,类似于JAVA,PHP等语言,class 作为对象的模板,来根据业务需求抽象出一个公共类供对象使用,不过不要误解了我的说法,类是类,class 只是一个定义类的关键字,我们可以使用类实现很多功能,比如单例模式,访问器属性,静态方法,extends继承父类等等等等…

    两大编程思想

    • 面向过程
    • 面向对象

    面向过程(POP)

    面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的一次调用就可以了(按照分析好的步骤来依次执行解决问题)

    • 比如我要完成学习 ES6 的任务,那么我们怎么使用面向过程的思想来完成呢?
    1. 打开书
    2. 看书
    3. 实践
    4. 合上书
    • 一步一步进行,依次进行,这就是面向过程

    面向对象(OOP)

    面向对象是把事务分解成一个个对象,然后对象之间分工合作

    • 同样我要完成学习 ES6 的任务,那么我们怎么使用面向对象的思想来完成呢?
    1. 我们有一个书的对象
    • 书的属性:可以打开
    • 属的属性:可以合上
    1. 我也是一个对象
    • 我的属性:看书
    • 我的属性:实践
    1. 使用我的和大象的功能来完成任务

    与面向过程不同的是面向对象是以对象来划分问题,而不是步骤,每一个对象都是功能中心,具有明确分工,并且相对灵活,代码可复用、容易进行维护以及开发

    • 特性
    1. 封装性
    2. 继承性
    3. 多态性

    我们怎么去选择使用?

    1. 面向过程:
    • 优点:

    性能相对于面向对象较高

    • 缺点:

    相对于面向对象编程不易维护,不易复用,不易于扩展

    1. 面向对象
    • 优点:

    易于维护,易于复用,易于扩展,高内聚低耦合,更灵活

    • 缺点:

    性能相对于面向过程来说较低

    进一步理解面向对象编程思想

    面向对象其实本质就是描述我们现实生活中的事物,这个事物可以是具体的,也可以是抽象的

    我们可以抽象一个对象,这个对象必然有一些属性,比如我们可以想象一下女娲视角:

    女娲造人的时候如果一个一个的捏,细心的捏,那么这是很累的,很庞大工程,那么人都有生命特性,有心脏,有肺部等等,为什么不把这些功能封装起来作为一个抽象模板,身高面貌由女娲捏,而这些复杂的生命功能就可以直接使用抽象模板了?因为这是人的共有类啊

    所以这就引出了面向对象的思维:

    • 抽象对象共有属性和行为封装为一个类
    • 对类进行实例化,获取类的对象(每一个实例化的对象都具有公有类的功能和方法)

    😀COOL!!


    类和对象

    对象

    初学编程对于面向对象可能会有一点误解,不如我们基于 JavaScript 的 Object 数据类型来理解对象

    ECMAScript 中的对象其实就是一组数据和功能的集合,我们可以通过 new 操作符和对象类型的名称来创建一个对象

    const obj = new Object()
    
    • 1

    ECMAScript 中的 Object 其实也是派生其他对象的基类,Object 类型的所有属性和方法在派生的对象上同样存在

    那么再回到我们的面向对象谈论中,什么是对象?

    在 JavaScript 中对象是一组无序相关属性和方法的集合,所有事物都是对象,比如一个字面量,一个数组,一个函数等等都是一个对象

    而对象是由属性和方法组成的:

    • 属性:描述了事物的特征
    • 方法:描述了事物的行为

    或者你也可以基于 Web 开发来理解,CSS 可以称为这个网页的特征描述,JavaScript 可以称为这个网页的行为执行,而 HTML 则是这个网页的一个公共类,这个网页则是一个对象,基于 HTML 模板我们可以更改 CSS 和 JavaScript 来创建出一个新的网页对象 🤓

    类(class)

    OK,我想现在是时候回到代码中了,我们可以使用class关键字来声明一个类,之后我们将以这个类进行实例化创建对象

    我们之前也提到了,类抽象了对象的公共部分,它其实是泛指的某一大类,对象是特指的一个事物对象,是通过实例化得到的一个具体对象

    我们怎么创建一个类

    class Name {
    	// class body
    }
    
    // 创建实例
    const username = new Name()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    怎么使用类添加共有属性

    • 传递参数使用 constructor 构造函数,它用于 传递参数,返回实例对象 ,通过 new 命令生成对象实例时 自动调用 该对象,如果没有显示定义,类内部会 自动帮我们创建 一个 constructor() 构造函数
    class User {
    	// 存放类的共有属性
    	constructor(...user) {
    		this.user = [...user].flat()
    	}
    }
    
    const user1 = new User("Anna", 18, ["打代码", "玩游戏"])
    const user2 = new User("Bun", 20, ["打代码", "写文章"])
    const user3 = new User("Jons", 22, ["打代码", "跑步"])
    
    console.log(user1.user)
    console.log(user2.user)
    console.log(user3.user)
    
    // [ 'Anna', 18, '打代码', '玩游戏' ]
    // [ 'Bun', 20, '打代码', '写文章' ]
    // [ 'Jons', 22, '打代码', '跑步' ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    怎么使用类添加共有方法

    我们直接把方法写到类里面就可以,相当于类的共享方法

    class User {
    	constructor(...user) {
    		this.user = [...user].flat()
    	}
    	testMethod(timeOut) {
    		setTimeout(() => {
    			console.log(`每个人有${this.user.length}条信息`)
    		}, timeOut)
    	}
    }
    
    const user1 = new User("Anna", 18, ["打代码", "玩游戏"])
    const user2 = new User("Bun", 20, ["打代码", "写文章"])
    const user3 = new User("Jons", 22, ["打代码", "跑步"])
    
    user1.testMethod(1000)
    user2.testMethod(1000)
    user3.testMethod(1000)
    
    // 4
    // 4
    // 4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    所以我们刚才了解的是类的 封装性 ,将属性于方法放在一个类中进行统一调用管理,非常的方便 😀

    当然,我们也可以使用原型挂载:

    class Test {
      constructor() {
        // 属性
        (this.name = 'Brave-AirPig'), (this.age = 22);
      }
    }
    
    // 原型挂载方法
    
    Test.prototype.run = () => console.log('我跑起来了');
    
    // 实例化
    
    const test = new Test();
    
    console.log(test.name, test.age);
    test.run();
    
    // Brave-AirPig 22
    // 我已经跑起来了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    类的继承性

    字面意思:子承父类呗,对于熟练使用 CSS 的我们,对于继承这个思想应该非常的熟悉了,关于什么是继承我就不做过多的阐述了

    继承关键词: extends

    class User {
    	constructor() {
    		this.username = "Anna"
    	}
    	testMethod() {
    		console.log(this.username)
    	}
    }
    
    class UserEctype extends User {}
    
    const userEctype = new UserEctype()
    
    userEctype.testMethod()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    访问调用父类上的构造函数

    关键字:super

    用于访问和调用对象父类上的函数,可以调用父类的构造函数,也可以调用父类的普通函数

    class User {
    	constructor(name) {
    		this.username = name
    	}
    	testMethod() {
    		console.log(this.username)
    	}
    }
    
    class UserEctype extends User {
    	constructor(name) {
    		super(name) // 调用父类中的构造函数
    	}
    }
    
    const userEctype = new UserEctype("Anna")
    
    userEctype.testMethod()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    如果我们不写 super() 的话,子类传递的实参是不能被父类的构造函数所获取并执行的

    访问调用父类上的构造函数

    依然是我们的 super 关键字:

    class User {
    	testMethod() {
    		console.log("Anna")
    	}
    }
    
    class UserEctype extends User {
    	testMethod() {
    		super.testMethod() // 调用父类中的构造函数
    	}
    }
    
    const userEctype = new UserEctype()
    
    userEctype.testMethod()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    所以我们得出了一个结论:在继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的;如果子类没有该方法,就去父类中查找有没有这个方法,如果有的话,就执行父类的这个方法;使用 super 关键字可以修改优先级,先去查找父类中的方法,子类想在构造函数中使用 super 调用父类构造函数,需要放到子类构造函数的顶部

    this 的指向问题:

    • constructor 构造函数中的 this 指向当前创建的实例对象
    • 方法中的 this 指向方法调用者,其实这样说有点不严谨了,函数中的 this 指向我们都应该遵循 指向调用者这样一个思想

    简单总结与扩展

    // ES5怎么写
    function Test() {
      this.name = 'Brave-AirPig';
      this.age = 22;
    }
    
    Test.prototype.run = () => console.log('我跑起来了');
    
    const test = new Test();
    
    console.log(test.name, test.age);
    test.run();
    
    // Brave-AirPig 22
    // 我跑起来了
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    // ES6写法
    class Test {
      constructor() {
        // 属性
        this.name = 'Brave-AirPig';
        this.age = 22;
      }
      // 方法
      run() {
        console.log('我已经跑起来了');
      }
    }
    
    // 实例化
    
    const test = new Test();
    
    console.log(test.name, test.age);
    test.run();
    
    // Brave-AirPig 22
    // 我已经跑起来了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    表达式形式定义类

    const Test = class {
      constructor() {
        this.name = 'Brave-AirPig';
        this.age = 22;
      }
    };
    const test = new Test();
    
    console.log(test.name, test.age);
    
    // Brave-AirPig 22
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    单例模式

    const Test = new (class {
      constructor() {
        this.name = 'Brave-AirPig';
        this.age = 22;
      }
    })();
    
    console.log(Test.name, Test.age);
    
    // Brave-AirPig 22
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    访问器属性

    访问器属性我们通过 set 来监听属性值的变化来设置对应值,我们可以做一些操作

    set那就必须存在get,否则的话set就没有用武之地了,通过 get 来获取值

    const Test = new (class {
      constructor(name, age) {
        this.name = name;
        this.age = age;
        this.message = '';
      }
      set age(value) {
        if (value >= 18) this.message = '成年';
        else this.message = '未成年';
      }
    
      get age() {
        return this.message;
      }
    })('Brave-AirPig', 22);
    
    // 修改值被set监听到,get来进行获取
    Test.age = 17;
    console.log(Test.message);
    
    Test.age = 18;
    console.log(Test.message);
    
    // 未成年
    // 成年
    
    • 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

    静态方法

    class Test {
      constructor(name, age) {
        this.name = name;
        this.age = age;
      }
      static run() {
        console.log('我跑起来了');
      }
    }
    
    Test.run();
    // 我跑起来了
    // 我们发现静态方法是不需要实例化就可以执行方法
    // 并且该类的实例无法直接执行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    继承类

    派生类继承自基类,也就是说子类继承于父类

    class TestFather {
      constructor(name, age) {
        this.name = name;
        this.age = age;
      }
      run() {
        console.log('我跑起来了');
      }
    }
    
    class TestSon extends TestFather {
      constructor(name, age) {
        // 派生类和基类有具有constructor构造函数,产生了冲突,我们必须使用super调用基类的构造函数
        // 当然你可以选择派生类不写构造函数,这样的话,也不会发生错误,JavaScript自动添加了构造函数以及super
        super(name, age);
      }
      // 覆盖基类方法
      run() {
        console.log(`${this.name}跑起来了`);
        // 我基类派生类的方法都要 -- super 继承
        super.run();
      }
    }
    
    const testSon = new TestSon('Brave-AirPig', 22);
    
    console.log(testSon.name, testSon.age);
    
    testSon.run();
    
    // Brave-AirPig 22
    // Brave-AirPig跑起来了
    // 我跑起来了
    
    • 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
  • 相关阅读:
    java.util.IllegalFormatConversionException: f != java.lang.String 问题解决!
    常用类(总结)
    优思学院|为何CPK要大于1.33?
    获取1688店铺详情 API接口(获取卖家昵称、店铺类型、公司信息、店铺标题、店铺主页)
    基于 Python 的音乐流派分类
    PHP 程序员为什么依然是外包公司的香饽饽?
    C# async / await 用法
    工商银行卡安全码怎么看
    读书笔记:《权力之治:人工智能时代的算法规制》
    【线性代数】P8 逆矩阵&矩阵方程以及逆矩阵的性质
  • 原文地址:https://blog.csdn.net/weixin_63836026/article/details/126266855