网上充斥着各种关于Js的继承的文章,一看标题大约都是“Js继承的5种方式、8种办法”,当然总结得也很好,只是有些地方个人觉得有凑数的嫌疑,仔细分析似乎有些牵强附会的意思。这篇文章就通过原型的角度重新讲讲到底什么才是Js的继承,如果你对Js的原型还不是很熟悉,请移步。
一、什么情况才算是子继承了父?
继承,在现实生活中是富二代发家致富的必备手段,在我们程序语言的世界里面同样也是程序“发家致富”的手段。顾名思义,继承就是“把你的东西给我”,当然不能白给你,除非你是人家儿子,或者你认人家当干爹,总之,就是继承了之后你和被继承者之间有一定的继承关系。
这种继承关系在Js语言中怎么判断?怎么才知道我是不是继承的你?讨论这个之前,先了解一个关键字:instanceof。
instanceof用来判断某个function的prototype属性是否存在于另外一个对象的原型链上。
什么意思?某个function的prototype属性存在另外一个对象的原型链上意味着什么?通过这篇讨论我们知道,在Js中,所有的值都是由function构建而来,再通过这篇讨论我们又知道,function的prototype属性是提供给由它所构建的值的__proto__属性引用的。
也就是说,某个function的prototype属性,一定存在于由它所构建的对象的原型链上,除非手动修改
所以说,如果某个function的prototype属性存在于另外一个对象的原型链上,我们就视为这个对象是由该function构建而来,所以该对象instanceof该function。那么回答上面的问题:继承关系在Js语言中怎么判断,或者说怎么体现?
一个对象如果是由子function构建而来,那么它一定也要被视为由父function构建而来,也就是说子function构建出来的对象一定也要 instanceof父function。
换言之,就是如果A继承了B,那么A的prototype属性和B的prototype都要存在于new A()对象的原型链上。
这是毋庸质疑的,是由Js语言的new关键字从语言层面实现的,因为a.__proto__只能引用一个值,所以要想B.prototype存在于a的原型链上,只能是通过A.prototype.__proto__属性往下接。
捋一下,因为name_b属性在a自己的属性列表里面没有找到,
然后会去a.__proto__,也就是A.prototype里面找,也没有,
然后会去A.prototype.__proto_
_,也就是B.prototype里面找,也没有,
然后会去B.prototype.__proto__,也就是Object.prototype里面找,也没有,
然后会去Object.prototype.__proto__里面找,然而发现Object.prototype.__proto__是null,所以最终没有找到,返回undefined。
所以以上方法虽然语法上继承了,但是没有实质的意义,a只能继承B.prototype的属性,因为虽然B.prototype在a的原型链上了,但是name_b并不在a的原型链上,注意name_b不是B的属性,而是由B构建的对象的属性。所以要想name_b在a的原型链上,就需要由B构建的对象也要在a.原型链上。
因为,B.prototype在b的原型链上,b又在a的原型链上,所以B.prototype也就在a的原型链上了,这样a不仅可以继承b的属性,也可以继承B.prototype的所有属性,实现了整个Js原型继承的逻辑自洽。
也有这样做的
二、换汤不换药的其他写法之一详解
由于Js语言的灵活性,看似可以通过各种方式实现继承,其实本质都是一样的。
貌似还不错,甚至有一些人看到P.call(this)这样的写法觉得很优雅,很牛逼的样子,其实就是调用P,然后把this指向C,它比new的方式调用P少做了一些事情。假如父类方法有参数呢?
上面的继承方式虽然很别扭的支持了参数,之所以别扭,是因为非要把灵活的Js当作死板的Java用,或者说非要把
基于function的Js当作基于Class的Java用。当然上面的方式出了别扭还有其他更严重的问题。
其实上面两个问题是一个问题,就是P.prototype不在c的原型链中。解决方法也很简单
思考
完整代码
三、object之间的分享
上面描述的都是一个function继承另一个function,实际上不是function继承,而是通过在function上做手脚,使function构建的对象之间实现继承。在function上做手脚的好处是所有由该function构建出来的对象都继承了父function的属性,当然我们也可以通过修改原型链实现object之间的属性分享。
四、有些事,你搞着搞着就晕了
还是那句话,由于Js语言的灵活特性,你还可以想出很多所谓实现继承的方式,比如
再比如
无论什么方式,都没有原型继承优雅(原型就是为了继承而生的),或者说都是原型继承的变体,归根结底就是怎么复制父类属性的问题。还有就是无论什么继承都需要处理原型链,才能实现继承的逻辑自洽。
所以,请忽略其他乱七八糟的继承,你大概也不会真正用到它们,知道怎么回事就行。但是关于Js的原型,一定得掌握,这可以说是Js继承的本质。