ES6类的继承

大家好,我是小庄。

今天来梳理一下ES6中的继承。

顺带记录一下其中比较容易遗忘的知识点。

1、extends关键字

实际上,继承的关键是extends:

class myClass{
}
class children extends myClass{
}

分析:
上面通过extends 继承了myClass的所有属性和方法。

2、super关键字

super关键字在类中有两种完全不同的表现形式:

1、代表函数时使用

ES6 要求,子类的构造函数必须执行一次super函数。代表父类的构造函数。作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错。

class A {}
class B extends A {
  constructor() {
    super();
  }
}

上面中super虽然是表示A的构造函数,然而返回的是子类B的实例。即super内部的this指的是B的实例
上面一行话用代码来解释就是:

A.prototype.constructor.call(this)。

2、代表对象时使用

super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
虽然个人觉得这种设计无疑会增加程序员对于super的理解难度,但是熟悉就好了。
要区分什么是原型对象和对象本身。

class A {
  constructor() {
    this.a = 2;
  }
  p() {
    return 2;
  }
}
class B extends A {
  constructor() {
    super();
    console.log(super.p()); // 2
  }
  get m() {
    return super.a; // undefined
  }
}
let b = new B();
b.m;

super.p()很明显是作为对象使用的,这里就代指父类的原型对象,而p方法是定义在A的原型上面的,所以返回值是2。理所当然调用b.m就取不到值。
这也从侧面说明:constructor()方法一般情况下就是只A本身,而类中的一般方法是定义在类的原型对象上的。

来看另外一个例子:

class A {
  constructor() {
    this.x = 1;
  }
  print() {
    console.log(this.x);
  }
}
class B extends A {
  constructor() {
    super();
    this.x = 2;
  }
  m() {
    super.print();
  }
}
let b = new B();
b.m() 

猜一猜最后会输出什么,1或者2?
由上面的一些内容推出,调用print方法,而print方法是定义在A类中的,那么打印的值是不是应该为1呢?

答案是2.
这个JS函数的执行作用域有关。

该怎么理解呢?

当函数执行的时候会生成一个作用域,运行到b.m()时,会创建属于m的一个上下文,m里面的this指向他本身,print被调用时,引擎会先在print内部找this.x,没找到就去运行的上一级找,就找到了m,m中也没有就去m定义的上一级找,如果还没找到就一级一级的向上查找,发现constructor中有一个变量x,就终止继续查找。

如果稍微改动一下上面B的代码:

class B extends A {
  constructor() {
    super();
  }
  m() {
    super.print();
  }
}
let b = new B();
b.m() 

此时,答案就是1.
因为B继承了A,B的原型是指向A的,在B中找不到就会去A中找,所以答案是1。

为了加强记忆再看一个实例:

class A {
  constructor() {
    this.x = 1;
  }
}

class B extends A {
  constructor() {
    super();
    this.x = 2;
    super.x = 3;
    console.log(super.x); // undefined
    console.log(this.x); // 3
  }
}

let b = new B();

请问一个打印值是多少?
有同学很自信的说那不就是 1嘛。

答案是错的。

当在子类B中调用super(); 还记得代表什么吗?

A.prototype.constructor.call(this)。

是的,super.x = 3;其实指的是子类B中x的赋值,相当于 this.x=3。

而在输出super.x时,执行的是A.prototype.x。而A的原型上并没有x所以会输出undefined。

虽然这看起来是有点绕,但还是解释的通。
显然第二次输出的值就是3.

补充助于理解的代码:

class A {
    constructor() {
      this.x = 1;
    }
    test() {
        console.log('这边是测试');
    }
    y = 2
}

class B extends A {
    constructor() {
        super();
        this.x = 2;
        // super.x = 3, 其实指的是子类B中x的赋值,相当于 this.x=3
        super.x = 3;
        // super.test = function testFun() {}, 相当于 this.test = function testFun(){}
        super.test = function testFun() {
            console.log('执行了这边的测试');
        }
        // super.testB = function() {}, 相当于 this.testB = function(){}
        super.testB = function() {
            console.log('这里是修改后的testB函数');
        }
        console.log(super.test());// '这边是测试'
        console.log(super.y);// undefined
        console.log(super.x); // undefined
        console.log(this.x); // 3
    }
    testB() {
        console.log('这里是testB函数');
    }
}

let b = new B();
b.test();   // '执行了这边的测试'
b.testB(); // '这里是修改后的testB函数'

3、类的 prototype 属性和proto属性

又到了绕弯的环节,还是那句话熟悉就好了。虽然我觉得这种设计确实不是那么的合理。

类作为构造函数,那么具有prototype 属性也理所当然。那么proto属性是哪里来的?

JS万物皆对象,虽然不准确但是类确实也是对象的一种,所以proto就来了,有人把proto叫做隐式原型,其实不妥。proto__作为JS原型链的桥梁,只是这种属性在不同的浏览器中暴露的程度不同,Google就可以访问对象的proto。
关于原型链的问题留作以后在单独解释,回到类上来,类同时具有prototype 属性和
proto__属性,那么类的原型链是这样的:

class A {
}
class B extends A {
}
B.__proto__ === A // true
B.prototype.__proto__ === A.prototype // true

你大概会从中看出prototype和proto的区别。

这两条继承链,可以这样理解:作为一个对象,子类(B)的原型(proto属性)是父类(A);作为一个构造函数,子类(B)的原型对象(prototype属性)是父类的原型对象(prototype属性)的实例。

由此可以看出:

子类的原型proto是父类,那么子类的原型的原型就是父类的原型。

公众号:深漂程序员小庄,专注于分享编码经验、技术干货、面试经验、偶尔分享深漂日常、工作书籍、实用书籍等。

#校招##社招##前端工程师##牛客#
全部评论
感谢分享,学到了不少知识啊
点赞
送花
回复
分享
发布于 2022-07-05 19:39

相关推荐

6 17 评论
分享
正在热议
# 牛客帮帮团来啦!有问必答 #
1151203次浏览 17148人参与
# 通信和硬件还有转码的必要吗 #
11194次浏览 101人参与
# 不去互联网可以去金融科技 #
20336次浏览 255人参与
# 和牛牛一起刷题打卡 #
18899次浏览 1635人参与
# 实习与准备秋招该如何平衡 #
203348次浏览 3625人参与
# 大厂无回复,继续等待还是奔赴小厂 #
4970次浏览 30人参与
# OPPO开奖 #
19192次浏览 267人参与
# 通信硬件薪资爆料 #
265880次浏览 2484人参与
# 国企是理工四大天坑的最好选择吗 #
2220次浏览 34人参与
# 互联网公司评价 #
97672次浏览 1280人参与
# 简历无回复,你会继续海投还是优化再投? #
25034次浏览 354人参与
# 0offer是寒冬太冷还是我太菜 #
454821次浏览 5124人参与
# 国企和大厂硬件兄弟怎么选? #
53896次浏览 1012人参与
# 参加过提前批的机械人,你们还参加秋招么 #
14636次浏览 349人参与
# 硬件人的简历怎么写 #
82284次浏览 852人参与
# 面试被问第一学历差时该怎么回答 #
19393次浏览 213人参与
# 你见过最离谱的招聘要求是什么? #
28057次浏览 248人参与
# 学历对求职的影响 #
161224次浏览 1804人参与
# 你收到了团子的OC了吗 #
538675次浏览 6386人参与
# 你已经投递多少份简历了 #
344169次浏览 4963人参与
# 实习生应该准时下班吗 #
96965次浏览 722人参与
# 听劝,我这个简历该怎么改? #
63517次浏览 622人参与
牛客网
牛客企业服务