原型继承

转载 2015年07月06日 18:48:53

在传统的基于Class的语言如Java、C++中,继承的本质是扩展一个已有的Class,并生成新的Subclass。

由于这类语言严格区分类和实例,继承实际上是类型的扩展。但是,JavaScript由于采用原型继承,我们无法直接扩展一个Class,因为根本不存在Class这种类型。

但是办法还是有的。我们先回顾Student构造函数:

function Student(props) {
    this.name = props.name || 'Unnamed';
}

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
}

以及Student的原型链:

protos

现在,我们要基于Student扩展出PrimaryStudent,可以先定义出PrimaryStudent

function PrimaryStudent(props) {
    // 调用Student构造函数,绑定this变量:
    Student.call(this, props);
    this.grade = props.grade || 1;
}

但是,调用了Student构造函数不等于继承了StudentPrimaryStudent创建的对象的原型是:

new PrimaryStudent() ----> PrimaryStudent.prototype ----> Object.prototype ----> null

必须想办法把原型链修改为:

new PrimaryStudent() ----> PrimaryStudent.prototype ----> Student.prototype ----> Object.prototype ----> null

这样,原型链对了,继承关系就对了。新的基于PrimaryStudent创建的对象不但能调用PrimaryStudent.prototype定义的方法,也可以调用Student.prototype定义的方法。

如果你想用最简单粗暴的方法这么干:

PrimaryStudent.prototype = Student.prototype;

是不行的!如果这样的话,PrimaryStudentStudent共享一个原型对象,那还要定义PrimaryStudent干啥?

我们必须借助一个中间对象来实现正确的原型链,这个中间对象的原型要指向Student.prototype。为了实现这一点,参考道爷(就是发明JSON的那个道格拉斯)的代码,中间对象可以用一个空函数F来实现:

// PrimaryStudent构造函数:
function PrimaryStudent(props) {
    Student.call(this, props);
    this.grade = props.grade || 1;
}

// 空函数F:
function F() {
}

// 把F的原型指向Student.prototype:
F.prototype = Student.prototype;

// 把PrimaryStudent的原型指向一个新的F对象,F对象的原型正好指向Student.prototype:
PrimaryStudent.prototype = new F();

// 把PrimaryStudent原型的构造函数修复为PrimaryStudent:
PrimaryStudent.prototype.constructor = PrimaryStudent;

// 继续在PrimaryStudent原型(就是new F()对象)上定义方法:
PrimaryStudent.prototype.getGrade = function () {
    return this.grade;
};

// 创建xiaoming:
var xiaoming = new PrimaryStudent({
    name: '小明',
    grade: 2
});
xiaoming.name; // '小明'
xiaoming.grade; // 2

// 验证原型:
xiaoming.__proto__ === PrimaryStudent.prototype; // true
xiaoming.__proto__.__proto__ === Student.prototype; // true

// 验证继承关系:
xiaoming instanceof PrimaryStudent; // true
xiaoming instanceof Student; // true

用一张图来表示新的原型链:

proto-extends

注意,函数F仅用于桥接,我们仅创建了一个new F()实例,而且,没有改变原有的Student定义的原型链。

如果把继承这个动作用一个extends()函数封装起来,还可以隐藏F的定义,并简化代码:

function extends(Child, Parent) {
    var F = function () {};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
}

这个extends()函数可以复用:

function Student(props) {
    this.name = props.name || 'Unnamed';
}

Student.prototype.hello = function () {
    alert('Hello, ' + this.name + '!');
}

function PrimaryStudent(props) {
    Student.call(this, props);
    this.grade = props.grade || 1;
}

// 实现原型继承链:
extends(PrimaryStudent, Student);

// 绑定其他方法到PrimaryStudent原型:
PrimaryStudent.prototype.getGrade = function () {
    return this.grade;
};

小结

JavaScript的原型继承实现方式就是:

  1. 定义新的构造函数,并在内部用call()调用希望“继承”的构造函数,并绑定this

  2. 借助中间函数F实现原型链继承,最好通过封装的extends函数完成;

  3. 继续在新的构造函数的原型上定义新方法。

原文地址http://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/0014344997013405abfb7f0e1904a04ba6898a384b1e925000

详解原型链继承

详解原型链继承 2014-10-07 18:30 126人阅读 评论(0) 收藏 举报  分类: Javascript高级(10)  版权声明:本文为博主原...
  • jfkidear
  • jfkidear
  • 2016年09月25日 21:38
  • 1146

解释下原型继承的原理。

prototype与[[prototype]] 在有面象对象基础的前提下,来看一段代码: 1 2 3 4 5 6 7 ...
  • xujie_0311
  • xujie_0311
  • 2015年03月19日 22:26
  • 2108

JavaScript原型、闭包、继承和原型链等等总结

几年之前学习过Javascript,当时学得比较浅显,现在又开始学了,发现Javascript其实挺难的,有些地方还是得花时间去理解的,于是看了很多的视频和博客,自己在这里小小的总结下。。。 1.一切...
  • Ocean111best
  • Ocean111best
  • 2016年09月17日 21:10
  • 1885

js继承,各种继承的优缺点(原型链继承,组合继承,寄生组合继承)

javascript继承方式,以及各种继承的优缺点(原型链继承,组合继承,寄生组合继承)...
  • xuqinggangsls
  • xuqinggangsls
  • 2016年05月24日 15:09
  • 3727

浅析JavaScript原型链与原型链式继承

原型链与原型链式继承上篇总结了下JavaScript的作用域链的问题,欢迎拍砖!浅析JavaScript作用域链继上篇总结中提出的情况 同样都是链,但这两个链往上追朔的顶点是不一样的;并且为了增强...
  • SirM2z
  • SirM2z
  • 2016年01月10日 20:44
  • 1046

javascript中继承(一)-----原型链继承的个人理解

【寒暄】好久没有更新博客了,说来话长,因为我下定决心要从一个后台程序员转为Front End,其间走过了一段漫长而艰辛的时光,今天跟大家分享下自己对javascript中原型链继承的理解。 总的...
  • ladycode
  • ladycode
  • 2016年04月29日 15:51
  • 2899

JS原型继承和类式继承

类式继承(构造函数) JS中其实是没有类的概念的,所谓的类也是模拟出来的。特别是当我们是用new 关键字的时候,就使得“类”的概念就越像其他语言中的类了。类式继承是在函数对象内调用父类的构造函数,使...
  • liuqiao1123
  • liuqiao1123
  • 2016年05月06日 11:26
  • 3691

继承的简单理解,原型对象继承,类继承,混合方式继承

我们都知道构造函数、原型和实例直接的关系,如果我们让原型对象等于另一个类型的实例,结果会怎么样呢?显然此时的原型对象将包含一个指向另一个原型的指针,相应的另一个原型中也包含着一个指向另一个构造函数的指...
  • u013415353
  • u013415353
  • 2016年07月16日 22:56
  • 529

Javascript继承(原型链)

Javascript继承(原型链)   继承是OO语言中的一个最为人津津乐道的概念。许多OO语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。由于...
  • Andy_java_
  • Andy_java_
  • 2016年10月28日 10:22
  • 313

ES6学习——类语法:继承中的原型链

上篇文章中我们讲了些类的基本概念,其中提到ES6中的类语法是对原型继承的一种封装。JS的原型继承一共有两条原型链,显示和隐式,那么在类继承的语法中,这两条原型链又是怎么样的呢? 继续使用上篇文章中的例...
  • kittyjie
  • kittyjie
  • 2016年01月11日 08:00
  • 1460
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:原型继承
举报原因:
原因补充:

(最多只允许输入30个字)