当我们创建一个对象时,我们可以发现能使用很多种函数,但是我们明明没有定义过它们,对于这种情况我们会产生一种疑惑。
当我们在浏览器中打印 obj 时你会发现,在 obj 上居然还有一个 proto 属性,那么看来之前的疑问就和这个属性有关系了。
其实每个 JS 对象都有 proto 属性,这个属性指向了原型。这个属性在现在来说已经不推荐直接去使用它了,这只是浏览器在早期为了让我们访问到内部属性 [[prototype]] 来实现的一个东西。
当我们用构造函数生成实例对象,有一个缺点,那就是无法共享属性和方法。因此js决定为构造函数设置一个 prototype 属性。
这个属性包含一个对象(以下简称” prototype 对象”),所有实例对象需要共享的属性和方法,都放在这个对象里面;那些不需要共享的属性和方法,就放在构造函数里面。
实例对象一旦创建,将自动引用 prototype 对象的属性和方法。也就是说,实例对象的属性和方法,分成两种,一种是本地的,另一种是引用的。
1 | function DOG(name){ |
现在,species属性放在prototype对象里,是两个实例对象共享的。只要修改了prototype对象,就会同时影响到两个实例对象。
1 | DOG.prototype.species = '猫科'; |
JavaScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。简单回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针
1 | function Father(){ |
以上代码定义了两个类型:Father 和 Son。每个类型分别有一个属性和一个方法。它们的主要区别是 Son 继承了 Father,而继承是通过创建 Father 的实例,并将该实例赋给 Son.prototype 实现的。实现的本质是重写原型对象,代之以一个新类型的实例。换句话说,原来存在于 Father 的实例中的所有属性和方法,现在也存在于 Son.prototype 中了。在确立了继承关系之后,我们给 Son.prototype 添加了一个方法,这样就在继承了 Father 的属性和方法的基础上又添加了一个新方法。