这里先说基于原型链实现的继承。那首先就得明白什么是原型链了;

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。

那么,假如我们让原型对象等于另一个类型的实例,此时的原型对象将包含指向另一个原型对象的指针,相应地,另一个原型对象也包含着指向另一个构造函数的指针。

假如另一个原型又是另一个类型的实例,那么上述关系依然成立。层层递进,就成一条链了。

实现原型链的基本模式:

function SuperType() {

  this.property = true;

}

SuperType.prototype.getSuperValue = function() {

  return this.property;

}

function SubType() {

  this.subproperty = false;

}

SubType.prototype = new SuperType();  //实现继承

SubType.prototype.getSubValue = function() {

  return this.subproperty;

}

var instance = new SubType();

alert(instance.getSuperValue); // true

上述SubType通过重写原型对象的方式继承了SuperType,所以原来位于SuperType里的方法,现在也位于SubType里了,我们没有使用SubType默认提供的原型,而是给它换了一个新原型。这个新原型就是SuperType的实例。

于是,新原型不仅有作为SuperType实例所有的属性和方法,内部还有一个指针,指向了SuperType的原型。

从而,instance指向SubType的原型,SubType的原型又指向SuperType的原型;getSuperValue()的方法还是在SuperType.prototype中,但property则位于SubType.prototype中,因为property是一个实例属性,而getSuperValue()则是一个原型方法。既然SubType.prototype现在是SuperType的实例,那么property当然位于该实例中了

此外,instance.constructor现在指向的是SuperType了,因为原来SubType.prototype指向了SuperType的原型,而这个原型对象的constructor属性指向的是SuperType

默认的原型

我们知道,所有的引用类型默认都继承了Object,而这个继承也是原型链实现的。所有的函数的默认原型都是Object的实例,因此默认原型里面都会包含一个指针,指向Object.prototype

这也是所有自定义类型都会继承toString(),valueof()等默认方法的根本原因

原型和实例的关系

alert(isntance instanceof Object) ; // true

alert(isntance instanceof SuperType);  // true

alert(isntance instanceof SubType) ; // true

alert(Object.prototype.isPrototypeOf(instance));  // true

alert(SuperType.prototype.isPrototypeOf(instance)); // true

alert(SubType.prototype.isPrototypeOf(instance)); // true

通过原型链实现继承,不能使用字面量对象创建原型方法,那样会重写原型链,切断原来原型与实例之间的联系

原型链存在的问题:

(1)最主要的问题是来自包含引用类型值的原因

举例:

function SuperType() {

  this.colors = ['red', 'yellow', 'blue'];

}

function SubType() {

}

SubType.prototype = new SuperType();

var instance1 = new SubType();

instance1.colors.push('black');

var instance2 = new SubType();

alert(instance1.colors);  // 'red,yellow,blue,black'

alert(instance2.colors); // 'red,yellow,blue,black'

SuperType的每个实例都会包含各自的colors属性,当SubType通过原型链继承之后,SubType.prototype也包含了一个colors属性,为所有实例所共享,所有修改instance1,会反映到instance2

(2)另一个问题:不能向超类型的构造函数中传递参数

解决办法:

1.借用构造函数(经典继承)

function SuperType () {

  this.colors = ['red', 'blue', 'green'];

}

function SubType() {

  SuperType.call(this)

}

var instance1 = new SubType();

instance1.colors.push('black');

alert(instance1.colors); // "red,blue,green,black"

var instance2 = new SubType();

alert(instance2.colors) // "red,blue,green"

代码红色部分借调了超类型的构造函数,通过使用call()方法,我们实际上是在(未来将要)新创建的SubType实例的环境下调用了SuperType构造函数。就会在新SubType对象上执行SuperType函数中定义的所有对象初始化代码

优势: 传递参数,可以在子类型构造函数中向超类型构造函数传递参数

function SuperType (name) {

  this.name = name;

}

function SubType () {

  SuperType.call (this, 'bob');  //继承SuperType,同时传递参数

  this.age = 29;  //实例属性

}

var instance = new SubType();

alert(instance.name); // 'bob'

alert(instance.age);  //29

问题:方法都在构造函数中定义,无法复用。而且,在超类型原型中定义的方法,对子类型不可见

2.组合继承

思路:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承

function SuperType(name) {

  this.name = name;

  this.colors = ['red', 'blue', 'green'];

}

