乐趣区

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

文章开头说的话
首先你必须明白 (或者记住) 的 JavaScript 常识:

在 JavaScript 中每个函数都有一个 prototype 属性
在 JavaScript 中每个对象都有一个__proto__属性
在 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”);
从上面的代码中,我们可以看到:

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

那他们之前的关系是怎么样的呢?
从图中我们可以看到:

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

用官方术语说,就是:
函数的 prototype 所指向的对象就是该函数创建的实例的原型(即:a2 和 a2 的原型是 Animal.prototype)
那么问题来了,什么是原型呢?在 JavaScript 中,每个对象 (null 除外) 在创建的时候都会与之关联另外一个对象,对象和原型之间通过__proto__进行关联
原型的作用
在上面的代码中,我们可以看到实例对象中并没有 eat 方法,但是每个实例对象都可以调用 eat 方法,那中间的过程是怎样的呢?
当我们调用实例对象 (a1 和 a2) 的方法 (eat) 的时候,如果找到则直接调用实例对象的方法或者属性;如果找不到,就会查找与之关联的原型上是否有这个方法,如果这个原型没有,就会继续向上查找该原型的原型(原型的原型后面探讨)
原型的原型
在上面我们提到了如果在原型上找不到相应的属性或者方法,就会在原型的原型上查找,那么什么是原型的原型呢?

首先在文章开头我们说每个对象都有原型,而原型也是对象,所以原型也是有原型的(听起来有点绕)
那之前代码的 Animal.prototype 的原型指向哪里呢 (即 Animal.prototype.__proto__) 指向谁呢?这里 Animal.prototype 是 JavaScript 内置构造函数 Object 生成的呢,那是不是应该指向 Object.prototype 呢?答案是是的。
那 Object.prototype 也是对象,它的原型呢?Object.prototype.__proto__指向哪个对象呢?答案是:null; 即:

Object.prototype.__proto__ === null // true
// 表示如果查找属性的时候到 Object.prototype 时还是没有就停止,没有了
最后画张图:
原型链
注意到上图中的蓝色线条部分了吗,这就是大名鼎鼎的原型链。
补充的知识

constructor: 这个是原型中的自带属性,指向构造函数
__proto__: 这个属性其实是浏览器实现的,不是标准的访问原型的方式;ES5 中规定的正式方法是:Object.getPrototypeOffang’fa

Object.getPrototypeOf(a1) === Animal.prototype // true
以上知识,最终的图如下:
思考题:

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

退出移动版