之前我们学习的类的定义是这样的:
class Person { constructor(name) { this.pname = name; } hello() { console.log(`hello, ${this.pname}`); } }
现在我们再定义一个类:
class Student extends Person { }
注意:Student后面跟了extends。这被称之为:继承。
子类可以自动具有父类的属性和方法:
let fg = new Student('飞哥');
注意:Student中完全没有任何定义
演示:fg.name 和 fg.hello()
其他:
可以用来检测对象类型:
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始终指代的是“当前”对象或类。
为Student添加一个constructor:
constructor(name, sId) { super(name); this.sId = sId; }
其中,通过super()调用了父类的构造函数。(断点演示)
此外,要注意:
在其他方法中,super还可以调用父类成员(属性和方法)
但属性的调用有些奇怪:
learn() { console.log('this.pname:' + this.pname); //飞哥 console.log('super.pname:' + super.pname); //undefined }
我们可以这样理解:继承是类(模板)的继承,不是实例的继承。
使用:(建议仅用于调用父类方法)
morningCall() { super.hello(); console.log('起床啦,(`⌒´メ)'); }
复习:JavaScript
fg.__proto__ === Student.prototype
MDN不推荐使用__proto__,而是:
Object.getPrototypeOf(fg)
我们这里仅做演示用。
当我们通过对象的点运算符(.)获取对象成员时,解释器会:
为了区分“自己的”和“其他途径获得的”,JavaScript提供了hasOwnProperty()方法。
凡是直接在对象(变量名/this/super)上点(.)出来的属性,都是对象自己的。
但是,理解这个方法的结果需要一些知识贮备。
演示:调用一个子类的hasOwnProperty()方法,传入的参数为
注意:get和set定义的属性本质上仍然是函数/方法,本节课里的“属性”并非指他们。
之前我们在类中(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__中查找……)
演示:
我们也可以在constructor中定义一个方法:
constructor(name, sId) { this.exam = function () { console.log('exam...'); } }
表面上看,两种方法都可以被不同对象调用,但他们的实现机制是不一样的:
每个对象,都有一个constructor属性;
其实就指向构造该对象的类/函数:
zl.constructor === Student //true
继承的本质(prototype)
在当前对象/类上找不到,就往其__proto__上找;所以,将父类/父类的prototype赋值给子类/子类对象的__proto__,是不是就OK了?所以,JavaScript的继承依赖于原型链:
Student.__proto__ === Person
Student.prototype.__proto__ === Person.prototype
所有对象(包括包装对象,不包括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的“面向对象”,为什么没有“多态”?
多快好省!前端后端,线上线下,名师精讲
更多了解 加: