javascipt之继承

99 阅读4分钟

写在前面


开始写文章的初衷是看了周岭先生的《认知觉醒》,里面讲述的如何高效率学习,且能激励自己,莫过于有一定的产出,在此感谢冴羽老师,以及借他认识的低调务实优秀中国好青年群友们也给前端之路上迷茫的我提供了很大的帮助。

正文


什么是继承?一般认知里,继承可以理解为子从父中获得其属性(我得到了我老豆的财产),而在javascipt里,继承则意味着一种关联,关联起两个对象,从而能让一个对象通过委托访问另一个对象的属性和方法。

如何实现继承?主要有六种方式,各有优缺点,下面会一一讲述。

1.原型链继承


举例说明

给出两个构造函数

function Parent(){this.name = "xiaohong";}

function Child(){}

要使两者有继承关系(子承父),我们可以:

Child.prototype = new Parent();

创建一个实例:var child1 = new Child();

则child1可继承Parent.name

原型链继承由此实现。

但同时也会出现问题:

(1)父中引用类型的属性会被所有创建的实例共享。

我们将Parent中的name属性改为引用类型

function Parent(){this.name = ["xiaohong"];}

再创建一个实例:var child2 = new Child();

通过child2向name添加元素:child2.name.push("daming");

(注意:这里添加元素的方法是push,而不是child2.name = ······,直接使用child2.name = ······则是为child2创建了一个属于他自己的属性name)

查看child2.name

再查看child1.name

惊人的一致!

(2)子不能向父传递参数

传了也没啥用,啥都改不了

咋解决?往下看

2.借用构造函数继承


同样是两个构造函数:

function Parent(){this.name = "xiaohong";}

function Child(){Parent.call(this);}

这里与原型链继承的差别在于:使用了call方法来实现继承,为什么说是'借用'?。

是因为在Child的构造函数里,我们通过call"借用"到了Parent的属性,放在了自己身上。

所以他叫借用构造函数继承。(读书人的事能叫偷嘛)

我们来验证一下:

创建一个实例:var child1 = new Child();

然后查看一下:

那么他是否会出现和原型链继承一样的问题呢?

我们重复一下之前push的操作:

然后查看一下:

ohhhhhhh!

那么是否能传参呢?

看这里

function Parent(name){
  this.name = name;
}
function Child(name){
  Parent.call(this,name);
}
var child1 = new Child("daming");
console.log(child1.name);//'daming'

当然可以!

但也还是会有一些问题:

  1. 方法都在构造函数中定义,每次实例化对象都得创建一遍方法,基本无法实现函数复用
  2. call方法仅仅调用了父级构造函数的属性及方法,没有办法调用父级构造函数原型对象的方法

\

于是就出现了采这二者之长的:

3.组合继承


就把上面这俩结合在一起呗:

function Parent(){
			this.name = 'daming';
		}
function Child(){
			Parent.call(this,name);
		}
Child.prototype = new Parent();
		

就会发现这样既可以通过原型链继承父级以上的方法,又可以通过借用构造函数继承来获取属性,避免共享问题。

大家可以自行验证一下。

但他同样也有缺点:

每次创建实例都会调用两次父级的构造函数。

Child.prototype = new Parent();Parent.call(this,name);

4.原型式继承


我们来定义一个创建对象的函数createObj,参数为父级object

function createObj(obj){
			function F(){}
			F.prototype = obj;
			return new F();
		}

这个继承方式实际上也就是Object.create() (ES5)

和原型链继承有点类似,缺点也一样。

5.寄生式继承


原型式继承的增强版!

增强在哪儿?

function parasitism(obj){
			var clone = createObj(obj);
			clone.pro = function(){
				console.log("我新增强了,多了一个方法!");
			}
			return clone;
		}

寄生寄生,先寄在原型式继承的身上,然后自己再生长出新的方法。

缺点也和借用构造函数继承类似,每创建一个实例就得创建一次方法。

6.寄生组合式继承


先把上面的组合继承搬过来

function Parent(){
			this.name = 'daming';
		}
function Child(){
			Parent.call(this,name);
		}
Child.prototype = new Parent();
		

前面我们说了,组合继承的缺点就是每次创建实例会调用两次父级构造函数,那么我们可以利用寄生继承来解决这个问题,即不使用 Child.prototype = new Parent() ,而是通过一个中介,间接的让 Child.prototype 访问到 Parent.prototype,这样就能减少一次父级构造函数的调用。

如何实现?

function F(){}
F.prototype = Parent.prototype;
Child.prototype =new F();

这也是引用类型继承最理想的方式。

有兴趣的也可以将其封装方便后续使用。

\

参考

  1. JavaScript深入之继承的多种方式和优缺点 冴羽
  2. 6种JavaScript继承方式及优缺点 有鱼是只猫
  3. 《你不知道的javascript》
  4. 《javascript高级程序设计》

\

\