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,试试以下两题: ...
随机推荐
- C# 程序集反射
namespace AssemblyLibrary { public class AssemblyLibrary { public static object LoadAssembly(string ...
- Android之EditText文本变化的监听
监听EditText的文本变化需要给EditText控件加一个addTextChangeListener监听器 editText.addTextChangeListener(textWatcher); ...
- iOS 项目审核被拒原因汇总
1.程序运行崩溃 Your app crashes on iPhone running iOS connected to an IPv6 network when we: - app crashes ...
- (转)为什么安装win10后其他软件不能上网
原文地址: http://zhidao.baidu.com/question/426358794987815412.html?qbl=relate_question_0&word=%C9%FD ...
- Oracle基础 exp/imp命令
一.导出方式: 使用exp/imp方式导出数据分为四种方式: 1.表方式导出:一个或多个指定的表,包括表的定义.表数据.表的所有者授权.表索引.表约束,以及创建在该表上的触发器.也可以只导出结构,不导 ...
- msql_DDL_创建table
• 语法: create table 表名(• 列名1 列类型 [<列的完整性约束>],• 列名2 列类型 [<列的完整性约束>],• ... ... ); • PRIMARY ...
- hdu 4711 动态规划
思路:其实这题是个挺水的动态规划,一开始就能AC,可是不知道错哪了,瞎改瞎交,WA了数十次.AC之后怎么改都是AC,也不知道改了什么地方,郁闷死了~~~难道开始时的测试数据有问题??? dp[i][j ...
- java的技术调用栈图示例
- 两种局部刷新UITableView的方法的使用条件
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ //1.取消选 ...
- spring(三)----大概是最简单的面向切面了
面向切面编程,是spring的一大特点,可以说是spring最独特的特点了(个人认为). 记得当初学习面向切面编程的时候,可能是面向对象思想根深蒂固了,怎么也理解不了什么叫面向切面... 其实对于面向 ...