zoukankan      html  css  js  c++  java
  • [JavaScript]JavaScript中的原型和原型链

    JavaScript中的原型和原型链

    上一篇博文总结了JS中的数据类型以及所要注意的事项,这篇博文打算顺着数据类型来理解一下关于原型和原型链的问题。

    学习JS中的原型和原型链,要想理解其中的关系和用法,得先搞清楚几个概念。

    全局对象

    MDN中,关于全局对象的定义如下:

    全局的对象( global objects )或称标准内置对象,不要和 "全局对象(global object)" 混淆。这里说的全局的对象是说在全局作用域里的对象。

    ECMAScript 规定全局对象叫做 global,但是浏览器把 window 作为全局对象。

    window 的属性就是全局变量。

    全局变量

    全局变量划分为两类:

    • ECMAScript所规定的属性
      • global.parseInt
      • global.parseFloat
      • global.Number
      • global.String
      • global.Boolean
      • global.Object
    • 私有属性(即浏览器自己添加的)
      • window.alert
      • window.prompt
      • window.comfirm
      • window.console.log
      • window.console.dir
      • window.document
      • window.document.createElement
      • window.document.getElementById

    所有 API 都可以在 MDN 里找到详细的资料。

    现在我们以第一种全局变量为例,说一下全局函数

    全局函数

    先来看一下这段代码:

    
    var s = "string"
    var s2 = new String("string")
    
    

    变量 s 和 s2 的不同之处在于:两者所对应的内存地址不同,除此之外,s 创建的一个基本类型的值,即只有一个字符串 "string";而 s2 创建的是一个对象,里面除了s2的字符串值"string"之外,还包含了很多可以使用的属性。

    如图:

    当我们分别对 s 和 s2 进行操作时:

    s.charAt(0)     //"s"
    s2.charAt(0)    //"s"
    s.name = "here"
    s.name      //undefined
    s2.name = "there"
    s2.name     //"there"
    

    我们可以看到,在对待 s 时,我们可以像使用对象一样去使用基本类型数据,但是却不能对其属性有效赋值。

    之所以能这样去使用基本类型,是因为JavaScript引擎内部在处理对某个基本类型 s进行形如s.sth的操作时,会在内部临时创建一个对应的包装类型(对数字类型来说就是Number类型)的临时对象,并把对基本类型的操作代理到对这个临时对象身上,使得对基本类型的属性访问看起来像对象一样。但是在操作完成后,临时对象就扔掉了,下次再访问时,会重新建立临时对象,当然对之前的临时对象的修改都不会有效了。

    现在我们的 s 和 s2 都可以使用类似charAt()、charCodeAt()等等的函数,那么这些函数又保存在哪里呢?我们在对诸如s、s2等对象或是临时对象操作的时候是分别给他们赋予这些函数吗?

    接下来我们可以引出理解原型链的关键一步:

    __proto__

    对应上一个问题,这些函数保存在哪里?

    这些函数保存在一个公用属性组成的对象里(暂称为A),

    然后让每一个对象的 proto 存储这个 A 的地址。

    (__proto__由浏览器生成)

    s2.__proto__
    

    如图:

    s2.__proto__代表的是String特有的属性,如果我们要调用的函数在A(即String公有属性)中没有,那还怎么办呢?

    他就会上溯一层,来到s2.__proto__.__proto__(即所有对象的公用属性,暂称为B)中查看:

    s2.__proto__.__proto__
    

    如图:

    好了,我们的流程已经渐渐清晰:

    我们需要对象s2读取一个属性,我们先从s2的本体找,如果他没有被赋予这个属性,那么通过s2.__proto__所记录的地址,找到A对象,如果这个A对象里面也没有这个属性,那么我们通过A对象中__proto__所记录的地址,找到B对象,如果B对象是最后一层,那么再找下去就是null。

    再来简化一下:

    s2 -> s2.__proto__ -> A -> A.__proto__ -> B -> null

    现在我们终于可以把原型和原型链带上了:

    A 其实就是 String.prototype

    B 其实就是 Object.prototype

    原型链是:

    s2 -> s2.__proto__ -> String.prototype -> s2.__proto__.__proto__ -> Object.prototype -> null

    自此为止,我们对于原型与原型链的概念已经了解得差不多,每当我们声明一个数据类型(除了undefined和null),他已经存在一条原型链,这条链的起点在于我们刚声明的数据,终点在于null,其中Object.prototype是所有对象的公用属性,我们可以通过__proto__,来查看属性对象合集中有没有包含某个属性。

    总结起来就是:

    var 对象 = new 函数();
    对象.proto === 对象的构造函数.prototype

    再加上一张我用思维导图总结出来的图,希望能帮到对原型、原型链还感到困惑的朋友。

  • 相关阅读:
    Windows Power Shell
    一个自律的人有多可怕!
    Android之TextureView浅析
    BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第9章节--client对象模型和REST APIs概览 介绍SP2013中远程APIs
    敌兵布阵(线段树)
    kendo AutoComplete实现多筛选条件
    Android 65K问题之Multidex原理分析及NoClassDefFoundError的解决方法
    让我心碎的五道题
    输入一列数组,输出它的逆序数组
    centos下配置防火墙port失败
  • 原文地址:https://www.cnblogs.com/No-harm/p/9502720.html
Copyright © 2011-2022 走看看