不同于基于类的编程语言如C++和java,Javascript是基于原型的继承模式。同时,因为Javascript是一个非常灵活的语言,有许多方式来继承。

第一个和最基本的概念是关于构造函数和原型链的。父对象的构造函数称为父对象。子对象的构造函数称为子对象,相应的父对象和子对象分别是父对象和子对象。

对象中有一个隐藏属性{原型}(注释不是原型)。这是__proto__的浏览器,但它是不可访问的一些环境。它是指对象的原型,当访问任何对象的属性或方法时,它首先搜索对象的所有属性。如果找不到,它将根据原型}在原型链上一步一步地搜索原型对象上的属性,直到找到它,否则它将返回到未定义的位置。

1。原型链继承:

原型链是在Javascript中实现继承的默认方法。如果我们想使子对象继承父对象,最简单的方法是将子对象构造函数的原型属性指向父对象的一个实例。

复制代码代码如下所示:

函数(父){ }

函数(子){ }

child.prototype =新的母公司()



这一次,孩子的原型属性被重写,指向一个新对象,但是新对象的构造函数属性,但是没有子对象的权利,js引擎不会自动完成我们的工作,我们需要手动将子原型对象的构造函数属性返回给子对象:

复制代码代码如下所示:

child.prototype.constructor =孩子



以上是Javascript默认继承机制,属性和方法的重用将需要迁移到原型对象,而不会被重用的部分设置为自己的属性对象,但是这种继承需要创建一个新的实例作为原型对象,效率会很低。

2。原型继承(非原型链):

为了避免重复创建方法的问题原型对象实例的需要,可以直接到子对象的构造函数母原型对象的构造函数的原型,所以在Parent.prototype的所有属性和方法可以重复使用,而不需要重新创建的原型对象实例:

复制代码代码如下所示:

child.prototype = parent.prototype

child.prototype.constructor =孩子



但我们知道,在Javascript中,对象引用类型的存在,这种方法实际上是一个指针,拥有Child.prototype和parent.prototype指向相同的对象,因此,当我们想在子对象的原型的话继续一些属性的扩展,对父对象的原型将被改写。因为这里的原型对象实例总是只有一个,它也是继承方式。

三.临时构造函数的继承:

为了解决上述问题,我们可以借用一个临时构造函数来扮演中间角色。原型的所有操作都是在临时构造函数的实例上完成的,而不影响父对象的原型。

复制代码代码如下所示:

Var(f =函数){ }

f.prototype = parent.prototype

child.prototype =新的f()

child.prototype.constructor =孩子



同时,为了访问父类原型的属性,我们可以添加一个属性的父对象的原型,如尤伯杯,这样我们可以直接访问子对象上child.constructor.uber母原型对象。

我们可以把上面的工作封装成一个函数,然后调用这个函数来促进这种继承方式。

复制代码代码如下所示:

函数扩展(子、父){

Var(f =函数){ }

f.prototype = parent.prototype

child.prototype =新的f()

child.prototype.constructor =孩子

child.uber = parent.prototype

}



然后你可以这样称呼它:

复制代码代码如下所示:

伸展(狗、动物)



4。属性复制:

这种继承模式基本上不会改变原型链之间的关系,而是直接将父原型对象中的所有属性复制到子对象原型中。当然,这里的复制只适用于基本数据类型,而对象类型只支持引用传递。

复制代码代码如下所示:

功能延长(孩子,父母){

var p = parent.prototype

var c = child.prototype

对于(var i){

c {我} = { } }

}

c.uber = P

}



该方法重构了一些原型属性,并且构造对象的效率较低,但可以减少原型链的查找,但我个人认为这种方法的优点并不明显。

5之间的继承。物体:

除了基于构造函数的继承方法之外,构造函数还可以直接从对象之间继承,即对象属性的直接复制,包括浅拷贝和深拷贝。

浅拷贝:

接受要继承的对象,并创建一个新的空对象,将继承对象的属性复制到新对象并返回新对象:

复制代码代码如下所示:

功能extendcopy(P){

var

对于(var i){

c {我} = { } }

}

c.uber = P

C返回

}



复制完成后,在新对象中需要重写的属性可以手动重写。

深拷贝:

浅拷贝问题也很明显,它不能复制对象类型的属性,只能传递引用。为了解决这一问题,必须采用深度复制,深拷贝的关键是复制的递归调用。当我们检测到对象类型的属性时,我们创建相应的对象或数组,并逐一复制基本类型值。

复制代码代码如下所示:

功能deepcopy(P,C){

C = C | | { }

对于(var i){

如果(p.hasownproperty(I)){

如果(typeof p {我} = 'object){

C {我} = Array.isArray(P { } { } { }):

deepcopy(P {我},C {我})

{人}

c {我} = { } }

}

}

}

返回C

}



一个ES5的array.isarray()方法被用来确定的参数是一个数组。如果没有环境来实现这种方法,就必须手动封装一个垫片。

复制代码代码如下所示:

array.isarray =功能(P){

返回p是数组

}



但是,instanceof运算符的使用不能从不同的框架确定数组变量,但这是不。

6。原型继承:

在父对象的帮助下,由父对象创建一个带有父对象的新对象:

复制代码代码如下所示:

函数对象(o){

var n

函数(f){ }

f.prototype = O

新f()

n.uber = O

返回N

}



在这里,父对象直接设置为它的子对象的原型,和object.create()方法在ES5是它的实现方式。

7。原型继承与属性复制:

在原型继承方法中,我们用父对象作为原型构建子对象。同时,我们还可以导入需要在父对象外部复制属性的对象。

复制代码代码如下所示:

功能ojbectplus(O,东西){

var n

函数(f){ }

f.prototype = O

新f()

n.uber = O

对于(var i){

{ {我} =物料{ }

}

返回N

}



8。多重继承:

此方法不涉及原型链的操作,该链被引入需要复制属性的多个对象,并依次执行属性的完整副本。

复制代码代码如下所示:

函数多(){

var,=,i = 0 { },

arguments.length len =

对于(i = 0;i;;i;+;+){

数据=参数{ }

对于(var中的键){

{ {我} =物料{ }

}

}

返回N

}



根据对象的顺序,它们依次被复制。也就是说,如果后面条目中的对象与前面的对象具有相同的属性,则后者将覆盖前者。

9。构造函数的借用:

电话()和运用()方法在Javascript中是非常好的,和他们不断变化的执行上下文的方式也可以在继承的实现发挥作用的功能。构造函数的借贷是指在子对象构造函数的父对象的构造函数对这个操作使用:

复制代码代码如下所示:

函数(父){ }

parent.prototype.name = 'parent

函数子(){

Parent.apply(这个参数)

}

新子()

console.log(孩子的名字)



这种方法的最大优点是,在子对象的构造函数中,它是子对象属性的完全重构。引用类型的变量将生成一个新值而不是一个引用,因此对子对象上的任何操作都不会影响父对象。

这种方法的缺点是在子对象的构造中不使用新的操作符,因此子对象不继承父原型对象上的任何属性。在上面的代码中,子元素的name属性是未定义的。

为了解决这个问题,该实例可以再次手动设置父子对象构造函数原型对象:

复制代码代码如下所示:

child.prototype =新的母公司()



但这将带来另一个问题,即父对象的构造函数将被调用两次,一次是借用父对象构造函数,另一个是继承原型。

为了解决这个问题,我们需要删除父对象构造函数的调用,并且不能忽略构造函数的借用。然后我们只能删除最后一个调用。实现继承原型的另一种方法是迭代复制。

复制代码代码如下所示:

延长(孩子,父母)



你可以使用延长()方法的实现之前。