Javascript原型链
原型链的关系
在Javascript中,只要创建了一个新函数,就会为该函数创建prototype属性,指向函数的原型对象,Object.prototype是所有对象最顶层的原型。所有对象都继承由Object构造函数创建的原型对象,也就是说,所有的原型对象都继承Object.prototype的属性,而这个原型对象默认有一个constructor属性指向Object构造方法的原型。
默认所有原型对象都会有constructor属性,这个属性包含一个指向prototype属性所在函数的指针。通过构造函数创建实例后,每个实例内部都有一个指针指向函数的prototype对象。Firefox,safari,Chrome中这个指针是__proto__,可以通过脚本访问。但是在IE中这个属性是不可见的,所以无法在IE中修改__proto__。同时,这个属性的关联关系是在实例与构造函数的prototope对象之间,而不是实例跟构造函数之间。
事实上,虽然IE无法访问内部的__proto__ 属性,但是通过以下方法可以确定在IE中有这样的隐式关系:
Person.prototype.isPrototypeOf(person1)
如果内部有一个指向Person.prototype 的指针,那么就会返回true。
具体看以下例子:
function Person(){
}
Person.prototype.name = “Kingle”;
Person.prototype.job = “FE”;
var person1 = new Person();
var person2 = new Person();
具体关系看下图:(所有图示均省略了与Person构造函数的关系)

从上图 可以看出: Person.prototype.constructor 指向 Person。可以说,由prototype、constructor、__proto__三个指针构成了整个javascript的原型链。另外要注意一点,__proto__这个属性存在于实例中,而prototype只存在函数对象中,实例中不存在prototype对象。
关于prototype属性
由于函数本身也是一个对象,因此它拥有来自构造函数的原型,即javascript的Function object。但是,函数本身的prototype属性仅仅用于函数实例的属性继承,而函数本身不会使用这个关联的prototype。(在prototype中设置的属性将直接作用于所有实例)
如下代码,我们使用Person.prototype来为所有Person实例设置属性,而不是用person1.prototype来为person1设置实例。
function Person(){}
Person.prototype.name = “Thom”;
var person1 = new Person(){};
person1.name; // Thom
Person.name; // undefined
关于constructor属性
那么constructor属性呢?可以尝试以下代码:
console.log(person1.constructor); //function Person(){ }
console.log(person1.constructor == Person.constructor); //false
console.log(person1.constructor == Person.prototype.constructor); //true
可以看出,原型的属性是共享的,因此,constructor属性也是共享的,可以通过实例访问。
但是,另一方面,无法通过实例修改原型中的值。如果在实例中添加了一个属性,并且属性名跟原型中的相同,那么将会在实例中创建该属性并屏蔽原型中的那个属性。(如果对应属性的值不是引用对象)。
普通变量的原型
如果是普通变量的情况,变量其实是对应构造函数的实例,因此存在__proto__属性,并指向对应构造函数的原型:
var num1 = 1;
console.log(num1.prototype);//undefined
console.log(num1.__proto__);//Number{}
var str = "1";
console.log(str.prototype);//undefined
console.log(str.__proto__);//String{}
var obj = {};
console.log(obj.prototype);//undefined
console.log(obj.__proto__);//Object{}
原型链属性的读取
由于读取对象属性时,先搜索实例本身,如果找到对应属性则停止搜索,如果没有找到,再搜索原型对象。因此,在实例中添加原型同名属性,不会影响到其他实例。如下代码:
function Person(){}
Person.prototype.name = "Alan";
var person1 = new Person();
var person2 = new Person();
person1.name = "Nolen";
alert(person1.name); //Nolen
alert(person2.name); //Alan
如果想删除实例中的属性,使其重新访问原型对象中的属性,可以用delete操作符:
function Person(){}
Person.prototype.name = "Alan";
var person1 = new Person();
var person2 = new Person();
person1.name = "Nolen";
alert(person1.name); //Nolen
alert(person2.name); //Alan
delete person1.name;
alert(person1.name);//Alan
in 操作符及 hasOwnProperty()方法
javascript中检测对象属性有两种方式,一种是使用in操作符,一种是hasOwnProperty()方法。这两种方式的区别是:in操作符会同时检测原型和实例,而hasOwnProperty方法只会检测实例。看下面的例子:
function Person(){}
Person.prototype.name = "Alan";
var person1 = new Person();
alert(person1.hasOwnProperty("name"));//false
alert("name" in person1);//true
person1.name = "Nolen";
alert(person1.hasOwnProperty("name"));//true
alert("name" in person1);//true
因此,在使用for-in循环的时候,如果只想访问实例中的属性,就要配套使用hasOwnProperty方法,如:
for(var prop in person1){
if(person1.hasOwnProperty(prop)){
alert(prop);
}
}
原型的重写
由于为原型一个个单独添加属性和方法不利于封装,因此更好的做法是用封装好的属性和方法的字面量来重写整个原型对象。如下:
function Person(){}
Person.prototype = {
constructor : Person,
name : "Alan",
age : 1,
sayName : function(){
}
};
注意constructor 的属性,如果不在新的原型对象中重新设定,那么constructor属性将不在指向Person。因为这里完全重写了默认的prototype对象,因此constructor属性将指向新的contructor—object构造函数。因此将无法通过constructor确定对象类型。
另一方面,由于原型和实例之间通过指针关联起来,因此对原型做的修改可以从实例上表现出来,如下:
function Person(){}
Person.prototype.name = "Alan";
var person1 = new Person();
Person.prototype.age = 1;
alert(person1.name); //Alan
alert(person1.age);
但是,如果使用重写原型的方法,就相当于切断了构造函数与最初原型的联系。一个实例只拥有一个唯一的原型,而实例中的指针又只指向原型,而不是构造函数。因此,原型的重写最好在实例之前,否则实例将指向最初的原型。如下例子:
function Person(){}
var person1 = new Person();
Person.prototype = {
constructor : Person,
name : "Alan",
age : 1
};
alert(person1.name); //undifined
alert(person1.constructor.prototype.name);//Alan
通过图示可以更直观的理解。下图为重写原型之前的原型链:

