原型继承
一、在编程中,我们经常会想获取并扩展一些东西。
【示例】例如,我们有一个user对象及其属性和方法,并希望将admin和guest作为基于user稍加修改的变体。我们想重用user中的内容,而不是复制/重新实现它的方法,而只是在其之上构建一个新的对象。 可使用原型继承(Prototypal inheritance)。 |
---|
1、原型继承(Prototypal inheritance)这个语言特性能够帮助我们实现这一需求。
二、所有的JavaScript对象至少继承于一个对象。
1、被继承的对象被称作原型,并且继承的属性可以通过构造函数的prototype对象找到。
三、原型继承是js中最常用的一种继承方式。
特点
一、原型继承的特点:它是把父类中私有+公有的都继承到了子类原型上(子类公有的)。
核心
一、核心:原型继承并不是把父类中的属性和方法克隆一份一模一样的给B,而是让B和A之间增加了原型链的链接,以后B的实例n想要用A中的getX方法,需要一级一级的向上查找来使用。
new
构造函数
构造函数创建对象,添加本地属性和属性值
一、我们可以像Java一样,为构造器提供参数以初始化实例的属性值。
1、下图展示了一种实现方式
二、对象在JavaScript中的定义
function Employee (name, dept) {
this.name = name || "";
this.dept = dept || "general";
}
function WorkerBee (projs) {
this.projects = projs || [];
}
WorkerBee.prototype = new Employee;
function Engineer (mach) {
this.dept = "engineering";
this.machine = mach || "";
}
Engineer.prototype = new WorkerBee;
1、定义过程使用了一种设置默认值的特殊惯用法
this.name = name || ''
(1)JavaScript的逻辑或操作符(||)会对第一个参数进行判断。
①如果该参数值运算后结果为真,则操作符返回该值。
②否则,操作符返回第二个参数的值。
2、使用这些定义,当创建对象的实例时,可以为本地定义的属性指定值。
(1)可以使用以下语句创建一个新的Engineer
var jane = new Engineer('balau')
(2)Jane的属性:
jane.name == "";
jane.dept == "engineering";
jane.projects == [];
jane.machine == "belau"
3、由上面对类的定义,无法为诸如name这样的继承属性指定初始值。如果想在JavaScript中为继承的属性指定初始值,需要在构造函数中添加更多的代码。
直接调用原型链上的更高层次对象
一、还可以通过调用原型链上的更高层次对象的构造函数,让构造器添加更多的属性。
二、Engineer构造函数的新定义
function Engineer (name, projs, mach) {
this.base = WorkerBee
this.base(name, 'engineering', projs)
this.machine = mach || ''
}
三、如果创建了一个新的Engineer对象
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】假设如下语句
function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}
var jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
Employee.prototype.specialty = "none";
1、对象jane不会继承specialty属性。您必须显式地设置原型才能确保动态的继承。
2、如果修改成如下语句
function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}
Engineer.prototype = new WorkerBee;
var jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
Employee.prototype.specialty = "none";
(1)现在jane对象的specialty属性为’none’了