javascript中的原型详解

 前言

  都知道,原型能够使对象的方法达到复用的目的,而不是每个对象都存在相同方法。

概念

  在详细了解原型之前,需要明白以下概念:
    prototype:原型,函数自带的属性,也是一个对象;
    constructor:原型对象自带的一个属性,指向宿主原型的宿主方法;
    __proto__:对象的一个属性,指向实例化出该对象的方法的原型。

  例:
    一个方法 A,实例化出一个 a,则此时 a.__proto__ === A.prototype,而 A.prototype.constructor === A

 

new 实现

  想要了解原型是如何复用对象的,就得了解一个方法是怎么 new 出对象的,直接看代码:

 1 function MyNew(fn, ...args){
 2     let obj = Object.create(fn.prototype)
 3     let res = fn.apply(obj, args)
 4     return res instanceof Object ? res : obj
 5 }
 6 
 7 function A (name){
 8     this.name = name
 9 }
10 let a = MyNew(A, "jane")
11 console.log(a.name)

  上面代码,通过自己实现的 MyNew 成功实例化 A,并成功打印出 jane。
  通过上面代码,可以看出 new 其实就是新创建出一个Object,然后再通过 apply 将新创建的对象(this)绑定到A上,再返回,这样就成功访问 A 的 name 并打印。

  

  下面再给 A 的 prototype 添加一个方法:

1 function A (name){
2      this.name = name
3 }
4 A.prototype.fn = function(){
5      console.log(1)  
6 }
7 let a = MyNew(A, "jane")
8 a.fn()

  A 的 实例仍然可以访问 A 上的 prototype 上的方法。再看 MyNew ,这是因为,在创建 obj 的时候,已经将 obj 的 __proto__ 链接在了 A.prototype 上了,这样在调用方法时,在自身对象找不到的情况下,就会往 __proto__ 找,而 __proto__ 又链接在 A.prototype 上,最终在 A.prototype 上找到方法。

  再看下面的方式调用:

1 a.hello()
2 
3 // ------
4 
5 a.toString()

  当 a 调用 hello 方法时会报错,而当 a 调用 toString 方法时却可以成功调用。
  这里 hello 在原型上没有找到,属于正常保存,而 toString 是因为在 Object 上找到了,所以没报错。这里容易忽略的一点就是 prototype 也是一个对象,也有 __proto__ 属性,而 prototype 的 __proto__ 则指向 Object.prototype,所以最终在 Object 上找到 toString。

 

prototype.constructor

  这个属性( constructor )与 class 中的 constructor 构造不同,并没有什么特殊的地方,甚至可以改掉其值,而唯一性作用可能是获取其宿主原型的宿主方法吧。

 

黑话

  本文中提到了宿主原型,宿主方法,为了避免误解,这里说一下它们的意思:
    一个 prototype ,而 prototype 中有 constructor ,而 constructor 中的宿主原型就是指 prototype。
    一个方法 A,A 有 prototype ,而 prototype 的宿主方法就是 A 。

 

参考

  https://segmentfault.com/a/1190000022927245

  https://zhuanlan.zhihu.com/p/30012224

上一篇:java魔法类之ReflectionFactory介绍


下一篇:qt父类的英文定义