JavaScript中的类继承

  • 时间:
  • 浏览:0
  • 来源:大发pk10_pk10app争霸_大发pk10app争霸

  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/309/07/08/1519036.html