首先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. C++ STL 栈和队列详解

    一.解释: 1.栈 栈是一种特殊的线性表.其特殊性在于限定插入和删除数据元素的操作只能在线性表的一端进行.如下所示: 结论:后进先出(Last In First Out),简称为LIFO线性表. 举个 ...

  2. hdu--3782--找规律--xxx定律

    /* Name: hdu--3782--xxx定律 Date: 17/04/17 21:34 Description: 找规律题,又想打表了 */ /* for(int i=2;i<30;++i ...

  3. poj-2287---Tian Ji -- The Horse Racing

    #include<iostream> #include<algorithm> using namespace std; bool cmp(const int,const int ...

  4. unity 单指双指事件(单指点击移动,双指滑动拖放)

    using System.Collections; using System.Collections.Generic; using UnityEngine; public class TouchCon ...

  5. 初识matplotlib

    最好将配置项与代码分离,在代码之外用一个永久的文件设定matplotlib参数默认值 配置文件选择放在当前工作目录,包括以下配置项: P13

  6. SO_REUSEADDR与SO_REUSEPORT平台差异性与测试

    前些天,与另外一个项目组的同事聊天的时候,谈到他遇到的一个有意思的BUG.在window上启动服务器,然后客户端连接的时候收到一些奇怪的消息,查证了,原来是他自己的另一个工具也在相同的地址上监听,客户 ...

  7. 使用pg_upgrade工具为PostgreSQL进行版本升级

    公司的测试环境和生产环境都是9.2.4版本,而开发环境是9.0.18版本. PS: 不知道公司里的其它同事,是如何做数据库迁移的,利用第三方可视化工具备份数据库,然后恢复到其它高版本的环境?反正我没成 ...

  8. Orleans例子再进一步

    Orleans例子再进一步 这是Orleans系列文章中的一篇.首篇文章在此 步骤 现在我想再添加一个方法,到IGrains项目内,这个方法里面有个延迟3秒,然后返回一个Task<string& ...

  9. python-----运算符及while循环

    一.运算符 计算机可以进行的运算有很多种,不只是加减乘除,它和我们人脑一样,也可以做很多运算. 种类:算术运算,比较运算,逻辑运算,赋值运算,成员运算,身份运算,位运算,今天我们先了解前四个. 算术运 ...

  10. DVWA笔记之二:Command Injection

    命令注入 1.Low级别 <?php  if( isset( $_POST[ 'Submit' ]  ) ) {      // Get input      $target = $_REQUE ...