You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
想一下,其实子类的prototype并不关心父类的构造函数中定义了哪些内容,因为父类构造函数中的内容已经在SuperClass.call(this);中继承给了子类的构造函数,我们要的只是父类prototype的内容,所以subClass.prototype = new superClass();是产生了一点冗余的,但是又不能直接赋值,因为父子两个类的prototype需要有独立的内存空间。所以,我们可以找一个能提供独立空间存放父类prototype的‘中间人’:
Uh oh!
There was an error while loading. Please reload this page.
Javascript中的继承一直是一个难点,因为js有别于其他面向对象的语言,js是基于 原型(prototype)
的。
prototype是一个很难琢磨得透也很难掌握的东西,当然也许有人会跳出来说现在都用ES6,typescript啦,谁还学prototype。这样想就错了,首先,ES6和typescript远远没有你想的这么普及,谁敢把不经过编译的es6和ts直接放到线上跑?编译后还不是一样回到prototype。其次,prototype能干的事情多的去了,不单单只是new或继承几个类,有兴趣可以看看Vue的响应式数组方法是怎么做的。
扯远了,今天写这篇东西是因为刚刚看到了一篇关于js继承的文章,想把一些思考和总结记下来。
如何实现继承
为什么在js中继承很麻烦?
因为js中没有
extends
关键字(ES6前)因为js既能访问到实例属性也能访问到原型属性
js对象是引用类型
一点一点来分析。
壹
首先在ES6之前,js中是没有又甜又可爱的
extends
语法糖的,那要继承怎么办,只能自己在现有的js语法里面各种找方法实现(而且还不好找)。貳
其次,在一个js对象中,既有来自构造函数的属性,也能访问到其
_proto_
,也就是构造函数的prototype
的属性(通常是方法)。这么理解这句话呢?看下面的例子:可以看到,

getTotal
是绑定在foo
的构造函数的prototype
中的一个方法,在实例化Foo
后,foo
既能访问它自身的属性count
,也能访问getTotal
方法。但是getTotal
方法并没有在foo
对象里面,所以很明显,当要访问一个对象的某个属性/方法时,js引擎首先在对象里面找,如果找不到,再顺着原型链往上找。foo
和foo.__proto__
关系如下:那么,也就是说,想要在js中继承一个类,就必须要做到两点:
子类要继承父类中的属性/方法
子类的prototype要继承父类的prototype
做不到第二点的都不是完整继承。
第一点的常规实现方法是:
在子类的构造函数里面用一下
call
(当然也可以用apply
,个人比较喜欢用call
)调用父类的构造函数就行。然后第二点,思路是这样子:
由于
prototype
是对象而不是函数,所以没法用call
或者apply
的方法了。叁
但是事情没有这么简单,仔细观察上面的代码:
发现问题了吗?js中的对象都是引用类型(对什么是引用类型不了解的可以看看这篇文章:JS中的深拷贝),对象的直接赋值都是改变指针指向的地址,也就是说:
子类的
prototype
和父类的prototype
共享同一片内存空间了。会造成什么问题?会造成当你想要往子类的
prototype
里添加属性/方法的时候,父类的prototype
也会被修改:这显然不好,但是怎么改进?不就是想要有独立分配的内存空间嘛,很简单,我们有父类
SuperClass
,我们直接让子类的prototype
指向父类的实例:为什么这种方法可以?其实父类的实例里面是没有
getA
这个方法的,getA
在父类的prototype
里面,但是前面已经说过了,js找一个对象的属性/方法是会顺着原型链往上找的:目前为止他们的恩怨情仇大概是这个样子:
看上图不难发现,有一个地方貌似不太合理(我故意画出来了),就是子类的
prorotype
的constructor
指针居然指向了父类的构造函数:所以我们要做一个修正:
寄生组合继承
我们把上面的所有实现都糅合起来,放进一个函数里面,并且命名为
myExtends
(不能直接用extends
因为是保留字):这样看起来清晰多了,但是还不完美。上面的代码中,父类的构造函数
SuperClass
一共被调用了两次,这不是好事,我们要想办法优化一下。想一下,其实子类的
prototype
并不关心父类的构造函数中定义了哪些内容,因为父类构造函数中的内容已经在SuperClass.call(this);
中继承给了子类的构造函数,我们要的只是父类prototype
的内容,所以subClass.prototype = new superClass();
是产生了一点冗余的,但是又不能直接赋值,因为父子两个类的prototype
需要有独立的内存空间。所以,我们可以找一个能提供独立空间存放父类prototype
的‘中间人’:这样就完美了。
下面贴上完整代码:
其实上面这种实现,就是js中使用最普遍的寄生组合继承的实现。结合了各种继承的优点,而且实现起来也比较简单,容易理解。
--EOF--
The text was updated successfully, but these errors were encountered: