JS Pro-深入面向对象的程序设计之继承的详解


Posted in Javascript onMay 07, 2013

原型链(prototype chaining):

利用原型来继承属性和方法。回顾一下构造函数(constructor),原型对象(prototype)和实例(instance)的关系。每一个构造函数都有一个prototype属性,该属性指向一个prototype对象;prototype对象也有constructor属性,指向该函数;而实例也有一个内部指针(__proto__)指向这个prototype对象。如果这个prototype对象是另外一个对象的实例会是怎样的呢?这样该prototype对象就包含一个指向另一个类型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。

JS的继承很简单,就是把子类的prototype设为父类的一个(实例化)对象

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

最终的结果:instance的__proto__指向SubType.prototype对象,而SubType.prototype对象的__proto__属性又指向SuperType.prototype对象。getSuperValue()是一个方法,所以仍然存在于原型中,而property是一个实例属性,所以现在存在于SubType.prototype这个实例中。  instance.constructor现在指向的是SuperType,这是由于SubType.prototype指向SuperType.prototype,而SuperType.prototype的constructor属性指向SuperType函数,所以instance.constructor指向SuperType。

默认情况下,所有引用类型都继承Object。这是因为所有函数的原型对象,默认都是Object的一个实例,所以内部prototype(__proto__)指向Object.Prototype。

原型和实例的关系:可以使用2种方法来确定原型与实例之间的关系。
- instancef操作符:使用该操作符来测试实例与原型链中出现过的构造函数,都会返回true

alert(instance instanceof Object); //true
alert(instance instanceof SuperType); //true
alert(instance instanceof SubType); //true

- isPrototypeOf()方法:只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。
alert(Object.prototype.isPrototypeOf(instance)); //true
alert(SuperType.prototype.isPrototypeOf(instance)); //true
alert(SubType.prototype.isPrototypeOf(instance)); //true

给子类添加方法的注意点:我们有的时候会给子类添加方法,或者是重写父类的某些方法。这个时候就要注意,这些方法必须在继承后再定义。以下的例子里,SubType在继承SuperType后,我们给它添加了新的方法getSubValue(),而且重写了getSuperValue()方法。对于后者,只有SubType的实例才会使用重写的方法,SuperType的实例还是会使用原有的getSuperValue()方法。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){

return this.property;
};
function SubType(){

t his.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
//new method
SubType.prototype.getSubValue = function (){

return this.subproperty;
};
//override existing method
SubType.prototype.getSuperValue = function (){

return false;
};
var instance = new SubType();
alert(instance.getSuperValue()); //false

另外一个需要注意的是,通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样会重写原型链。如下面的代码,在SubType继承SuperType以后,使用对象字面量给原型添加方法,但这样做,会重写SubType原型,重写后的SubType.prototype包含的是一个Object的实例,从而也切断了与SuperType的关系。
function SuperType(){
this.property = true;
}
SuperType.prototype.getSuperValue = function(){

return this.property;
};
function SubType(){

this.subproperty = false;
}
//inherit from SuperType
SubType.prototype = new SuperType();
//try to add new methods - this nullifies the previous line
SubType.prototype = {

getSubValue : function (){


return this.subproperty;

},

someOtherMethod : function (){


return false;

}
};
var instance = new SubType();
alert(instance.getSuperValue()); //error!

原型链的问题:和原型一样,当使用引用类型值的时候,原型链就会出问题了。回顾一下之前的内容,包含一个引用类型值的原型属性会被所有实例共享,这就是为什么我们要把引用类型值在构造函数中定义,而不是在原型中定义。在通过原型链实现继承时,原型实际上会变成另一个类型的实例,于是,原先的实例属性也顺利成章的变成现在的原型属性了。
function SuperType(){
this.colors = [“red”, “blue”, “green”];
}
function SubType(){
}
//inherit from 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”

在SuperType构造函数中,我们定义了一个colors数组,每一个SuperType实例都会拥有它自己的这个colors数组。但是当SubType使用原型链继承SuperType以后,SubType.prototype变成SuperType的一个实例,因此它拥有自己的colors属性,也就是说SubType.prototype.colors属性。所以,当创建SubType实例的时候,所有实例都共享这一属性了。如上面的代码所示。

第二个问题就是:在创建子类的实例时,不能向超类的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类的构造函数传递参数。由于这些问题,我们不会单独使用原型链。

--------------------------------------------------------------------------------

借用构造函数(Contructor stealing):
为了解决上述问题,开发人员发明了一种叫做借用构造函数的技术。这种技术的思路就是:在子类型构造函数的内部调用超类型构造函数。(函数,只不过是在特定环境中执行代码的对象?)我们可以通过使用apply()或call()方法在新创建的对象上执行构造函数。

function SuperType(){
this.colors = [“red”, “blue”, “green”];
}
function SubType(){

//inherit from SuperType

SuperType.call(this);
}
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”

我们在SubType里使用call()方法调用SuperType的构造函数,实际上就是在新的SubType对象上执行SuperType()函数中定义的所有对象初始化代码。结果就是每个SubType实例都具有自己的colors属性的副本。

传递参数:使用借用构造函数方法的一个很大的好处在于就是,我们可以从子类的构造函数传递参数到父类的构造函数中。

function SuperType(name){
this.name = name;
}
function SubType(){

//inherit from SuperType passing in an argument

SuperType.call(this, “Nicholas”);

//instance property

this.age = 29;
}
var instance = new SubType();
alert(instance.name); //”Nicholas”;
alert(instance.age); //29

新的SuperType构造函数新增了一个参数name,我们在call SuperType的同时,往SuperType传递参数"Nicholas"。为了不让超类型的构造函数重写子类型的属性,可以在调用超类型构造函数后再定义子类的属性。

借用构造函数的问题:方法都在构造函数中定义,无法复用。而且在超类型的原型中定义的方法,对子类型而言是不可见的。结果所有类型都只能使用构造函数模式。

--------------------------------------------------------------------------------

组合继承:
结合原型链及借用构造函数各自的优点的一种继承模式。使用原型链继承原型属性及方法,使用借用构造函数来继承实例属性。如下面例子,我们使用call()方法调用SuperType的构造函数(每个SubType实例都拥有自己的name和colors属性,以及SubType的age属性);然后再把SuperType实例赋值给SubType的原型,使其继承SuperType的sayName()方法(每个实例都共用这个方法)。

function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
    alert(this.name);
};
function SubType(name, age){
    //inherit properties
    SuperType.call(this, name);
    this.age = age;
}
//inherit methods
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
    alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27

