键盘敲烂,月薪过万作业不做,等于没学
当前系列: ES进阶 修改讲义

extends

之前我们学习的类的定义是这样的:

        class Person {
            constructor(name) {
                this.pname = name;
            }
            hello() {
                console.log(`hello, ${this.pname}`);
            }
        }

现在我们再定义一个类:

        class Student extends Person {
        }

注意:Student后面跟了extends。这被称之为:继承。

  • Student被称之为:子类/派生(derived)类
  • Person被称之为:父类/基(base)类

子类可以自动具有父类的属性和方法:

        let fg = new Student('飞哥');

注意:Student中完全没有任何定义

演示:fg.name 和 fg.hello()

其他:

  • 可以一直继承(祖先/后代类)
  • 只有父类,没有“母类”
  • 父类仍然存在
  • 继承是“类”的继承,不是“对象”的


instanceof

可以用来检测对象类型:

fg instanceof Student  //true
fg instanceof Person   //true

一般用于函数中进行判断,比如传入参数person

function exam(student) {
    if (student instanceof OnlineStudent) {
        //成绩加5分
    } else {
        //......
    }
}


静态

我们前面学过的静态方法也是可以被继承的。

将hello()改成静态方法:

    static hello() {
        console.log(`hello, ${this.pname}`);    //this是运行时值
    }

演示:Student.hello()

注意:this始终指代的是“当前”对象或类。


super

可以在子类通过super调用父类成员。

构造函数

为Student添加一个constructor:

            constructor(name, sId) {
                super(name);
                this.sId = sId;
            }

