原型与原型链

发布于 2023-01-02  33 次阅读


认识

  • 对应名称
    • prototype:原型
    • __proto__:原型链(原型的连接点)
  • 从属关系
    • prototype -> 函数的一个属性:这个属性是个普通对象 {}
    • __proto__ -> 对象的一个属性:这个属性也是个普通对象 {}
    • 对象的 proto 保存着该对象的构造函数的 prototype obj.__proto__ === Object.prototype
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 的关系

  • 函数也可以看做是个对象,它也有自身 __proto__ 指向 Function.prototype:Function.__proto__ === Function.prototype
  • Function 是用来创建函数的函数,本身又是由自身构造出的对象: Function.__proto__ === Function.prototype
  • Object 构造函数本身也是对象,也有 Object.__proto__ === Function.prototype
// 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.prototype.constructor === Test
  • test.__proto__.constructor === Test
// 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__);

原型存在在原型链上,但现在链不存在了(就是最终到达 Object.prototype 的这个链儿 在 obj1 这断掉了)

  • 所以只要原型链断掉,往回倒得所有对象就都无法通过 自己.__proto__ 找到原型,为 undefined(也就是指针不指向任何原型了,就是 undefined 了)
  • __proto__ 是原型链的指针,原型链都被破坏了,这个指针也断了
  • __proto__ 是 Object 的 属性
最后更新于 2023-07-20