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,试试以下两题: ...
随机推荐
- Asp.Net处理URL空格变%20问题
在Web前端需要页面跳转的时候我们可能会这样子用:window.location.href = "page.html?parameters",如果刚好parameters里面带有空 ...
- 用PredicateBuilder实现Linq动态拼接查询
在使用Linq查询的时候,特别是如果你在使用Entiry Framwork,有时会遇到动态查询的情况(客户的查询条件是不固定的拼接查询).我们能想到的第一方案应该是拼接SQL,的确这样是可以达到我们的 ...
- 功能分解——Android下画分时图与k线图有感
最近工作极度繁忙,已经好久没有更新博客了,总感觉要是再不抽空总结总结点东西,分分钟就会被懒惰的状态给打到了.同时也希望同学们谨记,如果你已经决定要坚持某些正确的东西,比如背完某章单词,看一完本书抑或是 ...
- python 复杂表达式,以及表单的处理
d = { 'Adam': 95, 'Lisa': 85, 'Bart': 59 } def generate_tr(name, score): if score < 60: return '& ...
- 有关EL表达式的一些笔记
JSP页面中使用SUN公司的EL函数库,需要导入JSTL开发包,并在页面中导入EL函数库. <%--引入EL函数库 --%> <%@taglib uri="http://j ...
- Xutils3的使用
Xutils是前两年很火的一个三方库(githup地址),是一个工具类,分为4个模块:DbUtils.HttpUtils.ViewUtils. BitmapUtils,还有一个非常使用功能就是LogU ...
- Java快速入门
Java 是什么? Java 的特点: 面向对象 平台无关(跨平台): 简单 安全 体系结构 - 中性 可移植 健壮 多线程 解释型 高性能 分布式 动态 Java环境设置: Java SE可免费提供 ...
- http请求数据
/// <summary> /// http请求post数据 /// </summary> /// <param name=&q ...
- EL表达式与JSTL(C)标签
一.EL表达式: Expression Language提供了在 JSP 脚本编制元素范围外(例如:脚本标签)使用运行时表达式的功能.脚本编制元素是指页面中能够用于在JSP 文件中嵌入 Java 代码 ...
- [转]Android 应用的自动升级、更新模块的实现
本文转自:http://www.oschina.net/question/163910_28462 我们看到很多Android应用都具有自动更新功能,用户一键就可以完成软件的升级更新.得益于Andro ...