JavaScript的原型链
本篇文章为作者对原型链的一些自己的理解和总结,如果有哪里不对或者不够详细欢迎补充。
在说到JavaScript的原型链时,会遇到以下几个关键字:
原型、prototype、__proto__、constructor、实例、原型链。
首先我们知道,在JavaScript中,大多数的量、方法、函数都可以看作对象。
而原型在理解上来说就是当我们创建上述的JavaScript对象(null除外)时,一定会有一个对象与这个创建的对象相关联,这个对象就是我们所说的原型,每一个对象都会从原型中“继承”属性。
一. Prototype
在JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象。
例如:
1 function Student(age){ 2 this.age=age; 3 } 4 Student.prototype.name = 'Alice' 5 const student1=new Student(); 6 const student2=new Student(); 7 console.log(student1.name); //Alice 8 console.log(student2.name); //Alice 9 Student.prototype.name = 'Pop' 10 console.log(student2.name); //Pop
函数的prototype指向了一个对象,而这个对象正是调用构造函数时创建的对象的原型,也就是student1和student2的原型。
上面的代码中我们也可以看到,随着原型的name的改变,这些实例的name也发生了改变。
而在这里,我们通过一个图让大家了解原型。

二. __proto__
这是每个对象(除null外)都会有的属性,叫做__proto__,这个属性会指向该对象的原型。
1 function Student(age){ 2 this.age=age; 3 } 4 const student = new Student(); 5 console.log(student.__proto__===Student.prototype); // true
而这之间的关系可以用下图表示

三. Constructor
每个原型都有一个constructor属性,指向该关联的构造函数。
1 function Student(age){ 2 this.age=age; 3 } 4 console.log(Student===Student.prototype.constructor) // true
而这之间的关系可以用下图表示

这里我们再看一个例子:
1 function Student(age){ 2 this.age=age; 3 } 4 console.log(Student===Student.prototype.constructor) // true 5 const student = new Student(); 6 console.log(student.constructor===Student); //true
为什么上面的第二个的返回会是true?
因为在得到student.constructor时,student是没有这个属性的,因此它会从它的原型中,也就是student.prototype中寻找constructor,也就变成了第一个的判断,因此才会与构造函数相同,返回true。
四. 实例与原型的关系
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止,这是JavaScript中的重要的一部分。
1 function Student(age){ 2 this.age=age; 3 } 4 Student.prototype.name = 'Alice' 5 const student = new Student(); 6 student.name='pop' 7 console.log(student.name) // pop 查找到的是实例的属性 8 delete student.name 9 console.log(student.name) // Alice 在实例中查找不到该属性,向上查询,在原型中查找到该属性
在上面这个代码中,我为student实例添加了一个name属性,为Student原型添加了name属性,当查找student的name属性时,先在student本事查找这个属性,查找到了便返回,值就是第一个输出的值,然后因为我删除了student的name属性。当再次寻找name属性时,student本身并没有这个属性,因此从他的原型中寻找这个属性,于是在Student.prototype中找到了name属性,返回。
那么如果Student.prototype也没有这个属性,他会继续找到哪里呢?
答案是找到原型的原型。
我们知道,JavaScript万物皆为对象,那么原型也不例外,他也是一个对象,既然是对象,那么就可以用Object的方式去创建他。
1 const obj = new Object(); 2 obj.name = 'Alice' 3 console.log(obj.name) // Alice
这里我们可以通过代码来看任意构造函数的原型和对象原型之间的关系之间的关系
function Student(age) { this.age = age; } console.log(Student.prototype.__proto__===Object.prototype); // true
没错,原型就是这么被创建出来的(或者说你可以这么理解),这样我们在更新我们的原型的关系图。

五. 原型链
简单的回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。——摘自《javascript高级程序设计》
其实看起来就像是套娃一样对不对,没错,就是上面的第四点的不断重复。
那么object.prototype的原型呢?
1 console.log(Object.prototype.__proto__) // null
null 表示“没有对象”,即该处不应该有值。
所以到这里时,就已经查找停止了。
那么最后我们的关系图就变成了这样

图中的student(实例)->Student.prototype(原型)->Object.prototype->null 这条相互关联的原型的链就是原型链。
文章中的代码可查看我的GitHub:https://github.com/FuloliyaLansfroya/allProject

浙公网安备 33010602011771号