JS进阶——类和对象

198 阅读5分钟

1.面向对象编程介绍

面向过程:分析出解决问题所需要的步骤,再用函数分步解决。

面向对象:把事务分解成对象,再由对象之间分工合作。

面向对象是以对象功能来划分问题,而不是步骤。

每一个对象都是功能中心,具有明确分工。灵活、可复用、易于维护。

面向对象的特性:

  • 封装性
  • 继承性
  • 多态性

2.ES6中的类和对象

对象:可以用来描述现实事物,事物又可以分为具体的事物和抽象的事物。

手机(泛指,抽象);我手里的这一部手机(具体化,实例化,具体)。

面向对象的思维特点:

  1. 抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板)
  2. 对类进行实例化,获取类的对象

面向对象编程就需要考虑:有哪些对象,按照面向对象的思维特点,不断创建对象,使用对象,指挥对象做事情。

2.1 对象

现实生活中,对象是一个具体的事物,看得见摸得着的实物。

JavaScript:对象是一组无序的相关属性和方法的集合,所有的事物都是对象。例如字符串、数值、数组、函数等。

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

  • 属性:事物的特征(常用名词)
  • 方法:事物的行为(常用动词)

2.2 类 class

ES6新特性,新增概念,可以使用class关键字声明一个类,之后用这个类实例化对象。

  • 类抽象了对象的公共部分,泛指一大类(class)
  • 对象特指某一个,通过类实例化一个具体的对象

2.3 创建类

image.png

类必须使用new实例化对象

image.png

image.png

2.4 类 的constructor 构造函数

constructor() 方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法。如果没有显示类内部会自动给我们创建一个constructor().

        // 1. 创建一个类 class
        class Star {
            // 类的构造函数 constructor()
            constructor(uname, age) {
                this.uname = uname;
                this.age = age;
            }
        }

        // 2. 实例化对象
        var ldh = new Star('刘德华', 18);
        var zxy = new Star('张学友', 22);
        console.log(ldh);
        console.log(zxy);

image.png

注意点:

(1)通过类class 关键字创建类,类名习惯性首字母大写;

(2)类里面有个constructor函数,这个函数可以传递参数,也可以返回实例对象;

(3)constructor函数,只要 new生成实例的时候,就会自动调用,如果我们不写明,类也会自动生成这个函数;

(4)生成实例new 不能省略;

(5)创建类 类名后面不加小括号,生成实例 类名后加小括号,构造函数不加function.

2.5 类添加方法

        // 1. 创建一个类 class
        class Star {
            // 类的构造函数 constructor()
            constructor(uname, age) {
                this.uname = uname;
                this.age = age;
            }
            // 类中添加方法
            sing(song) {
                // console.log('会唱歌');
                console.log(this.uname + '唱了' + song);
            }
        }

        // 2. 实例化对象
        var ldh = new Star('刘德华', 18);
        var zxy = new Star('张学友', 22);
        console.log(ldh);
        console.log(zxy);
        // 调用
        ldh.sing('冰雨');

3.类的继承

3.1 继承

子类可以继承父类的属性和方法。

        class Father {
            constructor() {

            }
            money() {
                console.log(1000);
            }
        }
        // 继承extends
        class Son extends Father {

        }
        var son = new Son();
        // 类Son 里没有方法,但是还是可以调用父类的方法 
        son.money();  // 1000

3.2 super

super() 关键字可以用来调用父类中的函数,包括构造函数和普通函数。

1. 调用构造函数
        class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            sum() {
                console.log(this.x + this.y);
            }

        }
        class Son extends Father {
            constructor(x, y) {
                // super关键字 可以调用父类中的构造函数
                super(x, y);
                // 这里不用this.x是由于指向问题,没办法传递参数进行函数调用(计算)
            }

        }
        var son = new Son(1, 2);
        son.sum();  // 3
2. 调用普通函数

(1)不继承就调用自身

        // 1.子类(这里指Son)不继承,调用say()方法直接调用自己内部的函数
        class Father {
            say() {
                return '这是父亲';
            }
        }
        class Son {
            say() {
                console.log('这是儿子');
            }
        }
        var son = new Son();
        son.say(); // 这是儿子

(2)继承但由于自己也有这个方法,也会调用自己内部的

        // 2. 继承但由于自己也有这个方法,也会调用自己内部的
        class Father {
            say() {
                return '这是父亲';
            }
        }
        class Son extends Father {
            say() {
                console.log('这是儿子');
            }
        }
        var son = new Son();
        son.say(); // 这是儿子

(3)子类自己内部没有,会调用父类里的

        // 3. 子类自己内部没有,会调用父类里的
        class Father {
            say() {
                //     return 'test'; 
                console.log('这是父亲');
            }
        }
        class Son extends Father {

        }
        var son = new Son();
        son.say(); // 这是父亲

(4)super关键字调用父类中的普通函数

        // 4. super关键字调用父类中的普通函数
        class Father {
            say() {
                return '这是父亲';
                // console.log('这是父亲'); // 会 我是父亲 undefined
            }
        }
        class Son extends Father {
            say() {
                console.log(super.say());
            }

        }
        var son = new Son();
        son.say(); // 这是父亲 
3. 子类继承父类方法并扩展自身方法
        // 父类有加法方法
        class Father {
            constructor(x, y) {
                this.x = x;
                this.y = y;
            }
            sum() {
                console.log(this.x + this.y);
            }
        }
        class Son extends Father {
            constructor(x, y) {
                // 1. 利用 super 调用父类中的sum方法
                super(x, y);
                // 而且必须在子类的this之前调用
                this.x = x;
                this.y = y;
            }
            // 2. 扩展自身减法
            subtract() {
                console.log(this.x - this.y);
            }

        }
        var son = new Son(3, 1);
        son.sum();
        son.subtract();

        // 注意:super在子类构造函数中使用时,必须在this之前调用
        // (即必须先调用父类构造函数,再使用子类构造函数)

4.类和对象的三个注意点

  1. ES6中的类没有变量提升,所以要先定义类,再实例化对象。

  2. 类里面共有的属性和方法一定要加this使用。

  3. 一定要注意类里面的this指向问题:

    constructor里面的this指向实例对象,方法里的this指向这个方法的调用者。

        var that;
        class Star {
            constructor(uname, age) {
                // construcutor 中的this 指向 创建的实例
                that = this;
                this.uname = uname;
                this.age = age;
                // this.sing();
                this.btn = document.querySelector('button'); // 选择button标签
                this.btn.onclick = this.sing;  // 点击后再调用,所以不加()

            }
            sing() {
                // this 指向 btn,因为btn调用了sing()
                console.log(that.uname);
                // that里面存储的是constructor里的this
                // 在constructor外部声明一个全局变量,把this存到里面;在这里调用that,相当于调用了constructor里面的this
            }
            dance() {
                // 这个函数中,ldh调用了,所以指向ldh
                console.log(this);
            }
        }
        var ldh = new Star('刘德华');
        ldh.dance();