文章开头说的话
首先你必须明白(或者记住)的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吗?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。