理解js中的原型链
对象有”prototype”属性,函数对象有”prototype”属性,原型对象有”constructor”属性。
关于原型
- 在JavaScript中,原型也是一个对象,通过原型可以实现对象的属性继承,JavaScript的对象实例中都包含了”[[Prototype]]”内部属性,这个属性所对应的就是该对象的原型。“[[Prototype]]”作为对象的内部属性,是不能被直接访问的。所以为了方便查看一个对象的原型,Firefox和Chrome中提供了__proto__这个非标准(不是所有浏览器都支持)的访问器。在JavaScript的原型对象还包含一个”constructor”属性,这个属性对应创建所有指向该原型的实例的构造函数。
- 在JavaScript中,只要创建了一个新函数,那么这个函数就有一个prototype属性,这个属性指向函数的原型对象。在默认情况下所有原型都会自动获取一个constructor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。当一个函数被用作构造函数来创建实例时,这个函数的prototype属性值会被作为原型赋值给所有对象实例(也就是设置 实例的`__proto__`属性),也就是说,所有实例的原型引用的是函数的prototype属性。(****`只有函数对象才会有这个属性!`****)
- JavaScript中函数也是对象,所以就可以通过_proto_查找到构造函数对象的原型。
 Function对象作为一个函数,就会有prototype属性,该属性将对应”function () {}”对象。
 Function对象作为一个对象,就有__proto__属性,该属性对应”Function.prototype”,也就是说,”Function._proto_ === Function.prototype”。
- 对于所有的对象,都有__proto__属性,这个属性对应与对象的原型.
 对于函数对象,除了__proto__属性之外,还有prototype属性,当一个函数被用作构造函数来创建实例时,该函数的prototype属性值将被作为原型赋值给所有对象实例(也就是设置实例的__proto__属性)
原型链
因为每个对象和原型都有原型,对象的原型指向原型对象,而父的原型又指向父的父,这种原型层层连接起来的就构成了原型链。
主要思想: 利用prototype属性重写原型对象。利用原型让一个引用类型继承另一个引用类型的属性和方法。
面向对象语言有两种继承方式:接口继承(只继承方法名);实现继承(继承实际的方法)。但在ECMAScript中,函数名没多大含义,只是函数体的引用而已,因此,ECMAScript无法实现接口继承,只支持实现继承。实现继承,主要是依靠原型链来完成的。
构造函数、原型、实例之间的关系
(1)每个构造函数都有一个原型对象,Func.prototype指向了这个原型对象。
(2)原型对象都包含一个指向构造函数的指针,Func.prototype.constructor等于Func,可以理解为prototype和constructor是两个方向相反的指针,在构造函数和原型之间搭建起桥梁。
(3)每个实例,都包含一个指向原型对象的内部指针__proto__,当然这个对开发人员是不可见的。
(4)如果把构造函数A的原型,设成构造函数B的一个实例,那么构造函数A的原型里就包含了指向另一个原型的指针,从而形成了原型链。这样就实现了继承。(5)通过原型链实现继承的情况下,搜索过程会沿着原型链向上:使用对象的属性或方法时,解析器先搜索实例内部,找不到就搜索实例的原型内部,再找不到就搜索实例的原型的构造函数的原型内部,一级一级向上查找,一直到原型链末端才会停下来。
(6)原型链的最顶端是Object,这就是为什么所有引用类型instanceof Object都会返回true。换句话说,只要instanceof后面的构造函数在实例的原型链中,都会返回true
如下:
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

原型链结构图如下:

