我从来没有使用过 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 种类型的数据成员:private
、public
、shared
和static
。我们已经看到了私有和公共数据成员。
共享数据成员是定义在类上的那些属性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.area
area
volume
您可能会在以下小提琴中看到上述程序的输出。
因此您可能会看到,这个类模式比 John Resig 的“简单 JavaScript 继承”模式强大得多,Class
构造函数只有 47 行代码(没有被缩小)。