SuperType.prototype.sayName = function() {

  alert(this.name);

};

function SubType (name, age) {

  SuperType.call(this, name);  //继承属性

  this.age = age;

}

// 继承方法

SubType.prototype = new SuperType();

SubType.prototype.constructor = SubType;

SubType.prototype.sayAge = function () {

  alert(this.age);

};

var instance1 = new SubType('bob', 20);

instance1.colors.push('black');

alert(instance1.colors); // "red,blue,green,black"

instance1.sayName();  // 'bob'

instance1.sayAge();  // 20

var instance2 = new SubType('alice', 21);

alert(instance2.colors); // "red,blue,green"

instance2.sayName(); // 'alice'

instance2.sayAge(); // 21

3.原型式继承(object.create())

实现原理

function object(o) {

  function F(){}

  F.prototype = o;

  return new F()  //返回的是构造函数的实例,所以只有_proto_属性,指向F.prototype

}

举例:

var person = {

  name: 'bob',

  friends: ['a', 'b', 'c']

}

var anotherPerson = Object.create(person);

anotherPerson.name = 'alice';

anotherPerson.friends.push('d');

var yetAnotherPerson = Object.create(person);

yetAnotherPerson.name = 'Linda';

yetAnotherPerson.friends.push('e');

alert(person.friends);  // 'a,b,c,d,e'

在没有必要兴师动众地创建构造函数,而只想让一个对象与一个对象保持类似的情况下,可以用原型式继承,不过,

包含引用类型的值的属性始终都要共享相应的值,就像使用原型模型一样

4.寄生式继承

function createAnother (original) {

  var clone = object.create(original);  // 通过调用函数创建一个新对象

  clone.sayHi = function() {   // 以某种方式增强对象

    alert('Hi');

  }

  return clone;  //返回这个对象

}

var person = {

  name: 'bob',

  friends: ['a', 'b', 'c']

}

var anotherPerson = createAnother(person);

anotherPerson对象具有了person所有属性和方法, 由于采用object.create()创建,所以类似于原型式继承

5.寄生组合式继承

组合式继承最大的不足是无论什么情况下,会调用两次超类型构造函数;一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。

看下面例子:

function SuperType(name) {

  this.name = name;

  this.colors = ['red', 'blue', 'green']

}

SuperType.prototype.sayName = function() {

  alert(this.name)

}

function SubType() {

  SuperType.call(this,name);  //第二次调用SuperType()

  this.age = age;

}

SubType.prototype = new SuperType();  //第一次调用SuperType()

SubType.prototype.constructor = SubType;

SubType.prototype.sayAge = function() {

  alert(this.age);

}

解释: 第一次调用SuperType()时,将SuperType()的实例属性赋给了SubType.prototype;相当于在SubType.prototype上创建了两个属性name,colors

第二次调用SubType()构造函数时,又会调用SuperType()一次,这次又在新对象上创建了实例属性name和colors,于是,这两个属性就屏蔽了原型中的两个同名属性。这样就在SubType.prototype上创建了多余的不必要的属性

寄生组合式继承:不必为了指定子类型的原型而调用超类型构造函数,我们所需要的无非就是超类型原型的一个副本而已。

        本质上,就是利用寄生式继承来继承超类型的原型,再将结果指定给子类型原型

function inheritPrototype(subType, superType) {

  var prototype = object.create(superType.prototype);  //创建对象

  prototype.constructor = subType;    // 增强对象

  subType.prototype = prototype;  //指定对象

}

所以寄生式组合继承:

function SuperType(name) {

  this.name = name;

  this.colors = ['red', 'blue', 'green']

}

SuperType.prototype.sayName = function() {

  alert(this.name)

}

function SubType() {

  SuperType.call(this,name);

  this.age = age;

}

inheritPrototype(subType, superType);

SubType.prototype.sayAge = function() {

  alert(this.age);

}

