首先js是一种面对对象的语言,虽然大多数时候是以面对过程的形式展现出来。先来看一段代码:

function Base() {
this.name = 'tarol';
}
function Sub() {
this.age = 18;
}
var b = new Base;
Sub.prototype = b;
var s = new Sub;
console.log(s.name); //'tarol'

  结果相信都知道,但是实现的原理却不明,于是逐行解析:

Sub.prototype = b;

  prototype是函数对象(函数也是对象)的特有属性,普通对象是不存在这个属性的,该属性默认为{}。这个属性的作用就是,将这个属性赋予给、使用此函数作为构造函数进行实例化的对象、作为该对象[[Propertype]]的内部属性。所谓内部属性就是通过js不能访问的属性,庆幸的是现代浏览器开放了这个属性的访问,一般属性名为__proto__。所以:

var s = new Sub;

  这一段(也可以说每个new操作)可以展开:

var s = {};
s.__proto__ = Sub.prototype;
Sub.call(s);

  可见,对象实例化后,与构造函数的耦合在于前者的__proto__属性和后者的prototype属性为同一个对象的引用。

  然后__proto__这个内部属性是用来做什么的呢,是用来实现js中的“继承”的。这个“继承”之所以打引号是用来区别c++和java中的类式继承的思想,这种方式称之为原型继承。

  原型继承的原理是:每个对象都保有一个指向其他对象的引用(也就是__proto__即原型),这条引用最终指向null(null也是对象),当访问这个对象的任一属性时,如果在本对象中没有找到,则向其__proto__指向的对象中寻找,直到原型链的尽头null。需要注意的是:原型链中的所有元素都是实例化的对象。

  现在回到上面的代码,当访问对象s的属性name时,在s中没有找到,于是跑到s的__proto__指向的Base的实例对象中寻找,找到了name为'tarol'。

  但如果访问s的属性gender时,在s和s.__proto__中都没有找到,于是继续向s.__proto__.__proto__中寻找。由s.__proto__ === new Base,可知s.__proto__.__proto__ === Base.prototype(即(new Constructor()).__proto__ === Constructor.prototype)。上面说到,prototype默认是{},于是继续在Base.prototype.__proto__中找,即(new Object()).__proto__ === Object.prototype。如果没有添加一些自定义的属性,Object.prototype同样是{},这样看来,似乎要陷入无限的循环当中,但其实到这里原型链就走向了尽头,因为浏览器会定义Object.prototype.__proto__ = null。用下面的流程梳理下,-->代表原型链上的传递,===代表对象不同引用间的替换。

  s.gender --> s.__proto__.gender === Sub.prototype.gender === b.gender --> b.__proto__.gender === Base.prototype.gender === (new Object()).gender --> (new Object()).__proto__.gender === Object.prototype.gender --> Object.prototype.__proto__ === null

  Object.prototype.__proto__也是所有对象原型链的尽头,包括Function(函数的构造函数本身也是对象)、Date(普通对象)、Math(单体内置对象),因为Function.prototype是默认的{},即进入上面流程中(new Object()).gender这一阶段。

  最后举个栗子:

function Class(){}
var a = new Class;
Class.prototype = new Class;
var b = new Class;
console.log(b.__proto__.__proto__ === a.__proto__);

  其中最让人困惑的估计是

Class.prototype = new Class;

  如果用类式继承来理解,会误以为这句代码会陷入死循环调用当中,但注意上面红字标明那句话:原型链中的所有元素都是实例化的对象。也就是,这里只是将一个对象插入Class对象的原型链最前端(除对象本身外),至于这个对象是new Class还是new Glass(见第一段红字,这句代码修改了构造函数的引用,使__proto__和prototype之间的耦合解除了,可以说这个对象和构造函数“分家”了),都不影响调用的过程。所以,这句代码的目的是修改了Class构造函数,让其实例化的对象的原型链长度+1。

  写的有些凌乱,如果过段时间我看不懂了再回来改一改。

  参考:

  《理解JavaScript面向对象的思路》By 温神

  《关于__proto__和prototype的一些理解》By TonyCoolZhu

  《JavaScript高级程序设计》By Nicholas C.Zakas

