上一篇中我们提到了创建一个类的方法 ,在这篇中我们来学习一下面向对象编程的三大特征之一:继承。
当一个类被扩展后,子类就拥有了父类的所有静态属性和实例属性,如果你重写一个方法时,想调用父类同名的方法的话,可以用this._super,如下例:
上一篇中我们定义了一个基本的类:
$.Class('Monster',
/* @static */
{
count : 0
},
/* @prototype */
{
init : function(name) {
// saves name on the monster instance
this.name = name;
// sets the health
this.health = 10;
// increments count
this.constructor.count++;
},
eat : function(smallChildren) {
this.health += smallChildren;
return this.health;
},
fight : function() {
this.health -= 2;
return this.health;
}
});
下面我们对这个类进行扩展:
Monster("SeaMonster",{
eat: function( smallChildren ) {
return this._super(smallChildren / 2);
},
fight: function() {
this.health -= 1;
return this.health;
}
});
var lockNess = new SeaMonster('Lock Ness');
out(lockNess.eat(4)); //health = 12
out(lockNess.fight()); //health = 11
注意this._super的使用,它调用了父类的同名方法 。
静态属性继承:
$.Class("First", {
staticMethod : function() {
return 1;
}
}, {})
First("Second", {
staticMethod : function() {
return this._super() + 1;
}
}, {})
out(Second.staticMethod()) // -> 2
$.Class("MyNamespace.MyClass", {}, {
init : function(name) {
this.name = name;
},
speak : function() {
console.log("My name is " + this.name);
}
});
var cls = new MyNamespace.MyClass("Jack");
cls.speak();
out(MyNamespace.MyClass.shortName);
out(MyNamespace.MyClass.fullName);
命名空间是一个很好的方式,它可以避免相同的类名冲突,只要放在不同的命名空间下面。
命名空间是区分不同功能性代码很好的方式,大家可以参考一下Ruby onRails's [http://api.rubyonrails.org/classes/ActiveRecord/Base.html|ActiveRecord]。
遗憾的是,js并没有提供一个很好的方式来决定一个对象的名字,所以必须开发者来提供。
我们可以用如下命名方式来命名我们的类:
$.Class("MyOrg.MyClass",{},{});
MyOrg.MyClass.shortName //-> 'MyClass'
MyOrg.MyClass.fullName //-> 'MyOrg.MyClass'
shortName(不带命名空间)和fullName(带命名空间)作为类的静态属性使用。
setup和init函数
Class提供了静态(setup)和实例化(init)初始化函数, setup会在init执行前调用,并且被用于‘正常化’ init的参数。
典型情况下,你不需要使用setup方法,使用init方法即可,除非你在调用init初始化前要做复杂的预处理。
$.Class("MyClass", {
setup : function() {
console.log("static setup");
},
init : function() {
console.log("static constructor");
}
}, {
setup : function() {
console.log("prototype setup");
},
init : function() {
console.log("prototype constructor");
}
});
上面的示例执行结果是:
static setup
static constructor
prototype constructor
Setup函数
setup函数在init函数之前被调用。
静态setup函数会传递基类的扩展函数跟随的参数。
实例setup函数会传递类的构造函数参数。
如果一个setup函数返回一个数组,这个数组会被做为init方法的参数。这使用setup函数有能力把正常化参数传递给init函数。这使用setup方法能按你的方式完美的运行。
下面举一下简单的例子:[jQuery.Controller.prototype.setup]
$.Class("jQuery.Controller",{
...
},{
setup: function( el, options ) {
...
return [$(el),
$.extend(true,
this.Class.defaults,
options || {} ) ]
}
})
典型情况下,你不需要生成和重写setup函数。
init函数
init函数在setup函数之后调用。
典型情况下,init函数和setup函数会接受一样的参数。
如下例:
$.Class("Task", {
setup : function() {
var arr = $.map($.makeArray(arguments),function(n){
return n + 2;
});
console.log(arr);
},
init : function() {
var arr = $.map($.makeArray(arguments),function(n){
return n * 2;
});
console.log(arr);
}
});
new Task(2, 3, 4);
结果为:
[4,5,6]
[4,6,8]
可以看出argument传的参数是一样的。
Proxy方法
和jquery的proxy方法一样,Class也提供了一个proxy方法[jQuery.Class.static.proxy proxy]用于返回一个方法的回调,通过它可以使得this总是指向类本身或类的实例。
如下例:
$.Class("Todo", {
init : function(name) {
this.name = name;
},
get : function() {
$.get("/jmvc/stuff/jmvc.js ", this.proxy('show'));
},
show : function(txt) {
out(this.name + " " + txt);
}
});
new Todo("Trash").get();
proxy方法可以作用于静态方法和实例方法 。
Class的构造函数
$.Class( [NAME , STATIC,] PROTOTYPE )
从这个定义类的函数可以看出,name,staic是可选的。
NAME:string -- 如果提供,它会被用于设置shortName和fullName,并在window对象上增加必要的命名空间。
STATIC:object -- 如果提供,将在类上创建静态属性和方法 。
PROTOTYPE:object -- 用于创建类的实例方法。
当一个类被创建时,静态的setup方法和init方法会被调用。
创建一个类的实例,可以使用如下方式:
new Class([args ... ]) -> instance
这个类的实例会拥有PROTOTYPE对象上的所有属性和方法。
当一个类的实例被创建时,实例的setup方法和init方法会被调用。