原型,原型链,继承

Posted Angel Home

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原型,原型链,继承相关的知识,希望对你有一定的参考价值。

原型,原型链,继承

1.原型

原型的概念源于构造函数,我们js在es6之前没有具体类的概念,我们通过构造函数来实现类,在创建构造函数的同时,我们没有显式的创建一个对象,而是在实例这个构造函数时,创建了一个对象实例,让这个实例继承了构造函数上的属性和方法。

我们首先看一个简单的构造函数

this is a Person Class

function Person(name) {

this.name = name;

this.sayName = function() {

console.log(\'my name is \' + this.name);

}

}

var person = new Person(\'liming\');

// my name is liming 

第一,我们通过上面的自定义构造函数可以实现了一个简单的类,那么在这整个过程中,我们为什么要引入原型的概念?

通过以上自定义构造函数,我们实现一个简单的Person类;但是我们发现,每实例一个person,都需要重新定义一个sayName方法,或者我们通过以下方法实现

function Person(name) {

this.name = name;

sayName(name);

}

function sayName(name) {

console.log(\'my name is \' + name);

}

var person = new Person(\'liming\');

// my name is liming

这样我们也可以实现类,但是过多的方法,会生成很多全局方法

综上我们引入了原型的方法,即每个构造函数都有一个原型属性prototype,所有的实例都将从prototype上继承方法和属性

 

第二,我们看一下在new的过程中,发生了什么?能够让实例继承构造函数的方法和变量?

使用js模拟new方法如下,来了解new的过程

function _new() {

// 创建一个新对象{}

let target = {};

let {constructor, ...arg} = [...arguments];

// 原型链链接

target._proto_ = constructor.prototype;

// 将构造函数的属性和方法添加到新的对象上

let result = constructor.apply(target, args);

if (result && (typeof result === \'object\' || typeof result === \'function\')) {

// 如果构造函数返回一个对象,那就返回这个对象

return result

}

// 如果构造函数返回的不是一个对象,那就返回这个新对象

return target

}

第三,了解完这两点之后,我们了解到,每次创建一个构造函数,都会创建一个prototype属性,这个属性是一个指针,指向这个构造函数所有的实例对象,这个指针上面挂有所有指定类的所有属性和方法;这个prototype就是我们今天所说的原型

 

2.原型链

2.1.原型链

我们如何更系统的了解构造函数,原型,实例之间的

通过对原型的了解,我们知道每一个对象都有对应的原型对象, 原型对象也有自己的原型对象,这样就形成了一个链式结构

person -> Person.prototype -> Object.protoype -> null

在原型链中查找属性时,从对象本身一直到查到原型链的终点;为了更好的了解原型链,我们在上图添加更好的描述原型链。

 

原型链如图所示,其实值得注意的有两点(红色圈出)

(1)构造函数的prototype和_proto_都指向了构造函数的原型对象,这是因为构造函数的构造函数是本生,_proto_是可认为是作为实例角色访问的

 

(2)Object的_proto_=null也是整个原型链的终点;

(3)Object.getPrototype(person)可以获取一个对象的原型对象-这个在利用原型链实现继承的时候是很重要的一个方法;

(4)通过实例不可以修改原型中的属性值,需要的重新设置值进行覆盖;

(5)可以通过Person.hasOwnPrototype()方法检测一个属性是来自实例还是 原型;

(6)in-无论属性存在于实例还是原型中,都会返回true;

for-in 包括对象中不可枚举的属性,实例和原型中的属性都可以遍历到;- IE8以后开发人员设置的属性都是可枚举的;

(7)keys获取所有可枚举的实例属性,getOwnPrototype获取所有实例属性,不管可枚举还是不可枚举

(8)在创建一个构造函数同时,会隐形的创建一个prototype属性,这个属性会自动获得construtor属性,下面的写法会重写prototype,导致construtor不在指定Proson

 

2.2 原型对象的问题

所有实例共享原型属性,对于引用变量,存在一处修改,多处被修改的问题(基础变量不存在)

2.3.构造函数模式和原型模式

所有实例共享属性放在prototype上定义,属于自身所有属性放在构造函数中进行定义

2.4.动态原型模式

将所有的信息都封装到构造函数中,在必要的时候对原型进行初始化

2.5.寄生构造函数模式

这种模式下,构造函数返回的对象和构造函数或构造函数的原型属性没有任何关系,和定义在外部的对象一样

3.继承

继承就是让一个对象可以访问另一个对象的属性和方法,我们学习了原型链,也可以领悟到继承的概念,但是都大多是内置对象的继承。es6中的继承也主要依靠原型链进行实现。

 

person -> Person.prototype -> Object.prototype

 

3.1原型继承

 

 

由上图我们可以看出,subType的原型本来指向的是Object的原型,我们通过将superType的实例赋给subType的方式,改变的subType原型的指向,利用原型链实现了继承。

这种继承存在的弊端

  1. 子类要重写或添加新的方法,需要在父类实例覆盖子类原型之后

 

  1. 子类不能通过字面量的方式添加属性,会导致子类原型属性被重写;子类与父类的联系被切断

 

  1. 之前说到原型模式的时候,由于所有的实例共享原型属性,引起的引用类型问题;我们在用原型链实现继承的时候,原型实际上会变成另一个的实例,那么实例的属性和方法都会被继承,实例上的引用变量也会被原型的实例继承和修改。

3.2借用构造函数/伪造对象/经典继承

  1. 为保证父类不重写子类属性,在子类继承父类之后,再对子类属性进行定义

  2. 单独使用构造函数,原型属性和方法共享就无从谈起了,所有的实例只能从构造函数上获取

3.3组合继承/伪经典继承

利用原型链实现对原型属性和方法的继承,使用构造函数实现对实例属性的继承

 

 

  1. 融合了原型和构造函数的所有优点,是比较常用的继承方式

  2. 多次调用父类构造函数引起的效能问题--至少两次调用父类构造函数

3.4原型式继承

原理:基于已有对象创建新对象

 

  1. 像原型模式一样,存在引用类型共享的问题

3.5寄生式继承

原理:封装一个继承功能的函数,根据调用函数创建一个基于父类的新对象,在该函数内部在对该函数进行加强。

  1. 与构造函数模式相似,由于函数不能复用,效率低下

3.6寄生组合继承

原理:使用构造函数来继承属性,使用原型链继承原型属性但是,在指定子类型的原型时,不用实例化父类的构造函数

  1. 引用类型最适合的继承方式

  2. 保持原型链没改变

以上是关于原型,原型链,继承的主要内容,如果未能解决你的问题,请参考以下文章

原型,原型链,给予原型和class的继承

javascript类继承系列二(原型链)

理解js中是原型链? 如何实现继承?

javascript 的继承

JavaScript高级原型和继承相关:原型对象函数原型原型链和继承继承的优化对象判断相关方法

JavaScript高级原型和继承相关:原型对象函数原型原型链和继承继承的优化对象判断相关方法