javascript继承

靶冒充

构想原始的 ECMAScript 时,根本未曾打算规划目标冒充(object
masquerading)。它是在开发者开始知道函数的干活方式,尤其是何许以函数环境遭受运用
this 关键字后才发展下。

那个规律如下:构造函数使用 this
关键字给有属性和方赋值(即利用类似声明的构造函数方式)。因为构造函数只是一个函数,所以只是要
ClassA 构造函数成为 ClassB 的点子,然后调用它。ClassB 就会见接 ClassA
的构造函数中定义的性与方式。例如,用底的章程定义 ClassA 和 ClassB:

function ClassA(sColor) {
    this.color = sColor;
    this.sayColor = function () {
        alert(this.color);
    };
}

function ClassB(sColor) {
}

尚记呢?关键字 this
引用的凡构造函数当前缔造的靶子。不过以此措施吃,this
指向的所属的目标。这个原理是将 ClassA
作为健康函数来确立继续机制,而不是当做构造函数。如下使用构造函数 ClassB
可以实现连续机制:

function ClassB(sColor) {
    this.newMethod = ClassA;
    this.newMethod(sColor);
    delete this.newMethod;
}

每当即时段代码中,为 ClassA 赋予了措施
newMethod(请牢记,函数叫就是赖为其的指针)。然后调用该措施,传递让它的凡
ClassB 构造函数的参数 sColor。最后一行代码删除了对 ClassA
的援,这样后便无能够重调用它。

享有新特性与初方式都必须在剔除了新办法的代码行后概念。否则,可能会见挂超类的有关属性和方式:

function ClassB(sColor, sName) {
    this.newMethod = ClassA;
    this.newMethod(sColor);
    delete this.newMethod;

    this.name = sName;
    this.sayName = function () {
        alert(this.name);
    };
}

为说明前面的代码有效,可以运行下面的例证:

var objA = new ClassA("blue");
var objB = new ClassB("red", "John");
objA.sayColor();    //输出 "blue"
objB.sayColor();    //输出 "red"
objB.sayName();        //输出 "John"

靶冒充可以实现多更继承

趣的凡,对象冒充可以支撑多再度继承。也就是说,一个近似可以继续多单超类。用
UML 表示的多级继承机制如下图所示:

图片 1

诸如,如果是个别单近乎 ClassX 和 ClassY,ClassZ
想继承这半只类似,可以使下的代码:

function ClassZ() {
    this.newMethod = ClassX;
    this.newMethod();
    delete this.newMethod;

    this.newMethod = ClassY;
    this.newMethod();
    delete this.newMethod;
}

这里存在一个弊端,如果在个别只类似 ClassX 和 ClassY
具有同名的特性或方式,ClassY
具有高优先级。因为它于后面的类继承。除这点小问题外,用对象冒充实现多双重继承机制好。

鉴于这种持续方法的风靡,ECMAScript 的老三本为 Function
对象加入了点儿个方式,即 call() 和 apply()。

call() 方法

call() 方法是与经典的靶子冒充艺术极其相似之点子。它的率先单参数用作 this
的靶子。其他参数都一直传送让函数自身。例如:

function sayColor(sPrefix,sSuffix) {
    alert(sPrefix + this.color + sSuffix);
};

var obj = new Object();
obj.color = "blue";

sayColor.call(obj, "The color is ", "a very nice color indeed.");

在这例子中,函数 sayColor()
在对象外定义,即使它不属另外对象,也得以引用关键字 this。对象 obj 的
color 属性等于 blue。调用 call() 方法时,第一个参数是 obj,说明该授予
sayColor() 函数惨遭之 this 关键字值是
obj。第二独跟老三独参数是字符串。它们同 sayColor() 函数着的参数 sPrefix
和 sSuffix 匹配,最后生成的信息 “The color is blue, a very nice color
indeed.” 将被出示出。

只要跟后续机制的对象冒充艺术并利用该方法,只待用眼前三履的赋值、调用和去代码替换即可:

function ClassB(sColor, sName) {
    //this.newMethod = ClassA;
    //this.newMethod(color);
    //delete this.newMethod;
    ClassA.call(this, sColor);

    this.name = sName;
    this.sayName = function () {
        alert(this.name);
    };
}

此,我们要给 ClassA 中之重点字 this 等于新创造的 ClassB 对象,因此
this 是率先个参数。第二单参数 sColor 对个别单近乎来说都是绝无仅有的参数。

apply() 方法

apply() 方法时有发生三三两两个参数,用作 this
的对象同要传送给函数的参数的数组。例如:

function sayColor(sPrefix,sSuffix) {
    alert(sPrefix + this.color + sSuffix);
};

var obj = new Object();
obj.color = "blue";

sayColor.apply(obj, new Array("The color is ", "a very nice color indeed."));

其一事例与前方的例子一样,只是现在调用的凡 apply() 方法。调用 apply()
方法时,第一只参数仍是 obj,说明该授予 sayColor() 函数惨遭之 this
关键字值是 obj。第二单参数是由于片单字符串构成的再三组,与 sayColor()
函数惨遭的参数 sPrefix 和 sSuffix 匹配,最后生成的信息据是 “The color is
blue, a very nice color indeed.”,将为显示出。

