首先创建一个构造函数
function Person(name,age){
this.name=name;
this.age=age;
this.eat=function(){
console.log("吃");
}
}
接着我们实例化两个对象
let stu1 = new Person("小明",20);
let stu2 = new Person("小花",30);
我们调用两个对象的eat方法
stu1.eat();
stu2.eat();
//结果肯定是 吃 吃
但是这两个方法不是同一个方法
console.log(stu1.eat()===stu2.eat());//false
这是为什么呢?每个实例对象都有自己的eat方法,而这个eat方法不是共享的
如果我要创建100个对象,方法的实现是一样的,但却不能共享,岂不是很浪费空间?
天无绝人之路,通过原型的方式是可以解决的:
//原型添加方法
Person.prototype.eat=function(){
console.log("吃东西");
}
此时构造函数和原型对象中都有eat方法,默认先找构造函数,构造函数没有找原型对象,原型对象没有undefind
我们删掉之前构造函数中的eat(),此时:
console.log(stu1.eat===stu2.eat);//true
原型的作用:解决数据共享,节省内存空间
那么原型对象是什么呢?
Person() 是一个构造函数,我们通过console.dir(Person),查看他的详细信息,

stu1是一个实例对象,我们通过console.dir(stu1),查看他的详细对象

从stu1的详细中看到他并没有eat()方法,那么他是如何访问这个方法的呢?
我们打开prototype这个属性:

由此推出:实例对象的__proto__的指向和该实例对象所在的构造函数的prototype的指向相同,
那么实例对象就可以直接访问prototype中的方法了
下面图中的construtor是什么?


构造函数中的prototype中的constructor指向的是原型对象所在的构造函数
而构造函数、实例对象、原型对象三者之间的关系是什么呢?
- 实例对象是通过构造函数创建的
- 构造函数中prototype叫原型对象
- 构造函数中的prototype中有constructor叫构造器,指向的是该原型对象所在的构造函数
- 实例对象中__proto__叫原型对象
- 实例对象中__proto__指向的是该实例对象所在的构造函数的prototype
说到这里我们就要开始推导原型链了:
可以这么理解:原型链就是实例对象和原型对象之间的一种关系
我们说:构造函数中有prototype,实例对象中有__proto__
实例对象的__proto__指向的是该实例对象所在的构造函数中的prototype
换句话说就是:对象的__proto__指向的肯定是某个函数的prototype
往下看:

构造函数中prototype是对象,这个prototype中有__proto__也是对象,那么这个__proto__指向的是谁呢?
它指向的肯定是某个构造函数的prototype
此时,观察它的构造器的指向----是Object()
那么得出结论:
Person.prototype.__proto__===Object.prototype//true
继续观察Person():

既然看到了__proto__,逆推思想:说明这个函数实际上也是一个对象(因为对象中有__proto__);
所以Person()的__proto__指向的肯定是其构造函数的prototype; 对象是通过构造函数创建的,那么Person()的构造函数又是谁呢?
看Person.proto.constructor的指向--------Function()
得出结论:
Person.__proto__===Function.prototype//true
这个时候我们知道了:
js中的函数实际上都是通过Function这个构造函数来创建的(来实例化出来的);
接下来,我们往下深挖,Function

Function.prototype.__proto__===Object.prototype//true

Function.__proto__=== Function.prototype;//true
接下来,还有最后一个Object

Object.__proto__=== Function.prototype;//true

Object.prototype.__proto__ === null;
总结原型链:
