JS模仿AS3事件机制

2010-4-18 评论(3) 分类:技术文章 Tags:

练习下自定义事件的实现,想让JS可以用AS3的语法使用自定义事件,实现很简单,因为没考虑复杂的情况吧。试过后发现有些地方还是模仿不了,最主要不一样的地方是:

1.侦听函数没法自动绑定那个函数所在的object,必须手动把object传进去,像这样:dispatcher.addEventListener(SampleDispatcher.ACTION, e.action, e);
最后还要传“e”这个obj进去,否则函数action里的this不指向e

2.使用的时候继承十分麻烦,还得小心翼翼,看demo就知道了。

(更多…)

YUI3的沙箱机制

2010-4-10 评论(2) 分类:技术文章 Tags:

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沙箱

去除了YUI Loader以及require等参数,参考自这里
(更多…)

web workers各浏览器下参数传递的差异

2010-4-5 评论(0) 分类:技术文章 Tags:

做军旗游戏时想用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还好,真是奇怪~
(更多…)

搭建maven & cometd过程

2010-3-27 评论(8) 分类:技术文章 Tags:

近来想做个web game,需要双方持久链接,查了下资料,HTML5的websocket太遥远了得N年后才能流行,用FLASH模拟的socket又限制了没有FLASH插件的浏览器无法玩,最后研究了下comet,发现用这个实现应该靠谱,貌似WEBQQ,gmail里的gtalk,校内的站内提示和聊天都是用comet类似技术。后来找到cometd和pushlet,据说cometd更好,于是就开始捣鼓配置cometd。

配置环境这事儿,要是有有经验的人指点就好了,可以省去90%的时间,可惜没有~只能胡乱摸索。在完全不晓得maven是什么的情况下,配置这个cometd真是折腾死我了,先随便乱整费了很多时间,无果,再在eclipse下载maven插件,捣鼓半天运行不了,最后老老实实地用命令行的方式配置maven,也费了不少时间,但终于运行成功了。记下这个配置的过程,可能以后重装电脑要重新配置时有用。

目前在eclipse还是弄不好maven,运行项目时下载不到所需要的插件无法运行,我纳闷怎么在命令行就可以,在eclipse就不行,maven下载位置都是指向同一个,运行的也是同一个项目,但还是折腾不好。

配置maven

1.到http://maven.apache.org/下载maven,解压到任意一位置,以下假定maven解压地址为C:\Program Files\maven

2.配置环境变量,在变量path的值后面加上C:\Program Files\maven\bin

3.确保jdk环境配置无误:

a.检查变量path的值里有无jdk地址,例如c:\Program Files\Java\jdk1.6.0_10\bin;
b.检查变量有无变量JAVA_HOME,没有则加上,值为JDK地址

4.打开命令提示符(开始-运行-cmd),输入mvn -version检查mvn是否成功配置。

5.打开C:\Program Files\maven\conf\settings.xml,找到<pluginGroups></pluginGroups>,在里面加入<pluginGroup>org.mortbay.jetty</pluginGroup> 如果不加入这句,maven会无法找到和下载jetty,待会运行cometd时会出现这样的错误:

org.apache.maven.plugins:maven-jetty-plugin does not exist or no valid version could be found

6.maven自动下载插件等东西时默认保存在 我的文档/.m2/ 下,如果要改变这个位置,例如保存到maven目录下的down文件夹,可以在setting.xml里面加入<localRepository>/down</localRepository>。注意,目录名字不能有空格,例如c:\program files这个地址是不合法的。
(更多…)

JavaScript里关于this的疑惑

2010-3-12 评论(1) 分类:技术文章

几天前自己捣鼓东西,碰到一个问题:能不能通过一个函数得到此函数所在的对象?例如:

var obj = function() {
    this.fn = function(){
        alert(this);
    }
}

function get(fn) {
    fn();
}

var myObj = new obj();
get(myObj.fn)

目的是让myObj里的函数fn的this始终指向myObj
上面这样把myObj.fn传入到get并在里面运行,结果myObj.fn的this就改变了指向window
可以通过传入myObj再用call达到目的,想问的是,能否不传myObj就能达到目的

