在Java、C++、C#等OO语言中,都支持两种继承方式:接口继承和实现继承。接口继承制继承方法签名,实现继承则继承实际的方法和属性。在SCMAScript中,由于函数没有签名,所以无法实现接口继承,只支持实现继承。

实现继承主要依靠原型链来实现。

一、原型链

原型链是利用原型让一个引用类型继承另一个引用类型的方法,在DOM笔记(十二):又谈原型对象中,描述了构造函数、实例和原型之间的关系:

每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而每个实例都包含一个指向原型对象的内部指针。如果将原型对象作为另一个构造函数的实例,会如何?

function SuperType()
{
this.property = true;
}
SuperType.prototype.getSuperValue=function()
{
return this.property;
};
function SubType()
{
this.subproperty = false;
}
// 继承SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue=function()
{
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true

DOM笔记(十一):JavaScript对象的基本认识和创建中提到了,Object类型是所有对象的根元素。所以,SubType也是继承了Object,内部有一个指针指向Object的原型。  现在,他们的关系变成了这样:

当instance调用getSuperValue()时,会进行搜索:1)在实例中搜索;2)在实例中找不到,就在SubType.prototype中搜索;3)在SubType.prototype中找不到,就在SuperType.prototype中找。找到后就停止搜索,返回结果值。但instance调用toString()时,就一直会搜索到原型链的末端—Object.prototype。

根据原型链,需要注意的是,instance.constructor不再指向SubType,而是指向了SuperType。

二、原型继承小结

  1、确定原型跟实例的关系

DOM笔记(十二):又谈原型对象中提到了isPrototypeOf()方法,另外也可以用instanceof确立这种关系的存在。

//均返回true
alert(instance instanceof Object);
alert(instance instanceof SuperType);
alert(instance instanceof SubType);
alert(Object.prototype.isPrototypeOf(instance));
alert(SuperType.prototype.isPrototypeOf(instance));
alert(SubType.prototype.isPrototypeOf(instance));

2、当在子类中定义与父类中同名的属性和方法时,父类中对应的属性和方法会被屏蔽。

function SuperType()
{
this.property = true;
}
SuperType.prototype.getSuperValue=function()
{
return this.property;
};
function SubType()
{
this.property = false;    //同名属性
}
// 继承SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue=function()
{
return this.property;
};
var instance = new SubType();
alert(instance.getSuperValue()); //false
  3、原型链实现继承,不能使用对象字面量创建原型方法,防止重写原型链。
function SuperType()
{
this.property = true;
}
SuperType.prototype.getSuperValue=function()
{
return this.property;
};
function SubType()
{
this.subproperty = false;
}
// 继承SuperType
SubType.prototype = new SuperType();
// 使用字面量添加新方法,导致上一行无效
SubType.prototype={
getSubValue:function()
{
return this.subproperty;
}
};
var instance = new SubType();
alert(instance.getSuperValue()); //error:undefined is not a function

  4、原型继承存在两个问题:1)引用类型的原型属性会被所有实例共享;2)原型继承时,不能通过子类的实例向父类的构造函数中传递参数。

function SuperType()
{
this.colors = ["red","blue","white"];
}
function SubType(){ }
// 继承SuperType
SubType.prototype = new SuperType();
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //red,blue,white,black
var instance2 = new SubType();
alert(instance2.colors); //red,blue,white,black

instance1和instance2共享了colors数组属性(引用类型)。

三、其他继承方式

1、借用构造函数

利用原型继承时,引用类型的原型属性会被所有实例共享。但是借用构造函数就能避免这种问题,并且能够利用apply()和call()传递参数给父类的构造函数:

function SuperType()
{
this.colors = ["red","blue","white"];
}
function SubType()
{
// 继承SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //red,blue,white,black
var instance2 = new SubType();
alert(instance2.colors); //red,blue,white
  2、组合继承(推荐)
  组合继承即原型继承+构造函数继承:原型链实现对原型属性和方法的继承,构造函数实现对实例属性的继承。
function SuperType(name)
{
this.name=name;
this.colors = ["red","blue","white"];
}
SuperType.prototype.sayName=function()
{
alert(this.name);
};
function SubType(name,age)
{
// 继承SuperType属性,并传递参数给父类的构造函数
SuperType.call(this,name);              //第一次调用SuperType()
this.age=age;
} // 继承SuperType方法
SubType.prototype = new SuperType();      //第二次调用SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge=function()
{
alert(this.age);
};
var instance1 = new SubType("dwqs",20);
instance1.colors.push("black");
alert(instance1.colors); //red,blue,white,black
instance1.sayName(); //dwqs
instance1.sayAge(); //20
var instance2 = new SubType("i94web",25);
alert(instance2.colors); //red,blue,white
instance2.sayName(); //i94web
instance2.sayAge(); //25
  为了确保SuperType构造函数不会重写子类的原型,建议在调用父类构造函数之后再添加子类的属性。
  组合继承的不足之处在于:1)无论什么情况,都会调用两次父类构造函数;2)子类需要重写父类中的属性,并且会包含两组name和colors属性(父类属性):一组在实例上,一组在原型上
  3、原型式继承
  此种方式是借用原型,基于已有的对象创建新的对象,同时不必创建自定义类型。但是需要借助于如下的一个函数:
function object(o)
{
function F(){}
F.prototype=o;
return new F();
}
  o是已经存在的对象,返回的对象与o有相同的属性和方法。
var person={
name:“dwqs”,
colors:["red","blue","white"]
};
var per1 = object(person);
alert(per1.name); //dwqs
// 覆盖已有的属性
per1.name = “i94web”; per1.age = 20; //新增属性
alert(per1.age); //20
per1.colors.push(“black”); alert(per1.name); //i94web
alert(per1.colors); //red,blue,white,black
var per2 = object(person);
alert(per2.name); //dwqs
// 所有实例共享引用类型的属性
alert(per2.colors); //red,blue,white,black

其混乱关系如下:

per1
和per2会共享colors属性(引用类型)。在ECMAScript
5中,Object.create(obj,[props])规范了这种方式:obj是已经存在的对象,props可选,是一个包含额外属性的对象,格式
与Object.defineProperties()的第二个参数格式一样。

var person={
name:"dwqs",
colors:["red","blue","white"]
};
var per1 = Object.create(person,{
age:{
value:20
}
});
alert(per1.age); //20

4、寄生式继承

该方式与原型式继承差不多,也要借用object函数,然后在内部已某种方式来增强对象。

function object(o)
{
function F(){}
F.prototype=o;
return new F();
}
var person={
name:"dwqs",
colors:["red","blue","white"]
};
function createObj(obj)
{
var clone = object(obj);
// 增强对象
clone.age = 20;
clone.Hello=function()
{
alert("Hello,my age is "+this.age);
};
return clone;
}
var per1 = createObj(person);
per1.Hello(); //Hello,my age is 20

  5、寄生组合式继承

在第2中方式,组合继承虽然时JavaScript钟最常用的方式,但是也存在不足,上面已经提到,不再赘述。而寄生组合式继承能解决这两个不足,其基本模式如下:

function object(o)
{
function F(){}
F.prototype=o;
return new F();
}
function inheritPrototype(subType,superType)
{
var obj = object(superType.prototype); //创建对象
obj.constructor = subType; //增强对象
subType.prototype = obj; //指定对象
}

重写组合继承中的示例:

function object(o)
{
function F(){}
F.prototype=o;
return new F();
}
function inheritPrototype(subType,superType)
{
var obj = object(superType.prototype); //创建对象
obj.constructor = subType; //增强对象
subType.prototype = obj; //指定对象
}
function SuperType(name)
{
this.name=name;
this.colors = ["red","blue","white"];
}
SuperType.prototype.sayName=function()
{
alert(this.name);
};
function SubType(name,age)
{
// 继承SuperType属性,并传递参数给父类的构造函数
SuperType.call(this,name);
this.age=age;
}
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge=function()
{
alert(this.age);
};
var instance1 = new SubType("dwqs",20);
instance1.colors.push("black");
alert(instance1.colors); //red,blue,white,black
instance1.sayName(); //dwqs
instance1.sayAge(); //20
var instance2 = new SubType("i94web",25);
alert(instance2.colors); //red,blue,white
instance2.sayName(); //i94web
instance2.sayAge(); //25

只调用了一次SuperType()构造函数,并且避免了SuperType.prototype上创建多余和不必要的属性,原型链保持不变,能够使用instanceof和isPrototypeOf()方法。

原文首发:http://www.ido321.com/1381.html

DOM笔记(十三):JavaScript的继承方式的更多相关文章

  1. JavaScript各种继承方式和优缺点

    好久没写博客啦,嘻嘻,这个月是2017年的最后一个月啦,大家应该都开始忙着写年终总结了吧,嘻嘻,小颖今天给大家分享下Javascript中的几种继承方式以及他们的优缺点. 1.借助构造函数实现继承 原 ...

  2. javascript中继承方式及优缺点(一)

    分别介绍原型链继承.call/apply继承(借用构造函数继承).组合继承.原型式继承.寄生式继承.寄生组合式继承 1. 原型链继承 核心:将父类的实例作为子类的原型 function SuperTy ...

  3. javascript中继承方式及优缺点(三)

    文以<JavaScript高级程序设计>上的内容为骨架,补充了ES6 Class的相关内容,从我认为更容易理解的角度将继承这件事叙述出来,希望大家能有所收获. 1. 继承分类 先来个整体印 ...

  4. javascript中继承方式及优缺点(二)

    一.原型链继承 方式1: 原型链继承 (1)流程: ​ 1.定义父类型构造函数. ​ 2.给父类型的原型添加方法. ​ 3.定义子类型的构造函数. ​ 4.创建父类型的对象赋值给子类型的原型. ​ 5 ...

  5. JavaScript 六种继承方式

    title: JS的六种继承方式 date: 2017-06-27 05:55:49 tags: JS categories: 学习 --- 继承是面向对象编程中又一非常重要的概念,JavaScrip ...

  6. JavaScript各种继承方式(二):借用构造函数继承(constructor stealing)

    一 原理 在子类的构造函数中,通过call ( ) 或 apply ( ) 的形式,调用父类的构造函数来实现继承. function Fruit(name){ this.name = name; th ...

  7. 详解JavaScript对象继承方式

    一.对象冒充 其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数,所以可使 Parent 构造函数成为 Children 的方法,然 ...

  8. JavaScript对象继承方式

    一.对象冒充 其原理如下:构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式).因为构造函数只是一个函数,所以可使 Parent 构造函数 成为 Children 的方法, ...

  9. 谈谈JavaScript中继承方式

    聊一聊js中的继承 一.简单继承---使用原型赋值的方式继承,将实例化的对象,赋值给子级的原型 父级构造函数 function Parent(param) { this.name = 'parent' ...

随机推荐

  1. 【Effective Java】阅读

    Java写了很多年,很惭愧,直到最近才读了这本经典之作<Effective Java>,按自己的理解总结下,有些可能还不够深刻 一.Creating and Destroying Obje ...

  2. vue混入(mixins)

    混入(mixins)是一种分发vue组件中可复用功能的非常灵活的方式.混入对象可以包含任意组件选项. 当组件使用混入对象时,所以混入对象的选项将被混入该组件本身选项,当组件和混入对象含有同名选项时,这 ...

  3. vue环境搭建(一)

    1.Vue依赖node npm命令执行,需要下载node 下载地址 2.安装全局vue-cli脚手架(搭建环境所需要模板),  window+ r 打开命令工具,输入cmd  ,这时显示命令行工具,输 ...

  4. 关于imageview matrix

    Matrix 是 Android SDK 提供的一个矩阵类,它代表一个 3 X 3 的矩阵 那么这 9 个浮点数的作用和意义是什么呢,从 Android 官方文档上看,它为这个数组中的每一个元素都定义 ...

  5. C# 反射 Type.GetFields 方法

    using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Reflecti ...

  6. 怎么查找Linux系统调用

    目前都是这么找: git grep SYSCALL_DEFINE | grep signal系统调用的函数名本来是叫sys_xxx,现在都是用宏SYSCALL_DEFINE(XXX),搞得无法找到sy ...

  7. WSGI学习系列Pecan

    Pecan Introduce Pecan是一个轻量级的基于Python的Web框架, Pecan的目标并不是要成为一个“full stack”的框架, 因此Pecan本身不支持类似Session和D ...

  8. [PHP]memcache安装

    1.memcached 安装sudo apt-get install memcached memcached 参数说明memcached -d -m 50 -p 11211 -u root-m 指定使 ...

  9. winfrom C#树勾选等

    AfterCheck /// <summary> /// 树勾选 /// </summary> /// <param name="sender"> ...

  10. Jvm方法区以及static的内存分配图

    前面的几篇都没有太明确地指出 方法区 是什么?现在通过一些资料的收集和学习,下面做一些总结 什么是方法区: 方法区是系统分配的一个内存逻辑区域,是JVM在装载类文件时,用于存储类型信息的(类的描述信息 ...