原型链的关系

在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原型链的更多相关文章

  1. JavaScript学习总结(十七)——Javascript原型链的原理

    一.JavaScript原型链 ECMAScript中描述了原型链的概念,并将原型链作为实现继承的主要方法.其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法.在JavaScript中, ...

  2. javascript原型链中 this 的指向

    为了弄清楚Javascript原型链中的this指向问题,我写了个代码来测试: var d = { d: 40 }; var a = { x: 10, calculate: function (z) ...

  3. 明白JavaScript原型链和JavaScrip继承

    原型链是JavaScript的基础性内容之一.其本质是JavaScript内部的设计逻辑. 首先看一组代码: <script type="text/javascript"&g ...

  4. Javascript 原型链资料收集

    Javascript 原型链资料收集 先收集,后理解. 理解JavaScript的原型链和继承 https://blog.oyanglul.us/javascript/understand-proto ...

  5. 资料--JavaScript原型链

    JavaScript原型链 原文出处:https://www.cnblogs.com/chengzp/p/prototype.html 目录 创建对象有几种方法 原型.构造函数.实例.原型链 inst ...

  6. JavaScript原型链:prototype与__proto__

    title: 'JavaScript原型链:prototype与__proto__' toc: false date: 2018-09-04 11:16:54 主要看了这一篇,讲解的很清晰,最主要的一 ...

  7. JavaScript原型链及其污染

    JavaScript原型链及其污染 一.什么是原型链? 1.JavaScript中,我们如果要define一个类,需要以define"构造函数"的方式来define: functi ...

  8. 图解Javascript原型链

    本文尝试阐述Js中原型(prototype).原型链(prototype chain)等概念及其作用机制.上一篇文章(图解Javascript上下文与作用域)介绍了Js中变量作用域的相关概念,实际上关 ...

  9. 画一画javascript原型链

    在javascript中,几种数据类型String,Number,Boolean,Object,Function都是函数,可称之为函数对象. 可以说拥有prototype属性的都是函数. 所有对象都拥 ...

  10. JavaScript原型链和instanceof运算符的暧昧关系

    时间回到两个月前,简单地理了理原型链.prototype以及__proto__之间的乱七八糟的关系,同时也简单了解了下typeof和instanceof两个运算符,但是,anyway,试试以下两题: ...

随机推荐

  1. Android小项目之九 两种上下文的区别

    ------- 源自梦想.永远是你IT事业的好友.只是勇敢地说出我学到! ---------- 按惯例,写在前面的:可能在学习Android的过程中,大家会和我一样,学习过大量的基础知识,很多的知识点 ...

  2. iOS 中的XML解析代码(SAX)

    1.XML解析(SAX) NSXMLParser SAX 大文件 1)打开文档 - (void)parserDidStartDocument:(NSXMLParser *)parser 2)开始查找起 ...

  3. IOS开发UI篇之tableView 的用法详解

    1.我们知道tableView是IOS中的高级视图,其继承与ScrollView,故我们知道他有具有ScrollView的所有功能.而且还扩展了许多.当然在这里就不一一介绍了. 2.tableView ...

  4. angularJs中将字符串转换为HTML格式

    首先定义一个filter: .filter( 'to_trusted', ['$sce', function ($sce) { return function (text) { return $sce ...

  5. [转]position:relative leaves an empty space

    本文转自:http://stackoverflow.com/questions/5229081/positionrelative-leaves-an-empty-space 在使用relative进行 ...

  6. [转]如何烧录tizen镜像文件?(图文教程)

    http://blog.csdn.net/flydream0/article/details/9179143 上一篇文章我已讲过如何制作镜像文件(http://blog.csdn.net/flydre ...

  7. 锋利的jQuery第2版学习笔记6、7章

    第6章,jQuery与Ajax的应用 Ajax的优势和不足 Ajax的优势 1.不需要插件支持 2.优秀的用户体验 3.提高Web程序的性能 4.减轻服务器和带宽的负担 Ajax的不足 1.浏览器对X ...

  8. a标签中使用img后的高度多了4px

    前两天,在做一个网站的时候,发现a标签中使用img后的高度多了4px,各种纠结. 最后,仔细分析,终于找到原因了,因为img是行内元素,默认display: inline; 它与文本的默认行为类似,下 ...

  9. 基础面试题——Javascript

    1.介绍js的基本数据类型 Undefined.Null.Boolean.Number.String 2.js有哪些内置对象? 数据封装类对象:Object.Array.Boolean.Number ...

  10. ANDROID内存优化——大汇总(转)

    原文作者博客:转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! ANDROID内存优化(大汇总——上) 写在最前: 本文的思路主要借鉴了20 ...