前言:

  继承 是 OO 语言中的一个最为人津津乐道的概念。许多 OO 语言都支持两种继承方式:接口继承 和 实现继承。接口继承只继承方法签名,而实现继承则继承实际的方法。如前所述,由于函数没有签名,在 ECMAScript 中无法实现接口继承。

  ECMAScript 只支持实现继承,而且其 实现继承 主要依靠 原型链 来实现的。

继承方式:

  1、原型链继承

    ECMAScript 中将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。

function SuperType(){
this.property=true;
}
SuperType.prototype.getSuperValue=function(){
return this.property;
}
function SubType(){
this.subproperty=false;
}
//通过创建SuperType的实例继承了SuperType
SubType.prototype=new SuperType(); SubType.prototype.getSubValue=function(){
return this.subproperty;
}
var instance=new SubType();
alert(instance.getSuperValue()); //true

  缺点:

  (1)、包含引用类型值的原型属性会被所有实例共享,这会导致对一个实例的修改会影响另一个实例;

  (2)、在创建子类型的实例时,不能向超类型的构造函数中传递参数。

  由于这两个问题的存在,实践中很少单独使用原型链  

  

  下面例子清楚的说明了第一个问题

function SuperType(){
this.colors=["red", "blue", "green"];
}
function SubType(){
}
//继承了SuperType
SubType.prototype=new SuperType();
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,black

  2、借用 构造函数 实现继承

  在解决原型中包含引用类型值所带来的问题中,使用借用构造函数技术来解决。

   借用构造函数的基本思想,即在子类型构造函数的内部调用超类型构造函数。

   函数只不过是在特定环境中执行代码的对象,因此通过使用 apply()call() 方法可以在新创建的对象上执行构造函数。

function SuperType(name){
this.name=name;
}
function SubType(){
//继承了SuperType,同时还传递了参数
SuperType.call(this,"mary");
//实例属性
this.age=22;
}
var instance=new SubType();
alert(instance.name); //mary
alert(instance.age); //

  缺点:

  (1)、无法避免构造函数模式存在的问题,方法都在构造函数中定义,因此无法复用函数;

  (2)、在超类型的原型中定义的方法,对子类型而言是不可见的。

  因此这种技术很少单独使用。

  3、组合继承

  组合继承,指的是将原型链和借用构造函数的技术组合到一起。

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

   这样,既通过在原型上定义方法实现了函数的复用,又能够保证每个实例都有它自己的属性。

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); // 第一次调用 SuperType
this.age=age;
}
//继承方法 使用原型链实现
SubType.prototype=new SuperType(); // 第二次调用 SuperType
SubType.prototype.constructor=SubType;
subType.prototype.sayAge=function(){
alert(this.age);
};
var instance1=new SubType("mary", 22);
instance1.colors.push("black");
alert(instance1.colors); //red,blue,green,black
instance1.sayName(); //mary
instance1.sayAge(); // var instance2=new SubType("greg", 25);
alert(instance2.colors); //red,blue,green
instance2.sayName(); //greg
instance2.sayAge(); //

  组合继承避免了原型链和借用构造函数的缺点,融合了他们的优点,是JavaScript中最常用的继承模式。

   缺点:无论在什么情况下,都会调用两次超类型构造函数,一次是在创建子类型原型的时候,一次是在子类型构造函数的内部

  4、原型式继承

    原型式继承是借助原型可以基于已有的对象创建新对象,同是还不比因此创建自定义类型。

function object(o) {
function F() {}
F.prototype = o
return new F()
}

    在 object 函数内部,先创建一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。

    本质上来说,object对传入其中的对象执行了一次浅复制。

var person = {
name: 'Gaosirs',
friends: ['Shelby', 'Court']
}
var anotherPerson = object(person)
console.log(anotherPerson.friends) // ['Shelby', 'Court']

    这种模式要去你必须有一个对象作为另一个对象的基础。

    在这个例子中,person作为另一个对象的基础,把person传入object中,该函数就会返回一个新的对象。

    这个新对象将person作为原型,所以它的原型中就包含一个基本类型和一个引用类型。

    所以意味着如果还有另外一个对象关联了person,anotherPerson修改数组friends的时候,也会体现在这个对象中。

  

  Object.create()方法

    ES5通过Object.create()方法规范了原型式继承,可以接受两个参数,一个是用作新对象原型的对象和一个可选的为新对象定义额外属性的对象,行为相同,基本用法和上面的object一样,除了object不能接受第二个参数以外。

