前面介绍了prototype.js
和Mootools.js
是如何实现类,及其类的属性和作用的。今天介绍的klass.js
就是单纯的实现面向对象的库,只有90多行,也照例分析吧。
实现类的步骤
- 第一步是使用
klass
新建类,初始化的固定函数是initialize
,不能使用其它名称 - 子类使用
ParentClassName.extend{// 子类方法}
继承 - 子类中与父类的同名方法,如果需要在父类的同名方法上拓展,需要在子类的方法体中使用
this.supr(args)
- 如果需要在类的外面增加方法,可以使用
methods
方法
// 使用 klass 创建类
var Person = klass({
// 初始函数固定为 initialize,
initialize:function(name) {
this.name = name;
this.friends = ['jack', 'mark'];
},
getName: function(){
console.log("My name is " + this.name);
},
setFriends:function(friend){
this.friends.push(friend);
},
getFriends:function(){
console.log(this.friends)
}
});
// 使用 methods 给类添加方法,子类可以继承该方法
Person.methods({
getAge:function(age){
console.log("My age is " + age);
}
})
// 子类直接通过 ParentClassName.extend 来继承父类
var Chinese = Person.extend({
initialize:function(name, addr){
// 使用 this.supr(args)来使用父类的同名函数
this.supr(name);
this.addr = addr;
},
getAddr:function(){
console.log("My address is " + this.addr);
}
});
// 子类直接通过 ParentClassName.extend 来继承父类
var Japanese = Person.extend({
initialize:function(name){
this.supr(name);
}
})
// 实例化类
var men = new Chinese('allen', 'BeiJing');
men.getName(); // My name is allen
men.getAge(23); // My age is 23
men.getAddr(); // My address is BeiJing
// 以下验证 - 子类继承父类的属性,修改了之后,其他子类再次继承父类,父类的属性的值为何不会改变
var allen = new Person();
allen.getFriends(); // ["jack", "mark"]
var women = new Japanese();
women.setFriends("lisa");
women.getFriends(); // ["jack", "mark", "lisa"]
var men = new Chinese();
men.setFriends('peter');
men.getFriends(); //["jack", "mark", "peter"]
var wallen = new Person();
wallen.getFriends(); //["jack", "mark"]
JS是如何实现类的方法,有几个重要的问题需要搞清楚
- JS是如何创建类的
- 子类是如何实现继承父类属性和方法的
- 子类继承父类的属性,修改了之后,其他子类再次继承父类,父类的属性的值为何不会改变
- 子类和父类的同名函数,在同名函数中使用
$supr
,是如何做到在子类中共存的
如何实现,不在类中,而是使用methods
往类中添加方法的
下面来通过klass.js
来具体分析
!function (name, context, definition) {
if (typeof define == 'function') define(definition)
else if (typeof module != 'undefined') module.exports = definition()
else context[name] = definition()
}('klass', this, function () {
var context = this
, f = 'function'
// 是否使用了 supr 来调用父类的同名函数
, fnTest = /xyz/.test(function () {xyz}) ? /\bsupr\b/ : /.*/
, proto = 'prototype'
// o 为创建类时,类的方法
function klass(o) {
return extend.call(isFn(o) ? o : function () {}, o, 1)
}
// 判断对象是否是函数
function isFn(o) {
return typeof o === f
}
// 将父类的方法/属性,附加到子类中
function wrap(k, fn, supr) {
return function () {
var tmp = this.supr
// 当前函数的 supr,就是父类的同名函数
this.supr = supr[proto][k]
var undef = {}.fabricatedUndefined
var ret = undef
try {
// this 是当前类,fn 是父类的函数,fn 的上下文是绑定在当前类中,所以就等于父类的方法就继承到子类中了
ret = fn.apply(this, arguments)
} finally {
this.supr = tmp
}
return ret
}
}
// 子类继承父类的属性,如果有同名函数,就使用 wrap 方法处理,如果没有,就完全继承该属性
// what : child; o : parent; supr: parentClass
function process(what, o, supr) {
for (var k in o) {
if (o.hasOwnProperty(k)) {
what[k] = isFn(o[k])
&& isFn(supr[proto][k])
&& fnTest.test(o[k])
? wrap(k, o[k], supr) : o[k]
}
}
}
// 实现继承的主函数, o创建类时的所有函数,fromSub 为1,不明白为什么不把fromSub设置为true/false
function extend(o, fromSub) {
// must redefine noop each time so it doesn't inherit from previous arbitrary classes
function noop() {}
noop[proto] = this[proto]
var supr = this
, prototype = new noop()
, isFunction = isFn(o)
, _constructor = isFunction ? o : this
, _methods = isFunction ? {} : o
function fn() {
// 如果当前类设置类 initialize 函数,就把传给当前类的参数传递给该函数
if (this.initialize) this.initialize.apply(this, arguments)
else {
// 如果没有设置 initialize ,传入类的参数也能被其它函数使用
fromSub || isFunction && supr.apply(this, arguments)
_constructor.apply(this, arguments)
}
}
// 使用 methods 添加方法到当前类,o 为使用 methods 内的方法
fn.methods = function (o) {
process(prototype, o, supr)
fn[proto] = prototype
return this
}
// 指定 fn 的 constructor
fn.methods.call(fn, _methods).prototype.constructor = fn
// 使用 ParentClassName.extend 来实现继承时候的
fn.extend = arguments.callee
// 使用 implement 来重写父类的方法或者拓展父类的方法
// o : 函数名 ; optFn : 函数
fn[proto].implement = fn.statics = function (o, optFn) {
o = typeof o == 'string' ? (function () {
var obj = {}
obj[o] = optFn
return obj
}()) : o
// 使用 process 把 implement 中的函数添加到当前类中
process(this, o, supr)
return this
}
return fn
}
return klass
});
JS
面向对象系列
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。