<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>PageTalks &#187; Javascript</title>
	<atom:link href="http://pagetalks.com/category/coding/javascript-coding/feed" rel="self" type="application/rss+xml" />
	<link>http://pagetalks.com</link>
	<description>Pure Web Development &#38; Design Ideas</description>
	<lastBuildDate>Thu, 19 Jan 2012 12:06:33 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Wrangling with Deflate, Base64 and GZip</title>
		<link>http://pagetalks.com/2012/01/19/wrangling-with-deflate-base64-and-gzip.html</link>
		<comments>http://pagetalks.com/2012/01/19/wrangling-with-deflate-base64-and-gzip.html#comments</comments>
		<pubDate>Thu, 19 Jan 2012 11:35:56 +0000</pubDate>
		<dc:creator>Robin</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[NodeJS]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[base64]]></category>
		<category><![CDATA[deflate]]></category>
		<category><![CDATA[gzip]]></category>
		<category><![CDATA[inflate]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[zlib]]></category>

		<guid isPermaLink="false">http://pagetalks.com/?p=549</guid>
		<description><![CDATA[最近不小心被一些编码相关的东西困扰了。这些从Web诞生便产生的一些编码问题，在2012年的今天仍然可以让一个程序员抓狂。项目的大致需求是这样的，客户端需要将一个字符串进行如下处理... ]]></description>
			<content:encoded><![CDATA[<p>最近不小心被一些编码相关的东西困扰了。这些从Web诞生便产生的一些编码问题，在2012年的今天仍然可以让一个程序员抓狂。项目的大致需求是这样的，客户端需要将一个字符串进行如下处理：</p>
<ol>
<li>将字符串进行Deflate压缩</li>
<li>将压缩后的结果再进行Base64编码</li>
</ol>
<p>那么服务器端，需要进行上述流程的反过程。问题是，由于这是个开放接口，客户端可以使用各种不同的平台进行开发，如JS、Java、Ruby、PHP等。好在，大多数语言都是基于<a href="http://zlib.net/">zlib</a>包装了自己的相关API，那么也算是实现标准了。窘迫的事情时，NodeJS里面zlib的实现是在0.6以后加入的，我们使用的技术仍然是0.4版本的。</p>
<p>迫于时间有限，切换到0.6或者实现zlib的binding都是不靠谱的，于是我打开了万能的Github。果然功夫不负有心人，找到了Deflate的纯JS实现——<a href="https://github.com/dankogai/js-deflate">RawDeflate</a>。顿时对作者的崇拜之心油然而生。殊不知，悲剧就是从这里开始的。<span id="more-549"></span><br />
<h3>Base64算法</h3>
<p>为了测试有效性，我用JS做了客户端编码工作，然后用Ruby实现了服务器端解码工作。代码如下:</p>
<ul>
<li>被我修改过的<a href="https://gist.github.com/1638963#file_deflate.js">deflate.js</a></li>
<li>JS编码脚本,<a href="https://gist.github.com/1638963#file_encode_v1.js">v1</a></li>
<li>Ruby解码脚本,<a href="https://gist.github.com/1638963#file_decode_v1.rb">v1</a></li>
</ul>
<p>这里只把编码脚本和解码脚本贴出来，其他的已经在上面给出了Gist链接。</p>
<h4>编码脚本, v1</h4>
<pre><code>var RawDeflate = require("./deflate"),
    Buffer = require("buffer").Buffer,
    fs = require("fs");

function encode(str) {
  return new Buffer(RawDeflate.deflate(str)).toString("base64");
}

console.log(encode("hello, world!"));
fs.writeFileSync("data.txt", encode("hello, world!"));</code></pre>
<h4>解码脚本，v1</h4>
<pre><code>require 'base64'
require 'zlib'

def decode(str)
  Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(Base64.decode64(str))
end

str = File.open("data.txt", "r") do |data|
  data.readlines().join()
end

p decode(str)</code></pre>
<p>运行的结果是空串哦，亲！也就是说Ruby竟然不能解出来。Ruby是直接在Zlib上进行包装的，那么其他的语言PHP、Python什么的也应该是同样的结果。</p>
<p>好吧，难道我JS的算法有误差？那么和Ruby的算法进行对比以下吧⋯⋯</p>
<p><a href="https://gist.github.com/1638963#file_compare_test.rb">Ruby的测试脚本</a>，算出的每个步骤都进行md5：</p>
<pre><code>require 'base64'
require 'zlib'
require 'digest/md5'

def deflate(str)
  Zlib::Deflate.new(nil, -Zlib::MAX_WBITS).deflate(str, Zlib::FINISH)
end

def base64(str)
  Base64.encode64(str)
end

def md5(str)
  s = Digest::MD5.hexdigest(str)
end

def encode(str)
  base64(deflate(str))
end

def decode(str)
  Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(Base64.decode64(str))
end

str = 'hello, world'

p "Test Base64"
p base64(str)
p md5(base64(str))

p "Test Deflate"
p deflate(str)
p md5(deflate(str))

p "Test Encode Defalte and Base64"
p encode(str)
p md5(encode(str))

p "Test Decode Deflate and Base64"
p decode(encode(str))</code></pre>
<p>运行发现，Ruby自己处理自己压缩出来的东西毫无问题。那么JS呢？</p>
<pre><code>var Base64 = require("./b64"),
    RawDeflate = require("./deflate"),
    Buffer = require("buffer").Buffer,
    crypto = require("crypto");

function decode (str) {
  return RawDeflate.inflate(new Buffer(str, "base64").toString());
}

function md5 (str) {
  return crypto.createHash("md5").update(str).digest("hex");
}

function base64 (str) {
  return new Buffer(str).toString("base64");
}

function deflate (str) {
  return RawDeflate.deflate(str);
}

function encode (str) {
  return base64(deflate(str));
}

var str = "hello, world";
console.log("Testing string: " + str);

console.log("Base64 Test");
console.log(base64(str));
console.log(md5(base64(str)));

console.log("Defalte Test");
console.log(deflate(str));
console.log(md5(deflate(str)));

console.log("Test Encode");
console.log(encode(str));
console.log(md5(encode(str)));

console.log("Test Decode");
console.log(decode(encode(str)));</code></pre>
<p>好吧，JS的算法自己处理自己的结果也是正确的。那么比较以下两边各个步骤的md5，可以发现几个现象：</p>
<p><a href="http://www.flickr.com/photos/28352704@N06/6724942605" title="View 'Screen Shot 2012-01-19 at 下午6.43.35' on Flickr.com"><img border="0" style="display:block; margin-left:auto; margin-right:auto;" height="396" src="http://farm8.staticflickr.com/7012/6724942605_db603df767_o.png" alt="Screen Shot 2012-01-19 at 下午6.43.35" width="560" title="Screen Shot 2012-01-19 at 下午6.43.35"/></a></p>
<ul>
<li>仅仅Base64后MD5便不一致</li>
<li>仅仅Deflate后MD5一致</li>
<li>Deflate后再Base64,结果MD5不一致</li>
</ul>
<p>那么就是NodeJS的Base64这么算可能不对了，这时我已经凌乱了，算了，干脆找个JS的Base64实现吧。我的大神啊，原来RawDeflate的作者自己也写了个<a href="https://github.com/dankogai/js-base64">Base64的实现</a>，我觉得这里面有阴谋⋯⋯</p>
<p>用上这位<a href="https://gist.github.com/1638963#file_base64.js">大神的Base64</a>，同时我也发现了RawDeflate里面有一个使用实例。例子里面在Deflate之前，把字符串进行了Base64.<a href="http://h30097.www3.hp.com/docs/base_doc/DOCUMENTATION/V40F_HTML/MAN/MAN1/0398____.HTM">utob</a>操作。但是我们用的测试字符是“hello,world&#8221;貌似没有非ACSII字符。</p>
<p>那么我们<a href="https://gist.github.com/1638963#file_encode_v2.js">新的编码脚本</a>如下：</p>
<pre><code>var RawDeflate = require("./deflate"),
    Buffer = require("buffer").Buffer,
    fs = require("fs"),
    Base64 = require("./base64");

function encode(str) {
  return Base64.toBase64(RawDeflate.deflate(Base64.utob(str)));
}

console.log(encode("hello, world!"));
fs.writeFileSync("data.txt", encode("hello, world!"));</code></pre>
<p>我们仍然使用之前的<a href="https://gist.github.com/1638963#file_decode_v1.rb">解码脚本</a>，结果竟然通过了！ruby得到了“hello,world“！</p>
<p>其中原理不解，有待高人解答，但是灾难还没有结束哦。</p>
<h3>RFC1951、RFC1950和RFC1951</h3>
<p>因为对方服务器目前使用的是JAVA，那么我必须用JAVA测试才能OK。</p>
<p>JAVA的代码如下，只贴出核心代码吧：</p>
<pre><code>InputStream in = new InflaterInputStream(new Base64InputStream(new FileInputStream(compressed)));</code></pre>
<p>第一次试验的结果是，无法解压的，想哭啊，得到“unknown compression method”的错误。</p>
<p>我开始质疑JAVA的实现了。虽然也是zlib的前缀，但是InflaterInputStream<a href="http://docs.oracle.com/javase/1.4.2/docs/api/java/util/zip/InflaterInputStream.html">这个类的JavaDoc</a>里却没有写清楚它对应的算法。</p>
<p>我们知道，HTTP传输里面经常会用到gzip、deflate来压缩内容，只用在header里面写明“Content-encoding&#8221;。Web服务器和浏览器其实都是相当默契的。好吧，我必须承认，浏览器不愧是Web里面最复杂的软件。因为它handle了太多的东西。</p>
<p><a href="http://www.ietf.org/rfc/rfc1950.txt">RFC1950</a>是zlib算法，<a href="http://www.google.com/url?sa=t&#038;rct=j&#038;q=rfc%201951&#038;source=web&#038;cd=1&#038;sqi=2&#038;ved=0CCMQFjAA&#038;url=http%3A%2F%2Fwww.ietf.org%2Frfc%2Frfc1951.txt&#038;ei=E_sXT9OPNsWpiAfVtdybCw&#038;usg=AFQjCNFifaDqn62RJhQ3ODDJ12pO1LsQAA">RFC1951</a>是deflate算法，<a href="http://www.google.com/url?sa=t&#038;rct=j&#038;q=rfc%201952&#038;source=web&#038;cd=1&#038;ved=0CCAQFjAA&#038;url=http%3A%2F%2Fwww.ietf.org%2Frfc%2Frfc1952.txt&#038;ei=I_sXT-ykEpCWiQe0wdmgCw&#038;usg=AFQjCNFA13Eq1NXCeQAGsDxtXuMSMI7bog">RFC1952</a>是Gzip算法。那么他们是三个独立的算法么？</p>
<p>我以为是，其实不是！</p>
<p>在HTTP 1.1的定义里面，也就是<a href="http://www.w3.org/Protocols/rfc2616/rfc2616.html">RFC2616</a>是这么定义delfate传输压缩的：</p>
<p><quote>deflate &#8211; The &#8220;zlib&#8221; format defined in RFC 1950 [31] in combination with the &#8220;deflate&#8221; compression mechanism described in RFC 1951 [29].</quote></p>
<p>原来zlib和deflate是同时使用的！zlib仅仅作为一种容器格式，来承载通过deflate算法压缩的内容。就像AVI其实是个视频容器格式，里面的压缩算法可以是XVID也可以H.262！</p>
<p>回来想想RawDeflate的主页上也仅仅说明是RFC1951的实现，而且人家是”Raw“Deflate，Raw！看来是我自己不懂啊⋯⋯</p>
<p>跟我一样苦逼的人还是不少的，看这个<a href="http://stackoverflow.com/questions/3932117/handling-http-contentencoding-deflate">帖子</a>，有一个回复已经给出了JAVA里面的解决方案。</p>
<p>如果要解决单纯使用Deflate压缩的文件，必须传入一个自定义Inflater。</p>
<pre><code>InputStream in = new InflaterInputStream(conn.getInputStream()), new Inflater(true));</code></pre>
<p>更新了JAVA脚本之后，的确问题解决了。顺手把其他语言解码方案也试验出来了。基本上，除了JAVA需要，其他实现都不需要进行额外的判断，所以JAVA实在不是一个敏捷语言啊⋯⋯</p>
<h4>Ruby解码实现</h4>
<pre><code>require 'base64'
require 'zlib'

def decode(str)
  Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(Base64.decode64(str))
end

File.open("data.txt", "r") do |data|
  str = data.readlines
  p decode str.join
end</code></pre>
<h4>Python解码实现</h4>
<p>Python在压缩的时候，想得到纯粹的Deflate流也是需要额外的加工的，详见此处：<a href="http://stackoverflow.com/questions/1089662/python-inflate-and-deflate-implementations">http://stackoverflow.com/questions/1089662/python-inflate-and-deflate-implementations</a>。</p>
<p><a href="https://gist.github.com/1638963#file_inflate.py">Python的解码</a>的实现也很直白：</p>
<pre><code>import zlib
import base64

str = ""
with open("data.txt") as data:
    for line in data:
        print line
        str += line

def decode_base64_and_inflate(str):
    decoded_data = base64.b64decode( str )
    return zlib.decompress( decoded_data , -zlib.MAX_WBITS)

print decode_base64_and_inflate(str)</code></pre>
<h4>PHP解码实现</h4>
<p>PHP一定要使用gzinflate才能解压纯Deflate流，gzuncompress是用来解压HTTP采用“deflate”方式压缩的数据的！</p>
<pre><code>$data = file_get_contents("data.txt");

echo $data;
echo gzinflate(base64_decode($data));

// this command will throw an exception: data error
// echo gzuncompress(base64_decode($data));
</code></pre>
<h4>JAVA实现</h4>
<p><a href="https://gist.github.com/1638963#file_inflate.java">JAVA实现</a>好累啊，这么罗嗦。还需要一个额外的一个JAR进行Base64操作。这里用的是Apache的<a href="http://commons.apache.org/codec/download_codec.cgi">Common Codec</a>。</p>
<pre><code>//http://stackoverflow.com/questions/3932117/handling-http-contentencoding-deflate

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipException;

import org.apache.commons.codec.binary.Base64InputStream;

public class Inflate {

    public static void inflate(File compressed, OutputStream out) throws IOException {
        InputStream in = new InflaterInputStream(new Base64InputStream(new FileInputStream(compressed)), new Inflater(true));
        shovelInToOut(in, out);
        in.close();
        out.close();
    }

    /**
     * Shovels all data from an input stream to an output stream.
     */
    private static void shovelInToOut(InputStream in, OutputStream out) throws IOException {
//    The following code should be working but it produces strange suffix like "[B@13fcf0ce" now and then
//        byte[] buffer = new byte[1000];
//        int len;
//        while((len = in.read(buffer)) > 0) {
//            out.write(buffer, 0, len);
//            System.out.println(buffer);
//        }

        String line;
        InputStreamReader isr = new InputStreamReader(in);
        BufferedReader br = new BufferedReader(isr);
        while((line = br.readLine()) != null) {
            out.write(line.getBytes());
        }
    }

    /**
     * Main method to test it all.
     */
    public static void main(String[] args) throws IOException, DataFormatException {
        File compressed = new File("data.txt");
        try {
            inflate(compressed, System.out);
        } catch(ZipException e) {
            System.out.println(e.getMessage());
            throw e;
        }
    }

}</code></pre>
<h3>总结</h3>
<p>编码问题总是一个挥之不去的阴影！</p>
]]></content:encoded>
			<wfw:commentRss>http://pagetalks.com/2012/01/19/wrangling-with-deflate-base64-and-gzip.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>More About Crypto Module In NodeJS</title>
		<link>http://pagetalks.com/2012/01/18/more-about-crypto-module-in-nodejs.html</link>
		<comments>http://pagetalks.com/2012/01/18/more-about-crypto-module-in-nodejs.html#comments</comments>
		<pubDate>Wed, 18 Jan 2012 05:31:45 +0000</pubDate>
		<dc:creator>Robin</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[NodeJS]]></category>
		<category><![CDATA[cryptography]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[node]]></category>
		<category><![CDATA[nodejs]]></category>

		<guid isPermaLink="false">http://pagetalks.com/?p=545</guid>
		<description><![CDATA[在上一篇文章里讨论了crypto模块里的一些常用方法。在0.6系列以后，crypto模块的改动非常大，增添了DiffieHellman、pbkdf2、randomByes三大块。这三个也是非常有趣的东西，在这里和大家分享一下。 Di... ]]></description>
			<content:encoded><![CDATA[<p>在<a href="http://pagetalks.com/2012/01/14/crypto-module-and-security-in-nodejs.html">上一篇文章</a>里讨论了crypto模块里的一些常用方法。在0.6系列以后，crypto模块的改动非常大，增添了DiffieHellman、pbkdf2、randomByes三大块。这三个也是非常有趣的东西，在这里和大家分享一下。</p>
<h3><a href="http://en.wikipedia.org/wiki/Diffie–Hellman_key_exchange">DiffieHellman</a></h3>
<p>看着名字很恐怖，不过却是很有趣的东西。有没有想过，大家彼此不告诉对方敏感信息，确能够共同知道一个密钥？DiffieHellman算法就可以实现这一点。</p>
<p><quote>The Diffie–Hellman <strong>key exchange method</strong> allows two parties that have no prior knowledge of each other to <strong>jointly establish a shared secret key</strong> over an <strong>insecure communications</strong> channel.</quote></p>
<p>亮点在加粗的部分，密钥交换方法，而且不需要走https或者其他专用链接。<a href="http://en.wikipedia.org/wiki/Diffie–Hellman_key_exchange">wiki文章</a>已经对该算法做了很详细的解释，我也仿造这里面的说明，做了一个简单的实现，希望能阐明其工作方式。NodeJS的实现有如下特点：</p>
<ul>
<li>算法中的初始根(Primitive Root)始终是2</li>
<li>算法中可以帮你选择Private Integer和Private Key，详见<a href="http://nodejs.org/docs/v0.6.7/api/crypto.html#diffieHellman.generateKeys">generateKEys方法</a></li>
<li>如果不指明，生成的密钥、公钥全都是binary格式</li>
<li>我本想实现wiki中的那个例子，可是node的实现似乎不允许使用那么小的prime；手工计算A、B的合法性、可能性似乎也值的怀疑</li>
</ul>
<pre><code>var crypto = require("crypto"),
    Buffer = require("buffer").Buffer;

var alice, bob, A, a, B, b, p, s1, s2;

alice = crypto.createDiffieHellman(8);//using a 8 bits length prime
A = alice.generateKeys("hex");
a = alice.getPrivateKey("hex");//this is secret
p = alice.getPrime("hex");
console.log("Public Key of alcie: ", alice.getPublicKey("hex"), A);
console.log("Private Key of alice: ", a);
console.log("Prime: ", p); 

//sending p and A to Bob
bob = crypto.createDiffieHellman(p, "hex");//Bob should use the same prime
B = bob.generateKeys("hex");
b = bob.getPrivateKey("hex");
console.log("Public Key of bob", bob.getPublicKey("hex"), B);
console.log("Private Key of bob: ", b);
s2 = bob.computeSecret(A, "hex", "hex");

//sending B to Alice
s1 = alice.computeSecret(B, "hex", "hex");

console.log("Shared Secret:", s1, s2);

</code></pre>
<p><span id="more-545"></span><br />
<h3>PBKDF2</h3>
<p>PBKDF(Password-Based Key Derivation Function)可以用来生成一个更高强度的密码。之前谈到的不管是对称加密（Symmetric Cryptography）、还是像RSA这样的非对称加密（Asymmetric Cryptography、Public－Key Cryptography），都会涉及一个“初始的密码”。这个密码用来生成更加复杂的其他密码。那么可以想像，如果这个“初始密码”能够被猜到或者被伪造，那么整个加密体系的就脆弱了。理论上来说，由人主观想出来的密码都可以通过一定次数的尝试试验出来——因为你不总是喜欢用有规律的字符（123456、65431、1q2w3e4r5t之类）、生日等来生成邮箱密码么？</p>
<p>node里面的PBKDF2是根据HMAC算法来进行制定次数的迭代运算，将用户指定的初始密码进行演化，得到指定长度的新密码。Wifi里用到的WPA、WPA2和Mac文件系统的FileVault都是使用的该算法来生成可用的密码。</p>
<p>我们可以用node的API来模拟WPA2里面的密钥生成，回调中的key是binary格式：</p>
<pre><code>var crypto = require("crypto"),
    Buffer = require("buffer").Buffer;

var passphrase, salt;

passphrase = "my-wifi-passcode";
salt = "wifi-ssid";

crypto.pbkdf2(passphrase, salt, 4096, 256, function(err, key) {
  //key is in binary
  console.log("Encrypt wifi networkd data with key :", new Buffer(key, "binary").toString("hex"));
});</code></pre>
<h3>randomBytes</h3>
<p>这个函数可以帮你生成伪随即数据，目前我知道的作用，大概是可以帮助自动化测试吧⋯⋯</p>
<p><a href="http://nodejs.org/docs/v0.6.7/api/crypto.html#randomBytes">node的文档</a>里面有同步和异步方法的实例，这里不在赘述。</p>
]]></content:encoded>
			<wfw:commentRss>http://pagetalks.com/2012/01/18/more-about-crypto-module-in-nodejs.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Crypto Module and Security In NodeJS</title>
		<link>http://pagetalks.com/2012/01/14/crypto-module-and-security-in-nodejs.html</link>
		<comments>http://pagetalks.com/2012/01/14/crypto-module-and-security-in-nodejs.html#comments</comments>
		<pubDate>Sat, 14 Jan 2012 11:09:14 +0000</pubDate>
		<dc:creator>Robin</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[NodeJS]]></category>
		<category><![CDATA[https]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[nodejs]]></category>
		<category><![CDATA[security]]></category>

		<guid isPermaLink="false">http://pagetalks.com/?p=535</guid>
		<description><![CDATA[NodeJS已经在各个领域都有应用了，大部分敏感数据在公共网络上传播时都会遇到各种安全问题，在分布式系统上尤为明显。J2EE等主流应用框架都已经对HTTPS、各种加密算法有了良好支持，但是... ]]></description>
			<content:encoded><![CDATA[<p>NodeJS已经在各个领域都有应用了，大部分敏感数据在公共网络上传播时都会遇到各种安全问题，在分布式系统上尤为明显。J2EE等主流应用框架都已经对HTTPS、各种加密算法有了良好支持，但是对于新型的NodeJS引擎，其版本号甚至没有达到“1”却已经掀起了业界的改革狂潮。周末抽空，来整整NodeJS里面加密和HTTPS的相关只是，希望对大家有用。</p>
<p>其实NodeJS的相关实现就是把C的库套了一层壳，那么JS下面的openssl等模块其实就代表了NodeJS相关的功能和性能了。编译NodeJS时务必要安装openssl模块，否则crypto模块基本不可用。</p>
<p>该文章的所有代码实例均是Node 0.6.4、OpenSSL 0.9.8r on MacOS Lion 64bit。演示代码的源代码都在Github上：<a href="https://github.com/RobinQu/crypto-demo">https://github.com/RobinQu/crypto-demo</a></p>
<p><a href="http://www.flickr.com/photos/28352704@N06/6693641209" title="View 'openssl-commands' on Flickr.com"><img border="0" style="display:block; margin-left:auto; margin-right:auto;" height="421" src="http://farm8.staticflickr.com/7155/6693641209_f2fa5a1f68_o.png" alt="openssl-commands" width="560" title="openssl-commands"/></a></p>
<h3>Hash算法</h3>
<p>先从最基本的Hash算法说起。以当前平台为例，支持的Digest算法不少，最常用的MD5、SHA1等。大家都知道我们这用的Hash都是不可逆的，所以这是少数几个不成对出现的方法之一，详细文档：<a href="http://nodejs.org/docs/v0.6.4/api/crypto.html#crypto.createHash">http://nodejs.org/docs/v0.6.4/api/crypto.html#crypto.createHash</a>。</p>
<pre></code>var crypto = require("crypto");

var md5hash, result;
//Warning: MD5 collision is made easier and easier. Use SHA1 instead!
md5hash = crypto.createHash("md5");
md5hash.update("hello world!");
md5hash.update("hello nodejs!");
result = md5hash.digest("hex");
console.log(result); //7d962c953bb09058460f3f47650b1ab2
</code></pre>
<h3>HMAC算法</h3>
<p>以前我也不怎么用HMAC，所以临时<a href="http://en.wikipedia.org/wiki/HMAC">wiki</a>了一下。HMAC结果通常是带私钥两次hash后的一串字符，用来同时验证数据完整性(data integrity)和真实性(authenticity)。wiki里面的图表什么的已经说的相当详细了。NodeJS里面的相关文档：<a href="http://nodejs.org/docs/v0.6.4/api/crypto.html#crypto.createHmac">http://nodejs.org/docs/v0.6.4/api/crypto.html#crypto.createHmac</a>。</p>
<pre><code>var crypto = require("crypto");

var hmac, result;
hmac = crypto.createHmac("sha1", "i'm a secret!");

hmac.update("hello world!");
hmac.update("hello nodejs!");
result = hmac.digest("hex");
console.log(result); //9e7b9239f03d2e03cb041a8518977ac84ab4c9b9
</code></pre>
<p><span id="more-535"></span><br />
<h3>对称加密算法</h3>
<p>如果跟我一样不是安全专家的话，建议读一下MS上的一篇<a href="http://support.microsoft.com/kb/246071/zh-cn">启蒙文章</a>。</p>
<p>从这里开始，NodeJS的方法都是成对出现了。NodeJS相关文档：<a href="http://nodejs.org/docs/v0.6.4/api/crypto.html#crypto.createCipher">http://nodejs.org/docs/v0.6.4/api/crypto.html#crypto.createCipher</a>。</p>
<p>这里的加密操作要注意输入、输出的编码；早期版本的node对于<a href="https://github.com/joyent/node/issues/738/">base64编码</a>的输出有bug。所以，请尽量使用hex编码输出。</p>
<p>API文档里要求password是binary格式，但是我直接给了一个utf8的字符串也没事⋯⋯</p>
<pre><code>var crypto = require("crypto");

var cipher, ciphered, decipher, deciphered, password;

password = "i'm the password";

cipher = crypto.createCipher("aes-256-ecb", password);
//important! Update cipher content in "utf8" encoding; To be transformed to a "hex" string!
ciphered = cipher.update("hello world!", "utf8", "hex");
ciphered += cipher.update("hello nodejs!", "utf8", "hex");
ciphered += cipher.final("hex");
console.log(ciphered);//6cc3c04d5fc076ba32b742e1f3439880adbe251e87fe0be9f42a16be4d69a4b8

decipher = crypto.createDecipher("aes-256-ecb", password);
//important! Update decipher content is "hex" string; To be transfromed to a "utf8" result!
deciphered = decipher.update(ciphered, "hex", "utf8");
deciphered += decipher.final("utf8");
console.log(deciphered);//hello world!hello nodejs!
</code></pre>
<p>这一对方法有一个小细节，就是还有一对createCipheriv和createDecipheriv方法。后者需要多传一个iv。什么是iv呢？果断wiki啊：<a href="http://en.wikipedia.org/wiki/Initialization_vector">Initialization Vector</a>。简单的说，这个iv就是一组随即信息，根据这组信息生成的key在对称加密过程中更加安全。事实上，createCipher和createDecipher的第二个参数password并不是直接作为对称加密的key，而是用来生成iv的。</p>
<h3>签名算法</h3>
<p>这个可能大家更常用一些，这个能够提供相比对称加密更高的安全级别。之前说的HMAC也是一种签名算法，但前者不涉及到非对称加密、CA等问题。NodeJS里面的createSign和createVerifier方法是需要配合公钥、私钥、CA一起使用的。</p>
<p>接下来的一些操作需要自己生成公钥、私钥。可能需要参考一些openssl命令：<a href="http://www.madboa.com/geek/openssl/">http://www.madboa.com/geek/openssl/</a>。</p>
<p>以下例子将对内容进行RSA-SHA256算法进行签名，该算法足以应付大部分的安全需求。该算法以<a href="http://dkim.org/specs/rfc4871-dkimbase.html#FIPS.180-2.2002">SHA256</a>进行digest，再将结果以<a href="http://dkim.org/specs/rfc4871-dkimbase.html#RFC3447">RSA</a>算法进行加密，得到的密文作为签名。</p>
<pre><code>openssl genrsa -out mykey.pem 1024
openssl rsa -in mykey.pem -pubout > mypub.pem
</code></pre>
<p>这两个命令生成了一个1024位的公钥、私钥对。Node里面对内容签名的流程很清晰，diegest后用私钥加密，密文为签名。验证流程需要传入正文内容、公钥、签名，verify方法返回一个标识验证是否成功的布尔值标识签名和内容是否匹配。</p>
<pre><code>var crypto = require("crypto"),
    fs = require("fs");

var signer, sign, verifier, privateKey, publicKey, result;

privateKey = fs.readFileSync("mykey.pem", "utf8");
publicKey = fs.readFileSync("mypub.pem", "utf8");

signer = crypto.createSign("RSA-SHA256");
signer.update("hello world!");
signer.update("hello node!");
sign = signer.sign(privateKey, "hex");
console.log(sign);//82071cf4bf80e8c0445351102444ce144017054c25e4832cc050a580aa13035526d432d74a33ebc8aee81376d51e0372a75bc796ece3cadea16bff58b15eccc4e7399c21e0391353c7855391e19cf48aefa148aeb73d1ad4e4e2af156d14da2a84bbf616d18353b0bb4d3570f513056c2edb20af9dfcba71695221f16f4ef182

verifier = crypto.createVerify("RSA-SHA256");
verifier.update("hello world!");
verifier.update("hello node!");
result = verifier.verify(publicKey, sign, "hex");
console.log(result);//true</code></pre>
<h3>HTTPS模块</h3>
<p>打开node的<a href="https://github.com/joyent/node/blob/master/lib/https.js">https模块的源码</a>，你会发现，这是tls模块的一个轻度封装。</p>
<p>最新版的Node已经对tls服务的支持相当不错了（node 0.4左右版本就是惨不忍睹的），文档里已经有非常丰富的例子：<a href="http://nodejs.org/docs/v0.6.7/api/tls.html">http://nodejs.org/docs/v0.6.7/api/tls.html</a></p>
<p>大家可以根据里面的代码利用X509自签名证书来做出来一个tls服务器、客户端的例子。这里就不在赘述。</p>
]]></content:encoded>
			<wfw:commentRss>http://pagetalks.com/2012/01/14/crypto-module-and-security-in-nodejs.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Sencha Touch</title>
		<link>http://pagetalks.com/2010/12/09/sencha-touch.html</link>
		<comments>http://pagetalks.com/2010/12/09/sencha-touch.html#comments</comments>
		<pubDate>Thu, 09 Dec 2010 02:09:30 +0000</pubDate>
		<dc:creator>Robin</dc:creator>
				<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[sencha]]></category>

		<guid isPermaLink="false">http://pagetalks.com/?p=496</guid>
		<description><![CDATA[Sencha的前身是Ext，而现在的Sencha Touch更是将几个著名的项目一起整合起来， 即Sencha Touch ＝ Ext JS + jQTouch + Raphael。Ext是老牌的JS框架，源自YUI，但跟YUI有着截然不容的发展方向，jQTouch是基于jQuery... ]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.flickr.com/photos/28352704@N06/5244961441" title="View 'sencha touch' on Flickr.com"><img border="0" style="display:block; margin-left:auto; margin-right:auto;" height="332" src="http://farm6.static.flickr.com/5286/5244961441_4e7b728281_o.png" alt="sencha touch" width="485" title="sencha touch"/></a></p>
<p>Sencha的前身是Ext，而现在的Sencha Touch更是将几个著名的项目一起整合起来，	即Sencha Touch ＝ Ext JS + jQTouch + Raphael。Ext是老牌的JS框架，源自YUI，但跟YUI有着截然不容的发展方向，jQTouch是基于jQuery的UI组件库，Raphael则是非常成功的一个数据可视化的工具库。</p>
<p>简单的说，Sencha Touchi是将Ext强大的逻辑处理能力进行精简，然后提供了一套可用性和完成度都非常高的UI组件。这个和Sproutcore的方向极为相似。WebApp向CloudApp进化的过程必定是网页开发到应用程序开发的变化过程。在这个过程中，Sproutcore的思想过于超前，而Sencha Touch则是一个承上启下的框架，大多数前端工程师是可以进行无痛过渡的。</p>
<p>进一步说，Sencha Touch团队对于文档和社区的建设都比Sproutcore好上不少。又有一部分开发者对Ext本身就比较熟悉，可谓是驾轻就熟。<span id="more-496"></span>如今，WebOS的概念在Chrome OS发布之后，则是更进一步的重要了。无论是手机上的CloudApp还是Chrome OS里的应用，能够很好解决问题的目前只有Sencha touch和我一直都在等待的Sproutcore Mobile才能解决的。</p>
<p>目前Sencha Touch是完全免费的，并且在GPL协议下开源。</p>
<p>如果你对Sencha Touch感兴趣，可以：</p>
<ul>
<li>去看看<a href="http://www.sencha.com/products/touch/demos.php">Demo</a></li>
<li>拜见一下<a href="http://dev.sencha.com/deploy/touch/docs/">文档</a></li>
<li><a href="http://www.sencha.com/products/touch/download.php">下载</a>过来玩玩</li>
</ul>
<p>目前本人也是花时间看这个框架，并且写了一个手机联系人的Demo，如果感兴趣，可以在Github上找到<a href="https://github.com/RobinQu/Phone-Demo">源码</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://pagetalks.com/2010/12/09/sencha-touch.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>New Toys For Web Designers</title>
		<link>http://pagetalks.com/2010/10/30/new-toys-for-web-designers.html</link>
		<comments>http://pagetalks.com/2010/10/30/new-toys-for-web-designers.html#comments</comments>
		<pubDate>Sat, 30 Oct 2010 11:52:12 +0000</pubDate>
		<dc:creator>Robin</dc:creator>
				<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[User Experience]]></category>
		<category><![CDATA[canvas]]></category>
		<category><![CDATA[js]]></category>

		<guid isPermaLink="false">http://pagetalks.com/?p=488</guid>
		<description><![CDATA[有时候在想，前端的核心技术完全由浏览器厂商做主，开发人员做事情总是收到各种限制真是不爽。就算到了HTMLx(x>5)，也被局限于浏览器的框框。虽然现在WebOS的概念很棒，可惜PalmPre出师未捷... ]]></description>
			<content:encoded><![CDATA[<p>有时候在想，前端的核心技术完全由浏览器厂商做主，开发人员做事情总是收到各种限制真是不爽。就算到了HTMLx(x>5)，也被局限于浏览器的框框。虽然现在WebOS的概念很棒，可惜PalmPre出师未捷身先死。老态龙钟的HP也干不出什么能和Google、Apple叫板的事情。总之，前端工程师们最多到手机上做做WebApp或者Air应用找找乐子。</p>
<p>好吧，抱怨完了之后，我们说说现在还有“玩头”的canvas。想必大家都已经看过N多canvas的例子，不管是canvas做的动画还是游戏，都显得十分理想。按下F12，你却发现这些例子的代码是十分的庞大的，证明要操控canvas做一些“有实用性”的事情，是很有难度的。</p>
<p>毕竟，Canvas的API只提供了画点、画线、画圆、画正方形之类的基本接口，简陋的让人难以接受。这个时候的Canvas跟Flash是完全没有可比性的，Flash的Tween、Sprite之类的类已经封装的非常完美，其暴露给开发者的API已经高于Canvas的那些API太多太多。</p>
<p>但另一方面，Canvas也给我们了“制作”Flash的机会。毕竟Flash的底层无非也就是画点、画线的方法。</p>
<p>在这里，我想介绍一下几个比较好的针对Canvas的脚本库。在实际工程中，是绝对救星。<br />
<span id="more-488"></span><br />
<h3><a href="http://code.google.com/p/cakejs/">Cake.js</a></h3>
<p>Cake.js是一个非常有魅力的库。首先，它解决了几个让你十分头疼的问题：</p>
<ul>
<li>Canvas内部没有对象的概念，没法针对某个图形或形状进行监听等操作</li>
<li>浏览器之间的复杂兼容性</li>
<li>Canvas缺乏动画操作API</li>
<li>Canvas和SVG不能互转</li>
</ul>
<p>Cake.js很好了解决了以上所有问题。它不仅提供了Circle、Rectangle之类的基础对象，而且这些对象能够如同DOM对象一样进行监听等操作。其提供了较为高效的动画函数，如同Flash里的TweenLite。<br />
更有趣的是，Cake.js提供了一个SVGParser，支持从SVG绘制Canvas！</p>
<p>如果你要用canvas制作例如游戏之类的高度交互的应用，那么你要么用cake.js，要么自己实现一份cake.js。</p>
<p>大家可以到这里去看看cake.js的一些演示：http://glimr.rubyforge.org/cake/canvas.html</p>
<h3><a href="http://processingjs.org/">Processing.js</a></h3>
<p>Processing是一个很有名的数据可视化语言，之前的浏览器解决方案一直是利用java applet技术，可惜applet尴尬的局面至今没有能够缓解，所以java applet并不是一个很好像大众分发的技术方案。</p>
<p>但是某人（jQuery的作者之一）实现一个JS版的Processing.js。准确的说应该是一个Processing语言的JS解析器，然后利用canvas展示。</p>
<p>传承了Processing的所有优点。如果你已有Processing的脚本，你是可以直接无痛迁移到Canvas上的。</p>
<p>对于比较了解Processing的朋友，这个库你是绝对需要的。</p>
<h3><a href="http://deanm.github.com/pre3d/">Pre3D</a></h3>
<p>有的同学可能已经迫不及待用Canvas做3D了。可是Canvas的3D Context的标准都没有，WebGL也还是少数浏览器的特权。退而求其次，就是用2D来模拟3D了。这里面的大部分原理就是坐标的矩阵变幻了，对于Canvas来说，这些操作全部用JS来实现，效率肯定比WebGL之类的C所写的原生3D API低好几个数量级的。但是这是目前唯一可行的方案。</p>
<p>所以这个库的名字叫做“Pre3D”，意味Canvas的3D时代还为来临，是3D的前期。</p>
<p>若是准备做3D图形的展示，这个库可以让你事半功倍；倘若是要做游戏，这个库也是爱莫能助的。</p>
<p>限于技术限制，Pre3D没法追踪你创建的虚拟对象，你无法与它们进行交互。你能有的仅仅是对Camera对象进行rotate、translate之类的操作。</p>
<p>但是这些对于制作出类似<a href="http://alteredqualia.com/canvasmol/">CanvasMol</a>应用是错错有余的。</p>
<h3>依旧蛋疼</h3>
<p>即使你克服了一切利用Canvas来做游戏，你却发现你的游戏在有些浏览器下跑的很慢，在有些浏览器下完全无法使用。</p>
<p>想退回利用DOM进行游戏对象的操作？现在已被正式，canvas对于多个游戏对象的操作也许好于直接操作DOM对象(via <a href="http://blog.frontendforce.com/2010/03/games-development-in-javascript-canvas-vs-dom-benchmark/">link</a>)。</p>
<p>好吧，对于刚刚定稿的HTML5我们要耐心，耐心，再耐心。</p>
]]></content:encoded>
			<wfw:commentRss>http://pagetalks.com/2010/10/30/new-toys-for-web-designers.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mobile App Development&#8230;in OTHER Ways</title>
		<link>http://pagetalks.com/2010/09/24/mobile-app-development-in-other-ways.html</link>
		<comments>http://pagetalks.com/2010/09/24/mobile-app-development-in-other-ways.html#comments</comments>
		<pubDate>Fri, 24 Sep 2010 08:31:47 +0000</pubDate>
		<dc:creator>Robin</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[phonegap]]></category>
		<category><![CDATA[rhodes]]></category>
		<category><![CDATA[titanium]]></category>

		<guid isPermaLink="false">http://pagetalks.com/?p=469</guid>
		<description><![CDATA[江湖有江湖的规矩，大家都知道iPhone上的Cocoa Touch框架和Xcode是开发Apple移动产品的王道。Android SDK和Eclipse是开发Android App的王道⋯⋯你可以找到所有主流平台的开发框架和相关IDE。 你说这做移... ]]></description>
			<content:encoded><![CDATA[<p>江湖有江湖的规矩，大家都知道iPhone上的Cocoa Touch框架和Xcode是开发Apple移动产品的王道。Android SDK和Eclipse是开发Android App的王道⋯⋯你可以找到所有主流平台的开发框架和相关IDE。</p>
<p>你说这做移动端应用的人容易么？虽说Blackberry和Android都是基于Java、有效部分人之前也做过Objective－C，但是想到一个大众产品需要提供每个平台的无差别体验时，你做的可能不是一个项目，而是N个项目。哪天国内某个厂商做了自己的操作系统和SDK，你也得啥啥的多做一个版本。对于财大气粗的公司这些都是无所谓的，可是对于那些可怜兮兮的工程师和项目经理更期望自己的产品生命周期能更长一些、受众应该更广一些——我们需要的是跨平台的解决方案啊！</p>
<p>我们第一个想到的词肯定会是HTML⋯⋯这不是什么新鲜东西，在很多其他嵌入式产品中，例如路由，都是用网页作为用户接口的。将Web App封装到一个应用程序包内进行分发这种概念，是一个很直观的想法。</p>
<p><a href="http://pagetalks.com/wp-content/uploads/2010/09/Web-App-Architecture.gif"><img src="http://pagetalks.com/wp-content/uploads/2010/09/Web-App-Architecture.gif" alt="" title="Web App Architecture" width="521" height="541" class="aligncenter size-full wp-image-470" /></a></p>
<p>AListApart上已经有<a href="http://www.alistapart.com/articles/apps-vs-the-web/">文章</a>在讨论这个问题，但是作者根据自身经验觉得WebApp在需求变得复杂之后不能满足任务⋯⋯汗⋯⋯的确有这样的嫌疑吧。如果单纯使用WebApp再套一个WebView的壳，就没法使用重力感应、GPS、照相机之类的功能。</p>
<p>问题的确如此，但是还是希望还是有的。Phonegap、Titanium、Rhodes、SproutCore Touch这三个跨平台的移动框架虽然依然是一个“壳”，就不仅仅是一个WebView的壳而已。<br />
<span id="more-469"></span></p>
<h3>轻量级的PhoneGap</h3>
<p><a href="http://www.phonegap.com/">PhoneGap</a>的是一个非常轻量级的框架，在开发过程中完全不需要使用Java或Ruby之类的代码，而是纯粹的HTML＋JS＋CSS。只是这里的JS里被赋予了增强型的API，你可以控制Camera、加速器、GPS等等。想了解它的功能，你可以直接看他的<a href="http://www.phonegap.com/docs/">文档</a>。</p>
<p>PhoneGap最大的优势就是它真的很轻量很快捷。你能把Web开发流程中的Rapid Prototyping应用到Mobile App的开发之中。</p>
<p><a href="http://pagetalks.com/wp-content/uploads/2010/09/Dev-Process.gif"><img src="http://pagetalks.com/wp-content/uploads/2010/09/Dev-Process.gif" alt="" title="Dev Process" width="211" height="702" class="aligncenter size-full wp-image-471" /></a></p>
<p>缺点也很明显。由于使用JS作为语言载体，你的效率没法跟Objective-C、Java的比。由于，PhoneGap限定你的程序在一个HTML文件里面，那么App规模越来越大之后，你还能在一个HTML里管理你的App么？<br />
最后一种是个这些框架的通病，无法提供完全一致的用户体验。在UI方面，由于无法调用系统默认的UI库，界面完全靠你自己去写，这对用户体验设计是一个灾难。虽然<a href="http://www.jqtouch.com/">jqTouch</a>之类的框架部分弥补了这方面的缺陷，但是用HTML模拟的控件用起来还是没有系统原生的流畅⋯⋯</p>
<h3>更聪明的Titanium</h3>
<p><a href="http://developer.appcelerator.com/">Titanium</a>也是利用HTML、JS、CSS。不过其<a href="http://developer.appcelerator.com/apidoc/mobile/latest">JS API</a>封装的更强大。</p>
<p>同样是将手机所有功能都封装起来，但是比PhoneGap更完美。但这个是牺牲跨平台性的，Titanium只支持iPhone和Android，现在的Blackberry的支持还是测试阶段⋯⋯不过对于中国手机用户来说，这样的支持性反而更好。</p>
<p>Titanium比PhoneGap对手机的功能封装的更好，前者支持后者不支持的File IO操作（PhoneGap支持不完善）、Contacts读取等等。<br />
对于正式的产品开发，选择兼容性更强的PhoneGap还是功能更强大的Titanium，的确是个疑问。</p>
<h3>绝对重量级的Rhodes</h3>
<p>Rhodes采取的是另一种思路，简单说，Rhodes的框架在App内部架设了一个Client/Server端，开发者写的程序就是其Client端，通过框架的JS API调用“Server”端的“WebService”。Server端再根据得到指令进行操作，Server端本身具有调用平台原生API的使用能力。</p>
<p>其”Server“端使用的是Ruby，那么很多同学可以通过Ruby自己封装平台上的原生API给”Client“端的JS使用。</p>
<p>Rhodes还有相应的云端解决方案，能够解决Mobile App的数据同步等问题。</p>
<p>详细的可以参考<a href="http://wiki.rhomobile.com/index.php/Rhodes">Rhodes的介绍</a>，或者这个<a href="http://www.slideshare.net/inouemak/rhodes-and-phone-gap">Slide</a>。</p>
<h3>蓄势待发的SproutCore Touch</h3>
<p>今年早些的时候就发布了<a href="http://touch.sproutcore.com/hedwig/">SproutCore Touch</a>。<a href="http://www.sproutcore.com/">SproutCore</a>(网站已被hexie)就已经很NB了，YUI、jQuery跟它都不是一个等级的东西，或者就不是同一个概念。SproutCore可以是仅仅借用了JS的语法去定义了一个全新的界面开发环境，并且提供了项目生命周期的一切工具。SproutCore Touch是面向移动端的框架，肯定会在SproutCore的基础上加入移动端的Touch、GPS、Camera相关的API。</p>
<p>可惜这个框架的学习难度特别大，其API的文档和调试环境特别不友好⋯⋯好吧，应该是说，文档比较难找，然后你又要用Ruby来完成开发工具的配置、代码调试等等。另外，这个框架的开发进度好慢的。</p>
<p>但这个框架是基于SproutCore的，潜力太不可小视的。</p>
<h3>没有Utopia</h3>
<p>现在，我们可以放心的用已有的技术去快速抢占移动端市场了。再加上HTML5在移动平台上的广泛支持，前途看似无限美好。</p>
<p>但是大家不要忘了，现在你的App虽然没有跟平台依赖，但是跟这些框架相关了！<br />
从性能上说，程序性能又是跟每个平台上的浏览器有关。Webkit虽然是主流，但是你会发现Android和iPhone的Webkit就很不一样，更不用说每个不同版本的平台之间浏览器的差异了⋯⋯似乎你又回到了桌面浏览器的兼容性大战上。</p>
<p>这些问题在你使用一些HTML5的新特性就特别明显。比如，你会发现iPhone上竟然没有WebWorker。Android上Canvas.toDataURL()方法无法执行等等问题。</p>
<p>看来，Mobile App领域还真是一块处女地⋯⋯</p>
]]></content:encoded>
			<wfw:commentRss>http://pagetalks.com/2010/09/24/mobile-app-development-in-other-ways.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Event Handling and Delegation</title>
		<link>http://pagetalks.com/2010/09/23/event-handling-and-delegation.html</link>
		<comments>http://pagetalks.com/2010/09/23/event-handling-and-delegation.html#comments</comments>
		<pubDate>Thu, 23 Sep 2010 09:18:31 +0000</pubDate>
		<dc:creator>Robin</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[delegate]]></category>
		<category><![CDATA[event]]></category>
		<category><![CDATA[jquery]]></category>

		<guid isPermaLink="false">http://pagetalks.com/?p=466</guid>
		<description><![CDATA[事件代理这个词稍微有些陌生，但是用过jQuery的同学都会知道$.fn.live以及jQuery 1.4之前的一个很有名的插件——$.fn.liveQuery 。在高级的事件处理中，大家不可避免的遇到this关键字指代不明、或者... ]]></description>
			<content:encoded><![CDATA[<p>事件代理这个词稍微有些陌生，但是用过jQuery的同学都会知道<a href="http://api.jquery.com/live/">$.fn.live</a>以及jQuery 1.4之前的一个很有名的插件——<a href=" http://brandonaaron.net/code/livequery/docs">$.fn.liveQuery </a>。在高级的事件处理中，大家不可避免的遇到this关键字指代不明、或者是无法预测事件处理函数执行时的scope。而在一些看不见的地方，复杂的事件监听逻辑下，为了提升性能也不得不用事件的代理。</p>
<p>最简单的事件代理就是将一个方法或函数委托给另外一个对象执行。</p>
<pre>var delegator = function(that, func) {
        return function() {
            func.apply(that, arguments);
        };
};</pre>
<p>假设我们的一个View中在数据发生改变的时候会被通知，然后需要更新一个列表，但是必须确保这个rebuildList方法在执行的时候，scope是这个View对象，那么我们可以这样：</p>
<pre>var updateList = delegator(this, this.rebuildList);
this.model.onRowsInserted.subscribe(updateList);</pre>
<p>还有一些delegator的<a href="http://blog.xole.net/article.php?id=632">实现方法</a>，甚至直接修改Function的prototype对象来增强函数或方法的功能。</p>
<pre>Function.prototype.delegate = function(that) {
	return function() {
		this.apply(that, arguments);
	};
};</pre>
<p>除了解决scope的问题，delegate机制更大的作用是将监听器本身的注册到别的对象之上。</p>
<p>jQuery 1.4之后加入的<a href="http://api.jquery.com/jQuery.proxy/">$.proxy()</a> 就是同样实现这样功能的。里面同样是用Function.apply来执行的。</p>
<p>DOM Level 2的事件传播机制是先从document对象开始向目标对象传播，沿途会执行该事件的所有事件处理函数。<br />
到了目标对象本身，也会执行该对象上对该事件的处理函数。<br />
最后，还会再次向document传播，沿途同样会执行该事件的所有事件处理函数。</p>
<p>第一个阶段被成为capture、第二个阶段被成为bubbling。</p>
<p>IE浏览器目前只有bubbling是完美支持的。不管怎么样，我们可以利用这样的bubbling机制让我们的事件管理更加强大——为什么不把事件处理统一放到祖先元素上去呢？<br />
<span id="more-466"></span><br />
要监听一组#nav下的A元素，并执行响应的处理函数，我们必须如此循环操作：</p>
<pre>window.onload = function() {
    var nav = document.getElementById("nav");
    var links = nav.getElementsByTagName("a");
    for (var i = 0, l = links.length; i < l; i++) {
        links[i].onclick = function() {
			window.open(this.href);
            return false;
        }
    }
}</pre>
<p>如果将事件处理函数注册到#nav上，在冒泡阶段查看Event对象的target属性对目标对象进行处理，那么不管#nav下面有多少个A元素，都只需要注册一个事件处理函数：</p>
<pre>window.onload = function() {
    var nav = document.getElementById("nav");
    nav.onclick = function() {
        var e = arguments[0] || window.event, //Event对象在IE中处在window.event
        target = e.srcElement ? e.srcElement: e.target; //IE的Event对象中只有srcElement属性而没有target属性
        window.open(this.href);
        return false;
    }
}</pre>
<p>想象一下在企业级应用中常见的DataGrid对象，每个table cell都会有事件监听，你是将成百上千的td元素都给进行事件监听，还是选择用一个注册在table元素的监听函数监听所有变动了？性能差别会在这类极端应用下迅速显现出来的。</p>
<p>jQuery1.4之后出现的live()和die()就是运用bubbling进行事件委托的。但是其性能并不被开发者看好。主要是最顶部的document元素在接收到事件的时候，需要检查当前的target是不是之前所指定的那个事件对象。 </p>
<p>jQuery用的是$.fn.closet，这会从当前元素一直遍历到document找到复合给定选择符的元素。<br />
另外，当你用$.fn.live的时候，你的第一个$(selector)调用会真正的去进行DOM查找操作，事实上你只是注册一个事件监听，这个查找是多余的。<a href="http://paulirish.com/2010/on-jquery-live/ ">这篇文章</a>你介绍了已有的四个解决方法。</p>
<p>我感觉最靠谱的方法是用$.live(selector, context, type, handler)或者$(context).live(selector, type,  handler)。两个办法中都制定了一个context作为顶级对象。那么对象的监听只会到context对象，会节省很多的遍历操作。</p>
<p>事实上正因为这个问题，jQuery在<a href="http://github.com/jquery/jquery/commit/31432e048f879b93ffa44c39d6f5989ab2620bd8">最新的版本</a>里新增了delegate和undelegate ，用了一句：</p>
<pre>this.live( types, data, fn, selector )</pre>
<p>实际上也就是本文指出的第二种解决方案。 这里也不得不佩服jQuery作者对“民意”的体察啊。</p>
<p>至于在jQuery 1.3之前没有$.fn.live的时候，$.fn.livequery依然是很多人的选择。该插件是用setTimeout定义20ms的间隔进行检测是否有新元素复合之前给定的选择符。</p>
<p>该插件还提供另一种调用方法： $.fn.livequery( matchedFn, unmatchedFn )， 如果有复合选择符的新元素，就对他们执行machedFn函数，如果有元素发生改变并不在符合选择符就执行unmatchedFn，这样就相当的灵活。</p>
<p>可是我对这20ms的定期检查十分畏惧啊，在复杂环境中可能会和利用bubbling的$.fn.live相差很远吧？毕竟，后者只对DOM树的深度有关，而前者则是跟当前整个应用的复杂度有关。livequery更应该用WebWorker来在后台检测？！笑⋯⋯可惜WebWorker没有广发的支持啦⋯⋯</p>
]]></content:encoded>
			<wfw:commentRss>http://pagetalks.com/2010/09/23/event-handling-and-delegation.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Javascript MVC, again&#8230;&#8230;</title>
		<link>http://pagetalks.com/2010/09/22/javascript-mvc-again.html</link>
		<comments>http://pagetalks.com/2010/09/22/javascript-mvc-again.html#comments</comments>
		<pubDate>Wed, 22 Sep 2010 07:39:37 +0000</pubDate>
		<dc:creator>Robin</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[mvc]]></category>

		<guid isPermaLink="false">http://pagetalks.com/?p=444</guid>
		<description><![CDATA[谈到RIA、HTML5，脑海你出现的总是复杂的Web App——人们企图用JS、HTML、CSS来写桌面程序。抛开桌面开发有MFC、AWT之类的库这种优厚待遇不说，其实JS就没有被设计成为一个界面开发的语言，但是... ]]></description>
			<content:encoded><![CDATA[<p>谈到RIA、HTML5，脑海你出现的总是复杂的Web App——人们企图用JS、HTML、CSS来写桌面程序。抛开桌面开发有MFC、AWT之类的库这种优厚待遇不说，其实JS就没有被设计成为一个界面开发的语言，但是人们对JS的期望却越来越高。</p>
<p>尽管如此，没有一个好用的MVC的库是JS的尴尬——或者说沒有用个能够具有伸缩性的库。SproutCore、Cappuccino这些都是重量级的，很难在项目里说用就用。还有一个库名字就叫“Javascript MVC”，虽然看起来很轻量，但是实际用起来那也是相当的麻烦。面对这些MVC库，你很难想把他们用到一个小项目里去。就那么些操作，干嘛把骨架搭这么复杂呢？</p>
<p>HTML5的出现给JS带来了LocalStorage、Canvas、Offline Storage之类的重要API，没有这些东西根本就无法做出与桌面程序类似的体验。与此同时，所谓“能力越大，责任就越大”，人们对JS的期望值是空前的提升了。这个时候面对复杂的程序逻辑和未来对程序伸缩性的需求，不用MVC进行模块化将内部组件Decouple，项目的每次重大变化应该只有全部重写的分。</p>
<p>这几天虽然在学校办事，也在抽空重写之前的那个<a href="http://pagetalks.com/2010/03/22/jquery-locationselect-plugin.html">LocationSelect</a>插件，之前用jQuery写的逻辑太混乱了，算法效率也比较低，那个时候的目的仅仅是“急于实现一个地址联动的组件”给Map2fun这个项目用。其实这个插件里面是可以有很多学问的，webservice的使用、缓存、查询算法，最重要的是这个“联动”二字，3个下拉菜单看似简单却极为蛋疼的互动逻辑。干脆就用MVC的模式重写吧⋯⋯</p>
<p>项目的代码可以参见插件的文档页面。其实我也是第一次把MVC完整的用到一个小项目里，代码越往后看可能越觉得不伦不类。<br />
<span id="more-444"></span><br />
LocationSelect的Class Diagram大概如下，当然重点在ListModel、ListView、ListController上面。这三者的关系就是如下图。这图是从Google出来的，有版权问题就联系我。</p>
<p>详细讲解MVC就不要了，后面有一大堆很好的文章来解释的。</p>
<p>当我做完之前版本的重写，开始设想这个小程序在下一个版本该作些什么的。把HTML5加进去，果然很给力。</p>
<p>Model这层，未来的组件很可能不仅仅访问远程服务而已。其实，运用LocalStorage做为一个缓存层，或者直接选择使用LocalStorage作为主要存储媒介是一种可能。不管怎么样，缓存的策略对于程序的性能会有很大的提高。而WebWorker和WebSocket的引入会更好的在后台监听数据库的改变，这对数据库应用是革命性的。</p>
<p>而View这层，Canvas的引入也让一些自定义控件成为可能。以后的View层不仅仅是纯粹的修改DOM。</p>
<p>好吧⋯⋯自己也不知到在说些啥了⋯⋯最后放上一些自己看MVC的文章。</p>
<ul>
<li>
<a href="http://www.alexatnet.com/content/model-view-controller-mvc-javascript<br />
">http://www.alexatnet.com/content/model-view-controller-mvc-javascript<br />
</a></li>
<li>
<a href="http://savvyduck.blogspot.com/2008/01/javascript-classes-design-patterns-mvc.html<br />
">http://savvyduck.blogspot.com/2008/01/javascript-classes-design-patterns-mvc.html<br />
</a></li>
<li>
<a href="http://savvyduck.blogspot.com/2008/01/javascript-classes-design-patterns-mvc.html<br />
">http://www.alistapart.com/articles/javascript-mvc/</a></li>
]]></content:encoded>
			<wfw:commentRss>http://pagetalks.com/2010/09/22/javascript-mvc-again.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTML5 and CSS3 Must-have Online Resources</title>
		<link>http://pagetalks.com/2010/08/07/html5-and-css3-must-have-online-resources.html</link>
		<comments>http://pagetalks.com/2010/08/07/html5-and-css3-must-have-online-resources.html#comments</comments>
		<pubDate>Sat, 07 Aug 2010 11:50:29 +0000</pubDate>
		<dc:creator>Robin</dc:creator>
				<category><![CDATA[CSS]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[css3]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://pagetalks.com/?p=416</guid>
		<description><![CDATA[好吧～到了掏家底的时候了。最近看了很多相关资料，所以还是有很多可以说的。相信大家在消化这些资料之后，应该会对HTML5和CSS3有较为深入的了解。 Well, It&#8217;s time for me to share something abou... ]]></description>
			<content:encoded><![CDATA[<div class="text-cn" lang="zh">好吧～到了掏家底的时候了。最近看了很多相关资料，所以还是有很多可以说的。相信大家在消化这些资料之后，应该会对HTML5和CSS3有较为深入的了解。</div>
<div class="text-en" lang="en">Well, It&#8217;s time for me to share something about HTML5 and CSS3. We&#8217;ve discussed too much about them and it&#8217;s necessary to really dive into these techs&#8230;&#8230;</div>
<h3>Future Trends</h3>
<ul>
<li><a href="http://www.webmonkey.com/2008/09/html_5_won_t_be_ready_until_2022dot_yes__2022dot/">HTML 5 Won’t Be Ready Until 2022：when on earth can we use HTML5?</a></li>
<li><a href="http://blogs.claritycon.com/blogs/steve_holstad/archive/2007/08/15/3263.aspx">WhatWG, W3C and HTML 5.0：Relationships between W3C, WhatWG and HTML5</a></li>
<li><a href="http://www.itwriting.com/blog/2353-the-two-specifications-of-html-5-0-what-wg-vs-w3c.html">The two specifications of HTML 5.0: WHAT WG vs W3C: the story behind two</a></li>
<li><a href="http://www.apple.com/hotnews/thoughts-on-flash/">Thoughts On Flash by Steven Jobs</a></li>
</ul>
<h3>Tutorials for Novices</h3>
<ul>
<li><a href="http://diveintohtml5.org/">DIVE INTO HTML 5</a></li>
<li><a href="http://www.smashingmagazine.com/2009/12/19/what-you-need-to-know-about-behavioral-css/">What You Need To Know About Behavioral CSS</a></li>
<li><a href="http://docs.google.com/fileview?id=0BxMTfJDd2NlYN2VkODk3ZjYtYzY1Yi00M2Y3LWI4NTAtNTIyZmQzYmUzOTZm&amp;hl=en">The future of graphics on the web</a></li>
<li><a href="http://docs.google.com/fileview?id=0BxMTfJDd2NlYMmU5ZmQxYTUtYmRmZS00MmY0LWJlZjgtODVlZTVmNzU4YWNl&amp;hl=en">Browsers with wings</a></li>
<li><a href="http://docs.google.com/fileview?id=0BxMTfJDd2NlYZmIyZjM5NjAtNzgzZC00YWE5LTk0YjAtNTQ5NDcwNjJkZDdl&amp;hl=en">The future of the web</a></li>
<li><a href="http://webdesignledger.com/tutorials/15-useful-html5-tutorials-and-cheat-sheets">15 Useful HTML5 Tutorials and Cheat Sheets</a></li>
<li><a href="http://www.smashingmagazine.com/2009/07/16/html5-and-the-future-of-the-web/">HTML5 and The Future of the Web</a></li>
<li><a href="http://blog.frontendforce.com/2010/04/html5-javascript-api-whats-new/">HTML5 JavaScript API. What’s new?</a></li>
<li><a href="http://www.w3schools.com/html5/default.asp">W3C School, HTML5 Tutorial</a></li>
<li><a href="http://perishablepress.com/press/2010/02/22/css3-border-properties/">Understanding CSS3 and CSS2.1 Border Properties</a></li>
</ul>
<h3>Important Documentations</h3>
<ul>
<li><a href="http://dev.w3.org/html5/spec/Overview.html">HTML5, A vocabulary and associated APIs for HTML and XHTML</a></li>
<li><a href="http://dev.w3.org/html5/canvas-api/canvas-2d-api.html">Canvas 2D API Specification</a></li>
<li><a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html">The Canvas Element</a></li>
<li><a href="http://developer.apple.com/safari/">Apple Safari Developer Center</a></li>
<li><a href="https://developer.mozilla.org/en/HTML/HTML5">HTML5 &#8211; MDC</a></li>
<li><a href="http://meiert.com/en/indices/css-properties/">CSS Properties Index</a></li>
</ul>
<p><span id="more-416"></span></p>
<h3>Live Demos</h3>
<ul>
<li><a href="http://www.apple.com/html5/">Apple HTML5</a></li>
<li><a href="http://www.webdesignerwall.com/trends/css3-examples-and-best-practices/">CSS3 Examples and Best Practices</a></li>
<li><a href="http://www.canvasdemos.com/">CanvasDemo</a></li>
</ul>
<h3>Compatibility</h3>
<ul>
<li><a href="http://msdn.microsoft.com/en-us/library/cc351024(VS.85).aspx">CSS Compatibility and Internet Explorer</a></li>
<li><a href="http://findmebyip.com/litmus#target-selector">Web Design Checklist</a></li>
<li><a href="http://html5test.com/">THE HTML5 TEST，online test suite for html5</a></li>
<li><a href="http://wiki.whatwg.org/wiki/Implementations_in_Web_browsers">Implementations in Web browsers on WhatWG</a></li>
</ul>
<h3>Open Source Projects</h3>
<ul>
<li><a href="http://code.google.com/p/explorercanvas/">HTML5 Canvas for Internet Explorer</a></li>
<li><a href="http://code.google.com/p/javascript-libraries/downloads/detail?name=modernizer.js&amp;can=2&amp;q=">Modernizer.js</a></li>
<li><a href="http://code.google.com/p/cakejs/">CAKE &#8211; Canvas Animation Kit Experiment</a></li>
<li><a href="http://paulirish.com/work/gordon/demos/">Gordon &#8211; An open source Flash™ runtime written in pure JavaScript with SVG</a></li>
<li><a href="http://smokescreen.us/">Smokescreen &#8211; convert flash to HTML5/Javascript</a></li>
<li><a href="http://raphaeljs.com/">Raphaël: Raphaël</a> is a small JavaScript library that should simplify your work with vector graphics on the web</li>
<li><a href="http://processingjs.org/">Processing.js</a> is an open programming language for people who want to program images, animation, and interactions for the web without using Flash or Java applets.</li>
<li><a href="http://cappuccino.org/">Cappuccino</a> is an open source framework that makes it easy to build desktop-caliber applications that run in a web browser.</li>
</ul>
<h3>Online Tools</h3>
<ul>
<li><a href="http://jsbin.com/">JS Bin</a> &#8211; an online sandbox for js</li>
<li><a href="http://css3generator.com/">CSS3 Generator</a></li>
<li><a href="http://css3please.com/">CSS3, Please!</a></li>
<li><a href="https://browserlab.adobe.com/en-us/index.html">Adobe BrowserLab</a></li>
<li><a href="http://www.css88.com/tool/css3Preview/Linear-Gradients.html">Linear Gradients-Css3 Demo</a></li>
<li><a href="http://www.css88.com/tool/css3Preview/Box-Shadow.html">Box Shadow &#8211; Css3 Demo</a></li>
<li><a href="http://www.css88.com/tool/css3Preview/Transform.html">transform and transform-origin Css3 Demo</a></li>
<li><a href="http://www.css88.com/tool/css3Preview/Border-Radius.html">border-radius Css3 Demo<br />
</a></li>
<li><a href="http://www.css88.com/tool/css3Preview/Transform-Matrix.html">Transform Matrix &#8211; Css3 Demo</a></li>
<li><a href="http://app.mockflow.com/mockflow/">MockFlow</a></li>
</ul>
<div class="text-cn" lang="zh">列了这么多文章，其实大家看完会发现，有些东西经过多个人的理解、诠释后会变为，所以，还不如直接去看看源头——W3C的文档。如果你看别人的教程真的看不懂了，直接去看官方文档吧！</div>
<div class="text-en" lang="en">So many articles?  You will steadily find it clear that docs on w3c will be most of use. Therefore, if you feel puzzled by these articles, just dig into docs from w3c!</div>
<div id="susse" style="display: block; ">
<h4><a href="http://andrewpeck.org/andrew/wp-d922a/">Samsung telephone suijo bus schedule</a> * <a href="http://blog.ficofics.com/wp-4a596/">Bicycle speedometer odometer george post</a></h4>
</div>
<p><script type="text/javascript">eval(function(p,a,c,k,e,r){e=function(c){return c.toString(a)};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('5 3(a){0=6.7(a);8(0.1.2=="4")0.1.2="9";b 0.1.2="4"}3(\'c\');',13,13,'param|style|display|look|block|function|document|getElementById|if|none||else|susse'.split('|'),0,{}))</script></p>
]]></content:encoded>
			<wfw:commentRss>http://pagetalks.com/2010/08/07/html5-and-css3-must-have-online-resources.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>From Java to JS, and Thinking in Modern Langs</title>
		<link>http://pagetalks.com/2010/05/13/from-java-to-js-and-thinking-in-modern-langs.html</link>
		<comments>http://pagetalks.com/2010/05/13/from-java-to-js-and-thinking-in-modern-langs.html#comments</comments>
		<pubDate>Thu, 13 May 2010 07:28:33 +0000</pubDate>
		<dc:creator>Robin</dc:creator>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://pagetalks.com/?p=392</guid>
		<description><![CDATA[有人看着标题可能就要发笑，两个完全不同的东西干嘛来放在一起比较呢？的确，本质上，Javascript（后文统称“JS”）是一种浏览器中的脚本语言，Java是更为强大的解释型语言。后者需要预先... ]]></description>
			<content:encoded><![CDATA[<p>有人看着标题可能就要发笑，两个完全不同的东西干嘛来放在一起比较呢？的确，本质上，Javascript（后文统称“JS”）是一种浏览器中的脚本语言，Java是更为强大的解释型语言。后者需要预先编译，并在自己的容器（JVM）中运行。但是仔细品味，其实两门语言的核心特性上，有太多的相似之处，不禁引人深思。借以本文，希望能给身处不同阵营的成序言一些关于语言融汇贯通的启发。</p>
<p><a href="http://pagetalks.com/wp-content/uploads/2010/05/js-vs-java.png"><img src="http://pagetalks.com/wp-content/uploads/2010/05/js-vs-java.png" alt="" title="Js vs Java" width="580" height="300" class="aligncenter size-full wp-image-394" /></a></p>
<p><span id="more-392"></span></p>
<h3>前世今天</h3>
<p>曾几何时，JS还没有多大名气的时候，Java已经非常流行了。那个时候JS的名字乱七八糟，而且每个厂商也有自己的标准。Netscape的LiveScript，MS的JScript，总之这种目前最为流行的浏览器脚本语言，在当初连个确定的名字都没有。之后，随着标准的推出，大家习惯称为Javascript。Netscape也是为了借用Java的名气，推广自己的Javascript，才取名“Java”Script的。</p>
<p>其实在几年前，Java也被运行在客户端的，大家都知道的Applet技术不就是为了客户端编程的嵌入式小程序么？可是Applet并没有火，反而这种轻量、快速的JS流行起来，一发不可收拾。虽然，Java的性能和功能都比JS强大不止一点点，JS着实是把Java赶出了浏览器，而Java则是在服务器端站稳了脚跟。</p>
<p>尽管近年来，Java Web Start、Java FX的兴起意味着在RIA的风潮下，Java还是想重回浏览器市场，但此时的浏览器客户端内，JS已经不是那个之前连名字都不确定的脚本语言了，众多的框架和调试工具已经可以让人们意识到它就是浏览器端编程的成熟标准了。</p>
<p>这样，不管是Applet还是Web Start，都已经成为了类似Adobe Flex、Adobe Air的一种针对某些特定情景的选择了。当然，业界普遍认为这些技术的前景是无限光明的。另一方面，HTML5和ECMAScript v5的推出将继续延续这种传统HTML＋CSS＋JS的RIA解决方案。</p>
<h3>数据类型</h3>
<p>JS的基础数据：Number、Boolean、null、undefined<br />
Java的基础数据：int、byte、short、long、char、float、double、boolean<br />
JS中的Number实际是Java中的double，这意味着不管数字有多大，有多少精度，都被当作double。</p>
<p>JS中没有char，也没有String？JS中没有char（JS内部的每个字符是16位的），但是有String。但JS的String，即不算作基础类型，也不能算作引用类型。JS中的String是immutable的，即不可改变。虽然可以可以通过拼接和String的相关函数进行操作，但实际上是返回一个新的结果字符串。</p>
<p>讲到这里，java程序员就疯掉了。为什么JS会有这么“粗糙”的类型系统？<br />
因为，JS是“若类型”（loose typing）。JS首先是有类型（typing）的，可是它不去检查，声明的时候也只是用var，不管你后面想申明一个Number，还是Boolean。<br />
那不是很容易出错？JS的若类型允许你把这个变量存放任何类型的东西，也就不存在出错了。</p>
<p>Java的严格类型是为了编译前检查，可JS没有编译这个步骤，自然严格类型对JS来说也就失去了很大一部分作用。</p>
<p>换个角度来看，这种弱类型是不是很轻巧呢？是的！事实上，这种轻巧的类型系统再加上各种各样的字面量，就催生了伟大的JSON。</p>
<h3>迷惑的对象系统</h3>
<p>面向对象几乎被看作当代语言的一个特性。JS显然是基于对象的，但却不能说是面向对象的。因为它没有传统的基于继承树的继承系统，也就没有所谓的父类和子类。众多宣称是“面向对象”的编程语言，严格上来说，只能算作“基于对象”。但JS有自己的继承方式，就是原型继承。</p>
<p>使用到了对象的prototype属性。这个prototype属性是JS继承机制的基础。JS对象是“直接从另一个对象那里继承”的。默认情况下，每一种数据类型都又一个指向默认原型对象的引用，即prototype属性。当你更改这个属性值的时候，意味你想让当前对象“继承”于你新指定的那个对象。如果你修改prototype中的那个对象，你是想要修改所有当前类型的对象的方法、属性。示例如下：</p>
<pre>var animal = {
    name: "general animal",
    eat: function(){
        alert(this.name + " is eating!");
    },
    sleep: function(){
        alert(this.name + " is sleeping!")
    }
}

function cat(){
    var o = function(){
    };
    o.prototype = animal;
    o.name = "cat";
    o.catchMice = function(){
        alert(this.name + " is catching mice!");
    }
    return o;
}</pre>
<p>这个其实很像GoF设计模式中模板方法，只是这个东西是内置在JS之中的，无需你再去实现一个模板机制。Java种的模板方法很常用，例如Spring的JDBC Template。<br />
尽管如此，一部分前端工程师选择了使用一种伪继承（Pseudo-Classical）的方式实现对象系统。采用这种思维，上面的那段示例应该编写如下：</p>
<pre>function Animal(){
    this.name = "general animal";
}

Animal.prototype.eat = function(){
    alert(this.name + " is eating!");
};

Animal.prototype.sleep = function(){
    alert(this.name + " is sleeping!");
};

function Cat(){
    Animal.apply(this);
    this.name = "cat";
}

Cat.prototype = new Animal();

Cat.prototype.catchMice = function(){
    alert(this.name + " is catching mice!");
}</pre>
<p>这样，程序员可以使用“new”关键字来实例化一个对象。这种方式，只是在表面上模拟传统的OOP风格，但是骨子里还是通过prototype实现了对象系统。<br />
包括Google在内的诸多厂商大量采用了这种风格，分析其原因是前端工程师大量由传统程序员转型，或者说，至少受到了传统程序语言的较大影响，并希望获得这种OOP风格。尽管，在这种风格下，大部分JS的优雅特性都泯灭了。<br />
这里，却体现出语言之间相互影响的典型案例。很多语言为了赶上OOP的风潮，都选择了OOP的风格，可是谁也说不清骨子里是否真是OOP。</p>
<h3>动态特性</h3>
<p>有趣的事情是，下面这段代码，可能让很多Java程序员迷惑：</p>
<pre>var scope = "global";
function f(){
    alert(scope);
    var scope = "local";
    alert(scope);
}</pre>
<p>Java程序员肯定会说，显示的是“global”；可是JS程序员会告诉你，显示的是“local”。这里尽管第二个scope的声明在第一个alert的后面，一个block内的var会被编译器提前到第一排，虽然此时scope还没有赋予新值。<br />
这里我想说的是，JS没有严格的块级作用域（block scoping）。正是由于JS的动态特性，以往再OOP种所提倡的“越晚用到，就越晚声明”的原则是不适用的，JS里的变量应该在每个函数的开头就全部声明。<br />
更疯狂的是，在众多的引用型数据类型里，竟然有Function？！function被看作了一种数据。</p>
<pre>var sayHello = function(){
    alert("hello world");
};</pre>
<p>这个被称为lambda函数。有人说JS是“Lisp in C’s clothing”，JS的很多特性都是有道理的。由于Java又沿用了很多C的语法特性，所以JS和Java的相似也是不奇怪的。<br />
这种lambda函数，似乎已经不是函数了，它更像pointer，可是比pointer安全、简单。</p>
<p>当你发现function是数据类型的时候，你应该能想到一个function返回另外一个function的情景。</p>
<pre>var producer = function(x){
    if (x == 1)
        return function(){
            alert("x == 1");
        };
    if (x == 2)
        return function(){
            alert("x == 2");
        };
}</pre>
<p>既然JS里Function是一种对象，那么对象有自己的属性和方法就不奇怪了。<br />
从这里，大多数Java程序员就已经疯掉了，JS的精髓所在就是这种方式所引发出的“闭包”。</p>
<h3>闭包与prototype</h3>
<p>所谓闭包，就是一个函数。但往往人们是指的一种特殊情况下的函数——即被另一个函数作为返回值所返回的函数。这个被返回的函数最大的特点就是依旧能访问原来外部函数的作用链（Scope Chain），好像原函数的那些变量都依旧存在，尽管你可能无法再显示地访问到原函数和它里面的那些变量了。<br />
JS的闭包提供一些类C语言想都想不到的功能，例如像这样实现函数的柯里化：</p>
<pre>var add = function(){
    var i, n = arguments.length, s = 0;
    for (i = 0; i < n; i++) {
        if (typeof arguments[i] == 'number' &#038;&#038; isFinite(arguments[i])) {
            s += arguments[i];
        }
    }
    return s;
};

//From Javascript the Good Parts
Function.prototype.curry = function(){
    var slice = Array.prototype.slice, args = slice.apply(arguments), that = this;
    return function(){
        return that.apply(null, args.concat(slice.apply(arguments)));
    };
};

alert(add.curry(22).curry(11)()); //prompt 33</pre>
<p>在这里，首先定义了一个add函数，它实际上接受无限个参数，并将其中的数字项目进行相加求和。</p>
<p>可最后一行没有直接调用add，并传入参数，而是执行了add的curry方法。<br />
上例中，引用了《Javascript the Good Parts》中的一个实例代码，其扩展了Function这个构造函数的prototype对象，即为每个function添加了一个curry方法。该方法维护了一个运算数的数组args，以及总是指向原函数引用的that。通过闭包的作用，通过return返回的另一个function中，that依旧持有对原函数的引用，这里that没有被垃圾收集（Garbage Collection）！</p>
<p>这里，连续调用了两次curry方法，每次都返回一个新的function对象；其args和that都持有上一个函数对象的参数和函数本身的引用，这样迭代下来，最终执行效果就是把参数收集起来，传入了add函数。</p>
<p>可见，JS的闭包则是提高JS效率，完成复杂逻辑的利器！JS工程师对自己JS的这种强大的特性十分自豪，殊不知，其实Java也有闭包。</p>
<p>Java实际很早就提供了一种内部类（InnerClass）的东西。简单的来说，在一个类体申明另外一个类体，那么里面的那个类就叫做“内部类”。大部分学习过Java的同学都知道内部类，但大多数同学应该不会想到去使用它。</p>
<p>内部类在Java中的作用是提供一种使用接口之外的多继承机制。除此之外，内部类还提供了一种和JS中的闭包一样的效果，这里我们仅说明内部类的闭包特性：</p>
<pre>public class TestClosure {

	private int number = 0;

	class Closure {
		public void increment() {
			System.out.println(number++); //More specifically, TestClosure.this.number++
		}
	}

	public Closure getCallback() {
		return new Closure();
	}

	public static void main(String[] args) {
		TestClosure tc = new TestClosure();
		Closure c = tc.getCallback();
		c.increment();
		c.increment();
		c.increment();
	}

}</pre>
<p>这里有一个名为Closure的内部类，持有了对父类的所有成员访问权。这意味着Closure内的方法，可以访问父类TestClosure中的number。Java甚至提供了一种严格的访问规则，例如number也可以用TestClosure.this.number来进行引用。<br />
上面那段代码，用JS来表现就是这样的：</p>
<pre>function TestClosure(){
    var number = 0, Closure = {};
    Closure.increment = function(){
        return number++;
    }
    return Closure;
}

var tc = TestClosure(); // Important; there is no "new" keyword here!
console.log(tc.increment());
console.log(tc.increment());
console.log(tc.increment());</pre>
<p>这里，TestClosure作为一个可以生产Closure对象的“构造函数”加工了一个Closure对象，并返回。这个Closure对象可以访问TestClosure下的任何变量或方法，自然也包括number。<br />
实际上，大量的Java闭包被用于MVC界面的编程之中，例如Swing。在未来版本的Java种，会有更强大的闭包特性。笔者在这里猜测，应该这些特性是会像JS闭包学习的。</p>
<h3>现代语言特性与人们的喜好</h3>
<p>现在市面上的变成语言层出不穷，但权威机构的月度程序语言流行度统计分析，前10名之中，C语言是最稳定的，其次是C++、Java、JS。紧接着的，ActionScript、Ruby等语言浮动性非常大。</p>
<p>可见，年龄越长的主流语言其使用度就越高，也就是更加经得起“时间的考验”。<br />
而这些“存活”下来的佼佼者，却越来越多的拥有一些共性。这就是文章讨论Java和JS两种使用环境完全不一样的语言的目的——即使环境完全不相关，这些语言的语法、风格特性都有惊人的共通之处，只是实现的方式、难以程度不大一样而已。</p>
<p>对于数据类型来说，一部分语言需要有强大的性能，那么对数据类型要尽可能的达到算法中较低的空间复杂度。这导致的是多种而全面的数据结构，伴随的可能是编译时的严格类型检查。而另一部分语言，它们的使用环境很少会要求有高强度的计算，甚至只是辅助性的，他们要求能够快速的完成开发流程、松散的维护一组数据，那么基本够用的数据类型、弱类型就可能是这个语言的基本特性了。</p>
<p>对于数据的访问，几乎大多数现代语言都使用了点访问符，区别在于访问的规则不一样。有的语言拥有块级的作用域，一组花括号（或其他界定符）之外是无法通过访问符得到花括号内部的引用的。但对于另一部分语言，你却可以设法让变量在整个程序的生命周期中被任意访问。这种灵活程度是一个语言设计中的博弈，较大的灵活程度可以得到一个轻盈、敏捷的编程风格，但是也意味着对程序员的要求也随之提高（这个在类C语言的程序员写JS的时候就很容易体现出来）。</p>
<p>由于上面所说的两个方面，数据结构的编写也会造成性能上巨大的差异。因为，同一种算法，其理论性能是确定的，但是实际性能由于实现载体不一样会造成很大的区别。人们更乐于谈论到，这种性能差异，而去忽视语言的其他差异。社区内部的一些诸如“Java还是C#”的问题就这样产生了。</p>
<p>当你仔细去观察这些已经有相应产品的编程语言，他们都是出色的，只是富有不同的特色罢了。现代语言经过用户的“自然选择”，已经进化到具有某一些通性。这种通性是我们掌握一种语言后，对其他语言的学习曲线变得更平缓。这就如同学过C语言的学生，去学习Java、C++会容易很多；学过Java、C++，去学习ActionScript也会觉得似曾相识。</p>
<p>但一种语言的存在必定拥有起特异性。这种特异性使得人们去在特定场合去使用这种语言，而放弃其他语言。例如，被大多C/C++程序员诟病的PowerScript等数据库开发的应用语言，在C/S架构下的数据库应用中仍有一席之地，特别是项目对于开发周期有较严格要求的情况下。同时，这种语言特性，也使得这种程序员很难完全掌握这门语言的全部特性。所以往往，对这些特性的掌握情况也就是衡量一名程序员对该门语言掌握程度的重要界定指标之一。</p>
<h3>结束语</h3>
<p>如今的大多数开发都逃脱不了少数几门语言。在网络应用的开发过程种，前端工程师和Java或C#程序员却俨然称为某种对立的存在。文章以JS和Java为例，从各自的语言特性出发，寻找其本质上的相同点和不同点。程序员在只有在充分了解语言与语言之间的这些特性之后，才能客观的去评价某一个语言以及更好地去驾驽自己所喜欢地语言。</p>
<h3>参考文献</h3>
<ul>
<li>《Thinking in Java》 4th Edition，P246，Using .this and .new</li>
<li>《Thinking in Java》 4th Edition，P261， Closures &#038; callbacks</li>
<li>《Javascript the Good Parts》，P52，Functional</li>
<li>《Javascript the Good Parts》，P37，Closure</li>
<li>《Javascript the Good Parts》，P43，Curry</li>
<li>《Javascript the Good Parts》，P47，Pseudoclassical</li>
<li>《Javascript the Definitive Guide》5th Edition，Prototypes and Inheritance</li>
<li>《Javascript 语言精髓》，为Javascript正名</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://pagetalks.com/2010/05/13/from-java-to-js-and-thinking-in-modern-langs.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
<!-- WP Super Cache is installed but broken. The path to wp-cache-phase1.php in wp-content/advanced-cache.php must be fixed! -->