var person = {
name: 'Gaosirs',
friends: ['Shelby', 'Court']
}
var anotherPerson = Object.create(person)
console.log(anotherPerson.friends) // ['Shelby', 'Court']

  5、寄生式继承

   寄生式继承的思路寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程中的函数,该函数在内部已某种方式来增强对象,最后返回对象。

function createAnother(o) {
var clone = Object.create(o) // 创建一个新对象
clone.sayHi = function() { // 添加方法
console.log('hi')
}
return clone // 返回这个对象
}
var person = {
name: 'GaoSirs'
}
var anotherPeson = createAnother(person)
anotherPeson.sayHi()

  缺点:使用寄生式继承来为对象添加函数,会因为做不到函数复用而降低效率,这个与构造函数模式类似。

  6、寄生组合式继承

      在前面说的组合模式(原型链+构造函数)中,继承的时候需要调用两次父类构造函数。

    使用寄生组合式继承,可以规避这些问题。

    这种模式通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。

    本质上就是使用寄生式继承来继承父类的原型,在将结果指定给子类型的原型。

    

function inheritPrototype(subType, superType) {
var prototype = Object.create(superType.prototype); // 创建对象
prototype.constructor = subType;            // 增强对象       
subType.prototype = prototype;             // 指定对象
}

    该函数实现了寄生组合模式的最简单形式。 

    这个函数接受两个参数,一个子类,一个父类。

    第一步创建父类原型的副本,第二步将创建的副本添加constructor属性,第三部将子类的原型指向这个副本。

