JavaScript中的类继承

  • 时间:
  • 浏览:3

  JavaScript是那么 无class的面向对象语言,它使用原型继承而非类继承。这会让那先 使用传统面向对象语言如C++和Java的系统多多线程 员们感到困惑。正如我们 所看了的,JavaScript的原型继承比类继承具有更强的表现力。

  但首先,要搞清楚我们 为那先 那么 关注继承?主要有那么 导致 。首先是方便类型的转换。我们 希望语言系统要能对那先 类似类的引用进行自动转换。而对于那么 要求对引用对象进行显示转换的类型系统来说只能获得很少的类型安全性。这对于强类型语言来说特别要,怎么让在像JavaScript那么 的松散型语言中,永远不只能对对象引用进行强制转换。

  第还还有一个导致 是代码的复用。代码中存在极少量拥有相同最好的措施的对象是十分常见的。类能要能通过一组定义来创建它们。另外存在什么都类似的对象也很普遍,那先 对象中只能少数有关去掉 和修改的最好的措施存在区别。类的继承能要能很有效地正确处理那先 问題报告 ,但原型继承更有效。

  为了说明你这名 点,我们 将介绍一些语法糖,它允许我们 以类似传统的class的语言来编写代码。为什么会么会你能要能们 将介绍一些有用的模式,那先 模式不适用于传统的class语言。最后,我们 将对语法糖进行解释。

  首先,我们 去掉 了那么 Parenizor类,含有set和get那么 最好的措施,分别用来设置和获取value,以及那么 toString最好的措施,用来对parens中的value进行包装。

function Parenizor(value) {
    this.setValue(value);
}

Parenizor.method('setValue', function (value) {
    this.value = value;
    return this;
});

Parenizor.method('getValue', function () {
    return this.value;
});

Parenizor.method('toString', function () {
    return '(' + this.getValue() + ')';
});

  语法看起来特别不太一样,怎么让应该很好懂。最好的措施method接受最好的措施的名称和那么 function,并将你这名 function作为公共最好的措施去掉 到类中。

  为什么会么会你能要能们 能要能那么 写:

myParenizor = new Parenizor(0);
myString = myParenizor.toString();

  正如你所期望的,myString的值为"(0)".

  现在我们 创建那么 类继承Parenizor,除了toString最好的措施中对于value为空或0的情况汇报会输出"-0-"外其余都和Parenizor相同。

function ZParenizor(value) {
    this.setValue(value);
}

ZParenizor.inherits(Parenizor);

ZParenizor.method('toString', function () {
    if (this.getValue()) {
        return this.uber('toString');
    }
    return "-0-";
});

  这里的inherits最好的措施与Java中的extends最好的措施类似,uber最好的措施也与Java中的super最好的措施类似。它允许那么 最好的措施调用父类中的最好的措施(什么都 改了名称以避开保留字的限制)。

  为什么会么会你能要能们 能要能那么 写:

myZParenizor = new ZParenizor(0);
myString = myZParenizor.toString();

  你这名 次,myString的值为"-0-".

  JavaScript那么 类,为什么会么会你能要能们 能要能通过编程来实现它。

  通过操作那么 函数的原型对象,我们 能要能实现多重继承,从而使我们 能要能用多个类的最好的措施来构建那么 类。混合多重继承肯能难以实现,并肯能存在最好的措施名称的冲突。我们 能要能在JavaScript中实现混合多重继承,怎么让在本例中我们 将使用那么 更严格的被称之为Swiss继承的形式。

  假设有那么 NumberValue类,含有那么 最好的措施setValue,该最好的措施检查value否有为某个特定范围内的数字,必要的并且 要抛出异常。我们 只只能ZParenizorsetValuesetRange最好的措施,而不只能toString最好的措施。那么 我们 能要能那么 写:

ZParenizor.swiss(NumberValue, 'setValue', 'setRange');

  那么 只会将我们 只能的最好的措施去掉 到类中。

  ZParenizor还有另外一种写法。除了从Parenizor类继承,我们 还能要能在构造函数中调用Parenizor的构造函数,并传递返回的结果。通过你这名 最好的措施,我们 给构造函数去掉 特权最好的措施,而不用再去为其去掉 公共最好的措施。

function ZParenizor2(value) {
    var that = new Parenizor(value);
    that.toString = function () {
        if (this.getValue()) {
            return this.uber('toString');
        }
        return "-0-"
    };
    return that;
}

  类的继承是is-a关系(公有继承),而寄生继承是was-a-but-now's-a关系(私有继承与公有继承)。构造函数在对象的构造中发挥了很大的作用。注意ubersuper最好的措施仍然可用于特权最好的措施。

  JavaScript的动态性允许我们 去掉 或替换现有类的最好的措施,method最好的措施能要能随时被调用,那么 类的所有实例在现在和将来后要有你这名 最好的措施。我们 能要能在任几并且 对那么 类进行扩展。继承具有追溯性,我们 把你这名 叫做类的扩充(Class Augmentation),以正确处理与Java的extends产生混淆。

  在静态面向对象语言中,肯能你你要那么 对象与那么 对象略微不同,就只能定义那么 新的类。在JavaScript中,你能要能将最好的措施去掉 到单个的对象中,而不只能在定义额外的类。你这名 非常强大,肯能你只只能写很少的类,怎么让类都能要能很简单。回想一下,JavaScript对象就像哈希表,你能要能随时去掉 新的值,肯能值是function,那么 它就成了那么 最好的措施。

  怎么让在中间的示例中,我根本不只能ZParenizor类。我不你要简单地修改我的实例。

myParenizor = new Parenizor(0);
myParenizor.toString = function () {
    if (this.getValue()) {
        return this.uber('toString');
    }
    return "-0-";
};
myString = myParenizor.toString();

  我将toString最好的措施去掉 到我的myParenizor实例中,而那么 使用任何形式的继承。我们 能要能修改单个的实例,肯能语言是无class的。

  为了使中间的示例能正常工作,我写了还还有一个sugar最好的措施。首先是method最好的措施,它将那么 实例最好的措施去掉 到类中。

Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};

  它在Function.prototype上去掉 了那么 公共最好的措施,怎么让所有的函数都通过Class Augmentation(类的扩充)获得了该最好的措施。它接受那么 名称和那么 函数,并将它们去掉 到函数的原型对象中。

  它返回this. 当我编写那么 不只能返回值的最好的措施时,我通常后要返回this,那么 就具有了那么 级联式的编程风格。

  接下来是inherits最好的措施,它用来表示那么 类从那么 类继承。应该在那么 类都被定义并且 再调用你这名 最好的措施,怎么让在继承类的最好的措施并且 去掉 该最好的措施。

Function.method('inherits', function (parent) {
    this.prototype = new parent();
    var d = {}, 
        p = this.prototype;
    this.prototype.constructor = parent; 
    this.method('uber', function uber(name) {
        if (!(name in d)) {
            d[name] = 0;
        }        
        var f, r, t = d[name], v = parent.prototype;
        if (t) {
            while (t) {
                v = v.constructor.prototype;
                t -= 1;
            }
            f = v[name];
        } else {
            f = p[name];
            if (f == this[name]) {
                f = v[name];
            }
        }
        d[name] += 1;
        r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
        d[name] -= 1;
        return r;
    });
    return this;
});

  我们 继续对Function进行扩充。我们 创建了那么 父类的实例,并将其作为新的原型。我们 还修改了构造函数的字段,并将uber最好的措施去掉 到原型中。

  Uber最好的措施在被委托人的原型中查找指定的最好的措施。这是在寄生继承或对象扩充的情况汇报下调用的函数。肯能我们 进行类的继承,那么 我们 就只能在父类的原型中找到你这名 函数。Return语录使用函数的apply最好的措施来调用function,显示地设置this并传递那么 数组参数。参数(肯能有语录)从arguments数组中获取。可惜arguments数组有的是那么 真正的数组,什么都我们 不得不再次使用apply来调用的slice最好的措施。

  最后,是swiss最好的措施。

Function.method('swiss', function (parent) {
    for (var i = 1; i < arguments.length; i += 1) {
        var name = arguments[i];
        this.prototype[name] = parent.prototype[name];
    }
    return this;
});

  Swiss最好的措施对arguments进行遍历。对每那么 name,它都从父类的原型中一键复制那么 成员到新类的原型中。

  JavaScript能要能像class语言一样来使用,但它也具有相当独特的表现力。我们 研究了类的继承,Swiss继承,寄生继承,类的扩充以及对象的扩充。你这名 极少量代码的复用模式来自于一种被认为比Java更小,更简单的语言。

  类的对象非常严格,要将那么 新成员去掉 到对象中,唯一的最好的措施什么都 创建那么 新类。而在JavaScript中,对象是松散的,能要能通过简单的赋值操作将那么 新成员去掉 到对象中。

  肯能JavaScript中的对象非常灵活,什么都你只能对类的层次型态进行不同的考虑。深度1次的型态何必 太适用,相反,浅层次的型态更高效,更具有表现力。

我从事编写JavaScript代码肯能有14年了,为什么会么会你能要能从来那么 发现只能使用uber函数。Super在class模式中十分重要,怎么让在原型和函数式模式含有的是只能的。现在看来我早期尝试在JavaScript中支持class模式是那么 错误。

原文地址:Classical Inheritance in JavaScript

相关链接:http://www.cnblogs.com/sanshi/archive/5009/07/08/1519036.html