<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><atom:link rel="hub" href="http://tumblr.superfeedr.com/" xmlns:atom="http://www.w3.org/2005/Atom"/><description>F2E 
Web Developer
UED Taobao
Fudan University</description><title>goddyzhao</title><generator>Tumblr (3.0; @goddyzhao)</generator><link>http://blog.goddyzhao.me/</link><item><title>Charles: Mac下的fiddler</title><description>&lt;a href="http://www.charlesproxy.com/"&gt;Charles: Mac下的fiddler&lt;/a&gt;: &lt;p&gt;这款软件是和fiddler最像的代理软件。需要和firefox配合使用（内置了firefox的插件，自动安装）。不过不管怎么说，它满足了我经常要代理文件来debug的需求。btw，它是收费的！&lt;/p&gt;</description><link>http://blog.goddyzhao.me/post/22631825198</link><guid>http://blog.goddyzhao.me/post/22631825198</guid><pubDate>Mon, 07 May 2012 22:50:31 -0400</pubDate><category>mac</category><category>http proxy</category><category>fiddler alternative</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>动态修改script标签中的src属性存在的问题</title><description>&lt;p&gt;今天某个同事遇到一个诡异的问题，问题描述如下：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;需求&lt;/strong&gt;：通过脚本动态修改script标签的src来载入一段外部脚本并执行&lt;br/&gt;&lt;strong&gt;实现方式(#1)&lt;/strong&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;script type="text/javascript" id="external-script"&amp;gt;
&amp;lt;/script&amp;gt;
&amp;lt;script type="text/javascript"&amp;gt;
    document.getElementById('external-script').src='url2';
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;url2的内容如下&lt;/strong&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;alert('I am dynamic');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;结果&lt;/strong&gt;：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;Chrome: 什么事都没发生（没有请求url2）&lt;/li&gt;
&lt;li&gt;Firefox: 什么事都没发生（没有请求url2）&lt;/li&gt;
&lt;li&gt;IE9：什么事都没发生（请求url2但不执行url2的脚本）&lt;/li&gt;
&lt;li&gt;IE(6,7,8): I am dynamic(请求并执行了url2的脚本)&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;注意实现方式中，第一段的script标签中间是有内容的（空格、换行符以及回车符）。&lt;/p&gt;

&lt;p&gt;如何来解释这个问题呢？要解释这个问题，我们来看两个变种的例子，第一个例子（明确内联内容)，如下所示（#2）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;script type="text/javascript" id="external-script"&amp;gt;
alert('I am inline');
&amp;lt;/script&amp;gt;
&amp;lt;script type="text/javascript"&amp;gt;
    document.getElementById('external-script').src='url2';
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;结果如下：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;Chrome: I am inline（没有请求url2）&lt;/li&gt;
&lt;li&gt;Firefox: I am inline（没有请求url2）&lt;/li&gt;
&lt;li&gt;IE9：I am inline（请求url2但不执行url2的脚本）&lt;/li&gt;
&lt;li&gt;IE(6,7,8): I am inline I am dynamic(请求并执行了url2的脚本)&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;再来看看第二个变种的例子(#3)：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;script type="text/javascript" id="external-script" src="url1"&amp;gt;
alert('I am inline script');
&amp;lt;/script&amp;gt;
&amp;lt;script type="text/javascript"&amp;gt;
    document.getElementById('external-script').src='url2';
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中url1的内容如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;alert('I am url1');
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;结果如下：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;Chrome: I am url1（没有请求url2）&lt;/li&gt;
&lt;li&gt;Firefox: I am url1（没有请求url2）&lt;/li&gt;
&lt;li&gt;IE9：I am url1（请求url2但不执行url2的脚本）&lt;/li&gt;
&lt;li&gt;IE(6,7,8): I am url1 I am dynamic(请求并执行了url2的脚本)&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;首先这里肯定的是src属性是修改成功的，可以通过看dom的变化看到src已经设置进去了。这个时候我们比对这三个例子，思考几十秒。分析下这三个例子，其实#2和#1是一样的，这里用#2是为了说明#1中的空格、换行符以及回车符会被浏览器认为是内联的脚本。通过比对#2和#3，是不是会让你想到什么？没错，我们第一个会想到的就是：&lt;em&gt;当script标签既有src属性又有内联脚本的时候浏览器该如何处理？&lt;/em&gt; ， 先来解释这个问题。&lt;/p&gt;

&lt;p&gt;一谈到浏览器应该怎样处理，就不得不翻出各种宝典，这次不再是葵花宝典了，而是九阴真经(&lt;a href="http://www.w3.org/TR/1999/REC-html401-19991224/interact/scripts.html#h-18.2.4" target="_blank"&gt;W3C的HTML4标准&lt;/a&gt;)，标准中关于script标签的src部分有如下一段话：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If the src attribute is not set, user agents must interpret the contents of the element as the script. If the src has a URI value, user agents must ignore the element&amp;#8217;s contents and retrieve the script via the URI&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;上面这段话的意思就是说：&lt;em&gt;如果src没有设置，那么就执行内联脚本，如果src设置了浏览器就必须忽略内敛脚本而要去请求src指定的url的内容&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;这解释了为什么#3中标准浏览器（甚至IE6，7，8）都没有执行内联脚本（因为src设置了url1）。&lt;/p&gt;

&lt;p&gt;搞清楚了这个基础问题之后，接下来问题就定位到了&lt;em&gt;动态修改script的src属性的时候浏览器如何处理？&lt;/em&gt; ，从结果来看，标准的浏览器都没有去请求url2（更改src无效），这回IE6，7，8终于犯傻了。当然了，咱们也不能随随便便说人家犯傻，要拿出证据，这个时候继续拿出九阴真经&lt;a href="http://www.w3.org/TR/2012/WD-html5-20120329/the-script-element.html#the-script-element" target="_blank"&gt;W3C的HTML5标准&lt;/a&gt;，其中有这样一句话：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Changing the src, type, charset, async, and defer attributes dynamically has no direct effect; these attribute are only used at specific times described below.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;意思就是说：&lt;em&gt;修改src是没用的，对src的处理只会在特定的时候进行（个人猜测就是第一次看到这个属性的时候浏览器会去做相应处理，之后就无视它了）。&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;好了，这下真相大白了：这解释了为啥#3和#1中除了IE6，7，8之外其他浏览器都没有去请求url2（IE9请求了，但没执行），而且实验发现IE6，7，8对动态修改src都会做请求执行处理。&lt;/p&gt;

&lt;p&gt;最后，这个故事至少告诉我们：写script标签的时候千万别手贱打回车。&lt;/p&gt;

&lt;p&gt;参考文档：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.w3.org/TR/2012/WD-html5-20120329/the-script-element.html#the-script-element" target="_blank"&gt;HTML5标准文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.w3.org/TR/1999/REC-html401-19991224/interact/scripts.html#h-18.2.4" target="_blank"&gt;HTML4标准文档&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><link>http://blog.goddyzhao.me/post/21911199803</link><guid>http://blog.goddyzhao.me/post/21911199803</guid><pubDate>Fri, 27 Apr 2012 09:42:00 -0400</pubDate><category>javascript</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>如何判断页面的编码？</title><description>&lt;p&gt;在写爬虫程序（web页面）的时候，经常需要去解析页面的内容，而在解析前就必须要知道该页面是用何种字符集来编码的，这样才能有效的避免乱码的问题。&lt;/p&gt;

&lt;p&gt;那么如何才能得知目的页面的编码呢？让我们来看看来自“&lt;a href="http://www.w3.org/TR/html4/charset.html" target="_blank"&gt;W3C&lt;/a&gt;”的官方解释：&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;An HTTP &amp;#8220;charset&amp;#8221; parameter in a &amp;#8220;Content-Type&amp;#8221; field&lt;/li&gt;
&lt;li&gt;A META declaration with &amp;#8220;http-equiv&amp;#8221; set to &amp;#8220;Content-Type&amp;#8221; and a value set for &amp;#8220;charset&amp;#8221;&lt;/li&gt;
&lt;li&gt;The charset attribute set on an element that designates an external resource&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;上述描述中表现了检测页面编码的优先级，也就是说首先会看http头信息中的“Content-Type”字段、如果没有，就去看Meta信息，还没有的话，对于一些外链（如css、JavaScript）就会看这种元素专门的charset字段。如果检查完上述三种方式之后还是无法确定呢？那就采用默认的&lt;em&gt;ISO-8859-1&lt;/em&gt;字符集来解析。&lt;/p&gt;

&lt;p&gt;顺便提一句，既然是W3C的标准，那就说明标准浏览器都是这么工作的哦！&lt;/p&gt;</description><link>http://blog.goddyzhao.me/post/20532303004</link><guid>http://blog.goddyzhao.me/post/20532303004</guid><pubDate>Thu, 05 Apr 2012 12:43:24 -0400</pubDate><category>http</category><category>encoding</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>通过什么途径能够深入了解JavaScript引擎是如何工作的？</title><description>&lt;p&gt;昨天收到一封来自深圳的一位前端童鞋的邮件，邮件内容如下（很抱歉，未经过他的允许，公开邮件内容，不过我相信其他人肯定也有同样的问题，所以，直接把问题原文抛出来）：&lt;/p&gt;

&lt;p&gt;“读了你的几篇关于JS（变量对象、作用域、上下文、执行代码）的文章，我个人觉得有点抽象，难以深刻理解。我想请教下通过什么途径能够深入点的了解javascript解析引擎在执行代码前后是怎么工作的，ecma英文版实在看不下去呵呵。”&lt;/p&gt;

&lt;p&gt;其实这个问题个人觉得太笼统了，直接回答很难回答，所以，我打算先把他的问题拆解成如下几个子问题，并对其表达个人的观点，希望对有同样困惑的童鞋能够有所帮助。&lt;/p&gt;

&lt;h2&gt;1. 什么是JavaScript解析引擎？&lt;/h2&gt;

&lt;p&gt;简单地说，JavaScript解析引擎就是能够“读懂”JavaScript代码，并准确地给出代码运行结果的一段程序。比方说，当你写了 &lt;em&gt;var a = 1 + 1;&lt;/em&gt; 这样一段代码，JavaScript引擎做的事情就是看懂（解析）你这段代码，并且将a的值变为2。&lt;/p&gt;

&lt;p&gt;学过编译原理的人都知道，对于静态语言来说（如Java、C++、C），处理上述这些事情的叫&lt;strong&gt;编译器（Compiler）&lt;/strong&gt;，相应地对于JavaScript这样的动态语言则叫&lt;strong&gt;解释器（Interpreter）&lt;/strong&gt;。这两者的区别用一句话来概括就是：&lt;strong&gt;编译器是将源代码编译为另外一种代码（比如机器码，或者字节码），而解释器是直接解析并将代码运行结果输出&lt;/strong&gt;。 比方说，firebug的console就是一个JavaScript的解释器。&lt;/p&gt;

&lt;p&gt;但是，现在很难去界定说，JavaScript引擎它到底算是个解释器还是个编译器，因为，比如像V8（Chrome的JS引擎），它其实为了提高JS的运行性能，在运行之前会先将JS编译为本地的机器码（native machine code），然后再去执行机器码（这样速度就快很多），相信大家对&lt;strong&gt;JIT（Just In Time Compilation）&lt;/strong&gt;一定不陌生吧。&lt;/p&gt;

&lt;p&gt;我个人认为，不需要过分去强调JavaScript解析引擎到底是什么，了解它究竟做了什么事情我个人认为就可以了。对于编译器或者解释器究竟是如何看懂代码的，翻出大学编译课的教材就可以了。&lt;/p&gt;

&lt;p&gt;这里还要强调的就是，JavaScript引擎本身也是程序，代码编写而成。比如V8就是用C/C++写的。&lt;/p&gt;

&lt;h2&gt;2. JavaScript解析引擎与ECMAScript是什么关系？&lt;/h2&gt;

&lt;p&gt;JavaScript引擎是一段程序，我们写的JavaScript代码也是程序，如何让程序去读懂程序呢？这就需要定义规则。比如，之前提到的&lt;em&gt;var a = 1 + 1;&lt;/em&gt;，它表示：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;左边var代表了这是申明（declaration）,它申明了a这个变量&lt;/li&gt;
&lt;li&gt;右边的+表示要将1和1做加法&lt;/li&gt;
&lt;li&gt;中间的等号表示了这是个赋值语句&lt;/li&gt;
&lt;li&gt;最后的分号表示这句语句结束了&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;上述这些就是规则，有了它就等于有了衡量的标准，JavaScript引擎就可以根据这个标准去解析JavaScript代码了。那么这里的ECMAScript就是定义了这些规则。其中ECMAScript 262这份文档，就是对JavaScript这门语言定义了一整套完整的标准。其中包括：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;var，if，else，break，continue等是JavaScript的关键词&lt;/li&gt;
&lt;li&gt;abstract，int，long等是JavaScript保留词&lt;/li&gt;
&lt;li&gt;怎么样算是数字、怎么样算是字符串等等&lt;/li&gt;
&lt;li&gt;定义了操作符（+，-，&amp;gt;，&amp;lt;等）&lt;/li&gt;
&lt;li&gt;定义了JavaScript的语法&lt;/li&gt;
&lt;li&gt;定义了对表达式，语句等标准的处理算法，比如遇到&lt;strong&gt;==&lt;/strong&gt;该如何处理&lt;/li&gt;
&lt;li&gt;⋯⋯&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;标准&lt;/strong&gt;的JavaScript引擎就会根据这套文档去实现，注意这里强调了&lt;strong&gt;标准&lt;/strong&gt;，因为也有不按照标准来实现的，比如IE的JS引擎。这也是为什么JavaScript会有兼容性的问题。至于为什么IE的JS引擎不按照标准来实现，就要说到浏览器大战了，这里就不赘述了，自行Google之。&lt;/p&gt;

&lt;p&gt;所以，简单的说，ECMAScript定义了语言的标准，JavaScript引擎根据它来实现，这就是两者的关系。&lt;/p&gt;

&lt;h2&gt;3. JavaScript解析引擎与浏览器又是什么关系？&lt;/h2&gt;

&lt;p&gt;简单地说，JavaScript引擎是浏览器的组成部分之一。因为浏览器还要做很多别的事情，比如解析页面、渲染页面、Cookie管理、历史记录等等。那么，既然是组成部分，因此一般情况下JavaScript引擎都是浏览器开发商自行开发的。比如：IE9的Chakra、Firefox的TraceMonkey、Chrome的V8等等。&lt;/p&gt;

&lt;p&gt;从而也看出，不同浏览器都采用了不同的JavaScript引擎。因此，&lt;strong&gt;我们只能说要深入了解哪个JavaScript引擎&lt;/strong&gt;。&lt;/p&gt;

&lt;h2&gt;4. 深入了解其内部原理的途径有哪些？&lt;/h2&gt;

&lt;p&gt;搞清楚了前面三个问题，那这个问题就好回答了。个人认为，主要途径有如下几种（依次由浅入深）：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;看讲JavaScript引擎工作原理的书&lt;br/&gt;
这种方式最方便，不过我个人了解到的这样的书几乎没有，但是&lt;a href="http://dmitrysoshnikov.com/" target="_blank"&gt;Dmitry A.Soshnikov&lt;/a&gt;博客上的文章真的是非常的赞，建议直接看英文，实在英文看起来吃力的，可以看我翻译的&lt;a href="http://blog.goddyzhao.me/JavaScript-Internal" target="_blank"&gt;译本&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;看ECMAScript的标准文档&lt;br/&gt;
这种方式相对直接，原汁原味，因为引擎就是根据标准来实现的。目前来说，可以看&lt;a href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf" target="_blank"&gt;第五版&lt;/a&gt;和&lt;a href="http://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%203rd%20edition,%20December%201999.pdf" target="_blank"&gt;第三版&lt;/a&gt;，不过要看懂也是不容易的。&lt;/li&gt;
&lt;li&gt;看JS引擎源代码&lt;br/&gt;
这种方式最直接，当然也最难了。因为还牵涉到了如何实现词法分析器，语法分析器等等更加底层的东西了，而且并非所有的引擎代码都是开源的。&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;5. 以上几种方式中第一种都很难看明白怎么办？&lt;/h2&gt;

&lt;p&gt;其实第一种方式中的文章，作者已经将文档中内容提炼出来，用通俗易懂的方式阐述出来了。如果，看起来还觉得吃力，那说明还缺少两块的东西：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;对JavaScript本身还理解的不够深入&lt;br/&gt;
如果你刚刚接触JavaScript，或者说以前甚至都没有接触过。那一下子就想要去理解内部工作原理，的确是很吃力的。首先应该多看看书，多实践实践，从知识和实践的方式来了解JavaScript预言特性。这种情况下，你只需要了解现象。比方说，&lt;em&gt;(function(){})()&lt;/em&gt; 这样可以直接调用该匿名函数、用闭包可以解决循环中的延迟操作的变量值获取问题等等。要了解这些，都是需要多汲取和实践的。实践这里就不多说了，而知识汲取方面可以多看看书和博客。这个层面的书就相对比较多了，&lt;a href="http://www.amazon.com/Professional-JavaScript-Developers-Nicholas-Zakas/dp/1118026691/" target="_blank"&gt;《Professional JavaScript for Web Developers》&lt;/a&gt;就是本很好的书（中文版请自行寻找）。&lt;/li&gt;
&lt;li&gt;缺乏相应的领域知识&lt;br/&gt;
当JavaScript也达到一定深度了，但是，还是看不大明白，或者没法很深入到内部去一探究竟。那就意味着缺少对应的领域知识。这里明显的就是编译原理相关的知识。不过，其实对这块了解个大概基本看起来就没问题了。要再继续深入，那需要对编译原理了解的很深入，比如说词法分析采用什么算法，一般怎么处理。会有什么问题，如何解决，AST生成算法一般有哪几种等等。那要看编译原理方面的书，也有基本经典的书，比如&lt;a href="http://www.amazon.com/Compilers-Principles-Techniques-Tools-2nd/dp/0321486811/" target="_blank"&gt;《Compilers: Principles, Techniques, and Tools》&lt;/a&gt;这本也是传说中的龙书，还有非常著名的&lt;a href="http://mitpress.mit.edu/sicp/full-text/book/book.html" target="_blank"&gt;《SICP》&lt;/a&gt;和&lt;a href="http://www.cs.brown.edu/~sk/Publications/Books/ProgLangs/" target="_blank"&gt;《PLAI》&lt;/a&gt;。不过其实根据个人经验，对于Dmitry的文章，要看懂它，只要你对JavaScript有一定深度的了解，同时你大学计算机的课程都能大致掌握了（尤其是操作系统），也就是说基础不错，理解起来应该没问题。因为这些文章基本没有涉及底层编译相关的，只是在解释文档的内容，并且其中很多东西都是相通的，比如：context的切换与CPU的进程切换、函数相关的的局部变量的栈存储、函数退出的操作等等都是一致的。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;以上就是个人对这个问题的看法，除此之外，我觉得，学习任何技术都不能操之过急，要把基础打扎实了，这样学什么都会很快。&lt;/p&gt;</description><link>http://blog.goddyzhao.me/post/18554142516</link><guid>http://blog.goddyzhao.me/post/18554142516</guid><pubDate>Thu, 01 Mar 2012 11:26:39 -0500</pubDate><category>JavaScript</category><category>JavaScript-Internal</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>说说为什么 [] == ![] 为true</title><description>&lt;p&gt;此前在微博上无意中看到有人问&lt;strong&gt;“为什么alert([] ==&amp;#160;![])会是true？”&lt;/strong&gt;，&lt;br/&gt;
刚看到这个问题我也说不上来究竟是什么原因，只知道这个肯定又是和&lt;strong&gt;==&lt;/strong&gt;操作相关的类型转换问题。&lt;br/&gt;
于是，就翻开了&lt;a href="http://www.ecma-international.org/publications/standards/Ecma-262.htm" target="_blank"&gt;&lt;strong&gt;“葵花宝典（ECMA-262-5th）”&lt;/strong&gt;&lt;/a&gt;，你懂的。&lt;/p&gt;

&lt;p&gt;在宝典的帮助下，我尝试着来解释下该问题的原因：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;首先看看&lt;strong&gt;==&lt;/strong&gt;这个操作内部是如何工作的  &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;宝典中的关于&lt;strong&gt;==&lt;/strong&gt;操作的工作描述如下(11.9.1)：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The production EqualityExpression: &lt;em&gt;EqualityExpression&lt;/em&gt; == &lt;em&gt;RelationalExpression&lt;/em&gt; is evaluated as follows:&lt;br/&gt;
  1. Let &lt;em&gt;lref&lt;/em&gt; be the result of evaluating &lt;em&gt;EqualityExpression&lt;/em&gt;&lt;br/&gt;
  2. Let &lt;em&gt;lval&lt;/em&gt; be GetValue(&lt;em&gt;lref&lt;/em&gt;)&lt;br/&gt;
  3. Let &lt;em&gt;rref&lt;/em&gt; be the result of evaluating &lt;em&gt;RelationalExpression&lt;/em&gt;&lt;br/&gt;
  4. Let &lt;em&gt;rval&lt;/em&gt; be GetValue(&lt;em&gt;rref&lt;/em&gt;)&lt;br/&gt;
  5. Return the result of performing abstract equality comparison &lt;em&gt;rval==lval&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;&lt;li&gt;根据上面的步骤，我们来对问题作如下解析：  &lt;/li&gt;
&lt;/ul&gt;&lt;ol&gt;&lt;li&gt;先求GetValue([])  &lt;/li&gt;
&lt;li&gt;再求GetValue(![])  &lt;/li&gt;
&lt;li&gt;最后求 GetValue([]) == GetValue(![])  &lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;先要搞清楚GetValue方法是干嘛的，继续看宝典关于GetValue的描述(8.7.1)：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;&lt;li&gt;If Type(V) is not Reference, return V  &lt;/li&gt;
  &lt;li&gt;&amp;#8230;.  &lt;/li&gt;
  &lt;/ol&gt;&lt;/blockquote&gt;

&lt;p&gt;对于解释我们的问题，看到这里就足够了，因为[]和![]都不属于Reference，所以，GetValue([])和GetValue(![])都返回自身。&lt;br/&gt;
这里关于什么是Reference不想再赘述了，要详细了解的可以看宝典（8.7）。&lt;br/&gt;
那么，上述问题进一步转化成了如下问题：&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;GetValue([])为 []  &lt;/li&gt;
&lt;li&gt;GetValue(![])为 false， （这里！会使得[]强制转化为Boolean类型）  &lt;/li&gt;
&lt;li&gt;这里就成了求 &lt;strong&gt;[] == false&lt;/strong&gt;的问题  &lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;也就是说： &lt;strong&gt;[] ==&amp;#160;![]&lt;/strong&gt; 现在转化为了 &lt;strong&gt;[] == false&lt;/strong&gt;。&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;根据&amp;#8221;abstract equality comparison&amp;#8221;算法来求结果：  &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;宝典中第五步就提到了根据&amp;#8221;abstract equality comparison&amp;#8221;来求最后的结果。 
现在先来看看[]和false的类型，两者类型显而易见，前者是Object,后者Boolean。&lt;br/&gt;
然后，我们进一步来看看这个算法是如何的(11.9.3)，以下只列出了和我们这个问题相关的算法步骤,其中有这么一条：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The comparison x == y, where x and y are values, produces &lt;strong&gt;true&lt;/strong&gt; or &lt;strong&gt;false&lt;/strong&gt;. Such a comparison is performed as follows:&lt;br/&gt;
  7. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这句话很容易理解，就是要把y转化类型为数值，也就是说false变为0。&lt;br/&gt;
这样以来，问题有变成了求：  &lt;strong&gt;[] == 0&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;继续看宝典中这个算法(11.9.3)，其中有这么一条：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The comparison x == y, where x and y are values, produces &lt;strong&gt;true&lt;/strong&gt; or &lt;strong&gt;false&lt;/strong&gt;. Such a comparison is performed as follows:&lt;br/&gt;
  9. If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x) == y.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;于是，问题有变成了求： &lt;strong&gt;ToPrimitive([]) == 0&lt;/strong&gt;。&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;查看ToPrimitive的工作机制（9.1）  &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;其中对于Object有这种转换描述：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Return a default value for the Object. The default value of an object is retrieved by calling the [[DefaultValue]] internal method of the object,&lt;br/&gt;
  passing the optional hint PreferredType. The behaviour of the&lt;br/&gt;
  [[DefaultValue]] internal method is defined by this specification for all native
  ECMAScript objects in 8.12.8.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;继续顺藤摸瓜，看[[DefaultValue]](hint)，我们的例子中hint是Number，因为它是和0去做比较。&lt;br/&gt;
