实现自己的js框架
两年前写的,现在发出来,也当是一个记录。
我知道贴代码有些人会反对,觉得应该用文字讲细致,但是我觉得用文字我没发用简单的文字来表达,看代码反而更直接,这个是见仁见智的。
很早之前一直用extjs,这个确实非常强大,但是在IE下一直都有一个性能的问题,搞得要让客户换chrome,firefox浏览器才行。
之所以要写自己js框架,原因是觉得自己实现也简单,维护也方便,一些管理应用的项目可以用,网站这些我还是推荐直接使用jquery,毕竟更加简单直接没有那么多可给你重复应用的地方。
可能你要问为什么不用jquery插件,一样可以。但是这些插件代码质量不能保证,风格也是奇形怪状,出了问题还要去看代码,改了之后又跟原作者的有出入,以后同步更加麻烦。
不用jquery插件,但是jquery却是个好东西,解决了浏览器兼容的大问题,所以我这个就是基于jquery来实现的,因为一直喜欢extjs的代码风格,所以如果你使用过extjs的话,使用起来会轻车熟路。
js的继承是原型继承的方式,最开始的时候我确实也用原型的方式来实现,但是后面我发现有些问题,而且理解起来不是太清晰,所以我采用一种简单清晰的方法,写起来是继承但是实际上它只是一个对象。
它同样有着抽象方法,有父类,有重载,有重写等等。
先看一下大概结构如下图:
现在说下几个重要的地方
首先就是如何定义一个类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
define:
function
(className, defaultConfig) {
if
(!wyl.isString(className) || className.isEmpty()) {
throw
new
Error(
"[wyl.define] Invalid class name '"
+ className +
"' specified, must be a non-empty string"
);
}
var
namespace_arr = className.split(
'.'
);
var
namespace = window;
var
class_Name = namespace_arr.last();
wyl.each(namespace_arr,
function
(ns) {
if
(ns == class_Name) {
return
; }
if
(!namespace[ns]) {
namespace[ns] = {};
}
namespace = namespace[ns];
});
if
(namespace[class_Name]) {
throw
new
Error(
'重复定义了'
+ className);
}
defaultConfig.className = className;
for
(
var
p
in
defaultConfig) {
if
(wyl.isFunction(defaultConfig[p])) {
defaultConfig[p].fnName = p;
}
}
namespace[class_Name] = defaultConfig;
}
|
接着就是如何创建一个类的实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
getDefineConfig:
function
(namespaces, i, context) {
if
(context ==
null
) {
return
null
; }
if
(namespaces.length == i) {
return
context;
}
var
ns = namespaces[i];
return
wyl.getDefineConfig(namespaces, i + 1, context[ns]);
}
create:
function
(className, config) {
if
(!wyl.isString(className) || className.isEmpty()) {
throw
new
Error(
"[wyl.create] Invalid class name '"
+ nsclassName +
"' specified, must be a non-empty string"
);
}
var
namespaces = className.split(
'.'
);
var
defineConfig = wyl.getDefineConfig(namespaces, 0, window);
if
(!defineConfig) {
throw
'{0}类型未定义。'
.format(className); }
config = config || {};
if
(!config.id) { config.id = wyl.newId(); }
var
instance = {};
wyl.apply(instance, config, defineConfig);
//继承的关键代码如下
if
(instance.extend) {
var
base = wyl.create(instance.extend);
for
(
var
p
in
base) {
if
(instance.hasOwnProperty(p)) {
//把父类的方法重新命名赋值到这个实例上
if
(wyl.isFunction(instance[p]) && wyl.isFunction(base[p])) {
var
baseFnName = instance.extend +
'_'
+ p;
instance[p][
'baseFnName'
] = instance.extend +
'_'
+ p;
instance[baseFnName] = base[p];
}
}
else
{
instance[p] = base[p];
}
}
}
instance.initConfig = config;
if
(
typeof
(instance.init) ==
'function'
) {
instance.init.apply(instance);
}
if
(wyl.isFunction(instance.initComponent)) {
instance.initComponent.apply(instance);
}
wyl.each(instance.abstractMethods,
function
(methodName) {
this
[methodName] =
function
() {
throw
this
.className +
' has not implement '
+ methodName; }
},
this
);
this
.intanceMgr[instance.id] = instance;
return
instance;
}
|
对于所有的类型我定义了一个基类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
wyl.define(
'wyl.object'
, {
initConfig:
null
,
abstractMethods: [],
//ext4.0中调用父类的方法就是这个
callParent:
function
() {
var
me =
this
;
//调用的时候根据之前create里面加入的隐藏信息baseFnName来找到父类的方法并调用它
var
fnname = me.callParent.caller[
'baseFnName'
];
var
fn = me[fnname];
if
(wyl.isFunction(fn)) {
fn.call(me);
}
},
addEvents:
function
() {
var
me =
this
;
if
(!wyl.eventMgr[me.id]) { wyl.eventMgr[me.id] = {}; }
var
myEventMgr = wyl.eventMgr[me.id];
wyl.each(arguments,
function
(eventName) {
if
(!myEventMgr[eventName]) { myEventMgr[eventName] = []; }
});
},
fireEvent:
function
(eventName, prams) {
var
me =
this
;
var
myEventMgr = wyl.eventMgr[me.id];
var
listeners = myEventMgr[eventName];
if
(wyl.isArray(listeners)) {
wyl.each(listeners,
function
(listener) {
var
scope = listener.scope ? listener.scope :
this
;
if
(listener.callback &&
typeof
(listener.callback) ==
'function'
) {
listener.callback.call(scope, prams);
}
});
}
},
on:
function
(eventName, options) {
var
me =
this
;
if
(!wyl.eventMgr[me.id]) { wyl.eventMgr[me.id] = {}; }
var
myEventMgr = wyl.eventMgr[me.id];
if
(!myEventMgr[eventName]) { myEventMgr[eventName] = []; }
var
removeIndex =
null
;
wyl.each(myEventMgr[eventName],
function
(item, i) {
var
b = options.callback == item.callback;
if
(b) { removeIndex = i; }
return
!b;
});
if
(removeIndex && removeIndex > -1) {
myEventMgr[eventName].removeAt(removeIndex);
}
myEventMgr[eventName].push(options);
},
destroy:
function
() {
delete
wyl.eventMgr[
this
.id];
delete
wyl.intanceMgr[
this
.id];
},
init:
function
() {
wyl.apply(
this
,
this
.initConfig);
}
});
|
界面的控件,我也定义了一个基类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
wyl.define(
'wyl.Component'
, {
extend:
'wyl.object'
,
containerSelector:
null
,
container:
null
,
abstractMethods: [
'render'
],
getContainer:
function
() {
if
(
this
.container ===
null
) {
this
.container = $(
this
.containerSelector).first();
if
(
this
.container ==
null
) {
throw
'控件必须要设置containerSelector或不存在.'
; }
}
return
this
.container;
},
setContainer:
function
(selector) {
if
(
typeof
(selector) !=
'string'
) {
throw
'设置container必须是string类型.'
; }
this
.containerSelector = selector;
},
getWidth:
function
() {
return
this
.getContainer().width() - 2;
},
getHeight:
function
() {
return
this
.getContainer().height() - 2;
}
});
|
大概的思路就是容器会负责分配给下面子控件的容器setContainer,子控件得到自己的容器getContainer,一个控件不需要关系其他控件,只需要控制好自己内部的控件元素就行了。
里面定义了一个抽象方法render,每个控件都必须实现这个方法。容器render的时候,同时如果自己内部有子控件,也render自己内部的子控件。