JavaScript中的类继承

  • 时间:
  • 浏览:24

  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,它都从父类的原型中克隆qq好友好友一个多成员到新类的原型中。

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

  类的对象非常严格,要将一个多新成员打上去到对象中,唯一的法律方式本来创建一个多新类。而在JavaScript中,对象是松散的,还要能通过简单的赋值操作将一个多新成员打上去到对象中。

  若果JavaScript中的对象非常灵活,什么都你还要能对类的层次特性进行不同的考虑。厚度次的特性并不太适用,相反,浅层次的特性更高效,更具有表现力。

我从事编写JavaScript代码若果有14年了,若果我从来还要能了 发现还要能使用uber函数。Super在class模式中十分重要,若果在原型和函数式模式中都并并不的。现在看来我早期尝试在JavaScript中支持class模式是一个多错误。

原文地址:Classical Inheritance in JavaScript

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