2007-01-15

今天,我开出了一个新的分类——“技术快餐” 。它的目的,就是在短时间内,使用一些技巧或者什么手法,来解决身边的一些“小”问题。既然是快餐,那么他就可能投机取巧,并且很有可能他就不是一个非常完美的解决方案。但是毕竟快嘛,用起来简单,方便,能够大体上得到一个(大致)精确的结果,我就满意了。

今天说一下如何判断js里面function的使用情况。

问题描述:我现在要调查一下线上服务的loadtime情况,其中能够奏效的很明显的一点就是较少js的体积。这里并不是讲究如何去压缩等等。而是一个简单的调查。
现在已经存在了大量的js lib,相信一些web的developer手上也有一些自己封装好的lib。用的时候,一个include,很是方便。但是当你在两大的lib里面,东拼西凑的完成你的功能的时候,其实也有一部分存在的代码,从来也没有执行过一次。这个时候,找到并处理他们是一个很有意义的事情。

思路:在每一个function里面,加入一个log,这样,当这个function被调用的时候,我就知道了,当覆盖了全部(或者大部分)的feature以后,js function的使用率自然一目了然。
log,不用什么高深的log4js等等,就是简单的apache access log就可以了。
在页面中动态的生成一个image对象,然后当需要记录log的时候,只要把image对象的src设置成为一个标记就可以。这样在apache的access_log当中就会有记录了。

