From 4eb4b072da696c287f03bc87af7e598dbaf3547b Mon Sep 17 00:00:00 2001 From: goddyzhao Date: Tue, 31 Jan 2012 11:05:49 +0800 Subject: [PATCH 01/55] append my information and test for the pull request --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 1bdab2b..9f5f446 100644 --- a/README.markdown +++ b/README.markdown @@ -5,7 +5,7 @@ **“JavaScript patterns”中译本** - 《JavaScript 模式》 - 作者:[Stoyan Stefanov](http://www.phpied.com/) -- 翻译:[拔赤](http://jayli.github.com/) +- 翻译:[拔赤](http://jayli.github.com/)、[goddyzhao](http://goddyzhao.me) 偷懒是程序员的优良品质,模式则是先人们总结的偷懒招式。Stoyan Stefanov 的这本书,从 JavaScript 的实际使用场景出发,提炼了不少可以让前端们偷懒的实用招式。模式的探索、创新,将永远是程序员自我提升的一条修炼之道。值得一读。 From 3afe2d1317512b73f43dd6a83c8e903150932058 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 15 Jun 2012 14:05:26 +0800 Subject: [PATCH 02/55] =?UTF-8?q?=E7=BF=BB=E8=AF=91=E5=AE=8C=E2=80=9C?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=87=BD=E6=95=B0=E2=80=9D=E4=B8=80?= =?UTF-8?q?=E8=8A=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 50c2a81..7a6eba8 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -435,13 +435,14 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 // calling as a method spooky.boo(); // "Boo!" spooky.boo(); // "Boo!" - console.log(spooky.boo.property); + console.log(spooky.boo.property); // "properly" - // "properly" // using the self-defined function scareMe(); // Double boo! scareMe(); // Double boo! console.log(scareMe.property); // undefined +从结果来看,当自定义函数被赋值给一个新的变量的时候,这段使用自定义函数的代码的执行结果与我们可能期望的结果并不一样。每当prank()运行的时候,它都弹出“Boo!”。同时它也重写了scareMe()函数,但是prank()自己仍然能够使用之前的定义,包括属性property。在这个函数被作为spooky对象的boo()方法调用的时候,结果也一样。所有的这些调用,在第一次的时候就已经修改了全局的scareMe()的指向,所以当它最终被调用的时候,它的函数体已经被修改为弹出“Double boo”。它也就不能获取到新添加的属性“property”。 + From 3da9522ce82a28d0d532142a59a2c56f53b59b0f Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 15 Jun 2012 18:43:32 +0800 Subject: [PATCH 03/55] =?UTF-8?q?=E7=AC=AC4=E7=AB=A0=20=E7=AB=8B=E5=8D=B3?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E5=87=BD=E6=95=B0=20=E4=B8=80=E8=8A=82?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E5=AE=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/chapter4.markdown b/chapter4.markdown index 7a6eba8..bcb8459 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -445,4 +445,38 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 从结果来看,当自定义函数被赋值给一个新的变量的时候,这段使用自定义函数的代码的执行结果与我们可能期望的结果并不一样。每当prank()运行的时候,它都弹出“Boo!”。同时它也重写了scareMe()函数,但是prank()自己仍然能够使用之前的定义,包括属性property。在这个函数被作为spooky对象的boo()方法调用的时候,结果也一样。所有的这些调用,在第一次的时候就已经修改了全局的scareMe()的指向,所以当它最终被调用的时候,它的函数体已经被修改为弹出“Double boo”。它也就不能获取到新添加的属性“property”。 +## 立即执行函数 +立即执行函数是一种语法模式,它会使函数在定义后立即执行。看这个例子: + + (function () { + alert('watch out!'); + }()); + +这种模式其实本质上只是一个在创建后就被执行的函数表达式(具名或者匿名)。“立即执行函数”这种说法并没有在ECMAScript标准中被定义,但它作为一个名词,有助于我们的描述和讨论。 + +这种模式由以下几个部分组成: + +- 使用函数表达式定义一个函数。(不能使用函数声明。) +- 在最后加入一对括号,这会使函数立即被执行。 +- 把整个函数包裹到一对括号中(只在没有将函数赋值给变量时需要)。 + +下面这种语法也很常见(注意右括号的位置),但是JSLint倾向于第一种: + + (function () { + alert('watch out!'); + })(); + +这种模式很有用,它为我们提供一个作用域的沙箱,可以在执行一些初始化代码的时候使用。想象这样的场景:当页面加载的时候,你需要运行一些代码,比如绑定事件、创建对象等等。所有的这些代码都只需要运行一次,所以没有必要创建一个具名函数。但是这些代码需要一些临时变量,而这些变量在初始化完之后又不会再次用到。显然,把这些变量作为全局变量声明也是不合适的。正因为如此,我们才需要立即执行函数。它可以把你所有的代码包裹到一个作用域里面,而不会暴露任何变量到全局作用域中: + + (function () { + + var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], + today = new Date(), + msg = 'Today is ' + days[today.getDay()] + ', ' + today.getDate(); + + alert(msg); + + }()); // "Today is Fri, 13" + +如果这段代码没有包裹到立即执行函数中,那么变量days,today,msg都会是全局变量,而这仅仅是由初始化代码遗留下来的垃圾。 \ No newline at end of file From 4f429817339d2022fc69d84f8993567b00271073 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 15 Jun 2012 19:35:50 +0800 Subject: [PATCH 04/55] =?UTF-8?q?=E7=AB=8B=E5=8D=B3=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E7=9A=84=E5=87=BD=E6=95=B0=20=E4=B8=80=E8=8A=82=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 101 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 96 insertions(+), 5 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index bcb8459..3d2519a 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -445,15 +445,15 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 从结果来看,当自定义函数被赋值给一个新的变量的时候,这段使用自定义函数的代码的执行结果与我们可能期望的结果并不一样。每当prank()运行的时候,它都弹出“Boo!”。同时它也重写了scareMe()函数,但是prank()自己仍然能够使用之前的定义,包括属性property。在这个函数被作为spooky对象的boo()方法调用的时候,结果也一样。所有的这些调用,在第一次的时候就已经修改了全局的scareMe()的指向,所以当它最终被调用的时候,它的函数体已经被修改为弹出“Double boo”。它也就不能获取到新添加的属性“property”。 -## 立即执行函数 +## 立即执行的函数 -立即执行函数是一种语法模式,它会使函数在定义后立即执行。看这个例子: +立即执行的函数是一种语法模式,它会使函数在定义后立即执行。看这个例子: (function () { alert('watch out!'); }()); -这种模式其实本质上只是一个在创建后就被执行的函数表达式(具名或者匿名)。“立即执行函数”这种说法并没有在ECMAScript标准中被定义,但它作为一个名词,有助于我们的描述和讨论。 +这种模式其实本质上只是一个在创建后就被执行的函数表达式(具名或者匿名)。“立即执行的函数”这种说法并没有在ECMAScript标准中被定义,但它作为一个名词,有助于我们的描述和讨论。 这种模式由以下几个部分组成: @@ -467,7 +467,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 alert('watch out!'); })(); -这种模式很有用,它为我们提供一个作用域的沙箱,可以在执行一些初始化代码的时候使用。想象这样的场景:当页面加载的时候,你需要运行一些代码,比如绑定事件、创建对象等等。所有的这些代码都只需要运行一次,所以没有必要创建一个具名函数。但是这些代码需要一些临时变量,而这些变量在初始化完之后又不会再次用到。显然,把这些变量作为全局变量声明也是不合适的。正因为如此,我们才需要立即执行函数。它可以把你所有的代码包裹到一个作用域里面,而不会暴露任何变量到全局作用域中: +这种模式很有用,它为我们提供一个作用域的沙箱,可以在执行一些初始化代码的时候使用。设想这样的场景:当页面加载的时候,你需要运行一些代码,比如绑定事件、创建对象等等。所有的这些代码都只需要运行一次,所以没有必要创建一个具名函数。但是这些代码需要一些临时变量,而这些变量在初始化完之后又不会再次用到。显然,把这些变量作为全局变量声明也是不合适的。正因为如此,我们才需要立即执行的函数。它可以把你所有的代码包裹到一个作用域里面,而不会暴露任何变量到全局作用域中: (function () { @@ -479,4 +479,95 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 }()); // "Today is Fri, 13" -如果这段代码没有包裹到立即执行函数中,那么变量days,today,msg都会是全局变量,而这仅仅是由初始化代码遗留下来的垃圾。 \ No newline at end of file +如果这段代码没有包裹到立即执行函数中,那么变量days,today,msg都会是全局变量,而这仅仅是由初始化代码遗留下来的垃圾。 + +### 立即执行的函数的参数 + +立即执行的函数也可以接受参数,看这个例子: + + // prints: + // I met Joe Black on Fri Aug 13 2010 23:26:59 GMT-0800 (PST) + + (function (who, when) { + + console.log("I met " + who + " on " + when); + + }("Joe Black", new Date())); + +通常的做法,会把全局对象当作一个参数传给立即执行的函数,以保证在函数内部也可以访问到全局对象,而不是使用window对象,这样可以使得代码在非浏览器环境中使用时更具可移植性。 + +值得注意的是,一般来说尽量不要给立即执行的函数传入太多的参数,否则的话,你在阅读代码的时候需要频繁地上下滚动代码。 + +### 立即执行的函数的返回值 + +和其它的函数一样,立即执行的函数也可以返回值,并且这些返回值也可以被赋值给变量: + + var result = (function () { + return 2 + 2; + }()); + +如果省略括号的话也可以达到同样的目的,因为如果需要将返回值赋值给变量,那么括号就不是必需的。省略第一对括号的代码是这样子: + + var result = function () { + return 2 + 2; + }(); + +这种写法更简洁,但是同时也容易造成误解。如果有人在阅读代码的时候忽略了最后的一对括号,那么他会以为result指向了一个函数。而事实上result是指向这个函数运行后的返回值,在这个例子中是4。 + +还有一种写法也可以得到同样的结果: + + var result = (function () { + return 2 + 2; + })(); + +前面的例子中,立即执行的函数返回的是一个基本类型的数值。但事实上,除了基本类型以外,一个立即执行的函数可以返回任意类型的值,包括返回一个函数都可以。你可以利用立即执行的函数的作用域来存储一些私有的数据,这些数据只能在返回的内层函数中被访问。 + +在下面的例子中,立即执行的函数的返回值是一个函数,这个函数会简单地返回res的值,并且它被赋值给了变量getResult。而res就是一个预先计算好的变量,它被存储在立即执行函数的闭包中: + + var getResult = (function () { + var res = 2 + 2; + return function () { + return res; + }; + }()); + +在定义一个对象的属性的时候也可以使用立即执行的函数。设想一下这样的场景:你需要定义一个对象的属性,这个属性在对象的生命周期中都不会改变,但是在定义之前,你需要做一点额外的工作来得到正确的值。这种情况下你就可以使用立即执行的函数来包裹那些额外的工作,然后将它的返回值作为对象属性的值。下面是一个例子: + +var o = { + message: (function () { + var who = "me", + what = "call"; + return what + " " + who; + }()), + getMsg: function () { + return this.message; + } +}; +// usage +o.getMsg(); // "call me" +o.message; // "call me" + + +在这个例子中,o.message是一个字符串,而不是一个函数,但是它需要一个函数在脚本载入后来得到这个属性值。 + +### 好处和用法 + +立即执行的函数应用很广泛。它可以帮助我们做一些不想留下全局变量的工作。所有定义的变量都只是立即执行的函数的本地变量,你完全不用担心临时变量会污染全局对象。 + +> 立即执行的函数还有一些名字,比如“自调用函数”或者“自执行函数”,因为这些函数会在被定义后立即执行自己。 + +这种模式也经常被用到书签代码中,因为书签代码会在任何一个页面运行,所以需要保持全局命名空间干净是一个非常残酷的需求。 + +这种模式也可以让你包裹一些独立的特性到一个封闭的模块中。设想你的页面是静态的,在没有JavaScript的时候工作正常,然后,本着渐进增强的精神,你给页面加入了一点增加代码。这时候,你就可以把你的代码(也可以叫“模块”或者“特性”)到一个立即执行的函数中并且保证页面在有没有它的时候都可以正常工作。然后你就可以加入更多的增强特性,或者对它们进行移除、进行独立测试或者允许用户禁用等等。 + +你可以使用下面的模板定义一段函数代码,我们叫它module1: + + // module1 defined in module1.js + (function () { + + // all the module 1 code ... + + }()); + +套用这个模板,你就可以编写其它的模块。然后在发布到线上的时候,你就可以决定在这个时间节点上哪些特性是可以使用的,然后使用发布脚本将它们打包上线。 + From 95ef617b14c0ee624c6ca90b87ebb93762d53cc5 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 15 Jun 2012 20:24:15 +0800 Subject: [PATCH 05/55] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E7=AC=AC4=E7=AB=A0?= =?UTF-8?q?=20=E7=AB=8B=E5=8D=B3=E5=88=9D=E5=A7=8B=E5=8C=96=E7=9A=84?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=20=E6=A0=87=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chapter4.markdown b/chapter4.markdown index 3d2519a..f943a41 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -571,3 +571,5 @@ o.message; // "call me" 套用这个模板,你就可以编写其它的模块。然后在发布到线上的时候,你就可以决定在这个时间节点上哪些特性是可以使用的,然后使用发布脚本将它们打包上线。 +## 立即初始化的对象 + From 064724b85981d4cdc4f3e405a9ffbd8c663a8bcc Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 17 Jun 2012 18:01:15 +0800 Subject: [PATCH 06/55] =?UTF-8?q?=E7=AC=AC4=E7=AB=A0=20=E7=AB=8B=E5=8D=B3?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E7=9A=84=E5=87=BD=E6=95=B0=20?= =?UTF-8?q?=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/chapter4.markdown b/chapter4.markdown index f943a41..6fe0046 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -573,3 +573,40 @@ o.message; // "call me" ## 立即初始化的对象 +还有另外一种可以避免污染全局作用域的方法,和前面描述的立即执行的函数相似,叫做“立即初始化的对象”模式。这种模式使用一个带有init()方法的对象来实现,这个方法在对象被创建后立即执行。初始化的工作由init()函数来完成。 + +下面是一个立即初始化的对象模式的例子: + + ({ + // here you can define setting values + // a.k.a. configuration constants + maxwidth: 600, + maxheight: 400, + + // you can also define utility methods + gimmeMax: function () { + return this.maxwidth + "x" + this.maxheight; + }, + + // initialize + init: function () { + console.log(this.gimmeMax()); + // more init tasks... + } + }).init(); + +在语法上,当你使用这种模式的时候就像在使用对象字面量创建一个普通对象一样。除此之外,还需要将对象字面量用括号括起来,这样能让JavaScript引擎知道这是一个对象字面量,而不是一个代码块(if或者for循环之类)。在括号后面,紧接着就执行了init()方法。 + +你也可以将对象字面量和init()调用一起写到括号里面。简单地说,下面两种语法都是有效的: + + ({...}).init(); + ({...}.init()); + +这种模式的好处和自动执行的函数模式是一样的:在做一些一次性的初始化工作的时候保护全局作用域不被污染。从语法上看,这种模式似乎比只包含一段代码在一个匿名函数中要复杂一些,但是如果你的初始化工作比较复杂(这种情况很常见),它会给整个初始化工作一个比较清晰的结构。比如,一些私有的辅助性函数可以被很轻易地看出来,因为它们是这个临时对象的属性,但是如果是在立即执行的函数模式中,它们很可能只是一些散落和函数。 + +这种模式的一个弊端是,JavaScript压缩工具可能不能像压缩一段包裹在函数中的代码一样有效地压缩这种模式的代码。这些私有的属性和方法不被会重命名为一些更短的名字,因为从压缩工具的角度来看,保证压缩的可靠性更重要。在写作本书的时候,Google出品的Closure Compiler的“advanced”模式是唯一会重命名立即初始化的对象的属性的压缩工具。一个压缩后的样例是这样: + + ({d:600,c:400,a:function(){return this.d+"x"+this.c},b:function(){console.log(this.a())}}).b(); + +> 这种模式主要用于一些一次性的工作,并且在init()方法执行完后就无法再次访问到这个对象。如果希望在这些工作完成后保持对对象的引用,只需要简单地在init()的末尾加上return this;即可。 + From 294eca40e89b334c3db67e1a1d8e088e60d8b07e Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 18 Jun 2012 12:49:21 +0800 Subject: [PATCH 07/55] =?UTF-8?q?=E7=AC=AC4=E7=AB=A0=20=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E7=A8=8B=E5=BA=8F=20=E4=B8=80=E8=8A=82?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/chapter4.markdown b/chapter4.markdown index 6fe0046..9c973ea 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -610,3 +610,65 @@ o.message; // "call me" > 这种模式主要用于一些一次性的工作,并且在init()方法执行完后就无法再次访问到这个对象。如果希望在这些工作完成后保持对对象的引用,只需要简单地在init()的末尾加上return this;即可。 + +## 启动时间程序 + +启动时间程序(也叫加载时间程序)是一种优化模式。当你知道某种条件在整个程序生命周期中都不会变化的时候,那么对这个条件的探测只做一次就很有意义。浏览器探测(或者特征检测)是一个典型的例子。 + +举例说明,当你探测到XMLHttpRequest被作为一个本地对象支持时,就知道浏览器不会在程序执行过程中改变这一情况,也不用突然又需要去处理ActiveX对象。当环境不发生变化的时候,你的代码就没有必要在需要在每次XHR对象时探测一遍(并且得到同样的结果)。 + +另外一些可以从启动时间程序中获益的场景是获得一个DOM元素的computed styles或者是绑定事件处理函数。大部分程序员在他们的客户端编程生涯中都编写过事件绑定和取消绑定相关的组件,像下面的例子: + + // BEFORE + var utils = { + addListener: function (el, type, fn) { + if (typeof window.addEventListener === 'function') { + el.addEventListener(type, fn, false); + } else if (typeof document.attachEvent === 'function') { // IE + el.attachEvent('on' + type, fn); + } else { // older browsers + el['on' + type] = fn; + } + }, + removeListener: function (el, type, fn) { + // pretty much the same... + } + }; + +这段代码的问题就是效率不高。每当你执行utils.addListener()或者utils.removeListener()时,同样的检查都会被重复执行。 + +如果使用启动时间程序,那么浏览器探测的工作只需要在初始化代码的时候执行一次。在初始化的时候,代码探测一次环境,然后重新定义这个函数在剩下来的程序生命周期中应该怎样工作。下面是一个例子,看看如何达到这个目的: + + // AFTER + + // the interface + var utils = { + addListener: null, + removeListener: null + }; + + // the implementation + if (typeof window.addEventListener === 'function') { + utils.addListener = function (el, type, fn) { + el.addEventListener(type, fn, false); + }; + utils.removeListener = function (el, type, fn) { + el.removeEventListener(type, fn, false); + }; + } else if (typeof document.attachEvent === 'function') { // IE + utils.addListener = function (el, type, fn) { + el.attachEvent('on' + type, fn); + }; + utils.removeListener = function (el, type, fn) { + el.detachEvent('on' + type, fn); + }; + } else { // older browsers + utils.addListener = function (el, type, fn) { + el['on' + type] = fn; + }; + utils.removeListener = function (el, type, fn) { + el['on' + type] = null; + }; + } + +说到这里,要特别提醒一下关于浏览器探测的事情。当你使用这个模式的时候,不要对浏览器特性过度假设。举个例子,如果你探测到浏览器不支持window.addEventListener,不要假设这个浏览器是IE,也不要认为它不支持原生的XMLHttpRequest,虽然这个结论在整个浏览器历史上的某个点是正确的。当然,也有一些情况是可以放心地做一些特性假设的,比如.addEventListener和.removeEventListerner,但是通常来讲,浏览器的特性在发生变化时都是独立的。最好的策略就是分别探测每个特性,然后使用启动时间程序,使这种探测只做一次。 From b9c3c33f4f3285c9b412cc3fb907860b44ef692b Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 18 Jun 2012 14:13:00 +0800 Subject: [PATCH 08/55] =?UTF-8?q?=E7=AC=AC4=E7=AB=A0=20=E5=A4=87=E5=BF=98?= =?UTF-8?q?=E5=BD=95=20=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91=E4=B8=80?= =?UTF-8?q?=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/chapter4.markdown b/chapter4.markdown index 9c973ea..5909efc 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -672,3 +672,46 @@ o.message; // "call me" } 说到这里,要特别提醒一下关于浏览器探测的事情。当你使用这个模式的时候,不要对浏览器特性过度假设。举个例子,如果你探测到浏览器不支持window.addEventListener,不要假设这个浏览器是IE,也不要认为它不支持原生的XMLHttpRequest,虽然这个结论在整个浏览器历史上的某个点是正确的。当然,也有一些情况是可以放心地做一些特性假设的,比如.addEventListener和.removeEventListerner,但是通常来讲,浏览器的特性在发生变化时都是独立的。最好的策略就是分别探测每个特性,然后使用启动时间程序,使这种探测只做一次。 + +## 函数属性——一种备忘录模式 + +函数也是对象,所以它们可以有属性。事实上,函数也确实本来就有一些属性。比如,对一个函数来说,不管是用什么语法创建的,它会自动拥有一个length属性来标识这个函数期待接受的参数个数: + + function func(a, b, c) {} + console.log(func.length); // 3 + +任何时候都可以给函数添加自定义属性。添加自定义属性的一个有用场景是缓存函数的执行结果(返回值),这样下次同样的函数被调用的时候就不需要再做一次那些可能很复杂的计算。缓存一个函数的运行结果也就是为大家所熟知的备忘录(memoization)。 + +在下面的例子中,myFunc函数创建了一个cache属性,可以通过myFunc.cache访问到。这个cache属性是一个对象(hash表),传给函数的参数会作为对象的key,函数执行结果会作为对象的值。函数的执行结果可以是会用到的任何的复杂数据结构: + + var myFunc = function (param) { + if (!myFunc.cache[param]) { + var result = {}; + // ... expensive operation ... + myFunc.cache[param] = result; + } + return myFunc.cache[param]; + }; + + // cache storage + myFunc.cache = {}; + +上面的代码假设函数只接受一个参数param,并且这个参数是基本类型(比如字符串)。如果你有更多更复杂的参数,则通常需要对它们进行序列化。(译注:序列化后来作为缓存对象的key。)比如,你需要将arguments对象序列化为JSON字符串,然后使用JSON字符串作为cache对象的key: + + var myFunc = function () { + + var cachekey = JSON.stringify(Array.prototype.slice.call(arguments)), + result; + + if (!myFunc.cache[cachekey]) { + result = {}; + // ... expensive operation ... + myFunc.cache[cachekey] = result; + } + return myFunc.cache[cachekey]; + }; + + // cache storage + myFunc.cache = {}; + + From b8f0f51ad43e507e5b16d2d4803001057ae2e4d0 Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 18 Jun 2012 19:21:05 +0800 Subject: [PATCH 09/55] =?UTF-8?q?=E7=AC=AC4=E7=AB=A0=20=E5=A4=87=E5=BF=98?= =?UTF-8?q?=E5=BD=95=20=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91=E5=AE=8C?= =?UTF-8?q?=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/chapter4.markdown b/chapter4.markdown index 5909efc..1bee367 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -714,4 +714,23 @@ o.message; // "call me" // cache storage myFunc.cache = {}; +需要注意的是,在序列化的过程中,对象的“标识”将会丢失。如果你有两个不同的对象,却碰巧有相同的属性,那么他们会共享同样的缓存内容。 + +前面代码中的函数名还可以使用arguments.callee来替代,这样就不用将函数名硬编码。不过尽管现阶段这个办法可行,但是仍然需要注意,arguments.callee在ECMAScript 5的严格模式中是不被允许的: + + var myFunc = function (param) { + + var f = arguments.callee, + result; + + if (!f.cache[param]) { + result = {}; + // ... expensive operation ... + f.cache[param] = result; + } + return f.cache[param]; + }; + + // cache storage + myFunc.cache = {}; From 23b44939d3b34fcce59e747d7cba030eb9333f94 Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 18 Jun 2012 21:02:42 +0800 Subject: [PATCH 10/55] =?UTF-8?q?=E7=AC=AC4=E7=AB=A0=20=E5=AF=B9=E8=B1=A1?= =?UTF-8?q?=E7=9A=84=E9=85=8D=E7=BD=AE=20=E4=B8=80=E8=8A=82=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/chapter4.markdown b/chapter4.markdown index 1bee367..a1b8b5d 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -734,3 +734,50 @@ o.message; // "call me" // cache storage myFunc.cache = {}; + +## 对象的配置 + +对象的配置模式是一种提供更简洁的API的方法,尤其是当你正在写一个即将被其它程序调用的类库或者任何其它代码的时候。 + +软件在开发和维护过程中需要不断改变是一个不争的事实。这样的事情总是以一些有限的需求开始,但是随着开发的进行,越来越多的功能会不断被加进来。 + +设想一下你正在写一个名为addPerson()的函数,它接受一个姓和一个名,然后在列表中加入一个人: + + function addPerson(first, last) {...} + +然后你意识到,生日也必须要存储,此外,性别和地址也作为可选项存储。所以你修改了函数,添加了一些新的参数(还得非常小心地将可选参数放到最后): + + function addPerson(first, last, dob, gender, address) {...} + +这个时候,函数已经显得有点长了。然后,你又被告知需要添加一个用户名,并且不是可选的。现在这个函数的调用者需要将所有的可选参数传进来,并且得非常小心地保证不弄混参数的顺序: + + addPerson("Bruce", "Wayne", new Date(), null, null, "batman"); + +传一大串的参数真的很不方便。一个更好的办法就是将它们替换成一个参数,并且把这个参数弄成对象;我们叫它conf,是“configuration”(配置)的缩写: + + addPerson(conf); + +然后这个函数的使用者就可以这样: + + var conf = { + username: "batman", + first: "Bruce", + last: "Wayne" + }; + addPerson(conf); + +对象的配置模式的好处是: + +- 不需要记住参数的顺序 +- 可以很安全地跳过可选参数 +- 拥有更好的可读性和可维护性 +- 更容易添加和移除参数 + +对象的配置模式的坏处是: + +- 需要记住参数的名字 +- 参数名字不能被压缩 + +举些实例,这个模式对创建DOM元素的函数或者是给元素设定CSS样式函数会非常实用,因为元素和CSS样式可能会有很多但是大部分可选的属性。 + + From 8b97503df74d3abde7c04cf23bf13f26bb99f13d Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 18 Jun 2012 22:53:59 +0800 Subject: [PATCH 11/55] =?UTF-8?q?=E6=B7=BB=E5=8A=A0TooBug=E5=90=8D?= =?UTF-8?q?=E5=AD=97=20=E6=B7=BB=E5=8A=A0=E7=AC=AC4=E7=AB=A0=E9=93=BE?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.markdown | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.markdown b/README.markdown index 9f5f446..dea4860 100644 --- a/README.markdown +++ b/README.markdown @@ -5,7 +5,7 @@ **“JavaScript patterns”中译本** - 《JavaScript 模式》 - 作者:[Stoyan Stefanov](http://www.phpied.com/) -- 翻译:[拔赤](http://jayli.github.com/)、[goddyzhao](http://goddyzhao.me) +- 翻译:[拔赤](http://jayli.github.com/)、[goddyzhao](http://goddyzhao.me)、[TooBug](http://www.toobug.net) 偷懒是程序员的优良品质,模式则是先人们总结的偷懒招式。Stoyan Stefanov 的这本书,从 JavaScript 的实际使用场景出发,提炼了不少可以让前端们偷懒的实用招式。模式的探索、创新,将永远是程序员自我提升的一条修炼之道。值得一读。 @@ -96,14 +96,14 @@ - [库中的回调](chapter4.markdown#a) - [返回函数](chapter4.markdown#a) - [自定义函数](chapter4.markdown#a) -- 立即执行的函数 - - 立即执行的函数的参数 - - 立即执行的函数的返回值 - - 好处和用法 -- 立即初始化的对象 -- 启动时间程序 -- 函数属性——一种备忘录模式 -- 对象的配置 +- [立即执行的函数](chapter4.markdown#a) + - [立即执行的函数的参数](chapter4.markdown#a) + - [立即执行的函数的返回值](chapter4.markdown#a) + - [好处和用法](chapter4.markdown#a) +- [立即初始化的对象](chapter4.markdown#a) +- [启动时间程序](chapter4.markdown#a) +- [函数属性——一种备忘录模式](chapter4.markdown#a) +- [对象的配置](chapter4.markdown#a) - 柯里化 (Curry) - 函数应用 - 部分应用 From fc8cb949169f465b8057b7717d9077854f7ca233 Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 19 Jun 2012 19:22:34 +0800 Subject: [PATCH 12/55] =?UTF-8?q?=E7=AC=AC4=E7=AB=A0=20=E6=9F=AF=E9=87=8C?= =?UTF-8?q?=E5=8C=96=E4=B9=8B=E5=87=BD=E6=95=B0=E5=BA=94=E7=94=A8=E5=B0=8F?= =?UTF-8?q?=E8=8A=82=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/chapter4.markdown b/chapter4.markdown index a1b8b5d..247915e 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -781,3 +781,48 @@ o.message; // "call me" 举些实例,这个模式对创建DOM元素的函数或者是给元素设定CSS样式函数会非常实用,因为元素和CSS样式可能会有很多但是大部分可选的属性。 +## 柯里化 (Curry) + +在本章剩下的部分,我们将讨论一下关于柯里化和部分应用的话题。但是在我们开始这个话题之前,先看一下到底什么是函数应用。 + +### 函数应用 + +在一些纯粹的函数式编程语言中,对函数的描述不是被调用(called或者invoked),而是被应用(applied)。在JavaScript中也有同样的东西——我们可以使用Function.prototype.apply()来应用一个函数,因为在JavaScript中,函数实际上是对象,并且他们拥有方法。 + +下面是一个函数应用的例子: + + // define a function + var sayHi = function (who) { + return "Hello" + (who ? ", " + who : "") + "!"; + }; + + // invoke a function + sayHi(); // "Hello" + sayHi('world'); // "Hello, world!" + + // apply a function + sayHi.apply(null, ["hello"]); // "Hello, hello!" + +从上面的例子中可以看出来,调用一个函数和应用一个函数有相同的结果。apply()接受两个参数:第一个是在函数内部绑定到this上的对象,第二个是一个参数数组,参数数组会在函数内部变成一个类似数组的arguments对象。如果第一个参数为null,那么this将指向全局对象,这正是当你调用一个函数(且这个函数不是某个对象的方法)时发生的事情。 + +当一个函数是一个对象的方法时,我们不再像前面的例子一样传入null。(译注:主要是为了保证方法中的this绑定到一个有效的对象而不是全局对象。)在下面的例子中,对象被作为第一个参数传给apply(): + + var alien = { + sayHi: function (who) { + return "Hello" + (who ? ", " + who : "") + "!"; + } + }; + + alien.sayHi('world'); // "Hello, world!" + sayHi.apply(alien, ["humans"]); // "Hello, humans!" + +在这个例子中,sayHi()中的this指向alien。而在上一个例子中,this是指向的全局对象。(译注:这个例子的代码有误,最后一行的sayHi并不能访问到alien的sayHi方法,需要使用alien.sayHi.apply(alien, ["humans"])才可正确运行。另外,在sayHi中也没有出现this。) + +正如上面两个例子所展现出来的一样,我们所谓的函数调用跟函数应用的一种语法糖没有什么太大的差别。 + +需要注意的是,除了apply()之外,Function.prototype对象还有一个call()方法,但是它仍然只是apply()的一种语法糖。(译注:这两个方法的区别在于,apply()只接受两个参数,第二个参数为需要传给函数的参数数组,而call()则接受任意多个参数,从第二个开始将参数依次传给函数。)不过有种情况下使用这个语法糖会更好:当你的函数只接受一个参数的时候,你可以省去为唯一的一个元素创建数组的工作: + + // the second is more efficient, saves an array + sayHi.apply(alien, ["humans"]); // "Hello, humans!" + sayHi.call(alien, "humans"); // "Hello, humans!" + From 1945ff898e5bde481d50caab4acf91d5e50c6e5e Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 19 Jun 2012 19:47:15 +0800 Subject: [PATCH 13/55] =?UTF-8?q?=E7=AC=AC4=E7=AB=A0=20=E6=9F=AF=E9=87=8C?= =?UTF-8?q?=E5=8C=96=E4=B9=8B=E9=83=A8=E5=88=86=E5=BA=94=E7=94=A8=E4=B8=80?= =?UTF-8?q?=E5=B0=8F=E8=8A=82=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/chapter4.markdown b/chapter4.markdown index 247915e..45cf7d9 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -826,3 +826,54 @@ o.message; // "call me" sayHi.apply(alien, ["humans"]); // "Hello, humans!" sayHi.call(alien, "humans"); // "Hello, humans!" +### 部分应用 + +现在我们知道了,调用一个函数实际上就是给它应用一堆参数,那是否能够只传一部分参数而不传全部呢?这实际上跟我们手工处理数学函数非常类似。 + +假设已经有了一个add()函数,它的工作是把x和y两个数加到一起。下面的代码片段展示了当x为5、y为4时的计算步骤: + + // for illustration purposes + // not valid JavaScript + + // we have this function + function add(x, y) { + return x + y; + } + + // and we know the arguments + add(5, 4); + + // step 1 -- substitute one argument + function add(5, y) { + return 5 + y; + } + + // step 2 -- substitute the other argument + function add(5, 4) { + return 5 + 4; + } + +在这个代码片段中,step 1和step 2并不是有效的JavaScript代码,但是它展示了我们手工计算的过程。首先获得第一个参数的值,然后将未知的x和已知的值5替换到函数中。然后重复这个过程,直到替换掉所有的参数。 + +step 1是一个所谓的部分应用的例子:我们只应用了第一个参数。当你执行一个部分应用的时候并不能获得结果(或者是解决方案),取而代之的是另一个函数。 + +下面的代码片段展示了一个虚拟的partialApply()方法的用法: + + var add = function (x, y) { + return x + y; + }; + + // full application + add.apply(null, [5, 4]); // 9 + + // partial application + var newadd = add.partialApply(null, [5]); + // applying an argument to the new function + newadd.apply(null, [4]); // 9 + +正如你所看到的一样,部分应用给了我们另一个函数,这个函数可以在稍后调用的时候接受其它的参数。这实际上跟add(5)(4)是等价的,因为add(5)返回了一个函数,这个函数可以使用(4)来调用。我们又一次看到,熟悉的add(5, 4)也跟add(5)(4)的一种语法糖差不了多少。 + +现在,让我们回到地球:并不存在这样的一个partialApply()函数,并且函数的默认表现也不会像上面的例子中那样。但是你完全可以自己去写,因为JavaScript的动态特性完全可以做到这样。 + +让函数理解并且处理部分应用的过程,叫柯里化。 + From e1f55a97042d4cc2905a7e2e3471233161bbda7d Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 19 Jun 2012 21:58:24 +0800 Subject: [PATCH 14/55] =?UTF-8?q?=E7=AC=AC4=E7=AB=A0=20=E6=9F=AF=E9=87=8C?= =?UTF-8?q?=E5=8C=96=E4=B9=8B=E6=9F=AF=E9=87=8C=E5=8C=96=E4=B8=80=E5=B0=8F?= =?UTF-8?q?=E8=8A=82=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 91 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/chapter4.markdown b/chapter4.markdown index 45cf7d9..c11728f 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -875,5 +875,94 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 现在,让我们回到地球:并不存在这样的一个partialApply()函数,并且函数的默认表现也不会像上面的例子中那样。但是你完全可以自己去写,因为JavaScript的动态特性完全可以做到这样。 -让函数理解并且处理部分应用的过程,叫柯里化。 +让函数理解并且处理部分应用的过程,叫柯里化(Currying)。 + +### 柯里化(Currying) + +柯里化和辛辣的印度菜可没什么关系;它来自数学家Haskell Curry。(Haskell编程语言也是因他而得名。)柯里化是一个变换的函数的过程。柯里化的另外一个名字也叫schönfinkelisation,来自另一位数学家——Moses Schönfinkelisation——这种变换的最初发明者。 + +所以我们怎样对一个函数进行柯里化呢?其它的函数式编程语言也许已经原生提供了支持并且所有的函数已经默认柯里化了。在JavaScript中我们可以修改一下add()函数使它柯里化,然后支持部分应用。 + +来看一个例子: + + // a curried add() + // accepts partial list of arguments + function add(x, y) { + var oldx = x, oldy = y; + if (typeof oldy === "undefined") { // partial + return function (newy) { + return oldx + newy; + }; + } + // full application + return x + y; + } + + // test + typeof add(5); // "function" + add(3)(4); // 7 + + // create and store a new function + var add2000 = add(2000); + add2000(10); // 2010 + +在这段代码中,第一次调用add()时,在返回的内层函数那里创建了一个闭包。这个闭包将原来的x和y的值存储到了oldx和oldy中。当内层函数执行的时候,oldx会被使用。如果没有部分应用,即x和y都传了值,那么这个函数会简单地将他们相加。这个add()函数的实现跟实际情况比起来有些冗余,仅仅是为了更好地说明问题。下面的代码片段中展示了一个更简洁的版本,没有oldx和oldy,因为原始的x已经被存储到了闭包中,此外我们复用了y作为本地变量,而不用像之前那样新定义一个变量newy: + + // a curried add + // accepts partial list of arguments + function add(x, y) { + if (typeof y === "undefined") { // partial + return function (y) { + return x + y; + }; + } + // full application + return x + y; + } + +在这些例子中,add()函数自己处理了部分应用。有没有可能用一种更为通用的方式来做同样的事情呢?换句话说,我们能不能对任意一个函数进行处理,得到一个新函数,使它可以处理部分参数?下面的代码片段展示了一个通用函数的例子,我们叫它schonfinkelize(),正是用来做这个的。我们使用schonfinkelize()一部分原因是它比较难发音,另一部分原因是它听起来比较像动词(使用“curry”则不是那么明确),而我们刚好需要一个动词来表明这是一个函数转换的过程。 + +这是一个通用的柯里化函数: + + function schonfinkelize(fn) { + var slice = Array.prototype.slice, + stored_args = slice.call(arguments, 1); + return function () { + var new_args = slice.call(arguments), + args = stored_args.concat(new_args); + return fn.apply(null, args); + }; + } + +这个schonfinkelize可能显得比较复杂了,只是因为在JavaScript中arguments不是一个真的数组。从Array.prototype中借用slice()方法帮助我们将arguments转换成数组,以便能更好地对它进行操作。当schonfinkelize()第一次被调用的时候,它使用slice变量存储了对slice()方法的引用,同时也存储了调用时的参数(stored\_args),参数保留了除第一个以外的,因为第一个参数是要被柯里化的函数。schonfinkelize()返回了一个函数。当这个返回的函数被调用的时候,它可以(通过闭包)访问到已经存储的参数stored\_args和slice。新的函数只需要合并老的部分应用的参数(stored\_args)和新的参数(new\_args),然后将它们应用到原来的函数fn(也可以在装饰中访问到)即可。 + +现在有了通用的柯里化函数,就可以做一些测试了: + + // a normal function + function add(x, y) { + return x + y; + } + + // curry a function to get a new function + var newadd = schonfinkelize(add, 5); + newadd(4); // 9 + + // another option -- call the new function directly + schonfinkelize(add, 6)(7); // 13 + +用来做函数转换的schonfinkelize()并不局限于单个参数或者单步的柯里化。这里有些更多用法的例子: + + // a normal function + function add(a, b, c, d, e) { + return a + b + c + d + e; + } + + // works with any number of arguments + schonfinkelize(add, 1, 2, 3)(5, 5); // 16 + + // two-step currying + var addOne = schonfinkelize(add, 1); + addOne(10, 10, 10, 10); // 41 + var addSix = schonfinkelize(addOne, 2, 3); + addSix(5, 5); // 16 From befd9bc14ba828716c81db26a18b8fe6a2f28476 Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 19 Jun 2012 22:07:14 +0800 Subject: [PATCH 15/55] =?UTF-8?q?=E7=AC=AC4=E7=AB=A0=20=E6=9F=AF=E9=87=8C?= =?UTF-8?q?=E5=8C=96=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/chapter4.markdown b/chapter4.markdown index c11728f..45ce126 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -966,3 +966,8 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 var addSix = schonfinkelize(addOne, 2, 3); addSix(5, 5); // 16 +### 什么时候使用柯里化 + +当你发现自己在调用同样的函数并且传入的参数大部分都相同的时候,就是考虑柯里化的理想场景了。你可以通过传入一部分的参数动态地创建一个新的函数。这个新函数会存储那些重复的参数(所以你不需要再每次都传入),然后再在调用原始函数的时候将整个参数列表补全,正如原始函数期待的那样。 + + From 1d2746565fb1041505207937a66112d19b70fad4 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Jun 2012 13:22:12 +0800 Subject: [PATCH 16/55] =?UTF-8?q?=E7=AC=AC4=E7=AB=A0=20=E5=87=BD=E6=95=B0?= =?UTF-8?q?=20=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 49 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/chapter4.markdown b/chapter4.markdown index 45ce126..6ff901b 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -467,7 +467,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 alert('watch out!'); })(); -这种模式很有用,它为我们提供一个作用域的沙箱,可以在执行一些初始化代码的时候使用。设想这样的场景:当页面加载的时候,你需要运行一些代码,比如绑定事件、创建对象等等。所有的这些代码都只需要运行一次,所以没有必要创建一个具名函数。但是这些代码需要一些临时变量,而这些变量在初始化完之后又不会再次用到。显然,把这些变量作为全局变量声明也是不合适的。正因为如此,我们才需要立即执行的函数。它可以把你所有的代码包裹到一个作用域里面,而不会暴露任何变量到全局作用域中: +这种模式很有用,它为我们提供一个作用域的沙箱,可以在执行一些初始化代码的时候使用。设想这样的场景:当页面加载的时候,你需要运行一些代码,比如绑定事件、创建对象等等。所有的这些代码都只需要运行一次,所以没有必要创建一个带有名字的函数。但是这些代码需要一些临时变量,而这些变量在初始化完之后又不会再次用到。显然,把这些变量作为全局变量声明也是不合适的。正因为如此,我们才需要立即执行的函数。它可以把你所有的代码包裹到一个作用域里面,而不会暴露任何变量到全局作用域中: (function () { @@ -971,3 +971,50 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 当你发现自己在调用同样的函数并且传入的参数大部分都相同的时候,就是考虑柯里化的理想场景了。你可以通过传入一部分的参数动态地创建一个新的函数。这个新函数会存储那些重复的参数(所以你不需要再每次都传入),然后再在调用原始函数的时候将整个参数列表补全,正如原始函数期待的那样。 +##小结 + +在JavaScript中,开发者对函数的理解和运用的要求是比较苛刻的。在本章中,主要讨论了有关函数的一些背景知识和术语。你知道了JavaScript函数中两个重要的特性,也就是: + +1. 函数是一等对象,他们可以被作为值传递,也可以拥有属性和方法。 +2. 函数拥有本地作用域,而大括号不产生块级作用域。另外需要注意的是,变量的声明会被提前到本地作用域顶部。 + +创建一个函数的语法有: + +1. 带有名字的函数表达式 +2. 函数表达式(和上一种一样,但是没有名字),也就是为大家熟知的“匿名函数” +3. 函数声明,与其它语言的函数语法相似 + +在介绍完背景和函数的语法后,介绍了一些有用的模式,按分类列出: + +1. API模式,它们帮助我们为函数给出更干净的接口,包括: + +- 回调模式 +传入一个函数作为参数 + +- 对象的配置 +帮助保持函数的参数数量可控 + +- 返回函数 +函数的返回值是另一个函数 + +- 柯里化 +新函数在已有函数的基础上再加上一部分参数构成 + +2. 初始化模式,这些模式帮助我们用一种干净的、结构化的方法来做一些初始化工作(在web页面和应用中非常常见),通过一些临时变量来保证不污染全局命名空间。这些模式包括: + +- 立即执行的函数 +当它们被定义后立即执行 + +- 立即初始化的对象 +初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 + +- 启动时间程序 +使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 + +3. 性能模式,这些模式帮助提高代码的性能,包括: + +- 备忘录 +利用函数的属性,使已经计算过的值不用再次计算 + +- 自定义函数 +重写自身的函数体,使第二次及后续的调用做更少的工作 \ No newline at end of file From a525fba92d9d387e14fd62f09db1042b19258120 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Jun 2012 18:50:30 +0800 Subject: [PATCH 17/55] =?UTF-8?q?=E7=AC=AC4=E7=AB=A0=20=E5=90=8E=E9=9D=A2?= =?UTF-8?q?=E5=87=A0=E8=8A=82=E9=80=9A=E8=AF=BB=E3=80=81=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=B6=A6=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 101 +++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 6ff901b..8d172ba 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -402,7 +402,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 当函数中包含一些初始化操作,并希望这些初始化只执行一次,那么这种模式是非常适合这个场景的。因为能避免的重复执行则尽量避免,函数的一部分可能再也不会执行到。在这个场景中,函数执行一次后就被重写为另外一个函数了。 -使用这种模式可以帮助提高应用的执行效率,因为重新定义的函数执行的更少。 +使用这种模式可以帮助提高应用的执行效率,因为重新定义的函数执行更少的代码。 >这种模式的另外一个名字是“函数的懒惰定义”,因为直到函数执行一次后才重新定义,可以说它是“某个时间点之后才存在”,简称“懒惰定义”。 @@ -442,7 +442,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 scareMe(); // Double boo! console.log(scareMe.property); // undefined -从结果来看,当自定义函数被赋值给一个新的变量的时候,这段使用自定义函数的代码的执行结果与我们可能期望的结果并不一样。每当prank()运行的时候,它都弹出“Boo!”。同时它也重写了scareMe()函数,但是prank()自己仍然能够使用之前的定义,包括属性property。在这个函数被作为spooky对象的boo()方法调用的时候,结果也一样。所有的这些调用,在第一次的时候就已经修改了全局的scareMe()的指向,所以当它最终被调用的时候,它的函数体已经被修改为弹出“Double boo”。它也就不能获取到新添加的属性“property”。 +从结果来看,当自定义函数被赋值给一个新的变量的时候,这段使用自定义函数的代码的执行结果与我们期望的结果可能并不一样。每当prank()运行的时候,它都弹出“Boo!”。同时它也重写了scareMe()函数,但是prank()自己仍然能够使用之前的定义,包括属性property。在这个函数被作为spooky对象的boo()方法调用的时候,结果也一样。所有的这些调用,在第一次的时候就已经修改了全局的scareMe()的指向,所以当它最终被调用的时候,它的函数体已经被修改为弹出“Double boo”。它也就不能获取到新添加的属性“property”。 ## 立即执行的函数 @@ -453,7 +453,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 alert('watch out!'); }()); -这种模式其实本质上只是一个在创建后就被执行的函数表达式(具名或者匿名)。“立即执行的函数”这种说法并没有在ECMAScript标准中被定义,但它作为一个名词,有助于我们的描述和讨论。 +这种模式本质上只是一个在创建后就被执行的函数表达式(具名或者匿名)。“立即执行的函数”这种说法并没有在ECMAScript标准中被定义,但它作为一个名词,有助于我们的描述和讨论。 这种模式由以下几个部分组成: @@ -467,7 +467,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 alert('watch out!'); })(); -这种模式很有用,它为我们提供一个作用域的沙箱,可以在执行一些初始化代码的时候使用。设想这样的场景:当页面加载的时候,你需要运行一些代码,比如绑定事件、创建对象等等。所有的这些代码都只需要运行一次,所以没有必要创建一个带有名字的函数。但是这些代码需要一些临时变量,而这些变量在初始化完之后又不会再次用到。显然,把这些变量作为全局变量声明也是不合适的。正因为如此,我们才需要立即执行的函数。它可以把你所有的代码包裹到一个作用域里面,而不会暴露任何变量到全局作用域中: +这种模式很有用,它为我们提供一个作用域的沙箱,可以在执行一些初始化代码的时候使用。设想这样的场景:当页面加载的时候,你需要运行一些代码,比如绑定事件、创建对象等等。所有的这些代码都只需要运行一次,所以没有必要创建一个带有名字的函数。但是这些代码需要一些临时变量,而这些变量在初始化完之后又不会再次用到。显然,把这些变量作为全局变量声明是不合适的。正因为如此,我们才需要立即执行的函数。它可以把你所有的代码包裹到一个作用域里面,而不会暴露任何变量到全局作用域中: (function () { @@ -479,7 +479,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 }()); // "Today is Fri, 13" -如果这段代码没有包裹到立即执行函数中,那么变量days,today,msg都会是全局变量,而这仅仅是由初始化代码遗留下来的垃圾。 +如果这段代码没有被包裹到立即执行函数中,那么变量days、today、msg都会是全局变量,而这些变量仅仅是由因为初始化而遗留下来的垃圾,没有任何用处。 ### 立即执行的函数的参数 @@ -496,7 +496,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 通常的做法,会把全局对象当作一个参数传给立即执行的函数,以保证在函数内部也可以访问到全局对象,而不是使用window对象,这样可以使得代码在非浏览器环境中使用时更具可移植性。 -值得注意的是,一般来说尽量不要给立即执行的函数传入太多的参数,否则的话,你在阅读代码的时候需要频繁地上下滚动代码。 +值得注意的是,一般情况下尽量不要给立即执行的函数传入太多的参数,否则会有一件麻烦的事情,就是你在阅读代码的时候需要频繁地上下滚动代码。 ### 立即执行的函数的返回值 @@ -506,7 +506,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 return 2 + 2; }()); -如果省略括号的话也可以达到同样的目的,因为如果需要将返回值赋值给变量,那么括号就不是必需的。省略第一对括号的代码是这样子: +如果省略括号的话也可以达到同样的目的,因为如果需要将返回值赋给变量,那么第一对括号就不是必需的。省略括号的代码是这样子: var result = function () { return 2 + 2; @@ -520,9 +520,9 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 return 2 + 2; })(); -前面的例子中,立即执行的函数返回的是一个基本类型的数值。但事实上,除了基本类型以外,一个立即执行的函数可以返回任意类型的值,包括返回一个函数都可以。你可以利用立即执行的函数的作用域来存储一些私有的数据,这些数据只能在返回的内层函数中被访问。 +前面的例子中,立即执行的函数返回的是一个基本类型的数值。但事实上,除了基本类型以外,一个立即执行的函数可以返回任意类型的值,甚至返回一个函数都可以。你可以利用立即执行的函数的作用域来存储一些私有的数据,这些数据只能在返回的内层函数中被访问。 -在下面的例子中,立即执行的函数的返回值是一个函数,这个函数会简单地返回res的值,并且它被赋值给了变量getResult。而res就是一个预先计算好的变量,它被存储在立即执行函数的闭包中: +在下面的例子中,立即执行的函数的返回值是一个函数,这个函数会简单地返回res的值,并且它被赋给了变量getResult。而res是一个预先计算好的变量,它被存储在立即执行函数的闭包中: var getResult = (function () { var res = 2 + 2; @@ -533,19 +533,20 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 在定义一个对象的属性的时候也可以使用立即执行的函数。设想一下这样的场景:你需要定义一个对象的属性,这个属性在对象的生命周期中都不会改变,但是在定义之前,你需要做一点额外的工作来得到正确的值。这种情况下你就可以使用立即执行的函数来包裹那些额外的工作,然后将它的返回值作为对象属性的值。下面是一个例子: -var o = { - message: (function () { - var who = "me", - what = "call"; - return what + " " + who; - }()), - getMsg: function () { - return this.message; - } -}; -// usage -o.getMsg(); // "call me" -o.message; // "call me" + var o = { + message: (function () { + var who = "me", + what = "call"; + return what + " " + who; + }()), + getMsg: function () { + return this.message; + } + }; + + // usage + o.getMsg(); // "call me" + o.message; // "call me" 在这个例子中,o.message是一个字符串,而不是一个函数,但是它需要一个函数在脚本载入后来得到这个属性值。 @@ -556,9 +557,9 @@ o.message; // "call me" > 立即执行的函数还有一些名字,比如“自调用函数”或者“自执行函数”,因为这些函数会在被定义后立即执行自己。 -这种模式也经常被用到书签代码中,因为书签代码会在任何一个页面运行,所以需要保持全局命名空间干净是一个非常残酷的需求。 +这种模式也经常被用到书签代码中,因为书签代码会在任何一个页面运行,所以需要非常苛刻地保持全局命名空间干净。 -这种模式也可以让你包裹一些独立的特性到一个封闭的模块中。设想你的页面是静态的,在没有JavaScript的时候工作正常,然后,本着渐进增强的精神,你给页面加入了一点增加代码。这时候,你就可以把你的代码(也可以叫“模块”或者“特性”)到一个立即执行的函数中并且保证页面在有没有它的时候都可以正常工作。然后你就可以加入更多的增强特性,或者对它们进行移除、进行独立测试或者允许用户禁用等等。 +这种模式也可以让你包裹一些独立的特性到一个封闭的模块中。设想你的页面是静态的,在没有JavaScript的时候工作正常,然后,本着渐进增强的精神,你给页面加入了一点增加代码。这时候,你就可以把你的代码(也可以叫“模块”或者“特性”)放到一个立即执行的函数中并且保证页面在有没有它的时候都可以正常工作。然后你就可以加入更多的增强特性,或者对它们进行移除、进行独立测试或者允许用户禁用等等。 你可以使用下面的模板定义一段函数代码,我们叫它module1: @@ -602,7 +603,7 @@ o.message; // "call me" ({...}).init(); ({...}.init()); -这种模式的好处和自动执行的函数模式是一样的:在做一些一次性的初始化工作的时候保护全局作用域不被污染。从语法上看,这种模式似乎比只包含一段代码在一个匿名函数中要复杂一些,但是如果你的初始化工作比较复杂(这种情况很常见),它会给整个初始化工作一个比较清晰的结构。比如,一些私有的辅助性函数可以被很轻易地看出来,因为它们是这个临时对象的属性,但是如果是在立即执行的函数模式中,它们很可能只是一些散落和函数。 +这种模式的好处和自动执行的函数模式是一样的:在做一些一次性的初始化工作的时候保护全局作用域不被污染。从语法上看,这种模式似乎比只包含一段代码在一个匿名函数中要复杂一些,但是如果你的初始化工作比较复杂(这种情况很常见),它会给整个初始化工作一个比较清晰的结构。比如,一些私有的辅助性函数可以被很轻易地看出来,因为它们是这个临时对象的属性,但是如果是在立即执行的函数模式中,它们很可能只是一些散落的函数。 这种模式的一个弊端是,JavaScript压缩工具可能不能像压缩一段包裹在函数中的代码一样有效地压缩这种模式的代码。这些私有的属性和方法不被会重命名为一些更短的名字,因为从压缩工具的角度来看,保证压缩的可靠性更重要。在写作本书的时候,Google出品的Closure Compiler的“advanced”模式是唯一会重命名立即初始化的对象的属性的压缩工具。一个压缩后的样例是这样: @@ -611,13 +612,13 @@ o.message; // "call me" > 这种模式主要用于一些一次性的工作,并且在init()方法执行完后就无法再次访问到这个对象。如果希望在这些工作完成后保持对对象的引用,只需要简单地在init()的末尾加上return this;即可。 -## 启动时间程序 +## 条件初始化 -启动时间程序(也叫加载时间程序)是一种优化模式。当你知道某种条件在整个程序生命周期中都不会变化的时候,那么对这个条件的探测只做一次就很有意义。浏览器探测(或者特征检测)是一个典型的例子。 +条件初始化(也叫条件加载)是一种优化模式。当你知道某种条件在整个程序生命周期中都不会变化的时候,那么对这个条件的探测只做一次就很有意义。浏览器探测(或者特征检测)是一个典型的例子。 -举例说明,当你探测到XMLHttpRequest被作为一个本地对象支持时,就知道浏览器不会在程序执行过程中改变这一情况,也不用突然又需要去处理ActiveX对象。当环境不发生变化的时候,你的代码就没有必要在需要在每次XHR对象时探测一遍(并且得到同样的结果)。 +举例说明,当你探测到XMLHttpRequest被作为一个本地对象支持时,就知道浏览器不会在程序执行过程中改变这一情况,也不会出现突然需要去处理ActiveX对象的情况。当环境不发生变化的时候,你的代码就没有必要在需要在每次XHR对象时探测一遍(并且得到同样的结果)。 -另外一些可以从启动时间程序中获益的场景是获得一个DOM元素的computed styles或者是绑定事件处理函数。大部分程序员在他们的客户端编程生涯中都编写过事件绑定和取消绑定相关的组件,像下面的例子: +另外一些可以从条件初始化中获益的场景是获得一个DOM元素的computed styles或者是绑定事件处理函数。大部分程序员在他们的客户端编程生涯中都编写过事件绑定和取消绑定相关的组件,像下面的例子: // BEFORE var utils = { @@ -637,7 +638,7 @@ o.message; // "call me" 这段代码的问题就是效率不高。每当你执行utils.addListener()或者utils.removeListener()时,同样的检查都会被重复执行。 -如果使用启动时间程序,那么浏览器探测的工作只需要在初始化代码的时候执行一次。在初始化的时候,代码探测一次环境,然后重新定义这个函数在剩下来的程序生命周期中应该怎样工作。下面是一个例子,看看如何达到这个目的: +如果使用条件初始化,那么浏览器探测的工作只需要在初始化代码的时候执行一次。在初始化的时候,代码探测一次环境,然后重新定义这个函数在剩下来的程序生命周期中应该怎样工作。下面是一个例子,看看如何达到这个目的: // AFTER @@ -671,18 +672,18 @@ o.message; // "call me" }; } -说到这里,要特别提醒一下关于浏览器探测的事情。当你使用这个模式的时候,不要对浏览器特性过度假设。举个例子,如果你探测到浏览器不支持window.addEventListener,不要假设这个浏览器是IE,也不要认为它不支持原生的XMLHttpRequest,虽然这个结论在整个浏览器历史上的某个点是正确的。当然,也有一些情况是可以放心地做一些特性假设的,比如.addEventListener和.removeEventListerner,但是通常来讲,浏览器的特性在发生变化时都是独立的。最好的策略就是分别探测每个特性,然后使用启动时间程序,使这种探测只做一次。 +说到这里,要特别提醒一下关于浏览器探测的事情。当你使用这个模式的时候,不要对浏览器特性过度假设。举个例子,如果你探测到浏览器不支持window.addEventListener,不要假设这个浏览器是IE,也不要认为它不支持原生的XMLHttpRequest,虽然这个结论在整个浏览器历史上的某个点是正确的。当然,也有一些情况是可以放心地做一些特性假设的,比如.addEventListener和.removeEventListerner,但是通常来讲,浏览器的特性在发生变化时都是独立的。最好的策略就是分别探测每个特性,然后使用条件初始化,使这种探测只做一次。 -## 函数属性——一种备忘录模式 +## 函数属性——Memoization模式 函数也是对象,所以它们可以有属性。事实上,函数也确实本来就有一些属性。比如,对一个函数来说,不管是用什么语法创建的,它会自动拥有一个length属性来标识这个函数期待接受的参数个数: function func(a, b, c) {} console.log(func.length); // 3 -任何时候都可以给函数添加自定义属性。添加自定义属性的一个有用场景是缓存函数的执行结果(返回值),这样下次同样的函数被调用的时候就不需要再做一次那些可能很复杂的计算。缓存一个函数的运行结果也就是为大家所熟知的备忘录(memoization)。 +任何时候都可以给函数添加自定义属性。添加自定义属性的一个有用场景是缓存函数的执行结果(返回值),这样下次同样的函数被调用的时候就不需要再做一次那些可能很复杂的计算。缓存一个函数的运行结果也就是为大家所熟知的Memoization。 -在下面的例子中,myFunc函数创建了一个cache属性,可以通过myFunc.cache访问到。这个cache属性是一个对象(hash表),传给函数的参数会作为对象的key,函数执行结果会作为对象的值。函数的执行结果可以是会用到的任何的复杂数据结构: +在下面的例子中,myFunc函数创建了一个cache属性,可以通过myFunc.cache访问到。这个cache属性是一个对象(hash表),传给函数的参数会作为对象的key,函数执行结果会作为对象的值。函数的执行结果可以是任何的复杂数据结构: var myFunc = function (param) { if (!myFunc.cache[param]) { @@ -696,7 +697,7 @@ o.message; // "call me" // cache storage myFunc.cache = {}; -上面的代码假设函数只接受一个参数param,并且这个参数是基本类型(比如字符串)。如果你有更多更复杂的参数,则通常需要对它们进行序列化。(译注:序列化后来作为缓存对象的key。)比如,你需要将arguments对象序列化为JSON字符串,然后使用JSON字符串作为cache对象的key: +上面的代码假设函数只接受一个参数param,并且这个参数是基本类型(比如字符串)。如果你有更多更复杂的参数,则通常需要对它们进行序列化。比如,你需要将arguments对象序列化为JSON字符串,然后使用JSON字符串作为cache对象的key: var myFunc = function () { @@ -735,9 +736,9 @@ o.message; // "call me" myFunc.cache = {}; -## 对象的配置 +## 配置对象 -对象的配置模式是一种提供更简洁的API的方法,尤其是当你正在写一个即将被其它程序调用的类库或者任何其它代码的时候。 +配置对象模式是一种提供更简洁的API的方法,尤其是当你正在写一个即将被其它程序调用的类库之类的代码的时候。 软件在开发和维护过程中需要不断改变是一个不争的事实。这样的事情总是以一些有限的需求开始,但是随着开发的进行,越来越多的功能会不断被加进来。 @@ -766,19 +767,19 @@ o.message; // "call me" }; addPerson(conf); -对象的配置模式的好处是: +配置对象模式的好处是: - 不需要记住参数的顺序 - 可以很安全地跳过可选参数 - 拥有更好的可读性和可维护性 - 更容易添加和移除参数 -对象的配置模式的坏处是: +配置对象模式的坏处是: - 需要记住参数的名字 - 参数名字不能被压缩 -举些实例,这个模式对创建DOM元素的函数或者是给元素设定CSS样式函数会非常实用,因为元素和CSS样式可能会有很多但是大部分可选的属性。 +举些实例,这个模式对创建DOM元素的函数或者是给元素设定CSS样式的函数会非常实用,因为元素和CSS样式可能会有很多但是大部分可选的属性。 ## 柯里化 (Curry) @@ -818,7 +819,7 @@ o.message; // "call me" 在这个例子中,sayHi()中的this指向alien。而在上一个例子中,this是指向的全局对象。(译注:这个例子的代码有误,最后一行的sayHi并不能访问到alien的sayHi方法,需要使用alien.sayHi.apply(alien, ["humans"])才可正确运行。另外,在sayHi中也没有出现this。) -正如上面两个例子所展现出来的一样,我们所谓的函数调用跟函数应用的一种语法糖没有什么太大的差别。 +正如上面两个例子所展现出来的一样,我们将所谓的函数调用当作函数应用的一种语法糖并没有什么太大的问题。 需要注意的是,除了apply()之外,Function.prototype对象还有一个call()方法,但是它仍然只是apply()的一种语法糖。(译注:这两个方法的区别在于,apply()只接受两个参数,第二个参数为需要传给函数的参数数组,而call()则接受任意多个参数,从第二个开始将参数依次传给函数。)不过有种情况下使用这个语法糖会更好:当你的函数只接受一个参数的时候,你可以省去为唯一的一个元素创建数组的工作: @@ -871,7 +872,7 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 // applying an argument to the new function newadd.apply(null, [4]); // 9 -正如你所看到的一样,部分应用给了我们另一个函数,这个函数可以在稍后调用的时候接受其它的参数。这实际上跟add(5)(4)是等价的,因为add(5)返回了一个函数,这个函数可以使用(4)来调用。我们又一次看到,熟悉的add(5, 4)也跟add(5)(4)的一种语法糖差不了多少。 +正如你所看到的一样,部分应用给了我们另一个函数,这个函数可以在稍后调用的时候接受其它的参数。这实际上跟add(5)(4)是等价的,因为add(5)返回了一个函数,这个函数可以使用(4)来调用。我们又一次看到,熟悉的add(5, 4)也差不多是add(5)(4)的一种语法糖。 现在,让我们回到地球:并不存在这样的一个partialApply()函数,并且函数的默认表现也不会像上面的例子中那样。但是你完全可以自己去写,因为JavaScript的动态特性完全可以做到这样。 @@ -879,7 +880,7 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 ### 柯里化(Currying) -柯里化和辛辣的印度菜可没什么关系;它来自数学家Haskell Curry。(Haskell编程语言也是因他而得名。)柯里化是一个变换的函数的过程。柯里化的另外一个名字也叫schönfinkelisation,来自另一位数学家——Moses Schönfinkelisation——这种变换的最初发明者。 +柯里化和辛辣的印度菜可没什么关系;它来自数学家Haskell Curry。(Haskell编程语言也是因他而得名。)柯里化是一个变换函数的过程。柯里化的另外一个名字也叫schönfinkelisation,来自另一位数学家——Moses Schönfinkelisation——这种变换的最初发明者。 所以我们怎样对一个函数进行柯里化呢?其它的函数式编程语言也许已经原生提供了支持并且所有的函数已经默认柯里化了。在JavaScript中我们可以修改一下add()函数使它柯里化,然后支持部分应用。 @@ -920,7 +921,7 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 return x + y; } -在这些例子中,add()函数自己处理了部分应用。有没有可能用一种更为通用的方式来做同样的事情呢?换句话说,我们能不能对任意一个函数进行处理,得到一个新函数,使它可以处理部分参数?下面的代码片段展示了一个通用函数的例子,我们叫它schonfinkelize(),正是用来做这个的。我们使用schonfinkelize()一部分原因是它比较难发音,另一部分原因是它听起来比较像动词(使用“curry”则不是那么明确),而我们刚好需要一个动词来表明这是一个函数转换的过程。 +在这些例子中,add()函数自己处理了部分应用。有没有可能用一种更为通用的方式来做同样的事情呢?换句话说,我们能不能对任意一个函数进行处理,得到一个新函数,使它可以处理部分参数?下面的代码片段展示了一个通用函数的例子,我们叫它schonfinkelize(),正是用来做这个的。我们使用schonfinkelize()这个名字,一部分原因是它比较难发音,另一部分原因是它听起来比较像动词(使用“curry”则不是那么明确),而我们刚好需要一个动词来表明这是一个函数转换的过程。 这是一个通用的柯里化函数: @@ -934,7 +935,7 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 }; } -这个schonfinkelize可能显得比较复杂了,只是因为在JavaScript中arguments不是一个真的数组。从Array.prototype中借用slice()方法帮助我们将arguments转换成数组,以便能更好地对它进行操作。当schonfinkelize()第一次被调用的时候,它使用slice变量存储了对slice()方法的引用,同时也存储了调用时的参数(stored\_args),参数保留了除第一个以外的,因为第一个参数是要被柯里化的函数。schonfinkelize()返回了一个函数。当这个返回的函数被调用的时候,它可以(通过闭包)访问到已经存储的参数stored\_args和slice。新的函数只需要合并老的部分应用的参数(stored\_args)和新的参数(new\_args),然后将它们应用到原来的函数fn(也可以在装饰中访问到)即可。 +这个schonfinkelize可能显得比较复杂了,只是因为在JavaScript中arguments不是一个真的数组。从Array.prototype中借用slice()方法帮助我们将arguments转换成数组,以便能更好地对它进行操作。当schonfinkelize()第一次被调用的时候,它使用slice变量存储了对slice()方法的引用,同时也存储了调用时的除去第一个之外的参数(stored\_args),因为第一个参数是要被柯里化的函数。schonfinkelize()返回了一个函数。当这个返回的函数被调用的时候,它可以(通过闭包)访问到已经存储的参数stored\_args和slice。新的函数只需要合并老的部分应用的参数(stored\_args)和新的参数(new\_args),然后将它们应用到原来的函数fn(也可以在闭包中访问到)即可。 现在有了通用的柯里化函数,就可以做一些测试了: @@ -973,7 +974,7 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 ##小结 -在JavaScript中,开发者对函数的理解和运用的要求是比较苛刻的。在本章中,主要讨论了有关函数的一些背景知识和术语。你知道了JavaScript函数中两个重要的特性,也就是: +在JavaScript中,开发者对函数的理解和运用的要求是比较苛刻的。在本章中,主要讨论了有关函数的一些背景知识和术语。介绍了JavaScript函数中两个重要的特性,也就是: 1. 函数是一等对象,他们可以被作为值传递,也可以拥有属性和方法。 2. 函数拥有本地作用域,而大括号不产生块级作用域。另外需要注意的是,变量的声明会被提前到本地作用域顶部。 @@ -991,7 +992,7 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 - 回调模式 传入一个函数作为参数 -- 对象的配置 +- 配置对象 帮助保持函数的参数数量可控 - 返回函数 @@ -1008,12 +1009,12 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 - 立即初始化的对象 初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 -- 启动时间程序 +- 条件初始化 使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 -3. 性能模式,这些模式帮助提高代码的性能,包括: +3. 性能模式,这些模式帮助提高代码的执行速度,包括: -- 备忘录 +- Memoization 利用函数的属性,使已经计算过的值不用再次计算 - 自定义函数 From ce1c625c533965369b0261979e88dd92a93ee1c3 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Jun 2012 19:02:08 +0800 Subject: [PATCH 18/55] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E6=AD=A3README=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.markdown | 198 +++++++++++++++++++++++----------------------- chapter4.markdown | 36 ++++++++- 2 files changed, 134 insertions(+), 100 deletions(-) diff --git a/README.markdown b/README.markdown index dea4860..2b98f3e 100644 --- a/README.markdown +++ b/README.markdown @@ -11,105 +11,105 @@ # 目录 -## [第一章 概述](chapter1.markdown) - -- [模式](chapter1.markdown) -- [JavaScript:概念](chapter1.markdown#a2) - - [面向对象](chapter1.markdown#a3) - - [无类](chapter1.markdown#a4) - - [原型](chapter1.markdown#a5) - - [运行环境](chapter1.markdown#a6) -- [ECMAScript 5](chapter1.markdown#a7) -- [JSLint](chapter1.markdown#a8) -- [控制台工具](chapter1.markdown#a9) - -## [第二章 高质量JavaScript基本要点](chapter2.markdown) - -- [编写可维护的代码](chapter2.markdown#a2) -- [减少全局对象](chapter2.markdown#a3) - - [全局对象带来的困扰](chapter2.markdown#a4) - - [忘记var时的副作用](chapter2.markdown#a5) - - [访问全局对象](chapter2.markdown#a6) - - [单 var 模式](chapter2.markdown#a7) - - [声明提前:分散的 var 带来的问题](chapter2.markdown#a8) -- [for 循环](chapter2.markdown#a9) -- [for-in 循环](chapter2.markdown#a10) -- [(不)扩充内置原型](chapter2.markdown#a11) -- [switch 模式](chapter2.markdown#a12) -- [避免隐式类型转换](chapter2.markdown#a13) - - [避免使用 eval()](chapter2.markdown#a14) -- [使用parseInt()进行数字转换](chapter2.markdown#a15) -- [编码风格](chapter2.markdown#a16) - - [缩进](chapter2.markdown#a17) - - [花括号](chapter2.markdown#a18) - - [左花括号的放置](chapter2.markdown#a19) - - [空格](chapter2.markdown#a20) -- [命名规范](chapter2.markdown#a21) - - [构造器命名中的大小写](chapter2.markdown#a22) - - [单词分隔](chapter2.markdown#a23) - - [其他命名风格](chapter2.markdown#a24) -- [书写注释](chapter2.markdown#a25) -- [书写API文档](chapter2.markdown#a26) - - [一个例子:YUIDoc](chapter2.markdown#a27) -- [编写易读的代码](chapter2.markdown#a28) -- [相互评审](chapter2.markdown#a29) -- [生产环境中的代码压缩(Minify)](chapter2.markdown#a30) -- [运行JSLint](chapter2.markdown#a31) -- [小结](chapter2.markdown#a32) - -## [第三章 直接量和构造函数](chapter3.markdown) - -- [对象直接量](chapter3.markdown#a2) - - [对象直接量语法](chapter3.markdown#a3) - - [通过构造函数创建对象](chapter3.markdown#a4) - - [获得对象的构造器](chapter3.markdown#a5) -- [自定义构造函数](chapter3.markdown#a6) - - [构造函数的返回值](chapter3.markdown#a7) -- [强制使用new的模式](chapter3.markdown#a8) - - [命名约定](chapter3.markdown#a9) - - [使用that](chapter3.markdown#a10) - - [调用自身的构造函数](chapter3.markdown#a11) -- [数组直接量](chapter3.markdown#a12) - - [数组直接量语法](chapter3.markdown#a13) - - [有意思的数组构造器](chapter3.markdown#a14) - - [检查是不是数组](chapter3.markdown#a15) -- [JSON](chapter3.markdown#a16) - - [使用JSON](chapter3.markdown#a17) -- [正则表达式直接量](chapter3.markdown#a18) - - [正则表达式直接量语法](chapter3.markdown#a19) -- [原始值的包装对象](chapter3.markdown#a20) -- [Error对象](chapter3.markdown#a21) -- [小结](chapter3.markdown#a22) - -## [第四章 函数](chapter4.markdown#a) - -- [背景知识](chapter4.markdown#a) - - [术语释义](chapter4.markdown#a) - - [声明 vs 表达式:命名与提前](chapter4.markdown#a) - - [函数的name属性](chapter4.markdown#a) - - [函数提前](chapter4.markdown#a) -- [回调模式](chapter4.markdown#a) - - [一个回调的例子](chapter4.markdown#a) - - [回调和作用域](chapter4.markdown#a) - - [异步事件监听](chapter4.markdown#a) - - [超时](chapter4.markdown#a) - - [库中的回调](chapter4.markdown#a) -- [返回函数](chapter4.markdown#a) -- [自定义函数](chapter4.markdown#a) -- [立即执行的函数](chapter4.markdown#a) - - [立即执行的函数的参数](chapter4.markdown#a) - - [立即执行的函数的返回值](chapter4.markdown#a) - - [好处和用法](chapter4.markdown#a) -- [立即初始化的对象](chapter4.markdown#a) -- [启动时间程序](chapter4.markdown#a) -- [函数属性——一种备忘录模式](chapter4.markdown#a) -- [对象的配置](chapter4.markdown#a) -- 柯里化 (Curry) - - 函数应用 - - 部分应用 - - 柯里化 - - 什么时候使用柯里化 -- 小节 +## [第一章 概述](blob/master/chapter1.markdown) + +- [模式](blob/master/chapter1.markdown) +- [JavaScript:概念](blob/master/chapter1.markdown#a2) + - [面向对象](blob/master/chapter1.markdown#a3) + - [无类](blob/master/chapter1.markdown#a4) + - [原型](blob/master/chapter1.markdown#a5) + - [运行环境](blob/master/chapter1.markdown#a6) +- [ECMAScript 5](blob/master/chapter1.markdown#a7) +- [JSLint](blob/master/chapter1.markdown#a8) +- [控制台工具](blob/master/chapter1.markdown#a9) + +## [第二章 高质量JavaScript基本要点](blob/master/chapter2.markdown) + +- [编写可维护的代码](blob/master/chapter2.markdown#a2) +- [减少全局对象](blob/master/chapter2.markdown#a3) + - [全局对象带来的困扰](blob/master/chapter2.markdown#a4) + - [忘记var时的副作用](blob/master/chapter2.markdown#a5) + - [访问全局对象](blob/master/chapter2.markdown#a6) + - [单 var 模式](blob/master/chapter2.markdown#a7) + - [声明提前:分散的 var 带来的问题](blob/master/chapter2.markdown#a8) +- [for 循环](blob/master/chapter2.markdown#a9) +- [for-in 循环](blob/master/chapter2.markdown#a10) +- [(不)扩充内置原型](blob/master/chapter2.markdown#a11) +- [switch 模式](blob/master/chapter2.markdown#a12) +- [避免隐式类型转换](blob/master/chapter2.markdown#a13) + - [避免使用 eval()](blob/master/chapter2.markdown#a14) +- [使用parseInt()进行数字转换](blob/master/chapter2.markdown#a15) +- [编码风格](blob/master/chapter2.markdown#a16) + - [缩进](blob/master/chapter2.markdown#a17) + - [花括号](blob/master/chapter2.markdown#a18) + - [左花括号的放置](blob/master/chapter2.markdown#a19) + - [空格](blob/master/chapter2.markdown#a20) +- [命名规范](blob/master/chapter2.markdown#a21) + - [构造器命名中的大小写](blob/master/chapter2.markdown#a22) + - [单词分隔](blob/master/chapter2.markdown#a23) + - [其他命名风格](blob/master/chapter2.markdown#a24) +- [书写注释](blob/master/chapter2.markdown#a25) +- [书写API文档](blob/master/chapter2.markdown#a26) + - [一个例子:YUIDoc](blob/master/chapter2.markdown#a27) +- [编写易读的代码](blob/master/chapter2.markdown#a28) +- [相互评审](blob/master/chapter2.markdown#a29) +- [生产环境中的代码压缩(Minify)](blob/master/chapter2.markdown#a30) +- [运行JSLint](blob/master/chapter2.markdown#a31) +- [小结](blob/master/chapter2.markdown#a32) + +## [第三章 直接量和构造函数](blob/master/chapter3.markdown) + +- [对象直接量](blob/master/chapter3.markdown#a2) + - [对象直接量语法](blob/master/chapter3.markdown#a3) + - [通过构造函数创建对象](blob/master/chapter3.markdown#a4) + - [获得对象的构造器](blob/master/chapter3.markdown#a5) +- [自定义构造函数](blob/master/chapter3.markdown#a6) + - [构造函数的返回值](blob/master/chapter3.markdown#a7) +- [强制使用new的模式](blob/master/chapter3.markdown#a8) + - [命名约定](blob/master/chapter3.markdown#a9) + - [使用that](blob/master/chapter3.markdown#a10) + - [调用自身的构造函数](blob/master/chapter3.markdown#a11) +- [数组直接量](blob/master/chapter3.markdown#a12) + - [数组直接量语法](blob/master/chapter3.markdown#a13) + - [有意思的数组构造器](blob/master/chapter3.markdown#a14) + - [检查是不是数组](blob/master/chapter3.markdown#a15) +- [JSON](blob/master/chapter3.markdown#a16) + - [使用JSON](blob/master/chapter3.markdown#a17) +- [正则表达式直接量](blob/master/chapter3.markdown#a18) + - [正则表达式直接量语法](blob/master/chapter3.markdown#a19) +- [原始值的包装对象](blob/master/chapter3.markdown#a20) +- [Error对象](blob/master/chapter3.markdown#a21) +- [小结](blob/master/chapter3.markdown#a22) + +## [第四章 函数](blob/master/chapter4.markdown) + +- [背景知识](blob/master/chapter4.markdown#a2) + - [术语释义](blob/master/chapter4.markdown#a3) + - [声明 vs 表达式:命名与提前](blob/master/chapter4.markdown#a4) + - [函数的name属性](blob/master/chapter4.markdown#a5) + - [函数提前](blob/master/chapter4.markdown#a6) +- [回调模式](blob/master/chapter4.markdown#a7) + - [一个回调的例子](blob/master/chapter4.markdown#a8) + - [回调和作用域](blob/master/chapter4.markdown#a9) + - [异步事件监听](blob/master/chapter4.markdown#a10) + - [超时](blob/master/chapter4.markdown#a11) + - [库中的回调](blob/master/chapter4.markdown#a12) +- [返回函数](blob/master/chapter4.markdown#a12) +- [自定义函数](blob/master/chapter4.markdown#a14) +- [立即执行的函数](blob/master/chapter4.markdown#a15) + - [立即执行的函数的参数](blob/master/chapter4.markdown#a16) + - [立即执行的函数的返回值](blob/master/chapter4.markdown#a17) + - [好处和用法](blob/master/chapter4.markdown#a18) +- [立即初始化的对象](blob/master/chapter4.markdown#a19) +- [条件初始化](blob/master/chapter4.markdown#a20) +- [函数属性——Memoization模式](blob/master/chapter4.markdown#a21) +- [配置对象](blob/master/chapter4.markdown#a22) +- [柯里化 (Curry)](blob/master/chapter4.markdown#a23) + - [函数应用](blob/master/chapter4.markdown#a24) + - [部分应用](blob/master/chapter4.markdown#a25) + - [柯里化](blob/master/chapter4.markdown#a26) + - [什么时候使用柯里化](blob/master/chapter4.markdown#a27) +- [小结](blob/master/chapter4.markdown#a28) ## 第五章 对象创建模式 diff --git a/chapter4.markdown b/chapter4.markdown index 8d172ba..8bb2ee5 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -1,3 +1,4 @@ + # 函数 熟练运用函数是JavaScript程序员的必备技能,因为在JavaScript中函数实在是太常用了。它能够完成的任务种类非常之多,而在其他语言中则需要很多特殊的语法支持才能达到这种能力。 @@ -6,6 +7,8 @@ 现在让我们来一起揭秘JavaScript函数,我们首先从一些背景知识开始说起。 + + ## 背景知识 JavaScript的函数具有两个主要特性,正是这两个特性让它们与众不同。第一个特性是,函数是一等对象(first-class object),第二个是函数提供作用域支持。 @@ -30,6 +33,7 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 函数的第二个重要特性是它能提供作用域支持。在JavaScript中没有块级作用域(译注:在JavaScript1.7中提供了块级作用域部分特性的支持,可以通过let来声明块级作用域内的“局部变量”),也就是说不能通过花括号来创建作用域,JavaScript中只有函数作用域(译注:这里作者的表述只针对函数而言,此外JavaScript还有全局作用域)。在函数内所有通过var声明的变量都是局部变量,在函数外部是不可见的。刚才所指花括号无法提供作用域支持的意思是说,如果在if条件句内、或在for或while循环体内用var定义了变量,这个变量并不是属于if语句或for(while)循环的局部变量,而是属于它所在的函数。如果不在任何函数内部,它会成为全局变量。在第二章里提到我们要减少对全局命名空间的污染,那么使用函数则是控制变量的作用域的不二之选。 + ### 术语释义 首先我们先简单讨论下创建函数相关的术语,因为精确无歧义的术语约定和我们所讨论的各种模式一样重要。 @@ -66,7 +70,7 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 >另外我们经常看到“函数直接量”。它用来表示函数表达式或带命名的函数表达式。由于这个术语是有歧义的,所以最好不要用它。 - + ### 声明 vs 表达式:命名与提前 那么,到底应该用哪个呢?函数声明还是函数表达式?在不能使用函数声明语法的场景下,只能使用函数表达式了。下面这个例子中,我们给函数传入了另一个函数对象作为参数,以及给对象定义方法: @@ -102,6 +106,7 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 return bar; } + ### 函数的name属性 选择函数定义模式的另一个考虑是只读属性name的可用性。尽管标准规范中并未规定,但很多运行环境都实现了name属性,在函数声明和带有名字的函数表达式中是有name的属性定义的。在匿名函数表达式中,则不一定有定义,这个是和实现相关的,在IE中是无定义的,在Firefox和Safari中是有定义的,但是值为空字符串。 @@ -120,6 +125,7 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 >我们可以将一个带名字的函数表达式赋值给变量,变量名和函数名不同,这在技术上是可行的。比如:`var foo = function bar(){};`。然而,这种用法的行为在浏览器中的兼容性不佳(特别是IE中),因此并不推荐大家使用这种模式。 + ### 函数提前 通过前面的讲解,你可能以为函数声明和带名字的函数表达式是完全等价的。事实上不是这样,主要区别在于“声明提前”的行为。 @@ -170,6 +176,8 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 - 函数是对象 - 函数提供局部变量作用域 + + ## 回调模式 函数是对象,也就意味着函数可以当作参数传入另外一个函数中。当你给函数writeCode()传入一个函数参数introduceBugs(),在某个时刻writeCode()执行了(或调用了)introduceBugs()。在这种情况下,我们说introduceBugs()是一个“回调函数”,简称“回调”: @@ -188,6 +196,7 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 注意introduceBugs()是如何作为参数传入writeCode()的,当作参数的函数不带括号。括号的意思是执行函数,而这里我们希望传入一个引用,让writeCode()在合适的时机执行它(调用它)。 + ### 一个回调的例子 我们从一个例子开始,首先介绍无回调的情况,然后在作修改。假设你有一个通用的函数,用来完成某种复杂的逻辑并返回一大段数据。假设我们用findNodes()来命名这个通用函数,这个函数用来对DOM树进行遍历,并返回我所感兴趣的页面节点: @@ -262,6 +271,7 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 node.style.display = "block"; }); + ### 回调和作用域 在上一个例子中,执行回调函数的写法是: @@ -329,6 +339,7 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 // ... }; + ### 异步事件监听 JavaScript中的回调模式已经是我们的家常便饭了,比如,如果你给网页中的元素绑定事件,则需要提供回调函数的引用,以便事件发生时能调用到它。这里有一个简单的例子,我们将console.log()作为回调函数绑定了document的点击事件: @@ -339,6 +350,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 “不要打电话给我,我会打给你”,这是好莱坞很有名的一句话,很多电影都有这句台词。电影中的主角不可能同时应答很多个电话呼叫。在JavaScript的异步事件模型中也是同样的道理。电影中是留下电话号码,JavaScript中是提供一个回调函数,当时机成熟时就触发回调。有时甚至提供了很多回调,有些回调压根是没用的,但由于这个事件可能永远不会发生,因此这些回调的逻辑也不会执行。比如,假设你从此不再用“鼠标点击”,那么你之前绑定的鼠标点击的回调函数则永远也不会执行。 + ### 超时 另外一个最常用的回调模式是在调用超时函数时,超时函数是浏览器window对象的方法,共有两个:setTimeout()和setInterval()。这两个方法的参数都是回调函数。 @@ -350,10 +362,13 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 再次需要注意,函数thePlotThickens是作为变量传入setTimeout的,它不带括号,如果带括号的话则立即执行了,这里只是用到这个函数的引用,以便在setTimeout的逻辑中调用到它。也可以传入字符串“thePlotThickens()”,但这是一种反模式,和eval()一样不推荐使用。 + ### 库中的回调 回调模式非常简单,但又很强大。可以随手拈来灵活运用,因此这种模式在库的设计中也非常得宠。库的代码要尽可能的保持通用和重用,而回调模式则可帮助库的作者完成这个目标。你不必预料和实现你所想到的所有情形,因为这会让库变的膨胀而臃肿,而且大多数用户并不需要这些多余的特性支持。相反,你将精力放在核心功能的实现上,提供回调的入口作为“钩子”,可以让库的方法变得可扩展、可定制。 + + ## 返回函数 函数是对象,因此当然可以作为返回值。也就是说,函数不一定非要返回一坨数据,函数可以返回另外一个定制好的函数,或者可以根据输入的不同按需创造另外一个函数。 @@ -386,6 +401,8 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 next(); // 2 next(); // 3 + + ## 自定义函数 我们动态定义函数,并将函数赋值给变量。如果将你定义的函数赋值给已经存在的函数变量的话,则新函数会覆盖旧函数。这样做的结果是,旧函数的引用就丢弃掉了,变量中所存储的引用值替换成了新的。这样看起来这个变量指代的函数逻辑就发生了变化,或者说函数进行了“重新定义”或“重写”。说起来有些拗口,实际上并不复杂,来看一个例子: @@ -445,6 +462,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 从结果来看,当自定义函数被赋值给一个新的变量的时候,这段使用自定义函数的代码的执行结果与我们期望的结果可能并不一样。每当prank()运行的时候,它都弹出“Boo!”。同时它也重写了scareMe()函数,但是prank()自己仍然能够使用之前的定义,包括属性property。在这个函数被作为spooky对象的boo()方法调用的时候,结果也一样。所有的这些调用,在第一次的时候就已经修改了全局的scareMe()的指向,所以当它最终被调用的时候,它的函数体已经被修改为弹出“Double boo”。它也就不能获取到新添加的属性“property”。 + ## 立即执行的函数 立即执行的函数是一种语法模式,它会使函数在定义后立即执行。看这个例子: @@ -481,6 +499,8 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 如果这段代码没有被包裹到立即执行函数中,那么变量days、today、msg都会是全局变量,而这些变量仅仅是由因为初始化而遗留下来的垃圾,没有任何用处。 + + ### 立即执行的函数的参数 立即执行的函数也可以接受参数,看这个例子: @@ -498,6 +518,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 值得注意的是,一般情况下尽量不要给立即执行的函数传入太多的参数,否则会有一件麻烦的事情,就是你在阅读代码的时候需要频繁地上下滚动代码。 + ### 立即执行的函数的返回值 和其它的函数一样,立即执行的函数也可以返回值,并且这些返回值也可以被赋值给变量: @@ -551,6 +572,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 在这个例子中,o.message是一个字符串,而不是一个函数,但是它需要一个函数在脚本载入后来得到这个属性值。 + ### 好处和用法 立即执行的函数应用很广泛。它可以帮助我们做一些不想留下全局变量的工作。所有定义的变量都只是立即执行的函数的本地变量,你完全不用担心临时变量会污染全局对象。 @@ -572,6 +594,8 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 套用这个模板,你就可以编写其它的模块。然后在发布到线上的时候,你就可以决定在这个时间节点上哪些特性是可以使用的,然后使用发布脚本将它们打包上线。 + + ## 立即初始化的对象 还有另外一种可以避免污染全局作用域的方法,和前面描述的立即执行的函数相似,叫做“立即初始化的对象”模式。这种模式使用一个带有init()方法的对象来实现,这个方法在对象被创建后立即执行。初始化的工作由init()函数来完成。 @@ -612,6 +636,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 > 这种模式主要用于一些一次性的工作,并且在init()方法执行完后就无法再次访问到这个对象。如果希望在这些工作完成后保持对对象的引用,只需要简单地在init()的末尾加上return this;即可。 + ## 条件初始化 条件初始化(也叫条件加载)是一种优化模式。当你知道某种条件在整个程序生命周期中都不会变化的时候,那么对这个条件的探测只做一次就很有意义。浏览器探测(或者特征检测)是一个典型的例子。 @@ -674,6 +699,8 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 说到这里,要特别提醒一下关于浏览器探测的事情。当你使用这个模式的时候,不要对浏览器特性过度假设。举个例子,如果你探测到浏览器不支持window.addEventListener,不要假设这个浏览器是IE,也不要认为它不支持原生的XMLHttpRequest,虽然这个结论在整个浏览器历史上的某个点是正确的。当然,也有一些情况是可以放心地做一些特性假设的,比如.addEventListener和.removeEventListerner,但是通常来讲,浏览器的特性在发生变化时都是独立的。最好的策略就是分别探测每个特性,然后使用条件初始化,使这种探测只做一次。 + + ## 函数属性——Memoization模式 函数也是对象,所以它们可以有属性。事实上,函数也确实本来就有一些属性。比如,对一个函数来说,不管是用什么语法创建的,它会自动拥有一个length属性来标识这个函数期待接受的参数个数: @@ -736,6 +763,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 myFunc.cache = {}; + ## 配置对象 配置对象模式是一种提供更简洁的API的方法,尤其是当你正在写一个即将被其它程序调用的类库之类的代码的时候。 @@ -782,10 +810,12 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 举些实例,这个模式对创建DOM元素的函数或者是给元素设定CSS样式的函数会非常实用,因为元素和CSS样式可能会有很多但是大部分可选的属性。 + ## 柯里化 (Curry) 在本章剩下的部分,我们将讨论一下关于柯里化和部分应用的话题。但是在我们开始这个话题之前,先看一下到底什么是函数应用。 + ### 函数应用 在一些纯粹的函数式编程语言中,对函数的描述不是被调用(called或者invoked),而是被应用(applied)。在JavaScript中也有同样的东西——我们可以使用Function.prototype.apply()来应用一个函数,因为在JavaScript中,函数实际上是对象,并且他们拥有方法。 @@ -827,6 +857,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 sayHi.apply(alien, ["humans"]); // "Hello, humans!" sayHi.call(alien, "humans"); // "Hello, humans!" + ### 部分应用 现在我们知道了,调用一个函数实际上就是给它应用一堆参数,那是否能够只传一部分参数而不传全部呢?这实际上跟我们手工处理数学函数非常类似。 @@ -878,6 +909,7 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 让函数理解并且处理部分应用的过程,叫柯里化(Currying)。 + ### 柯里化(Currying) 柯里化和辛辣的印度菜可没什么关系;它来自数学家Haskell Curry。(Haskell编程语言也是因他而得名。)柯里化是一个变换函数的过程。柯里化的另外一个名字也叫schönfinkelisation,来自另一位数学家——Moses Schönfinkelisation——这种变换的最初发明者。 @@ -967,11 +999,13 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 var addSix = schonfinkelize(addOne, 2, 3); addSix(5, 5); // 16 + ### 什么时候使用柯里化 当你发现自己在调用同样的函数并且传入的参数大部分都相同的时候,就是考虑柯里化的理想场景了。你可以通过传入一部分的参数动态地创建一个新的函数。这个新函数会存储那些重复的参数(所以你不需要再每次都传入),然后再在调用原始函数的时候将整个参数列表补全,正如原始函数期待的那样。 + ##小结 在JavaScript中,开发者对函数的理解和运用的要求是比较苛刻的。在本章中,主要讨论了有关函数的一些背景知识和术语。介绍了JavaScript函数中两个重要的特性,也就是: From 8e486cc74ec7fe714ec51fdd5bcb249c3276fed1 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Jun 2012 19:03:39 +0800 Subject: [PATCH 19/55] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.markdown | 198 ++++++++++++++++++++++++------------------------ 1 file changed, 99 insertions(+), 99 deletions(-) diff --git a/README.markdown b/README.markdown index 2b98f3e..f91e753 100644 --- a/README.markdown +++ b/README.markdown @@ -11,105 +11,105 @@ # 目录 -## [第一章 概述](blob/master/chapter1.markdown) - -- [模式](blob/master/chapter1.markdown) -- [JavaScript:概念](blob/master/chapter1.markdown#a2) - - [面向对象](blob/master/chapter1.markdown#a3) - - [无类](blob/master/chapter1.markdown#a4) - - [原型](blob/master/chapter1.markdown#a5) - - [运行环境](blob/master/chapter1.markdown#a6) -- [ECMAScript 5](blob/master/chapter1.markdown#a7) -- [JSLint](blob/master/chapter1.markdown#a8) -- [控制台工具](blob/master/chapter1.markdown#a9) - -## [第二章 高质量JavaScript基本要点](blob/master/chapter2.markdown) - -- [编写可维护的代码](blob/master/chapter2.markdown#a2) -- [减少全局对象](blob/master/chapter2.markdown#a3) - - [全局对象带来的困扰](blob/master/chapter2.markdown#a4) - - [忘记var时的副作用](blob/master/chapter2.markdown#a5) - - [访问全局对象](blob/master/chapter2.markdown#a6) - - [单 var 模式](blob/master/chapter2.markdown#a7) - - [声明提前:分散的 var 带来的问题](blob/master/chapter2.markdown#a8) -- [for 循环](blob/master/chapter2.markdown#a9) -- [for-in 循环](blob/master/chapter2.markdown#a10) -- [(不)扩充内置原型](blob/master/chapter2.markdown#a11) -- [switch 模式](blob/master/chapter2.markdown#a12) -- [避免隐式类型转换](blob/master/chapter2.markdown#a13) - - [避免使用 eval()](blob/master/chapter2.markdown#a14) -- [使用parseInt()进行数字转换](blob/master/chapter2.markdown#a15) -- [编码风格](blob/master/chapter2.markdown#a16) - - [缩进](blob/master/chapter2.markdown#a17) - - [花括号](blob/master/chapter2.markdown#a18) - - [左花括号的放置](blob/master/chapter2.markdown#a19) - - [空格](blob/master/chapter2.markdown#a20) -- [命名规范](blob/master/chapter2.markdown#a21) - - [构造器命名中的大小写](blob/master/chapter2.markdown#a22) - - [单词分隔](blob/master/chapter2.markdown#a23) - - [其他命名风格](blob/master/chapter2.markdown#a24) -- [书写注释](blob/master/chapter2.markdown#a25) -- [书写API文档](blob/master/chapter2.markdown#a26) - - [一个例子:YUIDoc](blob/master/chapter2.markdown#a27) -- [编写易读的代码](blob/master/chapter2.markdown#a28) -- [相互评审](blob/master/chapter2.markdown#a29) -- [生产环境中的代码压缩(Minify)](blob/master/chapter2.markdown#a30) -- [运行JSLint](blob/master/chapter2.markdown#a31) -- [小结](blob/master/chapter2.markdown#a32) - -## [第三章 直接量和构造函数](blob/master/chapter3.markdown) - -- [对象直接量](blob/master/chapter3.markdown#a2) - - [对象直接量语法](blob/master/chapter3.markdown#a3) - - [通过构造函数创建对象](blob/master/chapter3.markdown#a4) - - [获得对象的构造器](blob/master/chapter3.markdown#a5) -- [自定义构造函数](blob/master/chapter3.markdown#a6) - - [构造函数的返回值](blob/master/chapter3.markdown#a7) -- [强制使用new的模式](blob/master/chapter3.markdown#a8) - - [命名约定](blob/master/chapter3.markdown#a9) - - [使用that](blob/master/chapter3.markdown#a10) - - [调用自身的构造函数](blob/master/chapter3.markdown#a11) -- [数组直接量](blob/master/chapter3.markdown#a12) - - [数组直接量语法](blob/master/chapter3.markdown#a13) - - [有意思的数组构造器](blob/master/chapter3.markdown#a14) - - [检查是不是数组](blob/master/chapter3.markdown#a15) -- [JSON](blob/master/chapter3.markdown#a16) - - [使用JSON](blob/master/chapter3.markdown#a17) -- [正则表达式直接量](blob/master/chapter3.markdown#a18) - - [正则表达式直接量语法](blob/master/chapter3.markdown#a19) -- [原始值的包装对象](blob/master/chapter3.markdown#a20) -- [Error对象](blob/master/chapter3.markdown#a21) -- [小结](blob/master/chapter3.markdown#a22) - -## [第四章 函数](blob/master/chapter4.markdown) - -- [背景知识](blob/master/chapter4.markdown#a2) - - [术语释义](blob/master/chapter4.markdown#a3) - - [声明 vs 表达式:命名与提前](blob/master/chapter4.markdown#a4) - - [函数的name属性](blob/master/chapter4.markdown#a5) - - [函数提前](blob/master/chapter4.markdown#a6) -- [回调模式](blob/master/chapter4.markdown#a7) - - [一个回调的例子](blob/master/chapter4.markdown#a8) - - [回调和作用域](blob/master/chapter4.markdown#a9) - - [异步事件监听](blob/master/chapter4.markdown#a10) - - [超时](blob/master/chapter4.markdown#a11) - - [库中的回调](blob/master/chapter4.markdown#a12) -- [返回函数](blob/master/chapter4.markdown#a12) -- [自定义函数](blob/master/chapter4.markdown#a14) -- [立即执行的函数](blob/master/chapter4.markdown#a15) - - [立即执行的函数的参数](blob/master/chapter4.markdown#a16) - - [立即执行的函数的返回值](blob/master/chapter4.markdown#a17) - - [好处和用法](blob/master/chapter4.markdown#a18) -- [立即初始化的对象](blob/master/chapter4.markdown#a19) -- [条件初始化](blob/master/chapter4.markdown#a20) -- [函数属性——Memoization模式](blob/master/chapter4.markdown#a21) -- [配置对象](blob/master/chapter4.markdown#a22) -- [柯里化 (Curry)](blob/master/chapter4.markdown#a23) - - [函数应用](blob/master/chapter4.markdown#a24) - - [部分应用](blob/master/chapter4.markdown#a25) - - [柯里化](blob/master/chapter4.markdown#a26) - - [什么时候使用柯里化](blob/master/chapter4.markdown#a27) -- [小结](blob/master/chapter4.markdown#a28) +## [第一章 概述](javascript.patterns/blob/master/chapter1.markdown) + +- [模式](javascript.patterns/blob/master/chapter1.markdown) +- [JavaScript:概念](javascript.patterns/blob/master/chapter1.markdown#a2) + - [面向对象](javascript.patterns/blob/master/chapter1.markdown#a3) + - [无类](javascript.patterns/blob/master/chapter1.markdown#a4) + - [原型](javascript.patterns/blob/master/chapter1.markdown#a5) + - [运行环境](javascript.patterns/blob/master/chapter1.markdown#a6) +- [ECMAScript 5](javascript.patterns/blob/master/chapter1.markdown#a7) +- [JSLint](javascript.patterns/blob/master/chapter1.markdown#a8) +- [控制台工具](javascript.patterns/blob/master/chapter1.markdown#a9) + +## [第二章 高质量JavaScript基本要点](javascript.patterns/blob/master/chapter2.markdown) + +- [编写可维护的代码](javascript.patterns/blob/master/chapter2.markdown#a2) +- [减少全局对象](javascript.patterns/blob/master/chapter2.markdown#a3) + - [全局对象带来的困扰](javascript.patterns/blob/master/chapter2.markdown#a4) + - [忘记var时的副作用](javascript.patterns/blob/master/chapter2.markdown#a5) + - [访问全局对象](javascript.patterns/blob/master/chapter2.markdown#a6) + - [单 var 模式](javascript.patterns/blob/master/chapter2.markdown#a7) + - [声明提前:分散的 var 带来的问题](javascript.patterns/blob/master/chapter2.markdown#a8) +- [for 循环](javascript.patterns/blob/master/chapter2.markdown#a9) +- [for-in 循环](javascript.patterns/blob/master/chapter2.markdown#a10) +- [(不)扩充内置原型](javascript.patterns/blob/master/chapter2.markdown#a11) +- [switch 模式](javascript.patterns/blob/master/chapter2.markdown#a12) +- [避免隐式类型转换](javascript.patterns/blob/master/chapter2.markdown#a13) + - [避免使用 eval()](javascript.patterns/blob/master/chapter2.markdown#a14) +- [使用parseInt()进行数字转换](javascript.patterns/blob/master/chapter2.markdown#a15) +- [编码风格](javascript.patterns/blob/master/chapter2.markdown#a16) + - [缩进](javascript.patterns/blob/master/chapter2.markdown#a17) + - [花括号](javascript.patterns/blob/master/chapter2.markdown#a18) + - [左花括号的放置](javascript.patterns/blob/master/chapter2.markdown#a19) + - [空格](javascript.patterns/blob/master/chapter2.markdown#a20) +- [命名规范](javascript.patterns/blob/master/chapter2.markdown#a21) + - [构造器命名中的大小写](javascript.patterns/blob/master/chapter2.markdown#a22) + - [单词分隔](javascript.patterns/blob/master/chapter2.markdown#a23) + - [其他命名风格](javascript.patterns/blob/master/chapter2.markdown#a24) +- [书写注释](javascript.patterns/blob/master/chapter2.markdown#a25) +- [书写API文档](javascript.patterns/blob/master/chapter2.markdown#a26) + - [一个例子:YUIDoc](javascript.patterns/blob/master/chapter2.markdown#a27) +- [编写易读的代码](javascript.patterns/blob/master/chapter2.markdown#a28) +- [相互评审](javascript.patterns/blob/master/chapter2.markdown#a29) +- [生产环境中的代码压缩(Minify)](javascript.patterns/blob/master/chapter2.markdown#a30) +- [运行JSLint](javascript.patterns/blob/master/chapter2.markdown#a31) +- [小结](javascript.patterns/blob/master/chapter2.markdown#a32) + +## [第三章 直接量和构造函数](javascript.patterns/blob/master/chapter3.markdown) + +- [对象直接量](javascript.patterns/blob/master/chapter3.markdown#a2) + - [对象直接量语法](javascript.patterns/blob/master/chapter3.markdown#a3) + - [通过构造函数创建对象](javascript.patterns/blob/master/chapter3.markdown#a4) + - [获得对象的构造器](javascript.patterns/blob/master/chapter3.markdown#a5) +- [自定义构造函数](javascript.patterns/blob/master/chapter3.markdown#a6) + - [构造函数的返回值](javascript.patterns/blob/master/chapter3.markdown#a7) +- [强制使用new的模式](javascript.patterns/blob/master/chapter3.markdown#a8) + - [命名约定](javascript.patterns/blob/master/chapter3.markdown#a9) + - [使用that](javascript.patterns/blob/master/chapter3.markdown#a10) + - [调用自身的构造函数](javascript.patterns/blob/master/chapter3.markdown#a11) +- [数组直接量](javascript.patterns/blob/master/chapter3.markdown#a12) + - [数组直接量语法](javascript.patterns/blob/master/chapter3.markdown#a13) + - [有意思的数组构造器](javascript.patterns/blob/master/chapter3.markdown#a14) + - [检查是不是数组](javascript.patterns/blob/master/chapter3.markdown#a15) +- [JSON](javascript.patterns/blob/master/chapter3.markdown#a16) + - [使用JSON](javascript.patterns/blob/master/chapter3.markdown#a17) +- [正则表达式直接量](javascript.patterns/blob/master/chapter3.markdown#a18) + - [正则表达式直接量语法](javascript.patterns/blob/master/chapter3.markdown#a19) +- [原始值的包装对象](javascript.patterns/blob/master/chapter3.markdown#a20) +- [Error对象](javascript.patterns/blob/master/chapter3.markdown#a21) +- [小结](javascript.patterns/blob/master/chapter3.markdown#a22) + +## [第四章 函数](javascript.patterns/blob/master/chapter4.markdown) + +- [背景知识](javascript.patterns/blob/master/chapter4.markdown#a2) + - [术语释义](javascript.patterns/blob/master/chapter4.markdown#a3) + - [声明 vs 表达式:命名与提前](javascript.patterns/blob/master/chapter4.markdown#a4) + - [函数的name属性](javascript.patterns/blob/master/chapter4.markdown#a5) + - [函数提前](javascript.patterns/blob/master/chapter4.markdown#a6) +- [回调模式](javascript.patterns/blob/master/chapter4.markdown#a7) + - [一个回调的例子](javascript.patterns/blob/master/chapter4.markdown#a8) + - [回调和作用域](javascript.patterns/blob/master/chapter4.markdown#a9) + - [异步事件监听](javascript.patterns/blob/master/chapter4.markdown#a10) + - [超时](javascript.patterns/blob/master/chapter4.markdown#a11) + - [库中的回调](javascript.patterns/blob/master/chapter4.markdown#a12) +- [返回函数](javascript.patterns/blob/master/chapter4.markdown#a12) +- [自定义函数](javascript.patterns/blob/master/chapter4.markdown#a14) +- [立即执行的函数](javascript.patterns/blob/master/chapter4.markdown#a15) + - [立即执行的函数的参数](javascript.patterns/blob/master/chapter4.markdown#a16) + - [立即执行的函数的返回值](javascript.patterns/blob/master/chapter4.markdown#a17) + - [好处和用法](javascript.patterns/blob/master/chapter4.markdown#a18) +- [立即初始化的对象](javascript.patterns/blob/master/chapter4.markdown#a19) +- [条件初始化](javascript.patterns/blob/master/chapter4.markdown#a20) +- [函数属性——Memoization模式](javascript.patterns/blob/master/chapter4.markdown#a21) +- [配置对象](javascript.patterns/blob/master/chapter4.markdown#a22) +- [柯里化 (Curry)](javascript.patterns/blob/master/chapter4.markdown#a23) + - [函数应用](javascript.patterns/blob/master/chapter4.markdown#a24) + - [部分应用](javascript.patterns/blob/master/chapter4.markdown#a25) + - [柯里化](javascript.patterns/blob/master/chapter4.markdown#a26) + - [什么时候使用柯里化](javascript.patterns/blob/master/chapter4.markdown#a27) +- [小结](javascript.patterns/blob/master/chapter4.markdown#a28) ## 第五章 对象创建模式 From 530467a271ea40af1d11a0f2c2212d88f96acb03 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Jun 2012 19:07:47 +0800 Subject: [PATCH 20/55] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=B0=8F=E7=BB=93?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E5=88=97=E8=A1=A8=E8=A1=A8=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 8bb2ee5..9984640 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -1023,33 +1023,33 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 1. API模式,它们帮助我们为函数给出更干净的接口,包括: -- 回调模式 -传入一个函数作为参数 + - 回调模式 + - 传入一个函数作为参数 -- 配置对象 -帮助保持函数的参数数量可控 + - 配置对象 + - 帮助保持函数的参数数量可控 -- 返回函数 -函数的返回值是另一个函数 + - 返回函数 + - 函数的返回值是另一个函数 -- 柯里化 -新函数在已有函数的基础上再加上一部分参数构成 + - 柯里化 + - 新函数在已有函数的基础上再加上一部分参数构成 2. 初始化模式,这些模式帮助我们用一种干净的、结构化的方法来做一些初始化工作(在web页面和应用中非常常见),通过一些临时变量来保证不污染全局命名空间。这些模式包括: -- 立即执行的函数 -当它们被定义后立即执行 + - 立即执行的函数 + - 当它们被定义后立即执行 -- 立即初始化的对象 -初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 + - 立即初始化的对象 + - 初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 -- 条件初始化 -使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 + - 条件初始化 + - 使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 3. 性能模式,这些模式帮助提高代码的执行速度,包括: -- Memoization -利用函数的属性,使已经计算过的值不用再次计算 + - Memoization + - 利用函数的属性,使已经计算过的值不用再次计算 -- 自定义函数 -重写自身的函数体,使第二次及后续的调用做更少的工作 \ No newline at end of file + - 自定义函数 + - 重写自身的函数体,使第二次及后续的调用做更少的工作 \ No newline at end of file From b3d83bba2aa9240c66e188266ee6568021b00f31 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Jun 2012 19:07:47 +0800 Subject: [PATCH 21/55] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=B0=8F=E7=BB=93?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E5=88=97=E8=A1=A8=E8=A1=A8=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 8bb2ee5..21457d9 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -1023,33 +1023,33 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 1. API模式,它们帮助我们为函数给出更干净的接口,包括: -- 回调模式 -传入一个函数作为参数 + - 回调模式 + - 传入一个函数作为参数 -- 配置对象 -帮助保持函数的参数数量可控 + - 配置对象 + - 帮助保持函数的参数数量可控 -- 返回函数 -函数的返回值是另一个函数 + - 返回函数 + - 函数的返回值是另一个函数 -- 柯里化 -新函数在已有函数的基础上再加上一部分参数构成 + - 柯里化 + - 新函数在已有函数的基础上再加上一部分参数构成 2. 初始化模式,这些模式帮助我们用一种干净的、结构化的方法来做一些初始化工作(在web页面和应用中非常常见),通过一些临时变量来保证不污染全局命名空间。这些模式包括: -- 立即执行的函数 -当它们被定义后立即执行 + - 立即执行的函数 + - 当它们被定义后立即执行 -- 立即初始化的对象 -初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 + - 立即初始化的对象 + - 初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 -- 条件初始化 -使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 + - 条件初始化 + - 使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 3. 性能模式,这些模式帮助提高代码的执行速度,包括: -- Memoization -利用函数的属性,使已经计算过的值不用再次计算 + - Memoization + - 利用函数的属性,使已经计算过的值不用再次计算 -- 自定义函数 -重写自身的函数体,使第二次及后续的调用做更少的工作 \ No newline at end of file + - 自定义函数 + - 重写自身的函数体,使第二次及后续的调用做更少的工作 \ No newline at end of file From 5395354cca44f93fbd3595d3382ecfb2989c6a62 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Jun 2012 19:18:52 +0800 Subject: [PATCH 22/55] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=B0=8F=E7=BB=93?= =?UTF-8?q?=E4=B8=AD=E5=88=97=E8=A1=A8=E7=9A=84=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 21457d9..ffde6b6 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -1023,33 +1023,33 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 1. API模式,它们帮助我们为函数给出更干净的接口,包括: - - 回调模式 - - 传入一个函数作为参数 + - 回调模式 + - 传入一个函数作为参数 - - 配置对象 - - 帮助保持函数的参数数量可控 + - 配置对象 + - 帮助保持函数的参数数量可控 - - 返回函数 - - 函数的返回值是另一个函数 + - 返回函数 + - 函数的返回值是另一个函数 - - 柯里化 - - 新函数在已有函数的基础上再加上一部分参数构成 + - 柯里化 + - 新函数在已有函数的基础上再加上一部分参数构成 2. 初始化模式,这些模式帮助我们用一种干净的、结构化的方法来做一些初始化工作(在web页面和应用中非常常见),通过一些临时变量来保证不污染全局命名空间。这些模式包括: - - 立即执行的函数 - - 当它们被定义后立即执行 + - 立即执行的函数 + - 当它们被定义后立即执行 - - 立即初始化的对象 - - 初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 + - 立即初始化的对象 + - 初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 - - 条件初始化 - - 使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 + - 条件初始化 + - 使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 3. 性能模式,这些模式帮助提高代码的执行速度,包括: - - Memoization - - 利用函数的属性,使已经计算过的值不用再次计算 + - Memoization + - 利用函数的属性,使已经计算过的值不用再次计算 - - 自定义函数 - - 重写自身的函数体,使第二次及后续的调用做更少的工作 \ No newline at end of file + - 自定义函数 + - 重写自身的函数体,使第二次及后续的调用做更少的工作 \ No newline at end of file From c78593d58b8f5b5982894a8a6ea14828091ce087 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Jun 2012 19:18:52 +0800 Subject: [PATCH 23/55] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=B0=8F=E7=BB=93?= =?UTF-8?q?=E4=B8=AD=E5=88=97=E8=A1=A8=E7=9A=84=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 47 ++++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 21457d9..bf48a2f 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -1022,34 +1022,23 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 在介绍完背景和函数的语法后,介绍了一些有用的模式,按分类列出: 1. API模式,它们帮助我们为函数给出更干净的接口,包括: - - - 回调模式 - - 传入一个函数作为参数 - - - 配置对象 - - 帮助保持函数的参数数量可控 - - - 返回函数 - - 函数的返回值是另一个函数 - - - 柯里化 - - 新函数在已有函数的基础上再加上一部分参数构成 - + - 回调模式 + - 传入一个函数作为参数 + - 配置对象 + - 帮助保持函数的参数数量可控 + - 返回函数 + - 函数的返回值是另一个函数 + - 柯里化 + - 新函数在已有函数的基础上再加上一部分参数构成 2. 初始化模式,这些模式帮助我们用一种干净的、结构化的方法来做一些初始化工作(在web页面和应用中非常常见),通过一些临时变量来保证不污染全局命名空间。这些模式包括: - - - 立即执行的函数 - - 当它们被定义后立即执行 - - - 立即初始化的对象 - - 初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 - - - 条件初始化 - - 使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 - + - 立即执行的函数 + - 当它们被定义后立即执行 + - 立即初始化的对象 + - 初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 + - 条件初始化 + - 使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 3. 性能模式,这些模式帮助提高代码的执行速度,包括: - - - Memoization - - 利用函数的属性,使已经计算过的值不用再次计算 - - - 自定义函数 - - 重写自身的函数体,使第二次及后续的调用做更少的工作 \ No newline at end of file + - Memoization + - 利用函数的属性,使已经计算过的值不用再次计算 + - 自定义函数 + - 重写自身的函数体,使第二次及后续的调用做更少的工作 \ No newline at end of file From b49c4b5199fc866b272b26008a350132f234e80b Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Jun 2012 19:24:42 +0800 Subject: [PATCH 24/55] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=B0=8F=E7=BB=93?= =?UTF-8?q?=E4=B8=AD=E5=88=97=E8=A1=A8=E7=9A=84=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 82 ++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index bf48a2f..17f4929 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -602,37 +602,37 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 下面是一个立即初始化的对象模式的例子: - ({ - // here you can define setting values - // a.k.a. configuration constants - maxwidth: 600, - maxheight: 400, - - // you can also define utility methods - gimmeMax: function () { - return this.maxwidth + "x" + this.maxheight; - }, - - // initialize - init: function () { - console.log(this.gimmeMax()); - // more init tasks... - } - }).init(); - + ({ + // here you can define setting values + // a.k.a. configuration constants + maxwidth: 600, + maxheight: 400, + + // you can also define utility methods + gimmeMax: function () { + return this.maxwidth + "x" + this.maxheight; + }, + + // initialize + init: function () { + console.log(this.gimmeMax()); + // more init tasks... + } + }).init(); + 在语法上,当你使用这种模式的时候就像在使用对象字面量创建一个普通对象一样。除此之外,还需要将对象字面量用括号括起来,这样能让JavaScript引擎知道这是一个对象字面量,而不是一个代码块(if或者for循环之类)。在括号后面,紧接着就执行了init()方法。 你也可以将对象字面量和init()调用一起写到括号里面。简单地说,下面两种语法都是有效的: - ({...}).init(); - ({...}.init()); + ({...}).init(); + ({...}.init()); 这种模式的好处和自动执行的函数模式是一样的:在做一些一次性的初始化工作的时候保护全局作用域不被污染。从语法上看,这种模式似乎比只包含一段代码在一个匿名函数中要复杂一些,但是如果你的初始化工作比较复杂(这种情况很常见),它会给整个初始化工作一个比较清晰的结构。比如,一些私有的辅助性函数可以被很轻易地看出来,因为它们是这个临时对象的属性,但是如果是在立即执行的函数模式中,它们很可能只是一些散落的函数。 这种模式的一个弊端是,JavaScript压缩工具可能不能像压缩一段包裹在函数中的代码一样有效地压缩这种模式的代码。这些私有的属性和方法不被会重命名为一些更短的名字,因为从压缩工具的角度来看,保证压缩的可靠性更重要。在写作本书的时候,Google出品的Closure Compiler的“advanced”模式是唯一会重命名立即初始化的对象的属性的压缩工具。一个压缩后的样例是这样: - ({d:600,c:400,a:function(){return this.d+"x"+this.c},b:function(){console.log(this.a())}}).b(); - + ({d:600,c:400,a:function(){return this.d+"x"+this.c},b:function(){console.log(this.a())}}).b(); + > 这种模式主要用于一些一次性的工作,并且在init()方法执行完后就无法再次访问到这个对象。如果希望在这些工作完成后保持对对象的引用,只需要简单地在init()的末尾加上return this;即可。 @@ -1022,23 +1022,25 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 在介绍完背景和函数的语法后,介绍了一些有用的模式,按分类列出: 1. API模式,它们帮助我们为函数给出更干净的接口,包括: - - 回调模式 - - 传入一个函数作为参数 - - 配置对象 - - 帮助保持函数的参数数量可控 - - 返回函数 - - 函数的返回值是另一个函数 - - 柯里化 - - 新函数在已有函数的基础上再加上一部分参数构成 + - 回调模式 + + 传入一个函数作为参数 + + - 配置对象 + - 帮助保持函数的参数数量可控 + - 返回函数 + - 函数的返回值是另一个函数 + - 柯里化 + - 新函数在已有函数的基础上再加上一部分参数构成 2. 初始化模式,这些模式帮助我们用一种干净的、结构化的方法来做一些初始化工作(在web页面和应用中非常常见),通过一些临时变量来保证不污染全局命名空间。这些模式包括: - - 立即执行的函数 - - 当它们被定义后立即执行 - - 立即初始化的对象 - - 初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 - - 条件初始化 - - 使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 + - 立即执行的函数 + - 当它们被定义后立即执行 + - 立即初始化的对象 + - 初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 + - 条件初始化 + - 使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 3. 性能模式,这些模式帮助提高代码的执行速度,包括: - - Memoization - - 利用函数的属性,使已经计算过的值不用再次计算 - - 自定义函数 - - 重写自身的函数体,使第二次及后续的调用做更少的工作 \ No newline at end of file + - Memoization + - 利用函数的属性,使已经计算过的值不用再次计算 + - 自定义函数 + - 重写自身的函数体,使第二次及后续的调用做更少的工作 \ No newline at end of file From 69b477888a5a134d0116bd7ebbaf75f4802a8484 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Jun 2012 19:26:03 +0800 Subject: [PATCH 25/55] Update master --- chapter4.markdown | 1 - 1 file changed, 1 deletion(-) diff --git a/chapter4.markdown b/chapter4.markdown index 17f4929..c09a335 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -1025,7 +1025,6 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 - 回调模式 传入一个函数作为参数 - - 配置对象 - 帮助保持函数的参数数量可控 - 返回函数 From d6403eaa23bee11f58ea3be621991fae6f7a11a7 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Jun 2012 19:28:58 +0800 Subject: [PATCH 26/55] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=B0=8F=E7=BB=93?= =?UTF-8?q?=E9=83=A8=E5=88=86=E5=88=97=E8=A1=A8=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4-MarkdownPadPreview.html | 1376 ------------------------------ chapter4.markdown | 24 +- 2 files changed, 16 insertions(+), 1384 deletions(-) delete mode 100644 chapter4-MarkdownPadPreview.html diff --git a/chapter4-MarkdownPadPreview.html b/chapter4-MarkdownPadPreview.html deleted file mode 100644 index fc00f09..0000000 --- a/chapter4-MarkdownPadPreview.html +++ /dev/null @@ -1,1376 +0,0 @@ - - - -chapter4.markdown - - - - -

函数

- -

熟练运用函数是JavaScript程序员的必备技能,因为在JavaScript中函数实在是太常用了。它能够完成的任务种类非常之多,而在其他语言中则需要很多特殊的语法支持才能达到这种能力。

- -

在本章将会介绍在JavaScript中定义函数的多种方式,包括函数表达式和函数声明、以及局部作用域和变量声明提前的工作原理。然后会介绍一些有用的模式,帮助你设计API(为你的函数提供更好的接口)、搭建代码架构(使用尽可能少的全局对象)、并优化性能(避免不必要的操作)。

- -

现在让我们来一起揭秘JavaScript函数,我们首先从一些背景知识开始说起。

- -

背景知识

- -

JavaScript的函数具有两个主要特性,正是这两个特性让它们与众不同。第一个特性是,函数是一等对象(first-class object),第二个是函数提供作用域支持。

- -

函数是对象,那么:

- -
    -
  • 可以在程序执行时动态创建函数
  • -
  • 可以将函数赋值给变量,可以将函数的引用拷贝至另一个变量,可以扩充函数,除了某些特殊场景外均可被删除。
  • -
  • 可以将函数作为参数传入另一个函数,也可以被当作返回值返回。
  • -
  • 函数可以包含自己的属性和方法
  • -
- -

对于一个函数A来说,首先它是对象,拥有属性和方法,其中某个属性碰巧是另一个函数B,B可以接受函数作为参数,假设这个函数参数为C,当执行B的时候,返回另一个函数D。乍一看这里有一大堆相互关联的函数。当你开始习惯函数的许多用法时,你会惊叹原来函数是如此强大、灵活并富有表现力。通常说来,一说到JavaScript的函数,我们首先认为它是对象,它具有一个可以“执行”的特性,也就是说我们可以“调用”这个函数。

- -

我们通过new Function()构造器来生成一个函数,这时可以明显看出函数是对象:

- -
// antipattern
-// for demo purposes only
-var add = new Function('a, b', 'return a + b');
-add(1, 2); // returns 3
-
- -

在这段代码中,毫无疑问add()是一个对象,毕竟它是由构造函数创建的。这里并不推荐使用Function()构造器创建函数(和eval()一样糟糕),因为程序逻辑代码是以字符串的形式传入构造器的。这样的代码可读性差,写起来也很费劲,你不得不对逻辑代码中的引号做转义处理,并需要特别关注为了让代码保持一定的可读性而保留的空格和缩进。

- -

函数的第二个重要特性是它能提供作用域支持。在JavaScript中没有块级作用域(译注:在JavaScript1.7中提供了块级作用域部分特性的支持,可以通过let来声明块级作用域内的“局部变量”),也就是说不能通过花括号来创建作用域,JavaScript中只有函数作用域(译注:这里作者的表述只针对函数而言,此外JavaScript还有全局作用域)。在函数内所有通过var声明的变量都是局部变量,在函数外部是不可见的。刚才所指花括号无法提供作用域支持的意思是说,如果在if条件句内、或在for或while循环体内用var定义了变量,这个变量并不是属于if语句或for(while)循环的局部变量,而是属于它所在的函数。如果不在任何函数内部,它会成为全局变量。在第二章里提到我们要减少对全局命名空间的污染,那么使用函数则是控制变量的作用域的不二之选。

- -

术语释义

- -

首先我们先简单讨论下创建函数相关的术语,因为精确无歧义的术语约定和我们所讨论的各种模式一样重要。

- -

看下这个代码片段:

- -
// named function expression
-var add = function add(a, b) {
-    return a + b;
-};
-
- -

这段代码描述了一个函数,这种描述称为“带有命名的函数表达式”。

- -

如果函数表达式将名字省略掉(比如下面的示例代码),这时它是“无名字的函数表达式”,通常我们称之为“匿名函数”,比如:

- -
// function expression, a.k.a. anonymous function
-var add = function (a, b) {
-    return a + b;
-};
-
- -

因此“函数表达式”是一个更广义的概念,“带有命名的函数表达式”是函数表达式的一种特殊形式,仅仅当需要给函数定义一个可选的名字时使用。

- -

当省略第二个add,它就成了无名字的函数表达式,这不会对函数定义和调用语法造成任何影响。带名字和不带名字唯一的区别是函数对象的name属性是否是一个空字符串。name属性属于语言的扩展(未在ECMA标准中定义),但很多环境都实现了。如果不省略第二个add,那么属性add.name则是"add",name属性在用Firebug的调试过程中非常有用,还能让函数递归调用自身,其他情况可以省略它。

- -

最后来看一下“函数声明”,函数声明的语法和其他语言中的语法非常类似:

- -
function foo() {
-    // function body goes here
-}
-
- -

从语法角度讲,带有命名的函数表达式和函数声明非常像,特别是当不需要将函数表达式赋值给一个变量的时候(在本章后面所讲到的回调模式中有类似的例子)。多数情况下,函数声明和带命名的函数表达式在外观上没有多少不同,只是它们在函数执行时对上下文的影响有所区别,下一小节会讲到。

- -

两种语法的一个区别是末尾的分号。函数声明末尾不需要分号,而函数表达式末尾是需要分号的。推荐你始终不要丢掉函数表达式末尾的分号,即便JavaScript可以进行分号补全,也不要冒险这样做。

- -
-

另外我们经常看到“函数直接量”。它用来表示函数表达式或带命名的函数表达式。由于这个术语是有歧义的,所以最好不要用它。

-
- -

声明 vs 表达式:命名与提前

- -

那么,到底应该用哪个呢?函数声明还是函数表达式?在不能使用函数声明语法的场景下,只能使用函数表达式了。下面这个例子中,我们给函数传入了另一个函数对象作为参数,以及给对象定义方法:

- -
// this is a function expression,
-// pased as an argument to the function `callMe`
-callMe(function () {
-    // I am an unnamed function expression
-    // also known as an anonymous function
-});
-
-// this is a named function expression
-callMe(function me() {
-    // I am a named function expression
-    // and my name is "me"
-});
-
-// another function expression
-var myobject = {
-    say: function () {
-        // I am a function expression
-    }
-};
-
- -

函数声明只能出现在“程序代码”中,也就是说在别的函数体内或在全局。这个定义不能赋值给变量或属性,同样不能作为函数调用的参数。下面这个例子是函数声明的合法用法,这里所有的函数foo(),bar()和local()都使用函数声明来定义:

- -
// global scope
-function foo() {}
-
-function local() {
-    // local scope
-    function bar() {}
-    return bar;
-}
-
- -

函数的name属性

- -

选择函数定义模式的另一个考虑是只读属性name的可用性。尽管标准规范中并未规定,但很多运行环境都实现了name属性,在函数声明和带有名字的函数表达式中是有name的属性定义的。在匿名函数表达式中,则不一定有定义,这个是和实现相关的,在IE中是无定义的,在Firefox和Safari中是有定义的,但是值为空字符串。

- -
function foo() {} // declaration
-var bar = function () {}; // expression
-var baz = function baz() {}; // named expression
-
-foo.name; // "foo"
-bar.name; // ""
-baz.name; // "baz"
-
- -

在Firebug或其他工具中调试程序时name属性非常有用,它可以用来显示当前正在执行的函数。同样可以通过name属性来递归的调用函数自身。如果你对这些场景不感兴趣,那么请尽可能的使用匿名函数表达式,这样会更简单、且冗余代码更少。

- -

和函数声明相比而言,函数表达式的语法更能说明函数是一种对象,而不是某种特别的语言写法。

- -
-

我们可以将一个带名字的函数表达式赋值给变量,变量名和函数名不同,这在技术上是可行的。比如:var foo = function bar(){};。然而,这种用法的行为在浏览器中的兼容性不佳(特别是IE中),因此并不推荐大家使用这种模式。

-
- -

函数提前

- -

通过前面的讲解,你可能以为函数声明和带名字的函数表达式是完全等价的。事实上不是这样,主要区别在于“声明提前”的行为。

- -
-

术语“提前”并未在ECMAScript中定义,但是并没有其他更好的方法来描述这种行为了。

-
- -

我们知道,不管在函数内何处声明变量,变量都会自动提前至函数体的顶部。对于函数来说亦是如此,因为他们也是一种对象,赋值给了变量。需要注意的是,函数声明定义的函数不仅能让声明提前,还能让定义提前,看一下这段示例代码:

- -
// antipattern
-// for illustration only
-
-// global functions
-function foo() {
-    alert('global foo');
-}
-function bar() {
-    alert('global bar');
-}
-
-function hoistMe() {
-
-    console.log(typeof foo); // "function"
-    console.log(typeof bar); // "undefined"
-
-    foo(); // "local foo"
-    bar(); // TypeError: bar is not a function
-
-    // function declaration:
-    // variable 'foo' and its implementation both get hoisted
-
-    function foo() {
-        alert('local foo');
-    }
-
-    // function expression:
-    // only variable 'bar' gets hoisted
-    // not the implementation
-    var bar = function () {
-        alert('local bar');
-    };
-}
-hoistMe();
-
- -

在这段代码中,和普通的变量一样,hoistMe()函数中的foo和bar被“搬运”到了顶部,覆盖了全局的foo和bar。不同之处在于,局部的foo()定义提前至顶部并能正常工作,尽管定义它的位置并不靠前。bar()的定义并未提前,只是声明提前了。因此当程序执行到bar()定义的位置之前,它的值都是undefined,并不是函数(防止当前上下文查找到作用域链上的全局的bar(),也就“覆盖”了全局的bar())。

- -

到目前为止我们介绍了必要的背景知识和函数定义相关的术语,下面开始介绍一些JavaScript所提供的函数相关的好的模式,我们从回调模式开始。同样,再次强调JavaScript函数的两个特殊特性,掌握这两点至关重要:

- -
    -
  • 函数是对象
  • -
  • 函数提供局部变量作用域
  • -
- -

回调模式

- -

函数是对象,也就意味着函数可以当作参数传入另外一个函数中。当你给函数writeCode()传入一个函数参数introduceBugs(),在某个时刻writeCode()执行了(或调用了)introduceBugs()。在这种情况下,我们说introduceBugs()是一个“回调函数”,简称“回调”:

- -
function writeCode(callback) {
-    // do something...
-    callback();
-    // ...
-}
-
-function introduceBugs() {
-    // ... make bugs
-}
-
-writeCode(introduceBugs);
-
- -

注意introduceBugs()是如何作为参数传入writeCode()的,当作参数的函数不带括号。括号的意思是执行函数,而这里我们希望传入一个引用,让writeCode()在合适的时机执行它(调用它)。

- -

一个回调的例子

- -

我们从一个例子开始,首先介绍无回调的情况,然后在作修改。假设你有一个通用的函数,用来完成某种复杂的逻辑并返回一大段数据。假设我们用findNodes()来命名这个通用函数,这个函数用来对DOM树进行遍历,并返回我所感兴趣的页面节点:

- -
var findNodes = function () {
-    var i = 100000, // big, heavy loop
-        nodes = [], // stores the result
-        found; // the next node found
-    while (i) {
-        i -= 1;
-        // complex logic here...
-        nodes.push(found);
-    }
-    return nodes;
-};
-
- -

保持这个函数的功能的通用性并一贯返回DOM节点组成的数组,并不会发生对节点的实际操作,这是一个不错的注意。可以将操作节点的逻辑放入另外一个函数中,比如放入一个hide()函数中,这个函数用来隐藏页面中的节点元素:

- -
var hide = function (nodes) {
-    var i = 0, max = nodes.length;
-    for (; i < max; i += 1) {
-        nodes[i].style.display = "none";
-    }
-};
-
-// executing the functions
-hide(findNodes());
-
- -

这个实现的效率并不高,因为它将findNodes()所返回的节点数组重新遍历了一遍。最好在findNodes()中选择元素的时候就直接应用hide()操作,这样就能避免第二次的遍历,从而提高效率。但如果将hide()的逻辑写死在findNodes()的函数体内,findNodes()就变得不再通用了(译注:如果我将hide()的逻辑替换成其他逻辑怎么办呢?),因为修改逻辑和遍历逻辑耦合在一起了。如果使用回调模式,则可以将隐藏节点的逻辑写入回调函数,将其传入findNodes()中适时执行:

- -
// refactored findNodes() to accept a callback
-var findNodes = function (callback) {
-    var i = 100000,
-        nodes = [],
-        found;
-
-    // check if callback is callable
-    if (typeof callback !== "function") {
-        callback = false;
-    }
-    while (i) {
-        i -= 1;
-
-        // complex logic here...
-
-        // now callback:
-        if (callback) {
-            callback(found);
-        }
-
-        nodes.push(found);
-    }
-    return nodes;
-};
-
- -

这里的实现比较直接,findNodes()多作了一个额外工作,就是检查回调函数是否存在,如果存在的话就执行它。回调函数是可选的,因此修改后的findNodes()也是和之前一样使用,是可以兼容旧代码和旧API的。

- -

这时hide()的实现就非常简单了,因为它不用对元素列表做任何遍历了:

- -
// a callback function
-var hide = function (node) {
-    node.style.display = "none";
-};
-
-// find the nodes and hide them as you go
-findNodes(hide);
-
- -

正如代码中所示,回调函数可以是事先定义好的,也可以是一个匿名函数,你也可以将其称作main函数,比如这段代码,我们利用同样的通用函数findNodes()来完成显示元素的操作:

- -
// passing an anonymous callback
-findNodes(function (node) {
-    node.style.display = "block";
-});
-
- -

回调和作用域

- -

在上一个例子中,执行回调函数的写法是:

- -
callback(parameters);
-
- -

尽管这种写法可以适用大多数的情况,而且足够简单,但还有一些场景,回调函数不是匿名函数或者全局函数,而是对象的方法。如果回调函数中使用this指向它所属的对象,则回调逻辑往往并不像我们希望的那样执行。

- -

假设回调函数是paint(),它是myapp的一个方法:

- -
var myapp = {};
-myapp.color = "green";
-myapp.paint = function (node) {
-    node.style.color = this.color;
-};
-
- -

函数findNodes()大致如下:

- -
var findNodes = function (callback) {
-    // ...
-    if (typeof callback === "function") {
-        callback(found);
-    }
-    // ...
-};
-
- -

当你调用findNodes(myapp.paint),运行结果和我们期望的不一致,因为this.color未定义。因为findNodes()是全局函数,this指向的是全局对象。如果findNodes()是dom对象的方法(类似dom.findNodes()),那么回调函数内的this则指向dom,而不是myapp。

- -

解决办法是,除了传入回调函数,还需将回调函数所属的对象当作参数传进去:

- -
findNodes(myapp.paint, myapp);
-
- -

同样需要修改findNodes()的逻辑,增加对传入的对象的绑定:

- -
var findNodes = function (callback, callback_obj) {
-    //...
-    if (typeof callback === "function") {
-        callback.call(callback_obj, found);
-    }
-    // ...
-};
-
- -

在后续的章节会对call()和apply()有更详细的讲述。

- -

其实还有一种替代写法,就是将函数当作字符串传入findNodes(),这样就不必再写一次对象了,换句话说:

- -
findNodes(myapp.paint, myapp);
-
- -

可以写成:

- -
findNodes("paint", myapp);
-
- -

在findNodes()中的逻辑则需要修改为:

- -
var findNodes = function (callback, callback_obj) {
-
-    if (typeof callback === "string") {
-        callback = callback_obj[callback];
-    }
-
-    //...
-    if (typeof callback === "function") {
-        callback.call(callback_obj, found);
-    }
-    // ...
-};
-
- -

异步事件监听

- -

JavaScript中的回调模式已经是我们的家常便饭了,比如,如果你给网页中的元素绑定事件,则需要提供回调函数的引用,以便事件发生时能调用到它。这里有一个简单的例子,我们将console.log()作为回调函数绑定了document的点击事件:

- -
document.addEventListener("click", console.log, false);
-
- -

客户端浏览器中的大多数编程都是事件驱动的,当网页下载完成,则触发load事件,当用户和页面产生交互时也会触发多种事件,比如click、keypress、mouseover、mousemove等等。正是由于回调模式的灵活性,JavaScript天生适于事件驱动编程。回调模式能够让程序“异步”执行,换句话说,就是让程序不按顺序执行。

- -

“不要打电话给我,我会打给你”,这是好莱坞很有名的一句话,很多电影都有这句台词。电影中的主角不可能同时应答很多个电话呼叫。在JavaScript的异步事件模型中也是同样的道理。电影中是留下电话号码,JavaScript中是提供一个回调函数,当时机成熟时就触发回调。有时甚至提供了很多回调,有些回调压根是没用的,但由于这个事件可能永远不会发生,因此这些回调的逻辑也不会执行。比如,假设你从此不再用“鼠标点击”,那么你之前绑定的鼠标点击的回调函数则永远也不会执行。

- -

超时

- -

另外一个最常用的回调模式是在调用超时函数时,超时函数是浏览器window对象的方法,共有两个:setTimeout()和setInterval()。这两个方法的参数都是回调函数。

- -
var thePlotThickens = function () {
-    console.log('500ms later...');
-};
-setTimeout(thePlotThickens, 500);
-
- -

再次需要注意,函数thePlotThickens是作为变量传入setTimeout的,它不带括号,如果带括号的话则立即执行了,这里只是用到这个函数的引用,以便在setTimeout的逻辑中调用到它。也可以传入字符串“thePlotThickens()”,但这是一种反模式,和eval()一样不推荐使用。

- -

库中的回调

- -

回调模式非常简单,但又很强大。可以随手拈来灵活运用,因此这种模式在库的设计中也非常得宠。库的代码要尽可能的保持通用和重用,而回调模式则可帮助库的作者完成这个目标。你不必预料和实现你所想到的所有情形,因为这会让库变的膨胀而臃肿,而且大多数用户并不需要这些多余的特性支持。相反,你将精力放在核心功能的实现上,提供回调的入口作为“钩子”,可以让库的方法变得可扩展、可定制。

- -

返回函数

- -

函数是对象,因此当然可以作为返回值。也就是说,函数不一定非要返回一坨数据,函数可以返回另外一个定制好的函数,或者可以根据输入的不同按需创造另外一个函数。

- -

这里有一个简单的例子:一个函数完成了某种功能,可能是一次性初始化,然后都基于这个返回值进行操作,这个返回值恰巧是另一个函数:

- -
var setup = function () {
-    alert(1);
-    return function () {
-        alert(2);
-    };
-};
-
-// using the setup function
-var my = setup(); // alerts 1
-my(); // alerts 2
-
- -

因为setup()把返回的函数作了包装,它创建了一个闭包,我们可以用这个闭包来存储一些私有数据,这些私有数据可以通过返回的函数进行操作,但在函数外部不能直接读取到这些私有数据。比如这个例子中提供了一个计数器,每次调用这个函数计数器都会加一:

- -
var setup = function () {
-    var count = 0;
-    return function () {
-        return (count += 1);
-    };
-};
-
-// usage
-var next = setup();
-next(); // returns 1
-next(); // 2
-next(); // 3
-
- -

自定义函数

- -

我们动态定义函数,并将函数赋值给变量。如果将你定义的函数赋值给已经存在的函数变量的话,则新函数会覆盖旧函数。这样做的结果是,旧函数的引用就丢弃掉了,变量中所存储的引用值替换成了新的。这样看起来这个变量指代的函数逻辑就发生了变化,或者说函数进行了“重新定义”或“重写”。说起来有些拗口,实际上并不复杂,来看一个例子:

- -
var scareMe = function () {
-    alert("Boo!");
-    scareMe = function () {
-        alert("Double boo!");
-    };
-};
-// using the self-defining function
-scareMe(); // Boo!
-scareMe(); // Double boo!
-
- -

当函数中包含一些初始化操作,并希望这些初始化只执行一次,那么这种模式是非常适合这个场景的。因为能避免的重复执行则尽量避免,函数的一部分可能再也不会执行到。在这个场景中,函数执行一次后就被重写为另外一个函数了。

- -

使用这种模式可以帮助提高应用的执行效率,因为重新定义的函数执行更少的代码。

- -
-

这种模式的另外一个名字是“函数的懒惰定义”,因为直到函数执行一次后才重新定义,可以说它是“某个时间点之后才存在”,简称“懒惰定义”。

-
- -

这种模式有一种明显的缺陷,就是之前给原函数添加的功能在重定义之后都丢失了。如果将这个函数定义为不同的名字,函数赋值给了很多不同的变量,或作为对象的方法使用,那么新定义的函数有可能不会执行,原始的函数会照旧执行(译注:由于函数的赋值是引用的赋值,函数赋值给多个变量只是将引用赋值给了多个变量,当某一个变量定义了新的函数,也只是变量的引用值发生变化,原函数本身依旧存在,当程序中存在某个变量的引用还是旧函数的话,旧函数还是会依旧执行)。

- -

让我们来看一个例子,scareMe()函数在这里作为一等对象来使用:

- -
    -
  1. 给他增加了一个属性
  2. -
  3. 函数对象赋值给一个新变量
  4. -
  5. 函数依旧可以作为方法来调用
  6. -
- -

看一下这段代码:

- -
// 1. adding a new property
-scareMe.property = "properly";
-
-// 2. assigning to a different name
-var prank = scareMe;
-
-// 3. using as a method
-var spooky = {
-    boo: scareMe
-};
-
-// calling with a new name
-prank(); // "Boo!"
-prank(); // "Boo!"
-console.log(prank.property); // "properly"
-
-// calling as a method
-spooky.boo(); // "Boo!"
-spooky.boo(); // "Boo!"
-console.log(spooky.boo.property);   // "properly"
-
-// using the self-defined function
-scareMe(); // Double boo!
-scareMe(); // Double boo!
-console.log(scareMe.property); // undefined
-
- -

从结果来看,当自定义函数被赋值给一个新的变量的时候,这段使用自定义函数的代码的执行结果与我们期望的结果可能并不一样。每当prank()运行的时候,它都弹出“Boo!”。同时它也重写了scareMe()函数,但是prank()自己仍然能够使用之前的定义,包括属性property。在这个函数被作为spooky对象的boo()方法调用的时候,结果也一样。所有的这些调用,在第一次的时候就已经修改了全局的scareMe()的指向,所以当它最终被调用的时候,它的函数体已经被修改为弹出“Double boo”。它也就不能获取到新添加的属性“property”。

- -

立即执行的函数

- -

立即执行的函数是一种语法模式,它会使函数在定义后立即执行。看这个例子:

- -
(function () {
-    alert('watch out!');
-}());
-
- -

这种模式本质上只是一个在创建后就被执行的函数表达式(具名或者匿名)。“立即执行的函数”这种说法并没有在ECMAScript标准中被定义,但它作为一个名词,有助于我们的描述和讨论。

- -

这种模式由以下几个部分组成:

- -
    -
  • 使用函数表达式定义一个函数。(不能使用函数声明。)
  • -
  • 在最后加入一对括号,这会使函数立即被执行。
  • -
  • 把整个函数包裹到一对括号中(只在没有将函数赋值给变量时需要)。
  • -
- -

下面这种语法也很常见(注意右括号的位置),但是JSLint倾向于第一种:

- -
(function () {
-    alert('watch out!');
-})();
-
- -

这种模式很有用,它为我们提供一个作用域的沙箱,可以在执行一些初始化代码的时候使用。设想这样的场景:当页面加载的时候,你需要运行一些代码,比如绑定事件、创建对象等等。所有的这些代码都只需要运行一次,所以没有必要创建一个带有名字的函数。但是这些代码需要一些临时变量,而这些变量在初始化完之后又不会再次用到。显然,把这些变量作为全局变量声明是不合适的。正因为如此,我们才需要立即执行的函数。它可以把你所有的代码包裹到一个作用域里面,而不会暴露任何变量到全局作用域中:

- -
(function () {
-
-    var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
-        today = new Date(),
-        msg = 'Today is ' + days[today.getDay()] + ', ' + today.getDate();
-
-    alert(msg);
-
-}()); // "Today is Fri, 13"
-
- -

如果这段代码没有被包裹到立即执行函数中,那么变量days、today、msg都会是全局变量,而这些变量仅仅是由因为初始化而遗留下来的垃圾,没有任何用处。

- -

立即执行的函数的参数

- -

立即执行的函数也可以接受参数,看这个例子:

- -
// prints:
-// I met Joe Black on Fri Aug 13 2010 23:26:59 GMT-0800 (PST)
-
-(function (who, when) {
-
-    console.log("I met " + who + " on " + when);
-
-}("Joe Black", new Date()));
-
- -

通常的做法,会把全局对象当作一个参数传给立即执行的函数,以保证在函数内部也可以访问到全局对象,而不是使用window对象,这样可以使得代码在非浏览器环境中使用时更具可移植性。

- -

值得注意的是,一般情况下尽量不要给立即执行的函数传入太多的参数,否则会有一件麻烦的事情,就是你在阅读代码的时候需要频繁地上下滚动代码。

- -

立即执行的函数的返回值

- -

和其它的函数一样,立即执行的函数也可以返回值,并且这些返回值也可以被赋值给变量:

- -
var result = (function () {
-    return 2 + 2;
-}());
-
- -

如果省略括号的话也可以达到同样的目的,因为如果需要将返回值赋给变量,那么第一对括号就不是必需的。省略括号的代码是这样子:

- -
var result = function () {
-    return 2 + 2;
-}();
-
- -

这种写法更简洁,但是同时也容易造成误解。如果有人在阅读代码的时候忽略了最后的一对括号,那么他会以为result指向了一个函数。而事实上result是指向这个函数运行后的返回值,在这个例子中是4。

- -

还有一种写法也可以得到同样的结果:

- -
var result = (function () {
-    return 2 + 2;
-})();
-
- -

前面的例子中,立即执行的函数返回的是一个基本类型的数值。但事实上,除了基本类型以外,一个立即执行的函数可以返回任意类型的值,甚至返回一个函数都可以。你可以利用立即执行的函数的作用域来存储一些私有的数据,这些数据只能在返回的内层函数中被访问。

- -

在下面的例子中,立即执行的函数的返回值是一个函数,这个函数会简单地返回res的值,并且它被赋给了变量getResult。而res是一个预先计算好的变量,它被存储在立即执行函数的闭包中:

- -
var getResult = (function () {
-    var res = 2 + 2;
-    return function () {
-        return res;
-    };
-}());
-
- -

在定义一个对象的属性的时候也可以使用立即执行的函数。设想一下这样的场景:你需要定义一个对象的属性,这个属性在对象的生命周期中都不会改变,但是在定义之前,你需要做一点额外的工作来得到正确的值。这种情况下你就可以使用立即执行的函数来包裹那些额外的工作,然后将它的返回值作为对象属性的值。下面是一个例子:

- -
var o = {
-    message: (function () {
-        var who = "me",
-            what = "call";
-        return what + " " + who;
-    }()),
-    getMsg: function () {
-        return this.message;
-    }
-};
-
-// usage
-o.getMsg(); // "call me"
-o.message; // "call me"
-
- -

在这个例子中,o.message是一个字符串,而不是一个函数,但是它需要一个函数在脚本载入后来得到这个属性值。

- -

好处和用法

- -

立即执行的函数应用很广泛。它可以帮助我们做一些不想留下全局变量的工作。所有定义的变量都只是立即执行的函数的本地变量,你完全不用担心临时变量会污染全局对象。

- -
-

立即执行的函数还有一些名字,比如“自调用函数”或者“自执行函数”,因为这些函数会在被定义后立即执行自己。

-
- -

这种模式也经常被用到书签代码中,因为书签代码会在任何一个页面运行,所以需要非常苛刻地保持全局命名空间干净。

- -

这种模式也可以让你包裹一些独立的特性到一个封闭的模块中。设想你的页面是静态的,在没有JavaScript的时候工作正常,然后,本着渐进增强的精神,你给页面加入了一点增加代码。这时候,你就可以把你的代码(也可以叫“模块”或者“特性”)放到一个立即执行的函数中并且保证页面在有没有它的时候都可以正常工作。然后你就可以加入更多的增强特性,或者对它们进行移除、进行独立测试或者允许用户禁用等等。

- -

你可以使用下面的模板定义一段函数代码,我们叫它module1:

- -
// module1 defined in module1.js
-(function () {
-
-    // all the module 1 code ...
-
-}());
-
- -

套用这个模板,你就可以编写其它的模块。然后在发布到线上的时候,你就可以决定在这个时间节点上哪些特性是可以使用的,然后使用发布脚本将它们打包上线。

- -

立即初始化的对象

- -

还有另外一种可以避免污染全局作用域的方法,和前面描述的立即执行的函数相似,叫做“立即初始化的对象”模式。这种模式使用一个带有init()方法的对象来实现,这个方法在对象被创建后立即执行。初始化的工作由init()函数来完成。

- -

下面是一个立即初始化的对象模式的例子:

- -
({
-    // here you can define setting values
-    // a.k.a. configuration constants
-    maxwidth: 600,
-    maxheight: 400,
-
-    // you can also define utility methods
-    gimmeMax: function () {
-        return this.maxwidth + "x" + this.maxheight;
-    },
-
-    // initialize
-    init: function () {
-        console.log(this.gimmeMax());
-        // more init tasks...
-    }
-}).init();
-
- -

在语法上,当你使用这种模式的时候就像在使用对象字面量创建一个普通对象一样。除此之外,还需要将对象字面量用括号括起来,这样能让JavaScript引擎知道这是一个对象字面量,而不是一个代码块(if或者for循环之类)。在括号后面,紧接着就执行了init()方法。

- -

你也可以将对象字面量和init()调用一起写到括号里面。简单地说,下面两种语法都是有效的:

- -
({...}).init();
-({...}.init());
-
- -

这种模式的好处和自动执行的函数模式是一样的:在做一些一次性的初始化工作的时候保护全局作用域不被污染。从语法上看,这种模式似乎比只包含一段代码在一个匿名函数中要复杂一些,但是如果你的初始化工作比较复杂(这种情况很常见),它会给整个初始化工作一个比较清晰的结构。比如,一些私有的辅助性函数可以被很轻易地看出来,因为它们是这个临时对象的属性,但是如果是在立即执行的函数模式中,它们很可能只是一些散落的函数。

- -

这种模式的一个弊端是,JavaScript压缩工具可能不能像压缩一段包裹在函数中的代码一样有效地压缩这种模式的代码。这些私有的属性和方法不被会重命名为一些更短的名字,因为从压缩工具的角度来看,保证压缩的可靠性更重要。在写作本书的时候,Google出品的Closure Compiler的“advanced”模式是唯一会重命名立即初始化的对象的属性的压缩工具。一个压缩后的样例是这样:

- -
({d:600,c:400,a:function(){return this.d+"x"+this.c},b:function(){console.log(this.a())}}).b();
-
- -
-

这种模式主要用于一些一次性的工作,并且在init()方法执行完后就无法再次访问到这个对象。如果希望在这些工作完成后保持对对象的引用,只需要简单地在init()的末尾加上return this;即可。

-
- -

条件初始化

- -

条件初始化(也叫条件加载)是一种优化模式。当你知道某种条件在整个程序生命周期中都不会变化的时候,那么对这个条件的探测只做一次就很有意义。浏览器探测(或者特征检测)是一个典型的例子。

- -

举例说明,当你探测到XMLHttpRequest被作为一个本地对象支持时,就知道浏览器不会在程序执行过程中改变这一情况,也不用突然又需要去处理ActiveX对象。当环境不发生变化的时候,你的代码就没有必要在需要在每次XHR对象时探测一遍(并且得到同样的结果)。

- -

另外一些可以从条件初始化中获益的场景是获得一个DOM元素的computed styles或者是绑定事件处理函数。大部分程序员在他们的客户端编程生涯中都编写过事件绑定和取消绑定相关的组件,像下面的例子:

- -
// BEFORE
-var utils = {
-    addListener: function (el, type, fn) {
-        if (typeof window.addEventListener === 'function') {
-            el.addEventListener(type, fn, false);
-        } else if (typeof document.attachEvent === 'function') { // IE
-            el.attachEvent('on' + type, fn);
-        } else { // older browsers
-            el['on' + type] = fn;
-        }
-    },
-    removeListener: function (el, type, fn) {
-        // pretty much the same...
-    }
-};
-
- -

这段代码的问题就是效率不高。每当你执行utils.addListener()或者utils.removeListener()时,同样的检查都会被重复执行。

- -

如果使用条件初始化,那么浏览器探测的工作只需要在初始化代码的时候执行一次。在初始化的时候,代码探测一次环境,然后重新定义这个函数在剩下来的程序生命周期中应该怎样工作。下面是一个例子,看看如何达到这个目的:

- -
// AFTER
-
-// the interface
-var utils = {
-    addListener: null,
-    removeListener: null
-};
-
-// the implementation
-if (typeof window.addEventListener === 'function') {
-    utils.addListener = function (el, type, fn) {
-        el.addEventListener(type, fn, false);
-    };
-    utils.removeListener = function (el, type, fn) {
-        el.removeEventListener(type, fn, false);
-    };
-} else if (typeof document.attachEvent === 'function') { // IE
-    utils.addListener = function (el, type, fn) {
-        el.attachEvent('on' + type, fn);
-    };
-    utils.removeListener = function (el, type, fn) {
-        el.detachEvent('on' + type, fn);
-    };
-} else { // older browsers
-    utils.addListener = function (el, type, fn) {
-        el['on' + type] = fn;
-    };
-    utils.removeListener = function (el, type, fn) {
-        el['on' + type] = null;
-    };
-}
-
- -

说到这里,要特别提醒一下关于浏览器探测的事情。当你使用这个模式的时候,不要对浏览器特性过度假设。举个例子,如果你探测到浏览器不支持window.addEventListener,不要假设这个浏览器是IE,也不要认为它不支持原生的XMLHttpRequest,虽然这个结论在整个浏览器历史上的某个点是正确的。当然,也有一些情况是可以放心地做一些特性假设的,比如.addEventListener和.removeEventListerner,但是通常来讲,浏览器的特性在发生变化时都是独立的。最好的策略就是分别探测每个特性,然后使用条件初始化,使这种探测只做一次。

- -

函数属性——Memoization模式

- -

函数也是对象,所以它们可以有属性。事实上,函数也确实本来就有一些属性。比如,对一个函数来说,不管是用什么语法创建的,它会自动拥有一个length属性来标识这个函数期待接受的参数个数:

- -
function func(a, b, c) {}
-console.log(func.length); // 3
-
- -

任何时候都可以给函数添加自定义属性。添加自定义属性的一个有用场景是缓存函数的执行结果(返回值),这样下次同样的函数被调用的时候就不需要再做一次那些可能很复杂的计算。缓存一个函数的运行结果也就是为大家所熟知的Memoization。

- -

在下面的例子中,myFunc函数创建了一个cache属性,可以通过myFunc.cache访问到。这个cache属性是一个对象(hash表),传给函数的参数会作为对象的key,函数执行结果会作为对象的值。函数的执行结果可以是会用到的任何的复杂数据结构:

- -
var myFunc = function (param) {
-    if (!myFunc.cache[param]) {
-        var result = {};
-        // ... expensive operation ...
-        myFunc.cache[param] = result;
-    }
-    return myFunc.cache[param];
-};
-
-// cache storage
-myFunc.cache = {};
-
- -

上面的代码假设函数只接受一个参数param,并且这个参数是基本类型(比如字符串)。如果你有更多更复杂的参数,则通常需要对它们进行序列化。(译注:序列化后来作为缓存对象的key。)比如,你需要将arguments对象序列化为JSON字符串,然后使用JSON字符串作为cache对象的key:

- -
var myFunc = function () {
-
-    var cachekey = JSON.stringify(Array.prototype.slice.call(arguments)),
-        result;
-
-    if (!myFunc.cache[cachekey]) {
-        result = {};
-        // ... expensive operation ...
-        myFunc.cache[cachekey] = result;
-    }
-    return myFunc.cache[cachekey];
-};
-
-// cache storage
-myFunc.cache = {};
-
- -

需要注意的是,在序列化的过程中,对象的“标识”将会丢失。如果你有两个不同的对象,却碰巧有相同的属性,那么他们会共享同样的缓存内容。

- -

前面代码中的函数名还可以使用arguments.callee来替代,这样就不用将函数名硬编码。不过尽管现阶段这个办法可行,但是仍然需要注意,arguments.callee在ECMAScript 5的严格模式中是不被允许的:

- -
var myFunc = function (param) {
-
-    var f = arguments.callee,
-        result;
-
-    if (!f.cache[param]) {
-        result = {};
-        // ... expensive operation ...
-        f.cache[param] = result;
-    }
-    return f.cache[param];
-};
-
-// cache storage
-myFunc.cache = {};
-
- -

配置对象

- -

配置对象模式是一种提供更简洁的API的方法,尤其是当你正在写一个即将被其它程序调用的类库或者任何其它代码的时候。

- -

软件在开发和维护过程中需要不断改变是一个不争的事实。这样的事情总是以一些有限的需求开始,但是随着开发的进行,越来越多的功能会不断被加进来。

- -

设想一下你正在写一个名为addPerson()的函数,它接受一个姓和一个名,然后在列表中加入一个人:

- -
function addPerson(first, last) {...}
-
- -

然后你意识到,生日也必须要存储,此外,性别和地址也作为可选项存储。所以你修改了函数,添加了一些新的参数(还得非常小心地将可选参数放到最后):

- -
function addPerson(first, last, dob, gender, address) {...}
-
- -

这个时候,函数已经显得有点长了。然后,你又被告知需要添加一个用户名,并且不是可选的。现在这个函数的调用者需要将所有的可选参数传进来,并且得非常小心地保证不弄混参数的顺序:

- -
addPerson("Bruce", "Wayne", new Date(), null, null, "batman");
-
- -

传一大串的参数真的很不方便。一个更好的办法就是将它们替换成一个参数,并且把这个参数弄成对象;我们叫它conf,是“configuration”(配置)的缩写:

- -
addPerson(conf);
-
- -

然后这个函数的使用者就可以这样:

- -
var conf = {
-    username: "batman",
-    first: "Bruce",
-    last: "Wayne"
-};
-addPerson(conf);
-
- -

配置对象模式的好处是:

- -
    -
  • 不需要记住参数的顺序
  • -
  • 可以很安全地跳过可选参数
  • -
  • 拥有更好的可读性和可维护性
  • -
  • 更容易添加和移除参数
  • -
- -

配置对象模式的坏处是:

- -
    -
  • 需要记住参数的名字
  • -
  • 参数名字不能被压缩
  • -
- -

举些实例,这个模式对创建DOM元素的函数或者是给元素设定CSS样式函数会非常实用,因为元素和CSS样式可能会有很多但是大部分可选的属性。

- -

柯里化 (Curry)

- -

在本章剩下的部分,我们将讨论一下关于柯里化和部分应用的话题。但是在我们开始这个话题之前,先看一下到底什么是函数应用。

- -

函数应用

- -

在一些纯粹的函数式编程语言中,对函数的描述不是被调用(called或者invoked),而是被应用(applied)。在JavaScript中也有同样的东西——我们可以使用Function.prototype.apply()来应用一个函数,因为在JavaScript中,函数实际上是对象,并且他们拥有方法。

- -

下面是一个函数应用的例子:

- -
// define a function
-var sayHi = function (who) {
-    return "Hello" + (who ? ", " + who : "") + "!";
-};
-
-// invoke a function
-sayHi(); // "Hello"
-sayHi('world'); // "Hello, world!"
-
-// apply a function
-sayHi.apply(null, ["hello"]); // "Hello, hello!"
-
- -

从上面的例子中可以看出来,调用一个函数和应用一个函数有相同的结果。apply()接受两个参数:第一个是在函数内部绑定到this上的对象,第二个是一个参数数组,参数数组会在函数内部变成一个类似数组的arguments对象。如果第一个参数为null,那么this将指向全局对象,这正是当你调用一个函数(且这个函数不是某个对象的方法)时发生的事情。

- -

当一个函数是一个对象的方法时,我们不再像前面的例子一样传入null。(译注:主要是为了保证方法中的this绑定到一个有效的对象而不是全局对象。)在下面的例子中,对象被作为第一个参数传给apply():

- -
var alien = {
-    sayHi: function (who) {
-        return "Hello" + (who ? ", " + who : "") + "!";
-    }
-};
-
-alien.sayHi('world'); // "Hello, world!"
-sayHi.apply(alien, ["humans"]); // "Hello, humans!"
-
- -

在这个例子中,sayHi()中的this指向alien。而在上一个例子中,this是指向的全局对象。(译注:这个例子的代码有误,最后一行的sayHi并不能访问到alien的sayHi方法,需要使用alien.sayHi.apply(alien, ["humans"])才可正确运行。另外,在sayHi中也没有出现this。)

- -

正如上面两个例子所展现出来的一样,我们所谓的函数调用跟函数应用的一种语法糖没有什么太大的差别。

- -

需要注意的是,除了apply()之外,Function.prototype对象还有一个call()方法,但是它仍然只是apply()的一种语法糖。(译注:这两个方法的区别在于,apply()只接受两个参数,第二个参数为需要传给函数的参数数组,而call()则接受任意多个参数,从第二个开始将参数依次传给函数。)不过有种情况下使用这个语法糖会更好:当你的函数只接受一个参数的时候,你可以省去为唯一的一个元素创建数组的工作:

- -
// the second is more efficient, saves an array
-sayHi.apply(alien, ["humans"]); // "Hello, humans!"
-sayHi.call(alien, "humans"); // "Hello, humans!"
-
- -

部分应用

- -

现在我们知道了,调用一个函数实际上就是给它应用一堆参数,那是否能够只传一部分参数而不传全部呢?这实际上跟我们手工处理数学函数非常类似。

- -

假设已经有了一个add()函数,它的工作是把x和y两个数加到一起。下面的代码片段展示了当x为5、y为4时的计算步骤:

- -
// for illustration purposes
-// not valid JavaScript
-
-// we have this function
-function add(x, y) {
-    return x + y;
-}
-
-// and we know the arguments
-add(5, 4);
-
-// step 1 -- substitute one argument
-function add(5, y) {
-    return 5 + y;
-}
-
-// step 2 -- substitute the other argument
-function add(5, 4) {
-    return 5 + 4;
-}
-
- -

在这个代码片段中,step 1和step 2并不是有效的JavaScript代码,但是它展示了我们手工计算的过程。首先获得第一个参数的值,然后将未知的x和已知的值5替换到函数中。然后重复这个过程,直到替换掉所有的参数。

- -

step 1是一个所谓的部分应用的例子:我们只应用了第一个参数。当你执行一个部分应用的时候并不能获得结果(或者是解决方案),取而代之的是另一个函数。

- -

下面的代码片段展示了一个虚拟的partialApply()方法的用法:

- -
var add = function (x, y) {
-    return x + y;
-};
-
-// full application
-add.apply(null, [5, 4]); // 9
-
-// partial application
-var newadd = add.partialApply(null, [5]);
-// applying an argument to the new function
-newadd.apply(null, [4]); // 9
-
- -

正如你所看到的一样,部分应用给了我们另一个函数,这个函数可以在稍后调用的时候接受其它的参数。这实际上跟add(5)(4)是等价的,因为add(5)返回了一个函数,这个函数可以使用(4)来调用。我们又一次看到,熟悉的add(5, 4)也跟add(5)(4)的一种语法糖差不了多少。

- -

现在,让我们回到地球:并不存在这样的一个partialApply()函数,并且函数的默认表现也不会像上面的例子中那样。但是你完全可以自己去写,因为JavaScript的动态特性完全可以做到这样。

- -

让函数理解并且处理部分应用的过程,叫柯里化(Currying)。

- -

柯里化(Currying)

- -

柯里化和辛辣的印度菜可没什么关系;它来自数学家Haskell Curry。(Haskell编程语言也是因他而得名。)柯里化是一个变换的函数的过程。柯里化的另外一个名字也叫schönfinkelisation,来自另一位数学家——Moses Schönfinkelisation——这种变换的最初发明者。

- -

所以我们怎样对一个函数进行柯里化呢?其它的函数式编程语言也许已经原生提供了支持并且所有的函数已经默认柯里化了。在JavaScript中我们可以修改一下add()函数使它柯里化,然后支持部分应用。

- -

来看一个例子:

- -
// a curried add()
-// accepts partial list of arguments
-function add(x, y) {
-    var oldx = x, oldy = y;
-    if (typeof oldy === "undefined") { // partial
-        return function (newy) {
-            return oldx + newy;
-        };
-    }
-    // full application
-    return x + y;
-}
-
-// test
-typeof add(5); // "function"
-add(3)(4); // 7
-
-// create and store a new function
-var add2000 = add(2000);
-add2000(10); // 2010
-
- -

在这段代码中,第一次调用add()时,在返回的内层函数那里创建了一个闭包。这个闭包将原来的x和y的值存储到了oldx和oldy中。当内层函数执行的时候,oldx会被使用。如果没有部分应用,即x和y都传了值,那么这个函数会简单地将他们相加。这个add()函数的实现跟实际情况比起来有些冗余,仅仅是为了更好地说明问题。下面的代码片段中展示了一个更简洁的版本,没有oldx和oldy,因为原始的x已经被存储到了闭包中,此外我们复用了y作为本地变量,而不用像之前那样新定义一个变量newy:

- -
// a curried add
-// accepts partial list of arguments
-function add(x, y) {
-    if (typeof y === "undefined") { // partial
-        return function (y) {
-            return x + y;
-        };
-    }
-    // full application
-    return x + y;
-}
-
- -

在这些例子中,add()函数自己处理了部分应用。有没有可能用一种更为通用的方式来做同样的事情呢?换句话说,我们能不能对任意一个函数进行处理,得到一个新函数,使它可以处理部分参数?下面的代码片段展示了一个通用函数的例子,我们叫它schonfinkelize(),正是用来做这个的。我们使用schonfinkelize()一部分原因是它比较难发音,另一部分原因是它听起来比较像动词(使用“curry”则不是那么明确),而我们刚好需要一个动词来表明这是一个函数转换的过程。

- -

这是一个通用的柯里化函数:

- -
function schonfinkelize(fn) {
-    var slice = Array.prototype.slice,
-    stored_args = slice.call(arguments, 1);
-    return function () {
-        var new_args = slice.call(arguments),
-        args = stored_args.concat(new_args);
-        return fn.apply(null, args);
-    };
-}
-
- -

这个schonfinkelize可能显得比较复杂了,只是因为在JavaScript中arguments不是一个真的数组。从Array.prototype中借用slice()方法帮助我们将arguments转换成数组,以便能更好地对它进行操作。当schonfinkelize()第一次被调用的时候,它使用slice变量存储了对slice()方法的引用,同时也存储了调用时的参数(stored_args),参数保留了除第一个以外的,因为第一个参数是要被柯里化的函数。schonfinkelize()返回了一个函数。当这个返回的函数被调用的时候,它可以(通过闭包)访问到已经存储的参数stored_args和slice。新的函数只需要合并老的部分应用的参数(stored_args)和新的参数(new_args),然后将它们应用到原来的函数fn(也可以在装饰中访问到)即可。

- -

现在有了通用的柯里化函数,就可以做一些测试了:

- -
// a normal function
-function add(x, y) {
-    return x + y;
-}
-
-// curry a function to get a new function
-var newadd = schonfinkelize(add, 5);
-newadd(4); // 9
-
-// another option -- call the new function directly
-schonfinkelize(add, 6)(7); // 13
-
- -

用来做函数转换的schonfinkelize()并不局限于单个参数或者单步的柯里化。这里有些更多用法的例子:

- -
// a normal function
-function add(a, b, c, d, e) {
-    return a + b + c + d + e;
-}
-
-// works with any number of arguments
-schonfinkelize(add, 1, 2, 3)(5, 5); // 16
-
-// two-step currying
-var addOne = schonfinkelize(add, 1);
-addOne(10, 10, 10, 10); // 41
-var addSix = schonfinkelize(addOne, 2, 3);
-addSix(5, 5); // 16
-
- -

什么时候使用柯里化

- -

当你发现自己在调用同样的函数并且传入的参数大部分都相同的时候,就是考虑柯里化的理想场景了。你可以通过传入一部分的参数动态地创建一个新的函数。这个新函数会存储那些重复的参数(所以你不需要再每次都传入),然后再在调用原始函数的时候将整个参数列表补全,正如原始函数期待的那样。

- -

小结

- -

在JavaScript中,开发者对函数的理解和运用的要求是比较苛刻的。在本章中,主要讨论了有关函数的一些背景知识和术语。你知道了JavaScript函数中两个重要的特性,也就是:

- -
    -
  1. 函数是一等对象,他们可以被作为值传递,也可以拥有属性和方法。
  2. -
  3. 函数拥有本地作用域,而大括号不产生块级作用域。另外需要注意的是,变量的声明会被提前到本地作用域顶部。
  4. -
- -

创建一个函数的语法有:

- -
    -
  1. 带有名字的函数表达式
  2. -
  3. 函数表达式(和上一种一样,但是没有名字),也就是为大家熟知的“匿名函数”
  4. -
  5. 函数声明,与其它语言的函数语法相似
  6. -
- -

在介绍完背景和函数的语法后,介绍了一些有用的模式,按分类列出:

- -
    -
  1. API模式,它们帮助我们为函数给出更干净的接口,包括:

    - -
      -
    • 回调模式 -传入一个函数作为参数

    • -
    • 配置对象 -帮助保持函数的参数数量可控

    • -
    • 返回函数 -函数的返回值是另一个函数

    • -
    • 柯里化 -新函数在已有函数的基础上再加上一部分参数构成

    • -
  2. -
  3. 初始化模式,这些模式帮助我们用一种干净的、结构化的方法来做一些初始化工作(在web页面和应用中非常常见),通过一些临时变量来保证不污染全局命名空间。这些模式包括:

    - -
      -
    • 立即执行的函数 -当它们被定义后立即执行

    • -
    • 立即初始化的对象 -初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法

    • -
    • 条件初始化 -使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行

    • -
  4. -
  5. 性能模式,这些模式帮助提高代码的性能,包括:

    - -
      -
    • Memoization -利用函数的属性,使已经计算过的值不用再次计算

    • -
    • 自定义函数 -重写自身的函数体,使第二次及后续的调用做更少的工作

    • -
  6. -
- - - - \ No newline at end of file diff --git a/chapter4.markdown b/chapter4.markdown index c09a335..92c18dd 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -1026,20 +1026,28 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 传入一个函数作为参数 - 配置对象 - - 帮助保持函数的参数数量可控 + + 帮助保持函数的参数数量可控 - 返回函数 - - 函数的返回值是另一个函数 + + 函数的返回值是另一个函数 - 柯里化 - - 新函数在已有函数的基础上再加上一部分参数构成 + + 新函数在已有函数的基础上再加上一部分参数构成 2. 初始化模式,这些模式帮助我们用一种干净的、结构化的方法来做一些初始化工作(在web页面和应用中非常常见),通过一些临时变量来保证不污染全局命名空间。这些模式包括: - 立即执行的函数 - - 当它们被定义后立即执行 + + 当它们被定义后立即执行 - 立即初始化的对象 - - 初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 + + 初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 - 条件初始化 - - 使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 + + 使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 3. 性能模式,这些模式帮助提高代码的执行速度,包括: - Memoization - - 利用函数的属性,使已经计算过的值不用再次计算 + + 利用函数的属性,使已经计算过的值不用再次计算 - 自定义函数 - - 重写自身的函数体,使第二次及后续的调用做更少的工作 \ No newline at end of file + + 重写自身的函数体,使第二次及后续的调用做更少的工作 \ No newline at end of file From 95c1f6f367bf3bf8c243b63cd5da516ada745681 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 23 Jun 2012 20:49:09 +0800 Subject: [PATCH 27/55] =?UTF-8?q?=E7=AC=AC=E5=85=AD=E7=AB=A0=20=E7=B1=BB?= =?UTF-8?q?=E5=BC=8F=E7=BB=A7=E6=89=BF=20vs=20=E7=8E=B0=E4=BB=A3=E7=BB=A7?= =?UTF-8?q?=E6=89=BF=E6=A8=A1=E5=BC=8F=20=E4=B8=80=E8=8A=82=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 chapter6.markdown diff --git a/chapter6.markdown b/chapter6.markdown new file mode 100644 index 0000000..865f46f --- /dev/null +++ b/chapter6.markdown @@ -0,0 +1,32 @@ + +# 代码复用模式 + +代码复用是一个既重要又有趣的话题,因为努力在自己或者别人写的代码上写尽量少且可以复用的代码是件很自然的事情,尤其当这些代码是经过测试的、可维护的、可扩展的、有文档的时候。 + +当我们说到代码复用的时候,想到的第一件事就是继承,本章会有很大篇幅讲述这个话题。你将看到好多种方法来实现“类式(classical)”和一些其它方式的继承。但是,最最重要的事情,是你需要记住终极目标——代码复用。继承是达到这个目标的一种方法,但是不是唯一的。在本章,你将看到怎样基于其它对象来构建新对象,怎样使用混元,以及怎样在不使用继承的情况下只复用你需要的功能。 + +在做代码复用的工作的时候,谨记Gang of Four 在书中给出的关于对象创建的建议:“优先使用对象创建而不是类继承”。(译注:《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software)是一本设计模式的经典书籍,该书作者为Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides,被称为“Gang of Four”,简称“GoF”。) + + +## 类式继承 vs 现代继承模式 + +在讨论JavaScript的继承这个话题的时候,经常会听到“类式继承”的概念,那我们先看一下什么是类式(classical)继承。classical一词并不是来自某些古老的、固定的或者是被广泛接受的解决方案,而仅仅是来自单词“class”。(译注:classical也有“经典”的意思。) + +很多编程语言都有原生的类的概念,作为对象的蓝本。在这些语言中,每个对象都是一个指定类的实例(instance),并且(以Java为例)一个对象不能在不存在对应的类的情况下存在。在JavaScript中,因为没有类,所以类的实例的概念没什么意义。JavaScript的对象仅仅是简单的键值对,这些键值对都可以动态创建或者是改变。 + +但是JavaScript拥有构造函数(constructor functions),并且有语法和使用类非常相似的new运算符。 + +在Java中你可能会这样写: + + Person adam = new Person(); + +在JavaScript中你可以这样: + + var adam = new Person(); + +除了Java是强类型语言需要给adam添加类型Person外,其它的语法看起来是一样的。JavaScript的创建函数调用看起来感觉Person是一个类,但事实上,Person仅仅是一个函数。语法上的相似使得非常多的开发者陷入对JavaScript类的思考,并且给出了很多模拟类的继承方案。这样的实现方式,我们叫它“类式继承”。顺便也提一下,所谓“现代”继承模式是指那些不需要你去想类这个概念的模式。 + +当需要给项目选择一个继承模式时,有不少的备选方案。你应该尽量选择那些现代继承模式,除非团队已经觉得“无类不欢”。 + +本章先讨论类式继承,然后再关注现代继承模式。 + From 3df9191b936aa4c316ae019c2663a803a561e382 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 24 Jun 2012 11:27:51 +0800 Subject: [PATCH 28/55] =?UTF-8?q?=E7=AC=AC=E5=85=AD=E7=AB=A0=20=E7=B1=BB?= =?UTF-8?q?=E5=BC=8F=E7=BB=A7=E6=89=BF=E6=9C=9F=E6=9C=9B=E7=9A=84=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=20=20=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91=E5=AE=8C?= =?UTF-8?q?=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/chapter6.markdown b/chapter6.markdown index 865f46f..33ce8f8 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -30,3 +30,28 @@ 本章先讨论类式继承,然后再关注现代继承模式。 +## 类式继承的期望结果 + +实现类式继承的目标是基于构造函数Child()来创建一个对象,然后从另一个构造函数Parent()获得属性。 + +> 尽管我们是在讨论类式继承,但还是尽量避免使用“类”这个词。“构造函数”或者“constructor”虽然更长,但是更准确,不会让人迷惑。通常情况下,应该努力避免在跟团队沟通的时候使用“类”这个词,因为在JavaScript中,很可能每个人都会有不同的理解。 + +下面是定义两个构造函数Parent()和Child()的例子: + + // the parent constructor + function Parent(name) { + this.name = name || 'Adam'; + } + + // adding functionality to the prototype + Parent.prototype.say = function () { + return this.name; + }; + + // empty child constructor + function Child(name) {} + + // inheritance magic happens here + inherit(Child, Parent); + +上面的代码定义了两个构造函数Parent()和Child(),say()方法被添加到了Parent()构建函数的原型(prototype)中,inherit()函数完成了继承的工作。inherit()函数并不是原生提供的,需要自己实现。让我们来看一看比较大众的实现它的几种方法。 \ No newline at end of file From e23948ea461f32aa3e5b7e7c35d133df5a13f819 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 24 Jun 2012 11:34:57 +0800 Subject: [PATCH 29/55] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=AC=AC=E5=85=AD?= =?UTF-8?q?=E7=AB=A0=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.markdown b/README.markdown index f91e753..8a6b8b0 100644 --- a/README.markdown +++ b/README.markdown @@ -140,10 +140,10 @@ - method() 方法 - 小节 -## 第六章 代码重用模式 +## [第六章 代码重用模式](javascript.patterns/blob/master/chapter6.markdown#a) -- 类式继承 vs 现代继承模式 -- 类式继承的期望结果 +- [类式继承 vs 现代继承模式](javascript.patterns/blob/master/chapter6.markdown#a) +- [类式继承的期望结果](javascript.patterns/blob/master/chapter6.markdown#a) - 经典模式 1 ——默认模式 - 使用原型链 - 模式 1 的缺陷 From f556b52f94044cfdaf66d03bcfbf48519038d0ca Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 3 Jul 2012 13:37:18 +0800 Subject: [PATCH 30/55] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF1?= =?UTF-8?q?=E2=80=94=E2=80=94=E9=BB=98=E8=AE=A4=E6=A8=A1=E5=BC=8F=20?= =?UTF-8?q?=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 66 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/chapter6.markdown b/chapter6.markdown index 33ce8f8..ac86869 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -54,4 +54,68 @@ // inheritance magic happens here inherit(Child, Parent); -上面的代码定义了两个构造函数Parent()和Child(),say()方法被添加到了Parent()构建函数的原型(prototype)中,inherit()函数完成了继承的工作。inherit()函数并不是原生提供的,需要自己实现。让我们来看一看比较大众的实现它的几种方法。 \ No newline at end of file +上面的代码定义了两个构造函数Parent()和Child(),say()方法被添加到了Parent()构建函数的原型(prototype)中,inherit()函数完成了继承的工作。inherit()函数并不是原生提供的,需要自己实现。让我们来看一看比较大众的实现它的几种方法。 + +## 类式继承1——默认模式 + +最常用的一种模式是使用Parent()构造函数来创建一个对象,然后把这个对象设为Child()的原型。这是可复用的inherit()函数的第一种实现方法: + + function inherit(C, P) { + C.prototype = new P(); + } + +需要强调的是原型(prototype属性)应该指向一个对象,而不是函数,所以它需要指向由父构造函数创建的实例(对象),而不是构造函数自己。换句话说,请注意new运算符,有了它这种模式才可以正常工作。 + +之后在应用中使用new Child()创建对象的时候,它将通过原型拥有Parent()实例的功能,像下面的例子一样: + + var kid = new Child(); + kid.say(); // "Adam" + +### 跟踪原型链 + +在这种模式中,子对象既继承了(父对象)“自己的属性”(添加给this的实例属性,比如name),也继承了原型中的属性和方法(比如say())。 + +我们来看一下在这种继承模式中原型链是怎么工作的。为了讨论方便,我们假设对象是内在中的一块空间,它包含数据和指向其它空间的引用。当使用new Parent()创建一个对象时,这样的一块空间就被分配了(图6-1中的2号)。它保存着name属性的数据。如果你尝试访问say()方法(比如通过(new Parent).say()),2号空间中并没有这个方法。但是在通过隐藏的链接__proto__指向Parent()构建函数的原型prototype属性时,就可以访问到包含say()方法的1号空间(Parent.prototype)了。所有的这一块都是在幕后发生的,不需要任何额外的操作,但是知道它是怎样工作的以及你正在访问或者修正的数据在哪是很重要的。注意,__proto__在这里只是为了解释原型链,这个属性在语言本身中是不可用的,尽管有一些环境提供了(比如Firefox)。 + +![图6-1 Parent()构造函数的原型链](./Figure/chapter6/6-1.jpg) + +图6-1 Parent()构造函数的原型链 + +现在我们来看一下在使用inherit()函数之后再使用var kid = new Child()创建一个新对象时会发生什么。见图6-2。 + +![图6-2 继承后的原型链](./Figure/chapter6/6-2.jpg) + +图6-2 继承后的原型链 + +Child()构造函数是空的,也没有属性添加到Child.prototype上,这样,使用new Child()创建出来的对象都是空的,除了有隐藏的链接__proto__。在这个例子中,__proto__指向在inherit()函数中创建的new Parent()对象。 + +现在使用kid.say()时会发生什么?3号对象没有这个方法,所以通过原型链找到2号。2号对象也没有这个方法,所以也通过原型链找到1号,刚好有这个方法。接下来say()方法引用了this.name,这个变量也需要解析。于是沿原型链查找的过程又走了一遍。在这个例子中,this指向3号对象,它没有name属性。然后2号对象被访问,并且有name属性,值为“Adam”。 + +最后,我们多看一点东西,假如我们有如下的代码: + + var kid = new Child(); + kid.name = "Patrick"; + kid.say(); // "Patrick" + +图6-3展现了这个例子的原型链: + +![图6-3 继承并且给子对象添加属性后的原型链](./Figure/chapter6/6-3.jpg) + +图6-3 继承并且给子对象添加属性后的原型链 + +设定kid.name并没有改变2号对象的name属性,但是它直接在3号对象上添加了自己的name属性。当kid.say()执行时,say方法在3号对象中找,然后是2号,最后到1号,像前面说的一样。但是这一次在找this.name(和kid.name一样)时很快,因为这个属性在3号对象中就被找到了。 + +如果通过delete kid.name的方式移除新添加的属性,那么2号对象的name属性将暴露出来并且在查找的时候被找到。 + +### 这种模式的缺点 + +这种模式的一个缺点是既继承了(父对象)“自己的属性”,也继承了原型中的属性。大部分情况下你可能并不需要“自己的属性”,因为它们更可能是为实例对象添加的,并不用于复用。 + +> 一个在构造函数上常用的规则是,用于复用的成员(译注:属性和方法)应该被添加到原型上。 + +在使用这个inherit()函数时另外一个不便是它不能够让你传参数给子构造函数,这些参数有可能是想再传给父构造函数的。考虑下面的例子: + + var s = new Child('Seth'); + s.say(); // "Adam" + +这并不是我们期望的结果。事实上传递参数给父构造函数是可能的,但这样需要在每次需要一个子对象时再做一次继承,很不方便,因为需要不断地创建父对象。 \ No newline at end of file From 0f060dd7b7b6e7beb1294562e6e3427d15ac3cd5 Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 3 Jul 2012 13:37:39 +0800 Subject: [PATCH 31/55] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=AC=AC6=E7=AB=A0?= =?UTF-8?q?=E6=8F=92=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Figure/chapter6/6-1.jpg | Bin 0 -> 16984 bytes Figure/chapter6/6-2.jpg | Bin 0 -> 24232 bytes Figure/chapter6/6-3.jpg | Bin 0 -> 26729 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Figure/chapter6/6-1.jpg create mode 100644 Figure/chapter6/6-2.jpg create mode 100644 Figure/chapter6/6-3.jpg diff --git a/Figure/chapter6/6-1.jpg b/Figure/chapter6/6-1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..805968027345c232dc27ceeef52a4a05571db217 GIT binary patch literal 16984 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%0D>Y7V9|@bj z(K3=KkO=>^n8{Q6OEFW=bJDc1WIqAi-{J$FJQYQO7!{kD9D}!<0S|Vn;lXtvnC;sE zSS&iako0RQRpTrQtfU&4?J?aMC3b=^SQ$KS%LWHGujuJ${IUN-5XC%y()F{J3f577 z?OKWuD!%4#jcDNmkFJJ;Bsx(vH;Z&~4?G&G=YIxVHr3?(lR@b(f&}8PHQFxepEX)e zzm^0K*m=|+R08v&Kgf;JL7;!v>|Y{BG@v`{AaCHhh*ZS#|0p>;7lnV`qGmjU{V!DxkFvidyBu5Uy@xK`a@w>>G8`V#WI~I-pXp6r!78i{ywmcz@hh`| z65HVPoA|}9;9JHJy+XwNx*sZcM)y;8?eYBS^ySJIG|IKqQX^GGyL)Or^M2VuzAs|6 m5?7|a;+T8IaqqZv-<`g5VhkVw_>ayn{41*b_8g2n_TK=Oixpu2 literal 0 HcmV?d00001 diff --git a/Figure/chapter6/6-2.jpg b/Figure/chapter6/6-2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3e1c70d2c35cf5b26eaff0b604064905b7b74768 GIT binary patch literal 24232 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 Dq4pDT literal 0 HcmV?d00001 diff --git a/Figure/chapter6/6-3.jpg b/Figure/chapter6/6-3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0bda99d3a90c9c3e778fb630f44c5fdc4e47d4ff GIT binary patch literal 26729 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 XX|s~W{GVul6Q=kUTbzBwzV7-L%E=|M literal 0 HcmV?d00001 From 6bf9b83f5a570cc257e333e7d5906d0809745a44 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 4 Jul 2012 13:31:47 +0800 Subject: [PATCH 32/55] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF2?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=80=9F=E7=94=A8=E6=9E=84=E9=80=A0=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E7=AC=AC=E4=B8=80=E5=B0=8F=E8=8A=82=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 56 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/chapter6.markdown b/chapter6.markdown index ac86869..09df7d8 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -38,20 +38,20 @@ 下面是定义两个构造函数Parent()和Child()的例子: - // the parent constructor + //parent构造函数 function Parent(name) { this.name = name || 'Adam'; } - // adding functionality to the prototype + //给原型增加方法 Parent.prototype.say = function () { return this.name; }; - // empty child constructor + //空的child构造函数 function Child(name) {} - // inheritance magic happens here + //继承 inherit(Child, Parent); 上面的代码定义了两个构造函数Parent()和Child(),say()方法被添加到了Parent()构建函数的原型(prototype)中,inherit()函数完成了继承的工作。inherit()函数并不是原生提供的,需要自己实现。让我们来看一看比较大众的实现它的几种方法。 @@ -118,4 +118,50 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 var s = new Child('Seth'); s.say(); // "Adam" -这并不是我们期望的结果。事实上传递参数给父构造函数是可能的,但这样需要在每次需要一个子对象时再做一次继承,很不方便,因为需要不断地创建父对象。 \ No newline at end of file +这并不是我们期望的结果。事实上传递参数给父构造函数是可能的,但这样需要在每次需要一个子对象时再做一次继承,很不方便,因为需要不断地创建父对象。 + + +## 类式继承2——借用构造函数 + +下面这种模式解决了从子对象传递参数到父对象的问题。它借用了父对象的构造函数,将子对象绑定到this,同时传入参数: + + function Child(a, c, b, d) { + Parent.apply(this, arguments); + } + +使用这种模式时,只能继承在父对象的构造函数中添加到this的属性,不能继承原型上的成员。 + +使用借用构造函数的模式,子对象通过复制的方式继承父对象的成员,而不是像类式继承1中那样获得引用。下面的例子展示了这两者的不同: + + //父构造函数 + function Article() { + this.tags = ['js', 'css']; + } + var article = new Article(); + + //BlogPost通过类式继承1(默认模式)从article继承 + function BlogPost() {} + BlogPost.prototype = article; + var blog = new BlogPost(); + //注意你不需要使用`new Article()`,因为已经有一个实例了 + + //StaticPage通过借用构造函数的方式从Article继承 + function StaticPage() { + Article.call(this); + } + var page = new StaticPage(); + + alert(article.hasOwnProperty('tags')); // true + alert(blog.hasOwnProperty('tags')); // false + alert(page.hasOwnProperty('tags')); // true + +在上面的代码片段中,Article()被两种方式分别继承。默认模式使blog可以通过原型链访问到tags属性,所以它自己并没有tags属性,hasOwnProperty()返回false。page对象有自己的tags属性,因为它是使用借用构造函数的方式继承,复制(而不是引用)了tags属性。 + +注意在修改继承后的tags属性时的不同: + + blog.tags.push('html'); + page.tags.push('php'); + alert(article.tags.join(', ')); // "js, css, html" + +在这个例子中,blog对象修改了tags属性,同时,它也修改了父对象,因为实际上blog.tags和article.tags是引向同一个数组。而对pages.tags的修改并不影响父对象article,因为pages.tags在继承的时候是一份独立的拷贝。 + From 00805e5f0adef8e1bd482f2bdd0735ea44235b8c Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 5 Jul 2012 13:03:51 +0800 Subject: [PATCH 33/55] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF2?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=80=9F=E7=94=A8=E6=9E=84=E9=80=A0=E5=87=BD?= =?UTF-8?q?=E6=95=B0=20=E6=8F=92=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Figure/chapter6/6-4.jpg | Bin 0 -> 36936 bytes Figure/chapter6/6-5.jpg | Bin 0 -> 4550 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Figure/chapter6/6-4.jpg create mode 100644 Figure/chapter6/6-5.jpg diff --git a/Figure/chapter6/6-4.jpg b/Figure/chapter6/6-4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b42531b8cffab9dc8578e73b08ad1311bd1d10f6 GIT binary patch literal 36936 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 literal 0 HcmV?d00001 diff --git a/Figure/chapter6/6-5.jpg b/Figure/chapter6/6-5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0325547e0d9e51ea09faf180bf788f334aaa6306 GIT binary patch literal 4550 zcmds(XIN9|w#PSwCPf575j0VX!qARXK_!ETG^t7p0~tl635WurXG8=6fuSo1L5iR> zsgck|X^O^Bgb%ZQ;S);6J;E?%6 zvx@)+2M6#o`vF+vfDr)VMX3eB9hzT-^M;ygYn@{DOi4`~m_(2SpDF9TYhz zAaF?Hkcb#qTwGjGSW-#?EF}sS2k&pf0b;M=;y%F5eE=*ZAO!xm8|ybfgb%RdI0NF4 z1vo`GKq4HhE&vJu9NcVg_Z|MDad5JI`=-U;A3zZh$1OdF^)00e%Tdsl(C=Cr&CV{h+0-qkCFU z-{^wzMH5pq^UGK49UPsU;qKQxZg_fm``if%4nc;7h2M+49~b{1Au&B8Gb=mi(c|2r z;*!#5W#!Lb)YUgMHep^hW8ZXkb@%kXeb+ZQ^l5m6FiISonVp+o`11AJ;u3jdb8CBt zLfzfl=i&fBzhnKK>>s#9*j$`kTp%vqeJ&18B-=nDT-?V`@rWAQ@?H%TlhwG(2R@(n zwD$D@IZZo~__f;u{1Wn7GYaH=w7-%4YhW?|5!v6t{+nwI5Cn0slLrz3U;x92*?W&h zKvNH^vj9nxowLx%Ru=HvI}{b$Uvf?e2s5JlQ?D1CVF9svtsXbeTJMzBGC%0Y<}n8y zL*v)|rZD-9-#UngPC}5q{@tb9$$ngXOCGYBX#R6f)(b!WkZRZ z_WZbgnZj>9qM^VieqZg9T{F)Nfc#p?9H9DEHTlWjtLlhXo`R(;&j@Zm8+&9duVO z+e`JZ@7|%KBvkk&3jiswfZ65qi2(kevlsoM$pUJpncH8PJQggVdw`M3OR0g8G!RU+ z2E{@_7O)ZuYYSrmchgw_Ip}*As`wQ_|DTRk5;h0sw~jiRkX6QVy(!4cI(Z=@k|wK~ zYVXqQ6E~(-6GxfvF((-wL>7SA7tU_YC8}j5)d!IzZID0BxMf{sh*C?dF{@H(5>4XSmP!H5F zk$+u{(QKi=IrY(2xq@i7sFpbHPg#DN{$+WB@jjq+4@!ef!J?O00OtZbx8L+H{K%mF zmmU*l@%IW0$R_byhv}9x7RfFa%t?ZJqdM;>zHG9d@gi#Evc?2<9JgT7k=*xo&>zu^ zu#8mcUposUEq6+L4Ee;(_FBbE8dZHZIkz+aP;h{!U-1-%Bg_sWITm0{*GQf8+++LE?Uq z<~x?#MWV!Mp$?4Z!;FShwj!YVX6^6YIgKSBt$kc=4BXTd?P)(Yi1MnDjyzXjEf9KR z#=WctgnTh9v$sCld0a&)2L=1{9C5fUfm%&EEDy@eVhJ=yKC7 z>%yTY2;KVG8M77fxxUNI!~s85xSs8;bd!1{zIi@5+T6_G3T@QSd7MBBF&KZ_t!|4r z<%4cef2bv#8Vwo*tP>M};YXZ7&rTB`N~{yT#cJ^sFSbv4EBz#qTd*#wR~n#Vy*H_Q&rw73jK? zD)IvnvP}G3PYA3bCsDl>G2?1C)CD;<;OD=ZFYfTVan0=PIgQJF?H7xYPAa(cri!0rH9h8MW^F150)I^O%()Eax zgZOm^Rm^H&sdqRsxz)Z(W}DQL>`~~}TWV5-U2^a(FCVL8GFrc?%6iy4deVY06s$Kj zXP3#o+#fFw(2tVjA*vX%9aYniecM-0*k2=9;py6RWWz1fusC-Y&d&+hWfMBDayub&mA(@mDJoyrGy zafAC}^xJ3?&J#W{POasY#W2}0{*01#^CJJ_2fvySx8!vC%@*zL(Dh9%6?+1CC|PsG zqLQn|U>lC_&D7hgQ1}r&kj53MMoow!b1plkUY3ODcD(%52$c=})U8m)f9!;29rjHIS_!iUc)Qba@RZOFzoiN~As zob@k{x*B<9w^V(xaApAuPf)j=792L^cPe2#(LG2CjKjZ`drnx=N20322cGiu#j|92 z$wiHcK9|0*n$^-UXI`_Nk{0Go!cdcRTT1y*uLqKQpfRfu{Vq7*2U|+o%V>wMrCt@B zw-gusr^_%I^h@eoRBLN$)HFk^Q~FW>B&*ojuxzZjeaE!u$Aokg(bo0A;gTc#KW&;* zhPR~}Y>jQW!M5zH?oaF_9Ign$MRJf%zCjB%@-&k+e09+BFPiR9@7ZAMtxsXv-+PyN zWqe^SC2sjmsRg=87DWbH`u4PIBFSoZug<{uaq^BjUj|*0i0&4lCFyfG(fi2`W+AH^ zXomBbmUS`uPo%PYYHG(uf*)z^PV3AJ8oQ^SeLtVV>$q{Em)59Ok<+0Yv3;{oTXubZ zYC2>Wq~(|oO>q-O2#@(Hmw$fe8+Jz_zYXh6Pz_TVs8>m=Do_3RxzuW=KDDo|8s-HR zdQAqIkX8sU$BoLp?Uo=>Ys+C*ugY^O<|=_T>MOh3Z$^F0Ix>*i9Ke9HfL|k=)GEqm zS9c2b9g&%kgXJth z+ue|6MCDu>-1tU~JFTiR<5Jd)Z7AR?;7N0*UC=O1tT$@Q469wU3Ln5jmTRUVgXdPO zQmy2VLo(N-2x-eGXwc>9HVY@xm_@bEEjOc5_+q-TYB4TK2QLeol1-BddobxaLChA1 zK8&=T!^gJ^za$^5U?f$+N0surbjE@!ch-LOe{Xpcr@Xe2q33{@oJ&UEMmMG~&TPqz znXD|AZmrg?WZH*%q%6s2>rSS$WaYflzSf}6nW$LE`RmpT=+CcMKw&9?L6&3zA||71 zuy0DiEFjXD{k5{;#i2<>26A4LN@U#n{hF-+{zdG(?qF%2>$~U1+FQ7dpB^n>pf^6l zMr5@rz9}b@5x=1idm)6|k0ad18wn5AgH@HCNGxFIT-RRd2lzvo%MrV2C&D8Kqx$2U zK1R3Ej&|~}fP`c`Y-RsEyS&4rO~Eqg9LA##Y9c`B z+ldExkDcr89S;^43M_Ynwgn^Q4xh|pa?9yee^Qeg7G9ynv4BVU1jgn*krj=*m%+8x zvHpO)W&h$suxv3T?!GvBM<8{N-xW0_f+B;XV&<4JIS5J@f^N+7XM=#3ExV%sEMh!= zvi1Mv)pn39qZLW$78uI{+GW|lg^K%egs1=&ea2@!J#*Vn5?)oVMiG-(z=J|3>lWsDM|SQ* z)S0qHr(*s3T3i+iClpkrr=qNM?WsuAbLyte$n0=c(ZIH)tm+Axb^5@hx4867R`HWd z5-{p6Yy*dNISaF=v5V_s4I+KUk!HLtyVrDYce>r(M-BSb#t9}gxx^+Dsmkq_uV9;E`?9&VFB=I-UgkCA k@3j_W@nJ;C#mGDn|Gk(DUI(%OLyH>PEIZbB(JbO$0SPDWB>(^b literal 0 HcmV?d00001 From ac36271a79411f1d432dfbb4b70b2c13c6284e8d Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 5 Jul 2012 13:06:53 +0800 Subject: [PATCH 34/55] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF2?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=80=9F=E7=94=A8=E6=9E=84=E9=80=A0=E5=87=BD?= =?UTF-8?q?=E6=95=B0=20=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91=E5=AE=8C?= =?UTF-8?q?=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 69 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/chapter6.markdown b/chapter6.markdown index 09df7d8..2753b17 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -165,3 +165,72 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 在这个例子中,blog对象修改了tags属性,同时,它也修改了父对象,因为实际上blog.tags和article.tags是引向同一个数组。而对pages.tags的修改并不影响父对象article,因为pages.tags在继承的时候是一份独立的拷贝。 + +### 原型链 + +我们来看一下当我们使用熟悉的Parent()和Child()构造函数和这种继承模式时原型链是什么样的。为了使用这种继承模式,Child()有明显变化: + + //父构造函数 + function Parent(name) { + this.name = name || 'Adam'; + } + + //在原型上添加方法 + Parent.prototype.say = function () { + return this.name; + }; + + //子构造函数 + function Child(name) { + Parent.apply(this, arguments); + } + + var kid = new Child("Patrick"); + kid.name; // "Patrick" + typeof kid.say; // "undefined" + +如果看一下图6-4,就能发现new Child对象和Parent之间不再有链接。这是因为Child.prototype根本就没有被使用,它指向一个空对象。使用这种模式,kid拥有了自己的name属性,但是并没有继承say()方法,如果尝试调用它的话会出错。这种继承方式只是一种一次性地将父对象的属性复制为子对象的属性,并没有__proto__链接。 + +![图6-4 使用借用构造函数模式时没有被关联的原型链](./Figure/chapter6/6-4.jpg) + +图6-4 使用借用构造函数模式时没有被关联的原型链 + + +### 利用借用构造函数模式实现多继承 + +使用借用构造函数模式,可以通过借用多个构造函数的方式来实现多继承: + + function Cat() { + this.legs = 4; + this.say = function () { + return "meaowww"; + } + } + + function Bird() { + this.wings = 2; + this.fly = true; + } + + function CatWings() { + Cat.apply(this); + Bird.apply(this); + } + + var jane = new CatWings(); + console.dir(jane); + +结果如图6-5,任何重复的属性都会以最后的一个值为准。 + +![图6-5 在Firebug中查看CatWings对象](./Figure/chapter6/6-5.jpg) + +图6-5 在Firebug中查看CatWings对象 + + +### 借用构造函数的利与弊 + +这种模式的一个明显的弊端就是无法继承原型。如前面所说,原型往往是添加可复用的方法和属性的地方,这样就不用在每个实例中再创建一遍。 + +这种模式的一个好处是获得了父对象自己成员的拷贝,不存在子对象意外改写父对象属性的风险。 + +那么,在上一个例子中,怎样使一个子对象也能够继承原型属性呢?怎样能使kid可以访问到say()方法呢?下一种继承模式解决了这个问题。 From 0765f5e048dabed17a871b74542ab842f2cc379e Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 5 Jul 2012 13:24:59 +0800 Subject: [PATCH 35/55] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF3?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=80=9F=E7=94=A8=E5=B9=B6=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E5=8E=9F=E5=9E=8B=20=E6=8F=92=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Figure/chapter6/6-6.jpg | Bin 0 -> 30972 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Figure/chapter6/6-6.jpg diff --git a/Figure/chapter6/6-6.jpg b/Figure/chapter6/6-6.jpg new file mode 100644 index 0000000000000000000000000000000000000000..78926190d4ee8efedc04412eaac6eb391227c809 GIT binary patch literal 30972 zcmeFZcU)87wlBI6DN>~O5~YZOfJzgDL`9m2p(s^|3Q`3Wqz9rPT|iJkK?zbsq(pir z(iK6ZcR~QALqZJ@lDGVw-`;2YbKm#&JNw+vd-uH?SbwZG=O}ZHIlg0zIjKX`Nr3&b zww^XXLqh}H1pff)D4+?@9ysvxAN(Bz|LGX%=nfvFV`5;SXJlbwVPR%sW@bIi$3+L(4%!Z3Z9!Ktl&o`;*{5e>4X`8tEAr4>2)=50tS32WV(%4;-ZZNj3O% zAb1}*$U(<>^z;RKE~8rvV)wbvJc@tAD1Nc1fycO;Afa&EBlr*#FCV{vpyV;B<0qsQ zm6TP^s;X&T($dz^)w^tR?YgO%xrL>z-5q-eM<-{`2VUMjzJC58k3+-4BO;>`o;^=Y zN`CP&B_lH{`)$s<-1o&LrDf$6l~vV^P0cN>ZJ$23_w@Gl4-5_skKm`KXTJZKots}E zuB~ruZf%oxc7O6k1JM2lTYu;5FMM%;d>uG=koF+MPrhgl_<#p3$3eQIr|CH_7%|+s z&n0%|5hM4-_%}rjhr|_(2|Twwx|w(-6!DV8pRE1C*}ukE@PCQ3zccoqe2oAsv^1dd zXgL5Fuy2wPbsYGgaL~3wWyrPz>I%e6yh9U;Wpt?Pz3=;}g>92cY|M%4@OV^d8HyNPq|KRwWe( zFABtbN?yVd3cUUH4tL^Q=4;*tzM~zc0`}-9BW*2#Dy=DqsDisE$+sq{0EhLQ;B$Xx zb?1Vpj=7>o`**zS=wXTjhv?v!VrWmX~Iogx%KIxQSz_>51xCahi6=MBulRuvaS;b_pnvtM-LHg7Fnd^^cIINBKYsr^~8MeGhxm|Bx z@>7A4#NMC|+?pGN3AJR>gW8Cd9w9QcPyree+}fJ&1`!jk=z1skLvI^rSMM&>sci2Ab<{^h*()?lttxK_K5jT}Dkw2q>tsm;*HtrFiP#?9rPUMm z_V%~+IsFR5DMEwIxD7WP4~$?2p#s7fS5q^?cFhw42Ze)AIM?Ti&IGzy$8k`BuuY=M zzEDLAoXpck1=^1Pc133NUFJg33dAbewi$)5LmE&4?Pa0~6`%tJLu`lreraprT}s)L z>Q~jyHdghS-1_QC(b6xCl_g}Jf4!KHPa}RXWL~E~a|~J`*A|Ov@+YNIVn(NOC`{8- zVEXxAFC;Ga8Eu+xvlVW@)@Oi-p&AQ&9yr0TA&jD`^Otj*@$0fmE+Q9ytc?t;P5hYZY&JWsDP8mNyC&Ve!`BjT-V0z4 z=-zbvqxWg_?_K@V-mg{n=ihZdSs>>wyS#$2&MXVIxNd__)Gy9>mUQ&`i$|{j_=W%1 z@UI2A9(QkdtVUFv3a~Ctx$mmDN1;hFaa17I=(j6o6-GM!wtpxtf(rcK(A?*sE>v0T zOX3uQV-(e>QsAp|?VG!4>$<_9t?sPZaen0cHev6x7*xe8IR8x#)*xpj$H1{(D&UNW zJ*C|&ktTUjXTQjDpO;Yj*7eLW^@NN{iTn|b{o9<^odNEo*{STPX$=qN9ju=V8YOy+h%(EYW&L9e&XyscyW(=m@-60ctO z6{ejxC6|dmg-J(W9>vs94t@I`ZcGxXLrsbXpooKw{&(J%cxrS`M(g!)U8$C_Jt1>W z7)Y1zz%H}I`Nx@(cJLxR$SZ?pD(Su+=12RO%sWN}jW6D9Bu^iiDZD>X$M?m?7m_Gy ziNW`ji~VHtx!g_OESQMf4G>r|le^!rb;%Xein>U!m#mt}qR z(c8YNIx2>oyRe+bSg9>?`iyQWwqP*Tmdu96uv(Yc~z+H>7AIBBxCs~~TD6q5# zzm--thvGxo`9hVDcHzwSmkB?fxn~VaJ@#@R|M;qEj~lB{wDTG}avNTEuP4X)`t#In_lnJ;O}{XOnjqH#gPK_JJ;^F&I{%}fsi;Zj{3DfA$wtRw^11G}5${@T{z{$~{ z+k5vaIvxIA>8p4xPGDF_BA`P4OfhX2>_l5Br31U>N(Go;e_HZ>!fcZxS%aw48l+&g zod)L|nIFK|-=?|A8+?!PE_t+eeaO}wIoNMeCWNwtBr88lqyhzS4#IMBWDq;~5XlxF z<1nR#X&Seeh3aM(Q97*=hA$h~L~5BVD)Je#Tpt__=lJiaD{XnGJ#zCSAxUjW(+#mKPN+KyFp^ zk~Iz|;T(G93hm8iW9nTmJoK$IXfR7i?5w$ zK1EY$9pw`^L3i%*%!IPK4j$7AkMgk*CW%kGb2;zY?YhU7@s|b^92mXIv7;|ry4Lcb z#7(v~HL&F#_sK`++gIcWT_N@2uw}&bJ26t63(r!JXwRLrIg+#V3B~id9GG|^`}U!X-0HJ?FUl{R9gW?zZ9y?0 zFL)A7-GzO1T2GPe*QkJgxL#j@+eUm)GDp+N1T6`>FOcLT>((AVCk`U31hU(g3eD8L zDmHT$Hm4xGwNBG-h}Z@jT0(5*oh*yR6zw+wf$wgNFh|N>;Jq zxXOHyI=9@X(BXpRnYNy&{e)tzJgtL=6fZR+*h?h3S7e?tyfd_V76kt`1Ym z*!PysyNTZubGF>WC?CtW2KdYrBn-vd^X;C}r=313L0cBJeDF3E0B0ckNK6p(LK@Ir zdkdQ4HSCr%xS6-ezp~<`DyQkUa^B8-IFf(l)cg18H_f!(lLe}XU=KPUMNbojQ@+?- zJStcglQOJ#xYabYoA%VWP{t{VH65|HJu)e<$hKh@ET=jh5#ChKLR5Kj5^2EpE1`@@XByfxJdS=ab~8_*x)`T}G_K!^q-+?{pmSFH7-9`eh$% zdsQX41dmSpu^2yR1Pr;3MxMW`AijPyXs#ftT}z!R(%~}tw}dQ{H}Fk zJaKI(X1OfPrJR&Uh+1spBEd`uTh(dYM21j#n^l9_01iJlo=IPc$Vr^&cgGp=qtBnP z2;L}mFgg*2{}dC%VhI{D#3WTa`#6T??`1m z(Xsf~IapDjDfkF;TZJfdKiLG2UYd8s)E~9-MKs;B)_mb6YwA^&pmYB-=Z!-_x7y!L zr;P3Aedk33y-k8|>v#)@$F`=vk$f#KC2gBozQ2*#Ai?f-`+YwBbOH~p-2l9GHC&#s zILXk+MmLw-J!CzfCPNs1EEpN`mHD2WE&XIB2lfotQDbcn9f9m)n;*1f<)og}pYt8u zLiN=Kz9r=tml8Y5MR~)UQ?3x5aJR)As+Hdk_naBcT(>-C40X+Zbn?7X5ut0!J+`Zv z3LJLa-mYGK^1#|zD6vltgQP>f(aMAi+uXhT%z#P!;+~C0p?=tDa+`#iA-cpMF5*F) zpj0gKg5NZ&XN$kTlUYh`*g1Idh_-b`)QIAz2oNRMB}slYe?? zs+OU`QpVqt{N;HR# z#dO6mY^j@}?Oa~z<{Y>o@}hMKgmtl-uj={c9^A6dYrwrH@0)g{{@Wg?5y3a`K%*9%tEi5P_ zD^d*=xE+h30$Cn|MP{*-K`OB30(w15M&OjY5o-yM7(fud%u0l7k@O}DU~p36-dXec zt!j~&y1K$D%y`>7O+l-Sj2juB4s@gglPvTsGKC;adqD!5*RtBO0-doa&?AdA%MI#+ zl?*m^I!y948E6`koF?}c_pmJU$$Tn4YOGlaoiZcZUssj|PIl-?JWJ41!hue_0{I9P z$S|3VBTJnG0oAyFltOXeTDI})@$PjWvSJ&EP<4N`p_dj~^4 zj)9TB2TD=_mM%gFB@!MCBfKsFG28>xKel@JXRA=XZK)BHB_udpSWgpCl&FfOGAae{l!fD`^#YhP0%6#@gOuZl}|C>PT!e zhsp0G2tQj9?LpiA!2KqLi=b-^Z*i!JTBaPMw0HUlDmvZs+A9$gzgZl%eun1br!Ah1 zHnIY&k!@EJ$_OgVyNe3MbpI|o4|rw3<-oX96#gw#o(goE?k8ah8MXg`jISUOpsot` zWKno6KM<_T%*&JPQI8=TmysZnf4q#Q0z#P-0W_XrpA&r$g+J#&1zP83(D$~U??IG7 zW&gE-pJ@MY!^raQ!>9*A=%50Ou({_X5EUL8g;7*=ey9C26u*383ktfyzc=v{I{sD7 zm@F**C50W;eS)Av;VMUw-Qs>HJ|VE_=P+rLT4p9*b-gGw50wmR#{@Dd-oH_f^|p(# z5=yk{_{2ba#H$6mx)nt#c*mGDJLSGq;8CPoqAf|J8$IZyYAWz7nC-|W&qmkpRR42{ zTmJuE;{V2n3Hg1rNbH4|<_wW~x8Q?H@|(wp}#f?*-=oqa6FJZ{*KY`jyiysRx+ zx2oZ^(D=NG>FWZ&iT{{A9LV(FHgK##y%7G=ItaY1r}PVgJUmePYQ&gZ@rX#tz%M`+-GM5pR5FB+p{lLF@(C-e?5PLp3!xQ!@*jwY|} zQ{~jRS8i9PZb#gUr8yE?r$%c{1uQ2iXEk}+8khZ5m&)q2c_US3kOJiu^c*e`4Hs|)h5B_rj`34mz(lMg~J*qB4d2J}rOztRQqtRQ(7eMQ* zBMaPvl&FwX!yD0iJc{Uj@u*x9X4-2xyqTE_(EFM-_{YU4y_WmRynn^Y#&Sf*Rk)@3 zvY;PdKt<9laFgXunDgvXClxq`C*)9C45X-l?iA+Cem0WXXWH6he#1QsDvoZwH^i^V zCPMNtFZ-apmMC#@E%1#bhj{trQV;qst^!I+*6;E}1@$_|w&WqcgUvSlf=C+(K{RKY zxh5+?k1qRrL8)?sH&No5E>?ke=VI8`fzN=MWEQ2@l0FxYJK%yERx8Mkiz=%i%<~y# z=)Fbc>GOx-eT41JEl*vGPpHqR#6^e-ke-eVJ~}#BYJiyH4RObJ6)z+O3H4|8+NN6V z4>_%98XOAnHaf=8!JTlK)AYjz>Y>^X^qwH8qM=?aYVZP)u52PV-~5K=Jq&AU_k^C8 zlbWuc^h@)vRxI34KMF?~wC}QNE1PLdjZU>D`hdaNAYNUEaeR#OsA1nGZN;a@oj=ZK z4xS@dMyH*kl~*6+*^_nhQCkg0PkGmfQUPw?Rl%$T*s+n1GsBiTY4KT-R=dk={P@^mxZ5@!ezB0%*vF|z$*xC1{ir>ZozhdC{CG(x9uE_C zDj|inc4c{|yyDbO&SPIH&g#B=ey)9nrgE^>EWGm+aeMjxO;?80Ta#>TV{Jb7GQV3b zU%<=I2NXBB*2dnGfLm@ez1tKE1V5qo3V*tKKi$25cKr9cfQHuAS`IEs&0odCB+i^e z;|aj93L{#+Ja_@MBsyp##viN*Du*M+HZOQU!F9{cH0Dpofj0l0yJg6qBf(A2vP zDd)RSOi%&8N@28rX_;;#ic3U``#LAjG#2!+$SJs%u03uq)V_F9T~rk$3d&SUiYKf$ zr*Zd*I_4{ND$ic24DjI9igip;k=8wOishcJ`c^s{%l=F3%ovoL3Jm3DQGt2k>+?#) z*1SCt(rg2-vcFDvkP`V)MbFYLeJd-wzU#*P@fS0WUju-2K!ffUWVmZ;0yR^P)T9Cp zpjTpR6WrV)W4>;~X)rN6me_`blMrvo7T>13mGbtDSq0E4Noroh75b!zhG1`_%GQAQ z#j|fD?94u+2w%})IBu?t40>39v;=x0IW5>-Srj{#0J=9r--xFJ-kg*e$VK0?9BI1a z%2QXX=kCtLI2|R)6p?=OIPF7V`asbG+<`fNo#@RtZVPkS@`}8sgM8bkYiRC$SXujW z%sYwZ6E0@x<5z%(2)Yb^HctiqX#b~=Hcxb?L?fDEWC1F$rAR3eZQRYOLEDk^8`R_< z-Nm>*ILBQSwY3$fJrm~Ow{py5O?Rcag!8JZI`IA!H%%3S71gAM1l{zELE=w~jM|TZ z{GG4ANl0hFb_kj))wh=vMRK7C^iJXSd9e{Fk^&W2as+*>2c!zn+s8~nC@fT9PY_xr z+OVnT1O1*+588{r-i6^`LuEnl(S;OF1wvqy*0=(l4}r$kT~&^lFzjzPNe=L6)P^PH zC}?>=zqBZG(P#;BE!lI$QU;It?dD&J+wBWr=$9P({~X~Se@dM17m16LE>Hm}1hItD zst8J0ACVQrP6eJ`fvrT5;ZIrkho7T*|p@F~5o z$U4Hq*S~LMNIN{3?w;OwxxdkkBHe+EDCx`pVQ8~7jvtVTxLp;jYp<1Xf;%&lCG6nM z5X5Vvps!RwknFSv`AlGFQsK4XNp2DqtMA#A!Et8yXIcJuHe0*Z<*sP>!57!bwCA@! z!T;oGpxM@2;%dt|+=$Lc1S?dOd0On=C$yD%|Ehy%!rgosF+sH-jf+2^An-Z@Od$s&DB3r z(}%H5qvtS7%Y53AQ%O#Rg61FN976}ub#)iNS7ItBbqg|dGp@xd8orMJm>2+>A+r`3 z;T_5ezN;1nVv@kOU>flOs`1T!Hf(O;?>+YiFuji0Z2|KTFSl?n>NDa_2WlbpKzta! z^&CtAzH=w5jJQ()J{__&WWUUI1tVQb;eSd6Tm$0^>pNd^ zyRALv9R51NKy)Z`kPm%**43p+e*p{A*@0;m_rB1}89nv1Lr_x8lwbbiOdRM2SY1IW zz{@@5(bbYrxA7VhMERUUfrVvR(PPRnwW#B7bxo`_p9p$!)4aZ)F*+t(>x^Owr*PE3 zcBZh2F5Tw^AcPpOT%?PK{0@vPxOGu{G2bmW4C!^q;@+tB7L+s$F9gPV(qwi_>$qv+ z!EnJinoLBr$C0#`sK9I#2MlGCrcGQ;P!J zv_v%WI#%n|oXV4`6Wbl3Vzp6C#@0$Pg(*Ed9X>%@Cn0G^OTV{Wi7F~UIuvym)aoga zP|cn(ho@xxOmPMdE}2I^y4*)k9ADA79P7KSR-bFR^&QoZ?Gz1vctUr2qIS`W)OqL4 z=c!N>8!2{ikM`5L0oXP(R_Qrg$@<=SP4Q4G08KnrMhx!KLR&|cvayf778_vBadB16 z;yYjr=w1!-vBsU*YS_q3(`f!s_PT&?--_HlkxUajbHM*Z&K4#D<_3wbaAFAl8Yx)zH0w|w4k3hGS_wx#K*#3OG;<+D+O9K^FiqCK*niK^zX-KDW6?8-4`}}3A zN|RX7lll$@9+|OV6cn~`dtiIn6GWgbL`_P|V%I*m%iQsECU;3Jm+P*@nd?m@ScqG2 zWW@=6J00Ayj}jhzk~S_qx)!Rl~?! zpIag-Fn>&wCo5jh!@r&4QuKvWd+7jhpbGm4Mel_YB5ROrr^lt;>4#m=jwJ)~NGqnV zBb_t()5}SN3ip2qFzYjmbEZG#MrM@F`jkDYdviYCN|-@oXZNy5FE0mG;z;O1&@DtBw*j!&9tD9g%Dy`h&T_sv9z4pYmxrWl7tHMwMbG@8c+Dpg%sR#I9~6E#H3!g?;r zkI_(B+-KReo)>wel=O1H&Ot8zov%YP6jE(vV5QZ6Dafm&m_eIO=#08sjz@LTL4UJX zK|KZ-saC|p*(Q$po<;P?2xhp~)Q@wB3N#l6j&|Jqu;XJ(TX@dYJ(>)0AWS#fNc0DZ z&2PcjYvDvLL~lMqw*p@5w01>V$Kfu^lhNB31RQ)m->qo(;OgDwo;iA?avCqf6kKSaqEo4A~{`>(XBuk$-I<4|ZzE%PM zTBT{-N$pB?c<6&emb3b?-YPW3F)Wvb)1&X1_X(d+->BK+BSQ$|;f?wT-nXGS3pV1> z#{Gl3X+I7+^xlr$S~wUy9(P)tL!3S9PWJ79Te%hAt{y@l=Fuk1?+iXyaA`X**EaLJ zF)zkeZf7U?rVs!46u){iy6)jvTe@h@NWCCQoDhH@B@=2IpsK~m4O=#{ffrdVS6kVH zXK%P^ikp+9nnl@q6I3bmoc!_ja~UVa$1;u__gU+$AQ=bYd!(IUH-@}Q@B{InP(j#HiyUt=AIa( z`sMIF8*G9L-s{%$cKo4aU zvE~fnLM?HEu`t0tR1uaOCCIn$2fFF$vVJ6Vr083=$)KE}Z>T&(w!TpVB^R0~d0%AO zKLsD8=-lH7WAUmPDoaJM>2V&lzPvRwrbTaOl=Ost{MGcEnGowi%^NDM3Eku~1LN+c zff|pUqF)D{+A^XL{StML#5TodU4!Etv<0Lr4=sD`?7}|piUg&#>NESCIi#* z?C|;Jlu|Ixcdm5L{Uv1EW&P5r@JH=?{@1hLYb%V$W<;Gx;5O0%{;P*y+eZz+*98xa z%t5oj9XvR{RJKxG6toimH zik+68N*$_cn(I}->*2Apb05tz;|5A{2s+&&z5|ppGpoI>^;Mr8tm!$&4M^fqqpkz5 zwH!S}&k^^wf+|RR?)b%X%ccfrnyyy6L_c!pce1DRUNZJYY)8N@F9r#dj}s~y10j3o zngp+@A?;{Rrn@EGhDGVX6h!N;()$8tcgS<%v{9dpgHvCZIovH8dUvTWpPc`FbaEtPTO9sDiRD7jM(Jurz+ewT8}I~#aG#-^c_t|kJ}f_i2;E= z9>n!t^WCY2WmrrlA+O=G5B597VeZugZ;w=eX~9(k<>5^c=@U1LsvR2=MpO?R;$b6% zalAdhSn6*k&bG8k_=;=65uWQz26A}e%A$#WTT1O?A@OGs)skJ%n?faDJ;4}2V=xuS zyGF!PgyEF0aU??rN{mS>m>jzg+f&1(Q2~c_FwsqSbyOd${7_h3>yN(L@Jh$ zj$P6~}vVqchPFBEPL5S{%4mpcEzrAFrR4v3WlB+0ns9klN-nUioBoiX9WOJVQE_sX9f zgjbd7U*hO=D^?Fy;ok|{u?zAqDN=uC;>$d_`iR2Y=_mgfc_Q1_L%-#ERiJYJEd{#I zhNC)X$L?<`5p>AM!EBXw?lg*g7QO!(@}JuM$uc-Fe17u02U`t7{0Qun2bI=?;=@Ks z5TFsei(}EPwJu5$V+R<~Zpu4Vz|M>DG|!ZIJA!<8b{UbiWUk!6eM$s3LVTe;y4=l} z+6ifU@liAFBNG{{1rDX70;YfIHx@?thi;E-`RpM;meYe|NG^DHVbX2L;l&Zw*u0GM zwvKlT#psSYwu;-Wgz5l=j0a;k&5THJy#8t!81ZN8)u|{VWPg7(Ab-m~SW$Ooyta^m zwJO?ev2tY*LHpaFLToyxseq_Dk_rrWckSc4$RWd+A7pIDUO+5N0~ddEX|nO~m%iLmhb%ms zH@E){T;TAh*V?8^O)8kVt%88b_zbV=tCLN2b2HnU){O}@G3`{1iHV0MlMMi32J z8QBQwGrdNkn~Uv0>~+G;cA~YoO>_8-MW2_3X9Gyvyteww{k^X?p|?QKIS`cK0%B#EAIfC_!|uT@bcB!#7MLE@pISA zxcY zQh~%gYr)PtGT$X)qWTAy^n6yht)$ObsC6M}J&uhia!?9CD*)b^jWR zvhT{D!z4WDia_y@uTL+^s^MAt=3O#69@)wA%0rgyLMMVn z5+hVU5)jKG)9%qpyVhC$19fFZmKtqUf{8X4b&68}ie4Uvh&G`BU*hPCE66 zQ*lpl>~^F(lP|q0G3u&z^Xm8gCXV-|HGXo_V9Divtdc(3^)9cvtYgP|=jx9rBI*%D zL=Jj#VK@8LNX5f44u=Ene2t<+GcTi-`eOMD9p{Vnr&V`Afa&PpN0BDVp(nA-i_jzecN`V##M)i< zs;)CX50j8v;`Y$JE=Btcao88tMuE7;`Lne+ZZ4k*w7Nlh6o`L$SFG-dHOlVufuWI- zOG5W9Xgv4ufPF7VMWk%F$5KuZ!&_hg86~I_ag#6@QY(_^ov%H2@Y~!jTiBo_wn|J%)brxe6!>ww13Y|EFjx+o3lTR3 zZ-#`Um{%XvdElD-!z%D7_67ff89}5+#p|59VjYS59a-S2koNe8l5S`6!oS8#r#cLk znmwX)p*fIurqFz2mcDt{LX4uX5t8DjYFr#;Ik|gNR`knU?C$Jj8Gg`NSCuHAI>l(T zn&`AH^RivNumg0?Z~rwZsIIv+0@gYilE9$x&15R@Z6I?WbgX6!zhM{K+4gCi;s58B z?;ICVytfg_*0(3hwyC#~D}5H$FV&M(s8H*qZeNLbvU>@%%}pY1?XrS{&1Wu0(5D7? zxZ=^ACz^Bxyr%^gQ~|RGC@>+f;y8;SS!2xQ!(sT7P8k>WqqoY-_O42pRCF3MO$$}R zntm2|@%~!hb=5>9rkKB3%hu?7?k5`tV7tY_vf!;$a?S`QamF=LC$Yx+E5nyG(!8l@ z!ChNeJsNj(iB9Jv3)Re1kf+5AC@*DM0? zitt)}JR-7*jaWfiakmCmw|uXvNJ7HJUbXtWir?I1V)NfHK{x*CvA5hc&~gdrTHin< zY-qmAi263eq}h}7bR(TDngY(r?H8eZsG;olsJRx55h-)>918Rk4%h17h4OMwx|Zb~ zx@%Vzs;RComUxn8GCqaSHS%mqDy*K5G7e$!!tpHfj)*3t@$|iMsmZv=-~aES_FL8UhjW?pDZ=uER0JL4tK?QMx|w32mbO=Bi<50;!vZSQThmf+jfj!PB_f&WoaJUK_`E7Gpm_-w>rCEFh!urE;YJO>#v)LT#8R!uLoxvH^rJ3OfL%3Ew)Rwux9$%6KI-N9{Uq5}9?G zVNxS3WQvvVdfc_DBm;TtfwI1sOhb`PzL#kVOKJBT7VatBK!;=RjIM^q&w2WCwI$aJ z_7y0)7_b>7xvB|OD(Sk0FiQBoh)H4*(bYGj>Y|r6xbi6 zh|bQIDi zClUMHbzBq*=nQb_kd@nhHtsL=N8^|@jgB2@Z;k5r3*Nmp^A+A(+p-S#73GC}q-&mB zx%P0<=g4}TGsv=Q94HRsjlYWn@>3kybLc`iU7c7kp6+g6yKCV`pqH?<_bcgzXC1}8RGJ{g zTXA?e8_7ziw7;zmg1rU<#{3PiW> zJ)cC@_b*`o5`js2l^6$SN{Oz9wuS5)2TEU#{lmn|JY5nRBi?|t?jI9z$Gg?sfj+uF z_2N#9iW6_i3yI3+h@y8ea?&1lh$09K!^ebOzLT=P+9-ke7NXZ&s% z7KZU+lgl@?T?VND-oh6~3DsZ&BO~C@BPxAI{fxaIg@9vk$@pjNeXw>gC)@@LA(`5M54?eEf|VH$ zU_Xs;el3XIE~bHBL+~7ml(J?Ju$5m`R81swQ$!bbHA9=_HafLwy|0Z8@$Qt@ulUdJAv}XHo!f4M?EVrSo!8Bh8I=d zADINIn4DEJT)rsu{KlOksS6x512In^%K}Etj1r%PY_pYQt((uu0U|N;a{p+^Ui!4ix&rrYSk^ zecPJ+`J#|?xxQy$+@qb)P#+&Z*0!|nwjZTjSq`TRzTatl{W0`bjkFeD#Mv(<*x$PS ziLAdgfCbwpK!wPGiis>timDMmR|2|If1&W7K6)~E=?C^NBj`67XJijBSOT<|*zRKt-xdp36 zWb2`wv*|OPZR7UExT;~Nf(Rbi3DJp-jlKfAwZ-@K}$){CP(I;g>up;*tog>1>UupF5MqWiNt#Cikx-SA8optw|=fb1u|Sdv<|F2 z+jqy!LAS95R+f1=><<=i3k>lm(_9#jO5qzoF$ z_&L!1MOK5;IEDy%KJ?#EYZ?#6w^R#$_+r9u5^+)4`{`sJygaLwo)d;tPiaMQ;}ES+ z=cinRbiczKfX^}+BJK3tNkDvvb$BdUU>e0nhLem`LW3X%QxH}%thlN)YJM|7S9IRK zSl?@;_|%;frNO$hue>l`gz$%@=z}*%QGv3OdP@7I2KL*X&wN$bxQ&VsD={B(APCF3 z_tzBrlNMYu)Lo_-Ldj;gh?*a9>}07oY~1xRuBdEhOKIyC*ZWFkT?t<#2cCtYX9y~l?$80^g} z#DptZgS97A>#`kj96=|2L0kSKXIh>?XR;2~um*oOes00gS~(NB)#VmU7n*7(EgAns zuPkz;#y@3yHz(g|)~rW9-*n|iLt(lv-FWKb6p?mq(oit1oW~)CgNIp#GiL*%pPR*_ znxNLGZZi@HOhO!CJECZnkIcDm{a|=!vSyqo?@&rUjhROHF6^>TU}W)%e?LzmKITxZ zQu>HfTrO@=)-Bs$D_8onr=0n#8tHdZukJAbrUgbcTHJ>J>|lua!w$gymW>*e+LJ^o z5e7=|b<#i9FUR_=DDrQ-yipS~fMI9lV|c@WLHIAiTroRJkVx#-A=EyrGh}F*qQ%Ng zsZ9qbUEbO;nOEqH^)j z%zBrlcOn7N;LlQhGRacdbj@*VZN&UTg653YiDQ=p+$p<+#j~;qrtDAD&mwaW zOe5X#djYca{71h@CCwTbiO0K2gv&L_euWph)U*Je`bjbwkb%6Kz17tq)DOzmaenSn zBTM0i|AEy0nI!+&HHI`KqjogjCaU08hSL*u@(9V}f@D^+|PMY=5eMPW3Ye zO5(`WOGip~pOpMopspfe6ZGHJrC$TdAy6stTvp42K>uHN7yiK_2>72-S z1B0m4byVu6?N7}7{U>IQ0(RoIvi1{8;V~0Oy2)^8!ITIyqDS^lX+xVW0S`OH7SK*oFto;+?Z!8qn zr6~N{&A1JtprhcB*HVHI+J0_okr7Pc^iX&tCjiB5}cY9x$8sZcF@*nfV*{Y zBfz>)BLf}s=~ZxZiwrX8Uo=KJ?}JMf{w&n_dy$UZUbys9&>d8hD7eW+lss6$di-tX zuiXcMp1^NaI(&gmiQcd?SbW^i?LM^lz^YSj6CsWlT&QlXpOvej+k-%E=!Rx3RU+kTk+Dh=2#q?^rSeydwD@)=V#vt5_@RUH4CHiYwvb`t+0j<{b4 z@81Qp9Dl8ex@Nt7_2PDz`?syY7H%2jSaVaKc@j&+!pl8utCam~?k+2)qztL)Lg-K<{**;T&MDn`n9N@b0TKg#7#nzfB z=bDK%6grQL%Y%TKgYYB&22A|#+|`KY2exG3rD!xE`eI46et)Usvb_Uz+A# zBk$7jcp`lKkC!@BSCT=T%QB|nIKuUFd>Q0#n!q~ zN{si$DJWPc7~zyLdHP2c#=ESXEJUP6=#+YE3uCf*iM|M3$gPuxv8*8jNDyiTx;H0od=$KVsw z<~|$uKZ3Rw!ToGPH8zoCdb0jFg6Kv(o8 zDcW}=uYLZk0iRdOu2ok4V0E!ZERK}12Kz>2<{zhIom!fis@%yB9Nl!_v>kZ;Omgl-j%?DxY=#^MS{Gab ztku>#;xEuLC(){Ji`AEsbcp9k>y5k0nJP&YDoOXcq_tkZUwITEATcd5U>qHmpw)Um zrPg9W(Zq1Y(5Wx_zd8f`^*wM#60HNvW+07$Tt?7i&sxRYVH7Fjq5a6h)9TS3>dT`s zX?qG2b?mPO)5uKyLKY;8jPZ)dTf`8(*;(`QVqPbx)YJRyOnh$25B`V*3g2&Wfa7h@ zgPs`vg2;CT?tYZOe5~YLIA|;0eButzmOVOu9>PaDnepaU_7#`32F8x|8S1l)L0PG1tZW<}l9+ zN((e62V3OBw$*|P@?HFMB9P+w)-Lh51%)x4T|opjopS^i0)DC1wj@9$BV8i%`%gSCHidl^ndg*?DzKCuOQp6;nw6ETm?oCPaYs z)p5hqQM)?eJ~3gQi%zDO{xSbUa@{h>aaazEu+@iD{u zX7<=-bd(^habK;`^|H+9-ty%+3#XB*$!>1)@;TC0c6#yM*I$&dzvoWS@)*Ii4fG&k zna*bQOoJO1UTj?R6v4g2l|@z^FUe+Fux|Eje8;DbX6#M$IqxoVj$S()8a-Mb zbTjMbF6X%!w&UOyQUUJFWIxiINg4fdO9b=oSar)1GHX8p-bJ%WmeJW%&isYvbprRuV#3sSUtII_@$JN_TYC_ z#VEt8`sLx>Hj1Q{JCoQ@<)G7P?Jh4j8C{dF_8hrqUt3ejbfo;^J!XYUfh8K-k4k*8 z6iFA~vjO@XFb2y>6)|XZ1Kj#2@Og zy5irdYsziqH)dzbTT!_kVF^P}!{M1)P2*o;v7XeFS zsIjlW%=R1oUUG5FEY#o|bwTo9_&&m_#q-k{t z-!ERe)eTmIa8Q9eP6UaVym>RR3HA^9y-DUuP1Uxf1j`_2?s~4w3J_Lt2oY zx#p1MbqU*Hn3Jwciu)HrTkcMN>T(uA6?9#Z|m?(08&erpyrih+flQMaEKH1 z2r(|QR>6L)JQt65b`NPQ<6Gu5eU6QD4~;3qY2DCICt*z>3sM5=H5F$ z@1Ok0lXK3_UTg2O)_UJ}?W8SaNT#wH0XH*>Ka9m9>6TzJ(Z*35YRr%Xy^pA9@A3r5 zLNaxhF?W&pBalb=lu1wq<7SBMF=Zi+bzy=J8`nnnY3q}&%ES~SVNWX=q-=W3XN6z3 zx*C3YPH{X+aeVw+FEr9-vtZS3m1_ZZ0W!8ZJN>K-MUrB4Ih3rcc1rTfJbg-HV0_+0 zh@;l99Z`{nhjT&YqHPY!b|z0s*xjw{^>(MXaphV>vZEmUjnb3qT-jym5n8yIc=pdB z4$4<3Uz4mMGTW00o|we(o+<1bIy%2R@RrJzkKMoC5Rh8D86Su} zDUDXSV$MX;T=A_L0|*VEw>VIXxfrR z6sEW&uV%X;mA^*QsR73%kD=kUd)kd?xGH=;WCC@)D#z=Kf+jJ~6VQ4f?%=7>1={&{ zQLhHQ=xg(&c=I00=QhtXR%(iTO++SK7f?>yxj|qWqhqZBIQas%t0-2biPd_tyYdOk z`*3H_P3<$^f?BYRO&K;zf=P+m5wIV)-~DL2@zIBPAAlNu?9(qmt|6iDuURRs;Q;NV z_n**C1RB2QK0h(h|2N-l!=Ebjuh`PbifqV%Wvo+UNM$=t*Uc)heiS1?=A4wyGtf^4 zDz1I8ew)qFg;FrKrG)M$v!qn+p3hC}MzV9HuQVG$lt49neQ?P(Uz87DcHJ8&-cN}- zgKcq3Ko9`%?1~gMp6}!V!cj`>Zxk^=ccJHq(CIx9m!l|Pgls*lBGn1w93ZQdS{~GB z4XjULHb!&^U*BGCm7M1bQ(rkDw?#u{6+XPxHCsb|Vepkh3O%0!|KNDwL_+WMkfuh; zE&>47c5dqhnFzN|ZiN-TvYN1TuHp3x{t)$qGs!1xY&=AI zt2w?V#vT{jr}nVy7{uKQVTf5r1*&qE!K<<{65d@)J0D7YH@DflA2J8O%X}wKGH*u{ z)4h7K=3-}_+w~i)l=H4_FC$UvId7nM8)TfJ~ucE>2lAqbxCzuK#XMK9eT!ASm z77jKU#4uWp-@UUN=(4rv8Q}J*_me}inO#*jId$a8r!*qq|C2=EauD9?T9mWX|ot3=5?xp_2!NfoWugqUVxX~VGr=IcQh67 z&w+^!Br&p5KiL!%dG75nb#16L0RGPRwz1zDY9{y*qqi-IL}&0WrBzTaASD@>1Z3z>j5o* zDpR&WPEaSJz9-PwuU@kzBunB-TN6ut%^*;1UF#66tuZQM2+4RkAJd?p*th+WZ3)JM zZX8rr%b#+wbY1!u+g{}{v5dKqfU3$%I1IL0fj=Rt@b5k62sc zmds4?QrlquUnR-f!DP&CColeppH}Fv{r1)%pA?Rc=O#@{;oDI&ApU2 z<3fwbK+5sEw%->bS?cRUT8yztlZ+0&@jg=PA_ zaq`(ewl%$;mN&6)c#E^*Q;LB|suGnq1|lXawt5~A5~rif^HAP4r<9n6ih+FZ|c z+uL(alQ%P%>XK1 z%MHXIVA%wJ?cyqr)2O?0XP_eZ>DAI?G|+X5AioT(y_LqY=Z&%7kd7@gVE1-3FCw}@ z{ZX|NQz$t1Eq?M%l67f8?R5o(o|+Ac+91~Lw&JT3b-OO)@guLxIa*Q)g>FxmYzud5 z)++&}U=Bgyutf@>ACzd40O0Mz>(}t%BRSCU`)O_j*%HnI{|G4DIspzq{Pf|~No{f< zykBiKid66;<+@a9Ycavm;e>}~&Kl%9PXCYG8RF4hs9n@9un{2 z?9qn02)JX zey_-ozo_4CA-N0b-+W93(3;(}-|j51Z4Dwj)uOTl35`wh`JJ;xV)|BX{`+BGUnRvK zJ|91L<|pkZ!h*QSnQg!I^ybIlI+L0EPPVAMcZ8GE+BM}vP_>!#4-HrHSH2|;73NyI zID7hfpL%Z9%4768PR@|l=b`)51&a0aDN3M<_&qkZR`O|Ls5ilQ)-4Ddy)IW3O&XcE zO21UfCKP8LrIFqY4$TqIjY)ElgJZR0+@SQcDT83y+Fie76T}*3<)TF)ga6mcORqZf zJpxQ8Ef;-!-wyQIfbm7xFF6Db!=~y+{6bSib)R^&pwe#EtGkIYY(XPs0|c8y4PO@h zD>7pG1e?fW8(ru7#moD{PjFE7VO_!KxRWNQHC={r?jaJ&+}O=$X_Dcy`l8%rv3O$X zqLqr?&Hk4*&TDdosRgz5CSUKfk5`*Iw#S*(Y#4i?_`FFTU zYP2px7*#-Z7Flm-&mn4GPNwQBmd_ znVrXZr{^}?Y$M@RO-z=MCNTv4%JA4Dyk_yZWTFSvCerWtsc7}R;xU?2ED9V zu19M$4J{0u!*@qkqEglpKMN+=IX*bkc1O|f;~EvuSaiy@sa2`gtP7Y1>-!QLh(Pw& z&TMT%2ytOPhp1~jk-O$*#v5W^R9Rd=Y|3}sUqK4mQVu!Gdv{&$`wEAx?Tn7Cd=U|l zpgSn>hVSVICok>qhcS z`*O3ai%(Hf)wapXHE0X-EpcqsdHvpvh_@ z)BlJjuNkxs@GT<-C{k-ZCwu0eD;TPH>_$0EI^79J0Fu)3x-d7bt++0Rv*Q zJ}K>}^aAs?1>!m*)DeV5Fs*bKYl|+!1crC#m}|~`sE_{A#d)Px`fD=_*~*_`SZKyz z6x{)*!UEMK8f+%(RCTyr_Nvd=Vy1X<4Z!C3?OCMSVoFvj6fNT~rFZ8AqRfA5J7Xu*gBQ8! zj!Xo~nW4&3i0v$v7+_KHmLU90N@7z&ctFC&%Bm!}$6e4vw7w!tm-89zxgZmmG*pBb zNpPGEL(cjVq{74{7qY}kELTZfvl#7X8{9zZWQ#f$#h`w-&SPf&Q5gV${xpSuwqJJN zy*wdzepGh>f+^_1Ge)b{$T?}mr9FMg*{Zqi?dT_u1wq1~RRiT6Y{T@dd9yn!#JxpT z1aBVWJxOnkFKv<&PE!%PnTDCy4HRo=4d~&2aqP~q*i?|D#S9MU6#lO*Q8=oienZ!? zlMRn-QDcSDvq!&L1(xgvde)l%7nc;>+#dD<-_sptd;)%pvJO`iE}BhnWGAN0S0P3p z?7xSPrcH-Kwb@hWVu6TLD#e&2#vL$D|L&utV0m4{MqUdcy#&`@ZR_h;$;nqw zC@OdJk;Mvc4y9auAz5*(;F*%S6+r1Q0F(@o`GArE2BaQQzasxcAIuAndOXN$cHSsv z$1wt^;jusgCUv$W;1<)>zi(S%=a2L-*L$u3dKfUUX#&TlSS!b`d3g4`Q`SD@Ob4V9 zEGkTg2Y_&b4IuF-0D6z}{-6Vr`=MxJn;HroDhu3^ZTx&F7Hka__Q0?1D?Q9gXwPr^ zm2*O;L;WS*VwO@jVzL@ckqP30_1o~^rJ{C9=tx=w#xB=Werfu1~egbObRK@i4AIF4y4Al|wH5d<`J zTlPX`c68ZqPTu{*DkiBTNVlMDO}cr_7jN7SJCE-O#$GF4b`2<)7|B$X@y)Smcf(NE z$0D6HKgpZa@`cH0zN%+bLKI2B|H8bP_5`c(aJQjp*Ok?=C1O_i9ZR2J@S7FOsXFJg zji?V>3SF|AJPR;0czwgg=5;e=CX$X*lPtju@v3Kb`ZV#u++9?{wr1_{n$ZbwogBlS z`K0(%3qGcbtFbj1##L8NPih90g$)Wyk64o%GxP>rSjK7(k;;ssyAg zkxfTZ78xKE*&K%xO#WodBH+xZ1eM^dz)=Ev-Hs$o=EIw>;pQfNui@Df^H+wHJoUl8 z$I2l{j~GPyRHONiNqHe0jQj^iq|}cwEcwQb8=VEA;Py$R-S5pjv}wNOeltn>;PZby z1L%+IdGF_wkM+u~N&xCSMi2XbIjMDvT%>WbcEY2=wwPPis<*eY6jf37WaeI2_k3Wb z`RD-vW6@H>rq9Fr`X9n^DF-skHqk^_mE&0D-V>!P$#=dUj_KWaDfKiJO)k=%Vc?%xROdnz_IpW16{S1>G?RyJ4ET%tC3h#)~2aC_Sz#iayri7LbFHKEww=$@$GP< zW)YO7cQ*ieYC=DtC}%YpKYlj?Ma#_J#w-%SRNMJrKCVs`wG=Y9s+$p~tPTB!BG9rOb5qO>MlIgB>lKV(URADvIf5g4axI=v0_$7uD6b!_8` zg7v{p;A@_F>$_SKKvA1(c0+F&EFE5^y&vKqW|0X$7?I%9)DpZ%-^JsVrQDBFqavFS zMcnOX4A>w!+Qa}mZ?I}!!ALv*4g2!A6KN8dnCI$~8FtmK(+yPk#m?CST(sMxjZ#UV z<}gve(2R8BPnr$mk9?|n_i8~#Mq;;jk|>vp3&5*BEQW-41n9fJ|BZrEl^3c}j^E3} zW^(IQ4pe2{>eq?U_jRdJz1I1JNATHdzE+cJ@Lu{|fq9>Y?yehn&b z$X8_~Q*&&UXzz#0&Q}I4(&^|5Ua(FPb`nd>2-=Zg{pwxE+m~mzs23<>OGJ}|fV_h- z8($WoEQQsxrESmM7+Oyrqb)m45q5cg!uPd(2VOquvF89Iy~MRPB=?~pRI)L>ISu!0 z4JxG4QCM`>)i9UhE-E*9*%cgSCz_o4vX)!^)tRn)fgC2g(N;z4{NKhpcB{rmUqNOX zErzbS^BJ*Ujoo@{q~0HHNXtzc)zBA3lF7ikd|mrGW;jM5W2Ld;Zm zkJ0cZ6`5T9ZwPh_|Fgtb>PH$i8AyB$z9+sBKUDc@L+@Cj+{vdc zkK}~!X-Po5-{&@n#_KvCDUOzKWIiSM?@jqM02t$_v!AM;FGpp#J?sgv*nawb-%-b> zb)c52`OXYbbO`|#WwFgc;zO2yqxikyA8r5l&VDAIU9|z1eY9~P@;PLc`vLnp5H}r9 zMjk1^ei)P7H$~u{w~+tY0gyRU?Wl{Ow(`61{~yBt&%)pB3yZ!{O>|ICV(2UoxSxcC z&ruh?S*nkLX$}@G4Sf>QP(PPM5tkVo7(`McE2p>AYlbFL+gFG3F|;PIUHwQdnn-C$ zrt_SaTCnu7ye@xj--%;M)XLpQj_qu`2VX=dCO4PzSaQ7j|53y6hpvI Date: Thu, 5 Jul 2012 13:25:11 +0800 Subject: [PATCH 36/55] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF3?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=80=9F=E7=94=A8=E5=B9=B6=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E5=8E=9F=E5=9E=8B=20=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/chapter6.markdown b/chapter6.markdown index 2753b17..13b2867 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -234,3 +234,49 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 这种模式的一个好处是获得了父对象自己成员的拷贝,不存在子对象意外改写父对象属性的风险。 那么,在上一个例子中,怎样使一个子对象也能够继承原型属性呢?怎样能使kid可以访问到say()方法呢?下一种继承模式解决了这个问题。 + + +## 类式继承3——借用并设置原型 + +综合以上两种模式,首先借用父对象的构造函数,然后将子对象的原型设置为父对象的一个新实例: + + function Child(a, c, b, d) { + Parent.apply(this, arguments); + } + Child.prototype = new Parent(); + +这样做的好处是子对象获得了父对象自己的成员,也获得了父对象中可复用的(在原型中实现的)方法。子对象也可以传递任何参数给父构造函数。这种行为可能是最接近Java的,子对象继承了父对象的所有东西,同时可以安全地修改自己的属性而不用担心修改到父对象。 + +一个弊端是父构造函数被调用了两次,所以不是很高效。最后,(父对象)自己的属性(比如这个例子中的name)也被继承了两次。 + +我们来看一下代码并做一些测试: + + //父构造函数 + function Parent(name) { + this.name = name || 'Adam'; + } + + //在原型上添加方法 + Parent.prototype.say = function () { + return this.name; + }; + + //子构造函数 + function Child(name) { + Parent.apply(this, arguments); + } + Child.prototype = new Parent(); + + var kid = new Child("Patrick"); + kid.name; // "Patrick" + kid.say(); // "Patrick" + delete kid.name; + kid.say(); // "Adam" + +跟前一种模式不一样,现在say()方法被正确地继承了。可以看到name也被继承了两次,在删除掉自己的拷贝后,在原型链上的另一个就被暴露出来了。 + +图6-6展示了这些对象之间的关系。这些关系有点像图6-3中展示的,但是获得这种关系的方法是不一样的。 + +![图6-6 除了继承“自己的属性”外,原型链也被保留了](./Figure/chapter6/6-6.jpg) + +图6-6 除了继承“自己的属性”外,原型链也被保留了 From 1d95c0eae5385ea4cd940a716d4aabebf5b10218 Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 5 Jul 2012 13:29:25 +0800 Subject: [PATCH 37/55] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E6=A0=87=E9=A2=98=E5=92=8C=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.markdown | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.markdown b/README.markdown index 8a6b8b0..84d5942 100644 --- a/README.markdown +++ b/README.markdown @@ -144,14 +144,14 @@ - [类式继承 vs 现代继承模式](javascript.patterns/blob/master/chapter6.markdown#a) - [类式继承的期望结果](javascript.patterns/blob/master/chapter6.markdown#a) -- 经典模式 1 ——默认模式 - - 使用原型链 - - 模式 1 的缺陷 -- 经典模式 2 ——借用构造器 - - 原型连 - - 通过借用构造函数实现多重继承 - - 借用构造器模式的利弊 -- 经典模式 3 ——借用并设置原型 +- [类式继承 1 ——默认模式](javascript.patterns/blob/master/chapter6.markdown#a) + - [跟踪原型链](javascript.patterns/blob/master/chapter6.markdown#a) + - [这种模式的缺点](javascript.patterns/blob/master/chapter6.markdown#a) +- [类式继承 2 ——借用构造函数](javascript.patterns/blob/master/chapter6.markdown#a) + - [原型链](javascript.patterns/blob/master/chapter6.markdown#a) + - [通过借用构造函数实现多继承](javascript.patterns/blob/master/chapter6.markdown#a) + - [借用构造器模式的利与弊](javascript.patterns/blob/master/chapter6.markdown#a) +- [类式继承 3 ——借用并设置原型](javascript.patterns/blob/master/chapter6.markdown#a) - 经典模式 4 ——共享原型 - 经典模式 5 —— 临时构造器 - 存储父类 From eb5e18bb4422bbf2fd15f10f7ce1073ea6ea8163 Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 10 Jul 2012 13:03:05 +0800 Subject: [PATCH 38/55] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF4?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=85=B1=E4=BA=AB=E5=8E=9F=E5=9E=8B=20?= =?UTF-8?q?=E6=8F=92=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Figure/chapter6/6-7.jpg | Bin 0 -> 25223 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Figure/chapter6/6-7.jpg diff --git a/Figure/chapter6/6-7.jpg b/Figure/chapter6/6-7.jpg new file mode 100644 index 0000000000000000000000000000000000000000..461949ddb5709834b5ae30a56e8c8f5e59830bba GIT binary patch literal 25223 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 literal 0 HcmV?d00001 From 781989e178c5b2fd2233056579f5c0f49e437d89 Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 10 Jul 2012 13:03:38 +0800 Subject: [PATCH 39/55] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF4?= =?UTF-8?q?=E2=80=94=E2=80=94=E5=85=B1=E4=BA=AB=E5=8E=9F=E5=9E=8B=20?= =?UTF-8?q?=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/chapter6.markdown b/chapter6.markdown index 13b2867..69de815 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -280,3 +280,23 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 ![图6-6 除了继承“自己的属性”外,原型链也被保留了](./Figure/chapter6/6-6.jpg) 图6-6 除了继承“自己的属性”外,原型链也被保留了 + + +## 类式继承4——共享原型 + +不像前一种类式继承模式需要调用两次父构造函数,下面这种模式根本不会涉及到调用父构造函数的问题。 + +一般的经验是将可复用的成员放入原型中而不是this。从继承的角度来看,则是任何应该被继承的成员都应该放入原型中。这样你只需要设定子对象的原型和父对象的原型一样即可: + + function inherit(C, P) { + C.prototype = P.prototype; + } + +这种模式的原型链很短并且查找很快,因为所有的对象实际上共享着同一个原型。但是这样也有弊端,那就是如果子对象或者在继承关系中的某个地方的任何一个子对象修改这个原型,将影响所有的继承关系中的父对象。(译注:这里应该是指会影响到所有从这个原型中继承的对象。) + +如图6-7,子对象和父对象共享同一个原型,都可以访问say()方法。但是,子对象不继承name属性。 + +![图6-7 (父子对象)共享原型时的关系](./Figure/chapter6/6-7.jpg) + +图6-7 (父子对象)共享原型时的关系 + From 31367c538a123c5afb074e2e6c5a03878d07c549 Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 10 Jul 2012 13:20:00 +0800 Subject: [PATCH 40/55] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF5?= =?UTF-8?q?=E2=80=94=E2=80=94=E4=B8=B4=E6=97=B6=E6=9E=84=E9=80=A0=E5=87=BD?= =?UTF-8?q?=E6=95=B0=20=E6=8F=92=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Figure/chapter6/6-8.jpg | Bin 0 -> 33152 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Figure/chapter6/6-8.jpg diff --git a/Figure/chapter6/6-8.jpg b/Figure/chapter6/6-8.jpg new file mode 100644 index 0000000000000000000000000000000000000000..848cf57f309b7a28eeb4b6c3c62d59e29eab741a GIT binary patch literal 33152 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 Date: Tue, 10 Jul 2012 13:20:26 +0800 Subject: [PATCH 41/55] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF5?= =?UTF-8?q?=E2=80=94=E2=80=94=E4=B8=B4=E6=97=B6=E6=9E=84=E9=80=A0=E5=87=BD?= =?UTF-8?q?=E6=95=B0=20=E7=AC=AC=E4=B8=80=E5=B0=8F=E8=8A=82=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/chapter6.markdown b/chapter6.markdown index 69de815..f776e6c 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -300,3 +300,29 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 图6-7 (父子对象)共享原型时的关系 + +## 类式继承5——临时构造函数 + +下一种模式通过打断父对象和子对象原型的直接链接解决了共享原型时的问题,同时还从原型链中获得其它的好处。 + +下面是这种模式的一种实现方式,F()函数是一个空函数,它充当了子对象和父对象的代理。F()的prototype属性指向父对象的原型。子对象的原型是一这个空函数的一个实例: + + function inherit(C, P) { + var F = function () {}; + F.prototype = P.prototype; + C.prototype = new F(); + } + +这种模式有一种和默认模式(类式继承1)明显不一样的行为,因为在这里子对象只继承原型中的属性(图6-8)。 + +![图6-8 使用临时(代理)构造函数F()实现类式继承](./Figure/chapter6/6-8.jpg) + +图6-8 使用临时(代理)构造函数F()实现类式继承 + +这种模式通常情况下都是一种很棒的选择,因为原型本来就是存放复用成员的地方。在这种模式中,父构造函数添加到this中的任何成员都不会被继承。 + +我们来创建一个子对象并且检查一下它的行为: + + var kid = new Child(); + +如果你访问kid.name将得到undefined。在这个例子中,name是父对象自己的属性,而在继承的过程中我们并没有调用new Parent(),所以这个属性并没有被创建。当访问kid.say()时,它在3号对象中不可用,所以在原型链中查找,4号对象也没有,但是1号对象有,它在内在中的位置会被所有从Parent()创建的构造函数和子对象所共享。 From e1f5f5936ced5667cd388460afd314effa7e4399 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 18 Jul 2012 13:17:57 +0800 Subject: [PATCH 42/55] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF5?= =?UTF-8?q?=E2=80=94=E2=80=94=E4=B8=B4=E6=97=B6=E6=9E=84=E9=80=A0=E5=87=BD?= =?UTF-8?q?=E6=95=B0=20=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91=E5=AE=8C?= =?UTF-8?q?=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 59 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/chapter6.markdown b/chapter6.markdown index f776e6c..07472e9 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -326,3 +326,62 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 var kid = new Child(); 如果你访问kid.name将得到undefined。在这个例子中,name是父对象自己的属性,而在继承的过程中我们并没有调用new Parent(),所以这个属性并没有被创建。当访问kid.say()时,它在3号对象中不可用,所以在原型链中查找,4号对象也没有,但是1号对象有,它在内在中的位置会被所有从Parent()创建的构造函数和子对象所共享。 + + +### 存储父类(Superclass) + +在上一种模式的基础上,还可以添加一个指向原始父对象的引用。这很像其它语言中访问超类(superclass)的情况,有时候很方便。 + +我们将这个属性命名为“uber”,因为“super”是一个保留字,而“superclass”则可能误导别人认为JavaScript拥有类。下面是这种类式继承模式的一个改进版实现: + + function inherit(C, P) { + var F = function () {}; + F.prototype = P.prototype; + C.prototype = new F(); + C.uber = P.prototype; + } + + +### 重置构造函数引用 + +这个近乎完美的模式上还需要做的最后一件事情就是重置构造函数(constructor)的指向,以便未来在某个时刻能被正确地使用。 + +如果不重置构造函数的指向,那所有的子对象都会认为Parent()是它们的构造函数,而这个结果完全没有用。使用前面的inherit()的实现,你可以观察到这种行为: + + // parent, child, inheritance + function Parent() {} + function Child() {} + inherit(Child, Parent); + + // testing the waters + var kid = new Child(); + kid.constructor.name; // "Parent" + kid.constructor === Parent; // true + +constructor属性很少用,但是在运行时检查对象很方便。你可以重新将它指向期望的构造函数而不影响功能,因为这个属性更多是“信息性”的。(译注:即它更多的时候是在提供信息而不是参与到函数功能中。) + +最终,这种类式继承的Holy Grail版本看起来是这样的: + + function inherit(C, P) { + var F = function () {}; + F.prototype = P.prototype; + C.prototype = new F(); + C.uber = P.prototype; + C.prototype.constructor = C; + } + +类似这样的函数也存在于YUI库(也许还有其它库)中,它将类式继承的方法带给了没有类的语言。如果你决定使用类式继承,那么这是最好的方法。 + +> “代理函数”或者“代理构造函数”也是指这种模式,因为临时构造函数是被用作获取父构造函数原型的代理。 + +一种常见的对Holy Grail模式的优化是避免每次需要继承的时候都创建一个临时(代理)构造函数。事实上创建一次就足够了,以后只需要修改它的原型即可。你可以用一个立即执行的函数来将代理函数存储到闭包中: + + var inherit = (function () { + var F = function () {}; + return function (C, P) { + F.prototype = P.prototype; + C.prototype = new F(); + C.uber = P.prototype; + C.prototype.constructor = C; + } + }()); \ No newline at end of file From b50f58e7274a0bab09b7e7dfec0867a3372d7850 Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 24 Jul 2012 13:28:12 +0800 Subject: [PATCH 43/55] =?UTF-8?q?Klass=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E4=B8=80=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/chapter6.markdown b/chapter6.markdown index 07472e9..042fbd7 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -384,4 +384,47 @@ constructor属性很少用,但是在运行时检查对象很方便。你可以 C.uber = P.prototype; C.prototype.constructor = C; } - }()); \ No newline at end of file + }()); + + +## Klass + +有很多JavaScript类库模拟了类,创造了新的语法糖。具体的实现方式可能会不一样,但是基本上都有一些共性,包括: + +- 有一个约定好名字的方法,如initialize、_init或者其它相似的名字,会被自动调用,来充当类的构造函数。 +- 类可以从其它类继承 +- 在子类中可以访问到父类(superclass) + +> 我们在这里做一下变化,在本章的这部分自由地使用“class”单词,因为主题就是模拟类。 + +为避免讨论太多细节,我们来看一下JavaScript中一种模拟类的实现。首先,这种解决方案从客户的角度来看将如何被使用? + + var Man = klass(null, { + __construct: function (what) { + console.log("Man's constructor"); + this.name = what; + }, + getName: function () { + return this.name; + } + }); + +这种语法糖的形式是一个名为klass()的函数。在一些实现方式中,它可能是Klass()构造函数或者是增强的Object.prototype,但是在这个例子中,我们让它只是一个简单的函数。 + +这个函数接受两个参数:一个被继承的类和通过对象字面量提供的新类的实现。受PHP的影响,我们约定类的构造函数必须是一个名为\_\_construct的方法。在前面的代码片段中,建立了一个名为Man的新类,并且它不继承任何类(意味着继承自Object)。Man类有一个在\_\_construct建立的自己的属性name和一个方法getName()。这个类是一个构造函数,所以下面的代码将正常工作(并且看起来像类实例化的过程): + + var first = new Man('Adam'); // logs "Man's constructor" + first.getName(); // "Adam" + +现在我们来扩展这个类,创建一个SuperMan类: + + var SuperMan = klass(Man, { + __construct: function (what) { + console.log("SuperMan's constructor"); + }, + getName: function () { + var name = SuperMan.uber.getName.call(this); + return "I am " + name; + } + }); + From f866b8c133cc91e8ec53032977d70900969f1310 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 1 Aug 2012 13:17:31 +0800 Subject: [PATCH 44/55] =?UTF-8?q?Klass=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/chapter6.markdown b/chapter6.markdown index 042fbd7..bcd0644 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -428,3 +428,60 @@ constructor属性很少用,但是在运行时检查对象很方便。你可以 } }); +这里,klass()的第一个参数是将被继承的Man类。值得注意的是,在getName()中,父类的getName()方法首先通过SuperMan类的uber静态属性被调用。我们来测试一下: + + var clark = new SuperMan('Clark Kent'); + clark.getName(); // "I am Clark Kent" + +第一行在console中记录了“Man's constructor”,然后是“Superman's constructor”。在一些语言中,父类的构造函数在子类构造函数被调用的时候会自动执行,这个特性也可以模拟。 + +用instanceof运算符测试返回希望的结果: + + clark instanceof Man; // true + clark instanceof SuperMan; // true + +最后,我们来看一下klass()函数是怎样实现的: + + var klass = function (Parent, props) { + + var Child, F, i; + + // 1. + // new constructor + Child = function () { + if (Child.uber && Child.uber.hasOwnProperty("__construct")) { + Child.uber.__construct.apply(this, arguments); + } + if (Child.prototype.hasOwnProperty("__construct")) { + Child.prototype.__construct.apply(this, arguments); + } + }; + + // 2. + // inherit + Parent = Parent || Object; + F = function () {}; + F.prototype = Parent.prototype; + Child.prototype = new F(); + Child.uber = Parent.prototype; + Child.prototype.constructor = Child; + + // 3. + // add implementation methods + for (i in props) { + if (props.hasOwnProperty(i)) { + Child.prototype[i] = props[i]; + } + } + + // return the "class" + return Child; + }; + +这个klass()实现有三个明显的部分: + +1. 创建Child()构造函数,这也是最后返回的将被作为类使用的函数。在这个函数里面,如果__construct方法存在的话将被调用。同样是在父类的__construct(如果存在)被调用前使用静态的uber属性。也可能存在uber没有定义的情况——比如从Object继承,因为它是在Man类中被定义的。 +2. 第二部分主要完成继承。只是简单地使用前面章节讨论过的Holy Grail类式继承模式。只有一个东西是新的:如果Parent没有传值的话,设定Parent为Object。 +3. 最后一部分是类真正定义的地方,循环需要实现的方法(如例子中的__constructt和getName),并将它们添加到Child的原型中。 + +什么时候使用这种模式?其实,最好是能避免则避免,因为它带来了在这门语言中不存在的完整的类的概念,会让人疑惑。使用它需要学习新的语法和新的规则。也就是说,如果你或者你的团队对类感到习惯并且同时对原型感到不习惯,这种模式可能是一个可以探索的方向。这种模式允许你完全忘掉原型,好处就是你可以将语法变种得像其它你所喜欢的语言一样。 From 2346813ad48098ea4e1f9e71b618d27cbf122470 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 1 Aug 2012 13:19:09 +0800 Subject: [PATCH 45/55] =?UTF-8?q?Klass=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chapter6.markdown b/chapter6.markdown index bcd0644..bc4073a 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -480,8 +480,8 @@ constructor属性很少用,但是在运行时检查对象很方便。你可以 这个klass()实现有三个明显的部分: -1. 创建Child()构造函数,这也是最后返回的将被作为类使用的函数。在这个函数里面,如果__construct方法存在的话将被调用。同样是在父类的__construct(如果存在)被调用前使用静态的uber属性。也可能存在uber没有定义的情况——比如从Object继承,因为它是在Man类中被定义的。 +1. 创建Child()构造函数,这也是最后返回的将被作为类使用的函数。在这个函数里面,如果__construct方法存在的话将被调用。同样是在父类的\_\_construct(如果存在)被调用前使用静态的uber属性。也可能存在uber没有定义的情况——比如从Object继承,因为它是在Man类中被定义的。 2. 第二部分主要完成继承。只是简单地使用前面章节讨论过的Holy Grail类式继承模式。只有一个东西是新的:如果Parent没有传值的话,设定Parent为Object。 -3. 最后一部分是类真正定义的地方,循环需要实现的方法(如例子中的__constructt和getName),并将它们添加到Child的原型中。 +3. 最后一部分是类真正定义的地方,循环需要实现的方法(如例子中的\_\_constructt和getName),并将它们添加到Child的原型中。 什么时候使用这种模式?其实,最好是能避免则避免,因为它带来了在这门语言中不存在的完整的类的概念,会让人疑惑。使用它需要学习新的语法和新的规则。也就是说,如果你或者你的团队对类感到习惯并且同时对原型感到不习惯,这种模式可能是一个可以探索的方向。这种模式允许你完全忘掉原型,好处就是你可以将语法变种得像其它你所喜欢的语言一样。 From ac510e23bed1f9b099bf200d0a3470d40963ac81 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 1 Aug 2012 13:20:09 +0800 Subject: [PATCH 46/55] =?UTF-8?q?Klass=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter6.markdown b/chapter6.markdown index bc4073a..2ecbf37 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -480,7 +480,7 @@ constructor属性很少用,但是在运行时检查对象很方便。你可以 这个klass()实现有三个明显的部分: -1. 创建Child()构造函数,这也是最后返回的将被作为类使用的函数。在这个函数里面,如果__construct方法存在的话将被调用。同样是在父类的\_\_construct(如果存在)被调用前使用静态的uber属性。也可能存在uber没有定义的情况——比如从Object继承,因为它是在Man类中被定义的。 +1. 创建Child()构造函数,这也是最后返回的将被作为类使用的函数。在这个函数里面,如果\_\_construct方法存在的话将被调用。同样是在父类的\_\_construct(如果存在)被调用前使用静态的uber属性。也可能存在uber没有定义的情况——比如从Object继承,因为它是在Man类中被定义的。 2. 第二部分主要完成继承。只是简单地使用前面章节讨论过的Holy Grail类式继承模式。只有一个东西是新的:如果Parent没有传值的话,设定Parent为Object。 3. 最后一部分是类真正定义的地方,循环需要实现的方法(如例子中的\_\_constructt和getName),并将它们添加到Child的原型中。 From 9929c9cebce6bdd31ca01a125bfaf2d7d5f6816f Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 6 Aug 2012 13:24:39 +0800 Subject: [PATCH 47/55] =?UTF-8?q?=E5=8E=9F=E5=9E=8B=E7=BB=A7=E6=89=BF?= =?UTF-8?q?=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 90 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/chapter6.markdown b/chapter6.markdown index 2ecbf37..af28415 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -485,3 +485,93 @@ constructor属性很少用,但是在运行时检查对象很方便。你可以 3. 最后一部分是类真正定义的地方,循环需要实现的方法(如例子中的\_\_constructt和getName),并将它们添加到Child的原型中。 什么时候使用这种模式?其实,最好是能避免则避免,因为它带来了在这门语言中不存在的完整的类的概念,会让人疑惑。使用它需要学习新的语法和新的规则。也就是说,如果你或者你的团队对类感到习惯并且同时对原型感到不习惯,这种模式可能是一个可以探索的方向。这种模式允许你完全忘掉原型,好处就是你可以将语法变种得像其它你所喜欢的语言一样。 + + +## 原型继承 + +现在,让我们从一个叫作“原型继承”的模式来讨论没有类的现代继承模式。在这种模式中,没有任何类进来,在这里,一个对象继承自另外一个对象。你可以这样理解它:你有一个想复用的对象,然后你想创建第二个对象,并且获得第一个对象的功能。下面是这种模式的用法: + + //需要继承的对象 + var parent = { + name: "Papa" + }; + + //新对象 + var child = object(parent); + + //测试 + alert(child.name); // "Papa" + +在这个代码片段中,有一个已经存在的使用对象字面量创建的对象叫parent,我们想创建一个和parent有相同的属性和方法的对象叫child。child对象使用object()函数创建。这个函数在JavaScript中并不存在(不要与构造函数Object()混淆),所以我们来看看怎样定义它。 + +与Holy Grail类式继承相似,可以使用一个空的临时构造函数F(),然后设定F()的原型为parent对象。最后,返回一个临时构造函数的新实例。 + + function object(o) { + function F() {} + F.prototype = o; + return new F(); + } + +图6-9展示了使用原型继承时的原型链。在这里child总是以一个空对象开始,它没有自己的属性但通过原型链(\_\_proto\_\_)拥有父对象的所有功能。 + +//TODO:图6-9 + +### 讨论 + +在原型继承模式中,parent不需要使用对象字面量来创建。(尽管这是一种更觉的方式。)可以使用构造函数来创建parent。注意,如果你这样做,那么自己的属性和原型上的属性都将被继承: + + // parent constructor + function Person() { + // an "own" property + this.name = "Adam"; + } + // a property added to the prototype + Person.prototype.getName = function () { + return this.name; + }; + + // create a new person + var papa = new Person(); + // inherit + var kid = object(papa); + + // test that both the own property + // and the prototype property were inherited + kid.getName(); // "Adam" + +在这种模式的另一个变种中,你可以选择只继承已存在的构造函数的原型对象。记住,对象继承自对象,不管父对象是怎么创建的。这是前面例子的一个修改版本: + + // parent constructor + function Person() { + // an "own" property + this.name = "Adam"; + } + // a property added to the prototype + Person.prototype.getName = function () { + + }; + + // inherit + var kid = object(Person.prototype); + + typeof kid.getName; // "function", because it was in the prototype + typeof kid.name; // "undefined", because only the prototype was inherited + +###例外的ECMAScript 5 + +在ECMAScript 5中,原型继承已经正式成为语言的一部分。这种模式使用Object.create方法来实现。换句话说,你不再需要自己去写类似object()的函数,它是语言原生的了: + + var child = Object.create(parent); + +Object.create()接收一个额外的参数——一个对象。这个额外对象中的属性将被作为自己的属性添加到返回的子对象中。这让我们可以很方便地将继承和创建子对象在一个方法调用中实现。例如: + + var child = Object.create(parent, { + age: { value: 2 } // ECMA5 descriptor + }); + child.hasOwnProperty("age"); // true + +你可能也会发现原型继承模式已经在一些JavaScript类库中实现了,比如,在YUI3中,它是Y.Object()方法: + + YUI().use('*', function (Y) { + var child = Y.Object(parent); + }); \ No newline at end of file From d04c03aef65abe70adc4f16b6b01b467e8b25a94 Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 13 Aug 2012 23:49:13 +0800 Subject: [PATCH 48/55] =?UTF-8?q?=E9=80=9A=E8=BF=87=E5=A4=8D=E5=88=B6?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E7=BB=A7=E6=89=BF=20=E4=B8=80=E8=8A=82?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 78 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/chapter6.markdown b/chapter6.markdown index af28415..561152a 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -574,4 +574,80 @@ Object.create()接收一个额外的参数——一个对象。这个额外对 YUI().use('*', function (Y) { var child = Y.Object(parent); - }); \ No newline at end of file + }); + + +## 通过复制属性继承 + +让我们来看一下另外一种继承模式——通过复制属性继承。在这种模式中,一个对象通过简单地复制另一个对象来获得功能。下面是一个简单的实现这种功能的extend()函数: + + function extend(parent, child) { + var i; + child = child || {}; + for (i in parent) { + if (parent.hasOwnProperty(i)) { + child[i] = parent[i]; + } + } + return child; + } + +这是一个简单的实现,仅仅是遍历了父对象的成员然后复制它们。在这个实现中,child是可选参数,如果它没有被传入一个已有的对象,那么一个全新的对象将被创建并被返回: + + var dad = {name: "Adam"}; + var kid = extend(dad); + kid.name; // "Adam" + +上面给出的实现叫作对象的“浅拷贝”(shallow copy)。另一方面,“深拷贝”是指检查准备复制的属性本身是否是对象或者数组,如果是,也遍历它们的属性并复制。如果使用浅拷贝的话(因为在JavaScript中对象是按引用传递),如果你改变子对象的一个属性,而这个属性恰好是一个对象,那么你也会改变父对象。实际上这对方法来说可能很好(因为函数也是对象,也是按引用传递),但是当遇到其它的对象和数组的时候可能会有些意外情况。考虑这种情况: + + var dad = { + counts: [1, 2, 3], + reads: {paper: true} + }; + var kid = extend(dad); + kid.counts.push(4); + dad.counts.toString(); // "1,2,3,4" + dad.reads === kid.reads; // true + +现在让我们来修改一下extend()函数以便做深拷贝。所有你需要做的事情只是检查一个属性的类型是否是对象,如果是,则递归遍历它的属性。另外一个需要做的检查是这个对象是真的对象还是数组。我们可以使用第3章讨论过的数组检查方式。最终深拷贝版的extend()是这样的: + + function extendDeep(parent, child) { + var i, + toStr = Object.prototype.toString, + astr = "[object Array]"; + + child = child || {}; + + for (i in parent) { + if (parent.hasOwnProperty(i)) { + if (typeof parent[i] === "object") { + child[i] = (toStr.call(parent[i]) === astr) ? [] : {}; + extendDeep(parent[i], child[i]); + } else { + child[i] = parent[i]; + } + } + } + return child; + } + +现在测试时这个新的实现给了我们对象的真实拷贝,所以子对象不会修改父对象: + + var dad = { + counts: [1, 2, 3], + reads: {paper: true} + }; + var kid = extendDeep(dad); + + kid.counts.push(4); + kid.counts.toString(); // "1,2,3,4" + dad.counts.toString(); // "1,2,3" + + dad.reads === kid.reads; // false + kid.reads.paper = false; + kid.reads.web = true; + dad.reads.paper; // true + +通过复制属性继承的模式很简单且应用很广泛。例如Firebug(JavaScript写的Firefox扩展)有一个方法叫extend()做浅拷贝,jQuery的extend()方法做深拷贝。YUI3提供了一个叫作Y.clone()的方法,它创建一个深拷贝并且通过绑定到子对象的方式复制函数。(本章后面将有更多关于绑定的内容。) + +它并不高深,根本并没有原型牵涉到这种模式中,它只是跟对象和它们属性有关的模式。 \ No newline at end of file From 37936472e45bf16020cc5c291399ee30630a4ba1 Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 30 Oct 2012 05:49:51 -0400 Subject: [PATCH 49/55] =?UTF-8?q?=E7=AC=AC=E5=85=AD=E7=AB=A0=20=E6=B7=B7?= =?UTF-8?q?=E5=85=83=E4=B8=80=E8=8A=82=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 72 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/chapter6.markdown b/chapter6.markdown index 561152a..4b5c6ec 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -38,22 +38,22 @@ 下面是定义两个构造函数Parent()和Child()的例子: - //parent构造函数 - function Parent(name) { - this.name = name || 'Adam'; - } - - //给原型增加方法 - Parent.prototype.say = function () { - return this.name; - }; - - //空的child构造函数 - function Child(name) {} - - //继承 - inherit(Child, Parent); - + //parent构造函数 + function Parent(name) { + this.name = name || 'Adam'; + } + + //给原型增加方法 + Parent.prototype.say = function () { + return this.name; + }; + + //空的child构造函数 + function Child(name) {} + + //继承 + inherit(Child, Parent); + 上面的代码定义了两个构造函数Parent()和Child(),say()方法被添加到了Parent()构建函数的原型(prototype)中,inherit()函数完成了继承的工作。inherit()函数并不是原生提供的,需要自己实现。让我们来看一看比较大众的实现它的几种方法。 ## 类式继承1——默认模式 @@ -589,7 +589,7 @@ Object.create()接收一个额外的参数——一个对象。这个额外对 child[i] = parent[i]; } } - return child; + return child; } 这是一个简单的实现,仅仅是遍历了父对象的成员然后复制它们。在这个实现中,child是可选参数,如果它没有被传入一个已有的对象,那么一个全新的对象将被创建并被返回: @@ -650,4 +650,40 @@ Object.create()接收一个额外的参数——一个对象。这个额外对 通过复制属性继承的模式很简单且应用很广泛。例如Firebug(JavaScript写的Firefox扩展)有一个方法叫extend()做浅拷贝,jQuery的extend()方法做深拷贝。YUI3提供了一个叫作Y.clone()的方法,它创建一个深拷贝并且通过绑定到子对象的方式复制函数。(本章后面将有更多关于绑定的内容。) -它并不高深,根本并没有原型牵涉到这种模式中,它只是跟对象和它们属性有关的模式。 \ No newline at end of file +这种模式并不高深,因为根本没有原型牵涉进来,而只跟对象和它们的属性有关。 + +## 混元(Mix-ins) + +既然谈到了通过拷贝属性来继随,就让我们顺便多说一点,来讨论一下“混元”模式。除了前面说的从一个对象复制,你还可以从任意多数量的对象中复制属性,然后将它们混在一起组成一个新对象。 + +实现很简单,只需要遍历传入的每个参数然后复制它们的每个属性: + + function mix() { + var arg, prop, child = {}; + for (arg = 0; arg < arguments.length; arg += 1) { + for (prop in arguments[arg]) { + if (arguments[arg].hasOwnProperty(prop)) { + child[prop] = arguments[arg][prop]; + } + } + } + return child; + } + +现在我们有了一个通用的混元函数,我们可以传递任意数量的对象进去,返回的结果将是一个包含所有传入对象属性的新对象。下面是用法示例: + + var cake = mix( + {eggs: 2, large: true}, + {butter: 1, salted: true}, + {flour: "3 cups"}, + {sugar: "sure!"} + ); + +图6-10展示了在Firebug的控制台中用console.dir(cake)展示出来的混元后cake对象的属性。 + +![图6-10 在Firebug中查看cake对象](./Figure/chapter6/6-10.jpg) + +图6-10 在Firebug中查看cake对象 + +> 如果你习惯了某些将混元作为原生部分的语言,那么你可能期望修改一个或多个父对象时也影响子对象。但在这个实现中这是不会发生的事情。这里我们只是简单地遍历、复制自己的属性,并没有与父对象的链接。 + From 8aadabf2176b4b23b2edd7cc9802582e83361840 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 31 Oct 2012 01:34:39 -0400 Subject: [PATCH 50/55] =?UTF-8?q?=E7=AC=AC=E5=85=AD=E7=AB=A0=E5=85=A8?= =?UTF-8?q?=E9=83=A8=E7=BF=BB=E8=AF=91=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 128 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/chapter6.markdown b/chapter6.markdown index 4b5c6ec..19c983d 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -686,4 +686,132 @@ Object.create()接收一个额外的参数——一个对象。这个额外对 图6-10 在Firebug中查看cake对象 > 如果你习惯了某些将混元作为原生部分的语言,那么你可能期望修改一个或多个父对象时也影响子对象。但在这个实现中这是不会发生的事情。这里我们只是简单地遍历、复制自己的属性,并没有与父对象的链接。 + +## 借用方法 + +有时候会有这样的情况:你希望使用某个已存在的对象的一两个方法,你希望能复用它们,但是又真的不希望和那个对象产生继承关系,因为你只希望使用你需要的那一两个方法,而不继承那些你永远用不到的方法。受益于函数方法call()和apply(),通过借用方法模式,这是可行的。在本书中,你其实已经见过这种模式了,甚至在本章extendDeep()的实现中也有用到。 + +如你所熟知的一样,在JavaScript中函数也是对象,它们有一些有趣的方法,比如call()和apply()。这两个方法的唯一区别是后者接受一个参数数组以传入正在调用的方法,而前者只接受一个一个的参数。你可以使用这两个方法来从已有的对象中借用方法: + + //call() example + notmyobj.doStuff.call(myobj, param1, p2, p3); + // apply() example + notmyobj.doStuff.apply(myobj, [param1, p2, p3]); + +在这个例子中有一个对象myobj,而且notmyobj有一个用得着的方法叫doStuff()。你可以简单地临时借用doStuff()方法,而不用处理继承然后得到一堆myobj中你永远不会用的方法。 + +你传一个对象和任意的参数,这个被借用的方法会将this绑定到你自己的对象上。简单地说,你的对象会临时假装成另一个对象以使用它的方法。这就像实际上获得了继承但又免除了“继承税”(指你不需要的属性和方法)。 + +### 例:从数组借用 + +这种模式的一种常见用法是从数组借用方法。 + +数组有很多很有用但是一些“类数组”对象(如arguments)不具备的方法。所以arguments可以借用数组的方法,比如slice()。这是一个例子: + + function f() { + var args = [].slice.call(arguments, 1, 3); + return args; + } + + // example + f(1, 2, 3, 4, 5, 6); // returns [2,3] + +在这个例子中,有一个空数组被创建了,因为要借用它的方法。同样的事情也可以使用一种看起来代码更长的方法来做,那就是直接从数组的原型中借用方法,使用Array.prototype.slice.call(...)。这种方法代码更长一些,但是不用创建一个空数组。 + +### 借用并绑定 + +当借用方法的时候,不管是通过call()/apply()还是通过简单的赋值,方法中的this指向的对象都是基于调用的表达式来决定的。但是有时候最好的使用方式是将this的值锁定或者提前绑定到一个指定的对象上。 + +我们来看一个例子。这是一个对象one,它有一个say()方法: + + var one = { + name: "object", + say: function (greet) { + return greet + ", " + this.name; + } + }; + + // test + one.say('hi'); // "hi, object" + +现在另一个对象two没有say()方法,但是它可以从one借用: + + var two = { + name: "another object" + }; + + one.say.apply(two, ['hello']); // "hello, another object" + +在这个例子中,say()方法中的this指向了two,this.name是“another object”。但是如果在某些场景下你将th函数赋值给了全局变量或者是将这个函数作为回调,会发生什么?在客户端编程中有非常多的事件和回调,所以这种情况经常发生: + + // assigning to a variable + // `this` will point to the global object + var say = one.say; + say('hoho'); // "hoho, undefined" + // passing as a callback + var yetanother = { + name: "Yet another object", + method: function (callback) { + return callback('Hola'); + } + }; + yetanother.method(one.say); // "Holla, undefined" + +在这两种情况中say()中的this都指向了全局对象,所以代码并不像我们想象的那样正常工作。要修复(换言之,绑定)一个方法的对象,我们可以用一个简单的函数,像这样: + + function bind(o, m) { + return function () { + return m.apply(o, [].slice.call(arguments)); + }; + } + +这个bind()函数接受一个对象o和一个方法m,然后把它们绑定在一起,再返回另一个函数。返回的函数通过闭包可以访问到o和m。也就是说,即使在bind()返回之后,内层的函数仍然可以访问到o和m,而o和m会始终指向原始的对象和方法。让我们用bind()来创建一个新函数: + + var twosay = bind(two, one.say); + twosay('yo'); // "yo, another object" + +正如你看到的,尽管twosay()是作为一个全局函数被创建的,但this并没有指向全局对象,而是指向了通过bind()传入的对象two。不论无何调用twosay(),this将始终指向two。 + +绑定是奢侈的,你需要付出的代价是一个额外的闭包。 + +### Function.prototype.bind() + +ECMAScript5在Function.prototype中添加了一个方法叫bind(),使用时和apply和call()一样简单。所以你可以这样写: + + var newFunc = obj.someFunc.bind(myobj, 1, 2, 3); + +这意味着将someFunc()主myobj绑定了并且传入了someFunc()的前三个参数。这也是一个在第4章讨论过的部分应用的例子。 + +让我们来看一下当你的程序跑在低于ES5的环境中时如何实现Function.prototype.bind(): + + if (typeof Function.prototype.bind === "undefined") { + Function.prototype.bind = function (thisArg) { + var fn = this, + slice = Array.prototype.slice, + args = slice.call(arguments, 1); + + return function () { + return fn.apply(thisArg, args.concat(slice.call(arguments))); + }; + }; + } + +这个实现可能看起来有点熟悉,它使用了部分应用,将传入bind()的参数串起来(除了第一个参数),然后在被调用时传给bind()返回的新函数。这是用法示例: + + var twosay2 = one.say.bind(two); + twosay2('Bonjour'); // "Bonjour, another object" + +在这个例子中,除了绑定的对象外,我们没有传任何参数给bind()。下一个例子中,我们来传一个用于部分应用的参数: + + var twosay3 = one.say.bind(two, 'Enchanté'); + twosay3(); // "Enchanté, another object" + +##小结 + +在JavaScript中,继承有很多种方案可以选择。学习和理解不同的模式是有好处的,因为这可以增强你对这门语言的掌握能力。在本章中你看到了很多类式继承和现代继承的方案。 + +但是,也许在开发过程中继承并不是你经常面对的一个问题。这一部分是因为这个问题已经被使用某种方式或者某个你使用的类库解决了,另一部分是因为你不需要在JavaScript中建立很长很复杂的继承链。在静态强类型语言中,继承可能是唯一可以利用代码的方法,但在JavaScript中你可能有更多更简单更优化的方法,包括借用方法、绑定、拷贝属性、混元等。 + +记住,代码复用才是目标,继承只是达成这个目标的一种手段。 + From ef837e2697d32e56e2db7ca4a74317d847ece823 Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 1 Nov 2012 01:41:21 -0400 Subject: [PATCH 51/55] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=AC=AC=E5=85=AD?= =?UTF-8?q?=E7=AB=A0=E9=85=8D=E5=9B=BE;=E6=B7=BB=E5=8A=A0README=E4=B8=AD?= =?UTF-8?q?=E7=AC=AC=E5=85=AD=E7=AB=A0=E9=93=BE=E6=8E=A5;=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E5=B0=91=E9=87=8F=E6=8E=AA=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Figure/chapter6/6-10.jpg | Bin 0 -> 7650 bytes Figure/chapter6/6-9.jpg | Bin 0 -> 13766 bytes README.markdown | 54 +++++++++++++++++++-------------------- chapter6.markdown | 48 ++++++++++++++++++++++------------ 4 files changed, 58 insertions(+), 44 deletions(-) create mode 100644 Figure/chapter6/6-10.jpg create mode 100644 Figure/chapter6/6-9.jpg diff --git a/Figure/chapter6/6-10.jpg b/Figure/chapter6/6-10.jpg new file mode 100644 index 0000000000000000000000000000000000000000..61720cf72b4483ee38901bb341c11d78910c78ee GIT binary patch literal 7650 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 literal 0 HcmV?d00001 diff --git a/README.markdown b/README.markdown index 84d5942..ad89f86 100644 --- a/README.markdown +++ b/README.markdown @@ -140,33 +140,33 @@ - method() 方法 - 小节 -## [第六章 代码重用模式](javascript.patterns/blob/master/chapter6.markdown#a) - -- [类式继承 vs 现代继承模式](javascript.patterns/blob/master/chapter6.markdown#a) -- [类式继承的期望结果](javascript.patterns/blob/master/chapter6.markdown#a) -- [类式继承 1 ——默认模式](javascript.patterns/blob/master/chapter6.markdown#a) - - [跟踪原型链](javascript.patterns/blob/master/chapter6.markdown#a) - - [这种模式的缺点](javascript.patterns/blob/master/chapter6.markdown#a) -- [类式继承 2 ——借用构造函数](javascript.patterns/blob/master/chapter6.markdown#a) - - [原型链](javascript.patterns/blob/master/chapter6.markdown#a) - - [通过借用构造函数实现多继承](javascript.patterns/blob/master/chapter6.markdown#a) - - [借用构造器模式的利与弊](javascript.patterns/blob/master/chapter6.markdown#a) -- [类式继承 3 ——借用并设置原型](javascript.patterns/blob/master/chapter6.markdown#a) -- 经典模式 4 ——共享原型 -- 经典模式 5 —— 临时构造器 - - 存储父类 - - 重置构造器引用 -- Klass -- 原型继承 - - 讨论 - - 除了ECMAScript5之外 -- 通过拷贝属性继承 -- 混元 -- 借用方法 - - 例子:从数组借用 - - 借用和绑定 - - Function.prototype.bind() -- 小节 +## [第六章 代码复用模式](javascript.patterns/blob/master/chapter6.markdown#a1) + +- [类式继承 vs 现代继承模式](javascript.patterns/blob/master/chapter6.markdown#a2) +- [类式继承的期望结果](javascript.patterns/blob/master/chapter6.markdown#a3) +- [类式继承 1 ——默认模式](javascript.patterns/blob/master/chapter6.markdown#a4) + - [跟踪原型链](javascript.patterns/blob/master/chapter6.markdown#a5) + - [这种模式的缺点](javascript.patterns/blob/master/chapter6.markdown#a6) +- [类式继承 2 ——借用构造函数](javascript.patterns/blob/master/chapter6.markdown#a7) + - [原型链](javascript.patterns/blob/master/chapter6.markdown#a8) + - [利用借用构造函数模式实现多继承](javascript.patterns/blob/master/chapter6.markdown#a9) + - [借用构造函数的利与弊](javascript.patterns/blob/master/chapter6.markdown#a10) +- [类式继承 3 ——借用并设置原型](javascript.patterns/blob/master/chapter6.markdown#a11) +- [经典模式 4 ——共享原型](javascript.patterns/blob/master/chapter6.markdown#a12) +- [经典模式 5 —— 临时构造函数](javascript.patterns/blob/master/chapter6.markdown#a13) + - [存储父类](javascript.patterns/blob/master/chapter6.markdown#a14) + - [重置构造函数引用](javascript.patterns/blob/master/chapter6.markdown#a15) +- [Klass](javascript.patterns/blob/master/chapter6.markdown#a16) +- [原型继承](javascript.patterns/blob/master/chapter6.markdown#a17) + - [讨论](javascript.patterns/blob/master/chapter6.markdown#a18) + - [例外的ECMAScript 5](javascript.patterns/blob/master/chapter6.markdown#a19) +- [通过复制属性继承](javascript.patterns/blob/master/chapter6.markdown#a20) +- [混元(Mix-ins)](javascript.patterns/blob/master/chapter6.markdown#a21) +- [借用方法](javascript.patterns/blob/master/chapter6.markdown#a22) + - [例:从数组借用](javascript.patterns/blob/master/chapter6.markdown#a23) + - [借用并绑定](javascript.patterns/blob/master/chapter6.markdown#a24) + - [Function.prototype.bind()](javascript.patterns/blob/master/chapter6.markdown#a25) +- [小结](javascript.patterns/blob/master/chapter6.markdown#a26) ## 第七章 设计模式 diff --git a/chapter6.markdown b/chapter6.markdown index 19c983d..395653d 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -7,7 +7,7 @@ 在做代码复用的工作的时候,谨记Gang of Four 在书中给出的关于对象创建的建议:“优先使用对象创建而不是类继承”。(译注:《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software)是一本设计模式的经典书籍,该书作者为Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides,被称为“Gang of Four”,简称“GoF”。) - + ## 类式继承 vs 现代继承模式 在讨论JavaScript的继承这个话题的时候,经常会听到“类式继承”的概念,那我们先看一下什么是类式(classical)继承。classical一词并不是来自某些古老的、固定的或者是被广泛接受的解决方案,而仅仅是来自单词“class”。(译注:classical也有“经典”的意思。) @@ -30,6 +30,7 @@ 本章先讨论类式继承,然后再关注现代继承模式。 + ## 类式继承的期望结果 实现类式继承的目标是基于构造函数Child()来创建一个对象,然后从另一个构造函数Parent()获得属性。 @@ -56,6 +57,7 @@ 上面的代码定义了两个构造函数Parent()和Child(),say()方法被添加到了Parent()构建函数的原型(prototype)中,inherit()函数完成了继承的工作。inherit()函数并不是原生提供的,需要自己实现。让我们来看一看比较大众的实现它的几种方法。 + ## 类式继承1——默认模式 最常用的一种模式是使用Parent()构造函数来创建一个对象,然后把这个对象设为Child()的原型。这是可复用的inherit()函数的第一种实现方法: @@ -71,6 +73,7 @@ var kid = new Child(); kid.say(); // "Adam" + ### 跟踪原型链 在这种模式中,子对象既继承了(父对象)“自己的属性”(添加给this的实例属性,比如name),也继承了原型中的属性和方法(比如say())。 @@ -107,6 +110,7 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 如果通过delete kid.name的方式移除新添加的属性,那么2号对象的name属性将暴露出来并且在查找的时候被找到。 + ### 这种模式的缺点 这种模式的一个缺点是既继承了(父对象)“自己的属性”,也继承了原型中的属性。大部分情况下你可能并不需要“自己的属性”,因为它们更可能是为实例对象添加的,并不用于复用。 @@ -120,7 +124,7 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 这并不是我们期望的结果。事实上传递参数给父构造函数是可能的,但这样需要在每次需要一个子对象时再做一次继承,很不方便,因为需要不断地创建父对象。 - + ## 类式继承2——借用构造函数 下面这种模式解决了从子对象传递参数到父对象的问题。它借用了父对象的构造函数,将子对象绑定到this,同时传入参数: @@ -165,7 +169,7 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 在这个例子中,blog对象修改了tags属性,同时,它也修改了父对象,因为实际上blog.tags和article.tags是引向同一个数组。而对pages.tags的修改并不影响父对象article,因为pages.tags在继承的时候是一份独立的拷贝。 - + ### 原型链 我们来看一下当我们使用熟悉的Parent()和Child()构造函数和这种继承模式时原型链是什么样的。为了使用这种继承模式,Child()有明显变化: @@ -195,7 +199,7 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 图6-4 使用借用构造函数模式时没有被关联的原型链 - + ### 利用借用构造函数模式实现多继承 使用借用构造函数模式,可以通过借用多个构造函数的方式来实现多继承: @@ -226,7 +230,7 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 图6-5 在Firebug中查看CatWings对象 - + ### 借用构造函数的利与弊 这种模式的一个明显的弊端就是无法继承原型。如前面所说,原型往往是添加可复用的方法和属性的地方,这样就不用在每个实例中再创建一遍。 @@ -235,7 +239,7 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 那么,在上一个例子中,怎样使一个子对象也能够继承原型属性呢?怎样能使kid可以访问到say()方法呢?下一种继承模式解决了这个问题。 - + ## 类式继承3——借用并设置原型 综合以上两种模式,首先借用父对象的构造函数,然后将子对象的原型设置为父对象的一个新实例: @@ -281,7 +285,7 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 图6-6 除了继承“自己的属性”外,原型链也被保留了 - + ## 类式继承4——共享原型 不像前一种类式继承模式需要调用两次父构造函数,下面这种模式根本不会涉及到调用父构造函数的问题。 @@ -300,7 +304,7 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 图6-7 (父子对象)共享原型时的关系 - + ## 类式继承5——临时构造函数 下一种模式通过打断父对象和子对象原型的直接链接解决了共享原型时的问题,同时还从原型链中获得其它的好处。 @@ -327,7 +331,7 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 如果你访问kid.name将得到undefined。在这个例子中,name是父对象自己的属性,而在继承的过程中我们并没有调用new Parent(),所以这个属性并没有被创建。当访问kid.say()时,它在3号对象中不可用,所以在原型链中查找,4号对象也没有,但是1号对象有,它在内在中的位置会被所有从Parent()创建的构造函数和子对象所共享。 - + ### 存储父类(Superclass) 在上一种模式的基础上,还可以添加一个指向原始父对象的引用。这很像其它语言中访问超类(superclass)的情况,有时候很方便。 @@ -341,7 +345,7 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 C.uber = P.prototype; } - + ### 重置构造函数引用 这个近乎完美的模式上还需要做的最后一件事情就是重置构造函数(constructor)的指向,以便未来在某个时刻能被正确地使用。 @@ -386,7 +390,7 @@ constructor属性很少用,但是在运行时检查对象很方便。你可以 } }()); - + ## Klass 有很多JavaScript类库模拟了类,创造了新的语法糖。具体的实现方式可能会不一样,但是基本上都有一些共性,包括: @@ -486,7 +490,7 @@ constructor属性很少用,但是在运行时检查对象很方便。你可以 什么时候使用这种模式?其实,最好是能避免则避免,因为它带来了在这门语言中不存在的完整的类的概念,会让人疑惑。使用它需要学习新的语法和新的规则。也就是说,如果你或者你的团队对类感到习惯并且同时对原型感到不习惯,这种模式可能是一个可以探索的方向。这种模式允许你完全忘掉原型,好处就是你可以将语法变种得像其它你所喜欢的语言一样。 - + ## 原型继承 现在,让我们从一个叫作“原型继承”的模式来讨论没有类的现代继承模式。在这种模式中,没有任何类进来,在这里,一个对象继承自另外一个对象。你可以这样理解它:你有一个想复用的对象,然后你想创建第二个对象,并且获得第一个对象的功能。下面是这种模式的用法: @@ -514,8 +518,11 @@ constructor属性很少用,但是在运行时检查对象很方便。你可以 图6-9展示了使用原型继承时的原型链。在这里child总是以一个空对象开始,它没有自己的属性但通过原型链(\_\_proto\_\_)拥有父对象的所有功能。 -//TODO:图6-9 +![图6-9 原型继承模式](./Figure/chapter6/6-9.jpg) +图6-9 原型继承模式 + + ### 讨论 在原型继承模式中,parent不需要使用对象字面量来创建。(尽管这是一种更觉的方式。)可以使用构造函数来创建parent。注意,如果你这样做,那么自己的属性和原型上的属性都将被继承: @@ -556,7 +563,8 @@ constructor属性很少用,但是在运行时检查对象很方便。你可以 typeof kid.getName; // "function", because it was in the prototype typeof kid.name; // "undefined", because only the prototype was inherited - + + ###例外的ECMAScript 5 在ECMAScript 5中,原型继承已经正式成为语言的一部分。这种模式使用Object.create方法来实现。换句话说,你不再需要自己去写类似object()的函数,它是语言原生的了: @@ -576,7 +584,7 @@ Object.create()接收一个额外的参数——一个对象。这个额外对 var child = Y.Object(parent); }); - + ## 通过复制属性继承 让我们来看一下另外一种继承模式——通过复制属性继承。在这种模式中,一个对象通过简单地复制另一个对象来获得功能。下面是一个简单的实现这种功能的extend()函数: @@ -652,9 +660,10 @@ Object.create()接收一个额外的参数——一个对象。这个额外对 这种模式并不高深,因为根本没有原型牵涉进来,而只跟对象和它们的属性有关。 + ## 混元(Mix-ins) -既然谈到了通过拷贝属性来继随,就让我们顺便多说一点,来讨论一下“混元”模式。除了前面说的从一个对象复制,你还可以从任意多数量的对象中复制属性,然后将它们混在一起组成一个新对象。 +既然谈到了通过复制属性来继承,就让我们顺便多说一点,来讨论一下“混元”模式。除了前面说的从一个对象复制,你还可以从任意多数量的对象中复制属性,然后将它们混在一起组成一个新对象。 实现很简单,只需要遍历传入的每个参数然后复制它们的每个属性: @@ -687,6 +696,7 @@ Object.create()接收一个额外的参数——一个对象。这个额外对 > 如果你习惯了某些将混元作为原生部分的语言,那么你可能期望修改一个或多个父对象时也影响子对象。但在这个实现中这是不会发生的事情。这里我们只是简单地遍历、复制自己的属性,并没有与父对象的链接。 + ## 借用方法 有时候会有这样的情况:你希望使用某个已存在的对象的一两个方法,你希望能复用它们,但是又真的不希望和那个对象产生继承关系,因为你只希望使用你需要的那一两个方法,而不继承那些你永远用不到的方法。受益于函数方法call()和apply(),通过借用方法模式,这是可行的。在本书中,你其实已经见过这种模式了,甚至在本章extendDeep()的实现中也有用到。 @@ -702,6 +712,7 @@ Object.create()接收一个额外的参数——一个对象。这个额外对 你传一个对象和任意的参数,这个被借用的方法会将this绑定到你自己的对象上。简单地说,你的对象会临时假装成另一个对象以使用它的方法。这就像实际上获得了继承但又免除了“继承税”(指你不需要的属性和方法)。 + ### 例:从数组借用 这种模式的一种常见用法是从数组借用方法。 @@ -718,6 +729,7 @@ Object.create()接收一个额外的参数——一个对象。这个额外对 在这个例子中,有一个空数组被创建了,因为要借用它的方法。同样的事情也可以使用一种看起来代码更长的方法来做,那就是直接从数组的原型中借用方法,使用Array.prototype.slice.call(...)。这种方法代码更长一些,但是不用创建一个空数组。 + ### 借用并绑定 当借用方法的时候,不管是通过call()/apply()还是通过简单的赋值,方法中的this指向的对象都是基于调用的表达式来决定的。但是有时候最好的使用方式是将this的值锁定或者提前绑定到一个指定的对象上。 @@ -775,6 +787,7 @@ Object.create()接收一个额外的参数——一个对象。这个额外对 绑定是奢侈的,你需要付出的代价是一个额外的闭包。 + ### Function.prototype.bind() ECMAScript5在Function.prototype中添加了一个方法叫bind(),使用时和apply和call()一样简单。所以你可以这样写: @@ -807,11 +820,12 @@ ECMAScript5在Function.prototype中添加了一个方法叫bind(),使用时和 var twosay3 = one.say.bind(two, 'Enchanté'); twosay3(); // "Enchanté, another object" + ##小结 在JavaScript中,继承有很多种方案可以选择。学习和理解不同的模式是有好处的,因为这可以增强你对这门语言的掌握能力。在本章中你看到了很多类式继承和现代继承的方案。 -但是,也许在开发过程中继承并不是你经常面对的一个问题。这一部分是因为这个问题已经被使用某种方式或者某个你使用的类库解决了,另一部分是因为你不需要在JavaScript中建立很长很复杂的继承链。在静态强类型语言中,继承可能是唯一可以利用代码的方法,但在JavaScript中你可能有更多更简单更优化的方法,包括借用方法、绑定、拷贝属性、混元等。 +但是,也许在开发过程中继承并不是你经常面对的一个问题。这一部分是因为这个问题已经被使用某种方式或者某个你使用的类库解决了,另一部分是因为你不需要在JavaScript中建立很长很复杂的继承链。在静态强类型语言中,继承可能是唯一可以利用代码的方法,但在JavaScript中你可能有更多更简单更优化的方法,包括借用方法、绑定、复制属性、混元等。 记住,代码复用才是目标,继承只是达成这个目标的一种手段。 From 0323ee198bc2fdc8dc82dfcc4444fc018ef84f40 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 2 Nov 2012 02:03:19 -0400 Subject: [PATCH 52/55] =?UTF-8?q?=E7=AC=AC=E4=B8=83=E7=AB=A0=20=E5=8D=95?= =?UTF-8?q?=E4=BE=8B=E7=AC=AC=E4=B8=80=E5=B0=8F=E8=8A=82=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 chapter7.markdown diff --git a/chapter7.markdown b/chapter7.markdown new file mode 100644 index 0000000..251daca --- /dev/null +++ b/chapter7.markdown @@ -0,0 +1,33 @@ + +# 设计模式 + +在GoF(Gang of Four)的书中提出的设计模式为面向对象的软件设计中遇到的一些普遍问题提供了解决方案。它们已经诞生很久了,而且被证实在很多情况下是很有效的。这正是你需要熟悉它的原因,也是我们要讨论它的原因。 + +尽管这些设计模式跟语言和具体的实现方式无关,但它们多年来被关注到的方面仍然主要是在强类型静态语言比如C++和Java中的应用。 + +JavaScript作为一种基于原型的弱类型动态语言,使得有些时候实现某些模式时相当简单,甚至不费吹灰之力。 + +让我们从第一个例子——单例模式——来看一下在JavaScript中和静态的基于类的语言有什么不同。 + +## 单例 + +单例模式的核心思想是让指定的类只存在唯一一个实例。这意味着当你第二次使用相同的类去创建对象的时候,你得到的应该和第一次创建的是同一个对象。 + +这如何应用到JavaScript中呢?在JavaScript中没有类,只有对象。当你创建一个对象时,事实上根本没有另一个对象和它一样,这个对象其实已经是一个单例。使用对象字面量创建一个简单的对象也是一种单例的例子: + + var obj = { + myprop: 'my value' + }; + +在JavaScript中,对象永远不会相等,除非它们是同一个对象,所以既然你创建一个看起来完全一样的对象,它也不会和前面的对象相等: + + var obj2 = { + myprop: 'my value' + }; + obj === obj2; // false + obj == obj2; // false + +所以你可以说当你每次使用对象字面量创建一个对象的时候就是在创建一个单例,并没有特别的语法迁涉进来。 + +> 需要注意的是,有的时候当人们在JavaScript中提出“单例”的时候,它们可能是在指第5章讨论过的“模块模式”。 + From 512f755e866974d5dcf292da91b2fc7e6861f9a9 Mon Sep 17 00:00:00 2001 From: liunian Date: Thu, 15 Nov 2012 14:08:15 +0800 Subject: [PATCH 53/55] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20delete=20=E7=9A=84?= =?UTF-8?q?=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chapter2.markdown b/chapter2.markdown index 2913054..8d76863 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -111,6 +111,8 @@ JavaScript 使用函数来管理作用域,在一个函数内定义的变量称 也就是说,隐式全局变量并不算是真正的变量,但他们是全局对象的属性成员。属性是可以通过delete运算符删除的,而变量不可以被删除: +>(译注:在浏览器环境中,所有 JavaScript 代码都是在 window 作用域内的,所以在这种情况下,我们所说的全局变量其实都是 window 下的一个属性,故可以用 delete 删除,但在如 nodejs 或 gjs 等非浏览器环境下,显示声明的全局变量无法用 delete 删除。) + // define three globals var global_var = 1; global_novar = 2; // antipattern From b6b364faa0a80faefe8e814e3e1a2789652c182f Mon Sep 17 00:00:00 2001 From: liunian Date: Thu, 15 Nov 2012 14:27:42 +0800 Subject: [PATCH 54/55] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E8=AF=AD=E5=8F=A5=E7=9A=84=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index 55c32b0..f32a30e 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -1,7 +1,7 @@ # 第三章 直接量和构造函数 -JavaScript中的直接量模式更加简洁、富有表现力,且在定义对象时不容易出错。本章将对直接量展开讨论,包括对象、数组和正则表达式直接量,以及为什么要使用等价的内置构造器函数来创建它们,比如Object()和Array()等。本章同样会介绍JSON格式,JSON是使用数组和对象直接量的形式定义的一种数据转换格式。本章还会讨论自定义构造函数,包括如何强制使用new以确保构造函数的正确执行。 +JavaScript中的直接量模式更加简洁、富有表现力,且在定义对象时不容易出错。本章将对直接量展开讨论,包括对象、数组和正则表达式直接量,以及为什么要优先使用它们而不是如`Object()`和`Array()`这些等价的内置构造器函数。本章同样会介绍JSON格式,JSON是使用数组和对象直接量的形式定义的一种数据转换格式。本章还会讨论自定义构造函数,包括如何强制使用new以确保构造函数的正确执行。 本章还会补充讲述一些基础知识,比如内置包装对象Number()、String()和Boolean(),以及如何将它们和原始值(数字、字符串和布尔值)比较。最后,快速介绍一下Error()构造函数的用法。 @@ -338,7 +338,7 @@ ECMAScript5中修正了这种非正常的行为逻辑。在严格模式中,thi console.log(a.length); // 3 console.log(typeof a[0]); // "undefined" -尽管构造器的行为并不像我们想象的那样,当给new Array()传入一个浮点数时情况就更糟糕了。这时结果就会出错(译注:给new Array()传入浮点数会报“范围错误”RangError,new Array(3.00)则不会报错),因为数组长度不可能是浮点数。 +或许上面的情况看起来还不算是太严重的问题,但当 `new Array()` 的参数是一个浮点数而不是整数时则会导致严重的错误,这是因为数组的长度不可能是浮点数。 // using array literal var a = [3.14]; From 5bac56fc65bdaf7adffd4449ede7e8f0fd5a958b Mon Sep 17 00:00:00 2001 From: liunian Date: Thu, 15 Nov 2012 14:27:42 +0800 Subject: [PATCH 55/55] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E8=AF=AD=E5=8F=A5=E7=9A=84=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 2 +- chapter3.markdown | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 8d76863..e4516e8 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -111,7 +111,7 @@ JavaScript 使用函数来管理作用域,在一个函数内定义的变量称 也就是说,隐式全局变量并不算是真正的变量,但他们是全局对象的属性成员。属性是可以通过delete运算符删除的,而变量不可以被删除: ->(译注:在浏览器环境中,所有 JavaScript 代码都是在 window 作用域内的,所以在这种情况下,我们所说的全局变量其实都是 window 下的一个属性,故可以用 delete 删除,但在如 nodejs 或 gjs 等非浏览器环境下,显示声明的全局变量无法用 delete 删除。) +>(译注:在浏览器环境中,所有 JavaScript 代码都是在 window 作用域内的,所以在这种情况下,我们所说的全局变量其实都是 window 下的一个属性,故可以用 delete 删除,但在如 nodejs 或 gjs 等非浏览器环境下,显式声明的全局变量无法用 delete 删除。) // define three globals var global_var = 1; diff --git a/chapter3.markdown b/chapter3.markdown index 55c32b0..f32a30e 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -1,7 +1,7 @@ # 第三章 直接量和构造函数 -JavaScript中的直接量模式更加简洁、富有表现力,且在定义对象时不容易出错。本章将对直接量展开讨论,包括对象、数组和正则表达式直接量,以及为什么要使用等价的内置构造器函数来创建它们,比如Object()和Array()等。本章同样会介绍JSON格式,JSON是使用数组和对象直接量的形式定义的一种数据转换格式。本章还会讨论自定义构造函数,包括如何强制使用new以确保构造函数的正确执行。 +JavaScript中的直接量模式更加简洁、富有表现力,且在定义对象时不容易出错。本章将对直接量展开讨论,包括对象、数组和正则表达式直接量,以及为什么要优先使用它们而不是如`Object()`和`Array()`这些等价的内置构造器函数。本章同样会介绍JSON格式,JSON是使用数组和对象直接量的形式定义的一种数据转换格式。本章还会讨论自定义构造函数,包括如何强制使用new以确保构造函数的正确执行。 本章还会补充讲述一些基础知识,比如内置包装对象Number()、String()和Boolean(),以及如何将它们和原始值(数字、字符串和布尔值)比较。最后,快速介绍一下Error()构造函数的用法。 @@ -338,7 +338,7 @@ ECMAScript5中修正了这种非正常的行为逻辑。在严格模式中,thi console.log(a.length); // 3 console.log(typeof a[0]); // "undefined" -尽管构造器的行为并不像我们想象的那样,当给new Array()传入一个浮点数时情况就更糟糕了。这时结果就会出错(译注:给new Array()传入浮点数会报“范围错误”RangError,new Array(3.00)则不会报错),因为数组长度不可能是浮点数。 +或许上面的情况看起来还不算是太严重的问题,但当 `new Array()` 的参数是一个浮点数而不是整数时则会导致严重的错误,这是因为数组的长度不可能是浮点数。 // using array literal var a = [3.14];