效果见这里:http://bangswork.googlecode.com/svn/trunk/lab/effect/index.html
jquery的动画总体思路是:
有一个fx类专门处理动画,fx的各个实例共享一个timers数组和一个setInterval。对每个传进来的dom的每个属性值都新建一个fx实例去处理,一个fx实例对应一个dom的一个属性的变化。fx里有个step函数,可以计算出当前这个时刻这个属性要达到什么值。
这个step会通过共用的一个setInterval每13毫秒执行一次,这就可以使得它行成动画。另外如果浏览器速度太慢无法达到13毫秒执行一次step,动画也会按时完成,因为是根据当前系统时间计算属性要达到的值的。
每一个fx实例的step都会放进timers数组,实际上setInterval是持续执行timers里的每一个函数,这样只用一个setInterval就让众多属性“一起动”了。step里判断到超过了动画运行的时间,就会返回false让它从timers里移除,timers为空时clearInterval。
下面简单实现这整个过程:
(全文…)
1.把对象变成类数组
var a = { name : "jquery" },
b = ["elem1", "elem2", "elem3"];
Array.prototype.push.apply(a, b);
alert(a) //[Object object]
//可以像遍历数组一样遍历这个对象
for ( var i = 0; i < a.length; i ++ ) {
alert(a[i])
}
jquery就是用这种方法把jquery对象变成类数组的,效率还很高。
2.去掉一层嵌套数组
var a = [[1,2], 3, [4,5,6]]; var b = Array.prototype.concat.apply([], a); console.info(b) //1,2,3,4,5,6
concat方法:
arrayObject.concat(arrayX,arrayX,……,arrayX)
concat() 方法用于连接两个或多个数组。arrayX为n个数组or元素参数
console.info([1].concat([2,3], 4)) //1,2,3,4
apply方法:
fn.apply(obj, args)
在obj的作用域下调用函数fn,并把数组args里的第i个元素作为第i个参数传给fn
例如
fn.apply(window, [1,2,3]) 等于 fn(1,2,3) (直接调用一个函数作用域就是window)
合起来就有上面那个效果了。
3.复制数组
利用Array.splice(0)可以快速复制一个数组
var a = [1,2,3], b = a.splice(0); alert(b)//1,2,3 alert(a==b)//false
4.高效合并字符串
var a = ["<div>","<span>","</span>","</div>"];
alert(a.join(”")) //”<div><span></span></div>”
通过这种方式合并字符串效率比 + 号高了近七八倍,因为js的机制导致每次给字符串追加内容都是抛弃原有字符串新建另一个追加后的字符串,所以效率会低。
在firefox下测试:
var a = []
for (var i = 0; i < 100000; i++) {
a.push("<div>");
}
console.time("arr");
a.join("");
console.timeEnd("arr");console.time("str");
var b = "";
for (var i = 0; i < 100000; i++) {
b += "<div>";
}
console.timeEnd("str");
//arr:19ms
//str:141ms
仿照jquery的API写了专用于iphone的jquery,索性叫iquery。虽然API很多一样,但里面的实现大多不一样,sizzle引擎改成了现代浏览器自带的queryselector,其他API很多实现上是简化了,很多并不常用的方法和功能没加上,增加了iphone特有的touch等事件,详细看文档。因为内部实现是自己写的,又没有经过很多测试,所以还不能像jquery那样随心所欲地用不怕出错,还拿不出手,只能自己用,打算边用边测试,先放上源码和文档。
文档同时也算是iquery的使用演示。建议在iphone/itouch下查看,同时还可以挺好地支持电脑上的chrome和safari,其他浏览器不行~
P.S 目前文档还有很多方法没有加上详细说明
源码:http://code.google.com/p/bangswork/source/browse/trunk/iquery/iquery.js
目前压缩前17.8K,压缩后9.4K
练习下自定义事件的实现,想让JS可以用AS3的语法使用自定义事件,实现很简单,因为没考虑复杂的情况吧。试过后发现有些地方还是模仿不了,最主要不一样的地方是:
1.侦听函数没法自动绑定那个函数所在的object,必须手动把object传进去,像这样:dispatcher.addEventListener(SampleDispatcher.ACTION, e.action, e);
最后还要传“e”这个obj进去,否则函数action里的this不指向e
2.使用的时候继承十分麻烦,还得小心翼翼,看demo就知道了。
YUI2
在YUI2.x里,每一个模块功能的引入都会直接添加在全局的YAHOO里,例如dom.js里:
var Y = YAHOO.util;
Y.Dom = {...}
这样在整个页面范围内,YAHOO.util里就多了一个功能Dom。
YUI3-add()
在YUI3里,每一个模块引入时并没有把功能直接添加到全局的YUI里,看看YUI3里的dom.js:
YUI.add('dom-base', function(Y) {
//在这里添加dom方法
Y.DOM = {
...
}
...
}, '3.0.0' );
再看看yui.js里add的源码:
add: function(name, fn, version, details) {
YUI.Env.mods[name] = {
name: name,
fn: fn,
version: version,
details: details || {}
};
return this; // chain support
}
所以YUI3里引入每个模块时只是把这个模块的内容储存在YUI.Evn.mods里。
YUI3-use()
需要使用某个模块时,在创建YUI实例以后,用use取出来执行模块里的程序,为这个YUI实例添加相应的方法:
YUI().use('dom-base', function(Y) {
//这里可以用到模块dom-base里对YUI添加的方法Y.DOM
alert(Y.DOM) //[object object]
})
而在同一个页面里,YUI实例里如果没有指明use(’dom-base’),就没有Y.DOM这个方法
YUI().use('', function(Y) {
alert(Y.DOM) //undefined
})
沙箱
这里YUI().use(”,function(Y){…})就是一个安全沙箱,可以确保这里面的Y是纯天然无污染的,Y实例里有什么功能完全取决于use里传进的模块名称,function(Y){}里面的程序跟外界是隔离的,在里面创建的变量(除了全局变量)以及对YUI的添加修改都不会影响到同个页面上其他人写的程序。
但是这个纯天然无污染是有点代价的,就是每次都要新建一个YUI实例,消耗内存,但如果不怕Y被污染,可以不每次都创建实例:
var Y = YUI();
Y.use('dom-base', function(Y) {
//可以同时使用dom-base和oop模块里添加的方法
});
Y.use('oop', function(Y) {
//可以同时使用dom-base和oop模块里添加的方法
});
add()和use()配合一些参数(例如require)和YUI Loader就成了YUI3模块化编程的基础。
最简化的YUI沙箱
AI版(AI很笨):http://bangswork.googlecode.com/svn/trunk/army/Army-AI/index.html
前言
小时候玩的棋类游戏中,军旗算是玩得比较多的,接下来就是跳棋了。不知有多少年没碰过军旗,上次偶然在姐家下了一盘,挺怀念的,网上查了一下,四国军棋是挺多,但我们的玩法是不断翻开盖住的棋子的,据说这叫翻翻棋,边锋游戏有,下载后发现不咋地,应该是十几年前做的了,我想自己做一个网页版的。奇怪怎么这么久还没有大型的网页版的棋牌游戏,总比QQ游戏方便很多吧。
前期
前期得先把棋盘画好,定好棋子在上面的走动规则。话说这棋盘画得我自己挺满意的,呵,高仿真啊,我是把棋盘拍照下来照着画的,不过缺点是高度太大了,对于小电脑一个屏幕可能还看不到整个棋盘。棋子就不咋地,很一般,不过想换肤很容易。
程序的实现方面,这次我还是用了jquery,本来想用YUI练习下的,但发现YUI本来就不适合用来做这样的游戏,只适合用于分模块构建中大型网站,另外它没有在dom里保存data的功能,所以还是用回jquery。
(全文…)
做军旗游戏时想用web workers消除计算时界面的停滞,实现了后发现在firefox下正常,但在chrome/safari下却不能用,网上关于web workers的资料太少了,也可能是因为我没找到,结果就一直debug了半天,才找出原因所在,原因就是:chrome/safari下,web worker传递的参数全都会被转换成字符串。
chrome/safari下,只能用字符串通信,无法传递其他数据,无论是数组,object,布尔值,数值,全都会被转成字符串,而在firefox下则支持所有数据类型的传递。
看这段示例代码,以下测试在firefox3.5 safari4.0.5 chrome4.1下进行:
HTML:
<!DOCTYPE HTML PUBLIC "-//W3C/DTD HTML4.0 Transitional//EN">
<html>
<head>
<title>Test threads fibonacci</title>
</head>
<body>
<script type="text/javascript">
var worker = new Worker("testWorker.js");
worker.onmessage = function (event) {
alert("type:" + typeof event.data);
alert(event.data[1]);
};
worker.postMessage([2,3]);
</script>
</body>
</html>
testWorker.js:
onmessage = function (event) {
postMessage(event.data.name);
};
safari/chrome显示:type:string “,”(字符串”2,3″第二个字符)
firefox显示:type:object 3(数组第二个元素)
可以把worker.postMessage([2,3]);的参数改成其他类型试试,safari/chrome显示出来的type都是string。
写出来好简单,发现这个却花了我挺多时间,firefox对google创造出来的东西的支持比chrome还好,真是奇怪~
(全文…)
问题
YUI2.8的自定义事件如果添加了两个相同的侦听函数,在移除这些侦听函数时会有错误:
var testEvent = new YAHOO.util.CustomEvent("testEvent");
testEvent.subscribe(eventHandler);
testEvent.subscribe(eventHandler);
testEvent.unsubscribe(eventHandler);
function eventHandler(){
alert("testEvent fire");
}
testEvent.fire();
//只会显示一个"testEvent fire"
上面看似没问题,注册了两个相同的侦听函数,执行一次unsubscribe移除了一个,于是只剩一个侦听函数,但事实不是这样:
var testEvent = new YAHOO.util.CustomEvent("testEvent");
testEvent.subscribe(eventHandler);
testEvent.subscribe(eventHandler);
testEvent.subscribe(eventHandler);
testEvent.subscribe(eventHandler);
testEvent.unsubscribe(eventHandler);
function eventHandler(){
alert("testEvent fire");
}
testEvent.fire();
//只会显示两个"testEvent fire"
注册四个相同的侦听函数,执行了一次unsubscribe,却只剩下两个侦听函数有效。
刚接触jquery时很好奇,一个jQuery对象,那么多方法可以用,要是每个对象都保存着这些方法,那得占用多大的内存空间啊。很显然,那些方法都是共用的,只保存一份。今天闲着,研究了下。
要知道哪些方法是每个实例各有一份,哪些方法是共享的,还是先来研究下js的基础:js内部是怎样调用一个实例的属性/方法的。看这段代码:
var Test = function () {
this.a = "a";
this.b = "b";
this.c = function(){
return "c";
}
}
Test.prototype = {
b: "b_prototype",
d: function(){ return "d"; },
e: "e"
}
var t1 = new Test();
var t2 = new Test();
var t1 = new Test(),这一句,在 Firefox 下,不考虑参数传递,可以用下面的代码来表示 new 的过程:
var o = {__proto__: Test.prototype};
Test.apply(o);
t1 = o;
//摘自http://lifesinger.org/blog/2009/06/yui3-oop/
经过Test.apply(o);这一句,o = {__proto__: Test.prototype, a: “a”, b: “b”, c: function(){ return “c” } }
调用t1(即o)实例的方法/属性时,会先在o查找是否存在,如果存在,即返回,不存在,则沿着原型链__proto__往上查找,实例对象t1的__proto__指向原型Test.prototype,而Test.prototype.__proto__指向父类的原型,一层层往上搜索,这里Test继承原始天尊Object,所以Test.prototype.__proto__ === Object.prototype
alert(t1.a) //a 在对象中找到a = “a”
alert(t1.b) //b 在对象中找到b = “b”,不再继续查找,忽略对象原型里的b = “b_prototype”
alert(t1.e) //e 在对象中找不到d,沿原型链找到Test.prototype有属性e = “e”
从new语句的分解可以看到,在构造函数里用this.xx = xx生成的方法/属性是每个实例对象都会复制一份的,而prototype里的方法/属性则所有对象共享一份。
alert(t1.c === t2.c) //false t1 t2两个对象的方法c是不同的拷贝,分别保存在t1 t2里
alert(t1.d === t2.d) //true 两个方法d都指向Test.prototype.d
需要注意的是,如果你给t1.d赋了新的值或函数,并不等于修改了Test.prototype.d,这点曾经迷惑了我:
t1.d = “new d”;
alert(t1d === t2.d) //false t2仍然指向Test.prototype.d,而t1在自己对象里新增了属性d,调用t1.d时不会再沿原型链向上查找
回过头看看jQuery,很明显,所有jQuery实例用到的方法都保存在prototype里,只保存一份,jQuery实例对象本身只包含了length selector context三个属性,以及搜索到的DOM。例:
<div id="tid1" class="tclass"></div>
<div id="tid2" class="tclass"></div>
//在jquery源码里jQuery.fn == jQuery.fn.init.prototype == jQuery.prototype
$(".tclass") == {__proto__:jQuery.fn.init.prototype,
selector: ".tclass",
context: document,
length: 2,
0:/*指向DOM元素<div id="tid1" class="tclass"></div>*/,
1:/*指向DOM元素<div id="tid2" class="tclass"></div>*/
}





