JavaScript 原型与原型链详解
构造函数的缺点
JavaScript 通过构造函数生成新对象,因此构造函数可以视为对象的模板。实例对象的属性和方法,可以定义在构造函数内部。
function Animal(name) {
this.name = name;
this.attr = function () {
console.log('animal');
};
}
const small_dog = new Animal('dog');
const small_cat = new Animal('cat');
上面代码中,Animal 函数是一个构造函数,函数内部定义了 name 属性,所有实例对象都会生成这个属性,即这个属性会定义在实例对象上面。
那么通过构造函数生成对象实例有什么缺点呢,每一个构造函数生成的实例,它们之间的属性是无法共享的,如上代码,每个对象实例都会有 attr 方法,但 attr 方法所做的事情是一样的, 没有必要每次生成一个实例就新建一个 attr 方法,这个方法应该做到共享。
如何解决上述问题,这时候就要用到 JavaScript 的原型对象(prototype)
原型对象(prototype)属性的作用
JavaScript 继承机制的设计思想就是,原型对象的所有属性和方法,都能被实例对象共享。也就是说,如果属性和方法定义在原型上,那么所有实例对象就能共享。
JavaScript规定,每个函数都有一个prototype属性,指向一个对象。
如下代码,函数 f 默认具有 prototype 属性,指向一个对象。
function f() {}
console.log(f);
typeof f.prototype; // "object"
对于普通函数来说,该属性基本无用。但是,对于构造函数来说,生成实例的时候,该属性会自动成为实例对象的原型。
如下代码,构造函数 Animal 的 prototype 属性,就是实例对象 small_dog 和 small_cat 的原型对象。原型对象上添加一个 attr 属性,结果,实例对象都共享了该属性。
当我们修改 Animal.prototype.attr = 'modify-animal' 会发现实例对象的值也被修改了,可以得出
- 原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在所有实例对象上(如下示例的
attr属性) - 可以理解为当实例对象本身没有某个属性或方法的时候,它会到原型对象去寻找该属性或方法(如下示例的
attr属性) - 如果实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法(如下示例的
age属性)
function Animal(name, age) {
this.name = name;
this.age = age;
}
Animal.prototype.attr = 'animal';
Animal.prototype.age = '18';
const small_dog = new Animal('dog', '8');
const small_cat = new Animal('cat');
console.log(small_dog.attr);
console.log(small_cat.attr);
Animal.prototype.attr = 'modify-animal';
console.log(small_dog.attr);
console.log(small_cat.attr);
console.log('small_dog.age:', small_dog.age);
console.log('small_cat.age:', small_cat.age);

综上所述:原型对象的作用就是定义所有实例对象共享的属性和方法。实例对象可以视作从原型对象衍生出来的子对象。
原型链
JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型
- 所有对象的原型最终都可以上溯到
Object.prototype(Object构造函数的prototype属性) - 所有对象都有
valueOf和toString方法的原因就是因为是从Object.prototype继承的。 Object.prototype的原型是null,因此原型链的尽头就是null。
Object.getPrototypeOf(Object.prototype); // null
原型链的作用
- 在读取对象的某个属性时,
JavaScript引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。 如果直到最顶层的Object.prototype还是找不到,则返回undefined。 - 如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性
一级级向上,在整个原型链上寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。

还没有留言,来发表第一条吧~