﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>博客园-Klesh.Cn</title><link>http://www.cnblogs.com/Klesh/</link><description>concentrating on knowing more...</description><language>zh-cn</language><lastBuildDate>Thu, 28 Aug 2008 22:27:57 GMT</lastBuildDate><pubDate>Thu, 28 Aug 2008 22:27:57 GMT</pubDate><ttl>60</ttl><item><title>C#和Java的闭包－Jon谈《The Beauty of Closures》</title><link>http://www.cnblogs.com/Klesh/archive/2008/05/15/The-Beauty-of-Closures.html</link><dc:creator>Klesh Wong</dc:creator><author>Klesh Wong</author><pubDate>Thu, 15 May 2008 08:41:00 GMT</pubDate><guid>http://www.cnblogs.com/Klesh/archive/2008/05/15/The-Beauty-of-Closures.html</guid><wfw:comment>http://www.cnblogs.com/Klesh/comments/1198563.html</wfw:comment><comments>http://www.cnblogs.com/Klesh/archive/2008/05/15/The-Beauty-of-Closures.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cnblogs.com/Klesh/comments/commentRss/1198563.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/Klesh/services/trackbacks/1198563.html</trackback:ping><description><![CDATA[<P>原文：<A title=http://csharpindepth.com/Articles/Chapter5/Closures.aspx href="http://csharpindepth.com/Articles/Chapter5/Closures.aspx">http://csharpindepth.com/Articles/Chapter5/Closures.aspx</A></P>
<P>第一段略。。。</P>
<P>大多数讲闭包的文章都是说函数式语言，因为它们往往对闭包的支持最完善。当你在使用函数式语言时，很可能已经清楚了解了什么是闭包，所以我想写一篇在经典OO语言出现的闭包有什么用处应该也是很合适的事情。这篇文章我准备讲一下C#（1、2、3）和JAVA（7以前版本）的闭包。</P>
<H2>什么是闭包？</H2>
<P>简单来讲，闭包允许你将一些行为封装，将它像一个对象一样传来递去，而且它依然能够访问到原来第一次声明时的上下文。这样可以使控制结构、逻辑操作等从调用细节中分离出来。访问原来上下文的能力是闭包区别一般对象的重要特征，尽管在实现上只是多了一些编译器技巧。</P>
<P>利用例子来观察闭包的好处（和实现）会比较容易， 下面大部份内容我会使用一个单一的例子来进行讲解。例子会有JAVA和C#（不同版本）来说明不同的实现。所有的代码可以<A href="http://csharpindepth.com/Files/Closures.zip">点这里下载</A>。</P>
<H2>需求场景：过滤列表</H2>
<P>按一定条件过滤某个列表是很常见的需求。虽然写几行代码遍历一下列表，把满足条件的元素挑出来放到新列表的“内联”方式很容易满足需求，但把判断逻辑提取出来还是比较优雅的做法。唯一的难点就是如何封装“判定一个元素是否符合条件”逻辑，闭包正好可以解决这个问题。</P>
<P>虽然我上面说了“过滤”这个词，但它可能会有两个截然不同的意思“把元素滤出来放到列表里”或者把“把元素滤出来扔掉”。比如说“偶数过滤”是把“偶数”保留下来还是过滤掉？所以我们使用另一个术语“断言”。断言就是简单地指某样东西是不是满足某种条件。在我们的例子中即是生成一个包含了原列表满足断言条件的新列表。</P>
<P>在C#中，比较自然地表现一个断言就是通过delegate，事实上C# 2.0有一个Predicate&lt;T&gt;类型。（顺带一提，因为某些原因，LINQ更偏向于Func&lt;T, bool&gt;；我不知道这是为什么，相关的解释也很少。然而这两个泛型类的作用其实是一样的。）在Java中没有delegate，因此我们会使用只有一个方法的interface。当然C#中我们也可以使用interface，但会使得代码看起来很混乱，而且不能使用匿名函数和拉姆达表达式－C#中符合闭包特征的实现。下面的interface/delegate供大家参考：</P><PRE><SPAN style="COLOR: #008000">// Declaration for System.Predicate&lt;T&gt;</SPAN>
<SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">delegate</SPAN> <SPAN style="COLOR: #0000ff">bool</SPAN> Predicate&lt;T&gt;(T obj)</PRE><PRE><SPAN style="COLOR: #008000">// Predicate.java</SPAN>
<SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">interface</SPAN> Predicate&lt;T&gt;
{
    <SPAN style="COLOR: #0000ff">boolean</SPAN> match(T item);
}</PRE>
<P>在两种语言中过滤用的代码都比较简单，得先说明在这里我会避免使用C#的Extension Method来让代码看起来更加简单明了。－但是使用过LINQ的人要注意where这个Extension Method。（它们的延迟执行有些区别，但这里我会避免触及）</P><PRE><SPAN style="COLOR: #008000">// In ListUtil.cs</SPAN>
<SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">class</SPAN> ListUtil
{
    <SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">static</SPAN> IList&lt;T&gt; Filter&lt;T&gt;(IList&lt;T&gt; source, Predicate&lt;T&gt; predicate)
    {
        List&lt;T&gt; ret = <SPAN style="COLOR: #0000ff">new</SPAN> List&lt;T&gt;();
        <SPAN style="COLOR: #0000ff">foreach</SPAN> (T item <SPAN style="COLOR: #0000ff">in</SPAN> source)
        {
            <SPAN style="COLOR: #0000ff">if</SPAN> (predicate(item))
            {
                ret.Add(item);
            }
        }
        <SPAN style="COLOR: #0000ff">return</SPAN> ret;
    }
}</PRE><PRE><SPAN style="COLOR: #008000">// In ListUtil.java</SPAN>
<SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">class</SPAN> ListUtil
{
    <SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">static</SPAN> &lt;T&gt; List&lt;T&gt; filter(List&lt;T&gt; source, Predicate&lt;T&gt; predicate)
    {
        ArrayList&lt;T&gt; ret = <SPAN style="COLOR: #0000ff">new</SPAN> ArrayList&lt;T&gt;();
        <SPAN style="COLOR: #0000ff">for</SPAN> (T item : source)
        {
            <SPAN style="COLOR: #0000ff">if</SPAN> (predicate.match(item))
            {
                ret.add(item);
            }
        }
        <SPAN style="COLOR: #0000ff">return</SPAN> ret;
    }
}</PRE>
<P>（两种语言中我都写了一个Dump方法用来输出指定list的内容）</P>
<P>现在我们已经定义好“过滤”的方法，接下来就是要调用它。为了演示闭包的重要作用，我会先使用一个简单的不需要使用到闭包都能解决的案例，然后再进一步到比较难的案例。</P>
<H2>过滤案例1：找出长度较短的字符串（固定长度）</H2>
<P>我们的需求场景都会比较简单基础，但希望大家能从中看出它们的不同之处。我们将会有一个字符串list，然后根据这个list生成另一个只包含长度较“短”的字符串list。建立list很简单－建立断言才是难点。</P>
<P>在C# 1.0中只能通过单独的方法来表现一个断言逻辑，然后再创建一个delegate指向该方法。（当然由于代码使用了泛并不能真地在C# 1.0下面通过编译，但要注意delegate实例是如何被建立的－这是重点）</P><PRE><SPAN style="COLOR: #008000">// In Example1a.cs</SPAN>
<SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> Main()
{
    Predicate&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; predicate = <SPAN style="COLOR: #0000ff">new</SPAN> Predicate&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt;(MatchFourLettersOrFewer);
    IList&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}

<SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">bool</SPAN> MatchFourLettersOrFewer(<SPAN style="COLOR: #0000ff">string</SPAN> item)
{
    <SPAN style="COLOR: #0000ff">return</SPAN> item.Length &lt;= 4;
}</PRE>
<P>在C# 2.0中有三种方式实现，第一，使用上面一样的代码；第二，利用方法组转换（Method Group Conversion）对代码进行简化；第三，利用匿名函数将断言直接写在调用上下文中。使用方法组转换比较浪费时间－它只是把<CODE>new Predicate&lt;string&gt;(MatchFourLettersOrFewer)</CODE> 变成了 <CODE>MatchFourLettersOrFewer。在<A href="http://csharpindepth.com/Files/Closures.zip">示例代码</A>中有它的实现（在<CODE>Example1b.cs中</CODE>）。相对而言，匿名函数要有趣得多：</CODE></P><PRE><SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> Main()
{
    Predicate&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; predicate = <SPAN style="COLOR: #0000ff">delegate</SPAN>(<SPAN style="COLOR: #0000ff">string</SPAN> item)
        {
            <SPAN style="COLOR: #0000ff">return</SPAN> item.Length &lt;= 4;
        };
    IList&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}</PRE>
