首先,你要理解在javascript中,每当一个新函数创建时,都会生成一个prototype属性,我们管它叫做原型对象。看一个例子:

function foo(){
this.name='qiangqiang';
this.age=23;
this.colors=['red','orange','yellow'];
}
foo.prototype.z=2;
console.log(foo.prototype);

查看一下foo.ptototype

 

看以看到foo的原型对象的结构,有一个构造函数,__proto__属性,还有一个我们在原型对象里面创建的属性,其中前两个是prototype属性默认的。

这时,我们再继续创建一个foo的实例对象。

var f=new foo();
console.log(f.__proto__);

再看一下结果

与上面的结果是相同的,注意,这里的实例化出的对象f没有prototype属性,它只有一个__proto__属性而且是指向foo.prototype的。所以当我们,

console.log(f.z)的时候,会打印出2,而这也正是沿着原型链向上查找的原因。

接下来:

console.log(f)

我们看到了对象f的所有属性。

我们在写一段这样的代码。

function nextfoo(){
}
nextfoo.prototype=new foo();
console.log(nextfoo.prototype);

我们可以看到,这时nextfoo.prototype属性和f的属性是一致的,这里其实就叫做重写了nextfoo的原型对象,在他的原型对象里面包括了,foo中定义的name,age属性、__proto__属性(指向foo的原型对象)、constructor属性(foo的构造函数)。

这时,我们在写这样一段代码

var nextf=new nextfoo();
nextf.colors.push('black');
console.log(nextf.colors);
var nextf2=new nextfoo();
console.log(nextf2.colors);

我们可以看到由于都是引用的原型对象中的属性,因此对象实例之间会相互影响。

还有一个问题,

console.log(f.name);
console.log(f.age);

那么这里的name和this到底是沿着原型链向上查找到的呢,还是自己本身创建的呢。我们在来试一下。

console.log(f.hasOwnProperty('name'));

通过这个,我们看以判断出name是属于自身的属性,这里的原因就是this的绑定的问题啦,建议先把http://www.cnblogs.com/qqqiangqiang/p/5316973.html理解了,其实this是隐式绑定在了f对象上,因此会创建出属于自身的属性。

明白了以上这些,那么理解继承就不是问题啦。。。

实现继承主要是通过原型链来实现的。
其基本思想是利用原型链让一个引用类型继承另一个引用类型的属性和方法。
实现原型链有一种基本模式,其代码大致如下。

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

以上代码定义了两个类型:SuperType和SubType。每个类型分别有一个属性和方法。他们的主要区别是SubType继承了SuperType,而继承是通过创建SuperType的实例,并将该实例赋给SubType.prototype实现的。实现的本质是重写原型对象,代之以一个新类型的实例。换句话说,原来存在于SuperType的实例中的所有属性和方法,现在也存在于SubType.prototype中了。

确定原型和实例的关系有以下几种方法:

alert(instance instanceof Object);//true
alert(instance instanceof SupperType);//true
alert(instance instanceof SubType);//true alert(Object.prototype.isPrototypeOf(instance));//true
alert(SupperType.ptototype.isPrototypeOf(instance));//true
alert(SubType.prototype.isPrototypeOf(instance));//true

3、谨慎的定义方法

给原型添加方法的代码一定要放在替换原型的语句之后,例子:

function SuperType(){
this.property=true;
}
SuperType.prototype.getSupervalue=function(){
return this.property;
};
function SubType(){
this.subproperty=false;
}
SubType.prototype=new SuperType(); SubType.ptototype.getSubValue=function(){
return this.subproperty;
}
SuperType.prototype.getSuperValue=function(){
return fasle;
};
var instance=new SubType();
alert(instance.getSuperValue());//false
var instance1=new SupperType();
alert(instance1.getSuperValue());//true;

  

当通过SubType的实例调用getSuperValue()时,调用的就是这个重写的方法,但通过SuperType的实例调用时还是原来的那个方法。
还有一点需要注意,就是通过原型链继承的时,不能使用对象字面量创建原型方法,这样做就会重写原型链,

原型链导致的问题:

function SuperType(){
this.colors=['red','orange','yellow'];
}
function SubType(){}
SubType.prototype=new SuperType();
console.log(SubType.prototype);
var instance1=new SubType();
instance1.colors.push('black');
console.log(instance1.colors); //'red,orange,yellow,black'
var instance2=new SubType();
console.log(instance2.colors);//'red,orange,yellow,black'

  

