『跟着老谭学 JavaScript』使用原型

原型是什么

我们创建的每个函数都有一个 prototype 属性,这个属性是一个引用,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。

为什么要使用原型

我们知道通过构造函数,我们可以方便地创建对象,但同时也存在一个问题,那就是每个方法都要在每个实例中重新创建一遍

使用原型对象的好处是可以让所有实例共享它所包含的属性和方法。

组合使用构造函数模式和原型模式

单纯地使用原型模式存在的问题:

  1. 所有实例在默认情况下都取得相同的属性值;
  2. 原型中所有属性都是被很多实例共享的,这一点对于包含引用类型值的属性来说,可能就不是我们想要的了。

利用构造函数和原型对象各自的特点,构造函数模式用于定义实例属性,原型模式用于定义方法和共享属性。这样即使所有实例共享同一个方法,减少代码的冗余,又使每个实例有自己特有的属性。

原型是动态的:任何时刻原型中添加了新的属性或方法,集成该原型的对象都能立刻使用该属性或方法。

重写原型:任何情况下,都可重写原型的属性和方法;调用对象方法时,如果在对象中找不到,将在原型中查找它。如果在对象中找到了,对象中的属性或方法会覆盖原型中的属性或方法。

建立原型链

ECMAScript 只支持实现继承,而且其实现继承主要是依靠原型链来实现的。

没错,建立原型链的目的就是为了继承。

我们先来看下构造函数、原型和实例的关系:

[图片上传失败...(image-ab70b7-1515160669449)]

  1. 每个构造函数都有一个原型对象(prototype 属性);
  2. 原型对象都包含一个指向构造函数的指针;
  3. 实例都包含一个指向原型对象的内部指针;
  4. SubType 类型的原型对象是 SuperType 类型的实例;

注意:instance.constructor 现在指向的是 SuperType,这是因为现在 instance 实例所指向的原型对象中并没有指向构造函数的指针,它是 SuperType 类型的一个实例,这个实例中又存在指向原型对象的指针,这个原型对象所指向的构造函数是 SuperType。

确定原型和实例的关系

有两种方式可确认原型和实例的关系:

alert(instance instanceof SubType);
alert(SubType.prototype.isPrototypeOf(instance));

原型链的问题

包含属性值的原型对象,其属性值会被所有实例共享,而这也正是为什么要在构造函数中,而不是在原型对象中定义属性的原因。在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了。下列代码可以用来说明这个问题:

组合继承

组合继承使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。下面来看一个例子。

组合继承避免了原型链和借用构造函数的缺陷,并结合了它们的优点,是 JavaScript 中最常用的继承模式。而且,instanceof 和 isPrototypeOf() 也能够用于识别基于组合继承创建的对象。

你可能感兴趣的:(『跟着老谭学 JavaScript』使用原型)