JavaScript中的类继承

  • 时间:
  • 浏览:1
  • 来源:大发uu快3_uu快3神彩_大发uu快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中的对象非常灵活,全都你不想 对类的层次行态进行不同的考虑。淬硬层 次的行态不想太适用,相反,浅层次的行态更高效,更具有表现力。

我从事编写JavaScript代码肯能有14年了,后来我从来没哟发现不想 使用uber函数。Super在class模式中十分重要,后来在原型和函数式模式中总不想 能的。现在看来我早期尝试在JavaScript中支持class模式是八个错误。

原文地址:Classical Inheritance in JavaScript

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