0

我正在使用John Resig 的“简单 JavaScript 继承”来创建一个可以继承的类。我也在使用 KnockoutJS 来计算 observables。问题在于试图将这两个概念结合起来。当我尝试在计算的 observable 中获取对 self 的引用时,我得到的是“Window”对象而不是预期的实际对象。这是一个快速的代码示例:

window.mynamespace.myclass = Class.extend({
    init: function() {

    },
    someProperty: ko.observable(10),
    someComputedProperty: ko.computed(function() {
       return this.someProperty();  
    }, this)
});

不幸的是 this.someProperty() 无法找到,因为“this”是对 Window 的引用。有什么想法或想法吗?

4

2 回答 2

2

我从来没有使用过 KnockoutJS 的经验。然而,我精通 JavaScript 中的继承,我不赞成 John Resig 的“简单 JavaScript 继承”模式(主要是因为它没有私有变量)。相反,我更喜欢使用我自己的类模式。我想你可能会觉得很有趣:

window.mynamespace.myclass = new Class(function (uber) {
    var self = this;

    function constructor() {
    }

    this.someProperty = ko.observable(10);

    this.someComputedProperty = ko.computed(function () {
        return self.someProperty();
    });

    return constructor;
});

您也可以使用您的方法并简单地跳过创建self. 由于该类不是从您可以this在类定义中使用的对象创建的(在 John Resig 的“简单 JavaScript 继承”模式中您不能这样做):

window.mynamespace.myclass = new Class(function (uber) {
    function constructor() {
    }

    this.someProperty = ko.observable(10);

    this.someComputedProperty = ko.computed(function () {
        return self.someProperty();
    }, this);

    return constructor;
});

您也可以直接将方法添加到window.mynamespace.myclass.prototype. 这样self.someProperty()你就可以返回而不是返回myclass.prototype.someProperty()。如果您需要任何关于我的课堂模式的帮助,请随时问我。

编辑:

构造Class函数有两个参数:定义类的函数和派生自的可选 基类。第一个参数(即function必须返回另一个函数,它是类的构造函数(类似于 C++ 或 Java 构造函数)。

让我们创建一个简单的Rectangle类:

var Rectangle = new Class(function () {
    // width and height are private variables
    var width;
    var height;

    // the class constructor accepts two arguments
    function constructor(length, breadth) {
        // save the parameters
        width = length;
        height = breadth;
    }

    // area is a public function
    this.area = function () {
        // return the area of the rectangle
        return width * height;
    };

    return constructor; // always remember to return the constructor
});

现在您可以创建该类Rectangle的实例,如此小提琴中所示。

现在让我们做一些更有趣的事情——继承。构造函数的第二个参数Class是派生自的基类。让我们创建一个Square派生自 class 的类Rectangle

// notice that the class definition function accepts a parameter called uber
var Square = new Class(function (uber) {
    // return the constructor of the class Square
    return function (side) {
        // call the base class constructor - uber
        uber(side, side);
    };
}, Rectangle); // Square derives from Rectangle

这就是事情变得有趣的地方。好的,所以我们在这个类模式中有 4 种类型的数据成员:privatepublicsharedstatic。我们已经看到了私有和公共数据成员。

共享数据成员是定义在类上的那些属性prototype。它们由类的所有实例共享。

静态数据成员是在类本身上定义的那些属性。它们不被类的实例继承。

在上面的示例中,当Square派生自它时,Rectangle它继承了Rectangle. 事实上,我们也可以在定义Rectangle Square定义一个新的共享或静态数据成员,它仍然会被Square. 让我们用一个例子来演示这个概念:

alert(Square.staticDataMember); // staticDataMember is undefined
Rectangle.staticDataMember = "It works!"; // define staticDataMember on Rectangle
alert(Square.staticDataMember); // staticDataMember is inherited from Rectangle

以上是针对静态数据成员的。让我们看看共享数据成员的情况:

var square = new Square(5); // create an instance of Square
alert(square.sharedDataMember); // sharedDataMember is undefined
Rectangle.prototype.sharedDataMember = 0; // define sharedDataMember on Rectangle
alert(square.sharedDataMember); // sharedDataMember is inherited from Rectangle

您可能会在以下小提琴中看到上述程序的输出。

这很酷,但是私有和公共数据成员呢?它们是如何遗传的?好吧,当我们创建一个新类时,私有和公共数据成员不会被继承。它们在创建类的新实例时被继承。这是因为同一类的不同实例具有不同的私有和公共数据成员。

当我们创建派生类的实例(如Square)时,我们不会继承它的基类(即Rectangle)的私有和公共数据成员,直到我们调用基类构造函数(即uber)。只有当我们调用基类构造函数时,私有和公共数据成员才会被继承(实际上只有公共数据成员被继承——其他的被称为私有是有原因的):

var Square = new Class(function (uber) {
    return function (side) {
        alert(this.area); // area is undefined
        uber(side, side); // inherit public members from an instance of Rectangle
        alert(this.area); // area is now defined
    };
}, Rectangle);

您可能会在以下小提琴中看到上述程序的输出。

现在让我们为 kicks 创建另一个类,在此过程中,我们还将演示多级继承:

var Cube = new Class(function (uber) {
    var side; // the length of a side of the cube

    function constructor() {
        side = arguments[0]; // save the first argument passed to the constructor
        uber = uber(side); // call the base constructor and save its instance
    }

    this.area = function () {
        return 6 * uber.area(); // return the surface area of the cube
    };

    this.volume = function () {
        return side * uber.area(); // return the volume of the cube
    };

    return constructor; // remember to return the constructor
}, Square); // derive from Square

这是新事物。在这里,我们创建了一个新area函数,它隐藏了中area定义的函数Rectangle,但是如果我们想从派生类访问基类方法怎么办?

好吧,当我们调用基类构造函数(即uber)时,它会返回基类的实例。由于我们不再需要基类构造函数,我们将实例保存为uber. 然后我们可以使用和函数中看到的那样调用基类area方法。uber.areaareavolume

您可能会在以下小提琴中看到上述程序的输出。

因此您可能会看到,这个类模式比 John Resig 的“简单 JavaScript 继承”模式强大得多,Class构造函数只有 47 行代码(没有被缩小)。

于 2012-08-20T16:34:59.863 回答
1

您可以随时将它们添加到init. 在淘汰赛自己的示例中,它们在构造函数中进行绑定。

window.mynamespace.myclass = Class.extend({
    init: function() {
        this.someProperty = ko.observable(10);
        this.someComputedProperty = ko.computed(function() {
           return this.someProperty();  
        }, this);  
    }
});

或者捕获对绑定的引用this并忘记绑定:

window.mynamespace.myclass = Class.extend({
    init: function() {
        var self = this;
        self.someProperty = ko.observable(10);
        self.someComputedProperty = ko.computed(function() {
           return self.someProperty();  
        });  
    }
});

编辑:

为了演示如何扩展类:

window.mynamespace.myotherclass = window.mynamespace.myclass.extend({
    init: function() {
      // do anything myotherclass init, like another observable
      this.someOtherProperty = ko.observable(10);

      // incidentally you *can* have private variables with this pattern
      var imPrivate = 1;
      this.getImPrivate = function() {
        return imPrivate;
      };

      // then call super (maybe with arguments, maybe just passing them)
      this._super('foobar');
    }
});
于 2012-08-20T16:26:16.937 回答