<?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>Shining Ray &#187; javascript</title>
	<atom:link href="http://shiningray.cn/tag/javascript/feed" rel="self" type="application/rss+xml" />
	<link>http://shiningray.cn</link>
	<description>一缕阳光</description>
	<lastBuildDate>Mon, 21 Jun 2010 07:11:46 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Flash恐惧症</title>
		<link>http://shiningray.cn/flash-kong-ju-zheng.html</link>
		<comments>http://shiningray.cn/flash-kong-ju-zheng.html#comments</comments>
		<pubDate>Tue, 12 May 2009 15:17:32 +0000</pubDate>
		<dc:creator>ShiningRay</dc:creator>
				<category><![CDATA[感想]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://shiningray.cn/?p=534</guid>
		<description><![CDATA[我发现我在开发上对Flash有恐惧症。Flash/Flex现在作为不可多的的RIA应用的开发平台，有着许多优势。但是我总是排斥使用Flash/Flex。虽然我在Flash5/MX的时代也做过些动画，那个时候还没有成形的ActionScript，还要用telltarget，Flash也并非开放标准的。当时能用Flash做RIA应用是非常了不起的，因为那个时候没有Flex这种套件框架，也没有适用的IDE，虽然我也很想尝试用Flash做一个社区，但是后来失败了。再后来我就再也没有碰过Flash。 然而我倒并不排斥Flash应用，包括AIR，我觉得很多优秀的应用用起来非常不错。究竟是什么令我在开发上如此排斥Flash呢？我仔细分析了一下，想到以下问题： 当时Flash不是开放的。当时Flash只能使用Macromedia Flash来制作，几乎没有其他够用的工具（我记得有个Swiss）。随着Flash的标准开放，现在情况改变了，有不少的第三方工具。不过因为当时Flash的不开放，让我错过了。 当时Flash在其他平台上的支持非常差。当然，现在已经好很多了。不过即使现在，Flash在Linux下的实现还是有些问题，CPU占用率非常高。Flash曾经是没有Linux开发工具的，现在也没有多少，当然后来Adobe推出了Flex for Linux，结果最近看一则新闻说因为Flex for Linux用得太少，所以Adobe要停止维护Flex for Linux了。这样看来，如果要跨平台，无论是开发时还是运行时，Flash比起html+js还是差一些。 过去Flash主要作为动画来使用，文件非常庞大，那时候也很少使用动态加载。现在用Flex做客户端应用非常方便，但是Flex相对于HTML来说，还是过于庞大。同样的，Flex开发套件也太庞大了，尤其是如果把Java也算进去的话。而我使用HTML/JS可以需要用文本编辑器和一个浏览器。 Flash并非事实上的标准，网站可以不使用Flash，却不能不使用HTML。现在主流的浏览器，都支持JavaScript，却不一定有Flash。 其实我认为我害怕使用Flash最关键的是在于我在开发上的思维转变，过去我很喜欢很酷很眩的特效，觉得有了这些东西，程序肯定就很牛B。但后来我意识到这种想法是幼稚的，这些特效都是浮云，要先抓住问题本质，要去掉这些复杂的外观，直接呈现内容。而如果不需要特效，Flash比起html + js就没有明显的优势了，而我更喜欢html+js的简洁快速。 ～～～～～～～～～～～～～～～～～～～～～～～～ Flash作为RIA，目前是如日中天，AIR也是相当流行，但实际上是杀机四伏。 微软也推出了Silverlight与Flash直面竞争。 由于JavaScript的发展，各种JavaScript特效框架的出现，过去需要使用Flash才能实现的很多特效现在可以直接使用js实现，直接消除了一部分对于Flash的需求。 JavaScript的一些工具包，也能实现很多窗体控件，使开发简单，如extjs dojo jquery-ui等，除了IDE上支持不够。这对于Flash的Flex框架也是一种威胁。 Adobe的AIR还比较聪明，没有完全把赌注押在Flash上，兼容了html+js的开发，Mozilla已经推出了Prism来竞争，而Prism的最强大的地方莫过于既有的Web应用可以直接运行在Prism上。 其实对Flash最大的威胁来自于HTML5： 过去JavaScript一直有跨域的问题，而且对长连接支持有些问题，HTML5中的Websocket为Ajax提供了更强大的通讯能力，Flash的优势又进一步减弱了。 利用HTML5中canvas，JavaScript就可以进行高速的2D图形绘制，这也是抢了Flash的市场。 HTML5支持离线存储Dom Stoarge，Flash的又一个特性被直接支持 以上这些标准组件在某些浏览器中已经被支持了，Flash对于浏览器毕竟只是第三方支持，但是HTML5的这些东西将来可都是直接在浏览器核心中的。 我想还有一个不得不提的就是3D了，Flash现在也可以实现3D，但是Flash的3D效果只能算普通，效率也比较低，做复杂的3D应用是不适合的。在这方面，Java Applet却可以调用OpenGL，效率就不在同一个层次上了。而最近Google为Chrome推出的o3d，也是Flash强劲的对手。 所以，我觉得Flash未来的好日子有限。]]></description>
			<content:encoded><![CDATA[<p>我发现我在开发上对Flash有恐惧症。Flash/Flex现在作为不可多的的RIA应用的开发平台，有着许多优势。但是我总是排斥使用Flash/Flex。虽然我在Flash5/MX的时代也做过些动画，那个时候还没有成形的ActionScript，还要用telltarget，Flash也并非开放标准的。当时能用Flash做RIA应用是非常了不起的，因为那个时候没有Flex这种套件框架，也没有适用的IDE，虽然我也很想尝试用Flash做一个社区，但是后来失败了。再后来我就再也没有碰过Flash。</p>
<p>然而我倒并不排斥Flash应用，包括AIR，我觉得很多优秀的应用用起来非常不错。究竟是什么令我在开发上如此排斥Flash呢？我仔细分析了一下，想到以下问题：</p>
<p>当时Flash不是开放的。当时Flash只能使用Macromedia Flash来制作，几乎没有其他够用的工具（我记得有个Swiss）。随着Flash的标准开放，现在情况改变了，有不少的第三方工具。不过因为当时Flash的不开放，让我错过了。</p>
<p>当时Flash在其他平台上的支持非常差。当然，现在已经好很多了。不过即使现在，Flash在Linux下的实现还是有些问题，CPU占用率非常高。Flash曾经是没有Linux开发工具的，现在也没有多少，当然后来Adobe推出了Flex for Linux，结果最近看一则新闻说因为Flex for Linux用得太少，所以Adobe要停止维护Flex for Linux了。这样看来，如果要跨平台，无论是开发时还是运行时，Flash比起html+js还是差一些。</p>
<p>过去Flash主要作为动画来使用，文件非常庞大，那时候也很少使用动态加载。现在用Flex做客户端应用非常方便，但是Flex相对于HTML来说，还是过于庞大。同样的，Flex开发套件也太庞大了，尤其是如果把Java也算进去的话。而我使用HTML/JS可以需要用文本编辑器和一个浏览器。</p>
<p>Flash并非事实上的标准，网站可以不使用Flash，却不能不使用HTML。现在主流的浏览器，都支持JavaScript，却不一定有Flash。</p>
<p>其实我认为我害怕使用Flash最关键的是在于我在开发上的思维转变，过去我很喜欢很酷很眩的特效，觉得有了这些东西，程序肯定就很牛B。但后来我意识到这种想法是幼稚的，这些特效都是浮云，要先抓住问题本质，要去掉这些复杂的外观，直接呈现内容。而如果不需要特效，Flash比起html + js就没有明显的优势了，而我更喜欢html+js的简洁快速。</p>
<p>～～～～～～～～～～～～～～～～～～～～～～～～</p>
<p>Flash作为RIA，目前是如日中天，AIR也是相当流行，但实际上是杀机四伏。</p>
<p>微软也推出了Silverlight与Flash直面竞争。</p>
<p>由于JavaScript的发展，各种JavaScript特效框架的出现，过去需要使用Flash才能实现的很多特效现在可以直接使用js实现，直接消除了一部分对于Flash的需求。</p>
<p>JavaScript的一些工具包，也能实现很多窗体控件，使开发简单，如extjs dojo jquery-ui等，除了IDE上支持不够。这对于Flash的Flex框架也是一种威胁。</p>
<p>Adobe的AIR还比较聪明，没有完全把赌注押在Flash上，兼容了html+js的开发，Mozilla已经推出了Prism来竞争，而Prism的最强大的地方莫过于既有的Web应用可以直接运行在Prism上。</p>
<p>其实对Flash最大的威胁来自于HTML5：</p>
<ul>
<li>过去JavaScript一直有跨域的问题，而且对长连接支持有些问题，HTML5中的Websocket为Ajax提供了更强大的通讯能力，Flash的优势又进一步减弱了。</li>
<li>利用HTML5中canvas，JavaScript就可以进行高速的2D图形绘制，这也是抢了Flash的市场。</li>
<li>HTML5支持离线存储Dom Stoarge，Flash的又一个特性被直接支持</li>
</ul>
<p>以上这些标准组件在某些浏览器中已经被支持了，Flash对于浏览器毕竟只是第三方支持，但是HTML5的这些东西将来可都是直接在浏览器核心中的。</p>
<p>我想还有一个不得不提的就是3D了，Flash现在也可以实现3D，但是Flash的3D效果只能算普通，效率也比较低，做复杂的3D应用是不适合的。在这方面，Java Applet却可以调用OpenGL，效率就不在同一个层次上了。而最近Google为Chrome推出的o3d，也是Flash强劲的对手。</p>
<p>所以，我觉得Flash未来的好日子有限。</p>
]]></content:encoded>
			<wfw:commentRss>http://shiningray.cn/flash-kong-ju-zheng.html/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>在SpiderMonkey中产生可调用的对象</title>
		<link>http://shiningray.cn/make-an-object-callable-in-spidermonkey.html</link>
		<comments>http://shiningray.cn/make-an-object-callable-in-spidermonkey.html#comments</comments>
		<pubDate>Fri, 03 Oct 2008 09:25:27 +0000</pubDate>
		<dc:creator>ShiningRay</dc:creator>
				<category><![CDATA[备忘]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[spidermonkey]]></category>

		<guid isPermaLink="false">http://shiningray.cn/?p=280</guid>
		<description><![CDATA[我应该对标题做一个更详细的解释：用C语言在SpiderMonkey中产生一个在JavaScript中可以当成函数被调用的对象，换句话说，就是一个非Function的对象，在JavaScript中可以被当成Function进行调用。例如，我有一个Hash对象，当我在var h = new Hash()之后，可以直接调用h(key)，h并非一个函数对象，却可以以这种函数调用的方式来获取键key对应的值。 首先，必须要在创建这个Hash类的结构时，将JSClass中的&#8221;call&#8221;字段设置为相应的函数，如下： static JSBool call_hash(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ /* here the obj refers to the global object, not the callee itself */ *rval = JSVAL_NULL; return JS_TRUE; } static JSClass hash_class = { "Hash", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, finalize_hash, 0, 0, [...]]]></description>
			<content:encoded><![CDATA[<p>我应该对标题做一个更详细的解释：用C语言在SpiderMonkey中产生一个在JavaScript中可以当成函数被调用的对象，换句话说，就是一个非Function的对象，在JavaScript中可以被当成Function进行调用。例如，我有一个Hash对象，当我在<code>var h = new Hash()</code>之后，可以直接调用<code>h(key)</code>，h并非一个函数对象，却可以以这种函数调用的方式来获取键key对应的值。</p>
<p>首先，必须要在创建这个Hash类的结构时，将<a href="http://developer.mozilla.org/En/SpiderMonkey/JSAPI_Reference/JSClass">JSClass</a>中的&#8221;call&#8221;字段设置为相应的函数，如下：</p>
<pre><code>static
JSBool call_hash(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
jsval *rval){
/* here the obj refers to the global object, not the callee itself */
*rval = JSVAL_NULL;
return JS_TRUE;
}

static
JSClass hash_class = {
"Hash", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
finalize_hash,
0,
0,
call_hash,
0,
0,
0,
0,
0
};
</code></pre>
<p>现在，这里有一个很关键的问题便是如何在SpiderMonkey调用call_hash函数的时候，能够让call_hash函数知道被调用的对象（callee）是谁。然而，Mozilla的官方文档并没有对此作出任何解释。于是我在邮件列表中问了这个问题，有人给出了一个很特别的技巧——<strong>引擎调用call函数的时候，argv[-2]便是被调用者本身。</strong></p>
<hr/>
<p>在把玩了Spidermonkey一段时间之后，我还是打算放弃spidermonkey，虽然这是一个很成熟很强大的脚本引擎，但是他的API还是有些混乱的，从本文的这个问题的解决方案就可以看得出来。</p>
]]></content:encoded>
			<wfw:commentRss>http://shiningray.cn/make-an-object-callable-in-spidermonkey.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>函数式JavaScript编程指南</title>
		<link>http://shiningray.cn/functional_javascript_programming.html</link>
		<comments>http://shiningray.cn/functional_javascript_programming.html#comments</comments>
		<pubDate>Wed, 02 Jan 2008 14:15:46 +0000</pubDate>
		<dc:creator>ShiningRay</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[函数式编程]]></category>

		<guid isPermaLink="false">http://shiningray.cn/functional_javascript_programming.html</guid>
		<description><![CDATA[JavaScript Programming 原文地址：http://www.pfeiffer-mediation.de/remast/javascript.php&#160;翻译：ShiningRay 函数式JavaScript编程指南 简介 你是否知道JavaScript其实也是一个函数式编程语言呢？本指南将教你如何利用JavaScript的函数式特性。 要求：你应当已经对JavaScript和DOM有了一个基本的了解。 写这篇指南的目的是因为关于JavaScript编程的资料太多了但是极少的资料提到了JavaScript的函数式特性。在本指南中，我只会讲解这些基本知识而不会深入其它的函数式语言或这是Lambda算子。 你可以点击所有的例子然后你所看到的代码就会被执行，这样就可以令指南变得具有交互性。你也可以使用这个沙箱来尝试。 第一课 —— 匿名函数 我们将首先介绍匿名函数。一个匿名函数就是一个没有名字的函数。你可以认为他们是一次性函数。当你只需要用一次某个函数式，他们就特别有用。通过使用匿名函数，没有必要把函数一直放在内存中，所以使用匿名函数更加有效率。 例Example: 下面两个函数处理同样的事情，而average在给z赋值结束之后一直保留——但匿名函数则不会。 function average(x,y) { return (x+y)/2; } var z = average(1,3); alert(z); var z = function(x,y) { return (x+y)/2; } (1,3); alert(z); 这很自然得引出了我们下面的一节课函数作为值。 第二课 &#8211; 函数作为值 事实上，我们一般在JavaScript中声明函数的方式可以看作是一个简化了的语法（也就是语法糖，syntactic sugar）。 例： 下面两个表达式其实完全一样。所以左边的表达式仅仅是右边的简写。 function average(x,y) { return (x+y)/2; } alert( average(1,3) ); var average [...]]]></description>
			<content:encoded><![CDATA[<div id="breadcrumbs">JavaScript Programming 原文地址：<a href="http://www.pfeiffer-mediation.de/remast/javascript.php">http://www.pfeiffer-mediation.de/remast/javascript.php</a>&nbsp;翻译：<a href="http://shiningray.cn">ShiningRay</a></div>
<h1>函数式JavaScript编程指南</h1>
<h2>简介</h2>
<p>你是否知道JavaScript其实也是一个函数式编程语言呢？本指南将教你如何利用JavaScript的函数式特性。</p>
<p><strong>要求：</strong>你应当已经对JavaScript和DOM有了一个基本的了解。</p>
<p>写这篇指南的目的是因为关于JavaScript编程的资料太多了但是极少的资料提到了JavaScript的函数式特性。在本指南中，我只会讲解这些基本知识而不会深入其它的函数式语言或这是Lambda算子。
      </p>
<p>你可以点击所有的例子然后你所看到的代码就会被执行，这样就可以令指南变得具有交互性。你也可以使用这个<a href="#playground">沙箱</a>来尝试。</p>
<h2>第一课 —— 匿名函数</h2>
<p>我们将首先介绍<em>匿名函数</em>。一个匿名函数就是一个没有名字的函数。<br/>你可以认为他们是一次性函数。当你只需要用一次某个函数式，他们就特别有用。通过使用匿名函数，没有必要把函数一直放在内存中，所以使用匿名函数更加有效率。</p>
<p class="example">例Example:</p>
<p>下面两个函数处理同样的事情，而<kbd>average</kbd>在给<kbd>z</kbd>赋值结束之后一直保留——但匿名函数则不会。 </p>
<pre>function average(x,y) {
  return (x+y)/2;
}
var z = average(1,3);
alert(z);
</pre>
<pre>var z = function(x,y) {
      return (x+y)/2;
    } (1,3);
alert(z);
</pre>
<p>这很自然得引出了我们下面的一节课<strong>函数作为值</strong>。</p>
<h2>第二课 &#8211; 函数作为值</h2>
<p>事实上，我们一般在JavaScript中声明函数的方式可以看作是一个简化了的语法（也就是<em>语法糖</em>，<em>syntactic sugar</em>）。</p>
<p class="example">例：</p>
<p>下面两个表达式其实完全一样。所以左边的表达式仅仅是右边的简写。</p>
<pre>function average(x,y) {
  return (x+y)/2;
}
alert( average(1,3) );
</pre>
<pre>var average = function(x,y) {
  return (x+y)/2;
}
alert( average(1,3) );
</pre>
<p>从这里可以得出一个结论，函数是一个值就像字符串、数字或数组一样。这还出现几个问题：</p>
<dl>
<dt>我是否可以把函数作为参数传递？</dt>
<dd>可以，见下面的例子。</dd>
<dt>是否可以实时生成函数？</dt>
<dd>当然了，这是一个高级的主题，它可以通过<kbd>eval</kbd>函数来完成。<b>小提示：</b>看看本页面的源代码。</dd>
</dl>
<p class="example">例：</p>
<p>这个例子演示了如何把函数作为参数传递。</p>
<pre>var applyFun = function (f,x,y) { return f(x,y); };

var add = function(x,y) {
  return x+y;
};

alert( applyFun(add,3,4) ); // 7
</pre>
<h2>第三课 &#8211; 两种方式调用函数</h2>
<p>在JavaScript中，有两种调用函数的方式。一般的方式是把参数放在括号中，如<kbd>alert(42)</kbd>。另一种方式是同时把函数和参数都放在括号中，如<kbd>(alert)(42)</kbd>。</p>
<p class="example">例：</p>
<pre>alert(42);
</pre>
<pre>(alert) (42);
</pre>
<pre>(function(x) { alert(x-13); }) (55);
</pre>
<p><strong>为什么函数两边的括号很重要：</strong>如果你写了括号，那么在括号中的代码就会被先计算。在计算之后，括号所在的地方就会有一个值。这个值可能是一个字符串、一个数字或一个函数。</p>
<h2>第四课 &#8211; “短路”条件调用</h2>
<p>现在我们将学习如何使用“短路”条件调用。使用这个方法可以缩短源代码同时代码也变得更加可读。</p>
<p class="example">例：</p>
<p>这个语法并不是用在左表达式上，而是用在右表达式上。</p>
<pre onclick="alert((false &amp;&amp; 4) || (true &amp;&amp; 2));">var f = false; var t = true;var z;
if(f)
  z = 4;
else if(t)
  z = 2;
alert(z);
</pre>
<pre onclick="alert((false &amp;&amp; 4) || (true &amp;&amp; 2));">var f = false; var t = true;
var z = (f &amp;&amp; 4) || (t &amp;&amp; 2);
alert(z);
</pre>
<h2>第五课 &#8211; 它好在哪里</h2>
<p>OK，现在我们已经学习了一些函数式JavaScript的内容。那么它好在哪里？函数式JavaScript编程之所以很重要有三条主要的理由：</p>
<ol>
<li>它有助于写出模块化和可服用的代码。</li>
<li>它对事件处理程序非常有效。</li>
<li>它很有趣！</li>
</ol>
<p>在下面的篇幅中，我会给出更多关于前两条理由的信息</p>
<h3>1. 模块化和可复用的代码</h3>
<p>现在你已经知道如何将函数作为值使用，那么你也应该试试！一个很好的例子是数组内建的<kbd>sort</kbd>方法。预定义的<kbd>sort()</kbd>把所有的对象转换成字符串并把他们按照词语的顺序排序。但如果我们有用户自定义的对象或者数字那么它就不是很有用了。于是这个函数可以让你给他一个进行比较的函数作为参数，如<kbd>sort(compareFunction)</kbd>。这个方法让我们甚至不用接触实际的<kbd>sort</kbd>方法。</p>
<p class="example">例:</p>
<pre>var myarray = new Array(6,7,9,1,-1);
var sortAsc = function(x,y) { return x-y; };
var sortDesc = function(x,y) { return y-x; };
myarray.sort(sortDesc);
alert(myarray);

myarray.sort(sortAsc);
alert(myarray);
</pre>
<h3>2. 事件处理程序</h3>
<p>对事件处理程序使用函数式编程也许是最直观的函数作为值得应用了。既然这样我们马上就演示一个例子。</p>
<p class="example">简单的例子：;ie</p>
<p>现在有一个Button类，带一个自定义的onclick行为。</p>
<pre onclick="alert('Press on the button on the right!\n Nothing happening here.');">function Button(clickFunction) {
	this.button = document.createElement("button");
	this.button.appendChild(document.createTextNode("Press me!"));
	this.button.onclick = clickFunction;
}
var bt = new Button(function() { alert("42"); });
</pre>
<div id="buttonClassTest">
      <script type="text/javascript">
//<![CDATA[
function Button(clickFunction) {
	this.button = document.createElement("button");
	this.button.appendChild(document.createTextNode("Press me!"));
	this.button.onclick = clickFunction;
}
var bt = new Button(function() { alert("42"); });
document.getElementById("buttonClassTest").appendChild(bt.button);
//]]&gt;
</script></p>
<p><strong>练习：</strong> 为什么我们要把<kbd>alert</kbd>包裹在一个匿名函数中？</p>
<p class="example">高级例子：</p>
<p>现在我们想改进我们的Button类。每一个按钮都被分配了一个值当按钮被点击时显示该值。首先我们调整我们的类：</p>
<pre onclick="alert('Nothing happening here.');">function Button(value) {
	this.value = value;
	this.button = document.createElement("button");
	this.button.appendChild(document.createTextNode("test"));
}
</pre>
<p>下面你也许要尝试写下面的代码：</p>
<pre onclick="alert('Nothing happening here.');">
this.button.onclick = function() { alert(this.value); };
</pre>
<p>如果你执行它你就会发现提示框中间是空的。为什么会这样呢？其实原因在于JavaScript的可见性规则。当onclick函数被执行时<kbd>this</kbd>指向的是按钮的DOM节点而非自定义的按钮对象。</p>
<p><strong>我们如何解决这个问题？</strong> 使用函数式编程：</p>
<pre onclick="alert('Nothing happening here.');">this.button.onclick = (function(v) {
			return function() { alert(v); };
			}) (this.value);
</pre>
<p>这种情况下执行该匿名函数会将<kbd>v</kbd>绑定到<kbd>this.value</kbd>上。</p>
<h2><a title="link to top of page" name="playground"></a>沙箱</h2>
<form style="PADDING-RIGHT: 3px; PADDING-LEFT: 3px; PADDING-BOTTOM: 3px; COLOR: white; PADDING-TOP: 3px; BACKGROUND-COLOR: #53626f" onsubmit="return false;" action="#">
<p>在此处输入你的代码，并按下<em>计算</em>.
<p>
	  <textarea id="fun" style="WIDTH: 99.3%; HEIGHT: 100px" name="user_eingabe">alert(42);</textarea> </p>
<input onclick="doEvaluate();" type="button" value="计算" />
	</form>
<h2>更多信息</h2>
<p>下面是关于函数式JavaScript编程的一些有趣的链接：</p>
<ul>
<li><a href="http://w3future.com/html/stories/callbacks.xml">w3future.com</a> &#8211; 针对事件处理函数和回调函数的函数式编程 </li>
<li><a href="http://www.svendtofte.com/code/practical_functional_js/">svendtofte.com</a> &#8211; 实用的（&amp; 函数式的）JavaScript代码片断 </li>
<li><a href="http://www.svendtofte.com/code/usefull_prototypes/">svendtofte.com</a> &#8211; 极好的JavaScript库（包括<kbd>map</kbd>, <kbd>fold</kbd>, &#8230;） </li>
<li><a href="http://blog.codingforums.com/index.php/main/blogentry/pure_functional_programming_in_javascript/">CodingForums</a> &#8211; 关于使用Lambda算子的函数式JavaScript的一篇更加理论性的文章</li>
<li><a href="http://ling.ucsd.edu/~barker/Lambda/">Lambda tutorial</a> &#8211; 关于在JavaScript中编码Lambda算子的教程 </li>
<li><a href="http://www.crockford.com/javascript/little.html">The Little JavaScripter</a> &#8211; 关于 <a href="http://www.schemers.org/">Scheme</a> 和 JavaScript 之间的比较</li>
</ul>
<h2>展望</h2>
<p>本节给大家展示一下JavaScript的未来。一个非常振奋人心的JavaScript特性——<em>E4X</em>，一个JavaScript中直接的XML支持。</p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/E4X">Wikipedia on E4X</a> &#8211; 关于 E4X 的很好的介绍</li>
<li><a href="http://developer.mozilla.org/presentations/xtech2005/e4x/">Mozilla E4X</a> &#8211; Brandon Eich （Mozilla首席架构师）关于E4X的演示 </li>
</ul>
</div>
]]></content:encoded>
			<wfw:commentRss>http://shiningray.cn/functional_javascript_programming.html/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>超酷的JavaScript小游戏</title>
		<link>http://shiningray.cn/chao-ku-de-javascript-xiao-you-xi.html</link>
		<comments>http://shiningray.cn/chao-ku-de-javascript-xiao-you-xi.html#comments</comments>
		<pubDate>Fri, 16 Nov 2007 10:34:07 +0000</pubDate>
		<dc:creator>ShiningRay</dc:creator>
				<category><![CDATA[日记]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[prototypejs]]></category>
		<category><![CDATA[游戏]]></category>

		<guid isPermaLink="false">http://shiningray.cn/2007/11/16/chao-ku-de-javascript-xiao-you-xi/</guid>
		<description><![CDATA[http://wholewheatweb.com 乍一看感觉像Flash做的，其实他只用了200行JavaScript代码！ 当然，该游戏还是基于 Prototype 1.6 以及Scriptaculous 1.8 看看DIGG上的评论]]></description>
			<content:encoded><![CDATA[<p><a href="http://wholewheatweb.com/">http://wholewheatweb.com</a></p>
<p>乍一看感觉像Flash做的，其实他只用了200行JavaScript代码！</p>
<p>当然，该游戏还是基于 <a href="http://www.prototypejs.org/">Prototype 1.6</a> 以及<a href="http://script.aculo.us/">Scriptaculous 1.8</a></p>
<p><a href="http://digg.com/programming/I_Can_t_Belive_it_s_Not_Flash_Addictive_game_in_under_200_lines_of_JS">看看DIGG上的评论</a></p>
]]></content:encoded>
			<wfw:commentRss>http://shiningray.cn/chao-ku-de-javascript-xiao-you-xi.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>JavaScript高级程序设计</title>
		<link>http://shiningray.cn/professional-javascript-for-web-developers.html</link>
		<comments>http://shiningray.cn/professional-javascript-for-web-developers.html#comments</comments>
		<pubDate>Fri, 08 Sep 2006 15:14:23 +0000</pubDate>
		<dc:creator>ShiningRay</dc:creator>
				<category><![CDATA[介绍]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[书籍]]></category>

		<guid isPermaLink="false">http://shiningray.cn/professional-javascript-for-web-developers.html</guid>
		<description><![CDATA[基本信息 【英文名】Professional JavaScript for Web Developers 【作者】Nicholas C. Zakas 【译者】曹力 张欣 【ISBN】7115152098 【出版时间】2006-9-15 【页码】670 【原出版社】Wrox 【简介】本书从最早期Netscape浏览器中的Javascript开始讲起，直到当前它对XML和Web服务的具体支持，展示了如何充分利用这种功能强大的语言开发自己的应用程序，以解决当今Web开发者面对的商业问题。 活动主页 译者序 亲爱的读者： 当您从书架上拿出这本书的时候，我想您肯定对Ajax技术有着浓厚的兴趣，而本书也正是您的正确选择。本书的作者Nicholas C. Zakas用通俗易懂的语言，将JavaScript的诞生、现在的状况、未来的发展和与其紧密相关的各种技术一一详尽地叙述出来，刚学JavaScript的朋友，可以按部就班成为高手，而已经是高手的朋友，则可以将本书作为参考手册。 第1章讲述了JavaScript的起源，给大家一个关于JavaScript正确的认知。 第2~5章详细介绍了JavaScript语言本身，揭示了一些JavaScript不为认知的语言特点。 第6~9章介绍了JavaScript和浏览器进行交互的一些基础知识和一些进阶知识，如DOM的基础、正则表达式。 第10~13章介绍了一些更加高级的JavaScript技巧，这些技巧可以构建良好的客户端逻辑，包括表格排序、拖动等。 第14章关于错误处理的内容，既有如何编程处理JavaScript错误，也包含了如何调试JavaScript的方法，而调试一直是JavaScript的弱项。 第16、17章讲述了利用JavaScript进行客户端到服务器的同学，不仅仅介绍了现在的Ajax技术的基础XML HTTP Request，还介绍了曾经出现过的一些方法。第17章更明确的介绍了如何调用Web服务。 第19章，介绍了如何考虑生产环境中JavaScript所需要注意的一些事情，如安全性、性能等。 第20章，展望了JavaScript未来的发展。 本书除了介绍了JavaScript的各个方面外，更难得的是，作者更涵盖了现今各个流行浏览器之间在这些方面的区别，并帮助读者，解决这些问题。 本书第1～5章由张欣翻译，第6～20章由曹力翻译，全书由张欣统稿、润色及审校。还要感谢全体工作人员的努力才将本书完成。 我们深深地感我们的家人和朋友。在翻译过程中，他们给予了我们莫大的关心、支持和帮助。 限于我们的水平，翻译过程中的疏漏和错误再作难免，请广大读者批评指正。 曹力 2006年于东南大学 目录 第1章 JavaScript是什么 1 1.1 历史简述 1 1.2 JavaScript实现 2 1.2.1 ECMAScript 3 1.2.2 DOM 5 1.2.3 [...]]]></description>
			<content:encoded><![CDATA[<p><a class="imagelink" href="http://www.nirvanastudio.org/wp-content/uploads/2006/09/profession-javascript-cover.jpg" title="profession-javascript-cover.jpg"><img id="image112"  style="float:right" src="http://www.nirvanastudio.org/wp-content/uploads/2006/09/profession-javascript-cover.thumbnail.jpg" alt="profession-javascript-cover.jpg" /></a></p>
<h3>基本信息</h3>
<ul>
<li>【英文名】<a href="http://www.wrox.com/WileyCDA/WroxTitle/productCd-0764579088.html">Professional JavaScript for Web Developers</a></li>
<li>【作者】Nicholas C. Zakas</li>
<li>【译者】<a href="http://shiningray.cn/about/">曹力</a> 张欣</li>
<li>【ISBN】7115152098</li>
<li>【出版时间】2006-9-15 </li>
<li>【页码】670</li>
<li>【原出版社】<a href="http://www.wrox.com/">Wrox</a></li>
<li>【简介】本书从最早期Netscape浏览器中的Javascript开始讲起，直到当前它对XML和Web服务的具体支持，展示了如何充分利用这种功能强大的语言开发自己的应用程序，以解决当今Web开发者面对的商业问题。</li>
<li><a href="http://www.dearbook.com.cn/2006/jsppk97/index.htm">活动主页</a></li>
</ul>
<h3>译者序</h3>
<p><span id="more-87"></span></p>
<p>亲爱的读者：</p>
<p>当您从书架上拿出这本书的时候，我想您肯定对Ajax技术有着浓厚的兴趣，而本书也正是您的正确选择。本书的作者Nicholas C. Zakas用通俗易懂的语言，将JavaScript的诞生、现在的状况、未来的发展和与其紧密相关的各种技术一一详尽地叙述出来，刚学JavaScript的朋友，可以按部就班成为高手，而已经是高手的朋友，则可以将本书作为参考手册。</p>
<p>第1章讲述了JavaScript的起源，给大家一个关于JavaScript正确的认知。</p>
<p>第2~5章详细介绍了JavaScript语言本身，揭示了一些JavaScript不为认知的语言特点。</p>
<p>第6~9章介绍了JavaScript和浏览器进行交互的一些基础知识和一些进阶知识，如DOM的基础、正则表达式。</p>
<p>第10~13章介绍了一些更加高级的JavaScript技巧，这些技巧可以构建良好的客户端逻辑，包括表格排序、拖动等。</p>
<p>第14章关于错误处理的内容，既有如何编程处理JavaScript错误，也包含了如何调试JavaScript的方法，而调试一直是JavaScript的弱项。</p>
<p>第16、17章讲述了利用JavaScript进行客户端到服务器的同学，不仅仅介绍了现在的Ajax技术的基础XML HTTP Request，还介绍了曾经出现过的一些方法。第17章更明确的介绍了如何调用Web服务。</p>
<p>第19章，介绍了如何考虑生产环境中JavaScript所需要注意的一些事情，如安全性、性能等。</p>
<p>第20章，展望了JavaScript未来的发展。</p>
<p>本书除了介绍了JavaScript的各个方面外，更难得的是，作者更涵盖了现今各个流行浏览器之间在这些方面的区别，并帮助读者，解决这些问题。</p>
<p>本书第1～5章由张欣翻译，第6～20章由曹力翻译，全书由张欣统稿、润色及审校。还要感谢全体工作人员的努力才将本书完成。</p>
<p>我们深深地感我们的家人和朋友。在翻译过程中，他们给予了我们莫大的关心、支持和帮助。</p>
<p>限于我们的水平，翻译过程中的疏漏和错误再作难免，请广大读者批评指正。</p>
<p align="right">曹力<br />
2006年于东南大学</p>
<h3>目录</h3>
<pre>
第1章  JavaScript是什么 1
1.1  历史简述 1
1.2  JavaScript实现 2
1.2.1  ECMAScript 3
1.2.2  DOM 5
1.2.3  BOM 8
1.3  小结 8
第2章  ECMAScript基础 9
2.1  语法 9
2.2  变量 10
2.3  关键字 12
2.4  保留字 12
2.5  原始值和引用值 13
2.6  原始类型 13
2.6.1  typeof运算符 14
2.6.2  Undefined类型 14
2.6.3  Null类型 15
2.6.4  Boolean类型 15
2.6.5  Number类型 15
2.6.6  String类型 17
2.7  转换 18
2.7.1  转换成字符串 18
2.7.2  转换成数字 19
2.7.3  强制类型转换 20
2.8  引用类型 22
2.8.1  Object类 22
2.8.2  Boolean类 23
2.8.3  Number类 23
2.8.4  String类 24
2.8.5  instanceof运算符 28
2.9  运算符 28
2.9.1  一元运算符 28
2.9.2  位运算符 32
2.9.3  Boolean运算符 37
2.9.4  乘性运算符 40
2.9.5  加性运算符 41
2.9.6  关系运算符 42
2.9.7  等性运算符 43
2.9.8  条件运算符 45
2.9.9  赋值运算符 45
2.9.10  逗号运算符 46
2.10  语句 46
2.10.1  if语句 46
2.10.2  迭代语句 47
2.10.3  有标签的语句 48
2.10.4  break语句和continue语句 48
2.10.5  with语句 50
2.10.6  switch语句 50
2.11  函数 51
2.11.1  无重载 53
2.11.2  arguments对象 53
2.11.3  Function类 54
2.11.4  闭包 56
2.12  小结 57
第3章  对象基础 58
3.1  面向对象术语 58
3.1.1  面向对象语言的要求 58
3.1.2  对象的构成 59
3.2  对象应用 59
3.2.1  声明和实例化 59
3.2.2  对象引用 59
3.2.3  对象废除 59
3.2.4  早绑定和晚绑定 60
3.3  对象的类型 60
3.3.1  本地对象 60
3.3.2  内置对象 70
3.3.3  宿主对象 75
3.4  作用域 75
3.4.1  公用、受保护和私有作用域 75
3.4.2  静态作用域并非静态的 76
3.4.3  关键字this 76
3.5  定义类或对象 78
3.5.1  工厂方式 78
3.5.2  构造函数方式 80
3.5.3  原型方式 80
3.5.4  混合的构造函数/原型方式 81
3.5.5  动态原型方法 82
3.5.6  混合工厂方式 83
3.5.7  采用哪种方式 84
3.5.8  实例 84
3.6  修改对象 86
3.6.1  创建新方法 86
3.6.2  重定义已有方法 87
3.6.3  极晚绑定 88
3.7  小结 88
第4章  继承 89
4.1  继承机制实例 89
4.2  继承机制的实现 90
4.2.1  继承的方式 90
4.2.2  一个更实际的例子 96
4.3  其他继承方式 100
4.3.1  zInherit 100
4.3.2  xbObjects 104
4.4  小结 108
第5章  浏览器中的JavaScript 109
5.1  HTML中的JavaScript 109
5.1.1  〈script/〉标签 109
5.1.2  外部文件格式 110
5.1.3  内嵌代码和外部文件 111
5.1.4  标签放置 111
5.1.5  隐藏还是不隐藏 113
5.1.6  〈noscript/〉标签 113
5.1.7  XHTML中的改变 114
5.2  SVG中的JavaScript 116
5.2.1  SVG基础 116
5.2.2  SVG中的〈script/〉标签 117
5.2.3  SVG中的标签放置 118
5.3  BOM 119
5.3.1  window对象 119
5.3.2  document对象 130
5.3.3  location对象 133
5.3.4  navigator对象 135
5.3.5  screen对象 136
5.4  小结 137
第6章  DOM基础 138
6.1  什么是DOM？ 138
6.1.1  XML简介 138
6.1.2  针对XML的API 141
6.1.3  节点的层次 141
6.1.4  特定语言的DOM 144
6.2  对DOM的支持 145
6.3  使用DOM 145
6.3.1  访问相关的节点 145
6.3.2  检测节点类型 146
6.3.3  处理特性 147
6.3.4  访问指定节点 148
6.3.5  创建和操作节点 150
6.4  HTML DOM特征功能 155
6.4.1  让特性像属性一样 155
6.4.2  table方法 156
6.5  遍历DOM 158
6.5.1  NodeIterator 158
6.5.2  TreeWalker 163
6.6  测试与DOM标准的一致性 165
6.7  DOM Level 3 166
6.8  小结 166
第7章  正则表达式 167
7.1  正则表达式支持 167
7.1.1  使用RegExp对象 168
7.1.2  扩展的字符串方法 169
7.2  简单模式 170
7.2.1  元字符 170
7.2.2  使用特殊字符 170
7.2.3  字符类 172
7.2.4  量词 174
7.3  复杂模式 177
7.3.1  分组 177
7.3.2  反向引用 178
7.3.3  候选 179
7.3.4  非捕获性分组 180
7.3.5  前瞻 181
7.3.6  边界 182
7.3.7  多行模式 183
7.4  理解RegExp对象 184
7.4.1  实例属性 184
7.4.2  静态属性 185
7.5  常用模式 186
7.5.1  验证日期 187
7.5.2  验证信用卡号 188
7.5.3  验证电子邮件地址 192
7.6  小结 193
第8章  检测浏览器和操作系统 194
8.1  navigator对象 194
8.2  检测浏览器的方式 194
8.2.1  对象/特征检测法 194
8.2.2  user-agent字符串检测法 195
8.3  user-agent字符串简史 196
8.3.1  Netscape Navigator 3.0与 IE3.0 196
8.3.2  Netscape Communicator 4.0与IE 4.0 197
8.3.3  IE 5.0及更高版本 198
8.3.4  Mozilla 198
8.3.5  Opera 200
8.3.6  Safari 201
8.3.7  结语 201
8.4  浏览器检测脚本 201
8.4.1  方法学 202
8.4.2  第一步 202
8.4.3  检测Opera 204
8.4.4  检测Konqueror/Safari 206
8.4.5  检测IE 208
8.4.6  检测Mozilla 209
8.4.7  检测Netscape Communicator       4.x 210
8.5  平台/操作系统检测脚本 211
8.5.1  方法学 211
8.5.2  第一步 212
8.5.3  检测Windows操作系统 212
8.5.4  检测Macintosh操作系统 214
8.5.5  检测Unix操作系统 214
8.6  全部脚本 215
8.7  例子：登录页面 219
8.8  小结 224
第9章  事件 225
9.1  今天的事件 225
9.2  事件流 226
9.2.1  冒泡型事件 226
9.2.2  捕获型事件 227
9.2.3  DOM事件流 228
9.3  事件处理函数/监听函数 229
9.3.1  IE 230
9.3.2  DOM 231
9.4  事件对象 232
9.4.1  定位 233
9.4.2  属性/方法 233
9.4.3  相似性 235
9.4.4  区别 238
9.5  事件的类型 240
9.5.1  鼠标事件 240
9.5.2  键盘事件 244
9.5.3  HTML事件 246
9.5.4  变化事件 251
9.6  跨平台的事件 252
9.6.1  EventUtil对象 252
9.6.2  添加/删除事件处理函数 252
9.6.3  格式化event对象 254
9.6.4  获取事件对象 258
9.6.5  示例 259
9.7  小结 260
第10章  高级DOM技术 261
10.1  样式编程 261
10.1.1  DOM样式的方法 263
10.1.2  自定义鼠标提示 264
10.1.3  可折叠区域 265
10.1.4  访问样式表 266
10.1.5  最终样式 270
10.2  innerText和innerHTML 271
10.3  outerText和outerHTML 273
10.4  范围 274
10.4.1  DOM中的范围 274
10.4.2  IE中的范围 284
10.4.3  范围在实际中的应用 288
10.5  小结 288
第11章  表单和数据完整性 289
11.1  表单基础 289
11.2  对〈form/〉元素进行脚本编写 291
11.2.1  获取表单的引用 291
11.2.2  访问表单字段 291
11.2.3  表单字段的共性 292
11.2.4  聚焦于第一个字段 292
11.2.5  提交表单 293
11.2.6  仅提交一次 294
11.2.7  重置表单 295
11.3  文本框 295
11.3.1  获取/更改文本框的值 296
11.3.2  选择文本 297
11.3.3  文本框事件 298
11.3.4  自动选择文本 298
11.3.5  自动切换到下一个 299
11.3.6  限制textarea的字符数 300
11.3.7  允许/阻止文本框中的字符 301
11.3.8  使用上下按键操作数字文本 306
11.4  列表框和组合框 308
11.4.1  访问选项 309
11.4.2  获取/更改选中项 309
11.4.3  添加选项 310
11.4.4  删除选项 311
11.4.5  移动选项 312
11.4.6  重新排序选项 313
11.5  创建自动提示的文本框 313
11.5.1  匹配 314
11.5.2  内部机制 314
11.6  小结 316
第12章  表格排序 317
12.1  起点——数组 317
12.2  对单列的表格排序 319
12.2.1  比较函数 320
12.2.2  sortTable()函数 320
12.3  对多列表格进行排序 323
12.3.1  比较函数生成器 323
12.3.2  修改sortTable()方法 324
12.3.3  逆序排列 325
12.3.4  对不同的数据类型进行排序 327
12.3.5  高级排序 330
12.4  小结 334
第13章  拖放 335
13.1  系统拖放 335
13.1.1  拖放事件 336
13.1.2  数据传输对象dataTransfer 341
13.1.3  dragDrop()方法 345
13.1.4  优点及缺点 346
13.2  模拟拖放 346
13.2.1  代码 347
13.2.2  创建放置目标 349
13.2.3  优点及缺点 352
13.3  zDragDrop 352
13.3.1  创建可拖动元素 352
13.3.2  创建放置目标 353
13.3.3  事件 353
13.3.4  例子 354
13.4  小结 355
第14章  错误处理 356
14.1  错误处理的重要性 356
14.2  错误和异常 357
14.3  错误报告 358
14.3.1  IE（Windows） 358
14.3.2  IE（MacOS） 359
14.3.3  Mozilla（所有平台） 359
14.3.4  Safari（MacOS） 360
14.3.5  Opera 7（所有平台） 361
14.4  处理错误 362
14.4.1  onerror事件处理函数 362
14.4.2  try...catch语句 365
14.5  调试技巧 370
14.5.1  使用警告框 370
14.5.2  使用Java控制台 371
14.5.3  将消息写入JavaScript控制台 （仅限Opera 7+） 372
14.5.4  抛出自定义错误 372
14.5.5  JavaScript校验器 373
14.6  调试器 374
14.6.1  Microsoft Script Debugger 374
14.6.2  Venkman 376
14.7  小结 383
第15章  JavaScript中的XML 384
15.1  浏览器中的XML DOM支持 384
15.1.1  IE中的XML DOM支持 384
15.1.2  Mozilla中XML DOM支持 388
15.1.3  通用接口 393
15.2  浏览器中的XPath支持 403
15.2.1  XPath简介 403
15.2.2  IE中的XPath支持 404
15.2.3  Mozilla中的XPath支持 404
15.3  浏览器中的XSLT支持 408
15.3.1  IE中的XSLT支持 410
15.3.2  Mozilla中XSLT支持 413
15.4  小结 415
第16章  客户端与服务器端的通信 416
16.1  cookie 416
16.1.1  cookie的成分 416
16.1.2  其他安全限制 417
16.1.3  JavaScript中的cookie 417
16.1.4  服务器端的cookie 419
16.1.5  在客户端与服务器端之间传递cookie 422
16.2  隐藏框架 423
16.3  HTTP请求 426
16.3.1  使用HTTP首部 428
16.3.2  实现的复制品 429
16.3.3  进行GET请求 430
16.3.4  进行POST请求 430
16.4  LiveConnect请求 431
16.4.1  进行GET请求 431
16.4.2  进行POST请求 433
16.5  智能HTTP请求 435
16.5.1  get()方法 435
16.5.2  post()方法 438
16.6  实际使用 439
16.7  小结 439
第17章  Web服务 440
17.1  Web服务快速入门 440
17.1.1  Web服务是什么？ 440
17.1.2  WSDL 441
17.2  IE中的Web服务 443
17.2.1  使用WebService组件 444
17.2.2  WebService组件例子 445
17.3  Mozilla中的Web服务 447
17.3.1  加强的特权 447
17.3.2  使用SOAP方法 448
17.3.3  使用WSDL代理 451
17.4  跨浏览器的方案 454
17.4.1  WebService对象 454
17.4.2  Temperature 服务 456
17.4.3  使用TemperatureService对象 458
17.5  小结 458
第18章  与插件进行交互 459
18.1  为何使用插件 459
18.2  流行的插件 460
18.3  MIME类型 460
18.4  嵌入插件 461
18.4.1  加入参数 461
18.4.2  Netscape 4.x 462
18.5  检测插件 462
18.5.1  检测Netscape式插件 463
18.5.2  检测ActiveX插件 467
18.5.3  跨浏览器检测 469
18.6  Java applet 470
18.6.1  嵌入applet 470
18.6.2  在JavaScript中引用applet 471
18.6.3  创建applet 471
18.6.4  JavaScript到Java的通信 472
18.6.5  Java到JavaScript的通信 475
18.7  Flash动画 477
18.7.1  嵌入Flash动画 477
18.7.2  引用Flash动画 478
18.7.3  JavaScript到Flash的通信 478
18.7.4  Flash到JavaScript通信 481
18.8  ActiveX控件 483
18.9  小结 485
第19章  部署问题 486
19.1  安全性 486
19.1.1  同源策略 486
19.1.2  窗口对象问题 487
19.1.3  Mozilla特有的问题 488
19.1.4  资源限制 490
19.2  国际化 491
19.2.1  使用JavaScript检测语言 491
19.2.2  策略 492
19.2.3  字符串的思考 492
19.3  优化JavaScript 495
19.3.1  下载时间 495
19.3.2  执行时间 499
19.4  知识产权的问题 512
19.4.1  混淆 512
19.4.2  Microsoft Script Encoder（仅IE） 513
19.5  小结 514
第20章  JavaScript的未来 515
20.1  ECMAScript 4 515
20.1.1  Netscape的提案 515
20.1.2  实现 521
20.2  ECMAScript for XML 522
20.2.1  途径 522
20.2.2  for each..in循环 524
20.2.3  新的类 524
20.2.4  实现 532
20.3  小结 532
索引
</pre>
]]></content:encoded>
			<wfw:commentRss>http://shiningray.cn/professional-javascript-for-web-developers.html/feed</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>JavaScript = C + Lisp</title>
		<link>http://shiningray.cn/javascript-c-lisp.html</link>
		<comments>http://shiningray.cn/javascript-c-lisp.html#comments</comments>
		<pubDate>Sun, 02 Apr 2006 23:31:35 +0000</pubDate>
		<dc:creator>ShiningRay</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Haskell]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[Lisp]]></category>
		<category><![CDATA[函数式编程]]></category>

		<guid isPermaLink="false">http://shiningray.cn/javascript-c-lisp.html</guid>
		<description><![CDATA[作者： William Taysom 原文地址：http://www.jadetower.org/muses/archives/000307.html 翻译：ShiningRay 我在过去的几周内一直在写JavaScript代码——使用我们的对话框系统来个性化Mozilla。假设你要求：&#8220;嘿，电脑，我要教你如何在Amazon.com上找书。首先你象这样进入Amazon，然后在这里输入你要的书的名字。点击&#8220;Go&#8221;然后&#8230;&#8230;&#8221;我的困难在于对Mozilla编码使我的对话框系统可以&#8220;看&#8221;浏览器中正在进行什么然后自己可以执行这些动作。 由于Mozilla中较高的层次是用JavaScript实现的。所以我一直在废寝忘食研究它（我的Rhino book里面全是我做的书签）我写的越多，我越觉得它像Lisp。 考虑以下代码： semanticAccepter = acceptOnlyIf( acceptNot(emptyTextAccepter), scriptFilter, acceptAny( textAccepter, linkAccepter, formRelatedElementAccepter, linkImageAccepter)); usefulContentOfThePage = new SemTree(semanticAccepter); 这里SemTree是一个对象，它允许你从一个HTML DOM 树中选出某些你感兴趣的节点，去掉那些你不感兴趣的节点。（根本上说，这是一个TreeWalker 类的包装器。）若要建立一个 SemTree ，你要给出一个接受器。一个接受器只是一个判断给定节点是否能被接受的一个函数： function emptyTextAccepter(n) { return (n instanceof Text) &#38;&#38; n.data.match(/^\s*$/); } 一旦有了一些基本的接受器和筛选器，很容易就可以定义组合筛选器──一种将筛选器以特殊形式组合起来的函数： function acceptAny() { var disjuncts = arguments; return function(n) { for (var i = 0; [...]]]></description>
			<content:encoded><![CDATA[<p align="center">作者： William Taysom </p>
<p align="center">原文地址：<a href="http://www.jadetower.org/muses/archives/000307.html">http://www.jadetower.org/muses/archives/000307.html</a></p>
<p align="center">翻译：<a href="http://shiningray.cn">ShiningRay</a></p>
<p>我在过去的几周内一直在写JavaScript代码——使用我们的对话框系统来个性化Mozilla。假设你要求：&ldquo;嘿，电脑，我要教你如何在Amazon.com上找书。首先你象这样进入Amazon，然后在这里输入你要的书的名字。点击&ldquo;Go&rdquo;然后&hellip;&hellip;&rdquo;我的困难在于对Mozilla编码使我的对话框系统可以&ldquo;看&rdquo;浏览器中正在进行什么然后自己可以执行这些动作。</p>
<p>由于Mozilla中较高的层次是用JavaScript实现的。所以我一直在废寝忘食研究它（我的<a href="http://www.oreilly.com/catalog/jscript4/">Rhino book</a>里面全是我做的书签）我写的越多，我越觉得它像Lisp。</p>
<p>考虑以下代码：</p>
<pre lang="javascript">semanticAccepter = acceptOnlyIf(
    acceptNot(emptyTextAccepter),
    scriptFilter,
    acceptAny(
        textAccepter,
        linkAccepter,
        formRelatedElementAccepter,
        linkImageAccepter));
usefulContentOfThePage = new SemTree(semanticAccepter);</pre>
<p>这里<tt>SemTree</tt>是一个对象，它允许你从一个<a href="http://www.w3.org/DOM/">HTML DOM</a> 树中选出某些你感兴趣的节点，去掉那些你不感兴趣的节点。（根本上说，这是一个<tt>TreeWalker</tt> 类的包装器。）若要建立一个 <tt>SemTree</tt> ，你要给出一个接受器。一个接受器只是一个判断给定节点是否能被接受的一个函数：</p>
<pre lang="javascript">function emptyTextAccepter(n) {
    return (n instanceof Text) &amp;&amp; n.data.match(/^\s*$/);
}</pre>
<p>一旦有了一些基本的接受器和筛选器，很容易就可以定义组合筛选器──一种将筛选器以特殊形式组合起来的函数：</p>
<pre lang="javascript">function acceptAny() {
    var disjuncts = arguments;
    return function(n) {
        for (var i = 0; i &lt; disjuncts.length; ++i) {
            var shouldAccept = disjuncts[i](n);
            if (shouldAccept) return shouldAccept;
        }
        return false;
    }
}</pre>
<p>当它被调用的时候，以接受器作为参数，<tt>acceptAny</tt>返回一个新的接受器，可以接受只要是 <tt>disjuncts</tt> 能接受的那些给定节点 <tt>n</tt>。所以，<tt>semanticAccepter</tt> 中出现的<tt>acceptAny</tt> 能接受文本、链接、表单和链接中的图像。相反地，<tt>acceptOnlyIf</tt>只能接受被所有接受器组件接受的节点。<tt>acceptOnlyIf</tt>的定义类似于<tt>acceptAny</tt>：</p>
<pre lang="javascript">function acceptOnlyIf() {
    var conjuncts = arguments;
    return function(n) {
        for (var i = 0; i &lt; conjuncts.length; ++i) {
            var shouldAccept = conjuncts[i](n);
            if (shouldAccept != true) return shouldAccept;
        }
        return true;
    }
}</pre>
<p><tt>acceptOnlyIf</tt>和<tt>acceptAny</tt>如此相似让我纳闷是否有一个通用的方法可以将任一个组合器（像否定、短路与、短路或）变成一个组合筛选器（象<tt>acceptNot</tt>、<tt>acceptOnlyIf</tt>、<tt>acceptAny</tt>）。的确有，但<tt>JavaScript</tt>不胜任这个任务。要完成这个，我们需要更强大的武器。</p>
<p><a name="dropdownid" id="dropdownid">将函数的能力定义得和单子的功能一样微小</a></p>
<p>通过提供第一类型函数，JavaScript迈出了进入更广阔世界的第一步。而这个世界中最具影响的便是<a href="http://www.haskell.org/">Haskell</a>和它的一些变体。由于接受器告诉我们是否要接受一个节点，它们应该是一个从节点到布尔值的函数。在Haskell中，我们这样写：</p>
<pre lang="haskell">type Accepter = Node -&gt; Bool</pre>
<p>这样，否定是一个布尔到另一布尔，合取和析取是从一个布尔的列表到一个单个布尔值，这样写：</p>
<pre lang="haskell">not ::  Bool  -&gt; Bool
and :: [Bool] -&gt; Bool
or  :: [Bool] -&gt; Bool</pre>
<p>组合接受器有类似的形式：</p>
<pre lang="haskell">acceptNot    ::  Accepter  -&gt; Accepter
acceptOnlyIf :: [Accepter] -&gt; Accepter
acceptAny    :: [Accepter] -&gt; Accepter</pre>
<p>我们给<tt>semanticAccepter</tt>下的定义和JavaScript版的类似：</p>
<pre lang="haskell">semanticAccepter = acceptOnlyIf [
    acceptNot emptyTextAccepter,
    scriptFilter,
    acceptAny [
        textAccepter,
        linkAccepter,
        formRelatedElementAccepter,
        linkImageAccepter]];</pre>
<p>我们怎样定义一个类似<tt>acceptOnlyIf</tt>的组合器？Haskell没有像一些语言中的命令结构。取代的是递归：</p>
<pre lang="haskell">acceptOnlyIf []     n = true
acceptOnlyIf (a:as) n | a n        = true
                      | otherwise  = acceptOnlyIf as n </pre>
<p>由于某些原因，短变量名是Haskell中的标准。<tt>n</tt>是一个节点，<tt>a</tt>是一个接受器，<tt>as</tt>是一个接受器的列表。（以&#8217;s'结尾是代表某个变量是列表的标准形式。）你上面看到的定义是正确的，但我不常用这个方法。我会使用一个优美的小函数叫<tt>map</tt>：</p>
<pre lang="haskell">map :: (a -&gt; b) -&gt; ([a] -&gt; [b])
map f []     = []
map f (x:xs) = f x : map xs</pre>
<p>当传了一个函数和一个列表，<tt>map</tt>返回对列表中的每个元素执行函数后生成的列表。有了<tt>map</tt>后，<tt>acceptOnlyIf</tt><br />
就可以这样定义。</p>
<pre lang="haskell">acceptOnlyIf as n = and (map (\a -&gt; a n) as)</pre>
<p>这里语法 (\a -&gt; a n) 基本意思和JavaScript下面的一样：</p>
<pre lang="haskell">function(a) {
    return a(n);
}</pre>
<p>整个<tt>acceptOnlyIf</tt>的定义本质上说明了，&ldquo;给出一个节点<tt>n</tt>，找出每个接受器对<tt>n</tt>的值，然后返回这些值的合取值（和值<tt>AND</tt>）。&rdquo;有了这种定义函数的优美方法之后，它们之间的相似之处立刻显现出来了：</p>
<pre lang="haskell">acceptOnlyIf as n = and (map (\a -&gt; a n) as)
acceptAny    as n = or  (map (\a -&gt; a n) as)</pre>
<p>
这样，泛化就是一些琐碎的事了：</p>
<pre lang="haskell">liftCombiner :: ([Bool] -&gt; Bool) -&gt; ([Accepter] -&gt; Accepter)
liftCombiner c as n = c (map (\a -&gt; a n) as)
acceptOnlyIf = liftCombiner and
acceptAny    = liftCombiner or</pre>
<p>现在我们是否可以更进一步了呢？我们是否可以也将 <tt>not</tt>搞成 <tt>acceptNot</tt>呢？<tt>and</tt>和 <tt>not</tt>的主要区别在于 <tt>and</tt>参数是一个布尔值的列表，而<tt>not</tt>只能针对单个布尔值。要更进一步泛化 <tt>liftCombiner</tt>，我们必须：</p>
<ol>
<li>找出可以描绘出基本值和列表的共同特性的结构。</li>
<li>将这个结构应用到合成组合器的问题中.  </li>
</ol>
<p>Haskell 正好有我们所需要的。它称为单子<a href="http://en.wikipedia.org/wiki/Monad">Monad</a>.</p>
<p>什么是单子？</p>
<p>之前就有人问过这个问题。单子到处可见。大多数结构和过程/进程像数据类型、函数、对象、、异常、I/O、副作用、同步、事务、分析器和编程语言，都可以接受单独的、原子的操作。组对（Pair）、元组（tuple）、列表、树、图——这些数据结构都有一个单子级的解释，是常常不止一个。由于是单子的东西太多了，所以很难对它们进行描述。不过我还是可以给你一个数学上的定义。但是正如&ldquo;A continuation is the rest of the computation &rdquo;所说的，给出单子的定义只有在你已经对它有了一些感性的认识才有用。否则直接给出定义只会混淆你的观点。那先让我们研究一下这个谦虚的列表作为我们受到单子启发的途径。</p>
<p>检验结构有很多方法。其中一个方法是查看各部分是如何一起工作来组成整体的。当以这种方法分析列表的时候，我们发现它们是连接的解码：一个列表要么是空的要么是一个由一个head和一个tail组成的Cons（一种构造函数，返回一个新的列表）。head是一个列表的元素，tail则是列表的其它部分。</p>
<pre lang="haskell">data LinkedList a = Nil | Cons a (List a)</pre>
<p>若要定义列表[1, 2, 3]，我们这样写：To define the list [1, 2, 3], we write:</p>
<pre lang="haskell">oneTwoThree = Cons 1 (Cons 2 (Cons 3 (Cons Nil)))</pre>
<p> Haskell中的列表就是这样工作的。除了用[]代表 <tt>Nil</tt>和用: 代表 <tt>Cons</tt>。逗号可以用来分隔条目，可以写成这样：</p>
<pre lang="haskell">oneTwoThree = 1 : (2 : (3 : []))</pre>
<p>链表有一个很长很有名的历史。不幸的是分解材料并不会显露出单子。</p>
<p>另一种分析列表的方法是通过研究它是如何和其它东西相关、进行交互的。Haskell提供了&ldquo;class&rdquo;机制根据和它们相关联的方法来定义对象。类似于抽象数据类型和接口：</p>
<pre lang="haskell">class List ls where
    nil  :: ls a
    cons :: a -&gt; ls a -&gt; ls a
    head :: ls a -&gt; a
    tail :: ls a -&gt; ls a
    map  :: (a -&gt; b) -&gt; (ls a -&gt; ls b)</pre>
<p>这是一个看似完美的列表类，但它几乎没有从分解材料中抽象出什么东西。所有的方法，除了 <tt>map</tt>，都是特定于列表的逻辑结构的，<tt>map</tt>抓住了一个较抽象的概念。它将一个函数作为参数，然后返回一个被函数处理过新的列表。回忆一下 <tt>map </tt>对定义 <tt>acceptAny </tt>和 <tt>acceptOnlyIf </tt>起了多大的帮助？这很明确是个值得研究的函数。</p>
<p>还有哪些其它函数对列表作为一个独立于它特定实现和形态的数据结构来说是至关重要的呢？好吧，还应该有一个方法 <tt>unit</tt>来把一个单独的元素放入一个列表，而且我们还需要一个 <tt>join </tt>来将一个列表的列表组成一个长的列表。这个类定义像这样：</p>
<pre lang="haskell">class List ls where
    unit :: a -&gt; ls a
    map  :: (a -&gt; b) -&gt; (ls a -&gt; ls b)
    join :: ls (ls a) -&gt; ls a</pre>
<p>
以下是链表的实现：</p>
<pre lang="haskell">instance List [] where
    unit a = [a]
    map  f []     = []
    map  f (a:as) = f a : map f as

    join []       = []
    join [ls:lss] = ls ++ join lss</pre>
<p>这些方法一目了然：<tt>unit</tt>将参数放入列表，<tt>map</tt>对列表中的每个元素执行函数，<tt>join</tt>将一个列表的列表连成一串。让我们确定一下串连接符（++）的定义吧：</p>
<pre lang="haskell">(++) []     ls = ls
(++) (k:ks) ls = k : (ks ++ ls)
[1, 2, 3] ++ [4, 5, 6]        --&gt; [1, 2, 3, 4, 5, 6]
unit 5                        --&gt; [5]
map (\x -&gt; x + x) [1, 2, 3]   --&gt; [2, 4, 6]
join [[1], [2, 3], [4, 5, 6]] --&gt; [1, 2, 3, 4, 5, 6]</pre>
<p>这些函数都十分简单而且十分有用。它们确实是列表的标准成分，但就好比火药一样，如果你把它们正确地组合起来，你就能引爆一些东西：一个单子。我们等不及这个式子了：</p>
<pre lang="haskell">class Monad m where
    return :: a -&gt; m a
    (&gt;&gt;=)  :: m a -&gt; (a -&gt; m b) -&gt; m b</pre>
<p>就是它了。两个函数：<tt>return</tt>将一些东西放入单子中，同时（&gt;&gt;=），称为&ldquo;绑定&rdquo;，将一个单子变成另一个。我们立即来看看单子到底能做什么。不过首先为了明确起见，我们先将我们的列表实现扩展成应用一个单子：</p>
<pre lang="haskell">
instance Monad [] where

    return a   = [a]

    (&gt;&gt;=) ls f = join (map f ls)
</pre>
<p><tt>return</tt>定义和 <tt>unit</tt>一样：将它自己放入一个列表中。绑定 (&gt;&gt;=)将<tt>map</tt> 和 <tt>join</tt> 组合成一个操作。首先你将函数<tt>k</tt>映射到列表 <tt>ls</tt> ，然后由于 <tt>f</tt>返回一个列表的列表，你再用 <tt>join</tt> 将结果组成单个列表。</p>
<p>但单子这东西有什么好处呢？设想你想要将一个列表中的所有值和另一个列表中的值相加来产生一个大的列表：</p>
<pre lang="haskell">sumTwoLists [1, 2, 3] [4, 5, 6] --&gt; [5, 6, 7, 6, 7, 8, 7, 8, 9]</pre>
<p>JavaScript代码应该像：</p>
<pre lang="javascript">function sumTwoLists(ks, ls) {
    var result = [];
    for (var i = 0; i &lt; ks.length; ++i) {
        for (var j = 0; j &lt; ls.length; ++j) {
            result.push(ks[i] + ls[j]);
        }
    }
    return result;
}</pre>
<p>应用了单子我们可以这样写：</p>
<pre lang="haskell">sumTwoLists ks ls = return (ks &gt;&gt;= \k -&gt;
                            ls &gt;&gt;= \l -&gt;
                            k + l)</pre>
<p>代码看来很简练，但没什么特别的。幸运的是，由于单子是如此有用，所以更好的语法形式也出现很久了。我们应该这样写：</p>
<pre lang="haskell">sumTwoLists ks ls = do k &lt;- ks
                       l &lt;- ls
                       return k + l</pre>
<p>这称为&ldquo;do notation做标记&rdquo;为了明显起见。还有另一种变体，我个人喜欢叫&ldquo;列表包含语法&rdquo;或者&ldquo;我无法相信这没有成为一个学说&rdquo;：</p>
<pre lang="haskell">sumTwoLists ks ls = [k + l | k &lt;- ks, l &lt;- ls]</pre>
<p>
不论哪种方法，都是所有的单子之上的一种较好语法。这种较好语法也许看上去像某种命令语言或什么蛇的东西（指Python语言──译注）。但我不接受其它替代语法，在Haskell中任何单子都能使用两者之一。</p>
<p>我们看看一个列表单子是如何工作的，但我们仅有的单子是 <tt>LinkedList</tt>。对于<tt>not</tt>，我们只需要一个直接变换值的单子就行了：一个一致单子。它不会对值做特殊的改变，仅仅直接返回值：</p>
<pre lang="haskell">instance Monad Identity where
    return a  = a
    (&gt;&gt;=) a f = f a</pre>
<p>现在我们终于可以完整地定义 <tt>liftCombiner</tt>了：</p>
<pre lang="haskell">liftCombiner c as n = c [a n | a &lt;- as]</pre>
<p>
现在&ldquo;让组合器能工作在所有接受器上&rdquo;的这个想法已经不难实现了：</p>
<pre lang="haskell">acceptOnlyIf = liftCombiner and
acceptAny    = liftCombiner or
acceptNot    = liftCombiner not</pre>
<h3>最后的思考</h3>
<p>今天我们看了如何处理组合的接受器（从节点到布尔的函数），组合器可以是任何单子。结果接受器也是单子。你认为是否有一种方法，可以让组合器组合任意单子和其它单子（除了接受器之外）？如果有，怎样做？如果没有，单子之间要有怎样的关联才能这样？</p>
]]></content:encoded>
			<wfw:commentRss>http://shiningray.cn/javascript-c-lisp.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>在Java2平台企业版中应用异步JavaScript技术和XML（AJAX）</title>
		<link>http://shiningray.cn/ajax-with-j2ee.html</link>
		<comments>http://shiningray.cn/ajax-with-j2ee.html#comments</comments>
		<pubDate>Sun, 02 Apr 2006 23:23:22 +0000</pubDate>
		<dc:creator>ShiningRay</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[解决方案]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[J2EE]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://shiningray.cn/?p=150</guid>
		<description><![CDATA[作者Greg Murray, 2005年6月9日&#160; 翻译：ShiningRay@Nirvana Studio2005年9月9日 任何试过过Flickr、GMail、Google Suggest或者是Google Maps的人都会意识到一种新型的动态Web应用正在逐渐浮出水面。这些应用外观和表现都和传统的桌面应用程序很像，而他们不需要依赖于插件或者是特定于浏览器的功能。过去Web应用只是一系列HTML页面，他们任意一部份内容的更改都必须重新载入页面。像JavaScript编程语言和层叠样式表 （CSS）之类的技术已经成熟，可以有效地应用他们来创建高动态的Web应用，而且可以运行在所有的主流浏览器中。本文将会详细介绍你马上就可以使用的一些技术，让他们使你的Web应用像桌面应用更加丰富和更有交互性。 介绍异步JavaScript技术和XML（AJAX） 使用JavaScript技术，一个HTML页面可以异步地对服务器（一般是载入页面的服务器）发送请求并获取XML文档。然后JavaScript可以使用XML文档来更新或改动HTML页面的文档对象模型（DOM）。最近形成了一个术语AJAX（Asynchronous JavaScript Technology and XML）来描述这种交互模型。 AJAX其实不是很新的东西。这些技术对于Windows平台上专注于Internet Explorer的开发人员来说，已经存在好几年了。直到最近，这个技术才被作为Web远程技术或者远程脚本技术被大家了解。Web开发人员也有一段时间曾经使用过插件、Java applet和隐藏框架来模拟这种交互模型。最近发生的变化是，对XMLHttpRequest对象的支持已经成为所有平台上的主流浏览器都包括的特性了。JavaScript技术的XMLHttpRequest对象是。尽管在正式的JavaScript技术标准中并没有提到这种对象，然而今天主流的浏览器都对他提供了支持。而当代的浏览器如Firefox、Internet Explorer以及Safari在JavaScript技术和CSS的支持上有些细微的差别，但是这种差别是可以处理的。如果你要考虑支持较老的浏览器，AJAX也许就不能成为你的解决方法。 基于AJAX的客户端之所以独特的原因是客户端包含了用JavaScript嵌入的特定于页面的控制逻辑。应用JavaScript技术的页面基于事件进行交互，如文档载入、鼠标点击、焦点改变甚至是定时器。AJAX交互使得表现层逻辑更加清晰地与数据分离。一个HTML页面也可以根据需要每次读入适当的数据，而不是每次需要显示一个更改时都重新载入整个页面。AJAX要求一种不同的服务器架构来支持它这种交互模型。以前，服务器端Web应用关注于对每个导致服务器调用的客户端事件都生成HTML文档。然后客户端对每个回应都要重新读入并重新渲染完整的HTML页面。富Web应用(Rich Web Application)关注于，让一个客户端获取一个HTML文档让它表现为一个模板或者是一个容器，可以基于事件并使用从服务器端组件中获取的XML 数据来对文档注入内容。 一些AJAX交互的应用如： 实时表单数据检验：像用户ID、序列号、邮政编码或者是特殊的票据代码这类需要服务器端验证的数据也可以在用户提交表单之前进行验证。 自动补全:像电子邮件地址、姓名或城市名之类的表单数据都可以根据用户情况自动补全。 处理细节操作：根据一个客户端事件，一个HTML页面可以根据现存的一些数据再去获取更多详细的信息，如现在有一个产品列表，客户端可以控制查看单独的产品信息而无需刷新页面。 复杂的用户界面控件：像树型控件、菜单和进度条之类不要求页面刷新的控件也能实现。 页面内刷新数据：HTML页面可以从服务器上查询最新的数据如分数、股指、天气还有其它的特定于应用的数据。 服务器端通知：一个HTML页面可以通过对服务器进行定时查询来模拟一个服务器的事件通知推送，实现像通知客户端一个消息、刷新页面数据或将客户端重定向到另一个页面。 这个列表并未把所有的应用都列出来，但它已经显示了AJAX交互可以让Web应用比从前能做更多的事情。但尽管这些好处是值得关注的，这种方式也有一些缺点： 复杂度：服务器端开发人员必需理解，HTML客户端页面中的表现层逻辑以及生成HTML客户端页面所需的XML内容的服务器端逻辑。HTML页面开发人员必须了解JavaScript技术。如果开发新的框架和发展已有的框架来支持这种交互模型，那么AJAX应用的创建就会越来越简单。 XMLHttpRequest对象的标准化：XMLHttpRequest对象还不是JavaScript技术标准的一部分，这就意味着根据客户端的不同，应用的行为也有所会不同。 JavaScript技术的实现：AJAX交互极大地依赖于JavaScript技术，而由于客户端的原因JavaScript还有一些细微的差别。见QuirksMode.org来了解更多关于浏览器之间区别的内容。 调试:AJAX应用也难于调试，因为流程逻辑是同时嵌在客户端中和服务器上的。 代码可见：客户端的JavaScript可以很容易通过“查看源代码”被人看见。一个没有良好设计的AJAX应用很可能被黑客攻击或被他人剽窃。 当开发人员在使用AJAX交互模型上获得更多的经验后，AJAX技术的框架和模式就会慢慢浮现出来。现在就关注于完全通用的AJAX交互框架，还为时过早。本文和相关的解决方案将关注于在现有的Java 2平台企业版（J2EE）上如何对AJAX进行支持，像servlet，JavaServer Page（JSP）软件、JavaServer Face应用和Java标准标签库（JSTL）。 AJAX交互剖析 现在我们已经讨论了AJAX是什么以及一些高层次的问题。那现在让我们把所有的零件放在一起来展示一个具有AJAX的J2EE应用。 首先考虑一个例子。一个Web应用包括了一个静态HTML页面，或者是一个由JSP生成的HTML页面，这个JSP中还包括了一个HTML表单，它需要服务器端逻辑来对表单中的数据进行检验，而不用刷新页面。一个名为ValidateServlet服务器端组件(servlet)用来提供这种验证逻辑。图一描述了这种具有验证逻辑的AJAX交互的细节。 图1: 一个提供验证逻辑的AJAX交互 以下条目代表了图1中出来AJAX交互的过程： 发生一个客户端事件。 创建和配置一个XMLHttpRequest对象。 XMLHttpRequest对象进行一个调用。 ValidateServlet对请求进行处理。 ValidateServlet返回一个包含了结果的XML文档。 XMLHttpRequest对象调用callback()函数并处理结果。 更新 HTML DOM。 [...]]]></description>
			<content:encoded><![CDATA[<div><em>作者<a href="#author">Greg Murray</a>, 2005年6月9日&nbsp; 翻译：<a href="http://shiningray.cn">ShiningRay</a>@<a href="http://www.nirvanastudio.org">Nirvana Studio</a>2005年9月9日</em></div>
<div>任何试过过Flickr、GMail、Google Suggest或者是Google Maps的人都会意识到一种新型的动态Web应用正在逐渐浮出水面。这些应用外观和表现都和传统的桌面应用程序很像，而他们不需要依赖于插件或者是特定于浏览器的功能。过去Web应用只是一系列HTML页面，他们任意一部份内容的更改都必须重新载入页面。像JavaScript编程语言和层叠样式表 （CSS）之类的技术已经成熟，可以有效地应用他们来创建高动态的Web应用，而且可以运行在所有的主流浏览器中。本文将会详细介绍你马上就可以使用的一些技术，让他们使你的Web应用像桌面应用更加丰富和更有交互性。</div>
<h2>介绍异步JavaScript技术和XML（AJAX）</h2>
<hr/>
<p>使用JavaScript技术，一个HTML页面可以异步地对服务器（一般是载入页面的服务器）发送请求并获取XML文档。然后JavaScript可以使用XML文档来更新或改动HTML页面的文档对象模型（DOM）。最近形成了一个术语<a href="http://www.adaptivepath.com/publications/essays/archives/000385.php" target="_blank">AJAX</a>（Asynchronous JavaScript Technology and XML）来描述这种交互模型。</p>
<p>AJAX其实不是很新的东西。这些技术对于Windows平台上专注于Internet Explorer的开发人员来说，已经存在好几年了。直到最近，这个技术才被作为Web远程技术或者远程脚本技术被大家了解。Web开发人员也有一段时间曾经使用过插件、Java applet和隐藏框架来模拟这种交互模型。最近发生的变化是，对<code>XMLHttpRequest</code>对象的支持已经成为所有平台上的主流浏览器都包括的特性了。JavaScript技术的<code>XMLHttpRequest</code>对象是。尽管在正式的JavaScript技术标准中并没有提到这种对象，然而今天主流的浏览器都对他提供了支持。而当代的浏览器如Firefox、Internet Explorer以及Safari在JavaScript技术和CSS的支持上有些细微的差别，但是这种差别是可以处理的。如果你要考虑支持较老的浏览器，AJAX也许就不能成为你的解决方法。</p>
<p>基于AJAX的客户端之所以独特的原因是客户端包含了用JavaScript嵌入的特定于页面的控制逻辑。应用JavaScript技术的页面基于事件进行交互，如文档载入、鼠标点击、焦点改变甚至是定时器。AJAX交互使得表现层逻辑更加清晰地与数据分离。一个HTML页面也可以根据需要每次读入适当的数据，而不是每次需要显示一个更改时都重新载入整个页面。AJAX要求一种不同的服务器架构来支持它这种交互模型。以前，服务器端Web应用关注于对每个导致服务器调用的客户端事件都生成HTML文档。然后客户端对每个回应都要重新读入并重新渲染完整的HTML页面。富Web应用(Rich Web Application)关注于，让一个客户端获取一个HTML文档让它表现为一个模板或者是一个容器，可以基于事件并使用从服务器端组件中获取的XML 数据来对文档注入内容。</p>
<p>一些AJAX交互的应用如：</p>
<ul>
<li><b>实时表单数据检验：</b>像用户ID、序列号、邮政编码或者是特殊的票据代码这类需要服务器端验证的数据也可以在用户提交表单之前进行验证。</li>
<li><b>自动补全:</b>像电子邮件地址、姓名或城市名之类的表单数据都可以根据用户情况自动补全。</li>
<li><b>处理细节操作：</b>根据一个客户端事件，一个HTML页面可以根据现存的一些数据再去获取更多详细的信息，如现在有一个产品列表，客户端可以控制查看单独的产品信息而无需刷新页面。</li>
<li><b>复杂的用户界面控件：</b>像树型控件、菜单和进度条之类不要求页面刷新的控件也能实现。</li>
<li><b>页面内刷新数据：</b>HTML页面可以从服务器上查询最新的数据如分数、股指、天气还有其它的特定于应用的数据。</li>
<li><b>服务器端通知：</b>一个HTML页面可以通过对服务器进行定时查询来模拟一个服务器的事件通知推送，实现像通知客户端一个消息、刷新页面数据或将客户端重定向到另一个页面。</li>
</ul>
<p>这个列表并未把所有的应用都列出来，但它已经显示了AJAX交互可以让Web应用比从前能做更多的事情。但尽管这些好处是值得关注的，这种方式也有一些缺点：</p>
<ul>
<li><b>复杂度：</b>服务器端开发人员必需理解，HTML客户端页面中的表现层逻辑以及生成HTML客户端页面所需的XML内容的服务器端逻辑。HTML页面开发人员必须了解JavaScript技术。如果开发新的框架和发展已有的框架来支持这种交互模型，那么AJAX应用的创建就会越来越简单。</li>
<li><b><code>XMLHttpRequest</code>对象的标准化：</b><code>XMLHttpRequest</code>对象还不是JavaScript技术标准的一部分，这就意味着根据客户端的不同，应用的行为也有所会不同。</li>
<li><b>JavaScript技术的实现：</b>AJAX交互极大地依赖于JavaScript技术，而由于客户端的原因JavaScript还有一些细微的差别。见<a href="http://www.quirksmode.org/">QuirksMode.org</a>来了解更多关于浏览器之间区别的内容。</li>
<li><b>调试:</b>AJAX应用也难于调试，因为流程逻辑是同时嵌在客户端中和服务器上的。</li>
<li><b>代码可见：</b>客户端的JavaScript可以很容易通过“查看源代码”被人看见。一个没有良好设计的AJAX应用很可能被黑客攻击或被他人剽窃。</li>
</ul>
<p>当开发人员在使用AJAX交互模型上获得更多的经验后，AJAX技术的框架和模式就会慢慢浮现出来。现在就关注于完全通用的AJAX交互框架，还为时过早。本文和相关的解决方案将关注于在现有的Java 2平台企业版（J2EE）上如何对AJAX进行支持，像servlet，JavaServer Page（JSP）软件、JavaServer Face应用和Java标准标签库（JSTL）。</p>
<h2>AJAX交互剖析</h2>
<hr />
<p>现在我们已经讨论了AJAX是什么以及一些高层次的问题。那现在让我们把所有的零件放在一起来展示一个具有AJAX的J2EE应用。</p>
<p>首先考虑一个例子。一个Web应用包括了一个静态HTML页面，或者是一个由<a href="http://java.sun.com/products/jsp">JSP</a>生成的HTML页面，这个JSP中还包括了一个HTML表单，它需要服务器端逻辑来对表单中的数据进行检验，而不用刷新页面。一个名为<code>ValidateServlet</code>服务器端组件(<a href="http://java.sun.com/products/servlet">servlet</a>)用来提供这种验证逻辑。图一描述了这种具有验证逻辑的AJAX交互的细节。</p>
<div class="padbottom10">
<table align="center" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td>
<img src="/wp-content/uploads/2006/04/interaction.jpg" alt="AJAX Interaction" border="0" />
</td>
</tr>
<tr>
<td class="grey3">
<div class="pad6">
<span class="dkcaption1" style="text-align:center">图1: 一个提供验证逻辑的AJAX交互</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<p>以下条目代表了图1中出来AJAX交互的过程：</p>
<ol>
<li><a href="#event_occurs">发生一个<b>客户端事件</b></a>。</li>
<li><a href="#configure_xmlhttprequest">创建和配置一个<code>XMLHttpRequest</code>对象。</a></li>
<li><a href="#make_call"><code>XMLHttpRequest</code>对象进行一个调用。</a></li>
<li><a href="#serverside_processing"><code>ValidateServlet</code>对请求进行处理。</a></li>
<li><a href="#xml_returned"><code>ValidateServlet</code>返回一个包含了结果的XML文档。</a></li>
<li><a href="#post_process"><code>XMLHttpRequest</code>对象调用<code>callback()</code>函数并处理结果。</a></li>
<li><a href="#update_dom">更新 HTML DOM。</a></li>
</ol>
<p>现在让我们逐个研究这个AJAX模型的每一步。</p>
<h3><a name="event_occurs">1.</a>发生一个客户端事件。</h3>
<p>在一个事件发生时可以调用相应的JavaScript函数。在这里，<code>validate()</code>函数可以被映射到一个链接或者是表单组件的<code>onkeyup</code>事件上去。</p>
<p><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  --></p>
<table class="grey4" border="0" cellpadding="10" cellspacing="0" width="100%">
<tbody>
<tr>
<td>
<code lang="html">&lt;input type="text"<br />
    size="20"<br />
    id="userid"<br />
    name="id"<br />
    onkeyup="validate();"&gt;</code>
</td>
</tr>
</tbody>
</table>
<p><br/><br />
<!-- END VCD7 CODE SAMPLE COMPONENT  --></p>
<p>每次用户在表单域中按下一个键时，表单元素将都调用<code>validate()</code>函数。&nbsp;</p>
<h3><a name="configure_xmlhttprequest">2. 建立和配置一个<code>XMLHttpRequest</code>对象</a></h3>
<p>创建和配置一个<code>XMLHttpRequest</code>对象</p>
<p><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  --></p>
<table class="grey4" border="0" cellpadding="10" cellspacing="0" width="100%">
<tbody>
<tr>
<td>
<code lang="javascript">var req;</p>
<p>function validate() {<br />
    var idField = document.getElementById("idField");<br />
    var url = "validate?id=" + escape(idField.value);<br />
    if (window.XMLHttpRequest) {<br />
        req = new XMLHttpRequest();<br />
    } else if (window.ActiveXObject) {<br />
        req = new ActiveXObject("Microsoft.XMLHTTP");<br />
    }<br />
    req.open("GET", url, true);<br />
    req.onreadystatechange = callback;<br />
    req.send(null);<br />
}</code>
</td>
</tr>
</tbody>
</table>
<p><br/><br />
<!-- END VCD7 CODE SAMPLE COMPONENT  --></p>
<p><code>validate()</code>函数建立了一个<code>XMLHttpRequest</code>对象并对象中的open函数。open函数需要两个参数：HTTP方法，可以是<code>GET</code>或<code>POST</code>; 和对象进行交互的服务器端组件的URL；一个布尔变量，表示是否要进行异步调用。API是<code>XMLHttpRequest.open(String method, String URL, boolean asynchronous)</code>。如果一个交互被设置为异步， (<code>true</code>) 那就必须指明一个回调函数。可以使用<code>req.onreadystatechange = callback;</code>来设置这个交互的回调函数。详细内容见第六节。</p>
<h3><a name="make_call">3.</a><code>XMLHttpRequest</code>对象进行调用</h3>
<p>当收到了语句<code>req.send(null);</code>，就会进行一次调用。HTTP<code>GET</code>的情况下，内容可以是<code>null</code>或者留空。当调用<code>XMLHttpRequest</code>的这个函数时，也会对已经配置了的URL进行调用。在下面这个例子中，要发送的数据(<code>id</code>)将作为一个URL参数。</p>
<p>使用HTTP<code>GET</code>,两个重复的请求将返回同样的结果。当使用HTTP<code>GET</code>方法时，要注意URL的长度，包括已经转义的URL参数，可能会受到某些浏览器和服务器端的Web容器的限制。当发送的数据会影响到服务器端的应用程序的状态时，就应该使用HTTP<code>POST</code>方法。使用HTTP<code>POST</code>必须要对<code>XMLHttpRequest</code>对象设置一个<code>Content-Type</code>头，使用以下语句：</p>
<p><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  --></p>
<table class="grey4" border="0" cellpadding="10" cellspacing="0" width="100%">
<tbody>
<tr>
<td>
<code lang="java">req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");<br />
req.send("id=" + escape(idTextField.value));<br />
</code>
</td>
</tr>
</tbody>
</table>
<p><br/><br />
<!-- END VCD7 CODE SAMPLE COMPONENT  --></p>
<p>当从JavaScript中发送表单值得时候，你应该考虑对字段值进行编码。JavaScript中有一个函数<code>escape()</code>，应该用他来确保区域化的内容被正确编码，同时特殊字符也被正确转义。</p>
<h3><a name="serverside_processing">4.&nbsp;</a><code>ValidateServlet</code>对请求进行处理.</h3>
<p>一个映射到URI &#8220;validate&#8221; 的servlet将检验user ID是不是已经在数据库中存在了。</p>
<p>一个servlet处理一个<code>XMLHttpRequest ，就像对待其它的HTTP请求一样。</code>下面的例子显示了服务器从请求中抽取出<code>id</code>参数并检验是否被占用了。</p>
<p><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  --></p>
<table class="grey4" border="0" cellpadding="10" cellspacing="0" width="100%">
<tbody>
<tr>
<td>
<code lang="java">public class ValidateServlet extends HttpServlet {</p>
<p>    private ServletContext context;<br />
    private HashMap users = new HashMap();</p>
<p>    public void init(ServletConfig config) throws ServletException {<br />
        this.context = config.getServletContext();<br />
        users.put("greg","account data");<br />
        users.put("duke","account data");<br />
    }</p>
<p>    public void doGet(HttpServletRequest request, HttpServletResponse  response)<br />
    throws IOException, ServletException {</p>
<p>        String targetId = request.getParameter("id");</p>
<p>        if<sup>1</sup>) {<br />
            response.setContentType("text/xml");<br />
            response.setHeader("Cache-Control", "no-cache");<br />
            response.getWriter().write("&lt;message&gt;valid&lt;/message&gt;");<br />
        } else {<br />
            response.setContentType("text/xml");<br />
            response.setHeader("Cache-Control", "no-cache");<br />
            response.getWriter().write("&lt;message&gt;invalid&lt;/message&gt;");<br />
        }<br />
    }<br />
}<br />
</code>
</td>
</tr>
</tbody>
</table>
<p><br/><br />
<!-- END VCD7 CODE SAMPLE COMPONENT  --></p>
<p>在这个例子中，一个简单的HashMap用来存放存在的用户名。在这个例子中，我们假设用户的ID是<code>duke</code>。</p>
<h3><a name="xml_returned">5.</a><code>ValidateServlet</code>返回一个包含结果的XML文档</h3>
<p>用户ID &#8220;duke&#8221; 在<code>users HashMap</code>的用户ID列表中出现了。将在应答中写一个包含值为<code>invalid</code>的<code>message</code>元素的XML文档。更复杂的用例将要求DOM、XSLT或其他API来生成这个应答。</p>
<p><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  --></p>
<table class="grey4" border="0" cellpadding="10" cellspacing="0" width="100%">
<tbody>
<tr>
<td>
<code lang="java">response.setContentType("text/xml");<br />
response.setHeader("Cache-Control", "no-cache");<br />
response.getWriter().write("&lt;message&gt;invalid&lt;/message&gt;");<br />
</code>
</td>
</tr>
</tbody>
</table>
<p><br/><br />
<!-- END VCD7 CODE SAMPLE COMPONENT  --></p>
<p>开发人员必须注意两个事情。第一，<code>Content-Type</code>必须设为<code>text/xml</code>。第二，<code>Cache-Control</code>必须设为<code>no-cache</code>。<code>XMLHttpRequest</code>对象只会处理<code>Content-Type</code>为<code>text/xml</code>的应答，同时把将<code>Cache-Control</code>设为<code>no-cache</code>将确保浏览器不会从缓存相同的URL（包括参数）返回的应答。</p>
<h3><a name="post_process">6.</a><code>XMLHttpRequest</code>对象调用<code>callback()</code>函数并处理结果。</h3>
<p><code>XMLHttpRequest</code>对象已经配置为当有<code>readyState</code>改变的时候就调用<code>callback()</code>函数。让我们假设已经<code>ValidateServlet</code>调用了而且<code>ValidateServlet</code>是<code>4</code>,表示<code>XMLHttpRequest</code>的调用已经完成。HTTP状态代码<code>200</code>表示一个成功的HTTP交互。</p>
<p><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  --></p>
<table class="grey4" border="0" cellpadding="10" cellspacing="0" width="100%">
<tbody>
<tr>
<td>
<code lang="javascript">function callback() {<br />
if (req.readyState == 4) {<br />
    if (req.status == 200) {<br />
        // update the HTML DOM based on whether or not message is valid<br />
    }<br />
}<br />
}<br />
</code>
</td>
</tr>
</tbody>
</table>
<p>浏览器维护了一个所显示的文档的对象形式（也就是所谓的Docuemt Object Model或DOM）。HTML页面中的JavaScript可以访问DOM，同时在页面载入完之后，可以使用API来修改DOM。
</p>
<p>根据成功的请求，JavaScript代码可以修改HTML页面的DOM。从<code>ValidateServlet</code>获得的对象形式的XML文档可以通过<code>req.responseXML</code>在JavaScript中获得，<code>req</code>是一个<code>XMLHttpRequest</code>对象。DOM API给JavaScript提供了获取这个文档中的内容以及修改HTML页面的DOM的方法。所返回的字符串形式的XML文档可以通过<code>req.responseText</code>获得。现在我们看看如何在JavaScript中使用DOM API，先看以下从<code>ValidateServlet</code>返回的XML文档。</p>
<p><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  --></p>
<table class="grey4" border="0" cellpadding="10" cellspacing="0" width="100%">
<tbody>
<tr>
<td><code lang="xml">&lt;message&gt;<br />
valid<br />
&lt;/message&gt;<br />
</code></td>
</tr>
</tbody>
</table>
<p>这个例子是一个简单的只包含了一个<code>message</code>元素的XML片断，里面只有一个简单的字符串<code>valid</code>或<code>invalid</code>。一个更高级的例子可以包含多于一个的消息和可以给用户看的有效的名字：</p>
<p><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  --></p>
<table class="grey4" border="0" cellpadding="10" cellspacing="0" width="100%">
<tbody>
<tr>
<td><code lang="javascript">function parseMessage() {<br />
    var message = req.responseXML.getElementsByTagName("message")[0];<br />
    setMessage(message.childNodes[0].nodeValue);<br />
}<br />
</code></td>
</tr>
</tbody>
</table>
<p><code>parseMessages()</code>函数将处理一个从<code>ValidateServlet</code>获取的XML文档。这个函数会调用<code>setMessage()</code>with the，并给出<code>message</code>作为参数来更新HTML DOM。</p>
<h3><a name="update_dom">7.</a>更新HTML DOM</h3>
<p>JavaScript技术可以使用很多API从HTML DOM中获得任何元素对象的引用。推荐的获得元素引用的方法是调用<code>document.getElementById("userIdMessage")</code>,&nbsp;<code>"userIdMessage"</code>是HTML文档中出现的一个元素的ID属性。有了这个元素的引用，就可以使用JavaScript来修改元素的属性、修改元素的样式、添加、删除或修改子元素。</p>
<p>一个常见的改变元素主体内容的方法是设置元素的<code>innerHTML</code>属性，如下所示：</p>
<p><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  --></p>
<table class="grey4" border="0" cellpadding="10" cellspacing="0" width="100%">
<tbody>
<tr>
<td>
<code lang="html">&lt;script type="text/javascript"&gt;<br />
function setMessage(message) {<br />
    var userMessageElement = document.getElementById("userIdMessage");<br />
    userMessageElement.innerHTML = "&lt;font color=\"red\"&gt;" + message + " &lt;/font&gt;";<br />
}<br />
&lt;/script&gt;<br />
&lt;body&gt;<br />
&lt;div id="userIdMessage"&gt;&lt;/div&gt;<br />
&lt;/body&gt;<br />
</code></td>
</tr>
</tbody>
</table>
<p><!-- END VCD7 CODE SAMPLE COMPONENT  --></p>
<p>受到影响的那部分HTML页面会立刻根据<code>innerHTML</code>的设置重新渲染。如果<code>innerHTML</code>属性包含类似<code>&lt;image&gt;</code>或者是<code>&lt;iframe&gt;</code>之类的元素，那么由那些元素所指定的内容同样会被获取并渲染。</p>
<p>这种途径的主要缺点是HTML元素是作为字符串硬编码在JavaScript中的。JavaScript中硬编码的HTML标记不是一种好的实践，因为它使代码难于阅读、维护和修改。我们应该考虑在JavaScript中使用DOM API来创建和修改HTML元素。把显示和JavaScript代码的字符串混在一起只会让页面更难于阅读和编辑。</p>
<p>另一种修改HTML DOM的方法是动态地产生新的元素并把他们作为子元素追加到目标元素，如下面的例子所示：</p>
<p><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  --></p>
<table class="grey4" border="0" cellpadding="10" cellspacing="0" width="100%">
<tbody>
<tr>
<td><code lang="html">&lt;script type="text/javascript"&gt;<br />
function setMessage(message) {<br />
    var userMessageElement = document.getElementById("userIdMessage");<br />
    var userIdMessageFont = document.getElementById("userIdMessageFont");<br />
    var messageElement = document.createTextNode(message);</p>
<p>    if (userMessageElement.childNodes[0]) {<br />
        // 更新元素<br />
        userIdMessageFont.replaceChild(messageElement, userIdMessageFont.childNodes[0]);<br />
    } else {<br />
        // 建立一个新的元素<br />
        var fontElement = document.createTextNode("font");<br />
        fontElement.setAtribute("id", "userIdMessageFont");<br />
        fontElement.setAtribute("color", "red");<br />
        userMessageElement.appendChild(fontElement);<br />
        fontElement.appendChild(messageElement);<br />
    }<br />
}<br />
&lt;/script&gt;<br />
&lt;body&gt;<br />
&lt;div id="userIdMessage"&gt;&lt;/div&gt;<br />
&lt;/body&gt;<br />
</code></td>
</tr>
</tbody>
</table>
<p><br/><br />
<!-- END VCD7 CODE SAMPLE COMPONENT  --></p>
<p>这个范例展示了JavaScript技术的DOM API可以用来更有目的地建立或改变一个元素。当然JavaScript的DOM AP在不同的浏览器上也可能有差别，所以你必须在开发应用程序时小心。</p>
<h2>Java BluePrint的解决方案目录</h2>
<p>The<a href="https://bpcatalog.dev.java.net/nonav/solutions.html" target="_blank">Java Blueprints Solutions Catalog</a>是用来收集J2EE技术上AJAX的最佳实践的。每个解决方案包含一个问题和方法的描述、一个设计文档和可运行的源码。这些解决方案是为了让你根据需要在自己的应用程序中复用。以下是已经提供的AJAX交互：</p>
<h3>自动补全</h3>
<p><strong>自动补全</strong>提供了当用户在一个HTML表单中输入一个请求时对数据浏览的简化方式。当用户面对一大片数据时，可以在输入数据时把可能的完整形式显示给用户。然后选择其中一个完整形式可以保证用户输入的数据已经存在在服务器上。</p>
<p>考虑一个大公司的一个名字查找的Web应用。如图2所示，只要输入姓或名的开头几个字母就可以得到人的列表。用户可以然后就只要点击一下就可以浏览用户的详细信息。</p>
<div class="padbottom10">
<table align="center" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td>
<img src="/wp-content/uploads/2006/04/auto-complete.jpg" alt="Autocompletion of a Name" border="0" />
</td>
</tr>
<tr>
<td class="grey3">
<div class="pad6">
<span class="dkcaption1" style="text-align:text">图2：名字自动补全</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<h3>进度条</h3>
<p>在Web应用中，一个服务器端任务也可能要花一段时间去完成。这段时间很可能会超过HTTP交互的时间上限（超时）。当用户不知道这个任务什么时候才能完成时，用户很可能会重新提交一次表单或直接退出会话状态。一般来说，Web应用使用页面刷新来跟踪服务器端操作的状态，这种方式可能会让人厌烦而且也不准确。AJAX可以用来仅在一个HTML页面中跟踪服务器端操作的状态而无需刷新页面。用户可以以图形方式看到服务器端操作的进度，如图3。</p>
<div class="padbottom10">
<table align="center" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td><img src="/wp-content/uploads/2006/04/progress-bar.jpg" alt="Progress Bar" border="0" /></td>
</tr>
<tr>
<td class="grey3">
<div class="pad6"><span class="dkcaption1" style="text-align:center">图3：<span class="dkgrey">进度条</span>&nbsp;</span></div>
</td>
</tr>
</tbody>
</table>
</div>
<h3>刷新数据</h3>
<p>向一个HTML页面提供最新的数据或服务器消息提醒在现在的Web世界中也是十分重要的，因为现在的Web世界中数据一直不停变化。尽管它不是一个实实在在的推送技术，但它可以通过使用AJAX交互不断进行查询来模拟。当数据需要更新或者要进行提醒，HTML页面将会动态地改变。图4显示了HTML页面中的一个服务器端计数器。这个计数器会在页面后台自动更新。</p>
<div class="padbottom10">
<table align="center" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td><img src="/wp-content/uploads/2006/04/refreshing-data.jpg" alt="Server-side Counter Shows Refreshing Data" border="0" /></td>
</tr>
<tr>
<td class="grey3">
<div class="pad6"><span class="dkcaption1">图4：服务器端计数器在刷新数据</span></div>
</td>
</tr>
</tbody>
</table>
</div>
<h3>实时检验</h3>
<p>不是所有的表单域都可以单独用JavaScript技术在客户端完成。某些表单数据要求服务器端的验证逻辑。传统和Web应用曾使用页面刷新来完成这种验证，但这可能有些让人烦。</p>
<p>考虑一个需要一个唯一用户ID的Web应用。使用AJAX交互，用户可以在输入的时候就知道ID是否有效（图5）。</p>
<div class="padbottom10">
<table align="center" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td><img src="/wp-content/uploads/2006/04/validation-invalid.jpg" alt="Invalidating the ID as User Types" border="0" /></td>
</tr>
<tr>
<td class="grey3">
<div class="pad6"><span class="dkcaption1">图5：指出用户ID无效</span></div>
</td>
</tr>
</tbody>
</table>
</div>
<p>当一个用户输入了一个无效的用户ID，应用程序禁止了提交按钮并且向用户显示了一个信息（图6）。</p>
<div class="padbottom10">
<table align="center" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td><img src="/wp-content/uploads/2006/04/validation-valid.jpg" alt="Validating the ID as User Types" border="0" /></td>
</tr>
<tr>
<td class="grey3">
<div class="pad6"><span class="dkcaption1">图6：用户ID通过验证</span></div>
</td>
</tr>
</tbody>
</table>
</div>
<p>用户马上就能知道用户ID是可用的也是有效的。</p>
<p><br/><br />
<!--<span class="sp10">&nbsp;</span><br />&#8211;></p>
<h2>最后的思考</h2>
<div class="contentdivider">
<table class="grey4" border="0" cellpadding="0" cellspacing="0" width="100%">
<tbody>
<tr>
<td>
<img src="/wp-content/uploads/2006/04/a.gif" alt=" " border="0" height="4" width="1" />
</td>
</tr>
</tbody>
</table>
</div>
<p>我们已经看到AJAX交互可以解决很多问题。配合HTTP处理、数据库、Web服务、XML处理和业务对象等API，J2EE技术已经提供了一个开发和部属基于AJAX应用的一个良好的基础。有了对于这个交互模型的更好的理解，今天的应用程序可以变得更加有交互性，给最终用户更好的体验。</p>
<p>使用AJAX要求你使用支持<code>XMLHttpRequest</code>对象的最新浏览器版本。使用AJAX还要求大量对JavaScript技术和CSS的应用。作为一个应用程序架构师或是一个开发人员，你要会针对浏览器支持、架构复杂度和对开发人员的培训等方面来衡量开发一个富应用的需要。当AJAX编程模型不断地发展，现有的技术和框架会让这种转变更加容易。</p>
<p>很明显的是，突出的Web应用都越来越有交互性了。那么你的呢？</p>
<p><br/></p>
<h2>更多信息</h2>
<ul>
<li><a href="https://bpcatalog.dev.java.net/nonav/solutions.html">JavaBlueprints Solutions Catalog</a>包含AJAX的代码范例和文档。</li>
<li>The Apple Developer connection 有一些关于<a href="http://developer.apple.com/internet/webcontent/xmlhttpreq.html"><code>XMLHttpRequest</code></a>对象的不错的文档。</li>
<li>W3C定义的<a href="http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html">JavaScript technology DOM bindings</a>。</li>
<li><a href="http://www.quirksmode.org/">QuirksMode</a>上有浏览器对JavaScript和CSS支持性的极好的参考资料</li>
<li>Jesse James Garrett对<a href="http://www.adaptivepath.com/publications/essays/archives/000385.php">AJAX</a>的定义。</li>
</ul>
<h2><a name="author">关于作者</a></h2>
<hr/>
<p>Greg Murray 是is a Sun Microsystems 的一名工程师，是servlet标准的领导人，BluePrint小组的前成员，在这个小组时他已经开始关注Web层次问题。他也是《<cite>Enterprise Applications With the Java 2 Platform,Enterprise Edition</cite>和<cite>Designing Web Services With the J2EE 1.4 Platform</cite>(Addison-Wesley)》一书的协助编撰者。</p>
<ol class="footnotes"><li id="footnote_0_150" class="footnote">targetId != null) &amp;&amp; !users.containsKey(targetId.trim(</li></ol>]]></content:encoded>
			<wfw:commentRss>http://shiningray.cn/ajax-with-j2ee.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>嵌入JavaScript引擎梗概教程</title>
		<link>http://shiningray.cn/how-to-embed-js-engine.html</link>
		<comments>http://shiningray.cn/how-to-embed-js-engine.html#comments</comments>
		<pubDate>Sun, 02 Apr 2006 08:58:03 +0000</pubDate>
		<dc:creator>ShiningRay</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[脚本引擎]]></category>

		<guid isPermaLink="false">http://shiningray.cn/how-to-embed-js-engine.html</guid>
		<description><![CDATA[嵌入JavaScript引擎 梗概教程 作者：Brendan Eich 2000年2月21日 翻译：ShiningRay @ NirvanaStudio 如何启动VM并执行一个脚本 如果不使用任何错误检查这样： JS_起头的返回指针的函数会返回空（null） JS_起头的返回布尔值的函数会返回假（false） （错误照例会被保存在一个JSBool变量ok中）。 JSRuntime *rt; JSContext *cx; JSObject *global; JSClass global_class = { &#34;global&#34;,0, JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_PropertyStub, JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,JS_FinalizeStub }; /* * 你必须有：You always need: * 每个进程一个运行时（runtime）, * 每个线程一个上下文（context）， * 每个上下文有一个全局对象（global）, * 标准类（如Date）。 */ rt = JS_NewRuntime(0x100000); cx = JS_NewContext(rt, 0x1000); global = JS_NewObject(cx, &#38;global_class, NULL, NULL); JS_InitStandardClasses(cx, [...]]]></description>
			<content:encoded><![CDATA[<div align="right">
<h1>嵌入JavaScript引擎</h1>
<h1>梗概教程</h1>
<h4>作者：Brendan Eich</h4>
<h4>2000年2月21日</h4>
<p>翻译：<a href="http://shiningray.cn">ShiningRay</a> @ <a href="http://www.nirvanastudio.org">NirvanaStudio</a></p>
</div>
<h2>如何启动VM并执行一个脚本</h2>
<p>如果不使用任何错误检查这样：</p>
<p>JS_起头的返回指针的函数会返回空（null）</p>
<p>JS_起头的返回布尔值的函数会返回假（false）<br />
（错误照例会被保存在一个JSBool变量ok中）。 </p>
<blockquote>
<pre lang="c">    JSRuntime *rt;
    JSContext *cx;
    JSObject *global;
    JSClass global_class = {
        &quot;global&quot;,0,
        JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,JS_PropertyStub,
        JS_EnumerateStub,JS_ResolveStub,JS_ConvertStub,JS_FinalizeStub
    }; 

    /*
     * 你必须有：You always need:
     *        每个进程一个运行时（runtime）,
     *        每个线程一个上下文（context），
     *        每个上下文有一个全局对象（global）,
     *        标准类（如Date）。
     */
    rt = JS_NewRuntime(0x100000); 

    cx = JS_NewContext(rt, 0x1000);
    global = JS_NewObject(cx, &amp;global_class, NULL, NULL);
    JS_InitStandardClasses(cx, global); 

    /*
     * 现在假设脚本包含了要计算的一些JS，比方说像“22/7”Math.PI的
     * 不太好的近似值，或者一些更长的东西，像：
     * &quot;(function fact(n){if (n &lt;= 1) return 1; return n * fact(n-1)})(5)&quot;
     * 来计算5！
     */
    char *script = &quot;...&quot;;
    jsval rval;
    JSString *str;
    JSBool ok; 

    ok = JS_EvaluateScript(cx, global, script, strlen(script),
                           filename, lineno, &amp;rval); 

    str = JS_ValueToString(cx, rval);
    printf(&quot;script result: %s\n&quot;, JS_GetStringBytes(str));</pre>
</blockquote>
<h2>如何从JavaScript中调用C函数</h2>
<p>假设有个C函数叫diot，他在被调用时需要至少两个实参（如果调用者少提供了几个，JS引擎需要保证undefined值传给了那些缺少的参数）： </p>
<blockquote><pre lang="c">    #define DOIT_MINARGS 2
    static JSBool
    doit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
    {
        /*
         * 在argv中查找argc个实参，设置*rval来给调用者返回一个值
         */
        ...
    }</pre>
</blockquote>
<p>然后把它和JS连接起来，你要写： </p>
<blockquote><pre lang="c">    ok = JS_DefineFunction(cx, global, &quot;doit&quot;, doit, DOIT_MINARGS, 0);</pre>
</blockquote>
<p>或者，如果你有一堆的本地函数要调用，你可以把它们放在一个表格中： </p>
<blockquote><pre lang="c">    static JSFunctionSpec my_functions[] = {
        {&quot;doit&quot;, doit, DOIT_MINARGS, 0, 0},
        etc...
        {0,0,0,0,0},
    };</pre>
</blockquote>
<p>（最后，全为0的函数表示表格结束）并且用： </p>
<blockquote><p><code lang="c">    ok = JS_DefineFunctions(cx, global, my_functions);</code></p></blockquote>
<h2>如何从C中调用JavaScript函数（像“onClick”）</h2>
<p>假设点击事件是发生在最顶层或者是有焦点的UI元素，位置为(x,y)：</p>
<blockquote><pre lang="c">    JSObject *target, *event;
    jsval argv[1], rval; 

   /*
    * 找到事件目标并且产生一个事件对象来表示这个点击。
    * 将cx传给NewEventObject这样JS_NewObject就可以被调用了。
    */
    target = FindEventTargetAt(cx, global, x, y);
&nbsp;event = NewEventObject(cx, &quot;click&quot;, x, y);
    argv[0] = OBJECT_TO_JSVAL(event); 

    /* 要模拟DOM，你也许还想尝试一下“onclick”*/
    ok = JS_CallFunctionName(cx, target, &quot;onClick&quot;, 1, argv, &amp;rval); 

    /* 现在测试rval来看看我们是否需要取消事件. */
    if (JSVAL_IS_BOOLEAN(rval) &amp;&amp; !JSVAL_TO_BOOLEAN(rval))
        CancelEvent(event);</pre>
</blockquote>
<p>再次声明，我省略了错误检查（例如在调用之后检查!ok），同时我伪造了一些C的事件管理程序来模拟DOM的协议——如果他的处理程序返回假就取消这个事件。</p>
<hr />
<p>原文地址：<a href="http://www.mozilla.org/js/spidermonkey/tutorial.html">http://www.mozilla.org/js/spidermonkey/tutorial.html</a> </p>
]]></content:encoded>
			<wfw:commentRss>http://shiningray.cn/how-to-embed-js-engine.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JavaScript-C引擎嵌入开发指南</title>
		<link>http://shiningray.cn/javascript-c-engine-embedders-guide.html</link>
		<comments>http://shiningray.cn/javascript-c-engine-embedders-guide.html#comments</comments>
		<pubDate>Sun, 02 Apr 2006 08:29:29 +0000</pubDate>
		<dc:creator>ShiningRay</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[C语言]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://shiningray.cn/?p=153</guid>
		<description><![CDATA[JavaScript-C引擎嵌入开发指南 翻译：ShiningRay@Nirvana Studio原文地址：http://www.mozilla.org/js/spidermonkey/apidoc/jsguide.html JavaScript-C引擎概览 本文档提供了一个JavaScript（JS）引擎的C语言实现的概述，他介绍了你如何在你的应用程序中嵌入脚本引擎来让它们可以使用JS。有两大理由让你在应用程序中嵌入JS引擎：使用脚本来自动操作你的应用程序；同时使用JS引擎和脚本无论何时都可以提供跨平台的功能并消除了应用程序解决方案对平台的依赖性。 受支持的JavaScript版本 本JS引擎支持从JS 1.0版到JS1.4。JS 1.3和更高版本符合ECMAScript-262规范。JS引擎解析、编译和执行包含JS语句和函数的脚本。这个引擎可以处理要用来执行脚本的JS数据类型和对象内存分配，同时它可以清除——垃圾回收——内存中已经不需要的数据类型和对象。 你如何使用这个引擎？ 通常，你将JS引擎作为一个共享的资源进行构建。例如，在Windows和Windows NT上，这个引擎是一个DLL文件，在Unix上是一个共享库。然后你把你的应用程序和他连接，同时嵌入式JS引擎应用程序编程接口（API）就可以在你的应用程序中调用了。JS引擎的API提供了以下几种分类的函数： 数据类型操作 运行时控制 类和对象创生的维护 函数和脚本执行 字符串处理 错误处理 安全性控制 调试支持 你在每个嵌入了JS调用的应用程序中将会用到这些功能分类中的某些部分，象运行时控制和数据类型操作。例如，在你调用其他JS功能之前，你必须通过调用JS_NewRuntime函数来新建和初始化JS引擎。其他功能分类，像安全控制，提供一些可选的特性，你可以根据需要在你的应用程序中使用它们。 这个引擎和应用程序有什么关系？ 从概念上来讲，JS引擎在你的系统上是一个共享资源。通过在你的应用程序中嵌入引擎API命令你可以向JS引擎传递处理的请求。这个引擎，反过来，处理你的请求，并把返回值或者状态信息返回给你的应用程序。图1.1描述了它们一般的关系： 图 1.1 例如，假设你正在使用JS引擎来使你的应用程序能通过JS脚本自动运行，同时假设你的应用程序运行一个脚本来对一个用户进行身份验证并且设置一个用户对这个应用程序的访问权限。首先，你的应用程序可能新建一个代表用户的自定义JS对象，包括了用户的名字、ID、访问权限和一个可能的用户拥有权限在应用程序中使用的函数的列表。 在这个情况下，你的应用程序给JS引擎发送的的第一个请求可能是对JS_NewObject的调用来新建一个自定义对象。当JS引擎新建了这个对象，他返回一个指针给你的应用程序。你的应用程序可以再次调用JS引擎来执行使用这个对象的脚本。例如，在建立了用户对象之后，你的应用程序会立刻给JS_EvaluateScript传递一个脚本来立刻编译执行。那个脚本可以获得并验证用户信息，然后建立用户对其他应用程序特性的访问权限。 事实上，你的应用程序和JS引擎之间的关系远比图1.1中显示的要复杂的多。例如，它假设你已经为你的平台构建了JS引擎。它还假设你的应用程序包含了jsapi.h还假设应用程序对引擎进行的第一个调用已经初始化了JS运行时。 当JS引擎接受到了一个初始化的请求时，他会为JS运行时分配内存。图1.2描述了这个过程： 图 1.2 这个运行时是一个内存空间，在其中可以维护你的应用程序所使用的变量、对象和上下文。一个上下文是指，针对JS引擎所使用的线程的脚本执行状态。每个同时存在的脚本或者线程都必须有它自己的上下文。一个单独的JS运行时可以包含很多上下文、对象和变量。 几乎所有的JS引擎调用都要求有一个上下文的参数，所以在创建了运行时之后你的应用程序首先要做的一件事情是调用JS_NewContext来至少创建一个上下文。实际你需要的上下文数量由你的应用程序中所期望同时运行的脚本的数量决定。从另一方面说，如果同一时间只有一个脚本被编译执行，那么你就知需要建立单独的一个上下文，你可以对每个脚本重复使用它。 在你新建了上下文之后，你会通常想要初始化引擎内置的JS对象，可以通过调用JS_InitStandardClasses实现。内置的对象有Array,Boolean,Date,Math,Number,和String字符串对象，大多数脚本都会用到。 大多数应用程序也要用到自定义JS对象。这些对象是特定于你的应用程序的。他们通常代表了数据结构和应用程序中脚本使用的方法。要新建一个自定义对象，你要组装一个JS类来生成这个对象，调用JS_InitClass来在运行时设立这个类，然后调用JS_NewObject来在引擎中新建你这个自定义对象的实例。最后，如果你的对象有一些属性，你也许要通过调用JS_SetProperty来设置他们的默认值。 即使你在创建一个对象的时候给JS引擎传递了一个特定的上下文，最后这个对象还是独立于这个上下文存在的。任何脚本都可以和任意上下文相关联来访问任何对象。图1.3描述了脚本和运行时、上下文以及对象之间的关系。 图 1.3 如图1.3所示，脚本和上下文完全是互相独立存在的及时他们可以访问相同的对象。在给定的运行时中，一个应用程序可以任意未分配的上下文来访问任何对象。也可能有时你想确保能为独占的使用而保留某些上下文和对象。在这些情况下，给你的应用程序新建单独的运行时：一个针对共享上下文和对象，另一个（或者更多的，取决于你的应用程序的需求）针对私有的运行时和对象。 注意:同一时间只能有一个线程被授权访问特定上下文。 构建引擎 在你可以在你的应用程序中使用JS之前，你必须将JS引擎构建成一个可共享的库。在大多数情况下，引擎代码已经包括了Make文件来自动构建。 例如，在Unix下，js源代码目录包括了一个基本的Gnu Make文件——Makefile.ref和一个config目录。config目录包括了平台特定的.mk文件来配合Makefile.ref对你的环境进行构建。在Windows NT下，NMake文件是js.mak。 请阅读源代码目录中任何的readme文件，也许其中包括了和更新的编译指导或者其他信息。 嵌入引擎有什么必要条件？ 如果要让你的应用程序可以执行JS，就要在你的应用程序代码中嵌入合适的引擎。嵌入一般有五步： 在你的C模块中加入#include jsapi.h来确保编译器知道有哪些引擎的API可以调用。极少部分特殊的JS引擎工作时会要求你包含额外的头文件。例如，要在你的应用程序中调用JS调试器，你要在合适的模块里面包含jsdbgapi.h。 大部分在JS源代码中的其它的头文件不应该被引用。这样做可能会使你的程序依赖于引擎内部的接口，而这些接口可能随着版本发布而更改。 在你的应用程序中提供支持结构和变量声明。例如，如果你打算给JS引擎传递一个脚本呢，提供一个字符串变量保存了你的应用程序的脚本的版本的文字信息。使用jsapi.h中定义的JS数据类型来声明结构和变量。 使用JavaScript编写特定应用的对象。这些对象常常会与操作在你C程序中的结构的结构和方法进行通讯，特别是如果你在使用JS引擎来自动操作你的应用程序。 在程序代码中嵌入合适的JS引擎API调用和变量引用，包括初始化内置JS对象，和创建组成任何应用程序要用的自定义对象。 大多数JS引擎调用都会返回一个值。如果这个值是零或者空，它通常表示一个错误的情况发生了。如果值非零，它一般表示成功；在这些情况下，返回的值常常会是你的程序需要使用的指针，或者存起来留以后引用。很重要的是，你的程序至少应该每次检查JS调用返回的值。 [...]]]></description>
			<content:encoded><![CDATA[<h1>JavaScript-C引擎嵌入开发指南</h1>
<p>翻译：<a href="http://shiningray.cn">ShiningRay</a>@<a href="/">Nirvana Studio</a>原文地址：<a href="http://www.mozilla.org/js/spidermonkey/apidoc/jsguide.html">http://www.mozilla.org/js/spidermonkey/apidoc/jsguide.html</a></p>
<hr />
<h2 align="center">JavaScript-C引擎概览</h2>
<p>本文档提供了一个JavaScript（JS）引擎的C语言实现的概述，他介绍了你如何在你的应用程序中嵌入脚本引擎来让它们可以使用JS。有两大理由让你在应用程序中嵌入JS引擎：使用脚本来自动操作你的应用程序；同时使用JS引擎和脚本无论何时都可以提供跨平台的功能并消除了应用程序解决方案对平台的依赖性。</p>
<h2><a name="Supported_Versions_of_JavaScript"></a>受支持的JavaScript版本</h2>
<p>本JS引擎支持从JS 1.0版到JS1.4。JS 1.3和更高版本符合ECMAScript-262规范。JS引擎解析、编译和执行包含JS语句和函数的脚本。这个引擎可以处理要用来执行脚本的JS数据类型和对象内存分配，同时它可以清除——垃圾回收——内存中已经不需要的数据类型和对象。</p>
<h2>你如何使用这个引擎？</h2>
<p>通常，你将JS引擎作为一个共享的资源进行构建。例如，在Windows和Windows NT上，这个引擎是一个DLL文件，在Unix上是一个共享库。然后你把你的应用程序和他连接，同时嵌入式JS引擎应用程序编程接口（API）就可以在你的应用程序中调用了。JS引擎的API提供了以下几种分类的函数：</p>
<ul>
<li>
<p>数据类型操作</p>
</li>
<li>
<p>运行时控制</p>
</li>
<li>
<p>类和对象创生的维护</p>
</li>
<li>
<p>函数和脚本执行</p>
</li>
<li>
<p>字符串处理</p>
</li>
<li>
<p>错误处理</p>
</li>
<li>
<p>安全性控制</p>
</li>
<li>
<p>调试支持</p>
</li>
</ul>
<p>你在每个嵌入了JS调用的应用程序中将会用到这些功能分类中的某些部分，象运行时控制和数据类型操作。例如，在你调用其他JS功能之前，你必须通过调用<code>JS_NewRuntime</code>函数来新建和初始化JS引擎。其他功能分类，像安全控制，提供一些可选的特性，你可以根据需要在你的应用程序中使用它们。</p>
<h2>这个引擎和应用程序有什么关系？</h2>
<p>从概念上来讲，JS引擎在你的系统上是一个共享资源。通过在你的应用程序中嵌入引擎API命令你可以向JS引擎传递处理的请求。这个引擎，反过来，处理你的请求，并把返回值或者状态信息返回给你的应用程序。图1.1描述了它们一般的关系：</p>
<p><strong>图 1.1</strong></p>
<p><img src="/wp-content/uploads/2006/04/over1.gif" alt="图1.1"/></p>
<p>例如，假设你正在使用JS引擎来使你的应用程序能通过JS脚本自动运行，同时假设你的应用程序运行一个脚本来对一个用户进行身份验证并且设置一个用户对这个应用程序的访问权限。首先，你的应用程序可能新建一个代表用户的自定义JS对象，包括了用户的名字、ID、访问权限和一个可能的用户拥有权限在应用程序中使用的函数的列表。</p>
<p>在这个情况下，你的应用程序给JS引擎发送的的第一个请求可能是对<code>JS_NewObject</code>的调用来新建一个自定义对象。当JS引擎新建了这个对象，他返回一个指针给你的应用程序。你的应用程序可以再次调用JS引擎来执行使用这个对象的脚本。例如，在建立了用户对象之后，你的应用程序会立刻给<code>JS_EvaluateScript</code>传递一个脚本来立刻编译执行。那个脚本可以获得并验证用户信息，然后建立用户对其他应用程序特性的访问权限。</p>
<p>事实上，你的应用程序和JS引擎之间的关系远比图1.1中显示的要复杂的多。例如，它假设你已经为你的平台构建了JS引擎。它还假设你的应用程序包含了<code>jsapi.h</code>还假设应用程序对引擎进行的第一个调用已经初始化了JS运行时。</p>
<p>当JS引擎接受到了一个初始化的请求时，他会为JS运行时分配内存。图1.2描述了这个过程：</p>
<p><strong>图 1.2</strong></p>
<p><img src="/wp-content/uploads/2006/04/over2.gif" alt="图 1.2"/></p>
<p>这个运行时是一个内存空间，在其中可以维护你的应用程序所使用的变量、对象和上下文。一个上下文是指，针对JS引擎所使用的线程的脚本执行状态。每个同时存在的脚本或者线程都必须有它自己的上下文。一个单独的JS运行时可以包含很多上下文、对象和变量。</p>
<p>几乎所有的JS引擎调用都要求有一个上下文的参数，所以在创建了运行时之后你的应用程序首先要做的一件事情是调用<code>JS_NewContext</code>来至少创建一个上下文。实际你需要的上下文数量由你的应用程序中所期望同时运行的脚本的数量决定。从另一方面说，如果同一时间只有一个脚本被编译执行，那么你就知需要建立单独的一个上下文，你可以对每个脚本重复使用它。
</p>
<p>在你新建了上下文之后，你会通常想要初始化引擎内置的JS对象，可以通过调用<code>JS_InitStandardClasses</code>实现。内置的对象有<code>Array</code>,<code>Boolean</code>,<code>Date</code>,<code>Math</code>,<code>Number</code>,和<code>String</code>字符串对象，大多数脚本都会用到。</p>
<p>大多数应用程序也要用到自定义JS对象。这些对象是特定于你的应用程序的。他们通常代表了数据结构和应用程序中脚本使用的方法。要新建一个自定义对象，你要组装一个JS类来生成这个对象，调用<code>JS_InitClass</code>来在运行时设立这个类，然后调用<code>JS_NewObject</code>来在引擎中新建你这个自定义对象的实例。最后，如果你的对象有一些属性，你也许要通过调用<code>JS_SetProperty</code>来设置他们的默认值。</p>
<p>即使你在创建一个对象的时候给JS引擎传递了一个特定的上下文，最后这个对象还是独立于这个上下文存在的。任何脚本都可以和任意上下文相关联来访问任何对象。图1.3描述了脚本和运行时、上下文以及对象之间的关系。</p>
<p><strong>图 1.3</strong></p>
<p><img src="/wp-content/uploads/2006/04/over3.gif" alt="图1.3" /></p>
<p>如图1.3所示，脚本和上下文完全是互相独立存在的及时他们可以访问相同的对象。在给定的运行时中，一个应用程序可以任意未分配的上下文来访问任何对象。也可能有时你想确保能为独占的使用而保留某些上下文和对象。在这些情况下，给你的应用程序新建单独的运行时：一个针对共享上下文和对象，另一个（或者更多的，取决于你的应用程序的需求）针对私有的运行时和对象。</p>
<blockquote><p><strong>注意:</strong>同一时间只能有一个线程被授权访问特定上下文。</p></blockquote>
<h2>构建引擎</h2>
<p>在你可以在你的应用程序中使用JS之前，你必须将JS引擎构建成一个可共享的库。在大多数情况下，引擎代码已经包括了Make文件来自动构建。</p>
<p>例如，在Unix下，<code>js</code>源代码目录包括了一个基本的Gnu Make文件——<code>Makefile.ref</code>和一个<code>config</code>目录。<code>config</code>目录包括了平台特定的<code>.mk</code>文件来配合<code>Makefile.ref</code>对你的环境进行构建。在Windows NT下，NMake文件是<code>js.mak</code>。</p>
<p>请阅读源代码目录中任何的<code>readme</code>文件，也许其中包括了和更新的编译指导或者其他信息。</p>
<h2>嵌入引擎有什么必要条件？</h2>
<p>如果要让你的应用程序可以执行JS，就要在你的应用程序代码中嵌入合适的引擎。嵌入一般有五步：</p>
<ol>
<li>
<p>在你的C模块中加入<code>#include jsapi.h</code>来确保编译器知道有哪些引擎的API可以调用。极少部分特殊的JS引擎工作时会要求你包含额外的头文件。例如，要在你的应用程序中调用JS调试器，你要在合适的模块里面包含<code>jsdbgapi.h</code>。</p>
<blockquote><p>大部分在JS源代码中的其它的头文件<strong>不</strong>应该被引用。这样做可能会使你的程序依赖于引擎内部的接口，而这些接口可能随着版本发布而更改。
</p></blockquote>
</li>
</ol>
<ol>
<li>
<p>在你的应用程序中提供支持结构和变量声明。例如，如果你打算给JS引擎传递一个脚本呢，提供一个字符串变量保存了你的应用程序的脚本的版本的文字信息。使用<code>jsapi.h</code>中定义的JS数据类型来声明结构和变量。</p>
</li>
<li>
<p>使用JavaScript编写特定应用的对象。这些对象常常会与操作在你C程序中的结构的结构和方法进行通讯，特别是如果你在使用JS引擎来自动操作你的应用程序。</p>
</li>
<li>
<p>在程序代码中嵌入合适的JS引擎API调用和变量引用，包括初始化内置JS对象，和创建组成任何应用程序要用的自定义对象。</p>
</li>
<li>
<p>大多数JS引擎调用都会返回一个值。如果这个值是零或者空，它通常表示一个错误的情况发生了。如果值非零，它一般表示成功；在这些情况下，返回的值常常会是你的程序需要使用的指针，或者存起来留以后引用。很重要的是，你的程序至少应该每次检查JS调用返回的值。</p>
</li>
</ol>
<p>以下代码片断描述了嵌入使用的大部分过程，除了JS脚本的建立，这点也不在本文的介绍范围之内。如要查询有关创建脚本的信息——JavaScript这个语言——请看<i>客户端JavaScript指导</i>，如果要得到关于编写服务器端对象，见<i>服务器端JavaScript指导</i>。</p>
<p><code lang="c">.<br />
.<br />
.<br />
#include &lt;stdio.h&gt;<br />
#include &lt;stdlib.h&gt;<br />
#include &lt;string.h&gt;</p>
<p>/* 包含JS引擎API头文件 */<br />
#include &quot;jsapi.h&quot;<br />
.<br />
.<br />
.</p>
<p>/* 主程序建立全局JS变量，包括运行时,<br />
 * 一个上下文，和一个全局对象，然后初始化JS运行时,<br />
 * 并创建一个上下文. */</p>
<p>int main(int argc, char **argv)<br />
{<br />
  int c, i;<br />
  /*建立全局的JS变量，包括全局和自定义对象 */</p>
<p>  JSVersion version;<br />
  JSRuntime *rt;<br />
  JSContext *cx;<br />
  JSObject  *glob, *it;<br />
  JSBool builtins;</p>
<p>  /* 初始化JS运行时，并返回结果给rt  */<br />
  rt = JS_NewRuntime(8L * 1024L * 1024L);</p>
<p>  /* 如果rt没有值，结束程序 */<br />
  if (!rt)<br />
    return 1;</p>
<p>  /* 新建一个上下文并且把它和JS运行时相关联 */<br />
  cx = JS_NewContext(rt, 8192);</p>
<p>  /* 如果cx没有值，在此结束程序 */<br />
  if (cx == NULL)<br />
    return 1;</p>
<p>  /* 新建全局对象 */<br />
  glob = JS_NewObject(cx, clasp, NULL, NULL);</p>
<p>  /* 初始化内置JS对象和全局对象 */<br />
  builtins = JS_InitStandardClasses(cx, glob);</p>
<p>  .<br />
  .<br />
  .</p>
<p>  return 0;</p>
<p>}<br />
</code></p>
<p>这个范例代码十分简单，它描述了嵌入JS引擎调用所必须的关键元素。如果想要更完整的例子——也就是以上这段代码片断的出处——参见<code>js.c</code>，这个范例应用的源代码是包含在JS引擎的源代码中的。</p>
<h2>理解关键嵌入开发概念</h2>
<p>大多数你要创建的JavaScript应用，你都会想要遵循一些权威的的JS API嵌入实践。以下部分讲述了任何程序中都要使用到的API调用。</p>
<p>在很多情况下，嵌入某些特定API调用的顺序决定这个程序的成功。例如，你必须在调用其它JS引擎函数之前初始化一个JS运行时。相应的，你要在关闭程序之前释放JS运行时。因此，典型的程序的<strong>main</strong>函数像一种三明治，在任何你提供的功能前后先初始化最后释放JS运行时：</p>
<p><code lang="c">int main(int argc, char **argv)<br />
{<br />
  int c, i;</p>
<p>  /*建立全局JS变量，包括全局对象global和自定义对象 */<br />
  JSVersion version;<br />
  JSRuntime *rt;<br />
  JSContext *cx;<br />
  JSObject  *glob, *it;</p>
<p>  .<br />
  .<br />
  .</p>
<p>  /* 初始化JS运行时，并把返回的结果放在rt中 */<br />
  rt = JS_NewRuntime(8L * 1024L * 1024L);</p>
<p>  /* 如果rt没有值，程序结束。 */<br />
  if (!rt)<br />
    return 1;</p>
<p>  .<br />
  .<br />
  .</p>
<p>  /* 建立一个上下文 */<br />
  cx = JS_NewContext(rt, 8192);</p>
<p>  /* 如果cx值为空，则结束程序 */<br />
  if (cx == NULL)<br />
    return 1;</p>
<p>  /* 初始化内置JS对象和全局对象 */<br />
  builtins = JS_InitStandardClasses(cx, glob);</p>
<p>  .<br />
  .<br />
  .</p>
<p>  /* 把你的代码扔这里，包括你要用来创建自定义JS对象的JS API函数调用。<br />
   * JS对象模型在这里开始。 */</p>
<p>  .<br />
  .<br />
  .</p>
<p>  /* 在退出应用程序之前，释放JS运行时 */<br />
  JS_DestroyRuntime(rt);<br />
</code></p>
<p>正如这个例子所描述的，嵌入了JS引擎的函数的应用程序要负责建立JS运行时，这是他最先要完成的动作之一，同时它还要负责在退出之前释放运行时。一般来说，确保运行时被初始化和释放的最佳位置是在中央JS调度程序的模块中嵌入必要的调用，无论你将使用哪一个模块作为在应用程序的中央调度模块。</p>
<p>在你初始化了运行时之后，你可以建立应用程序的JS对象模型。这个对象模型决定了你的JS对象互相之间的关系。JS对象本质上是分层次的。所有的JS对象都是默认与全局（global）对象相关的，他们都是全局对象的子孙。当你初始化标准JS类的时候，你会自动获得一个全局对象：</p>
<p><code lang="c">builtins = JS_InitStandardClasses(cx, glob);</code></p>
<p>全局对象会建立一些基本属性和方法，其他对象都会继承这些属性和方法。当你创建你自己的对象时，他们可以自动使用这些已经定义在全局对象上的属性和方法。你可以通过在自定义对象上重新对他们进行定义来覆盖这些默认属性和方法，否则可以直接接受默认的赋值。</p>
<p>你也可以基于其他的内置JS对象新建自定义对象，或者基于其他自定义对象来新建对象。无论哪种情况，你新建的对象在继承链中将继承他祖先的所有属性和方法，一直追溯到全局对象。如果要了解更多关于全局和自定义对象地内容，请参见“<a href="FIXME.htm#1014372">初始化内置和全局</a><a href="FIXME.htm#1014372">JS</a><a href="FIXME.htm#1014372">对象</a>”以及“<a href="FIXME.htm#1014443">创建和初始化自定义对象</a>”。</p>
<h2>管理一个运行时</h2>
<p>JS运行时是一块内存空间，在这里面JS引擎可以管理与JS函数和脚本相关的上下文、对象和变量。在执行任何JS函数或者是脚本之前，你必须初始化一个运行时。初始化运行时的API调用是<code>JS_NewRuntime</code>。<code>JS_NewRuntime</code>有一个参数，是一个无符号整数，它指明了在垃圾收集发生之前，分配给运行时的内存最大数值，单位是字节。例如：</p>
<p><code lang="c">rt = JS_NewRuntime(8L * 1024L * 1024L);</code></p>
<p>如上面列举的，<code>JS_NewRuntime</code>也会返回一个值，这个值是一个指向新建的运行时的指针。一个非空的返回值表示运行时被成功创建了。</p>
<p>一般来说，一个应用程序只需要一个运行时。但是，你还是可以创建多个运行时的，我们可以在必要的时候调用<code>JS_NewRuntime</code>并把返回值存在不同的指针中。</p>
<p>当不再需要JS运行时的时候，应该把它销毁来释放他占用的内存资源，以便给其他应用程序来使用。根据你的应用程序中JS的使用范围，你可以选择在JS使用结束立刻销毁运行时，或者，你可以选择一直保留运行时知道你的应用程序即将结束。无论哪种情况，都必须使用<code>JS_DestroyRuntime</code>来释放运行时，当运行时不再需要的时候：</p>
<p><code lang="c">JS_DestroyRuntime(rt);</code></p>
<p>如果你使用了多个运行时，要确保在结束应用程序前，每一个都被正确释放了。</p>
<h2>管理上下文</h2>
<p>几乎所有的JS API调用都要求你传送一个上下文作为参数。在JavaScript引擎中，一个上下文唯一对应一个脚本。引擎把上下文信息传送给运行脚本的那个线程。每个同步执行的脚本必须被分配一个唯一的上下文。当一个脚本执行完之后，他的上下文就不再被使用了，这时候这个上下文就可以再次被分配给一个新的脚本，或者可以释放他。</p>
<p>要为一个脚本创建新的上下文，可以使用<code>JS_NewContext</code>函数。该函数有两个参数：一个指针指向上下文所需结合的运行时，和为上下文分配的栈空间的大小，以字节为单位。如果成功，函数返回新建的上下文的指针。例如：</p>
<p><code lang="c">JSContext *cx;<br />
  .<br />
  .<br />
  .<br />
  cx = JS_NewContext(rt, 8192);<br />
</code></p>
<p>运行时必须已经存在。你为上下文指定的栈的大小应该足够容纳使用这个上下文的脚本所创建的任何变量和对象。注意和分配和维护上下文相关有一个特定的数量，因为你可能要：</p>
<ol>
<li>
<p>只创建同一时刻在你的应用程序中所需要的数量一样多的上下文。</p>
</li>
<li>
<p>只要上下文有可能被应用程序用到，就保留他们，而不是每当需要的时候再重新新建不需要了就立刻销毁。</p>
</li>
</ol>
<p>当不再需要某一个上下文时，应该把它销毁来释放它占用的内存资源留给其他的应用使用。根据你的应用程序中的JS使用范围，你可以选择在使用完上下文之后，就立刻销毁，或者，更多情况下，你可以考虑为以后重复使用来保留上下文直到应用程序结束为止。不管哪种情况，当他不再需要用到的时候，可以使用<code>JS_DestroyContext</code>来释放上下文。这个函数带一个参数，也就是指向要销毁的上下文的指针：</p>
<p><code lang="c">JS_DestroyContext(cx);</code></p>
<p>如果你的应用创建了多个运行时的话，应用程序需要了解上下文和哪个运行时相关联。在这种情况下，可以调用<code>JS_GetRuntime</code>，并且把上下文作为参数传递给他。<code>JS_GetRuntime</code>会返回一个指向某个合适的运行时的指针，如果存在的话：</p>
<p><code lang="c">rt = JS_GetRuntime(cx);</code></p>
<p>当你创建一个上下文的时候，你要给他分配栈空间，这个空间将为那些被使用这个上下文的脚本所创建的变量和对象所使用。你也可以用给定的上下文仅仅用来储存大量数据，只要分配所需的最小的栈空间。调用<code>JS_SetContextPrivate</code>函数来建立一个指向上下文使用的私有数据的指针，并调用<code>JS_GetContextPrivate</code>函数来获取这个指针，这样就可以访问这些数据了。你的应用程序要负责创建和管理这个可选的私有数据。</p>
<p>若要创建私有数据并把它和一个上下文关联：</p>
<ol>
<li>
<p>根据需要建立私有数据，可以使用一个普通的 C void 指针变量。</p>
</li>
<li>
<p>调用<code>JS_SetContextPrivate</code>，并指明通过哪个上下文来建立私有数据，并给出指向数据的指针。</p>
</li>
</ol>
<p>例如：</p>
<pre>JS_SetContextPrivate(cx, pdata);</pre>
<p>如果要在以后获取数据，调用<code>JS_GetContextPrivate</code>函数，并把上下文作为参数传递给他。该函数会返回指向私有数据的指针：</p>
<pre>pdata = JS_GetContextPrivate(cx);</pre>
<h2>初始化内置的和全局的JS对象</h2>
<p>JavaScript引擎提供了一些内置对象，他们会简化你的某些开发任务。例如，内置的<code>Array</code>对象让你更方便地在JS引擎中创建和处理数组结构。类似，<code>Date</code>对象提供了一个统一的处理日期数据的机制。要查阅引擎支持的内置对象的完整列表，请参看<a target="doc-frameset" href="sparse-frameset.html%3FJS_InitStandardClasses">JS_InitStandardClasses</a>。</p>
<p>JS引擎始终使用函数和全局对象。一般来说，全局对象存在于JS脚本的场景背后，为所有其它JS对象提供了一个默认的空间范围和存储了你在程序中创建和使用的全局变量。在你创建你自己的对象之前，你需要初始化全局对象。函数对象将启用对象支持和构造器调用。</p>
<p><code>JS_InitStandardClasses</code>, 这个API调用将初始化全局和函数对象还有引擎内置的对象，这样你的应用程序就可以使用他们了：</p>
<pre>JSBool builtins;
.
.
.
builtins = JS_InitStandardClasses(cx, glob);
</pre>
<p><code>JS_InitStandardClasses</code>会返回一个JS boolean值来表示初始化是否成功。</p>
<p>你可以为你的应用程序指定一个不同的全局对象。例如，Netscape Navigator就使用了他自己的全局对象<code>window</code>。若要为你的应用程序更改全局对象，可以调用<code>JS_SetGlobalObject</code>函数。详细信息请查阅<a target="doc-frameset" href="sparse-frameset.html%3FJS_SetGlobalObject">JS_SetGlobalObject</a>的参考条目。</p>
<h2>创建和初始化自定义对象</h2>
<p>除了可以使用引擎内置对象之外，你还可以新建、初始化和使用你自己的JS对象。如果你使用JS引擎处理脚本对你的应用进行自动化操作，这点尤其重要。自定义JS对象可以提供最直接的程序服务，另外他们也可以作为你的程序服务的一个接口。例如，一个自定义JS对象提供了某种直接的服务，像处理应用程序所有的网络访问、作为数据服务的中间层。也可以是使用一个JS对象映射到应用程序中以后的数据和函数中，这样能为C代码提供一个面向对象的接口。这样一个自定义对象对应用程序自身来说扮演了一个接口的角色——从应用程序中把值传递给用户，并且接受和处理用户的输入然后再返回给应用程序。这种对象也可以用来对应用程序内部的函数进行访问控制。</p>
<p>有两种方法可以创建自定义对象：</p>
<ul>
<li>
<p>写一个JS脚本，用来创建对象，以及他的属性、方法和构造器，然后把这个脚本在运行时传递给JS引擎。</p>
</li>
<li>
<p>在你的程序中嵌入定义对象的属性和方法的代码，调用引擎来初始化新对象，然后通过其它的引擎调用来设置对象的属性。这个方法的一个好处是你的程序可以包含直接处理所嵌对象的本地方法。</p>
</li>
</ul>
<p>无论哪种情况，如果你创建了一个对象然后要将他在运行时中持久化，以便在此运行时中可以被其他对象调用，那么你必须通过<code>JS_AddRoot</code>或<code>&nbsp;</code><code>JS_AddNamedRoot</code>调用来确定这个对象的“根”。使用这两个函数会确保JS引擎去跟踪这些对象并在适当的时候通过垃圾收集过程中清理掉他们。</p>
<h2>从脚本中建立一个对象</h2>
<p>要从脚本中创建自定义JS对象的一个原因是，只需要一个在脚本运行期间存在对象。要创建这种持续在脚本调用期间的对象的话，你也可以直接在你的应用程序中嵌入对象的代码，而不用使用一个脚本。</p>
<blockquote><p><strong>注意：</strong>你同样可以使用脚本创建持久对象。</p></blockquote>
<p>要使用脚本创建一个自定义对象：</p>
<ol>
<li>
<p>定义和说明对象。他的目的是什么？他的数据成员（属性）有哪些？他有哪些方法（函数）？他是否需要一个运行时构造函数？</p>
</li>
<li>
<p>编写出定义和创建对象的JS脚本。例如：</p>
<p><code lang="javascript">function myfun(){<br />
var x = newObject();<br />
.<br />
.<br />
.<br />
}<br />
</code></p>
<blockquote><p><strong>注意：</strong>使用JavaScript编写的对象并不在应用程序嵌入JS引擎的代码中。关于对象编写的更多内容，请参阅《客户端JavaScript指导》和《服务器端JavaScript指导》。</p></blockquote>
<p>在应用程序中嵌入合适的JS引擎调用来编译和执行脚本。你有两种选择：1.) 仅使用一个函数调用来编译和执行脚本：<code><a target="doc-frameset" href="sparse-frameset.html%3FJS_EvaluateScript">JS_EvaluateScript</a></code>,<code><a target="doc-frameset" href="sparse-frameset.html%3FJS_EvaluateUCScript">JS_EvaluateUCScript</a></code>或者2.) 使用<code><a target="doc-frameset" href="sparse-frameset.html%3FJS_CompileScript">JS_CompileScript</a></code>或者<code><a target="doc-frameset" href="sparse-frameset.html%3FJS_CompileUCScript">JS_CompileUCScript</a></code>,来一次性编译脚本，然后可以用一个独立的函数调用<code><a target="doc-frameset" href="sparse-frameset.html%3FJS_ExecuteScript">JS_ExecuteScript</a></code>. 来重复执行已经编译的代码。这些调用的“UC”版可以提供对统一码脚本的支持。</p>
</li>
</ol>
<p>你使用脚本创建的一个对象只可以在脚本的生命周期内启用，或者也可以在脚本运行结束之后持久化。一般来说，一旦脚本运行结束，他的所有对象都会被销毁。在大部分情况下，这种行为正是你的应用程序需要的。然而，在其他的情况下，你可能希望某对象持续在脚本之间，或者你的应用程序的整个生命周期。这样的话你需要直接在你的应用程序中嵌入对象创建代码，或者你必须把对象直接连接到全局对象这样他会一直持续只要全局对象本身存在。</p>
<h2>在应用程序中嵌入一个自定义对象</h2>
<p>当必须进行对象持久化时，或者你认为需要对几个脚本都可用的对象时，嵌入一个自定义JS对象在应用程序中是很有用的。例如，一个代表了用户的ID和访问权限的自定义对象可能会在应用程序的整个生命期中都会用到。他事先一次性创建和组装了对象，节省了很多时间，而不用每次要检验用户ID或者权限时一遍又一遍用脚本创建对象。</p>
<p>一种在应用程序中嵌入自定义对象的方法是：</p>
<ol>
<li>
<p>创建一个<code><a target="doc-frameset" href="sparse-frameset.html%3FJSPropertySpec">JSPropertySpec</a></code><code>数据类型，并把它和属性的信息组装成对象的属性，包括参数的获取（get）和设置（set）方法的名称。</code></p>
</li>
<li>
<p>创建一个<code><a target="doc-frameset" href="sparse-frameset.html%3FJSFunctionSpec">JSFunctionSpec</a></code><code>数据类型，并把它和方法的信息组装成对象使用的方法。</code></p>
</li>
<li>
<p>创建一个实际的C函数用来处理对象的方法调用。</p>
</li>
<li>
<p>调用<code><a target="doc-frameset" href="sparse-frameset.html%3FJS_NewObject">JS_NewObject</a></code><code>或者</code><code><a target="doc-frameset" href="sparse-frameset.html%3FJS_ConstructObject">JS_ConstructObject</a></code><code>来实例化这个对象。</code></p>
</li>
<li>
<p>调用<code><a target="doc-frameset" href="sparse-frameset.html%3FJS_DefineFunctions">JS_DefineFunctions</a></code><code>来创建这个对象的方法。</code></p>
</li>
<li>
<p>调用<code><a target="doc-frameset" href="sparse-frameset.html%3FJS_DefineProperties">JS_DefineProperties</a></code><code>来创建这个对象的属性。</code></p>
</li>
</ol>
<p>描述持久的自定义JS对象的代码必须放在应用程序执行的开始部分附近，在任何依赖于该对象的代码之前。嵌入的实例化和组装自定义对象的引擎调用也应该出现在任何依赖这个对象的代码之前。</p>
<blockquote><p><strong>注意：</strong>在大多数情况下还有一个更方便的在程序代码中创建自定义对象的方法是调用<code>JS_DefineObject</code>来创建对象，然后反复调用<code>JS_SetProperty</code>来设置对象的属性。关于定义一个对象的更多的信息，参见<code><a target="doc-frameset" href="sparse-frameset.html%3FJS_DefineObject">JS_DefineObject</a></code>。关于设置对象属性的更多信息，参见<code><a target="doc-frameset" href="sparse-frameset.html%3FJS_SetProperty">JS_SetProperty</a></code>。</p></blockquote>
<h2>为对象提供私有数据</h2>
<p>像上下文那样，你可以把大量的数据和一个对象相关联而无需把数据存储在这个对象中。调用<code>JS_SetPrivate</code>来建立一个指向私有数据的指针，并且调用<code>JS_GetPrivate</code>来获得这个指针这样就可以访问数据了。你的应用程序要对这些可选的私有数据的创建和管理负责。</p>
<p>要创建私有数据并把它和一个对象相关联的话：</p>
<ol>
<li>
<p>根据需要建立私有数据，可以使用一个普通的 C void 指针变量。</p>
</li>
<li>
<p>调用<code>JS_SetPrivate</code>, 制定要为那个对象建立私有数据，并给出指向数据的指针。</p>
</li>
</ol>
<p>例如：</p>
<pre>JS_SetPrivate(cx, obj, pdata);
</pre>
<p>如果要以后再获取数据，调用<code>JS_GetPrivate</code>并且把对象作为一个参数传递。这个函数将返回一个指向对象私有数据的指针：</p>
<pre>pdata =<code>JS_GetPrivate</code>(cx, obj);
</pre>
<h2>处理统一码（Unicode）</h2>
<p>JS引擎现在提供了很多API函数的支持统一码的版本。这些函数允许你直接给引擎传递使用统一码编码的脚本进行编译和运行。下面的表格列出了标准引擎函数和他们对应的统一码版本：</p>
<table cellspacing="2" cellpadding="5" border="0">
<tbody>
<tr>
<th>
<p>标准函数</p>
</th>
<th>
<p>统一码支持函数</p>
</th>
</tr>
<tr>
<td>
<p>JS_DefineProperty</p>
</td>
<td>
<p>JS_DefineUCProperty</p>
</td>
</tr>
<tr>
<td>
<p>JS_DefinePropertyWithTinyId</p>
</td>
<td>
<p>JS_DefineUCPropertyWithTinyId</p>
</td>
</tr>
<tr>
<td>
<p>JS_LookupProperty</p>
</td>
<td>
<p>JS_LookupUCProperty</p>
</td>
</tr>
<tr>
<td>
<p>JS_GetProperty</p>
</td>
<td>
<p>JS_GetUCProperty</p>
</td>
</tr>
<tr>
<td>
<p>JS_SetProperty</p>
</td>
<td>
<p>JS_SetUCProperty</p>
</td>
</tr>
<tr>
<td>
<p>JS_DeleteProperty2</p>
</td>
<td>
<p>JS_DeleteUCProperty2</p>
</td>
</tr>
<tr>
<td>
<p>JS_CompileScript</p>
</td>
<td>
<p>JS_CompileUCScript</p>
</td>
</tr>
<tr>
<td>
<p>JS_CompileScriptForPrincipals</p>
</td>
<td>
<p>JS_CompileUCScriptForPrincipals</p>
</td>
</tr>
<tr>
<td>
<p>JS_CompileFunction</p>
</td>
<td>
<p>JS_CompileUCFunction</p>
</td>
</tr>
<tr>
<td>
<p>JS_CompileFunctionForPrincipals</p>
</td>
<td>
<p>JS_CompileUCFunctionForPrincipals</p>
</td>
</tr>
<tr>
<td>
<p>JS_EvaluateScript</p>
</td>
<td>
<p>JS_EvaluateUCScript</p>
</td>
</tr>
<tr>
<td>
<p>JS_EvaluateScriptForPrincipals</p>
</td>
<td>
<p>JS_EvaluateUCScriptForPrincipals</p>
</td>
</tr>
<tr>
<td>
<p>JS_NewString</p>
</td>
<td>
<p>JS_NewUCString</p>
</td>
</tr>
<tr>
<td>
<p>JS_NewStringCopyN</p>
</td>
<td>
<p>JS_NewUCStringCopyN</p>
</td>
</tr>
<tr>
<td>
<p>JS_NewStringCopyZ</p>
</td>
<td>
<p>JS_NewUCStringCopyZ</p>
</td>
</tr>
<tr>
<td>
<p>JS_InternString</p>
</td>
<td>
<p>JS_InternUCString</p>
</td>
</tr>
<tr>
<td>
<p>&#8211;</p>
</td>
<td>
<p>JS_InternUCStringN</p>
</td>
</tr>
</tbody>
</table>
<p>处理统一码的函数工作方式与原来的同名函数一样，除了原来的函数使用参数<code>char *</code>，而统一码版本的函数参数为<code>jschar *</code><code>。</code></p>
<h2>操作JS数据类型</h2>
<p>JavaScript定义了他自己的数据类型。其中一部分直接对应C中的副本。其他的，诸如<code>JSObject</code>,<code>jsdouble</code>, 和<code>&nbsp;</code><code>JSString</code>，对 JavaScript有特殊意义。</p>
<p>一般而言，你在应用程序中声明和使用JS数据类型就和使用标准C数据类型一样。然而，JS引擎对JS数据类型，也就是需要超过一个字空间的变量变量<code>JSObject</code>,<code>jsdouble</code>, 和<code>JSString</code><code>有不同的跟踪。引擎周期性地检查这些变量，察看他们是否还在使用中。如果不再使用了，就收集他们，释放存储空间来重新使用。</code></p>
<p>垃圾收集可以有效重复利用堆的资源，但是过分频繁的垃圾收集也会对性能造成影响。你可以根据JS运行时控制垃圾收集的频率，根据你给程序分配的JS运行时的大小和你应用程序使用的JS变量和对象的数量之间的关系。如果你的程序要创建和使用很多JS对象和变量，你可能就要分配足够大的运行时来减少垃圾收集的可能频率。</p>
<blockquote><p><strong>注意</strong>你的应用程序要在任何时候调用同样能<code><a target="doc-frameset" href="sparse-frameset.html%3FJS_GC">JS_GC</a></code>或者<code><a target="doc-frameset" href="sparse-frameset.html%3FJS_MaybeGC">JS_MaybeGC</a></code>来强制进行垃圾收集。<code>JS_GC</code>将强制进行垃圾收集。<code>JS_MaybeGC</code>则会根据条件进行垃圾收集，如果你调用这个函数时，初始化时分配的空间的特定比例已经被使用的话，就进行垃圾收集。</p></blockquote>
<h2>操作JS值</h2>
<p>除了JS数据类型之外，JS引擎也使用JS值，称之为<code>jsval</code>。一个<code>jsval</code>本质上是一个指向任意JS数据类型（除了整型）的一个指针。对于整型，<code>jsval</code>直接包含了他自身的整数值。在其他的情况下，指针还会被编码，添加关于它所指的数据的类型的额外信息。使用可以提高引擎的效率，同时也可以让很多API函数来处理不同类型的数据。</p>
<p>引擎API包含了一组用来测试JS值中的JS数据类型的宏。有：</p>
<ul>
<li>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJSVAL_IS_OBJECT">JSVAL_IS_OBJECT</a></code></p>
</li>
<li>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJSVAL_IS_NUMBER">JSVAL_IS_NUMBER</a></code></p>
</li>
<li>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJSVAL_IS_INT">JSVAL_IS_INT</a></code></p>
</li>
<li>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJSVAL_IS_DOUBLE">JSVAL_IS_DOUBLE</a></code></p>
</li>
<li>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJSVAL_IS_STRING">JSVAL_IS_STRING</a></code></p>
</li>
<li>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJSVAL_IS_BOOLEAN">JSVAL_IS_BOOLEAN</a></code></p>
</li>
</ul>
<p>Besides testing a<code>jsval</code>，你也可以检测他是否属于一个基本JS数据类型 (<code><a target="doc-frameset" href="sparse-frameset.html%3FJSVAL_IS_PRIMITIVE">JSVAL_IS_PRIMITIVE</a></code>)。基本类型包括未定义（undefined）、空（null）、 布尔（boolean）、数值（numeric）和字符串（string）类型。</p>
<p>你可以测试<code>jsval</code>所指的值是否为<code>NULL</code>(<code><a target="doc-frameset" href="sparse-frameset.html%3FJSVAL_IS_NULL">JSVAL_IS_NULL</a></code>) 或者<code>void</code>(<code><a target="doc-frameset" href="sparse-frameset.html%3FJSVAL_IS_VOID">JSVAL_IS_VOID</a></code>)。</p>
<p>如果<code>jsval</code>指向了一个JS数据类型是<code>JSObject</code>,<code>jsdouble</code>, 或者<code>jsstr</code>，你可以将jsval转换成他的内在的类型，只要相应使用<code>JSVAL_TO_OBJECT</code>,<code>JSVAL_TO_DOUBLE</code>和<code>JSVAL_TO_STRING</code>。在某些情况下，你的应用程序或者JS引擎调用要求使用一个特定的数据类型的变量或者参数而非一个<code>jsval</code>时，就很有用了。类似地，你可以使用<code>OBJECT_TO_JSVAL</code>,<code>DOUBLE_TO_JSVAL</code>, 和<code>STRING_TO_JSVAL</code>, 把<code>JSObject</code>,<code>jsdouble</code>, 和<code>jsstr</code>相应地转换成<code>jsval</code>。</p>
<h2>操作JS字符串</h2>
<p>在JavaScript中你的很多工作都回涉及字符串。JS引擎实现了一个JS字符串类型，<code>JSString</code>，一个指向JS字符—<code>jschar</code>—数组的指针，用来处理支持统一码的字符串。引擎也实现了一系列丰富的通用和统一码字符串管理程序。最后，JS引擎提供了对限定字符串的支持，这可以将两个或多个相同的字符串创建时在内存中共享一个单独的实例。对于<code>JSString</code>类型的字符串，引擎会跟踪和管理字符串资源。</p>
<p>通常情况下，当你在处理JS引擎使用的字符串时，你应该使用JS API中的字符串处理函数来创建和复制字符串。还有创建以空字符结尾的和特定长度的字符串的例程，以及获取字符串长度和比较字符串。</p>
<h2>统一码字符串支持</h2>
<p>使用统一码（Unicode）的API字符串函数的名称和标准的引擎API字符串行数是一一对应的，规则如下：如果标准函数名是<code>JS_NewStringCopyN</code>，相应的统一码版本就是<code>JS_NewUCStringCopyN</code><code>。同样有针对限定字符串的支持统一码的API字符串函数。</code></p>
<h2>限定字符串支持</h2>
<p>为了节省存储空间，JS引擎提供了对共享一个单独的字符串实例支持，这些字符串属于一些独立的不可变化的文字。这种被共享的字符串被称为“限定字符串”（interned string）。当你觉得某个特定的文本会被创建并且反复在程序中使用多次的话，那可以使用限定字符串。</p>
<p>引擎的API提供了几种工作于限定字符串的函数调用：</p>
<ul>
<li>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJS_InternString">JS_InternString</a></code>, 用来创建或者复用一个<code>JSString</code>.</p>
</li>
<li>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJS_InternUCString">JS_InternUCString</a></code>, 用来创建或者复用一个统一码的<code>JSString</code>.</p>
</li>
<li>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJS_InternUCStringN">JS_InternUCStringN</a></code>, 用以创建或者复用一个固定长度的统一码<code>JSString</code>。</p>
</li>
</ul>
<h2>管理安全性</h2>
<p>现在使用JavaScript 1.3，JS引擎加入了安全性增强API函数来编译和执行传送给引擎的脚本和函数。JS安全模型是基于Java的基本安全模型的。该模型提供了一个公共安全接口，但是实际的安全控制由你去实现。
</p>
<p>在使用JavaScript的应用中使用安全管理的一个常用的方法是比较脚本的来源和限制脚本的交互。例如，你可能会比较两个或多个脚本的代码源并且只允许来自相同的代码源的脚本修改共享代码源的脚本的属性。</p>
<p>如要实现安全JS，请按照以下几步：</p>
<ol>
<li>
<p>在代码中声明一个或多个<code>JSPrincipals</code>类型的结构体（struct）。</p>
</li>
<li>
<p>把实现了安全信息的函数列表添加到数组中。这些包括了为程序提供原则数组的函数，和使用给定原则的JS对象的引用计数增减机制。</p>
</li>
<li>
<p>把<code>JSPrincipals</code>结构和你的安全信息组装起来。这个信息可以包括一般代码源信息。</p>
</li>
<li>
<p>在运行时，使用一些特定的JS API调用来编译和执行所有要应用安全性的脚本和函数，他们将要求传递一个<code>JSPrincipals</code>结构。下面的表格列出了这些API函数和他们的作用：</p>
</li>
</ol>
<dl>
<dd>
<table cellspacing="2" cellpadding="5" border="0">
<tbody>
<tr>
<td>
<p>函数</p>
</td>
<td>
<p>目的</p>
</td>
</tr>
<tr>
<td>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJS_CompileScriptForPrincipals">JS_CompileScriptForPrincipals</a></code></p>
</td>
<td>
<p>编译（但是不执行）一个启用安全控制的脚本。</p>
</td>
</tr>
<tr>
<td>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJS_CompileUCScriptForPrincipals">JS_CompileUCScriptForPrincipals</a></code></p>
</td>
<td>
<p>编译（但不执行）一个启用安全控制、统一码编码的脚本。</p>
</td>
</tr>
<tr>
<td>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJS_CompileFunctionForPrincipals">JS_CompileFunctionForPrincipals</a></code></p>
</td>
<td>
<p>从一个文本串创建一个启用安全控制的JS函数。</p>
</td>
</tr>
<tr>
<td>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJS_CompileUCFunctionForPrincipals">JS_CompileUCFunctionForPrincipals</a></code></p>
</td>
<td>
<p>从一个统一码编码的字符串中创建一个带安全信息的JS函数。</p>
</td>
</tr>
<tr>
<td>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJS_EvaluateScriptForPrincipals">JS_EvaluateScriptForPrincipals</a></code></p>
</td>
<td>
<p>编译和执行一个启用安全控制的脚本。</p>
</td>
</tr>
<tr>
<td>
<p><code><a target="doc-frameset" href="sparse-frameset.html%3FJS_EvaluateUCScriptForPrincipals">JS_EvaluateUCScriptForPrincipals</a></code></p>
</td>
<td>
<p>编译并执行一个启用安全控制且用统一码编码的脚本。</p>
</td>
</tr>
</tbody>
</table>
</dd>
</dl>
]]></content:encoded>
			<wfw:commentRss>http://shiningray.cn/javascript-c-engine-embedders-guide.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>JavaScript中的类继承</title>
		<link>http://shiningray.cn/classical-inheritance-in-javascript.html</link>
		<comments>http://shiningray.cn/classical-inheritance-in-javascript.html#comments</comments>
		<pubDate>Sun, 02 Apr 2006 08:27:04 +0000</pubDate>
		<dc:creator>ShiningRay</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[类]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">http://shiningray.cn/classical-inheritance-in-javascript.html</guid>
		<description><![CDATA[JavaScript中的类继承 DouglasCrockfordwww.crockford.com 翻译 ShiningRay @ www.nirvanastudio.org And you think you&#8217;re so clever and classless and free&#8211;John Lennon JavaScript一种没有类的，面向对象的语言，它使用原型继承来代替类继承。这个可能对受过传统的面向对象语言（如C++和Java）训练的程序员来说有点迷惑。JavaScript的原型继承比类继承有更强大的表现力，现在就让我们来看看。 Java JavaScript 强类型 弱类型 静态 动态 基于类 基于原型 类 函数 构造器 函数 方法 函数 但首先，为什么我们如此关心继承呢？主要有两个原因。第一个是类型有利。我们希望语言系统可以自动进行类似类型引用的转换cast。小类型安全可以从一个要求程序显示地转换对象引用的类型系统中获得。这是强类型语言最关键的要点，但是这对像JavaScript这样的弱类型语言是无关的，JavaScript中的类引用无须强制转换。 第二个原因是为了代码的复用。在程序中常常会发现很多对象都会实现同一些方法。类让建立单一的一个定义集中建立对象成为可能。在对象中包含其他对象也包含的对象也是很常见的，但是区别仅仅是一小部分方法的添加或者修改。类继承对这个十分有用，但原型继承甚至更有用。 要展示这一点，我们要介绍一个小小的“甜点”可以主我们像一个常规的类语言一样写代码。我们然后会展示一些在类语言中没有的有用的模式。最后，我们会就会解释这些“甜点”。 类继承 首先，我们建立一个Parenizor类，它有成员 value的get和set方法，还有一个会将value包装在括号内的toString方法。 function Parenizor(value) { this.setValue(value); } Parenizor.method('setValue', function (value) { this.value = value; return this; }); Parenizor.method('getValue', function [...]]]></description>
			<content:encoded><![CDATA[<h1 align="center">JavaScript中的类继承</h1>
<p align="center"><a href="mailto:douglas@crockford.com">DouglasCrockford</a><br /><tt><a href="http://www.crockford.com/">www.crockford.com</a></tt></p>
<p align="center">翻译 <a href="http://shiningray.cn">ShiningRay</a> @ <a href="http://www.nirvanastudio.org/">www.nirvanastudio.org</a></p>
<p align="center"><cite>And you think you&#8217;re so clever and classless and free</cite><br />&#8211;John Lennon</p>
<p><a href="http://www.crockford.com/javascript">JavaScript</a>一种没有类的，面向对象的语言，它使用原型继承来代替类继承。这个可能对受过传统的面向对象语言（如C++和Java）训练的程序员来说有点迷惑。JavaScript的原型继承比类继承有更强大的表现力，现在就让我们来看看。</p>
<table cellspacing="0" cellpadding="2" rules="none" width="204" border="1" frame="void">
<colgroup>
<col width="99" />
<col width="97" /></colgroup>
<tbody>
<tr>
<th width="99">
<p>Java</p>
</th>
<th width="97">
<p>JavaScript</p>
</th>
</tr>
<tr>
<td width="99">
<p>强类型</p>
</td>
<td width="97">
<p>弱类型</p>
</td>
</tr>
<tr>
<td width="99">
<p>静态</p>
</td>
<td width="97">
<p>动态</p>
</td>
</tr>
<tr>
<td width="99">
<p>基于类</p>
</td>
<td width="97">
<p>基于原型</p>
</td>
</tr>
<tr>
<td width="99">
<p>类</p>
</td>
<td width="97">
<p>函数</p>
</td>
</tr>
<tr>
<td width="99">
<p>构造器</p>
</td>
<td width="97">
<p>函数</p>
</td>
</tr>
<tr>
<td width="99">
<p>方法</p>
</td>
<td width="97">
<p>函数</p>
</td>
</tr>
</tbody>
</table>
<p>但首先，为什么我们如此关心继承呢？主要有两个原因。第一个是类型有利。我们希望语言系统可以自动进行类似类型引用的转换<i>cast</i>。小类型安全可以从一个要求程序显示地转换对象引用的类型系统中获得。这是强类型语言最关键的要点，但是这对像JavaScript这样的弱类型语言是无关的，JavaScript中的类引用无须强制转换。</p>
<p>第二个原因是为了代码的复用。在程序中常常会发现很多对象都会实现同一些方法。类让建立单一的一个定义集中建立对象成为可能。在对象中包含其他对象也包含的对象也是很常见的，但是区别仅仅是一小部分方法的添加或者修改。类继承对这个十分有用，但原型继承甚至更有用。</p>
<p>要展示这一点，我们要介绍一个小小的“甜点”可以主我们像一个常规的类语言一样写代码。我们然后会展示一些在类语言中没有的有用的模式。最后，我们会就会解释这些“甜点”。</p>
<h2>类继承</h2>
<p>首先，我们建立一个<tt>Parenizor</tt>类，它有成员 <tt>value</tt>的<tt>get</tt>和<tt>set</tt>方法，还有一个会将<tt>value</tt>包装在括号内的<tt>toString</tt>方法。</p>
<pre lang="javascript">function Parenizor(value) {
    this.setValue(value);
}

Parenizor.method('setValue', function (value) {
    this.value = value;
    return this;
});

Parenizor.method('getValue', function () {
    return this.value;
});

Parenizor.method('toString', function () {
    return '(' + this.getValue() + ')';
});</pre>
<p>这个语法可能没什么用，但它很容易看出其中类的形式。<tt>method</tt>方法接受一个方法名和一个函数，并把它们放入类中作为公共方法。</p>
<p>现在我们可以写成</p>
<pre lang="javascript">myParenizor = new Parenizor(0);
myString = myParenizor.toString();</pre>
<p>正如期望的那样，<tt>myString</tt>是 <tt>&quot;(0)&quot;</tt><tt>。</tt></p>
<p>现在我们要建立另一个继承自<tt>Parenizor</tt>的类，它基本上是一样的除了<tt>toString</tt>方法将会产生<tt>&quot;-0-&quot;</tt>如果<tt>value</tt>是零或者空。</p>
<pre lang="javascript">function ZParenizor(value) {
    this.setValue(value);
}

ZParenizor.inherits(Parenizor);

ZParenizor.method(&quote;toString&quote;, function () {
    if (this.getValue()) {
        return this.uber('toString');
    }
    return &quot;-0-&quot;;
});</pre>
<p><tt>inherits</tt>方法类似于Java的<tt>extends </tt>。<tt>uber</tt>方法类似于<tt>Java</tt>的<tt>super</tt>。它令一个方法调用父类的方法（更改了名称是为了避免和保留字冲突）。</p>
<p>我们可以写成这样</p>
<pre lang="javascript">myZParenizor = new ZParenizor(0);
myString = myZParenizor.toString();</pre>
<p>这次, <tt>myString</tt>是 <tt>&quot;-0-&quot;</tt>.</p>
<p>JavaScript 并没有类，但我们可以编程达到这个目的。</p>
<h2>多继承</h2>
<p>通过操作一个函数的<tt>prototype</tt>对象，我们可以实现多继承。混合多继承难以实现而且可能会遭到名称冲突的危险。我们可以在JavaScript中实现混合多继承，但这个例子我们将使用一个较规范的形式称为瑞士继承<a href="http://www.cosmik.com/aa-october99/stan_freberg.html"><i>SwissInheritance</i></a>.</p>
<p>假设有一个<tt>NumberValue</tt>类有一个<tt>setValue</tt>方法用来检查 <tt>value</tt><tt>是不是在一个指定范围内的一个数，并在适当的时候抛出异常。我们只要它的</tt><tt>setValue</tt><tt>和 </tt><tt>setRange</tt><tt>方法给我们的</tt><tt>ZParenizor</tt><tt>。我们当然不想要它的</tt><tt>toString</tt><tt>方法。这样，我们写到：</tt></p>
<pre lang="javascript">ZParenizor.swiss(NumberValue, 'setValue', 'setRange');</pre>
<p>这个将仅仅添加需要的方法。</p>
<h2>寄生继承</h2>
<p>这是另一个书写 <tt>ZParenizor</tt><tt>类的方法。并不从 </tt><tt>Parenizor</tt><tt>继承，而是写了一个调用了</tt><tt>Parenizor</tt><tt>构造器的构造器，并对结果修改最后返回这个结果。这个构造器添加的是特权方法而非公共方法。</tt></p>
<pre lang="javascript">function ZParenizor2(value) {
    var self = new Parenizor(value);
    self.toString = function () {
        if (this.getValue()) {
            return this.uber('toString');
        }
        return &quot;-0-&quot;
    };
    return self;
}</pre>
<p>类继承是一种“是……”的关系，而寄生继承是一个关于“原是……而现在是……”的关系。构造器在对象的构造中扮演了大量的角色。注意<tt>uber </tt><tt>（代替</tt><tt>super</tt><tt>关键字）对特权方法仍有效。</tt></p>
<h2>类扩展</h2>
<p>JavaScript的动态性让我们可以对一个已有的类添加或替换方法。我们可以在任何时候调用方法。我们可以随时地扩展一个类。继承不是这个方式。所以我们把这种情况称为“类扩展”来避免和Java的<tt>extends──</tt>也叫扩展，但不是一回事──相混淆。</p>
<h2>对象扩展</h2>
<p>在静态面向对象语言中，如果你想要一个对象和另一个对象有所区别，你必须新建立一个类。但在JavaScript中，你可以向单独的对象添加方法而不用新建类。这会有巨大的能量因为你就可以书写尽量少的类，类也可以写得更简单。想想JavaScript的对象就像哈希表一样。你可以在任何时候添加新的值。如果这个值是一个函数，那他就会成为一个方法。</p>
<p>这样在上面的例子中，我完全不需要 <tt>ZParenizor</tt>类。我只要简单修改一下我的实例就行了。</p>
<pre lang="javascript">myParenizor = new Parenizor(0);
myParenizor.toString = function () {
    if (this.getValue()) {
        return this.uber('toString');
    }
    return &quot;-0-&quot;;
};
myString = myParenizor.toString();</pre>
<p>我们给 <tt>myParenizor</tt>实例添加了一个 <tt>toString</tt>方法而没有使用任何继承。我们可以演化单独的实例因为这个语言是无类型的。</p>
<h2>小甜点</h2>
<p>要让上面的例子运行起来，我写了四个“甜点”方法。首先，<tt>method</tt>方法，可以把一个实例方法添加到一个类中。</p>
<pre lang="javascript">Function.prototype.method = function (name, func) {
    this.prototype[name] = func;
    return this;
};</pre>
<p>这个将会添加一个公共方法到 <tt>Function.prototype</tt>中，这样通过类扩展所有的函数都可以用它了。它要一个名称和一个函数作为参数。</p>
<p>它返回 <tt>this</tt>。当我写一个没有返回值的方法时，我通常都会让它返回<tt>this</tt>。这样可以形成链式语句。</p>
<p>下面是 <tt>inherits</tt>方法，它会指出一个类是继承自另一个类的。它必须在两个类都定义完了之后才能定义，但要在方法继承之前调用。</p>
<pre lang="javascript">Function.method('inherits', function (parent) {
    var d = 0, p = (this.prototype = new parent());

    this.method('uber', function uber(name) {
        var f, r, t = d, v = parent.prototype;
        if (t) {
            while (t) {
                v = v.constructor.prototype;
                t -= 1;
            }
            f = v[name];
        } else {
            f = p[name];
            if (f == this[name]) {
                f = v[name];
            }
        }
        d += 1;
        r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
        d -= 1;
        return r;
    });
    return this;
});</pre>
<p>再来，我们扩展 <tt>Function</tt><tt>类。我们加入一个 </tt><tt>parent</tt><tt>类的实例并将它做为新的</tt><tt>prototype</tt>。我们也必须修正<tt>constructor</tt>字段，同时我们加入<tt>uber</tt>方法。</p>
<p><tt>uber</tt>方法将会在自己的<tt>prototype</tt>中查找某个方法。这个是寄生继承或类扩展的一种情况。如果我们是类继承，那么我们要找到<tt>parent</tt>的<tt>prototype</tt>中的函数。<tt>return</tt>语句调用了函数的<tt>apply</tt>方法来调用该函数，同时显示地设置<tt>this</tt>并传递参数。参数（如果有的话）可以从<tt>arguments</tt>数组中获得。不幸的是，<tt>arguments</tt>数组并不是一个真正的数组，所以我们又要用到<tt>apply</tt>来调用数组中的<tt>slice</tt>方法。</p>
<p>最后，<tt>swiss</tt>方法</p>
<pre lang="javascript">Function.method('swiss', function (parent) {
    for (var i = 1; i &lt; arguments.length; i += 1) {
        var name = arguments[i];
        this.prototype[name] = parent.prototype[name];
    }
    return this;
});</pre>
<p>The <tt>swiss</tt>方法对每个参数进行循环。每个名称，它都将<tt>parent</tt>的原型中的成员复制下来到新的类的<tt>prototype</tt>中。</p>
<h2>总结</h2>
<p>JavaScript可以像类语言那样使用，但它也有一种十分独特的表现层次。我们已经看过了类继承、瑞士继承、寄生继承、类扩展和对象扩展。这一等系列代码复用的模式都能来自这个一直被认为是很小、很简单的JavaScript语言。</p>
<p>类对象属于“硬的”。给一个“硬的”对象添加成员的唯一的方法是建立一个新的类。在JavaScript中，对象是“软的”。要给一个“软”对象添加成员只要简单的赋值就行了。</p>
<p>因为JavaScript中的类是这样地灵活，你可能会还想到更复杂的类继承。但深度继承并不合适。浅继承则较有效而且更易表达。</p>
]]></content:encoded>
			<wfw:commentRss>http://shiningray.cn/classical-inheritance-in-javascript.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
