Filed under javascript

玩了下SproutCore

照着文档做了个hello world,运行起来后发现这货完全是浏览器本地运行,压根儿没有服务器的请求。嗯,这下是标准的HTML5了吧。

一个静态的本地应用有什么用呢?有人也有这种疑惑:没有服务器段那如何和数据库打交道呢?如果还是要调用Rails,那表明这个框架只能算上半个。

所以把这东西当作一个C-S的框架比如Swing来看就容易明白了。或者更直接点:它就是为手机上的移动web应用准备的,这个应用可以是http的web应用,也可以是本地的js+css应用。

作为一个C-S框架,比web应用要更关注界面中组件之间的交互,事件绑定和数据绑定是要优先考虑的,而这也是SproutCore的强处。

如果用它通过REST直接连CouchDB之类的文档数据库,倒是一个好的组合。

另外由于HTML5已经足够强大,比如图形和拖放,所以完全可以用javascript来写些“本地”应用。你可能说Swing和Gtk之流已经很成熟了呀?但答案是:HTML5是标准。

所以我关心的一点是SproutCore的模版语言仿佛走上了java JSTL的老路子:

{{#collection SC.TemplateCollectionView contentBinding="Todos.todoListController"}}}
   {{view Todos.MarkDoneView}}
{{/collection}}

又是XML,极端难看啊。如果页面要微调,肿么半呢?

Dijit和jQuery UI Widget对比

一直以为Dojo的Widget模型虽然先进,但是过于庞大,很难说是“非侵入式”。比如widget的声明:

<button dojoType=”dijit.form.Button” id=”searchButton”>
Search
</button>

这里的dojoType就是非HTML标准的,难免让人反感。Dojo是模块化的,一个widget由template和js组成,分别为view和controller(一般json为model)。与此不同的是jQuery UI Widget采用了简单的方法:view和controller都可以嵌入到HTML主页面,view是标准的HTML,然后js会对这个element做后期处理,比如绑定事件、处理CSS和定义接口。

具体参考这里的一篇文章。这是篇信息量很大文章,值得一读。比如$(‘.target’).green3()是在target element上面创建widget,感觉是在普通的DOM对象上面附上一个定制的JavaScript对象,然后$(‘.target’).green3(‘darker’)是调用JS对象上的darker方法。

可以看到,jQuery的做法更贴切toolkit的名字:只是一个工具,不对已有模式做大变化,相比DOJO就可以称的上一个框架了。(jQuery倾向于函数式编程,而Dojo则走的是OOP的路子。)我一直顾虑Dojo的效率,考虑到复杂的依赖关系,冗长的加载时间,如果没有对其进行定制或者对其不了解,很难做到轻量级的开发模型。(定制是必须的,Dojo也在这上面花了不少功夫)

如果Dojo或Dijit能分离出一个类似Ext core的东西就好了!只保留一个紧凑的组件模型。可能已经有,而我还不知道。(有个dojo mobile的东西,好像有点参考价值。)

我想象中的一种开发场景为:页面得到json reponse,通过template将其转化为html,然后使用一种绑定机制对该HTML段落进行绑定。能否有一种方式简化这个过程?这个过程需要有一致性,简洁、明了。。。

Asynchronous Javascript

本来以为Javascript是有异步机制的,后来发现其实大多数的实现都是使用setTimeout + callback,单单使用callback是不能达到异步的,比如:

	// callback style:
	function renderLotsOfData(data, callback){
		var success = false
		try{
			for(var x in data){
				renderDataitem(data[x]);
			}
			success = true;
		}catch(e){ }
		if(callback){
			callback(success);
		}
	}
	// using callback style
	renderLotsOfData(someDataObj, function(success){
		// handles success or failure
		if(!success){
			promptUserToRecover();
		}
	});
	renderOthers();

renderOthers必须等到callback函数运行完后再触发,所以其实这里是block的,只是callback的使用有“代码段”上的异步的感觉。

dojo.Defferred乍看很神秘,其实只不过是对callback函数的管理,可以有callback函数链的效果,也使复杂的情况下的编码更容易理解。

有些地方使用Node.js的方法process.nextTick来实现异步,文档里面简单的说“This is not a simple alias to setTimeout(fn, 0), it’s much more efficient.” 具体能做到多高效不得而知。而且Node.js虽然很cool,也只能在server端使用。所以感觉js中真正的异步也只有AJAX了,当然,大多数的异步要求也围绕request/response。

最好的方案大概是使用event的subscribe/publish模式了,这在Dojo和Node.js中都大量使用。要在Java中实现这种“异步的Observer模式”需要使用Executor的线程技术,不过由于缺少闭包,这时候模式的使用看上去更像为了弥补语言的缺陷。

Node.js号称任何东西都是异步的,比如I/O的操作,并以此获得Scalability。很多人对此表示怀疑,争论最后变为event和thread这两种模式的比较。Node and Scaling in the Small vs Scaling in the Large这篇指出scaling在大型系统上是综合的考虑,没有哪一种技术是“银弹”,或者说技术的选择已经无关紧要,架构或者设计才是重要的。当然这是冷静的思考,不过Node.js现在版本才0.48,大概没有人会用在大型系统中吧。我觉得event模式优点在于逻辑清晰,而thread代码复杂,难以维护。

感觉语法级别的异步和并发也只有java能做到了,也是久经考验的。Node.js的稳定性则依赖chrome V8引擎的c++代码,多少有点不靠谱,但是javascript语法灵活,这是极大优点。Ruby如果要使用异步还的靠第三方包,不过一般的web应用对这方面也不需要,php啥都没有,照样十分流行。

Javascript闭包(回调函数)的问题

回调函数网络上一大堆,这里举个实际的例子:

<script src="../javascripts/jquery-1.4.2.js"></script>

<script language="javascript">

  $(document).ready(init);

  function init(){
    console.log("....init");
    var sd = new Date();
    $('.list').each( function(){
      this.onclick= function(){
        console.log("you click,haha");
        process(sd);
      };
    });
  };

  function process(youDate){
    console.log("I got it:" + youDate);
  }

</script>

<div id="nav">
  <div>11</div>
  <div>22</div>
  <div>33</div>
</div>

逻辑很简单:当页面加载时注册回调函数,当点击div时激发回调函数。这里注册和激发是异步的。

注意回调函数可以访问上一级的成员变量sd(或许在FP中称为运行上下文),这个跟java的observer模式中的内部类可以访问容器类的this指针一样,这就是说当你点击div时,原来已经产生的时间变量sd还在,这样打印出来的时间都是旧的时间。这是closure的灵活强大的地方(《JavaScript: The Good Parts》里面说,“使用闭包来进行信息掩藏的方式,它是另一个减少有效全局污染的方法”。真知灼见啊!,否则又要使用大量的全局变量),但是跟Java一样,这里由于引用的关系可能会出现垃圾回收的困难,java用弱引用来处理这个问题。这篇文章Memory leak patterns in JavaScript有描述,里面提到了circular references,大概意思是javascript对象引用了DOM对象,而DOM对象反过来又引用了javascript对象。上面代码改成:

$('.list').click( function(){
  console.log("you click,haha");
  process(s);
});

可能更好点,因为this.onclick处可能隐含了DOM对象到Javascript对象的引用,而在$(‘.list’)处又有javascript obj到dom obj的引用。这个我没有求证,毕竟这种内存泄漏在浏览器上关掉当前页后就应该不存在了,不会引起很明显的问题,知道有这个事就好了。