一、原型链的概念
JavaScript是一门面向对象的编程语言,JavaScript 中的所有事物都是对象,并且对象与对象之间不是彼此独立的,而是有“继承”关系的。
这种“继承”关系与Java或者C#中的继承不同。Java或C#中是有类的概念的,子类继承父类,通过extends关键字实现;而JavaScript中没有真正意义上的class(类),但有与之类似的概念——原型对象;A继承B,则A的“__proto__”属性会指向B的“prototype”属性,从而有以下示意图:
A与B的关系为: A.__proto__ ----------> B.prototype , 这句话的意思就是,A的原型(__proto__) 指向 B的原型对象(prototype)。
从A继承B的示意图中,我们看到一条具有指向关系的链,那么这是不是就是今天的主题——原型链 呢?答案是:不是
原型链要求末尾节点为null,B.prototype 不等于null,谁的原型会为null呢?答案是:Object对象的原型对象——Object.prototype,所以有以下示意图:
所以,我把原型链的定义概括为:一群具有继承关系的对象,通过__proto__属性建立指向关系,并最终指向null,从而形成一条指向关系链,我们称之为原型链。
二、为什么会有这样的指向关系
现在,虽然知道了什么是原型链,但是为什么是这样的指向关系,形成这样的一条链,下面我们继续来探讨:
首先,了解概念:
①、对象的分类 如上文所说,JavaScript中所有的事物都是对象,而这些对象,可以分为两大类:函数对象和普通对象。分类的依据是:是否具有原型对象(prototype),或者是否具有prototype属性。函数对象具有prototype属性,而普通对象没有;
②、prototype 原型对象,对象中的所有属性和方法,将继承给子对象,由子对象们共享;
③、__proto__ 原型。任何由继承而来的对象,都有且仅有一个原型,并且由其__proto__属性指明。
由于JavaScript中一切皆对象,并且这些对象最终都继承自Object对象,所以在原型链中末尾的两个节点,一定是Object.prototype和null。
三、对象
前文中,我们无数次提到了一个概念,那就是对象,什么是对象?对象即属性和方法的集合,最典型的
var obj = { name: 'hjx', age: 24, sayHello: function() { console.log('Hello!'); } }
obj既为一个对象,此对象有属性name,age,有方法sayHello()。仅以“属性和方法的集合”这几个字的解释,我们还很难理解JavaScript中的继承关系和原理,所以,引入一下概念:
①、隐式属性 由__proto__属性指向的属性和方法。
②、显示属性 非__proto__属性指向的属性和方法,显示属性分为共享属性和非共享属性。
③、共享属性 继承给子对象,由子对象共享的属性和方法。(共享:所有子对象的公共资源,一旦改变,所有子对象访问时均已改变。本质上是在继承的时候,所有子对象的__proto__属性指向它,在内存中此属性仅此一份,子对象和父对象均可去读写)
④、非共享属性(本地属性) 继承给子对象时,由子对象单独享有的属性和方法,也成为本地属性。(单独享有:继承时,在每个子对象的内存空间中都复制了一份,在子对象中各自独立)
用一个等式来表达对象: 对象 = 显示属性(共享属性 + 本地属性) + 隐式属性
四、关系图
关系图总结:
①、子对象的__proto__属性指向父对象的prototype属性(子对象的原型指向父对象的原型对象);
②、Object对象的__proto__属性指向Function对象的prototype属性(Object对象的原型指向Function对象的原型对象);
③、Function对象的__proto__属性指向自己的prototype属性(Function对象的原型指向自己的原型对象);
④、Function对象的prototype属性的__proto__属性指向Object的prototype属性(Function对象的原型对象的原型指向Object对象的原型对象,因为,Function对象的原型对象是一个对象,由Object对象继承而来)
⑤、Object对象的prototype属性的__proto__属性指向null(Object对象的原型对象的原型指向null,因为,一切对象的原型最终都指向Object对象的原型对象,然而Object对象的原型对象又是一个对象,JavaScript规定它指向null,所以Object对象的原型对象Object.prototype总是在原型链上,除null意外的顶端位置)
完~