JavaScript中的类继承

  • 时间:
  • 浏览:0

  JavaScript是另另另俩个 无class的面向对象语言,它使用原型继承而非类继承。这会让哪些使用传统面向对象语言如C++和Java的多多tcp连接 员们感到困惑。正如你们都 所看了的,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