<P>这样一来，就不再需要一个外部独立的方法用来封装断言逻辑，并且，断言放在了被使用的点上。很好很强大。它背后是怎么工作的呢？如果你用ildasm或者reflector去看一下生成的代码，你会发现其实它了第一个版本产生的代码很大程度是一样的，编译器只是帮我们完成了某些工作。稍后我们会看到它更强悍的能力。</P>
<P>在C# 3.0中除了有上面三种方式，还有拉姆达表达式。对于本文来讲，拉姆达表达式只是匿名函数的一个简化形式。（这两种东东最大的区别在于LINQ中的拉姆达表达式能被转换成表达式树，但这与本文无关）使用拉姆达表达式：</P><PRE><SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> Main()
{
    Predicate&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; predicate = item =&gt; item.Length &lt;= 4;
    IList&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}</PRE>
<P>由于在右边使用了<CODE>&lt;=</CODE>，看起来像是有个大箭头指着<CODE>item.Length</CODE>，但为了保持前后一致，只好请大家将就着看了。这里其实可以写成等价的<CODE>Predicate&lt;string&gt; predicate = item =&gt; item.Length &lt; 5</CODE>；</P>
<P>在Java中没有delegate－只能实现上面定义的interface。最简单的方法就是定义一个类并实现该interface，如：</P><PRE><SPAN style="COLOR: #008000">// In FourLetterPredicate.java</SPAN>
<SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">class</SPAN> FourLetterPredicate <SPAN style="COLOR: #0000ff">implements</SPAN> Predicate&lt;String&gt;
{
    <SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">boolean</SPAN> match(String item)
    {
        <SPAN style="COLOR: #0000ff">return</SPAN> item.length() &lt;= 4;
    }
}

<SPAN style="COLOR: #008000">// In Example1a.java</SPAN>
<SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> main(String[] args)
{
    Predicate&lt;String&gt; predicate = <SPAN style="COLOR: #0000ff">new</SPAN> FourLetterPredicate();
    List&lt;String&gt; shortWords = ListUtil.filter(SampleData.WORDS, predicate);
    ListUtil.dump(shortWords);
}</PRE>
<P>这里没有使用任何华丽的语言特性，为了实现一点小小的逻辑，它使用了一整个独立的类。根据Java的惯例，类应该放在单独的文件里，这使得程序的可读性变差。当然可以使用嵌套类的方式来避免这种问题，但逻辑还是离开了使用它的地方－相当于啰嗦版的C# 1.0解决方案。（这里不打算给出嵌套版的实现代码，有需要的朋友可以看看打包代码里面<CODE>Example1b.java。</CODE>）Java可以通过匿名类把代码书写成内联的方式，在匿名类的光芒照射下，代码进化了：</P><PRE><SPAN style="COLOR: #008000">// In Example 1c.java</SPAN>
<SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> main(String[] args)
{
    Predicate&lt;String&gt; predicate = <SPAN style="COLOR: #0000ff">new</SPAN> Predicate&lt;String&gt;()
    {
        <SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">boolean</SPAN> match(String item)
        {
            <SPAN style="COLOR: #0000ff">return</SPAN> item.length() &lt;= 4;
        }
    };
    
    List&lt;String&gt; shortWords = ListUtil.filter(SampleData.WORDS, predicate);
    ListUtil.dump(shortWords);
}</PRE>
<P>如你所见，比起C# 2.0和C# 3.0的代码，这个显得还是比较啰嗦了点，但至少代码被放在了它应该在的地方。这就是Java目前支持的闭包……接下来本文进入第二个例子。</P>
<H2>过滤案例2：找出长度较短的字符串（可变长度）</H2>
<P>目前为止我们的断言并不需要访问到原来的“上下文”－长度是硬编码的，然后字符串是以参数的形式传进去的。现在，需求变动一下，允许用户指定多长的字符串才算是合适的。</P>
<P>首先，我们回到C# 1.0。它其实不支持真正的闭包－找不到一块简单的地方来存储我们需要的变量。当然，我们可以在当前方法的上下文中声明一个变量来解决这个问题（比如利用静态成员变量），但这明显不是一个好的解决方法－理由只有一个，类马上变成了线程不安全的。解决的方法就是不要把状态存储在当前上下文中，转而存储在新建的类中。这么一来，代码看起来跟原来的Java代码非常相似，区别只是这里使用delegate，而Java使用interface。</P><PRE><SPAN style="COLOR: #008000">// In VariableLengthMatcher.cs</SPAN>
<SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">class</SPAN> VariableLengthMatcher
{
    <SPAN style="COLOR: #0000ff">int</SPAN> maxLength;

    <SPAN style="COLOR: #0000ff">public</SPAN> VariableLengthMatcher(<SPAN style="COLOR: #0000ff">int</SPAN> maxLength)
    {
        <SPAN style="COLOR: #0000ff">this</SPAN>.maxLength = maxLength;
    }

    <SPAN style="COLOR: #008000">/// &lt;summary&gt;</SPAN>
    <SPAN style="COLOR: #008000">/// Method used as the action of the delegate</SPAN>
    <SPAN style="COLOR: #008000">/// &lt;/summary&gt;</SPAN>
    <SPAN style="COLOR: #0000ff">public</SPAN> bool Match(string item)
    {
        <SPAN style="COLOR: #0000ff">return</SPAN> item.Length &lt;= maxLength;
    }
}