根据宝典（8.12.8）描述：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When the [[DefaultValue]] internal method of O is called with hint Number, the following steps are taken:&lt;br/&gt;
  1. Let valueOf be the result of calling the [[Get]] internal method of object O with argument &amp;#8220;valueOf&amp;#8221;.&lt;br/&gt;
  2. If IsCallable(valueOf) is true then&lt;br/&gt;
     a. Let val be the result of calling the [[Call]] internal method of valueOf, with O as the this value and an empty argument list.&lt;br/&gt;
     b. If val is a primitive value, return val.&lt;br/&gt;
  3. Let toString be the result of calling the [[Get]] internal method of object O with argument &amp;#8220;toString&amp;#8221;.&lt;br/&gt;
     a. Let str be the result of calling the [[Call]] internal method of toString, with O as the this value and an empty argument list.&lt;br/&gt;
     b. If str is a primitive value, return str.&lt;br/&gt;
  &amp;#8230;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这里不对valueOf再去做赘述了，MDN上面有很&lt;a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/ValueOf" target="_blank"&gt;简短的说明&lt;/a&gt;,大致意思如下：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;默认，每个对象都有从Object继承下来的valueOf方法。其中每个内置的核心对象都会重载该方法来返回正确的值，对于没有基础类型值的对象，则返回对象自身。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;那么，对于我们的情况来说，进入了算法中的2，但是，因为val是对象不是基础类型，所以继续进入第3步，这个时候关键来了：&lt;br/&gt;&lt;strong&gt;开始调用[]的toString方法，这个时候会返回&amp;#34;&amp;#34;，一个空的字符串，因此ToPrimitive([])为&amp;#34;&amp;#34;&lt;/strong&gt;&lt;br/&gt;
因此，问题又转化成了：&lt;br/&gt;&lt;strong&gt;&amp;#34;&amp;#34; == 0&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;现在答案就很明显了，根据宝典的==工作原理如下描述（11.9.3）：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:&lt;br/&gt;
  5. If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;好了，根据算法，会将&amp;#34;&amp;#34;转化为数值类型，那么自然就变成了0，于是 0 == 0 是很自然而然的。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;总结：&lt;/strong&gt;&lt;br/&gt;
最终问题就从： &lt;strong&gt;[] ==&amp;#160;![]&lt;/strong&gt; 变成了 &lt;strong&gt;0 == 0&lt;/strong&gt;。答案自然是&lt;strong&gt;true&lt;/strong&gt;了。&lt;br/&gt;
其实遇到这种语言层面的问题，直接看宝典即可。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;说明：&lt;/strong&gt;&lt;br/&gt;
以上诸如 11.9.3 这样的数字均表示葵花宝典中的章节。&lt;/p&gt;

&lt;h2&gt;参考资料&lt;/h2&gt;

&lt;hr&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.ecma-international.org/publications/standards/Ecma-262.htm" target="_blank"&gt;葵花宝典（ECMA-262-5th）&lt;/a&gt;  &lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/ValueOf" target="_blank"&gt;MDN&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><link>http://blog.goddyzhao.me/post/13962242607</link><guid>http://blog.goddyzhao.me/post/13962242607</guid><pubDate>Fri, 09 Dec 2011 03:54:00 -0500</pubDate><category>JavaScript</category><category>JavaScript Inernal</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>Rise of Node - Part I</title><description>&lt;a href="http://www.slideshare.net/goddy128/rise-of-node"&gt;Rise of Node - Part I&lt;/a&gt;: &lt;p&gt;Node入门介绍，内容涵盖Node的家族史，以及核心的单线程非阻塞概念，最后有大量的学习资源给初学Node的童鞋&lt;/p&gt;</description><link>http://blog.goddyzhao.me/post/12596308197</link><guid>http://blog.goddyzhao.me/post/12596308197</guid><pubDate>Thu, 10 Nov 2011 07:03:35 -0500</pubDate><category>nodejs</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>使用node-inspector来调试node</title><description>&lt;p&gt;大部分程序员（比如我），开发的过程中，其实只有20%的时间在写代码，另外80%的时间都在调试代码。完全符合著名的&lt;a href="http://en.wikipedia.org/wiki/80/20" target="_blank"&gt;80/20法则&lt;/a&gt;。&lt;br/&gt;
好吧，我承认我在滥用伟大的法则，废话很多。其实，我就想说明&lt;strong&gt;我们大部分时间都在调试（Debug）而不是在写代码&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;所以，要想尽一切办法提高调试的效率，这样有助于提高开发效率。
对于node的调试，官方wiki中有&lt;a href="https://github.com/joyent/node/wiki/Using-Eclipse-as-Node-Applications-Debugger" target="_blank"&gt;在eclipse中调试node&lt;/a&gt;的文章。&lt;/p&gt;

&lt;p&gt;但是，不知为何，我在eclipse中总是不成功（eclipse 3.7 + ubuntu 11.04）,况且，要是不用eclipse那咋办呢？&lt;br/&gt;
于是，我就另谋出路，在github上一番寻觅之后，发现了：&lt;a href="https://github.com/dannycoates/node-inspector" target="_blank"&gt;node-inspector&lt;/a&gt;。&lt;br/&gt;
用了一把之后，感觉神清气爽，遂推荐给大家。&lt;/p&gt;

&lt;p&gt;那么如何使用呢？其实官方有很详细的教程，不过这里我还是要赘述下，并且以joyent官方的例子来作为调试代码：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// dbgtest.js

var sys=require('sys');
var count = 0;

sys.debug("Starting ...");

function timer_tick() {
  count = count+1;
  sys.debug("Tick count: " + count);
  if (count === 10) {
    count += 1000;
    sys.debug("Set break here");
 }
 setTimeout(timer_tick, 1000);
}

timer_tick();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;具体调试步骤如下：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;以debug模式来运行上述代码&lt;/strong&gt;： node默认运行方式是直接 node filename。node还提供了两种debug模式运行：&lt;br/&gt;&lt;ol&gt;&lt;li&gt;node &amp;#8212;debug[=port] filename （这种方式，其实就是在指定的端口（默认为 5858）监听远程调试连接）  &lt;/li&gt;
&lt;li&gt;node &amp;#8212;debug-brk[=port] filename （这种方式在监听的同时，会在代码执行的时候，强制断点在第一行，这样有个好处就是：&lt;strong&gt;可以debug到node内部的是如何运行的&lt;/strong&gt;）&lt;br/&gt;
这里采用第二种方式运行： &lt;em&gt;node &amp;#8212;debug-brk dbgtest.js&lt;/em&gt;。&lt;br/&gt;
这个时候终端就会出现这样一个log：  &lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;&lt;code&gt;debugger listening on port 5858&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;说明已经开始监听了。&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;启动node-inspector&lt;/strong&gt;： 首 info  - socket.io started
visit &lt;a href="http://0.0.0.0:8080/debug?port=5858" target="_blank"&gt;http://0.0.0.0:8080/debug?port=5858&lt;/a&gt; to start debugging先，我们要&lt;strong&gt;全局&lt;/strong&gt;安装node-inspector模块，如果你真不知道如何安装模块，或者不知到为啥要全局安装，可以参看&lt;a href="http://goddyzhao.tumblr.com/post/9835631010/no-direct-command-for-local-installed-command-line-modul" target="_blank"&gt;这篇文章&lt;/a&gt;），
然后，直接输入命令： &lt;em&gt;node-inspector&lt;/em&gt; 就运行起来了。起来后，终端可以看到如下log：  &lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;&lt;code&gt; info  - socket.io started
visit &lt;a href="http://0.0.0.0:8080/debug?port=5858" target="_blank"&gt;http://0.0.0.0:8080/debug?port=5858&lt;/a&gt; to start debugging&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;通过看log也大致能够猜到了，node-inspector就是利用socket.io来监听5858端口实现的。&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;使用浏览器调试工具来调试node（如： chrome developer tool）&lt;/strong&gt;： 在chrome中输入： &lt;em&gt;http://localhost:8080/debug?port=5858&lt;/em&gt;，就会出现如下页面：&lt;br/&gt;&lt;img src="http://farm7.static.flickr.com/6235/6249843270_42121ed082_z.jpg" alt="chrome下调试界面"/&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;这里很明显看到，直接代码就停在第一行，那么，接下来如何断点来debug相信身为前端的童鞋就不用我再赘述了吧。&lt;/p&gt;</description><link>http://blog.goddyzhao.me/post/11522397416</link><guid>http://blog.goddyzhao.me/post/11522397416</guid><pubDate>Sun, 16 Oct 2011 09:33:00 -0400</pubDate><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>JavaScript内部原理实践——真的懂JavaScript吗？</title><description>&lt;p&gt;通过翻译了&lt;a href="http://dmitrysoshnikov.com/" target="_blank"&gt;Dmitry A.Soshnikov&lt;/a&gt;的关于ECMAScript-262-3&amp;#160;&lt;a href="http://goddyzhao.tumblr.com/JavaScript-Internal" target="_blank"&gt;JavaScript内部原理&lt;/a&gt;的文章，
从理论角度对JavaScript中部分特性的内部工作机制有了一定的了解。&lt;br/&gt;
但是，邓爷爷说过：&lt;strong&gt;“实践才是检验真理的唯一标准”&lt;/strong&gt;。&lt;br/&gt;
所以，我打算通过&lt;strong&gt;从内部原理来解释一些经常在笔试或者面试中遇到的关于JavaScript语言层面的题目&lt;/strong&gt;来进一步学习和掌握JavaScript内部工作原理。&lt;/p&gt;

&lt;p&gt;那么，首先就是要去找那些题目，google了一圈终于找到了来自&lt;strong&gt;Dmitry Baranovskiy&lt;/strong&gt;的非常著名的&lt;a href="http://dmitry.baranovskiy.com/post/91403200" target="_blank"&gt;5个问题&lt;/a&gt;，
这5个问题，NCZ给出了&lt;a href="http://www.nczonline.net/blog/2010/01/26/answering-baranovskiys-javascript-quiz/" target="_blank"&gt;非常清楚的解释&lt;/a&gt;。
不过，我还是想尝试下从low-level——JavaScript内部工作机制的角度去解释下这些问题。&lt;/p&gt;

&lt;p&gt;好吧，我承认我废话很多，那就开始吧。&lt;/p&gt;

&lt;h2&gt;问题 #1&lt;/h2&gt;

&lt;hr&gt;&lt;pre&gt;&lt;code&gt;if (!("a" in window)) {
    var a = 1;
}
alert(a);&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;正确答案：&lt;/strong&gt; undefined  &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;解释：&lt;/strong&gt;&lt;br/&gt;
这个问题，初一看感觉答案很自然是1。因为从上到下执行下去，&lt;em&gt;if&lt;/em&gt;语句中的条件应该为 &lt;em&gt;true&lt;/em&gt;，因为&amp;#8221;a&amp;#8221;的确是没有定义啊。
随后，顺理成章地进入 &lt;em&gt;var a = 1;&lt;/em&gt;，最后，alert出来就应该是1。&lt;/p&gt;

&lt;p&gt;而事实上，从JavaScript内部工作原理去看，在&lt;a href="http://goddyzhao.tumblr.com/post/11141710441/variable-object" target="_blank"&gt;变量对象&lt;/a&gt;中讲过，
JavaScript处理上下文分为两个阶段：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;进入执行上下文  &lt;/li&gt;
&lt;li&gt;执行代码  &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;可以理解为，第一个阶段是静态处理阶段，第二个阶段为动态处理阶段。&lt;/p&gt;

&lt;p&gt;而在静态处理阶段，就会创建 &lt;em&gt;变量对象（variable object）&lt;/em&gt;，并且将变量申明作为属性进行填充。&lt;br/&gt;
到了执行阶段，才会根据执行情况，来对变量对象中属性（就是申明的变量）的值进行更新。&lt;/p&gt;

