一、原型链介绍
原型链是JavaScript中对象之间通过原型(prototype)关联起来的机制。它是实现继承和属性查找的重要概念。
在JavaScript中,每个对象都有一个原型(除了一些特殊的对象,如null)。对象的原型可以是另一个对象,而这个原型对象本身也有自己的原型,这样一直延续下去形成了原型链。
当我们访问对象的属性或方法时,如果对象本身没有该属性或方法,JavaScript会沿着原型链向上查找,直到找到该属性或方法或者到达原型链的末端(即null)为止。
例如,假设有一个对象A,它有一个属性x。当我们使用A.x访问属性x时,JavaScript首先查找对象A自身是否有属性x。如果找到了,则返回该属性的值。如果没有找到,JavaScript会继续在A的原型上查找属性x,即A的原型对象的属性。如果找到了,返回该属性的值。如果还没找到,JavaScript继续在A的原型的原型上查找,以此类推,直到找到属性x或者到达原型链的末端。
通过原型链,对象可以继承和共享原型对象上的属性和方法。当我们在一个对象上调用一个方法时,如果该方法不存在于对象本身,JavaScript会在原型链上继续向上查找,直到找到该方法。这样可以实现属性和方法的继承和共享,节省内存并提高代码的重用性。
原型链是JavaScript中实现面向对象编程的基础机制,它使得对象之间可以建立关联,并通过继承和共享实现代码的复用。
二、prototype和__proto
首先,对于这两个属性,我一开始是很模糊陌生的。就算去听了课,或者在别的文章看到懂了之后。过了一段时间就很容易忘记,因此想要把自己作为一个完全不懂的人,去摸索。
关于这两个属性,可以知道他们是在对象和函数(函数其实也是对象)中出现了,然后通过代码实验,不断摸索出他们之间的关系。
2.1 函数foo原型及其创建出来的对象之间的关系
function foo() {
}
console.log("===================foo的prototype和__proto__=====================")
console.log(foo.prototype) // 返回一个对象,有constructor属性
console.log(foo.__proto__)
var obj = new foo()
console.log("===================foo生成的obj的prototype和__proto__=====================")
console.log(obj.prototype)
console.log(obj.__proto__)
// 可知
console.log(foo.prototype === obj.__proto__) // true
console.log(foo.prototype.constructor === foo) // true
console.log(foo.prototype.constructor.prototype === foo.prototype) // true
描述:
- foo的prototype是一个
对象,__proto__是一个对象的原型 - obj的prototype是undefined,__proto__是一个
对象 - foo的prototype和obj的__proto__指向同一个地方
- foo.prototype.constructor与foo相等,即:指向同一个地方
- foo.prototype.constructor.prototype === foo.prototype,即:指向同一个地方
根据上述描述,可以画出以下这张图
这个图清楚的看出三者之间的关系,我们明显可以看到,我当时并不知道foo的__proto__指向什么的东西,于是去查了下资料,发现这里头大有玄机,后续说明。
2.2 内置对象Function与Object的关系
为什么突然说到这两个对象呢?显然是跟上述的描述是有关系的,我通过对这两个对象的prototype和__proto__属性进行摸索,也总结出了一些东西,看下述代码和图片就可以明白了。
console.log(Function.__proto__)
console.log(Function.prototype)
console.log(Function.constructor)
console.log(Object.prototype)
console.log(Object.__proto__)
console.log(Object.constructor)
console.log(Function.__proto__ === Function.prototype) // true
console.log(Object.__proto__ === Function.prototype) // true
console.log(Object.__proto__ === Function.__proto__) // true
console.log(Object.constructor === Function)
console.log(Function.constructor === Function)
// Function原型和Object原型之间的关系
// Object原型的属性
console.log("Object原型的属性")
console.log(Object.prototype.prototype) // undefined
console.log(Object.prototype.__proto__) // null
console.log(Object.prototype.constructor == Object)
// Function原型的属性
console.log("Function原型的属性")
console.log(Function.prototype.prototype) // undefined
console.log(Function.prototype.__proto__) // 有值
console.log(Function.prototype.__proto__ == Object.prototype)
描述:
- Function的__proto__和prototype都指向一个
对象的原型 - Object的prototype指向一个
原型对象,__proto__指向一个对象的原型 - 根据1、2描述以及代码结果可知,Function.__proto__、Function.prototype、Object.__proto__三者相等
- Function的
constructor属性以及Object的constructor属性均指向Function - Function对象原型的
\_\_proto\_\_指向Object对象原型 - Object对象原型的
constructor指向Object对象
根据上述描述,画出以下这张图
2.3 foo与Function原型之间的关系
通过2.1与2.2想必读者已经知道foo的关系,以及Function和Object之间的原型、构造函数等关系
此时一句关键的代码可以直接验证这两者之间的联系
console.log(foo.__proto__ === Function.prototype) // 返回true
console.log(foo.prototype.__proto__ === Object.prototype) // 返回true
描述:
- foo的__proto__指向的是Function的原型对象
- foo的prototype的__proto__,即:foo原型对象的__proto__指向的是Object的原型对象
这个时候就可以把这两张图连接起来了
三、prototype和__proto__的分析和总结
3.1 分析
看着上一张图,相信第一次看见的人都会有点懵B,当然我也是一样的。根据观察后我慢慢分析出了一些规律,为以下:
- 对象就是对象
- 函数即是函数,也是对象;
- Function、Object既是函数也是对象
__proto__是对象所有的属性prototype是函数所有的属性- 任何原型都是对象即:没有prototype属性
- 根据1、2、3、4、5、6点可以知道,一个对象只有
__proto__属性,而一个函数可以有__proto__和prototype两个属性
3.2 结论
__proto__是指向当前对象、函数作为对象的原型prototype是指向拥有构造当前函数的构造函数的原型
用以上的结论,对上述的完整图的每一条链子就会发现很容易知道为什么是这样的原型链关系了