首先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. 如何使用MVP+Dagger2+RxJava+Retrofit开发(1)

    概述 在2016年5,6月份开始在知乎上看到开发方法,那时候记得是看mvc,mvp,mvvm这三种开发模式区别,后面进一步了解到google在github上开源了使用这3种模式进行Android开发的 ...

  2. json对象、构造原型、组合继承

    一.json对象套路 var stu = { "name": "龙姑娘", age: 16, classmate: { name: "李小玉" ...

  3. angular学习(七)-- Service

    1.7 服务:Service 如果做过后台开发,那么对 Angular 中的服务就好理解多了. 在 Angular 中,服务的概念和后台的服务概念基本是一样的,差别只是在于技术细节. 服务是对公共代码 ...

  4. python进阶(7):面向对象进阶

    学了面向对象三大特性继承,多态,封装.今天我们看看面向对象的一些进阶内容,反射和一些类的内置函数. 一.isinstance和issubclass class Foo: pass class Son( ...

  5. Slave_SQL_Runing:NO 复制出现问题的解决办法

    --Slave_SQL_Runing:NO 复制出现问题的解决办法 -------------------------------------------------------2014/05/21 ...

  6. Oracle impdp通过network_link不落地方式导入数据

    --Oracle impdp通过network_link不落地方式导入数据 -----------------------------------------------------2014/01/1 ...

  7. macvlan 网络隔离和连通 - 每天5分钟玩转 Docker 容器技术(57)

    上一节我们创建了两个 macvlan 并部署了容器,网络结构如下: 本节验证 macvlan 之间的连通性. bbox1 能 ping 通 bbox3,bbox2 能 ping 通 bbox4.即:同 ...

  8. Android自定义控件系列之基础篇

    一.概述 在android开发中很多UI控件往往需要进行定制以满足应用的需要或达到更加的效果,接下来就通过一个系列来介绍自定义控件,这里更多是通过一些案例逐步去学习,本系列有一些典型的应用,掌握好了大 ...

  9. jquery.cxcalendar 插件基本使用

    <link href="~/Content/Calendar/css/jquery.cxcalendar.css" rel="stylesheet" /& ...

  10. js与php的区别

     1 . PHP拼字符串用的是点.         js用+号.2.  php文件要放在wamp文件里面的www里面.3.  php与js的嵌入方式相同,只是嵌入的标记不一样.4.  php输出语法用 ...