首先,此文是对于javascript原型链的一些私人见解,若能博君会心一笑,在下荣幸之至!

  为了阐述我的理解,首先提前声明一些前置知识,欢迎指正:

栈内存和堆内存:

  栈内存每个地址分配的地址长度较窄,且长度固定,用于检索和快速遍历,一般存着值类型数据,如string,number,booleen,null。

  堆内存的每个地址分配的地址长度是弹性变化的,用于存储具有一定结构的数据,如函数对象function,实例对象object等,为了提高检索效率,所有堆内存里对象的存储地址都记录在栈内存内,这样通过栈内存遍历就可以快速定位堆内存内的目标。

  在原型链里,常有这种情况:多个栈内存地址__proto__指向另一个栈内存地址prototype,而prototype指向堆内存内某一对象地址。

重点1 -> prototype和__proto__(隐式属性):

  对象分为函数对象和实例对象,函数对象又分为构造函数对象和普通函数对象。

  首先所有对象都具有__proto__属性,而特别地,函数对象内一定有prototype属性,打印任何对象时浏览器内默认不显示隐式属性,

  所有函数都有prototype这一属性对象(原型),且存在constructor属性存储自身堆内存所在地址,即存在隐式属性prototype:{constructor:f}。

重点2 -> 自有属性和继承属性(显式属性和隐式属性):

  var a = {}

  console.log( a.b )  // undefined

  a.__proto__ = { b:'b' }

  console.log( a.b )  // b

  函数对象一般有两个隐式属性,prototype指向自己的原型对象,__proto__一般会指向另一个函数对象的原型对象。

  从第三行代码看出对象的隐式属性是可以随意修改的,这也是javascript的一大特点,第四行代码实际上查找的是a.__proto__.b,浏览器隐藏了这一过程。

    访问函数的属性时,存在一个两遍查找的过程,第一遍查找自有属性,如果自有属性没有去继承属性__proto__里查找,__proto__会指向另一个函数对象的原型,在此原型里找同名属性,那如果没有找到怎么办呢?

  我们上文说过,函数对象有两个隐式属性,既然自己的prototype指向的这个原型对象里没有它需要找的同名属性,就会通过自己的另一个隐式属性__proto__再指向其构造函数的prototype,再到prototype指向的原型对象里去查找,由此形成原型链,事实上,原型链在进入到最后一环之前一直是以这样一种简单的规则勾连的。

重点3 -> 继承本质:

  var a = function () {} // #不一定要是构造函数

  var b = new a();

  console.log( b.__proto__ === a.prototype ) // true #类型和存储的地址都相同

  所有被函数构造出来的实例对象,其隐式属性__proto__指向其构造函数的隐式属性prototype。

  如果一个构造函数构造了许多实例对象,它们所有的__proto__相当于前文提到的栈内存入口,指向同一个构造函数的prototye,而构造函数的prototype是第一个指向其原型对象的栈内存入口,一般也可以将prototype说成原型对象,当然本质是其指向堆内存里的对象地址。

  堆内存里的原型对象所具有的属性和方法,javascript通过一些底层方法使得实例对象省略.__proto__的形式直接访问,且__proto__一旦找到对应的属性或方法就返回结果,不再往上寻找更高一级的原型对象,这就是继承的本质。

顶级构造函数Object 和 Function:

  Function.prototype.hasOwnProperty = function () { return 'Function prototype' } // #Object的原型里有内嵌hasOwnProperty方法

  function f () { this.x = 1 }

  f.x = 1

  var obj = new f()

  console.log( f.hasOwnProperty( 'x' ))  // Function prototype

  console.log(obj.hasOwnProperty( 'x' )) // true

  console.log( Object.hasOwnProperty( 'x' )) // Function prototype

  console.log( Object.__proto__ === Object.prototype ) // false

  console.log( Object.__proto__ === Function.prototype ) //  # Object将会继承Function的原型!

  console.log( Function.prototype === Function.__proto__ ) // true  #这意味着Function会自己继承自己的原型!

  console.log( Function.hasOwnProperty( 'x' )) // Function prototype

  上文说到如果自己的原型对象里没有要查找的同名属性,则通过另一个隐式属性__proto__再指向另一个函数的prototype,如此循环往复,最后都会到达两个顶级的构造函数,此时顶级函数Object的隐式属性__proto__将会找到Function的原型对象,令人惊讶的是Function的__proto__指向自身的prototye,这就意味着Function会继承自己的原型对象!

  对于所有实例对象和衍生函数而言,Object和Function是原型链的终点,它们两之间的继承关系显得特别混乱,如果按原型链的规则来制定最后一级的继承关系,势必引起所有函数和对象逻辑上的混乱。

  事实上,为了让函数具备对象的特性,在逻辑上是不可能有一套通用的规则的。

  我们假设javascript这种语言不再赋予函数对象的特性,那么原型链不仅会变得特别好理解,而且层次分明。

  Object和Function两个构造函数可以各司其职,各不相关,一个负责构造对象,一个负责构造函数,多么美妙而有序。所有对象的顶级原型都是Object.prototype,所有函数的顶级原型都是Function.prototype,然而javascript作为一门混沌而具有无穷潜力的语言,善于搜刮各种其他语言的优势填补自身的短板,而其他热门语言所具有的一大杀器便是继承的特性,对象和继承使javascript衍生出另一项绝活------构造函数。

  构造函数使得构造对象不再是Object函数的专利,但这也引申出另一个问题,那就是原型链的顶级原型和下一级原型之间需要谨慎应对(毕竟使用隐式属性模仿继承总有漏洞,需要一些手段来矫正规则),于是函数和实例对象的原型链在进入到最后一级的原型后,javascript在底层对它们添加了‘新的继承规则’。

  这些添加的规则使得所有实例对象和函数的继承特性在最后一级也表现得富有逻辑,比如一个实例对象,它既然不是函数,就不应该继承任何Function的原型属性和方法,因而Function的原型不会对任何实例对象造成干扰。

  一个函数,它若即是函数又是对象,在面向对象编程思想盛行的时代,就有继承Object所有方法和属性的权利,于是在函数的顶级原型Function.prototype上又加了一层Object.protype这一原型。

  Javascript通过隐式属性和在最后一级‘新的继承规则’成功复制了继承这一经典模式,除此之外,为了填补自身短板,适应各类编程思想,Javascript还发展出许多新的特性。

  比如摆脱弥补饱受诟病的弱类型特点,Javascript发展出strict模式下的编程,增加了许多报警机制,甚至衍生出一门新的语言Typescript。为了向Java等强类型语言致敬,发展出访问器属性,这一机制也是前端框架的核心原理之一.。

  最后,希望你我都能在Javascript语言中找到编程的乐趣,以上拙见,转载请注明出处。。

  

  

从原型链探究Javascript这么火的原因的更多相关文章

  1. JavaScript学习总结(四)——this、原型链、javascript面向对象

    一.this 在JavaScript中this表示:谁调用当前函数this就指向谁,不知道调用者时this指向window. JavaScript是由对象组成的,一切皆为对象,万物皆为对象.this是 ...

  2. js原型与原型链探究

    原型有一个非常重要的属性叫 prototype 一.先写一个简单的例子,看看 A的原型和A的实例 分别是什么 function A() {} var a = new A() console.log(a ...

  3. 前端知识体系:JavaScript基础-原型和原型链-理解JavaScript的执行上下文栈,可以应用堆栈信息快速定位问题

    理解JavaScript的执行上下文栈,可以应用堆栈信息快速定位问题(原文文档) 1.什么是执行上下文: 简而言之,执行上下文就是当前JavaScript代码被解析和执行时所在环境的抽象概念,Java ...

  4. 玩转JavaScript OOP[3]——彻底理解继承和原型链

    概述 上一篇我们介绍了通过构造函数和原型可以实现JavaScript中的“类”,由于构造函数和函数的原型都是对象,所以JavaScript的“类”本质上也是对象.这一篇我们将介绍JavaScript中 ...

  5. javascript继承--原型链的 继承

    作者的话:原型链是JavaScript中相当重要的一个知识点,这里我使用了函数结构图,来帮助我更好的理解 /* 原型链继承方式: 通过改变一个对象的原型对象的指向来继承另一个对象 原理: 我们知道,一 ...

  6. 《前端之路》之 JavaScript原型及原型链详解

    05:JS 原型链 在 JavaScript 的世界中,万物皆对象! 但是这各种各样的对象其实具体来划分的话就 2 种. 一种是 函数对象,剩下的就是 普通对象.其中 Function 和 Objec ...

  7. JavaScript中的原型链和继承

    理解原型链 在 JavaScript 的世界中,函数是一等公民. 上面这句话在很多地方都看到过.用我自己的话来理解就是:函数既当爹又当妈."当爹"是因为我们用函数去处理各种&quo ...

  8. Javascript之继承(原型链方式)

    1.原型链 原型链是JavaScript中继承的主要方法. 每个构造函数都拥有一个原型对象,原型对象都包含一个指向构造函数的指针(constructor),实例都包含一个指向原型对象的内部指针(__p ...

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

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

随机推荐

  1. js操作对象

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  2. JS案例五:设置全选、全不选以及反选

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. Python随笔--继承

  4. java线程入门一

    线程优先级: 在JAVA线程中,通过一个int型变量priority来控制线程优先级,线程的有限机为1-10,默认为5,优先级高的线程获得的运行时间要高于优先级低的线程.但这只是一个提示,操作系统和J ...

  5. volatile 与 JVM 指令重排序

    前言: 在做单例模式时 有博客在评论区 推荐使用 volatile 关键字 进行修饰 然后用了两天时间查资料看文档 发现涉及的面太广 虽然已经了解为什么要使用 volatile + synchroni ...

  6. rabbitmq web 管理系统的信息

    rabbitmq web 管理系统的信息   端口 15672 帐号密码 guest/guest 要开启web 管理, 需要手动执行命令: rabbitmq-plugins enable rabbit ...

  7. 2D 加速图形界面开发源代码亲写 想买来学习得加qq 313244484 20万当前代码,完整400万包写完

    #include "StdAfx.h" #include "GUIFrame.h" #include <stdlib.h> #include < ...

  8. C++面试笔记(3)

    20. 浅拷贝与深拷贝 如何理解C++中的浅拷贝与深拷贝 深拷贝和浅拷贝 在进行对象拷贝时,当对象包含对其他资源的引用,如果需要拷贝这个独享所引用的对象,那就是深拷贝,否则就是浅拷贝 *** 21.构 ...

  9. 第四次:渗透练习,xss学习

    xss学习 一.学习目的 初步了解xss攻击,不包括(DOM类型) 二.附加说明 1.xss介绍 https://baike.baidu.com/item/XSS%E6%94%BB%E5%87%BB/ ...

  10. OO第四次作业

    一.论述测试与正确性论证的差异 我认为论述测试代表从理论的角度来进行运行正确性的判断,而正确性测试则是从实践的角度来看待程序的正确性问题.两者之间有着明显的差异. 正确性论证是仅仅从代码的逻辑结构方面 ...