你真的明白javascript中的原型和原型链了吗

文章开头说的话

首先你必须明白(或者记住)的JavaScript常识:

  1. 在JavaScript中每个函数都有一个prototype属性
  2. 在JavaScript中每个对象都有一个__proto__属性
  3. 在JavaScript中函数是一等公民,即函数也是对象

prototype和__proto__

prototype到底是个啥呢?下面看下这段代码,我们慢慢来

// Animal是个构造函数,所以有prototype属性
function Animal(){}
// 在prototype上定义eat方法
Animal.prototype.eat = function(food){
  console.log("it is eating " + food);  
}
// 构造函数实例化a1
const a1 = new Animal();
// 构造函数实例化a2
const a2 = new Animal();
// 调用实例的方法
a1.eat("food");
a2.eat("food");

从上面的代码中,我们可以看到:

  1. 函数的prototype指向一个对象
  2. 函数实例化后的对象可以获取prototype指向对象的方法(和属性)

那他们之前的关系是怎么样的呢?
你真的明白javascript中的原型和原型链了吗

从图中我们可以看到:

  1. Animal的prototype指向一个对象
  2. Animal的实例通过__proto__关联到Animal的prototype指向的对象

用官方术语说,就是:

  1. 函数的prototype所指向的对象就是该函数创建的实例的原型(即:a2和a2的原型是Animal.prototype)

那么问题来了,什么是原型呢?
在JavaScript中,每个对象(null除外)在创建的时候都会与之关联另外一个对象,对象和原型之间通过__proto__进行关联

原型的作用

在上面的代码中,我们可以看到实例对象中并没有eat方法,但是每个实例对象都可以调用eat方法,那中间的过程是怎样的呢?

  1. 当我们调用实例对象(a1和a2)的方法(eat)的时候,如果找到则直接调用实例对象的方法或者属性;如果找不到,就会查找与之关联的原型上是否有这个方法,如果这个原型没有,就会继续向上查找该原型的原型(原型的原型后面探讨)

原型的原型

在上面我们提到了如果在原型上找不到相应的属性或者方法,就会在原型的原型上查找,那么什么是原型的原型呢?

  1. 首先在文章开头我们说每个对象都有原型,而原型也是对象,所以原型也是有原型的(听起来有点绕)
  2. 那之前代码的Animal.prototype的原型指向哪里呢(即Animal.prototype.__proto__)指向谁呢?这里Animal.prototype是JavaScript内置构造函数Object生成的呢,那是不是应该指向Object.prototype呢?答案是是的。
  3. 那Object.prototype也是对象,它的原型呢?Object.prototype.__proto__指向哪个对象呢?答案是:null;即:
Object.prototype.__proto__ === null // true
// 表示如果查找属性的时候到Object.prototype时还是没有就停止,没有了

最后画张图:
你真的明白javascript中的原型和原型链了吗

原型链

注意到上图中的蓝色线条部分了吗,这就是大名鼎鼎的原型链。

补充的知识

  1. constructor: 这个是原型中的自带属性,指向构造函数
  2. __proto__: 这个属性其实是浏览器实现的,不是标准的访问原型的方式;ES5中规定的正式方法是:Object.getPrototypeOffang'fa
Object.getPrototypeOf(a1) === Animal.prototype // true

以上知识,最终的图如下:
你真的明白javascript中的原型和原型链了吗

思考题:

  1. 在文章开头我们说过函数也是对象,既然是对象就有原型,那Animal的原型指向谁呢?
  2. Function.prototype === Function.__proto__ 是true吗?

相关推荐