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