使用KnockoutJS和简单类继承时丢失对self的引用

时间:2012-08-20 16:13:04

标签: javascript inheritance knockout.js

我使用John Resig's "Simple JavaScript Inheritance"创建一个可以继承的类。我也在使用KnockoutJS来计算可观察量。问题在于尝试将这两个概念结合起来。当我尝试在计算的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的引用。有什么想法或想法吗?

2 个答案:

答案 0 :(得分:2)

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

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构造函数有两个参数:函数定义类可选 基类派生自。第一个参数(即函数必须返回另一个函数,该函数是类的构造函数(类似于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的实例,如此fiddle中所示。

现在让我们做一些更有趣的事情 - 继承。 Class构造函数的第二个参数是派生自的基类。让我们创建一个类Square,它派生自类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

您可以在以下fiddle中看到上述程序的输出。

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

当我们创建派生类的实例(如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);

您可以在以下fiddle中看到上述程序的输出。

现在让我们为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.area。然后,我们可以使用area调用基类volume方法,如Class和{{1}}函数中所示。

您可以在以下shadowed中看到上述程序的输出。

因此,你可能会发现这个类模式比John Resig的“简单JavaScript继承”模式更强大,{{1}}构造函数只有47行代码(没有缩小)。

答案 1 :(得分:1)

您可以随时在init中添加它们。在knockout's own examples中,他们在构造函数中进行绑定。

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');
    }
});