function get(fn, obj) {
    fn.call(obj);
}
get(myObj.fn, myObj)

经过各方指点,知道:
(更多…)

YUI2.8自定义事件的一个小bug

2010-3-7 评论(1) 分类:技术文章 Tags:

问题

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(&quot;testEvent&quot;);
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实例内存空间占用

2010-2-8 评论(2) 分类:技术文章 Tags:

刚接触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>*/
                     }

FLEX中使用AS动态创建DataGrid

2010-2-6 评论(2) 分类:技术文章 Tags:

初学FLEX时,需要在AS里动态生成DataGrid,结果搞了很久才搞明白怎么把用MXML写出来的DataGrid改成用AS写出来,其中最主要的就是自定义itemEditor、itemRender怎么写,写篇东西整理总结下。

先看看下面这段代码:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
	<mx:Script>
		<![CDATA[
			import mx.collections.ArrayCollection;
			import mx.controls.DataGrid;
			import mx.events.ListEvent;
			[Bindable]
			private var dgDataArr:Array = [{name: "Jon", job: "officer"},
									  {name: "James", job: "seller"},
									  {name: "Jodon", job: "manager"}];

			private function itemClickHandler(e:ListEvent):void
			{
				trace(e.target);
			}

		]]>
	</mx:Script>
	<mx:Component id="comboboxEditor">
		<mx:ComboBox dataProvider="{dt}" editable="true">
			<mx:Script>
				<![CDATA[
					private var dt:Array = ["officer", "seller", "manager"];
				]]>
			</mx:Script>
		</mx:ComboBox>
    </mx:Component>
	<mx:DataGrid width="100%" x="10" y="20" fontSize="14" dataProvider="{dgDataArr}" editable="true" itemClick="itemClickHandler(event)">
		<mx:columns>
			<mx:DataGridColumn dataField="name" editable="false" />
			<mx:DataGridColumn dataField="job" itemEditor="{comboboxEditor}">
			</mx:DataGridColumn>
		</mx:columns>
	</mx:DataGrid>
</mx:Application>

代码实现的是创建一个DataGrid,其中job栏可以编辑,并且编辑器是自定义的一个ComboBox。效果如下:

现在看怎么用AS代替MXML创建同样的DataGrid。
(更多…)

KISSY Editor 源码学习笔记

2009-12-9 评论(0) 分类:技术文章 Tags:

KISSY Editor是淘宝的开源编辑器,基于YUI2.x开发,但感觉YUI在里面起到的就一个工具的作用,我没学过YUI2.x,对阅读源代码似乎也没多大影响。不过貌似YUI2.x本来就是一个工具库的角色。

觉得边看代码边写下来效果会好得多,以后尽量这样做~对这个源码看得并不仔细,只是试图把主要部分框出来,第一次这样做,写出来挺乱,确实是一个“笔记”来着,零零散散。以下记录和分析可能有挺多错误,望发现者指正。

基础

KISSY Editor由多个模块组成,除核心模块外,其他各个模块相互独立(配置模块除外,如smilies~config~wangwang)。各个插件也相互独立,方便日后扩展。
模块上用到的HTML模板都在开头定义,用全大写字母命名,模板上的参数用大括号括起,使用时用replace替换参数。所有的编辑器外观样式都通过className在css里定义。

创建一个编辑器可用KISSY.Editor(textarea, config)也可用new KISSY.Editor(textarea, config),第一种当成函数调用,会返回new KISSY.Editor(textarea, config)

基础函数:

  • 添加模块:add: function(name, fn, details),把所有模块都保存在属性mods里。
  • 添加插件:addPlugin: function(name, p), 把所有插件都保存在属性plugins里,不允许同名插件覆盖。
  • 加载所有模块:_loadModules 执行所有模块的fn,并把执行过的模块加入属性_attached里

核心模块

core~config: 配置模块
lang~*: 语言模块,保存在E.lang[*],自由扩展多种语言
core~plugin: 定义插件种类
core~dom: 封装一些与编辑器dom相关的方法到E.Dom
core~color: 封装颜色值转换相关方法和正则表达式E.Color
core~command: 不明
core~range: 封装Range方法(获取选中对象、选中文本等)到E.Range

