乐趣区

ES6中的类对象继承

1.1 对象

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

对象是由属性和方法组成的:
  属性:事物的特征,在对象中用属性来表示(常用名词)
  方法:事物的行为,在对象中用方法来表示(常用动词)

1.2 类 class

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

面向对象的思维特点:
  1、抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板)
  2、对类进行实例化,获取类的对象

1.3 创建类

语法:

class name {// class body}

创建实例:

var xx = new name();

注意:类必须使用 new 实例化对象

1.4 类 constructor 构造函数

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

注意:
  1、通过 class 关键字创建类,类名习惯性定义首字母大写
  2、类里面有个 constructor 构造函数,可以接收传递过来的参数,同时返回实例对象
  3、constructor 函数只要 new 生成实例时,就会自动调用这个函数,如果不写这个函数,类也会自动生成这个函数
  4、生成实例 new 不能省略
  5、语法规范:创建类 类名后面没有小括号,生成实例 类名后面加小括号,构造函数不需要加 function

1.5 类添加方法

语法:

class Person {constructor(name, age) {
        this.name = name;
        this.age = age
    }
    say() {console.log('hello,' + this.name)
    }
}

2.1 继承

现实中的继承:子承父业,比如我们都继承了父亲的姓。
程序中的继承:子类可以继承父类的一些属性和方法。

ES6 之前没有提供 extends 继承,我们可以通过 构造函数 + 原型对象 模拟实现继承,被称为组合继承。示例:

function Father(name, age) {
    this.name = name;
    this.age = age
}
Father.prototype.say = function() {console.log('I have lots of experience.')
}

类的继承语法:

class Father{// 父类}
class Son extends Father {// 子类继承父类}

继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的,如果子类里面没有,就去查找父类有没有这个方法,如果有就执行父类的这个方法(就近原则)

实例:

class Father {say() {return 'this is father'}
}
class Son extends Father { // 这样子类就继承了父类的属性和方法
    say() {
        return 'this is son'
        // return super.say(); //super 调用父类的方法,则输出 'this is father'}
}
let son = new Son();
son.say(); // 'this is son'

2.2 super 关键字

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

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(x, y); // 调用了父类中的构造函数 constructor
        this.z = z; // 定义子类独有的属性
    }
}
​
let son = new Son(1, 2); // 实例化了子类
son.sum();

注意: 子类在构造函数中使用 super, 必须放到 this 前面 (必须先调用父类的构造方法, 再使用子类构造方法),否则新建实例时会报错(this is not defined)。

因为子类没有自己的 this 对象,而是继承父类的 this 对象。如果不调用 super 方法,子类就得不到 this 对象。

注意:
  1、在 ES6 中没有变量提升,所以必须先定义类,才能通过类实例化对象
  2、类里面的共有属性和方法一定要加 this 使用
  3、类里面的 this 指向问题
  4、constructor 里面的 this 指向实例对象,方法里面的 this 指向这个方法的调用者

let that;
let _that;
class Star {constructor(uname, age) {console.log(this); // this 指向创建的实例对象 Star{}
        that = this;
        
        this.uname = uname;
        this.age = age;
    }
    sing() {
        // 这个里面的 this 指向的是实例对象 ldh, 因为 ldh 调用了这个函数
        _that = this;
        console.log(_that); // 实例对象,ldh
    }
}
var ldh = new Star('刘德华');
console.log(that === ldh);  //true
ldh.sing();
console.log(_that === ldh);  //true

2.3 call()

call() 方法调用一个对象。简单理解即调用这个函数, 并且修改函数运行时的 this 指向。

fun.call(thisArg, arg1, arg2, ...)
  • thisArg:当前调用函数 this 的指向对象
  • arg1, arg2:传递的其他参数
  • 返回值就是函数的返回值,因为它就是调用函数
  • 因此当我们想改变 this 指向,同时想调用这个函数的时候,可以使用 call,比如继承

2.4 借用构造函数继承父类型属性

核心原理:通过 call() 把父类型的 this 指向子类型的 this,这样就可以实现子类型继承父类型的属性。

function Father(name, age) {
    this.name = name;
    this.age = age;
}
function Son(name, age) {
    // this 指向 Son 构造函数的实例对象
    Father.call(this, name, age)
}
const son = new Son('刘德华', 18);
console.log(son);

2.5 借用原型对象继承父类型方法

一般情况下,对象的方法都在构造函数的原型对象中设置,那么通过构造函数创建的对象就会继承原型的属性和方法。

核心原理:
  ① 将子类所共享的方法提取出来,让子类的 prototype 原型对象 = new 父类()
  ② 本质:子类原型对象等于是实例化父类,因为父类实例化之后另外开辟空间,就不会影响原来父类原型对象
  ③ 将子类的 constructor 重新指向子类的构造函数

function Father(name, age) {
    this.name = name;
    this.age = age;
}
Father.prototype.money = function() {console.log('father money');
}
function Son(name, age) {
    // this 指向 Son 构造函数的实例对象
    Father.call(this, name, age)
}
// Son.prototype = Father.prototype; 这样直接赋值,如果修改了子原型对象,父原型对象也会跟着一起变化  即 console.log(Father.prototype) 也会有 exam 方法

Son.prototype = new Father(); // 此时 son 的原型对象指向 Father, 即 console.log(Son.prototype.constructor); 指向 Father, 所以需要利用 constructor 指回原来的构造函数
Son.prototype.constructor = Son;

Son.prototype.exam = function() {console.log('son exam');
}
const son = new Son('刘德华', 18);
console.log(son);
console.log(Father.prototype);
console.log(Son.prototype.constructor);

图解:

2.6 类的本质

  1、class 本质还是 function;

  2、类的所有方法都定义在类的 prototype 属性上;

  3、类创建的实例,里面也有 __proto__ 指向类的 prototype 原型对象;

  4、所以 ES6 的类它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已;

  5、所以 ES6 的类其实就是语法糖;

  6、语法糖:语法糖就是一种便捷写法。简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖。

退出移动版