<?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>西红柿爱番茄 &#187; lazy definition</title>
	<atom:link href="http://www.ilovejs.net/archives/tag/lazy-definition/feed" rel="self" type="application/rss+xml" />
	<link>http://www.ilovejs.net</link>
	<description>到了创造为主的阶段，不忘继续学习</description>
	<lastBuildDate>Thu, 15 Dec 2011 06:18:39 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>浅析Memoization</title>
		<link>http://www.ilovejs.net/archives/1040</link>
		<comments>http://www.ilovejs.net/archives/1040#comments</comments>
		<pubDate>Tue, 13 Jul 2010 08:27:14 +0000</pubDate>
		<dc:creator>Supersha</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[性能优化]]></category>
		<category><![CDATA[lazy definition]]></category>
		<category><![CDATA[Memoization]]></category>

		<guid isPermaLink="false">http://www.ilovejs.net/?p=1040</guid>
		<description><![CDATA[Memoization，简单的说就是优化计算机的性能，缓存那些重复性的函数操作和计算，使得第一次之后的调用可以直接从缓存中得到结果，而无需重新计算和运行复杂、费时的函数。这个跟Lazy Definition的原理是比较相似的，Lazy Definition主要是对函数进行重复定义，避免浏览器检测等恶心的事情。在javascript里实现Memoization的技术，Keith Gaughan做了相关的叙述：Memoization in JavaScript。特别是它对Fibonacci的优化让我特别的玩味。 对于Fibonacci普通的实现方式是： [javascript] //这个性能不咋的，函数调用太频繁了 function Fib(n) { if (n &#60; 2) { return n;} return &#8230; <a href="http://www.ilovejs.net/archives/1040" class="more-link">了解更多</a>]]></description>
			<content:encoded><![CDATA[<p>
<a  href="http://en.wikipedia.org/wiki/Memoization">Memoization</a>，简单的说就是优化计算机的性能，缓存那些重复性的函数操作和计算，使得第一次之后的调用可以直接从缓存中得到结果，而无需重新计算和运行复杂、费时的函数。这个跟<a  href="http://www.ilovejs.net/archives/126">Lazy Definition</a>的原理是比较相似的，L<a  href="http://www.ilovejs.net/archives/126">azy Definition</a>主要是对函数进行重复定义，避免浏览器检测等恶心的事情。在javascript里实现Memoization的技术，<a  href="http://talideon.com">Keith Gaughan</a>做了相关的叙述：<a  href="http://talideon.com/weblog/2005/07/javascript-memoization.cfm">Memoization in JavaScript</a>。特别是它对Fibonacci的优化让我特别的玩味。
</p>
<p>
对于Fibonacci普通的实现方式是：
</p>
<p>[javascript]<br />
//这个性能不咋的，函数调用太频繁了<br />
function Fib(n) {<br />
    if (n &lt; 2) { return n;}<br />
    return Fib(n &#8211; 1) + Fib(n &#8211; 2);<br />
}<br />
[/javascript]<br />
而<a  href="http://talideon.com">Keith Gaughan</a>对它的实现方式用Memoization进行了优化：<br />
[javascript]<br />
var IterMemoFib = function() {<br />
    var cache = [1, 1];<br />
    var fib = function(n) {<br />
        if (n &gt;= cache.length) {<br />
            for (var i = cache.length; i &lt;= n; i++) {<br />
                //这句代码很耐人寻味，还有循环的方式<br />
                cache[i] = cache[i - 2] + cache[i - 1];<br />
            }<br />
        }<br />
        return cache[n];<br />
    }<br />
    return fib;<br />
}();<br />
[/javascript]</p>
<p>
上面关于Fibonacci的优化，就是使用了Memoization技巧，将上一步的加法操作缓存起来，用户循环中下一轮的递增。缓存计算结果是Memorize应用最多的需求，避免了对计算结果的重复性计算，但是如果计算结果是动态的呢，或者说你想获取动态修改过后的计算结果呢？ 比如DOM的length属性，这个是易变的，这个时候就不能去获取缓存的结果了，需要重复计算。
</p>
<p><span id="more-1040"></span></p>
<p>
下面先来看一段<a href="http://blog.stevenlevithan.com/">Flagrant Badassery</a>编写的一个Timed Memoization函数，一方面用于缓存结果，另一方面则是在时间参数指定的时间间隔里清除掉缓存的结果：
</p>
<p>[javascript]<br />
var memorize=function(fn,timer){<br />
  var obj = {};<br />
  return function(){<br />
    var key = [].join.call(arguments,&quot;$&quot;);<br />
	if(timer){<br />
	     setTimeout(function(){delete obj[key];},timer);<br />
	}<br />
        //这个返回方式本人进行了一点修改<br />
	return (key in obj) ? obj[key] : obj[key] = fn.apply(this,arguments);<br />
  }<br />
}<br />
[/javascript]<br />
上面的代码思路不错，在指定的时间之后清楚缓存结果，但是我觉得这个思路还可以用于更多场合，比如清除对象，及时断开作用域链，使得无用的对象可以及时的回收占用的内存。基于这个思路，本人做了些修修补补，编写了下面的几个方法：<br />
[javascript]<br />
//将memorize函数生成的函数也缓存起来<br />
var memorize2 = function(fn,timer){<br />
  var key = [].join.call(arguments,&quot;$&quot;),obj={};<br />
  return memorize2[key] ? memorize2[key] : (memorize2[key] = function(){<br />
    var k = [].join.call(arguments,&quot;$&quot;);<br />
	if(timer){<br />
	  setTimeout(function(){delete obj[k];delete memorize2[key];},timer);<br />
	}<br />
	return obj[k] ? obj[k] : (obj[k] = fn.apply(this,arguments);<br />
  }<br />
}<br />
[/javascript]<br />
对于上面说到的，如果有些计算结果是会动态改变的，这时候就该在需要的时候进行重复计算，为此，编写了一个Function扩展，借鉴上面Timed Momeization的思路：<br />
[javascript]<br />
Function.prototype.memorize = function(timer){<br />
  var obj = {}, that = this, key = &quot;$&quot;;<br />
  return function(flag){<br />
    var args = [].slice.call(arguments,1) || [];<br />
	if(flag){<br />
	  return obj[key] = that.apply(this,args);<br />
	}<br />
	key = [].join.call(arguments,&quot;$&quot;);<br />
	if(timer){<br />
	  setTimeout(function(){delete obj[key];},timer);<br />
	}<br />
	return obj[key] ? obj[key] : obj[key] = that.apply(this,args);<br />
  }<br />
}<br />
[/javascript]</p>
<p>
如上面代码所示，使用memorize返回的函数中带有一个flag参数，如果不存在这个参数或者设置为false，则从缓存中去获取上一步缓存的结果，如果设置为true，则重复计算、并重新设置缓存的值。通过这样的实现方式，既可以通过参数的形式来判断获取动态改变的结果，也可以在不需要动态改变参数的时候获取缓存的结果，《<a  href="http://www.ilovejs.net/lab/memorize/">测试用例</a>》。使用方式如下：
</p>
<p>[javascript]<br />
var length = function(){<br />
  return div.getElementsByTagName(&quot;p&quot;).length;<br />
}.memorize();<br />
[/javascript]</p>
<p>
为此，Memoization对于一些静态的计算结果进行缓存，从而起到优化代码性能的作用；但是对于会动态改变的计算结果，则需要另当别论了。
</p>
<p>更多的参考文章：《<a  href="http://osteele.com/archives/2006/04/javascript-memoization">ONE-LINE JAVASCRIPT MEMOIZATION</a>》，《<a  href="http://talideon.com/weblog/2005/07/javascript-memoization.cfm">Memoization in JavaScript</a>》，《<a  href="http://blog.stevenlevithan.com/archives/timed-memoization">Timed Memoization</a>》，《<a  href="http://www.ilovejs.net/archives/126">Javascript的Lazy Definition Pattern</a>》</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ilovejs.net/archives/1040/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>并行加载、按顺序执行javascript文件</title>
		<link>http://www.ilovejs.net/archives/976</link>
		<comments>http://www.ilovejs.net/archives/976#comments</comments>
		<pubDate>Mon, 21 Jun 2010 04:27:11 +0000</pubDate>
		<dc:creator>Supersha</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[lazy definition]]></category>
		<category><![CDATA[并行加载]]></category>
		<category><![CDATA[性能优化]]></category>

		<guid isPermaLink="false">http://www.ilovejs.net/?p=976</guid>
		<description><![CDATA[之前一直在想法设法的实现js脚本文件按顺序执行，而且并行下载的测试，但是都无终而返。最先设想的是通过轮询的方式来实现按顺序加载，可是这样就导致不能并行加载了。 今天又拾起这个一直未实现的冲动，首先既然要并行加载、按顺序执行，那么就必须得给每一个js文件开一个http请求，而且这个请求是无顺序的，即是加载的顺序是无顺序的，但是在执行的时候，就得要按照声明的顺序来执行，比如下面的代码示例： [javascript] jL.load({&#34;label&#34;:&#34;test&#34;,&#34;url&#34;:&#34;test.js&#34;,isOrder:true}); jL.load({&#34;label&#34;:&#34;test2&#34;,&#34;url&#34;:&#34;test2.js&#34;,isOrder:false}); jL.load({&#34;label&#34;:&#34;test3&#34;,&#34;url&#34;:&#34;test3.js&#34;,isOrder:true,callback:function(){ alert(&#34;test3.js is load&#34;); }}).insert(); [/javascript] 上面的代码中，load方法的参数中包含是否按顺序加载脚本文件，并且在脚本加载下来的时候包含callback函数回调。如果不是按顺序下载的，则直接使用DOM Element的方式插入script到document.body中；insert方法负责开始操作那些俺顺序执行的脚本文件的加载和插入工作；label参数对于需要按顺序执行的脚本文件必不可少，用于标识当前脚本，对无需按顺序执行的脚本可有可无。 刚开始设想的原理是这样的：把需要按顺序执行的脚本通过load方法都临时储存到一个数组里（如果不需要，则直接在load方法里通过DOM Element方法插入），之后调用insert方法来执行上面临时数组（使用XHR Injection的方式获得每一个脚本的responseText），并将请求返回的脚本的内容也暂且储存在临时变量里，待全部的脚本都请求下来之后，再把全部脚本的内容都插入到Document文档里使得脚本起作用。《测试示例1》 上面的方案虽然实现了并行加载、按顺序执行，但是存在诸多弊端，如果有脚本文件比较大，请求的时间比较长，那么先于它加载下来的脚本就迟迟得不到执行了，而且在处理callback回调函数的时候也是在全部脚本加载下来以后才执行。 为此，在这个基础上，修改了实现方式：按顺序执行的脚本仍然使用XHR的方式来加载，并把内容临时储存在数组里，同时通过label标识着脚本文件添加一个load的boolean属性来标识该脚本是否已经加载完成。这样，我就可以使用定时器(setInterval)来检查按label标识顺序排好的数组来检查load属性是否为true，这样来插入脚本到文档中。这样，就可以尽早的将声明在前面的脚本在加载完成的时候就插入到文档内。《测试页面》 在代码中，因为在重复调用insert方法的情况下要处理临时数组和对象的清空问题比较难解决，所以就进行了再一次的封装，支持链式调用，而且总共也才只有load和insert方法，参数说明：label用于标识该脚本文件以示区分，对于isOrder为true时labe必不可少，isOrder为false时可有可无；callback是回调函数，url为脚本的地址。操作方式如下几种： [javascript] &#8230; <a href="http://www.ilovejs.net/archives/976" class="more-link">了解更多</a>]]></description>
			<content:encoded><![CDATA[<p>
之前一直在想法设法的实现js脚本文件按顺序执行，而且并行下载的测试，但是都无终而返。最先设想的是通过轮询的方式来实现按顺序加载，可是这样就导致不能并行加载了。
</p>
<p>今天又拾起这个一直未实现的冲动，首先既然要并行加载、按顺序执行，那么就必须得给每一个js文件开一个http请求，而且这个请求是无顺序的，即是加载的顺序是无顺序的，但是在执行的时候，就得要按照声明的顺序来执行，比如下面的代码示例：<br />
[javascript]<br />
jL.load({&quot;label&quot;:&quot;test&quot;,&quot;url&quot;:&quot;test.js&quot;,isOrder:true});<br />
jL.load({&quot;label&quot;:&quot;test2&quot;,&quot;url&quot;:&quot;test2.js&quot;,isOrder:false});<br />
jL.load({&quot;label&quot;:&quot;test3&quot;,&quot;url&quot;:&quot;test3.js&quot;,isOrder:true,callback:function(){<br />
  alert(&quot;test3.js is load&quot;);<br />
}}).insert();<br />
[/javascript]</p>
<p>
上面的代码中，load方法的参数中包含是否按顺序加载脚本文件，并且在脚本加载下来的时候包含callback函数回调。如果不是按顺序下载的，则直接使用DOM Element的方式插入script到document.body中；insert方法负责开始操作那些俺顺序执行的脚本文件的加载和插入工作；label参数对于需要按顺序执行的脚本文件必不可少，用于标识当前脚本，对无需按顺序执行的脚本可有可无。
</p>
<p><span id="more-976"></span></p>
<p>
刚开始设想的原理是这样的：把需要按顺序执行的脚本通过load方法都临时储存到一个数组里（如果不需要，则直接在load方法里通过DOM Element方法插入），之后调用insert方法来执行上面临时数组（使用XHR Injection的方式获得每一个脚本的responseText），并将请求返回的脚本的内容也暂且储存在临时变量里，待全部的脚本都请求下来之后，再把全部脚本的内容都插入到Document文档里使得脚本起作用。《<a  href="/lab/load-order/">测试示例1</a>》
</p>
<p>
上面的方案虽然实现了并行加载、按顺序执行，但是存在诸多弊端，如果有脚本文件比较大，请求的时间比较长，那么先于它加载下来的脚本就迟迟得不到执行了，而且在处理callback回调函数的时候也是在全部脚本加载下来以后才执行。
</p>
<p>
为此，在这个基础上，修改了实现方式：按顺序执行的脚本仍然使用XHR的方式来加载，并把内容临时储存在数组里，同时通过label标识着脚本文件添加一个load的boolean属性来标识该脚本是否已经加载完成。这样，我就可以使用定时器(setInterval)来检查按label标识顺序排好的数组来检查load属性是否为true，这样来插入脚本到文档中。这样，就可以尽早的将声明在前面的脚本在加载完成的时候就插入到文档内。《<a  href="/lab/load-order/index3.html">测试页面</a>》
</p>
<p>
在代码中，因为在重复调用insert方法的情况下要处理临时数组和对象的清空问题比较难解决，所以就进行了再一次的封装，支持链式调用，而且总共也才只有load和insert方法，参数说明：label用于标识该脚本文件以示区分，对于isOrder为true时labe必不可少，isOrder为false时可有可无；callback是回调函数，url为脚本的地址。操作方式如下几种：
</p>
<p>[javascript]<br />
//分类的方式，几个insert方法调用<br />
jL().load({&quot;label&quot;:&quot;jquery&quot;,&quot;url&quot;:&quot;jquery.js&quot;,isOrder:true,callback:function(){<br />
  alert($(&quot;#test&quot;).html());<br />
}})<br />
.load({&quot;label&quot;:&quot;test&quot;,&quot;url&quot;:&quot;test.js&quot;,isOrder:true})<br />
.insert();<br />
///////////////////////////////////<br />
jL().load({&quot;label&quot;:&quot;test2&quot;,&quot;url&quot;:&quot;test2.js&quot;,isOrder:true})<br />
.load({&quot;label&quot;:&quot;test3&quot;,&quot;url&quot;:&quot;test3.js&quot;,isOrder:true})<br />
.insert();<br />
//链式调用的方式，最后一个insert方法调用：<br />
jL().load({&quot;label&quot;:&quot;jquery&quot;,&quot;url&quot;:&quot;jquery.js&quot;,isOrder:false,callback:function(){<br />
  alert($(&quot;#test&quot;).html());<br />
}})<br />
.load({&quot;label&quot;:&quot;test&quot;,&quot;url&quot;:&quot;test.js&quot;,isOrder:true})<br />
.load({&quot;label&quot;:&quot;test2&quot;,&quot;url&quot;:&quot;test2.js&quot;,isOrder:true})<br />
.load({&quot;label&quot;:&quot;test3&quot;,&quot;url&quot;:&quot;test3.js&quot;,isOrder:true}).insert();<br />
//可以将加载信息都声明到一个load方法里：<br />
jL().load(<br />
{&quot;label&quot;:&quot;jquery&quot;,&quot;url&quot;:&quot;jquery.js&quot;,isOrder:true,callback:function(){<br />
  alert($(&quot;#test&quot;).html());<br />
}},<br />
{&quot;label&quot;:&quot;test&quot;,&quot;url&quot;:&quot;test.js&quot;,isOrder:true},<br />
{&quot;label&quot;:&quot;test2&quot;,&quot;url&quot;:&quot;test2.js&quot;,isOrder:true},<br />
{&quot;label&quot;:&quot;test3&quot;,&quot;url&quot;:&quot;test3.js&quot;,isOrder:true}<br />
)<br />
.insert();<br />
[/javascript]</p>
<p>
如上所示，操作简单方便而且实用，下面是js源码：
</p>
<p>[javascript]<br />
; (function(win) {<br />
  var _h = document.getElementsByTagName(&quot;head&quot;)[0];<br />
  //使用lazy Definition的方式声明xhr实例<br />
  var _xhr = function() {<br />
    _xhr = win.XMLHttpRequest ? function() {<br />
      return new XMLHttpRequest();<br />
    }: (new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;)) ? function() {<br />
      return new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);<br />
    }: function() {<br />
      return new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);<br />
    }<br />
    return _xhr();<br />
  }<br />
  //使用DOM Element的方式把脚本动态插入到文档?<br />
  var _domLoad = function(url, parent, callback) {<br />
    if (!url){<br />
       return;<br />
    }<br />
    var s = document.createElement(&quot;script&quot;);<br />
    s.setAttribute(&quot;src&quot;, url); (parent || _h).appendChild(s);<br />
    if (callback) {<br />
      if (s.readyState) { //IE,Opera<br />
        s.onreadystatechange = function() {<br />
          if (s.readyState === &quot;loaded&quot; || s.readyState === &quot;complete&quot;) {<br />
            s.onreadystatechange = null;<br />
            callback();<br />
          }<br />
        }<br />
      } else { // FF,Chrome,Safari<br />
        s.onload = function() {<br />
          s.onload = null;<br />
          callback();<br />
        }<br />
      }<br />
    }<br />
  }<br />
  //通过设置script标签的text属性来插入行内脚本到文档中<br />
  //不过一定要插入到head中，插入到body中在IE6下测试出?<br />
  var _script = function(text) {<br />
    var s = document.createElement(&quot;script&quot;);<br />
    s.text = text;<br />
    _h.appendChild(s);<br />
  }<br />
  //请求每一个js脚本文件<br />
  var _request = function(_p, _o) {<br />
    var _x = _xhr(),<br />
    item = _o._cache[_p.label];<br />
    _x.open(&quot;GET&quot;, _p.url, true);<br />
    _x.onreadystatechange = function() {<br />
      if (_x.readyState === 4 &amp;&amp; (_x.status === 200 || _x.status === 304)) {<br />
        item[&quot;text&quot;] = _x.responseText;<br />
        item[&quot;load&quot;] = true;<br />
      }<br />
    }<br />
    _x.send(null);<br />
  }<br />
  var _load =function (){<br />
	for(var i=0,l=arguments.length;i&lt;l;i++){<br />
	  var a = arguments[i];<br />
	  if(!a.isOrder){<br />
	    _domLoad(a.url,a.parent,a.callback);<br />
	  }else{<br />
	    this._param.push(a);<br />
	    this._cache[a.label]={};<br />
	    this._cache[a.label][&quot;callback&quot;]=a.callback;<br />
	  }<br />
	}<br />
	return this;<br />
  }<br />
  var _insert = function() {<br />
    if (!this._param.length) {<br />
      return;<br />
    }<br />
    var _o = null,i = 0;<br />
	//请求脚本文件<br />
    while (_o = this._param[i++]) {<br />
      _request(_o, this);<br />
    }<br />
	//定时器检查并插入脚本到文档中<br />
    var o = this._param.shift(),item = null,that = this;<br />
    var interval = setInterval(function() {<br />
      item = that._cache[o.label];<br />
      if (item[&quot;load&quot;]) {<br />
        _script(item[&quot;text&quot;]);<br />
        if (item[&quot;callback&quot;]) {<br />
          item[&quot;callback&quot;]();<br />
        }<br />
        if (! (o = that._param.shift())) {<br />
          clearInterval(interval);<br />
        }<br />
      }<br />
    },5);<br />
    return this;<br />
  }<br />
  var _jL = function() {<br />
    this._cache = {};<br />
    this._param = [];<br />
  }<br />
  _jL.prototype = {<br />
    load: _load,<br />
    insert: _insert<br />
  }<br />
  win.jL = function() {<br />
    return new _jL();<br />
  }<br />
})(this);<br />
[/javascript]</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ilovejs.net/archives/976/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>说说分支技术的一种替代方案</title>
		<link>http://www.ilovejs.net/archives/146</link>
		<comments>http://www.ilovejs.net/archives/146#comments</comments>
		<pubDate>Sat, 02 Jan 2010 20:14:14 +0000</pubDate>
		<dc:creator>Supersha</dc:creator>
				<category><![CDATA[Ajax]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[lazy definition]]></category>
		<category><![CDATA[分支]]></category>

		<guid isPermaLink="false">http://www.ilovejs.net/?p=146</guid>
		<description><![CDATA[大家应该都知道Javascript中分支技术的实现原理：是一种用来把浏览器间的差异封装到在运行期间进行设置的动态方法中的技术。没错，这个解释是出自《Javascript设计模式》。分支技术最大的特点是只在脚本加载是一次性的确定针对特定的浏览器的代码，这样一来，在初始化完成之后，每种浏览器都只会执行针对特定浏览器的代码，能够在运行时动态确定函数代码的能力。下面是分支技术的简单实现的一个例子： [javascript] //三个对象都声明相同的createXhr方法以示统一 var SimpleXhrFactory=(function(){ var standard={ createXhr:function(){ return new XMLHttpRequest(); } }; var activeXNew={ createXhr:function(){ return new ActiveXObject(&#34;Msxml2.XMLHTTP&#34;); } &#8230; <a href="http://www.ilovejs.net/archives/146" class="more-link">了解更多</a>]]></description>
			<content:encoded><![CDATA[<p>大家应该都知道Javascript中分支技术的实现原理：是一种用来把浏览器间的差异封装到在运行期间进行设置的动态方法中的技术。没错，这个解释是出自《<a  href="http://product.dangdang.com/product.aspx?product_id=20511558">Javascript设计模式</a>》。分支技术最大的特点是只在脚本加载是一次性的确定针对特定的浏览器的代码，这样一来，在初始化完成之后，每种浏览器都只会执行针对特定浏览器的代码，能够在运行时动态确定函数代码的能力。下面是分支技术的简单实现的一个例子：<br />
[javascript]<br />
//三个对象都声明相同的createXhr方法以示统一<br />
var SimpleXhrFactory=(function(){<br />
   var standard={<br />
       createXhr:function(){<br />
            return new XMLHttpRequest();<br />
       }<br />
   };<br />
   var activeXNew={<br />
       createXhr:function(){<br />
            return new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);<br />
       }<br />
   };<br />
   var activeXOld={<br />
      createXhr:function(){<br />
           return new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);<br />
      }<br />
   };</p>
<p>   var testObj;<br />
   try{<br />
       testObj=standard.createXhr();<br />
       return standard;<br />
   }catch(e){<br />
        try{<br />
              testObj=activeXNew.createXhr();<br />
              return activeXNew;<br />
        }catch(e){<br />
              testObj=activeXOld.createXhr();<br />
              return activeXNew;<br />
        }<br />
   }<br />
}();<br />
[/javascript]<br />
上面的例子就是分支技术的一个应用。我不知道大家对这段代码是否有什么意见或者异议，反正我是觉得冗长了，无端端的声明了三个对象不说，最后还是需要try&#8230;catch一套。下面是我所想到的一种解决方案来代替这个关于声明XMLHttpRequest实例对象的例子：<br />
[javascript]<br />
//在运行时就确定了特定的浏览器下将要执行哪个分支的函数<br />
var XHR = {}<br />
XHR.create = (function(){<br />
    if (window.XMLHttpRequest) {<br />
        return function(){<br />
            return new XMLHttpRequest();<br />
        }<br />
    }<br />
    else {<br />
        try {<br />
            //为了判断Msxml2和Microsoft版本的ActiveXObject，不得不实例化一个,不过也就是一次性过了而已<br />
            return new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;) &amp;&amp; function(){<br />
                return new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);<br />
            }<br />
        } catch (e) {<br />
            return function(){<br />
                return new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);<br />
            }<br />
        }<br />
    }<br />
})();<br />
[/javascript]</p>
<p>
上面的代码简洁明了，有一个不好的缺点就是需要在IE6下需要实例化多一个ActiveXObject(“Msxml2.XMLHTTP”)作为判断条件，IE7以上以及FF等其他浏览器则不会有这个实例化的过程，不过还是那句话，也就是在初始化的时候有这个过程而已，之后浏览器会根据特定的函数来执行。<a  href="http://www.ilovejs.net/lab/xhr.html">测试页面</a>
</p>
<p><span id="more-146"></span><br />
当然了，使用Lazy Definition Pattern的方式来实现也是同样的原理：<br />
[javascript]<br />
var XHR = {}<br />
XHR.create = function(){<br />
    if (window.XMLHttpRequest) {<br />
        XHR.create = function(){<br />
            return new XMLHttpRequest();<br />
        }<br />
    }<br />
    else {<br />
        try {<br />
            //为了判断Msxml2和Microsoft版本的ActiveXObject，不得不实例化一个<br />
            XHR.create = new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;) &amp;&amp;function(){<br />
                return new ActiveXObject(&quot;Msxml2.XMLHTTP&quot;);<br />
            }<br />
        } catch (e) {<br />
            XHR.create = function(){<br />
                return new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);<br />
            }<br />
        }<br />
    }<br />
    XHR.create();<br />
};<br />
[/javascript]</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ilovejs.net/archives/146/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Javascript的Lazy Definition Pattern</title>
		<link>http://www.ilovejs.net/archives/126</link>
		<comments>http://www.ilovejs.net/archives/126#comments</comments>
		<pubDate>Sat, 02 Jan 2010 13:59:20 +0000</pubDate>
		<dc:creator>Supersha</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[event]]></category>
		<category><![CDATA[lazy definition]]></category>

		<guid isPermaLink="false">http://www.ilovejs.net/?p=126</guid>
		<description><![CDATA[Lazy Definition Pattern是这样的一个模式：根据浏览器之间的解析javascript的差异性，使得创建封装的对象或者是函数的时候都需要使用浏览器的嗅探技术来做判断，而且对象或者方法每调用一次就需要去嗅探，这是一个非常不好的额外功。而解决这个瓶颈的方法之一就是Lazy Definition了。它会在浏览器第一次执行该对象或者函数的时候就记住这次的操作，以至于下面的重复调用不会再去执行浏览器的嗅探。让我们先从一个简单的addListener封装函数来一步步分析。 我们通常封装浏览器的添加事件函数的方式是使用下面的方式： [javascript] var addListener=function(el, type, handle) { if (el.addEventListener) { el.addEventListener(type, handle, false); }else if (el.attachEvent) &#8230; <a href="http://www.ilovejs.net/archives/126" class="more-link">了解更多</a>]]></description>
			<content:encoded><![CDATA[<p>
<a href="http://peter.michaux.ca/articles/lazy-function-definition-pattern">Lazy Definition Pattern</a>是这样的一个模式：根据浏览器之间的解析javascript的差异性，使得创建封装的对象或者是函数的时候都需要使用浏览器的嗅探技术来做判断，而且对象或者方法每调用一次就需要去嗅探，这是一个非常不好的额外功。而解决这个瓶颈的方法之一就是Lazy Definition了。它会在浏览器第一次执行该对象或者函数的时候就记住这次的操作，以至于下面的重复调用不会再去执行浏览器的嗅探。让我们先从一个简单的addListener封装函数来一步步分析。
</p>
<p>
我们通常封装浏览器的添加事件函数的方式是使用下面的方式：
</p>
<p>[javascript]<br />
var addListener=function(el, type, handle) {<br />
   if (el.addEventListener) {<br />
      el.addEventListener(type, handle, false);<br />
   }else if (el.attachEvent) {<br />
      el.attachEvent(&#8216;on&#8217; + type, handle);<br />
   } else {<br />
      el['on' + type] = handle;<br />
   }<br />
}<br />
[/javascript]</p>
<p>
上面是一种使用很普遍的封装方式，它的问题之一就是口头说的，每次addListener函数调用的时候，都需要去执行if判断，以至于使用正确的方式，讨厌的if的执行次数跟调用次数相同了，不爽！</p>
<p>为了解决上面讨厌的if的反复执行，我们使用Lazy Definition来改造addListener方法：<br />
[javascript]<br />
var addListener = function(el, type, handle){<br />
    if (el.addEventListener) {<br />
        //重新定义addListener的方法体，使得下次调用addListener方法的时候就直接调用这个方法体，下同<br />
        addListener = function(el, type, handle){<br />
            el.addEventListener(type, handle, false);<br />
        }<br />
    } else<br />
        if (el.attachEvent) {<br />
            addListener = function(el, type, handle){<br />
                el.attachEvent(&quot;on&quot; + type, function(e){<br />
                    handle.call(el, e);<br />
                });<br />
            }<br />
        } else {<br />
            addListener = function(el, type, handle){<br />
                el[&quot;on&quot; + type] = handle;<br />
            }<br />
        }<br />
    //第一次执行addListener方法<br />
    addListener(el, type, handle);<br />
}<br />
[/javascript]<br />
上面的addListener定义方式是YAHOO提出的一种Lazy Definition方式，它还有一种变体，我把它改成如下的方式（有点类似于分支技术）：<br />
[javascript]<br />
var addListener = function(el, type, handle){<br />
	//在这里也是重新定义addListener方法<br />
    addListener = (function(){<br />
        if (el.addEventListener) {<br />
            return function(el, type, handle){<br />
                el.addEventListener(type, handle, false);<br />
            };<br />
        } else if (el.attachEvent) {<br />
                return function(el, type, handle){<br />
                    el.attachEvent(&quot;on&quot; + type, function(e){<br />
                        handle.call(el, e);<br />
                    });<br />
                };<br />
            } else {<br />
                return function(el, type, handle){<br />
                    el[&quot;on&quot; + type] = handle;<br />
                };<br />
            }<br />
    })();<br />
    addListener(el, type, handle);<br />
}<br />
[/javascript]</p>
<p>现在Lazy Definition的原理已经很清晰了：就是在函数内部重新自定义自己的方法体来实现。【<a href="http://www.ilovejs.net/lab/event-lazy.html">测试页面</a>】。下面再用一个简单的函数来表述：
</p>
<p>[javascript]<br />
var foo=function(){<br />
    var t=new Date();<br />
    foo=function(){<br />
         return t;<br />
    }<br />
    return foo(); //用于第一次调用的时候执行<br />
}<br />
[/javascript]<br />
更多资料可以阅读：《<a href="http://peter.michaux.ca/articles/lazy-function-definition-pattern">Lazy Function Definition Pattern</a>》</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ilovejs.net/archives/126/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

