首页 > > > 彻底理解Javascript原型链
JS原型链是前端面试中的经典问题了,几乎所有前端面试或笔试都会考察应聘者对原型链的理解程度,同时这一块知识点也是JavaScript中的一个重点和难点。因为之前看红宝书JavaScript高级程序设计感觉里面讲的不是很透彻,所以今天就打算重新梳理总结一下。
首先,读者可以通过下图自我检测一下是否真正理解原型链:
如果看图能完全理解里面每一个箭头的,可以不用继续往下看了,你已经基本掌握原型链了;如果看图感觉箭头绕来绕去很复杂,也不用慌,看完这篇文章,你就能完全理解这张图了。
1.隐形原型和显性原型
在讲原型链之前,我们需要知道隐形原型(__proto__)和显性原型(prototype)这两个概念。
prototype
prototype属性,它是函数所独有的,它是指向函数的原型对象的一个指针,这个对象的用途就是包含所有实例对象共享的属性和方法(我们把这个对象叫做原型对象);
示例:
function Foo(){
}
Foo.prototype.name="martin";
let f1=new Foo();
let f2=new Foo();
console.log(f1.name); //martin
console.log(f2.name); //martin
可以看出,Foo函数原型对象上的属性和方法被每个实例所共享。
__proto__
__proto__ 是原型链查询的关键属性,它是指向对象构造函数原型对象的一个指针,它是对象独有的。
注意:函数中也有__proto__属性,因为函数是特殊的对象。
function Foo(){
}
Foo.prototype.name="martin";
let f1=new Foo();
console.log(f1.__proto__ === Foo.prototype); //true
其中Foo是f1的构造函数,所以f1的隐形原型指向的是Foo的原型对象。
2.什么是原型链
每个对象都有一个隐形原型 __proto__ 属性,里面存的是一个指针,指针指向该对象构造函数的原型对象,而该原型对象也有一个__proto__属性,里面也存着一个指针,该指针指向该对象的构造函数的原型对象,通过这种方式形成了一条向上寻找的链条,这个链条就叫做原型链。
示例:
function Foo(){
}
let f1=new Foo();
console.log(f1.__proto__ === Foo.prototype); //true
console.log(Foo.prototype.__proto__===Object.prototype); //true
console.log(Object.prototype.__proto__===null); //true
其中f1的隐形原型指向其构造函数Foo的原型对象,且Foo原型对象的构造函数为内置构造函数Object(所有对象的基础构造函数),因此Foo.prototype的隐形原型指向Object的原型对象,最后Object.prototype的隐形原型指向null(这个指向记住就好,底层代码规定好的),这就是一条完整的原型链了,如下图:
3.关键指向要记住
在内置的函数和原型对象中,有几个关键指向或者说是容易混淆的指向我们要记住。
Function.__proto__===Function.prototype,也就是Function内置函数(所有函数的基础构造函数)的构造函数是它自己,因此隐形原型指向自己的原型对象
Object.__proto__===Function.prototype,这个很好理解,Object是对象的基本构造函数,毕竟它也是一个函数,因此它的构造函数就是Function了。
Object.prototype.__proto__===null,也就是说Object.prototype的隐形原型是null,但是要注意的是,这里并不代表Object.prototype的构造函数是null,但是说明null是原型链的顶端,这其实是JS一直以来的一个Bug,记住就行。
4.原型链的查找
原型链的查找其实就是通过隐形原型(__proto__)连接的各个对象构造函数的原型对象(prototype),从而去查找原型链上各个原型对象上是否存在查找的属性或者方法,那原型链的具体查找顺序又是如何呢?
原型链的查找顺序
先找实例对象中是否有查找的属性或方法,若有则直接返回,没有则一步步沿着原型链在上层的原型对象中查找。
示例:
function life() {
}
life.prototype.name="martin";
function person(){
}
person.prototype=new life();
person.prototype.constructor=person;
let student=new person();
console.log(student.name); //martin
这个例子中的实例student的name属性查找顺序如下图所示:
先查找student实例,发现没有name属性,则向原型链上一person.prototype对象中查找,发现也没有,则继续向上一层的life.prototype对象中查找,发现有name属性,则直接返回name的属性值并停止原型链的查找。
5.自测题
下面可以做几个自测题检验一下自己是否理解,如果不理解请再多看几遍文章开头的图。
A instanceof B是用来检测B的原型对象(B.prototype)是否在A的原型链上,如果存在返回true。
console.log(Function instanceof Function);
console.log(Function instanceof Object);
console.log(Object instanceof Object);
console.log(Object instanceof Function);
Function instanceof Function即Function.prototype是否在Function的原型链上
返回true
Function instanceof Object即Object.prototype是否在Function的原型链上
返回true
Object instanceof Object即Object.prototype是否在Object的原型链上
返回true
Object instanceof Function即Function.prototype是否在Object的原型链上
返回true‘
IT人一直在路上
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/2318/viewspace-3146368/ ,如需转载,请注明出处,否则将追究法律责任。