JavaScript继承基础讲解,原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承
说好的讲解JavaScript继承,可是迟迟到现在讲解。废话不多说,直接进入正题。
既然你想了解继承,证明你对JavaScript面向对象已经有一定的了解,如还有什么不理解的可以参考《面向对象JS基础讲解,工厂模式、构造函数模式、原型模式、混合模式、动态原型模式》,接下来讲一般通过那些方法完成JavaScript的继承。
原型链
JavaScript中实现继承最简单的方式就是使用原型链,将子类型的原型指向父类型的实例即可,即“子类型.prototype = new 父类型();”,实现方法如下:
// 为父类型创建构造函数
function SuperType() {
this.name = ['wuyuchang', 'Jack', 'Tim'];
this.property = true;
} // 为父类型添加方法
SuperType.prototype.getSuerperValue = function() {
return this.property;
} // 为子类型创建构造函数
function SubType() {
this.test = ['h1', 'h2', 'h3', 'h4'];
this.subproperty = false;
} // 实现继承的关键步骤,子类型的原型指向父类型的实例
SubType.prototype = new SuperType(); // 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空
SubType.prototype.getSubValue = function() {
return this.subproperty;
} /* 以下为测试代码示例 */
var instance1 = new SubType();
instance1.name.push('wyc');
instance1.test.push('h5');
alert(instance1.getSuerperValue()); // true
alert(instance1.getSubValue()); // false
alert(instance1.name); // wuyuchang,Jack,Tim,wyc
alert(instance1.test); // h1,h2,h3,h4,h5 var instance2 = new SubType();
alert(instance2.name); // wuyuchang,Jack,Tim,wyc
alert(instance2.test); // h1,h2,h3,h4
可以看到如上的代码就是通过原型链实现的一个简单的继承,但看到测试代码示例中还是存在些问题。相信看了我的博文《面向对象JS基础讲解,工厂模式、构造函数模式、原型模式、混合模式、动态原型模式》的童鞋一定知道原型链代码存在的第一个问题是由于子类型的原型是父类型的实例,也就是子类型的原型中包含的父类型的属性,从而导致引用类型值的原型属性会被所有实例所共享。以上代码的instance1.name.push('wyc');就可以证明此问题的存在。而原型链的第二个问题就是:在创建子类型的实例时,不能向超类型的构造函数中传递参数。因此我们在实际开发中,很少单独使用原型链。
借用构造函数
为了解决原型链中存在的两个问题,开发人员开始使用一种叫做借用构造函数的技术来解决原型链中存在的问题。这种技术的实现思路也挺简单,只需要在子类型的构造函数内调用父类型的构造函数即可。别忘了,函数只不过是在特定环境中执行代码的对象,因此可以通过apply()或call()方法执行构造函数。代码如下:
// 为父类型创建构造函数
function SuperType(name) {
this.name = name;
this.color = ['pink', 'yellow'];
this.property = true; this.testFun = function() {
alert('http://www.cnblogs.com/wuyuchang/');
}
} // 为父类型添加方法
SuperType.prototype.getSuerperValue = function() {
return this.property;
} // 为子类型创建构造函数
function SubType(name) {
SuperType.call(this, name);
this.test = ['h1', 'h2', 'h3', 'h4'];
this.subproperty = false;
} // 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空
SubType.prototype.getSubValue = function() {
return this.subproperty;
} /* 以下为测试代码示例 */
var instance1 = new SubType(['wuyuchang', 'Jack', 'Nick']);
instance1.name.push('hello');
instance1.test.push('h5');
instance1.color.push('blue');
instance1.testFun(); // http://www.cnblogs.com/wuyuchang/
alert(instance1.name); // wuyuchang,Jack,Nick,hello
// alert(instance1.getSuerperValue()); // error 报错
alert(instance1.test); // h1,h2,h3,h4,h5
alert(instance1.getSubValue()); // false
alert(instance1.color); // pink,yellow,blue var instance2 = new SubType('wyc');
instance2.testFun(); // http://www.cnblogs.com/wuyuchang/
alert(instance2.name); // wyc
// alert(instance2.getSuerperValue()); // error 报错
alert(instance2.test); // h1,h2,h3,h4
alert(instance2.getSubValue()); // false
alert(instance2.color); // pink,yellow
可以看到以上代码中子类型SubType的构造函数内通过调用父类型"SuperType.call(this, name);",从而实现了属性的继承,也可以在子类型创建实例的时候为父类型传递参数了,但新的问题又来了。可以看到我在父类型的构造函数中定义了一个方法:testFun,在父类型的原型中定义了一个方法:getSuperValue。可是在实例化子类型后仍然是无法调用父类型的原型中定义的方法getSuperValue,只能调用父类型中构造函数的方法:testFun。这就同创建对象中只使用构造函数模式一样,使得函数没有复用性可言。考虑到这些问题,借用构造函数的技术也是很少单独使用的。
组合继承(原型链+借用构造函数)
顾名思义,组合继承就是结合使用原型链与借用构造函数的优点,组合而成的一个模式。实现也很简单,既然是结合,那当然结合了两方的优点,即原型链继承方法,而在构造函数继承属性。具体代码实现如下:
// 为父类型创建构造函数
function SuperType(name) {
this.name = name;
this.color = ['pink', 'yellow'];
this.property = true; this.testFun = function() {
alert('http://www.cnblogs.com/wuyuchang/');
}
} // 为父类型添加方法
SuperType.prototype.getSuerperValue = function() {
return this.property;
} // 为子类型创建构造函数
function SubType(name) {
SuperType.call(this, name);
this.test = ['h1', 'h2', 'h3', 'h4'];
this.subproperty = false;
} SubType.prototype = new SuperType(); // 在此处给子类型添加方法,一定要在实现继承之后,否则会在将指针指向父类型的实例,则方法为空
SubType.prototype.getSubValue = function() {
return this.subproperty;
} /* 以下为测试代码示例 */
var instance1 = new SubType(['wuyuchang', 'Jack', 'Nick']);
instance1.name.push('hello');
instance1.test.push('h5');
instance1.color.push('blue');
instance1.testFun(); // http://www.cnblogs.com/wuyuchang/
alert(instance1.name); // wuyuchang,Jack,Nick,hello
alert(instance1.getSuerperValue()); // true
alert(instance1.test); // h1,h2,h3,h4,h5
alert(instance1.getSubValue()); // false
alert(instance1.color); // pink,yellow,blue var instance2 = new SubType('wyc');
instance2.testFun(); // http://www.cnblogs.com/wuyuchang/
alert(instance2.name); // wyc
alert(instance2.getSuerperValue()); // true
alert(instance2.test); // h1,h2,h3,h4
alert(instance2.getSubValue()); // false
alert(instance2.color); // pink,yellow
以上代码通过SuperType.call(this, name);继承父类型的属性,通过SubType.prototype = new SuperType();继承父类型的方法。以上代码很方便的解决了原型链与借用构造函数所遇到的问题,成为了JavaScript中最为常用的实例继承的方法。但混合模式也并非没有缺点,可以看到在以上代码中在继承方法的时候实际已经继承了父类型的属性,只不过此时对于引用类型属于共享的,因此在子类型的构造函数内在次调用父类型的构造函数从而继承了父类型的属性而去覆盖了原型中所继承的属性,这样调用两次构造函数显然没有必要,但有什么方法可以解决呢?在解决此问题时先看以下两个模式。
原型式继承
原型式继承的的实现方法与普通继承的实现方法不同,原型式继承并没有使用严格意义上的构造函数,而是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。具体代码如下:
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
代码示例:
/* 原型式继承 */
function object(o) {
function F() {}
F.prototype = o;
return new F();
} var person = {
name : 'wuyuchang',
friends : ['wyc', 'Nicholas', 'Tim']
} var anotherPerson = object(person);
anotherPerson.name = 'Greg';
anotherPerson.friends.push('Bob'); var anotherPerson2 = object(person);
anotherPerson2.name = 'Jack';
anotherPerson2.friends.push('Rose'); alert(person.friends); // wyc,Nicholas,Tim,Bob,Rose
寄生式继承
/* 寄生式继承 */
function createAnother(original) {
var clone = object(original);
clone.sayHi = function() {
alert('hi');
}
return clone;
}
使用示例:
/* 原型式继承 */
function object(o) {
function F() {}
F.prototype = o;
return new F();
} /* 寄生式继承 */
function createAnother(original) {
var clone = object(original);
clone.sayHi = function() {
alert('hi');
}
return clone;
} var person = {
name : 'wuyuchang',
friends : ['wyc', 'Nicholas', 'Rose']
}
var anotherPerson = createAnother(person);
anotherPerson.sayHi();
寄生组合式继承
前面说过了JavaScrip中组合模式实现继承的缺点,现在我们就来解决它的缺点,实现思路是,对于构造函数继承属性,而原型链的混成形式继承方法,即不用在继承方法的时候实例化父类型的构造函数。代码如下:
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
/* 寄生组合式继承 */
function inheritPrototype(subType, superType) {
var prototype = object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
而在使用时只需要将组合模式中的“SubType.prototype = new SuperType();”这行代码替换成inheritPrototype(subType, superType);即可。寄生组合式继承的高效率体现在它只调用了一次父类型构造函数,避免了创建不必要的或多余的属性。与此同时,原型链还能保持不变,因此,还能够正常使用instanceof和isPrototypeof()。这也是目前来说最理想的继承方式了,目前也在向这种模式转型。(YUI也使用了这种模式。)
此博文参考《JavaScript高级程序设计第3版》,代码为经过改写,更具体,并加了注释使大家更易懂。如对JS继承方面有独到见解的童鞋不别吝啬,回复您的见解供大家参考!
JavaScript继承基础讲解,原型链、借用构造函数、混合模式、原型式继承、寄生式继承、寄生组合式继承的更多相关文章
- JavaScript构造函数+原型创建对象,原型链+借用构造函数模式继承父类练习
虽然经常说是做前端开发的,但常常使用的技术反而是JQuery比较多一点.在JavaScript的使用上相对而言少些.尤其是在创建对象使用原型链继承上面,在项目开发中很少用到.所以今天做个demo练习一 ...
- javascript面向对象基础讲解(工厂模式、构造函数模式、原型模式、混合模式、动态原型模式)
面向对象可以把程序中的关键模块都视为对象,而模块拥有属性及方法.这样我们如果把一些属性及方法封装起来,日后使用将非常方便,也可以避免繁琐重复的工作.接下来将为大家讲解在JS中面向对象的实现. 工厂 ...
- 【前端知识体系-JS相关】深入理解JavaScript原型(继承)和原型链
1. Javascript继承 1.1 原型链继承 function Parent() { this.name = 'zhangsan'; this.children = ['A', 'B', 'C' ...
- 前端总结·基础篇·JS(一)原型、原型链、构造函数和字符串(String)
前端总结系列 前端总结·基础篇·CSS(一)布局 前端总结·基础篇·CSS(二)视觉 前端总结·基础篇·CSS(三)补充 前端总结·基础篇·JS(一)原型.原型链.构造函数和字符串(String) 前 ...
- JS原型、原型链、构造函数、实例与继承
https://cloud.tencent.com/developer/article/1408283 https://cloud.tencent.com/developer/article/1195 ...
- JS中的继承(原型链、构造函数、组合式、class类)
1.继承 应注意区分继承和实例化,实例化是生成一个对象,这个对象具有构造函数的属性和方法:继承指的应该是利用父类生成一个新的子类构造函数,通过这个子类构造函数实例化的对象,具有子类的属性和方法,同时也 ...
- 1--面试总结-js深入理解,对象,原型链,构造函数,执行上下文堆栈,执行上下文,变量对象,活动对象,作用域链,闭包,This
参考一手资料:http://dmitrysoshnikov.com/ecmascript/javascript-the-core/中文翻译版本:https://zhuanlan.zhihu.com/p ...
- JS学习笔记——JavaScript继承的6种方法(原型链、借用构造函数、组合、原型式、寄生式、寄生组合式)
JavaScript继承的6种方法 1,原型链继承 2,借用构造函数继承 3,组合继承(原型+借用构造) 4,原型式继承 5,寄生式继承 6,寄生组合式继承 1.原型链继承. <script t ...
- js继承之组合继承(结合原型链继承 和 借用构造函数继承)
在我的前两篇文章中,我们已经介绍了 js 中实现继承的两种模式:原型链继承和借用构造函数继承.这两种模式都存在各自的缺点,所以,我们考虑是否能将这二者结合到一起,从而发挥二者之长.即在继承过程中,既可 ...
随机推荐
- 修饰模式(Decorator结构化)C#简单的例子
修饰模式(Decorator结构化)C#简单的例子 播放器的基本功能是移动.执行等.BaseAbility 新增功能:1.伤害技能harmAbility:2.阻碍技能BaulkAbility:3.辅助 ...
- POJ 2155 Matrix (D区段树)
http://poj.org/problem?id=2155 Matrix Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 1 ...
- 装双系统(win7/win8/ubuntu)问题总结
1.假设你要安装双系统,装系统是最好安装winows,然后再装ubuntu,因此,不解决它自己入选,我一般装ubuntu时刻.第一分区删除.然后用U硬盘直接安装ubuntu.在这一刻ubuntu将投入 ...
- Codeforces Round #243 (Div. 2) Problem B - Sereja and Mirroring 解读
http://codeforces.com/contest/426/problem/B 对称标题的意思大概是.应当指出的,当线数为奇数时,答案是线路本身的数 #include<iostream& ...
- 批处理命令 For循环命令具体解释!
批处理for命令具体解释FOR这条命令基本上都被用来处理文本,但还有其它一些好用的功能!看看他的基本格式(这里我引用的是批处理中的格式,直接在命令行仅仅须要一个%号)FOR 參数 %%变量名 IN ( ...
- hdu 最大三角形(凸包+旋转卡壳)
老师在计算几何这门课上给Eddy布置了一道题目,题目是这样的:给定二维的平面上n个不同的点,要求在这些点里寻找三个点,使他们构成的三角形拥有的面积最大.Eddy对这道题目百思不得其解,想不通用什么方法 ...
- do{}while(0)宏的作用的定义
看到开放源代码,宏定义经常这样用 #define some() do { do_somt_thing(); } while (0) 为什么这样用? 能够试一下.假如一个普通宏定义 #define so ...
- MVC基本概念和流程
MVC基本概念和流程 MVC的概念 Model(模型):包含数据和行为.不过现在一般都分离开来:Value Object(数据) 和 服务层(行为). View(视图):负责进行模型的展示,一般就是展 ...
- JS的parent对象
top: 该变更永远指分割窗口最高层次的浏览器窗口.如果计划从分割窗口的最高层次开始执行命令,就可以用top变量. parent: 该变量指的是包含当前分割窗口的父窗口.如果在一个窗口内有分割窗口,而 ...
- Sql Server 2008R2版本中有关外键Foreign的使用
原文:Sql Server 2008R2版本中有关外键Foreign的使用 1. 在数据库设计的过程中往往会想让2张表进行关联而使用到Foreign从而加强2张表之间的约束(如图) 以前有个问题一直没 ...