JS中的原型链

71 阅读3分钟

js中的原型链

image

由于Javascript中没有类的概念,因此也没有面向对象中的继承的概念。在完全没有任何继承机制的编程语言中,每个对象的方法都要明确设置一遍。假如Javascript没有继承机制,通过构造函数function A(name) {this.name= name;};创建了100个A的对象, 现在要为每个对象实现一个新的方法,就需要明确的为这100个对象都添加一个方法属性,当每个对象都有很多方法的时候,会带来巨大的性能开销和非常差的编码体验,使得代码编写方式最终会倒退到面向过程的方式。

Javascript由面向原型的编程语言self发展而来,通过原型实现了另一种方式的继承:属性共享。如果为了和面向对象中的继承区分,Javascript中通过原型实现的应该叫做共享。

Javascript中的原型在物理上是一个对象,可以是任意对象;原型这个词也表示一种机制。

每个对象都可以拥有一个原型,同时这个原型本身还可以有自己的原型,形成一个原型链,直到其原型为null的原型。

当获取对象的一个属性值的时候,先在当前对象的键值对集合中查找这属性,如果找到返回对应的值;如果没有,则在原型对象的键值对集合中查找,如果还没找到,则在原型的原型中查找,以此类推。

通过一个示例来演示原型的使用方法:

img

image-20220227153625057

上面例子中,PersonPrototype就是原型对象,Person是构造函数,将Person的prototype属性设置为PersonPrototype对象,那么通过new Person创建的所有对象的原型都是PersonPrototype。

通过示例可以看到,修改原型中showMyName方法的定义,p.showMyName()的输出也会跟着发生变化,这个现象和上面原型的定义一致。

如果明确的为对象p设置一个showMyName方法,那么p对象的键值对集合中将也包含showMyName的定义,根据上面规则的描述的规则,p对象中定义的showMyName将被使用,忽略了原型中对showMyName的定义。这个时候,我们可以通过p.proto.showMyName来访问原型中的showMyName方法,但是调用p.proto.showMyName的时候,this关键字绑定到了原型对象PersonPrototype,由于原型对象中没有name属性的定义,因此输出“undefined!”。

接下来再定义几个Person对象:

4776-20151222194126374-110500833

在上述代码中,所有Person对象都共享了原型对象的方法,除非某个Person对象中重新定义了原型中同名的方法。

在对象中定义与原型中同名的属性时,会在该对象中创建一个新的键值对,而不会覆盖原型中同名属性的值。

一般来说原型对象中通常定义方法和常量,不应该定义对象的状态值,因为多数情况下所有的对象共享一个状态值没有实际意义。比如上面的代码中,name属性在逻辑上不应该被定义在原型中,因为每个人都有自己的名字。

方法是最适合定义在原型中的。共享方法给所有的对象,正是原型的主要作用。