js 继承 原型链的更多相关文章

  1. 一篇文章理解JS继承——原型链/构造函数/组合/原型式/寄生式/寄生组合/Class extends

    说实在话,以前我只需要知道"寄生组合继承"是最好的,有个祖传代码模版用就行.最近因为一些事情,几个星期以来一直心心念念想整理出来.本文以<JavaScript高级程序设计&g ...

  2. JS继承——原型链

    许多OO语言支持两种继承:接口继承和实现继承.ECMAScript只支持实现继承,且继承实现主要依赖原型链实现. 原型链 基本思想:利用原型让一个引用类型继承另一个引用类型的属性和方法. 构造函数.原 ...

  3. 怎么理解js的原型链继承?

    前言 了解java等面向对象语言的童鞋应该知道.面向对象的三大特性就是:封装,继承,多态. 今天,我们就来聊一聊继承.但是,注意,我们现在说的是js的继承. 在js的es6语法出来之前,我们想实现js ...

  4. 前端基本知识(二):JS的原型链的理解

    之前一直对于前端的基本知识不是了解很详细,基本功不扎实,但是前端开发中的基本知识才是以后职业发展的根基,虽然自己总是以一种实践是检验真理的唯一标准,写代码实践项目才是唯一,但是经常遇到知道怎么去解决这 ...

  5. Javascript 组合继承 原型链继承 寄生继承

    Javascript继承通常有三种方式. 第一种:组合式继承: function SuperType(name) { this.name = name; this.colors = ["re ...

  6. js javascript 原型链详解

    看了许多大神的博文,才少许明白了js 中原型链的概念,下面给大家浅谈一下,顺便也是为了巩固自己 首先看原型链之前先来了解一下new关键字的作用,在许多高级语言中,new是必不可少的关键字,其作用是为了 ...

  7. JS中原型链继承

    当我们通过构造函数A来实现一项功能的时候,而构造函数B中需要用到构造函数A中的属性或者方法,如果我们对B中的属性或者方法进行重写就会出现冗杂的代码,同时写出来也很是麻烦.而在js中每个函数都有个原型, ...

  8. JS 面向对象之继承 -- 原型链

    ECMAScript只支持实现继承,其实现继承主要是靠原型链来实现. 原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 简单回顾下构造函数.原型和实例的关系: 每个构造函数都有 ...

  9. js重点--原型链继承详解

    上篇说过了关于原型链继承的问题,这篇详解一下. 1. function animals(){ this.type = "animals"; } animals.prototype. ...

随机推荐

  1. LVS(IPVS)了解

    从来都只是看文章,现在手工作一下. 参考URL: https://blog.csdn.net/langyue919/article/details/80935197 https://www.cnblo ...

  2. C#学习-析构函数

    析构函数用于在类销毁之前释放类实例所使用的托管和非托管资源. 对于C#应用程序所创建的大多数对象,可以依靠.NET Framework的垃圾回收器(GC)来隐式地执行内存管理任务. 但是,若创建封装了 ...

  3. AtCoder Regular Contest 101

    C题是个傻逼题, 一定是先向右,然后停了或者向左走到某一个点(左边同理)模拟就可以了 D题想了一会才想出来 和tjoi那道排序挺像的 二分答案变0/1来做 刚开始写的时候还把自己作为另外一类搞出来 这 ...

  4. bind注意事项(传引用参数的时候)

    默认情况下,bind的那些不是占位符的参数被拷贝到bind返回的可调用对象中. 当需要把对象传到bind中的参数中时,需要使用ref或者cref. 例如: #include<iostream&g ...

  5. jenkins创建svn项目自动部署

    Exec command ; cd /home/zhxjdk18/server/jenkins_project/owgd_site/admin; rm -rf * ; source .bashrc ; ...

  6. oralce不像Java,java中字符串+数字,能够得到结果字符串

    oracle得到的两个字段进行相加,要求都是number类型的,如果两个是字符串会自动转成number类型(前提是能够转) select a+b from (select '1' a,'2' b fr ...

  7. 20165220 2017-2018-2《Java程序设计》课程总结

    每周作业链接汇总: 20165220 我期望的师生关系 20165220 学习基础和C语言基础调查 20165220 Linux安装及学习 20165220 第一周学习总结 20165220 第二周学 ...

  8. Activity的启动

    --摘自<android插件化开发指南> 1.AMS管理着四大组件 2.为什么Hook不能在AMS那边?因为AMS属于android系统,android系统可以被Hook,那就是病毒了.四 ...

  9. 【LeetCode算法-14】Longest Common Prefix

    Write a function to find the longest common prefix string amongst an array of strings. If there is n ...

  10. UVA 315 Network (模板题)(无向图求割点)

    <题目链接> 题目大意: 给出一个无向图,求出其中的割点数量. 解题分析: 无向图求割点模板题. 一个顶点u是割点,当且仅当满足 (1) u为树根,且u有多于一个子树. (2) u不为树根 ...