core~instance: 实例化编辑器

  • render编辑框,在此模块开头定义编辑框模板,在传入的textArea前面插入编辑框,隐藏textArea
  • 创建工具栏和菜单栏,并调用各自的init()初始化: this.toolbar = new E.Toolbar(this); this.statusbar = new E.Statusbar(this);
  • 初始化所有插件(工具栏和状态栏的插件在各自的对象里初始化),给每个插件的属性editor传入当前编辑器实例 p.editor = this;
  • 连接到编辑框的三个属性:container contentWin contentDoc

core~toolbar: 工具栏模块

  • init(): 按照config.toolbar的参数用_addItem加载插件
  • _addItem(): 初始化插件,把插件加入工具栏
  • _bindItemUI: 给插件绑定事件
  • 三个updateState相关方法让鼠标点击编辑框时插件根据情况改变状态

core~statusbar: 状态栏模块 与工具栏类似

core~menu: 下拉菜单模块

工具栏有些插件点击后有下拉菜单供选择,例如字体、颜色等,在此模块定义通用方法,需要用到的插件可以调用。

插件模块

smilies~config~default、smilies~config~wangwang: 表情插件的配置文件 每一个为一套表情图片。

plugin~*: 插件模块

  • 每个模块使用E.addPlugin添加相应的插件
  • 每个插件里:
    • 如果定义了init(),则在初始化插件时会调用。
    • 每个插件都有属性type,指定它是哪个类型的插件
    • 放在toolbar上的插件,点击时执行插件的exec() 方法
    • 插件与外部的连接:editor: 编辑器实例,即Editor.Instance

暂时到这里~

JavaScript小型选择器mini源码解析

2009-10-22 评论(6) 分类:技术文章 Tags:

在网上发现一个JavaScript小型选择器—mini,其介绍在这里已经说得挺清楚了,就不再罗嗦了。简单来说,mini选择器只支持以下选择语句:

* `tag`
* `tag > .className`
* `tag > tag`
* `#id > tag.className`
* `.className tag`
* `tag, tag, #id`
* `tag#id.className`
* `.className`
* `span > * > b`

经过调查,以上选择语句已经满足了95%以上的需求。

mini选择器实例代码如下:

var pAnchors = mini('p > a'); // Returns an array.
for (var i = 0, l = pAnchors.length; i < l; ++i) {
    // Do stuff...
}

下载源码查看,发现源码并不难,至少比jquery简单得多,就想试着分析一下它的源码,练练手,之前我是想分析jquery源码的,但发现实在太难了,超出能力范围了,还是先从简单的代码开始吧。

mini选择器大体上,就是先把选择语句最右边的元素先选出来,再根据左边的父元素层层过滤得到符合整个语句的元素。

例如”#a table .red”这个语句的选择过程,就是先选出页面上所有class=”red”的dom元素,再在选出来的元素中判断其父元素是否为table,是则保存,不是则丢弃。这层筛选完后,把结果再进行一次筛选,判断其父元素是否id=”a”,是则保留,不是则丢弃,最后就筛选出了符合”#a table .red”的所有dom元素。

其余细节的解析,我用注释的方式加在代码上了。我发现要把分析代码的过程写出来真是很难,代码是看得懂,但就是表达不出来代码的意思。我现在写出来的那些注释,似乎有点乱,估计别人也挺难看懂,不过当练兵吧,我在写之前并没有完全了解mini的原理,写完后就清晰了,看跟写还是有很大区别的,写出来对自己挺有帮助。

有些地方其实我也不是知道得很清晰,可能会有错误存在。代码里我还有一些细节不理解,有疑问的地方我打上了**号,希望高手看到能告知吧~

这里可以看到,单独选择一个id占了所有选择语句的一半以上,个人感觉mini没有对id进行单独优化,算是不足吧,并且就算只选择一个id,mini(“#id”)返回的也是一个数组,很不方便,实用性不强。

(更多…)