<SPAN style="COLOR: #008000">// In Example2a.cs</SPAN>
<SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> Main()
{
    Console.Write("<SPAN style="COLOR: #8b0000">Maximum length of string to include? </SPAN>");
    <SPAN style="COLOR: #0000ff">int</SPAN> maxLength = <SPAN style="COLOR: #0000ff">int</SPAN>.Parse(Console.ReadLine());

    VariableLengthMatcher matcher = <SPAN style="COLOR: #0000ff">new</SPAN> VariableLengthMatcher(maxLength);
    Predicate&lt;string&gt; predicate = matcher.Match;
    IList&lt;string&gt; shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}</PRE>
<P>相对来说，C# 2.0和C# 3.0的改动要小得多：只需将硬编码的常量改成变量即可。先不管这背后的原理－一会看完Java版的代码后再来研究这个问题。</P><PRE><SPAN style="COLOR: #008000">// In Example2b.cs (C# 2)</SPAN>
<SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> Main()
{
    Console.Write("<SPAN style="COLOR: #8b0000">Maximum length of string to include? </SPAN>");
    <SPAN style="COLOR: #0000ff">int</SPAN> maxLength = <SPAN style="COLOR: #0000ff">int</SPAN>.Parse(Console.ReadLine());

    Predicate&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; predicate = <SPAN style="COLOR: #0000ff">delegate</SPAN>(<SPAN style="COLOR: #0000ff">string</SPAN> item)
    {
        <SPAN style="COLOR: #0000ff">return</SPAN> item.Length &lt;= maxLength;
    };
    IList&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}</PRE><PRE><SPAN style="COLOR: #008000">// In Example2c.cs (C# 3)</SPAN>
<SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> Main()
{
    Console.Write("<SPAN style="COLOR: #8b0000">Maximum length of string to include? </SPAN>");
    <SPAN style="COLOR: #0000ff">int</SPAN> maxLength = <SPAN style="COLOR: #0000ff">int</SPAN>.Parse(Console.ReadLine());

    Predicate&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; predicate = item =&gt; item.Length &lt;= maxLength;
    IList&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}</PRE>
<P>Java版的代码（使用了匿名类的那个版本）改动也比较简单，但有一点不爽的是－必须把参数声明为final。了解其原理前先来看一下代码：</P><PRE><SPAN style="COLOR: #008000">// In Example2a.java</SPAN>
<SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> main(String[] args) <SPAN style="COLOR: #0000ff">throws</SPAN> IOException
{
    System.out.print("<SPAN style="COLOR: #8b0000">Maximum length of string to include? </SPAN>");
    BufferedReader console = <SPAN style="COLOR: #0000ff">new</SPAN> BufferedReader(<SPAN style="COLOR: #0000ff">new</SPAN> InputStreamReader(System.in));
    <SPAN style="COLOR: #0000ff">final</SPAN> <SPAN style="COLOR: #0000ff">int</SPAN> maxLength = Integer.parseInt(console.readLine());
    
    Predicate&lt;String&gt; predicate = <SPAN style="COLOR: #0000ff">new</SPAN> Predicate&lt;String&gt;()
    {
        <SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">boolean</SPAN> match(String item)
        {
            <SPAN style="COLOR: #0000ff">return</SPAN> item.length() &lt;= maxLength;
        }
    };
    
    List&lt;String&gt; shortWords = ListUtil.filter(SampleData.WORDS, predicate);
    ListUtil.dump(shortWords);
}</PRE>
<P>那么，C#和Java的代码到底有什么不同呢？<EM>在Java中，变量的<STRONG><U>值</U></STRONG>被匿名类捕获。在C#中，<STRONG><U>变量本身</U></STRONG>被delegate捕获</EM>。为了证明C#捕获了变量本身，我们来改一下C# 3.0的代码，使变量的值在变量在过滤后发生改变，看看改变是否反映到下一次过滤：</P><PRE><SPAN style="COLOR: #008000">// In Example2d.cs</SPAN>
<SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> Main()
{
    Console.Write("<SPAN style="COLOR: #8b0000">Maximum length of string to include? </SPAN>");
    <SPAN style="COLOR: #0000ff">int</SPAN> maxLength = <SPAN style="COLOR: #0000ff">int</SPAN>.Parse(Console.ReadLine());

    Predicate&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; predicate = item =&gt; item.Length &lt;= maxLength;
    IList&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);

    Console.WriteLine("<SPAN style="COLOR: #8b0000">Now for words with &lt;= 5 letters:</SPAN>");
    maxLength = 5;
    shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}</PRE>
<P>注意，我们只是改变局部变量的值，而并没有重新创建delegate的实例，或者其它等价的操作。由于delegate其实是直接访问这个局部变量，所以其实它是能够知道变量发生的变化。再进一步，接下来在断言逻辑中直接对变量进行修改：</P><PRE><SPAN style="COLOR: #008000">// In Example2e.cs</SPAN>
<SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> Main()
{
    <SPAN style="COLOR: #0000ff">int</SPAN> maxLength = 0;

    Predicate&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; predicate = item =&gt; { maxLength++; <SPAN style="COLOR: #0000ff">return</SPAN> item.Length &lt;= maxLength; };
    IList&lt;<SPAN style="COLOR: #0000ff">string</SPAN>&gt; shortWords = ListUtil.Filter(SampleData.Words, predicate);
    ListUtil.Dump(shortWords);
}</PRE>
<P>我不打算再深入地讲这些是怎么实现的－《C# in Depth》第5章讲的都是这些细节。只是希望你们一些对“局部变量”的观念认识被完全颠倒。</P>
<P>我们已经看到了C#是如何对捕获的变量进行修改的，那Java呢？答案只有一个：你不能对捕获的变量进行修改。它已经被声明为final，所以这个问题其实是很无厘头的。而且就算你人品值爆糟，突然间能对该变量进行更改，也会发现断言逻辑根本对修改毫无反应。变量的值在断言声明的时候被拷贝并存储到匿名类内。不过，对于引用变量，它的成员发生改变还是能够被知道的。比如说，如果你引用了一个StringBuilder，然后对它进行Append操作，那在匿名类中是可以看到StringBuilder的改变。</P>
<H2>对比捕获策略：复杂性VS功能</H2>
<P>明显Java的设计局限性比较大，但也同时也比较容易理解，不容易发生概念混淆的情况，局部变量的行为和一般情况下没什么不同，大多数情况下，代码看起来也更简单易懂。比如下面的代码，利用Java runable interface和.NET Action delegate－两个都是会执行一些操作，不需要参数，也不返回任何值。首先看看C#的代码：</P><PRE><SPAN style="COLOR: #008000">// In Example3a.cs</SPAN>
<SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> Main()
{
    <SPAN style="COLOR: #008000">// First build a list of actions</SPAN>
    List&lt;Action&gt; actions = <SPAN style="COLOR: #0000ff">new</SPAN> List&lt;Action&gt;();
    <SPAN style="COLOR: #0000ff">for</SPAN> (<SPAN style="COLOR: #0000ff">int</SPAN> counter = 0; counter &lt; 10; counter++)
    {
        actions.Add(() =&gt; Console.WriteLine(counter));
    }

    <SPAN style="COLOR: #008000">// Then execute them</SPAN>
    <SPAN style="COLOR: #0000ff">foreach</SPAN> (Action action <SPAN style="COLOR: #0000ff">in</SPAN> actions)
    {
        action();
    }
}</PRE>
<P>会输出些什么？其实我们只声明了一个counter变量－所以其实所有的Action捕获的都是同一个counter变量。结果就是每一行都输出数字10。为了把代码“修正”到我们预期的效果（如输出0到9），则需要在循环体中使用另一个局部变量：</P><PRE><SPAN style="COLOR: #008000">// In Example3b.cs</SPAN>
<SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> Main()
{
    <SPAN style="COLOR: #008000">// First build a list of actions</SPAN>
    List&lt;Action&gt; actions = <SPAN style="COLOR: #0000ff">new</SPAN> List&lt;Action&gt;();
    <SPAN style="COLOR: #0000ff">for</SPAN> (<SPAN style="COLOR: #0000ff">int</SPAN> counter = 0; counter &lt; 10; counter++)
    {
        <SPAN style="COLOR: #0000ff">int</SPAN> copy = counter;
        actions.Add(() =&gt; Console.WriteLine(copy));
    }

    <SPAN style="COLOR: #008000">// Then execute them</SPAN>
    <SPAN style="COLOR: #0000ff">foreach</SPAN> (Action action <SPAN style="COLOR: #0000ff">in</SPAN> actions)
    {
        action();
    }
}</PRE>
<P>这样，每次循环体在执行的时候，都会取得一份counter的拷贝，而不是它本身－所以每个Action取得了不同的变量值。如果看一下编译器生成的代码，你就会完全明白这种结果是合情合理的，但这对于大多数第一次看到代码的程序员来说，其直觉得出的结果往往是相反的。（包括我）</P>
<P>在Java中则完全不存在第一个例子的情形－你根本不可能捕获到counter变量，因为它并没有被声明为final。使用final变量，最终得到下面类似C#的代码：</P><PRE><SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">static</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> main(String[] args)
{
    <SPAN style="COLOR: #008000">// First build a list of actions</SPAN>
    List&lt;Runnable&gt; actions = <SPAN style="COLOR: #0000ff">new</SPAN> ArrayList&lt;Runnable&gt;();        
    <SPAN style="COLOR: #0000ff">for</SPAN> (<SPAN style="COLOR: #0000ff">int</SPAN> counter=0; counter &lt; 10; counter++)
    {
        <SPAN style="COLOR: #0000ff">final</SPAN> <SPAN style="COLOR: #0000ff">int</SPAN> copy = counter;
        actions.add(<SPAN style="COLOR: #0000ff">new</SPAN> Runnable()
        {
            <SPAN style="COLOR: #0000ff">public</SPAN> <SPAN style="COLOR: #0000ff">void</SPAN> run()
            {
                System.out.println(copy);
            }
        });
    }
    
    <SPAN style="COLOR: #008000">// Then execute them</SPAN>
    <SPAN style="COLOR: #0000ff">for</SPAN> (Runnable action : actions)
    {
        action.run();
    }
}</PRE>有了“捕获变量的值”语义存在，代码显得清晰明了，更符合直觉。尽管代码看起来比较啰嗦没有C#那么爽，但Java强制只能使用唯一正确的方式去书写代码。但同时当你需要像原来C#代码的那种行为时（有时候确实有这种需求），用Java实现起来是会比较麻烦。（可以用一个只有一个元素的数组，然后引用这个数组，再对数组元素进行操作，代码看起来会比较杂乱）。 
<H2>我到底想讲些什么？</H2>
<P>在例子中，我们可以看到了闭包好处其实不多。当然，我们把控制结构和断言逻辑成功分拆开来，但这并没有使代码比原来的更加简洁。这种事经常发生，新特性在简单的情形往往是看起来没想像中那么好，有那么大的作用。闭包通常带来的好处，是<EM>可组合性</EM>，如果你觉得这么说有些扯淡，没错－这也是问题的一部份。当你对闭包运用很熟练甚至有些迷恋的时候，两者之间的联系就会变得越来越明显，否则是不容易看出其玄妙所在。</P>
<P>闭包不是被设计来提供可组合性。它做的不过是让delegate实现起来更加简单（或者只有一个方法的interface，下面统一用delegate简称）。如果没有闭包，直接写一个循环结构其实是比把封装了一些相关逻辑的delegate传给另一个方法去执行循环要来得简单。即使可以通过delegate调用“在已有类中添加的方法”，最终你还是没办法把逻辑代码放在最合适的地方，而且没了闭包提供的信息存储便利，则必须依靠方法外部的上下文来存储某些信息。</P>
<P>可见，闭包使delegate更加易用。这就意味着值得将API设计成为使用delegate的形式。（我认为这种情况并不适用于.NET 1.1下面基本上只能用来处理线程和订阅事件的delegate）当你开始用delegate的方式去解决问题时，如何去做变得显而易见。比如，最常见的就是创建一个用AND或者OR（也包括其它逻辑操作符）将两个断言串连起来的Predicate&lt;T&gt;。</P>
<P>当把某个delegate产生的结果装填进另一个列表，或者对delegate进行加工产生新的，就会有完全不同的组合方式，如果将逻辑当作可以被传递的某种数据来考虑时，所有不同类型的选择都是可行的。</P>
<P>这种编码方式的好处远不止上面说的那么多－整个LINQ都是基于这种方式。我们创建的过滤器只是一个可以将有序数据转换成另一组数据的例子。另外还有排序，分组，联接另一组数据和Projecting等操作。使用传统的编码方式去写这些操作虽不是非常痛苦的事情，但是如果“数据管道”中转换操作越来越多时，复杂性随之提高，另外，LINQ赋于对象延迟执行和数据流的能力，这种一次循环执行多次操作方式明显比多次循环执行一次操作要节约很多内存。即使每一个单独的转换操作被设计得很聪明高效，复杂性上还是依旧无法取得平衡－通过闭包封装简明扼要的代码片断以及良好设计的API带来的组合能力可以很好去除复杂性。</P>
<H2>结论</H2>
<P>刚开始接触闭包，可能不会对它有深刻印象。当然，它使得你的interface或者delegate实现起来更简单（取决于语言）。其威力只有在相关类库利用了它的特性之后才能体现出来，允许你将自定义行为放在合适的地方。当同一个类库同时允许你将几个简单的步骤以比较自然的方式组合起来实现一些重要行为时，其复杂性也只是几个步骤的总和－而不是大于这个总和。我不是赞同某些人鼓吹的可组合性是解决复杂性的银弹，但它肯定是很有用的技巧，而且由于闭包使得它在很多地方可以得以实施。</P>
<P>拉姆达表达式最重要特点就是简洁。看一下之前的Java和C#的代码，Java的代码显然比较笨拙冗长。很多Java闭包的倡议都是想解决这个问题。稍后我会发一篇文章讲一下我对这些不同倡议的看法。</P><img src ="http://www.cnblogs.com/Klesh/aggbug/1198563.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41943/" target="_blank">[新闻]轮回－新浪推出Facebook模式的SNS</a>]]></description></item><item><title>标准当然是好事情</title><link>http://www.cnblogs.com/Klesh/archive/2008/04/10/1146699.html</link><dc:creator>Klesh Wong</dc:creator><author>Klesh Wong</author><pubDate>Thu, 10 Apr 2008 05:54:00 GMT</pubDate><guid>http://www.cnblogs.com/Klesh/archive/2008/04/10/1146699.html</guid><wfw:comment>http://www.cnblogs.com/Klesh/comments/1146699.html</wfw:comment><comments>http://www.cnblogs.com/Klesh/archive/2008/04/10/1146699.html#Feedback</comments><slash:comments>47</slash:comments><wfw:commentRss>http://www.cnblogs.com/Klesh/comments/commentRss/1146699.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/Klesh/services/trackbacks/1146699.html</trackback:ping><description><![CDATA[<p>　　从现实情况出发，IE用户量占绝对优势乃是一个不争的事实，所以在目前的网页制作开发中，优先针对IE进行编码也是很合理的策略。</p> <p>　　固然，顾全当前现实情况是必要，可是世界并不是一成不变的，尤其是风云变幻的商业市场，用户会变化。从Google以及其它很多Web 2.0的网站上可以看出来，虽然表面上，它们的出现改变了很多用户的使用习惯，上网习惯；其实是人们本来就有了那种需要，那种倾向，而Google及那些成功的网站注意到了这些潜在的需要，然后以用户为依归，创造出适合用户需要的产品，因而获得成功。也就是说，如果一个网站，一个创意，根本就不符合人们需求的时候，它根本就不可能发展壮大。</p> <p>　　不是我扯远了，而是有些人忽略了一个本来应该有的共识：用户是上帝，是用户选择产品，产品最多只能针对某个群体。</p> <p>　　用户是自由的，他们可以选择使用IE，可以选择Firefox，可以选择Safari，可以选择Opera，等等，正所谓萝卜青菜，各有所爱。你BS某个群体用户，别人一样也会BS你，就更不可能有5%的用户去浏览你的网站。若是想做一个<strong>成功的Web应用</strong>,剥夺用户的选择权是不明智的，看一下那些成功的网站，有哪一个不是往“标准”靠拢的？有哪一个不是跨浏览器的？</p> <p>　　对用户们来讲，他们根本就不关心一个网站开发的技术难度。他们关心的是适不适合他们用，好不好用，在他们使用惯用的浏览器浏览使用网站的时候，出现了页面混乱，无法运行，影响用户体验的情况，他们就会觉得这个网站根本就靠不住。就别说指望留住这些用户，甚至于当他们和朋友谈起这个网站的时候，也会嗤之以鼻。有时候，用户可能会委曲求全使用某种他们不喜欢的浏览器，但有更好的网站出现时，他们会毫不犹豫地飞奔过去。</p> <p>　　对客户们或者老板来讲，目前实施标准化带来的跨浏览器问题只会让他们支付更多的成本。这些额外的成本其实并不会带来额外的好处，额外的利润，而仅仅是避免本来不应该存在的负面效应。所以未来的浏览器一定会向“标准”靠拢。</p> <p>　　特别是微软自己也在向“标准”靠拢的时候，仇恨标准是没有道理的，仇恨那些对标准支持比较好的浏览器更加没有道理。仇恨这些就是在仇恨自己的生命，仇恨自己的自由，仇恨自己的客户。过往IE也好，Netscape也好，即使那么的辉煌也已是当年之勇，历史是向前推进的，IE6能撑六年之久还不是因为没有竞争者的存在？一个商业公司，当客户只有他一个选择的时候，还指望这个公司能自己推陈出新，发展迅速是不现实的事情。有竞争者存在的行业才会发展，但中间一定会有一些比较强势，所以需要一些立场比较中立代表用户（包括开发者）利益的第三者来制定规则让这些公司公平竞争，让“标准”去靠拢微软，就像让裁判偏袒某一球队一样可笑。</p> <p>　　现下由于“标准”普及度不够而产生的“跨浏览器”是高成本，低回报的，势必式微，绝非长久之计，与其希冀靠此为生，倒不如把希望寄于“标准”普及，把更多的精力投入到开发更好的Web应用上面。普通用户也就罢了，但如果身为Web开发者还在唯IE是瞻，则是令人扼腕。</p><img src ="http://www.cnblogs.com/Klesh/aggbug/1146699.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41940/" target="_blank">[新闻]惠普139亿美元完成对EDS的收购</a>]]></description></item><item><title>另一个多版本IE共存软件 IETester</title><link>http://www.cnblogs.com/Klesh/archive/2008/04/07/1140228.html</link><dc:creator>Klesh Wong</dc:creator><author>Klesh Wong</author><pubDate>Mon, 07 Apr 2008 05:02:00 GMT</pubDate><guid>http://www.cnblogs.com/Klesh/archive/2008/04/07/1140228.html</guid><wfw:comment>http://www.cnblogs.com/Klesh/comments/1140228.html</wfw:comment><comments>http://www.cnblogs.com/Klesh/archive/2008/04/07/1140228.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.cnblogs.com/Klesh/comments/commentRss/1140228.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/Klesh/services/trackbacks/1140228.html</trackback:ping><description><![CDATA[<p><a href="http://tredosoft.com/Multiple_IE">Multiple IE</a> 应该很多人知道，可以在 XP 下实现ie3 到6多版本共存，很实用的WEB开发辅助工具。美中不足的是，用它来浏览网页，每次遇一个ActiveX控件时(最典型的就是Flash)，就会弹出窗口问你O不OK，相当烦人。并且，Multiple IE 也不支持Vista。不过这些还不是大问题，最大的问题是作者貌似好久不更新了。</p> <p align="center"><a href="http://www.cnblogs.com/images/cnblogs_com/Klesh/WindowsLiveWriter/IEIETester_A361/multiple-ie_2.png"><img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="108" alt="multiple-ie" src="http://www.cnblogs.com/images/cnblogs_com/Klesh/WindowsLiveWriter/IEIETester_A361/multiple-ie_thumb.png" width="244" border="0"></a> <br>(基于快捷方式的Multiple IE)</p> <p>所幸江山代有才人出，日前从朋友处听来有一名为<a href="http://www.my-debugbar.com/wiki/IETester/HomePage">IETester</a>的Freeware也有相似的功能，支持IE8 beta 1, IE7 IE 6 and IE5.5 以及 Vista 和 XP 操作系统。</p> <p align="center"><a href="http://www.cnblogs.com/images/cnblogs_com/Klesh/WindowsLiveWriter/IEIETester_A361/ietester_2.png"><img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="143" alt="ietester" src="http://www.cnblogs.com/images/cnblogs_com/Klesh/WindowsLiveWriter/IEIETester_A361/ietester_thumb.png" width="244" border="0"></a> <br>(基于Tab模式的IETester)</p> <p align="left">IETester当前版本是0.2，安装包是23.7M，XP下安装需要先装IE7，运行的界面如上，没有Multiple IE弹出OK窗口的问题。按照作者所说，目前已知的问题有：</p> <ul> <li> <div align="left">当窗口大小改变的时候，页面内容可能会消失（作者改进中）。</div></li> <li> <div align="left">上一页/下一页功能不正常。</div></li> <li> <div align="left">Focus功能不正常。</div></li></ul> <p align="left">使用中遇到有其它bug可以到<a href="http://www.my-debugbar.com/forum/">这里</a>反馈。</p><img src ="http://www.cnblogs.com/Klesh/aggbug/1140228.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41939/" target="_blank">[新闻]搜狗五笔输入法发布</a>]]></description></item><item><title>拜托，请别再阉割WebForm了</title><link>http://www.cnblogs.com/Klesh/archive/2007/12/25/please-dont-castrate-webform.html</link><dc:creator>Klesh Wong</dc:creator><author>Klesh Wong</author><pubDate>Tue, 25 Dec 2007 04:44:00 GMT</pubDate><guid>http://www.cnblogs.com/Klesh/archive/2007/12/25/please-dont-castrate-webform.html</guid><wfw:comment>http://www.cnblogs.com/Klesh/comments/1013895.html</wfw:comment><comments>http://www.cnblogs.com/Klesh/archive/2007/12/25/please-dont-castrate-webform.html#Feedback</comments><slash:comments>87</slash:comments><wfw:commentRss>http://www.cnblogs.com/Klesh/comments/commentRss/1013895.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/Klesh/services/trackbacks/1013895.html</trackback:ping><description><![CDATA[<p>　　WebForm的组件式开发思想，去到极致就是GridView，为啥？自动化程度，高；智能程度，高；代码重用度，高；开发效率，高！所以微软大大小小的DEMO都喜欢用它，拖一拖，拉一拉，写几行代码，一个完整表格就出来啦……。不过，当我们回到现实应用中，基本上它就是一个摆设，偶尔会用它来作一些烟雾测试。所以，抛弃不合用的控件，对WebForm来说，不会有什么区别，不会导致WebForm发生本质的变化。当需要提高对页面代码控制粒度的时候，依靠基础的Repeater/Literal来输出纯净的页面代码也是理所当然的事了。</p> <p>　　当完成一件事能用更简单基于只能用更简单的方式去做的时候，应该是一件自然而然的事才对。</p> <p>　　可是，当ViewState被切下来的时候，我就很惊奇了，失去了ViewState的WebForm，Postback自然也没什么意义了，那么，原本隐藏在WebForm后面的HTTP机制不是又暴露在用户面前？那么，还能像WinForm那样轻松地定阅处理各种事件吗？那么，WebForm所倡导的RAD<strong>理念</strong>呢？(千万别ignore理念这个词)</p> <p>　　接下来甚至于内联的写法也被提出来了，怎么说呢，既然是内联写法了，那HTML的细节也暴露无遗了吧？那如此这般的WebForm相对于MVC又有什么优点了？组件化没了，RAD没了，可视化也没了，那究竟什么是WebForm？难道说一个aspx + aspx.cs就可以说它是WebForm了？那这WebForm的定义也太宽太广了吧！真是相当地期待 ScottGu 站出来说一究竟什么是WebForm以平息这种乱局。</p> <p>　　被阉割了的WebForm相比之MVC究竟还有什么优势？对Web开发新手而言，门槛似乎比MVC高了吧？不仅仅是需要了解HTML、HTTP等这些相对低层的东西，就是WebForm开发方面根本就不会有太多阉割版WebForm的资料可以参考。若是对页面代码的控制粒度有那么高的话，何不放开怀抱去拥抱质的变化？我想MVC的倡导者原先也极有可能是阉割的WebForm，到了一定程度，阉割版的WebForm也满足不了需求的时候，质的变化就出现了，就是不肯拥抱变化的态度，才让我们总是跟在人家后面跑！</p> <p>　　WebForm比之MVC即有如男人之比女人，男人做事粗放但办事效率高，女人做事细致而繁琐。硬是要他们同质化最终只会不伦不类。唉，难道潮流就是流行这些中性的东西，像什么超女超男的……</p> <p>　　有PHP背景的界面人员也太不具代表性了，要是和广大开发人员合作的界面人员都有PHP背景，那世界就和谐了，那我们就是社会主义最终形态了！而且我就假装地球上的界面人员都有PHP背景了吧，用MVC的话你连什么组件都不用跟他讲了，直接告诉他我给你什么数据，你给我POST什么数据不就OK了？</p> <p>　　看了也许您会觉得我在说WebForm不如MVC似的，所以我要强调一下：不是男人比不上女人，只是不要让男人去干女人干的事！</p> <p>　　Separation of Concerns　－　关注点分离，说到这一点，我感觉国内.NET社区还是少一种开放的态度，昨天看《自由》兄的一篇贴子，评论里有位同志提了一句“MVC/IoC/ORM的<strong>理念</strong>相似”，后面竟然有人回说“不知道mvc和ioc/orm的相似性在哪里...”然后还讲了很多，很无奈啊！理念这个词完全被ignored，我想请大家一定要互相尊重，认真地阅读他人的文字。</p><img src ="http://www.cnblogs.com/Klesh/aggbug/1013895.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41938/" target="_blank">[新闻]新学年开始 大学生适用的十大互联网应用</a>]]></description></item><item><title>敏捷开发解决实数转中文大写金额问题</title><link>http://www.cnblogs.com/Klesh/archive/2007/11/13/958111.html</link><dc:creator>Klesh Wong</dc:creator><author>Klesh Wong</author><pubDate>Tue, 13 Nov 2007 07:55:00 GMT</pubDate><guid>http://www.cnblogs.com/Klesh/archive/2007/11/13/958111.html</guid><wfw:comment>http://www.cnblogs.com/Klesh/comments/958111.html</wfw:comment><comments>http://www.cnblogs.com/Klesh/archive/2007/11/13/958111.html#Feedback</comments><slash:comments>24</slash:comments><wfw:commentRss>http://www.cnblogs.com/Klesh/comments/commentRss/958111.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/Klesh/services/trackbacks/958111.html</trackback:ping><description><![CDATA[摘要: 实数转中文大写的问题，虽然不能算是太难，但却也不是那种能一气呵成，一蹴而就的简单问题，一步到位的想法很容易就会陷入泥潭；正确的做法应该是对转换的规律抽丝剥茧，由浅入深一步一步完成转换步骤，如此便能水到渠成……敏捷开发的思想很适用于解决此类问题，借此机会正好和大家分享一些敏捷开发的经验。 　　开始之前，先看一下大写位进换数情况先，这里以目前财务体系的中法换算为准：  个=10的0次方十=10的1次方&nbsp;&nbsp;<a href='http://www.cnblogs.com/Klesh/archive/2007/11/13/958111.html'>阅读全文</a><img src ="http://www.cnblogs.com/Klesh/aggbug/958111.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41937/" target="_blank">[新闻]Google拟在9月发布自有浏览器 或采用火狐内核</a>]]></description></item><item><title>或者可以把MonoRail称之为MVC2框架</title><link>http://www.cnblogs.com/Klesh/archive/2007/10/22/can-we-call-monorail-as-a-mvc2-framework.html</link><dc:creator>Klesh Wong</dc:creator><author>Klesh Wong</author><pubDate>Mon, 22 Oct 2007 10:07:00 GMT</pubDate><guid>http://www.cnblogs.com/Klesh/archive/2007/10/22/can-we-call-monorail-as-a-mvc2-framework.html</guid><wfw:comment>http://www.cnblogs.com/Klesh/comments/933451.html</wfw:comment><comments>http://www.cnblogs.com/Klesh/archive/2007/10/22/can-we-call-monorail-as-a-mvc2-framework.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.cnblogs.com/Klesh/comments/commentRss/933451.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/Klesh/services/trackbacks/933451.html</trackback:ping><description><![CDATA[<p>　　刚刚在CSDN上面看到一篇有趣的新闻《<a href="http://news.csdn.net/n/20071022/109816.html">看Java和动态语言的两极分化</a>》，里面有一段文字：</p> <blockquote> <p><strong>记者：&#8220;脚本语言仍然会大行其道&#8221;，在这种情况下，您认为Java</strong><strong>和脚本语言各在哪些领域发挥哪些作用呢？</strong>  <p>纯月：随着Java应用的深入，人们渐渐开始不再把Java作为万金油。Java在Web开发上的确不具有明显的优势，Struts，JSF等框架相比基于脚本的语言增加了开发复杂性，很多开发者开始回到基于JSP的简单MVC2+持久层的方案。甚至在其他表现层上，例如桌面应用中Java都不具有优势。而Java和JEE更适合开发企业后端系统，例如消息中间件，ESB等。</p></blockquote>　　查了一下MVC2的概念，找到一些链接，《<a href="http://iamin.blogdriver.com/iamin/727340.html">MVC与MVC2的区别与对比</a>》：  <blockquote> <p>mvc模式：<br>view接收用户输入，把命令传到controller<br>controller处理命令，更新model<br>model被更新后，会通知view需要update<br>view更新后向用户显示<br>mvc2模式：<br>由于mvc1中，model可以通知view，然后view就可以被更新，这在windows程序中很常见，像MFC的frame-document-view架构，如果document改变了，他会主动通知view进行update<br>但是在web中，作为model的java bean更新后，无法调用作为view的jsp（而且view通常很多，需要选一个），因此改为mvc2模式：<br>view接收用户输入，把命令传到controller<br>controller处理命令，更新model<br>model被更新后，controller会选一个view并forward到这个jsp，附带把model放到request参数<br>这个view获得model然后显示</p></blockquote> <p>　　言简意骇，或者可以把这些概念引用到.NET里来，把MonoRail的实现称之为MVC2，这样就不会在概念上和传统的MVC有所冲突了吧。</p> <img src ="http://www.cnblogs.com/Klesh/aggbug/933451.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41936/" target="_blank">[新闻]微软正在构思将Wi-Fi带入汽车</a>]]></description></item><item><title>MonoRail和WebForm,谁是瑞士军刀</title><link>http://www.cnblogs.com/Klesh/archive/2007/10/22/monorail-and-webform-which-is-the-Victorinox.html</link><dc:creator>Klesh Wong</dc:creator><author>Klesh Wong</author><pubDate>Mon, 22 Oct 2007 08:27:00 GMT</pubDate><guid>http://www.cnblogs.com/Klesh/archive/2007/10/22/monorail-and-webform-which-is-the-Victorinox.html</guid><wfw:comment>http://www.cnblogs.com/Klesh/comments/933335.html</wfw:comment><comments>http://www.cnblogs.com/Klesh/archive/2007/10/22/monorail-and-webform-which-is-the-Victorinox.html#Feedback</comments><slash:comments>17</slash:comments><wfw:commentRss>http://www.cnblogs.com/Klesh/comments/commentRss/933335.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/Klesh/services/trackbacks/933335.html</trackback:ping><description><![CDATA[<p>　　从学习成本上来看，无疑WebForm的初期学习成本是相当的低。对于完全的新手，控件拖放然后编写后台代码应该是相当容易理解和接受的方式；对于从WinForm上转过来的开发人员，这种开发方式则早已成为他们的习惯；即使是对我这种由Classicl ASP/PHP那种coding/html混编转过来的人来讲，WebForm也一度让我觉得很完美(coding/html各自放在不同的地方，让人有种说不出来的舒服)。学习WebForm<u>甚至不需要</u>去了解什么是HTTP，什么是HTML，什么是Javascript&#8230;&#8230;</p>
<p>　　但若要使用MonoRail，事情就麻烦了，初级阶段需要了解的东西会比WebForm多出很多来，比如说，首先得习惯用传统的HTTP视角去看待客户端和服务器之间的交互，像是在做Classical ASP/PHP做开发一样，每一次的交互都是独立的。你要了解什么是HTML，什么是FORM，什么是INPUT，为了实现一些比较特别的功能，还需要Javascript，然后还要另外还要学习模板语法&#8230;&#8230;</p>
<p>　　不难得出的结义是，<strong>WebForm比MonoRail容易上手</strong>。特别是对没做过web开发的人来讲，它隐藏了web的许多底层的东西，展现出来是一个<strong>近似</strong>WinForm的快速开发方式。行文至此，可以说，WebForm比MonoRail更像是一把瑞士军刀，难道WebForm的功能不是比MonoRail更加强大吗？你要什么控件就给你什么，不需要另外去弄，都集成好了，不是吗？反过来看MonoRail，原本在WebForm上简单一拖就有的控件，却需要另外花力气去实现。</p>
<p>　　<em>瑞士军刀是好东西，功能强大，携带方便，当然也没有什么学习成本，但是，这些优点不是MonoRail所具备的。</em></p>
<p>　　随着一步一步将WebForm应用到开发中去，我意识到，WebForm也只是易上手而已，当要对控件进行定制时，就不得不深入去了解控件的模型，事件，方法等，有时候已有的控件也未必能满足定制的需求，又不得不依赖于第三方控件，找不到第三方控件时，则得自己进行控件开发，当涉入到控件开发领域时（主要指自定义控件），我才了解到什么叫做<strong>高昂的学习成本</strong>。前前后后我曾经花了三个月在这上面，大概也是能做到一般复杂的控件开发而已。更加不幸的是这么一来，本来由设计师做的界面修改工作变成了要我来做。唉，罪过啊罪过，这简直是在浪费生命。</p>
<p>　　我想，既然大家选择了做Web开发，那怎么可能不去了解什么是HTTP？什么是HTML？什么是Javascript？WebForm能帮你挡得了一时，可 能帮你挡一世？要知道，即使你将来换了开发平台，掌握这些基础知识即使不是不可或缺的，至少也是大有裨益的。</p>
<p>　　然而，对于一个基础扎实的Web开发人员来讲，MonoRail还剩多少东西可学？看完MonoRail的 <a href="http://www.castleproject.org/monorail/gettingstarted/index.html">Getting Statrted Sample</a>&nbsp;就可以开工了，把它的<a href="http://www.castleproject.org/monorail/documentation/v1rc2/index.html">Documentation</a>全部看完也花不了多少时间。但若是要把WebForm玩转，进入控件开发领域的时候，不但要了解HTTP/HTML/Javascript/...等等这些底层的东西，还得额外去了解WebForm的生命周期，控件继承树，各种各样的接口&#8230;&#8230;</p>
　　<em>MonoRail不是瑞士军刀，它不帮你输出html，不帮你管理页面状态，主要还是帮助你把视图、控制器分开来而已，其他的都是辅助性可有可无的东西。</em> 
<img src ="http://www.cnblogs.com/Klesh/aggbug/933335.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41936/" target="_blank">[新闻]微软正在构思将Wi-Fi带入汽车</a>]]></description></item><item><title>理解Javascript的闭包</title><link>http://www.cnblogs.com/Klesh/archive/2007/10/13/923115.html</link><dc:creator>Klesh Wong</dc:creator><author>Klesh Wong</author><pubDate>Sat, 13 Oct 2007 04:43:00 GMT</pubDate><guid>http://www.cnblogs.com/Klesh/archive/2007/10/13/923115.html</guid><wfw:comment>http://www.cnblogs.com/Klesh/comments/923115.html</wfw:comment><comments>http://www.cnblogs.com/Klesh/archive/2007/10/13/923115.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cnblogs.com/Klesh/comments/commentRss/923115.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/Klesh/services/trackbacks/923115.html</trackback:ping><description><![CDATA[<p>　　先来看两段代码，若是你不能完全理解它们的原理，则本文对你还是有一点参考作用的。</p> <p>　　首先是我写的一段用来模拟私有成员的代码:</p><pre><font color="#0000ff">function </font><font color="#800000">Foobar(parameter) {</font>
	<font color="#0000ff">var </font><font color="#800000">privateVariable = </font><font color="#ff00ff">"I'm private Variable"</font><font color="#800000">;</font>
	<font color="#0000ff">var </font><font color="#800000">privateFunction = </font><font color="#0000ff">function</font><font color="#800000">() {</font>
		<font color="#0000ff">return </font><font color="#ff00ff">"I'm privateFunction and privateVariable is : " </font><font color="#800000">+ privateVariable;</font>
	<font color="#800000">}</font>

	<font color="#0000ff">this</font><font color="#800000">.publicVariable = </font><font color="#ff00ff">"I'm public Variable"</font><font color="#800000">;</font>
	<font color="#0000ff">this</font><font color="#800000">.publicFunction = </font><font color="#0000ff">function</font><font color="#800000">() {</font>
		<font color="#ff0000">document</font><font color="#800000">.write(</font><font color="#ff00ff">"parameter : " </font><font color="#800000">+ parameter + </font><font color="#ff00ff">"&lt;br /&gt;"</font><font color="#800000">);</font>
		<font color="#ff0000">document</font><font color="#800000">.write(</font><font color="#ff00ff">"privateVariable : " </font><font color="#800000">+ privateVariable + </font><font color="#ff00ff">"&lt;br /&gt;"</font><font color="#800000">);</font>
		<font color="#ff0000">document</font><font color="#800000">.write(</font><font color="#ff00ff">"privateFunction : " </font><font color="#800000">+ privateFunction() + </font><font color="#ff00ff">"&lt;br /&gt;"</font><font color="#800000">);</font>
		<font color="#ff0000">document</font><font color="#800000">.write(</font><font color="#ff00ff">"publicVariable : " </font><font color="#800000">+ </font><font color="#0000ff">this</font><font color="#800000">.publicVariable + </font><font color="#ff00ff">"&lt;br /&gt;"</font><font color="#800000">);</font>
	<font color="#800000">}</font>
<font color="#800000">}</font>
<font color="#0000ff">var </font><font color="#800000">foobar = </font><font color="#0000ff">new </font><font color="#800000">Foobar(</font><font color="#ff00ff">"I'm paramter"</font><font color="#800000">);</font>
<font color="#800000">foobar.publicFunction();</font>
<font color="#ff0000">document</font><font color="#800000">.write(</font><font color="#ff00ff">"typeof(foobar.privateVariable) : " </font><font color="#800000">+ </font><font color="#0000ff">typeof</font><font color="#800000">(foobar.privateVariable) + </font><font color="#ff00ff">"&lt;br /&gt;"</font><font color="#800000">);</font>
<font color="#ff0000">document</font><font color="#800000">.write(</font><font color="#ff00ff">"typeof(foobar.privateFunction) : " </font><font color="#800000">+ </font><font color="#0000ff">typeof</font><font color="#800000">(foobar.privateFunction) + </font><font color="#ff00ff">"&lt;br /&gt;"</font><font color="#800000">);</font>
<font color="#ff0000">document</font><font color="#800000">.write(</font><font color="#ff00ff">"typeof(foobar.publicVariable) : " </font><font color="#800000">+ </font><font color="#0000ff">typeof</font><font color="#800000">(foobar.publicVariable) + </font><font color="#ff00ff">"&lt;br /&gt;"</font><font color="#800000">);</font>
<font color="#ff0000">document</font><font color="#800000">.write(</font><font color="#ff00ff">"typeof(foobar.publicFunction) : " </font><font color="#800000">+ </font><font color="#0000ff">typeof</font><font color="#800000">(foobar.publicFunction) + </font><font color="#ff00ff">"&lt;br /&gt;"</font><font color="#800000">);</font>
</pre>
<p>　　输出内容：</p><pre><p>parameter : I'm paramter<br>privateVariable : I'm private Variable<br>privateFunction : I'm privateFunction and privateVariable is : I'm private Variable<br>publicVariable : I'm public Variable<br>typeof(foobar.privateVariable) : undefined<br>typeof(foobar.privateFunction) : undefined<br>typeof(foobar.publicVariable) : string<br>typeof(foobar.publicFunction) : function</p></pre>
<p>　　可以看到，privateVariable和privateFunction都被成员的隐藏了起来。再看一段来自dojo类库的代码：</p><pre><font color="#0000ff">var </font><font color="#000000">getImgInPositionedDivHtml = (</font><font color="#0000ff">function</font><font color="#000000">(){</font>
    <font color="#0000ff">var </font><font color="#000000">buffAr = [</font>
        <font color="#ff00ff">'&lt;div id="'</font><font color="#000000">,</font>
        <font color="#ff00ff">''</font><font color="#000000">,   </font><font color="#008000">//index 1, DIV ID attribute</font>
        <font color="#ff00ff">'" style="position:absolute;top:'</font><font color="#000000">,</font>
        <font color="#ff00ff">''</font><font color="#000000">,   </font><font color="#008000">//index 3, DIV top position</font>
        <font color="#ff00ff">'px;left:'</font><font color="#000000">,</font>
        <font color="#ff00ff">''</font><font color="#000000">,   </font><font color="#008000">//index 5, DIV left position</font>
        <font color="#ff00ff">'px;width:'</font><font color="#000000">,</font>
        <font color="#ff00ff">''</font><font color="#000000">,   </font><font color="#008000">//index 7, DIV width</font>
        <font color="#ff00ff">'px;height:'</font><font color="#000000">,</font>
        <font color="#ff00ff">''</font><font color="#000000">,   </font><font color="#008000">//index 9, DIV height</font>
        <font color="#ff00ff">'px;overflow:hidden;\"&gt;&lt;img src=\"'</font><font color="#000000">,</font>
        <font color="#ff00ff">''</font><font color="#000000">,   </font><font color="#008000">//index 11, IMG URL</font>
        <font color="#ff00ff">'\" width=\"'</font><font color="#000000">,</font>
        <font color="#ff00ff">''</font><font color="#000000">,   </font><font color="#008000">//index 13, IMG width</font>
        <font color="#ff00ff">'\" height=\"'</font><font color="#000000">,</font>
        <font color="#ff00ff">''</font><font color="#000000">,   </font><font color="#008000">//index 15, IMG height</font>
        <font color="#ff00ff">'\" alt=\"'</font><font color="#000000">,</font>
        <font color="#ff00ff">''</font><font color="#000000">,   </font><font color="#008000">//index 17, IMG alt text</font>
        <font color="#ff00ff">'\"&gt;&lt;\/div&gt;'</font>
    <font color="#000000">];</font>
    <font color="#0000ff">return </font><font color="#000000">(</font><font color="#0000ff">function</font><font color="#000000">(url, id, width, height, top, left, altText){</font>
        <font color="#000000">buffAr[</font><font color="#800080">1</font><font color="#000000">] = id;</font>
        <font color="#000000">buffAr[</font><font color="#800080">3</font><font color="#000000">] = top;</font>
        <font color="#000000">buffAr[</font><font color="#800080">5</font><font color="#000000">] = left;</font>
        <font color="#000000">buffAr[</font><font color="#800080">13</font><font color="#000000">] = (buffAr[</font><font color="#800080">7</font><font color="#000000">] = width);</font>
        <font color="#000000">buffAr[</font><font color="#800080">15</font><font color="#000000">] = (buffAr[</font><font color="#800080">9</font><font color="#000000">] = height);</font>
        <font color="#000000">buffAr[</font><font color="#800080">11</font><font color="#000000">] = url;</font>
        <font color="#000000">buffAr[</font><font color="#800080">17</font><font color="#000000">] = altText;</font>
        <font color="#0000ff">return </font><font color="#000000">buffAr.join(</font><font color="#ff00ff">''</font><font color="#000000">);</font>
    <font color="#000000">}); </font><font color="#008000">//:End of inner function expression.</font>
<font color="#000000">})();</font>
</pre>
<p>　　<font color="#000000">这是来自于dojo类库的一段代码，基本上，getImgInPositionedDivHtml是一个用来创建绝对定位div的html代码。这个div是有固定的模式的，变动的只是具体的一些属性，所以它用了一个buffAr来承载这个模式，getImgInPositionedDivHtml在接收到具体的属性值(url, id, width, height, top, left, altText)，就会把它放到buffAr的相应位置，再返回buffAr的join后的字符串。而无论getImgInPositionedDivHtml被执行多少次，buffAr只会被创建一次，类似于静态成员的行为。当然，在真正的面向对向语言中，我们是不能这么做的，因为还要考虑到线程安全的问题，有点扯远了。</font></p>
<p>　　这两段代码，都是利用了Javascript特有的闭包来实现各自的效果，什么是闭包呢？</p>
<p>　　我们知道，变量有作用域的概念，在一个函数体中，使用var声明局部变量只在函数体中能被引用，而在函数外部则是无法访问该变量的。理论上，局部变量会在函数执行完毕之后被释放，除非它有在其它地方被引用（也很有可能是在脚本被卸载的时候才统一回收的，但这不影响它能否被引用），最容易理解的就是被return回上层调用：</p><pre><font color="#0000ff">function </font><font color="#000000">getLocalVar() {</font>
	<font color="#0000ff">var </font><font color="#000000">localVar = </font><font color="#ff00ff">"I'm local var"</font><font color="#000000">;</font>
	<font color="#0000ff">return </font><font color="#000000">localVar;</font>
<font color="#000000">}</font>
<font color="#0000ff">var </font><font color="#000000">globalVar = getLocalVar();</font>
</pre>
<p>　　作用域的概念在很多编程语言中都有存在，在Javascript中一样也有，特别的地方就在于Javascript是一种动态语言，它可以定义内嵌函数。当你在一个函数内部定义一个新的函数时，内嵌函数就可以引用该函数的局部变量。：</p><pre><font color="#0000ff">function </font><font color="#000000">OutterFunction() {</font>
	<font color="#0000ff">var </font><font color="#000000">outterVar;</font>
	<font color="#0000ff">function </font><font color="#000000">InnerFunction() {</font>
		<font color="#0000ff">return </font><font color="#000000">outterVar;</font>
	<font color="#000000">}</font>
<font color="#000000">}</font>
</pre>
<p>　　当OutterFunction每次被调用时，解释器会为它分配一个栈区，所有的局部变量都会被放到这个栈区（闭包开始），栈区内的局部变量都是相互可见的，而InnerFunction也是局部变量之一，所以当OutterFunction执行完毕退出以后，InnerFunction依然可以访问到其他的局部变量（闭包形包）。要注意的是InnerFunction每次都被重新创建，每次返回都是不同的。换句话说，<u>闭包就是指返回出来的InnerFunction可以访问到OutterFunction的局部变量这种情形</u>。</p>
<p>　　理解并合理地使用闭包可以使程序更加灵活和优雅。很多Javascript类库都有闭包的应用，所以，如果需要经常和Javascript打交道的话，闭包是无法绕过的概念。</p><img src ="http://www.cnblogs.com/Klesh/aggbug/923115.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41935/" target="_blank">[新闻]对比测试：IE8 Beta 2 VS Firefox 3.0.1</a>]]></description></item><item><title>WebForm与MonoRail</title><link>http://www.cnblogs.com/Klesh/archive/2007/10/13/webform-and-monorail.html</link><dc:creator>Klesh Wong</dc:creator><author>Klesh Wong</author><pubDate>Sat, 13 Oct 2007 04:04:00 GMT</pubDate><guid>http://www.cnblogs.com/Klesh/archive/2007/10/13/webform-and-monorail.html</guid><wfw:comment>http://www.cnblogs.com/Klesh/comments/923099.html</wfw:comment><comments>http://www.cnblogs.com/Klesh/archive/2007/10/13/webform-and-monorail.html#Feedback</comments><slash:comments>29</slash:comments><wfw:commentRss>http://www.cnblogs.com/Klesh/comments/commentRss/923099.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/Klesh/services/trackbacks/923099.html</trackback:ping><description><![CDATA[<p align="center"><strong>本文所指WebForm，特指Microsoft在.NET平台上倡导的类似WinForm的组件式开发方式，所有Controller特指MonoRail里的Controller非MVC里的Controller。</strong><br />
</p>
<p>　　WebForm的开发理念源自于WinForm，基于控件的拖放及后台代码编写的开发方式的而且确是很方便快捷的开发方法，WinForm的成功很好地说明了这一点。整个WebForm的核心就是控件，以封装手，重用为目的而存在的控件使得WebForm极为容易上手。得力于WebForm方便快捷的开发方式，.NET在<strong>企业开发</strong>领域取得了巨大成功。</p>
<p>　　HTTP协议是无状态，WebForm为了要像WinForm那样保持控件的状态，不得不依赖于ViewState/Postback这些机制。<u>总的来说，WebForm是以控件为核心，依赖于ViewState、Postback机制最终构成的。脱离了它们，WebForm则不能称之为WebForm</u>。</p>
<p>　　然而，控件在带来方便的同时，其生成的HTML代码也难以维护；ViewState带来状态维护的方便，同时也造成了性能的损耗；在Postback机制中有一点让我很困惑的是：很多时候我们要在Page_Load判断是否第一次请求然后执行一些初始化（如绑定的动作）。为何Microsoft不在Page上加多一个<em>Initialize之类的事件</em>来代替现有的在<em>Page_Load中判断IsPostback</em>。这样做一来会比较优雅，二来也更加像WinForm了。</p>
<p><strong>　</strong>　再来看看aspx和cs，它们是WebForm<strong>表现</strong>和<strong>代码</strong>分离的形式，是的，仅仅只是<strong>表现</strong>和<strong>代码</strong>的分离而已。aspx上面的控件依赖于后台cs的控制，而后台的cs则依赖于aspx的存在。分离是物理上的，逻辑上它们还是紧密地结合在一起的，没有了谁都不行。专业点来说，它们是耦合在一起的。</p>
<p>　　在企业开发领域，由于软件将会在企业内部有限范围内使用，通常则不会对界面有太高要求，甚至大多数时候都是开发人员直接拼出来的界面。因此，WebForm生成的代码再恶心，再难以维护，aspx/cs耦合再严重也没什么影响。ViewState/Posback带来的性能损耗在使用人数有限的企业应用中也并不突出。所以用WebForm来代替<strong>一部份企业CS</strong>（Client/Server）应用好处是很明显的，比如说，系统更新时只需更新服务器，而不用每次分发客户端。</p>
<p>　　可是，毕竟不是所有使用.NET的人都在做企业开发，当用WebForm来做<strong>网站开发</strong>，我感觉就像牛刀杀鸡一般。在网站开发的世界里，我们的客人，通常会对界面会有所要求，甚至于有时候会要求生成的HTML代码要符合W3C标准&#8230;&#8230;所以，对HTML代码的控制变得重要起来；另一方面，作为放在internet上供众人浏览的网站，ViewState/Posback带来的性能开销变得不容忽视起来；aspx/cs耦合使得无法将它们作为设计师和程序员<strong>职责分离</strong>的承载体（特别是当程序员在页面上扔一些SqlDataSource之类的控件）。</p>
<p>　　牛刀不是不能杀鸡，但很多人毕竟不习惯用牛刀来杀鸡。MVP(Model-View-Presenter)才是真正切合网站开发实际情况的开发方式。虽然WebForm比MonoRail要强大，很多，但它毕竟不适合我们做网站开发的。</p>
<p>　　<a title="MonoRail 官方网站" href="http://www.castleproject.org/monorail/index.html">MonoRail</a>就是适合网站开发的一套MVP开发框架。与WebForm的开发理念不同，它着重于<strong>职责的分离</strong>。当Controller(MonoRail中的Presenter称为Controller)和View被切割隔离开来时，设计师和程序员和谐地分工合作也<u>成为了可能</u>（世事无绝对）。MVP的理念中，没有控件，没有Posback，当然也没有ViewState，客户端和服务器间的交流回归到了原始的Form/QueryString/Cookie的方式（难道这样不是更自然吗？），借由此，设计师重新找回了对HTML代码的控制权。程序员在Controller里专注于数据的处理，设计师（我通常认为设计师应该具有一定的html知识）通过学习简单的模板控制代码后，模板文件则会成为他的势力范围。</p>
<p>　　Contoller和View之间会通过松散的PropertyBag进行沟通，而不像WebForm的asp/cs那样紧密（cs会对aspx上的控件在在引用，或aspx上的控件会绑定cs产生的数据）。因此，View对呈现的逻辑有绝对的控制权，而单纯的View上的修改调整也不会对Controller产生影响。即使设计师把View全删光了，程序也不会出错，那么，你就再也不必为设计师的过失而背黑锅了。而本来应该由设计师完成的修改也不需要程序员来做了。</p>
<p>　　无论是WebForm也好，MonoRail也好，最大的区别还是它们的理念。其实，只要你有好的意识，使用WebForm一样也可以把界面和控制分离得很好；相反，在MonoRail的Controller里一样可以生成HTML代码再传给View&#8230;&#8230;</p>
<p>&nbsp;</p>
<br />
<p>追加两个分别用WebForm和MonoRail实现购物车功能的Demo</p>
<p><a href="/Files/Klesh/ShoppingCart-MonoRail.rar" title="Shopping Cart (MonoRail)">Shopping Cart (WebForm)<br />
<br />
</a><a href="/Files/Klesh/ShoppingCart-MonoRail.rar" title="Shopping Cart (MonoRail)">Shopping Cart (MonoRail)&nbsp;</a> <a href="/Files/Klesh/ShoppingCart-MonoRail.rar" title="Shopping Cart (MonoRail)"><br />
</a></p>
<img src ="http://www.cnblogs.com/Klesh/aggbug/923099.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41935/" target="_blank">[新闻]对比测试：IE8 Beta 2 VS Firefox 3.0.1</a>]]></description></item><item><title>理解Javascript中类的定义</title><link>http://www.cnblogs.com/Klesh/archive/2007/09/28/understanding-classs-definition-in-javascript.html</link><dc:creator>Klesh Wong</dc:creator><author>Klesh Wong</author><pubDate>Fri, 28 Sep 2007 03:32:00 GMT</pubDate><guid>http://www.cnblogs.com/Klesh/archive/2007/09/28/understanding-classs-definition-in-javascript.html</guid><wfw:comment>http://www.cnblogs.com/Klesh/comments/909109.html</wfw:comment><comments>http://www.cnblogs.com/Klesh/archive/2007/09/28/understanding-classs-definition-in-javascript.html#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://www.cnblogs.com/Klesh/comments/commentRss/909109.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/Klesh/services/trackbacks/909109.html</trackback:ping><description><![CDATA[摘要: 　　Foo在实例化后就是一个独立的个体，它的任何改动不会影响到原来的类型定义，也不会影响到其他的实例；而Bar则不同，它的所有实例都依然受prototype的影响，通过对Bar.prototype的修改或扩展，可以影响到所有包括已经实例化的实例。回过头来看Foo，由于它的成员，特别是成员函数是动态创建的，则可以利用它来模拟面向对象中的“私有成员&nbsp;&nbsp;<a href='http://www.cnblogs.com/Klesh/archive/2007/09/28/understanding-classs-definition-in-javascript.html'>阅读全文</a><img src ="http://www.cnblogs.com/Klesh/aggbug/909109.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41934/" target="_blank">[新闻]iPhone存在严重密码安全漏洞</a>]]></description></item></channel></rss>