「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」
前言
关于原型与原型链,可能准备过面试的同学都能够说出个一二,但有点时候还是模棱两可,总是喜欢用 可能, 大概, 也许 这样不确定的词,让面试官觉得你的基础不勾选扎实。
究其原因,还是因为没有真正对这块知识掌握的不够牢固,没有彻底搞明白prototype、 __proto__、 constructor三者之间的关系。笔者也存在这样的问题,所以想通过这样的方式来梳理一下三者的关系,这次彻底搞懂原型和原型链。
基础概念
ECMAscript将原型链作为实现继承的主要方法。基本原理是利用原型链实现一个对象继承另一个对象的属性和方法。
示例代码:
function FOO(){};
const f = new FOO();
【构造函数】用来初始化新对象的函数叫构造函数。 示例中的FOO()函数就是构造函数。
【实例对象】通过 new 关键字创建的对象叫实例对象。示例中的f。
【原型对象及prototype】构造函数有一个prototype属性,它指向实例对象的原型。常使用原型对象来实现继承。
【constructor】原型对象有constructor属性,指向原型对象的构造函数。
【proto】
__proto__ 属性像是指针,指向构造函数的原型对象。
其实我们这里只是讲了一个通过new关键字生成实例对象的特例。
具体__proto__的指向和生成实例的方式有关,下面就具体讲下。
不同浏览器对原型链的实现会有细微的差别。本篇文章均以chrome浏览器的实现为基础。
构造实例对象的方式决定 __proto__ 的指向
- 通过构造函数和
new构建
function FOO(){};
const f = new FOO();
由构造函数创建的对象,其__proto__指向其构造函数的原型对象。
f.__proto__ === FOO.prototype
// true
- 对象字面量构造的对象
const person = {
name: 'amingxiansen',
sex: 'male'
};
其__proto__指向Object.prototype。
等同于:
const person = new Object({
name: 'amingxiansen',
sex: 'male'
});
结果:
person.__proto__ === Object.prototype
// true
所以严格来讲,1 和 2 可以算作是同一种情况。
- 通过
Object.create()创建
const person1 = {
name: 'amingxiansen',
sex: 'male'
}
const person2 = Object.create(person1);
这里person2的 __proto__ 指向person1。
person2.__proto__ === person1
// true
在没有Object.create函数的日子里,人们是这样做的:
Object.create = function(p) {
function f(){}
f.prototype = p;
return new f();
}
这样就能保证新构建的对象的__proto__指向生成它的对象了。
图解
之前一直搞不清prototype和__proto__的区别,其实可以简单理解为:
构造函数.prototype === 实例对象.__proto__,他们都指向同一个原型对象。
贴上一位掘友提供的图示
总结
- 所有对象都有一个属性
__proto__指向一个对象,也就是原型 - 每个对象的原型都可以通过
constructor找到构造函数,构造函数也可以通过prototype找到原型 - 对象之间通过
__proto__连接起来,这样称之为原型链。当前对象上不存在的属性可以通过原型链一层层往上查找,直到顶层Object对象,再往上就是null了
其实理解其原理之后也没有那么难!
下回不怕面试官再问原型和原型链了!
补充
__proto__
绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于 Person.prototype 中,实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,当使用 obj.__proto__ 时,可以理解成返回了 Object.getPrototypeOf(obj)。
写在最后
我是前端Surpeman,一个热爱分享的表单工程师。如果觉得本文还不错,记得三连+关注,说不定哪天就用得上!您的鼓励是我坚持下去的最大动力❤️。
参考资源: