JavaScript学习笔记(三十六) Klass

Klass

许多JavaScript类库模仿(emulate)类,介绍一种新的语法糖(sugar syntax)。实现不同但通常有一些共同点,包括下面这些:
  • 被确定为类的构造方法的方法有一个命名的规范,比如initialize,_init或者其它类似的并且会被自动调用
  • 类从其它类继承
  • 在子类中可以访问到父类(parent class or superclass)
注意:在这个章节中,我们自由使用“类”这个词,因为主题是模仿一个类。

在没有深入太多细节之前,让我们看一个在JavaScript中模仿类的实现的例子。首先,从客户端程序猿角度,这个解决方案如何被使用?
var Man = klass(null, {
    __construct: function (what) {
        console.log("Man's constructor");
       this.name = what;
    },
    getName: function () {
        return this.name;
    }
});
这个语法糖以一个叫klass()的函数的形式出现。在一些实现中,你可以看到它为Klass()构造函数或者作为Object.prototype的扩展,但在这个例子中,我们让它作为一个简单函数。
这个函数接收两个参数:要被继承的父类和对面字面量提供的新类的实现。受PHP影响,让我们建立一个命名规范,类的构造方法必须命名为__construct
在前面这段代码中,一个叫做Man的新类被创建且没有继承任何东西(意味着在背后从Object继承)。Man类有一个在__construct中创建的自身属性name和一个getName()方法。
类是一个构造函数,那么下面的代码仍然可以工作(并且看起来就像类的实例化):
var first = new Man('Adam'); // logs "Man's constructor"
first.getName(); // "Adam"
现在让我们扩展(extend)这个类并创建一个SuperMan类:
var SuperMan = klass(Man, {
        __construct: function (what) {
            console.log("SuperMan's constructor");
        },
        getName: function () {
            var name = SuperMan.uber.getName.call(this);
            return "I am " + name;
        }
});
这里,klass()的第一个参数是要被继承Man类。注意在getName()中父类的getName()通过SuperMan静态属性uper先被调用。让我们测试一下:
var clark = new SuperMan('Clark Kent');
clark.getName(); // "I am Clark Kent"
控制台的第一行log是"Man’s constructor"然后是"Superman’s constructor"。在一些语言中,父类的构造方法在每次子类构造函数被调用时都会自动被调用,那么为什么不连这个也模仿了呢?

测试instanceof操作符返回预期的结果:
clark instanceof Man; // true
clark instanceof SuperMan; // true
最后让我们看一下如何实现klass()函数:
var klass = function(Parent, props) {
    var Child, F, i;
    // 1.
    // new constructor
    Child = function() {
        if (Child.uber && Child.uber.hasOwnProperty("__construct")) {
            Child.uber.__construct.apply(this, arguments);
        }
        if (Child.prototype.hasOwnProperty("__construct")) {
            Child.prototype.__construct.apply(this, arguments);
        }
    };
    // 2.
    // inherit
    Parent = Parent || Object;
    F = function() {};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.uber = Parent.prototype;
    Child.prototype.constructor = Child;
    // 3.
    // add implementation methods
    for (i in props) {
        if (props.hasOwnProperty(i)) {
            Child.prototype[i] = props[i];
        }
    }
    // return the "class"
    return Child;
};
klass()实现有三个有趣和独特的地方:
1.一个Child构造函数被创建。这个函数将会在最后被返回并且会被做一个类使用。在这个函数中, __construct方法如果存在则会被调用。当然,在这之前父类的__construct会先被调用(如果存在的话)通过静态的uper属性。可能存在uber不存在的可能——比如你从Object继承,和Man类定义一样。
2.第二部分处理继承,简单使用在前面讨论过的经典继承的圣杯模式。只有一件新东西:如果用来继承Parent没有被传递,设置Parent为Object。
3.最后一部分是遍历所有实现的方法(比如__construct和getName这个例子中),由类实际定义的并将它们添加到Child的prototype

什么时候使用这种模式?实际上,如果你避免这种用法更好,因为它带来了类的概念的整体混乱,类实际在语言中不存在。
它增加了新的语法和新的规则去学习和记忆。这意味着,如果你或者你的团结和类轻松相处并且同时对prototype感到不自在,那么这可能是可以探索的事情。
这种模式允许你完全忘记prototype,并且你可以将语法和规范稍作调整的和你最喜欢的语言类似






猜你喜欢

转载自blog.csdn.net/qq838419230/article/details/9570027