前言:
先说几个结论,方便理解:
- 每个对象都有一个属性叫__proto__指向其构造函数的prototype。
- prototype本身也是一个对象,所以他也有__proto__属性。
- 每个函数(构造函数)都有一个prototype对象。
- 原型链的终点(底部)是Object.prototye.__proto__ === null。
- 构造函数的prototype的constructor指向函数自己。
知道了以上几点,理解原型和原型链就比较简单了。
认识对象的原型
Javascript中每个对象都有一个属性__proto__,这个属性指向另一个对象(prototye)。
那么这个属性有什么用呢?
当我们通过引用对象的属性key来获取一个value时,它会触发 [[Get]]的操作;
这个操作会首先检查该属性是否有对应的属性,如果有的话就使用它;
如果对象中没有该属性,那么会属性__proto__指向的对象上的属性;
如何获取prototype这个对象呢?
方式一:通过对象的 __proto__ 属性可以获取到(但是这个是早期浏览器自己添加的,存在一定的兼容性问 题);
方式二:通过 Object.getPrototypeOf 方法可以获取到;
函数的原型prototype
每个函数都有其对应的prototype
function foo(){}
console.log(foo.prototype)
对象没有
let obj = {}
obj.prototype //对象没有这个属性
通过构造函数创建一个对象实例的内存表现
function Person(){};
let person1 = new Person();
let person1 = new Person();
\\相当于会执行以下操作
p = {};
p.__proto__ = Person.prototype;
\\
console.log(person1.__proto__ === Person.prototype); //true
console.log(person1.__proto__ === person2.__proto__); //true
内存表现:
原型继承关系#
关于面试:
原型链是什么:
涉及到的概念挺多的,先举例说明:
假设有一个普通对象x={},这个x会有一个隐藏属性__proto__,这个属性会指向Object.prototype。 即
此时,我们说x的原型是Object.prototype。而__proto__属性的唯一作用就是用来指向x的原型。
接下来说说原型链,还是举例说明:
假设有一个数组对象a =[],这个a也有一个隐藏属性叫__proto__,这个属性会指向Array.prototype。Array.prototype也有一个隐藏属性__proto__,指向Object.prototype。即
此时我们说a的原型是Array.prototype,跟上面的x一样,但也有点不一样。那就是Array.prototype也有一个隐藏属性__proto__指向Object.prototype,即
这样一来,a就有了两层原型,
- a的原型是Array.prototype
- a的原型的原型是Object.prototype
于是通过隐藏属性__proto__就形成了一个链条
以上就是原型链
怎么改变原型链:
看来好像只要改写x的__proto__属性就可以改变x的原型
x.__proto__ = 原型
但是这个写法是并不推荐的,推荐的写法是
const x = Object.create(原型);
//或者
const x = new 构造函数() //会导致x.__proto__=构造函数.prototype
原型链解决了什么问题(有什么用)
在没有class的情况下实现继承,以上面的数组a为例:
- 我们说a是Array的实例, a拥有Array.prototype里面的属性。
- Array继承了Object
- a又是Object的间接实例,a拥有Object.prototype的属性
这样一来a既有了Array.prototype里的属性,又有了Object.prototype里的属性。
这种继承相比于class实现的继承存在的缺点是,不支持私有属性。
所以怎么解决这个问题呢?那就直接使用class实现继承吧