在JavaScript面向对象中,原型链是一个很重要的知识点,经常出现在大大小小的面试题中,关于原型链的面试题有很多问法和知识点。今天主要分享一下我遇到的一个关于原型链的面试题。问题很简单,什么是原型链?(⊙_⊙;)
🔗面试题:什么是原型链?
说到原型链,就不得不先说说什么是原型对象了,这里先简单介绍一下原型对象
- 在JavaScript中,每个构造函数都有一个原型对象存在,这个原型对象通过构造函数的prototype属性来访问
- 利用原型对象,可以实现为所有的实例对象共享实例方法,不仅节省了内存,还体现了实例对象之间的联系。我们可以将实例方法定义在原型对象中,然后所有的实例方法就都可以访问原型对象的方法了。因此,原型对象其实就是所有实例对象的原型。
简单用代码演示一下
接下来回归主题,介绍原型链
当谈到继承时,JavaScript 只有一种结构:对象。每个实例对象(object)都有一个私有属性(称之为 __proto__ )指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象(__proto__),层层向上直到一个对象的原型对象为 null 。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。几乎所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例
JavaScript 对象是动态的属性“包”(指其自己的属性)。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾
代码演示
html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Documenttitle> head> <body> <script> // 让我们从一个函数里创建一个对象 o,它自身拥有属性 a 和 b 的: let f = function () { this.a = 1; this.b = 2; } /* 这么写也一样 function f() { this.a = 1; this.b = 2; } */ let o = new f(); // {a: 1, b: 2} // 在 f 函数的原型上定义属性 f.prototype.b = 3; f.prototype.c = 4; // 不要在 f 函数的原型上直接定义 f.prototype = {b:3,c:4};这样会直接打破原型链 // o.[[Prototype]] 有属性 b 和 c // (其实就是 o.__proto__ 或者 o.constructor.prototype) // o.[[Prototype]].[[Prototype]] 是 Object.prototype. // 最后 o.[[Prototype]].[[Prototype]].[[Prototype]] 是 null // 这就是原型链的末尾,即 null, // 根据定义,null 就是没有 [[Prototype]]。 // 综上,整个原型链如下: // {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null console.log(o.a); // 1 // a 是 o 的自身属性吗?是的,该属性的值为 1 console.log(o.b); // 2 // b 是 o 的自身属性吗?是的,该属性的值为 2 // 原型上也有一个'b'属性,但是它不会被访问到。 // 这种情况被称为"属性遮蔽 (property shadowing)" console.log(o.c); // 4 // c 是 o 的自身属性吗?不是,那看看它的原型上有没有 // c 是 o.[[Prototype]] 的属性吗?是的,该属性的值为 4 console.log(o.d); // undefined // d 是 o 的自身属性吗?不是,那看看它的原型上有没有 // d 是 o.[[Prototype]] 的属性吗?不是,那看看它的原型上有没有 // o.[[Prototype]].[[Prototype]] 为 null,停止搜索 // 找不到 d 属性,返回 undefined script> body> <script> script> html>
- 简单概括一下,在JavaScript中,对象有原型对象,原型对象也有原型对象,这样就形成了一个链式结构,称为原型链
参考来自🔽
🔗原型链的结构总结
总结为以下4点:
① 每个构造函数都有一个prototype属性指向原型对象。
function Person() {} // 定义函数 console.log(Person.prototype); // 输出结果:{constructor: ƒ} console.log(typeof Person.prototype); // 输出结果:object② 原型对象通过constructor属性指向构造函数。
function Person() {} // 通过原型对象访问构造函数 console.log(Person.prototype.constructor === Person); // 输出结果:true // 通过实例对象访问构造函数 var p1 = new Person(); console.log(p1.constructor === Person); // 输出结果:true③ 通过实例对象的__proto__属性可以访问原型对象。
function Person() {} var p1 = new Person(); console.log(p1.__proto__ === Person.prototype); // 输出结果:true④ Object的原型对象的__proto__属性为null。
console.log(Object.prototype.__proto__); // 输出结果:null
根据以上4点,绘制原型链的结构图如下
最后祝大家面试遇到原型链的问题都能完美答回答🎉