下图为重写原型后的原型链:

可以看出,修改原型后,原来的实例__proto__属性所指向的原型还是原来的原型,因此无法找到新原型的属性。
一些有趣的原型链
Object是本身的实例,因为在Object的原型链中存在着constructor属性指向Object:
console.log(Object.__proto__.__proto__.constructor === Object);//true
console.log(Object instanceof Object);//true
function Person(){}
console.log(Person instanceof Person);//false
Function的constructor指向本身,也就是说,Function.__proto__ 指向 Function.prototype:
console.log(Function.constructor === Function);//true console.log(Function.__proto__ === Function.prototype); //true
Javascript原型链的更多相关文章
- JavaScript学习总结(十七)——Javascript原型链的原理
一.JavaScript原型链 ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法.其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法.在JavaScript中, ...
- javascript原型链中 this 的指向
为了弄清楚Javascript原型链中的this指向问题,我写了个代码来测试: var d = { d: 40 }; var a = { x: 10, calculate: function (z) ...
- 明白JavaScript原型链和JavaScrip继承
原型链是JavaScript的基础性内容之一.其本质是JavaScript内部的设计逻辑. 首先看一组代码: <script type="text/javascript"&g ...
- Javascript 原型链资料收集
Javascript 原型链资料收集 先收集,后理解. 理解JavaScript的原型链和继承 https://blog.oyanglul.us/javascript/understand-proto ...
- 资料--JavaScript原型链
JavaScript原型链 原文出处:https://www.cnblogs.com/chengzp/p/prototype.html 目录 创建对象有几种方法 原型.构造函数.实例.原型链 inst ...
- JavaScript原型链:prototype与__proto__
title: 'JavaScript原型链:prototype与__proto__' toc: false date: 2018-09-04 11:16:54 主要看了这一篇,讲解的很清晰,最主要的一 ...
- JavaScript原型链及其污染
JavaScript原型链及其污染 一.什么是原型链? 1.JavaScript中,我们如果要define一个类,需要以define"构造函数"的方式来define: functi ...
- 图解Javascript原型链
本文尝试阐述Js中原型(prototype).原型链(prototype chain)等概念及其作用机制.上一篇文章(图解Javascript上下文与作用域)介绍了Js中变量作用域的相关概念,实际上关 ...
- 画一画javascript原型链
在javascript中,几种数据类型String,Number,Boolean,Object,Function都是函数,可称之为函数对象. 可以说拥有prototype属性的都是函数. 所有对象都拥 ...
- JavaScript原型链和instanceof运算符的暧昧关系
时间回到两个月前,简单地理了理原型链.prototype以及__proto__之间的乱七八糟的关系,同时也简单了解了下typeof和instanceof两个运算符,但是,anyway,试试以下两题: ...
随机推荐
- Python笔记(一)
我是一名211高校软件工程大三学生,由于前段时间一直在找实习公司.笔试面试了很多公司,虽然有一定的基础,但是还是被某些公司面试官像虐狗一样的虐了.最后找到了一个口碑比较好的外企,主攻信息安全方面.这段 ...
- mysql索引补充
索引补充 1.索引 索引是表的目录,在查找内容之前可以先在目录中查找索引位置,以此快速定位查询数据.对于索引,会保存在额外的文件中. 2.索引种类 普通索引:仅加速查询 唯一索引:加速查询 + 列 ...
- DIH中添加不同的数据源
需求:从mysql数据库中读取一个知识记录,从记录表中的字段值中获取一个文件路径,读取xml文件,xml文件中可能包含多个文档内容.建立索引. xml文件样例: <?xml version=&q ...
- 关于js-binding中Layer触摸事件的优化
关于js-binding中Layer触摸事件的优化 cocos2d-x 3.7 1. 目前js中监听触摸事件带来的不便(特别是cocosbuilder) 在目前的js-binding中,如果要监听la ...
- JVM 运行时内存结构
1.JVM内存模型 JVM运行时内存=共享内存区+线程内存区 1).共享内存区 共享内存区=持久带+堆 持久带=方法区+其他 堆=Old Space ...
- localStorage的跨与实现方案
实现原理: HTML5 的 postMessage 为解决跨域页面通信提供了一套可控的机制, 而 localStorage 则提供了易用简洁的本地存储方案? 这两者结合起来,能否实现跨域的本地存储呢 ...
- hdu 4421 Bit Magic
[题意] 这个函数是给A求B的,现在给你B,问你是否能有A的解存在. [2-SAT解法] 对于每个A[i]的每一位运行2-sat算法,只要跑到强连通就可以结束,应为只要判断是否有解,后面拓扑求解就不需 ...
- 转: ios app架构设计
http://keeganlee.me/post/architecture/20160107 看完这一系列文章后就知道怎么回答这类问题了: App架构设计经验谈:接口的设计 App架构设计经验谈:技术 ...
- Asp.Net复习篇之Asp.Net生命周期与Asp.Net页的生命周期
Asp.Net生命周期与Asp.Net页的生命周期是一个比较重要的话题,有时可能似乎知道一些,但又说不出个所以然,而且时常把这两个概念混淆.现在也是该好好理清思路,把这两个概念搞懂. Asp.Net生 ...
- Activity生命周期-Android
Activity常见的三种生命周期: 1.完整生命周期 oncreate-->onstart-->onresume-->onpause-->onstop-->ondest ...