构造函数、实例和原型对象
在学习原型这部分知识的时候,我一般通过这个图来了解构造函数,原型对象和实例之间的关系,同时也搞清楚属性中,prototype和__proto__的区别。
先简单说一下,构造函数和普通函数唯一的区别就是调用方式不同,除此之外,它们都是函数。那也就是说,任何只要使用new操作符调用就是构造函数。那么重点就来了:
- 每个函数都会创建一个prototype属性,也就是原型对象。原型对象当中有一个constructor属性指向这个构造函数。
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
this.sayName = function() {
console.log(this.name);
}
}
let p1 = new Person("dede", 18, "male");
console.dir(Person);
我们在chrome浏览器中可以看到
Person这个构造函数的prototype属性,其实也就是Person.prototype(原型对象)。接着,关注下prototype中的constructor指向了Person的构造函数,我们会发现constructor和prototype二者循环引用。
- 那么我们看Person创造出来的实例情况如何
console.dir(p1);
可以出来,p1这个实例是只有__proto__,而没有prototype这个属性。这是因为通过构造函数new出来的实例是没有prototype的属性的。这个__proto__指向的就是Person.prototype,也就是Person的原型对象。那么我们可以总结出来,实例和构造函数是没有直接关系的,它们都与原型对象有直接关系。
- 说到这里,可能还是没有搞清楚prototype和__proto__到底有啥区别。那么我们执行下这段代码。
console.log(p1.__proto__ === Person.prototype); //实例通过__proto__链接到原型对象,构造函数通过prototype链接到原型对象。
在chrome打印的结果是
也就是说,p1这个实例的__proto__指向的是Peroson这个构造函数的原型对象(Person.prototype)。那么疑问来了,构造函数中的__proto__指向什么呢?
Person这个构造函数对于Function来说也是一个实例,那么就可以知道Person.__proto__指向的就是Function.prototype。
那么接着Function这个就比较特殊,Function.__proto__指向的就是匿名函数的prototype。匿名函数对于Object也是实例,那么它的__proto__指向Object.prototype(这个欢迎大家补充)。就这样,层层连接在一次也就形成了原型链。
console.log(Person.__proto__ === Function.prototype);
console.log(Function.__proto__.__proto__ === Object.prototype);
打印出来的结果都为true。
总结:
- 只有构造函数(函数)才有prototype,new出来的实例只有__proto__。
- 实例和构造函数没有直接关系,它们都与原型对象有直接关系。那么,实例也就是通过原型对象来共享构造函数中的方法和属性。
- 每个函数都会自己的constructor,constructor和原型对象两者循环引用。