背景
最近在实现一个表单页面复制功能的时候遇到一个问题,本来页面是有创建、编辑功能的,现在要先加一个一键复制的功能,具体流程和编辑一样,name
字段加一个 -copy
,最后提交是用创建的接口。Edit
是继承的 Create
,Copy
来继承 Edit
,具体代码可以抽象成以下:
转载
https://wulv.site/2017-05-29/%E4%BB%8EES6%E4%B8%AD%E7%9A%84extends%E8%AE%B2js%E5%8E%9F%E5%9E%8B%E9%93%BE%E4%B8%8E%E7%BB%A7%E6%89%BF.html
|
|
问题
上面的代码最终会输出 test-copy
,也就是我想要的结果,但当时手贱,写了一个箭头函数,如下:
|
|
结果报错了,这就奇怪了,难道箭头函数和普通的函数声明还有这个区别吗?拓展一下,还有以下两种写法:
|
|
是不是有点晕了,四种写法结果居然出来三种结果,怎么看都觉得差不多啊。
原理
在 es5
的时候我们了解到继承的几种实现方式,原型链继承、寄生式继承、组合继承等,具体这篇博客写的很好,继承一个构造函数是要继承两个部分:
-
一个是实例属性,实例属性是每个实例都有一份各自互不干扰的属性。我们这么写:
123function Edit() {this.name = 'test';}其实在
ES6
中对应的就是=
这种写法:123class Edit {name = 'test'} -
一个是方法,方法则是共用的,放在
prototype
上,以此来节省内存。我们这么写:123Edit.prototype.setValue = function() {console.log( this.value);}在
ES6
中对应的就是声明函数这种写法:12345class Edit {setValue() {console.log( this.value);}}我们来看一下
babel
编译后copy
继承做了哪些事:
|
|
- 做了一个
_inherits
操作,这个里面做了两件事:- 将
Copy.prototype
设置为Edit.prototype
创建的对象, 并设置其constructor
属性(不可枚举) - 将
Copy.__proto__
指向Edit
- 将
- 将
Copy.__proto__
也就是Edit
执行Edit.apply(this, arguments)
,这不就是经典继承么。 - 执行一个
_createClass
操作,就是把自己的方法绑定到prototype
上。
总结来说就是:
- 继承原型链方法
- 继承实例属性
- 扩展添加的方法
到这里我们解释了两个class
和extends
所做的事情,那为什么创建对象执行方法就成了不同的结果呢?这要从js
对象怎么查找属性,也就是原型链有关。当调用一个对象的某个方法时,首先对象会查找本身有没有设置这个属性,也就是这样:
|
|
如果找不到的话,其实每个对象都有一个__proto__
属性,指向创建这个对象的构造函数的原型(这里也就是Object.prototype
),原型也就是一个对象,也有自己的属性和__proto__
,如果原型还找不到,就这样沿着__proto__
一直找下去,这就构成了js
的原型链。
回到我们上面的四种写法:
- 写法一:
Edit
和Copy
都将setValue
设置到原型上,Edit
创建的对象可以通过__proto__
找到自己的方法,super
操作也可以通过(Object.getPrototypeOf(Copy.prototype), 'setValue', this).call(this, value)
调用到Edit
的setValue
方法。 - 写法二:子类和父类都是实例属性,子类的
setValue
覆盖了父类,执行了子类的setValue
,然而父类的prototype
里并没有setValue
属性,报错。 - 写法三:子类继承了父类的实例属性,子类的原型里也有相同的属性,然而根据
js
原型链查找规则,优先使用了实例属性,也就只执行了父类的setValue
方法。 - 写法四:基本和写法一一样,不过子类先找到了自己的实例方法,没有从原型里查找。
总结
在ES6
里如果要使用super
使用父类的同名方法,父类的方法不能设置为实例方法。