拖欠方式也用于替换前三实践之赋值、调用和去新办法的代码:

function ClassB(sColor, sName) {
    //this.newMethod = ClassA;
    //this.newMethod(color);
    //delete this.newMethod;
    ClassA.apply(this, new Array(sColor));

    this.name = sName;
    this.sayName = function () {
        alert(this.name);
    };
}

平等的,第一独参数仍是 this,第二个参数是独自发生一个值 color 的数组。可以把
ClassB 的全体 arguments 对象作为次独参数传递给 apply() 方法:

function ClassB(sColor, sName) {
    //this.newMethod = ClassA;
    //this.newMethod(color);
    //delete this.newMethod;
    ClassA.apply(this, arguments);

    this.name = sName;
    this.sayName = function () {
        alert(this.name);
    };
}

自然,只有超类中之参数顺序和子类中的参数顺序完全一致时才方可传递参数对象。如果不是,就非得创造一个独的勤组,按照科学的顺序放置参数。此外,还而采用
call() 方法。

原型链(prototype chaining)

连续这种形式以 ECMAScript
中原本是用以原型链的。上亦然回介绍了定义类的原型方式。原型链扩展了这种办法,以平等栽有趣之办法贯彻持续机制。

在上一致回学了,prototype
对象是只模板,要实例化的目标还坐这模板也底蕴。总而言之,prototype
对象的别样性质和方还为传送给好看似的具备实例。原型链利用这种功效来兑现持续机制。

比方用原型方式重定义前面例子中之好像,它们将改成下列形式:

function ClassA() {
}

ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
    alert(this.color);
};

function ClassB() {
}

ClassB.prototype = new ClassA();

原型方式的神奇之处在当让突出展示的蓝色代码行。这里,把 ClassB 的 prototype
属性设置成 ClassA 的实例。这很有趣,因为想要 ClassA
的享有属性与方式,但与此同时未思量逐个以它们 ClassB 的 prototype 属性。还有比将
ClassA 的实例赋予 prototype 属性更好之措施呢?

小心:调用 ClassA
的构造函数,没有为它们传递参数。这在原型链中是标准做法。要力保构造函数没有另外参数。

跟目标冒充相似,子类的具有属性和章程还要出现于 prototype
属性被赋值后,因为以它之前赋值的富有方还见面于删。为什么?因为
prototype 属性被轮换成了新对象,添加了初办法的老对象将吃灭绝。所以,为
ClassB 类添加 name 属性和 sayName() 方法的代码如下:

function ClassB() {
}

ClassB.prototype = new ClassA();

ClassB.prototype.name = "";
ClassB.prototype.sayName = function () {
    alert(this.name);
};

可是由此运行下面的例证测试就段代码:

var objA = new ClassA();
var objB = new ClassB();
objA.color = "blue";
objB.color = "red";
objB.name = "John";
objA.sayColor();
objB.sayColor();
objB.sayName();

另外,在原型链中,instanceof 运算符的运作方式啊不行新鲜。对 ClassB
的所有实例,instanceof 为 ClassA 和 ClassB 都回到 true。例如:

var objB = new ClassB();
alert(objB instanceof ClassA);    //输出 "true"
alert(objB instanceof ClassB);    //输出 "true"

于 ECMAScript
的弱类型世界中,这是不过有用之工具,不过以对象冒充时无能够利用她。

原型链的坏处是勿支持多还继承。记住,原型链会用另外一样型的目标还写类的
prototype 属性。

掺杂方式

这种持续方式以构造函数定义类,并非以外原型。对象冒充的显要问题是必采取构造函数方式,这不是极端好的选择。不过假如以原型链,就无法利用带来参数的构造函数了。开发者如何选也?答案非常粗略,两者都因此。

当面前一样节,我们已教了创建类的卓绝好方式是故构造函数定义属性,用原型定义方法。这种方法同样适用于继续机制,用对象冒充继承构造函数的特性,用原型链继承
prototype 对象的点子。用当下简单种植艺术重写前面的例子,代码如下:

function ClassA(sColor) {
    this.color = sColor;
}

ClassA.prototype.sayColor = function () {
    alert(this.color);
};

function ClassB(sColor, sName) {
    ClassA.call(this, sColor);
    this.name = sName;
}

ClassB.prototype = new ClassA();

ClassB.prototype.sayName = function () {
    alert(this.name);
};

以斯例子中,继承机制由简单行突出展示的蓝色代码实现。在第一推行突出展示的代码中,在
ClassB 构造函数中,用对象冒充继承 ClassA 类的 sColor
属性。在其次执行突出展示的代码中,用原型链继承 ClassA
类的方法。由于这种混合方式以了原型链,所以 instanceof
运算符仍能科学运行。

脚的事例测试了就段代码:

var objA = new ClassA("blue");
var objB = new ClassB("red", "John");
objA.sayColor();    //输出 "blue"
objB.sayColor();    //输出 "red"
objB.sayName();    //输出 "John"