一个例子
 function Person (name) { this.name = name; }
 function Mother () { }
 Mother.prototype = {    //Mother的原型
     age: 18,
     home: ['Beijing', 'Shanghai']
 };
 Person.prototype = new Mother(); //Person的原型为Mother
 //用chrome调试工具查看,提供了__proto__接口查看原型
 var p1 = new Person('Jack'); //p1:'Jack'; __proto__:{__proto__:18,['Beijing','Shanghai']}
 var p2 = new Person('Mark'); //p2:'Mark'; __proto__:{__proto__:18,['Beijing','Shanghai']}
 p1.age = 20;
 /* 实例不能改变原型的基本值属性
  * 在p1实例下增加一个age属性的普通操作,与原型无关。跟var o={}; o.age=20一样。
  * p1:下面多了个属性age,而__proto__跟 Mother.prototype一样,age=18。
  * p2:只有属性name,__proto__跟 Mother.prototype一样
  */
 p1.home[0] = 'Shenzhen';
 /* 原型中引用类型属性的共享
  * p1:'Jack',20; __proto__:{__proto__:18,['Shenzhen','Shanghai']}
  * p2:'Mark';    __proto__:{__proto__:18,['Shenzhen','Shanghai']}
  */
 p1.home = ['Hangzhou', 'Guangzhou'];
 /* 其实跟p1.age=20一样的操作。换成这个理解: var o={}; o.home=['big','house']
  * p1:'Jack',20,['Hangzhou','Guangzhou']; __proto__:{__proto__:18,['Shenzhen','Shanghai']}
  * p2:'Mark';                             __proto__:{__proto__:18,['Shenzhen','Shanghai']}
  */
 delete p1.age;
 /* 删除实例的属性之后,原本被覆盖的原型值就重见天日了。
  * 这就是向上搜索机制
  * p1:'Jack',['Hangzhou','Guangzhou']; __proto__:{__proto__:18,['Shenzhen','Shanghai']}
  * p2:'Mark';                          __proto__:{__proto__:18,['Shenzhen','Shanghai']}
  */
 Person.prototype.lastName = 'Jin';
 /* 改写原型,动态反应到实例中。
  * 注意,这里我们改写的是Person的原型,就是往Mother里加一个lastName属性,等同于Mother.lastName='Jin'
  * 这里并不是改Mother.prototype,改动不同的层次,效果往往会有很大的差异。
  * p1:'Jack',['Hangzhou','Guangzhou']; __proto__:{'jin',__proto__:18,['Shenzhen','Shanghai']}
  * p2:'Mark';                          __proto__:{'jin',__proto__:18,['Shenzhen','Shanghai']}
  */
 Person.prototype = {
     age: 28,
     address: { country: 'USA', city: 'Washington' }
 };
 var p3 = new Person('Obama');
 /* 重写原型!因为在通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样会重写原型链。这个时候Person的原型已经完全变成一个新的对象了,
  * 换成这样理解:var a=10; b=a; a=20; c=a。所以b不变,变得是c,所以p3跟着新的原型变化,与之前的原型无关。
  * p1:'Jack',['Hangzhou','Guangzhou']; __proto__:{'jin',__proto__:18,['Shenzhen','Shanghai']}
  * p2:'Mark';                          __proto__:{'jin',__proto__:18,['Shenzhen','Shanghai']}
  * p3:'Obama';__proto__: 28 {country: 'USA', city: 'Washington'}
  */
 Mother.prototype.no = 9527;
 /* 改写原型的原型,动态反应到实例中。
  * 注意,这里我们改写的是Mother.prototype,p1p2会变,但上面p3跟之前的原型已经了无瓜葛了,所以不受影响。
  * p1:'Jack',['Hangzhou','Guangzhou']; __proto__:{'jin',__proto__:18,['Shenzhen','Shanghai'],9527}
  * p2:'Mark';                          __proto__:{'jin',__proto__:18,['Shenzhen','Shanghai'],9527}
  * p3:'Obama';__proto__: 28 {country: 'USA', city: 'Washington'}
  */
 Mother.prototype = {
     car: 2,
     hobby: ['run','walk']
 };
 var p4 = new Person('Tony');
 /* 重写原型的原型!这个时候Mother的原型已经完全变成一个新的对象了!
  * 由于上面Person与Mother已经断开联系了,这时候Mother怎么变已经不影响Person了。
  * p4:'Tony';__proto__: 28 {country: 'USA', city: 'Washington'}
  */
 Person.prototype = new Mother(); //再次绑定
 var p5 = new Person('Luffy');
 // 这个时候如果需要应用这些改动的话,那就要重新将Person的原型绑到mother上了
 // p5:'Luffy';__proto__:{__proto__: 2, ['run','walk']}
 p1.__proto__.__proto__.__proto__.__proto__ //null,你说原型链的终点不是null?
 Mother.__proto__.__proto__.__proto__    //null,你说原型链的终点不是null?
在第13行和第26行:p1.age = 20; p1.home = ['Hangzhou', 'Guangzhou'];这两个是对实例p1添加了两个属性,对其原型没有任何影响,等同于为普通对象添加属性。
第20行:p1.home[0] = 'Shenzhen' 它不会在p1下创建一个home数组属性,然后将其首位设为 'Shenzhen',而是修改了Mother原型中home属性的值。因为home在p1下并未被定义,所以也不能直接一步定义home[0],如果要在p1下创建一个 home 数组,可以这样写:
p1.home = [];
p1.home[0] = 'Shenzhen';
而之所以 p1.home[0] = 'Shenzhen' 不直接报错,是因为在原型链中有一个搜索机制。当我们输入 p1.object 的时候,原型链的搜索机制是先在实例中搜索相应的值,找不到就在原型中找,还找不到就再往上一级原型中搜索……一直到了原型链的终点,就是到null还没找到的话,就返回一个 undefined。当我们输入 p1.home[0] 的时候,也是同样的搜索机制,先搜索 p1 看有没有名为 home 的属性和方法,然后逐级向上查找。最后我们在Mother的原型里面找到了,所以修改他就相当于修改了 Mother 的原型啊。
理解js中的原型链的更多相关文章
- 理解js中的原型链,prototype与__proto__的关系
		说到prototype,就不得不先说下new的过程. 我们先看看这样一段代码: 1 <script type="text/javascript"> 2 var Pers ... 
