一、原型和原型链的基本概念
在JavaScript中,每个对象都有一个原型对象(prototype)。原型对象就是一个普通的对象,在创建新对象时,可以将该对象作为新对象的原型。原型对象可以包含共享的属性和方法,这些属性和方法可以被新对象继承和访问。对象之间通过原型链(prototype chain)互相关联,形成了一个原型的链条。
当访问对象的属性或方法时,JavaScript会首先在对象本身查找,如果找不到,就会沿着原型链向上查找,直到找到对应的属性或方法,或者到达原型链的顶层(Object.prototype)。
下面是使用数组创建原型和原型链的示例:
在JavaScript中,数组是内置的对象。它的构造函数是Array,所有的数组对象都是通过Array构造函数创建的。所以,我们可以将Array构造函数看作是数组对象的原型。
在创建一个数组时,例如let arr = [1,2,3]
,实际上是通过Array构造函数创建了一个新的数组对象arr,并将原型链与Array.prototype关联起来。也就是说,arr对象的原型是Array.prototype。
如图:
根据上面的解释和图例展示,可知,arr.proto 和 Array.prototype是相等的
let arr = [1, 2, 3]
console.log(arr.__proto__ == Array.prototype) // true
可能这里有的小伙伴还有些不太理解,那我换个方式说明一下;
创建数组的方式还可以是这样,
使用Array()
构造函数创建数组:
let arr = new Array(1, 2, 3); // 包含多个元素的数组
此时的这一句代码和图例的对应关系如下:
- Array --Array构造函数
- arr – arr对象
- arr_proto__和Array.protoType是同一个对象 – Array的原型对象
那么这是最基本的构造函数、实例、和原型对象之间的关系,那么原型链又是如何产生的呢?
一起来思考一个问题,arr对象是通过Array构造函数new出来的。那么Array构造函数又是从哪里来的呢,它会不会也是通过一个构造函数new出来的呢?
答案是肯定的,那么我们如何获得Array构造函数的构造函数呢?
我们可以看看arr对象是如何知道Array是他的构造函数的
console.log(arr.__proto__.constructor.name) // Array
这段代码的含义是获取了数组对象 arr 的原型对象(也就是 Array.prototype 对象),并访问其 constructor 属性的 name 属性。
arr.__proto__
获取了 arr 的原型对象。constructor
是原型对象的一个属性,它指向创建该对象的构造函数,对于数组来说,它指向 Array 构造函数。name
是函数对象的一个属性,表示函数的名字。
因此,该代码的含义是获取 arr 的原型对象的构造函数的名字,对于数组对象来说,该名字应为 “Array”。
所以我们可以用同样的方式知道Array的原型对象的构造函数的名字
console.log(Array.__proto__.constructor.name) // Function
如图,控制台打印出来的是Function.
那么我们接着上面的图例扩展,如下:
在以同样的方式来找function原型对象的构造函数是谁
console.log(Function.prototype.__proto__.constructor.name) // Object
再以同样的方式来找Object原型对象的构造函数是谁
console.log(Object.prototype.__proto__.constructor.name)
这次控制台报错了,如下:
说不能从null上获取属性constructor
从这个报错信息可得Object.prototype.__proto__是null
console.log(Object.prototype.__proto__) // null
这就证实了开头我们介绍的原型链的顶层是(Object.prototype)
最后我们再来看下Array原型对象的构造函数是谁
console.log(Array.prototype.