原型继承

一、在编程中,我们经常会想获取并扩展一些东西。

【示例】例如,我们有一个user对象及其属性和方法,并希望将admin和guest作为基于user稍加修改的变体。我们想重用user中的内容,而不是复制/重新实现它的方法,而只是在其之上构建一个新的对象。
可使用原型继承(Prototypal inheritance)。

1、原型继承(Prototypal inheritance)这个语言特性能够帮助我们实现这一需求。
二、所有的JavaScript对象至少继承于一个对象。
1、被继承的对象被称作原型,并且继承的属性可以通过构造函数的prototype对象找到。
三、原型继承是js中最常用的一种继承方式。

特点

一、原型继承的特点:它是把父类中私有+公有的都继承到了子类原型上(子类公有的)。

核心

一、核心:原型继承并不是把父类中的属性和方法克隆一份一模一样的给B,而是让B和A之间增加了原型链的链接,以后B的实例n想要用A中的getX方法,需要一级一级的向上查找来使用。

new

【见】new运算符

构造函数

构造函数创建对象,添加本地属性和属性值

一、我们可以像Java一样,为构造器提供参数以初始化实例的属性值。
1、下图展示了一种实现方式
image.png
二、对象在JavaScript中的定义

  1. function Employee (name, dept) {
  2. this.name = name || "";
  3. this.dept = dept || "general";
  4. }
  5. function WorkerBee (projs) {
  6. this.projects = projs || [];
  7. }
  8. WorkerBee.prototype = new Employee;
  9. function Engineer (mach) {
  10. this.dept = "engineering";
  11. this.machine = mach || "";
  12. }
  13. Engineer.prototype = new WorkerBee;

1、定义过程使用了一种设置默认值的特殊惯用法

  1. this.name = name || ''

(1)JavaScript的逻辑或操作符(||)会对第一个参数进行判断。
①如果该参数值运算后结果为真,则操作符返回该值。
②否则,操作符返回第二个参数的值。
2、使用这些定义,当创建对象的实例时,可以为本地定义的属性指定值。
(1)可以使用以下语句创建一个新的Engineer

  1. var jane = new Engineer('balau')

(2)Jane的属性:

  1. jane.name == "";
  2. jane.dept == "engineering";
  3. jane.projects == [];
  4. jane.machine == "belau"

3、由上面对类的定义,无法为诸如name这样的继承属性指定初始值。如果想在JavaScript中为继承的属性指定初始值,需要在构造函数中添加更多的代码。

直接调用原型链上的更高层次对象

一、还可以通过调用原型链上的更高层次对象的构造函数,让构造器添加更多的属性。
image.png
二、Engineer构造函数的新定义

  1. function Engineer (name, projs, mach) {
  2. this.base = WorkerBee
  3. this.base(name, 'engineering', projs)
  4. this.machine = mach || ''
  5. }

三、如果创建了一个新的Engineer对象

  1. var jane = new Engineer('Doe, Jane', ['navigator', 'javascript'], 'belau')

1、JavaScript会按以下步骤执行
(1)new操作符创建了一个新的对象,并将其proto属性设置为Engeer.prototype
(2)new 操作符将该新对象作为 this 的值传递给 Engineer 构造函数。
(3)构造函数为该新对象创建了一个名为 base 的新属性,并指向 WorkerBee 的构造函数。
① 这使得 WorkerBee 构造函数成为 Engineer 对象的一个方法。
(4)构造函数调用 base 方法,将传递给该构造函数的参数中的两个,作为参数传递给 base 方法,同时还传递一个字符串参数 “engineering”。
① 显式地在构造函数中使用”engineering” 表明所有 Engineer 对象继承的 dept 属性具有相同的值,且该值重载了继承自 Employee 的值。
(5)因为 base 是 Engineer 的一个方法,在调用 base 时,JavaScript 将在步骤 1 中创建的对象绑定给 this 关键字。这样,WorkerBee 函数接着将 “Doe, Jane” 和 “engineering” 参数传递给 Employee 构造函数。当从 Employee 构造函数返回时,WorkerBee 函数用剩下的参数设置 projects 属性。
(6)当从 base 方法返回后,Engineer 构造函数将对象的 machine 属性初始化为 “belau”。
(7)当从构造函数返回时,JavaScript 将新对象赋值给 jane 变量。
四、你可以认为,在Engineer的构造器中调用了WorkerBee的构造器,也就为Engineer对象设置好了继承关系。1、事实并非如此。
五、调用WorkerBee构造器确保了Engineer对象以所有在构造器中所指定的属性被调用。但是,如果后续在Employee或者WorkerBee原型中添加了属性,那些属性不会被Engineer对象继承。
【示例1】假设如下语句

  1. function Engineer (name, projs, mach) {
  2. this.base = WorkerBee;
  3. this.base(name, "engineering", projs);
  4. this.machine = mach || "";
  5. }
  6. var jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
  7. Employee.prototype.specialty = "none";

1、对象jane不会继承specialty属性。您必须显式地设置原型才能确保动态的继承。
2、如果修改成如下语句

  1. function Engineer (name, projs, mach) {
  2. this.base = WorkerBee;
  3. this.base(name, "engineering", projs);
  4. this.machine = mach || "";
  5. }
  6. Engineer.prototype = new WorkerBee;
  7. var jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
  8. Employee.prototype.specialty = "none";

(1)现在jane对象的specialty属性为’none’了