导致所有的SubType的实例都会共享这一个colors属性。
(**解释一下这个原因,由于是重写了原型对象,所以SubType的prototype原型原型对象里面有一个__proto__属性指向SuperType的原型对象,还有colors这个数组,所以SubType的所有实例都是共享这个数组的**)
还有一个问题就是,在创建子类型的实例的时候,不能向超类型的构造函数中传递参数。实际上,应该是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。

鉴于以上的两个问题,实践中很少会单独使用原型链。

继承的其他方式:
1、借用构造函数

function SuperType(name){
this.name=name;
}
function SubType(name){
SuperType.call(this,name);
this.age=29;
}
var instance=new SubType('Nicholas');
alert(instance.name);//Nicholas
var instance2=new SubType('dongzhiqiang');
alert(instance2.name);

  

这里最关键的是对于this的理解,SuperType.call(this,name)的意思是把SuperType函数的this对象绑定到SubType函数的this对象,所以说这里的关键就是如何判断SubType函数的this对象指向哪里,具体参考http://www.cnblogs.com/qqqiangqiang/p/5316973.html 可以看出this是分别指向instance和instance2对象的,换句话说,它就相当于分别在不同的对象中创建了colors这个数组。所以说不共享统一colors自然就不互相影响。
问题:方法都在构造函数中定义,因此函数的复用无从谈起。
2、组合继承

function SuperType(name){
this.name=name;
this.colors=['red','orange','yellow'];
}
SuperType.prototype.sayName=function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name);
this.age=age;
}
SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){
console.log(this.age);
};
var instance1=new SubType('Nicholas',29);
instance1.colors.push('black');
alert(instance1.colors);
instance1.sayName();
instance1.sayAge(); var instance2=new SubType('Greg',23);
alert(instance2.colors);
instance2.sayName();
instance2.sayAge();

  

组合继承避免了原型链和借用构造函数的缺陷,融合了他们的优点,成为javascript中最常用的继承模式。但是他的问题就是无论在什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。而且还会造成prototype已经有相应属性,而又在新创建的对象上创建了实例属性name和colors.
3、原型式继承

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

  

ECMAScript5通过新增Object.create()方法(类似于上面的object方法)规范化了原型式继承。这两个方法接收两个参数:一个用作新对象原型的对象和一个为新对象定义额外属性的对象。

var person={
name:'Nicholas',
friends:['Shelby','court','van']
};
var anthoerPerson=Object.create(person);
anthoerPerson.name='Greg';
anthoerPerson.push('Rob');
var yetAnotherPerson=Object.create(person);
yetAnotherPerson.name='Linda';
yetAnotherPerson.friends.push('Barible');
alert(person.friends); //'Shelby,Court,Van,Rob,Barible'

缺点同原型模式一样,会共享同一个值造成互相影响。

4、寄生式继承
寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个用于封装继承过程的函数,该函数在内部以某张方式来增强对象,最后再像是真地做了所有工作一样返回对象。

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:'Nicholas',
friends:['Shary','Court','Van']
}
var anotherPerson=createAnother(person);
anotherPerson.sayHi(); 5、寄生组合式继承
function inheritPrototype(subType,superType){
var prototype=Object(superType.prototype);
prototype.constructor=subType;
subType.prototype=prototype;
}
function SuperType(name){
this.name=name;
this.colors=['red','orange','yellow'];
}
SuperType.prototype.sayName=function(){
alert(this.name);
}
function SubType(name,age){
SuperType.call(this,name);
this.age=age;
}
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge=function(){
alert(this.age);
}
var sub=new SubType('maidi',36);
sub.sayName();
sub.sayAge();

其高效率体现在它只调用了一次SuperType构造函数,并且因此避免了在SubType.prototype上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;开发人员普遍认为组合式继承是引用类型最理想的继承范式。

