[转载]淘宝Kissy框架分析【二】 - BlueDream - 博客园

[转载]淘宝Kissy框架分析【二】 – BlueDream – 博客园.

首先,让我们从kissy核心文件夹开始. 第一个文件kissy.js也是主架构文件.

源码如下:

复制代码
/**
* @module j1616
* @author liangchaoyjs@163.com
*/
(
function(win, J, undefined) {
if (win[J] === undefined) win[J] = {};
= win[J];

var doc = win.document,

mix = function(r, s, ov, wl) {
if (!|| !r) return r;
if (ov === undefined) ov = true;
var i, p, l;

if (wl && (l = wl.length)) {
for (i = 0; i < l; i++) {
= wl[i];
if (p in s) {
if (ov || !(p in r)) {
r[p] 
= s[p];
}
}
}
else {
for (p in s) {
if (ov || !(p in r)) {
r[p] 
= s[p];
}
}
}
return r;

},

isReady = false,

readyList = [],

readyBound = false;

mix(J, {
version: @VERSION@,

_init: function() {
this.Env = { mods: {} };
},

add: function(name, fn) {
var self = this;

self.Env.mods[name] = {
name: name,
fn: fn
}

fn(self);
return self;
},

ready: function(fn) {
if (!readyBound) this._bindReady();

if (isReady) {
fn.call(win, 
this);
else {
readyList.push(fn);
}

return this;
},

_bindReady: function() {
var self = this,
doScroll 
= doc.documentElement.doScroll,
eventType 
= doScroll ? onreadystatechange : DOMContentLoaded,
COMPLETE 
= complete;

readyBound = true;

if (doc.readyState === COMPLETE) {
return self._fireReady();
}

//w3c mode
            if (doc.addEventListener) {
function domReady() {
doc.removeEventListener(eventType, domReady, 
false);
self._fireReady();
}
doc.addEventListener(eventType, domReady, 
false);
}
else {
// IE mode
                if (win != win.top) { // iframe
                    function stateChange() {
if (doc.readyState === COMPLETE) {
doc.detachEvent(eventType, stateChange);
self._fireReady();
}
}
doc.attachEvent(eventType, stateChange);
}
else {
function readyScroll() {
try {
doScroll(
left);
self._fireReady();
catch(e) {
setTimeout(readyScroll, 
1);
}
}
readyScroll();
}

win.attachEvent(onloadfunction() {
self._fireReady()
});
}
},

_fireReady: function() {
if (isReady) return;

isReady = true;
if (readyList) {
var fn, i = 0;
while (fn = readyList[i++]) {
fn.call(win, 
this);
}

readyList = null;
}
},

mix: mix,

merge: function() {
var o = {}, i, l = arguments.length;
for (i = 0; i < l; ++i) {
mix(o, arguments[i]);
}
return o;
},

augment: function(r, s, ov, wl) {
mix(r.prototype, s.prototype 
|| s, ov, wl);
return r;
},

extend: function(r, s, px, sx) {
if (!|| !r) return r;

var OP = Object.prototype,
= function(o) {
function F() {}
F.prototype 
= o;
return new F();
},
sp 
= s.prototype,
rp 
= O(sp);

r.prototype = rp;
rp.constructor 
= r;
r.superclass 
= sp;

if (s !== Object && sp.constructor === OP.constructor) {
sp.constructor 
= s;
}

if (px) {
mix(rp, px);
}

if (sx) {
mix(r, sx);
}

return r;
},

namespace: function() {
var l = arguments.length, o = null, i, j, p;

for (i = 0; i < l; ++i) {
= ( + arguments[i]).split(.);
[
appShop]
= this;
for (j = (win[p[0]] === o) ? 1 : 0; j < p.length; ++j) {
= o[p[j]] = o[p[j]] || {};
}
}
return o;
},

app: function(name, sx) {
var O = win[name] || {};

mix(O, thistrue, [_initaddnamespace]);

O._init();

return mix((win[name] = O), typeof sx === function ? sx() : sx);
},

log: function(msg, cat, src) {
if (this.Config.Debug) {
if (src) {
msg 
= src +  + msg;
}
if (win[console!== undefined && console.log) {
console[cat 
&& console[cat] ? cat : log](msg);
}
}
return this;
},

error: function(msg) {
if (this.Config.Debug) {
throw msg;
}
}

});

J._init();

J.Config = { debug: @DEBUG@ };

})(window, J1616);

复制代码

 

首先整个函数通过简单的闭包机制实现了沙箱.然后将win[J]暴露给全局.所以我们就可以J1616.xx引用属性了.

1. mix函数

作用:将s的属性拷贝给r. ov(默认为true)为true则属性覆盖,为false则不覆盖. wl如果定义了.那么只有当s中含有wl定义的属性才会进行属性拷贝.

测试用例:

复制代码
var r = {versionbeta0.0.1};
var s = {
version: 
beta0.0.2,
_init: 
function() {
alert(
init);
},
_login: 
function() {
alert(
login);
},
_logout: 
function() {
alert(
logout);
}
};

var J = J1616;
//////////////////////////////
//
J.mix(r, s);
//
alert(r.version);  // beta0.0.2
//
r._logout();       // logout
//
///////////////////////////
//
J.mix(r, s, false); 
//
alert(r.version);  // beta 0.0.1
//
///////////////////////////
//
J.mix(r, s, true, [‘_init’, ‘_login’]);
//
r._init();   // init
//
r._login();  // login
//
r._logout(); // error
//
///////////////////////////

复制代码

紧接着便用mix(J, {‘_init’:xx}); 实现了将属性赋予J上 其实就 等价于var J = {‘_init’:xxx};

2._init和add函数 

作用:

当j1616.js一加载的时候便会执行_init函数. 这个函数给J维护了一个Env对象. 目前没太大作用.但应该是为了以后扩展接口方便.

add函数. 是注册一个模块. 并将J1616实例传入到回调函数中.

测试用例:

复制代码
(function() {
var J = J1616;
J.add(
module-namefunction(J) {
alert(J.version); 
// 可以获取实例
        J.DOM = {
info: 
function() {
alert(
注册了一个DOM模块);
}
};
});
// 使用定义的module
    J.DOM.info();
})();
复制代码

3. ready函数

作用: 替代onload函数. onload函数需要图片等资源完全加载完毕才调用. 而ready在document构建完毕就开始执行.要更快一些.

原理:

两个标志: (1) isReady记录document是不是已经ready完毕.  (2)readyBound记录是不是已经绑定了ready函数.

一个数组: readyList用来存储排队的需ready执行的函数.

整个流程就是:

如果没有绑定ready 就开始绑定domReady函数. 如果已经绑定并且isReady为true 即文档已经ready完毕.那么就直接执行. 否则就放入readyList列表中存储. 一旦ready完毕 就循环列表,依次执行存储在数组中的函数.

监听domReady的方法:

w3c 使用DOMContentLoaded IE下如果是在iframe中使用onreadystatechange, 非iframe中使用doScroll(‘left’)去检测. 最后使用onload函数兜底.防止onreadystatechange和doScroll晚于onload执行. 由于fireReady中有if (isReady) return;所以保证了函数只会运行一次.

注意: Kissy源码有些问题,在IE下.如果没有图片等资源.页面很快就加载完毕. load函数要早于ready函数…. 所以这里我将ready函数给修正了.

复制代码
_bindReady: function() {
var self = this, DOMContentLoaded = null;
doScroll 
= doc.documentElement.doScroll,
eventType 
= doScroll ? onreadystatechange : DOMContentLoaded,
COMPLETE 
= complete;

readyBound = true;

if (doc.readyState === COMPLETE) {
return self._fireReady();
}

//w3c model
    if (doc.addEventListener) {
DOMContentedLoaded 
= function() {
doc.removeEventListener(eventType, DOMContentedLoaded, 
false);
self._fireReady();
}
doc.addEventListener(eventType, DOMContentedLoaded, 
false);
}
// IE model
    else {
DOMContentedLoaded 
= function() {
if (doc.readyState === COMPLETE) {
doc.detachEvent(eventType, DOMContentedLoaded);
self._fireReady();
}
}
// 主要是这句 onreadystatechange会确保在onload之前执行.而kissy只用在iframe分支里
        // 所以会出现onload要早于domready执行
        doc.attachEvent(eventType, DOMContentedLoaded);
win.attachEvent(
onloadfunction() {
self._fireReady()
});

var toplevel = false;

try {
toplevel 
= win.frameElement == null;
catch(e) {}

if (doScroll && toplevel) {
function readyScroll() {
try {
doScroll(
left);
catch(e) {
setTimeout(readyScroll, 
1);
return;
}
self._fireReady();
}
readyScroll();
}
}
},

_fireReady: function() {
if (isReady) return;

isReady = true;
if (readyList) {
var fn, i = 0;
while (fn = readyList[i++]) {
fn.call(win, 
this);
}

readyList = null;
}
},

复制代码

测试用例:

复制代码
<body>
<img id=inline-image src=http://veimages.gsfc.nasa.gov/2429/globe_west_2048.jpg“/>
<script>
(
function() {
var J = J1616;
J.ready(
function(J) {
alert(
domReady:  + J.version);
});
window.onload 
= function() {
alert(
onload:  + J.version);
}
})();
</script>
</body>
复制代码

4.merge函数 

作用: 将多个对象合并为1个对象.进行的是浅拷贝. 一般用于设置默认参数时候使用:

测试用例:

复制代码
(function() {
J1616.ready(
function(J) {
var source = {
version: 
beta0.0.1,
name: 
kissy
};

var property = {
version: 
beta0.0.2
};

var prototype = {
name: 
j1616
}

var config = J.merge(source, property, prototype);

alert([config.version, config.name]); // version: ‘beta0.0.2’ name: ‘j1616’
    });
})();

复制代码

5.augment函数

作用: 将一个函数的原型和另一个函数的原型或者一个对象合并. 然后也有ov和wl属性, 相当于改进版的mix函数.

测试用例:

复制代码
(function() {
function Base() {};
Base.prototype 
= {
name: 
kissy,
show: 
function() {
alert(
this.name);
}
};

var Base2 = {
name: 
j1616,
};

function Child() {};
Child.prototype 
= {
version: 
beta0.0.1,
show: 
function() {
alert(
this.version);
}
};
// 1.用Base的prototype覆盖Child的prototype. 覆盖的函数列表为show
    // 2.用Base2的属性去覆盖Child的prototype.
    //J1616.augment(Child, Base, true, [‘show’]);
    //J1616.augment(Child, Base2);
    //new Child().show(); // j1616

// 1.将Base的prototype和Child的prototype.属性合并但不覆盖
    // 2.用Base2的属性去覆盖Child的prototype.
    //J1616.augment(Child, Base, false);
    //J1616.augment(Child, Base2);
    //new Child().show(); // beta0.0.1
})();

复制代码

 

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