实施:使用sed命令来给每一个需要统计的js做一下处理。
比如,你有一个方法叫做
function a () {

}
处理以后,变成
function a() {
ltimg.src=”xxx” mce_src=”xxx”;

}
这样就达到了记录的目的了。
下面附上代码:
jsfuncname.sh
sed -e ’s/function \(.*\)\(.*\){/function \1 \2 { ltimg.src=”\/images\/ltimg.jpg?n=\1″ mce_src=”\/images\/ltimg.jpg?n=\1″;/g’ $1 | sed -e ’s/\(.*\)=\(.*\)function\(.*\){/\1=\2function\3{ ltimg.src=”\/images\/ltimg.jpg?n=\1″ mce_src=”\/images\/ltimg.jpg?n=\1″;/g’ | sed -e ‘1i\
if (typeof(ltimg) == “undefined”) ltimg=new Image();

使用方法,吧这段sh帖成一个sh文件,然后执行
sh jsfuncname.sh filename.js > newfile.js
这样newfile就是新的生成好的文件了。

下面是我测试用的一段code,
var a = function () {
aaa;
}

function a() {
}

function a () {
}

function a (a,b,) {
}

function a() { //tt
}

function a () { bb;
}

function a() { bb;}

(function a() {bb;})

var f= (function() { bb;})

if (typeof (a) == “function”) {
}

if (typeof (a) == “function”)
{}

if (typeof (a) == “function”) {}
if (typeof (a) == “function” && typeof (a) == “undefined”) {bb;}
if (typeof (a) == “undefined” && typeof (a) == “function”) {bb;}

至少,在我使用的环境下,他的表现还是完美的。

2006-09-5
tangrui.cn: ok
我已经把名字改成 lcore Le
不过还没 commit
Weiming: 我说,怎么没有up到

tangrui.cn: en

lazy load 的暂且先放一放
说那个资源和代码先后的问题
Weiming: 好,这个我觉得需要去调查以下
Sent at 10:30 PM on Monday
Weiming: ?
tangrui.cn: 啊,我在等你说呀
Weiming: 你不是说了,lazyload的,放一下,我说好,先去调查
tangrui.cn: 就是我上午说,代码文件要是访问资源文件的话,那么资源文件必须在代码文件之前引用
这就要求,比如 longjs.lang.lstring 必须先定义这个类型
Weiming: 上午说的,你说要把定义,单独放在__package__虾米,这个我不同意
如果
longjs.res = {};
然后所有的res,都挂在这个虾米呢?
下面
longjs.res.lstring = {};
long.res.lstring['en'];
这样就有了
tangrui.cn: 什么意思
Weiming: 就是,现在的res都是单独的
比如lstring的,
那么就是
longjs.lstring['en']这样的
tangrui.cn: res 不能单开命名空间呀
要不然我怎么能知道他放在哪里了
Weiming: 我的意思,res,不是一个单独的命名空间
longjs.lstring['en']这样的
tangrui.cn: 再说把他们放到 longjs.res 下面也不成呀,res 岂不成一个命名空间了
Weiming: res,是一个变量
或者说,应该是
longjs.__res
这样
他只是一个resource的挂节点
从一个逻辑的角度,
tangrui.cn: 那么你的资源就没有层次结构了呀
Weiming: longjs.__res,起始就是统一的管理resource了
还是有的,
只不过,你的原来的没有一个统一的根,现在有了
tangrui.cn: 再说这个东西没有放到前面的,还是那句话,你的资源是要跟类型走的
Weiming: 什么东西没有放到前面的?
tangrui.cn: 现在是因为可能有多语言把他们分开,但其是资源和类型是应该在一起的
Weiming: 资源,起始,做成
longjs.lstring['en'];
tangrui.cn: 即便像你说的那样,也应该是 longjs.lang.lstring.__res
Weiming: ok,停以下
tangrui.cn: ok
Weiming: lstring的res,是否是lstring的一部分?
先不考虑default的
tangrui.cn: 不是
Weiming: 那么,也就是没有res,我可以用lstring
tangrui.cn: 但是需要至少配备 default 的
Weiming: 先不考虑default
tangrui.cn: 是的
Weiming: 如果default的实现和其他完全一样
那么也没必要考虑
因为都是一样的
ok,这个地方,咱们没有分歧,就是lstring是可以,并且应该单独使用的
tangrui.cn: 对
Weiming: 如果,我现在有了res
那么他也应该在lstring加载后,以一种补充的方式
把自己”挂”上去
ok?
tangrui.cn: 是的
Weiming: 那么加载的顺序就定了(先不管怎么实现)
就是先lstring,再是他的res,(如果default和其他的一样,那么同样处理)
tangrui.cn: 是的
然后
Weiming: 在现阶段
咱们已经实现了lstring对longjs的挂接
可以使用这个说法么?
tangrui.cn: 什么叫 lstring 对 longjs 的挂接
Weiming: 如果从一个扩展的角度看,(也许不合适)
就是lstring,是对longjs的一个扩展
tangrui.cn: 这个不能这么说吧
Weiming: 为什么不能?
tangrui.cn: lstring 毕竟是 longjs native 的 class
不说那么拗口的名词,继续往下
Weiming: ok
反正,就是
现有了longjs,然后又了lstring
tangrui.cn: 对
Weiming: 并且,lstring,要么有,要么没有
不能说,我有一部分
tangrui.cn: 是的
Weiming: 那么,早晨你说的吧lstring的定义
也就是longjs.lstring = xxx
放在__package__里面,我觉得是不合适的
tangrui.cn: 但是,没有办法
Weiming: 其实,现在可以说,只有在static method的地方,才会出现
我使用res,但是没有
对吧
tangrui.cn: 对
Weiming: 我想想
先说结果
就是,这样
longjs.__res.lstring
那么再说我是如何理解
我现在也不知道这样是否合适
就是
现有longjs,后有lstring
longjs。lstring,从某种意义上
认为是lstring挂在longjs上面
那么,这个时候,能否认为
lstring的res,也是在和lstring加载的时候,也是挂在longjs.__res上面的
对于longjs,这些class,他的跟是longjs
对于他们的res,根是longjs.__res
也就是longjs管理所有的class等
longjs
longjs.__res管理所有的res
可否这么认为
tangrui.cn: 那就不对了
Weiming: ok
tangrui.cn: 你要是这么理解的话,那怎么组织都说得通了
我就算把资源放到 abc.def.hjk.__res.lssssss
Weiming: 我这样说,只是从一个树,变成了两个树
tangrui.cn: 下面我也能说,是在 longjs.lang.lstring 加载的时候挂上去的
但是既然咱么做了一个有层次结构的框架,那就资源也和类型必须在他的可控范围之内
Weiming: ok
tangrui.cn: 资源与类型不是平行的关系,而是资源从属与类型
Weiming: 对
tangrui.cn: 所以从组织上也需要是具有从属的层次
ft 又遇到一个问题
这个我觉得咱么明天再说吧,一晚上估计不一定能讨论出来
Weiming: 是,这是一个圈,妈的
tangrui.cn: 现在恐怕 lcore.using 要加上一个参数表示所在的 window 了
lcore.using(target, win);
Weiming: 不好吧
为什么这么玩?
tangrui.cn: 因为原来 lcore.using 没有前面的 lcore 所以
Weiming: 所以,都是本window?
我靠
tangrui.cn: 所以在 using 里面那到 this 就是那个 window
现在,lcore.using 的 this 都是 lcore
Weiming: 那,using就没用了
tangrui.cn: 如果你强行使用 window 那么这个 window 就是 jsFrame 而不是调用方法的那个 window
除非把 using 前面的 lcore 去掉
否则就要加上这个恶心的参数
Weiming: 我宁可叫做longjs_using
也不愿意加上window
tangrui.cn: 要不咱么这些 core 的方法都这么叫
longjs_undef
longjs._createPackage();
Weiming: ._
还俩?
tangrui.cn: 多打了一个
我已经提交了
Weiming: 豪
longjs里面,多少是和jsframe相关的?
tangrui.cn: 什么意思
Weiming: 就是,longjs,提供一系列的方法
他有多少是和window,frame相关的
比如using就和window相关
tangrui.cn: 类库部分没有
Weiming: longjs.js
这里面
tangrui.cn: 有的话也是靠类似 lcore.using 这样的方法来获得的
Weiming: using,应该是都是针对调用这个方法的window吧
tangrui.cn: 现在 lcore 的 unit test 最后一个有问题之外,其他的都是可以用的了
而且不能用到 function 里面
比如你应该 using(longjs.lang);
而不能 var MyClass = function() { using(longjs.lang) }; 这样就不对了
这时候 using 的 this 就是 MyClass 了
Weiming: var MyClass = function() { using(longjs.lang) };
这肯定不行
tangrui.cn: 所保险起见是应该提供这样的一个参数,但是可以允许省略
Weiming: 那就得写成
longjs_using
tangrui.cn: 是呀,前提都是这么写
要不这么写,就一定要提供这个参数
Weiming: 除了不好看,还有什么问题
tangrui.cn: 没什么问题
以前也是这么写的,就是没有 longjs 前缀
Weiming: 还是有一个前缀好
tangrui.cn: 我觉得宁愿加一个参数也不这么写
Weiming: longjs_using比using好
我是宁可不加这个参数
tangrui.cn: 加一个参数,无论如何是保险的
Weiming: 你想,肯定很多的人
都会问你
为什么
我这个页面,调用
longjs.using
还非得加一个window,我不就是在这个window么?
既然我再这个window,那么为什么还得加上
tangrui.cn: 但是这个方法不是在这个 window 里面 run 的
Weiming: 但是会很不爽
tangrui.cn: 这样的问题有的是
比如 win$()
你必须指定一个 window 即便你在这个 window 里面
Weiming: 哪个,我明显是要操作其他的window的,
我觉得win$可以省略
tangrui.cn: 这个可以说是 js 编程的一个特例,没什么办法
Weiming: 如果省略了,就是本window
还有,如果我没有jsframe
那么我怎么用?
tangrui.cn: 不可以省略,省略了就是 jsFrame 里的东西了
Weiming: 我觉得不能接受,肯定很多的人不接受
至少我就不接受
我宁可写一个不好看的longjs_win$
tangrui.cn: 因为现在 win$ 放到了 lcore 里面,以后应该放到比如 longjs.web.dom.LElement 这样的类型下面
无论如何是不能省略 window 的
Weiming: 不好
我宁可已给一个叫做longjs_win$的alias
tangrui.cn: 而且省略了 window 只是代码上面稍微好看了一点,但是会带来严重的不安全性
给 alias 也没有
Weiming: 如果我没有jsframe之类的东西
我只有这个也没
你让我都写一个这个东西,自己的这个window,
tangrui.cn: 再说 longjs 下面只放和 core 有关的,这些和 core 是无管的
Weiming: 很丑陋啊
tangrui.cn: 没有办法,我也不想这样
否则就是那个 class
Weiming: 如果这样,那么我要是用,我肯定写一个很小的做alias的js,然后每个页面都include
tangrui.cn: javascript 就这样,否则都写成平的,就一点问题都没有了
Weiming: 我觉得,这些和window相关的,
tangrui.cn: 这是他的事情了
Weiming: 给window是应该的
tangrui.cn: 如果他这样的话为什么还要用咱么的东西
Weiming: 但是,不给,默认是当前的window,也是应该的
ok,这就是问题了
tangrui.cn: 问题是做不到
Weiming: 你出这个东西,
目的是方便大家的使用啊
当然是越方便越好了
tangrui.cn: 那也要有一定的适用条件
Weiming: 还是这个问题
如果我只有一个页面,我也得都写window
我本来只有一个window啊
tangrui.cn: 所以我说有一个适用条件
Weiming: longjs,是不应该依赖jsFrame的
也就是,我必须用这个webfx么?
tangrui.cn: 按照有 jsFrame 的模式来用是 longjs 的长处,也是区别于 dojo 的地昂
地方
Weiming: 为什么我一定要有jsFrame
tangrui.cn: 你要是 single page 的首先一点就是他为什么要用你的东西而不用 dojo 的
Weiming: 我不是single page啊
tangrui.cn: 当然你不一定有,但是我的 api 是根据有 js frame 优化的
Weiming: 我也是每个页面都去跳转啊
做软件的一个点,就是让用户用起来很方便,
把麻烦留给自己,而不是用户
tangrui.cn: 没有我可以保证运行正常,有我也会保证运行正常,且他们能保持同样的 api 接口
Weiming: 可是,这样很烦啊
况且,我只有一个window,你为什么还让我非得写这个window
tangrui.cn: 你用 frame work 的初衷是要解决你棘手的问题
Weiming: 比如什么棘手的问题?
tangrui.cn: 这点东西相比之下就不足味道了
Weiming: 我不认为
当一个开发者,用一个库不爽的时候,他会选择放弃这个库
tangrui.cn: 当然对于他来讲我能不能有一个 tree 有一个 datagrid 是更重要的
问题是这样没什么不爽呀
Weiming: 难道这样很爽么?
就好像
tangrui.cn: 何况是咱么现在讨论的这个问题是没有办法jiejuede
Weiming: 你写所有的关于net的程序
tangrui.cn: 解决的,就算不爽也办法,我也觉得不爽,但是只能这样
Weiming: 所有的方法,你都需要写一个localhost
这样你也会不爽
我并不认为只能这样
这个是可以解决的
tangrui.cn: 当然可以有一个 work around 就是再提供一个 $() 方法
Weiming: 提供$,我也不认为是work around的方法
我认为是标准的做法
tangrui.cn: 这个方法里面直接调用 window.document.getElementById()
Weiming: 如果非要加上window,那么就必须给一个不要window的方阿飞
tangrui.cn: 如果你保证是 single page 就用这个方法,否则这个方法永远返回 js frame 里面的东西
那么 using 也可这么做
Weiming: 这个方法,为什么要放在jsFrame里面?
tangrui.cn: 只有在 single page 的时候允许他不用加上 window
Weiming: 操作本window,都应该可以不加上
tangrui.cn: 所有的方法都在 js frame 里面
Weiming: 为什么?
tangrui.cn: 现在所有的方法都在 js frame 里面,上面的 frame 没有任何类库相关的内容
Weiming: 为什么?
tangrui.cn: 不为什么呀,要不然咱么做这个干什么呢
Weiming: 那么,也就是我必须有一个jsFrame咯
tangrui.cn: 不需要呀,你不用 js frame 就把他们 perpage include 就可以了
反正不用 window 的重载只会返回代码所在的那个 window 里面的东西
Weiming: 如果
tangrui.cn: 所以通常是 js frame 除非他是 single page 的
Weiming: 如果,你提供了这个必须写window的东西
然后,有人写一个很小的做alias和一些window处理的js
要求每个页面都include
那么你认为,大家会不会使用这个
如果不会,为什么
tangrui.cn: 你要是这么说得话,就没有意义了
Weiming: 如果会,能有多少人用?
为什么没意义?
tangrui.cn: 他所有的都可以这么做,为什么好要用咱么的东西
Weiming: 做为使用者,
我不但要求功能强大,我还要求使用方便
你提供功能强大
那么为什么,我不能在你提供功能强大的基础上,做一个使用方便的呢
tangrui.cn: 再说,我已经给他提供了足够好用的变通方法,如果他再不认可,那还能有什么办法
让他自己写这样的框架最终也要写成这样
Weiming: 你的变通的方法,不也得是每个页面做include么
tangrui.cn: 不用呀
Weiming: 那么,我怎么用$
又能解决有window的,又能解决没有window的
tangrui.cn: 在多页面的情况下不能用 $
Weiming: 如果我是本页面,为什么不能?
tangrui.cn: 不能就是不能,javascript 不做不到
Weiming: 不会啊
$ = function (id, win) {
win = win || window;
}
这样就可以啊
tangrui.cn: 你要是在本页面 include 代码的话,那么这个代码至少不属于框架的一部分
Weiming: 我认为是框架的一部分
你既要给强大的功能,还要给最简单和方便的使用方法
tangrui.cn: 咱么可以提供很多这样的 util 来辅助用户
但是同时他这样带来的是代码的重复
Weiming: 重复在什么地方?
tangrui.cn: 作为一个框架你不能在任何时候都往 util 里面加东西
Weiming: 这个是肯定的
tangrui.cn: 比如 java 用户自然希望往屏幕上输出一行字,用 print(abc); 就可了
Weiming: 但是前提是我可以很”方便”的使用你”强大”的框架
tangrui.cn: 但是作为一个框架,他必须要 System.out.println();
Weiming: 这点我承认
tangrui.cn: 你认为这样不可以接受吗
Weiming: 我认为不可接受
所以我的,并且很多人的
tangrui.cn: 框架就要有框架的规矩
Weiming: java,如果是做一个大的
那么肯定有一个叫做print的方法
框架有框架的规矩,是针对框架内部说的
对于用户,就应该给最方便的
就好比
tangrui.cn: 如果有一个方法可以游离于框架之外,那么就会有更多的方法游离
不肯能,否则类库就没有必要设计了
Weiming: 我不认为,框架,只能从一个地方include
一个类库,也不是说,我必须只能从一个地方include
tangrui.cn: 我觉得这个也没有必要讨论了
Weiming: 如果又方便的方法,那么肯定会有人写,并且大量的人用
结论
tangrui.cn: 最后的结果,首先在类库内部没有办法,提供这样的方法,所以只能提供 win$ 和 $
然后在页面上可以随便 include 任何的代码
Weiming: win$放在那?
$放在那?
tangrui.cn: 但是这些不能算作框架的一部分,只是为了方便
Weiming: 页面include什么?
tangrui.cn: 都在方他们该在的地方
Weiming: 你说对于解析xml,算作框架的一部分么?
tangrui.cn: 框架中的方法肯定不能任意游离
如果咱么要做的话,就算
Weiming: xerces,
这个现在已经被包含在java里面了
但是开始他并没有
从这里,就能说明,框架,不是一个单根的
tangrui.cn: 但是他加到 java 里面去以后也不可能是游离的
就像 java 永远都无法提供 print 方法一样
Weiming: 我可以接受,框架有框架的规矩
但是,我要求吧util做为框架的一部分
tangrui.cn: 除非把它做进 vm 和编译器
Weiming: 他并不是游离
tangrui.cn: 这样的话就已经不属于框架类库的一部分了
Weiming: 每名把,什么做进vm?
为什么不属于?
这么说罢,从实用主意
你会不会使用这个东西?
tangrui.cn: 框架不能有 util 的东西,还是那句话,有一个就会有更多
Weiming: 你会不会在你的页面include这个东西
关键不是有多少,而是为什么有
如果他有他存在的理由,那么多少也是应该的
tangrui.cn: 或许说得准确一点,这个不算是类库的一部分,但是可以算作框架的一部分
Weiming: 这个说法,我接受
tangrui.cn: 我还是那句话,不建议他用这套框架写 single page 的东西
Weiming: 就好像,如果真的,java给出的sdk里面,有一个,print的方法,你觉得多少人还会用system.out.print
tangrui.cn: 否则重复做一个 dojo 没有意思
Weiming: 1,我并不是觉得,这个是重复的dojo
tangrui.cn: 那么他内部肯定也是转换成 system out
Weiming: 2,这个的目的是,就算当dojo用,也要比dojo好
这个是我的目标
tangrui.cn: 所以这样的方法不能是类库的一部分
Weiming: 用户不管你是不是转换
tangrui.cn: 问题是你和 dojo 竞争是没有意义的
Weiming: 所以,我坚持他是框架的一部分
我并没有和dojo竞争
竞争也没有意义
tangrui.cn: 我不需要比 dojo 做得好,别人用我的东西,是因为 dojo 做不到
Weiming: 但是如果,非要这么做,要保证比他出色
longjs不是针对single page
但是也要兼容single page
tangrui.cn: 当然,做就要做好了,但是咱么和 dojo 是没有竞争关系的
或者说是 dojo 的超集
Weiming: 有没有竞争关系,不是咱们说了算的,要看用户
很多地方,做成single page类似的是很有必要的
比如我做一个login 的一个块
我就要求要能够随意的include到其他的页面里面
这个很正常的
tangrui.cn: 这个就没有必要了吧,直接写 html 比用任何框架都划算
Weiming: 很有必要啊
比如我同时几个产品
都需要认证
login
当然只写一个就好了
tangrui.cn: 这个先讨论到这里吧,还有一个问题
Weiming: ok,什么?
tangrui.cn: 这个属于 user control 更高级了
就是我在想这个文档怎么写,肯定没有时间写所有的文档了,所以只能写用户文档
我想用户文档就写成 java doc
Weiming: 你指的用户文档,就是api doc么?
tangrui.cn: 然后应该有工具可以转成一份文档
Weiming: http://xdoclet.sourceforge.net/xdoclet/index.html
tangrui.cn: 其实 api doc 写好了就是用户文档
Weiming: http://xdoclet.sourceforge.net/xdoclet/index.html
http://www.docbook.org/
这些都是生成doc的工具
这个好说
tangrui.cn: java 和 msdn 都是这样,把说明写全了,再写上示例代码就足够了
Weiming: 示例代码必要
tangrui.cn: 那就好,睡觉了
Weiming: 呵呵,去吧
2006-07-14

刚刚忍不住手痒,下载了一个Firefox 2.0 Beta 1(可能过几天这个链接就换了)。写了一段JS,看来果然在JS1.7当中,Iterator是非常好用的。

代码如下
================================
<html>
<head>
<script language=javascript>
// add function for JavaScript Object
Object.prototype.newfunc = function(){};
function init() {
    var a = [1,2,3,4];
    var it;
    var s;

    // old method in JavaScript 1.5
    s = “”;
    for (it in a) {
        s += it + ” ==> ” + a[it] + “<BR>”;
    }
    document.getElementById(”old_in”).innerHTML = s;

    // new one in JavaScript 1.7
    s = “”;
    try {
        it = Iterator(a, true);
        while (true) {
            var key = it.next();
            s += key + ” ==> ” + a[key] + “<BR>”;
        }
    } catch (err if err == StopIteration) {
        //alert(”End of record.\n”);
    } catch (err) {
        //alert(”Unknown error: ” + err.description + “\n”);
    }
    document.getElementById(”new_in”).innerHTML = s;
}
</script>
</head>
<body onload=”init()”>
<div id=old_in></div>
<hr>

<div id=new_in></div>
<hr>

</body>
</html>
================================

运行结果如下:
0 ==> 1
1 ==> 2
2 ==> 3
3 ==> 4
newfunc ==> function () { }


0 ==> 1
1 ==> 2
2 ==> 3
3 ==> 4

 

可见,使用Iterator是可以避免在for之中错误的使用一些未知的新添加的方法的。     

有一点遗憾的就是,Iterator的结束是很丑陋的,不知道有没有什么好的方法。还需要继续研究。 

 

 

2006-07-13

Firefox 2.0 beta 1推出了,肯定很多人已经关注这个事情了,我则比较(更加)关心的是JavaScript的增强。JavaScript升级到1.7了(FF1.5支持JavaScript1.5,IE6sp1支持不完全的JavaScript1.5)

JavaScript 1.7里面有了一些新功能的支持,比如Generator和Iterator,let语法的支持,非结构化赋值(destructuring assignment)。
令我最关心和期待的就是Iterator的支持,据说只要自定义next方法和__iterator__属性就可以根据自己的需要定义返回的值了。Iterator的出现最大的好处就是可以放心的在一些内嵌的对象当中添加方法了。

以前这样的代码是很危险的:
var o = new Object();
for (var i in o){}
因为很可能某个第三方的库文件在Object当中添加了一些方法,(比如著名的prototype),这样,在不同的第三方库混用的时候,就很可能出现问题。

现在有了自定义的Iterator,那么我觉得是完全可以避免的,只要为Object写好合适的iterator就可以了。具体的代码我还没有尝试。

还有一个问题,就是不知道IE7支持JavaScript到什么版本,而且,IE6和Firefox1.5.*都还是JavaScript 1.5 并且很多人是不会升级的。
所以,JavaScript1.7也许在未来的一段时间内,还是一个美好的(但是不能实现的)愿景吧。

2006-06-20

如我前面的文章说的,对于JavaScript,一个类,就是一个function,他的类方法(也就是static的)都是作为这个function的一部分,而实例方法,都是在prototype上面的。
function ClassA() {
}

ClassA.staticMethod = function () {
}

ClassA.prototype.instanceMethod = function () {
}

在我这个实现当中,一个类的继承是拷贝父类的所有类方法,这样子类就有了父类的静态方法。
然后让子类的prototype.prototype指向父类的prototype.
然后可以根据自己的需要,重写一些方法。
function ClassB() {
}
ClassB.staticMethod = ClassA.staticMethod;
ClassB.prototype.prototype = ClassA.prototype;
ClassB.prototype.instanceMethod = function () {
// method 2
}

对于子类,使用一个prototype的链,来实现方法的实例方法的继承。之所以选择这种实现方法,是因为子类是要重写其中的某些方法的。而prototype又是一个reference,所以直接的写作ClassB.prototype = ClassA.prototype,会在重写ClassB的实例方法的同时,破坏ClassA的实例方法。而修改后的方法则会屏蔽父类的。

寻找方法的顺序是,instanceA.prototype.method -> ClassA.prototype.
此时对于类方法的继承,已经实现,现在需要实现在子类中,调用父类的方法。
对于Java,这样的使用是很平常的
public void method() {
super.method();
}
在JavsScript中,为了实现此类功能,所以必须保留一个parent的reference,指向ParentClass.prototype.
ClassB.prototype.parent = ClassA.prototype.
那么在instanceB里面调用this.parent.method.call(this);就可以使用父类的方法了。使用call调用,是为了把自己的数据传到父类。更漂亮的解决方法,我还没有想到。

所以完成的代码是
function ClassA() {
}

ClassA.prototype.method1 = function () {
}

ClassA.staticMethod = function () {
}

function ClassB(){
}

ClassB.staticMethod = ClassA.staticMethod;

ClassB.prototype.prototype = ClassB.prototype.parent = ClassA.prototype;

这个我抽象出来一个extend方法,

var LCore = function () {
}

LCore.extend = function (destination, source) {
// copy all functons
for (var prop in source) {
if (prop == “prototype”) {
continue;
}

destination.prototype[prop] = source[prop];
}

// make a reference for parent and reference prototype
destination.prototype.prototype = destination.prototype.parent = source.prototype;

return destination;
}

2006-05-13

停了一段时间,今天说说JavaScript的面向对象
js不是一个天生的oo的语言。但是他的函数是可以担负面向对象的概念的。并且js也内置了很多的对象,比如String,Date等等。

js有一个关键字new,可以认为是实例化一个function的对象,把function作为一个class。
比如
function MyObj () {
}
var myObj = new MyObj();
这个时候,myObj就是MyObj的一个实例。

有两点应该具体的说一下
1,JavaScript是一个prototype的语言
2,this在JavaScript里面的应用

首先我们说一下prototype,prototype是一个function的属性,也可以认为是一个类的属性。他用来记录这个类有那些方法。
比如我们要给MyObj定义一个sayHello的方法,我们可以这样写
MyObj.prototype.sayHello = function () {
alert(”Hello”);
}
这个时候,我们就可以调用myObj.sayHello()执行这个方法。

而this则表示这个类本身(严格意义上说,应该是这个函数本身),比如
MyObj.prototype.sayBye = function () {
alert(”bye” + this.name);
}
那么这里的this,就是实例变量本身。myObj.sayBye()。函数里面的this就是表示myObj。那么我们就可以实现带参数的构造函数。

function MyObj2(name) {
this.name = name;
}
var myO2 = new MyObj2(”tom”);
这样,我们就可以实现一般情况下使用的面向对象了。

需 要主意的是,JavaScript没有属性的共有私有机制,我们可以通过myO2.name访问他的name属性。而且js也不是强行规定的,我们要访问 myO2.age也是可以的,但是myO2并没有age的属性(或者说重来没有给age赋值)那么得到的就是undefined。
通过这个,我们就可以用Object来实现平常使用的Map(java语言,对于python应该是字典类型)。
var m = new Object();
m["name"] = “tom”;
m["age"] = 20;
alert(m["name"]);
注1:对于alert,JavaScript调用的是这个object的toString函数,如果没有,则会显示 ie:[object] ff:[object, Object]
注2:在js当中,new Object和{}是一样的,我们可以写 var m = {};
这也是js中简单的map的实现。不好的地方是遍历不方便,因为没有地方记录有那些key
for (var i in m) 这种遍历方法,会把所有的对于object进行扩展的方法都显示出来。

对Object的扩展,是通过对Object的prototype添加方法实现的。
比如js的String方法是没有trim的,我们可以自己做一个实现然后通过扩展String来提供这个方法
String.prototype.trim = function () {}
var a = ” 123 “.trim();

上面提到了一下this,之所以说一下this,是因为他并不完全等同于C++或者Java里面的this变量。
this在js中表示紧贴着调用地点的,非prototype扩展的方法。
比如上面提到的
MyObj.prototype.sayBye = function () {
alert(”Bye” + this.name);
}
这个里面的this,紧贴的非prototype的函数是MyObj(再次注意,js中class是通过函数实现的),所以this.name就是实例变量。
但是在这种情况
MyObj.prototype.doSomething = function () {
todo(function () {
alert(this.name);
});
}
这个时候,this表示的是这个匿名函数
function () {
alert(this.name)
}
那么这里就会出现错误,如果想要这样使用,应该使用辅助变量。
MyObj.prototype.doSomething = function () {
var me = this; //把自己的reference赋值给变量me
todo (function () {
alert(me.name); //通过me来访问myObj实例
});
}
而且所有的实例变量必须通过this来进行访问,直接写变量名称会认为是全局的变量。
上例中的alert(this.name)是正确的,而alert(name)是错误的。

还有最后说一下静态方法。我们可以用js来模拟静态方法(或者说是类方法)
就是直接把方法写在函数上
MyObj.staticMethod = function () {
}
这种方法都是实例无关的,调用的时候,通过MyObj.staticMethod()来调用。其实这也是一个变通的方法。

下一次我再说说类的继承。
大家有什么想要听的,告诉我,如果我会,那么我一定分享出来。

今天说一下函数参数。

函数参数,可以理解为函数的参数,还有就是函数作为参数(还是挺诡意)。
首先说一下函数的参数。
这个问题看上去没有什么可说的。因为作为拍代码的,都肯定知道这个东西。
function method1(a, b) {
return a + b;
}
很简单,a,b就是函数method1的参数。一般情况下,这样足够了。
但是在JavaScript当中,函数的调用,其中的参数个数是不强制的。(作为脚本语言,当然类型也没有强制)。
这里很象某些语言的不定参数列表(由于我没有过多的研究,所以不便多说)。
比如上面的方法
var c = method1(1,2); //正确
var d = method(1); //也正确
此时c的值是3,d的值是NaN。
那么我们如何来保证参数的个数呢?
JavaScript当中,每一个方法里面,有一个内欠的变量,arguments。这是一个数组变量,记录着传入这个方法一共有几个参数。
很象java里面main函数的 args数组
static public void main(String args[]) {…}
我们可以使用arguments.length来判断一共有几个参数
function method2() {
if (arguments.length == 2) {
return arguments[0] + arguments[1];
} else {
return 0;
}
}
这样我们就可以保证参数的个数了。

接下来,我们说一下函数作为参数。
在昨天的例子当中,我使用了很多这样的例子。比如
ServerHandler.getName(myID, fucntion (name) {
alert(”Hello:” + name);
});
这里面,getName方法的第二个参数就是一个方法,
function (name) {…}
在JavaScript 当中,任何的东西都是Object,一个函数的类型是Function,他是Object的一个子类(姑且这么说,JavaScript不是一个纯粹的面 向对象的语言,所以说子类有一些牵强)。所有的Object都可以作为参数被传入函数。那么一个函数作为参数就不足为奇了。

暂停一下,我们现来看一下这个例子。
function method3() {
alert(”Hello”);
}

var m = method1;
var v = method1();
上面的两个变量,m,v,他们的区别就是,v是method1运算的结果,在这里是undefined,因为method3在alert以后什么都没有返回。
而m,这句付值语句,是把method3的函数体,付值给了m(有兴趣的话,可以使用alert(m)来看一下结果)。如果现在写下如下的语句
m();
那么就会调用这个函数,然后alert一个Hello。

好,我们回来,上面我想说明的就是,当一个函数作为参数传入到另外的一个函数当中,我们是可以使用一个参数变量来接受这个函数的。并且在需要的时候,我们可以继续的分发,或者调用。
比如
function method4(m) {
m();
}

method4(method3); //注意,这里面的method3没有后面的括号(),他表示是这个函数体作为参数而不是运行结果作为参数。
在进入到method4里面之后,通过参数变量m,可以调用method3。

利 用函数作为参数,在JavaScript中使用的最普遍的就是callback方法了。当需要执行一个函数,传入一个callback function用来处理结果。这种方法我们在很多地方已经使用的非常多了。现在随着AJAX的流行,callback方法也越来的重要。如我昨天所说, 很多的和server通讯的方法都是无法预料何时能够结束的。那么传入一个callback function就很方便了。

这里,我一直有这样的一个难点,始终没有很好的解决办法。
就是一般情况,我们给出callback function,都是在无法控制返回时间的情况下采用的,那么此时,大多数的情况就是这个方法会使用另外的线程来执行,而程序会继续向下走。
使用返回值的函数调用,是会在这个函数调用的时候,线程阻塞,直到返回结果。
我现在遇到的情况就是,很多的时候,我无法返回值(比如AJAX),而使用callback function也挺麻烦。所以一直没有好的方法。
虽然我昨天说的信号量的方法可以变通的解决这个问题。但是在复杂的情况下(比如我现在正在开发的一套Web UI Framework)里面也是挺麻烦的。希望大家如果有好的方法可以告诉我。

最后,还要提及的一点就是,在函数调用的时候,还有一种方法就是call(或者apply)调用。
function method5(msg) {
alert(msg);
}
method5.call(x, “Hello”);
这个x就是某个Object的变量(从面向对象的角度可以说是一个实例(instance))。很类似于python的self。
这个使用方法在一般情况下意义不大。但是在某种面向对象实现的时候是非常有用的。我会在面向对象的文章中再次说明的。

从今天开始,我会不定期的写一些关于JavaScript的东西,包括语言,应用等方面。组成JavaScript系列。
如果没有特殊的说明,这里假定JavaScript的执行环境是在浏览器(browser)当中的。
今天开始第一次,讨论一下同步和异步。

曾经查询过一些JavaScript的信息,发现google出来的结果都是询问JavaScript如何能够实现异步的代码。
而我,很不幸,查询的却是如何让JavaScript实现异步调用的同步(是不是挺起来很诡异)。

首先说一下JavaScript当中的异步方法。
其实这个问题是大家经常要碰到的。而且这个实现也很简单。我就不多说了。
给两段代码

  1. setTimeout方法,他让你的代码在指定的时间(毫秒)之后执行指定的方法。只执行一次。
    比如:
    alert(1);
    setTimeout(”alert(2)”, 1000);
    alert(3);
    代码在执行到setTimeout的时候,会继续执行下面的代码(alert(3))而不会被阻塞。等待1000ms之后执行alert(2)
  2. setInterval方法,他让你的代码每隔指定的时间,执行指定的方法,直到调用clearInterval
    比如:
    alert(1);
    timer = setInterval(”alert(2)”, 1000);
    alert(3);
    代码基本上和上面的相同,不同的是,每隔1000ms就会执行一次alert(2),直到调用
    clearInterval(timer);

我们应该注意到setTimeout和setInterval都是window的方法。
我们可以直接使用,但是规范的还是调用window.setTimeout window.setInterval,之所以提及这个,我会在以后的JavaScript系列中继续讲解。

现在该说一下我遇到的问题了。
我现在使用dwr作为AJAX的server端引擎,在调用dwr方法的时候,需要提供一个回调方法(callback function)来接受server的返回结果。
而这个回调方法是不会被阻塞的。此时browser回启动另外的现成处理。
这个很好理解,因为dwr的这个方法执行的时间是无法预料的,如果此时调用被阻塞,而server又花相当长的时间进行处理。那么浏览器就会死在这里。从用户体验的角度是根本无法接受的。
这里的例子代码是


ServerHandler.getString(”Weiming”, function (str) { //”Weiming”是传回server的参数
alert(str);
}); // ServerHandler是dwr提供的server方法的interface,具体使用请参见dwr网站。
alert(1);
在执行的过程中,会先执行alert(1),然后在一个无法预料的时间后执行alert(str)。
如果一次简单的比如hello world的调用是不会出问题的。
但是如果我要执行的一系列的dwr function是有前后顺序的,比如后面执行的需要前面的返回结果,简单的代码书写顺序是无法保证执行顺序的。
var myID = null;
ServerHandler.getID(function (id) {
myID = id; //无法预料何时会执行这句话
});

ServerHandler.getUserWithID(myID, function (name) {
/*
此时myID还没有值,因为上面的 myID = id这段代码是需要一个时间段之后才会执行的
*/
alert(”hello:” + name);
});

比如这样的代码就会出错。那么如何解决呢?
最简单的实现方法就是callback function的嵌套。

ServerHandler.getID(function (id) {
ServerHandler.getUserWithID(id, function (name) {
alert(”hello:” + name);
}
});
这样我们就可以保证多个dwr方法调用的顺序了。这样貌似解决了问题。但是并不完美。
原因是当我们把JavaScript和Browser作为一个操作的平台和逻辑业务的平台(AJAX的应用程序,后面的JavaScript系列中会有提及),而不是一个简单的展示平台的时候。这样的回调函数嵌套就很难控制了。
这也就是我最开始指出的需要同步异步调用的一个方法。

最终我在公司的解决方案是这样的。
写一个信号量的类(JavaScript的面向对象会稍后讲解),当我需要执行一个方法的时候,我就申请一部分信号量。
把需要被执行的方法放进信号量的队列进行等待。等前面等待的方法(如果存在)执行后在执行。
信号量将作为一个参数被传入执行的方法,这样这个方法可以决定释放这个信号量还是继续分发。
比如
var s = new Semaphore();
var myID = null;
s.p(function (e) {  //把方法放入信号量队列
ServerHandler.getID(function (id) {
myID = id;
s.v(); //释放信号量
}
});

s.p(function (e) { //将第二个方法放到信号量队列,只有当前面的s.v()执行之后,这个方法才会执行。
ServerHandler.getName(myID, function (name) { //此时,可以保证myID一定有值
alert(”Hello:” + name);
s.v();
})
})

这里只是对信号量这个方法进行了简单的阐述。
信号量还支持创建自信号量,如果创建了子信号量,那么父信号量必须等带所有的孩子都归还了信号量之后才可以执行他里面的代码。
由于代码的版权是公司的,所以很抱歉,现在无法给出相应的完整的信号量的实现。
如果下一端我有时间的话,我会给出一个我实现的版本的。