原型与原型链
构造函数、原型、原型链关系图:
原型和原型链
js中的对象(js中一切皆对象)我们可以对它进行增删改查(CRUD)的操作。
如果新声明一个对象: let obj = {}
,会发现这个对象自带了一些属性和方法,这些属性和方法实际上是继承的。
比如: valueOf()
, toString()
, constructor
这些继承的属性和方法都在 obj.__proto__
对象中, obj.__proto__
对象继承自 Object.prototype
。
想要调用继承的属性和方法只需要 obj.property_name
,js提供了 __proto__
访问器来访问继承的属性和方法。
而 obj.__proto__
中也有 __proto__
,它的值是 null
。
其实 obj.__proto__
就是 Object
,而 obj.__proto__.__proto__
就是 null
,也就是说 Object
的原型为 null
。
我们在调用 obj.valueOf()
的时候,js会先找obj自己本身有没有 valueOf()
,如果有就用自己的,如果没有就会在 obj.__proto__
对象中找 valueOf()
,如果也没有就再在 obj.__proto__.__proto__
中找,直到找到或者找不到为止,而这个找的路径就称之为原型链。
实际上如果再定义一个对象,其 __proto__
对象也是和 obj.__proto__
一样的。如果修改了其中一个对象的 __proto__
,另一个对象的 __proto__
也是被修改了,因为他们都继承自 Object.prototype
。
如果想要异化两个对象的 valueOf()
的行为,可以单独给两个对象添加 valueOf()
方法。
比如:
1 | obj.valueOf = function() { |
对于上图的总结如下:
- 所有的对象都有
_proto_
属性,该属性对应该对象的原型 - 所有的函数对象都有
prototype
属性,该属性的值会被赋值给该函数创建的对象的_proto_
属性 - 所有的原型对象都有
constructor
属性,该属性对应创建所有指向该原型的实例的构造函数 - 函数对象和原型对象通过
prototype
和constructor
属性进行相互关联
只要记住一点,函数有原型对象( prototype
),其他的对象就只有原型( _proto_
)。所以这两个通常被称为显式原型和隐式原型。
prototype和__proto__
- 对于所有的对象,都有
_proto_
属性,这个属性对应该对象的原型。 - 对于函数对象,除了
_proto_
属性之外,还有prototype
属性,当一个函数被用作构造函数来创建实例时,该函数的prototype
属性值将被作为原型赋值给所有对象实例(也就是设置实例的_proto_属性)
实例:
1 | // 构造函数 |