JS继承之extends
2020-08-15 本文已影响0人
酷酷的凯先生
ES6中有关 class
的继承方式,引入了 extends
关键字。
但其本质仍然是 构造函数
+ 原型链的
组合式继承。
class A {
constructor(name, age) {
this.name = name;
this.age = age;
}
getName() {
console.log( this.name );
}
}
class B extends A {
constructor(name, age) {
super(name, age);
this.job = "web";
}
getJob() {
console.log( this.job );
}
getNameAndJob() {
onsole.log( super.getName() + this.job );
}
}
var b = new B("小黄", 20);
console.log(b.name); //小黄
console.log(b.age); //20
b.getName(); //小黄
b.getJob(); //web
b.getNameAndJob(); //小黄 web
# extends
上述 B 类
(class)通过 extends
关键字,继承了 A 类
的所有属性和方法。A 类
中的所有方法默认是添加到 B
的原型上,所以 extends
继承的实质仍然是原型链。
console.log("constructor" in b); //true
console.log("getName" in b); //true
console.log(b.hasOwnProperty("getName")); //false
console.log(b.hasOwnProperty("constructor")); //false
# super
super
这个关键字,既可以当作函数使用,也可以当作对象使用。当作函数使用时super
代表父类的构造函数,并在子类中执行 Parent.apply(this)
,从而将父类实例对象的属性和方法,添加到子类的 this
上面。
特别注意
- 子类必须在
constructor
方法中调用super
方法,如果子类没有定义constructor
方法,constructor
方法以及其内部的super
方法会被默认添加。
class A {
constructor(name, age) {
this.name = name;
this.age = age;
}
getName() {
console.log( this.name );
}
}
class B extends A {}
var b = new B("小黄", 20);
console.log(b.name); //小黄
console.log(b.age); //20
b.getName(); //小黄
console.log(b.hasOwnProperty("name")); //true
- 在子类的
constructor
方法中,只有调用super
之后,才可以使用this
关键字,否则会报错。
class A {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
class B extends A {
constructor(name, age) {
this.job = "web";
super(name, age);
}
}
var b = new B("小黄", 20); // 报错
-
super()
只能用在子类的constructor方法之中,用在其他地方就会报错。
class A {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
class B extends A {
toStr(name, age) {
super(name, age)
}
}
var b = new B("Tom", 20); //报错
super
作为对象时,在子类中指向父类的原型对象。即super = Parent.prototype
。
class A {
constructor(name, age) {
this.name = name;
this.age = age;
}
getName() {
console.log(this.name);
}
}
A.prototype.n = 33;
class B extends A {
constructor(name, age) {
super(name, age);
}
toStr() {
return super.n;
}
activeGetName() {
super.getName();
}
}
var b = new B("小黄", 20);
console.log(b.toStr()); //3
console.log(b.activeGetName()); //小黄
# 静态方法的继承
在一个方法前加上关键字
static
,就表示该方法不会被实例继承,但是父类的静态方法,会被子类继承。
class A {
static say() {
console.log("hello");
}
}
class B extends A {}
console.log(B.say()); //hello
也可以使用
super
在子类的静态方法中调用父类的静态方法。super
在静态方法中指向父类本身,而不是父类的原型对象。
class A {
static say() {
console.log("hello");
}
}
class B extends A {
static toStr() {
super.say();
}
}
var b = new B();
console.log(B.toStr()); //hello
# 继承表达式的类
类不但可以继承自其他类,也可以继承表达式。只要表达式可以被解析为一个函数并且通过
new
关键字可以创建新的实例对象即可。
- 继承传统形式的构造函数
let Obj = function(name) {
this.name = name;
}
Obj.prototype.getName = function() {
console.log(this.name);
}
class Person extends Obj {
constructor(name, age) {
super(name);
this.age = age;
}
}
const p = new Person("小黄", 29);
console.log(p.name); //小黄
console.log(p.age); //29
p.getName(); //小黄
- 继承函数返回结果
let fn = function() {
return class Person {
constructor(name) {
return {
name
}
}
}
}
class SomeOne extends fn() {
constructor(name) {
super(name);
}
}
let p = new SomeOne("小黄");
console.log(p.name); //小黄
# New.target
我们知道,函数内部有一个
new.target
对象用于判断函数是否通过new关键字调用。类构造函数也可以通过new.target
来确定类的调用形式。
class Obj {
//new Obj()时,new.target 的值为 Obj
constructor() {
if (new.target === Obj) {
console.log("不可以直接调用基类!");
}
}
fn() {
console.log(this.name);
}
}
class Person extends Obj {
//new Person("小黄")时,new.target的值为 Person
constructor(name) {
super();
this.name = name;
}
}
let p1 = new Person("小黄");
p1.fn(); //小黄
let p2 = new Obj(); // 不可以直接调用基类!
因为类必须通过 new
关键字调用,所以在类的构造函数中 new.target
的值永远不会是undefined
。