一、什么是原型链
在js中,当调用一个实例对象的某个属性或者方法的时候,如果当前实例对象没有这个属性或者方法,就会去当前实例对象的__proto__属性中查找,如果当前实例对象的__proto__属性中没有,就会继续从当前实例对象的__proto__的__proto__属性中查找,一直找到Object对象,因为Object对象的Object.prototype的__proto__值为null.
二、原型链中的两个重要属性
prototype:这个属性是函数拥有的。这个属性的属性值是一个对象,对象中有一个__proto__属性,这个属性会指向父级的prototype属性。
__proto__:这是一个隐式属性,实例化对象拥有,函数不会直接拥有这个属性。例如:const instance = new Person();则instance实例对象会拥有一个属性__proto__,这个__proto__属性的值和Person.prototype的值相同。
两个属性举例:
1.prototype举例
function doSomething(){}
console.log(doSomething.prototype);
//控制台输出
{
constructor: ƒ doSomething(),
__proto__: {//doSomething的父级是Object,因此和Object的prototype相同
constructor: {
//省略其他方法,因为Object对象的__proto__为null
__proto__: null
},
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
2.__proto__举例
function doSomething(){}
const instance = new doSomething();
console.log(instance);
//控制台输出
{
//实例对象中没有构造函数属性,而__proto__属性和doSomething.prototype的值相同
__proto__: {
constructor: {
},
__proto__:{//doSomething的父级是Object,因此和Object的prototype相同
//省略其他方法,因为Object对象的__proto__为null
__proto__: null
},
hasOwnProperty: ƒ hasOwnProperty(),
isPrototypeOf: ƒ isPrototypeOf(),
propertyIsEnumerable: ƒ propertyIsEnumerable(),
toLocaleString: ƒ toLocaleString(),
toString: ƒ toString(),
valueOf: ƒ valueOf()
}
}
可以发现,const instance = new doSomething();instance实例对象有一个__proto__属性(无构造函数constructor),实例对象的__proto__属性和doSomething.prototype的值相同(指向的是同一个对象),而doSomething.prototype中的__proto__是Object.prototype的值(因为doSomething的父级是Object),doSomething.prototype中的__proto__的__proto__就是Object的父级,因为js中规定Object的__proto__为null,所以Object是原型链最顶层。
三、为何需要原型链?
1.当你定义了一个函数,如果把公共的方法和属性都在构造函数中定义,那么每个实例对象中都会单独持有一份数据,这样会增加内存开销。而在prototype中定义,在内存中只有一份数据,所有的实例的方法和属性指向这个引用即可。
2.避免了代码冗余,公用的属性和方法,可以放到原型对象中。