javascript中关于继承的理解的更多相关文章

  1. 深入理解JavaScript中的继承

    1前言 继承是JavaScript中的重要概念,可以说要学好JavaScript,必须搞清楚JavaScript中的继承.我最开始是通过看视频听培训班的老师讲解的JavaScript中的继承,当时看的 ...

  2. JavaScript中的继承(原型链)

    一.原型链 ECMAScript中将原型链作为实现继承的主要方法,基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 实例1: function SupType() { this.pro ...

  3. 彻底搞懂JavaScript中的继承

    你应该知道,JavaScript是一门基于原型链的语言,而我们今天的主题 -- "继承"就和"原型链"这一概念息息相关.甚至可以说,所谓的"原型链&q ...

  4. JavaScript学习13 JavaScript中的继承

    JavaScript学习13 JavaScript中的继承 继承第一种方式:对象冒充 <script type="text/javascript"> //继承第一种方式 ...

  5. 浅谈JavaScript中的继承

    引言 在JavaScript中,实现继承的主要方式是通过原型链技术.这一篇文章我们就通过介绍JavaScript中实现继承的几种方式来慢慢领会JavaScript中继承实现的点点滴滴. 原型链介绍 原 ...

  6. 浅谈 JavaScript 中的继承模式

    最近在读一本设计模式的书,书中的开头部分就讲了一下 JavaScript 中的继承,阅读之后写下了这篇博客作为笔记.毕竟好记性不如烂笔头. JavaScript 是一门面向对象的语言,但是 ES6 之 ...

  7. 关于JavaScript中实现继承,及prototype属性

    感谢Mozilla 让我弄懂继承. JavaScript有八种基本类型,函数属于object.所以所有函数都继承自object.//扩展:对象,基本上 JavaScript 里的任何东西都是对象,而且 ...

  8. javascript中各种继承方式的优缺点

    javascript中实现继承的方式有很多种,一般都是通过原型链和构造函数来实现.下面对各种实现方式进行分析,总结各自的优缺点. 一 原型继承 let Super = functioin(name = ...

  9. javascript中实现继承的几种方式

    javascript中实现继承的几种方式 1.借用构造函数实现继承 function Parent1(){ this.name = "parent1" } function Chi ...

随机推荐

  1. python基础4

    内容概要: 一.递归 递归就是函数本身调用自己,直到满足指定条件之后一层层退出函数 递归特性: 必须有一个明确的结束条件 每次进入更深一层递归时,问题规模相比上次递归都应有所减少 递归效率不高,递归层 ...

  2. buildroot 重新编译 package

    /************************************************************************* * buildroot 重新编译 package ...

  3. 用js使得输入框input只能输入数字

        JS判断只能是数字和小数点1.文本框只能输入数字代码(小数点也不能输入)<input onkeyup="this.value=this.value.replace(/\D/g, ...

  4. yeoman生成react基本架构

    工欲善其事必先利其器.在开始react开始之前,我们先使用一系列的前段工具构建自己的前端集成解决方案. 环境配置: Bower,node js,npm,Grunt,Gulp,Yeoman 作者一直使用 ...

  5. GridView”的控件 必须放在具有 runat=server 的窗体标记内 “错误提示”

    在做导出数据到EXCEL程序中,出现了错误提示:类型“GridView”的控件“GridView1”必须放在具有 runat=server 的窗体标记 解决办法  重写 VerifyRendering ...

  6. wife信号如何传播

    方法一:像哈利波特一样穿墙而出 无论是wife信号还是广播信号本质上都属于电磁波.x光穿透力强所以可以穿透人体给体内照相,但是wife信号作为电磁波虽然也可以穿透墙而过,但是他的穿透能力实在是太弱了. ...

  7. 试制品 (nyoj 542)

    模拟 a 反应物集合 ; b 生成物集合; c 存在的化合物或单质集合; ans 新生成化合物集合 1.如果反应无均在已生成的化合物集合中,则完成反应,将合成物加入c集合 2.对每个方程式的反应物进行 ...

  8. 【 Note 】GDB调试

    GDB是在linux下的调试功能 命令: 启动文件: 普通调试 gdb 可执行文件 分屏调试 gdb -tui 可执行文件 ->调试: 运行 r 设置断点 b 删除断点 delete 断点编号 ...

  9. VS2012及以上版本 程序打包部署详解

    引用:  http://blog.csdn.net/zhang_xinxiu/article/details/9099757 程序编写测试完成后接下来我们要做的是打包部署程序,但VS2012让人心痛的 ...

  10. Python实现简单的HTTP服务器(支持文件上传下载)

    1.python内置模块 SimpleHTTPServer  (支持下载功能) 在对应的工作目录下,运行命令python -m SimpleHTTPServer 即可把当前目录下以共享服务的形式共享出 ...