&lt;p&gt;针对这个问题，其实际过程如下：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;进入执行上下文&lt;/strong&gt;：  创建VO，并填充变量申明 &lt;em&gt;a&lt;/em&gt;，VO如下所示：    &lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;&lt;code&gt;VO(global) = {
    a: undefined
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;所以，这个时候，a其实已经存在了。&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;执行代码&lt;/strong&gt;：  进入 &lt;em&gt;if&lt;/em&gt;语句，发现条件判断 &lt;em&gt;&amp;#8220;a&amp;#8221; in window&lt;/em&gt; 为&lt;strong&gt;true&lt;/strong&gt;。于是就不会进入if代码块，直接执行alert语句，因此，最终为&lt;strong&gt;undefined&lt;/strong&gt;。  &lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;问题 #2&lt;/h2&gt;

&lt;hr&gt;&lt;pre&gt;&lt;code&gt;var a = 1,
    b = function a(x) {
        x &amp;amp;&amp;amp; a(--x);
    };
alert(a);&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;正确答案：&lt;/strong&gt; 1    &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;解释：&lt;/strong&gt;&lt;br/&gt;
这个问题，第一反应可能会是将 function a打印出来。因为明明就看到了function a了。看似，也顺其自然。&lt;/p&gt;

&lt;p&gt;但是，事实并非如此。还是和此前一个问题一样。从两个阶段来分析：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;进入执行上下文&lt;/strong&gt;： 这个时候要注意了， &lt;em&gt;b = function a(){}&lt;/em&gt;，这里的 &lt;em&gt;function a&lt;/em&gt;并非函数申明，因为整个这个句话属于&lt;strong&gt;赋值语句（assignment statement）&lt;/strong&gt;，所以，这里的 &lt;em&gt;function a&lt;/em&gt;会被看作是函数表达式。
函数表达式是不会对VO造成影响的。所以，这个时候VO中其实只有 &lt;strong&gt;a和x（函数形参）&lt;/strong&gt;：  &lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;&lt;code&gt;VO(global) = {
    a: undefined,
    b: undefined
}&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;执行代码&lt;/strong&gt;： 这个时候a的值修改为1：  &lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;&lt;code&gt;VO(global) = {
    x: undefined,
    a: 1
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;所以，最后alert(a)的结果是1。&lt;/p&gt;

&lt;h2&gt;问题 #3&lt;/h2&gt;

&lt;hr&gt;&lt;pre&gt;&lt;code&gt;function a(x) {
    return x * 2;
}
var a;
alert(a);&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;正确答案：&lt;/strong&gt; 函数a  &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;解释：&lt;/strong&gt;&lt;br/&gt;
这个问题，很多人可能会以为是： undefined。理由可能是，明明看到了 &lt;em&gt;var a&lt;/em&gt;定义在了function a的后面，感觉应该会覆盖之前a的申明。&lt;/p&gt;

&lt;p&gt;事实又是怎样的呢？ 老套路，从两个阶段来分析：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;进入执行上下文&lt;/strong&gt;： 这里出现了名字一样的情况，一个是函数申明，一个是变量申明。那么，根据&lt;a href="http://goddyzhao.tumblr.com/post/11141710441/variable-object" target="_blank"&gt;变量对象&lt;/a&gt;
介绍的，填充VO的顺序是:  函数的形参 -&amp;gt; 函数申明 -&amp;gt; 变量申明。&lt;br/&gt;
上述例子中，变量a在函数a后面，那么，变量a遇到函数a怎么办呢？还是根据 &lt;em&gt;变量对象&lt;/em&gt;中介绍的，当变量申明遇到VO中已经有同名的时候，不会影响已经存在的属性。因此，VO如下所示：  &lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;&lt;code&gt;VO(global) = {
    a: 引用了函数申明“x”
}&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;执行代码&lt;/strong&gt;：啥也没变化  &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;所以，最终的结果是：函数a。&lt;/p&gt;

&lt;h2&gt;问题 #4&lt;/h2&gt;

&lt;hr&gt;&lt;pre&gt;&lt;code&gt;function b(x, y, a) {
    arguments[2] = 10;
    alert(a);
}
b(1, 2, 3);&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;正确答案：&lt;/strong&gt; 10  &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;解释：&lt;/strong&gt;&lt;br/&gt;
个人感觉这个问题其实不是很复杂。这里也不需要从两个阶段去分析了。根据 &lt;em&gt;变量对象&lt;/em&gt;中介绍的，&lt;strong&gt;arguments对象的properties-indexes和实际传递的参数是共享的&lt;/strong&gt;
也就是说，通过arguments[2]修改的参数，也会影响到a，所以，这里的值是10。但是，要注意的是和&lt;strong&gt;实际传递的值&lt;/strong&gt;，所以，如果把上述问题改成如下形式：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function b(x, y, a) {
    arguments[2] = 10;
    alert(a);
}
b(1, 2);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;结果就会是： &lt;strong&gt;undefined&lt;/strong&gt;。因为，并没有传递a的值。&lt;/p&gt;

&lt;h2&gt;问题 #5&lt;/h2&gt;

&lt;hr&gt;&lt;pre&gt;&lt;code&gt;function a() {
    alert(this);
}
a.call(null);&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;正确答案：&lt;/strong&gt; 全局对象（window）  &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;解释：&lt;/strong&gt;&lt;br/&gt;
这个问题，可能会比较困惑。因为懂call的童鞋都会觉得，call的时候把null传递为了当前的上下文了。里面的this应该是null才对啊。&lt;/p&gt;

&lt;p&gt;事实却是： 前面都没错，this会是null。但是，&lt;a href="http://goddyzhao.tumblr.com/post/11218727474/this" target="_blank"&gt;this&lt;/a&gt;中介绍过，null是没有任何意义的，因此，最终会变成全局对象。
所以，这里结果就变成了全局对象。  这里还有ECMAScript-262-3标准文档中的一句话作为证据：&lt;br/&gt;&lt;em&gt;“If thisArg is null or undefined, the called function is passed the global object as the this value. Otherwise, the called function is passed ToObject(thisArg) as the this value.”&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;总结&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;上面这5个问题其实也只是牵涉到了JavaScript内部原理中的部分知识点，要想了解更多，还是建议读完&lt;a href="http://goddyzhao.tumblr.com/JavaScript-Internal" target="_blank"&gt;JavaScript内部原理系列&lt;/a&gt;
以及去看&lt;a href="http://dmitrysoshnikov.com/" target="_blank"&gt;Dmitry A.Soshnikov&lt;/a&gt;的文章。&lt;/p&gt;</description><link>http://blog.goddyzhao.me/post/11478726832</link><guid>http://blog.goddyzhao.me/post/11478726832</guid><pubDate>Sat, 15 Oct 2011 11:05:00 -0400</pubDate><category>javascript</category><category>JavaScript Internal</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>闭包（Closures）</title><description>&lt;h2&gt;说明&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;此文译自&lt;a href="http://dmitrysoshnikov.com/" target="_blank"&gt;Dmitry A.Soshnikov&lt;/a&gt; 的文章&lt;a href="http://dmitrysoshnikov.com/ecmascript/chapter-6-closures/" target="_blank"&gt;closures&lt;/a&gt;&lt;br/&gt;
另，此文还有另外一位同事（彭森材）共同参译&lt;/p&gt;

&lt;h2&gt;概要&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;本文将介绍一个在JavaScript经常会拿来讨论的话题 —— &lt;em&gt;闭包（closure）&lt;/em&gt;。闭包其实已经是个老生常谈的话题了；
有大量文章都介绍过闭包的内容（其中不失一些很好的文章，比如，扩展阅读中Richard Cornford的文章就非常好），
尽管如此，这里还是要试着从理论角度来讨论下闭包，看看ECMAScript中的闭包内部究竟是如何工作的。&lt;/p&gt;

&lt;p&gt;正如在此前文章中提到的，这些文章都是系列文章，相互之间都是有关联的。因此，为了更好的理解本文要介绍的内容，
建议先去阅读下&lt;a href="http://goddyzhao.tumblr.com/post/11259644092/scope-chain" target="_blank"&gt;第四章 - 作用域链&lt;/a&gt;和
&lt;a href="http://goddyzhao.tumblr.com/post/11141710441/variable-object" target="_blank"&gt;第二章 - 变量对象&lt;/a&gt;。&lt;/p&gt;

&lt;h2&gt;概论&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;在讨论ECMAScript闭包之前，先来介绍下&lt;a href="http://en.wikipedia.org/wiki/Functional_programming" target="_blank"&gt;函数式编程&lt;/a&gt;（与ECMA-262-3 标准无关）中一些基本定义。
然而，为了更好的解释这些定义，这里还是拿ECMAScript来举例。&lt;/p&gt;

&lt;p&gt;众所周知，在函数式语言中（ECMAScript也支持这种风格），函数即是数据。就比方说，函数可以保存在变量中，可以当参数传递给其他函数，还可以当返回值返回等等。
这类函数有特殊的名字和结构。&lt;/p&gt;

&lt;h2&gt;定义&lt;/h2&gt;

&lt;hr&gt;&lt;blockquote&gt;
  &lt;p&gt;函数式参数（“&lt;em&gt;Funarg&lt;/em&gt;”） —— 是指值为函数的参数。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如下例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function exampleFunc(funArg) {
  funArg();
}
 
exampleFunc(function () {
  alert('funArg');
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子中funarg的实参是一个传递给&lt;em&gt;exampleFunc&lt;/em&gt;的匿名函数。&lt;/p&gt;

&lt;p&gt;反过来，接受函数式参数的函数称为 &lt;em&gt;高阶函数（high-order function 简称：HOF）&lt;/em&gt;。还可以称作：&lt;em&gt;函数式函数&lt;/em&gt; 或者 偏数理的叫法：&lt;em&gt;操作符函数&lt;/em&gt;。
上述例子中，&lt;em&gt;exampleFunc&lt;/em&gt; 就是这样的函数。&lt;/p&gt;

&lt;p&gt;此前提到的，函数不仅可以作为参数，还可以作为返回值。这类以函数为返回值的函数称为 _带函数值的函数（functions with functional value or function valued functions）。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function functionValued() {
  return function () {
    alert('returned function is called');
  };
})()();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以以正常数据形式存在的函数（比方说：当参数传递，接受函数式参数或者以函数值返回）都称作 &lt;em&gt;第一类函数（一般说第一类对象）&lt;/em&gt;。
在ECMAScript中，所有的函数都是第一类对象。&lt;/p&gt;

&lt;p&gt;接受自己作为参数的函数，称为 &lt;em&gt;自应用函数（auto-applicative function 或者 self-applicative function）&lt;/em&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function selfApplicative(funArg) {
 
  if (funArg &amp;amp;&amp;amp; funArg === selfApplicative) {
    alert('self-applicative');
    return;
  }
 
  selfApplicative(selfApplicative);
 
})();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;以自己为返回值的函数称为 &lt;em&gt;自复制函数（auto-replicative function 或者 self-replicative function）&lt;/em&gt;。
通常，“自复制”这个词用在文学作品中：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function selfReplicative() {
  return selfReplicative;
})();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在函数式参数中定义的变量，在“funarg”激活时就能够访问了（因为存储上下文数据的变量对象每次在进入上下文的时候就创建出来了）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function testFn(funArg) {
 
  // 激活funarg, 本地变量localVar可访问
  funArg(10); // 20
  funArg(20); // 30
 
}
 
testFn(function (arg) {
 
  var localVar = 10;
  alert(arg + localVar);
 
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然而，我们知道（特别在第四章中提到的），在ECMAScript中，函数是可以封装在父函数中的，并可以使用父函数上下文的变量。
这个特性会引发 &lt;em&gt;funarg问题&lt;/em&gt;。&lt;/p&gt;

&lt;h2&gt;Funarg问题&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;在&lt;a href="http://en.wikipedia.org/wiki/Stack-oriented_programming_language" target="_blank"&gt;面向堆栈的编程语言&lt;/a&gt;中，函数的本地变量都是保存在 &lt;em&gt;堆栈&lt;/em&gt;上的，
每当函数激活的时候，这些变量和函数参数都会压栈到该堆栈上。&lt;/p&gt;

&lt;p&gt;当函数返回的时候，这些参数又会从堆栈中移除。这种模型对将函数作为函数式值使用的时候有很大的限制（比方说，作为返回值从父函数中返回）。
绝大部分情况下，问题会出现在当函数有 &lt;em&gt;自由变量&lt;/em&gt;的时候。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;自由变量是指在函数中使用的，但既不是函数参数也不是函数的局部变量的变量&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function testFn() {
 
  var localVar = 10;
 
  function innerFn(innerParam) {
    alert(innerParam + localVar);
  }
 
  return innerFn;
}
 
var someFn = testFn();
someFn(20); // 30&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子中，对于&lt;strong&gt;innerFn&lt;/strong&gt;函数来说，&lt;strong&gt;localVar&lt;/strong&gt;就属于自由变量。&lt;/p&gt;

&lt;p&gt;对于采用 &lt;em&gt;面向堆栈&lt;/em&gt;模型来存储局部变量的系统而言，就意味着当&lt;strong&gt;testFn&lt;/strong&gt;函数调用结束后，其局部变量都会从堆栈中移除。
这样一来，当从外部对&lt;strong&gt;innerFn&lt;/strong&gt;进行函数调用的时候，就会发生错误（因为localVar变量已经不存在了）。&lt;/p&gt;

&lt;p&gt;而且，上述例子在 &lt;em&gt;面向堆栈&lt;/em&gt;实现模型中，要想将&lt;strong&gt;innerFn&lt;/strong&gt;以返回值返回根本是不可能的。
因为它也是&lt;strong&gt;testFn&lt;/strong&gt;函数的局部变量，也会随着&lt;strong&gt;testFn&lt;/strong&gt;的返回而移除。&lt;/p&gt;

&lt;p&gt;还有一个函数对象问题和当系统采用&lt;a href="http://en.wikipedia.org/wiki/Scope_(programming)#Dynamic_scoping" target="_blank"&gt;动态作用域&lt;/a&gt;，函数作为函数参数使用的时候有关。&lt;/p&gt;

&lt;p&gt;看如下例子（伪代码）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var z = 10;
 
function foo() {
  alert(z);
}
 
foo(); // 10 – 静态作用域和动态作用域情况下都是
 
(function () {
 
  var z = 20;
  foo(); // 10 – 静态作用域情况下, 20 – 动态作用域情况下
 
})();
 
// 将foo函数以参数传递情况也是一样的
 
(function (funArg) {
 
  var z = 30;
  funArg(); // 10 – 静态作用域情况下, 30 – 动态作用域情况下
 
})(foo);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们看到，采用动态作用域，变量（标识符）处理是通过动态堆栈来管理的。
因此，自由变量是在当前活跃的动态链中查询的，而不是在函数创建的时候保存起来的静态作用域链中查询的。&lt;/p&gt;

&lt;p&gt;这样就会产生冲突。比方说，即使Z仍然存在（与之前从堆栈中移除变量的例子相反），还是会有这样一个问题： 在不同的函数调用中，Z的值到底取哪个呢（从哪个上下文，哪个作用域中查询）？&lt;/p&gt;

&lt;p&gt;上述描述的就是两类 &lt;em&gt;funarg问题&lt;/em&gt; —— 取决于是否将函数以返回值返回（第一类问题）以及是否将函数当函数参数使用（第二类问题）。&lt;/p&gt;

&lt;p&gt;为了解决上述问题，就引入了 &lt;em&gt;闭包&lt;/em&gt;的概念。&lt;/p&gt;

&lt;h2&gt;闭包&lt;/h2&gt;

&lt;hr&gt;&lt;blockquote&gt;
  &lt;p&gt;闭包是代码块和创建该代码块的上下文中数据的结合。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;让我们来看下面这个例子（伪代码）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var x = 20;
 
function foo() {
  alert(x); // 自由变量 "x" == 20
}
 
// foo的闭包
fooClosure = {
  call: foo // 对函数的引用
  lexicalEnvironment: {x: 20} // 查询自由变量的上下文
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子中，“fooClosure”部分是伪代码。对应的，在ECMAScript中，“foo”函数已经有了一个内部属性——创建该函数上下文的作用域链。&lt;/p&gt;

&lt;p&gt;这里“lexical”是不言而喻的，通常是省略的。上述例子中是为了强调在闭包创建的同时，上下文的数据就会保存起来。
当下次调用该函数的时候，自由变量就可以在保存的（闭包）上下文中找到了，正如上述代码所示，变量“z”的值总是10。&lt;/p&gt;

&lt;p&gt;定义中我们使用的比较广义的词 —— “代码块”，然而，通常（在ECMAScript中）会使用我们经常用到的函数。
当然了，并不是所有对闭包的实现都会将闭包和函数绑在一起，比方说，在Ruby语言中，闭包就有可能是： 一个程序对象（procedure object）, 一个lambda表达式或者是代码块。&lt;/p&gt;

&lt;p&gt;对于要实现将局部变量在上下文销毁后仍然保存下来，基于堆栈的实现显然是不适用的（因为与基于堆栈的结构相矛盾）。
因此在这种情况下，上层作用域的闭包数据是通过 &lt;em&gt;动态分配内存&lt;/em&gt;的方式来实现的（基于“堆”的实现），配合使用垃圾回收器（&lt;em&gt;garbage collector&lt;/em&gt;简称GC）和 &lt;em&gt;引用计数（reference counting）&lt;/em&gt;。
这种实现方式比基于堆栈的实现性能要低，然而，任何一种实现总是可以优化的： 可以分析函数是否使用了自由变量，函数式参数或者函数式值，然后根据情况来决定 —— 是将数据存放在堆栈中还是堆中。&lt;/p&gt;

&lt;h2&gt;ECMAScript闭包的实现&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;讨论完理论部分，接下来让我们来介绍下ECMAScript中闭包究竟是如何实现的。
这里还是有必要再次强调下：ECMAScript只使用&lt;a href="http://en.wikipedia.org/wiki/Scope_(programming)#Lexical_scoping" target="_blank"&gt;静态（词法）作用域&lt;/a&gt;（而诸如Perl这样的语言，既可以使用静态作用域也可以使用动态作用域进行变量声明）。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var x = 10;
 
function foo() {
  alert(x);
}
 
(function (funArg) {
 
  var x = 20;
 
  // funArg的变量 "x" 是静态保存的，在该函数创建的时候就保存了
 
  funArg(); // 10, 而不是 20
 
})(foo);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;从技术角度来说，创建该函数的上层上下文的数据是保存在函数的内部属性 [[Scope]]中的。
如果你还不了解什么是[[Scope]]，建议你先阅读&lt;a href="http://goddyzhao.tumblr.com/post/11259644092/scope-chain" target="_blank"&gt;第四章&lt;/a&gt;,
该章节对[[Scope]]作了非常详细的介绍。如果你对[[Scope]]和作用域链的知识完全理解了的话，那对闭包也就完全理解了。&lt;/p&gt;

&lt;p&gt;根据函数创建的算法，我们看到 &lt;em&gt;在ECMAScript中，所有的函数都是闭包，因为它们都是在创建的时候就保存了上层上下文的作用域链（除开异常的情况）&lt;/em&gt;
（不管这个函数后续是否会激活 —— [[Scope]]在函数创建的时候就有了）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var x = 10;
 
function foo() {
  alert(x);
}
 
// foo is a closure
foo: &amp;lt;FunctionObject&amp;gt; = {
  [[Call]]: &amp;lt;code block of foo&amp;gt;,
  [[Scope]]: [
    global: {
      x: 10
    }
  ],
  ... // other properties
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;正如此前提到过的，出于优化的目的，当函数不使用自由变量的时候，实现层可能就不会保存上层作用域链。
然而，ECMAScript-262-3标准中并未对此作任何说明；因此，严格来说 —— 所有函数都会在创建的时候将上层作用域链保存在[[Scope]]中。&lt;/p&gt;

&lt;p&gt;有些实现中，允许对闭包作用域直接进行访问。比如Rhino，针对函数的[[Scope]]属性，对应有一个非标准的 __parent__属性，在第二章中作过介绍：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var global = this;
var x = 10;
 
var foo = (function () {
 
  var y = 20;
 
  return function () {
    alert(y);
  };
 
})();
 
foo(); // 20
alert(foo.__parent__.y); // 20
 
foo.__parent__.y = 30;
foo(); // 30
 
// 还可以操作作用域链
alert(foo.__parent__.__parent__ === global); // true
alert(foo.__parent__.__parent__.x); // 10&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;“万能”的[[Scope]]&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;这里还要注意的是：在ECMAScript中，同一个上下文中创建的闭包是共用一个[[Scope]]属性的。
也就是说，某个闭包对其中的变量做修改会影响到其他闭包对其变量的读取：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var firstClosure;
var secondClosure;
 
function foo() {
 
  var x = 1;
 
  firstClosure = function () { return ++x; };
  secondClosure = function () { return --x; };
 
  x = 2; // 对AO["x"]产生了影响, 其值在两个闭包的[[Scope]]中
 
  alert(firstClosure()); // 3, 通过 firstClosure.[[Scope]]
}
 
foo();
 
alert(firstClosure()); // 4
alert(secondClosure()); // 3&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;正因为这个特性，很多人都会犯一个非常常见的错误： 当在循环中创建了函数，然后将循环的索引值和每个函数绑定的时候，通常得到的结果不是预期的（预期是希望每个函数都能够获取各自对应的索引值）。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var data = [];
 
for (var k = 0; k &amp;lt; 3; k++) {
  data[k] = function () {
    alert(k);
  };
}
 
data[0](); // 3, 而不是 0
data[1](); // 3, 而不是 1
data[2](); // 3, 而不是 2&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子就证明了 —— 同一个上下文中创建的闭包是共用一个[[Scope]]属性的。因此上层上下文中的变量“k”是可以很容易就被改变的。&lt;/p&gt;

&lt;p&gt;如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;activeContext.Scope = [
  ... // higher variable objects
  {data: [...], k: 3} // activation object
];
 
data[0].[[Scope]] === Scope;
data[1].[[Scope]] === Scope;
data[2].[[Scope]] === Scope;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这样一来，在函数激活的时候，最终使用到的k就已经变成了3了。&lt;/p&gt;

&lt;p&gt;如下所示，创建一个额外的闭包就可以解决这个问题了：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var data = [];
 
for (var k = 0; k &amp;lt; 3; k++) {
  data[k] = (function _helper(x) {
    return function () {
      alert(x);
    };
  })(k); // 将 "k" 值传递进去
}
 
// 现在就对了
data[0](); // 0
data[1](); // 1
data[2](); // 2&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子中，函数“_helper”创建出来之后，通过参数“k”激活。其返回值也是个函数，该函数保存在对应的数组元素中。
这种技术产生了如下效果：  在函数激活时，每次“_helper”都会创建一个新的变量对象，其中含有参数“x”，“x”的值就是传递进来的“k”的值。
这样一来，返回的函数的[[Scope]]就成了如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;data[0].[[Scope]] === [
  ... // 更上层的变量对象
  上层上下文的AO: {data: [...], k: 3},
  _helper上下文的AO: {x: 0}
];
 
data[1].[[Scope]] === [
  ... // 更上层的变量对象
  上层上下文的AO: {data: [...], k: 3},
  _helper上下文的AO: {x: 1}
];
 
data[2].[[Scope]] === [
  ... // 更上层的变量对象
  上层上下文的AO: {data: [...], k: 3},
  _helper上下文的AO: {x: 2}
];&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们看到，这个时候函数的[[Scope]]属性就有了真正想要的值了，为了达到这样的目的，我们不得不在[[Scope]]中创建额外的变量对象。
要注意的是，在返回的函数中，如果要获取“k”的值，那么该值还是会是3。&lt;/p&gt;

&lt;p&gt;顺便提下，大量介绍JavaScript的文章都认为只有额外创建的函数才是闭包，这种说法是错误的。
实践得出，这种方式是最有效的，然而，从理论角度来说，在ECMAScript中所有的函数都是闭包。&lt;/p&gt;

&lt;p&gt;然而，上述提到的方法并不是唯一的方法。通过其他方式也可以获得正确的“k”的值，如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var data = [];
 
for (var k = 0; k &amp;lt; 3; k++) {
  (data[k] = function () {
    alert(arguments.callee.x);
  }).x = k; // 将“k”存储为函数的一个属性
}
 
// 同样也是可行的
data[0](); // 0
data[1](); // 1
data[2](); // 2&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Funarg和return&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;另外一个特性是从闭包中返回。在ECMAScript中，闭包中的返回语句会将控制流返回给调用上下文（调用者）。
而在其他语言中，比如，Ruby，有很多中形式的闭包，相应的处理闭包返回也都不同，下面几种方式都是可能的：可能直接返回给调用者，或者在某些情况下——直接从上下文退出。&lt;/p&gt;

&lt;p&gt;ECMAScript标准的退出行为如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function getElement() {
 
  [1, 2, 3].forEach(function (element) {
 
    if (element % 2 == 0) {
      // 返回给函数"forEach"，
      // 而不会从getElement函数返回
      alert('found: ' + element); // found: 2
      return element;
    }
 
  });
 
  return null;
}
 
alert(getElement()); // null, 而不是 2&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然而，在ECMAScript中通过try catch可以实现如下效果：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var $break = {};
 
function getElement() {
 
  try {
 
    [1, 2, 3].forEach(function (element) {
 
      if (element % 2 == 0) {
        // 直接从getElement"返回"
        alert('found: ' + element); // found: 2
        $break.data = element;
        throw $break;
      }
 
    });
 
  } catch (e) {
    if (e == $break) {
      return $break.data;
    }
  }
 
  return null;
}
 
alert(getElement()); // 2&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;理论版本&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;通常，程序员会错误的认为，只有匿名函数才是闭包。其实并非如此，正如我们所看到的 —— 正是因为作用域链，使得所有的函数都是闭包（与函数类型无关： 匿名函数，FE，NFE，FD都是闭包），
这里只有一类函数除外，那就是通过Function构造器创建的函数，因为其[[Scope]]只包含全局对象。
为了更好的澄清该问题，我们对ECMAScript中的闭包作两个定义（即两种闭包）：&lt;/p&gt;

&lt;p&gt;ECMAScript中，闭包指的是：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;从理论角度：所有的函数。因为它们都在创建的时候就将上层上下文的数据保存起来了。哪怕是简单的全局变量也是如此，因为函数中访问全局变量就相当于是在访问自由变量，这个时候使用最外层的作用域。    &lt;/li&gt;
&lt;li&gt;从实践角度：以下函数才算是闭包：&lt;br/&gt;&lt;ol&gt;&lt;li&gt;即使创建它的上下文已经销毁，它仍然存在（比如，内部函数从父函数中返回）  &lt;/li&gt;
&lt;li&gt;在代码中引用了自由变量  &lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;闭包实践&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;实际使用的时候，闭包可以创建出非常优雅的设计，允许对funarg上定义的多种计算方式进行定制。
如下就是数组排序的例子，它接受一个排序条件函数作为参数：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[1, 2, 3].sort(function (a, b) {
  ... // 排序条件
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;同样的例子还有，数组的map方法（并非所有的实现都支持数组map方法，SpiderMonkey从1.6版本开始有支持），该方法根据函数中定义的条件将原数组映射到一个新的数组中：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[1, 2, 3].map(function (element) {
  return element * 2;
}); // [2, 4, 6]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;使用函数式参数，可以很方便的实现一个搜索方法，并且可以支持无穷多的搜索条件：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;someCollection.find(function (element) {
  return element.someProperty == 'searchCondition';
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;还有应用函数，比如常见的forEach方法，将funarg应用到每个数组元素：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[1, 2, 3].forEach(function (element) {
  if (element % 2 != 0) {
    alert(element);
  }
}); // 1, 3&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;顺便提下，函数对象的 &lt;em&gt;apply&lt;/em&gt; 和 &lt;em&gt;call&lt;/em&gt;方法，在函数式编程中也可以用作应用函数。
apply和call已经在讨论“this”的时候介绍过了；这里，我们将它们看作是应用函数 —— 应用到参数中的函数（在apply中是参数列表，在call中是独立的参数）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function () {
  alert([].join.call(arguments, ';')); // 1;2;3
}).apply(this, [1, 2, 3]);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;闭包还有另外一个非常重要的应用 —— 延迟调用：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var a = 10;
setTimeout(function () {
  alert(a); // 10, 一秒钟后
}, 1000);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;也可以用于回调函数：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;...
var x = 10;
// only for example
xmlHttpRequestObject.onreadystatechange = function () {
  // 当数据就绪的时候，才会调用;
  // 这里，不论是在哪个上下文中创建，变量“x”的值已经存在了
  alert(x); // 10
};
..&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;还可以用于封装作用域来隐藏辅助对象：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = {};
 
// initialization
(function (object) {
 
  var x = 10;
 
  object.getX = function _getX() {
    return x;
  };
 
})(foo);
 
alert(foo.getX()); // get closured "x" – 10&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;总结&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;本文介绍了更多关于ECMAScript-262-3的理论知识，而我认为，这些基础的理论有助于理解ECMAScript中闭包的概念。&lt;/p&gt;

&lt;h2&gt;扩展阅读&lt;/h2&gt;

&lt;hr&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://jibbering.com/faq/notes/closures/" target="_blank"&gt;JavaScript闭包（by Richard Cornford）&lt;/a&gt;  &lt;/li&gt;
&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Funarg_problem" target="_blank"&gt;Funarg问题&lt;/a&gt;  &lt;/li&gt;
&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Closure_(computer_science)" target="_blank"&gt;闭包&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><link>http://blog.goddyzhao.me/post/11311499651</link><guid>http://blog.goddyzhao.me/post/11311499651</guid><pubDate>Tue, 11 Oct 2011 04:53:00 -0400</pubDate><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>函数（Functions）</title><description>&lt;h2&gt;说明&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;此文译自&lt;a href="http://dmitrysoshnikov.com/" target="_blank"&gt;Dmitry A.Soshnikov&lt;/a&gt; 的文章&lt;a href="http://dmitrysoshnikov.com/ecmascript/chapter-5-functions/" target="_blank"&gt;Functions&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;概要&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;本文将给大家介绍ECMAScript中的一般对象之一——函数。我们将着重介绍不同类型的函数以及不同类型的函数是如何影响上下文的变量对象以及函数的作用域链的。
我们还会解释经常会问到的问题，诸如：“不同方式创建出来的函数会不一样吗？（如果会，那么到底有什么不一样呢？）”：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = function () {
  ...
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述方式创建的函数和如下方式创建的有什么不同？&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo() {
  ...
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如下代码中，为啥一个函数要用括号包起来呢？&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function () {
  ...
})();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;由于本文和此前几篇文章都是有关联的，因此，要想完全搞懂这部分内容，建议先去阅读&lt;a href="http://goddyzhao.tumblr.com/post/11141710441/variable-object" target="_blank"&gt;第二章-变量对象&lt;/a&gt;
以及&lt;a href="http://goddyzhao.tumblr.com/post/11259644092/scope-chain" target="_blank"&gt;第四章-作用域链&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;下面，来我们先来介绍下函数类型。&lt;/p&gt;

&lt;h2&gt;函数类型&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;ECMAScript中包含三类函数，每一类都有各自的特性。&lt;/p&gt;

&lt;h2&gt;函数声明（Function Declaration）&lt;/h2&gt;

&lt;hr&gt;&lt;blockquote&gt;
  &lt;p&gt;函数声明（简称FD）是指这样的函数&lt;br/&gt;&lt;br/&gt;
   *  有函数名&lt;br/&gt;
   *  代码位置在：要么在程序级别或者直接在另外一个函数的函数体（FunctionBody）中&lt;br/&gt;
   *  在进入上下文时创建出来的&lt;br/&gt;
   *  会影响变量对象&lt;br/&gt;
   *  是以如下形式声明的&lt;/p&gt;
&lt;/blockquote&gt;

&lt;pre&gt;&lt;code&gt;function exampleFunc() {
  ...
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这类函数的主要特性是：只有它们可以影响变量对象（存储在上下文的VO中）。此特性同时也引出了非常重要的一点（变量对象的天生特性导致的） —— 它们在执行代码阶段就已经存在了（因为FD在进入上下文阶段就收集到了VO中）。&lt;/p&gt;

&lt;p&gt;下面是例子（从代码位置上来看，函数调用在声明之前）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;foo();
 
function foo() {
  alert('foo');
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;从定义中还提到了非常重要的一点 —— 函数声明在代码中的位置：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// 函数声明可以直接在程序级别的全局上下文中
function globalFD() {
  // 或者直接在另外一个函数的函数体中
  function innerFD() {}
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;除了上述提到了两个位置，其他位置均不能出现函数声明 —— 比方说，在表达式的位置或者是代码块中进行函数声明都是不可以的。&lt;/p&gt;

&lt;p&gt;介绍完了函数声明，接下来介绍&lt;em&gt;函数表达式&lt;/em&gt;（&lt;em&gt;function expression&lt;/em&gt;）。&lt;/p&gt;

&lt;h2&gt;函数表达式&lt;/h2&gt;

&lt;hr&gt;&lt;blockquote&gt;
  &lt;p&gt;函数表达式（简称：FE）是指这样的函数：&lt;br/&gt;
   
   *  代码位置必须要在表达式的位置&lt;br/&gt;
   *  名字可有可无&lt;br/&gt;
   *  不会影响变量对象&lt;br/&gt;
   *  在&lt;em&gt;执行代码&lt;/em&gt;阶段创建出来&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这类函数的主要特性是：它们的代码总是在表达式的位置。最简单的表达式的例子就是赋值表达式：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = function () {
  ...
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子中将一个匿名FE赋值给了变量“foo”，之后该函数就可以通过“foo”来访问了—— foo()。&lt;/p&gt;

&lt;p&gt;正如定义中提到的，FE也可以有名字：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = function _foo() {
  ...
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里要注意的是，在FE的外部可以通过变量“foo”——foo()来访问，而在函数内部（比如递归调用），还可以用“_foo”（译者注：但在外部是无法使用“_foo”的）。&lt;/p&gt;

&lt;p&gt;当FE有名字的时候，它很难和FD作区分。不过，如果仔细看这两者的定义的话，要区分它们还是很容易的： FE总是在表达式的位置。
如下例子展示的各类ECMAScript表达式都属于FE：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// 在括号中(grouping operator)只可能是表达式
(function foo() {});
 
// 在数组初始化中 —— 同样也只能是表达式
[function bar() {}];
 
// 逗号操作符也只能跟表达式
1, function baz() {};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;定义中还提到FE是在执行代码阶段创建的，并且不是存储在变量对象上的。如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// 不论是在定义前还是定义后，FE都是无法访问的
// (因为它是在代码执行阶段创建出来的),
 
alert(foo); // "foo" is not defined
 
(function foo() {});
 
// 后面也没用，因为它根本就不在VO中
 
alert(foo);  // "foo" is not defined&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;问题来了，FE要来干嘛？其实答案是很明显的 —— 在表达式中使用，从而避免对变量对象造成“污染”。最简单的例子就是将函数作为参数传递给另外一个函数：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo(callback) {
  callback();
}
 
foo(function bar() {
  alert('foo.bar');
});
 
foo(function baz() {
  alert('foo.baz');
});&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子中，部分变量存储了对FE的引用，这样函数就会保留在内存中并在之后，可以通过变量来访问（因为变量是可以影响VO的）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = function () {
  alert('foo');
};
 
foo();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如下例子是通过创建一个封装的作用域来对外部上下文隐藏辅助数据（例子中我们使用FE使得函数创建后就立马执行）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = {};
 
(function initialize() {
 
  var x = 10;
 
  foo.bar = function () {
    alert(x);
  };
 
})();
 
foo.bar(); // 10;
 
alert(x); // "x" is not defined&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们看到函数“foo.bar”（通过其[[Scope]]属性）获得了对函数“initialize”内部变量“x”的访问。
而同样的“x”在外部就无法访问到。很多库都使用这种策略来创建“私有”数据以及隐藏辅助数据。通常，这样的情况下FE的名字都会省略掉：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function () {
 
  // 初始化作用域
 
})();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;还有一个FE的例子是：在执行代码阶段在条件语句中创建FE,这种方式也不会影响VO：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = 10;
 
var bar = (foo % 2 == 0
  ? function () { alert(0); }
  : function () { alert(1); }
);
 
bar(); // 0&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;“有关括号”的问题&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;让我们回到本文之初，来回答下此前提到的问题 —— “为什么在函数创建之后立即进行函数调用时，需要用括号将其包起来？”。
要回答此问题，需要先介绍下关于表达式语句的限制。&lt;/p&gt;

&lt;p&gt;标准中提到，表达式语句（&lt;em&gt;ExpressionStatement&lt;/em&gt;）不能以左大括号&lt;strong&gt;{&lt;/strong&gt;开始 —— 因为这样一来就和代码块冲突了，
也不能以&lt;strong&gt;function&lt;/strong&gt;关键字开始，因为这样一来又和函数声明冲突了。比方说，以如下所示的方式来定义一个立马要执行的函数：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function () {
  ...
}();
 
// or with a name
 
function foo() {
  ...
}();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;对于这两种情况，解释器都会抛出错误，只是原因不同。&lt;/p&gt;

&lt;p&gt;如果我们是在全局代码（程序级别）中这样定义函数，解释器会以函数声明来处理，因为它看到了是以&lt;strong&gt;function&lt;/strong&gt;开始的。
在第一个例子中，会抛出&lt;strong&gt;语法错误&lt;/strong&gt;，原因是既然是个函数声明，则缺少函数名了（一个函数声明其名字是必须的）。&lt;/p&gt;

&lt;p&gt;而在第二个例子中，看上去已经有了名字了（foo），应该会正确执行。然而，这里还是会抛出&lt;strong&gt;语法错误&lt;/strong&gt; —— 组操作符内部缺少表达式。
这里要注意的是，这个例子中，函数声明后面的&lt;strong&gt;()&lt;/strong&gt;会被当组操作符来处理，而非函数调用的&lt;strong&gt;()&lt;/strong&gt;。因此，如果我们有如下代码：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// "foo" 是函数声明
// 并且是在进入上下文的时候创建的
 
alert(foo); // function
 
function foo(x) {
  alert(x);
}(1); // 这里只是组操作符，并非调用!
 
foo(10); // 这里就是调用了, 10&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码其实就是如下代码：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// function declaration
function foo(x) {
  alert(x);
}
 
// 含表达式的组操作符
(1);
 
// 另外一个组操作符
// 包含一个函数表达式
(function () {});
 
// 这里面也是表达式
("foo");
 
// etc&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当这样的定义出现在语句位置时，也会发生冲突并产生语法错误：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;if (true) function foo() {alert(1)}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述结构根据标准规定是不合法的。（表达式是不能以&lt;strong&gt;function&lt;/strong&gt;关键字开始的），然而，正如我们在后面要看到的，没有一种实现对其抛出错误，
它们各自按照自己的方式在处理。&lt;/p&gt;

&lt;p&gt;讲了这么多，那究竟要怎么写才能达到创建一个函数后立马就进行调用的目的呢？
答案是很明显的。它必须要是个函数表达式，而不能是函数声明。而创建表达式最简单的方式就是使用上述提到的组操作符。因为在组操作符中只可能是表达式。
这样一来解释器也不会纠结了，会果断将其以FE的方式来处理。这样的函数将在执行阶段创建出来，然后立马执行，随后被移除（如果有没有对其的引用的话）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function foo(x) {
  alert(x);
})(1); // 好了，这样就是函数调用了，而不再是组操作符了，1&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;要注意的是，在下面的例子中，函数调用，其括号就不再是必须的了，因为函数本来就在表达式的位置了，解释器自然会以FE来处理，并且会在执行代码阶段创建该函数：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = {
 
  bar: function (x) {
    return x % 2 != 0 ? 'yes' : 'no';
  }(1)
 
};
 
alert(foo.bar); // 'yes'&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;因此，对“括号有关”问题的完整的回答则如下所示：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;如果要在函数创建后立马进行函数调用，并且函数不在表达式的位置时，括号就是必须的 —— 这样情况下，其实是手动的将其转换成了FE。
   而当解释器直接将其以FE的方式处理的时候，说明FE本身就在函数表达式的位置 —— 这个时候括号就不是必须的了。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;另外，除了使用括号的方式将函数转换成为FE之外，还有其他的方式，如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1, function () {
  alert('anonymous function is called');
}();
 
// 或者这样
!function () {
  alert('ECMAScript');
}();
 
// 当然，还有其他很多方式
 
...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;不过，括号是最通用也是最优雅的方式。&lt;/p&gt;

&lt;p&gt;顺便提下，组操作符既可以包含没有调用括号的函数，又可以包含有调用括号的函数，这两者都是合法的FE：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function () {})();
(function () {}());&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;实现扩展： 函数语句&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;看如下代码，符合标准的解释器都无法解释这样的代码：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;if (true) {
 
  function foo() {
    alert(0);
  }
 
} else {
 
  function foo() {
    alert(1);
  }
 
}
 
foo(); // 1 还是 0 ? 在不同引擎中测试&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里有必要提下：根据标准，上述代码结构是不合法的，因为，此前我们就介绍过，函数声明是不能出现在代码块中的（这里if和else就包含代码块）。
此前提到的，函数声明只能出现在两个位置： 程序级别或者另外一个函数的函数体中。&lt;/p&gt;

&lt;p&gt;为什么这种结构是错误的呢？因为在代码块中只允许&lt;em&gt;语句&lt;/em&gt;。函数要想在这个位置出现的唯一可能就是要成为&lt;em&gt;表达式语句&lt;/em&gt;。
但是，根据定义表达式语句又不能以左大括号开始（这样会与代码块冲突）也不能以&lt;strong&gt;function&lt;/strong&gt;关键字开始（这样又会和FD冲突）。&lt;/p&gt;

&lt;p&gt;然而，在错误处理部分，标准允许实现对程序语法进行扩展。而上述例子就是其中一种扩展。目前，所有的实现中都不会对上述情况抛出错误，都会以各自的方式进行处理。&lt;/p&gt;

&lt;p&gt;因此根据标准，上述if-else中应当需要FE。然而，绝大多数实现中都在进入上下文的时候在这里简单地创建了FD，并且使用了最后一次的声明。
最后“foo”函数显示了1，尽管理论上else中的代码根本不会被执行到。&lt;/p&gt;

&lt;p&gt;而SpiderMonkey（TraceMonkey也是）实现中，会将上述情况以两种方式来处理： 一方面它不会将这样的函数以函数声明来处理（也就意味着函数会在执行代码阶段才会创建出来），
然而，另外一方面，它们又不属于真正的函数表达式，因为在没有括号的情况是不能作函数调用的（同样会有解析错误——和FD冲突），它们还是存储在变量对象中。&lt;/p&gt;

&lt;p&gt;我认为SpiderMonkey单独引入了自己的中间函数类型——（FE+FD），这样的做法是正确的。这样的函数会根据时间和对应的条件正确创建出来，不像FE。
和FD有点类似，可以在外部对其进行访问。SpiderMonkey将这种语法扩展命名为函数语句（&lt;em&gt;Function Statement&lt;/em&gt;）（简称FS）；这部分理论在MDC中有&lt;a href="https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference:Functions#Conditionally_defining_a_function" target="_blank"&gt;具体的介绍&lt;/a&gt;。
JavaScript的发明者 Brendan Eich也&lt;a href="https://mail.mozilla.org/pipermail/es-discuss/2008-February/005314.html" target="_blank"&gt;提到过&lt;/a&gt;这类函数类型。&lt;/p&gt;

&lt;h2&gt;有名字的函数表达式的特性（NFE）&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;当FE有名字之后（named function expression，简称：NFE），就产生了一个重要的特性。
正如在定义中提到的，函数表达式是不会影响上下文的变量对象的（这就意味着不论是在定义前还是在定义后，都是不可能通过名字来进行调用的）。
然而，FE可以通过自己的名字进行递归调用：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function foo(bar) {
 
  if (bar) {
    return;
  }
 
  foo(true); // "foo" name is available
 
})();
 
// but from the outside, correctly, is not
 
foo(); // "foo" is not defined&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里“foo”这个名字究竟保存在哪里呢？在foo的活跃对象中吗？非也，因为在foo函数中根本就没有定义任何“foo”。
那么是在上层上下文的变量对象中吗？也不是，因为根据定义——FE是不会影响VO的——正如我们在外层对其调用的结果所看到的那样。
那么，它究竟保存在哪里了呢？&lt;/p&gt;

&lt;p&gt;不卖关子了，马上来揭晓。当解释器在执行代码阶段看到了有名字的FE之后，它会在创建FE之前，创建一个辅助型的特殊对象，并把它添加到当前的作用域链中。
然后，再创建FE，在这个时候（根据第四章-作用域链描述的），函数拥有了[[Scope]]属性 —— 创建函数所在上下文的作用域链（这个时候，在[[Scope]]就有了那个特殊对象）。
之后，特殊对象中唯一的属性 —— FE的名字添加到了该对象中；其值就是对FE的引用。在最后，当前上下文退出的时候，就会把该特殊对象移除。
用伪代码来描述此算法就如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;specialObject = {};
 
Scope = specialObject + Scope;
 
foo = FunctionExpression;
foo.[[Scope]] = Scope;
specialObject.foo = foo; // {DontDelete}, {ReadOnly}
 
delete Scope[0]; // 从作用域链的最前面移除specialObject&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这就是为什么在函数外是无法通过名字访问到该函数的（因为它并不在上层作用域中存在），而在函数内部却可以访问到。&lt;/p&gt;

&lt;p&gt;而这里要注意的一点是： 在某些实现中，比如Rhino，FE的名字并不是保存在特殊对象中的，而是保存在FE的活跃对象中。
再比如微软的实现 —— JScript，则完全破坏了FE的规则，直接将该名字保存在上层作用域的变量对象中了，这样在外部也可以访问到。&lt;/p&gt;

&lt;h2&gt;NFE和SpiderMonkey&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;说到实现，部分版本的SpiderMonkey有一个与上述提到的特殊对象相关的特性，这个特性也可以看作是个bug（既然所有的实现都是严格遵循标准的，那么这个就是标准的问题了）。
此特性和标识符处理相关： 作用域链的分析是二维的，在标识符查询的时候，还要考虑作用域链中每个对象的原型链。&lt;/p&gt;

&lt;p&gt;当在&lt;em&gt;Object.prototype&lt;/em&gt;对象上定义一个属性，并将该属性值指向一个“根本不存在”的变量时，就能够体现该特性。
比如，如下例子中的变量“x”，在查询过程中，通过作用域链，一直到全局对象也是找不到“x”的。
然而，在SpiderMonkey中，全局对象继承自&lt;em&gt;Object.prototype&lt;/em&gt;，于是，对应的值就在该对象中找到了：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Object.prototype.x = 10;
 
(function () {
  alert(x); // 10
})();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;活跃对象是没有原型一说的。可以通过内部函数还证明。
如果在定义一个局部变量“x”并声明一个内部函数（FD或者匿名的FE），然后，在内部函数中引用变量“x”，这个时候该变量会在上层函数上下文中查询到（理应如此），而不是在&lt;em&gt;Object.prototype&lt;/em&gt;中：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Object.prototype.x = 10;
 
function foo() {
 
  var x = 20;
 
  // function declaration 
 
  function bar() {
    alert(x);
  }
 
  bar(); // 20, from AO(foo)
 
  // the same with anonymous FE
 
  (function () {
    alert(x); // 20, also from AO(foo)
  })();
 
}
 
foo();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在有些实现中，存在这样的异常：它们会在活跃对象设置原型。比方说，在&lt;em&gt;Blackberry&lt;/em&gt;的实现中，上述例子中变量“x”值就会变成10。
因为，“x”从&lt;em&gt;Object.prototype&lt;/em&gt;中就找到了：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;AO(bar FD or anonymous FE) -&amp;gt; no -&amp;gt;
AO(bar FD or anonymous FE).[[Prototype]] -&amp;gt; yes - 10&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当出现有名字的FE的特殊对象的时候，在SpiderMonkey中也是有同样的异常。该特殊对象是常见对象 —— “和通过new Object()表达式产生的一样”。
相应地，它也应当继承自&lt;em&gt;Object.prototype&lt;/em&gt;，上述描述只针对SpiderMonkey（1.7版本）。其他的实现（包括新的TraceMonkey）是不会给这个特殊对象设置原型的：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo() {
 
  var x = 10;
 
  (function bar() {
 
    alert(x); // 20, but not 10, as don't reach AO(foo)
 
    // "x" is resolved by the chain:
    // AO(bar) - no -&amp;gt; __specialObject(bar) -&amp;gt; no
    // __specialObject(bar).[[Prototype]] - yes: 20
 
  })();
}
 
Object.prototype.x = 20;
 
foo();&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;NFE和JScript&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;微软的实现——JScript，是IE的JS引擎（截至本文撰写时最新是JScript5.8——IE8），该引擎与NFE相关的bug有很多。每个bug基本上都和ECMA-262-3rd标准是完全违背的。
有些甚至会引发严重的错误。&lt;/p&gt;

&lt;p&gt;第一，针对上述这样的情况，JScript完全破坏了FE的规则：不应当将函数名字保存在变量对象中的。
另外，FE的名字应当保存在特殊对象中，并且只有在函数自身内部才可以访问（其他地方均不可以）。而JScript却将其直接保存在上层上下文的变量对象中。
并且，JScript居然还将FE以FD的方式处理，在进入上下文的时候就将其创建出来，并在定义之前就可以访问到：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// FE 保存在变量对象中
// 和FD一样，在定义前就可以通过名字访问到
testNFE();
 
(function testNFE() {
  alert('testNFE');
});
 
// 同样的，在定义之后也可以通过名字访问到
testNFE();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;正如大家所见，完全破坏了FE的规则。&lt;/p&gt;

&lt;p&gt;第二，在声明同时，将NFE赋值给一个变量的时候，JScript会创建两个不同的函数对象。
这种行为感觉完全不符合逻辑（特别是考虑到在NFE外层，其名字根本是无法访问到的）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = function bar() {
  alert('foo');
};
 
alert(typeof bar); // "function", NFE 有在VO中了 – 这里就错了
 
// 然后，还有更有趣的
alert(foo === bar); // false!
 
foo.x = 10;
alert(bar.x); // undefined
 
// 然而，两个函数完全做的是同样的事情
 
foo(); // "foo"
bar(); // "foo"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然而，要注意的是： 当将NFE和赋值给变量这两件事情分开的话（比如，通过组操作符），在定义好后，再进行变量赋值，这样，两个对象就相同了，返回true：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function bar() {});
 
var foo = bar;
 
alert(foo === bar); // true
 
foo.x = 10;
alert(bar.x); // 10&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个时候就好解释了。实施上，一开始的确创建了两个对象，不过之后就只剩下一个了。这里将NFE以FD的方式来处理，然后，当进入上下文的时候，FD bar就创建出来了。
在这之后，到了执行代码阶段，又创建出了第二个对象 —— FE bar，该对象不会进行保存。相应的，由于没有变量对其进行引用，随后FE bar对象就被移除了。
因此，这里就只剩下一个对象——FD bar对象，对该对象的引用就赋值给了foo变量。&lt;/p&gt;

&lt;p&gt;第三，通过&lt;em&gt;arguments.callee&lt;/em&gt;对一个函数进行间接引用，它引用的是和激活函数名一致的对象（事实上是——函数，因为有两个对象）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = function bar() {
 
  alert([
    arguments.callee === foo,
    arguments.callee === bar
  ]);
 
};
 
foo(); // [true, false]
bar(); // [false, true]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;第四，JScript会将NFE以FD来处理，但当遇到条件语句又不遵循此规则了。比如说，和FD那样，NFE会在进入上下文的时候就创建出来，这样最后一次定义的就会被使用：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = function bar() {
  alert(1);
};
 
if (false) {
 
  foo = function bar() {
    alert(2);
  };
 
}
bar(); // 2
foo(); // 1&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述行为从逻辑上也是可以解释通的： 当进入上下文的时候，最后一次定义的FD bar被创建出来（有alert(2)的函数），
之后到了执行代码阶段又一个新的函数 —— FE bar被创建出来，对其引用赋值给了变量foo。因此（if代码块中由于判断条件是false，因此其代码块中的代码永远不会被执行到）foo函数的调用会打印出1。
尽管“逻辑上”是对的，但是这个仍然算是IE的bug。因为它明显就破坏了实现的规则，所以我这里用了引号“逻辑上”。&lt;/p&gt;

&lt;p&gt;第五个JScript中NFE的bug和通过给一个未受限的标识符赋值（也就是说，没有var关键字）来创建全局对象的属性相关。
由于这里NFE会以FD的方式来处理，并相应地会保存在变量对象上，赋值给未受限的标识符（不是给变量而是给全局对象的一般属性），
当函数名和标识符名字相同的时候，该属性就不会是全局的了。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function () {
 
  // 没有var，就不是局部变量，而是全局对象的属性
 
  foo = function foo() {};
 
})();
 
// 然而，在匿名函数的外层，foo又是不可访问的
 
alert(typeof foo); // undefined&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里从“逻辑上”又是可以解释通的： 进入上下文时，函数声明在匿名函数本地上下文的活跃对象中。
当进入执行代码阶段的时候，因为foo这个名字已经在AO中存在了（本地），相应地，赋值操作也只是简单的对AO中的foo进行更新而已。
并没有在全局对象上创建新的属性。&lt;/p&gt;

&lt;h2&gt;通过Function构造器创建的函数&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;这类函数有别于FD和FE，有自己的专属特性： 它们的[[Scope]]属性中只包含全局对象：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var x = 10;
 
function foo() {
 
  var x = 20;
  var y = 30;
 
  var bar = new Function('alert(x); alert(y);');
 
  bar(); // 10, "y" is not defined
 
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们看到&lt;em&gt;bar&lt;/em&gt;函数的[[Scope]]属性并未包含foo上下文的AO —— 变量“y”是无法访问的，并且变量“x”是来自全局上下文。
顺便提下，这里要注意的是，Function构造器可以通过&lt;em&gt;new&lt;/em&gt;关键字和省略&lt;em&gt;new&lt;/em&gt;关键字两种用法。上述例子中，这两种用法都是一样的。&lt;/p&gt;

&lt;p&gt;此类函数其他特性则和&lt;a href="http://bclary.com/2004/11/07/#a-13.1.1" target="_blank"&gt;同类语法产生式&lt;/a&gt;以及&lt;a href="http://bclary.com/2004/11/07/#a-13.1.2" target="_blank"&gt;联合对象&lt;/a&gt;有关。
该机制在标准中建议在作优化的时候采用（当然，具体的实现者也完全有权利不使用这类优化）。比方说，有100元素的数组，在循环数组过程中会给数组每个元素赋值（函数），
这个时候，实现的时候就可以采用联合对象的机制了。这样，最终所有的数组元素都会引用同一个函数（只有一个函数）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var a = [];
 
for (var k = 0; k &amp;lt; 100; k++) {
  a[k] = function () {}; // 这里就可以使用联合对象
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;但是，通过Function构造器创建的函数就无法使用联合对象了：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var a = [];
 
for (var k = 0; k $lt; 100; k++) {
  a[k] = Function(''); // 只能是100个不同的函数
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;下面是另外一个和联合对象相关的例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo() {
 
  function bar(z) {
    return z * z;
  }
 
  return bar;
}
 
var x = foo();
var y = foo();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子，在实现过程中同样可以使用联合对象。来使得x和y引用同一个对象，因为函数（包括它们内部的[[Scope]]属性）物理上是不可分辨的。
因此，通过Function构造器创建的函数总是会占用更多内存资源。&lt;/p&gt;

&lt;h2&gt;函数创建的算法&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;如下所示使用伪代码表示的函数创建的算法（不包含联合对象的步骤）。有助于理解ECMAScript中的函数对象。此算法对所有函数类型都是一样的。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;F = new NativeObject();
 
// 属性 [[Class]] is "Function"
F.[[Class]] = "Function"
 
// 函数对象的原型
F.[[Prototype]] = Function.prototype
 
// 对函数自身引用
// [[Call]] 在函数调用时F()激活
// 同时创建一个新的执行上下文
F.[[Call]] = &amp;lt;reference to function&amp;gt;
 
// 内置的构造器
// [[Construct]] 会在使用“new”关键字的时候激活
// 事实上，它会为新对象申请内存
// 然后调用 F.[[Call]]来初始化创建的对象，将this值设置为新创建的对象
F.[[Construct]] = internalConstructor
 
// 当前上下文（创建函数F的上下文）的作用域名链
F.[[Scope]] = activeContext.Scope
// 如果是通过new Function(...)来创建的，则
F.[[Scope]] = globalContext.Scope
 
// 形参的个数
F.length = countParameters
 
// 通过F创建出来的对象的原型
__objectPrototype = new Object();
__objectPrototype.constructor = F // {DontEnum}, 在遍历中不能枚举
F.prototype = __objectPrototype
 
return F&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;要注意的是，F.[[Prototype]]是函数（构造器）的原型，而F.prototype是通过该函数创建出来的对象的原型（因为通常对这两个概念都会混淆，在有些文章中会将&lt;em&gt;F.prototype&lt;/em&gt;叫做“构造器的原型”，这是错误的）。&lt;/p&gt;

&lt;h2&gt;总结&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;本文介绍了很多关于函数的内容；不过在后面的关于对象和原型的文章中，还会提到函数作为构造器是如何工作的。&lt;/p&gt;

&lt;h2&gt;扩展阅读&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;ECMAScript标准：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;13 —— &lt;a href="http://bclary.com/2004/11/07/#a-13" target="_blank"&gt;函数定义&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;15.3 —— &lt;a href="http://bclary.com/2004/11/07/#a-15.3" target="_blank"&gt;函数对象&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;另外一篇文章：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;a href="http://yura.thinkweb2.com/named-function-expressions/" target="_blank"&gt;揭秘有名字的函数表达式（来自Juriy “kangax” Zaytsev）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><link>http://blog.goddyzhao.me/post/11273713920</link><guid>http://blog.goddyzhao.me/post/11273713920</guid><pubDate>Mon, 10 Oct 2011 10:57:00 -0400</pubDate><category>javascript</category><category>JavaScript Internal</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>作用域链（Scope Chain）</title><description>&lt;h2&gt;说明&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;此文译自&lt;a href="http://dmitrysoshnikov.com/" target="_blank"&gt;Dmitry A.Soshnikov&lt;/a&gt; 的文章&lt;a href="http://dmitrysoshnikov.com/ecmascript/chapter-4-scope-chain/" target="_blank"&gt;Scope Chain&lt;/a&gt;&lt;br/&gt;
另，此文还有另外一位同事（邵信衡）共同参译&lt;/p&gt;

&lt;h2&gt;概要&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;在&lt;a href="http://goddyzhao.tumblr.com/post/11141710441/variable-object" target="_blank"&gt;第二章&lt;/a&gt;变量对象的时候，
已经介绍过&lt;a href="http://goddyzhao.tumblr.com/post/10020230352/execution-context" target="_blank"&gt;执行上下文&lt;/a&gt;的数据是以变量对象的属性的形式进行存储的。&lt;/p&gt;

&lt;p&gt;还介绍了，每次进入执行上下文的时候，就会创建变量对象，并且赋予其属性初始值，随后在执行代码阶段会对属性值进行更新。&lt;/p&gt;

&lt;p&gt;本文要与执行上下文密切相关的另外一个重要的概念——&lt;em&gt;作用域链（Scope Chain）&lt;/em&gt;。&lt;/p&gt;

&lt;h2&gt;定义&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;若要简单扼要对作用域脸做个解释，那就是：作用域链和内部函数息息相关。&lt;/p&gt;

&lt;p&gt;众所周知，ECMAScript允许创建内部函数，甚至可以将这些内部函数作为父函数的返回值。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var x = 10;
 
function foo() {
 
  var y = 20;
 
  function bar() {
    alert(x + y);
  }
 
  return bar;
 
}
 
foo()(); // 30&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;每个上下文都有自己的变量对象：对于全局上下文而言，其变量对象就是&lt;em&gt;全局对象&lt;/em&gt;本身，对于函数而言，其变量对象就是&lt;em&gt;活跃对象&lt;/em&gt;。&lt;/p&gt;

&lt;p&gt;作用域链其实就是所有内部上下文的变量对象的列表。用于变量查询。比如，在上述例子中，“bar”上下文的作用域链包含了AO(bar),AO(foo)和VO(global)。&lt;/p&gt;

&lt;p&gt;下面就来详细介绍下作用域链。&lt;/p&gt;

&lt;p&gt;先从定义开始，随后再结合例子详细介绍：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;作用域链是一条变量对象的链，它和执行上下文有关，用于在处理标识符时候进行变量查询。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;函数上下文的作用域链在函数调用的时候创建出来，它包含了活跃对象和该函数的内部[[Scope]]属性。关于[[Scope]]会在后面作详细介绍。&lt;/p&gt;

&lt;p&gt;大致表示如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;activeExecutionContext = {
    VO: {...}, // 或者 AO
    this: thisValue,
    Scope: [ // 所用域链
      // 所有变量对象的列表
      // 用于标识符查询
    ]
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码中的&lt;em&gt;Scope&lt;/em&gt;定义为如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Scope = AO + [[Scope]]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;针对我们的例子来说，可以将Scope和[[Scope]]用普通的ECMAScript数组来表示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var Scope = [VO1, VO2, ..., VOn]; // 作用域链&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;除此之外，还可以用分层对象链的数据结构来表示，链中每一个链接都有对父作用域（上层变量对象）的引用。这种表示方式和第二章中讨论的某些实现中__parent__的概念相对应：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var VO1 = {__parent__: null, ... other data}; --&amp;gt;
var VO2 = {__parent__: VO1, ... other data}; --&amp;gt;
// etc.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然而，使用数组来表示作用域链会更方便，因此，我们这里就采用数组的表示方式。
除此之外，不论在实现层是否采用包含__parent__特性的分层对象链的数据结构，标准自身对其做了抽象的定义“作用域链是一个对象&lt;em&gt;列表&lt;/em&gt;”。
数组就是实现列表这一概念最好的选择。&lt;/p&gt;

&lt;p&gt;下面将要介绍的 AO+[[Scope]]以及标识符的处理方式，都和函数的生命周期有关。&lt;/p&gt;

&lt;h2&gt;函数的生命周期&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;函数的生命周期分为&lt;em&gt;创建&lt;/em&gt;阶段和&lt;em&gt;激活（调用）&lt;/em&gt;阶段。下面就来详细对其作介绍。&lt;/p&gt;

&lt;h2&gt;函数的创建&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;众所周知，在进入上下文阶段，函数声明会存储在变量/活跃对象中（VO/AO）。让我们来看一个全局上下文中变量声明和函数声明的例子（这种情况下，变量对象就是全局对象本身，应该还没忘记吧？）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var x = 10;
 
function foo() {
  var y = 20;
  alert(x + y);
}
 
foo(); // 30&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在函数激活后，我们看到了正确（预期）的结果——30。不过，这里有一个非常重要的特性。&lt;/p&gt;

&lt;p&gt;在说当前上下文的变量对象前。上述代码中我们看到变量“y”是在“foo”函数中定义的（意味着它存储在“foo”上下文的AO对象中），
然而变量“x”则并没有在“foo”上下文中定义，自然也不会添加到“foo”的AO中。乍一眼看过去，变量“x”压根就不在“foo”中存在；
然而，正如我们下面要看到的——仅仅只是“乍一眼看过去“而已。我们看到“foo”上下文的活跃对象中只包含一个属性——“y”：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;fooContext.AO = {
  y: undefined // undefined – 在进入上下文时, 20 – 在激活阶段
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;那么，“foo”函数到底是如何访问到变量“x”的呢？一个顺其自然的想法是：函数应当有访问更高层上下文变量对象的权限。
而事实也恰是如此，就是通过函数的内部属性[[Scope]]来实现这一机制的。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[[Scope]]是一个包含了所有上层变量对象的分层链，它属于当前函数上下文，并在函数创建的时候，保存在函数中。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这里要注意的很重要的一点是：[[Scope]]是在函数创建的时候保存起来的——静态的（不变的），只有一次并且一直都存在——直到函数销毁。
比方说，哪怕函数永远都不能被调用到，[[Scope]]属性也已经保存在函数对象上了。&lt;/p&gt;

&lt;p&gt;另外要注意的一点是：[[Scope]]与Scope(作用域链)是不同的，前者是函数的属性，后者是上下文的属性。
以上述例子来说，“foo”函数的[[Scope]]如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;foo.[[Scope]] = [
  globalContext.VO // === Global
];&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;之后，有了函数调用，就会进入函数上下文，这个时候会创建活跃对象并且this的值和Scope（作用域链）都会确定。下面来详细介绍下。&lt;/p&gt;

&lt;h2&gt;函数的激活&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;正如在“定义”这节提到的，在进入上下文，AO/VO创建之后，上下文的&lt;em&gt;Scope&lt;/em&gt;属性（作用域链，用于变量查询）会定义为如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Scope = AO|VO + [[Scope]]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里要注意的是活跃对象是&lt;em&gt;Scope&lt;/em&gt;数组的第一个元素。添加在作用域链的最前面：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Scope = [AO].concat([[Scope]]);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;此特性对处理标识符非常重要。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;处理标识符其实就是一个确定变量（或者函数声明）属于作用域链中哪个变量对象的过程。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;此算法返回的总是一个引用类型的值，其base属性就是对应的变量对象（或者如果变量不存在的时候则返回null），其property name属性的名字就是要查询的标识符。
要详细了解引用类型可以参看&lt;a href="http://goddyzhao.tumblr.com/post/11218727474/this" target="_blank"&gt;第三章-this&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;标识符处理过程包括了对应的变量名的属性查询，比如：在作用域链中会进行一系列的变量对象的检测，从作用域链的最底层上下文一直到最上层上下文。&lt;/p&gt;

&lt;p&gt;因此，在查询过程中上下文中的局部变量相比较上层上下文的变量会优先被查询到，换句话说，如果两个相同名字的变量存在于不同的上下文中时，处于底层上下文的变量会优先被找到。&lt;/p&gt;

&lt;p&gt;下面是一个相对比较复杂的例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var x = 10;
 
function foo() {
 
  var y = 20;
 
  function bar() {
    var z = 30;
    alert(x +  y + z);
  }
 
  bar();
}
 
foo(); // 60&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;针对上述代码，对应了如下的变量/活跃对象，函数的[[Scope]]属性以及上下文的作用域链：&lt;/p&gt;

&lt;p&gt;全局上下文的变量对象如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;globalContext.VO === Global = {
  x: 10
  foo: &lt;reference to function&gt;
};&lt;/reference&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在“foo”函数创建的时候，其[[Scope]]属性如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;foo.[[Scope]] = [
  globalContext.VO
];&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在“foo”函数激活的时候（进入上下文时），“foo”函数上下文的活跃对象如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;fooContext.AO = {
  y: 20,
  bar: &lt;reference to function&gt;
};&lt;/reference&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;同时，“foo”函数上下文的作用域链如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;fooContext.Scope = fooContext.AO + foo.[[Scope]] // i.e.:
 
fooContext.Scope = [
  fooContext.AO,
  globalContext.VO
];&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在内部“bar”函数创建的时候，其[[Scope]]属性如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;bar.[[Scope]] = [
  fooContext.AO,
  globalContext.VO
];&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在“bar”函数激活的时候，其对应的活跃对象如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;barContext.AO = {
  z: 30
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;同时，“bar”函数上下文的作用域链如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;barContext.Scope = barContext.AO + bar.[[Scope]] // i.e.:
 
barContext.Scope = [
  barContext.AO,
  fooContext.AO,
  globalContext.VO
];&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如下是“x”，“y”和“z”标识符的查询过程：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- "x"
-- barContext.AO // not found
-- fooContext.AO // not found
-- globalContext.VO // found - 10&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;- "y"
-- barContext.AO // not found
-- fooContext.AO // found - 20&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;- "z"
-- barContext.AO // found - 30&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;作用域的特性&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;接下来为大家介绍一些与作用域链和函数的[[Scope]]属性相关的重要特性。&lt;/p&gt;

&lt;h2&gt;闭包&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;在ECMAScript中，闭包和函数的[[Scope]]属性息息相关。正如此前介绍的，[[Scope]]是在函数创建的时候就保存在函数对象上了，并且直到函数销毁的时候才消失。
事实上，闭包就是函数代码和其[[Scope]]属性的组合。因此，[[Scope]]包含了函数创建所在的&lt;em&gt;词法环境&lt;/em&gt;（上层变量对象）。
上层上下文中的变量，可以在函数激活的时候，通过变量对象的词法链（函数创建的时候就保存起来了）查询到。&lt;/p&gt;

&lt;p&gt;如下例子所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var x = 10;
 
function foo() {
  alert(x);
}
 
(function () {
  var x = 20;
  foo(); // 10, but not 20
})();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们看到变量“x”是在“foo”函数的[[Scope]]中找到的。对于变量查询而言，词法链是在函数创建的时候就定义的，而不是在使用的调用的动态链（这个时候，变量“x”才会是20）。&lt;/p&gt;

&lt;p&gt;下面是另外一个（典型的）闭包的例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo() {
 
  var x = 10;
  var y = 20;
 
  return function () {
    alert([x, y]);
  };
 
}
 
var x = 30;
 
var bar = foo(); // anonymous function is returned
 
bar(); // [10, 20]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子再一次证明了处理标识符的时候，词法作用域链是在函数创建的时候定义的——变量“x”的值是10，而不是30。
并且，上述例子清楚的展示了函数（上述例子中指的是函数“foo”返回的匿名函数）的[[Scope]]属性，即使在创建该函数的上下文结束的时候依然存在。&lt;/p&gt;

&lt;p&gt;更多关于ECMAScript对闭包的实现细节会在第六章-闭包中做介绍。&lt;/p&gt;

&lt;h2&gt;通过Function构造器创建的函数的[[Scope]]属性&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;在前面的例子中，我们看到函数在创建的时候就拥有了[[Scope]]属性，并且通过该属性可以获取所有上层上下文中的变量。
然而，这里有个例外，就是当函数通过&lt;em&gt;Function&lt;/em&gt;构造器创建的时候。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var x = 10;
 
function foo() {
 
  var y = 20;
 
  function barFD() { // FunctionDeclaration
    alert(x);
    alert(y);
  }
 
  var barFE = function () { // FunctionExpression
    alert(x);
    alert(y);
  };
 
  var barFn = Function('alert(x); alert(y);');
 
  barFD(); // 10, 20
  barFE(); // 10, 20
  barFn(); // 10, "y" is not defined
 
}
 
foo();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子中，函数“barFn”就是通过&lt;em&gt;Function&lt;/em&gt;构造器来创建的，这个时候变量“y”就无法访问到了。
但这并不意味着函数“barFn”就没有内部的[[Scope]]属性了（否则它连变量“x”都无法访问到了）。
问题就在于当函数通过&lt;em&gt;Function&lt;/em&gt;构造器来创建的时候，其[[Scope]]属性永远都只包含全局对象。
哪怕在上层上下文中（非全局上下文）创建一个闭包都是无济于事的。&lt;/p&gt;

&lt;h2&gt;二维作用域链查询&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;在作用域链查询的时候还有很重要的一点：变量对象的原型（如果有的话）也是需要考虑的——因为原型是ECMAScript天生的特性：如果属性在对象中没有找到，那么会继续通过原型链进行查询。
比方说如下这些二维链：(1)在作用域链的链接上，(2)在每个作用域链接上——深入到原型链的链接上。如果在原型链（&lt;em&gt;Object.prototype&lt;/em&gt;）上定义了属性就能观察到效果了：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo() {
  alert(x);
}
 
Object.prototype.x = 10;
 
foo(); // 10&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;活跃对象是没有原型这一说的。通过如下例子可以看出：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo() {
 
  var x = 20;
 
  function bar() {
    alert(x);
  }
 
  bar();
}
 
Object.prototype.x = 10;
 
foo(); // 20&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;试想下，如果“bar”函数的活跃对象有原型的话，属性“x”则应当在Object.prototype中找到，因为它在AO中根本不存在。
然而，上述第一个例子中，在标识符处理阶段遍历了整个作用域链，到了全局对象（部分实现是这样的），该对象继承自Object.prototype，因此，最终变量“x”的值就变成了10。&lt;/p&gt;

&lt;p&gt;同样的情况，在某些版本的SpiderMonkey中，通过命名函数表达式（简称：NFE）也会发生，其中的存储了可选的函数表达式的名字的特殊对象也继承自&lt;em&gt;Object.prototype&lt;/em&gt;,
同样的，在某些版本的&lt;em&gt;Blackberry&lt;/em&gt;中，也是如此，其活跃对象是继承自&lt;em&gt;Object.prototype&lt;/em&gt;的。不过，关于这块详细的特性将会在第五章-函数中作介绍。&lt;/p&gt;

&lt;h2&gt;全局和eval上下文的作用域链&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;尽管这部分内容没多大意思，但还是值得一提的。全局上下文的作用域链中只包含全局对象。“eval”代码类型的上下文和调用上下文（&lt;em&gt;calling context&lt;/em&gt;）有相同的作用域链。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;globalContext.Scope = [
  Global
];
 
evalContext.Scope === callingContext.Scope;&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;执行代码阶段对作用域的影响&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;ECMAScript中，在运行时，执行代码阶段有两种语句可以修改作用域链——&lt;em&gt;with&lt;/em&gt;语句和&lt;em&gt;catch&lt;/em&gt;从句。在标识符查询阶段，这两者都会被添加到作用域链的最前面。
比如，当有with或者catch的时候，作用域链就会被修改如下形式：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Scope = withObject|catchObject + AO|VO + [[Scope]]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如下例子中，&lt;em&gt;with&lt;/em&gt;语句添加了foo对象，使得它的属性可以不需要前缀直接访问。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = {x: 10, y: 20};
 
with (foo) {
  alert(x); // 10
  alert(y); // 20
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;对应的作用域链修改为如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Scope = foo + AO|VO + [[Scope]]&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;接着来看下面这个例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var x = 10, y = 10;
 
with ({x: 20}) {
 
  var x = 30, y = 30;
 
  alert(x); // 30
  alert(y); // 30
}
 
alert(x); // 10
alert(y); // 30&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;发生了什么？怎么最外层的“y”变成了30？
在进入上下文的时候，“x”和“y”标识符已经添加到了变量对象。之后，到了执行代码阶段，发生了如下的改动：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;x=10, y=10  &lt;/li&gt;
&lt;li&gt;对象{x: 20}添加到了作用域链的最前面  &lt;/li&gt;
&lt;li&gt;在with中遇到了&lt;em&gt;var&lt;/em&gt;语句，当然了，这个时候什么也不会发生。因为早在进入上下文阶段所有的变量都已经解析过了并且添加到了对应的变量对象上了。  &lt;/li&gt;
&lt;li&gt;这里修改了“x”的值，原本“x”是在第二步的时候添加的对象{x: 20}（该对象被添加到了作用域链的最前面）中的“x”，现在变成了30。  &lt;/li&gt;
&lt;li&gt;同样的，“y”的值也修改了，由原本的10变成了30  &lt;/li&gt;
&lt;li&gt;之后，在with语句结束之后，其特殊对象从作用域链中移除（修改过的“x”——30，也随之移除），作用域链又恢复到了with语句前的状态。  &lt;/li&gt;
&lt;li&gt;正如在最后两个alert中看到的，“x”的值恢复到了原先的10，而“y”的值因为在with语句的时候被修改过了，因此变为了30。  &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;同样的，&lt;em&gt;catch&lt;/em&gt;从句（可以访问参数异常）会创建一个只包含一个属性（异常参数名）的新对象。如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;try {
  ...
} catch (ex) {
  alert(ex);
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;作用域链修改为如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var catchObject = {
  ex: &lt;exception object&gt;
};
 
Scope = catchObject + AO|VO + [[Scope]]&lt;/exception&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在&lt;em&gt;catch&lt;/em&gt;从句结束后，作用域链同样也会恢复到原先的状态。&lt;/p&gt;

&lt;h2&gt;总结&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;本文，介绍了几乎所有与执行上下文相关的概念以及相应的细节。后面的章节中，会给大家介绍函数对象的细节：函数的类型（&lt;em&gt;FunctionDeclaration&lt;/em&gt;，&lt;em&gt;FunctionExpression&lt;/em&gt;）和闭包。
顺便提下，本文中介绍过，闭包是和[[Scope]]有直接的关系，但是关于闭包的细节会在后续章节中作介绍。&lt;/p&gt;

&lt;h2&gt;扩展阅读&lt;/h2&gt;

&lt;hr&gt;&lt;ul&gt;&lt;li&gt;8.6.2 —— &lt;a href="http://bclary.com/2004/11/07/#a-8.6.2" target="_blank"&gt;[[Scope]]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;10.1.4 —— &lt;a href="http://bclary.com/2004/11/07/#a-10.1.4" target="_blank"&gt;作用域链和标识符的处理&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><link>http://blog.goddyzhao.me/post/11259644092</link><guid>http://blog.goddyzhao.me/post/11259644092</guid><pubDate>Sun, 09 Oct 2011 23:25:00 -0400</pubDate><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>this</title><description>&lt;h2&gt;说明&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;此文译自&lt;a href="http://dmitrysoshnikov.com/" target="_blank"&gt;Dmitry A.Soshnikov&lt;/a&gt; 的文章&lt;a href="http://dmitrysoshnikov.com/ecmascript/chapter-3-this/" target="_blank"&gt;this&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;概要&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;本文将进一步讨论与&lt;a href="http://goddyzhao.tumblr.com/post/10020230352/execution-context" target="_blank"&gt;执行上下文&lt;/a&gt;密切相关的概念——&lt;em&gt;this&lt;/em&gt;关键字。&lt;/p&gt;

&lt;p&gt;事实证明，&lt;em&gt;this&lt;/em&gt;这块的内容非常的复杂，它在不同执行上下文的情况下其值都会不同，并且会相应的引发一些问题。&lt;/p&gt;

&lt;p&gt;很多程序员一看到&lt;em&gt;this&lt;/em&gt;关键字，就会把它和面向对象的编程方式联系在一起，它指向利用构造器新创建出来的对象。在ECMAScript中，也支持this，然而，
正如大家所熟知的，this不仅仅只用来表示创建出来的对象。&lt;/p&gt;

&lt;p&gt;接下来给大家揭开在ECMAScript中this神秘的面纱。&lt;/p&gt;

&lt;h2&gt;定义&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;&lt;em&gt;This&lt;/em&gt;是执行上下文的一个属性：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;activeExecutionContext = {
  VO: {...},
  this: thisValue
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里的VO就是前一章介绍的&lt;a href="http://goddyzhao.tumblr.com/post/11141710441/variable-object" target="_blank"&gt;变量对象&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This&lt;/em&gt;与上下文的可执行代码类型有关，其值在&lt;em&gt;进入上下文阶段&lt;/em&gt;就确定了，并且在&lt;em&gt;执行代码阶段&lt;/em&gt;是不能改变的。&lt;/p&gt;

&lt;p&gt;下面就来详细对其作个介绍。&lt;/p&gt;

&lt;h2&gt;全局代码中This的值&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;这种情况下，一切都变得非常简单，&lt;em&gt;this&lt;/em&gt;的值总是&lt;em&gt;全局对象&lt;/em&gt;本身;因此，可以间接地获取引用：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// 显式定义全局对象的属性
this.a = 10; // global.a = 10
alert(a); // 10
 
// 通过赋值给不受限的标识符来进行隐式定义
b = 20;
alert(this.b); // 20
 
// 通过变量声明来进行隐式定义
// 因为全局上下文中的变量对象就是全局对象本身
var c = 30;
alert(this.c); // 30&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;函数代码中This的值&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;当&lt;em&gt;this&lt;/em&gt;在函数代码中的时候，事情就变得有趣多了。这种情况下是最复杂的，并且会引发很多的问题。&lt;/p&gt;

&lt;p&gt;函数代码中this值的第一个特性（同时也是最主要的特性）就是：它并非静态的绑定在函数上。&lt;/p&gt;

&lt;p&gt;正如此前提到的，this的值是在进入上下文的阶段确定的，并且在函数代码中的话，其值每次都会大不相同。&lt;/p&gt;

&lt;p&gt;然而，一旦进入执行代码阶段，其值就不能改变了。比方说，要想给this赋一个新的值是不可能的，因为this根本就不是变量（相反的，在Python语言中，它显示定义的&lt;em&gt;self&lt;/em&gt;对象是可以在运行时随意更改的）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = {x: 10};
 
var bar = {
  x: 20,
  test: function () {
 
    alert(this === bar); // true
    alert(this.x); // 20
 
    this = foo; // error, 不能更改this的值
 
    alert(this.x); // 如果没有错误，则其值为10而不是20
 
  }
 
};
 
// 在进入上下文的时候，this的值就确定了是“bar”对象
// 至于为什么，会在后面作详细介绍
 
bar.test(); // true, 20
 
foo.test = bar.test;
 
// 但是，这个时候，this的值又会变成“foo”
// 纵然我们调用的是同一个函数
 
foo.test(); // false, 10&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;因此，在函数代码中影响this值的因素是有很多的。&lt;/p&gt;

&lt;p&gt;首先，在一般的函数调用中，this的值是由激活上下文代码的调用者决定的，比如说，调用函数的外层上下文。this的值是由调用表达式的形式决定的。&lt;/p&gt;

&lt;p&gt;理解并谨记这一点是非常必要的，有利于在任何上下文中都能准确的确定this的值。&lt;/p&gt;

&lt;p&gt;影响调用上下文中的this的值的只有可能是调用表达式的形式，也就是调用函数的方式。
（一些关于JavaScript的文章和书籍中指出的“&lt;em&gt;this&lt;/em&gt;的值取决于函数的定义方式，如果是全局函数，则this的值就会设置为全局对象，如果是某个对象的方法，则this的值就会设置为该对象”——这纯属扯淡，根本就是在误人子弟）。
正如此前大家看到的，纵然是全局函数，this的值也会随着函数调用方式的不同而不同：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo() {
  alert(this);
}
 
foo(); // global
 
alert(foo === foo.prototype.constructor); // true
 
// 然而，同样的函数，以另外一种调用方式的话，this的值就不同了
 
foo.prototype.constructor(); // foo.prototype&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;调用一个对象的某个方法的时候，this的值也有可能不是该对象的：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = {
  bar: function () {
    alert(this);
    alert(this === foo);
  }
};
 
foo.bar(); // foo, true
 
var exampleFunc = foo.bar;
 
alert(exampleFunc === foo.bar); // true
 
// 同样地，相同的函数以不同的调用方式，this的值也就不同了
 
exampleFunc(); // global, false&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;那么，究竟调用表达式的方式是如何影响this的值的呢？为了完全搞明白这其中的奥妙，首先，这里有必要先介绍一种内部类型——引用类型（the &lt;em&gt;Reference&lt;/em&gt; type）。&lt;/p&gt;

&lt;h2&gt;引用类型&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;引用类型的值可以用伪代码表示为一个拥有两个属性的对象——&lt;em&gt;base&lt;/em&gt;属性（属性所属的对象）以及该base对象中的&lt;em&gt;propertyName&lt;/em&gt;属性：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var valueOfReferenceType = {
  base: &lt;base object&gt;,
  propertyName: &lt;property name&gt;
};&lt;/property&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;引用类型的值只有可能是以下两种情况：&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;当处理一个标识符的时候&lt;/li&gt;
&lt;li&gt;或者进行属性访问的时候&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;关于标识符的处理会在第四章——所用域链中作介绍，这里我们只要注意的是，此算法总返回一个引用类型的值（这对this的值是至关重要的）。&lt;/p&gt;

&lt;p&gt;标识符其实就是变量名，函数名，函数参数名以及全局对象的未受限的属性。如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = 10;
function bar() {}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;中间过程中，对应的引用类型的值如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var fooReference = {
  base: global,
  propertyName: 'foo'
};
 
var barReference = {
  base: global,
  propertyName: 'bar'
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;要从引用类型的值中获取一个对象实际的值需要&lt;em&gt;GetValue&lt;/em&gt;方法，该方法用伪代码可以描述成如下形式：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function GetValue(value) {
 
  if (Type(value) != Reference) {
    return value;
  }
 
  var base = GetBase(value);
 
  if (base === null) {
    throw new ReferenceError;
  }
 
  return base.[[Get]](GetPropertyName(value));
 
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码中的&lt;em&gt;[[Get]]&lt;/em&gt;方法返回了对象属性实际的值，包括从原型链中继承的属性：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;GetValue(fooReference); // 10
GetValue(barReference); // function object "bar"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;对于属性访问来说，有两种方式： &lt;em&gt;点符号&lt;/em&gt;（这时属性名是正确的标识符并且提前已经知道了）或者&lt;em&gt;中括号符号&lt;/em&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;foo.bar();
foo['bar']();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;中间过程中，得到如下的引用类型的值：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var fooBarReference = {
  base: foo,
  propertyName: 'bar'
};
 
GetValue(fooBarReference); // function object "bar"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;问题又来了，引用类型的值又是如何影响函数上下文中this的值的呢？——非常重要。这也是本文的重点。总的来说，决定函数上下文中this的值的规则如下所示：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;函数上下文中this的值是函数调用者提供并且由当前调用表达式的形式而定的。
   如果在调用括号()的左边，有引用类型的值，那么this的值就会设置为该引用类型值的base对象。
   所有其他情况下（非引用类型），this的值总是&lt;em&gt;null&lt;/em&gt;。然而，由于null对于this来说没有任何意义，因此会隐式转换为全局对象。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo() {
  return this;
}
 
foo(); // global&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码中，调用括号的左侧是引用类型的值（因为&lt;em&gt;foo&lt;/em&gt;是标识符）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var fooReference = {
  base: global,
  propertyName: 'foo'
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;相应的，this的值会设置为引用类型值的base对象，这里就是全局对象。&lt;/p&gt;

&lt;p&gt;属性访问也是类似的：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = {
  bar: function () {
    return this;
  }
};
 
foo.bar(); // foo&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;同样的，也是引用类型的值，它的base对象是foo对象，激活bar函数的时候，this的值就设置为foo对象了：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var fooBarReference = {
  base: foo,
  propertyName: 'bar'
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然而，同样的函数以不同的激活方式的话，this的值就完全不同了：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var test = foo.bar;
test(); // global&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;因为test也是标识符，这样就产生了另外的引用类型的值，其中base对象（全局对象）就是this的值：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var testReference = {
  base: global,
  propertyName: 'test'
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;至此，我们就可以精确的解释，为什么同样的函数，以不同的调用方式激活，this的值也会不同了——答案就是处理过程中，是不同的引用类型的值：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo() {
  alert(this);
}
 
foo(); // global, 因为
 
var fooReference = {
  base: global,
  propertyName: 'foo'
};
 
alert(foo === foo.prototype.constructor); // true
 
// 另一种调用方式
 
foo.prototype.constructor(); // foo.prototype, 因为
 
var fooPrototypeConstructorReference = {
  base: foo.prototype,
  propertyName: 'constructor'
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如下是另外一种（典型的）利用调用表达式来动态决定this值的例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo() {
  alert(this.bar);
}
 
var x = {bar: 10};
var y = {bar: 20};
 
x.test = foo;
y.test = foo;
 
x.test(); // 10
y.test(); // 20&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;函数调用以及非引用类型&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;正如此前提到过的，当调用括号左侧为非引用类型的时候，this的值会设置为null，并最终变成全局对象。&lt;/p&gt;

&lt;p&gt;我们来考虑下如下表达式：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function () {
  alert(this); // null =&amp;gt; global
})();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子中，有函数对象，但非引用类型对象（因为它不既不是标识符也不属于属性访问），因此，this的值最终设置为全局对象。&lt;/p&gt;

&lt;p&gt;如下是更为复杂的例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var foo = {
  bar: function () {
    alert(this);
  }
};
 
foo.bar(); // Reference, OK =&amp;gt; foo
(foo.bar)(); // Reference, OK =&amp;gt; foo
 
(foo.bar = foo.bar)(); // global?
(false || foo.bar)(); // global?
(foo.bar, foo.bar)(); // global?&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;看了上述代码，你可能又有疑问了：为什么明明是属性访问，但是最终this的值不是base对象而是全局对象呢？&lt;/p&gt;

&lt;p&gt;这里主要疑问在最后三个表达式，这三个表达式添加了特定的操作之后，调用括号左侧就不再是引用类型的值了。&lt;/p&gt;

&lt;p&gt;第一种情况——非常明确，是引用类型，最终this的值设置为base对象，foo。&lt;/p&gt;

&lt;p&gt;第二种情况有一个组操作符（&lt;em&gt;grouping operator&lt;/em&gt;），该操作符不会触发调用获取引用类型实际值的方法，比如：&lt;em&gt;GetValue&lt;/em&gt;方法。
相应的，处理组操作符中间过程中——获得的仍然是一个引用类型的值，这也就解释了为什么this的值设置成了base对象，&lt;em&gt;foo&lt;/em&gt;。&lt;/p&gt;

&lt;p&gt;第三种情况是一个&lt;em&gt;赋值操作符&lt;/em&gt;（&lt;em&gt;assignment operator&lt;/em&gt;）,与组操作符不同的是，它会触发调用GetValue方法（参见&lt;a href="http://bclary.com/2004/11/07/#a-11.13.1" target="_blank"&gt;11.13.1&lt;/a&gt;中的第三步）。
最后返回的时候就是一个函数对象了（而不是引用类型的值了），这就意味着this的值会设置为null，最终会变成全局对象。&lt;/p&gt;

&lt;p&gt;第四和第五种情况也是类似的——逗号操作符和OR逻辑表达式都会触发调用GetValue方法，于是相应地就会丢失原先的引用类型值，变成了函数类型，this的值就变成了全局对象了。&lt;/p&gt;

&lt;h2&gt;引用类型以及null（this的值）&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;有这么一种情况下，当调用表达式左侧是引用类型的值，但是this的值却是null，最终变为全局对象。
发生这种情况的条件是当引用类型值的base对象恰好为&lt;strong&gt;活跃对象&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;当内部子函数在父函数中被调用的时候就会发生这种情况。正如&lt;a href="http://goddyzhao.tumblr.com/post/11141710441/variable-object" target="_blank"&gt;第二章&lt;/a&gt;介绍的，
局部变量，内部函数以及函数的形参都会存储在指定函数的&lt;em&gt;活跃对象&lt;/em&gt;中：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo() {
  function bar() {
    alert(this); // global
  }
  bar(); // 和AO.bar()是一样的
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;活跃对象总是会返回this值为——null（用伪代码来表示，AO.bar()就相当于null.bar()）。然后，如此前描述的，this的值最终会由null变为全局对象。&lt;/p&gt;

&lt;p&gt;当函数调用包含在&lt;em&gt;with&lt;/em&gt;语句的代码块中，并且&lt;em&gt;with&lt;/em&gt;对象包含一个函数属性的时候，就会出现例外的情况。&lt;em&gt;with&lt;/em&gt;语句会将该对象添加到作用域链的最前面，在活跃对象的之前。
相应地，在引用类型的值（标识符或者属性访问）的情况下，base对象就不再是活跃对象了，而是with语句的对象。另外，值得一提的是，它不仅仅只针对内部函数，全局函数也是如此，
原因就是with对象掩盖了作用域链中更高层的对象（全局对象或者活跃对象）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var x = 10;
 
with ({
 
  foo: function () {
    alert(this.x);
  },
  x: 20
 
}) {
 
  foo(); // 20
 
}
 
// because
 
var  fooReference = {
  base: __withObject,
  propertyName: 'foo'
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当调用的函数恰好是&lt;em&gt;catch&lt;/em&gt;从句的参数时，情况也是类似的：在这种情况下，&lt;em&gt;catch&lt;/em&gt;对象也会添加到作用域链的最前面，在活跃对象和全局对象之前。
然而，这个行为在ECMA-262-3中被指出是个bug，并且已经在ECMA-262-5中修正了；因此，在这种情况下，this的值应该设置为全局对象，而不是catch对象。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;try {
  throw function () {
    alert(this);
  };
} catch (e) {
  e(); // __catchObject - in ES3, global - fixed in ES5
}
 
// on idea
 
var eReference = {
  base: __catchObject,
  propertyName: 'e'
};
 
// 然而，既然这是个bug
// 那就应该强制设置为全局对象
// null =&amp;gt; global
 
var eReference = {
  base: global,
  propertyName: 'e'
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;同样的情况还会在递归调用一个非匿名函数的时候发生（函数相关的内容会在第五章作相应的介绍）。在第一次函数调用的时候，base对象是外层的活跃对象（或者全局对象），
在接下来的递归调用的时候——base对象应当是一个存储了可选的函数表达式名字的特殊对象，然而，事实却是，在这种情况下，this的值永远都是全局对象：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function foo(bar) {
 
  alert(this);
 
  !bar &amp;amp;&amp;amp; foo(1); // "should" be special object, but always (correct) global
 
})(); // global&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;但函数作为构造器被调用时this的值&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;这里要介绍的是函数上下文中关于this值的另外一种情况——当函数作为构造器被调用的时候：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function A() {
  alert(this); // newly created object, below - "a" object
  this.x = 10;
}
 
var a = new A();
alert(a.x); // 10&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在这种情况下，&lt;a href="http://bclary.com/2004/11/07/#a-11.2.2" target="_blank"&gt;new&lt;/a&gt;操作符会调用“A”函数的内部&lt;a href="http://bclary.com/2004/11/07/#a-13.2.2" target="_blank"&gt;[[Construct]]&lt;/a&gt;。
在对象创建之后，会调用内部的&lt;a href="http://bclary.com/2004/11/07/#a-13.2.1" target="_blank"&gt;[[Call]]&lt;/a&gt;函数，然后所有“A”函数中this的值会设置为新创建的对象。&lt;/p&gt;

&lt;h2&gt;手动设置函数调用时this的值&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;&lt;em&gt;Function.prototype&lt;/em&gt;上定义了两个方法（因此，它们对所有函数而言都是可访问的），允许手动指定函数调用时this的值。这两个方法是：&lt;em&gt;.apply&lt;/em&gt;和&lt;em&gt;.call&lt;/em&gt;；
它们都接受第一个参数作为调用上下文中this的值。而它们的不同点其实无关紧要：对于&lt;em&gt;.apply&lt;/em&gt;来说，第二个参数接受数组类型（或者是类数组的对象，比如&lt;em&gt;arguments&lt;/em&gt;）,
而&lt;em&gt;.call&lt;/em&gt;方法接受任意多的参数。这两个方法只有第一个参数是必要的——this的值。&lt;/p&gt;

&lt;p&gt;如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var b = 10;
 
function a(c) {
  alert(this.b);
  alert(c);
}
 
a(20); // this === global, this.b == 10, c == 20
 
a.call({b: 20}, 30); // this === {b: 20}, this.b == 20, c == 30
a.apply({b: 30}, [40]) // this === {b: 30}, this.b == 30, c == 40&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;总结&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;本文我们讨论了ECMAScript中&lt;em&gt;this&lt;/em&gt;关键字的特性（相对C++或者Java而言，真的可以说是特性）。洗完此文对大家理解this关键字在ECMAScript中的工作原理有所帮助。&lt;/p&gt;

&lt;h2&gt;扩展阅读&lt;/h2&gt;

&lt;hr&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://bclary.com/2004/11/07/#a-10.1.7" target="_blank"&gt;This&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://bclary.com/2004/11/07/#a-11.1.1" target="_blank"&gt;this关键字&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://bclary.com/2004/11/07/#a-11.2.2" target="_blank"&gt;new操作符&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://bclary.com/2004/11/07/#a-11.2.3" target="_blank"&gt;函数调用&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><link>http://blog.goddyzhao.me/post/11218727474</link><guid>http://blog.goddyzhao.me/post/11218727474</guid><pubDate>Sun, 09 Oct 2011 03:27:00 -0400</pubDate><category>javascript</category><category>JavaScript Internal</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>变量对象（Variable object）</title><description>&lt;h2&gt;说明&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;此文译自&lt;a href="http://dmitrysoshnikov.com/" target="_blank"&gt;Dmitry A.Soshnikov&lt;/a&gt; 的文章&lt;a href="http://dmitrysoshnikov.com/ecmascript/chapter-2-variable-object/" target="_blank"&gt;Variable object&lt;/a&gt;&lt;br/&gt;
另，此文还有另外一位同事（宋珍珍）共同参译&lt;/p&gt;

&lt;h2&gt;概要&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;我们总是会在程序中定义一些函数和变量，之后会使用这些函数和变量来构建我们的系统。&lt;br/&gt;
然而，对于解释器来说，它又是如何以及从哪里找到这些数据的（函数，变量）？当引用一个对象的时候，在解释器内部又发生了什么？&lt;/p&gt;

&lt;p&gt;许多ECMA脚本程序员都知道，变量和&lt;a href="http://goddyzhao.tumblr.com/post/10020230352/execution-context" target="_blank"&gt;执行上下文&lt;/a&gt;是密切相关的：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var a = 10; // 全局上下文中的变量
 
(function () {
  var b = 20; // 函数上下文中的局部变量
})();
 
alert(a); // 10
alert(b); // "b" is not defined&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;不仅如此，许多程序员也都知道，ECMAScript标准中指出独立的作用域只有通过“函数代码”（可执行代码类型中的一种）才能创建出来。比方说，与C/C++不同的是，在ECMAScript中&lt;em&gt;for&lt;/em&gt;循环的代码块是无法创建本地上下文的：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;for (var k in {a: 1, b: 2}) {
  alert(k);
}
 
alert(k); // 尽管循环已经结束，但是变量“k”仍然在作用域中&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;下面就来详细介绍下，当声明变量和函数的时候，究竟发生了什么。&lt;/p&gt;

&lt;h2&gt;数据声明&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;既然变量和执行上下文有关，那它就该知道数据存储在哪里以及如何获取。这种机制就称作&lt;em&gt;变量对象&lt;/em&gt;：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A variable object (in abbreviated form — VO) is a special object related with an execution context and which stores:&lt;/p&gt;
  
  &lt;ul&gt;&lt;li&gt;variables (var, VariableDeclaration);  &lt;/li&gt;
  &lt;li&gt;function declarations (FunctionDeclaration, in abbreviated form FD);  &lt;/li&gt;
  &lt;li&gt;and function formal parameters&lt;br/&gt;
  declared in the context.  &lt;/li&gt;
  &lt;/ul&gt;&lt;/blockquote&gt;

&lt;p&gt;举个例子，可以用ECMAScript的对象来表示变量对象：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;VO = {};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;VO同时也是一个执行上下文的属性：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;activeExecutionContext = {
  VO: {
    // 上下文中的数据 (变量声明（var）, 函数声明（FD), 函数形参（function arguments）)
  }
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;对变量的间接引用（通过VO的属性名）只允许发生在&lt;em&gt;全局上下文&lt;/em&gt;中的变量对象上(全局对象本身就是变量对象，这部分会在后续作相应的介绍)。
对于其他的上下文而言，是无法直接引用VO的，因为VO是实现层的。&lt;/p&gt;

&lt;p&gt;声明新的变量和函数的过程其实就是在VO中创建新的和变量以及函数名对应的属性和属性值的过程。&lt;/p&gt;

&lt;p&gt;如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var a = 10;
 
function test(x) {
  var b = 20;
};
 
test(30);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码对应的变量对象则如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// 全局上下文中的变量对象
VO(globalContext) = {
  a: 10,
  test: &lt;reference to function&gt;
};
 
// “test”函数上下文中的变量对象
VO(test functionContext) = {
  x: 30,
  b: 20
};&lt;/reference&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;但是，在实现层（标准中定义的），变量对象只是一个抽象的概念。在实际执行上下文中，VO可能完全不叫VO，并且初始的结构也可能完全不同。&lt;/p&gt;

&lt;h2&gt;不同执行上下文中的变量对象&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;变量对象上的一些操作（比如：变量的初始化）和行为对于所有的执行上下文类型来说都已一样的。从这一点来说，将变量对象表示成抽象的概念更加合适。
函数上下文还能定义额外的与变量对象相关的信息。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;AbstractVO (generic behavior of the variable instantiation process)
 
  ║
  ╠══&amp;gt; GlobalContextVO
  ║        (VO === this === global)
  ║
  ╚══&amp;gt; FunctionContextVO
           (VO === AO, &lt;arguments&gt; object and &lt;formal parameters&gt; are added)&lt;/formal&gt;&lt;/arguments&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;接下来对这块内容进行详细介绍。&lt;/p&gt;

&lt;h2&gt;全局上下文中的变量对象&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;首先，有必要对&lt;em&gt;全局对象（Global object）&lt;/em&gt;作个定义。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;全局对象是一个在进入任何执行上下文前就创建出来的对象；此对象以单例形式存在；它的属性在程序任何地方都可以直接访问，其生命周期随着程序的结束而终止。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;全局对象在创建的时候，诸如Math,String,Date,parseInt等等属性也会被初始化，同时，其中一些对象会指向全局对象本身——比如，DOM中，全局对象上的&lt;em&gt;window&lt;/em&gt;属性就指向了全局对象（但是，并非所有的实现都是如此）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;global = {
  Math: ,
  String: 
  ...
  ...
  window: global
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在引用全局对象的属性时，前缀通常可以省略，因为全局对象是不能通过名字直接访问的。然而，通过全局对象上的&lt;em&gt;this&lt;/em&gt;值，以及通过如DOM中的window对象这样递归引用的方式都可以访问到全局对象：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;String(10); // 等同于 global.String(10);
 
// 带前缀
window.a = 10; // === global.window.a = 10 === global.a = 10;
this.b = 20; // global.b = 20;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;回到全局上下文的变量对象上——这里变量对象就是全局对象本身：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;VO(globalContext) === global;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;准确地理解这个事实是非常必要的：正是由于这个原因，当在全局上下文中声明一个变量时，可以通过全局对象上的属性来间地引用该变量（比方说，当变量名提前未知的情况下）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var a = new String('test');
 
alert(a); // directly, is found in VO(globalContext): "test"
 
alert(window['a']); // indirectly via global === VO(globalContext): "test"
alert(a === this.a); // true
 
var aKey = 'a';
alert(window[aKey]); // indirectly, with dynamic property name: "test"&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;函数上下文中的变量对象&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;在函数的执行上下文中，VO是不能直接访问的。它主要扮演被称作&lt;em&gt;活跃对象（activation object）&lt;/em&gt;（简称：AO）的角色。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;VO(functionContext) === AO;&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;活跃对象会在进入函数上下文的时候创建出来，初始化的时候会创建一个&lt;em&gt;arguments&lt;/em&gt;属性，其值就是&lt;em&gt;Arguments&lt;/em&gt;对象：&lt;/p&gt;
&lt;/blockquote&gt;

&lt;pre&gt;&lt;code&gt;AO = {
  arguments: &lt;argo&gt;
};&lt;/argo&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;Arguments&lt;/em&gt;对象是活跃对象上的属性，它包含了如下属性：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;em&gt;callee&lt;/em&gt; —— 对当前函数的引用  &lt;/li&gt;
&lt;li&gt;&lt;em&gt;length&lt;/em&gt; —— 实参的个数  &lt;/li&gt;
&lt;li&gt;&lt;em&gt;properties-indexes(数字，转换成字符串)&lt;/em&gt;其值是函数参数的值（参数列表中，从左到右）。properties-indexes的个数 == arguments.length;&lt;br/&gt;
arguments对象的properties-indexes的值和当前（实际传递的）形参是共享的。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;如下所示：&lt;br/&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo(x, y, z) {
 
  // 定义的函数参数（x,y,z）的个数
  alert(foo.length); // 3
 
  // 实际传递的参数个数
  alert(arguments.length); // 2
 
  // 引用函数自身
  alert(arguments.callee === foo); // true
 
  // 参数互相共享
 
  alert(x === arguments[0]); // true
  alert(x); // 10
 
  arguments[0] = 20;
  alert(x); // 20
 
  x = 30;
  alert(arguments[0]); // 30
 
  // 然而，对于没有传递的参数z，
  // 相关的arguments对象的index-property是不共享的
 
  z = 40;
  alert(arguments[2]); // undefined
 
  arguments[2] = 50;
  alert(z); // 40
 
}
 
foo(10, 20);&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子，在当前的Google Chrome浏览器中有个bug——参数z和arguments[2]也是互相共享的。&lt;/p&gt;

&lt;h2&gt;处理上下文代码的几个阶段&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;至此，也就到了本文最核心的部分了。处理执行上下文代码分为两个阶段：&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;进入执行上下文  &lt;/li&gt;
&lt;li&gt;执行代码  &lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;对变量对象的修改和这两个阶段密切相关。&lt;/p&gt;

&lt;p&gt;要注意的是，这两个处理阶段是通用的行为，与上下文类型无关（不管是全局上下文还是函数上下文都是一致的）。&lt;/p&gt;

&lt;h2&gt;进入执行上下文&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;一旦进入执行上下文（在执行代码之前），VO就会被一些属性填充（在此前已经描述过了）：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;函数的形参（当进入函数执行上下文时）&lt;br/&gt;
—— 变量对象的一个属性，其属性名就是形参的名字，其值就是实参的值；对于没有传递的参数，其值为&lt;em&gt;undefined&lt;/em&gt;  &lt;/li&gt;
&lt;li&gt;函数声明（&lt;em&gt;FunctionDeclaration&lt;/em&gt;, FD）
—— 变量对象的一个属性，其属性名和值都是函数对象创建出来的；如果变量对象已经包含了相同名字的属性，则替换它的值&lt;/li&gt;
&lt;li&gt;变量声明（&lt;em&gt;var&lt;/em&gt;，&lt;em&gt;VariableDeclaration&lt;/em&gt;）
—— 变量对象的一个属性，其属性名即为变量名，其值为&lt;em&gt;undefined&lt;/em&gt;;如果变量名和已经声明的函数名或者函数的参数名相同，则不会影响已经存在的属性。  &lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;看下面这个例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function test(a, b) {
  var c = 10;
  function d() {}
  var e = function _e() {};
  (function x() {});
}
 
test(10); // call&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当以10为参数进入“test”函数上下文的时候，对应的AO如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;AO(test) = {
  a: 10,
  b: undefined,
  c: undefined,
  d: &amp;lt;reference to FunctionDeclaration "d"&amp;gt;
  e: undefined
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;注意了，上面的AO并不包含函数“x”。这是因为这里的“x”并不是函数声明而是&lt;em&gt;函数表达式&lt;/em&gt;（&lt;em&gt;FunctionExpression&lt;/em&gt;，简称FE），函数表达式不会对VO造成影响。
尽管函数“_e”也是函数表达式，然而，正如我们所看到的，由于它被赋值给了变量“e”，因此它可以通过“e”来访问到。关于函数声明和函数表达式的区别会在第五章——函数作具体介绍。&lt;/p&gt;

&lt;p&gt;至此，处理上下文代码的第一阶段介绍完了，接下来介绍第二阶段——&lt;em&gt;执行代码&lt;/em&gt;阶段。&lt;/p&gt;

&lt;h2&gt;执行代码&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;此时，AO/VO的属性已经填充好了。（尽管，大部分属性都还没有赋予真正的值，都只是初始化时候的&lt;em&gt;undefined&lt;/em&gt;值）。&lt;/p&gt;

&lt;p&gt;继续以上一例子为例，到了执行代码阶段，AO/VO就会修改成为如下形式：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;AO['c'] = 10;
AO['e'] = &lt;reference to functionexpression&gt;;&lt;/reference&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;再次注意到，这里函数表达式“_e”仍在内存中，这是因为它被保存在声明的变量“e”中，而同样是函数表达式的“x”却不在AO/VO中：
如果尝试在定义前或者定义后调用“x”函数，这时会发生“x为定义”的错误。未保存的函数表达式只有在定义或者递归时才能调用。&lt;/p&gt;

&lt;p&gt;如下是更加典型的例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;alert(x); // function
 
var x = 10;
alert(x); // 10
 
x = 20;
 
function x() {};
 
alert(x); // 20&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子中，为何“x”打印出来是函数呢？为何在声明前就可以访问到？又为何不是10或者20呢？原因在于，根据规则——在进入上下文的时候，VO会被填充函数声明；
同一阶段，还有变量声明“x”，但是，正如此前提到的，变量声明是在函数声明和函数形参之后，并且，变量声明不会对已经存在的同样名字的函数声明和函数形参发生冲突，
因此，在进入上下文的阶段，VO填充为如下形式：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;VO = {};
 
VO['x'] = &amp;lt;引用了函数声明“x”&amp;gt;
 
// 发现var x = 10;
// 如果函数“x”还未定义
// 则 "x" 为undefined, 但是，在我们的例子中
// 变量声明并不会影响同名的函数值
 
VO['x'] = &amp;lt;值不受影响，仍是函数&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;随后，在执行代码阶段，VO被修改为如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;VO['x'] = 10;
VO['x'] = 20;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;正如在第二个和第三个alert显示的那样。&lt;/p&gt;

&lt;p&gt;如下例子再次看到在进入上下文阶段，变量存储在VO中（因此，尽管else的代码块永远都不会执行到，而“b”却仍然在VO中）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;if (true) {
  var a = 1;
} else {
  var b = 2;
}
 
alert(a); // 1
alert(b); // undefined, but not "b is not defined"&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;关于变量&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;大多数讲JavaScript的文章甚至是JavaScript的书通常都会这么说：“声明全局变量的方式有两种，一种是使用&lt;em&gt;var&lt;/em&gt;关键字（在全局上下文中），另外一种是不用&lt;em&gt;var&lt;/em&gt;关键字（在任何位置）”。
而这样的描述是错误的。要记住的是：&lt;br/&gt;&lt;em&gt;使用var关键字是声明变量的唯一方式&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;如下赋值语句：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;a = 10;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;仅仅是在全局对象上创建了新的属性（而不是变量）。“不是变量”并不意味着它无法改变，它是ECMAScript中变量的概念（它之后可以变为全局对象的属性，因为VO(globalContext) === global，还记得吧？）&lt;/p&gt;

&lt;p&gt;不同点如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;alert(a); // undefined
alert(b); // "b" is not defined
 
b = 10;
var a = 20;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;接下来还是要谈到VO和在不同阶段对VO的修改（进入上下文阶段和执行代码阶段）：&lt;br/&gt;
进入上下文：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;VO = {
  a: undefined
};&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们看到，这个阶段并没有任何“b”，因为它不是变量，“b”在执行代码阶段才出现。（但是，在我们这个例子中也不会出现，因为在“b”出现前就发生了错误）&lt;/p&gt;

&lt;p&gt;将上述代码稍作改动：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;alert(a); // undefined, we know why
 
b = 10;
alert(b); // 10, created at code execution
 
var a = 20;
alert(a); // 20, modified at code execution&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里关于变量还有非常重要的一点：与简单属性不同的是，变量是不能删除的{DontDelete},这意味着要想通过&lt;strong&gt;delete&lt;/strong&gt;操作符来删除一个变量是不可能的。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;a = 10;
alert(window.a); // 10
 
alert(delete a); // true
 
alert(window.a); // undefined
 
var b = 20;
alert(window.b); // 20
 
alert(delete b); // false
 
alert(window.b); // still 20&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;但是，这里有个例外，就是“eval”执行上下文中，是可以删除变量的：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;eval('var a = 10;');
alert(window.a); // 10
 
alert(delete a); // true
 
alert(window.a); // undefined&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;利用某些debug工具，在终端测试过这些例子的童鞋要注意了：其中Firebug也是使用了eval来执行终端的代码。因此，这个时候var也是可以删除的。&lt;/p&gt;

&lt;h2&gt;实现层的特性：__parent__属性&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;正如此前介绍的，标准情况下，是无法直接访问活跃对象的。然而，在某些实现中，比如知名的SpiderMonkey和Rhino,函数有个特殊的属性__parent__，
该属性是对该函数创建所在的活跃对象的引用（或者全局变量对象）。&lt;/p&gt;

&lt;p&gt;如下所示（SpiderMonkey,Rhino）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var global = this;
var a = 10;
 
function foo() {}
 
alert(foo.__parent__); // global
 
var VO = foo.__parent__;
 
alert(VO.a); // 10
alert(VO === global); // true&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子中，可以看到函数&lt;em&gt;foo&lt;/em&gt;是在全局上下文中创建的，相应的，它的__parent__属性设置为全局上下文的变量对象，比如说：全局对象。&lt;/p&gt;

&lt;p&gt;然而，在SpiderMonkey中以相同的方式获取活跃对象是不可能的：不同的版本表现都不同，内部函数的__parent__属性会返回null或者全局对象。&lt;br/&gt;
在Rhino中，以相同的方式获取活跃对象是允许的：&lt;/p&gt;

&lt;p&gt;如下所示（Rhino）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;var global = this;
var x = 10;
 
(function foo() {
 
  var y = 20;
 
  // the activation object of the "foo" context
  var AO = (function () {}).__parent__;
 
  print(AO.y); // 20
 
  // __parent__ of the current activation
  // object is already the global object,
  // i.e. the special chain of variable objects is formed,
  // so-called, a scope chain
  print(AO.__parent__ === global); // true
 
  print(AO.__parent__.x); // 10
 
})();&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;总结&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;本文，我们介绍了与执行上下文相关的对象。希望，本文能够对大家有所帮助，同时也希望本文能够起到解惑的作用。&lt;/p&gt;

&lt;h2&gt;扩展阅读&lt;/h2&gt;

&lt;hr&gt;&lt;ul&gt;&lt;li&gt;10.1.3 —— &lt;a href="http://bclary.com/2004/11/07/#a-10.1.3" target="_blank"&gt;变量初始化&lt;/a&gt;  &lt;/li&gt;
&lt;li&gt;10.1.5 —— &lt;a href="http://bclary.com/2004/11/07/#a-10.1.5" target="_blank"&gt;全局对象&lt;/a&gt;  &lt;/li&gt;
&lt;li&gt;10.1.6 —— &lt;a href="http://bclary.com/2004/11/07/#a-10.1.6" target="_blank"&gt;活跃对象&lt;/a&gt;  &lt;/li&gt;
&lt;li&gt;10.1.8 —— &lt;a href="http://bclary.com/2004/11/07/#a-10.1.8" target="_blank"&gt;参数对象&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><link>http://blog.goddyzhao.me/post/11141710441</link><guid>http://blog.goddyzhao.me/post/11141710441</guid><pubDate>Fri, 07 Oct 2011 11:26:00 -0400</pubDate><category>javascript</category><category>JavaScript Internal</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>安装node后node的js代码都跑哪里去了</title><description>&lt;p&gt;&lt;a href="http://nodejs.org" target="_blank"&gt;Node&lt;/a&gt;的&lt;a href="https://github.com/joyent/node" target="_blank"&gt;源代码&lt;/a&gt;有两部分组成：&lt;br/&gt;
1.  C/C++代码（包括Google的V8引擎代码）： 位于src目录下&lt;br/&gt;
2.  JavaScript代码：node.js位于src目录下，其余全部位于lib目录下，&lt;strong&gt;而且数量很多&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;但是，奇怪的是，按照node wiki上的&lt;a href="https://github.com/joyent/node/wiki/Installation" target="_blank"&gt;安装文档&lt;/a&gt;安装好Node之后，却发现在安装目录中并没有任何JavaScript文件，于是我就很想知道为什么（我犯贱）&lt;/p&gt;

&lt;p&gt;因为node运行的时候这些js代码都是需要的，而且基本是node的核心代码，&lt;br/&gt;
所以&lt;strong&gt;大致猜测&lt;/strong&gt;：JS文件在make的过程中可能被翻译然后合并到C代码中一同编译成了二进制代码&lt;br/&gt;
通过粗略地看了下node的几个和安装相关的脚本文件后，大概知道了真正的原因，如下：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;首先，回忆下node的安装过程&lt;/strong&gt;：&lt;br/&gt;
1.  ./configure &amp;#8212;prefex=node_install_path: 配置node安装，指定安装目录&lt;br/&gt;
2.  make：编译代码&lt;br/&gt;
3.  make install: 安装&lt;/p&gt;

&lt;p&gt;那么，按照这个顺序首先去看下&lt;strong&gt;configure&lt;/strong&gt;文件，发现其代码非常少：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;if [ ! -z "`echo $CC | grep ccache`" ]; then
  echo "Error: V8 doesn't like ccache. Please set your CC env var to 'gcc'"
  echo "  (ba)sh: export CC=gcc"
  exit 1
fi

CUR_DIR=$PWD

#possible relative path
WORKINGDIR=`dirname $0`
cd "$WORKINGDIR"
#abs path
WORKINGDIR=`pwd`
cd "$CUR_DIR"

"${WORKINGDIR}/tools/waf-light" --jobs=1 configure $*

exit $?&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;没啥发现，继续去看make执行的&lt;strong&gt;makefile&lt;/strong&gt;，makefile里面一堆代码，没怎么看懂，感觉没啥发现&lt;/p&gt;

&lt;p&gt;这个时候，心想这个思路不好，不一定能够找到原因，于是决定换个思路：&lt;br/&gt;
既然JavaScript文件都是在lib目录下，那直接搜索lib看看哪些文件用到了，这个方法果然奏效，搜索发现根目录下的&lt;strong&gt;node.gyp&lt;/strong&gt;文件非常可疑，&lt;br/&gt;
其文件内容就感觉是个build文件，其中有如下这段代码：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;'library_files': [
      'src/node.js',
      'lib/_debugger.js',
      'lib/_linklist.js',
      'lib/assert.js',
      'lib/buffer.js',
      ...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里就是把所有JavaScript文件都罗列出来了，这时已经看到光明了，在此文件中找&lt;strong&gt;library_files&lt;/strong&gt;这个变量，又发现如下代码：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;'actions': [
        {
          'action_name': 'node_js2c',

          'inputs': [
            './tools/js2c.py',
            '&amp;lt;@(library_files)',
          ],

          'outputs': [
            '&amp;lt;(SHARED_INTERMEDIATE_DIR)/node_natives.h',
          ],
          ...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;猜测就是一个build任务，其输入源就是这些JS文件，输出是一个C的头文件，这里有个文件极其可疑&lt;strong&gt;./tools/js2c.py&lt;/strong&gt;，继续追看该文件&lt;br/&gt;
终于找到真凶了：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# This is a utility for converting JavaScript source code into C-style
# char arrays. It is used for embedded JavaScript code in the V8
# library.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这三行注释就大致等于告诉我们了，node在安装过程中会把JavaScript代码翻译成C语言风格的字符数组，在V8引擎中运行，这下终于豁然开朗了。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;说明：&lt;/strong&gt;以上过程纯属自己YY，仅供大家一起YY。&lt;/p&gt;</description><link>http://blog.goddyzhao.me/post/10751120779</link><guid>http://blog.goddyzhao.me/post/10751120779</guid><pubDate>Tue, 27 Sep 2011 21:41:00 -0400</pubDate><category>nodejs</category><category>diveinto</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>执行上下文（Execution Context）</title><description>&lt;h2&gt;说明&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;此文译自&lt;a href="http://dmitrysoshnikov.com/" target="_blank"&gt;Dmitry A.Soshnikov&lt;/a&gt; 的文章&lt;a href="http://dmitrysoshnikov.com/ecmascript/chapter-1-execution-contexts/" target="_blank"&gt;Execution Context&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;概要&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;本文将向大家介绍ECMAScript的执行上下文以及相关的可执行代码类型。&lt;/p&gt;

&lt;h2&gt;定义&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;每当控制器到达ECMAScript可执行代码的时候，控制器就进入了一个执行上下文。&lt;br/&gt;
执行上下文（简称：EC）是个抽象的概念，ECMA-262标准中用它来区分不同类型的可执行代码。&lt;/p&gt;

&lt;p&gt;标准中并没有从技术实现的角度来定义执行上下文的具体结构和类型；这是实现标准的ECMAScript引擎所要考虑的问题。&lt;/p&gt;

&lt;p&gt;一系列活动的执行上下文从逻辑上形成一个栈。栈底总是全局上下文，栈顶是当前（活动的）执行上下文。当在不同的执行上下文间切换（退出的而进入新的执行上下文）的时候，栈会被修改（通过压栈或者退栈的形式）。&lt;/p&gt;

&lt;h2&gt;可执行代码类型&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;可执行代码类型和执行上下文相关。有的时候，当提到代码类型的时候，其实就是在说执行上下文。&lt;/p&gt;

&lt;p&gt;举个例子，我们将执行上下文的栈以数组的形式来表示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ECStask = [ ];&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;每次控制器进入一个函数（哪怕该函数被递归调用或者作为构造器），都会发生压栈的操作。内置eval函数工作的时候也不例外。&lt;/p&gt;

&lt;h2&gt;全局代码&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;这类代码是在“程序”级别上被处理的：比如，加载一个外部的js文件或者内联的js代码（被包含在&amp;lt;script&amp;gt;&amp;lt;/script&amp;gt;标签内）。全局代码不包含任何函数体内的代码。&lt;/p&gt;

&lt;p&gt;在初始化的时候（程序开始），ECStack如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ECStack = [
    globalContext
];&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;函数代码&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;一旦控制器进入函数代码（各类函数），就会有新的元素会被压栈到ECStack。要注意的是：实体函数代码并不包括内部函数的代码。如下所示，我们调用一个函数，该函数递归调用自己一次：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(function foo(bar){
    if (bar){

    return;

}

foo(true);
})();&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;之后，ECStack就被修改成如下所示:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;//首先激活foo函数
ECStack = [
    &lt;foo&gt; functionContext
    globalContext
];
//递归激活foo函数
ECStack = [
    &lt;foo&gt; functionContext - recursively
    &lt;foo&gt; functionContext
    globalContext
];&lt;/foo&gt;&lt;/foo&gt;&lt;/foo&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;每次函数返回，退出当前活动的执行上下文时，ECStack就会被执行对应的退栈操作——先进后出——和传统的栈实现一致。同样的，当抛出未捕获的异常时，也会退出一个或者多个执行上下文，ECStack也会做相应的退栈操作。待这些代码完成之后，ECStack中就只剩下一个执行上下文（globalContext）——直到整个程序结束。&lt;/p&gt;

&lt;h2&gt;Eval代码&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;说到eval代码就比较有意思了。这里要提到一个叫做调用上下文的概念，比如：调用eval函数时候的上下文，就是一个调用上下文，eval函数中执行的动作（例如：变量声明或者函数声明）会影响整个调用上下文：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;eval(‘var x = 10’);
(function foo(){
    eval(‘ var y = 20’);
})();
alert(x); // 10
alert(y); // ”y” is not defined&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;ECStack会被修改为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;ECStack = [
    globalContext
];
//eval(‘var x = 10’);
ECStack.push(
    evalContext,
    callingContext: globalContext
);

// eval exited context
ECStack.pop();

//foo function call
ECStack.push(&lt;foo&gt; functionContext);

//eval(‘ var y = 20’);
ECStack.push(
    evalContext,
    callingContext: &lt;foo&gt; functionContext
);

//return from eval
ECStack.pop();

//return from foo
ECStack.pop();&lt;/foo&gt;&lt;/foo&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在1.7以上版本SpiderMonkey的实现中（Firefox，Thunderbird浏览器内置的JS引擎），允许在调用eval函数的时候，将调用上下文作为第二个参数传递给eval函数。因此，如果传入的调用上下文存在的话，就有可能会影响该上下文中原有的私有变量（在该上下文中声明的变量）：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;function foo(){
    var x = 1;
    return function() { alert(x); }
};

var bar = foo();

bar(); // 1
eval(‘x = 2’, bar); //传递上下文，影响了内部变量“var x”
bar(); // 2&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;总结&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;这些基本理论对于后面执行上下文相关的细节（诸如变量对象、作用域链等等）分析是非常必要的。&lt;/p&gt;

&lt;h2&gt;扩展阅读&lt;/h2&gt;

&lt;hr&gt;&lt;p&gt;ECMA-363-3标准文档的对应的章节—— &lt;a href="http://bclary.com/2004/11/07/#a-10" target="_blank"&gt;10. 执行上下文&lt;/a&gt;&lt;/p&gt;</description><link>http://blog.goddyzhao.me/post/10020230352</link><guid>http://blog.goddyzhao.me/post/10020230352</guid><pubDate>Fri, 09 Sep 2011 22:44:00 -0400</pubDate><category>JavaScript</category><category>ECMAScript</category><category>JavaScript Internal</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>npm中本地安装命令行类型的模块是不注册Path的</title><description>&lt;p&gt;首先有必要解释下什么是&lt;strong&gt;命令行（Command Line）类型的模块&lt;/strong&gt;。&lt;br/&gt;
npm的模块一共分为三类：&lt;br/&gt;
1.  &lt;strong&gt;绑定型（Binding）&lt;/strong&gt;：本地模块，C++书写，如&lt;a href="https://github.com/pkrumins/node-png" target="_blank"&gt;node-png&lt;/a&gt;&lt;br/&gt;
2.  &lt;strong&gt;库型（Library）&lt;/strong&gt;：JS书写，直接使用require(&amp;#8216;module&amp;#8217;)这种方式，如&lt;a href="https://github.com/learnboost/Socket.IO-node" target="_blank"&gt;Socket.IO-node&lt;/a&gt;&lt;br/&gt;
3.  &lt;strong&gt;命令行型（Command Line）&lt;/strong&gt;: 以命令行形式使用，如&lt;a href="https://github.com/zpoley/json-command" target="_blank"&gt;json-command&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;显而易见，提供命令行形式调用方式的模块就属于命令行型的。那么如何来申明自己的模块以什么命令调用，调用执行代码又如何制定呢？&lt;/p&gt;

&lt;p&gt;这里就要说到npm模块的核心文件：&lt;strong&gt;package.json&lt;/strong&gt;，此文件是npm模块的配置信息（npm help json可以获取详细信息）。&lt;/p&gt;

&lt;p&gt;其中有一个key就叫&lt;strong&gt;“bin”&lt;/strong&gt;，具体形式为&lt;strong&gt;“{ &amp;#8220;bin&amp;#8221;: {&amp;#8220;command-name&amp;#8221;&amp;#160;: &amp;#8220;command-file&amp;#8221;}}”&lt;/strong&gt;，意思是申明用户可以直接使用&lt;strong&gt;“command-name”&lt;/strong&gt;这个命令，而输入该命令后就会去执行&lt;strong&gt;“command-file”&lt;/strong&gt;这个文件。&lt;br/&gt;
比如json-command这个模块，安装好后就可以看到它的package.json文件中bin配置项为&lt;strong&gt;“{&amp;#8220;bin&amp;#8221;&amp;#160;: {&amp;#8220;json&amp;#8221;&amp;#160;: &amp;#8220;./bin/json.js&amp;#8221;}}”&lt;/strong&gt;。这就申明了命令行名字为&lt;strong&gt;“json”&lt;/strong&gt;，对应地会去执行&lt;strong&gt;“bin目录下的json.js”&lt;/strong&gt;这个文件。&lt;/p&gt;

&lt;p&gt;其次还要解释下npm中安装模块的两种模式：&lt;br/&gt;
1.  &lt;strong&gt;全局模式&lt;/strong&gt;： npm -g install module-name，这种模式模块会被安装在node安装记录的lib所在目录的node_modules文件夹中，全局使用&lt;br/&gt;
2.  &lt;strong&gt;本地模式&lt;/strong&gt;： npm install module-name，这种模式模块只会被安装在当前目录的node_modules文件夹中，非全局使用&lt;/p&gt;

&lt;p&gt;理解了什么叫&lt;strong&gt;“命令行型的模块”&lt;/strong&gt;和&lt;strong&gt;“npm模块的安装模式”&lt;/strong&gt;之后，再来看看什么叫&lt;strong&gt;“npm中本地安装命令行类型的模块是不注册Path的”&lt;/strong&gt;？&lt;br/&gt;
这句话的意思是说：比如像json-command这样的命令行类型的模块，如果是“全局安装”，安装成功之后，就可以直接使用&lt;strong&gt;“json”&lt;/strong&gt;的命令了，而如果是本地模式安装，则没法使用&lt;strong&gt;“json”&lt;/strong&gt;命令。只能手动找到执行文件，然后调用./command-file这样调用。很是不爽！&lt;/p&gt;

&lt;p&gt;后来仔细想了下，npm这样设计也是有道理的，因为命令行的模块，通过命令行使用，一般也都会认为是全局的，就像压缩文件，合并文件之类的命令，一般是不会随着应用的发布而发布的，都只是开发过程中的中间工具。所以，npm对这种类型的模块只有当全局安装的时候才会可以直接使用命令。&lt;/p&gt;

&lt;p&gt;那么，&lt;strong&gt;“npm到底是如何实现全局直接使用，而本地就不能使用的呢？”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;通过查看npm的源码，稍作调试就能发现其中的奥妙了：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;通过npm的package.json配置文件就能知道npm的命令行入口是&lt;strong&gt;“bin/npm.js”&lt;/strong&gt;  &lt;/li&gt;
&lt;li&gt;npm.js有会去调用&lt;strong&gt;“模块根目录的npm.js”&lt;/strong&gt;，并会调用相关的命令，依据来自如下代码：  &lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;&lt;code&gt;npm = require("../npm")&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;npm.load(conf, function (er) {
  if (er) return errorHandler(er)
  npm.commands[npm.command](npm.argv, errorHandler)
})&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;“npm.commands&amp;#8230;(npm.argv,errorHandler)”&lt;/strong&gt;是一行典型的command模式代码，npm.js中的commands对象肯定维护了所有command的列表。依据来自如下代码：&lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;&lt;code&gt;var cmd = require(__dirname+"/lib/"+a+".js")&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;那么install命令就会去调用&lt;strong&gt;“lib/install.js”&lt;/strong&gt;的install方法，install方法会去做一些&lt;strong&gt;模块查询&lt;/strong&gt;，&lt;strong&gt;模块依赖关系处理&lt;/strong&gt;，&lt;strong&gt;模块下载&lt;/strong&gt;，&lt;strong&gt;模块安装&lt;/strong&gt;等工作，之后会去区分本地模式和全局模式，依据来自如下代码：&lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;&lt;code&gt;var fn = npm.config.get("global") ? installMany : installManyTop&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;之后会去调用lib/build.js中的build函数，依据来自于如下代码：&lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;&lt;code&gt;npm.commands.build([where], false, true, function (er) {
      return cb_(er, d)
    })&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;然后，再看看build函数的申明中，其&lt;strong&gt;第二个参数&lt;/strong&gt;正是表示模块是否全局安装，依据来自如下代码：&lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;&lt;code&gt;function build (args, global, didPre, didRB, cb) {&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;&lt;li&gt;最后，global参数被赋予给gtop变量，最后判断是否全局，如果是则注册Path否则直接调用回调函数返回，依据来自如下代码：&lt;/li&gt;
&lt;/ul&gt;&lt;pre&gt;&lt;code&gt;if (er || !gtop) return cb(er)
var dest = path.resolve(binRoot, b)
     , src = path.resolve(folder, pkg.bin[b])
     , out = npm.config.get("parseable")
                ? dest + "::" + src + ":BINFILE"
                : dest + " -&amp;gt; " + src&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里很清楚，如果&lt;strong&gt;“!gtop”&lt;/strong&gt;，即本地模式就直接调用cb并返回，否则，就去注册Path，其中&lt;strong&gt;“dest-&amp;gt;src”&lt;/strong&gt;会发现很眼熟，通常全局安装好一个命令行模块之后，都会显示这行log。这行代码其实就做了如下这件事情：&lt;br/&gt;&lt;strong&gt;将命令执行文件json.js link到node/bin/json命令中&lt;/strong&gt;&lt;br/&gt;
其中node的bin路径命令已经被加入到PATH中了，这样输入json的时候，系统自动去PATH中查找对应的执行文件，这样就能够找到了！&lt;/p&gt;

&lt;hr&gt;&lt;p&gt;&lt;strong&gt;参考文献&lt;/strong&gt;&lt;br/&gt;
*  &lt;a href="http://howtonode.org/how-to-module" target="_blank"&gt;&lt;a href="http://howtonode.org/how-to-module" target="_blank"&gt;http://howtonode.org/how-to-module&lt;/a&gt;&lt;/a&gt;&lt;br/&gt;
*  &lt;a href="https://github.com/isaacs/npm" target="_blank"&gt;&lt;a href="https://github.com/isaacs/npm" target="_blank"&gt;https://github.com/isaacs/npm&lt;/a&gt;&lt;/a&gt;&lt;/p&gt;</description><link>http://blog.goddyzhao.me/post/9835631010</link><guid>http://blog.goddyzhao.me/post/9835631010</guid><pubDate>Mon, 05 Sep 2011 11:38:00 -0400</pubDate><category>nodejs</category><category>npm</category><category>Module</category><category>install mode</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>Aliases of npm commands</title><description>&lt;img src="http://25.media.tumblr.com/tumblr_lr084kClWD1r28q3zo1_r1_400.jpg"/&gt;&lt;br/&gt;&lt;br/&gt;&lt;p&gt;Aliases of npm commands&lt;/p&gt;</description><link>http://blog.goddyzhao.me/post/9790782571</link><guid>http://blog.goddyzhao.me/post/9790782571</guid><pubDate>Sun, 04 Sep 2011 11:24:00 -0400</pubDate><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>如何在Eclipse中自定义Bundle</title><description>&lt;p&gt;写代码的时候，我经常有这样的需求：  &lt;strong&gt;希望把经常要写的代码做成模板，然后，像linux那样，输入部分字符，按下Tab就帮忙自动补齐&lt;/strong&gt;&lt;br/&gt;
以前用Netbeans和TextMate(Bundle)的时候，就很喜欢这样。&lt;br/&gt;
现在，在&lt;a href="http://eclipse.org" title="Eclipse官方网站" target="_blank"&gt;Eclipse&lt;/a&gt; + &lt;a href="http://www.aptana.com" title="Aptana官方网站" target="_blank"&gt;Aptana&lt;/a&gt;的环境下，也希望能够有这样的感受。于是，摸索出了自定义Bundle的方法，如下所示：&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;说明&lt;/strong&gt;： “&lt;strong&gt;Eclipse3.7环境下，&lt;/strong&gt;”&lt;br/&gt;&lt;strong&gt;场景&lt;/strong&gt;： 我经常在文件最上面要写上作者信息比如：goddyzhao&amp;lt;goddy128@gmail.com&amp;gt;&lt;br/&gt;
所以，我打算把这个作为一个snippet，下次直接输入&amp;#8221;auinfo&amp;#8221;,然后tab就帮我自动补齐。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;具体步骤如下:&lt;/strong&gt;&lt;br/&gt;
1. 点击&amp;#8221;&lt;strong&gt;Commands&lt;/strong&gt;&amp;#8221; -&amp;gt; &amp;#8220;&lt;strong&gt;Text&lt;/strong&gt;&amp;#8221; -&amp;gt; &amp;#8220;&lt;strong&gt;Edit this bundle&lt;/strong&gt;&amp;#8221;&lt;br/&gt;
    这个时候，在Eclipse的Project View中就能够看到一个项目，名字就叫“&lt;strong&gt;Text&lt;/strong&gt;”&lt;br/&gt;
2. 打开项目中&lt;strong&gt;snippets&lt;/strong&gt;目录下的&lt;strong&gt;snippets.rb&lt;/strong&gt;文件，文件内容都描述了一条条的snippet&lt;br/&gt;
    其格式如下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;snippet 'Lorem ipsum' do |s|
    s.trigger = 'lorem'
    s.expansion = 'Lorem ipsum dolor sit '
end&lt;/code&gt;&lt;/pre&gt;

&lt;ol&gt;&lt;li&gt;写法非常简单，一看就明白了。于是，跟着写上我自己的：  &lt;/li&gt;
&lt;/ol&gt;&lt;pre&gt;&lt;code&gt;snippet 'Author Information' do |s|
    s.trigger = 'auinfo'
    s.expansion = 'goddyzhao&lt;goddy128&gt;'
end&lt;/goddy128&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;ol&gt;&lt;li&gt;重启Eclipse，就OK了！&lt;/li&gt;
&lt;/ol&gt;</description><link>http://blog.goddyzhao.me/post/9730770336</link><guid>http://blog.goddyzhao.me/post/9730770336</guid><pubDate>Fri, 02 Sep 2011 22:31:00 -0400</pubDate><category>eclipse</category><category>aptana</category><category>snippet</category><category>JavaScript</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>Spawn a process from a build that lives longer than the build itself in Hudson(Jenkins)</title><description>&lt;p&gt;在做持续集成的时候，可能会有这样的需求：&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;在Build过程中，需要启动一个服务器，用来监听服务请求等等&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;这样的情况，通常build进程本身会启动服务器，然后服务器就一致处于监听状态。&lt;/div&gt;

&lt;div&gt;这样就带来了一个问题：&lt;strong&gt;由于服务器一直处在监听状态，因此，build进程一直无法结束，就导致了build就被hold住了，永远结束不了。&lt;/strong&gt;&lt;/div&gt;

&lt;p&gt;&lt;br/&gt;&lt;/p&gt;

&lt;div&gt;举个例子：&lt;/div&gt;

&lt;div&gt;假设Build过程中需要调用一个ant任务，该任务就负责启动一个jetty服务器，然后一直监听着：&lt;/div&gt;

&lt;div&gt;ant runserver&lt;/div&gt;

&lt;div&gt;这个时候就会把build过程hold住了。&lt;/div&gt;

&lt;p&gt;&lt;br/&gt;&lt;/p&gt;

&lt;div&gt;如何解决这个问题呢？第一反应就是build过程中，想办法调用一个shell脚本，然后利用linux/unix的后台运行能力来解决这个问题。（利用nohup 或者 &amp;amp;）：&lt;/div&gt;

&lt;div&gt;
&lt;ol&gt;&lt;li&gt;创建一个shell脚本：
#!/bin/bash
nohup ant runserver &amp;amp;&lt;/li&gt;
    &lt;li&gt;在jenkins中，build过程中创建一个shell build step，然后直接调用这个shell脚本&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;&lt;br/&gt;&lt;/p&gt;

&lt;div&gt;感觉好像解决问题了，但是，jenkins会报错：“&lt;strong&gt;descriptor leak&lt;/strong&gt;”&lt;/div&gt;

&lt;div&gt;为什么会这样呢？官方给出了这个问题的&lt;a title="jenkins wiki for spawning processes from build which lives longer than the build itself" href="https://wiki.jenkins-ci.org/display/JENKINS/Spawning+processes+from+build" target="_blank"&gt;解释&lt;/a&gt;：主要原因是jenkins和子进程之间是通过管道（stdin/stdout/stderr）来连接的。这样，jenkins就可以抓到子进程的输出，将它打印在自己的进程中。也正是由于这样，jenkins会一直等待子进程发送EOF信号，它才会认为子进程结束了。而利用linux的后台运行能力，主进程等不到这个EOF，于是就导致了“descriptor leak”的问题。&lt;/div&gt;

&lt;p&gt;&lt;br/&gt;&lt;/p&gt;

&lt;div&gt;难道我们就束手无策了吗？其实，官方同样给出了不同系统的解决方案，其中linux就是利用&lt;a title="deamonize website" href="http://software.clapper.org/daemonize/" target="_blank"&gt; daemonize&lt;/a&gt;工具。这个工具主要可以把程序当成unix的daemon程序来处理，它会自动去关闭所有打开的文件描述符（file descriptor）。&lt;/div&gt;

&lt;div&gt;有了它之后，jenkins的build进程就能够正常接收到EOF信号，并且正确的认为子进程已经结束了。然后，就可以把整个build进程结束了。具体做法就是：&lt;/div&gt;

&lt;div&gt;
&lt;ol&gt;&lt;li&gt;在shell脚本中，直接调用：
daemonize -o output-file absolute_path_of_the_shell_file&lt;/li&gt;
&lt;/ol&gt;
这里要注意的是：&lt;strong&gt;daemonize的程序必须要给出绝对路径 &lt;/strong&gt;

&lt;/div&gt;</description><link>http://blog.goddyzhao.me/post/9338574551</link><guid>http://blog.goddyzhao.me/post/9338574551</guid><pubDate>Wed, 24 Aug 2011 13:18:53 -0400</pubDate><category>Hudson</category><category>Linux</category><category>Continuous Intergration</category><dc:creator>zhaojinglizhengjie</dc:creator></item><item><title>使用Nodejs来编写Hudson（Jenkins）构建代码</title><description>&lt;h1&gt;Update：&lt;a title="jenkins website" href="http://jenkins-ci.org/" target="_blank"&gt;Jenkins&lt;/a&gt;的使用方式和如下一致！&lt;/h1&gt;

&lt;p&gt;Hudson中默认的Build方式有四种，分别是：&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;Shell脚本&lt;/li&gt;
    &lt;li&gt;Windows的批处理命令&lt;/li&gt;
    &lt;li&gt;调用Maven2任务&lt;/li&gt;
    &lt;li&gt;调用Ant任务&lt;/li&gt;
    &lt;li&gt;调用Maven3任务&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;如下图所示：&lt;/div&gt;

&lt;div&gt;&lt;img class="alignnone" title="Hudson default build type" src="http://farm7.static.flickr.com/6185/6065539495_d129638678_z.jpg" alt="" width="329" height="142"/&gt;&lt;/div&gt;

&lt;div&gt;这5种方式，除了shell和ant之外，其他我都很生疏。作为前端工程师，俺最熟悉的还是JavaScript，所以，自然而然就想到了，要是能够用JavaScript写构建代码就爽多了！&lt;/div&gt;

&lt;div&gt;Hudson强大的插件系统解决了我的问题，通过安装nodejs的hudson插件，就可以实现我的需求了，具体步骤如下：&lt;/div&gt;

&lt;div&gt;
&lt;ol&gt;&lt;li&gt;安装nodejs插件
“点击” Manage Hudson -&amp;gt; Manage Plugins -&amp;gt; 选择Available标签 -&amp;gt; 找到nodejs plugin
&lt;img class="alignnone" title=" nodejs plugin for hudson" src="http://farm7.static.flickr.com/6074/6065570925_754f19d4cc_z.jpg" alt="" width="473" height="97"/&gt;
点击“Install”进行安装&lt;/li&gt;
    &lt;li&gt;安装完成之后，hudson会自动重启，然后，再进入配置job界面，这时候就会发现build类型多了一种
&lt;img class="alignnone" title="nodejs build type in hudson" src="http://farm7.static.flickr.com/6195/6066139228_1b2edb877c_z.jpg" alt="" width="316" height="158"/&gt;&lt;/li&gt;
    &lt;li&gt;选择“Execute NodeJS script”之后，输入如下build代码
&lt;img class="alignnone" title="use nodejs to build code in hudson" src="http://farm7.static.flickr.com/6067/6066156878_4a927661fb_z.jpg" alt="" width="438" height="130"/&gt;&lt;/li&gt;
    &lt;li&gt;点击“Save”，然后“Build Now”，成功的话，通过“Console Output”就可以看到如下结果
&lt;img class="alignnone" title="hudson build result" src="http://farm7.static.flickr.com/6210/6065624727_e8cb4fb351_z.jpg" alt="" width="382" height="263"/&gt;&lt;/li&gt;
&lt;/ol&gt;
至此，就成功使用nodejs来书写构建代码了！

&lt;/div&gt;</description><link>http://blog.goddyzhao.me/post/9338524305</link><guid>http://blog.goddyzhao.me/post/9338524305</guid><pubDate>Wed, 24 Aug 2011 13:17:16 -0400</pubDate><category>nodejs</category><category>Continuous Intergration</category><category>Hudson</category><category>Jenkins</category><dc:creator>zhaojinglizhengjie</dc:creator></item></channel></rss>

