全面解析-JavaScript原型和原型链

231 阅读4分钟

原型和原型链

说起原型和原型链,想来前端的小伙伴们都不陌生,但是可能很多小伙伴和我一样,刚开始接触时,这什么鬼,看看大佬的文章,可能是因为专家盲点吧,这讲的什么?谁指向谁?

专家盲点 => 对一个事情知道的越多越不记得"不知道这个事情"的情形!

这篇文章将以一个刚刚摸清楚一点点情况的小白视角来介绍原型和原型链,如有错误,欢迎指正!

先介绍几个关键点

  1. 只有函数有prototype和__proto__,实例只有__proto__.
  2. 函数是 function 的实例,函数的 prototype 是objecj的实例.
  3. function._proto__和function.prototype相等
  4. objecj.prototype._proto__指向null(顶端)

以下来为大家解释这几点

什么是原型(prototype)?

  • JS中每一个函数都自带这个属性,他的值是一个对象,叫做原型对象;

  • 对于构造函数来讲,每通过构造函数实例化一个对象,都有一个隐含的属性_proto__ 指向该构造函数的原型对象(prototype).

  • 原型对象好比是公共区域,可以被每一个实例化对象访问到.

  • 在创建函数的时候,如果我们将方法写在原型对象中,不但不会造成全局变量的污染,还可以被每一个实例化对象所共享.

  • 原型的作用: 为实例化对象提供共享的属性和方法,通过原型添加的方法,可以完美的解决数据共享问题,从而节省内存空间.

构造函数、实例、原型对象、Function、Object之间的关系

函数也是对象,构造函数也是对象,可以理解为:构造函数是由"Function构造函数"实例化出来的函数对象。

什么意思呢,就是构造函数是"Function构造函数"使用function声明的实例化函数对象(构造函数本身也是对象),而我们一般说的实例是构造函数通过new实例化出来的对象(我们通常称之为实例).

这说明什么呢?

所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身。所有构造器都继承了Function.prototype的属性及方法。如length、call、apply、bind(ES5)。

知道了所有构造器(含内置及自定义)的__proto__都是Function.prototype,那Function.prototype的__proto__是谁呢?

相信都听说过JavaScript中函数也是一等公民,那从哪能体现呢?如下

console.log(Function.prototype.__proto__ === Object.prototype)

这说明所有的构造器也都是一个普通JS对象,可以给构造器添加/删除属性等。同时它也继承了Object.prototype上的所有方法:toString、valueOf、hasOwnProperty等。

最后Object.prototype的__proto__是谁?

Object.prototype.__proto__ === null // true

已经到顶了,为null。

  • 总结:
    • 实例是构造函数通过new实例化的对象
    • 可以将构造函数理解为"Function构造函数"通过Function 声明 实例化的对象
    • 同时又可以将"Function构造函数"理解为Object的实例
    • 原型的constructor属性,指向构造函数,构造函数又通过prototype属性指回原型
    • 实例的__proto__指向构造器的prototype,构造器(含内置及自定义)的__proto__都是Function.prototype,Function.prototype的__proto__指向Object.prototype,而Object.prototype的__proto__指向null(到达顶部)!

辅助以下图来帮助理解.

在往上是这样子的

原型链

每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数想指针(constructor),而实例对象都包含一个指向原型对象的内部指针(proto)。如果让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针(proto),另一个原型也包含着一个指向另一个构造函数的指针(constructor)。假如另一个原型又是另一个类型的实例……,直到到达达顶部Object.prototype.proto===null,这就是我们常说的原型链

上图:

JavaScript 默认并不会复制对象的属性,相反,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,这便是我们常说的继承.

有关继承的理解我将在之后的更新中继续和大家分享我的理解!

写在结尾

文章中的用图,都尽量使用较为简单的表达,希望可以让更多的人看明白.如有理解错误或者表述错误之处,欢迎大家的指正!