老话说的好JavaScript中万物皆对象,那么每个对象都有原型,而js的诸多特性都是利用原型来实现的,原型、原型链也是面试当中最常问的知识点之一, 今天就让我们来深入探讨一下原型和原型链~
原型
首先我们来看什么是原型 我们首先看会下 MDN 我们的描述
- 这其中也写了原型链,备注当中写的对象关系,如果顺着这个关系捋,其实就是原型链,原型链我们一会再说
获取原型
- 原型的操作无非两个部分,一个是获取,一个是设置首先我们来看如何获取一个对象的原型
- 首先我们声明一个对象
const a = { res: 1 }
console.log('a', a)
# Object.getPrototypeOf(a) 这是对象提供的获取原型的方法, 这里打印的其实就是下面的 【prototype】
打印如下:
这里我们看到一个属性[[ prototype ]]
, 这个就是我们所说的原型, 原型当中包含几个属性,这几个方法大家可以 自己到 MDN 上进行相关的了解, 这里我简单介绍一些,一个是 hasOwnProperty
- hasOwnProperty
返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。 这个还涉及到继承, MDN 上的解释是这样的:
const arr = [1, 2, , 3]
arr.forEach((item) => {
console.log('forEach', item)
})
for (let i = 0; i < arr.length; i++) {
console.log('for循环', arr[i], arr.hasOwnProperty(i))
}
大家可以看一下这段代码, 这里涉及到数组空位, 这个我在前面的文章有过分享,想详细了解的可以去看一下, 那么对象属性也是同理的, 我们获取一个对象自身属性的时候,就需要用到这个属性, 去过滤掉原型上的属性。
- constructor
这个方法返回该属性的构造函数,我们这个是一个对象,所以会返回 Object, 这个还涉及到一个数据类型检测,在数据类型检测的时候,一个 instanceof
const a = { res: 1 }
console.log('a', a instanceof Object)
const arr = [1, 2, , 3]
console.log('arr', arr instanceof Object)
console.log('arr', arr instanceof Array)
上面这几个大家应该都知道结果了, 肯定是返回 true, 所以使用这个方法检测类型会有弊端,原因就是其是沿着原型链查找,数组的第一级原型是 Array
, 再向上 就是 Objcet
了, 函数也是一样, 第一级是 Function
,再上一级是 Objcet
, 一会大家可以看一下我的图形关系.
设置原型
原型的关系其实主要是从我们的类来扩展的,那么肯定就会涉及到继承, 这里只例举一种原型继承~
class Parent {
constructor() {
this.parent = 100
}
}
class children extends Parent {
constructor() {
super()
this.children = 1
}
}
const rea = new children()
console.log('rea', rea)
打印如下:
这里会修改子类的原型,子类原型实际上就是父类的实例,继承这一块我之前也有文章有说,大家可以自行去了解~
- 那么另外一种设置原型的方法,就是
Object
的setPrototypeOf
了,我们先来看一下 MND 给出的解释:
下面是用过 这个方法实现的一种伪继承, 这种继承方式首先使用了 call
然后修改了原型, MDN 也给出了所有关于继承的 demo
包括一些性能问题也都指出~
function Human(name, level) {
this.name = name;
this.level = level;
}
function SuperHero(name, level) {
Human.call(this, name, level);
}
Object.setPrototypeOf(SuperHero.prototype, Human.prototype);
// Set the `[[Prototype]]` of `SuperHero.prototype`
// to `Human.prototype`
// To set the prototypal inheritance chain
Human.prototype.speak = function () {
return `${this.name} says hello.`;
}
SuperHero.prototype.fly = function () {
return `${this.name} is flying.`;
}
const superMan = new SuperHero('Clark Kent', 1);
console.log(superMan.fly()) // Clark Kent is flying.
console.log(superMan.speak()) // Clark Kent says hello.
原型链
原型的查找关系组成了原型链,原型链上有原型,这两个是一种包含关系。 我们来看一下 其官方概念:
这里我们看到, 其实核心就是 proto 和 prototype 的关系~ 也可以称之为 从任何一个数据类型出发,使用__proto__串联起来的对象链状结构
这里我们需要先了解对象访问机制:
-
当访问一个对象成员的时候,访问顺序:
自身查找,如果有直接使用 没有 -----> 到__proto__上查找,有直接使用,没有 -----> 就按照原型链结构继续去__proto__上查找 ...... -----> 最终一定能找到顶级原型(Object.prototype),有就使用,没有返回undefined
其最关键的关系,我已经总结成上面这张图形的这个样子, 大家可以观察一下, 从中间的 自定义构造函数A 出发, 另外下面还有一个子类,这个子类B 继承自中间的 A ,他们的 主要由三者构成, 1、实例对象 2、 构造函数 3、原型对象, 这三者之间相互关系由线条构成, 最终都走向 null
也就是 Object
原型对象之后的下一个指针。 这个图有什么不明白的地方,欢迎留言一起探讨~