深入理解javascript之继承
2015-10-27 08:56
483 查看
继承是面向对象编程的一个基础。javascript中的继承,主要是通过原型链来实现的。
其原理就是子类的原型指向了父类的原型,从而新实例化的对象拥有了父类的属性和方法。原型链一层一层往上搜索直到Object.prototype为止。关于原型的概念,这里就不再细说了,可以参考该文章:深入理解原型
接下来我们说说如何确定原型和实体的关系。第一种是使用instanceof操作符。
第二种是使用isPrototypeOf()方法,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。
原型链虽热很强大,但是也存在一些问题。在使用原型继承时,原型实际上会变成另一个类型的实例。于是,原来的实例属性也就顺理成章地变成了现在的原型属性了。
也就是说SonB的所有实例都会共享其属性。所以在实践中其实很少用到原型继承。
借用构造函数继承的基本模式如下:
可以看到,age属性不再共享,而且我们还可以向父类提供参数。
但是这种方法同样存在一个问题,就是方法都在构造函数中定义,那么函数的复用性就降低了。所以这种方法也很少单独使用。
组合继承的基本模式如下:
组合继承虽然常用,但是不代表它没有问题,它最大的问题就是无论什么情况下,都会调用两次父类构造函数:
寄生式继承的基本模式如下:
任何能够返回新对象的函数都适用于此模式,但是由于不能做到函数复用所以降低了效率。
所谓寄生组合式继承,就是通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其基本思路是:不必为了指定子类型的原型而调用父类的构造函数,我们需要的其实就是父类原型的一个副本而已。本质上,就是使用寄生式继承来继承父类的原型,然后再将这些结果指定给子类的原型。
寄生组合式继承的基本模式如下:
寄生组合式继承解决了组合继承两次调用父类构造函数的弊端,也完成了继承所需的所有功能,是最理想的继承方式。
最后,完整代码:demo
原型链继承
实现原型链继承的基本模式代码如下:/** * @description: 原型继承基本模式 * @author: 刘放 * @date: 2015/10/26 15:52 */ function Father(){ this.name = "刘放"; } Father.prototype.getName = function(){ return this.name; } function Son(){ this.Sonname = "小放"; } //继承Father Son.prototype = new Father(); Son.prototype.getSonName = function(){ return this.Sonname; }; var f = new Son(); document.write(f.getName()); //刘放
其原理就是子类的原型指向了父类的原型,从而新实例化的对象拥有了父类的属性和方法。原型链一层一层往上搜索直到Object.prototype为止。关于原型的概念,这里就不再细说了,可以参考该文章:深入理解原型
接下来我们说说如何确定原型和实体的关系。第一种是使用instanceof操作符。
//使用instanceof判断实例与原型的关系 document.write(f instanceof Object);//true document.write(f instanceof Father);//true document.write(f instanceof Son);//true
第二种是使用isPrototypeOf()方法,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。
//使用isPrototypeOf()方法判断实例与原型的关系 document.write(Object.prototype.isPrototypeOf(f));//true document.write(Father.prototype.isPrototypeOf(f));//true document.write(Son.prototype.isPrototypeOf(f));//true
原型链虽热很强大,但是也存在一些问题。在使用原型继承时,原型实际上会变成另一个类型的实例。于是,原来的实例属性也就顺理成章地变成了现在的原型属性了。
//使用原型继承出现的一些问题,就是原型属性共享 function FatherB(){ this.age = [12,13,14]; } function SonB(){ } SonB.prototype = new FatherB(); var fB = new SonB(); fB.age.push(15); document.write(fB.age);//12,13,14,15 //但是其他子类的属性也改变了 var fBB = new SonB(); document.write(fBB.age);//12,13,14,15
也就是说SonB的所有实例都会共享其属性。所以在实践中其实很少用到原型继承。
借用构造函数继承
为了解决原型链继承存在的共享属性问题。开发人员开始使用借用构造函数方式继承。实现就是在子类的内部调用父类的构造函数。在javascript中使用apply和call就可以改变作用域了,详细请看博文:call和apply借用构造函数继承的基本模式如下:
/** * @description: 借用构造函数继承基本模式 * @author: 刘放 * @date: 2015/10/26 16:27 */ function FatherC(name){ this.name = name; this.age = [12,13,14]; } function SonC(){ FatherC.call(this,"刘放"); } var fC = new SonC(); fC.age.push(15); document.write(fC.age);//12,13,14,15 var fCC = new SonC(); document.write(fCC.age);//12,13,14 document.write(fCC.name);//刘放
可以看到,age属性不再共享,而且我们还可以向父类提供参数。
但是这种方法同样存在一个问题,就是方法都在构造函数中定义,那么函数的复用性就降低了。所以这种方法也很少单独使用。
组合继承
组合继承是将原型链继承和借用构造函数继承的优点结合,是最常用的继承。其思路是使用原型链对原型属性和方法继承,而通过构造函数实现对实例属性的继承。这样既能保证复用性,也能保证每个实例都拥有自己的属性。组合继承的基本模式如下:
/** * @description: 组合继承基本模式 * @author: 刘放 * @date: 2015/10/26 16:47 */ function FatherD(name){ this.name = name; this.age = [12,13,14]; } FatherD.prototype.sayName = function(){ document.write(this.name); } function SonD(name,sex){ FatherD.call(this,name); this.sex = sex; } //继承方法 SonD.prototype = new FatherD(); SonD.prototype.constructor = SonD; SonD.prototype.saySex = function(){ document.write(this.sex); } var fD = new SonD("刘放","男"); fD.age.push(15); document.write(fD.age);//12,13,14,15 fD.sayName();//刘放 fD.saySex();//男 var fDD = new SonD("小放","女"); document.write(fDD.age);//12,13,14 fDD.sayName();//小放 fDD.saySex();//女
组合继承虽然常用,但是不代表它没有问题,它最大的问题就是无论什么情况下,都会调用两次父类构造函数:
FatherD.call(this,name); SonD.prototype = new FatherD();
寄生式继承
寄生式继承的思路和工厂模式类型,即创建一个用于封装继承过程的函数,该函数在内部以某种方法来增强对象,最后再像真是它做了所有工作一样返回对象。对工厂模式不熟悉的同学可以参考博文:设计模式寄生式继承的基本模式如下:
/** * @description: 寄生式继承基本模式 * @author: 刘放 * @date: 2015/10/26 17:16 */ function createSon(original){ var clone = Object.create(original); clone.sayHi = function(){ document.write("nihao"); }; return clone; } var FatherE = { name:"刘放", age:[12,13,14], }; var SonE = createSon(FatherE); SonE.sayHi();//nihao document.write(SonE.name);//刘放 //任何能够返回新对象的函数都适用于此模式,但是由于不能做到函数复用所以降低了效率
任何能够返回新对象的函数都适用于此模式,但是由于不能做到函数复用所以降低了效率。
寄生组合式继承
刚才也提到了,组合继承最大的问题就是调用了两次父类的构造函数,而我们利用寄生式和组合结合就可以解决该问题。所谓寄生组合式继承,就是通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其基本思路是:不必为了指定子类型的原型而调用父类的构造函数,我们需要的其实就是父类原型的一个副本而已。本质上,就是使用寄生式继承来继承父类的原型,然后再将这些结果指定给子类的原型。
寄生组合式继承的基本模式如下:
/** * @description: 寄生组合式继承基本模式 * @author: 刘放 * @date: 2015/10/26 17:30 */ function Extends(son,father){ var prototype = Object.create(father.prototype);//创建对象 prototype.constructor = son;//增强对象 son.prototype = prototype;//指定对象 } function FatherF(name){ this.name = name; this.age = [12,13,14]; } FatherF.prototype.sayName = function(){ document.write(this.name); } function SonF(name,sex){ this.name = name; this.sex = sex; } //继承 Extends(SonF,FatherF); SonF.prototype.saySex = function(){ document.write(this.sex); } var fF = new SonF("刘放","男"); fF.saySex();//男 fF.sayName();//刘放 //高效的只调用一次构造函数,最理想的继承方式。
寄生组合式继承解决了组合继承两次调用父类构造函数的弊端,也完成了继承所需的所有功能,是最理想的继承方式。
最后,完整代码:demo
相关文章推荐
- 关于JavaScript的namespace命名空间
- JavaScript事件
- onmouseover与onmouseout的js实现
- js限制文本框输入内容
- js友好提示是否继续,post提交
- js-事件2_键盘事件 oDiv.style.left=oDiv.offsetLeft-10+"px";
- js-事件1_获取鼠标坐标clientX+scrollLeft及兼容性写法_跟随鼠标移动的divs
- JSP实现的简单分页显示效果代码
- jsp实现针对excel及word文档的打印方法
- php语言中使用json的技巧及json的实现代码详解
- js数组如何添加json数据及js数组与json的区别
- JS实现浏览器状态栏文字从右向左弹出效果代码
- JavaScript模块规范之AMD规范和CMD规范
- JS实现浏览器状态栏显示时间的方法
- JS实现浏览器状态栏文字闪烁效果的方法
- JS基于Ajax实现的网页Loading效果代码
- jsp页面屏蔽输入框只读属性时backspace返回上一级页面的方法
- javascript倒序输出
- 原生Ajax至mini-ajax.js
- javascript操作cookie类之jscookie.js