其中,通过super()调用了父类的构造函数。(断点演示

此外,要注意:

  1. 在子类constructor中使用this之前必须通过super()调用父类的构造函数,因为:JavaScript中的子类对象是在父类对象基础上生成的……(和ES5相反)
    演示:错误信息
  2. 如果子类不写constructor,JavaScript会自动的生成一个constructor并调用super()


父类成员

在其他方法中,super还可以调用父类成员(属性和方法)

但属性的调用有些奇怪:

    learn() {
        console.log('this.pname:' + this.pname);    //飞哥
        console.log('super.pname:' + super.pname);    //undefined
 }

我们可以这样理解:继承是类(模板)的继承,不是实例的继承。



使用:(建议仅用于调用父类方法

  • 实例方法:指向父类对象。但是:
    1. 父类自有的(own)属性,子类无法取得。子类能取得的,是prototype上的(方法)
    2. 通过super调用父类方法时,父类方法体中的this,指向的是调用者(子类)
                  morningCall() {
                      super.hello();
                      console.log('起床啦,(`⌒´メ)');
                  }
    3. 如果在super上设置属性,实际上会设置在this上(为什么?偶买糕的……http://2ality.com/2015/02/es6-classes-final.html
    4. 无法删除super.property……
  • static方法:指向父类


原型链

复习:JavaScript

  • 使用function模拟class,而函数本身又是一种对象
  • 对象本质上是:(动态的)属性和方法的无序集合。


__proto__和prototype

但是,
  1. JavaScript对象有一个默认隐藏(private)的属性:__proto__ ,指向
  2. 创建该对象的javasScript函数(类)的prototype (所有函数都:
    • 有一个prototype
    • 可以使用new创建一个他们的实例 )
fg.__proto__ === Student.prototype

MDN不推荐使用__proto__,而是:

Object.getPrototypeOf(fg)

我们这里仅做演示用。


原型链

当我们通过对象的点运算符(.)获取对象成员时,解释器会:

  1. 首先在对象自己的成员中查找,
  2. 查找不到,继续在对象的__proto__中寻找,
  3. 还找不到,就继续在对象的__proto__的__proto__中寻找
  4. ……
  5. 直到找到属性(Property Shadow),或者对象的__proto__为null返回undefined
javascript对象的__proto__本身又是一个对象,所以__proto__上又还可以有__proto__,依次类推,__proto__能够形成一个“链条”,这就被称之为JavaScript的


hasOwnProperty()

为了区分“自己的”和“其他途径获得的”,JavaScript提供了hasOwnProperty()方法。

凡是直接在对象(变量名/this/super)上点(.)出来的属性,都是对象自己的。

但是,理解这个方法的结果需要一些知识贮备。


两种方法

演示:调用一个子类的hasOwnProperty()方法,传入的参数为

  • 属性名(this.XXX)时,无论是“自己的”,还是“父类的”,结果都为true
  • 方法名时,哪怕是“自己的”,结果也为false

注意:get和set定义的属性本质上仍然是函数/方法,本节课里的“属性”并非指他们。


prototype的属性

之前我们在类中(constructor之外)声明的方法,是属于类的prototype的:

Student.prototype.hasOwnProperty('learn')  //true
zl.__proto__.hasOwnProperty('learn')  //true
zl.hasOwnProperty('learn')  //false

同样,因为:同一个类的不同实例共享同一个prototype:

let atai = new Student();
atai.__proto__ === fg.__proto__  //true

所以不同对象能够共用同一个方法。当一个对象调用某个方法的时候,首先查找自己的属性;找不到,到其__proto__中查找……

演示:

  • 修改 zl.__proto__.learn,影响到另一个对象 gty.learn()
  • 修改 zl.learn (实际上是添加),不会影响gty.learn()


constructor里的声明

我们也可以在constructor中定义一个方法:

    constructor(name, sId) {
        this.exam = function () {
            console.log('exam...');
        }
    }

表面上看,两种方法都可以被不同对象调用,但他们的实现机制是不一样的:

  • exam(),点(.)出来,是因为每个对象都需要被构造(construct),是在构造时赋予该对象的
  • study(),是对象的prototype上的,是JavaScript解释器自动生成的并赋予给prototype的


constructor

每个对象,都有一个constructor属性;

其实就指向构造该对象的类/函数:

zl.constructor === Student  //true


继承的本质(prototype)

在当前对象/类上找不到,就往其__proto__上找;所以,将父类/父类的prototype赋值给子类/子类对象的__proto__,是不是就OK了?

所以,JavaScript的继承依赖于原型链:

  • 静态(static)的继承:类上找不到,就往类的__proto__上找。
    静态属性实际上是类是自己的属性。(演示:添加一个静态方法)
    Student.__proto__  === Person
  • 实例(instance)的继承:对象上找不到,就往对象的__proto__上找
    实例成员实际上是类的prototype的属性 (演示:添加一个实例方法)
    Student.prototype.__proto__ === Person.prototype


Function和Object

所有对象(包括包装对象,不包括primitive的字面量),都继承自Object

new Number(32) instanceof Object    //true
Number instanceof Object    //true
32 instanceof Object    //false

Function也不例外:

function greet(){}
greet instanceof Object    //true
greet instanceof Function    //true
Function instanceof Object

但是,JavaScript中有这么一句话:Function是Object(的子类),Object也是Function(的子类)。如何理解呢?

Object.__proto__ === Function.prototype  //Object对象是Function的实例
Object.__proto__ === Function.__proto__    //Object和Function是同一个类的两个实例 

Function.__proto__  === Function.prototype  //Function对象是Function类的实例
Function.__proto__.__proto__ === Object.prototype  //Fuction作为对象,是Object子类对象

Function.prototype.__proto__ === Object.prototype  //Function作为类,是Object的子类


其它应用

可以在prototype上为对象上动态地添加方法属性等,比如在之前的Student基础上:

        Student.prototype.begin = function () {
            console.log('开始学习');
        }

所有的Student对象就可以调用begin()方法:

 pzx.begin();
一些类库会这样做,比如JQuery
jQuery.fn = jQuery.prototype = {

注意:JQuery仍然用的ES5的写法,JQuery是一个function而不是class

jQuery = function( selector, context ) {

所以可以给prototype一个数组。

ES6中在prototype上添加多个方法,建议使用:

Object.assign(prototype, func_name1(){}, func_name2(){},...)

更多参考:MDN prototype


@想一想@:JavaScript的“面向对象”,为什么没有“多态”?


作业

  1. 在封装作业的基础上,为Course添加两个子类:主修课(MajorCourse)和辅修课(ElectiveCourse)
  2. 声明一个Stundent类,包含name和score两个属性,让Course的Students包含的是Student的对象
  3. 主修课需要参加考试,所以有一个Exam(student)方法;辅修课只需要测评,所以有一个方法Assess(student)
  4. 创建一个MajorCourse的实例,运行它的Exam(student)方法,结束课程(使用end()方法),并给该student该course上的score赋值为50-100的随机整数
  5. 创建一个ElectiveCourse的实例,运行它的Exam(student)方法,结束课程(使用end()方法),给该student该course上的score赋值为ABCDE中的一个随机值
  6. 给Course声明一个静态的GetStudentsByName(name),能根据不同的name返回不同的整数值(参加该course的学生数量)
  7. 在子类调用GetStudentsByName(name)
  8. 说明:为什么子类可以继承父类的实例和静态方法?
学习笔记
源栈学历
大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 ES进阶 中继续学习:

下一课: JavaScript:module

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码