原型式继承(Prototypal Inheritance):
function object(o){
function F(){}

F.prototype = o;

return new F();
}
--------------------------------------------------------------------------------

寄生式继承(Parasitic Inheritance):

缺点同构造函数

Javascript 相关文章推荐
将jQuery应用于login页面的问题及解决
Oct 17 Javascript
Jquery UI震动效果实现原理及步骤
Feb 04 Javascript
web网页按比例显示图片实现原理及js代码
Aug 09 Javascript
jquery中animate动画积累的解决方法
Oct 05 Javascript
js 自动播放的实例代码
Nov 19 Javascript
JS cookie中文乱码解决方法
Jan 28 Javascript
IntelliJ IDEA 安装vue开发插件的方法
Nov 21 Javascript
利用vue + element实现表格分页和前端搜索的方法
Dec 25 Javascript
JavaScript防止全局变量污染的方法总结
Aug 02 Javascript
JS函数节流和防抖之间的区分和实现详解
Jan 11 Javascript
vue实现折线图 可按时间查询
Aug 21 Javascript
JS实现按比例缩小图片宽高
Aug 24 Javascript
基于JavaScript实现继承机制之原型链(prototype chaining)的详解
May 07 #Javascript
基于JavaScript实现继承机制之构造函数+原型链混合方式的使用详解
May 07 #Javascript
使用javascript:将其它类型值转换成布尔类型值的解决方法详解
May 07 #Javascript
JQuery+CSS提示框实现思路及代码(纯手工打造)
May 07 #Javascript
基于IE下ul li 互相嵌套时的bug,排查,解决过程以及心得介绍
May 07 #Javascript
解决javascript:window.close()在chrome,Firefox下失效的问题
May 07 #Javascript
jQuery的slideToggle方法实例
May 07 #Javascript
You might like
php递归创建目录的方法
2015/02/02 PHP
CI框架简单邮件发送类实例
2016/05/18 PHP
PHP实现多图上传和单图上传功能
2018/05/17 PHP
ExtJS 2.0实用简明教程之应用ExtJS
2009/04/29 Javascript
js压缩工具 yuicompressor 使用教程
2010/03/31 Javascript
javascript中的一些注意事项 更新中
2010/12/06 Javascript
jquery jqPlot API 中文使用教程(非常强大的图表工具)
2011/08/15 Javascript
不用构造函数(Constructor)new关键字也能实现JavaScript的面向对象
2013/01/11 Javascript
js简单实现删除记录时的提示效果
2013/12/05 Javascript
jquery绑定事件不生效的解决方法
2014/02/11 Javascript
jquery显示loading图片直到网页加载完成的方法
2015/06/25 Javascript
深入理解ES6学习笔记之块级作用域绑定
2017/08/19 Javascript
JS简单实现点击跳转登陆邮箱功能的方法
2017/10/31 Javascript
JavaScript 中使用 Generator的方法
2017/12/29 Javascript
微信小程序项目实践之九宫格实现及item跳转功能
2018/07/19 Javascript
vue cli4.0项目引入typescript的方法
2020/07/17 Javascript
[43:47]完美世界DOTA2联赛PWL S3 LBZS vs Phoenix 第一场 12.09
2020/12/11 DOTA
python发送伪造的arp请求
2014/01/09 Python
在Python的Django框架中为代码添加注释的方法
2015/07/16 Python
python搭建虚拟环境的步骤详解
2016/09/27 Python
Python文件操作函数用法实例详解
2019/12/24 Python
python标准库os库的函数介绍
2020/02/12 Python
解决python-docx打包之后找不到default.docx的问题
2020/02/13 Python
Django如何使用jwt获取用户信息
2020/04/21 Python
Python利用Pillow(PIL)库实现验证码图片的全过程
2020/10/04 Python
python中实现词云图的示例
2020/12/19 Python
adidas旗下高尔夫装备供应商:TaylorMade Golf(泰勒梅高尔夫)
2016/08/28 全球购物
英国高级百货公司:Harvey Nichols
2017/01/29 全球购物
高三自我鉴定怎么写
2013/10/19 职场文书
服装公司总经理岗位职责
2013/11/30 职场文书
祖国在我心中演讲稿500字
2014/05/04 职场文书
竞选班干部演讲稿300字
2014/08/20 职场文书
庆国庆国旗下讲话稿2014
2014/09/21 职场文书
八年级上册语文教学计划
2015/01/22 职场文书
Nginx缓存设置案例详解
2021/09/15 Servers
仅仅使用 HTML/CSS 实现各类进度条的方式汇总
2021/11/11 HTML / CSS