function SuperType(name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType(name, job) {
// 继承属性
SuperType.call(this, name)
this.job = job
}
// 继承
inheritPrototype(SubType, SuperType)
var instance = new SubType('Gaosirs', 'student')
instance.sayName()

  

  补充:直接使用Object.create来实现,其实就是将上面封装的函数拆开,这样演示可以更容易理解。

function SuperType(name) {
this.name = name
this.colors = ['red', 'blue', 'green']
}
SuperType.prototype.sayName = function () {
console.log(this.name)
}
function SubType(name, job) {
// 继承属性
SuperType.call(this, name)
this.job = job
}
// 继承
SubType.prototype = Object.create(SuperType.prototype)
// 修复constructor
SubType.prototype.constructor = SubType
var instance = new SubType('Gaosirs', 'student')
instance.sayName()

  优点:只调用了一次 supertype 构造函数,因此避免在subtype.prototype上创建不必要的,多余的属性,与此同时,原型链还能保持不变,还能正常使用instanceof 和isPrototypeOf(),因此,寄生组合式继承被认为是引用类型最理想的继承范式。

  7、ES6中 Class ...  extends 关键字实现继承

    基本用法:Class之间通过使用extends关键字,这比通过修改原型链实现继承,要方便清晰很多

class Colorpoint extends Point {
constructor(x,y,color){
super(x,y); //调用父类的constructor(x,y)
this.color = color
}
toString(){
//调用父类的方法
return this.color + ' ' + super.toString();
}
}

    子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类没有自己的this对象,而是继承父类的this对象,然后对其进行加工,如果不调用super方法,子类就得不到this对象。因此,只有调用super之后,才可以使用this关键字。

    prototype 和__proto__:

    一个继承语句同时存在两条继承链:一条实现属性继承,一条实现方法的继承

class A extends B{}
A.__proto__ === B; //继承属性
A.prototype.__proto__ == B.prototype;//继承方法
随笔整理自
  https://www.cnblogs.com/annika/p/9073572.html
感谢博主分享!

JS 面向对象 ~ 继承的7种方式的更多相关文章

  1. js实现继承的5种方式 (笔记)

    js实现继承的5种方式 以下 均为 ES5 的写法: js是门灵活的语言,实现一种功能往往有多种做法,ECMAScript没有明确的继承机制,而是通过模仿实现的,根据js语言的本身的特性,js实现继承 ...

  2. js 实现继承的几种方式

    //js中实现继承的几种方式 //实现继承首先要有一个父类,先创造一个动物的父类 function Animal(name){ this.name = name; this.shoot = funct ...

  3. js 实现继承的6种方式(逐渐优化)

    <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8&quo ...

  4. js实现继承的5种方式

    js是门灵活的语言,实现一种功能往往有多种做法,ECMAScript没有明确的继承机制,而是通过模仿实现的,根据js语言的本身的特性,js实现继承有以下通用的几种方式1.使用对象冒充实现继承(该种实现 ...

  5. js实现继承的两种方式

    这是面试时面试官会经常问到问题: js的继承方式大致可分为两种:对象冒充和原型方式: 一.先说对象冒充,又可分为3种:临时属性方式.call().apply(): 1.临时属性方式: 当构造对象son ...

  6. 深入浅出js实现继承的7种方式

    给大家介绍7中js继承的方法 有些人认为JavaScript并不是真正的面向对象语言,在经典的面向对象语言中,您可能倾向于定义类对象,然后您可以简单地定义哪些类继承哪些类(参考C++ inherita ...

  7. JS 面向对象 ~ 创建对象的 9 种方式

    一.创建对象的几种方式 1.通过字面量创建 var obj = {}; 这种写法相当于: var obj = new Object(); 缺点:使用同一个接口创建很多单个对象,会产生大量重复代码 2. ...

  8. JavaScript面向对象(三)——继承与闭包、JS实现继承的三种方式

      前  言 JRedu 在之前的两篇博客中,我们详细探讨了JavaScript OOP中的各种知识点(JS OOP基础与JS 中This指向详解 . 成员属性.静态属性.原型属性与JS原型链).今天 ...

  9. JS实现继承的几种方式

    前言 JS作为面向对象的弱类型语言,继承也是其非常强大的特性之一.那么如何在JS中实现继承呢?让我们拭目以待. JS继承的实现方式 既然要实现继承,那么首先我们得有一个父类,代码如下: // 定义一个 ...

随机推荐

  1. 第21章 登录 - Identity Server 4 中文文档(v1.0.0)

    为了使IdentityServer能够代表用户发出令牌,该用户必须登录IdentityServer. 21.1 Cookie身份验证 使用由ASP.NET Core中的cookie身份验证处理程序管理 ...

  2. [.NET] 《Effective C#》快速笔记(二)- .NET 资源托管

    <Effective C#>快速笔记(二)- .NET 资源托管 简介 续 <Effective C#>读书笔记(一)- C# 语言习惯. .NET 中,GC 会帮助我们管理内 ...

  3. 【JVM】7、深入理解Java G1垃圾收集器

    本文首先简单介绍了垃圾收集的常见方式,然后再分析了G1收集器的收集原理,相比其他垃圾收集器的优势,最后给出了一些调优实践. 一,什么是垃圾回收 首先,在了解G1之前,我们需要清楚的知道,垃圾回收是什么 ...

  4. npm --save 、-D 、--save -dev

    npm install 和 npm i 是一样 --save 和 -S 是一样 --save-dev 和 -D 是一样的 区别: -S, --save 安装包信息将加入到dependencies(生产 ...

  5. Arcpy多线程热力图

        起因是这样一段对话,领导:你会用脚本生成热力图图片吗?我:可以研究下.领导:那这个需求就给你了.我:......     经过一番研究,研究出大概的思路,先将有经纬度的表中的数据筛选出表并生成 ...

  6. android java.lang.IllegalStateException: Circular dependencies cannot exist in RelativeLayout

    造成这个问题的原因是在xml文件中出现了重复依赖,何为重复依赖,如下:   以上便叫重复依赖 转载请标明出处:http://www.cnblogs.com/tangZH/p/8386978.html

  7. Jquery 使用和Jquery选择器

    jQuery中的顶级对象($) jQuery 中最常用的对象即 $ 对象,要想使用 jQuery 的方法必须通过 $ 对象.只有将普通的 Dom 对象封装成 jQuery 对象,然后才能调用 jQue ...

  8. uiautomatorviewer 查看元素报错: Error taking device screenshot: null 原因

    使用uiautomatorviewer 查看android某些页面元素,出现错误Error obtaining UI hierarchy  Reason: Error taking device sc ...

  9. 定时删除所有文件夹下的_desktop.ini文件

    写个批处理,删除对应的文件,命名为DELDesktopIni.bat,存于D盘根目录 @echo off :delini for %%a in ( C: D: E: ) DO ( del /f/s/a ...

  10. MongoDB 中的【加减乘除】运算

    很多同学因为对MongoDB不熟悉,加之应用的不是很多,有时候会认为MongoDB数据库对一些功能不支持,或者认为支持不好.今天我们 演示一下 MongoDB对“加减乘除”的使用. 在MongoDB数 ...