认识

function Test() {
  this.a = 1;
}
console.log(Test.prototype); // {constructor: Test()}

const test = new Test();
console.log(test.__proto__); // {constructor: Test}

console.log('test.__proto__ === Test.prototype', test.__proto__ === Test.prototype); // true

// 由于我们说 对象是有一个 __proto__ 属性的,所以 Test.prototype 也应该有个 __proto__ ,因为本质上 Test.prototype 也是个对象
console.log("Test.prototype.__proto__ === Object.prototype", Test.prototype.__proto__ === Object.prototype); // true
// 而 Test.prototype 本质上是由 Object 构造函数生成的,所以 Test.prototype.__proto__ === Object.prototype

// 由于 Object.prototype 也是个对象,所以应该也有 __proto__ :
console.log(Object.prototype.__proto__); // null
// 但我们会发现,Object.prototype 就是原型链的顶层了,它的 __proto__ 值为 null
console.log(Object.prototype); // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}

// 现在我们给 Test 构造函数中添加一个属性 a (新增的a在第 2 行),打印一下:
console.log(test); // Test {a: 1}
// 由于 Test.prototype 是个对象,我现在给这个对象上挂载一个属性b:
Test.prototype.b = 2;
// 再来打印下 test :
console.log(test);
// 那么 test.__proto__ 上就有了一个属性 b 。原因就是 test.__proto__ === Test.prototype,在 prototype 上加了个 b ,所以 __proto__ 上也就有 b

// 现在我们在 Object.prototype 上挂载一个 c :
Object.prototype.c = 3;
// 再来打印下 test :
console.log(test);
// 所以此时 Test.prototype.__proto__ 上也就有了个 c = 3

// 关系图如下:

/**
 * test {
 *  a: 1,
 *  __proto__ = Test.prototype = {
 *    b: 2,
 *    __proto__ = Object.prototype = {
 *      c: 3,
 *      没有 __proto__
 *    }
 *  }
 * }
 */

// 总结:原型链就是 以一个对象为基准,以 __proto__ 为连接的链条,一直到 Object.prototype 为止 的这个链叫原型链

🌈 Function.proto 和 Function.prototype 和 Object.proto 的关系

// Function Object 这俩既是对象又是函数
// Test 这个函数是由 Function 构造出来的,也就是底层应该是 const Test = new Function(...)
console.log(Test.__proto__);
// 所以有:
console.log("Test.__proto__ === Function.prototype", Test.__proto__ === Function.prototype);

// 但是 Function 有不合理的地方,就是:
console.log('Function.__proto__ === Function.prototype', Function.__proto__ === Function.prototype); // true
// ⬆️ 底层就是这么规定的,如何理解:Function 就是顶层,它自己就是由自己构造的,它用自己的构造函数构造了自身

// 我们知道 Object 其实也是个函数,因为对象就是通过 new Object()  创建出来的:
// const obj = {};
// const obj = new Object();

console.log("typeof Object", typeof Object); // function
// 由于 Object 本身是个函数,所以这个函数本身是 Function 构造的:
console.log("Object.__proto__ === Function.prototype", Object.__proto__ === Function.prototype); // true
// 由上导出
console.log("Object.__proto__ === Function.__proto__", Object.__proto__ === Function.__proto__); // true

🌈 判断属性在对象自身上还是在对象的原型上

// 判断属性是否存在对象本身身上,而不是在 prototype 上:
console.log("test.hasOwnProperty('a')", test.hasOwnProperty('a')); // true
console.log("test.hasOwnProperty('b')", test.hasOwnProperty('b')); // false
console.log("test.hasOwnProperty('c')", test.hasOwnProperty('c')); // false

// 判断属性是否在对象的链条上:
console.log("'a' in test", 'a' in test);  // true
console.log("'b' in test", 'b' in test);  // true
console.log("'c' in test", 'c' in test);  // true

🌈 函数原型上的 constructor

// test.constructor -> 实例化test对象的构造函数
console.log("test.constructor === Test", test.constructor === Test);

console.log(test);
// Test
//	__proto__:
// 		constructor: ƒ Test()
// 		__proto__: Object

// 注意:constructor是可以被改的
function Test1() {
  this.a = 111;
}
test.constructor = Test1;
console.log(test.constructor === Test1); // true

资源

https://player.bilibili.com/player.html?bvid=BV1ci4y157Ci&p=1&page=1

面试题

var obj1 = Object.create(null);
obj1.a = 1;

var obj2 = Object.create(obj1);
obj2.b = 2; // obj2 的原型有,但原型链没了

console.log(obj2.__proto__);