zoukankan      html  css  js  c++  java
  • 原型继承问题迷失

    发现问题

    在开发中需要实现一个自定义Error,它继承自Error,按照经验,一般会这样来做:

    function CustomError(message) {
      this.message = message;
    }
    
    CustomError.prototype = Object.create(Error.prototype, {
      name: {
        value: 'customError'
      },
      constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
    
    var myError = new CustomError('f**k error!');
    
    console.log(myError instanceof CustomError) // true
    console.log(myError instanceof Error) // true
    
    

    它工作的很好,没有明显的bug,所以长期以来,我都这么干。直到某天试着这样:

    > console.log(CustomError instanceof Error)
    < false
    

    为什么这里返回的是false

    再一个,回到题目本身,我想和大家讨论的是函数之间的继承关系。具体而言,就是CustomError通过原型链继承于Error,实现预期不仅表现在CustomError的每一个实例上,也表现在其本身——CustomError instanceof Error === true

    原型与原型链

    在回答问题之前,需要理一理原型和原型链是什么、干什么、以及它们之间的关系。

    是什么

    • 原型 prototype
      每个函数有一个prototype属性,指向函数的原型对象。
      它的值是一个对象(属性的集合),默认只有一个constructor属性,指向函数本身。
    • 原型链 __proto__
      1、每个对象默认有一个__proto__属性,指向创建该对象的函数的prototype。
      2、访问一个对象的属性时,先在该对象的基本属性中查找,如果没有,在沿着__proto__这条链向上查找,这就是原型链。
      3、函数也是对象,也默认有一个__proto__属性,指向Function.prototype。

    干什么

    • prototype:用来实现基于原型的继承和属性共享。
    • __proto__:构成原型链,同样用于实现基于原型的继承。

    什么关系

    emm,好像还是不太清楚它们之间的联系。但如果从内存的角度上看,就简单了许多。

    当一个函数CustomError被创建成功时,默认带有prototype__proto__两个属性,此时,它们之间的关系如下:

                                  +-------------+    
    Function.prototype     ------->             |
                                  |   内存(堆1)  |
    CustomError.__proto__  ------->             |
    |                             +-------------+
    |
    |                             +-------------+
    +----------.prototype  ------->             |
                                  |   内存(堆2)  |
    myError.__proto__ ------------>             |                               
                                  +-------------+
                          (图1)       
    

    解决之道

    有了以上的铺垫,解答之前的问题就简单多了。

    首先,我们通过CustomError.prototype = Object.create(Error.prototype)操作,改变了CustomError.prototype的指向。

                                  +-------------+    
    Function.prototype     ------->             |
                                  |   内存(堆1)  |
    CustomError.__proto__  ------->             |
    |                             +-------------+
    |                             
    +----------.prototype--+      +-------------+
                           |      |   内存(堆2)  |
    myError.__proto__ +    |      +-------------+                             
                      |    |      
                      |    |      +-------------+
                      |    +------>             |
                      |           |   内存(堆3)  |
                      +----------->             |
                                  +-------------+ 
                          (图2)       
    

    其次,这里的instanceof操作,就是判断CustomError的原型链(__proto__)上是否存在Error.prototype,其结果返回一个Boolean。

    所以结果false并不令人感到奇怪,因为没有任何迹象表明CustomError.__proto__改变了指向,且指向Error.prototype

    回过头,比较一下图1、2,思考两秒钟。

    上面的回答也暗含了解决之道:

    > CustomError.__proto__ = Error.prototype
    > CustomError instanceof Error
    < true
    

    总结

    尽管这样解决了问题,但MDN并不推荐这么做。

    一来,如MDN所讲,这个操作会很慢,可能有性能影响。

    二来,若CustomError.prototype = Object.create(Object.assign({},F1.prototype, F2.prototype)),还要继续改变CustomError.__proto__么?

    三来,我并不保证以上内容绝对正确。譬如说:通过Function.prototype.bind方法构造出来的函数,它没有prototype属性;Object.prototype.__proto__=== null

    烦请勘误指正~

    延展

    • Object.create()
      使用指定的原型对象及其属性去创建一个新的对象(依然维持着对原型对象的引用)。
    • instanceof 运算符
      用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。
    • isPrototypeOf()
      用于测试一个对象是否存在于另一个对象的原型链上。
      与 instanceOf 不同,在表达式object instanceOf AFunction中, object 的原型链是针对AFunction.prototype进行检查,而不是针对AFunction本身。
  • 相关阅读:
    mysql命令集锦
    linux 删除文件名带括号的文件
    linux下的cron定时任务
    struts2文件下载的实现
    贴一贴自己写的文件监控代码python
    Service Unavailable on IIS6 Win2003 x64
    'style.cssText' is null or not an object
    "the current fsmo could not be contacted" when change rid role
    远程激活程序
    新浪图片病毒
  • 原文地址:https://www.cnblogs.com/fayin/p/8432621.html
Copyright © 2011-2022 走看看