下面这张图是一位小伙伴面试前端岗位,被问到JavaScript的原型和原型链的知识点,没有答出来,面试结束之后,HR发给他的。我觉得这张图把prototype和__proto__解释的很好,在这里分享给大家,并为大家推导一下整幅图:

prototype:原型
__proto__:原型链
prototype从属于函数,是函数的一个原型属性(也叫prototype属性),是一个对象;__proto__是对象的属性,它本身也是一个对象。对象的__proto__属性保存着创建该对象的构造函数的prototype属性。每个对象(除了null)都默认有__proto__属性。
从属关系很重要,一定要搞明白!
上一期推文我们已经知道,用class关键字创建出来的类其实也是一个函数:
class Person {}
console.log(typeof Person); // function
既然类是函数,那么它一定有自己的prototype属性:
console.log(Person.prototype); // {constructor: ƒ}
用Person类创建一个对象,验证该对象有__proto__属性,并且根据从属关系,验证等于其构造函数(此处为Person类)的prototype属性:
const p = new Person();
console.log(p.__proto__); // {constructor: ƒ}
console.log(p.__proto__ === Person.prototype); // true
延用上面的例子,我们打印p.__proto__对象,会看到它的第一个属性就是constructor属性,并且这个constructor属性指向创建这个对象的类:

我们展开constructor属性:

发现constructor属性里面还有一个prototype属性,而且这个prototype属性刚好等于p.__proto__,如果我们再展开它:

……
经过不断的展开、观察,不难发现,对象p.__proto__的constructor属性指向构造它的类,而这个constructor属性的prototype属性又等于p.__proto__,也就是说,constructor属性和prototype属性是一对相反的方向,代码验证:
console.log(p.__proto__.constructor); // class Person {}
console.log(p.__proto__.constructor.prototype); // {constructor: ƒ}
console.log(p.__proto__.constructor.prototype.constructor); // class Person {}
console.log(p.__proto__.constructor.prototype.constructor.prototype); // {constructor: ƒ}
……
所以根据constructor属性,我们可以推测是什么构造出来的这个对象。
有了上边的基础,我们打印一下Person.prototype.__proto__:

我们发现,Person.prototype.__proto__的constructor属性指向的是Object,那么这段代码自然自然成立:
console.log(Person.prototype.__proto__.constructor === Object); // true
// 有了刚刚讲过的constructor属性的经验,我们可以得到:
console.log(Person.prototype.__proto__ === Object.prototype); // true
我们只需要再记住JS中这样的一个规定:
Object.prototype.__proto__ = null; // 原型链的最尾端为null
我们就基本解释左半张图:

相信大家都知道,在JS中,一切皆为对象。函数也不例外,在JS中,每一个函数实际上都是一个函数对象。既然知道了函数也是对象,函数也一定有__proto__属性。延用上边的例子:
console.log(typeof Person); // function
console.log(Person.__proto__); // ƒ () { [native code] }
展开Person.__proto__:

因为在JS的底层,Person类是由new Function()而来,即:
console.dir(Person.__proto__.constructor); // ƒ Function()
则:
console.dir(Person.__proto__ === Function.prototype); // true
又因为Function的类型也是函数:
console.log(typeof Function); // function
所以我们可以推出:
console.dir(Function.__proto__ === Function.prototype); // true
之前说过,一个对象的__proto__属性和prototype属性均为对象,且对象默认情况下都有自己的__proto__属性,我们打印一下Function.prototype.__proto__:

就是说:
console.log(Function.prototype.__proto__.constructor === Object); // true
从而得到:
console.log(Function.prototype.__proto__.constructor.prototype === Object.prototype); // true
抵消掉constructor.prototype之后得到:
console.log(Function.prototype.__proto__ === Object.prototype); // true
至此,文章开头的原型原型链图,解释(推导)完毕。
(完)