- 【转】理解js中的原型链,prototype与__proto__的关系
		说到prototype,就不得不先说下new的过程. 我们先看看这样一段代码: 1 <script type="text/javascript"> 2 var Pers ... 
- [转]理解js中的原型链,prototype与__proto__的关系
		本文转自:http://rockyuse.iteye.com/blog/1426510 说到prototype,就不得不先说下new的过程. 我们先看看这样一段代码: 1 <script typ ... 
- 深入理解JS对象和原型链
		函数在整个js中是最复杂也是最重要的知识 一个函数中存在多面性: 1.它本身就是一个普通的函数,执行的时候形成的私有作用域(闭包),形参赋值,预解释,代码执行,执行完 成后栈内存销毁/不销毁. 2.& ... 
- 理解js中的原型,原型对象,原型链
		目录 理解原型 理解原型对象 实例属性与原型属性的关系 更简单的原型语法 原型的动态性 原型链 理解原型 我们创建的每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象, ... 
- 关于js中的原型链的理解
		我们知道无论什么时候只要创建了一个函数,就会为该函数创建一个prototype属性,这个属性指向函数的原型对象,默认情况下所有原型对象都会自动获得一个constructor(构造函数)属性,这个属性包 ... 
- JS中注意原型链的“指向”
		昨天压缩Js文件时发现了项目中的一个prototype的问题代码如下所示: 1. <script> var XXX = function(){ }; var x1 = new XXX(); ... 
- JS中的原型链和原型的认识
		这篇文章主要是学习一下JavaScript中的难点------原型和原型链 自定义一个对象 我们学习一门编程语言,必然要使用它完成一些特定的功能,而面向对象的语言因为符合人类的认知规律,在这方面做得很 ... 
- js 中的原型链与继承
		ECMAScript中将原型链作为实现继承的主要方法,其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法. 1.原型链 先回忆一下构造函数和原型以及实例的关系:每个构造函数都有一个原型对 ... 
随机推荐
- ES5中, map 和 forEach的区别
			forEach和map区别在哪里知道吗? // forEach Array.prototype.forEach(callback(item, index, thisArr), thisArg) // ... 
- drf框架,restful接口规范,源码分析
			复习 """ 1.vue如果控制html 在html中设置挂载点.导入vue.js环境.创建Vue对象与挂载点绑定 2.vue是渐进式js框架 3.vue指令 {{ }} ... 
- Js代码中的span拼接
			今天遇到一个小需求,用bootstrap的table只有两个字段,占用太宽,页面不美观,组长要求用拼接,一行几列的形式展现出来.我在form表单中拼接了span,遇到以下问题: 1.点击查询,以前生成 ... 
- Go作用域
			package main import "fmt" //全局变量的定义 //num3 := 1000//不支持简短定义的写法 var num3 = 1000 func main() ... 
- GEE引擎假人系统自定义教程
			现如今传奇游戏玩家数量日渐减少.为了给服务器增加人气,很多GM在服务端中增加了自动登录和自动打怪的假人系统.由于该系统登录的假人可以自动练功,自动攻城和实现简单的对话.完全可以做到以假乱真的地步!所以 ... 
- Java经典面试笔试题及答案
			1.什么是对象序列化,为什么要使用? 所谓对象序列化就是把一个对象以二进制流的方式保存到硬盘上.好处:方便远程调用. 2.值传递与引用传递的区别? 所谓值传递就是把一个对象的值传给一个新的变量,但是系 ... 
- net core调用MimeKit发送QQ邮件
			一.在QQ邮箱内申请授权码,具体参考请官方文档 二.具体代码 public void TestSendMailDemo() { MimeMessage message = new MimeMessag ... 
- 国际化支持、activity生命周期、屏幕翻转的ui适配
			国际化 对于手机的不同语言做出不同的语言描述,这里只是简单的提一下,实际上针对比较大型的项目,有可能不同的语言要做不同的ui适配. 例如下面:中文长度不长但是在德语中占据很长的位置,这个就要针对性的适 ... 
- .NET基础拾遗(1)类型语法基础和内存管理基础【转】
			http://www.cnblogs.com/edisonchou/p/4787775.html Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串 ... 
- 用vscode写c/c++
			用vscode写c/c++ 1. 安装wsl windows下安装linux(ubuntu) 2. 打开设置 3. 输入run code 随便找一个地方粘贴,会出现一大段代码 4. 把c对应的代码修改 ... 