关于javascript原型链的个人理解的更多相关文章

  1. javascript原型链简单的理解

    在JavaScript中,一共有两种类型的值,原始值和对象值.每个对象都有一个内部属性[prototype],我们通常称之为原型.原型的值可以是一个对象,也可以是null.当然也可能是一个值,如果它的 ...

  2. Javascript 原型链资料收集

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

  3. 资料--JavaScript原型链

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

  4. JavaScript原型链及其污染

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

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

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

  6. javascript原型链中 this 的指向

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

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

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

  8. JavaScript原型链:prototype与__proto__

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

  9. 再次理解JavaScript原型链和匿名函数

    <!--------------------------------------------- 1.演示匿名加载 2.js单进程执行流 3.原型链理解 a.__proto__:属性每个对象都有 ...

随机推荐

  1. Linux系统C语言socket tcp套接字编程

    1.套接字的地址结构: typedef uint32_t in_addr_t; //32位无符号整数,用于表示网络地址 struct in_addr{ in_addr_t s_addr; //32位 ...

  2. 如何通过binlog获取我们想要的MySql语句?

    前言 MySql的binlog一般用于我们对数据的恢复,以及从数据库对主数据库的复制和更新. 假设此时我们有一个需要查询和读取Mysql最近操作DDL的信息,我们需要怎么处理? 聪明的你可能已经想到了 ...

  3. Python数据可视化Matplotlib——Figure画布背景设置

    之前在今日头条中更新了几期的Matplotlib教学短视频,在圈内受到了广泛好评,现应大家要求,将视频中的代码贴出来,方便大家学习. 为了使实例图像显得不单调,我们先将绘图代码贴上来,此处代码对Fig ...

  4. PAT1074 Reversing Linked List (25)详细题解

    02-1. Reversing Linked List (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue ...

  5. MySQL执行计划extra中的using index 和 using where using index 的区别

    本文出处:http://www.cnblogs.com/wy123/p/7366486.html (保留出处并非什么原创作品权利,本人拙作还远远达不到,仅仅是为了链接到原文,因为后续对可能存在的一些错 ...

  6. .Net,Dll扫盲篇,如何在VS中调试已经编译好的dll?

    什么是Dll? DLL 是一个包含可由多个程序同时使用的代码和数据的库. 例如,在 Windows 操作系统中,Comdlg32 DLL 执行与对话框有关的常见函数.因此,每个程序都可以使用该Dll中 ...

  7. Openfire服务器和Spark客户端配置

    一.Openfire服务器的配置 关于之前一直在进行的聊天app的项目,我们还没有完成,这次我们介绍一下,Openfire服务器的配置. 1.Openfire下载 Openfire下载地址:http: ...

  8. IOS中的单例设计模式

    单例设计模式是IOS开发中一种很重要很常用的一种设计模式.它的设计原理是无论请求多少次,始终返回一个实例,也就是一个类只有一个实例.下面是苹果官方文档中关于单例模式的图片: 如图所示,左边的图是默认的 ...

  9. 改进的Bresenham算法

    这里不仔细讲原理,只是把我写的算法发出来,跟大家分享下,如果有错误的话,还请大家告诉我,如果写的不好,也请指出来,一起讨论进步. 算法步骤: (1) 输入直线的两端点P0 (x0, y0)和P1 (x ...

  10. 命令行创建Maven项目卡住以及出错解决办法。

    第一次通过命令行创建maven项目.结果,果不其然啊,还是出问题了,不过出问题比没有出问题强,知道哪里有问题并学会解决也是一种收获. 遇到的第一个问题,在从仓库下载东西的时候会卡住,我开始以为是网速问 ...