基本介绍
js 中继承可以分为两种:对象冒充和原型链方式,下面分别举例介绍。
对象冒充
包括三种:临时属性方式、call()及apply()方式
临时属性方式
1 | function Person(name) { |
call()/apply()方式(实质上是改变了this指针的指向)
1 | function Person(name) { |
缺点
先来看这么一张内存分配图:
说明:
- 在OO概念中,
new
实例化后,对象就在堆内存中形成了自己的空间,值得注意的是,上图中的代码段。成员方法就是存在这个代码段的,并且方法是共用的。问题就在这里,通过对象冒充方式继承时,所有的成员方法都是指向this
的,也就是说new
之后,每个实例将都会拥有这个成员方法,并不是共用的,这就造成了大量的内存浪费。 - 并且通过对象冒充的方式,无法继承通过
prototype
方式定义的变量和方法,如以下代码将会出错:
1 | function Person(name) { |
原型链方式
1 | function Person() { |
接下来按照上面的例子来理解以下js原型链概念:
原型链可以理解成:js中每个对象均有一个隐藏的 __proto__
属性,一个实例化对象的 __proto__
属性指向其类的 prototype
方法,而这个 prototype
方法又可以被赋值成另一个实例化对象,这个对象的 __proto__
又需要指向其类,由此形成一条链,也就是前面代码中的:
1 | F2E.prototype = new Person(); |
这句是关键。js对象在读取某个属性时,会先查找自身属性,没有则再去依次查找原型链上对象的属性。也就是说原型链的方法是可以共用的,这样就解决了对象冒充浪费内存的缺点。
下面再来说缺点:
缺点显而易见,原型链方式继承,就是实例化子类时不能将参数传给父类,也就是为什么这个例子中 function Person()
没有参数,而是直接写成了 this.name='Simon'
的原因。下面的代码将不能达到预期的效果:
1 | function Person(name) { |
总结
最后,总结一下自认为较好的继承实现方式,成员变量采用对象冒充方式,成员方法采用原型链方式,代码如下:
1 | function Person(name) { |