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

<channel>
	<title>10V &#187; javascript 闭包</title>
	<atom:link href="http://www.smartwei.com/tag/javascript-%e9%97%ad%e5%8c%85/feed" rel="self" type="application/rss+xml" />
	<link>http://www.smartwei.com</link>
	<description>干一行，爱一行</description>
	<lastBuildDate>Fri, 13 Jan 2012 01:22:37 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>《javascript精粹》读书笔记 – 函数</title>
		<link>http://www.smartwei.com/javascript-good-part-note-function.html</link>
		<comments>http://www.smartwei.com/javascript-good-part-note-function.html#comments</comments>
		<pubDate>Fri, 14 Jan 2011 01:49:19 +0000</pubDate>
		<dc:creator>10V</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[javascript 闭包]]></category>
		<category><![CDATA[javascript精萃]]></category>

		<guid isPermaLink="false">http://www.smartwei.com/?p=305</guid>
		<description><![CDATA[记录《javascript精粹》中“函数”这一章节的知识点]]></description>
			<content:encoded><![CDATA[<p>1. 当实际参数的个数与形式参数的个数不匹配时，不会导致错误。<br />
如果实际参数值过多了，超出的参数值将被忽略；如果实际的参数值过少，缺失的值将被替换为undefined。</p>
<p>2. 在javascript中一共有四种调用模式： 方法调用模式、函数调用模式、构造器调用模式和apply调用模式。这些模式在如何初始化关键参数this上存在差异。<br />
a.方法调用模式：<br />
当一个函数被保存为对象的一个属性时，我们称它为一个方法。方法调用模式可以使用this去访问对象，所以它能够从对象中取值或修改对象。<br />
b.函数调用模式：<br />
当一个函数并非一个对象的属性时，它被当作一个函数来调用。当函数以此模式调用时，this被绑定到全局变量，这是语言设计上的一个错误。（正确的情况：当内部函数被调用时，this应该仍然被绑定到外部函数的this变量。）解决方法：如果该方法定义一个变量并给他赋值为this,那么内部函数就可以通过那个变量访问到this,按照约定，给那个变量命名为 that：<br />
[js]<br />
	//给 myObject增加一个double方法<br />
	myObject.double = function()<br />
	{<br />
		var that = this; //解决方法</p>
<p>		var helper = function()<br />
		{<br />
			that.value = add(that.value, that.value);<br />
		}</p>
<p>		helper(); //以函数的形式调用helper。<br />
	}</p>
<p>	//以方法的形式调用double<br />
	myObject.double();<br />
[/js]<br />
c.构造器调用模式：<br />
在一个函数前面带上new来调用，那么将创建一个隐藏连接到该函数的prototype成员的新对象，同时this将会被绑定到那个新对象上。<br />
[js]<br />
	//创建一个名为Quo的构造函数。它构造一个带有status属性的对象<br />
	var Quo = function(String)<br />
	{<br />
		this.status = String;<br />
	}</p>
<p>	//给Quo的所有实例提供一个名为get_status的公共方法<br />
	Quo.prototype.get_status = function()<br />
	{<br />
		return this.status;<br />
	}</p>
<p>	//构造一个Quo实例<br />
	var myQuo = new Quo(&quot;confused&quot;); //构造器模式调用<br />
	document.writeln(myQuo.get_status());<br />
[/js]<br />
按照约定，构造器函数保存在以大写格式命名的变量里。<br />
d.Apply调用模式:<br />
apply方法接受两个参数，第一个是将被绑定给this的值，第二个就是参数数组。它让我们构建一个参数数组并用其去调用函数。<br />
[js]<br />
	//构造一个Quo实例<br />
	var myQuo = new Quo(&quot;confused&quot;); //构造器模式调用<br />
	document.writeln(myQuo.get_status());</p>
<p>	//构建一个包含两个数字的数组，并将他们相加<br />
	add = function(a, b)<br />
	{<br />
		return a+b;<br />
	}</p>
<p>	var anArray = [3, 4];<br />
	var sum = add.apply(null, anArray);  //7</p>
<p>	//构建一个包含status成员的对象<br />
	var statusObject = {<br />
		status: &quot;A-OK&quot;<br />
	};</p>
<p>	//statusObject并没有继承自Quo.prototype,但我们可以再statusObject上调用<br />
	//get_status方法，尽管statusObject并没有一个名为get_status的方法。<br />
	var status = Quo.prototype.get_status.apply(statusObject);<br />
[/js]</p>
<p>3. 参数：<br />
当一个函数被调用时，会有一个默认的参数，就是 arguments “数组”。<br />
[js]<br />
	//构造一个将很多值相加的函数</p>
<p>	//注意该函数内部定义的变量sum不会与函数外部定义的sum产生冲突。<br />
	//该函数只能看到内部的那个变量。<br />
	var sum = function()<br />
	{<br />
		var i, sum=0;<br />
		for(i = 0; i &lt; arguments.length; i+=1)<br />
		{<br />
			sum += arguments[i];<br />
		}<br />
		return sum;<br />
	};</p>
<p>	document.writeln(sum(4, 8, 15, 16, 23, 42)); //108<br />
[/js]<br />
注：arguments并不是一个真正的数组。它知识一个“类似数组”的对象。arguments用友一个length属性，但它缺少所有的数组方法。</p>
<p>4. 返回:<br />
一个函数总是会返回一个值。如果没有置顶返回值，则返回undefined。<br />
如果函数以构造器方式被调用，且返回值不是一个对象，则返回this(该新对象)。</p>
<p>5.给类型增加方法：<br />
[js]<br />
	Function.prototype.method = function(name, func)<br />
	{<br />
		this.prototype[name] = func;<br />
		return this;<br />
	}</p>
<p>	//给Number添加一个integer方法，来提取数字钟的整数部分<br />
	//根据正负来判断是使用Math.ceil还是Math.floor<br />
	Number.method(&#8216;integer&#8217;, function(){<br />
	 return Math[this &lt; 0 ? 'ceil' : 'floor'](this);<br />
	});</p>
<p>	document.writeln((-10/3).integer()); //-3</p>
<p>	//去掉字符串两端空白的方法<br />
	String.method(&#8216;trim&#8217;, function()<br />
	{<br />
		return this.replace(/^\s+|\s+$/g, ”);<br />
	});<br />
[/js]<br />
基本类型的原型是公共结构，所以在类库混用时务必小心。一个保险的做法就是只在确定没有该方法时再添加：<br />
[js]<br />
	//有条件的增加一个方法</p>
<p>	Function.prototype.method = function(name, func)<br />
	{<br />
		if(!this.prototype[name])<br />
		{<br />
			this.prototype[name] = func;<br />
			return this;<br />
		}<br />
	};<br />
[/js]</p>
<p>6.闭包<br />
理解内部函数能访问外部函数的实际变量而无需<strong>复制</strong><br />
[js]<br />
//构造一个函数，用错误的方式给一个数组中的借点设置事件处理程序<br />
//当点击一个节点时，按照预想应该弹出一个对话框显示节点的序号<br />
//但他总是会显示节点的数目</p>
<p>var add_the_handlers = function(nodes) {<br />
	var i;<br />
	for (i = 0; i &lt; nodes.length; i += 1) {<br />
			nodes[i].onclick = function(e) {<br />
				alert(i);<br />
			}<br />
	}<br />
}<br />
[/js]<br />
add_the_handlers函数的目的是给每个事件处理器一个唯一值（i）。它未能达到目的的原因是事件处理器函数绑定了变量i，而不是<strong>函数在构造时的变量i的值</strong>。<br />
正确的写法：<br />
[js]<br />
//点击一个节点，将会弹出一个对话框显示节点的序号<br />
var add_the_handlers = function (nodes) {<br />
	var i;<br />
	for (i = 0; i &lt; nodes.length; i +=1 ) {<br />
		nodes[i].onclick = function (i) {<br />
			return funtion (e) {<br />
				return alert(e);<br />
			} ;<br />
		}(i);<br />
	}<br />
}<br />
[/js]<br />
现在，定义了一个函数并立即传递i进去执行， 而不是把一个函数赋值给onclick。那个函数将返回一个事件处理器函数。这个事件处理器函数绑定的是传递进去的i的值，而不只定义在add_the_handlers函数里的i的值。那个被返回的函数被复制给onclick。</p>
<p>7.javascript的单例模式<br />
Javascript的单例就是用对象字面量表示法创建的对象，对象的属性值可以是数值或函数，并且属性值在该对象的生命周期中不会发生变化。他通常作为<b>工具为程序其他部分提供功能支持</b></p>
<p>8.模块<br />
模块模式的一般形式是：一个定义了<b>私有变量和函数的函数</b>；利用<b>闭包</b>创建可以访问<b>私有变量和函数</b>的特权函数,或者把他们保存到一个可访问的地方。<br />
[js]<br />
String.method(&#8216;deentityify&#8217;, function (){<br />
	var entity = {<br />
		quot: &#8216;&quot;&#8217;,<br />
		lt: &#8216;&lt;&#8217;,<br />
		gt: &#8216;&gt;&#8217;<br />
	}</p>
<p>	return function () {<br />
		return this.replace(/&amp;([^&amp;;]+);/g,<br />
			function (a, b) {<br />
				var r = entity[b];<br />
				return typeof r === &#8216;string&#8217; ? r : a;<br />
		});<br />
	};<br />
}());<br />
[/js]<br />
模块模式利用了函数作用域和闭包来创建绑定对象与私有成员关联，摒弃了全局变量的使用，从而缓解这个JavaScript的最为糟糕的特性之一所带来的影响</p>
]]></content:encoded>
			<wfw:commentRss>http://www.smartwei.com/javascript-good-part-note-function.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>深入理解 JavaScript闭包(closure)</title>
		<link>http://www.smartwei.com/javascript-closure.html</link>
		<comments>http://www.smartwei.com/javascript-closure.html#comments</comments>
		<pubDate>Fri, 02 Jul 2010 03:26:55 +0000</pubDate>
		<dc:creator>10V</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[javascript 闭包]]></category>

		<guid isPermaLink="false">http://www.smartwei.com/?p=272</guid>
		<description><![CDATA[深入理解 JavaScript闭包(closure) - 本文介绍了闭包的基本概念、微观世界、以及应用场景，对于学习javascript闭包的朋友很有帮助]]></description>
			<content:encoded><![CDATA[<p>最近在网上查阅了不少Javascript闭包(closure)相关的资料，写的大多是非常的学术和专业。对于初学者来说别说理解闭包了，就连文字叙述都很难看懂。撰写此文的目的就是用最通俗的文字揭开Javascript闭包的真实面目。</p>
<p><strong>一、什么是闭包？</strong><br />
“官方”的解释是：闭包是一个拥有许多变量和绑定了这些变量的环境的表达式（通常是一个函数），因而这些变量也是该表达式的一部分。<br />
相信很少有人能直接看懂这句话，因为他描述的太学术。其实这句话通俗的来说就是：JavaScript中所有的function都是一个闭包。不过一般来说，嵌套的function所产生的闭包更为强大，也是大部分时候我们所谓的“闭包”。看下面这段代码：<br />
[js]<br />
function a() {<br />
    var i = 0;<br />
    function b() {<br />
        alert(++i);<br />
    }<br />
    return b;<br />
}<br />
var c = a();<br />
c();<br />
[/js]</p>
<p><span id="more-272"></span><br />
这段代码有两个特点：</p>
<ol>
<li>函数b嵌套在函数a内部；</li>
<li>函数a返回函数b。</li>
</ol>
<p>引用关系如图：<br />
<a href="http://www.smartwei.com/javascript-closure.html"><img src="http://www.smartwei.com/wp-content/uploads/2010/07/jsclosure.jpg" alt="jsclosure" title="jsclosure" width="228" height="175" class="aligncenter size-full wp-image-273" /></a></p>
<blockquote style="color:#C00; font-weight:bold"><p>
当函数a的内部函数b被函数a外的一个变量引用的时候，就创建了一个我们通常所谓的“闭包”。
</p></blockquote>
<p>让我们说的更透彻一些。所谓“闭包”，就是在构造函数体内定义另外的函数作为目标对象的方法函数，而这个对象的方法函数反过来引用外层函数体中的临时变量。这使得只要目标 对象在生存期内始终能保持其方法，就能间接保持原构造函数体当时用到的临时变量值。尽管最开始的构造函数调用已经结束，临时变量的名称也都消失了，但在目 标对象的方法内却始终能引用到该变量的值，而且该值只能通这种方法来访问。即使再次调用相同的构造函数，但只会生成新对象和方法，新的临时变量只是对应新 的值，和上次那次调用的是各自独立的。</p>
<p>为了更深刻的理解闭包，下面让我们继续探索闭包的作用和效果。</p>
<p><strong>二、闭包有什么作用和效果？</strong><br />
简而言之，闭包的作用就是在a执行完并返回后，闭包使得Javascript的垃圾回收机制GC不会收回a所占用的资源，因为a的内部函数b的执行需要依赖a中的变量。这是对闭包作用的非常直白的描述，不专业也不严谨，但你一定能看懂。理解闭包需要循序渐进的过程。<br />
在上面的例子中，由于闭包的存在使得函数a返回后，a中的i始终存在，这样每次执行c()，i都是自加1后alert出i的值。</p>
<p>那么我们来想象另一种情况，如果a返回的不是函数b，情况就完全不同了。因为a执行完后，b没有被返回给a的外界，只是被a所引用，而此时a也只会被b引用，因此函数a和b互相引用但又不被外界打扰(被外界引用)，函数a和b就会被GC回收。(关于Javascript的垃圾回收机制将在后面详细介绍)</p>
<p><strong>三、闭包的微观世界</strong><br />
如果要更加深入的了解闭包以及函数a和嵌套函数b的关系，我们需要引入另外几个概念：函数的执行环境(excution context)、活动对象(call object)、作用域(scope)、作用域链(scope chain)。以函数a从定义到执行的过程为例阐述这几个概念。</p>
<ol>
<li>当定义函数a的时候，js解释器会将函数a的作用域链(scope chain)设置为定义a时a所在的“环境”，如果a是一个全局函数，则scope chain中只有window对象。</li>
<li>当执行函数a的时候，a会进入相应的执行环境(excution context)。</li>
<li>在创建执行环境的过程中，首先会为a添加一个scope属性，即a的作用域，其值就为第1步中的scope chain。即a.scope=a的作用域链。</li>
<li>然后执行环境会创建一个活动对象(call object)。活动对象也是一个拥有属性的对象，但它不具有原型而且不能通过JavaScript代码直接访问。创建完活动对象后，把活动对象添加到a的作用域链的最顶端。此时a的作用域链包含了两个对象：a的活动对象和window对象。</li>
<li>下一步是在活动对象上添加一个arguments属性，它保存着调用函数a时所传递的参数。</li>
<li>最后把所有函数a的形参和内部的函数b的引用也添加到a的活动对象上。在这一步中，完成了函数b的的定义，因此如同第3步，函数b的作用域链被设置为b所被定义的环境，即a的作用域。</li>
</ol>
<p>到此，整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给c，又函数b的作用域链包含了对函数a的活动对象的引用，也就是说b可以访问到a中定义的所有变量和函数。函数b被c引用，函数b又依赖函数a，因此函数a在返回后不会被GC回收。</p>
<p>当函数b执行的时候亦会像以上步骤一样。因此，执行时b的作用域链包含了3个对象：b的活动对象、a的活动对象和window对象，如下图所示：<br />
<a href="http://www.smartwei.com/javascript-closure.html"><img src="http://www.smartwei.com/wp-content/uploads/2010/07/jsclosure-scopechain.jpg" alt="jsclosure-scopechain" title="jsclosure-scopechain" width="500" height="180" class="aligncenter size-full wp-image-274" /></a><br />
如图所示，当在函数b中访问一个变量的时候，搜索顺序是：</p>
<ol>
<li>先搜索自身的活动对象，如果存在则返回，如果不存在将继续搜索函数a的活动对象，依次查找，直到找到为止。</li>
<li>如果函数b存在prototype原型对象，则在查找完自身的活动对象后先查找自身的原型对象，再继续查找。这就是Javascript中的变量查找机制。</li>
<li>如果整个作用域链上都无法找到，则返回undefined。</li>
</ol>
<p>小结，本段中提到了两个重要的词语：函数的定义与执行。文中提到函数的作用域是在定义函数时候就已经确定，而不是在执行的时候确定（参看步骤1和3）。用一段代码来说明这个问题：<br />
[js]<br />
function f(x) {<br />
    var g = function () { return x; }<br />
    return g;<br />
}<br />
var h = f(1);<br />
alert(h());<br />
[/js]<br />
这段代码中变量h指向了f中的那个匿名函数(由g返回)。</p>
<ul>
<li>假设函数h的作用域是在执行alert(h())确定的，那么此时h的作用域链是：h的活动对象->alert的活动对象 ->window对象。</li>
<li>假设函数h的作用域是在定义时确定的，就是说h指向的那个匿名函数在定义的时候就已经确定了作用域。那么在执行的时候，h的作用域链为：h的活动对象->f的活动对象->window对象。</li>
</ul>
<p>如果第一种假设成立，那输出值就是undefined；如果第二种假设成立，输出值则为1。</p>
<p>运行结果证明了第2个假设是正确的，说明函数的作用域确实是在定义这个函数的时候就已经确定了。</p>
<p><strong>四、闭包的应用场景</strong></p>
<ol>
<li>保护函数内的变量安全。以最开始的例子为例，函数a中i只有函数b才能访问，而无法通过其他途径访问到，因此保护了i的安全性。</li>
<li>在内存中维持一个变量。依然如前例，由于闭包，函数a中i的一直存在于内存中，因此每次执行c()，都会给i自加1。</li>
<li>通过保护变量的安全实现JS私有属性和私有方法（不能被外部访问）推荐阅读：http://javascript.crockford.com/private.html<br />
私有属性和方法在Constructor外是无法被访问的 </li>
</ol>
<p>[js]<br />
function Constructor(&#8230;) {<br />
    var that = this;<br />
    var membername = value;<br />
    function membername(&#8230;) {&#8230;}<br />
}<br />
[/js]<br />
以上3点是闭包最基本的应用场景，很多经典案例都源于此。</p>
<p><strong>五、Javascript的垃圾回收机制</strong><br />
在Javascript中，如果一个对象不再被引用，那么这个对象就会被GC回收。如果两个对象互相引用，而不再被第3者所引用，那么这两个互相引用的对象也会被回收。因为函数a被b引用，b又被a外的c引用，这就是为什么函数a执行后不会被回收的原因。</p>
<p><strong>六、结语</strong><br />
理解JavaScript的闭包是迈向高级JS程序员的必经之路，理解了其解释和运行机制才能写出更为安全和优雅的代码。如果您对本文有任何的建议和疑问，欢迎留言。</p>
<p>作者: <a href="http://www.smartwei.com">石巍</a><br />
原载: <a href="http://www.felixwoo.com/archives/247">Felix Woo &#8211; 深入理解 JavaScript闭包(closure)</a><br />
版权所有，转载时必须以超链接形式注明作者和原始出处及本声明。</p>
<p>本文链接: <a href="http://www.smartwei.com/javascript-closure.html">http://www.smartwei.com/javascript-closure.html</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.smartwei.com/javascript-closure.html/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

