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,试试以下两题: ...
随机推荐
- oracle中to_date() 与 to_char() 日期和字符串转换
to_date("要转换的字符串","转换的格式") 两个参数的格式必须匹配,否则会报错. 即按照第二个参数的格式解释第一个参数. to_char(日期,& ...
- 简约之美Jodd-http--应用一箩筐
Jodd-http是一个微型的.简约的http client,然而简单而且方便.使用它可以轻松的实现发送请求和读取响应.它的目标就是日常应用变的非常简单,从而简化开发人员的工作. 了解Jodd-htt ...
- android 搭建开发环境
法一.直接集成的ADT+ECLIPSE 还有64位的jdk即可 法二.EClipse装好后,下载好SDK,ADT(在线或离线装) sdk下载后,点sdk.exe 文件. 如果更新失败,解决方法如下 C ...
- J2EE常用包:
J2EE常用包: java web开发必掌握的javax.servlet : servlet包及支持javax.servlet.http : http请求支持javax.servlet.jsp : 及 ...
- org.apache.hadoop.conf-Configuration
终于遇到第一块硬骨头 Hadoop没有使用java.util.Properties管理配置文件,而是自己定义了一套配置文件管理系统和自己的API. package org.apache.hadoop. ...
- DataGridView重绘painting简单实例
private void dataGridViewX1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) { & ...
- iOS UIImage DownLoad图片的下载缓存全部在此
iOS图片的下载缓存全部在此 分类: iOS编程 -- : 2075人阅读 评论() 收藏 举报 注意: 我的文章只写给自己看 ------------------------------------ ...
- GET和POST的主要区别
1.get是从服务器上获取数据,post是向服务器传送数据 2.在客户端上,get通过url提交数据,数据在url上可以看到,post方式,数据放置在HTMLHEADER内提交 3.对于get方式,服 ...
- SQL查询中的in与join效率比较
大多数情况下,程序员比较喜欢使用in来查询符合某些条件的数据,最近在查询某个角色有哪些用户的方法中,使用了in语句: ) FROM baseuser AND BaseUser.Id IN (SELEC ...
- linux_jvm_jhat_dump内存分析
jhat命令 jhat命令 -- Java Head Analyse Tool 用途:是用来分析java堆的命令,可以将堆中的对象以html的形式显示出来,包括对象的数量,大小等等,并支持对象查询 ...