From 6b0039676579f417e1b289932741ff227ff5785a Mon Sep 17 00:00:00 2001 From: xqin Date: Fri, 8 Feb 2013 12:04:08 +0800 Subject: [PATCH 001/145] Update chapter2.markdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加第39行最后一句中的 "或者是直接使用而未明的变量" 中少写的 "声" 字. 完整的应该是 "或者是直接使用而未声明的变量". --- chapter2.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter2.markdown b/chapter2.markdown index 2913054..9479936 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -36,7 +36,7 @@ ## 减少全局对象 -JavaScript 使用函数来管理作用域,在一个函数内定义的变量称作“局部变量”,局部变量在函数外部是不可见的。另一方面,“全局变量”是不在任何函数体内部声明的变量,或者是直接使用而未明的变量。 +JavaScript 使用函数来管理作用域,在一个函数内定义的变量称作“局部变量”,局部变量在函数外部是不可见的。另一方面,“全局变量”是不在任何函数体内部声明的变量,或者是直接使用而未声明的变量。 每一个JavaScript运行环境都有一个“全局对象”,不在任何函数体内使用this就可以获得对这个全局对象的引用。你所创建的每一个全局变量都是这个全局对象的属性。为了方便起见,浏览器都会额外提供一个全局对象的属性window,(常常)用以指向全局对象本身。下面的示例代码中展示了如何在浏览器中创建或访问全局变量: From e8aa7fda016864f0d61908b342c030879cb24d4c Mon Sep 17 00:00:00 2001 From: xqin Date: Tue, 12 Feb 2013 23:09:49 +0800 Subject: [PATCH 002/145] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=87=A0=E5=A4=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=B2=A1=E6=8D=A2=E8=A1=8C=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index dec662a..09faf54 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -183,7 +183,8 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 return this.myprop; } }; - console.log(myobj.myprop); // `myprop` is publicly accessible console.log(myobj.getProp()); // getProp() is public too + console.log(myobj.myprop); // `myprop` is publicly accessible + console.log(myobj.getProp()); // getProp() is public too 当你使用构造函数创建对象的时候也是一样的,所有的成员都是公有的: @@ -194,7 +195,8 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 }; } var toy = new Gadget(); - console.log(toy.name); // `name` is public console.log(toy.stretch()); // stretch() is public + console.log(toy.name); // `name` is public + console.log(toy.stretch()); // stretch() is public ### 私有成员 @@ -336,7 +338,8 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 }()); var toy = new Gadget(); - console.log(toy.getName()); // privileged "own" method console.log(toy.getBrowser()); // privileged prototype method + console.log(toy.getName()); // privileged "own" method + console.log(toy.getBrowser()); // privileged prototype method ### 将私有函数暴露为公有方法 From 7d3736d6dffe6dd91ff0d03f958028bd2f3a4e9e Mon Sep 17 00:00:00 2001 From: xqin Date: Wed, 13 Feb 2013 23:40:47 +0800 Subject: [PATCH 003/145] =?UTF-8?q?=E4=BF=AE=E6=AD=A3"=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F"=E4=B8=AD=E4=BB=A3=E7=A0=81=E5=9D=97?= =?UTF-8?q?=E5=AF=B9=E9=BD=90=E4=B8=8D=E6=AD=A3=E7=A1=AE=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 09faf54..f4223ac 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -427,11 +427,11 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 如果需要的话,你可以在立即执行的函数提供的闭包中声明私有属性和私有方法。函数顶部也是声明依赖的地方。在变量声明的下方,你可以选择性地放置辅助初始化模块的一次性代码。函数最终返回的是一个包含模块公共API的对象: -MYAPP.namespace('MYAPP.utilities.array'); -MYAPP.utilities.array = (function () { + MYAPP.namespace('MYAPP.utilities.array'); + MYAPP.utilities.array = (function () { // dependencies - var uobj = MYAPP.utilities.object, + var uobj = MYAPP.utilities.object, ulang = MYAPP.utilities.lang, // private properties @@ -442,26 +442,26 @@ MYAPP.utilities.array = (function () { // ... // end var - // optionally one-time init procedures - // ... - - // public API - return { + // optionally one-time init procedures + // ... - inArray: function (needle, haystack) { - for (var i = 0, max = haystack.length; i < max; i += 1) { - if (haystack[i] === needle) { - return true; + // public API + return { + + inArray: function (needle, haystack) { + for (var i = 0, max = haystack.length; i < max; i += 1) { + if (haystack[i] === needle) { + return true; + } } + }, + + isArray: function (a) { + return ops.call(a) === array_string; } - }, - - isArray: function (a) { - return ops.call(a) === array_string; - } - // ... more methods and properties - }; -}()); + // ... more methods and properties + }; + }()); 模块模式被广泛使用,这是一种值得强烈推荐的模式,它可以帮助组织代码,尤其是代码量在不断增长的时候。 From bd441531e7090631b6169467aead7db543775d79 Mon Sep 17 00:00:00 2001 From: xqin Date: Wed, 13 Feb 2013 23:46:26 +0800 Subject: [PATCH 004/145] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=83=A8=E5=88=86?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=89=87=E6=AE=B5=E7=BC=A9=E8=BF=9B=E4=B8=8D?= =?UTF-8?q?=E6=AD=A3=E7=A1=AE=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 80 +++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index f4223ac..96bbbb9 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -432,11 +432,11 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 // dependencies var uobj = MYAPP.utilities.object, - ulang = MYAPP.utilities.lang, + ulang = MYAPP.utilities.lang, // private properties - array_string = "[object Array]", - ops = Object.prototype.toString; + array_string = "[object Array]", + ops = Object.prototype.toString; // private methods // ... @@ -473,23 +473,23 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 MYAPP.utilities.array = (function () { - // private properties + // private properties var array_string = "[object Array]", ops = Object.prototype.toString, - // private methods - inArray = function (haystack, needle) { - for (var i = 0, max = haystack.length; i < max; i += 1) { - if (haystack[i] === needle) { - return i; - } + // private methods + inArray = function (haystack, needle) { + for (var i = 0, max = haystack.length; i < max; i += 1) { + if (haystack[i] === needle) { + return i; } - return −1; - }, - isArray = function (a) { - return ops.call(a) === array_string; - }; - // end var + } + return −1; + }, + isArray = function (a) { + return ops.call(a) === array_string; + }; + // end var // revealing public API return { @@ -508,33 +508,33 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 MYAPP.utilities.Array = (function () { - // dependencies + // dependencies var uobj = MYAPP.utilities.object, ulang = MYAPP.utilities.lang, - // private properties and methods... - Constr; - - // end var - - // optionally one-time init procedures - // ... - - // public API -- constructor - Constr = function (o) { - this.elements = this.toArray(o); - }; - // public API -- prototype - Constr.prototype = { - constructor: MYAPP.utilities.Array, - version: "2.0", - toArray: function (obj) { - for (var i = 0, a = [], len = obj.length; i < len; i += 1) { - a[i] = obj[i]; - } - return a; + // private properties and methods... + Constr; + + // end var + + // optionally one-time init procedures + // ... + + // public API -- constructor + Constr = function (o) { + this.elements = this.toArray(o); + }; + // public API -- prototype + Constr.prototype = { + constructor: MYAPP.utilities.Array, + version: "2.0", + toArray: function (obj) { + for (var i = 0, a = [], len = obj.length; i < len; i += 1) { + a[i] = obj[i]; } - }; + return a; + } + }; // return the constructor // to be assigned to the new namespace return Constr; @@ -671,7 +671,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 最后,我们来实现`Sandbox()`构造函数(你可能会很自然地想将这类构造函数命名为对你的类库或者应用有意义的名字): function Sandbox() { - // turning arguments into an array + // turning arguments into an array var args = Array.prototype.slice.call(arguments), // the last argument is the callback callback = args.pop(), From 5e9e1d266b3526ec2c2c3a563187242b29514b0d Mon Sep 17 00:00:00 2001 From: xqin Date: Thu, 14 Feb 2013 00:30:17 +0800 Subject: [PATCH 005/145] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=9C=AA=E6=8D=A2=E8=A1=8C,=E6=98=BE=E7=A4=BA=E5=9C=A8?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E4=B8=AD=E7=9A=84=E9=97=AE=E9=A2=98.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chapter5.markdown b/chapter5.markdown index 96bbbb9..cee3ad2 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -537,7 +537,8 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 }; // return the constructor - // to be assigned to the new namespace return Constr; + // to be assigned to the new namespace + return Constr; }()); From dfc7e1b781259016c9fc8cd947436c8d729d1b14 Mon Sep 17 00:00:00 2001 From: xqin Date: Thu, 14 Feb 2013 00:45:34 +0800 Subject: [PATCH 006/145] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20"=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=88=90=E5=91=98"=E6=AE=B5=E8=90=BD=E4=B8=AD?= =?UTF-8?q?=E5=B0=86"=E9=9D=99=E6=80=81=E6=88=90=E5=91=98"=E5=86=99?= =?UTF-8?q?=E6=88=90"=E8=A1=A8=E6=80=81=E6=88=90=E5=91=98"=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter5.markdown b/chapter5.markdown index cee3ad2..20d507d 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -728,7 +728,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 ## 静态成员 -静态属性和方法是指那些在所有的实例中保持一致的成员。在基于类的语言中,表态成员是用专门的语法来创建,使用时就像是类自己的成员一样。比如`MathUtils`类的`max()`方法会被像这样调用:`MathUtils.max(3, 5)`。这是一个公有静态成员的示例,即可以在不实例化类的情况下使用。同样也可以有私有的静态方法,即对类的使用者不可见,而在类的所有实例间是共享的。我们来看一下如何在JavaScript中实现公有和私有静态成员。 +静态属性和方法是指那些在所有的实例中保持一致的成员。在基于类的语言中,静态成员是用专门的语法来创建,使用时就像是类自己的成员一样。比如`MathUtils`类的`max()`方法会被像这样调用:`MathUtils.max(3, 5)`。这是一个公有静态成员的示例,即可以在不实例化类的情况下使用。同样也可以有私有的静态方法,即对类的使用者不可见,而在类的所有实例间是共享的。我们来看一下如何在JavaScript中实现公有和私有静态成员。 ### 公有静态成员 From 55d2c63b0fee938f10acb0dfe0d10ff6bc12b962 Mon Sep 17 00:00:00 2001 From: xqin Date: Thu, 14 Feb 2013 00:49:46 +0800 Subject: [PATCH 007/145] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=E6=96=87=E5=AD=97=E5=8F=8A=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=8D=A2=E8=A1=8C=E4=B8=8D=E6=AD=A3=E7=A1=AE=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 20d507d..affd962 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -732,7 +732,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 ### 公有静态成员 -在JavaScript中没有专门用于静态成员的语法。但通过给构造函数添加属性的方法,可以拥有和基于类的语言一样的使用语法。之所有可以这样做是因为构造函数和其它的函数一样,也是对象,可以拥有属性。前一章讨论过的Memoization模式也使用了同样的方法,即给函数添加属性。 +在JavaScript中没有专门用于静态成员的语法。但通过给构造函数添加属性的方法,可以拥有和基于类的语言一样的使用语法。之所以可以这样做是因为构造函数和其它的函数一样,也是对象,可以拥有属性。前一章讨论过的Memoization模式也使用了同样的方法,即给函数添加属性。 下面的例子定义了一个构造函数`Gadget`,它有一个静态方法`isShiny()`和一个实例方法`setPrice()`。`isShiny()`是一个静态方法,因为它不需要指定一个对象才能工作(就像你不需要先指定一个工具(gadget)才知道所有的工具是不是有光泽的(shiny))。但setPrice()却需要一个对象,因为工具可能有不同的定价: @@ -744,7 +744,8 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 return "you bet"; }; - // a normal method added to the prototype Gadget.prototype.setPrice = function (price) { + // a normal method added to the prototype + Gadget.prototype.setPrice = function (price) { this.price = price; }; From b6316f92a43b045b799ba110312da9cac6973d91 Mon Sep 17 00:00:00 2001 From: xqin Date: Thu, 14 Feb 2013 22:40:40 +0800 Subject: [PATCH 008/145] =?UTF-8?q?method=E6=96=B9=E6=B3=95=20=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=96=87=E5=AD=97=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原:"使用构造函数主须Java中使用类一样",修改为:"使用构造函数就像Java中使用类一样". 因不知道英文原文的语句是什么,所以这里只是自己根据上下文做相应的修改. --- chapter5.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter5.markdown b/chapter5.markdown index affd962..95c6ad3 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -1004,7 +1004,7 @@ JavaScript中是没有常量的,尽管在一些比较现代的环境中可能 JavaScript对于习惯于用类来思考的人来说可能会比较费解,这也是很多开发者希望将JavaScript代码变得更像基于类的语言的原因。其中的一种尝试就是由Douglas Crockford提出来的`method()`方法。其实,他也承认将JavaScript变得像基于类的语言是不推荐的方法,但不管怎样,这都是一种有意思的模式,你可能会在一些应用中见到。 -使用构造函数主须Java中使用类一样。它也允许你在构造函数体的`this`中添加实例属性。但是在`this`中添加方法却是不高效的,因为最终这些方法会在每个实例中被重新创建一次,这样会花费更多的内存。这也是为什么可重用的方法应该被放到构造函数的`prototype`属性(原型)中的原因。但对很多开发者来说,`prototype`可能跟个外星人一样陌生,所以你可以通过一个方法将它隐藏起来。 +使用构造函数就像Java中使用类一样。它也允许你在构造函数体的`this`中添加实例属性。但是在`this`中添加方法却是不高效的,因为最终这些方法会在每个实例中被重新创建一次,这样会花费更多的内存。这也是为什么可重用的方法应该被放到构造函数的`prototype`属性(原型)中的原因。但对很多开发者来说,`prototype`可能跟个外星人一样陌生,所以你可以通过一个方法将它隐藏起来。 > 给语言添加一个使用起来更方便的方法一般叫作“语法糖”。在这个例子中,你可以将`method()`方法称为一个语法糖方法。 From b6385753f811b4f07d3590e2a3784f58724de661 Mon Sep 17 00:00:00 2001 From: xqin Date: Fri, 15 Feb 2013 18:29:34 +0800 Subject: [PATCH 009/145] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=B8=80=E5=A4=84?= =?UTF-8?q?=E6=96=87=E5=AD=97=E9=94=99=E8=AF=AF?= 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 395653d..5b26fb5 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -78,7 +78,7 @@ 在这种模式中,子对象既继承了(父对象)“自己的属性”(添加给this的实例属性,比如name),也继承了原型中的属性和方法(比如say())。 -我们来看一下在这种继承模式中原型链是怎么工作的。为了讨论方便,我们假设对象是内在中的一块空间,它包含数据和指向其它空间的引用。当使用new Parent()创建一个对象时,这样的一块空间就被分配了(图6-1中的2号)。它保存着name属性的数据。如果你尝试访问say()方法(比如通过(new Parent).say()),2号空间中并没有这个方法。但是在通过隐藏的链接__proto__指向Parent()构建函数的原型prototype属性时,就可以访问到包含say()方法的1号空间(Parent.prototype)了。所有的这一块都是在幕后发生的,不需要任何额外的操作,但是知道它是怎样工作的以及你正在访问或者修正的数据在哪是很重要的。注意,__proto__在这里只是为了解释原型链,这个属性在语言本身中是不可用的,尽管有一些环境提供了(比如Firefox)。 +我们来看一下在这种继承模式中原型链是怎么工作的。为了讨论方便,我们假设对象是内存中的一块空间,它包含数据和指向其它空间的引用。当使用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) From caa2fa3a24baf111cf0234155dafb08f5f329e5b Mon Sep 17 00:00:00 2001 From: "dds_feng@qq.com" Date: Sat, 16 Feb 2013 00:19:00 +0800 Subject: [PATCH 010/145] =?UTF-8?q?=B5=DA5=D5=C2=B2=B9=CD=BC=205-2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Figure/chapter5/5-2.jpg | Bin 0 -> 11165 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Figure/chapter5/5-2.jpg diff --git a/Figure/chapter5/5-2.jpg b/Figure/chapter5/5-2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3b7aac7ca3b077365e67827b808bae2953463a74 GIT binary patch literal 11165 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μwmVmiBYUX3dK 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 Y;(vJY-|ZvubkF-M$NsOn2BJ;;FLStixBvhE literal 0 HcmV?d00001 From 636ba9279b63c98a8307710f0cb342f299ed5e42 Mon Sep 17 00:00:00 2001 From: xqin Date: Sat, 16 Feb 2013 00:23:51 +0800 Subject: [PATCH 011/145] =?UTF-8?q?=E5=9B=BE=E7=89=87=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter5.markdown b/chapter5.markdown index 95c6ad3..628a90a 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -260,7 +260,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 在Firebug控制台中打印出来的结果如图5-2: -![图5-2 私有对象被修改了](./figure/chapter5/5-2.jpg) +![图5-2 私有对象被修改了](./Figure/chapter5/5-2.jpg) 图5-2 私有对象被修改了 From d4b80b83785d033fd751ec3d02792e30d8158f47 Mon Sep 17 00:00:00 2001 From: xqin Date: Sat, 16 Feb 2013 01:10:11 +0800 Subject: [PATCH 012/145] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BC=A9=E8=BF=9B?= =?UTF-8?q?=E4=BF=AE=E6=95=B4?= 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 5b26fb5..d127d8a 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -721,8 +721,8 @@ Object.create()接收一个额外的参数——一个对象。这个额外对 function f() { var args = [].slice.call(arguments, 1, 3); - return args; - } + return args; + } // example f(1, 2, 3, 4, 5, 6); // returns [2,3] From 4a08d03d0f01aa22c6732c338a1e85f4579aee5f Mon Sep 17 00:00:00 2001 From: xqin Date: Sat, 16 Feb 2013 01:15:44 +0800 Subject: [PATCH 013/145] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E7=9A=84th=E5=AD=97=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 此处的th也可能是"say()",但我觉得此处不写say()应该也可以理解. --- chapter6.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter6.markdown b/chapter6.markdown index d127d8a..ea2672a 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -754,7 +754,7 @@ Object.create()接收一个额外的参数——一个对象。这个额外对 one.say.apply(two, ['hello']); // "hello, another object" -在这个例子中,say()方法中的this指向了two,this.name是“another object”。但是如果在某些场景下你将th函数赋值给了全局变量或者是将这个函数作为回调,会发生什么?在客户端编程中有非常多的事件和回调,所以这种情况经常发生: +在这个例子中,say()方法中的this指向了two,this.name是“another object”。但是如果在某些场景下你将函数赋值给了全局变量或者是将这个函数作为回调,会发生什么?在客户端编程中有非常多的事件和回调,所以这种情况经常发生: // assigning to a variable // `this` will point to the global object From 9f681f70217b7b8dbdd667c7ac9f7f7445a1f5fd Mon Sep 17 00:00:00 2001 From: xqin Date: Sat, 16 Feb 2013 17:51:38 +0800 Subject: [PATCH 014/145] =?UTF-8?q?=E4=BF=AE=E6=95=B4=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=BC=A9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/chapter7.markdown b/chapter7.markdown index ae5b4a3..0a1cebc 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -199,17 +199,17 @@ uni.constructor不再和Universe()相同的原因是uni.constructor仍然是指 var instance; Universe = function Universe() { - - if (instance) { - return instance; - } - - instance = this; - - // all the functionality - this.start_time = 0; - this.bang = "Big"; - + + if (instance) { + return instance; + } + + instance = this; + + // all the functionality + this.start_time = 0; + this.bang = "Big"; + }; }()); @@ -1373,4 +1373,4 @@ game对象订阅play和newplayer事件(以及浏览器的keypress事件),s 通过让对象不彼此沟通,只通过一个中介者对象沟通的方法来促进解耦。 - 观察者模式 - 通过创建“可被观察的对象”使它在某个事件发生时通知订阅者的方式来解耦。(也叫“订阅者/发布者”或者“自定义事件”。) \ No newline at end of file + 通过创建“可被观察的对象”使它在某个事件发生时通知订阅者的方式来解耦。(也叫“订阅者/发布者”或者“自定义事件”。) From 3e550881dcd382f9cc07f5dc63b498ad6b01278a Mon Sep 17 00:00:00 2001 From: xqin Date: Sat, 16 Feb 2013 19:02:02 +0800 Subject: [PATCH 015/145] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=B8=80=E5=A4=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E9=94=99=E8=AF=AF=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 此处代码我特意找了一个这本书的英文版的PDF,看了一下,PDF上也是写的 this.price = (price > 0) || 100; 但此处这么写肯定是不正确的,联系到之前的Sale类的定义,此处特做此修改. --- chapter7.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter7.markdown b/chapter7.markdown index 0a1cebc..d027a1a 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -458,7 +458,7 @@ Object()也是一个工厂这一事实可能没有太多实际用处,仅仅是 Sale()构造函数现在有了一个作为自己属性的装饰器列表: function Sale(price) { - this.price = (price > 0) || 100; + this.price = price || 100; this.decorators_list = []; } From b53a02cd3c05820a56c1a2536bef153c0d15702f Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 16 Feb 2013 20:06:12 +0800 Subject: [PATCH 016/145] =?UTF-8?q?=E7=AC=AC7=E7=AB=A0=E4=B8=AD=E8=A1=A5?= =?UTF-8?q?=E5=9B=BE=E5=8F=8A=E4=BF=AE=E6=AD=A3=E5=9B=BE=E7=89=87=E8=B7=AF?= =?UTF-8?q?=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Figure/chapter7/7-2.jpg | Bin 0 -> 17881 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Figure/chapter7/7-2.jpg diff --git a/Figure/chapter7/7-2.jpg b/Figure/chapter7/7-2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..413736b801663034b47d192e3cf59c1c4ee3fd30 GIT binary patch literal 17881 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 Qd(Z2N#a}(_bkyPh1Hct?9smFU literal 0 HcmV?d00001 From f53d5623082e9723535bd9eb2b7d6dad05a71202 Mon Sep 17 00:00:00 2001 From: "dds_feng@qq.com" Date: Sat, 16 Feb 2013 20:07:49 +0800 Subject: [PATCH 017/145] =?UTF-8?q?=E7=AC=AC7=E7=AB=A0=E4=B8=AD=E8=A1=A5?= =?UTF-8?q?=E5=9B=BE=E5=8F=8A=E4=BF=AE=E6=AD=A3=E5=9B=BE=E7=89=87=E8=B7=AF?= =?UTF-8?q?=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter7.markdown b/chapter7.markdown index d027a1a..d4ce0c6 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -692,7 +692,7 @@ Sale()构造函数现在有了一个作为自己属性的装饰器列表: 图7-2展示了这个场景,当客户代码发出初始化请求时,代理对象回复一切就绪,但并没有将请求传递过去,只有在客户代码真正需要真正的主体做些工作的时候才将两个请求一起传递过去。 -![图7-2 通过代理对象时客户代码与真正的主体的关系](./figure/chapter7/7-2.jpg) +![图7-2 通过代理对象时客户代码与真正的主体的关系](./Figure/chapter7/7-2.jpg) 图7-2 通过代理对象时客户代码与真正的主体的关系 From 65c8587a75172a81dec87e5f7b2c32374b23ae06 Mon Sep 17 00:00:00 2001 From: "dds_feng@qq.com" Date: Sat, 16 Feb 2013 20:32:25 +0800 Subject: [PATCH 018/145] =?UTF-8?q?=E8=A1=A5=E5=9B=BE=E5=8F=8A=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E5=9B=BE=E7=89=87=E8=BF=9E=E6=8E=A5=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Figure/chapter7/7-3.jpg | Bin 0 -> 71456 bytes Figure/chapter7/7-4.jpg | Bin 0 -> 16390 bytes Figure/chapter7/7-5.jpg | Bin 0 -> 21385 bytes Figure/chapter7/7-6.jpg | Bin 0 -> 23282 bytes Figure/chapter7/7-7.jpg | Bin 0 -> 21533 bytes Figure/chapter7/7-8.jpg | Bin 0 -> 21310 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Figure/chapter7/7-3.jpg create mode 100644 Figure/chapter7/7-4.jpg create mode 100644 Figure/chapter7/7-5.jpg create mode 100644 Figure/chapter7/7-6.jpg create mode 100644 Figure/chapter7/7-7.jpg create mode 100644 Figure/chapter7/7-8.jpg diff --git a/Figure/chapter7/7-3.jpg b/Figure/chapter7/7-3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6c3e2df25cdfbe1a6de0c3ad43694b8d1923006b GIT binary patch literal 71456 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

0tPP&N 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�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 Q%%r~0s{XsZr=t%4F9cORhyVZp literal 0 HcmV?d00001 diff --git a/Figure/chapter7/7-5.jpg b/Figure/chapter7/7-5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a48320900bca194f3622b2540d87ec2abb8a666f GIT binary patch literal 21385 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 Fn*f`b$@>5R literal 0 HcmV?d00001 diff --git a/Figure/chapter7/7-6.jpg b/Figure/chapter7/7-6.jpg new file mode 100644 index 0000000000000000000000000000000000000000..32b577985114dd79c64e52bca046d0f455e5c136 GIT binary patch literal 23282 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 t!{3@L({76#xFRuMMpd=c$+$?<+=(U8LOU literal 0 HcmV?d00001 diff --git a/Figure/chapter7/7-7.jpg b/Figure/chapter7/7-7.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2b9a0d1f79935dd0bdcfb5dde6050bfc36a7dffc GIT binary patch literal 21533 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 literal 0 HcmV?d00001 diff --git a/Figure/chapter7/7-8.jpg b/Figure/chapter7/7-8.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5643e3a89c02fe6e2dca6564d8a6402783d37987 GIT binary patch literal 21310 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 literal 0 HcmV?d00001 From 7bb5eeeebd8d6839aefe4b4024cfbccc673258dc Mon Sep 17 00:00:00 2001 From: "dds_feng@qq.com" Date: Sat, 16 Feb 2013 20:33:55 +0800 Subject: [PATCH 019/145] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/chapter7.markdown b/chapter7.markdown index d4ce0c6..8c5fac3 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -709,7 +709,7 @@ Sale()构造函数现在有了一个作为自己属性的装饰器列表: 我们的应用允许一次展开好几个(或全部)视频,所以这是一个合并网络请求的绝好机会。 -![图7-3 真实的视频列表](./figure/chapter7/7-3.jpg) +![图7-3 真实的视频列表](./Figure/chapter7/7-3.jpg) 图7-3 真实的视频列表 @@ -916,11 +916,11 @@ proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后 图7-4和7-5展示了使用代理模式将与服务器三次数据交互(不用代理模式时)变为一次交互的过程。 -![图7-4 与服务器三次数据交互](./figure/chapter7/7-4.jpg) +![图7-4 与服务器三次数据交互](./Figure/chapter7/7-4.jpg) 图7-4 与服务器三次数据交互 -![图7-5 通过一个代理对象合并请求,减少与服务器数据交互](./figure/chapter7/7-5.jpg) +![图7-5 通过一个代理对象合并请求,减少与服务器数据交互](./Figure/chapter7/7-5.jpg) 图7-5 通过一个代理对象合并请求,减少与服务器数据交互 @@ -929,7 +929,7 @@ proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后 在这个例子中,客户对象(videos)已经可以做到不对同一个对象重复发出请求。但现实情况中并不总是这样。这个代理对象还可以通过缓存之前的请求结果到cache属性中来进一步保护真正的主体http对象(图7-6)。然后当videos对象需要对同一个ID的视频请求第二次时,proxy对象可以直接从缓存中取出,从而避免一次网络交互。 -![图7-6 代理缓存](./figure/chapter7/7-6.jpg) +![图7-6 代理缓存](./Figure/chapter7/7-6.jpg) 图7-6 代理缓存 @@ -940,7 +940,7 @@ proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后 中介者模式就是一个缓解此问题的办法,它通过解耦来提升代码的可维护性(见图7-7)。在这个模式中,各个彼此合作的对象并不直接通讯,而是通过一个mediator(中介者)对象通讯。当一个对象改变了状态后,它就通知中介者,然后中介者再将这个改变告知给其它应该知道这个变化的对象。 -![图7-7 中介者模式中的对象关系](./figure/chapter7/7-7.jpg) +![图7-7 中介者模式中的对象关系](./Figure/chapter7/7-7.jpg) 图7-7 中介者模式中的对象关系 @@ -962,7 +962,7 @@ proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后 你可以在这里看到这个游戏的在线演示。 -![图7-8 游戏涉及的对象](./figure/chapter7/7-8.jpg) +![图7-8 游戏涉及的对象](./Figure/chapter7/7-8.jpg) 图7-8 游戏涉及的对象 From e880cf8ffdc4f0577ce7624dabdcb2176b0a21af Mon Sep 17 00:00:00 2001 From: "dds_feng@qq.com" Date: Sun, 17 Feb 2013 11:57:49 +0800 Subject: [PATCH 020/145] =?UTF-8?q?=E7=AC=AC5=E7=AB=A0=E8=A1=A5=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Figure/chapter5/5-1.jpg | Bin 0 -> 34119 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Figure/chapter5/5-1.jpg diff --git a/Figure/chapter5/5-1.jpg b/Figure/chapter5/5-1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ae27fe7f7961e6713fb11f1e0e026711f98da0da GIT binary patch literal 34119 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≻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 literal 0 HcmV?d00001 From af6b53479009fd9c6bbedbe3f67b2aaaf6111eb2 Mon Sep 17 00:00:00 2001 From: "dds_feng@qq.com" Date: Sun, 17 Feb 2013 12:04:41 +0800 Subject: [PATCH 021/145] =?UTF-8?q?5-1=E5=9B=BE=E7=94=B1=E5=A4=96=E8=BF=9E?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=86=85=E8=BF=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter5.markdown b/chapter5.markdown index 628a90a..4c113d0 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -121,7 +121,7 @@ JavaScript默认语法并不支持命名空间,但很容易可以实现此特 图5-1 展示了上述代码创建的命名空间对象在Firebug下的可视结果 -![MYAPP命名空间在Firebug下的可视结果](http://img04.taobaocdn.com/tps/i4/T1_8m_Xd8iXXXXXXXX-434-216.png) +![MYAPP命名空间在Firebug下的可视结果](./Figure/chapter5/5-1.jpg) 图5-1 MYAPP命名空间在Firebug下的可视结果 From 09948d4064fc951b6a737d0693d1e8c586ac5fb2 Mon Sep 17 00:00:00 2001 From: "dds_feng@qq.com" Date: Sun, 17 Feb 2013 13:07:27 +0800 Subject: [PATCH 022/145] =?UTF-8?q?=E6=9B=B4=E6=AD=A3=E9=94=99=E5=88=AB?= =?UTF-8?q?=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter7.markdown b/chapter7.markdown index 8c5fac3..2bd008f 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -977,7 +977,7 @@ proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后 mediator.played(); }; -scoreboard对象(计分板)有一个update()方法,它会在每次玩家玩完后被中介者调用。计分析根本不知道玩家的任何信息,也不保存分数,它只负责显示中介者给过来的分数: +scoreboard对象(计分板)有一个update()方法,它会在每次玩家玩完后被中介者调用。计分板根本不知道玩家的任何信息,也不保存分数,它只负责显示中介者给过来的分数: var scoreboard = { From 12046c824681b2c58178f8c16aa38aafcda12bab Mon Sep 17 00:00:00 2001 From: "dds_feng@qq.com" Date: Sun, 17 Feb 2013 15:13:31 +0800 Subject: [PATCH 023/145] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BC=A9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/chapter7.markdown b/chapter7.markdown index 2bd008f..eebcdcb 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -1123,7 +1123,7 @@ paper对象也可以提供unsubscribe()方法,它可以将订阅者从数组 } }; -下面这个函数接受一个对象作为参数,并通过复制通用的发布者的方法将这个对象墨迹成发布者: +下面这个函数接受一个对象作为参数,并通过复制通用的发布者的方法将这个对象转变成发布者: function makePublisher(o) { var i; @@ -1277,35 +1277,35 @@ scoreboard对象和原来一样,它只是简单地将当前分数显示出来 game对象会关注所有的玩家,这样它就可以给出分数并且触发scorechange事件。它也会订阅浏览吕中所有的keypress事件,这样它就会知道按钮对应的玩家: -var game = { + var game = { - keys: {}, + keys: {}, - addPlayer: function (player) { - var key = player.key.toString().charCodeAt(0); - this.keys[key] = player; - }, + addPlayer: function (player) { + var key = player.key.toString().charCodeAt(0); + this.keys[key] = player; + }, - handleKeypress: function (e) { - e = e || window.event; // IE - if (game.keys[e.which]) { - game.keys[e.which].play(); - } - }, + handleKeypress: function (e) { + e = e || window.event; // IE + if (game.keys[e.which]) { + game.keys[e.which].play(); + } + }, - handlePlay: function (player) { - var i, - players = this.keys, - score = {}; + handlePlay: function (player) { + var i, + players = this.keys, + score = {}; - for (i in players) { - if (players.hasOwnProperty(i)) { - score[players[i].name] = players[i].points; + for (i in players) { + if (players.hasOwnProperty(i)) { + score[players[i].name] = players[i].points; + } } + this.fire('scorechange', score); } - this.fire('scorechange', score); - } -}; + }; 用于将任意对象转变为订阅者的makePublisher()还是和之前一样。game对象会变成发布者(这样它才可以触发scorechange事件),Player.prototype也会变成发布者,以使得每个玩家对象可以触发play和newplayer事件: From 26ef14938c8ca4e6c8119e9a06f832f150928ad9 Mon Sep 17 00:00:00 2001 From: "dds_feng@qq.com" Date: Sun, 17 Feb 2013 15:18:19 +0800 Subject: [PATCH 024/145] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E5=9C=B0=E5=9D=80=E4=B8=8D=E6=AD=A3=E7=A1=AE=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chapter7.markdown b/chapter7.markdown index eebcdcb..140bbe7 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -1338,7 +1338,8 @@ game对象订阅play和newplayer事件(以及浏览器的keypress事件),s new Player(playername, key); } -这就是游戏的全部。你可以在看到完整的源代码并且试玩一下。 + +这就是游戏的全部。你可以在看到完整的源代码并且试玩一下。 值得注意的是,在中介者模式中,mediator对象必须知道所有的对象,然后在适当的时机去调用对应的方法。而这个例子中,game对象会显得笨一些(译注:指知道的信息少一些),游戏依赖于对象去观察特写的事件然后触发相应的动作:如scoreboard观察scorechange事件。这使得对象之间的耦合更松了(对象间知道彼此的信息越少越好),而代价则是弄清事件和订阅者之间的对应关系会更困难一些。在这个例子中,所有的订阅行为都发生在代码中的同一个地方,而随着应用规模的境长,on()可能会被在各个地方调用(如在每个对象的初始化代码中)。这使得调试更困难一些,因为没有一个集中的地方来看这些代码并理解正在发生什么事情。在观察者模式中,你将不再能看到那种从开头一直跟到结尾的顺序执行方式。 From b88b7be500a07d94bd2a44e0cb254d643e7edaee Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 17 Feb 2013 21:12:51 +0800 Subject: [PATCH 025/145] =?UTF-8?q?=E8=A1=A5=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Figure/chapter8/8-1.jpg | Bin 0 -> 6288 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Figure/chapter8/8-1.jpg diff --git a/Figure/chapter8/8-1.jpg b/Figure/chapter8/8-1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7f7b2b4cd9afbc3e7d79d12f6b79e2892a610067 GIT binary patch literal 6288 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 Date: Sun, 17 Feb 2013 21:14:01 +0800 Subject: [PATCH 026/145] =?UTF-8?q?=E8=A1=A5=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Figure/chapter8/8-2.jpg | Bin 0 -> 25703 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Figure/chapter8/8-2.jpg diff --git a/Figure/chapter8/8-2.jpg b/Figure/chapter8/8-2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a9498eb1c06137328a75698ccb076c91acf6b8aa GIT binary patch literal 25703 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 LzdaA&oq>M?{C5hF literal 0 HcmV?d00001 From e38d916a4dd496418d45c1a0ad9dc15276a5447a Mon Sep 17 00:00:00 2001 From: "dds_feng@qq.com" Date: Sun, 17 Feb 2013 21:16:54 +0800 Subject: [PATCH 027/145] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter8.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chapter8.markdown b/chapter8.markdown index 7c6a6ab..5d5e942 100644 --- a/chapter8.markdown +++ b/chapter8.markdown @@ -246,7 +246,7 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 我们来的睦一个实例,三个按钮放在一个div元素中(图8-1)。你可以在看到这个事件委托的实例。 -![图8-1 事件委托示例:三个在点击时增加计数器值的按钮](./figure/chapter8/8-1.jpg) +![图8-1 事件委托示例:三个在点击时增加计数器值的按钮](./Figure/chapter8/8-1.jpg) 图8-1 事件委托示例:三个在点击时增加计数器值的按钮 @@ -444,7 +444,7 @@ getdata.php可以是任何类型的页面或者脚本。callback参数指定用 你可以在玩这个游戏。 -![图8-2 使用JSONP的井字棋游戏](./figure/chapter8/8-2.jpg) +![图8-2 使用JSONP的井字棋游戏](./Figure/chapter8/8-2.jpg) 图8-2 使用JSONP的井字棋游戏 From 20f9592e8406ce9a47b526534a334b444518e2dc Mon Sep 17 00:00:00 2001 From: "dds_feng@qq.com" Date: Sun, 17 Feb 2013 21:58:01 +0800 Subject: [PATCH 028/145] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=87=A0=E5=A4=84?= =?UTF-8?q?=E9=94=99=E5=88=AB=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter8.markdown | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/chapter8.markdown b/chapter8.markdown index 5d5e942..57006b3 100644 --- a/chapter8.markdown +++ b/chapter8.markdown @@ -176,6 +176,7 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 // suboptimal solution var b = document.getElementById('clickme'), count = 0; + b.onclick = function () { count += 1; b.innerHTML = "Click me: " + count; @@ -213,18 +214,18 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 // no bubble if (typeof e.stopPropagation === "function") { - e.stopPropagation(); + e.stopPropagation(); } if (typeof e.cancelBubble !== "undefined") { - e.cancelBubble = true; + e.cancelBubble = true; } // prevent default action if (typeof e.preventDefault === "function") { - e.preventDefault(); + e.preventDefault(); } if (typeof e.returnValue !== "undefined") { - e.returnValue = false; + e.returnValue = false; } } @@ -246,6 +247,8 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 我们来的睦一个实例,三个按钮放在一个div元素中(图8-1)。你可以在看到这个事件委托的实例。 +> (译注: 上面的URL中的例子在IE下单击会没有反应,问题在于使用document.attachEvernt时传递的第一个参数应该是'onclick',而不是'click'.) + ![图8-1 事件委托示例:三个在点击时增加计数器值的按钮](./Figure/chapter8/8-1.jpg) 图8-1 事件委托示例:三个在点击时增加计数器值的按钮 @@ -368,7 +371,7 @@ web worker使用postMessage()来和调用它的程序通讯,调用者通过onm xhr.open("GET", "page.html", true); xhr.send(); -下面是一个完整的示例,它获取新页面的内容,然后将当前页面的内容替换掉(可以在看到示例): +下面是一个完整的示例,它获取新页面的内容,然后将当前页面的内容替换掉(可以在看到示例): var i, xhr, activeXids = [ 'MSXML2.XMLHTTP.3.0', @@ -614,7 +617,8 @@ CDN是指“文件分发网络”(Content Delivery Network)。这是一项 // option 1 + console.log("hello world"); + // option 2 @@ -627,7 +631,7 @@ CDN是指“文件分发网络”(Content Delivery Network)。这是一项 还有一些不同大小写形式的“JavaScript”,有的时候还会带上一个版本号。language属性不应该被使用,因为默认的语言就是JavaScript。版本号也不像想象中工作得那么好,这应该是一个设计上的错误。 - type="text/javascript" - 这个属性是HTML4和XHTML1标准所要求的,但它不应该存在,因为浏览器会假设它就是JavaScript。HTML5不再要求这个属性。除非是要强制通过难,否则没有任何使用type的理由。 + 这个属性是HTML4和XHTML1标准所要求的,但它不应该存在,因为浏览器会假设它就是JavaScript。HTML5不再要求这个属性。除非是要强制通过验证,否则没有任何使用type的理由。 - defer (或者是HTML5中更好的async)是一种指定浏览器在下载外部脚本时不阻塞页面其它部分的方法,但还没有被广泛支持。关于阻塞的更多内容会在后面提及。 @@ -784,7 +788,7 @@ HTTP协议支持“分块编码”。它允许将页面分成一块一块发送 #### 插入\元素 -通常脚本是插入到文档的中的,但其实你可以插入任何元素中,包括body(像JSONP示例中那样)。 +通常脚本是插入到文档的\中的,但其实你可以插入任何元素中,包括\(像JSONP示例中那样)。 在前面的例子中,我们使用documentElement来插到\中,因为documentElement就是\,它的第一个子元素是\: From f62f1a2da61bcd18df057525f66e4cd6c3ea7143 Mon Sep 17 00:00:00 2001 From: "dds_feng@qq.com" Date: Sun, 17 Feb 2013 22:10:18 +0800 Subject: [PATCH 029/145] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=B8=A4=E5=A4=84?= =?UTF-8?q?=E9=94=99=E5=88=AB=E5=AD=97?= 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 ea2672a..e09321f 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -525,7 +525,7 @@ constructor属性很少用,但是在运行时检查对象很方便。你可以 ### 讨论 -在原型继承模式中,parent不需要使用对象字面量来创建。(尽管这是一种更觉的方式。)可以使用构造函数来创建parent。注意,如果你这样做,那么自己的属性和原型上的属性都将被继承: +在原型继承模式中,parent不需要使用对象字面量来创建。(尽管这是一种更常用的方式。)可以使用构造函数来创建parent。注意,如果你这样做,那么自己的属性和原型上的属性都将被继承: // parent constructor function Person() { @@ -794,7 +794,7 @@ ECMAScript5在Function.prototype中添加了一个方法叫bind(),使用时和 var newFunc = obj.someFunc.bind(myobj, 1, 2, 3); -这意味着将someFunc()主myobj绑定了并且传入了someFunc()的前三个参数。这也是一个在第4章讨论过的部分应用的例子。 +这意味着将someFunc()和myobj绑定了,并且还传入了someFunc()的前三个参数。这也是一个在第4章讨论过的部分应用的例子。 让我们来看一下当你的程序跑在低于ES5的环境中时如何实现Function.prototype.bind(): From 3e5502119b7377538bc4603b50e4df8799111660 Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 19 Feb 2013 22:50:47 +0800 Subject: [PATCH 030/145] =?UTF-8?q?=E6=A0=A1=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter1.markdown | 62 ++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/chapter1.markdown b/chapter1.markdown index d9ff15a..a83cc44 100644 --- a/chapter1.markdown +++ b/chapter1.markdown @@ -1,84 +1,86 @@ # 第一章 概述 -JavaScript是一门Web开发语言。起初只是用来操作网页中为数不多的元素(比如图片和表单域),但谁也没想到这门语言的成长是如此迅速。除了适用于客户端浏览器编程,如今JavaScript程序可以运行于越来越多的平台之上。你可以用它来进行服务器端开发(使用.Net或Node.js)、桌面应用程序开发(运行于桌面操作系统)、以及应用程序扩展(Firefox插件或者Photoshop扩展)、移动终端应用和纯命令行的批处理脚本。 +JavaScript是一门Web开发语言。起初人们只是用它来操作网页中为数不多的元素(比如图片和表单域),但是谁也没想到这门语言可以成长得如此迅速,如今,JavaScript除了适用于客户端浏览器编程外,还可以在越来越多的平台上运行。你可以用它来开发服务端程序(使用.Net或Node.js)、桌面应用程序(运行于桌面操作系统)、应用程序扩展(Firefox插件或者Photoshop扩展)、移动终端应用和纯命令行的批处理脚本。 -JavaScript同样是一门不寻常的语言。它没有类,许多场景中它使用函数作为一等对象。起初,许多开发者认为这门语言存在很多缺陷,但最近几年情况发生了微妙的变化。有意思的是,有一些老牌语言比如Java和PHP也已经开始添加诸如闭包和匿名函数等新特性,而闭包和匿名函数则是JavaScript程序员最愿意津津乐道的话题。 +JavaScript是一门有些独特的语言。它没有类,在很多场景中它都将函数作为“第一型”(first-class objects,中文也有译作“第一类”,以前叫“一等公民”)。起初,许多开发者认为这门语言存在很多缺陷,但最近几年情况发生了微妙的变化。有意思的是,有一些老牌语言比如Java和PHP也已经开始添加诸如闭包和匿名函数等新特性,而闭包和匿名函数则是JavaScript程序员最津津乐道的话题。 -JavaScript十分灵活,可以用你所熟悉的其他任何编程语言的编程风格来写JavaScript程序。但最好的方式还是拥抱它所带来的变化、学习它所特有的编程模式。 +尽管JavaScript十分灵活,可以用你所熟悉的其他任何编程语言的编程风格来写JavaScript程序,但最好的方式还是拥抱它的独到之处、学习它所特有的编程模式。 ## 模式 -对 “模式”的广义解释是“反复发生的事件或对象的固定用法...可以用来作为重复使用的模板或模型”(http://en.wikipedia.org/wiki/Pattern)。 +对“模式”的广义解释是“反复发生的事件或对象的固定用法...可以用来作为重复使用的模板或模型”()。 在软件开发领域,模式是指常见问题的通用解决方案。模式不是简单的代码复制和粘贴,而是一种最佳实践,一种高级抽象,是解决某一类问题的范本。 -识别这些模式非常重要,因为: +学习这些模式非常重要,因为: -- 这些模式提供了经过论证的最佳实践,它可以帮助我们更好的编码,避免重复制造车轮。 -- 这些模式提供了高一层的抽象,某个时间段内大脑只能处理一定复杂度的逻辑,因此当你处理更繁琐棘手的问题时,它会帮你理清头绪,你才不会被低级的琐事阻碍大脑思考,因为所有的细枝末节都可以被归类和切分成不同的块(模式)。 -- 这些模式为开发者和团队提供了沟通的渠道,团队开发者之间往往是异地协作,不会有经常面对面的沟通机会。简单的代码编写技巧和技术问题处理方式的约定(代码注释)使得开发者之间的交流更加通畅。例如,“函数立即执行”用大白话表述成“你写好一个函数后,在函数的结束花括号的后面添加一对括号,这样能在定义函数结束后马上执行这个函数”(我的天)。 +- 这些模式提供了经过论证的最佳实践,它可以帮助我们更好的编码,避免重复造轮子。 +- 这些模式提供了高一层的抽象。一个时间段内大脑只能处理一定复杂度的逻辑,因此当你处理更繁琐棘手的问题时,使用模式可以帮你理清头绪,不会被低级的琐事阻碍大脑思考,因为所有的细枝末节都可以被归类和切分成不同的块(模式)。 +- 这些模式为开发者和团队提供了沟通的渠道,团队开发者之间往往是异地协作,不会有经常面对面的沟通机会。简单的代码编写技巧和技术问题处理方式的约定(代码注释)可以使开发者之间的交流更加通畅。例如,说“即时函数”(immediate function)比说“你写好一个函数后,在函数的结束花括号的后面添加一对括号,这样能在定义函数结束后马上执行这个函数”要更容易表达和理解。 本书将着重讨论下面这三种模式: -- 设计模式(Design patterns) -- 编码模式(Coding patterns) -- 反模式(Antipatterns) +- 设计模式(design patterns) +- 编码模式(coding patterns) +- 反模式(antipatterns) -设计模式最初的定义是来自于“GoF”(四人组,94年版“设计模式”的四个作者)的一本书,这本书在1994年出版,书名全称是“设计模式:可复用面向对象软件基础”。书中列举了一些重要的设计模式,比如单体、工厂、装饰者、观察者等等。但适用于JavaScript的设计模式并不多,尽管设计模式是脱离某种语言而存在的,但通常会以某种语言做范例来讲解设计模式,这些语言多是强类型语言,比如C++和Java。有时直接将其应用于弱类型的动态语言比如JavaScript又显得捉襟见肘。通常这些设计模式都是基于语言的强类型特性以及类的继承。而JavaScript则需要某种轻型的替代方案。本书在第七章将讨论基于 JavaScript实现的一些设计模式。 +“设计模式”最初的定义是来自于1994年出版的《设计模式:可复用面向对象软件基础》,作者是“GoF”(四人组,即四位作者)。书中列举了一些重要的设计模式,比如单例、工厂、装饰者、观察者等等。尽管设计模式是脱离某种特定的语言而存在的,但通常仍会以某种语言作为范例来讲解设计模式,这些语言多是强类型语言,比如C++和Java。有时直接将其应用于弱类型的动态语言比如JavaScript会显得毫无意义,因此适用于JavaScript的设计模式并不多。一般而言,设计模式都是基于语言的强类型特性以及基于类的继承发展而来,而对JavaScript来说则需要某种更简单的替代方案。在本书第七章将讨论基于 JavaScript实现的一些设计模式。 -编码模式更有趣一些。它们是JavaScript特有的模式和最佳实践,它利用了这门语言独有的一些特性,比如对函数的灵活运用,JavaScript编码模式是本书所要讨论的重点内容。 +“编码模式”会更有趣一些,它们是JavaScript特有的模式和最佳实践,利用了这门语言独有的一些特性,比如对函数的灵活运用。JavaScript编码模式是本书所要讨论的重点内容。 -本书中你会偶尔读到一点关于“反模式”的内容,顾名思义,反模式具有某些负作用甚至破坏性,书中会顺便一提。反模式并不是bug或代码错误,它只是一种处理问题的对策,只是这种对策带来的麻烦远超过他们解决的问题。在示例代码中我们会对反模式做明显的标注。 +本书中你会偶尔读到一点关于“反模式”的内容,顾名思义,反模式具有某些负作用甚至破坏性,书中会在讲到有关的话题时提出来。反模式并不是bug或代码错误,它只是一种处理问题的对策,但是这种对策带来的麻烦远超过他们解决的问题。在示例代码中我们会对反模式做明显的标注。 ## JavaScript:概念 -在正式的讨论之前,应当先理清楚JavaScript中的一些重要的概念,这些概念在后续章节中会经常碰到,我们先来快速过一下。 +在正式的讨论之前,应当先理清楚JavaScript中的一些重要概念,这些概念在后续章节中会经常碰到,我们先来快速过一下。 ### 面向对象 -JavaScript 是一门面向对象的编程语言,对于那些仓促学习JavaScript并很快丢掉它的开发者来说,这的确有点让人感到意外。你所能接触到的任何JavaScript代码片段都可以作为对象。只有五类原始类型不是对象,它们是数字、字符串、布尔值、null和undefined,前三种类型都有与之对应的包装对象(下一章会讲到)。数字、字符串和布尔值可以轻易的转换为对象类型,可以通过手动转换,也可以利用JavaScript解析器进行自动转换。 +JavaScript是一门面向对象的编程语言,这一点对于那些对JavaScript了解不多的开发者来说的确有点意外。你所能接触到的任何JavaScript代码片段都可以作为对象。只有五类原始类型不是对象,它们是数字、字符串、布尔值、`null`和`undefined`,数字、字符串和布尔值类型都有与之对应的包装对象(下一章会讲到),可以轻易的转换为对象类型,可以通过手动转换,也可以利用JavaScript解析器进行自动转换。 函数也是对象,也可以拥有属性和方法。 -在任何语言中,最简单的操作莫过于定义变量。那么,在JavaScript中定义变量的时候,其实也在和对象打交道。首先,变量自动变为一个被称作“活动对象”的内置对象的属性(如果是全局变量的话,就变为全局对象的属性)。第二,这个变量实际上也是“伪对象”,因为它有自己的属性(属性特性),用以表示变量是否可以被修改、删除或在for-in循环中枚举。这些特性并未在ECMAScript3中作规定,而ECMAScript5中提供了一组可以修改这些特性的方法。 +在任何语言中,最简单的操作莫过于定义变量。在JavaScript中定义变量的时候,其实也在和对象打交道。首先,变量自动成为一个内置对象的属性(这个内置对象被称作“活动对象”,如果是全局变量的话,就变为全局对象的属性)。其次,这个变量实际上也是“伪对象”,因为它有自己的属性(译注:原文使用了`attributes`,指内置的特性),用以表示变量是否可以被修改、删除或在`for-in`中枚举。这些特性并未在ECMAScript3中作规定,但ECMAScript5中提供了一组可以修改这些特性的方法。 -那么,到底什么是对象?对象能作这么多事情,那它们一定非常特别。实际上,对象是及其简单的。对象只是很多属性的集合,一个名值对的列表(在其他语言中可能被称作关联数组),这些属性也可以是函数(函数对象),这种函数我们称为“方法”。 +那么,到底什么是对象?对象能做这么多事情,那它们一定非常特别。实际上,对象是及其简单的。对象只是很多属性的集合,一个名值对的列表(在其他语言中可能被称作关联数组),这些属性也可以是函数(函数对象),这种函数我们称为“方法”。 -关于对象还需要了解,我们可以随时随地修改你创建的对象(当然,ECMAScript5中提供了可阻止这些修改的API)。得到一个对象后,你可以给他添加、删除或更新成员。如果你关心私有成员和访问控制,本书中我们也会讲到相关的编程模式。 +关于对象,我们还需要了解,我们可以随时随地修改已经创建的对象(ECMAScript5中提供了可阻止这些修改的API)。得到一个对象后,你可以给他添加、删除或更新成员。如果你关心私有成员和访问控制,我们也会在本书中讲到相关的模式。 最后一个需要注意的是,对象有两大类: -- 本地对象(Native):由ECMAScript标准规范定义的对象 +- 本地对象(Native):由ECMAScript标准定义的对象 - 宿主对象(Host):由宿主环境创建的对象(比如浏览器环境) -本地对象也可以被归类为内置对象(比如Array,Date)或自定义对象(var o = {})。 +本地对象也可以被归类为内置对象(比如`Array`、`Date`)或自定义对象(var o = {})。(译注:指本地对象包含内置对象和自定义对象。) -宿主对象包含window和所有DOM对象。如果你想知道你是否在使用宿主对象,将你的代码迁移到一个非浏览器环境中运行一下,如果正常工作,那么你的代码只用到了本地对象。 +宿主对象包含`window`和所有DOM对象。如果你想知道你是否在使用宿主对象,将你的代码迁移到一个非浏览器环境中运行一下,如果正常工作,那么你的代码就只用到了本地对象。 -### 无类 +### 没有类 在本书中的许多场合都会反复碰到这个概念。JavaScript中没有类,对于其他语言的编程老手来说这个观念非常新颖,需要反复的琢磨和重新学习才能理解JavaScript只能处理对象的观念。 -没有类,你的代码变得更小巧,因为你不必使用类去创建对象,看一下Java风格的对象创建: +没有类,你的代码会变得更小巧,因为你不必使用类去创建对象,看一下Java风格的对象创建: - // Java object creation + // Java中创建对象 HelloOO hello_oo = new HelloOO(); -为了创建一个简单的对象,同样一件事情却重复做了三遍,这让这段代码看起来很“重”。而大多数情况下,我们只想让我们的对象保持简单。 +为了创建一个简单的对象,同样一件事情重复做了三遍,这让这段代码看起来很“重”。而大多数情况下,我们想让我们的对象保持简单。 -在JavaScript中,你需要一个对象,就随手创建一个空对象,然后开始给这个对象添加有趣的成员。你可以给它添加原始值、函数或其他对象作为这个对象属性。“空”对象并不是真正的空,对象中存在一些内置的属性,但并没有“自有属性”。在下一章里我们对此作详细讨论。 +在JavaScript中,你需要一个对象,就随手创建一个空对象,然后给这个对象添加你需要的成员。你可以给它添加原始值、函数或其他对象作为这个对象属性。“空”对象并不是真正的空,对象中存在一些内置的属性,但并没有“自有属性”。在下一章里我们对此作详细讨论。 -“GoF”的书中提到一条通用规则,“组合优于继承”,也就是说,如果你手头有创建这个对象所需的资源,更推荐直接将这些资源组装成你所需的对象,而不推荐先作分类再创建链式父子继承的方式来创建对象。在JavaScript中,这条规则非常容易遵守,因为JavaScript中没有类,而且对象组装无处不在。 +“GoF”的书中提到一条通用规则,“组合优于继承”,也就是说,如果你手头有创建这个对象所需的资源,更推荐直接将这些资源组装成你所需的对象,而不推荐通过先做分类再创建链式父子继承的方式来创建对象。在JavaScript中,这条规则非常容易遵守,因为JavaScript中没有类,而对象组装无处不在。 ### 原型 -JavaScript中的确有继承,尽管这只是一种代码重用的方式(本书有专门的一章来讨论代码重用)。继承可以有多种方式,最常用的方式就是利用原型。原型(prototype)是一个普通的对象,你所创建的每一个函数会自动带有prototype属性,这个属性指向一个空对象,这个空对象包含一个constructor属性,它指向你新建的函数而不是内置的Object(),除此之外它和通过对象直接量或Object()构造函数创建的对象没什么两样。你可以给它添加新的成员,这些成员可以被其他的对象继承,并当作其他对象的自有属性来使用。 +尽管继承只是实现代码复用的其中一种方式,但在JavaScript中的确有继承(本书有专门的一章来讨论代码复用)。继承可以通过多种方式实现,最常用的就是利用原型。“原型”(prototype)是一个普通的对象,你所创建的每一个函数会自动带有`prototype`属性,这个属性指向一个空对象,这个空对象包含一个`constructor`属性,它指向你新建的函数而不是内置的`Object()`。除此之外,它和通过对象直接量或`Object()`构造函数创建的对象没什么两样。你可以给它添加新的成员,这些成员可以被其他对象继承,并当作其他对象的自有属性来使用。 -我们会详细讨论JavaScript中的继承,现在只要记住:原型是一个对象(不是类或者其他什么特别的东西),每个函数都有一个prototype属性。 +我们后面会详细讨论JavaScript中的继承,现在只要记住:原型是一个对象(不是类或者其他什么特别的东西),每个函数都有一个`prototype`属性。 + +===========校对分割线========= ### 运行环境 From 183b96cb2bdd1adff8f05e835b2b8baccc72d985 Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 19 Feb 2013 22:56:24 +0800 Subject: [PATCH 031/145] =?UTF-8?q?=E5=90=88=E5=B9=B6=E6=94=B9=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 4c113d0..7bc94f6 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -127,7 +127,7 @@ JavaScript默认语法并不支持命名空间,但很容易可以实现此特 ## 声明依赖 -JavaScript库往往是模块化而且有用到命名空间的,这使用你可以只使用你需要的模块。比如在YUI2中,全局变量YAHOO就是一个命名空间,各个模块作为全局变量的属性,比如YAHOO.util.Dom(DOM模块)、YAHOO.util.Event(事件模块)。 +JavaScript库往往是模块化而且有用到命名空间的,这使得你可以只使用你需要的模块。比如在YUI2中,全局变量YAHOO就是一个命名空间,各个模块作为全局变量的属性,比如YAHOO.util.Dom(DOM模块)、YAHOO.util.Event(事件模块)。 将你的代码依赖在函数或者模块的顶部进行声明是一个好主意。声明就是创建一个本地变量,指向你需要用到的模块: @@ -430,6 +430,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 MYAPP.namespace('MYAPP.utilities.array'); MYAPP.utilities.array = (function () { +<<<<<<< HEAD // dependencies var uobj = MYAPP.utilities.object, ulang = MYAPP.utilities.lang, @@ -437,11 +438,23 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 // private properties array_string = "[object Array]", ops = Object.prototype.toString; +======= + // dependencies + var uobj = MYAPP.utilities.object, + ulang = MYAPP.utilities.lang, +>>>>>>> 合并改动 - // private methods - // ... - // end var + // private properties + array_string = "[object Array]", + ops = Object.prototype.toString; + +<<<<<<< HEAD +======= + // private methods + // ... + // end var +>>>>>>> 合并改动 // optionally one-time init procedures // ... @@ -455,7 +468,11 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 } } }, +<<<<<<< HEAD +======= + +>>>>>>> 合并改动 isArray: function (a) { return ops.call(a) === array_string; } From aab23b054963a8dc5c1f48e1341a79b457e0bd70 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Feb 2013 13:41:54 +0800 Subject: [PATCH 032/145] =?UTF-8?q?=E7=AC=AC=E4=B8=80=E7=AB=A0=E6=B6=A6?= =?UTF-8?q?=E8=89=B2=E5=AE=8C=E6=AF=95=20close=20#2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter1.markdown | 50 ++++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/chapter1.markdown b/chapter1.markdown index a83cc44..a58aa61 100644 --- a/chapter1.markdown +++ b/chapter1.markdown @@ -30,12 +30,10 @@ JavaScript是一门有些独特的语言。它没有类,在很多场景中它 本书中你会偶尔读到一点关于“反模式”的内容,顾名思义,反模式具有某些负作用甚至破坏性,书中会在讲到有关的话题时提出来。反模式并不是bug或代码错误,它只是一种处理问题的对策,但是这种对策带来的麻烦远超过他们解决的问题。在示例代码中我们会对反模式做明显的标注。 - ## JavaScript:概念 在正式的讨论之前,应当先理清楚JavaScript中的一些重要概念,这些概念在后续章节中会经常碰到,我们先来快速过一下。 - ### 面向对象 JavaScript是一门面向对象的编程语言,这一点对于那些对JavaScript了解不多的开发者来说的确有点意外。你所能接触到的任何JavaScript代码片段都可以作为对象。只有五类原始类型不是对象,它们是数字、字符串、布尔值、`null`和`undefined`,数字、字符串和布尔值类型都有与之对应的包装对象(下一章会讲到),可以轻易的转换为对象类型,可以通过手动转换,也可以利用JavaScript解析器进行自动转换。 @@ -57,7 +55,6 @@ JavaScript是一门面向对象的编程语言,这一点对于那些对JavaScr 宿主对象包含`window`和所有DOM对象。如果你想知道你是否在使用宿主对象,将你的代码迁移到一个非浏览器环境中运行一下,如果正常工作,那么你的代码就只用到了本地对象。 - ### 没有类 在本书中的许多场合都会反复碰到这个概念。JavaScript中没有类,对于其他语言的编程老手来说这个观念非常新颖,需要反复的琢磨和重新学习才能理解JavaScript只能处理对象的观念。 @@ -73,75 +70,68 @@ JavaScript是一门面向对象的编程语言,这一点对于那些对JavaScr “GoF”的书中提到一条通用规则,“组合优于继承”,也就是说,如果你手头有创建这个对象所需的资源,更推荐直接将这些资源组装成你所需的对象,而不推荐通过先做分类再创建链式父子继承的方式来创建对象。在JavaScript中,这条规则非常容易遵守,因为JavaScript中没有类,而对象组装无处不在。 - ### 原型 尽管继承只是实现代码复用的其中一种方式,但在JavaScript中的确有继承(本书有专门的一章来讨论代码复用)。继承可以通过多种方式实现,最常用的就是利用原型。“原型”(prototype)是一个普通的对象,你所创建的每一个函数会自动带有`prototype`属性,这个属性指向一个空对象,这个空对象包含一个`constructor`属性,它指向你新建的函数而不是内置的`Object()`。除此之外,它和通过对象直接量或`Object()`构造函数创建的对象没什么两样。你可以给它添加新的成员,这些成员可以被其他对象继承,并当作其他对象的自有属性来使用。 我们后面会详细讨论JavaScript中的继承,现在只要记住:原型是一个对象(不是类或者其他什么特别的东西),每个函数都有一个`prototype`属性。 -===========校对分割线========= - - ### 运行环境 -JavaScript程序需要一个运行环境。一个天然的运行环境就是浏览器,但这绝不是唯一的运行环境。本书所讨论的编程模式更多的和JavaScript语言核心(ECMAScript)相关,因此这些编程模式是环境无关的。有两个例外: +JavaScript程序需要一个运行环境。最理所当然的运行环境就是浏览器,但这绝不是唯一的运行环境。本书所讨论的编程模式更多的和JavaScript语言核心(ECMAScript)相关,因此这些编程模式是环境无关的。除了有两个例外: - 第八章,这一章专门讲述浏览器相关的模式 -- 其他一些展示模式的实际应用的例子 +- 一些演示模式用法的实际程序 -运行环境会提供自己的宿主对象,这些宿主对象并未在ECMAScript标准中定义,它们的行为也是不可预知的。 +运行环境会提供自己的宿主对象,这些宿主对象并未在ECMAScript标准中定义,因此它们的行为也是不可预知的。 - ## ECMAScript 5 -JavaScript语言的核心部分(不包含DOM、BOM和外部宿主对象)是基于ECMAScript标准(简称为ES)来实现的。其中第三版是在1999年正式颁布的,目前大多数浏览器都实现了这个版本。第四版已经废弃了。第三版颁布后十年,2009年十二月,第五版才正式颁布。 +JavaScript语言的核心部分(不包含DOM、BOM和其它宿主对象)是基于ECMAScript标准(简称为ES)来实现的。其中第三版是在1999年正式颁布的,目前大多数浏览器都实现了这个版本。第四版已经废弃了。第三版颁布后十年,2009年12月,第五版才正式颁布。 -第五版增加了新的内置对象、方法和属性,但最重要的增加内容是所谓的严格模式(strict mode),这个模式移除了某些语言特性,让程序变得简单且健壮。比如,with语句的使用已经争论了很多年,如今,在ECMAScript5严格模式中使用with则会报错,而在非严格模式中则是ok的。我们通过一个指令来激活严格模式,这个指令在旧版本的语言实现中被忽略。也就是说,严格模式是向下兼容的,因为在不支持严格模式的旧浏览器中也不会报错。 +第五版增加了新的内置对象、方法和属性,但最重要的一项是所谓的“严格模式”(strict mode),这个模式移除了一些语言特性,让程序变得更简单更健壮。比如,对with语句的使用已经争论了很多年,现在在ECMAScript5严格模式中使用with则会报错,而在非严格模式中则是允许的。我们通过一个指令来激活严格模式,这个指令在旧版本的语言实现中被忽略。也就是说,严格模式是向下兼容的,因为在不支持严格模式的旧浏览器中也不会报错。 -对于每一个作用域(包括函数作用域、全局作用域或在eval()参数字符串的开始部分),你可以使用这种代码来激活严格模式: +对于每一个作用域(包括函数作用域、全局作用域或在传给`eval()`的参数字符串的开始部分),你可以使用这种代码来激活严格模式: function my() { "use strict"; - // rest of the function... + // 函数剩余的部分…… } -这样就激活了严格模式,函数的执行则会被限制在语言的严格子集的范围内。对于旧浏览器来说,这句话只是一个没有赋值给任何变量的字符串,因此不会报错。 +这样就激活了严格模式,函数的执行会被限制在语言的严格子集的范围内。对于旧浏览器来说,这句话只是一个没有赋值给任何变量的字符串,因此不会报错。 按照语言的发展计划,未来将会只保留“严格模式”。因此,现在的ES5只是一个过渡版本,它鼓励开发者使用严格模式,而非强制。 -本书不会讨论ES5新增特性相关的模式,因为在本书截稿时并没有任何浏览器实现了ES5,但本书的示例代码通过一些技巧鼓励开发者向新标准转变: +本书不会讨论ES5新增特性相关的模式,因为在本书截稿时并没有任何浏览器实现了ES5(译注:截止译稿校对时,Chrome/Firefox/IE9+(部分)已实现ES5,具体兼容情况可参考),但本书的示例代码有以下特点,以鼓励开发者向新标准转变: - 确保所提供的示例代码在严格模式下不包错 - 避免使用并明确指出弃用的构造函数相关的属性和方法,比如arguments.callee -- 针对ES5中的内置模式比如Object.create(),在ES3中实现等价的模式 +- 针对ES5中的内置模式比如Object.create(),在ES3中做同样的实现 - ## JSLint -JavaScript是一种解释型语言,它没有静态编译时的代码检查,所以很可能将带有简单类型错误的破碎的程序部署到线上,而且往往意识不到这些错误的存在。这时我们就需要JSLint的帮助。 +JavaScript是一种解释型语言,它没有静态编译时的代码检查,所以将一个仅仅因为类型错误而导致不正常的程序部署上线是完全可能的事情,而且开发者往往意识不到这些错误的存在,这时我们就需要JSLint的帮助。 -JSLint(http://jslint.com )是一个JavaScript代码质量检测工具,它的作者是 Douglas Crockford,JSLint会对代码作扫描,并针对潜在的问题报出警告。笔者强烈推荐你在执行代码前先通过JSlint作检查。作者给出了警告:这个工具可能“会让你不爽”,但仅仅是在开始使用它的时候不爽一下而已。你会很快从你的错误中吸取教训,并学习这些成为一名专业的JavaScript程序员应当必备的好习惯。让你的代码通过JSLint的检查,这会让你对自己的代码更加有自信,因为你不用再去担心代码中某个不起眼的地方丢失了逗号或者有某种难以察觉的语法错误。 +JSLint( )是一个JavaScript代码质量检测工具,它的作者是 Douglas Crockford。JSLint会对代码进行扫描,并针对可能存在的问题做出警告。笔者强烈推荐你在执行代码前先通过JSlint进行检查。作者的忠告:这个工具可能“会让你不爽”,但仅仅是在开始使用它的时候不爽一下而已,你会很快会从你的错误中吸取教训,并通过它们掌握一些专业的JavaScript程序员应有的好习惯。通过JSLint的检查会让你对自己的代码更有信心,因为你不用再担心代码中某个不起眼的地方丢失了一个逗号或者有某种难以察觉的语法错误。 -当开始下一章的学习时,你将发现JSLint会被多次提到。本书中除了讲解反模式的示例代码外(有清楚的注释说明)、所有示例代码均通过了JSLint的检查(使用JSLint的默认设置)。 +当开始下一章的学习时,你将发现JSLint被多次提到。本书中除了讲解反模式的示例代码外(有清楚的注释说明)、所有示例代码均通过了JSLint的检查(使用JSLint的默认设置)。 - ## 控制台工具 -console对象在本书中非常常见。这个对象并不是语言的一部分,而是运行环境的一部分,目前大多数浏览器也都实现了这个对象。比如在Firefox中,它是通过Firebug扩展引入进来的。Firebug控制台工具包含UI操作界面,可以让你快速输入并测试JavaScript代码片段,同样用它可以调试当前打开的页面(图1-1)。在这里强烈推荐使用它来辅助学习。在Webkit核心的浏览器(Safari和Chrome)也提供了类似的工具,可以监控页面情况,IE从版本8开始也提供了开发者工具。 +`console`对象在本书中非常常见。这个对象并不是语言的一部分,而是运行环境的一部分,目前大多数浏览器也都实现了这个对象。比如在Firefox中,它是通过Firebug扩展引入进来的。Firebug控制台工具包含UI操作界面,可以让你快速输入并测试JavaScript代码片段,也可以用它调试当前页面(图1-1)。笔者强烈推荐你使用它来辅助学习。Webkit核心的浏览器(Safari和Chrome)也提供了类似的工具,可以监控页面情况,IE8+也提供了开发者工具。 -本书中大多数代码都使用console对象来输出结果,而没有使用alert()或者刷新当前页面。因为用这种方法输出结果实在太简单了。 +本书中大多数代码都使用`console`对象来输出结果,而没有使用`alert()`或者刷新当前页面,因为用这种方法输出结果实在太方便了。 -图 1-1 使用Firebug控制台工具 +![console](./Figure/chapter1/1-1.jpg) -![console](http://img01.taobaocdn.com/tps/i1/T1AGmgXgxvXXXXXXXX-629-383.png) +图 1-1 使用Firebug控制台 -我们经常使用log()方法,它将传入的参数在控制台输出,有时会用到dir(),用以将传入的对象属性枚举出来,这里是一个例子: +我们经常使用`log()`方法,它将传入的参数在控制台输出,有时也会用到`dir()`,它可以将传入对象的属性枚举出来。例如: console.log("test", 1, {}, [1,2,3]); console.dir({one: 1, two: {three: 3}}); -当你在控制台输入内容时,则不必使用console.log()。为了避免混乱,有些代码片段仍然使用console.log()作输出,并假设所有的代码片段都使用控制台来作检测: +当你在控制台输入内容时,不必使用console.log()。为了避免混乱,有些代码片段仍然会使用console.log()进行输出,并假设所有的代码片段都使用控制台来运行: window.name === window['name']; // true @@ -150,5 +140,3 @@ console对象在本书中非常常见。这个对象并不是语言的一部分 console.log(window.name === window['name']); 这段代码在控制台中输出为true。 - - From c7b84556adebd74f649e2ecaa505dab35b04dbec Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Feb 2013 23:54:12 +0800 Subject: [PATCH 033/145] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=A0=87=E9=A2=98?= =?UTF-8?q?=EF=BC=8C=E4=B8=80=E5=A4=84=E8=AF=91=E6=B3=A8=E5=BE=AE=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter1.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chapter1.markdown b/chapter1.markdown index a58aa61..71d8d20 100644 --- a/chapter1.markdown +++ b/chapter1.markdown @@ -1,4 +1,4 @@ -# 第一章 概述 +# 第一章 绪言 JavaScript是一门Web开发语言。起初人们只是用它来操作网页中为数不多的元素(比如图片和表单域),但是谁也没想到这门语言可以成长得如此迅速,如今,JavaScript除了适用于客户端浏览器编程外,还可以在越来越多的平台上运行。你可以用它来开发服务端程序(使用.Net或Node.js)、桌面应用程序(运行于桌面操作系统)、应用程序扩展(Firefox插件或者Photoshop扩展)、移动终端应用和纯命令行的批处理脚本。 @@ -102,7 +102,7 @@ JavaScript语言的核心部分(不包含DOM、BOM和其它宿主对象)是 按照语言的发展计划,未来将会只保留“严格模式”。因此,现在的ES5只是一个过渡版本,它鼓励开发者使用严格模式,而非强制。 -本书不会讨论ES5新增特性相关的模式,因为在本书截稿时并没有任何浏览器实现了ES5(译注:截止译稿校对时,Chrome/Firefox/IE9+(部分)已实现ES5,具体兼容情况可参考),但本书的示例代码有以下特点,以鼓励开发者向新标准转变: +本书不会讨论ES5新增特性相关的模式,因为在本书截稿时并没有任何浏览器实现了ES5(译注:截止译稿校对时,Chrome/Firefox/IE9+已(部分)实现ES5,具体兼容情况可参考),但本书的示例代码有以下特点,以鼓励开发者向新标准转变: - 确保所提供的示例代码在严格模式下不包错 - 避免使用并明确指出弃用的构造函数相关的属性和方法,比如arguments.callee From 7707fc65eb75ef9de8ef3ea50f1684e147f28ce5 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 20 Feb 2013 23:54:55 +0800 Subject: [PATCH 034/145] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=A0=87=E9=A2=98?= =?UTF-8?q?=EF=BC=8C=E5=BA=8F=E8=A8=80=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 9479936..31b2d5b 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -1,38 +1,40 @@ -# 第二章 高质量JavaScript基本要点 +# 第二章 概要 -本章将对一些实质内容展开讨论,这些内容包括最佳实践、模式和编写高质量JavaScript代码的习惯,比如避免全局变量、使用单var声明、循环中的length预缓存、遵守编码约定等等。本章还包括一些非必要的编程习惯,但更多的关注点将放在总体的代码创建过程上,包括撰写API文档、组织相互评审以及使用JSLint。这些习惯和最佳实践可以帮助你写出更好的、更易读的和可维护的代码,当几个月后或数年后再重读你的代码时,你就会深有体会了。 +本章将概要介绍一些编写高质量JavaScript的最佳实践、模式和习惯,比如避免全局变量、使用单var声明、预缓存循环中的length、遵守编码约定等等。本章还包括一些编程习惯,这些习惯跟具体的代码关系不大,而是更多关注代码创建的总体过程,包括撰写API文档、code review以及使用JSLint。这些习惯和最佳实践可以帮助你写出更好更易读和可维护性更好的代码,当几个月或数年后你再重读你的代码时,就会深有体会了。 ## 编写可维护的代码 -修复软件bug成本很高,而且随着时间的推移,它们造成的损失也越来越大,特别是在已经打包发布了的软件发现了bug的时候。当然最好是发现bug立刻解决掉,但前提是你对你的代码依然很熟悉,否则当你转身投入到另外一个项目的开发中后,根本不记得当初代码的模样了。过了一段时间后你再去阅读当初的代码你需要: +修复软件bug成本很高,而且随着时间的推移,修复这些bug的成本会越来越高,尤其以出现在已经打包发布的软件中的bug为最甚。发现bug时立刻解决掉是最好的,但前提是你对你的代码依然很熟悉,否则当你转身投入到另外一个项目的开发中后,已经根本不记得当初的代码的模样了。当过了一段时间后你再去阅读当初的代码时你需要更多的时间: -- 时间来重新学习并理解问题 -- 时间去理解问题相关的代码 +- 重新学习并理解面临的问题 +- 理解用于问题的代码 -对大型项目或者公司来说还有一个不得不考虑的问题,就是解决这个bug的人和制造这个bug的人往往不是同一个人。因此减少理解代码所需的时间成本就显得非常重要,不管是隔了很长时间重读自己的代码还是阅读团队内其他人的代码。这对于公司的利益底线和工程师的幸福指数同样重要,因为每个人都宁愿去开发新的项目而不愿花很多时间和精力去维护旧代码。 +在大项目或者大公司的软件开发中还有另一个问题,就是解决这个bug的人和制造这个bug的人往往不是同一个人(而发现bug的往往又是另外一个人)。因此不管是隔了很长时间重读自己的代码还是阅读团队内其他人的代码,减少理解代码所需的时间成本都是非常重要的。这对于公司的利益底线和工程师的幸福指数同样重要,因为每个人都宁愿去开发新的项目而不愿花很多时间和精力去维护旧代码。 -另外一个软件开发中的普遍现象是,在读代码上花的时间要远远超过写代码的时间。常常当你专注于某个问题的时候,你会坐下来用一下午的时间产出大量的代码。当时的场景下代码是可以正常运行的,但当应用趋于成熟,会有很多因素促使你重读代码、改进代码或对代码做微调。比如: +软件开发中的另一个普遍现象是,在读代码上花的时间要远远超过写代码的时间。当你专注于某个问题的时候,你往往会坐下来用一下午的时间写出大量的代码。在当时的场景下,这些代码是可以正常运行的,但当应用趋于成熟,会有很多因素促使你重读代码、改进代码或对代码做微调。比如: - 发现了bug - 需要给应用添加新需求 - 需要将应用迁移到新的平台中运行(比如当市场中出现了新的浏览器时) - 代码重构 -- 由于架构更改或者更换另一种语言导致代码重写 +- 由于架构更改或者更换语言导致代码重写 这些不确定因素带来的后果是,少数人花几小时写的代码需要很多人花几个星期去阅读它。因此,创建可维护的代码对于一个成功的应用来说至关重要。 可维护的代码意味着代码是: - 可读的 -- 一致的 +- 风格一致的 - 可预测的 - 看起来像是同一个人写的 - 有文档的 本章接下来的部分会对这几点深入讲解。 +================校对分割线================ + ## 减少全局对象 From b16fd8b55c15190059500e77ff1fffa86c5c5801 Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 21 Feb 2013 13:22:03 +0800 Subject: [PATCH 035/145] =?UTF-8?q?=E6=A0=A1=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 31b2d5b..d57dd48 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -33,36 +33,36 @@ 本章接下来的部分会对这几点深入讲解。 -================校对分割线================ -## 减少全局对象 +## 减少全局变量 -JavaScript 使用函数来管理作用域,在一个函数内定义的变量称作“局部变量”,局部变量在函数外部是不可见的。另一方面,“全局变量”是不在任何函数体内部声明的变量,或者是直接使用而未声明的变量。 +JavaScript使用函数来管理作用域,在一个函数内定义的变量称作“本地变量”,本地变量在函数外部是不能被访问的。与之相对,“全局变量”是不在任何函数体内部声明的变量,或者是直接使用而未声明的变量。 -每一个JavaScript运行环境都有一个“全局对象”,不在任何函数体内使用this就可以获得对这个全局对象的引用。你所创建的每一个全局变量都是这个全局对象的属性。为了方便起见,浏览器都会额外提供一个全局对象的属性window,(常常)用以指向全局对象本身。下面的示例代码中展示了如何在浏览器中创建或访问全局变量: +每一个JavaScript运行环境都有一个“全局对象”,不在任何函数体内使用this就可以获得对这个全局对象的引用。你所创建的每一个全局变量都是这个全局对象的属性。为了方便起见,浏览器会额外提供一个全局对象的属性`window`,(一般)指向全局对象本身。下面的示例代码展示了如何在浏览器中创建或访问全局变量: - myglobal = "hello"; // antipattern + myglobal = "hello"; // 反模式 console.log(myglobal); // "hello" console.log(window.myglobal); // "hello" console.log(window["myglobal"]); // "hello" console.log(this.myglobal); // "hello" -### 全局对象带来的困扰 +### 全局变量的问题 -全局变量的问题是,它们在JavaScript代码执行期间或者整个web页面中始终是可见的。它们存在于同一个命名空间中,因此命名冲突的情况时有发生,毕竟在应用程序的不同模块中,经常会出于某种目的定义相同的全局变量。 +全局变量的问题是,它们在整个JavaScript应用或者是整个web页面中是始终被所有代码共享的。它们存在于同一个命名空间中,因此命名冲突的情况会时有发生,毕竟在应用程序的不同模块中,经常会出于某种目的定义相同的全局变量。 -同样,常常网页中所嵌入的代码并不是这个网页的开发者所写,比如: +同样,在网页中嵌入不是页面开发者编写的代码是很常见的,比如: - 网页中使用了第三方的JavaScript库 - 网页中使用了广告代码 - 网页中使用了用以分析流量和点击率的第三方统计代码 -- 网页中使用了很多组件,挂件和按钮等等 +- 网页中使用了很多组件、挂件和按钮等等 -假设某一段第三方提供的脚本定义了一个全局变量result。随后你在自己写的某个函数中也定义了一个全局变量result。这时,第二个变量就会覆盖第一个,这时就会导致第三方脚本停止工作。 +假设某一段第三方提供的脚本定义了一个全局变量result。随后你在自己写的某个函数中也定义了一个全局变量result。这时,第二个变量就会覆盖第一个,会导致第三方脚本工作不正常。 -因此,为了让你的脚本和这个页面中的其他脚本和谐相处,要尽可能少的使用全局变量,这一点非常重要。本书随后的章节中会讲到一些减少全局变量的技巧和策略,比如使用命名空间或者立即执行的匿名函数等,但减少全局变量最有效的方法是坚持使用var来声明变量。 +因此,为了让你的脚本和这个页面中的其他脚本和谐相处,要尽量少使用全局变量,这一点非常重要。本书随后的章节中会讲到一些减少全局变量的技巧和策略,比如使用命名空间或者即时函数等,但减少全局变量最有效的方法还是坚持使用`var`来声明变量。 +================校对分割线================ 由于JavaScript的特点,我们经常有意无意的创建全局变量,毕竟在JavaScript中创建全局变量实在太简单了。首先,你可以不声明而直接使用变量,再者,JavaScirpt中具有“隐式全局对象”的概念,也就是说任何不通过var声明(译注:在JavaScript1.7及以后的版本中,可以通过let来声明块级作用域的变量)的变量都会成为全局对象的一个属性(可以把它们当作全局变量)。看一下下面这段代码: From caebd0371661454ab59e6301a4e099fa8e7a1111 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 1 Mar 2013 20:22:34 +0800 Subject: [PATCH 036/145] =?UTF-8?q?=E5=87=8F=E5=B0=91=E5=85=A8=E5=B1=80?= =?UTF-8?q?=E5=8F=98=E9=87=8F=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 84 +++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 46 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index d57dd48..6cff288 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -1,9 +1,7 @@ - # 第二章 概要 本章将概要介绍一些编写高质量JavaScript的最佳实践、模式和习惯,比如避免全局变量、使用单var声明、预缓存循环中的length、遵守编码约定等等。本章还包括一些编程习惯,这些习惯跟具体的代码关系不大,而是更多关注代码创建的总体过程,包括撰写API文档、code review以及使用JSLint。这些习惯和最佳实践可以帮助你写出更好更易读和可维护性更好的代码,当几个月或数年后你再重读你的代码时,就会深有体会了。 - ## 编写可维护的代码 修复软件bug成本很高,而且随着时间的推移,修复这些bug的成本会越来越高,尤其以出现在已经打包发布的软件中的bug为最甚。发现bug时立刻解决掉是最好的,但前提是你对你的代码依然很熟悉,否则当你转身投入到另外一个项目的开发中后,已经根本不记得当初的代码的模样了。当过了一段时间后你再去阅读当初的代码时你需要更多的时间: @@ -33,8 +31,6 @@ 本章接下来的部分会对这几点深入讲解。 - - ## 减少全局变量 JavaScript使用函数来管理作用域,在一个函数内定义的变量称作“本地变量”,本地变量在函数外部是不能被访问的。与之相对,“全局变量”是不在任何函数体内部声明的变量,或者是直接使用而未声明的变量。 @@ -47,7 +43,6 @@ JavaScript使用函数来管理作用域,在一个函数内定义的变量称 console.log(window["myglobal"]); // "hello" console.log(this.myglobal); // "hello" - ### 全局变量的问题 全局变量的问题是,它们在整个JavaScript应用或者是整个web页面中是始终被所有代码共享的。它们存在于同一个命名空间中,因此命名冲突的情况会时有发生,毕竟在应用程序的不同模块中,经常会出于某种目的定义相同的全局变量。 @@ -62,70 +57,68 @@ JavaScript使用函数来管理作用域,在一个函数内定义的变量称 假设某一段第三方提供的脚本定义了一个全局变量result。随后你在自己写的某个函数中也定义了一个全局变量result。这时,第二个变量就会覆盖第一个,会导致第三方脚本工作不正常。 因此,为了让你的脚本和这个页面中的其他脚本和谐相处,要尽量少使用全局变量,这一点非常重要。本书随后的章节中会讲到一些减少全局变量的技巧和策略,比如使用命名空间或者即时函数等,但减少全局变量最有效的方法还是坚持使用`var`来声明变量。 -================校对分割线================ -由于JavaScript的特点,我们经常有意无意的创建全局变量,毕竟在JavaScript中创建全局变量实在太简单了。首先,你可以不声明而直接使用变量,再者,JavaScirpt中具有“隐式全局对象”的概念,也就是说任何不通过var声明(译注:在JavaScript1.7及以后的版本中,可以通过let来声明块级作用域的变量)的变量都会成为全局对象的一个属性(可以把它们当作全局变量)。看一下下面这段代码: +在JavaScript中有意无意地创建全局变量是件很容易的是,因为它有两个特性:首先,你可以不声明而直接使用变量,其次,JavaScirpt中具有“隐式全局对象”的概念,也就是说任何不通过`var`声明的变量都会成为全局对象的一个属性(可以把它们当作全局变量)。(译注:在ES6中可以通过`let`来声明块级作用域变量。)看一下下面这段代码: function sum(x, y) { - // antipattern: implied global + // 反模式:隐式全局变量 result = x + y; return result; } -这段代码中,我们直接使用了result而没有事先声明它。这段代码是能够正常工作的,但在调用这个方法之后,会产生一个全局变量result,这会带来其他问题。 +这段代码中,我们直接使用了`result`而没有事先声明它。这段代码的确是可以正常工作,但被调用后会产生一个全局变量`result`,这可能会导致其他问题。 -解决办法是,总是使用var来声明变量,下面代码就是改进了的sum()函数: +解决办法是,总是使用var来声明变量,下面代码就是改进了的`sum()`函数: function sum(x, y) { var result = x + y; return result; } -这里我们要注意一种反模式,就是在var声明中通过链式赋值的方法创建全局变量。在下面这个代码片段中,a是局部变量,但b是全局变量,而作者的意图显然不是如此: +另一种创建全局变量的反模式,就是在`var`声明中使用链式赋值的方法。在下面这个代码片段中,`a`是局部变量,但`b`是全局变量,而作者的意图显然不是这样: - // antipattern, do not use + // 反模式 function foo() { var a = b = 0; // ... } -为什么会这样?因为这里的计算顺序是从右至左的。首先计算表达式b=0,这里的b是未声明的,这个表达式的值是0,然后通过var创建了局部变量a,并赋值为0。换言之,可以等价的将代码写成这样: +为什么会这样呢?因为这里的计算顺序是从右至左的:首先计算表达式`b=0`,这里的`b`是未声明的;这个表达式的结果是`0`,然后通过var创建了本地变量`a`,并赋值为`0`。换言之,可以将代码写成这样: var a = (b = 0); -如果变量b已经被声明,这种链式赋值的写法是ok的,不会意外的创建全局变量,比如: +如果变量b已经被声明,这种链式赋值的写法是可以使用的,不会意外地创建全局变量,比如: function foo() { var a, b; // ... - a = b = 0; // both local + a = b = 0; // 两个都是本地变量 } -> 避免使用全局变量的另一个原因是出于可移植性考虑的,如果你希望将你的代码运行于不同的平台环境(宿主),使用全局变量则非常危险。很有可能你无意间创建的某个全局变量在当前的平台环境中是不存在的,你认为可以安全的使用,而在其他的环境中却是存在的。 +> 避免使用全局变量的另一个原因是出于可移植性考虑,如果你希望将你的代码运行于不同的平台环境(宿主),那么使用全局变量就非常危险。因为很有可能你无意间创建的某个全局变量在当前的平台环境中是不存在的,你以为可以安全地使用,而在另一个环境中却是本来就存在的。 - ### 忘记var时的副作用 -隐式的全局变量和显式定义的全局变量之间有着细微的差别,差别在于通过delete来删除它们的时候表现不一致。 +隐式创建的全局变量和显式定义的全局变量之间有着细微的差别,就是通过`delete`来删除它们的时候表现不一致。 -- 通过var创建的全局变量(在任何函数体之外创建的变量)不能被删除。 -- 没有用var创建的隐式全局变量(不考虑函数内的情况)可以被删除。 +- 通过`var`创建的全局变量(在任何函数体之外创建的变量)不能被删除。 +- 没有用`var`创建的隐式全局变量(不考虑函数内的情况)可以被删除。 -也就是说,隐式全局变量并不算是真正的变量,但他们是全局对象的属性成员。属性是可以通过delete运算符删除的,而变量不可以被删除: +也就是说,隐式全局变量并不算是真正的变量,但它们却是全局对象的属性。属性是可以通过`delete`运算符删除的,而变量不可以被删除: - // define three globals + // 定义三个全局变量 var global_var = 1; - global_novar = 2; // antipattern + global_novar = 2; // 反模式 (function () { - global_fromfunc = 3; // antipattern + global_fromfunc = 3; // 反模式 }()); - // attempt to delete + // 尝试删除 delete global_var; // false delete global_novar; // true delete global_fromfunc; // true - // test the deletion + // 测试删除结果 typeof global_var; // "number" typeof global_novar; // "undefined" typeof global_fromfunc; // "undefined" @@ -135,23 +128,22 @@ JavaScript使用函数来管理作用域,在一个函数内定义的变量称 ### 访问全局对象 -在浏览器中,我们可以随时随地通过window属性来访问全局对象(除非你定义了一个名叫window的局部变量)。但换一个运行环境这个方便的window可能就换成了别的名字(甚至根本就被禁止访问全局对象了)。如果不想通过这种写死window的方式来得到全局变量,有一个办法,你可以在任意层次嵌套的函数作用域内执行: +在浏览器中,我们可以随时随地通过`window`属性来访问全局对象(除非你定义了一个名叫`window`的局部变量)。但换一个运行环境这个`window`可能就换成了别的名字(甚至根本就被禁止访问全局对象了)。如果不想通过这种写死`window`的方式来访问全局变量,那么你可以在任意函数作用域内执行: var global = (function () { return this; }()); -这种方式总是可以得到全局对象,因为在被当作函数执行的函数体内(而不是被当作构造函数执行的函数体内),this总是指向全局对象。但这种情况在ECMAScript5的严格模式中行不通,因此在严格模式中你不得不寻求其他的替代方案。比如,如果你在开发一个库,你会将你的代码包装在一个立即执行的匿名函数中(在第四章会讲到),然后从全局作用域中给这个匿名函数传入一个指向this的参数。 +这种方式总是可以访问到全局对象,因为在被当作函数(而不是构造函数)执行的函数体内,`this`总是指向全局对象。但这种情况在ECMAScript5的严格模式中行不通,因此在严格模式中你不得不寻求其他的替代方案。比如,如果你在开发一个库,你会将你的代码包装在一个即时函数中(在第四章会讲到),然后从全局作用域给这个匿名函数传入一个指向`this`的参数。 - -### 单 var 模式 +### 单var模式 -在函数的顶部使用一个单独的var语句是非常推荐的一种模式,它有如下一些好处: +在函数的顶部使用唯一一个`var`语句是非常推荐的一种模式,它有如下一些好处: -- 在同一个位置可以查找到函数所需的所有变量 -- 避免当在变量声明之前使用这个变量时产生的逻辑错误(参照下一小节“声明提前:分散的 var 带来的问题”) +- 可以在同一个位置找到函数所需的所有变量 +- 避免在变量声明之前使用这个变量时产生的逻辑错误(参考下一小节“声明提前:分散的`var`带来的问题”) - 提醒你不要忘记声明变量,顺便减少潜在的全局变量 -- 代码量更少(输入更少且更易做代码优化) +- 代码量更少(输入代码更少且更易做代码优化) 单var模式看起来像这样: @@ -162,26 +154,25 @@ JavaScript使用函数来管理作用域,在一个函数内定义的变量称 myobject = {}, i, j; - // function body... + // 函数体… } -你可以使用一个var语句来声明多个变量,变量之间用逗号分隔。也可以在这个语句中加入变量的初始化,这是一个非常好的实践。这种方式可以避免逻辑错误(所有未初始化的变量都被声明了,且值为undefined)并增加了代码的可读性。过段时间后再看这段代码,你会体会到声明不同类型变量的惯用名称,比如,你一眼就可看出某个变量是对象还是整数。 +你可以使用一个`var`语句来声明多个变量,变量之间用逗号分隔,也可以在这个语句中加入变量初始化的部分。这是一种非常好的实践方式,可以避免逻辑错误(所有未初始化的变量都被声明了,且值为undefined),并增加了代码的可读性。过段时间后再看这段代码,你可以从初始化的值中大概知道这个变量的用法,比如你一眼就可看出某个变量是对象还是整数。 -你可以在声明变量时多做一些额外的工作,比如在这个例子中就写了sum=a+b这种代码。另一个例子就是当代码中用到对DOM元素时,你可以把对DOM的引用赋值给一些变量,这一步就可以放在一个单独的声明语句中,比如下面这段代码: +你可以在声明变量时做一些额外的工作,比如在这个例子中就写了`sum=a+b`这种代码。另一个例子就是当代码中用到对DOM元素时,你可以把DOM引用赋值的操作也放在这个变量声明语句中,比如下面这段代码: function updateElement() { var el = document.getElementById("result"), style = el.style; - // do something with el and style... + // 使用el和style… } - ### 声明提前:分散的 var 带来的问题 -JavaScript 中是允许在函数的任意地方写任意多个var语句的,其实相当于在函数体顶部声明变量,这种现象被称为“变量提前”,当你在声明之前使用这个变量时,可能会造成逻辑错误。对于JavaScript来说,一旦在某个作用域(同一个函数内)里声明了一个变量,这个变量在整个作用域内都是存在的,包括在var声明语句之前。看一下这个例子: +JavaScript允许在函数的任意地方写任意多个`var`语句,但它们的行为会像在函数体顶部声明变量一样,这种现象被称为“声明提前”,当你在声明语句之前使用这个变量时,可能会造成逻辑错误。对于JavaScript来说,一旦在某个作用域(同一个函数内)里声明了一个变量,那么这个变量在整个作用域内都是存在的,包括在`var`声明语句之前的位置。看一下这个例子: - // antipattern - myname = "global"; // global variable + // 反模式 + myname = "global"; // 全局变量 function func() { alert(myname); // "undefined" var myname = "local"; @@ -189,21 +180,22 @@ JavaScript 中是允许在函数的任意地方写任意多个var语句的,其 } func(); -这个例子中,你可能期望第一个alert()弹出“global”,第二个alert()弹出“local”。这种结果看起来是合乎常理的,因为在第一个alert执行时,myname还没有声明,这时就应该“寻找”全局变量中的myname。但实际情况并不是这样,第一个alert弹出“undefined”,因为myname已经在函数内有声明了(尽管声明语句在后面)。所有的变量声明都提前到了函数的顶部。因此,为了避免类似带有“歧义”的程序逻辑,最好在使用之前一起声明它们。 +这个例子中,你可能会期望第一个`alert()`弹出“global”,第二个`alert()`弹出“local”。这种结果看起来是合乎常理的,因为在第一个`alert()`执行时,`myname`还没有被声明,这时就应该“寻找”全局变量`myname`。但实际情况并不是这样,第一个`alert()`弹出“undefined”,因为`myname`已经在函数内被声明了(尽管声明语句在后面)。所有的变量声明都会被提前到函数的顶部,因此,为了避免类似带有“歧义”的程序逻辑,最好在使用之前一起声明它们。 上一个代码片段等价于下面这个代码片段: - myname = "global"; // global variable + myname = "global"; // 全局变量 function func() { - var myname; // same as -> var myname = undefined; + var myname; // 等价于 -> var myname = undefined; alert(myname); // "undefined" myname = "local"; alert(myname); // "local" } func(); ->这里有必要对“变量提前”作进一步补充,实际上从JavaScript引擎的工作机制上看,这个过程稍微有点复杂。代码处理经过了两个阶段,第一阶段是创建变量、函数和参数,这一步是预编译的过程,它会扫描整段代码的上下文。第二阶段是代码的运行,这一阶段将创建函数表达式和一些非法的标识符(未声明的变量)。从实用性角度来讲,我们更愿意将这两个阶段归成一个概念“变量提前”,尽管这个概念并没有在ECMAScript标准中定义,但我们常常用它来解释预编译的行为过程。 +> 这里有必要对“变量提前”做进一步补充,实际上从JavaScript引擎的工作机制上看,这个过程稍微有点复杂。代码处理经过了两个阶段,第一阶段是创建变量、函数和形参,也就是预编译的过程,它会扫描整段代码的上下文。第二阶段是在代码的运行时(runtime),这一阶段将创建函数表达式和一些非法的标识符(未声明的变量)。(译注:这两个阶段并没有包含代码的执行,是在执行前的处理过程。)从实用性角度来讲,我们更愿意将这两个阶段归成一个概念“变量提前”,尽管这个概念并没有在ECMAScript标准中定义,但我们常常用它来解释预编译的行为过程。 +================校对分割线================ ## for 循环 From a4dfd283e980b8b75dce774061ee8d60e44e56d9 Mon Sep 17 00:00:00 2001 From: TooBug Date: Wed, 6 Mar 2013 13:28:45 +0800 Subject: [PATCH 037/145] =?UTF-8?q?for=E5=BE=AA=E7=8E=AF=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 73 +++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 6cff288..a504ec1 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -125,7 +125,6 @@ JavaScript使用函数来管理作用域,在一个函数内定义的变量称 在ES5严格模式中,给未声明的变量赋值会报错(比如这段代码中提到的两个反模式)。 - ### 访问全局对象 在浏览器中,我们可以随时随地通过`window`属性来访问全局对象(除非你定义了一个名叫`window`的局部变量)。但换一个运行环境这个`window`可能就换成了别的名字(甚至根本就被禁止访问全局对象了)。如果不想通过这种写死`window`的方式来访问全局变量,那么你可以在任意函数作用域内执行: @@ -141,7 +140,7 @@ JavaScript使用函数来管理作用域,在一个函数内定义的变量称 在函数的顶部使用唯一一个`var`语句是非常推荐的一种模式,它有如下一些好处: - 可以在同一个位置找到函数所需的所有变量 -- 避免在变量声明之前使用这个变量时产生的逻辑错误(参考下一小节“声明提前:分散的`var`带来的问题”) +- 避免在变量声明之前使用这个变量时产生的逻辑错误(参考下一小节“声明提前:分散的var带来的问题”) - 提醒你不要忘记声明变量,顺便减少潜在的全局变量 - 代码量更少(输入代码更少且更易做代码优化) @@ -167,7 +166,7 @@ JavaScript使用函数来管理作用域,在一个函数内定义的变量称 // 使用el和style… } -### 声明提前:分散的 var 带来的问题 +### 声明提前:分散的var带来的问题 JavaScript允许在函数的任意地方写任意多个`var`语句,但它们的行为会像在函数体顶部声明变量一样,这种现象被称为“声明提前”,当你在声明语句之前使用这个变量时,可能会造成逻辑错误。对于JavaScript来说,一旦在某个作用域(同一个函数内)里声明了一个变量,那么这个变量在整个作用域内都是存在的,包括在`var`声明语句之前的位置。看一下这个例子: @@ -193,90 +192,88 @@ JavaScript允许在函数的任意地方写任意多个`var`语句,但它们 } func(); -> 这里有必要对“变量提前”做进一步补充,实际上从JavaScript引擎的工作机制上看,这个过程稍微有点复杂。代码处理经过了两个阶段,第一阶段是创建变量、函数和形参,也就是预编译的过程,它会扫描整段代码的上下文。第二阶段是在代码的运行时(runtime),这一阶段将创建函数表达式和一些非法的标识符(未声明的变量)。(译注:这两个阶段并没有包含代码的执行,是在执行前的处理过程。)从实用性角度来讲,我们更愿意将这两个阶段归成一个概念“变量提前”,尽管这个概念并没有在ECMAScript标准中定义,但我们常常用它来解释预编译的行为过程。 +> 这里有必要对“变量提前”做进一步补充,实际上从JavaScript引擎的工作机制上看,这个过程稍微有点复杂。代码处理经过了两个阶段:第一阶段是创建变量、函数和形参,也就是预编译的过程,它会扫描整段代码的上下文;第二阶段是在代码的运行时(runtime),这一阶段将创建函数表达式和一些非法的标识符(未声明的变量)。(译注:这两个阶段并没有包含代码的执行,是在执行前的处理过程。)从实用性角度来讲,我们更愿意将这两个阶段归成一个概念“变量提前”,尽管这个概念并没有在ECMAScript标准中定义,但我们常常用它来解释预编译的行为过程。 -================校对分割线================ - -## for 循环 +## for循环 -在for循环中,可以对数组或类似数组的对象(比如arguments和HTMLCollection对象)作遍历,最普通的for循环模式形如: +在`for`循环中,可以对数组或类似数组的对象(比如`arguments`和`HTMLCollection`对象)进行遍历,通常`for`循环模式形如: - // sub-optimal loop + // 非最优的循环方式 for (var i = 0; i < myarray.length; i++) { - // do something with myarray[i] + // 访问myarray[i]… } -这种模式的问题是,每次遍历都会访问数组的length属性。这降低了代码运行效率,特别是当myarray并不是一个数组而是一个HTMLCollection对象的时候。 +这种模式的问题是,每次遍历都会访问数组的length属性,这会降低代码运行效率,特别是当`myarray`不是一个数组而是一个`HTMLCollection`对象的时候。 -HTMLCollection是由DOM方法返回的对象,比如: +`HTMLCollection`是由DOM方法返回的对象集合,比如: - document.getElementsByName() - document.getElementsByClassName() - document.getElementsByTagName() -还有很多其他的HTMLCollection,这些对象是在DOM标准之前就已经在用了,这些HTMLCollection主要包括: +还有一些`HTMLCollection`是在DOM标准诞生之前就已经在用了并且现在仍然可用,包括: -**document.images** +- document.images -页面中所有的IMG元素 + 页面中所有的IMG元素 -**document.links** +- document.links -页面中所有的A元素 + 页面中所有的A元素 -**document.forms** +- document.forms -页面中所有的表单 + 页面中所有的表单 -**document.forms[0].elements** +- document.forms[0].elements -页面中第一个表单的所有字段 + 页面中第一个表单的所有字段 -这些对象的问题在于,它们均是指向文档(HTML页面)中的活动对象。也就是说每次通过它们访问集合的length时,总是会去查询DOM,而DOM操作则是很耗资源的。 +这些对象的问题在于,它们都会实时查询文档(HTML页面)中的对象。也就是说每次通过它们访问集合的`length`属性时,总是都会去查询DOM,而DOM操则是很耗资源的。 -更好的办法是为for循环缓存住要遍历的数组的长度,比如下面这段代码: +更好的办法是在`for`循环中缓存要遍历的数组的长度,比如下面这段代码: for (var i = 0, max = myarray.length; i < max; i++) { - // do something with myarray[i] + // 访问myarray[i]… } -通过这种方法只需要访问DOM节点一次以获得length,在整个循环过程中就都可以使用它。 +通过这种方法只需要获取`length`一次,然后在整个循环过程中使用它。 -不管在什么浏览器中,在遍历HTMLCollection时缓存length都可以让程序执行的更快,可以提速两倍(Safari3)到一百九十倍(IE7)不等。更多细节可以参照Nicholas Zakas的《高性能JavaScript》,这本书也是由O'Reilly出版。 +不管在什么浏览器中,在遍历`HTMLCollection`时缓存`length`都可以让程序执行的更快,可以提速2倍(Safari3)到190倍(IE7)不等。更多细节可以参照Nicholas Zakas的《高性能JavaScript》,这本书也是由O'Reilly出版。 -需要注意的是,当你在循环过程中需要修改这个元素集合(比如增加DOM元素)时,你更希望更新length而不是更新常量。 +需要注意的是,当你在循环过程中需要修改这个元素集合(比如增加DOM元素)时,你可能需要更新`length`。 -遵照单var模式,你可以将var提到循环的外部,比如: +按照“单var模式”,你可以将`var`提到循环的外部,比如: function looper() { var i = 0, max, myarray = []; - // ... + // … for (i = 0, max = myarray.length; i < max; i++) { - // do something with myarray[i] + // 访问myarray[i]… } } -这种模式带来的好处就是提高了代码的一致性,因为你越来越依赖这种单var模式。缺点就是在重构代码的时候不能直接复制粘贴一个循环体,比如,你正在将某个循环从一个函数拷贝至另外一个函数中,必须确保i和max也拷贝至新函数里,并且需要从旧函数中将这些没用的变量删除掉。 +当你越来越依赖“单var模式”时,带来的好处就是提高了代码的一致性。而缺点则是在重构代码的时候不能直接复制粘贴一个循环体,比如,你正在将某个循环从一个函数复制至另外一个函数中,那么必须确保`i`和`max`也复制到新函数里,并且需要从旧函数中将这些没用的变量删除掉。 最后一个需要对循环做出调整的地方是将i++替换成为下面两者之一: i = i + 1 i += 1 -JSLint提示你这样做,是因为++和--实际上降低了代码的可读性,如果你觉得无所谓,可以将JSLint的plusplus选项设为false(默认为true),本书所介绍的最后一个模式用到了: i += 1。 +JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读性,如果你觉得无所谓,可以将JSLint的`plusplus`选项设为`false`(默认为`true`)。稍后,在本书所介绍的最后一个模式中用到了:`i += 1`。 -关于这种for模式还有两种变化的形式,做了少量改进,原因有二: +关于这种`for`模式还有两种变化的形式,做了少量改进,原因有二: -- 减少一个变量(没有max) +- 减少一个变量(没有`max`) - 减量循环至0,这种方式速度更快,因为和零比较要比和非零数字或数组长度比较要高效的多 第一种变化形式是: var i, myarray = []; for (i = myarray.length; i--;) { - // do something with myarray[i] + // 访问myarray[i]… } 第二种变化形式用到了while循环: @@ -284,12 +281,12 @@ JSLint提示你这样做,是因为++和--实际上降低了代码的可读性 var myarray = [], i = myarray.length; while (i--) { - // do something with myarray[i] + // 访问myarray[i]… } -这些小改进只体现在性能上,此外,JSLint不推荐使用i--。 +这些小改进只能体现在对性能要求比较苛刻的地方,此外,JSLint不推荐使用`i--`。 - +================校对分割线================ ## for-in 循环 for-in 循环用于对非数组对象作遍历。通过for-in进行循环也被称作“枚举”。 From 9b1a964c308922cfb7ff4d829f54cea41f49b5b1 Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 7 Mar 2013 21:56:41 +0800 Subject: [PATCH 038/145] =?UTF-8?q?for-in=E5=BE=AA=E7=8E=AF=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index a504ec1..fd386ac 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -286,86 +286,83 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 这些小改进只能体现在对性能要求比较苛刻的地方,此外,JSLint不推荐使用`i--`。 -================校对分割线================ -## for-in 循环 +## for-in循环 -for-in 循环用于对非数组对象作遍历。通过for-in进行循环也被称作“枚举”。 +`for-in`循环用于对非数组对象进行遍历。通过`for-in`进行循环也被称作“枚举”(enumeration)。 -从技术角度讲,for-in循环同样可以用于数组(JavaScript中数组即是对象),但不推荐这样做。当使用自定义函数扩充了数组对象时,这时更容易产生逻辑错误。另外,for-in循环中属性的遍历顺序是不固定的,所以最好数组使用普通的for循环,对象使用for-in循环。 +从技术上讲,`for-in`循环同样可以用于数组(JavaScript中数组也是对象),但不推荐这样做。当数组对象被扩充了自定义函数时,可能会产生逻辑错误。另外,`for-in`循环中属性的遍历顺序是不固定的,所以最好数组使用普通的`for`循环,对象使用`for-in`循环。 -可以使用对象的hasOwnProperty()方法将从原型链中继承来的属性过滤掉,这一点非常重要。看一下这段代码: +可以使用对象的`hasOwnProperty()`方法过来从原型链中继承来的属性,这一点非常重要。看一下这段代码: - // the object + // 对象 var man = { hands: 2, legs: 2, heads: 1 }; - // somewhere else in the code - // a method was added to all objects + // 在代码的另一个地方给所有的对象添加了一个方法 if (typeof Object.prototype.clone === "undefined") { Object.prototype.clone = function () {}; } -在这段例子中,我们定义了一个名叫man的对象直接量。在代码中的某个地方(可以是man定义之前也可以是之后),给Object的原型中增加了一个方法clone()。原型链是实时的,这意味着所有的对象都可以访问到这个新方法。要想在枚举man的时候避免枚举出clone()方法,则需要调用hasOwnProperty()来对原型属性进行过滤。如果不做过滤,clone()也会被遍历到,而这不是我们所希望的: +在这个例子中,我们使用对象字面量定义了一个名叫`man`的对象。在代码中的某个地方(可以是`man`定义之前也可以是之后),给`Object`的原型增加了一个方法`clone()`。原型链是实时的,这意味着所有的对象都可以访问到这个新方法。要想在枚举`man`的时候避免枚举出`clone()`方法,就需要调用`hasOwnProperty()`来过滤来自原型的属性。如果不做过滤,`clone()`也会被遍历到,这是我们不希望看到的: - // 1. - // for-in loop + // 1.for-in循环 for (var i in man) { if (man.hasOwnProperty(i)) { // filter console.log(i, ":", man[i]); } } /* - result in the console + 控制台中的结果 hands : 2 legs : 2 heads : 1 */ - // 2. - // antipattern: - // for-in loop without checking hasOwnProperty() + // 2.反模式: + // 不使用hasOwnProperty()过滤的for-in循环 for (var i in man) { console.log(i, ":", man[i]); } /* - result in the console + 控制台中的结果 hands : 2 legs : 2 heads : 1 clone: function() */ -另外一种的写法是通过Object.prototype直接调用hasOwnProperty()方法,像这样: +另外一种调用`hasOwnProperty()`的方法是通过`Object.prototype`来调用,像这样: for (var i in man) { - if (Object.prototype.hasOwnProperty.call(man, i)) { // filter + if (Object.prototype.hasOwnProperty.call(man, i)) { // 过滤 console.log(i, ":", man[i]); } } -这种做法的好处是,当man对象中重新定义了hasOwnProperty方法时,可以避免调用时的命名冲突(译注:明确指定调用的是Object.prototype上的方法而不是实例对象中的方法),这种做法同样可以避免冗长的属性查找过程(译注:这种查找过程多是在原型链上进行查找),一直查找到Object中的方法,你可以定义一个变量来“缓存”住它(译注:这里所指的是缓存住Object.prototype.hasOwnProperty): +这种做法的好处是,在`man`对象中重新定义了`hasOwnProperty`方法的情况下,可以避免调用时的命名冲突。为了避免查找属性时从`Object`对象一路找到原型的冗长过程,你可以定义一个变量来“缓存”住它: var i, hasOwn = Object.prototype.hasOwnProperty; for (i in man) { - if (hasOwn.call(man, i)) { // filter + if (hasOwn.call(man, i)) { // 过滤 console.log(i, ":", man[i]); } } ->严格说来,省略hasOwnProperty()并不是一个错误。根据具体的任务以及你对代码的自信程度,你可以省略掉它以提高一些程序执行效率。但当你对当前要遍历的对象不确定的时候,添加hasOwnProperty()则更加保险些。 +> 严格说来,省略`hasOwnProperty()`并不是一个错误。根据具体的任务以及你对代码的自信程度,你可以省略掉它以提高一些程序执行效率。但当你对当前要遍历的对象不确定的时候,添加hasOwnProperty()则更加保险些。 -这里提到一种格式上的变化写法(这种写法无法通过JSLint检查),这种写法在for循环所在的行加入了if判断条件,他的好处是能让循环语句读起来更完整和通顺(“如果元素包含属性X,则拿X做点什么”): +这里介绍一种格式上的变种(这种写法无法通过JSLint检查),这种写法在`for`循环所在的行加入了`if`判断条件,他的好处是能让循环语句读起来更完整和通顺(“如果元素包含属性X,则对X做点什么”): - // Warning: doesn't pass JSLint + // 警告:无法通过JSLint检查 var i, hasOwn = Object.prototype.hasOwnProperty; - for (i in man) if (hasOwn.call(man, i)) { // filter + for (i in man) if (hasOwn.call(man, i)) { // 过滤 console.log(i, ":", man[i]); } +================校对分割线================ ## (不)扩充内置原型 From 47d9265da2dfef0cff22536c672bb82ab49f01c5 Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 7 Mar 2013 22:26:09 +0800 Subject: [PATCH 039/145] =?UTF-8?q?=EF=BC=88=E4=B8=8D=EF=BC=89=E6=89=A9?= =?UTF-8?q?=E5=85=85=E5=86=85=E7=BD=AE=E5=8E=9F=E5=9E=8B=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index fd386ac..4426cd5 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -362,30 +362,29 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 console.log(i, ":", man[i]); } -================校对分割线================ - ## (不)扩充内置原型 -我们可以扩充构造函数的prototype属性,这是一种非常强大的特性,用来为构造函数增加功能,但有时这个功能强大到超过我们的掌控。 +我们可以扩充构造函数的`prototype`属性来为构造函数增加功能,这个特性非常强大,但有时会强大到超过我们的掌控。 -给内置构造函数比如Object()、Array()、和Function()扩充原型看起来非常诱人,但这种做法严重降低了代码的可维护性,因为它让你的代码变得难以预测。对于那些基于你的代码做开发的开发者来说,他们更希望使用原生的JavaScript方法来保持工作的连续性,而不是使用你所添加的方法(译注:因为原生的方法更可靠,而你写的方法可能会有bug)。 +给内置构造函数如`Object()`、`Array()`、`Function()`扩充原型看起来非常诱人,但这种做法会严重降低代码的可维护性,因为它会让你的代码变得难以预测。对于那些基于你的代码来做开发的开发者来说,他们更希望使用原生的JavaScript方法来保持代码的一致性,而不愿意使用你所添加的方法。 -另外,如果将属性添加至原型中,很可能导致在那些不使用hasOwnProperty()做检测的循环中将原型上的属性遍历出来,这会造成混乱。 +另外,如果将属性添加至原型中,很可能导致原型上的属性在那些不使用`hasOwnProperty()`做过滤的循环中被遍历出来,从而造成混乱。 -因此,不扩充内置对象的原型是最好的,你也可以自己定义一个规则,仅当下列条件满足时做例外考虑: +因此,不扩充内置对象的原型是最好的,你也可以自己定义一个规则,仅当下列条件满足时才考虑扩充内置对象的原型: -1. 未来的ECMAScript版本的JavaScirpt会将你实现的方法添加为内置方法。比如,你可以实现ECMAScript5定义的一些方法,一直等到浏览器升级至支持ES5。这样,你只是提前定义了这些有用的方法。 -2. 如果你发现你自定义的方法已经不存在,要么已经在代码其他地方实现了,要么是浏览器的JavaScript引擎已经内置实现了。 -3. 你所做的扩充附带充分的文档说明,且和团队其他成员做了沟通。 +1. 未来的ECMAScript版本或者JavaScirpt会将你将要实现的方法添加为内置方法。比如,你可以实现ECMAScript5定义的一些方法,直到浏览器升级至支持ES5。这样,你只是提前定义了这些方法。 +2. 当某个属性或者方法是你在其它地方实现过的,或者是某个JavaScript引擎或浏览器的一部分,而你检查时又发现它不存在时。 +3. 在有充分的文档说明,并且和团队其他成员做了沟通的时候。 如果你遇到这三种情况之一,你可以给内置原型添加自定义方法,写法如下: if (typeof Object.protoype.myMethod !== "function") { Object.protoype.myMethod = function () { - // implementation... + // 实现… }; } +================校对分割线================ ## switch 模式 From 4fbf0f6e67f5109ccbe71e916c534410ca620b60 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 8 Mar 2013 09:11:46 +0800 Subject: [PATCH 040/145] =?UTF-8?q?switch=E6=A8=A1=E5=BC=8F=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 4426cd5..8bd5509 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -384,11 +384,9 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 }; } -================校对分割线================ - -## switch 模式 +## switch模式 -你可以通过下面这种模式的写法来增强switch语句的可读性和健壮性: +你可以通过下面这种模式来增强`switch`语句的可读性和健壮性: var inspect_me = 0, result = ''; @@ -405,12 +403,13 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 这个简单的例子所遵循的风格约定如下: -- 每个case和switch对齐(这里不考虑花括号相关的缩进规则) -- 每个case中的代码整齐缩进 -- 每个case都以break作为结束 -- 避免连续执行多个case语句块(当省略break时会发生),如果你坚持认为连续执行多case语句块是最好的方法,请务必补充文档说明,对于其他人来说,这种情况看起来是错误的。 -- 以default结束整个switch,以确保即便是在找不到匹配项时也会有正常的结果, +- 每个`case`和`switch`对齐(这里不考虑花括号相关的缩进规则)。 +- 每个`case`中的代码整齐缩进。 +- 每个`case`都以`break`作为结束。 +- 避免连续执行多个case语句块(省略break时),如果你坚持认为连续执行多个`case`语句块是最好的方法,请务必补充文档说明,对于其他人来说,会觉得这种情况是错误的写法。 +- 以`default`结束整个`switch`,以确保即便是在找不到匹配项时也有合理的结果。 +================校对分割线================ ## 避免隐式类型转换 From 0ad8c18095ccfefb8b751e08bd81f44a3e1722fc Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 8 Mar 2013 09:39:53 +0800 Subject: [PATCH 041/145] =?UTF-8?q?=E9=81=BF=E5=85=8D=E9=9A=90=E5=BC=8F?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E8=BD=AC=E6=8D=A2=20=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 49 ++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 8bd5509..a25e0d1 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -409,87 +409,84 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 - 避免连续执行多个case语句块(省略break时),如果你坚持认为连续执行多个`case`语句块是最好的方法,请务必补充文档说明,对于其他人来说,会觉得这种情况是错误的写法。 - 以`default`结束整个`switch`,以确保即便是在找不到匹配项时也有合理的结果。 -================校对分割线================ - ## 避免隐式类型转换 -在JavaScript的比较操作中会有一些隐式的数据类型转换。比如诸如false == 0或""==0之类的比较都返回true。 +在JavaScript对变量进行比较时会有一些隐式的数据类型转换。比如诸如`false == 0`或`"" == 0`之类的比较都返回`true`。 -为了避免隐式类型转换造对程序造成干扰,推荐使用===和!===运算符,它们较除了比较值还会比较类型。 +为了避免隐式类型转换对程序造成干扰,推荐使用`===`和`!==`运算符,它们除了比较值还会比较类型: var zero = 0; if (zero === false) { - // not executing because zero is 0, not false + // 不会执行,因为zero是0,不是false } - // antipattern + // 反模式 if (zero == false) { - // this block is executed... + // 代码块会执行… } -另外一种观点认为当==够用的时候就不必多余的使用===。比如,当你知道typeof的返回值是一个字符串,就不必使用全等运算符。但JSLint却要求使用全等运算符,这当然会提高代码风格的一致性,并减少了阅读代码时的思考(“这里使用==是故意的还是无意的?”)。 +有一种观点认为当`==`够用的时候就不必使用`===`。比如,当你知道`typeof`的返回值是一个字符串,就不必使用全等运算符。但JSLint却要求使用全等运算符,这无疑会提高代码风格的一致性,并减少了阅读代码时的思考量(“这里使用`==`是故意的还是无意的?”)。 - ### 避免使用eval() -当你想使用eval()的时候,不要忘了那句话“eval()是魔鬼”。这个函数的参数是一个字符串,它可以执行任意字符串。如果事先知道要执行的代码是有问题的(在运行之前),则没有理由使用eval()。如果需要在运行时动态生成执行代码,往往都会有更佳的方式达到同样的目的,而非一定要使用eval()。例如,访问动态属性时可以使用方括号: +当你想使用`eval()`的时候,不要忘了那句话“`eval()` is evil”(`eval()`是魔鬼)。这个函数的参数是一个字符串,它会将传入的字符串作为JavaScript代码执行。如果用来解决问题的代码是事先知道的(在运行之前),则没有理由使用`eval()`。如果需要在运行时动态生成并执行代码,那一般都会有更好的方式达到同样的目的,而非一定要使用`eval()`。例如,访问动态属性时可以使用方括号: - // antipattern + // 反模式 var property = "name"; alert(eval("obj." + property)); - // preferred + // 更好的方式 var property = "name"; alert(obj[property]); -eval()同样有安全隐患,因为你需要运行一些容易被干扰的代码(比如运行一段来自于网络的代码)。在处理Ajax请求所返回的JSON数据时会常遇到这种情况,使用eval()是一种反模式。这种情况下最好使用浏览器的内置方法来解析JSON数据,以确保代码的安全性和数据的合法性。如果浏览器不支持JSON.parse(),你可以使用JSON.org所提供的库。 +`eval()`还有安全隐患,因为你有可能会运行一些被干扰过的代码(比如一段来自于网络的代码)。这是一种在处理Ajax请求所返回的JSON数据时比较常见的反模式。这种情况下最好使用浏览器的内置方法来解析JSON数据,以确保代码的安全性和数据的合法性。如果浏览器不支持`JSON.parse()`,你可以使用JSON.org所提供的库。 -记住,多数情况下,给setInterval()、setTimeout()和Function()构造函数传入字符串的情形和eval()类似,这种用法也是应当避免的,这一点非常重要,因为这些情形中JavaScript最终还是会执行传入的字符串参数: +值得一提的是,多数情况下,给`setInterval()`、`setTimeout()`和`Function()`构造函数传入字符串的情形和`eval()`类似,这种用法也是应当避免的,因为这些情形中JavaScript最终还是会执行传入的字符串参数: - // antipatterns + // 反模式 setTimeout("myFunc()", 1000); setTimeout("myFunc(1, 2, 3)", 1000); - // preferred + // 更好的方式 setTimeout(myFunc, 1000); setTimeout(function () { myFunc(1, 2, 3); }, 1000); -new Function()的用法和eval()非常类似,应当特别注意。这种构造函数的方式很强大,但往往被误用。如果你不得不使用eval(),你可以尝试用new Function()来代替。这有一个潜在的好处,在new Function()中运行的代码会在一个局部函数作用域内执行,因此源码中所有用var定义的变量不会自动变成全局变量。还有一种方法可以避免eval()中定义的变量转换为全局变量,即是将eval()包装在一个立即执行的匿名函数内(详细内容请参照第四章)。 +`new Function()`的用法和`eval()`非常类似,应当特别注意。这种构造函数的方式很强大,但经常会被误用。如果你不得不使用`eval()`,你可以尝试用`new Function()`来代替。这有一个潜在的好处,在`new Function()`中运行的代码会在一个局部函数作用域内执行,因此源码中所有用`var`定义的变量不会自动变成全局变量。还有一种方法可以避免`eval()`中定义的变量被转换为全局变量,即是将`eval()`包装在一个即时函数内(详细内容请参见第四章)。 -看一下这个例子,这里只有un成为了全局变量,污染了全局命名空间: +看一下这个例子,这里只有`un`成为全局变量污染了全局命名空间: console.log(typeof un);// "undefined" console.log(typeof deux); // "undefined" console.log(typeof trois); // "undefined" var jsstring = "var un = 1; console.log(un);"; - eval(jsstring); // logs "1" + eval(jsstring); // 打印出 "1" jsstring = "var deux = 2; console.log(deux);"; - new Function(jsstring)(); // logs "2" + new Function(jsstring)(); // 打印出 "2" jsstring = "var trois = 3; console.log(trois);"; (function () { eval(jsstring); - }()); // logs "3" + }()); // 打印出 "3" console.log(typeof un); // "number" console.log(typeof deux); // "undefined" console.log(typeof trois); // "undefined" -eval()和Function构造函数还有一个区别,就是eval()可以修改作用域链,而Function更像是一个沙箱。不管在什么地方执行Function,它只能看到全局作用域。因此它不会太严重的污染局部变量。在下面的示例代码中,eval()可以访问且修改其作用域之外的变量,而Function不能(注意,使用Function和new Function是完全一样的)。 +`eval()`和`Function()`构造函数还有一个区别,就是`eval()`可以修改作用域链,而`Function`更像是一个沙箱。不管在什么地方执行`Function()`,它都只能看到全局作用域。因此它不会太严重的污染局部变量。在下面的示例代码中,`eval()`可以访问并修改其作用域之外的变量,而`Function()`则不能(注意,使用`Function()`和`new Function()`是完全一样的)。 (function () { var local = 1; - eval("local = 3; console.log(local)"); // logs 3 - console.log(local); // logs 3 + eval("local = 3; console.log(local)"); // 打印出 3 + console.log(local); // 打印出 3 }()); (function () { var local = 1; - Function("console.log(typeof local);")(); // logs undefined + Function("console.log(typeof local);")(); // 打印出 undefined }()); - +================校对分割线================ ## 使用parseInt()进行数字转换 可以使用parseInt()将字符串转换为数字。函数的第二个参数是转换基数(译注:“基数”指的是数字进制的方式),这个参数通常被省略。但当字符串以0为前缀时转换就会出错,例如,在表单中输入日期的一个字段。ECMAScript3中以0为前缀的字符串会被当作八进制数处理(基数为8)。但在ES5中不是这样。为了避免转换类型不一致而导致的意外结果,应当总是指定第二个参数: From 3d308938c4e977a1957626a6e865124de54a24de Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 8 Mar 2013 09:44:24 +0800 Subject: [PATCH 042/145] =?UTF-8?q?=E4=BD=BF=E7=94=A8parseInt()=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E6=95=B0=E5=AD=97=E8=BD=AC=E6=8D=A2=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index a25e0d1..7d21152 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -486,25 +486,25 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 Function("console.log(typeof local);")(); // 打印出 undefined }()); -================校对分割线================ ## 使用parseInt()进行数字转换 -可以使用parseInt()将字符串转换为数字。函数的第二个参数是转换基数(译注:“基数”指的是数字进制的方式),这个参数通常被省略。但当字符串以0为前缀时转换就会出错,例如,在表单中输入日期的一个字段。ECMAScript3中以0为前缀的字符串会被当作八进制数处理(基数为8)。但在ES5中不是这样。为了避免转换类型不一致而导致的意外结果,应当总是指定第二个参数: +你可以使用`parseInt()`将字符串转换为数字。函数的第二个参数是进制参数,这个参数应该被指定,但却通常被省略。当字符串以0为前缀时转换就会出问题,例如,在表单中输入日期的一个字段。ECMAScript3中以0为前缀的字符串会被当作八进制数处理,这一点在ES5中已经有了改变。为了避免转换类型不一致而导致的意外结果,应当总是指定第二个参数: var month = "06", year = "09"; month = parseInt(month, 10); year = parseInt(year, 10); -在这个例子中,如果省略掉parseInt的第二个参数,比如parseInt(year),返回值是0,因为“09”被认为是八进制数(等价于parseInt(year,8)),而且09是非法的八进制数。 +在这个例子中,如果省略掉parseInt的第二个参数,比如`parseInt(year)`,返回的值是0,因为“09”被认为是八进制数(等价于`parseInt(year,8)`),但09是非法的八进制数。 字符串转换为数字还有两种方法: - +"08" // result is 8 - Number("08") // 8 + +"08" // 结果为8 + Number("08") // 结果为8 -这两种方法要比parseInt()更快一些,因为顾名思义parseInt()是一种“解析”而不是简单的“转换”。但当你期望将“08 hello”这类字符串转换为数字,则必须使用parseInt(),其他方法都会返回NaN。 +这两种方法要比`parseInt()`更快一些,因为顾名思义`parseInt()`是一种“解析”而不是简单的“转换”。但当你期望将“08 hello”这类字符串转换为数字,则必须使用`parseInt()`,其他方法都会返回NaN。 +================校对分割线================ ## 编码风格 From 4292b213e4886926151527da90ea1acd9b4bf6e9 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 8 Mar 2013 13:04:03 +0800 Subject: [PATCH 043/145] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=A7=84=E8=8C=83=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 83 +++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 46 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 7d21152..2203199 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -504,22 +504,19 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 这两种方法要比`parseInt()`更快一些,因为顾名思义`parseInt()`是一种“解析”而不是简单的“转换”。但当你期望将“08 hello”这类字符串转换为数字,则必须使用`parseInt()`,其他方法都会返回NaN。 -================校对分割线================ - -## 编码风格 +## 代码规范 -确立并遵守编码规范非常重要,这会让你的代码风格一致、可预测、可读性更强。团队新成员通过学习编码规范可以很快进入开发状态、并写出团队其他成员易于理解的代码。 +确立并遵守代码规范非常重要,这会让你的代码风格一致、可预测,并且可读性更强。团队新成员通过学习代码规范可以很快进入开发状态,并写出让团队其他成员易于理解的代码。 -在开源社区和邮件组中关于编码风格的争论一直不断(比如关于代码缩进,用tab还是空格?)。因此,如果你打算在团队内推行某种编码规范时,要做好应对各种反对意见的心理准备,而且要吸取各种意见,这对确立并一贯遵守某种编码规范是非常重要,而不是斤斤计较的纠结于编码规范的细节。 +在开源社区和邮件组中关于编代风格的争论一直不断。(比如关于代码缩进,用tab还是空格?)因此,如果你打算在团队内推行某种编码规范时,要做好应对各种反对意见的心理准备,而且要吸取各种意见。确定并遵守代码规范非常重要,任何一种规范都可以,这甚至比代码规范中的具体约定是怎么样的还要重要。 - ### 缩进 -代码没有缩进几乎就不能读了,而不一致的缩进更加糟糕,因为它看上去像是遵循了规范,真正读起来却磕磕绊绊。因此规范的使用缩进非常重要。 +代码如果没有缩进就几乎不能读了,而不一致的缩进会使情况更加糟糕,因为它看上去像是遵守了规范,但真正读起来却没那么顺利。因此规范地使用缩进非常重要。 -有些开发者喜欢使用tab缩进,因为每个人都可以根据自己的喜好来调整tab缩进的空格数,有些人则喜欢使用空格缩进,通常是四个空格,这都无所谓,只要团队每个人都遵守同一个规范即可,本书中所有的示例代码都采用四个空格的缩进写法,这也是JSLint所推荐的。 +有些开发者喜欢使用tab缩进,因为每个人都可以根据自己的喜好来调整tab缩进的空格数,有些人则喜欢使用空格缩进,通常是四个空格。这都无所谓,只要团队每个人都遵守同一个规范即可,本书中所有的示例代码都采用四个空格的缩进写法,这也是JSLint所推荐的。(译注:电子版中看到的是用tab缩进,本译文也保留使用tab缩进。) -那么到底什么应该缩进呢?规则很简单,花括号里的内容应当缩进,包括函数体、循环(do、while、for和for-in)体、if条件、switch语句和对象直接量里的属性。下面的代码展示了如何正确的使用缩进: +那么到底什么时候应该缩进呢?规则很简单,花括号里的内容应当缩进,包括函数体、循环(`do`、`while`、`for`和`for-in`)体、`if`语句、`switch`语句和对象字面量里的属性。下面的代码展示了如何正确地使用缩进: function outer(a, b) { var c = 1, @@ -541,47 +538,45 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 return inner; } - ### 花括号 -应当总是使用花括号,即使是在可省略花括号的时候也应当如此。从技术角度讲,如果if或for中只有一个语句,花括号是可以省略的,但最好还是不要省略。这让你的代码更加工整一致而且易于更新。 +在特定的语句中应当总是使用花括号,即便是在可省略花括号的情况下也应当如此。从技术角度讲,如果`if`或`for`中只有一个语句,花括号是可以省略的,但最好还是不要省略,这会让你的代码更加工整一致而且易于修改。 -假设有这样一段代码,for循环中只有一条语句,你可以省略掉这里的花括号,而且不会有语法错误: +假设有这样一段代码,`for`循环中只有一条语句,你可以省略掉这里的花括号,而且不会有语法错误: - // bad practice + // 不好的方式 for (var i = 0; i < 10; i += 1) alert(i); -但如果过了一段时间,你给这个循环添加了另一行代码? +但如果过了一段时间,你给这个循环添加了另一行代码会怎样? - // bad practice + // 不好的方式 for (var i = 0; i < 10; i += 1) alert(i); alert(i + " is " + (i % 2 ? "odd" : "even")); -第二个alert实际处于循环体之外,但这里的缩进会迷惑你。长远考虑最好还是写上花括号,即便是在只有一个语句的语句块中也应如此: +第二个`alert`实际上在循环体之外,但这里的缩进会让你迷惑。从长远考虑最好还是写上花括号,即便是在只有一个语句的语句块中也应如此: - // better + // 更好的方式 for (var i = 0; i < 10; i += 1) { alert(i); } 同理,if条件句也应当如此: - // bad + // 不好的方式 if (true) alert(1); else alert(2); - // better + // 更好的试 if (true) { alert(1); } else { alert(2); } - ### 左花括号的位置 开发人员对于左大括号的位置有着不同的偏好,在同一行呢还是在下一行? @@ -598,9 +593,9 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 } -在这个例子中,看起来只是个人偏好问题。但有时候花括号位置的不同则会影响程序的执行。因为JavaScript会“自动插入分号”。JavaScript对行结束时的分号并无要求,它会自动将分号补全。因此,当函数return语句返回了一个对象直接量,而对象的左花括号和return不在同一行时,程序的执行就和预想的不同了: +在这个例子中,这个问题只是个人偏好问题。但有时候花括号位置的不同会影响程序的执行,因为JavaScript会“自动插入分号”。JavaScript对行尾是否有分号并没有要求,它会自动将分号补全。因此,当函数的`return`语句返回了一个对象字面量,而对象的左花括号和`return`又不在同一行时,程序的执行就和预期的不同了: - // warning: unexpected return value + // 警告:返回值和预期的不同 function func() { return { @@ -608,18 +603,18 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 }; } -可以看出程序作者的意图是返回一个包含了name属性的对象,但实际情况不是这样。因为return后会填补一个分号,函数的返回值就是undefined。这段代码等价于: +可以看出程序作者的意图是返回一个包含了`name`属性的对象,但实际情况不是这样。因为return后会填补一个分号,函数的返回值就是undefined。这段代码等价于: - // warning: unexpected return value + // 警告:返回值和预期的不同 function func() { return undefined; - // unreachable code follows... + // 下面的代码不会运行… { name: "Batman" }; } -结论,总是使用花括号,而且总是将左花括号与上一条语句放在同一行: +总结一下好的写法,在特写的语句中总是使用花括号,并且总是将左花括号与上一条语句放在同一行: function func() { return { @@ -627,28 +622,25 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 }; } ->关于分号应当注意:和花括号一样,应当总是使用分号,尽管在JavaScript解析代码时会补全行末省略的分号。严格遵守这条规则,可以让代码更加严谨,同时可以避免前面例子中所出现的歧义。 +> 关于分号也值得注意:和花括号一样,应当总是使用分号,尽管在JavaScript解析代码时会补全行末省略的分号,但严格遵守这条规则,可以让代码更加严谨,同时可以避免前面例子中所出现的歧义。 - ### 空格 -空格的使用同样有助于改善代码的可读性和一致性。在写英文句子的时候,在逗号和句号后面会使用间隔。在JavaScript中,你可以按照同样的逻辑在表达式(相当于逗号)和语句结束(相对于完成了某个“想法”)后面添加间隔。 +空格的使用同样有助于改善代码的可读性和一致性。在写英文句子的时候,在逗号和句号后面会使用间隔,在JavaScript中,你可以按照同样的逻辑在表达式(相当于逗号)和语句结束(相对于完成了某个“想法”的表达)后面添加间隔。 适合使用空格的地方包括: -- for循环中的分号之后,比如 `for (var i = 0; i < 10; i += 1) {...}` -- for循环中初始化多个变量,比如 `for (var i = 0, max = 10; i < max; i += 1) {...}` -- 分隔数组项的逗号之后,`var a = [1, 2, 3];` -- 对象属性后的逗号以及名值对之间的冒号之后,`var o = {a: 1, b: 2};` -- 函数参数中,`myFunc(a, b, c)` -- 函数声明的花括号之前,`function myFunc() {}` -- 匿名函数表达式function之后,`var myFunc = function () {};` +- for循环中的分号之后,比如`for (var i = 0; i < 10; i += 1) {...}` +- for循环中初始化多个变量,比如`for (var i = 0, max = 10; i < max; i += 1) {...}` +- 用于分隔数组元素的逗号之后,比如`var a = [1, 2, 3];` +- 对象属性后的逗号以及名值对之间的冒号之后,比如`var o = {a: 1, b: 2};` +- 函数参数中,比如`myFunc(a, b, c)` +- 函数声明的花括号之前,比如`function myFunc() {}` +- 匿名函数表达式`function`之后,比如`var myFunc = function () {};` -另外,我们推荐在运算符和操作数之间添加空格。也就是说在+, -, *, =, <, >, <=, >=, ===, !==, &&, ||, +=符号前后都添加空格。 +另外,我们推荐在运算符和操作数之间也添加空格。也就是说在`+`、`-`、`*`、`=`、`<`、`>`、`<=`、`>=`、`===`、`!==`、`&&`、`||`、`+=`符号前后都添加空格。 - // generous and consistent spacing - // makes the code easier to read - // allowing it to "breathe" + // 适当且一致的空格给代码留了“呼吸空间”,使代码更易读 var d = 0, a = b + 1; if (a && b && c) { @@ -656,9 +648,7 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 a += d; } - // antipattern - // missing or inconsistent spaces - // make the code confusing + // 反模式,缺少或者不正确的空格使得代码不易读 var d= 0, a =b+1; if (a&& b&&c) { @@ -668,11 +658,12 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 最后,还应当注意,最好在花括号旁边添加空格: -- 在函数、if-else语句、循环、对象直接量的左花括号之前补充空格({) -- 在右花括号和else和while之间补充空格 +- 在函数、`if-else`语句、循环、对象字面量的左花括号之前补充空格 +- 在右花括号和`else`或者`while`之间补充空格 ->垂直空白的使用经常被我们忽略,你可以使用空行来将代码单元分隔开,就像文学作品中使用段落作分隔一样。 +> 垂直空白的使用经常被我们忽略,你可以使用空行来将代码单元分隔开,就像文学作品中使用段落进行分隔一样。 +================校对分割线================ ## 命名规范 From 76f6c50d33e7f8b6f4bd206547a0f36c9c024cd7 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 8 Mar 2013 13:19:28 +0800 Subject: [PATCH 044/145] =?UTF-8?q?=E5=91=BD=E5=90=8D=E8=A7=84=E8=8C=83=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 2203199..4fe95ee 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -663,57 +663,50 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 > 垂直空白的使用经常被我们忽略,你可以使用空行来将代码单元分隔开,就像文学作品中使用段落进行分隔一样。 -================校对分割线================ - ## 命名规范 -另外一种可以提升你代码的可预测性和可维护性的方法是采用命名规范。也就是说变量和函数的命名都遵照同种习惯。 +另外一种可以提升你代码的可预测性和可维护性的方法是采用命名规范。也就是说变量和函数的命名都遵守同样的习惯。 -下面是一些建议的命名规范,你可以原样采用,也可以根据自己的喜好作调整。同样,遵循规范要比规范本身更加重要。 +下面是一些建议的命名规范,你可以原样采用,也可以根据自己的喜好作调整。同样,遵循规范要比规范本身是什么样更加重要。 - -### 构造器命名中的大小写 +### 构造函数命名中的大小写 -JavaScript中没有类,但有构造函数,可以通过new来调用构造函数: +JavaScript中没有类,但有构造函数,可以通过`new`来调用构造函数: var adam = new Person(); -由于构造函数毕竟还是函数,不管我们将它用作构造器还是函数,当然希望只通过函数名就可分辨出它是构造器还是普通函数。 +由于构造函数毕竟还是函数,如果只通过函数名就可分辨出它是构造函数还是普通函数是非常有用的。 首字母大写可以提示你这是一个构造函数,而首字母小写的函数一般只认为它是普通的函数,不应该通过new来调用它: function MyConstructor() {...} function myFunction() {...} -下一章将介绍一些强制将函数用作构造器的编程模式,但遵守我们所提到的命名规范会更好的帮助程序员阅读源码。 +下一章将介绍一些强制将普通函数用作构造函数的编程模式,但遵守我们所提到的命名规范会更好的帮助程序员阅读源码。 - ### 单词分隔 -当你的变量名或函数名中含有多个单词时,单词之间的分隔也应当遵循统一的约定。最常见的做法是“驼峰式”命名,单词都是小写,每个单词的首字母是大写。 - -对于构造函数,可以使用“大驼峰式”命名,比如MyConstructor(),对于函数和方法,可以采用“小驼峰式”命名,比如myFunction(),calculateArea()和getFirstName()。 +当你的变量名或函数名中含有多个单词时,单词之间的分隔也应当遵循统一的规范。最常见的是“驼峰式”(camel case)命名,单词都是小写,每个单词的首字母是大写。 -那么对于那些不是函数的变量应当如何命名呢?变量名通常采用小驼峰式命名,还有一个不错的做法是,变量所有字母都是小写,单词之间用下划线分隔,比如,first_name,favorite_bands和old_company_name,这种方法可以帮助你区分函数和其他标识符——原始数据类型或对象。 +对于构造函数,可以使用“大驼峰式”(upper camel case)命名,比如`MyConstructor()`,对于函数和方法,可以采用“小驼峰式”(lower camel case)命名,比如`myFunction()`、`calculateArea()`和`getFirstName()`。 -ECMAScript的属性和方法均使用Camel标记法,尽管多字的属性名称是罕见的(正则表达式对象的lastIndex和ignoreCase属性)。 +那么对于那些不是函数的变量应当如何命名呢?变量名通常采用小驼峰式命名,还有一个不错的做法是,变量所有字母都是小写,单词之间用下划线分隔,比如,`first_name`、`favorite_bands`和`old_company_name`,这种方法可以帮助你区分函数和其他标识符如原始类型数据或对象。 -在ECMAScript中的属性和方法均使用驼峰式命名,尽管包含多单词的属性名称(正则表达式对象中的lastIndex和ignoreCase)并不常见。 +ECMAScript的属性和方法均使用驼峰式命名,尽管包含多个单词的属性名称并不多见(正则表达式对象的`lastIndex`和`ignoreCase`属性)。 - ### 其他命名风格 有时开发人员使用命名规范来弥补或代替语言特性的不足。 -比如,JavaScript中无法定义常量(尽管有一些内置常量比如Number.MAX_VALUE),所以开发者都采用了这种命名习惯,对于那些程序运行周期内不会更改的变量使用全大写字母来命名。比如: +比如,JavaScript中无法定义常量(尽管有一些内置常量比如`Number.MAX_VALUE`),所以开发者都采用了一种命名规范,对于那些程序运行周期内不会更改的变量使用全大写字母来命名。比如: - // precious constants, please don't touch + // 常量,请勿修改 var PI = 3.14, MAX_WIDTH = 800; -除了使用大写字母的命名方式之外,还有另一种命名规约:全局变量都大写。这种命名方式和“减少全局变量”的约定相辅相成,并让全局变量很容易辨认。 +除了使用大写字母的命名方式之外,还有另一种命名规范:全局变量全大写。这种命名方式和“减少全局变量”的约定相辅相成,并让全局变量很容易辨认。 -除了常量和全局变量的命名惯例,这里讨论另外一种命名惯例,即私有变量的命名。尽管在JavaScript是可以实现真正的私有变量的,但开发人员更喜欢在私有成员或方法名之前加上下划线前缀,比如下面的例子: +除了常量和全局变量的命名规范,这里讨论另外一种命名规范,即私有变量的命名。尽管在JavaScript是可以实现真正的私有变量的,但开发人员更喜欢在私有成员或方法名之前加上下划线前缀,比如下面的例子: var person = { getName: function () { @@ -727,14 +720,15 @@ ECMAScript的属性和方法均使用Camel标记法,尽管多字的属性名 } }; -在这个例子中,getName()的身份是一个公有方法,属于稳定的API,而_getFirst()和_getLast()则是私有方法。尽管这两个方法本质上和公有方法无异,但在方法名前加下划线前缀就是为了警告用户不要直接使用这两个私有方法,因为不能保证它们在下一个版本中还能正常工作。JSLint会对私有方法作检查,除非设置了JSLint的nomen选项为false。 +在这个例子中,`getName()`是一个公有方法,是确定的API的一部分,而`_getFirst()`和`_getLast()`则是私有方法。尽管这两个方法本质上和公有方法没有区别,但在方法名前加下划线前缀就是为了告知用户不要直接使用这两个私有方法,因为不能保证它们在下一个版本中还能正常工作。JSLint会对私有方法作检查,除非设置了JSLint的`nomen`选项为`false`。 -下面介绍一些_private风格写法的变种: +下面介绍一些`_private`风格写法的变种: -- 在名字尾部添加下划下以表明私有,比如`name_`和`getElements_()` -- 使用一个下划线前缀表明受保护的属性_protected,用两个下划线前缀表明私有属性__private +- 在名字尾部添加下划线以表明私有,比如`name_`和`getElements_()` +- 使用一个下划线前缀表明受保护的属性`_protected`,用两个下划线前缀表明私有属性`__private` - 在Firefox中实现了一些非标准的内置属性,这些属性在开头和结束都有两个下划线,比如`__proto__`和`__parent__` +================校对分割线================ ## 书写注释 From a35d063339d88184355a182a47a1d6d8d38d0800 Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 21 Mar 2013 23:41:19 +0800 Subject: [PATCH 045/145] =?UTF-8?q?=E4=B9=A6=E5=86=99=E6=B3=A8=E9=87=8A=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 4fe95ee..f88db13 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -728,19 +728,17 @@ ECMAScript的属性和方法均使用驼峰式命名,尽管包含多个单词 - 使用一个下划线前缀表明受保护的属性`_protected`,用两个下划线前缀表明私有属性`__private` - 在Firefox中实现了一些非标准的内置属性,这些属性在开头和结束都有两个下划线,比如`__proto__`和`__parent__` -================校对分割线================ - -## 书写注释 +## 写注释 -写代码就要写注释,即便你认为你的代码不会被别人读到。当你对一个问题非常熟悉时,你会很快找到问题代码,但当过了几个星期后再来读这段代码,则需要绞尽脑汁的回想代码的逻辑。 +在写代码时,即便你认为你的代码不会被别人读到,也应该写好注释。因为当你对一个问题非常熟悉时,你会非常明白这些代码的作用,但当过了几个星期后再来读这段代码时,则需要绞尽脑汁的回想这些代码在干什么。 -你不必对显而易见的代码作过多的注释:每个变量和每一行都作注释。但你需要对所有的函数、他们的参数和返回值补充注释,对于那些有趣的或怪异的算法和技术也应当配备注释。对于阅读你的代码的其他人来说,注释就是一种提示,只要阅读注释、函数名以及参数,就算不读代码也能大概理解程序的逻辑。比如,这里有五到六行代码完成了某个功能,如果提供了一行描述这段代码功能的注释,读程序的人就不必再去关注代码的细节实现了。代码注释的写法并没有硬性规定,有些代码片段(比如正则表达式)的确需要比代码本身还多的注释。 +你不必对那些浅显易懂的代码写过多的注释,比如每个变量、每一行都写注释。但你应该对所有的函数、它们的参数和返回值进行注释,除此之外,对于那些值得注意的或是比较怪异的算法和技术也应当写好注释。对于其他阅读你代码的人来说,注释就是一种提示,只要阅读注释、函数名和参数,就算不读其它部分的代码也能大概理解程序的逻辑。比如,这里有五六行代码完成了某个功能,如果有一行描述这段代码功能的注释,读程序的人就不必再去关注代码的实现细节了。代码注释的写法并没有硬性规定,但有些代码片段(比如正则表达式)需要比代码本身更多的注释。 ->由于过时的注释会带来很多误导,这比不写注释还糟糕。因此保持注释时刻更新的习惯非常重要,尽管对很多人来说这很难做到。 +> 过时的注释会造成误导,这比不写注释还要糟糕。保持注释的状态为最新的习惯非常重要,尽管对很多人来说这很难做到。 -在下一小节我们会讲到,注释可以自动生成文档。 +在下一小节我们会讲到,利用注释可以自动生成文档。 - +================校对分割线================ ## 书写API文档 很多人都觉得写文档是一件枯燥且吃力不讨好的事情,但实际情况不是这样。我们可以通过代码注释自动生成文档,这样就不用再去专门写文档了。很多人觉得这是一个不错的点子,因为根据某些关键字和格式化的文档自动生成可阅读的参考手册本身就是“某种编程”。 From bb92e228803b92dd74b4ed1a26350d7873e19021 Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 21 Mar 2013 23:49:48 +0800 Subject: [PATCH 046/145] =?UTF-8?q?=E5=86=99API=E6=96=87=E6=A1=A3=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index f88db13..72d2b8f 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -738,26 +738,25 @@ ECMAScript的属性和方法均使用驼峰式命名,尽管包含多个单词 在下一小节我们会讲到,利用注释可以自动生成文档。 -================校对分割线================ -## 书写API文档 +## 写API文档 -很多人都觉得写文档是一件枯燥且吃力不讨好的事情,但实际情况不是这样。我们可以通过代码注释自动生成文档,这样就不用再去专门写文档了。很多人觉得这是一个不错的点子,因为根据某些关键字和格式化的文档自动生成可阅读的参考手册本身就是“某种编程”。 +很多人都觉得写文档是一件很枯燥而且吃力不讨好的事情,但实际情况并不是这样。我们可以通过代码注释自动生成文档,这样就不用再去专门写文档了。很多人觉得这是一个不错的点子,因为根据某些关键字和特定的格式自动生成可阅读的参考手册本身就是“某种编程”。 -传统的APIdoc诞生自Java世界,这个工具名叫“javadoc”,和Java SDK(软件开发工具包)一起提供。但这个创意迅速被其他语言借鉴。JavaScript领域有两个非常优秀的开源工具,它们是JSDoc Toolkit(http://code.google.com/p/jsdoc-toolkit/ )和YUIDoc(http://yuilibrary.com/projects/yuidoc )。 +最早利用注释生成API文档的工具诞生自Java业界,这个工具名叫“javadoc”,和Java SDK(软件开发工具包)一起提供,但这个创意迅速被其他语言借鉴。JavaScript领域有两个非常优秀的开源工具,它们是JSDoc Toolkit()和YUIDoc()。 -生成API文档的过程包括: +生成API文档的过程: -- 以特定的格式来组织书写源代码 +- 以特定的格式来写代码 - 运行工具来对代码和注释进行解析 - 发布工具运行的结果,通常是HTML页面 -你需要学习这种特殊的语法,包括十几种标签,写法类似于: +这种语法包括十几种标签(tag),写法类似于: /** * @tag value */ -比如这里有一个函数reverse(),可以对字符串进行反序操作。它的参数和返回值都是字符串。给它补充注释如下: +比如这里有一个函数`reverse()`,可以对字符串进行反序操作。它的参数和返回值都是字符串。给它补充注释如下: /** * Reverse a string @@ -770,9 +769,9 @@ ECMAScript的属性和方法均使用驼峰式命名,尽管包含多个单词 return output; }; -可以看到,@param是用来说明输入参数的标签,@return是用来说明返回值的标签,文档生成工具最终会为将这种带注释的源代码解析成格式化好的HTML文档。 +如你所见,`@param`是用来说明输入参数的标签,`@return`是用来说明返回值的标签,文档生成工具最终会将这种带注释的源代码解析成HTML文档。 - +================校对分割线================ ### 一个例子:YUIDoc YUIDoc最初的目的是为YUI库(Yahoo! User Interface)生成文档,但也可以应用于任何项目,为了更充分的使用YUIDoc你需要学习它的注释规范,比如模块和类的写法(当然在JavaScript中是没有类的概念的)。 From b7d4dc338cb0eb963adf5c536d4793ba247b7293 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 23 Mar 2013 12:06:02 +0800 Subject: [PATCH 047/145] =?UTF-8?q?=E7=A4=BA=E4=BE=8B=EF=BC=9AYUIDoc=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 48 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 72d2b8f..42f93a8 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -771,24 +771,23 @@ ECMAScript的属性和方法均使用驼峰式命名,尽管包含多个单词 如你所见,`@param`是用来说明输入参数的标签,`@return`是用来说明返回值的标签,文档生成工具最终会将这种带注释的源代码解析成HTML文档。 -================校对分割线================ -### 一个例子:YUIDoc +### 示例:YUIDoc -YUIDoc最初的目的是为YUI库(Yahoo! User Interface)生成文档,但也可以应用于任何项目,为了更充分的使用YUIDoc你需要学习它的注释规范,比如模块和类的写法(当然在JavaScript中是没有类的概念的)。 +YUIDoc的初衷是为YUI(Yahoo! User Interface)库生成文档,但其实它也可以应用于任何项目。为了更充分的使用YUIDoc,你需要学习它的注释规范,比如模块和类的写法。(尽管在JavaScript中其实是没有类的概念的)。 让我们看一个用YUIDoc生成文档的完整例子。 -图2-1展示了最终生成的文档的模样,你可以根据项目需要随意定制HTML模板,让生成的文档更加友好和个性化。 +图2-1展示了最终生成的文档的样子,你可以根据项目需要定制HTML模板,让生成的文档更加友好和个性化。 -这里同样提供了在线的demo,请参照 http://jspatterns.com/book/2/。 +这里提供了在线的demo,请参照。 这个例子中所有的应用作为一个模块(myapp)放在一个文件里(app.js),后续的章节会更详细的介绍模块,现在只需知道用可以用一个YUIDoc的标签来表示模块即可。 图2-1 YUIDoc生成的文档 -![pic](http://img02.taobaocdn.com/tps/i2/T1fSCgXdBsXXXXXXXX-781-647.png) +![YUIDoc生成的文档](./Figure/cahpter2/2-1.jpg) -app.js的开始部分: +`app.js`的开始部分: /** * My JavaScript application @@ -800,7 +799,7 @@ app.js的开始部分: var MYAPP = {}; -紧接着定义了一个包含两个方法的对象math_stuff,这两个方法分别是sum()和multi(): +紧接着定义了一个包含两个方法的对象`math_stuff`,这两个方法分别是`sum()`和`multi()`: /** * A math utility @@ -833,29 +832,29 @@ app.js的开始部分: } }; -这样就结束了第一个“类”的定义,注意粗体表示的标签。 +这样就完成了第一个“类”的定义,注意以下标签: -@namespace +- `@namespace` -指向你的对象的全局引用 + 包含对象的全局引用 -@class +- `@class` -代表一个对象或构造函数的不恰当的称谓(JavaScript中没有类) + 代表一个对象或构造函数(JavaScript中没有类) -@method +- `@method` -定义对象的方法,并指定方法的名称 + 定义对象的方法,并指定方法的名称 -@param +- `@param` -列出函数需要的参数,参数的类型放在一对花括号内,跟随其后的是参数名和描述 + 列出函数需要的参数,参数的类型放在一对花括号内,后面跟参数名和描述 -@return +- `@return` -和@param类似,用以描述方法的返回值,可以不带名字 + 和@param类似,用以描述方法的返回值,可以不带名字 -我们用构造函数来实现第二个“类”,给这个类的原型添加一个方法,能够体会到YUIDoc采用了不同的方式来创建对象: +我们来实现第二个“类”,使用一个构造函数,并给这个构造函数的原型添加一个方法,看看YUIDoc在面对不同的对象创建方式时是如何工作的: /** * Constructs Person objects @@ -889,13 +888,14 @@ app.js的开始部分: return this.first_name + ' ' + this.last_name; }; -在图2-1中可以看到生成的文档中Person构造函数的生成结果,粗体的部分是: +在图2-1中可以看到生成的文档中`Person`构造函数的生成结果,值得注意的部分是: -- @constructor 暗示了这个“类”其实是一个构造函数 -- @prototype 和 @type 用来描述对象的属性 +- `@constructor` 说明这个“类”其实是一个构造函数 +- `@prototype` 和 `@type` 用来描述对象的属性 -YUIDoc工具是语言无关的,只解析注释块,而不是JavaScript代码。它的缺点是必须要在注释中指定属性、参数和方法的名字,比如,@property first_name。好处是一旦你熟练掌握YUIDoc,就可以用它对任何语言源码进行注释的文档化。 +YUIDoc工具是与语言无关的,只解析注释块,而不是JavaScript代码。它的缺点是必须要在注释中指定属性、参数和方法的名字,比如,`@property first_name`。好处是一旦你熟练掌握YUIDoc,就可以用它对任何语言源码生成文档。 +================校对分割线================ ## 编写易读的代码 From 68f6c4b0929614f4131ab8e5056e9c9d68dfc0c9 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 23 Mar 2013 12:38:01 +0800 Subject: [PATCH 048/145] =?UTF-8?q?=E7=BC=96=E5=86=99=E6=98=93=E8=AF=BB?= =?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81=20=E6=A0=A1=E5=AF=B9=E5=AE=8C?= =?UTF-8?q?=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 42f93a8..d035af9 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -895,22 +895,21 @@ YUIDoc的初衷是为YUI(Yahoo! User Interface)库生成文档,但其实 YUIDoc工具是与语言无关的,只解析注释块,而不是JavaScript代码。它的缺点是必须要在注释中指定属性、参数和方法的名字,比如,`@property first_name`。好处是一旦你熟练掌握YUIDoc,就可以用它对任何语言源码生成文档。 -================校对分割线================ - ## 编写易读的代码 -这种将APIDoc格式的代码注释解析成API参考文档的做法看起来很偷懒,但还有另外一个目的,通过代码重审来提高代码质量。 +这种编写注释块来生成API文档的做法可不仅仅是为了偷懒,它还有另外一个作用,就是通过回头重看代码来提高代码质量。 -很多作者或编辑会告诉你“编辑非常重要”,甚至是写一本好书或好文章最最重要的步骤。将想法落实在纸上形成草稿只是第一步,草稿给读者提的信息往往重点不明晰、结构不合理、或不符合循序渐进的阅读习惯。 +随便一个作者或者编辑都会告诉你“编辑非常重要”,甚至是写一本好书或好文章最最重要的步骤。将想法落实在纸上形成草稿只是第一步,草稿确实可以给读者提供不少信息,但往往还不是重点最明晰、结构最合理、最符合阅读习惯的呈现形式。 -对于编程也是同样的道理,当你坐下来解决一个问题的时候,这时的解决方案只是一种“草案”,尽管能正常工作,但是不是最优的方法呢?是不是可读性好、易于理解、可维护佳或容易更新?当一段时间后再来review你的代码,一定会发现很多需要改进的地方,需要重新组织代码或删掉多余的内容等等。这实际上就是在“整理”你的代码了,可以很大程度提高你的代码质量。但事情往往不是这样,我们常常承受着高强度的工作压力,根本没有时间来整理代码,因此通过代码注释写文档其实是不错的机会。 +编程也是同样的道理,当你坐下来解决一个问题的时候,这时的解决方案只是一种“草案”,尽管能正常工作,但是不是最优的方法呢?是不是可读性好、易于理解、可维护和更新?假设当你过一段时间后再来回头看你的代码,一定会发现很多需要改进的地方,比如需要重新组织代码或删掉多余的内容等等。这实际上就是在“整理”你的代码了,可以很大程度上提高你的代码质量。但事实却不那么如愿,我们常常承受着高强度的工作,根本没有时间来整理代码,因此通过代码注释来写文档其实是个不错的机会。 -往往在写注释文档的时候,你会发现很多问题。你也会重新思考源代码中不合理之处,比如,某个方法中的第三个参数比第二个参数更常用,第二个参数多数情况下取值为true,因此就需要对这个方法接口进行适当的改造和包装。 +你往往会在写注释文档的时候发现很多问题,也会重新思考代码中的不合理之处,比如,某个方法中的第三个参数比第二个参数更常用,第二个参数多数情况下取值为`true`,因此就需要对这个方法进行适当的改造和包装。 -写出易读的代码(或API),是指别人能轻易读懂程序的思路。所以你需要采用更好的思路来解决手头的问题。 +写出易读的代码(或API),是指写代码时要有让别人能轻易读懂的意识。带着这个意识,你就需要不断思考采用更好的方法来解决手头的问题。 -尽管我们认为“草稿”不甚完美,但至少也算“抱佛脚”的权宜之计,一眼看上去是有点“草”,不过也无所谓,特别是当你处理的是一个关键项目时(会有人命悬与此)。其实你应当扔掉你所给出的第一个解决方案,虽然它是可以正常工作的,但毕竟是一个草率的方案,不是最佳方案。你给出的第二个方案会更加靠谱,因为这时你对问题的理解更加透彻。第二个方案不是简单的复制粘贴之前的代码,也不能投机取巧寻找某种捷径。 +说回“草稿”的问题,也算是“抱佛脚”的权宜之计,一眼看上去是有点“草”,不过至少是有用的,特别是当你处理的是一个关键项目时(比如人命关天时)。一个合适的思路是,你应当始终扔掉你所给出的第一个解决方案,虽然它是可以正常工作的,但毕竟是一个草稿,是一种仅用于验证解决问题可行性的方案。事实上,第二个方案往往会更好,因为这时你对问题的理解会更加透彻。在产生第二个方案的过程中,不要允许自己去复制粘贴之前的代码,这有助于阻止自己投机取巧利用之前的捷径,最后产生不完美的方案。 +================校对分割线================ ## 相互评审 From 651cc6997089d4a0089b686057f57dd2fd031ab9 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 23 Mar 2013 13:27:06 +0800 Subject: [PATCH 049/145] =?UTF-8?q?=E7=9B=B8=E4=BA=92=E8=AF=84=E5=AE=A1=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index d035af9..9c2f1a5 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -909,20 +909,19 @@ YUIDoc工具是与语言无关的,只解析注释块,而不是JavaScript代 说回“草稿”的问题,也算是“抱佛脚”的权宜之计,一眼看上去是有点“草”,不过至少是有用的,特别是当你处理的是一个关键项目时(比如人命关天时)。一个合适的思路是,你应当始终扔掉你所给出的第一个解决方案,虽然它是可以正常工作的,但毕竟是一个草稿,是一种仅用于验证解决问题可行性的方案。事实上,第二个方案往往会更好,因为这时你对问题的理解会更加透彻。在产生第二个方案的过程中,不要允许自己去复制粘贴之前的代码,这有助于阻止自己投机取巧利用之前的捷径,最后产生不完美的方案。 -================校对分割线================ - -## 相互评审 +## 同事评审(Peer Reviews) -另外一种可以提高代码质量的方法是组织相互评审。同行的评审很正式也很规范,即便是求助于特定的工具,也不失是一种开发生产线上值得提倡的步骤。但你可能觉得没有时间去作代码互审,没关系,你可以让坐在你旁边的同事读一下你的代码,或者和她(译注:注意是“她”而不是“他”)一起过一遍你的代码。 +另外一种可以提高代码质量的方法是组织相互评审。同事评审可以用一些工具辅助,可以很正式很规范,也是一种开发流程中值得提倡的步骤。你可能觉得没有时间去作代码互相评审,没关系,你可以让坐在你旁边的同事读一下你的代码,或者和她(译注:注意是“她”而不是“他”)一起过一遍你的代码。 -同样,当你在写APIDoc或任何其他文档的时候,同行的评审能帮助你的产出物更加清晰,因为你写的文档是让别人读的,你必须确保别人能理解你所作的东西。 +同样,当你在写API文档或者其他文档的时候,同事评审能让你的产出物更加清晰,因为你写的文档是本来就是让别人读的,你得让别人通过文档知道你所做的东西。 -同行的评审是一种非常不错的习惯,不仅仅是因为它能让代码变得更好,更重要的,在评审的过程中,评审人和代码作者通过分享和讨论,两人都能取长补短、相互促进。 +同事评审是一种很好的实践,不仅仅是因为它能让代码变得更好,更重要的是,在评审的过程中,评审人和代码作者通过分享和讨论,两人都能取长补短、相互促进。 -如果你的团队只有你一个开发人员,找不出第二个人能给你作代码评审,这也没关系。你可以通过将你的代码片段开源,或把有意思的代码片段贴在博客中,会有人对你的代码感兴趣的。 +如果你的团队只有你一个开发人员,找不出第二个人能给你作代码评审,这也没关系。你可以通过将你的代码片段开源,或把有意思的代码片段贴在博客中,让全世界的人为你评审。 -另外一个非常好的习惯是使用版本管理工具(CVS,SVN或Git),一旦有人修改并提交了代码,都会发邮件通知组内成员。虽然大部分邮件都进入了垃圾箱,但总是会碰巧有人在工作间隙看到你所提交的代码,并对代码做出一些评价。 +另外一个很好的实践是使用版本管理工具(CVS、SVN或Git),一旦有人修改并提交了代码,就会发邮件通知组内成员。虽然大部分邮件都进入了垃圾箱,但总是会碰巧有人在工作间隙看到你所提交的代码,并对代码做出一些评价。 +================校对分割线================ ## 生产环境中的代码压缩(Minify) From 1472689fb3aaf873418e8ca28d79ee6813d84188 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 23 Mar 2013 13:50:57 +0800 Subject: [PATCH 050/145] =?UTF-8?q?=E5=8F=91=E5=B8=83=E6=97=B6=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=8E=8B=E7=BC=A9=20=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 9c2f1a5..767c5e1 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -921,25 +921,24 @@ YUIDoc工具是与语言无关的,只解析注释块,而不是JavaScript代 另外一个很好的实践是使用版本管理工具(CVS、SVN或Git),一旦有人修改并提交了代码,就会发邮件通知组内成员。虽然大部分邮件都进入了垃圾箱,但总是会碰巧有人在工作间隙看到你所提交的代码,并对代码做出一些评价。 -================校对分割线================ - -## 生产环境中的代码压缩(Minify) +## 发布时的代码压缩(Minify) -这里所说的代码压缩(Minify)是指去除JavaScript代码中的空格、注释以及其他不必要的部分,用以减少JavaScript文件的体积,降低网络带宽损耗。我们通常使用类似YUICompressor(Yahoo!)或Closure Compiler(Google)的压缩工具来为网页加载提速。对于生产环境(译注:“生产环境”指的是项目上线后的正式环境)中的脚本是需要作压缩的,压缩后的文件体积能减少至原来的一半以下。 +这里所说的代码压缩(Minify)是指去除JavaScript代码中的空格、注释以及其他不必要的部分,用以减少JavaScript文件的体积,降低网络带宽消耗。我们通常使用压缩工具来进行压缩,比如YUICompressor(Yahoo!)或Closure Compiler(Google),这可以减少页面加载时间。压缩用于发布的的脚本是很重要的,压缩后的文件体积能减少至原来的一半以下。 -下面这段代码是压缩后的样子(这段代码是YUI2库中的Event模块): +下面这段代码是压缩后的样子(这段代码是YUI2库中的事件模块): YAHOO.util.CustomEvent=function(D,C,B,A){this.type=D;this.scope=C||window;this.silent =B;this.signature=A||YAHOO.util.CustomEvent.LIST;this.subscribers=[];if(!this.silent) {}var E="_YUICEOnSubscribe";if(D!==E){this.subscribeEvent=new YAHOO.util.CustomEvent(E,this,true);}... -除了去除空格、空行和注释之外,压缩工具还能缩短命名的长度(前提是保证代码的安全),比如这段代码中的参数A、B、C、D。压缩工具只会重命名局部变量,因为更改全局变量会破坏代码的逻辑。这也是要尽量使用局部变量的原因。如果你使用的全局变量是对DOM节点的引用,而且程序中多次用到,最好将它赋值给一个局部变量,这样能提高查找速度,代码也会运行的更快,此外还能提高压缩比、加快下载速度(译注:在服务器开启Gzip的情况下,对下载速度的影响几乎可以忽略不计)。 +除了去除空格、空行和注释之外,压缩工具还能缩短命名的长度(在保证代码安全的前提下),比如这段代码中的参数`A`、`B`、`C`、`D`。压缩工具只会重命名局部变量,因为更改全局变量会破坏代码的逻辑,这也是要尽量使用局部变量的原因。如果你使用的全局变量是对DOM节点的引用,而且程序中多次用到,那么最好将它赋值给一个局部变量,这样能提高查找速度,代码也会运行的更快,此外还能提高压缩比、加快下载速度。 -补充说明一下,Goolge Closure Compiler还会对全局变量进行压缩(在“高级”模式中),这是很危险的,且对编程规范的要求非常苛刻。它的好处是压缩比非常高。 +补充说明一下,Goolge Closure Compiler还会为了更高的压缩比对全局变量进行压缩(在“高级”模式中),这是很危险的,且对编程规范的要求非常苛刻。 -对生产环境的脚本做压缩是相当重要的步骤,它能提升页面性能,你应当使用工具来完成压缩。千万不要试图手写“压缩好的”代码,你应当坚持使用语义化的变量命名,并保留足够的空格、缩进和注释。你写的代码是需要被人阅读的,所以应当将注意力放在代码可读性和可维护性上,代码压缩的工作交给工具去完成。 +对用于生产环境的脚本做压缩是非常重要的步骤,因为它能提升页面性能,但你应当将这个过程交给工具来完成。千万不要试图手写“压缩好的”代码,你应当在编写代码时坚持使用语义化的变量命名,并保留足够的空格、缩进和注释。你写的代码是需要被人阅读的,所以应当将注意力放在代码可读性和可维护性上,将代码压缩的工作交给工具去完成。 +================校对分割线================ ## 运行 JSLint From ead3ae6659d4d62fc25d16ff6f6c7c9bd451479f Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 23 Mar 2013 14:09:36 +0800 Subject: [PATCH 051/145] =?UTF-8?q?=E7=AC=AC=E4=BA=8C=E7=AB=A0=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 767c5e1..ff1decb 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -1,6 +1,6 @@ # 第二章 概要 -本章将概要介绍一些编写高质量JavaScript的最佳实践、模式和习惯,比如避免全局变量、使用单var声明、预缓存循环中的length、遵守编码约定等等。本章还包括一些编程习惯,这些习惯跟具体的代码关系不大,而是更多关注代码创建的总体过程,包括撰写API文档、code review以及使用JSLint。这些习惯和最佳实践可以帮助你写出更好更易读和可维护性更好的代码,当几个月或数年后你再重读你的代码时,就会深有体会了。 +本章将概要介绍一些编写高质量JavaScript的最佳实践、模式和习惯,比如避免全局变量、使用单`var`声明、预缓存循环中的`length`、遵守编码约定等等。本章还包括一些编程习惯,这些习惯跟具体的代码关系不大,而是更多关注代码创建的总体过程,包括撰写API文档、同事评审以及使用JSLint。这些习惯和最佳实践可以帮助你写出更好更易读和可维护性更好的代码,当几个月或数年后你再重读你的代码时,就会深有体会了。 ## 编写可维护的代码 @@ -144,7 +144,7 @@ JavaScript使用函数来管理作用域,在一个函数内定义的变量称 - 提醒你不要忘记声明变量,顺便减少潜在的全局变量 - 代码量更少(输入代码更少且更易做代码优化) -单var模式看起来像这样: +单`var`模式看起来像这样: function func() { var a = 1, @@ -243,7 +243,7 @@ JavaScript允许在函数的任意地方写任意多个`var`语句,但它们 需要注意的是,当你在循环过程中需要修改这个元素集合(比如增加DOM元素)时,你可能需要更新`length`。 -按照“单var模式”,你可以将`var`提到循环的外部,比如: +按照“单`var`模式”,你可以将`var`提到循环的外部,比如: function looper() { var i = 0, @@ -255,7 +255,7 @@ JavaScript允许在函数的任意地方写任意多个`var`语句,但它们 } } -当你越来越依赖“单var模式”时,带来的好处就是提高了代码的一致性。而缺点则是在重构代码的时候不能直接复制粘贴一个循环体,比如,你正在将某个循环从一个函数复制至另外一个函数中,那么必须确保`i`和`max`也复制到新函数里,并且需要从旧函数中将这些没用的变量删除掉。 +当你越来越依赖“单`var`模式”时,带来的好处就是提高了代码的一致性。而缺点则是在重构代码的时候不能直接复制粘贴一个循环体,比如,你正在将某个循环从一个函数复制至另外一个函数中,那么必须确保`i`和`max`也复制到新函数里,并且需要从旧函数中将这些没用的变量删除掉。 最后一个需要对循环做出调整的地方是将i++替换成为下面两者之一: @@ -938,33 +938,29 @@ YUIDoc工具是与语言无关的,只解析注释块,而不是JavaScript代 对用于生产环境的脚本做压缩是非常重要的步骤,因为它能提升页面性能,但你应当将这个过程交给工具来完成。千万不要试图手写“压缩好的”代码,你应当在编写代码时坚持使用语义化的变量命名,并保留足够的空格、缩进和注释。你写的代码是需要被人阅读的,所以应当将注意力放在代码可读性和可维护性上,将代码压缩的工作交给工具去完成。 -================校对分割线================ - -## 运行 JSLint +## 运行JSLint -在上一章我们已经介绍了JSLint,这里我们介绍更多的使用场景。对你的代码进行JSLint检查是非常好的编程习惯,你应该相信这一点。 +在上一章我们已经介绍了JSLint,本章中也提到了数次。到现在你应该已经相信用JSLint检查你的代码是一种好的编程模式了。 -JSLint的检查点都有哪些呢?它会对本章讨论过的一些模式(单var模式、parseInt()的第二个参数、总是使用花括号)做检查。JSLint还包括其他方面的检查: +JSLint的检查点都有哪些呢?它会对本章讨论过的一些模式(单`var`模式、`parseInt()`的第二个参数、总是使用花括号)做检查。JSLint还包括其他方面的检查: -- 不可达代码 -- 在使用变量之前需要声明 +- 不可达代码(译注:指永远不可能运行的代码) +- 变量在声明之前被使用 - 不安全的UTF字符 -- 使用void、with、和eval +- 使用`void`、`with`或者`eval` - 无法正确解析的正则表达式 -JSLint是基于JavaScript实现的(它是可以通过JSLint检查的),它提供了在线工具,也可以下载使用,可以运行于很多种平台的JavaScript解析器。你可以将源码下载后在本地运行,支持的环境包括WSH(Windows Scripting Host,Windows)、JSC(JavaScriptCore,MacOSX)或Rhino(Mozilla开发的JavaScript引擎)。 +JSLint是基于JavaScript实现的(它自己的代码是可以通过JSLint检查的),它提供了在线工具,也可以下载使用,可以运行于很多种平台的JavaScript解析器。你可以将源码下载后在本地运行,支持的环境包括WSH(Windows Scripting Host,Windows)、JSC(JavaScriptCore,MacOSX)或Rhino(Mozilla开发的JavaScript引擎)。 -可以将JSLint下载后和你的代码编辑器配置在一起,着是一个不错的注意,这样每次你保存代码的时候都会自动执行代码检查(比如配置快捷键)。 +将JSLint下载后和你的代码编辑器配置在一起是个很不错的主意,这样每次你保存代码的时候都会自动执行代码检查。(为它配置一个快捷键也很有用)。 - ## 小结 -本章我们讲解了编写可维护性代码的含义,本章的讨论非常重要,它不仅关系着软件项目的成功与否,还关系到参与项目的工程师的“精神健康”和“幸福指数”。随后我们讨论了一些最佳实践和模式,它们包括: +本章我们讨论了编写可维护性代码的意义,它不仅关系着软件项目的成功与否,还关系到参与项目的工程师的“精神健康”和“幸福指数”。随后我们讨论了一些最佳实践和模式,它们包括: - 减少全局对象,最好每个应用只有一个全局对象 -- 函数都使用单var模式来定义,这样可以将所有的变量放在同一个地方声明,同时可以避免“声明提前”给程序逻辑带来的影响。 -- for循环、for-in循环、switch语句、“禁止使用eval()”、不要扩充内置原型 -- 遵守统一的编码规范(在任何必要的时候保持空格、缩进、花括号和分号)和命名约定(构造函数、普通函数和变量)。 - -本章还讨论了其他一些和代码本身无关的实践,这些实践和编码过程紧密相关,包括书写注释、生成API文档,组织代码评审、不要试图去手动了“压缩”(minify)代码而牺牲代码可读性、坚持使用JSLint来对代码做检查。 +- 函数都使用单`var`模式来定义,这样可以将所有的变量放在同一个地方声明,同时可以避免“声明提前”给程序逻辑带来的影响 +- `for`循环、`for-in`循环、`switch`语句、“避免使用`eval()`”、不要扩充内置原型 +- 遵守统一的编码规范(在任何必要的时候保持空格、缩进、花括号和分号)和命名规范(构造函数、普通函数和变量)。 +本章还讨论了其他一些和代码本身无关的实践,这些实践和编码过程紧密相关,包括写注释、写API文档、组织同事评审、不要试图去手动“压缩”(minify)代码而牺牲代码可读性、坚持使用JSLint来对代码进行检查。 \ No newline at end of file From d4b2e20d3da7790a80f9cb196ea53cc77b3ab18a Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 23 Mar 2013 14:09:36 +0800 Subject: [PATCH 052/145] =?UTF-8?q?=E7=AC=AC=E4=BA=8C=E7=AB=A0=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95=20close=20#3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter2.markdown | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index 767c5e1..ff1decb 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -1,6 +1,6 @@ # 第二章 概要 -本章将概要介绍一些编写高质量JavaScript的最佳实践、模式和习惯,比如避免全局变量、使用单var声明、预缓存循环中的length、遵守编码约定等等。本章还包括一些编程习惯,这些习惯跟具体的代码关系不大,而是更多关注代码创建的总体过程,包括撰写API文档、code review以及使用JSLint。这些习惯和最佳实践可以帮助你写出更好更易读和可维护性更好的代码,当几个月或数年后你再重读你的代码时,就会深有体会了。 +本章将概要介绍一些编写高质量JavaScript的最佳实践、模式和习惯,比如避免全局变量、使用单`var`声明、预缓存循环中的`length`、遵守编码约定等等。本章还包括一些编程习惯,这些习惯跟具体的代码关系不大,而是更多关注代码创建的总体过程,包括撰写API文档、同事评审以及使用JSLint。这些习惯和最佳实践可以帮助你写出更好更易读和可维护性更好的代码,当几个月或数年后你再重读你的代码时,就会深有体会了。 ## 编写可维护的代码 @@ -144,7 +144,7 @@ JavaScript使用函数来管理作用域,在一个函数内定义的变量称 - 提醒你不要忘记声明变量,顺便减少潜在的全局变量 - 代码量更少(输入代码更少且更易做代码优化) -单var模式看起来像这样: +单`var`模式看起来像这样: function func() { var a = 1, @@ -243,7 +243,7 @@ JavaScript允许在函数的任意地方写任意多个`var`语句,但它们 需要注意的是,当你在循环过程中需要修改这个元素集合(比如增加DOM元素)时,你可能需要更新`length`。 -按照“单var模式”,你可以将`var`提到循环的外部,比如: +按照“单`var`模式”,你可以将`var`提到循环的外部,比如: function looper() { var i = 0, @@ -255,7 +255,7 @@ JavaScript允许在函数的任意地方写任意多个`var`语句,但它们 } } -当你越来越依赖“单var模式”时,带来的好处就是提高了代码的一致性。而缺点则是在重构代码的时候不能直接复制粘贴一个循环体,比如,你正在将某个循环从一个函数复制至另外一个函数中,那么必须确保`i`和`max`也复制到新函数里,并且需要从旧函数中将这些没用的变量删除掉。 +当你越来越依赖“单`var`模式”时,带来的好处就是提高了代码的一致性。而缺点则是在重构代码的时候不能直接复制粘贴一个循环体,比如,你正在将某个循环从一个函数复制至另外一个函数中,那么必须确保`i`和`max`也复制到新函数里,并且需要从旧函数中将这些没用的变量删除掉。 最后一个需要对循环做出调整的地方是将i++替换成为下面两者之一: @@ -938,33 +938,29 @@ YUIDoc工具是与语言无关的,只解析注释块,而不是JavaScript代 对用于生产环境的脚本做压缩是非常重要的步骤,因为它能提升页面性能,但你应当将这个过程交给工具来完成。千万不要试图手写“压缩好的”代码,你应当在编写代码时坚持使用语义化的变量命名,并保留足够的空格、缩进和注释。你写的代码是需要被人阅读的,所以应当将注意力放在代码可读性和可维护性上,将代码压缩的工作交给工具去完成。 -================校对分割线================ - -## 运行 JSLint +## 运行JSLint -在上一章我们已经介绍了JSLint,这里我们介绍更多的使用场景。对你的代码进行JSLint检查是非常好的编程习惯,你应该相信这一点。 +在上一章我们已经介绍了JSLint,本章中也提到了数次。到现在你应该已经相信用JSLint检查你的代码是一种好的编程模式了。 -JSLint的检查点都有哪些呢?它会对本章讨论过的一些模式(单var模式、parseInt()的第二个参数、总是使用花括号)做检查。JSLint还包括其他方面的检查: +JSLint的检查点都有哪些呢?它会对本章讨论过的一些模式(单`var`模式、`parseInt()`的第二个参数、总是使用花括号)做检查。JSLint还包括其他方面的检查: -- 不可达代码 -- 在使用变量之前需要声明 +- 不可达代码(译注:指永远不可能运行的代码) +- 变量在声明之前被使用 - 不安全的UTF字符 -- 使用void、with、和eval +- 使用`void`、`with`或者`eval` - 无法正确解析的正则表达式 -JSLint是基于JavaScript实现的(它是可以通过JSLint检查的),它提供了在线工具,也可以下载使用,可以运行于很多种平台的JavaScript解析器。你可以将源码下载后在本地运行,支持的环境包括WSH(Windows Scripting Host,Windows)、JSC(JavaScriptCore,MacOSX)或Rhino(Mozilla开发的JavaScript引擎)。 +JSLint是基于JavaScript实现的(它自己的代码是可以通过JSLint检查的),它提供了在线工具,也可以下载使用,可以运行于很多种平台的JavaScript解析器。你可以将源码下载后在本地运行,支持的环境包括WSH(Windows Scripting Host,Windows)、JSC(JavaScriptCore,MacOSX)或Rhino(Mozilla开发的JavaScript引擎)。 -可以将JSLint下载后和你的代码编辑器配置在一起,着是一个不错的注意,这样每次你保存代码的时候都会自动执行代码检查(比如配置快捷键)。 +将JSLint下载后和你的代码编辑器配置在一起是个很不错的主意,这样每次你保存代码的时候都会自动执行代码检查。(为它配置一个快捷键也很有用)。 - ## 小结 -本章我们讲解了编写可维护性代码的含义,本章的讨论非常重要,它不仅关系着软件项目的成功与否,还关系到参与项目的工程师的“精神健康”和“幸福指数”。随后我们讨论了一些最佳实践和模式,它们包括: +本章我们讨论了编写可维护性代码的意义,它不仅关系着软件项目的成功与否,还关系到参与项目的工程师的“精神健康”和“幸福指数”。随后我们讨论了一些最佳实践和模式,它们包括: - 减少全局对象,最好每个应用只有一个全局对象 -- 函数都使用单var模式来定义,这样可以将所有的变量放在同一个地方声明,同时可以避免“声明提前”给程序逻辑带来的影响。 -- for循环、for-in循环、switch语句、“禁止使用eval()”、不要扩充内置原型 -- 遵守统一的编码规范(在任何必要的时候保持空格、缩进、花括号和分号)和命名约定(构造函数、普通函数和变量)。 - -本章还讨论了其他一些和代码本身无关的实践,这些实践和编码过程紧密相关,包括书写注释、生成API文档,组织代码评审、不要试图去手动了“压缩”(minify)代码而牺牲代码可读性、坚持使用JSLint来对代码做检查。 +- 函数都使用单`var`模式来定义,这样可以将所有的变量放在同一个地方声明,同时可以避免“声明提前”给程序逻辑带来的影响 +- `for`循环、`for-in`循环、`switch`语句、“避免使用`eval()`”、不要扩充内置原型 +- 遵守统一的编码规范(在任何必要的时候保持空格、缩进、花括号和分号)和命名规范(构造函数、普通函数和变量)。 +本章还讨论了其他一些和代码本身无关的实践,这些实践和编码过程紧密相关,包括写注释、写API文档、组织同事评审、不要试图去手动“压缩”(minify)代码而牺牲代码可读性、坚持使用JSLint来对代码进行检查。 \ No newline at end of file From 0d5b3ca5cdc699d5c041862de15b909cf5888fc4 Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 28 Mar 2013 13:03:40 +0800 Subject: [PATCH 053/145] =?UTF-8?q?=E5=BA=8F=E6=A0=A1=E5=AF=B9=E5=AE=8C?= =?UTF-8?q?=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 87 +++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index 55c32b0..0bb493a 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -1,16 +1,15 @@ - -# 第三章 直接量和构造函数 +# 第三章 字面量和构造函数 -JavaScript中的直接量模式更加简洁、富有表现力,且在定义对象时不容易出错。本章将对直接量展开讨论,包括对象、数组和正则表达式直接量,以及为什么要使用等价的内置构造器函数来创建它们,比如Object()和Array()等。本章同样会介绍JSON格式,JSON是使用数组和对象直接量的形式定义的一种数据转换格式。本章还会讨论自定义构造函数,包括如何强制使用new以确保构造函数的正确执行。 +JavaScript中的字面量模式更加简洁、有表现力,而且在定义对象时不容易出错。本章将会讨论字面量,包括对象、数组和正则表达式字面量,以及为什么字面量要比等价的内置构造函数(如`Object()`、`Array()`等)要更好。本章还会介绍JSON格式,JSON是使用数组和对象字面量的形式定义的一种数据传输格式。本章还会讨论自定义构造函数,包括如何强制使用`new`以确保构造函数正确执行。 -本章还会补充讲述一些基础知识,比如内置包装对象Number()、String()和Boolean(),以及如何将它们和原始值(数字、字符串和布尔值)比较。最后,快速介绍一下Error()构造函数的用法。 +为了方便使用字面量而不是构造函数,本章还会补充一些知识,比如内置包装构造函数`Number()`、`String()`和`Boolean()`,以及如何将它们和原始值(数字、字符串和布尔值)比较。最后,快速介绍一下`Error()`构造函数的用法。 - -## 对象直接量 +-------------------校对分隔线----------------- +## 对象字面量 我们可以将JavaScript中的对象简单的理解为名值对组成的散列表(hash table),在其他编程语言中被称作“关联数组”。其中的值可以是原始值也可以是对象,不管是什么类型,它们都是“属性”(properties),属性值同样可以是函数,这时属性就被称为“方法”(methods)。 -JavaScript中自定义的对象(用户定义的本地对象)任何时候都是可变的。内置本地对象的属性也是可变的。你可以先创建一个空对象,然后在需要时给它添加功能。“对象直接量写法(object literal notation)”是按需创建对象的一种理想方式。 +JavaScript中自定义的对象(用户定义的本地对象)任何时候都是可变的。内置本地对象的属性也是可变的。你可以先创建一个空对象,然后在需要时给它添加功能。“对象字面量写法(object literal notation)”是按需创建对象的一种理想方式。 看一下这个例子: @@ -46,7 +45,7 @@ JavaScript中自定义的对象(用户定义的本地对象)任何时候都 }; dog.fleas = true; -其实不必每次开始都创建空对象,对象直接量模式可以直接在创建对象时添加功能,就像下面这个例子所展示的: +其实不必每次开始都创建空对象,对象字面量模式可以直接在创建对象时添加功能,就像下面这个例子所展示的: var dog = { name: "Benji", @@ -58,9 +57,9 @@ JavaScript中自定义的对象(用户定义的本地对象)任何时候都 > 在本书中多次提到“空对象”(“blank object”和“empty object”)。这只是某种简称,要知道JavaScript中根本不存在真正的空对象,理解这一点至关重要。即使最简单的`{}`对象包含从Object.prototype继承来的属性和方法。我们提到的“空(empty)对象”只是说这个对象没有自己的属性,不考虑它是否有继承来的属性。 -### 对象直接量语法 +### 对象字面量语法 -如果你从来没有接触过对象直接量写法,第一次碰到可能会感觉怪怪的。但越到后来你就越喜欢它。本质上讲,对象直接量语法包括: +如果你从来没有接触过对象字面量写法,第一次碰到可能会感觉怪怪的。但越到后来你就越喜欢它。本质上讲,对象字面量语法包括: - 将对象主体包含在一对花括号内(`{` and `}`)。 - 对象内的属性或方法之间使用逗号分隔。最后一个名值对后也可以有逗号,但在IE下会报错,所以尽量不要在最后一个属性或方法后加逗号。 @@ -84,14 +83,14 @@ JavaScript中没有类的概念,这给JavaScript带来了极大的灵活性, var car = new Object(); car.goes = "far"; -从这个例子中可以看到,直接量写法的一个明显优势是,它的代码更少。“创建对象的最佳模式是使用直接量”还有一个原因,它可以强调对象就是一个简单的可变的散列表,而不必一定派生自某个类。 +从这个例子中可以看到,字面量写法的一个明显优势是,它的代码更少。“创建对象的最佳模式是使用字面量”还有一个原因,它可以强调对象就是一个简单的可变的散列表,而不必一定派生自某个类。 -另外一个使用直接量而不是Object构造函数创建实例对象的原因是,对象直接量不需要“作用域解析”(scope resolution)。因为新创建的实例有可能包含了一个本地的构造函数,当你调用Object()的时候,解析器需要顺着作用域链从当前作用域开始查找,直到找到全局Object构造函数为止。 +另外一个使用字面量而不是Object构造函数创建实例对象的原因是,对象字面量不需要“作用域解析”(scope resolution)。因为新创建的实例有可能包含了一个本地的构造函数,当你调用Object()的时候,解析器需要顺着作用域链从当前作用域开始查找,直到找到全局Object构造函数为止。 ### 获得对象的构造器 -创建实例对象时能用对象直接量就不要使用new Object()构造函数,但有时你希望能继承别人写的代码,这时就需要了解构造函数的一个“特性”(也是不使用它的另一个原因),就是Object()构造函数可以接收参数,通过参数的设置可以把实例对象的创建委托给另一个内置构造函数,并返回另外一个实例对象,而这往往不是你所希望的。 +创建实例对象时能用对象字面量就不要使用new Object()构造函数,但有时你希望能继承别人写的代码,这时就需要了解构造函数的一个“特性”(也是不使用它的另一个原因),就是Object()构造函数可以接收参数,通过参数的设置可以把实例对象的创建委托给另一个内置构造函数,并返回另外一个实例对象,而这往往不是你所希望的。 下面的示例代码中展示了给new Object()传入不同的参数:数字、字符串和布尔值,最终得到的对象都是由不同的构造函数生成的: @@ -117,12 +116,12 @@ JavaScript中没有类的概念,这给JavaScript带来了极大的灵活性, var o = new Object(true); console.log(o.constructor === Boolean); // true -Object()构造函数的这种特性会导致一些意想不到的结果,特别是当参数不确定的时候。最后再次提醒不要使用new Object(),尽可能的使用对象直接量来创建实例对象。 +Object()构造函数的这种特性会导致一些意想不到的结果,特别是当参数不确定的时候。最后再次提醒不要使用new Object(),尽可能的使用对象字面量来创建实例对象。 ## 自定义构造函数 -除了对象直接量和内置构造函数之外,你也可以通过自定义的构造函数来创建实例对象,正如下面的代码所示: +除了对象字面量和内置构造函数之外,你也可以通过自定义的构造函数来创建实例对象,正如下面的代码所示: var adam = new Person("Adam"); adam.say(); // "I am Adam" @@ -245,7 +244,7 @@ ECMAScript5中修正了这种非正常的行为逻辑。在严格模式中,thi return that; } -如果要创建简单的实例对象,甚至不需要定义一个局部变量that,可以直接返回一个对象直接量,就像这样: +如果要创建简单的实例对象,甚至不需要定义一个局部变量that,可以直接返回一个对象字面量,就像这样: function Waffle() { return { @@ -298,11 +297,11 @@ ECMAScript5中修正了这种非正常的行为逻辑。在严格模式中,thi 这里需要说明的是,在任何函数内部都会自行创建一个arguments对象,它包含函数调用时传入的参数。同时arguments包含一个callee属性,指向它所在的正在被调用的函数。需要注意,ES5严格模式中是禁止使用arguments.callee的,因此最好对它的使用加以限制,并删除任何你能在代码中找到的实例(译注:这里作者的表述很委婉,其实作者更倾向于全面禁止使用arguments.callee)。 -## 数组直接量 +## 数组字面量 -和其他的大多数一样,JavaScript中的数组也是对象。可以通过内置构造函数Array()来创建数组,类似对象直接量,数组也可以通过直接量形式创建。而且更推荐使用直接量创建数组。 +和其他的大多数一样,JavaScript中的数组也是对象。可以通过内置构造函数Array()来创建数组,类似对象字面量,数组也可以通过字面量形式创建。而且更推荐使用字面量创建数组。 -这里的实例代码给出了创建两个具有相同元素的数组的两种方法,使用Array()和使用直接量模式: +这里的实例代码给出了创建两个具有相同元素的数组的两种方法,使用Array()和使用字面量模式: // array of three elements // warning: antipattern @@ -315,18 +314,18 @@ ECMAScript5中修正了这种非正常的行为逻辑。在严格模式中,thi console.log(a.constructor === Array); // true -### 数组直接量语法 +### 数组字面量语法 -数组直接量写法非常简单:整个数组使用方括号括起来,数组元素之间使用逗号分隔。数组元素可以是任意类型,也包括数组和对象。 +数组字面量写法非常简单:整个数组使用方括号括起来,数组元素之间使用逗号分隔。数组元素可以是任意类型,也包括数组和对象。 -数组直接量语法简单直接、高雅美观。毕竟数组只是从位置0开始索引的值的集合,完全没必要包含构造器和new运算符的内容(代码会更多),保持简单即可。 +数组字面量语法简单直接、高雅美观。毕竟数组只是从位置0开始索引的值的集合,完全没必要包含构造器和new运算符的内容(代码会更多),保持简单即可。 ### 有意思的数组构造器 我们对new Array()敬而远之原因是为了避免构造函数带来的陷阱。 -如果给Array()构造器传入一个数字,这个数字并不会成为数组的第一个元素,而是设置数组的长度。也就是说,new Array(3)创建了一个长度为3的数组,而不是某个元素是3。如果你访问数组的任意元素都会得到undefined,因为元素并不存在。下面示例代码展示了直接量和构造函数的区别: +如果给Array()构造器传入一个数字,这个数字并不会成为数组的第一个元素,而是设置数组的长度。也就是说,new Array(3)创建了一个长度为3的数组,而不是某个元素是3。如果你访问数组的任意元素都会得到undefined,因为元素并不存在。下面示例代码展示了字面量和构造函数的区别: // an array of one element var a = [3]; @@ -347,7 +346,7 @@ ECMAScript5中修正了这种非正常的行为逻辑。在严格模式中,thi var a = new Array(3.14); // RangeError: invalid array length console.log(typeof a); // "undefined" -为了避免在运行时动态创建数组时出现这种错误,强烈推荐使用数组直接量来代替new Array()。 +为了避免在运行时动态创建数组时出现这种错误,强烈推荐使用数组字面量来代替new Array()。 >有些人用Array()构造器来做一些有意思的事情,比如用来生成重复字符串。下面这行代码返字符串包含255个空格(请读者思考为什么不是256个空格)。`var white = new Array(256).join(' ');` @@ -383,15 +382,15 @@ ECMAScript 5定义了一个新的方法Array.isArray(),如果参数是数组 ## JSON -上文我们刚刚讨论过对象和数组直接量,你已经对此很熟悉了,现在我们将目光转向JSON。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。很多语言中都实现了JSON,特别是在JavaScript中。 +上文我们刚刚讨论过对象和数组字面量,你已经对此很熟悉了,现在我们将目光转向JSON。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。很多语言中都实现了JSON,特别是在JavaScript中。 -JSON格式及其简单,它只是数组和对象直接量的混合写法,看一个JSON字符串的例子: +JSON格式及其简单,它只是数组和对象字面量的混合写法,看一个JSON字符串的例子: {"name": "value", "some": [1, 2, 3]} -JSON和对象直接量在语法上的唯一区别是,合法的JSON属性名均用引号包含。而在对象直接量中,只有属性名是非法的标识符时采用引号包含,比如,属性名中包含空格`{"first name": "Dave"}`。 +JSON和对象字面量在语法上的唯一区别是,合法的JSON属性名均用引号包含。而在对象字面量中,只有属性名是非法的标识符时采用引号包含,比如,属性名中包含空格`{"first name": "Dave"}`。 -在JSON字符串中,不能使用函数和正则表达式直接量。 +在JSON字符串中,不能使用函数和正则表达式字面量。 ### 使用JSON @@ -443,12 +442,12 @@ JSON和对象直接量在语法上的唯一区别是,合法的JSON属性名均 // {"name":"Fido","dob":"2010-04-11T22:36:22.436Z","legs":[1,2,3,4]} -## 正则表达式直接量 +## 正则表达式字面量 JavaScript中的正则表达式也是对象,可以通过两种方式创建它们: - 使用new RegExp()构造函数 -- 使用正则表达式直接量 +- 使用正则表达式字面量 下面的示例代码展示了创建正则表达式的两种方法,创建的正则用来匹配一个反斜杠(\): @@ -458,14 +457,14 @@ JavaScript中的正则表达式也是对象,可以通过两种方式创建它 // constructor var re = new RegExp("\\\\", "gm"); -显然正则表达式直接量写法的代码更短,且不必强制按照类构造器的思路来写。因此更推荐使用直接量写法。 +显然正则表达式字面量写法的代码更短,且不必强制按照类构造器的思路来写。因此更推荐使用字面量写法。 -另外,如果使用RegExp()构造函数写法,还需要考虑对引号和反斜杠进行转义,正如上段代码所示的那样,用了四个反斜杠来匹配一个反斜杠。这会增加正则表达式的长度,而且让正则变得难于理解和维护。刚开始学习正则表达式不是很容易,所以不要放弃任何一个简化它们的机会,所以要尽量使用直接量而不是通过构造函数来创建正则。 +另外,如果使用RegExp()构造函数写法,还需要考虑对引号和反斜杠进行转义,正如上段代码所示的那样,用了四个反斜杠来匹配一个反斜杠。这会增加正则表达式的长度,而且让正则变得难于理解和维护。刚开始学习正则表达式不是很容易,所以不要放弃任何一个简化它们的机会,所以要尽量使用字面量而不是通过构造函数来创建正则。 -### 正则表达式直接量语法 +### 正则表达式字面量语法 -正则表达式直接量使用两个斜线包裹起来,正则的主体部分不包括两端的斜线。在第二个斜线之后可以指定模式匹配的修饰符用以高级匹配,修饰符不需要引号引起来,JavaScript中有三个修饰符: +正则表达式字面量使用两个斜线包裹起来,正则的主体部分不包括两端的斜线。在第二个斜线之后可以指定模式匹配的修饰符用以高级匹配,修饰符不需要引号引起来,JavaScript中有三个修饰符: - g,全局匹配 - m,多行匹配 @@ -475,14 +474,14 @@ JavaScript中的正则表达式也是对象,可以通过两种方式创建它 var re = /pattern/gmi; -使用正则表达式直接量可以让代码更加简洁高效,比如当调用String.prototype.prelace()方法时,可以传入正则表达式参数: +使用正则表达式字面量可以让代码更加简洁高效,比如当调用String.prototype.prelace()方法时,可以传入正则表达式参数: var no_letters = "abc123XYZ".replace(/[a-z]/gi, ""); console.log(no_letters); // 123 有一种不得不使用new RegExp()的情形,有时正则表达式是不确定的,直到运行时才能确定下来。 -正则表达式直接量和Regexp()构造函数的另一个区别是,正则表达式直接量只在解析时创建一次正则表达式对象(译注:多次解析同一个正则表达式,会产生相同的实例对象)。如果在循环体内反复创建相同的正则表达式,则每个正则对象的所有属性(比如lastIndex)只会设置一次(译注:由于每次创建相同的实例对象,每个循环中的实例对象都是同一个,属性也自然相同),下面这个例子展示了两次都返回了相同的正则表达式的情形(译注:这里作者的表述只是针对ES3规范而言,下面这段代码在NodeJS、IE6-IE9、FireFox4、Chrome10、Safari5中运行结果和作者描述的不一致,Firefox 3.6中的运行结果和作者描述是一致的,原因可以在ECMAScript5规范第24页和第247页找到,也就是说在ECMAScript3规范中,用正则表达式创建的RegExp对象会共享同一个实例,而在ECMAScript5中则是两个独立的实例。而最新的Firefox4、Chrome和Safari5都遵循ECMAScript5标准,至于IE6-IE8都没有很好的遵循ECMAScript3标准,不过在这个问题上反而处理对了。很明显ECMAScript5的规范更符合开发者的期望)。 +正则表达式字面量和Regexp()构造函数的另一个区别是,正则表达式字面量只在解析时创建一次正则表达式对象(译注:多次解析同一个正则表达式,会产生相同的实例对象)。如果在循环体内反复创建相同的正则表达式,则每个正则对象的所有属性(比如lastIndex)只会设置一次(译注:由于每次创建相同的实例对象,每个循环中的实例对象都是同一个,属性也自然相同),下面这个例子展示了两次都返回了相同的正则表达式的情形(译注:这里作者的表述只是针对ES3规范而言,下面这段代码在NodeJS、IE6-IE9、FireFox4、Chrome10、Safari5中运行结果和作者描述的不一致,Firefox 3.6中的运行结果和作者描述是一致的,原因可以在ECMAScript5规范第24页和第247页找到,也就是说在ECMAScript3规范中,用正则表达式创建的RegExp对象会共享同一个实例,而在ECMAScript5中则是两个独立的实例。而最新的Firefox4、Chrome和Safari5都遵循ECMAScript5标准,至于IE6-IE8都没有很好的遵循ECMAScript3标准,不过在这个问题上反而处理对了。很明显ECMAScript5的规范更符合开发者的期望)。 function getRE() { var re = /[a-z]/; @@ -497,7 +496,7 @@ JavaScript中的正则表达式也是对象,可以通过两种方式创建它 reg.foo = "baz"; console.log(re2.foo); // "baz" ->在ECMAScript5中这种情形有所改变,相同正则表达式直接量的每次计算都会创建新的实例对象,目前很多现代浏览器也对此做了纠正(译注:比如在Firefox4就纠正了Firefox3.6的这种“错误”)。 +>在ECMAScript5中这种情形有所改变,相同正则表达式字面量的每次计算都会创建新的实例对象,目前很多现代浏览器也对此做了纠正(译注:比如在Firefox4就纠正了Firefox3.6的这种“错误”)。 最后需要提一点,不带new调用RegExp()(作为普通的函数)和带new调用RegExp()是完全一样的。 @@ -603,22 +602,22 @@ name属性是指创建这个对象的构造函数的名字,通常是“Errora ## 小结 -在本章里,我们讨论了多种直接量模式,它们是使用构造函数写法的替代方案,本章讲述了这些内容: +在本章里,我们讨论了多种字面量模式,它们是使用构造函数写法的替代方案,本章讲述了这些内容: -- 对象直接量写法——一种简洁优雅的定义对象的方法,名值对之间用逗号分隔,通过花括号包装起来 -- 构造函数——内置构造函数(内置构造函数通常都有对应的直接量语法)和自定义构造函数。 +- 对象字面量写法——一种简洁优雅的定义对象的方法,名值对之间用逗号分隔,通过花括号包装起来 +- 构造函数——内置构造函数(内置构造函数通常都有对应的字面量语法)和自定义构造函数。 - 一种强制函数以构造函数的模式执行(不管用不用new调用构造函数,都始终返回new出来的实例)的技巧 -- 数组直接量写法——数组元素之间使用逗号分隔,通过方括号括起来 +- 数组字面量写法——数组元素之间使用逗号分隔,通过方括号括起来 - JSON——是一种轻量级的数据交换格式 -- 正则表达式直接量 +- 正则表达式字面量 - 避免使用其他的内置构造函数:String()、Number()、Boolean()以及不同种类的Error()构造器 -通常除了Date()构造函数之外,其他的内置构造函数并不常用,下面的表格中对这些构造函数以及它们的直接量语法做了整理。 +通常除了Date()构造函数之外,其他的内置构造函数并不常用,下面的表格中对这些构造函数以及它们的字面量语法做了整理。 - + From e8ad59ac71d29cb7b4c57bf5863f920fb049d55d Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 28 Mar 2013 13:26:50 +0800 Subject: [PATCH 054/145] =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E5=AD=97=E9=9D=A2?= =?UTF-8?q?=E9=87=8F=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 69 +++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index 0bb493a..437d7e0 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -4,48 +4,46 @@ JavaScript中的字面量模式更加简洁、有表现力,而且在定义对 为了方便使用字面量而不是构造函数,本章还会补充一些知识,比如内置包装构造函数`Number()`、`String()`和`Boolean()`,以及如何将它们和原始值(数字、字符串和布尔值)比较。最后,快速介绍一下`Error()`构造函数的用法。 --------------------校对分隔线----------------- ## 对象字面量 -我们可以将JavaScript中的对象简单的理解为名值对组成的散列表(hash table),在其他编程语言中被称作“关联数组”。其中的值可以是原始值也可以是对象,不管是什么类型,它们都是“属性”(properties),属性值同样可以是函数,这时属性就被称为“方法”(methods)。 +我们可以将JavaScript中的对象简单地理解为名值对组成的散列表(hash table,也叫哈希表)。在其他编程语言中被称作“关联数组”。其中的值可以是原始值也可以是对象。不管是什么类型,它们都是“属性”(property),属性值同样可以是函数,这时属性就被称为“方法”(method)。 JavaScript中自定义的对象(用户定义的本地对象)任何时候都是可变的。内置本地对象的属性也是可变的。你可以先创建一个空对象,然后在需要时给它添加功能。“对象字面量写法(object literal notation)”是按需创建对象的一种理想方式。 看一下这个例子: - // start with an empty object + // 定义空对象 var dog = {}; - // add one property + // 添加一个属性 dog.name = "Benji"; - // now add a method + // 添加一个方法 dog.getName = function () { return dog.name; }; 在这个例子中,我们首先定义了一个空对象,然后添加了一个属性和一个方法,在程序的生命周期内的任何时刻都可以: -1.更改属性和方法的值,比如: +- 更改属性和方法的值,比如: dog.getName = function () { - // redefine the method to return - // a hardcoded value + // 重新定义方法,返回一个硬编码的值 return "Fido"; }; -2.完全删除属性/方法 +- 删除属性/方法 delete dog.name; -3.添加更多的属性和方法 +- 添加更多的属性和方法 dog.say = function () { return "Woof!"; }; dog.fleas = true; -其实不必每次开始都创建空对象,对象字面量模式可以直接在创建对象时添加功能,就像下面这个例子所展示的: +每次都创建空对象并不是必须的,对象字面量模式可以直接在创建对象时添加功能,就像下面这个例子: var dog = { name: "Benji", @@ -54,70 +52,69 @@ JavaScript中自定义的对象(用户定义的本地对象)任何时候都 } }; -> 在本书中多次提到“空对象”(“blank object”和“empty object”)。这只是某种简称,要知道JavaScript中根本不存在真正的空对象,理解这一点至关重要。即使最简单的`{}`对象包含从Object.prototype继承来的属性和方法。我们提到的“空(empty)对象”只是说这个对象没有自己的属性,不考虑它是否有继承来的属性。 +> 在本书中多次提到“空对象”(“blank object”和“empty object”),这只是一种简称,在JavaScript中根本不存在真正的空对象,理解这一点至关重要。即使最简单的`{}`对象也会包含从`Object.prototype`继承来的属性和方法。我们提到的“空(empty)对象”只是说这个对象没有自有属性(own properties),不考虑它是否有继承来的属性。 - ### 对象字面量语法 -如果你从来没有接触过对象字面量写法,第一次碰到可能会感觉怪怪的。但越到后来你就越喜欢它。本质上讲,对象字面量语法包括: +如果你从来没有接触过对象字面量的写法,可能会感觉怪怪的。但越到后来你就越喜欢它。本质上讲,对象字面量语法包括: -- 将对象主体包含在一对花括号内(`{` and `}`)。 +- 将对象主体包含在一对花括号内(`{` 和 `}`)。 - 对象内的属性或方法之间使用逗号分隔。最后一个名值对后也可以有逗号,但在IE下会报错,所以尽量不要在最后一个属性或方法后加逗号。 -- 属性名和值之间使用冒号分隔 -- 如果将对象赋值给一个变量,不要忘了在右括号`}`之后补上分号 +- 属性名和值之间使用冒号分隔。 +- 如果将对象赋值给一个变量,不要忘了在右括号`}`之后补上分号。 - ### 通过构造函数创建对象 -JavaScript中没有类的概念,这给JavaScript带来了极大的灵活性,因为你不必提前知晓关于对象的任何信息,也不需要类的“蓝图”。但JavaScript同样具有构造函数,它的语法和Java或其他语言中基于类的对象创建非常类似。 +JavaScript中没有类的概念,这给JavaScript带来了极大的灵活性,因为你不必提前知晓关于对象的任何信息,也不需要类的“蓝图”(译注:指类的结构)。但JavaScript同样具有构造函数,它的语法和Java或其他语言中基于类的对象创建非常类似。 -你可以使用自定义的构造函数来创建实例对象,也可以使用内置构造函数来创建,比如Object()、Date()、String()等等。 +你可以使用自定义的构造函数来创建对象实例,也可以使用内置构造函数来创建,比如`Object()`、`Date()`、`String()`等等。 下面这个例子展示了用两种等价的方法分别创建两个独立的实例对象: - // one way -- using a literal + // 一种方法,使用字面量 var car = {goes: "far"}; - // another way -- using a built-in constructor - // warning: this is an antipattern + // 另一种方法,使用内置构造函数 + // 注意:这是一种反模式 var car = new Object(); car.goes = "far"; 从这个例子中可以看到,字面量写法的一个明显优势是,它的代码更少。“创建对象的最佳模式是使用字面量”还有一个原因,它可以强调对象就是一个简单的可变的散列表,而不必一定派生自某个类。 -另外一个使用字面量而不是Object构造函数创建实例对象的原因是,对象字面量不需要“作用域解析”(scope resolution)。因为新创建的实例有可能包含了一个本地的构造函数,当你调用Object()的时候,解析器需要顺着作用域链从当前作用域开始查找,直到找到全局Object构造函数为止。 +另外一个使用字面量而不是`Object()`构造函数创建实例对象的原因是,对象字面量不需要“作用域解析”(scope resolution)。因为存在你已经创建了一个同名的构造函数`Object()`的可能,当你调用`Object()`的时候,解析器需要顺着作用域链从当前作用域开始查找,直到找到全局`Object()`构造函数为止。 + +### `Object()`构造函数的参数 - -### 获得对象的构造器 +> 译注:这小节的标题是Object Constructor Catch,恕译者水平有限,实在不知如何翻译,故自作主张修改了本节标题。 -创建实例对象时能用对象字面量就不要使用new Object()构造函数,但有时你希望能继承别人写的代码,这时就需要了解构造函数的一个“特性”(也是不使用它的另一个原因),就是Object()构造函数可以接收参数,通过参数的设置可以把实例对象的创建委托给另一个内置构造函数,并返回另外一个实例对象,而这往往不是你所希望的。 +创建实例对象时能用对象字面量就不要使用`new Object()`构造函数,但有时你可能是在别人写的代码基础上工作,这时就需要了解构造函数的一个“特性”(也是不使用它的另一个原因),就是`Object()`构造函数可以接收参数,通过这个参数可以把对象实例的创建过程委托给另一个内置构造函数,并返回另外一个对象实例,而这往往不是你想要的。 -下面的示例代码中展示了给new Object()传入不同的参数:数字、字符串和布尔值,最终得到的对象都是由不同的构造函数生成的: +下面的示例代码中展示了给`new Object()`传入不同的参数(数字、字符串和布尔值),最终得到的对象是由不同的构造函数生成的: - // Warning: antipatterns ahead + // 注意:这是反模式 - // an empty object + // 空对象 var o = new Object(); console.log(o.constructor === Object); // true - // a number object + // 数值对象 var o = new Object(1); console.log(o.constructor === Number); // true console.log(o.toFixed(2)); // "1.00" - // a string object + // 字符串对象 var o = new Object("I am a string"); console.log(o.constructor === String); // true - // normal objects don't have a substring() - // method but string objects do + // 普通对象没有substring()方法,但字符串对象有 console.log(typeof o.substring); // "function" - // a boolean object + // 布尔值对象 var o = new Object(true); console.log(o.constructor === Boolean); // true -Object()构造函数的这种特性会导致一些意想不到的结果,特别是当参数不确定的时候。最后再次提醒不要使用new Object(),尽可能的使用对象字面量来创建实例对象。 +`Object()`构造函数的这种特性会导致一些意想不到的结果,特别是当参数不确定的时候。最后再次提醒不要使用`new Object()`,尽可能的使用对象字面量来创建实例对象。 +-------------------校对分隔线----------------- ## 自定义构造函数 From ccead7489afcee7c02bff00996af942f862cd7c3 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 29 Mar 2013 21:49:27 +0800 Subject: [PATCH 055/145] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E6=9E=84?= =?UTF-8?q?=E9=80=A0=E5=87=BD=E6=95=B0=20=E7=AC=AC=E4=B8=80=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index 437d7e0..fca5af7 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -114,18 +114,16 @@ JavaScript中没有类的概念,这给JavaScript带来了极大的灵活性, `Object()`构造函数的这种特性会导致一些意想不到的结果,特别是当参数不确定的时候。最后再次提醒不要使用`new Object()`,尽可能的使用对象字面量来创建实例对象。 --------------------校对分隔线----------------- - ## 自定义构造函数 -除了对象字面量和内置构造函数之外,你也可以通过自定义的构造函数来创建实例对象,正如下面的代码所示: +除了对象字面量和内置构造函数之外,你也可以通过自定义的构造函数来创建对象实例,正如下面的代码所示: var adam = new Person("Adam"); adam.say(); // "I am Adam" -这里用了“类”Person创建了实例,这种写法看起来很像Java中的实例创建。两者的语法的确非常接近,但实际上JavaScript中没有类的概念,Person是一个函数。 +这种写法非常像Java中用`Person`类创建了一个实例,两者的语法非常接近,但实际上JavaScript中没有类的概念,`Person()`是一个函数。 -Person构造函数是如何定义的呢?看下面的代码: +`Person()`构造函数是如何定义的呢?看下面的代码: var Person = function (name) { this.name = name; @@ -134,20 +132,19 @@ Person构造函数是如何定义的呢?看下面的代码: }; }; -当你通过关键字new来调用这个构造函数时,函数体内将发生这些事情: +当你通过`new`来调用这个构造函数时,函数体内将发生这些事情: -- 创建一个空对象,将它的引用赋给this,继承函数的原型。 -- 通过this将属性和方法添加至这个对象 -- 最后返回this指向的新对象(如果没有手动返回其他的对象) +- 创建一个空对象,将它的引用赋给`this`,并继承函数的原型。 +- 通过`this`将属性和方法添加至这个对象。 +- 最后返回this指向的新对象(如果没有手动返回其他的对象)。 用代码表示这个过程如下: var Person = function (name) { - // create a new object - // using the object literal + // 使用对象字面量创建新对象 // var this = {}; - // add properties and methods + // 添加属性和方法 this.name = name; this.say = function () { return "I am " + this.name; @@ -156,24 +153,25 @@ Person构造函数是如何定义的呢?看下面的代码: //return this; }; -正如这段代码所示,say()方法添加至this中,结果是,不论何时调用new Person(),在内存中都会创建一个新函数(译注:所有Person的实例对象中的方法都是独占一块内存的)。显然这是效率很低的,因为所有实例的say()方法是一模一样的,因此没有必要“拷贝”多份。最好的办法是将方法添加至Person的原型中。 +上例中,为简便起见,`say()`方法被添加至`this`中,结果就是不论何时调用`new Person()`,在内存中都会创建一个新函数(`say()`),显然这是效率很低的,因为所有实例的`say()`方法是一模一样的。最好的办法是将方法添加至`Person()`的原型中。 Person.prototype.say = function () { return "I am " + this.name; }; -我们将会在下一章里详细讨论原型和继承。现在只要记住将需要重用的成员和方法放在原型里即可。 +我们将会在下一章里详细讨论原型和继承,现在只要记住将需要重用的成员放在原型里即可。 -关于构造函数的内部工作机制也会在后续章节中有更细致的讨论。这里我们只做概要的介绍。刚才提到,构造函数执行的时候,首先创建一个新对象,并将它的引用赋给this: +关于构造函数的内部工作机制也会在后续章节中有更细致的讨论。这里我们只做概要的介绍。刚才提到,构造函数执行的时候,首先创建一个新对象,并将它的引用赋给`this`: // var this = {}; -事实并不完全是这样,因为“空”对象并不是真的空,这个对象继承了Person的原型,看起来更像: +其实事实并不完全是这样,因为“空”对象并不是真的空,这个对象继承了`Person`的原型,看起来更像: // var this = Object.create(Person.prototype); -在后续章节会进一步讨论Object.create()。 +在后续章节会进一步讨论`Object.create()`。 +-------------------校对分隔线----------------- ### 构造函数的返回值 From 4ec19bbcca7de47b1306a042670924f0f5c365d3 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 29 Mar 2013 21:54:26 +0800 Subject: [PATCH 056/145] =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E5=87=BD=E6=95=B0=20=E6=A0=A1=E5=AF=B9=E5=AE=8C?= =?UTF-8?q?=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index fca5af7..57c642d 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -171,33 +171,30 @@ JavaScript中没有类的概念,这给JavaScript带来了极大的灵活性, 在后续章节会进一步讨论`Object.create()`。 --------------------校对分隔线----------------- - ### 构造函数的返回值 -用new调用的构造函数总是会返回一个对象,默认返回this所指向的对象。如果构造函数内没有给this赋任何属性,则返回一个“空”对象(除了继承构造函数的原型之外,没有“自己的”属性)。 +当使用`new`调用的时候,构造函数总是会返回一个对象,默认情况下返回`this`所指向的对象。如果构造函数内没有给`this`赋任何属性,则返回一个“空”对象(除了继承构造函数的原型之外,没有自有属性)。 -尽管我们不会在构造函数内写return语句,也会隐式返回this。但我们是可以返回任意指定的对象的,在下面的例子中就返回了新创建的that对象。 +尽管在构造函数中没有`return`语句的情况下,也会隐式返回`this`。但事实上我们是可以返回任意指定的对象的,在下面的例子中就返回了新创建的`that`对象。 var Objectmaker = function () { - // this `name` property will be ignored - // because the constructor - // decides to return another object instead + // name属性会被忽略,因为返回的是另一个对象 this.name = "This is it"; - // creating and returning a new object + // 创建并返回一个新对象 var that = {}; that.name = "And that's that"; return that; }; - // test + // 测试 var o = new Objectmaker(); console.log(o.name); // "And that's that" -我们看到,构造函数中其实是可以返回任意对象的,只要你返回的东西是对象即可。如果返回值不是对象(字符串、数字或布尔值),程序不会报错,但这个返回值被忽略,最终还是返回this所指的对象。 +可以看到,构造函数中其实是可以返回任意对象的,只要你返回的东西是对象即可。如果返回值不是对象(字符串、数字或布尔值),程序不会报错,但这个返回值被忽略,最终还是返回`this`所指的对象。 +-------------------校对分隔线----------------- ## 强制使用new的模式 From 185ead3fd64fb654c0c0ed11e95d7817ac0244fc Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 30 Mar 2013 10:37:00 +0800 Subject: [PATCH 057/145] =?UTF-8?q?=E5=BC=BA=E5=88=B6=E4=BD=BF=E7=94=A8new?= =?UTF-8?q?=E7=9A=84=E6=A8=A1=E5=BC=8F=20=E7=AC=AC=E4=B8=80=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index 57c642d..2451419 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -194,32 +194,30 @@ JavaScript中没有类的概念,这给JavaScript带来了极大的灵活性, 可以看到,构造函数中其实是可以返回任意对象的,只要你返回的东西是对象即可。如果返回值不是对象(字符串、数字或布尔值),程序不会报错,但这个返回值被忽略,最终还是返回`this`所指的对象。 --------------------校对分隔线----------------- - ## 强制使用new的模式 -我们知道,构造函数和普通的函数无异,只是通过new调用而已。那么如果调用构造函数时忘记new会发生什么呢?漏掉new不会产生语法错误也不会有运行时错误,但可能会造成逻辑错误,导致执行结果不符合预期。这是因为如果不写new的话,函数内的this会指向全局对象(在浏览器端this指向window)。 +我们知道,构造函数和普通的函数本质一样,只是通过`new`调用而已。那么如果调用构造函数时忘记`new`会发生什么呢?漏掉`new`不会产生语法错误也不会有运行时错误,但可能会造成逻辑错误,导致执行结果不符合预期。这是因为如果不写`new`的话,函数内的`this`会指向全局对象(在浏览器端`this`指向`window`)。 -当构造函数内包含this.member之类的代码,并直接调用这个函数(省略new),实际会创建一个全局对象的属性member,可以通过window.member或member访问到它。这必然不是我们想要的结果,因为我们要努力确保全局命名空间的整洁干净。 +当构造函数内包含`this.member`之类的代码,并直接调用这个函数(省略`new`),实际上会创建一个全局对象的属性`member`,可以通过`window.member`或`member`访问到。这不是我们想要的结果,因为我们要努力确保全局命名空间干净。 - // constructor + // 构造函数 function Waffle() { this.tastes = "yummy"; } - // a new object + // 新对象 var good_morning = new Waffle(); console.log(typeof good_morning); // "object" console.log(good_morning.tastes); // "yummy" - // antipattern: - // forgotten `new` + // 反模式,漏掉new var good_morning = Waffle(); console.log(typeof good_morning); // "undefined" console.log(window.tastes); // "yummy" -ECMAScript5中修正了这种非正常的行为逻辑。在严格模式中,this是不能指向全局对象的。如果在不支持ES5的JavaScript环境中,仍然后很多方法可以确保构造函数的行为即便在省略new调用时也不会出问题。 +ECMAScript5中修正了这种出乎意料的行为逻辑。在严格模式中,`this`不再指向全局对象。如果在不支持ES5的JavaScript环境中,也有一些方法可以确保有没有`new`时构造函数的行为都保持一致。 +-------------------校对分隔线----------------- ### 命名约定 From c87dad45b1774ab33bc0ca22d4988d2421772279 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 30 Mar 2013 10:52:45 +0800 Subject: [PATCH 058/145] =?UTF-8?q?=E5=BC=BA=E5=88=B6=E4=BD=BF=E7=94=A8new?= =?UTF-8?q?=E7=9A=84=E6=A8=A1=E5=BC=8F=20=E6=A0=A1=E5=AF=B9=E5=AE=8C?= =?UTF-8?q?=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index 2451419..18540cc 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -217,24 +217,21 @@ JavaScript中没有类的概念,这给JavaScript带来了极大的灵活性, ECMAScript5中修正了这种出乎意料的行为逻辑。在严格模式中,`this`不再指向全局对象。如果在不支持ES5的JavaScript环境中,也有一些方法可以确保有没有`new`时构造函数的行为都保持一致。 --------------------校对分隔线----------------- - -### 命名约定 +### 命名规范 -最简单的选择是使用命名约定,前面的章节已经提到,构造函数名首字母大写(MyConstructor),普通函数和方法名首字母小写(myFunction)。 +一种简单解决上述问题的方法就是命名规范,前面的章节已经讨论过,构造函数首字母大写(`MyConstructor()`),普通函数和方法首字母小写(`myFunction`)。 - ### 使用that -遵守命名约定的确能帮上一些忙,但约定毕竟不是强制,不能完全避免出错。这里给出了一种模式可以确保构造函数一定会按照构造函数的方式执行。不要将所有成员挂在this上,将它们挂在that上,并返回that。 +遵守命名规范有一定的作用,但规范毕竟不是强制,不能完全避免出现错误。这里给出了一种模式可以确保构造函数一定会按照构造函数的方式执行,那就是不要将所有成员添加到`this`上,而是将它们添加到`that`上,并返回`that`。 function Waffle() { - var that = {}; + var that = {}; that.tastes = "yummy"; return that; } -如果要创建简单的实例对象,甚至不需要定义一个局部变量that,可以直接返回一个对象字面量,就像这样: +如果要创建更简单一点的对象,甚至不需要局部变量`that`,直接返回一个对象字面量即可,就像这样: function Waffle() { return { @@ -242,21 +239,20 @@ ECMAScript5中修正了这种出乎意料的行为逻辑。在严格模式中, }; } -不管用什么方式调用它(使用new或直接调用),它同都会返回一个实例对象: +不管用什么方式调用它(使用`new`或直接调用),它都会返回一个实例对象: var first = new Waffle(), second = Waffle(); console.log(first.tastes); // "yummy" console.log(second.tastes); // "yummy" -这种模式的问题是丢失了原型,因此在Waffle()的原型上的成员不会继承到这些实例对象中。 +这种模式的问题是会丢失原型,因此在`Waffle()`的原型上的成员不会被继承到这些对象中。 -> 需要注意的是,这里用的that只是一种命名约定,that不是语言的保留字,可以将它替换为任何你喜欢的名字,比如self或me。 +> 需要注意的是,这里用的`that`只是一种命名规范,`that`并不是语言特性的一部分,它可以被替换为任何你喜欢的名字,比如`self`或`me`。 - ### 调用自身的构造函数 -为了解决上述模式的问题,能够让实例对象继承原型属性,我们使用下面的方法。在构造函数中首先检查this是否是构造函数的实例,如果不是,再通过new调用构造函数,并将new的结果返回: +为了解决上述模式的问题,能够让对象继承原型上的属性,我们使用下面的方法:在构造函数中首先检查`this`是否是构造函数的实例,如果不是,则通过`new`再次调用自己: function Waffle() { @@ -268,7 +264,7 @@ ECMAScript5中修正了这种出乎意料的行为逻辑。在严格模式中, } Waffle.prototype.wantAnother = true; - // testing invocations + // 测试 var first = new Waffle(), second = Waffle(); @@ -278,14 +274,15 @@ ECMAScript5中修正了这种出乎意料的行为逻辑。在严格模式中, console.log(first.wantAnother); // true console.log(second.wantAnother); // true -另一种检查实例的通用方法是使用arguments.callee,而不是直接将构造函数名写死在代码中: +还有一种比较通用的用来检查实例的方法是使用`arguments.callee`,而不是直接将构造函数名写死在代码中: if (!(this instanceof arguments.callee)) { return new arguments.callee(); } -这里需要说明的是,在任何函数内部都会自行创建一个arguments对象,它包含函数调用时传入的参数。同时arguments包含一个callee属性,指向它所在的正在被调用的函数。需要注意,ES5严格模式中是禁止使用arguments.callee的,因此最好对它的使用加以限制,并删除任何你能在代码中找到的实例(译注:这里作者的表述很委婉,其实作者更倾向于全面禁止使用arguments.callee)。 +这种模式利用了一个事实,即在任何函数内部都会创建一个`arguments`对象,它包含函数调用时传入的参数。同时`arguments`包含一个`callee`属性,指向正在被调用的函数。需要注意,ES5严格模式中已经禁止了`arguments.callee`的使用,因此最好对它的使用加以限制,并尽可能删除现有代码中已经用到的地方。 +-------------------校对分隔线----------------- ## 数组字面量 From 13909cb44a2ffde2c66c77e721b82bbdfd43cb94 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 30 Mar 2013 11:05:48 +0800 Subject: [PATCH 059/145] =?UTF-8?q?=E6=95=B0=E7=BB=84=E5=AD=97=E9=9D=A2?= =?UTF-8?q?=E9=87=8F=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E4=B8=80=E9=83=A8?= =?UTF-8?q?=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index 18540cc..6d978fc 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -282,31 +282,29 @@ ECMAScript5中修正了这种出乎意料的行为逻辑。在严格模式中, 这种模式利用了一个事实,即在任何函数内部都会创建一个`arguments`对象,它包含函数调用时传入的参数。同时`arguments`包含一个`callee`属性,指向正在被调用的函数。需要注意,ES5严格模式中已经禁止了`arguments.callee`的使用,因此最好对它的使用加以限制,并尽可能删除现有代码中已经用到的地方。 --------------------校对分隔线----------------- - ## 数组字面量 -和其他的大多数一样,JavaScript中的数组也是对象。可以通过内置构造函数Array()来创建数组,类似对象字面量,数组也可以通过字面量形式创建。而且更推荐使用字面量创建数组。 +和JavaScript中大多数“东西”一样,数组也是对象。可以通过内置构造函数`Array()`来创建数组,也可以通过字面量形式创建,就像对象字面量那样。而且更推荐使用字面量创建数组。 -这里的实例代码给出了创建两个具有相同元素的数组的两种方法,使用Array()和使用字面量模式: +这里的示例代码给出了创建两个具有相同元素的数组的两种方法,使用`Array()`和使用字面量模式: - // array of three elements - // warning: antipattern + // 有三个元素的数组 + // 注意:这是反模式 var a = new Array("itsy", "bitsy", "spider"); - // the exact same array + // 完全相同的数组 var a = ["itsy", "bitsy", "spider"]; - console.log(typeof a); // "object", because arrays are objects + console.log(typeof a); // "object",因为数组也是对象 console.log(a.constructor === Array); // true - ### 数组字面量语法 -数组字面量写法非常简单:整个数组使用方括号括起来,数组元素之间使用逗号分隔。数组元素可以是任意类型,也包括数组和对象。 +数组字面量写法非常简单:整个数组使用方括号括起来,数组元素之间使用逗号分隔。数组元素可以是任意类型,包括数组和对象。 -数组字面量语法简单直接、高雅美观。毕竟数组只是从位置0开始索引的值的集合,完全没必要包含构造器和new运算符的内容(代码会更多),保持简单即可。 +数组字面量语法简单直观而且优雅,毕竟数组只是从0开始索引的一些值的集合,完全没必要引入构造器和`new`运算符(还要写更多的代码)。 +-------------------校对分隔线----------------- ### 有意思的数组构造器 From ba8ae4484e650f07a12febcc34aeab0cf4902f83 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 30 Mar 2013 20:14:21 +0800 Subject: [PATCH 060/145] =?UTF-8?q?Array()=E6=9E=84=E9=80=A0=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E7=9A=84=E2=80=9C=E9=99=B7=E9=98=B1=E2=80=9D=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index 6d978fc..0ff6b18 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -304,37 +304,36 @@ ECMAScript5中修正了这种出乎意料的行为逻辑。在严格模式中, 数组字面量语法简单直观而且优雅,毕竟数组只是从0开始索引的一些值的集合,完全没必要引入构造器和`new`运算符(还要写更多的代码)。 --------------------校对分隔线----------------- - -### 有意思的数组构造器 +### Array()构造函数的“陷阱” -我们对new Array()敬而远之原因是为了避免构造函数带来的陷阱。 +我们对`new Array()`敬而远之还有一个原因,就是为了避免构造函数带来的陷阱。 -如果给Array()构造器传入一个数字,这个数字并不会成为数组的第一个元素,而是设置数组的长度。也就是说,new Array(3)创建了一个长度为3的数组,而不是某个元素是3。如果你访问数组的任意元素都会得到undefined,因为元素并不存在。下面示例代码展示了字面量和构造函数的区别: +如果给`Array()`构造函数传入一个数字,这个数字并不会成为数组的第一个元素,而是设置了数组的长度。也就是说,`new Array(3)`创建了一个长度为3的数组,而不是某个元素是3。如果你访问数组的任意元素都会得到`undefined`,因为元素并不存在。下面的示例代码展示了字面量和构造函数的区别: - // an array of one element + // 含有1个元素的数组 var a = [3]; console.log(a.length); // 1 console.log(a[0]); // 3 - // an array of three elements + // 含有3个元素的数组 var a = new Array(3); console.log(a.length); // 3 console.log(typeof a[0]); // "undefined" -尽管构造器的行为并不像我们想象的那样,当给new Array()传入一个浮点数时情况就更糟糕了。这时结果就会出错(译注:给new Array()传入浮点数会报“范围错误”RangError,new Array(3.00)则不会报错),因为数组长度不可能是浮点数。 +构造函数的行为可能有一点出乎意料,但当给`new Array()`传入一个浮点数时情况就更糟糕了,这时会出错(译注:给new Array()传入浮点数会报“范围错误”RangError),因为数组长度不可能是浮点数。 - // using array literal + // 使用数组字面量 var a = [3.14]; console.log(a[0]); // 3.14 var a = new Array(3.14); // RangeError: invalid array length console.log(typeof a); // "undefined" -为了避免在运行时动态创建数组时出现这种错误,强烈推荐使用数组字面量来代替new Array()。 +为了避免在运行时动态创建数组时出现这种错误,强烈推荐使用数组字面量来代替`new Array()`。 ->有些人用Array()构造器来做一些有意思的事情,比如用来生成重复字符串。下面这行代码返字符串包含255个空格(请读者思考为什么不是256个空格)。`var white = new Array(256).join(' ');` +> 有些人用`Array()`构造器来做一些有意思的事情,比如用来生成重复字符串。下面这行代码返回的字符串包含255个空格(请读者思考为什么不是256个空格)。`var white = new Array(256).join(' ');` +-------------------校对分隔线----------------- ### 检查是不是数组 From 119ab3314626781b4395977e91cf23ca0a057fa1 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 30 Mar 2013 20:26:12 +0800 Subject: [PATCH 061/145] =?UTF-8?q?=E6=95=B0=E7=BB=84=E5=AD=97=E9=9D=A2?= =?UTF-8?q?=E9=87=8F=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index 0ff6b18..c8736b5 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -333,9 +333,7 @@ ECMAScript5中修正了这种出乎意料的行为逻辑。在严格模式中, > 有些人用`Array()`构造器来做一些有意思的事情,比如用来生成重复字符串。下面这行代码返回的字符串包含255个空格(请读者思考为什么不是256个空格)。`var white = new Array(256).join(' ');` --------------------校对分隔线----------------- - -### 检查是不是数组 +### 检查是否数组 如果typeof的操作数是数组的话,将返回“object”。 @@ -363,6 +361,7 @@ ECMAScript 5定义了一个新的方法Array.isArray(),如果参数是数组 }; } +-------------------校对分隔线----------------- ## JSON From 84793159b1a0c527961bf6d1b65bd56bca9b206e Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 30 Mar 2013 21:13:47 +0800 Subject: [PATCH 062/145] =?UTF-8?q?JSON=20=E7=AC=AC=E4=B8=80=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index c8736b5..9ae4208 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -335,25 +335,24 @@ ECMAScript5中修正了这种出乎意料的行为逻辑。在严格模式中, ### 检查是否数组 -如果typeof的操作数是数组的话,将返回“object”。 +如果`typeof`的操作数是数组的话,将返回“object”。 console.log(typeof [1, 2]); // "object" -这个结果勉强说得过去,毕竟数组是一种对象,但对我们用处不大。往往你需要知道一个值是不是真正的数组。你可能见到过这种检查数组的方法:检查length属性、检查数组方法比如slice()等等。但这些方法非常脆弱,非数组的对象也可以拥有这些同名的属性。还有些人使用instanceof Array来判断数组,但这种方法在某些版本的IE里的多个iframe的场景中会出问题(译注:原因就是在不同iframe中创建的数组不会相互共享其prototype属性)。 +这个结果勉强说得过去,毕竟数组也是一种对象,但对我们来说这个结果却没什么用,实际上你往往是需要知道一个值是不是真正的数组。有时候你会见到一些检查数组的方法:检查`length`属性、检查数组方法比如`slice()`等等,但这些方法非常脆弱,非数组的对象也可以拥有这些同名的属性。还有些人使用`instanceof Array`来判断数组,但这种方法在某些版本的IE里的多个iframe的场景中会出问题(译注:原因就是在不同iframe中创建的数组不会相互共享其`prototype`属性)。 -ECMAScript 5定义了一个新的方法Array.isArray(),如果参数是数组的话就返回true。比如: +ECMAScript5定义了一个新的方法`Array.isArray()`,如果参数是数组的话就返回true。比如: Array.isArray([]); // true - // trying to fool the check - // with an array-like object + // 尝试用一个类似数组的对象去测试 Array.isArray({ length: 1, "0": 1, slice: function () {} }); // false -如果你的开发环境不支持ECMAScript5,可以通过Object.prototype.toString()方法来代替。如调用toString的call()方法并传入数组上下文,将返回字符串“[object Array]”。如果传入对象上下文,则返回字符串“[object Object]”。因此可以这样做: +如果你的开发环境不支持ECMAScript5,可以通过`Object.prototype.toString()`方法来代替。如调用`toString`的`call()`方法并传入数组上下文,将返回字符串“[object Array]”。如果传入对象上下文,则返回字符串“[object Object]”。因此可以这样做: if (typeof Array.isArray === "undefined") { Array.isArray = function (arg) { @@ -361,20 +360,19 @@ ECMAScript 5定义了一个新的方法Array.isArray(),如果参数是数组 }; } --------------------校对分隔线----------------- - ## JSON -上文我们刚刚讨论过对象和数组字面量,你已经对此很熟悉了,现在我们将目光转向JSON。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。很多语言中都实现了JSON,特别是在JavaScript中。 +我们刚刚讨论了对象和数组字面量,你应该很熟悉了,现在我们来看一看JSON。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,可以很容易地用在多种语言中,尤其是在JavaScript中。 JSON格式及其简单,它只是数组和对象字面量的混合写法,看一个JSON字符串的例子: {"name": "value", "some": [1, 2, 3]} -JSON和对象字面量在语法上的唯一区别是,合法的JSON属性名均用引号包含。而在对象字面量中,只有属性名是非法的标识符时采用引号包含,比如,属性名中包含空格`{"first name": "Dave"}`。 +JSON和对象字面量在语法上的唯一区别是,合法的JSON属性名均需要用引号包含。而在对象字面量中,只有属性名是非法的标识符时才使用引号包含,比如,属性名中包含空格`{"first name": "Dave"}`。 在JSON字符串中,不能使用函数和正则表达式字面量。 +-------------------校对分隔线----------------- ### 使用JSON From 29f7aeb9f2ea775f51daa520e7bdade50ce99eb6 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 30 Mar 2013 21:19:24 +0800 Subject: [PATCH 063/145] =?UTF-8?q?JSON=20=E6=A0=A1=E5=AF=B9=E5=AE=8C?= =?UTF-8?q?=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index 9ae4208..2e31fb8 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -372,44 +372,41 @@ JSON和对象字面量在语法上的唯一区别是,合法的JSON属性名均 在JSON字符串中,不能使用函数和正则表达式字面量。 --------------------校对分隔线----------------- - ### 使用JSON -在前面的章节中讲到,出于安全考虑,不推荐使用eval()来“粗糙的”解析JSON字符串。最好使用JSON.parse()方法,ES5中已经包含了这个方法,而且在现代浏览器的JavaScript引擎中已经内置支持JSON了。对于老旧的JavaScript引擎来说,你可以使用JSON.org所提供的JS文件(http://www.json.org/json2.js)来获得JSON对象和方法。 +在前面的章节中讲到,出于安全考虑,不推荐使用`eval()`来粗暴地解析JSON字符串。最好是使用`JSON.parse()`方法,ES5中已经包含了这个方法,并且现代浏览器的JavaScript引擎中也已经内置支持JSON了。对于老旧的JavaScript引擎来说,你可以使用JSON.org所提供的JS文件()来获得JSON对象和方法。 - // an input JSON string + // 输入JSON字符串 var jstr = '{"mykey": "my value"}'; - // antipattern + // 反模式 var data = eval('(' + jstr + ')'); - // preferred + // 更好的方式 var data = JSON.parse(jstr); console.log(data.mykey); // "my value" -如果你已经在使用某个JavaScript库了,很可能库中提供了解析JSON的方法,就不必再额外引入JSON.org的库了,比如,如果你已经使用了YUI3,你可以这样: +如果你已经在使用某个JavaScript库了,很可能这个库中已经提供了解析JSON的方法,就不必再额外引入JSON.org的库了,比如,如果你已经使用了YUI3,你可以这样: - // an input JSON string + // 输入JSON字符串 var jstr = '{"mykey": "my value"}'; - // parse the string and turn it into an object - // using a YUI instance + // 使用YUI来解析并将结果返回为一个对象 YUI().use('json-parse', function (Y) { var data = Y.JSON.parse(jstr); console.log(data.mykey); // "my value" }); -如果你使用的是jQuery,可以直接使用它提供的parseJSON()方法: +如果你使用的是jQuery,可以直接使用它提供的`parseJSON()`方法: - // an input JSON string + // 输入JSON字符串 var jstr = '{"mykey": "my value"}'; var data = jQuery.parseJSON(jstr); console.log(data.mykey); // "my value" -和JSON.parse()方法相对应的是JSON.stringify()。它将对象或数组(或任何原始值)转换为JSON字符串。 +和`JSON.parse()`方法相对应的是`JSON.stringify()`。它将对象或数组(或任何原始值)转换为JSON字符串。 var dog = { name: "Fido", @@ -419,10 +416,10 @@ JSON和对象字面量在语法上的唯一区别是,合法的JSON属性名均 var jsonstr = JSON.stringify(dog); - // jsonstr is now: + // jsonstr的值为 // {"name":"Fido","dob":"2010-04-11T22:36:22.436Z","legs":[1,2,3,4]} - +-------------------校对分隔线----------------- ## 正则表达式字面量 JavaScript中的正则表达式也是对象,可以通过两种方式创建它们: From a8652e5edbbe267506ded05ce5e321a5a981e159 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 31 Mar 2013 21:04:59 +0800 Subject: [PATCH 064/145] =?UTF-8?q?=E6=AD=A3=E5=88=99=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=E5=AD=97=E9=9D=A2=E9=87=8F=20=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index 2e31fb8..dc037d9 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -419,47 +419,45 @@ JSON和对象字面量在语法上的唯一区别是,合法的JSON属性名均 // jsonstr的值为 // {"name":"Fido","dob":"2010-04-11T22:36:22.436Z","legs":[1,2,3,4]} --------------------校对分隔线----------------- ## 正则表达式字面量 JavaScript中的正则表达式也是对象,可以通过两种方式创建它们: -- 使用new RegExp()构造函数 +- 使用`new RegExp()`构造函数 - 使用正则表达式字面量 -下面的示例代码展示了创建正则表达式的两种方法,创建的正则用来匹配一个反斜杠(\): +下面的示例代码展示了创建用来匹配一个反斜杠(\)的正则表达式的两种方法: - // regular expression literal + // 正则表达式字面量 var re = /\\/gm; - // constructor + // 构造函数 var re = new RegExp("\\\\", "gm"); -显然正则表达式字面量写法的代码更短,且不必强制按照类构造器的思路来写。因此更推荐使用字面量写法。 +显然正则表达式字面量写法的代码更短,而且不会让你觉得在用像类一样的构造函数的思想在写正则表达式,因此更推荐使用字面量写法。 -另外,如果使用RegExp()构造函数写法,还需要考虑对引号和反斜杠进行转义,正如上段代码所示的那样,用了四个反斜杠来匹配一个反斜杠。这会增加正则表达式的长度,而且让正则变得难于理解和维护。刚开始学习正则表达式不是很容易,所以不要放弃任何一个简化它们的机会,所以要尽量使用字面量而不是通过构造函数来创建正则。 +另外,如果使用`RegExp()`构造函数写法,还需要考虑对引号和反斜杠进行转义,正如上段代码所示的那样,用了四个反斜杠来匹配一个反斜杠。这会增加正则表达式的长度,而且让它变得难于理解和维护。正则表达式入门不是件容易的事,所以不要放弃任何一个简化它们的机会,尽量使用字面量而不是通过构造函数来创建正则表达式。 - ### 正则表达式字面量语法 -正则表达式字面量使用两个斜线包裹起来,正则的主体部分不包括两端的斜线。在第二个斜线之后可以指定模式匹配的修饰符用以高级匹配,修饰符不需要引号引起来,JavaScript中有三个修饰符: +正则表达式字面量使用两个斜杠包裹,主体部分不包括两端的斜线。在第二个斜线之后可以指定模式匹配的修饰符,修饰符不需要用引号引起来,JavaScript中有三个修饰符: -- g,全局匹配 -- m,多行匹配 -- i,忽略大小写的匹配 +- `g`,全局匹配 +- `m`,多行匹配 +- `i`,忽略大小写的匹配 -修饰符可以自由组合,而且顺序无关: +修饰符可以自由组合,而且与顺序无关: var re = /pattern/gmi; -使用正则表达式字面量可以让代码更加简洁高效,比如当调用String.prototype.prelace()方法时,可以传入正则表达式参数: +使用正则表达式字面量可以让代码更加简洁高效,比如当调用`String.prototype.prelace()`方法时,可以传入正则表达式参数: var no_letters = "abc123XYZ".replace(/[a-z]/gi, ""); console.log(no_letters); // 123 -有一种不得不使用new RegExp()的情形,有时正则表达式是不确定的,直到运行时才能确定下来。 +有一种不得不使用`new RegExp()`的场景,就是正则表达式是不确定,只有等到运行时才能确定下来的情况。 -正则表达式字面量和Regexp()构造函数的另一个区别是,正则表达式字面量只在解析时创建一次正则表达式对象(译注:多次解析同一个正则表达式,会产生相同的实例对象)。如果在循环体内反复创建相同的正则表达式,则每个正则对象的所有属性(比如lastIndex)只会设置一次(译注:由于每次创建相同的实例对象,每个循环中的实例对象都是同一个,属性也自然相同),下面这个例子展示了两次都返回了相同的正则表达式的情形(译注:这里作者的表述只是针对ES3规范而言,下面这段代码在NodeJS、IE6-IE9、FireFox4、Chrome10、Safari5中运行结果和作者描述的不一致,Firefox 3.6中的运行结果和作者描述是一致的,原因可以在ECMAScript5规范第24页和第247页找到,也就是说在ECMAScript3规范中,用正则表达式创建的RegExp对象会共享同一个实例,而在ECMAScript5中则是两个独立的实例。而最新的Firefox4、Chrome和Safari5都遵循ECMAScript5标准,至于IE6-IE8都没有很好的遵循ECMAScript3标准,不过在这个问题上反而处理对了。很明显ECMAScript5的规范更符合开发者的期望)。 +正则表达式字面量和构造函数还有另一个区别,就是字面量只在解析时创建一次正则表达式对象(译注:多次解析同一个正则表达式,会产生相同的实例对象)。如果在循环体内反复使用相同的字面量创建对象,则会返回第一次创建的对象以及它的属性(比如`lastIndex`)。下面这个例子展示了两次返回相同的正则表达式的情形。 function getRE() { var re = /[a-z]/; @@ -474,10 +472,11 @@ JavaScript中的正则表达式也是对象,可以通过两种方式创建它 reg.foo = "baz"; console.log(re2.foo); // "baz" ->在ECMAScript5中这种情形有所改变,相同正则表达式字面量的每次计算都会创建新的实例对象,目前很多现代浏览器也对此做了纠正(译注:比如在Firefox4就纠正了Firefox3.6的这种“错误”)。 +> 在ECMAScript5中这种情况有所改变,相同正则表达式字面量的每次计算都会创建新的实例对象,目前很多现代浏览器也对此做了纠正。 -最后需要提一点,不带new调用RegExp()(作为普通的函数)和带new调用RegExp()是完全一样的。 +最后需要提一点,不带`new`调用`RegExp()`(作为普通的函数)和带`new`调用`RegExp()`是完全一样的。 +-------------------校对分隔线----------------- ## 原始值的包装对象 From 558b84c6a647905b695bed06a045bd9c32b79177 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 31 Mar 2013 21:24:39 +0800 Subject: [PATCH 065/145] =?UTF-8?q?=E5=8E=9F=E5=A7=8B=E5=80=BC=E7=9A=84?= =?UTF-8?q?=E5=8C=85=E8=A3=85=E5=AF=B9=E8=B1=A1=20=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index dc037d9..9e349e5 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -476,64 +476,61 @@ JavaScript中的正则表达式也是对象,可以通过两种方式创建它 最后需要提一点,不带`new`调用`RegExp()`(作为普通的函数)和带`new`调用`RegExp()`是完全一样的。 --------------------校对分隔线----------------- - ## 原始值的包装对象 -JavaScript中有五种原始类型:数字、字符串、布尔值、null和undefined。除了null和undefined之外,其他三种都有对应的“包装对象”(wrapper objects)。可以通过内置构造函数来生成包装对象,Number()、String()、和Boolean()。 +JavaScript中有五种原始类型:数字、字符串、布尔值、`null`和`undefined`。除了`null`和`undefined`之外,其他三种都有对应的“包装对象”(primitive wrapper object)。可以通过内置构造函数`Number()`、`String()`和`Boolean()`来生成包装对象。 为了说明数字原始值和数字对象之间的区别,看一下下面这个例子: - // a primitive number + // 一个数字原始值 var n = 100; console.log(typeof n); // "number" - // a Number object + // 一个Number对象 var nobj = new Number(100); console.log(typeof nobj); // "object" -包装对象带有一些有用的属性和方法,比如,数字对象就带有toFixed()和toExponential()之类的方法。字符串对象带有substring()、chatAt()和toLowerCase()等方法以及length属性。这些方法非常方便,和原始值相比,这让包装对象具备了一定优势。其实原始值也可以调用这些方法,因为原始值会首先转换为一个临时对象,如果转换成功,则调用包装对象的方法。 +包装对象带有一些有用的属性和方法。比如,数字对象就带有`toFixed()`和`toExponential()`之类的方法,字符串对象带有`substring()`、`chatAt()`和`toLowerCase()`等方法以及`length`属性。这些方法非常方便,和原始值相比,这是包装对象的优势,但其实原始值也可以调用这些方法,因为原始值会首先转换为一个临时对象,如果转换成功,则调用包装对象的方法。 - // a primitive string be used as an object + // 像使用对象一样使用一个字符串原始值 var s = "hello"; console.log(s.toUpperCase()); // "HELLO" - // the value itself can act as an object + // 值本身也可以像对象一样 "monkey".slice(3, 6); // "key" - // same for numbers + // 数字也是一样 (22 / 7).toPrecision(3); // "3.14" 因为原始值可以根据需要转换成对象,这样的话,也不必为了用包装对象的方法而将原始值手动“包装”成对象。比如,不必使用new String("hi"),直接使用"hi"即可。 - // avoid these: + // 避免这些: var s = new String("my string"); var n = new Number(101); var b = new Boolean(true); - // better and simpler: + // 更好更简洁的办法: var s = "my string"; var n = 101; var b = true; -不得不使用包装对象的一个原因是,有时我们需要对值进行扩充并保持值的状态。原始值毕竟不是对象,不能直接对其进行扩充(译注:比如`1.property = 2`会报错)。 +不得不使用包装对象的一个场景是,有时我们需要对值进行扩充并保持值的状态。原始值毕竟不是对象,不能直接对其进行扩充。 - // primitive string + // 字符串原始值 var greet = "Hello there"; - // primitive is converted to an object - // in order to use the split() method + // 为使用split方法,原始值被转换为对象 greet.split(' ')[0]; // "Hello" - // attemting to augment a primitive is not an error + // 给原始值添加属性并不会报错 greet.smile = true; - // but it doesn't actually work + // 但实际上却没有作用 typeof greet.smile; // "undefined" -在这段示例代码中,greet只是临时转换成了对象,以保证访问其属性/方法时不会出错。另一方面,如果greet通过new String()定义为一个对象,那么扩充smile属性就会按照期望的那样执行。对字符串、数字或布尔值的扩充并不常见,除非你清楚自己想要什么,否则不必使用包装对象。 +在这段示例代码中,`greet`只是临时被转换成了对象,以保证访问其属性、方法时不会出错。而如果是另一种情况,`greet`通过`new String()`被定义为一个对象,那么扩充`smile`属性的过程就会像我们预期的那样。对字符串、数字或布尔值进行扩充的情况很少见,因此建议只在确实有必要的情况下使用包装对象。 -当省略new时,包装器将传给它的参数转换为原始值: +当省略`new`时,包装对象的构造函数将传给它的参数转换为原始值: typeof Number(1); // "number" typeof Number("1"); // "number" @@ -541,7 +538,7 @@ JavaScript中有五种原始类型:数字、字符串、布尔值、null和und typeof String(1); // "string" typeof Boolean(1); // "boolean" - +-------------------校对分隔线----------------- ## Error 对象 JavaScript中有很多内置的Error构造函数,比如Error()、SyntaxError(),TypeError()等等,这些“错误”通常和throw语句一起使用。这些构造函数创建的错误对象包含这些属性: From abdb0a818635c071669c8ee2a671476d4279de4f Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 31 Mar 2013 21:33:12 +0800 Subject: [PATCH 066/145] =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index 9e349e5..12a5c3a 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -538,42 +538,41 @@ JavaScript中有五种原始类型:数字、字符串、布尔值、`null`和` typeof String(1); // "string" typeof Boolean(1); // "boolean" --------------------校对分隔线----------------- -## Error 对象 +## 错误处理对象 -JavaScript中有很多内置的Error构造函数,比如Error()、SyntaxError(),TypeError()等等,这些“错误”通常和throw语句一起使用。这些构造函数创建的错误对象包含这些属性: +JavaScript中有很多内置的错误处理构造函数,比如`Error()`、`SyntaxError()`,`TypeError()`等等,它们通常和`throw`语句一起被使用。这些构造函数创建的错误对象包含这些属性: -**name** +- `name` -name属性是指创建这个对象的构造函数的名字,通常是“Errora”,有时会有特定的名字比如“RangeError” + name属性是指产生这个对象的构造函数的名字,通常是“Error”,有时会有特定的名字比如“RangeError” -**message** +- `message` -创建这个对象时传入构造函数的字符串 + 创建这个对象时传入构造函数的字符串 -错误对象还有其他一些属性,比如产生错误的行号和文件名,但这些属性是浏览器自行实现的,不同浏览器的实现也不一致,因此出于兼容性考虑,并不推荐使用这些属性。 +错误对象还有一些其他的属性,比如产生错误的行号和文件名,但这些属性是浏览器自行实现的,不同浏览器的实现也不一致,因此出于兼容性考虑,并不推荐使用这些属性。 -另一方面,throw可以抛出任何对象,并不限于“错误对象”,因此你可以根据需要抛出自定义的对象。这些对象包含属性“name”和“message”或其他你希望传递给异常处理逻辑的信息,异常处理逻辑由catch语句指定。你可以灵活运用抛出的错误对象,将程序从错误状态恢复至正常状态。 +`throw`可以抛出任何对象,并不限于“错误对象”,因此你可以根据需要抛出自定义的对象。这些对象包含属性“name”和“message”或其他你希望传递给异常处理逻辑的信息,异常处理逻辑由`catch`语句指定。你可以灵活运用抛出的错误对象,将程序从错误状态恢复至正常状态。 try { - // something bad happened, throw an error + // 一些不好的事情发生了,抛出错误 throw { - name: "MyErrorType", // custom error type + name: "MyErrorType", // 自定义错误类型 message: "oops", extra: "This was rather embarrassing", - remedy: genericErrorHandler // who should handle it + remedy: genericErrorHandler // 应该由谁处理 }; } catch (e) { - // inform the user + // 通知用户 alert(e.message); // "oops" - // gracefully handle the error - e.remedy(); // calls genericErrorHandler() + // 优雅地处理错误 + e.remedy(); // 调用genericErrorHandler() } -通过new调用和省略new调用错误构造函数是一模一样的,他们都返回相同的错误对象。 +使用`new`调用和省略`new`调用错误构造函数是一模一样的,他们都返回相同的错误对象。 - +-------------------校对分隔线----------------- ## 小结 在本章里,我们讨论了多种字面量模式,它们是使用构造函数写法的替代方案,本章讲述了这些内容: From db8e8992f26959cac86770ae1c53660a5e8476c1 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 31 Mar 2013 21:56:01 +0800 Subject: [PATCH 067/145] =?UTF-8?q?=E7=AC=AC=E4=B8=89=E7=AB=A0=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter3.markdown | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/chapter3.markdown b/chapter3.markdown index 12a5c3a..e9963f0 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -1,6 +1,6 @@ # 第三章 字面量和构造函数 -JavaScript中的字面量模式更加简洁、有表现力,而且在定义对象时不容易出错。本章将会讨论字面量,包括对象、数组和正则表达式字面量,以及为什么字面量要比等价的内置构造函数(如`Object()`、`Array()`等)要更好。本章还会介绍JSON格式,JSON是使用数组和对象字面量的形式定义的一种数据传输格式。本章还会讨论自定义构造函数,包括如何强制使用`new`以确保构造函数正确执行。 +JavaScript中的字面量模式更加简洁、有表现力,而且在定义对象时不容易出错。本章将会讨论字面量,包括对象、数组和正则表达式字面量,以及为什么字面量要比等价的内置构造函数(如`Object()`、`Array()`等)要更好。本章还会介绍JSON格式,JSON是使用数组和对象字面量的形式定义的一种数据交换格式。本章还会讨论自定义构造函数,包括如何强制使用`new`以确保构造函数正确执行。 为了方便使用字面量而不是构造函数,本章还会补充一些知识,比如内置包装构造函数`Number()`、`String()`和`Boolean()`,以及如何将它们和原始值(数字、字符串和布尔值)比较。最后,快速介绍一下`Error()`构造函数的用法。 @@ -572,20 +572,19 @@ JavaScript中有很多内置的错误处理构造函数,比如`Error()`、`Syn 使用`new`调用和省略`new`调用错误构造函数是一模一样的,他们都返回相同的错误对象。 --------------------校对分隔线----------------- ## 小结 在本章里,我们讨论了多种字面量模式,它们是使用构造函数写法的替代方案,本章讲述了这些内容: -- 对象字面量写法——一种简洁优雅的定义对象的方法,名值对之间用逗号分隔,通过花括号包装起来 -- 构造函数——内置构造函数(内置构造函数通常都有对应的字面量语法)和自定义构造函数。 -- 一种强制函数以构造函数的模式执行(不管用不用new调用构造函数,都始终返回new出来的实例)的技巧 -- 数组字面量写法——数组元素之间使用逗号分隔,通过方括号括起来 -- JSON——是一种轻量级的数据交换格式 +- 对象字面量写法——一种简洁优雅的定义对象的方法,通过花括号包裹,名值对之间用逗号分隔 +- 构造函数——内置构造函数(内置构造函数通常都有对应的字面量语法)和自定义构造函数 +- 一种强制函数以构造函数的模式运行行(不管用不用`new`调用构造函数,都始终返回`new`出来的实例)的技巧 +- 数组字面量写法——通过方括号包裹,数组元素之间使用逗号分隔 +- JSON——一种轻量级的数据交换格式 - 正则表达式字面量 -- 避免使用其他的内置构造函数:String()、Number()、Boolean()以及不同种类的Error()构造器 +- 避免使用其他的内置构造函数:`String()`、`Number()`、`Boolean()`以及不同种类的`Error()`构造函数 -通常除了Date()构造函数之外,其他的内置构造函数并不常用,下面的表格中对这些构造函数以及它们的字面量语法做了整理。 +通常情况下,除了`Date()`之外,其他的内置构造函数并不常用,下面的表格对这些构造函数以及它们的字面量语法做了整理。
内置构造函数(不推荐)直接量语法和原始值(推荐)字面量语法和原始值(推荐)
var o = new Object();
From e8e8a6b146ae07d8d97469b768dc9dbb9f951a05 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 14 Apr 2013 11:15:35 +0800 Subject: [PATCH 068/145] =?UTF-8?q?=E8=83=8C=E6=99=AF=E7=9F=A5=E8=AF=86=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 92c18dd..f266c16 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -1,14 +1,11 @@ - # 函数 -熟练运用函数是JavaScript程序员的必备技能,因为在JavaScript中函数实在是太常用了。它能够完成的任务种类非常之多,而在其他语言中则需要很多特殊的语法支持才能达到这种能力。 +熟练运用函数是JavaScript程序员的必备技能,因为在JavaScript中函数实在是太常用了。它能够完成各种类型的任务,而在其他语言中则需要很多特殊的语法支持才能拥有这种能力。 -在本章将会介绍在JavaScript中定义函数的多种方式,包括函数表达式和函数声明、以及局部作用域和变量声明提前的工作原理。然后会介绍一些有用的模式,帮助你设计API(为你的函数提供更好的接口)、搭建代码架构(使用尽可能少的全局对象)、并优化性能(避免不必要的操作)。 +本章将会介绍在JavaScript中定义函数的几种方式,包括函数表达式和函数声明以及局部作用域和变量声明提前的工作原理。然后会介绍一些有用的模式,帮助你设计API(为你的函数提供更好的接口)、搭建代码架构(使用尽可能少的全局对象)、并优化性能(避免不必要的操作)。 -现在让我们来一起揭秘JavaScript函数,我们首先从一些背景知识开始说起。 +现在让我们来一起揭秘JavaScript函数,首先从一些背景知识开始说起。 - - ## 背景知识 JavaScript的函数具有两个主要特性,正是这两个特性让它们与众不同。第一个特性是,函数是一等对象(first-class object),第二个是函数提供作用域支持。 @@ -16,22 +13,23 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 函数是对象,那么: - 可以在程序执行时动态创建函数 -- 可以将函数赋值给变量,可以将函数的引用拷贝至另一个变量,可以扩充函数,除了某些特殊场景外均可被删除。 -- 可以将函数作为参数传入另一个函数,也可以被当作返回值返回。 +- 可以将函数赋值给变量,可以将函数的引用拷贝至另一个变量,可以扩充函数,除了某些特殊场景外均可被删除 +- 可以将函数作为参数传入另一个函数,也可以被当作返回值返回 - 函数可以包含自己的属性和方法 -对于一个函数A来说,首先它是对象,拥有属性和方法,其中某个属性碰巧是另一个函数B,B可以接受函数作为参数,假设这个函数参数为C,当执行B的时候,返回另一个函数D。乍一看这里有一大堆相互关联的函数。当你开始习惯函数的许多用法时,你会惊叹原来函数是如此强大、灵活并富有表现力。通常说来,一说到JavaScript的函数,我们首先认为它是对象,它具有一个可以“执行”的特性,也就是说我们可以“调用”这个函数。 +有可能会有这样的情况:一个函数A,它也是一个对象,拥有属性和方法,其中某个属性是另一个函数B,B可以接受函数作为参数,假设这个函数参数为C,当执行B的时候,返回另一个函数D。乍一看这里有一大堆相互关联的函数,但当你开始习惯函数的许多用法时,你会惊叹原来函数是如此灵活、强大县且富有表现力。通常说来,一说到JavaScript的函数,我们首先认为它是一个对象,具有一个可以“执行”的特性,也就是说我们可以“调用”这个函数。 -我们通过new Function()构造器来生成一个函数,这时可以明显看出函数是对象: +我们通过`new Function()`构造函数来创建一个函数,这时可以明显看出函数是对象: - // antipattern - // for demo purposes only + // 反模式,仅用于演示 var add = new Function('a, b', 'return a + b'); - add(1, 2); // returns 3 + add(1, 2); // 返回 3 + +在这段代码中,毫无疑问`add()`是一个对象,因为它是由构造函数创建的。这里并不推荐使用`Function()`构造函数来创建函数(和`eval()`一样糟糕),因为程序逻辑代码是以字符串的形式传入构造器的。这样的代码可读性差,写起来也很费劲,你还要对代码中的引号做转义处理,并需要特别关注为了保持可读性而保留的空格和缩进。 -在这段代码中,毫无疑问add()是一个对象,毕竟它是由构造函数创建的。这里并不推荐使用Function()构造器创建函数(和eval()一样糟糕),因为程序逻辑代码是以字符串的形式传入构造器的。这样的代码可读性差,写起来也很费劲,你不得不对逻辑代码中的引号做转义处理,并需要特别关注为了让代码保持一定的可读性而保留的空格和缩进。 +函数的第二个重要特性是它能提供作用域支持。在JavaScript中没有块级作用域(译注:在JavaScript1.7中提供了块级作用域部分特性的支持,可以通过`let`来声明块级作用域内的“局部变量”),也就是说不能通过花括号来创建作用域,JavaScript中只有函数作用域(译注:这里只针对函数而言,此外JavaScript还有全局作用域)。在函数内所有通过`var`声明的变量都是局部变量,在函数外部是不可见的。刚才所说的花括号无法提供作用域支持的意思是说,如果在`if`条件句、`for`或`while`循环体内用`var`定义了变量,这个变量并不是属于`if`语句或`for`(`while`)循环的局部变量,而是属于它所在的函数。如果不在任何函数内部,它会成为全局变量。在第二章里提到我们要减少对全局命名空间的污染,那么使用函数则是控制变量作用域的最佳选择。 -函数的第二个重要特性是它能提供作用域支持。在JavaScript中没有块级作用域(译注:在JavaScript1.7中提供了块级作用域部分特性的支持,可以通过let来声明块级作用域内的“局部变量”),也就是说不能通过花括号来创建作用域,JavaScript中只有函数作用域(译注:这里作者的表述只针对函数而言,此外JavaScript还有全局作用域)。在函数内所有通过var声明的变量都是局部变量,在函数外部是不可见的。刚才所指花括号无法提供作用域支持的意思是说,如果在if条件句内、或在for或while循环体内用var定义了变量,这个变量并不是属于if语句或for(while)循环的局部变量,而是属于它所在的函数。如果不在任何函数内部,它会成为全局变量。在第二章里提到我们要减少对全局命名空间的污染,那么使用函数则是控制变量的作用域的不二之选。 +---------校对分割线--------- ### 术语释义 From 6d4a5e6721fac24a4148011c3249bced6e50ff4a Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 14 Apr 2013 11:50:36 +0800 Subject: [PATCH 069/145] =?UTF-8?q?=E8=83=8C=E6=99=AF=E7=9F=A5=E8=AF=86=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=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, 41 insertions(+), 50 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index f266c16..b28d8e7 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -29,113 +29,104 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 函数的第二个重要特性是它能提供作用域支持。在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的调试过程中非常有用,还能让函数递归调用自身,其他情况可以省略它。 +当省略第二个`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的函数表达式 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" + // 我是具名函数表达式,我的名字是“me” }); - // another function expression + // 另一个函数表达式 var myobject = { say: function () { - // I am a function expression + // 我是函数表达式 } }; -函数声明只能出现在“程序代码”中,也就是说在别的函数体内或在全局。这个定义不能赋值给变量或属性,同样不能作为函数调用的参数。下面这个例子是函数声明的合法用法,这里所有的函数foo(),bar()和local()都使用函数声明来定义: +函数声明只能出现在“程序代码”中,也就是说在别的函数体内或在全局。这个定义不能赋值给变量或属性,同样不能作为函数调用的参数。(译注:注意这里说的是函数声明的语句,而不是通过声明语句定义出来的函数本身。任何函数都是可以被赋值给变量和属性的,也可以被作为参数传递。)下面这个例子是函数声明的合法用法,这里所有的函数`foo()`,`bar()`和`local()`都使用函数声明来定义: - // global scope + // 全局作用域 function foo() {} function local() { - // local scope + // 本地作用域 function bar() {} return bar; } - ### 函数的name属性 -选择函数定义模式的另一个考虑是只读属性name的可用性。尽管标准规范中并未规定,但很多运行环境都实现了name属性,在函数声明和带有名字的函数表达式中是有name的属性定义的。在匿名函数表达式中,则不一定有定义,这个是和实现相关的,在IE中是无定义的,在Firefox和Safari中是有定义的,但是值为空字符串。 +选择用哪种模式定义函数时的另一个考虑是只读属性`name`的可用性。尽管标准规范中并未定义,但很多运行环境都实现了`name`属性,在函数声明和具名函数表达式中是有`name`属性的。在匿名函数表达式中,则不一定有定义,这个是和实现相关的,在IE中是无定义的,在Firefox和Safari中是有定义的,但是值为空字符串。 - function foo() {} // declaration - var bar = function () {}; // expression - var baz = function baz() {}; // named expression + function foo() {} // 函数声明 + var bar = function () {}; // 匿名函数表达式 + var baz = function baz() {}; // 具名函数表达式 foo.name; // "foo" bar.name; // "" baz.name; // "baz" -在Firebug或其他工具中调试程序时name属性非常有用,它可以用来显示当前正在执行的函数。同样可以通过name属性来递归的调用函数自身。如果你对这些场景不感兴趣,那么请尽可能的使用匿名函数表达式,这样会更简单、且冗余代码更少。 +在Firebug或其他工具中调试程序时`name`属性非常有用,它可以用来显示当前正在执行的函数。同样可以通过`name`属性来递归地调用函数自身。如果你对这些场景不感兴趣,那么请尽可能地使用匿名函数表达式,这样会更简单、且冗余代码更少。 -和函数声明相比而言,函数表达式的语法更能说明函数是一种对象,而不是某种特别的语言写法。 +相对函数声明而言,函数表达式的语法更能说明函数是一种和其它对象类似的对象,而不是语言中某种特别的组成部分。 ->我们可以将一个带名字的函数表达式赋值给变量,变量名和函数名不同,这在技术上是可行的。比如:`var foo = function bar(){};`。然而,这种用法的行为在浏览器中的兼容性不佳(特别是IE中),因此并不推荐大家使用这种模式。 +> 我们可以将一个带名字的函数表达式赋值给变量,变量名和函数名不同,这在技术上是可行的。比如:`var foo = function bar(){};`。然而,这种用法的行为在浏览器中的兼容性不好(特别是IE中),因此并不推荐大家使用这种模式。 - -### 函数提前 +### 声明提前 -通过前面的讲解,你可能以为函数声明和带名字的函数表达式是完全等价的。事实上不是这样,主要区别在于“声明提前”的行为。 +通过前面的讲解,你可能以为函数声明和具名函数表达式是完全等价的。事实上并不是这样,主要区别在于“声明提前”的行为。 ->术语“提前”并未在ECMAScript中定义,但是并没有其他更好的方法来描述这种行为了。 +> 术语“提前”并未在ECMAScript中定义,但是它是一种很好地描述这种行为的方法。 我们知道,不管在函数内何处声明变量,变量都会自动提前至函数体的顶部。对于函数来说亦是如此,因为他们也是一种对象,赋值给了变量。需要注意的是,函数声明定义的函数不仅能让声明提前,还能让定义提前,看一下这段示例代码: - // antipattern - // for illustration only + // 反模式,仅用于演示 - // global functions + // 全局函数 function foo() { alert('global foo'); } @@ -151,30 +142,30 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 foo(); // "local foo" bar(); // TypeError: bar is not a function - // function declaration: - // variable 'foo' and its implementation both get hoisted + // 函数声明: + // 变量foo和它的定义实现都被提前了 function foo() { alert('local foo'); } - // function expression: - // only variable 'bar' gets hoisted - // not the implementation + // 函数表达式: + // 只有变量bar被提前,它的定义实现没有被提前 var bar = function () { alert('local bar'); }; } hoistMe(); -在这段代码中,和普通的变量一样,hoistMe()函数中的foo和bar被“搬运”到了顶部,覆盖了全局的foo和bar。不同之处在于,局部的foo()定义提前至顶部并能正常工作,尽管定义它的位置并不靠前。bar()的定义并未提前,只是声明提前了。因此当程序执行到bar()定义的位置之前,它的值都是undefined,并不是函数(防止当前上下文查找到作用域链上的全局的bar(),也就“覆盖”了全局的bar())。 +在这段代码中,和普通的变量一样,`hoistMe()`函数中的`foo`和`bar`被“搬运”到了顶部,覆盖了全局的`foo()`和`bar()`。不同之处在于,本地的`foo()`的位置并不在前面,但它的定义却被提前到了顶部并能正常工作,而`bar()`的定义并未提前,只有声明提前了。因此当程序执行到`bar()`定义的位置之前,它的值都不是函数,而是`undefined`(在此期间全局的`bar()`都是被本地覆盖的)。 -到目前为止我们介绍了必要的背景知识和函数定义相关的术语,下面开始介绍一些JavaScript所提供的函数相关的好的模式,我们从回调模式开始。同样,再次强调JavaScript函数的两个特殊特性,掌握这两点至关重要: +到目前为止我们介绍了必要的背景知识和函数定义相关的术语,下面开始介绍一些JavaScript所提供的函数相关的模式,我们从回调模式开始。再次强调JavaScript函数的两个特性,掌握这两点至关重要: - 函数是对象 -- 函数提供局部变量作用域 +- 函数提供本地变量作用域 +---------校对分割线--------- ## 回调模式 From ad9301537c255db54c807a0b78d72f700c63821d Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 14 Apr 2013 23:08:21 +0800 Subject: [PATCH 070/145] =?UTF-8?q?=E5=9B=9E=E8=B0=83=E6=A8=A1=E5=BC=8F=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E4=B8=80=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 53 ++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index b28d8e7..908a227 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -164,45 +164,41 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 - 函数是对象 - 函数提供本地变量作用域 - ----------校对分割线--------- - ## 回调模式 -函数是对象,也就意味着函数可以当作参数传入另外一个函数中。当你给函数writeCode()传入一个函数参数introduceBugs(),在某个时刻writeCode()执行了(或调用了)introduceBugs()。在这种情况下,我们说introduceBugs()是一个“回调函数”,简称“回调”: +函数是对象,也就意味着函数可以当作参数传入另外一个函数中。给函数`writeCode()`传入一个函数参数`introduceBugs()`,在某个时刻`writeCode()`执行了(或调用了)`introduceBugs()`,在这种情况下,我们称`introduceBugs()`是一个“回调函数”,简称“回调”: function writeCode(callback) { - // do something... + // 做点什么…… callback(); - // ... + // …… } function introduceBugs() { - // ... make bugs + // …… } writeCode(introduceBugs); -注意introduceBugs()是如何作为参数传入writeCode()的,当作参数的函数不带括号。括号的意思是执行函数,而这里我们希望传入一个引用,让writeCode()在合适的时机执行它(调用它)。 +注意`introduceBugs()`作为参数传入`writeCode()`时,函数后面是不带括号的。括号的意思是执行函数,而这里我们希望传入一个引用,让`writeCode()`在合适的时机执行它(调用它)。 - -### 一个回调的例子 +### 回调的例子 -我们从一个例子开始,首先介绍无回调的情况,然后在作修改。假设你有一个通用的函数,用来完成某种复杂的逻辑并返回一大段数据。假设我们用findNodes()来命名这个通用函数,这个函数用来对DOM树进行遍历,并返回我所感兴趣的页面节点: +我们从一个例子开始,首先介绍无回调的情况,然后再进行修改。假设你有一个通用的函数,用来完成某种复杂的逻辑并返回一大段数据。假设这个通用函数叫`findNodes()`,用来对DOM树进行遍历,并返回页面节点: var findNodes = function () { - var i = 100000, // big, heavy loop - nodes = [], // stores the result - found; // the next node found + var i = 100000, // 大量耗时的循环 + nodes = [], // 存储结果 + found; // 标示下找到的节点 while (i) { i -= 1; - // complex logic here... + // 这里是复杂的逻辑…… nodes.push(found); } return nodes; }; -保持这个函数的功能的通用性并一贯返回DOM节点组成的数组,并不会发生对节点的实际操作,这是一个不错的注意。可以将操作节点的逻辑放入另外一个函数中,比如放入一个hide()函数中,这个函数用来隐藏页面中的节点元素: +保持这个函数的功能的通用性,让它只返回DOM节点组成的数组,而不对节点进行操作是一个很好的思想。可以将操作节点的逻辑放入另外一个函数中,比如`hide()`函数,这个函数用来隐藏页面中的节点元素: var hide = function (nodes) { var i = 0, max = nodes.length; @@ -211,27 +207,27 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 } }; - // executing the functions + // 执行函数 hide(findNodes()); -这个实现的效率并不高,因为它将findNodes()所返回的节点数组重新遍历了一遍。最好在findNodes()中选择元素的时候就直接应用hide()操作,这样就能避免第二次的遍历,从而提高效率。但如果将hide()的逻辑写死在findNodes()的函数体内,findNodes()就变得不再通用了(译注:如果我将hide()的逻辑替换成其他逻辑怎么办呢?),因为修改逻辑和遍历逻辑耦合在一起了。如果使用回调模式,则可以将隐藏节点的逻辑写入回调函数,将其传入findNodes()中适时执行: +这个实现的效率并不高,因为它将`findNodes()`所返回的节点数组重新遍历了一遍。更高效的办法是在`findNodes()`中选择元素的时候就直接应用`hide()`操作,这样就能避免第二次的遍历,从而提高效率。但如果将`hide()`的逻辑写死在`findNodes()`的函数体内,`findNodes()`就变得不再通用了,因为修改逻辑和遍历逻辑耦合在一起了。这时候如果使用回调模式,就可以将隐藏节点的逻辑写入回调函数,将其传入`findNodes()`中适时执行: - // refactored findNodes() to accept a callback + // 重构后的findNodes()接受一个回调函数 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); } @@ -241,25 +237,26 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 return nodes; }; -这里的实现比较直接,findNodes()多作了一个额外工作,就是检查回调函数是否存在,如果存在的话就执行它。回调函数是可选的,因此修改后的findNodes()也是和之前一样使用,是可以兼容旧代码和旧API的。 +这里的实现比较直接,`findNodes()`多作了一个额外工作,就是检查回调函数是否存在,如果存在的话就执行它。回调函数是可选的,因此修改后的`findNodes()`仍然可以和之前一样使用,是可以兼容旧代码和旧API的。 -这时hide()的实现就非常简单了,因为它不用对元素列表做任何遍历了: +这时`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()来完成显示元素的操作: +回调函数可以是事先定义好的,像上面的代码一样,也可以是一个在调用函数时创建的匿名函数,比如这段代码,我们利用同样的通用函数`findNodes()`来完成显示元素的操作: - // passing an anonymous callback + // 传入匿名回调函数 findNodes(function (node) { node.style.display = "block"; }); +---------校对分割线--------- ### 回调和作用域 From 2ce5b27e8a4c74d8d8bf4bdd0386663e646e75df Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 15 Apr 2013 19:44:37 +0800 Subject: [PATCH 071/145] =?UTF-8?q?=E5=9B=9E=E8=B0=83=E5=92=8C=E4=BD=9C?= =?UTF-8?q?=E7=94=A8=E5=9F=9F=20=E5=B0=8F=E8=8A=82=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 908a227..ff07ed4 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -256,17 +256,15 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 node.style.display = "block"; }); ----------校对分割线--------- - ### 回调和作用域 在上一个例子中,执行回调函数的写法是: callback(parameters); -尽管这种写法可以适用大多数的情况,而且足够简单,但还有一些场景,回调函数不是匿名函数或者全局函数,而是对象的方法。如果回调函数中使用this指向它所属的对象,则回调逻辑往往并不像我们希望的那样执行。 +尽管这种写法很简单,而且可以适用于大多数的情况,但还有一些场景,回调函数不是匿名函数或者全局函数,而是对象的方法,如果这种情况下回调函数中使用了`this`指向它所属的对象,则回调逻辑就可能不是我们期望的那样。 -假设回调函数是paint(),它是myapp的一个方法: +假设回调函数是`paint()`,它是`myapp`的一个方法: var myapp = {}; myapp.color = "green"; @@ -284,13 +282,13 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 // ... }; -当你调用findNodes(myapp.paint),运行结果和我们期望的不一致,因为this.color未定义。因为findNodes()是全局函数,this指向的是全局对象。如果findNodes()是dom对象的方法(类似dom.findNodes()),那么回调函数内的this则指向dom,而不是myapp。 +当你调用`findNodes(myapp.paint)`时,运行结果和我们期望的不一致,因为`this.color`未定义。这时候`this`指向的是全局对象,因为`findNodes()`是全局函数。如果`findNodes()`是dom对象的方法(类似`dom.findNodes()`),那么回调函数内的`this`指向该dom,而不是`myapp`。 解决办法是,除了传入回调函数,还需将回调函数所属的对象当作参数传进去: findNodes(myapp.paint, myapp); -同样需要修改findNodes()的逻辑,增加对传入的对象的绑定: +同样需要修改`findNodes()`的逻辑,增加对传入的对象的绑定: var findNodes = function (callback, callback_obj) { //... @@ -300,9 +298,9 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 // ... }; -在后续的章节会对call()和apply()有更详细的讲述。 +在后续的章节会对`call()`和`apply()`有更详细的讲述。 -其实还有一种替代写法,就是将函数当作字符串传入findNodes(),这样就不必再写一次对象了,换句话说: +其实还有一种替代写法,就是将函数名称以字符串传入`findNodes()`,这样就不必再写一次对象了,也就是说: findNodes(myapp.paint, myapp); @@ -310,7 +308,7 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 findNodes("paint", myapp); -在findNodes()中的逻辑则需要修改为: +在`findNodes()`中的逻辑则需要修改为: var findNodes = function (callback, callback_obj) { @@ -325,7 +323,7 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 // ... }; - +---------校对分割线--------- ### 异步事件监听 JavaScript中的回调模式已经是我们的家常便饭了,比如,如果你给网页中的元素绑定事件,则需要提供回调函数的引用,以便事件发生时能调用到它。这里有一个简单的例子,我们将console.log()作为回调函数绑定了document的点击事件: From bdd9c605fa458b868ce3c7c4f5200c58401a395e Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 15 Apr 2013 20:28:22 +0800 Subject: [PATCH 072/145] =?UTF-8?q?=E5=BC=82=E6=AD=A5=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E7=9B=91=E5=90=AC=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index ff07ed4..6d9dd9e 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -323,17 +323,17 @@ JavaScript的函数具有两个主要特性,正是这两个特性让它们与 // ... }; ----------校对分割线--------- ### 异步事件监听 -JavaScript中的回调模式已经是我们的家常便饭了,比如,如果你给网页中的元素绑定事件,则需要提供回调函数的引用,以便事件发生时能调用到它。这里有一个简单的例子,我们将console.log()作为回调函数绑定了document的点击事件: +JavaScript中的回调模式已经是我们的家常便饭了,比如,如果你给网页中的元素绑定事件,则需要提供回调函数的引用,以便事件发生时能调用到它。这里有一个简单的例子,我们将`console.log()`作为回调函数绑定到了`document`的点击事件上: document.addEventListener("click", console.log, false); -客户端浏览器中的大多数编程都是事件驱动的,当网页下载完成,则触发load事件,当用户和页面产生交互时也会触发多种事件,比如click、keypress、mouseover、mousemove等等。正是由于回调模式的灵活性,JavaScript天生适于事件驱动编程。回调模式能够让程序“异步”执行,换句话说,就是让程序不按顺序执行。 +客户端浏览器中的大多数编程都是事件驱动的,当网页下载完成,则触发`load`事件,当用户和页面产生交互时也会触发多种事件,比如`click`、`keypress`、`mouseover`、`mousemove`等等。JavaScript天生适合事件驱动编程,因为回调模式能够让程序“异步”执行,换句话说,就是让程序不按顺序执行。 -“不要打电话给我,我会打给你”,这是好莱坞很有名的一句话,很多电影都有这句台词。电影中的主角不可能同时应答很多个电话呼叫。在JavaScript的异步事件模型中也是同样的道理。电影中是留下电话号码,JavaScript中是提供一个回调函数,当时机成熟时就触发回调。有时甚至提供了很多回调,有些回调压根是没用的,但由于这个事件可能永远不会发生,因此这些回调的逻辑也不会执行。比如,假设你从此不再用“鼠标点击”,那么你之前绑定的鼠标点击的回调函数则永远也不会执行。 +“不要打电话给我,我会打给你”,这是好莱坞很有名的一句台词,可能很多人会对同一个角色说这句话,而电影中的主角不可能同时应答这些人的电话呼叫。在JavaScript的异步事件模型中也是同样的道理,不同的是,电影中是留下电话号码,JavaScript中是提供一个在适当的时机被调用的回调函数。有时甚至可以提供比实际需要更多的回调函数,因为可能某个特定的事件永远不会发生。比如,假设用户一直不点击“购买”,那么你之前写的用来验证信用卡号格式的函数就永远不会被调用执行。(译注:这段话有点不好翻译,前面的比喻看不懂。后面有两个方面的意思,一方面指回调函数并不一定会被执行,如果事件不发生,那么回调函数就永远不会被执行;另一方面指可以通过多个事件来绑定同一个回调函数,因为你无法确定用户会触发哪一个事件,比如到底是键盘操作还是鼠标操作。) +---------校对分割线--------- ### 超时 From fc5df2777d50385e899bc1304b5ea20597e3cd4a Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 15 Apr 2013 20:32:16 +0800 Subject: [PATCH 073/145] =?UTF-8?q?=E5=BB=B6=E6=97=B6=20=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 6d9dd9e..fe8c341 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -333,19 +333,18 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 “不要打电话给我,我会打给你”,这是好莱坞很有名的一句台词,可能很多人会对同一个角色说这句话,而电影中的主角不可能同时应答这些人的电话呼叫。在JavaScript的异步事件模型中也是同样的道理,不同的是,电影中是留下电话号码,JavaScript中是提供一个在适当的时机被调用的回调函数。有时甚至可以提供比实际需要更多的回调函数,因为可能某个特定的事件永远不会发生。比如,假设用户一直不点击“购买”,那么你之前写的用来验证信用卡号格式的函数就永远不会被调用执行。(译注:这段话有点不好翻译,前面的比喻看不懂。后面有两个方面的意思,一方面指回调函数并不一定会被执行,如果事件不发生,那么回调函数就永远不会被执行;另一方面指可以通过多个事件来绑定同一个回调函数,因为你无法确定用户会触发哪一个事件,比如到底是键盘操作还是鼠标操作。) ----------校对分割线--------- - -### 超时 +### 延时 -另外一个最常用的回调模式是在调用超时函数时,超时函数是浏览器window对象的方法,共有两个:setTimeout()和setInterval()。这两个方法的参数都是回调函数。 +另外一个最常用的回调模式是在调用延时函数的时候。延时函数是浏览器`window`对象的方法,共有两个:`setTimeout()`和`setInterval()`。这两个方法的参数都是回调函数。 var thePlotThickens = function () { console.log('500ms later...'); }; setTimeout(thePlotThickens, 500); -再次需要注意,函数thePlotThickens是作为变量传入setTimeout的,它不带括号,如果带括号的话则立即执行了,这里只是用到这个函数的引用,以便在setTimeout的逻辑中调用到它。也可以传入字符串“thePlotThickens()”,但这是一种反模式,和eval()一样不推荐使用。 +再次提醒,函数名`thePlotThickens`是作为变量传入`setTimeout`的,它不带括号,如果带括号的话就被立即执行了,而这里只是用到这个函数的引用,以便在`setTimeout()`的逻辑中调用它。也可以传入字符串`"thePlotThickens()"`,但这是一种反模式,和`eval()`一样不推荐使用。 +---------校对分割线--------- ### 库中的回调 From 58f54adcb0af8f5bbb38d65160b4263a837ef3ae Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 15 Apr 2013 20:34:32 +0800 Subject: [PATCH 074/145] =?UTF-8?q?=E7=B1=BB=E5=BA=93=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E5=9B=9E=E8=B0=83=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index fe8c341..4626e2d 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -344,13 +344,12 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 再次提醒,函数名`thePlotThickens`是作为变量传入`setTimeout`的,它不带括号,如果带括号的话就被立即执行了,而这里只是用到这个函数的引用,以便在`setTimeout()`的逻辑中调用它。也可以传入字符串`"thePlotThickens()"`,但这是一种反模式,和`eval()`一样不推荐使用。 ----------校对分割线--------- - -### 库中的回调 +### 类库中的回调 -回调模式非常简单,但又很强大。可以随手拈来灵活运用,因此这种模式在库的设计中也非常得宠。库的代码要尽可能的保持通用和重用,而回调模式则可帮助库的作者完成这个目标。你不必预料和实现你所想到的所有情形,因为这会让库变的膨胀而臃肿,而且大多数用户并不需要这些多余的特性支持。相反,你将精力放在核心功能的实现上,提供回调的入口作为“钩子”,可以让库的方法变得可扩展、可定制。 +回调模式非常简单,但又很强大,可以信手拈来灵活运用,因此这种模式在类库的设计中也非常得宠。类库的代码要尽可能保持通用和可复用,而回调模式则可帮助库的作者达成这个目标。你不必预料并实现你所想到的所有情形,这会让类库变得臃肿,而且大多数用户并不需要这些多余的特性支持。相反,你将精力放在核心功能的实现上,提供回调的入口作为“钩子”,可以让类库的方法变得可扩展、可定制。 +---------校对分割线--------- ## 返回函数 From 10b9c766eaaebbf3e99905836ea5d79889d3ecfc Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 15 Apr 2013 20:42:48 +0800 Subject: [PATCH 075/145] =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=87=BD=E6=95=B0=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 4626e2d..49644f9 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -349,13 +349,11 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 回调模式非常简单,但又很强大,可以信手拈来灵活运用,因此这种模式在类库的设计中也非常得宠。类库的代码要尽可能保持通用和可复用,而回调模式则可帮助库的作者达成这个目标。你不必预料并实现你所想到的所有情形,这会让类库变得臃肿,而且大多数用户并不需要这些多余的特性支持。相反,你将精力放在核心功能的实现上,提供回调的入口作为“钩子”,可以让类库的方法变得可扩展、可定制。 ----------校对分割线--------- - ## 返回函数 -函数是对象,因此当然可以作为返回值。也就是说,函数不一定非要返回一坨数据,函数可以返回另外一个定制好的函数,或者可以根据输入的不同按需创造另外一个函数。 +函数是对象,因此可以作为返回值。也就是说,函数不一定非要返回一坨数据,函数也可以返回另外一个函数,或者可以根据输入的不同按需创造另外一个函数。 -这里有一个简单的例子:一个函数完成了某种功能,可能是一次性初始化,然后都基于这个返回值进行操作,这个返回值恰巧是另一个函数: +这里有一个简单的例子:一个函数完成了某种功能,可能是一次性初始化,然后做了一些对返回值的操作,而这个返回值恰巧是另一个函数: var setup = function () { alert(1); @@ -364,11 +362,11 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 }; }; - // using the setup function + // 使用setup()函数 var my = setup(); // alerts 1 my(); // alerts 2 -因为setup()把返回的函数作了包装,它创建了一个闭包,我们可以用这个闭包来存储一些私有数据,这些私有数据可以通过返回的函数进行操作,但在函数外部不能直接读取到这些私有数据。比如这个例子中提供了一个计数器,每次调用这个函数计数器都会加一: +因为`setup()`包裹了返回的函数,因此它创建了一个闭包,我们可以用这个闭包来存储一些私有数据,这些私有数据可以通过返回的函数进行操作,但在函数外部不能直接读取到这些私有数据。比如这个例子中提供了一个计数器,每次调用这个函数时,计数器都会加一: var setup = function () { var count = 0; @@ -377,13 +375,14 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 }; }; - // usage + // 使用 var next = setup(); - next(); // returns 1 + next(); // 返回 1 next(); // 2 next(); // 3 +---------校对分割线--------- ## 自定义函数 From f052812535d27d2944fe2d0b6b49fc31db594c8c Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 15 Apr 2013 21:09:55 +0800 Subject: [PATCH 076/145] =?UTF-8?q?=E9=87=8D=E5=AE=9A=E4=B9=89=E5=87=BD?= =?UTF-8?q?=E6=95=B0=20=E6=A0=A1=E5=AF=B9=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, 18 insertions(+), 19 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 49644f9..e6ab9bf 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -382,11 +382,9 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 next(); // 3 ----------校对分割线--------- - -## 自定义函数 +## 重定义函数 -我们动态定义函数,并将函数赋值给变量。如果将你定义的函数赋值给已经存在的函数变量的话,则新函数会覆盖旧函数。这样做的结果是,旧函数的引用就丢弃掉了,变量中所存储的引用值替换成了新的。这样看起来这个变量指代的函数逻辑就发生了变化,或者说函数进行了“重新定义”或“重写”。说起来有些拗口,实际上并不复杂,来看一个例子: +函数可以被动态定义,也可以被赋值给变量。如果将你定义的函数赋值给已经存在的函数变量的话,则新函数会覆盖旧函数。这样做的结果是,旧函数的引用被丢弃掉,变量中所存储的引用值替换成了新的函数。这样看起来这个变量指代的函数逻辑就发生了变化,或者说函数进行了“重新定义”或“重写”。听起来很麻烦,但实际上并不复杂,来看一个例子: var scareMe = function () { alert("Boo!"); @@ -394,55 +392,56 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 alert("Double boo!"); }; }; - // using the self-defining function + // 使用重定义函数 scareMe(); // Boo! scareMe(); // Double boo! -当函数中包含一些初始化操作,并希望这些初始化只执行一次,那么这种模式是非常适合这个场景的。因为能避免的重复执行则尽量避免,函数的一部分可能再也不会执行到。在这个场景中,函数执行一次后就被重写为另外一个函数了。 +当函数中包含一些初始化操作,并希望这些初始化操作只执行一次,那么这种模式是非常合适的,因为我们要避免重复执行不需要的代码。在这个场景中,函数执行一次后就被重写为另外一个函数了。 -使用这种模式可以帮助提高应用的执行效率,因为重新定义的函数执行更少的代码。 +使用这种模式可以帮助提高应用的执行效率,因为重新定义的函数执行的代码量更少。 ->这种模式的另外一个名字是“函数的懒惰定义”,因为直到函数执行一次后才重新定义,可以说它是“某个时间点之后才存在”,简称“懒惰定义”。 +> 这种模式的另外一个名字是“函数的懒惰定义”,因为直到函数执行一次后才重新定义,可以说它是“某个时间点之后才存在”,简称“懒惰定义”。 -这种模式有一种明显的缺陷,就是之前给原函数添加的功能在重定义之后都丢失了。如果将这个函数定义为不同的名字,函数赋值给了很多不同的变量,或作为对象的方法使用,那么新定义的函数有可能不会执行,原始的函数会照旧执行(译注:由于函数的赋值是引用的赋值,函数赋值给多个变量只是将引用赋值给了多个变量,当某一个变量定义了新的函数,也只是变量的引用值发生变化,原函数本身依旧存在,当程序中存在某个变量的引用还是旧函数的话,旧函数还是会依旧执行)。 +这种模式有一个明显的缺陷,就是之前给原函数添加的功能在重定义之后都丢失了。同时,如果这个函数被重定义为不同的名字,被赋值给不同的变量,或者是作为对象的方法使用,那么重定义的部分并不会生效,原来的函数依然会被执行。 -让我们来看一个例子,scareMe()函数在这里作为一等对象来使用: +让我们来看一个例子,`scareMe()`函数在这里作为一等对象来使用: 1. 给他增加了一个属性 -2. 函数对象赋值给一个新变量 -3. 函数依旧可以作为方法来调用 +2. 函数对象被赋值给一个新变量 +3. 函数还被作为方法来调用 看一下这段代码: - // 1. adding a new property + // 1. 添加一个新属性 scareMe.property = "properly"; - // 2. assigning to a different name + // 2. 被赋值给一个不同名的变量 var prank = scareMe; - // 3. using as a method + // 3. 作为方法使用 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”。 +从结果来看,当重定义函数被赋值给一个新的变量的时候,这段使用重定义函数的代码的执行结果与我们期望的结果可能并不一样。每当`prank()`被调用的时候,它都弹出“Boo!”。同时它也重写了`scareMe()`函数,但是`prank()`自己仍然能够使用之前的定义,包括属性`property`。在这个函数被作为`spooky`对象的`boo()`方法调用的时候,结果也一样。所有的这些调用,在第一次的时候就已经修改了全局的`scareMe()`的指向,所以当它最终被调用的时候,它的函数体已经被修改为弹出“Double boo”,也就不能获取到新添加的属性`scareMe.property`。 +---------校对分割线--------- ## 立即执行的函数 From 3ec80da78df23f6116ef25aae58611de627659b2 Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 15 Apr 2013 21:35:47 +0800 Subject: [PATCH 077/145] =?UTF-8?q?=E5=8D=B3=E6=97=B6=E5=87=BD=E6=95=B0=20?= =?UTF-8?q?=E7=AC=AC=E4=B8=80=E9=83=A8=E5=88=86=E6=A0=A1=E5=AF=B9=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 | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index e6ab9bf..f1ff96e 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -441,17 +441,15 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 从结果来看,当重定义函数被赋值给一个新的变量的时候,这段使用重定义函数的代码的执行结果与我们期望的结果可能并不一样。每当`prank()`被调用的时候,它都弹出“Boo!”。同时它也重写了`scareMe()`函数,但是`prank()`自己仍然能够使用之前的定义,包括属性`property`。在这个函数被作为`spooky`对象的`boo()`方法调用的时候,结果也一样。所有的这些调用,在第一次的时候就已经修改了全局的`scareMe()`的指向,所以当它最终被调用的时候,它的函数体已经被修改为弹出“Double boo”,也就不能获取到新添加的属性`scareMe.property`。 ----------校对分割线--------- - -## 立即执行的函数 +## 即时函数 -立即执行的函数是一种语法模式,它会使函数在定义后立即执行。看这个例子: +即时函数是一种语法模式,它会使函数在定义后立即执行。看这个例子: (function () { alert('watch out!'); }()); -这种模式本质上只是一个在创建后就被执行的函数表达式(具名或者匿名)。“立即执行的函数”这种说法并没有在ECMAScript标准中被定义,但它作为一个名词,有助于我们的描述和讨论。 +这种模式本质上只是一个在创建后就被执行的函数表达式(具名或者匿名)。“即时函数”这种说法并没有在ECMAScript标准中被定义,但它作为一个名词,有助于我们的描述和讨论。 这种模式由以下几个部分组成: @@ -465,7 +463,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 alert('watch out!'); })(); -这种模式很有用,它为我们提供一个作用域的沙箱,可以在执行一些初始化代码的时候使用。设想这样的场景:当页面加载的时候,你需要运行一些代码,比如绑定事件、创建对象等等。所有的这些代码都只需要运行一次,所以没有必要创建一个带有名字的函数。但是这些代码需要一些临时变量,而这些变量在初始化完之后又不会再次用到。显然,把这些变量作为全局变量声明是不合适的。正因为如此,我们才需要立即执行的函数。它可以把你所有的代码包裹到一个作用域里面,而不会暴露任何变量到全局作用域中: +这种模式很有用,它为我们提供一个作用域的沙箱,可以在执行一些初始化代码的时候使用。设想这样的场景:当页面加载的时候,你需要运行一些代码,比如绑定事件、创建对象等等。所有的这些代码都只需要运行一次,所以没有必要创建一个带有名字的函数。但是这些代码需要一些临时变量,而这些变量在初始化完之后又不会再次被用到。显然,把这些变量作为全局变量声明是不合适的。正因为如此,我们才需要即时函数。它可以把你所有的代码包裹到一个作用域里面,而不会暴露任何变量到全局作用域中: (function () { @@ -477,13 +475,14 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 }()); // "Today is Fri, 13" -如果这段代码没有被包裹到立即执行函数中,那么变量days、today、msg都会是全局变量,而这些变量仅仅是由因为初始化而遗留下来的垃圾,没有任何用处。 +如果这段代码没有被包裹到立即执行函数中,那么变量`days`、`today`、`msg`都会是全局变量,而这些变量仅仅是因为初始化而遗留下来的垃圾,没有任何用处。 +---------校对分割线--------- -### 立即执行的函数的参数 +### 即时函数的参数 -立即执行的函数也可以接受参数,看这个例子: +即时函数也可以接受参数,看这个例子: // prints: // I met Joe Black on Fri Aug 13 2010 23:26:59 GMT-0800 (PST) @@ -494,14 +493,14 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 }("Joe Black", new Date())); -通常的做法,会把全局对象当作一个参数传给立即执行的函数,以保证在函数内部也可以访问到全局对象,而不是使用window对象,这样可以使得代码在非浏览器环境中使用时更具可移植性。 +通常的做法,会把全局对象当作一个参数传给即时函数,以保证在函数内部也可以访问到全局对象,而不是使用window对象,这样可以使得代码在非浏览器环境中使用时更具可移植性。 -值得注意的是,一般情况下尽量不要给立即执行的函数传入太多的参数,否则会有一件麻烦的事情,就是你在阅读代码的时候需要频繁地上下滚动代码。 +值得注意的是,一般情况下尽量不要给即时函数传入太多的参数,否则会有一件麻烦的事情,就是你在阅读代码的时候需要频繁地上下滚动代码。 -### 立即执行的函数的返回值 +### 即时函数的返回值 -和其它的函数一样,立即执行的函数也可以返回值,并且这些返回值也可以被赋值给变量: +和其它的函数一样,即时函数也可以返回值,并且这些返回值也可以被赋值给变量: var result = (function () { return 2 + 2; @@ -521,9 +520,9 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 return 2 + 2; })(); -前面的例子中,立即执行的函数返回的是一个基本类型的数值。但事实上,除了基本类型以外,一个立即执行的函数可以返回任意类型的值,甚至返回一个函数都可以。你可以利用立即执行的函数的作用域来存储一些私有的数据,这些数据只能在返回的内层函数中被访问。 +前面的例子中,即时函数返回的是一个基本类型的数值。但事实上,除了基本类型以外,一个即时函数可以返回任意类型的值,甚至返回一个函数都可以。你可以利用即时函数的作用域来存储一些私有的数据,这些数据只能在返回的内层函数中被访问。 -在下面的例子中,立即执行的函数的返回值是一个函数,这个函数会简单地返回res的值,并且它被赋给了变量getResult。而res是一个预先计算好的变量,它被存储在立即执行函数的闭包中: +在下面的例子中,即时函数的返回值是一个函数,这个函数会简单地返回res的值,并且它被赋给了变量getResult。而res是一个预先计算好的变量,它被存储在立即执行函数的闭包中: var getResult = (function () { var res = 2 + 2; @@ -532,7 +531,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 }; }()); -在定义一个对象的属性的时候也可以使用立即执行的函数。设想一下这样的场景:你需要定义一个对象的属性,这个属性在对象的生命周期中都不会改变,但是在定义之前,你需要做一点额外的工作来得到正确的值。这种情况下你就可以使用立即执行的函数来包裹那些额外的工作,然后将它的返回值作为对象属性的值。下面是一个例子: +在定义一个对象的属性的时候也可以使用即时函数。设想一下这样的场景:你需要定义一个对象的属性,这个属性在对象的生命周期中都不会改变,但是在定义之前,你需要做一点额外的工作来得到正确的值。这种情况下你就可以使用即时函数来包裹那些额外的工作,然后将它的返回值作为对象属性的值。下面是一个例子: var o = { message: (function () { @@ -555,13 +554,13 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 ### 好处和用法 -立即执行的函数应用很广泛。它可以帮助我们做一些不想留下全局变量的工作。所有定义的变量都只是立即执行的函数的本地变量,你完全不用担心临时变量会污染全局对象。 +即时函数应用很广泛。它可以帮助我们做一些不想留下全局变量的工作。所有定义的变量都只是即时函数的本地变量,你完全不用担心临时变量会污染全局对象。 -> 立即执行的函数还有一些名字,比如“自调用函数”或者“自执行函数”,因为这些函数会在被定义后立即执行自己。 +> 即时函数还有一些名字,比如“自调用函数”或者“自执行函数”,因为这些函数会在被定义后立即执行自己。 这种模式也经常被用到书签代码中,因为书签代码会在任何一个页面运行,所以需要非常苛刻地保持全局命名空间干净。 -这种模式也可以让你包裹一些独立的特性到一个封闭的模块中。设想你的页面是静态的,在没有JavaScript的时候工作正常,然后,本着渐进增强的精神,你给页面加入了一点增加代码。这时候,你就可以把你的代码(也可以叫“模块”或者“特性”)放到一个立即执行的函数中并且保证页面在有没有它的时候都可以正常工作。然后你就可以加入更多的增强特性,或者对它们进行移除、进行独立测试或者允许用户禁用等等。 +这种模式也可以让你包裹一些独立的特性到一个封闭的模块中。设想你的页面是静态的,在没有JavaScript的时候工作正常,然后,本着渐进增强的精神,你给页面加入了一点增加代码。这时候,你就可以把你的代码(也可以叫“模块”或者“特性”)放到一个即时函数中并且保证页面在有没有它的时候都可以正常工作。然后你就可以加入更多的增强特性,或者对它们进行移除、进行独立测试或者允许用户禁用等等。 你可以使用下面的模板定义一段函数代码,我们叫它module1: @@ -578,7 +577,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 ## 立即初始化的对象 -还有另外一种可以避免污染全局作用域的方法,和前面描述的立即执行的函数相似,叫做“立即初始化的对象”模式。这种模式使用一个带有init()方法的对象来实现,这个方法在对象被创建后立即执行。初始化的工作由init()函数来完成。 +还有另外一种可以避免污染全局作用域的方法,和前面描述的即时函数相似,叫做“立即初始化的对象”模式。这种模式使用一个带有init()方法的对象来实现,这个方法在对象被创建后立即执行。初始化的工作由init()函数来完成。 下面是一个立即初始化的对象模式的例子: @@ -607,7 +606,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 ({...}).init(); ({...}.init()); -这种模式的好处和自动执行的函数模式是一样的:在做一些一次性的初始化工作的时候保护全局作用域不被污染。从语法上看,这种模式似乎比只包含一段代码在一个匿名函数中要复杂一些,但是如果你的初始化工作比较复杂(这种情况很常见),它会给整个初始化工作一个比较清晰的结构。比如,一些私有的辅助性函数可以被很轻易地看出来,因为它们是这个临时对象的属性,但是如果是在立即执行的函数模式中,它们很可能只是一些散落的函数。 +这种模式的好处和自动执行的函数模式是一样的:在做一些一次性的初始化工作的时候保护全局作用域不被污染。从语法上看,这种模式似乎比只包含一段代码在一个匿名函数中要复杂一些,但是如果你的初始化工作比较复杂(这种情况很常见),它会给整个初始化工作一个比较清晰的结构。比如,一些私有的辅助性函数可以被很轻易地看出来,因为它们是这个临时对象的属性,但是如果是在即时函数模式中,它们很可能只是一些散落的函数。 这种模式的一个弊端是,JavaScript压缩工具可能不能像压缩一段包裹在函数中的代码一样有效地压缩这种模式的代码。这些私有的属性和方法不被会重命名为一些更短的名字,因为从压缩工具的角度来看,保证压缩的可靠性更重要。在写作本书的时候,Google出品的Closure Compiler的“advanced”模式是唯一会重命名立即初始化的对象的属性的压缩工具。一个压缩后的样例是这样: @@ -1015,7 +1014,7 @@ step 1是一个所谓的部分应用的例子:我们只应用了第一个参 新函数在已有函数的基础上再加上一部分参数构成 2. 初始化模式,这些模式帮助我们用一种干净的、结构化的方法来做一些初始化工作(在web页面和应用中非常常见),通过一些临时变量来保证不污染全局命名空间。这些模式包括: - - 立即执行的函数 + - 即时函数 当它们被定义后立即执行 - 立即初始化的对象 From 49f7044f46abe47e80b6e066b4ad053b60b9a380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=92=B1=E5=BC=A0=E7=9B=9B?= Date: Thu, 18 Apr 2013 17:05:03 +0800 Subject: [PATCH 078/145] Update chapter1.markdown --- chapter1.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter1.markdown b/chapter1.markdown index 71d8d20..37bf43b 100644 --- a/chapter1.markdown +++ b/chapter1.markdown @@ -104,7 +104,7 @@ JavaScript语言的核心部分(不包含DOM、BOM和其它宿主对象)是 本书不会讨论ES5新增特性相关的模式,因为在本书截稿时并没有任何浏览器实现了ES5(译注:截止译稿校对时,Chrome/Firefox/IE9+已(部分)实现ES5,具体兼容情况可参考),但本书的示例代码有以下特点,以鼓励开发者向新标准转变: -- 确保所提供的示例代码在严格模式下不包错 +- 确保所提供的示例代码在严格模式下不报错 - 避免使用并明确指出弃用的构造函数相关的属性和方法,比如arguments.callee - 针对ES5中的内置模式比如Object.create(),在ES3中做同样的实现 From 588187d4c0e21c7bf0f499102e67b86924ce68ec Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 19 Apr 2013 12:36:24 +0800 Subject: [PATCH 079/145] =?UTF-8?q?=E5=8D=B3=E6=97=B6=E5=87=BD=E6=95=B0=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index f1ff96e..0f4807d 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -478,13 +478,11 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 如果这段代码没有被包裹到立即执行函数中,那么变量`days`、`today`、`msg`都会是全局变量,而这些变量仅仅是因为初始化而遗留下来的垃圾,没有任何用处。 ----------校对分割线--------- - ### 即时函数的参数 即时函数也可以接受参数,看这个例子: - // prints: + // 打印出: // I met Joe Black on Fri Aug 13 2010 23:26:59 GMT-0800 (PST) (function (who, when) { @@ -493,11 +491,10 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 }("Joe Black", new Date())); -通常的做法,会把全局对象当作一个参数传给即时函数,以保证在函数内部也可以访问到全局对象,而不是使用window对象,这样可以使得代码在非浏览器环境中使用时更具可移植性。 +通常我们会把全局对象当作一个参数传给即时函数,以保证在函数内部也可以访问到全局对象,而不是使用`window`对象,这样可以使得代码在非浏览器环境中使用时更具可移植性。 值得注意的是,一般情况下尽量不要给即时函数传入太多的参数,否则会有一件麻烦的事情,就是你在阅读代码的时候需要频繁地上下滚动代码。 - ### 即时函数的返回值 和其它的函数一样,即时函数也可以返回值,并且这些返回值也可以被赋值给变量: @@ -512,7 +509,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 return 2 + 2; }(); -这种写法更简洁,但是同时也容易造成误解。如果有人在阅读代码的时候忽略了最后的一对括号,那么他会以为result指向了一个函数。而事实上result是指向这个函数运行后的返回值,在这个例子中是4。 +这种写法更简洁,但是同时也容易造成误解。如果有人在阅读代码的时候忽略了最后的一对括号,那么他会以为`result`指向了一个函数。而事实上`result`是指向这个函数运行后的返回值,在这个例子中是4。 还有一种写法也可以得到同样的结果: @@ -520,9 +517,9 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 return 2 + 2; })(); -前面的例子中,即时函数返回的是一个基本类型的数值。但事实上,除了基本类型以外,一个即时函数可以返回任意类型的值,甚至返回一个函数都可以。你可以利用即时函数的作用域来存储一些私有的数据,这些数据只能在返回的内层函数中被访问。 +前面的例子中,即时函数返回的是一个基本类型的数值。但事实上,一个即时函数可以返回任意类型的值,甚至返回一个函数都可以。你可以利用即时函数的作用域来存储一些私有的数据,这些数据只能在返回的内层函数中被访问。 -在下面的例子中,即时函数的返回值是一个函数,这个函数会简单地返回res的值,并且它被赋给了变量getResult。而res是一个预先计算好的变量,它被存储在立即执行函数的闭包中: +在下面的例子中,即时函数的返回值是一个函数,这个函数会简单地返回`res`的值,并且这个值被赋给了变量`getResult`。而`res`是一个预先计算好的变量,它被存储在即时函数的闭包中: var getResult = (function () { var res = 2 + 2; @@ -531,7 +528,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 }; }()); -在定义一个对象的属性的时候也可以使用即时函数。设想一下这样的场景:你需要定义一个对象的属性,这个属性在对象的生命周期中都不会改变,但是在定义之前,你需要做一点额外的工作来得到正确的值。这种情况下你就可以使用即时函数来包裹那些额外的工作,然后将它的返回值作为对象属性的值。下面是一个例子: +在定义一个对象属性的时候也可以使用即时函数。设想一下这样的场景:你需要定义一个对象的属性,这个属性在对象的生命周期中都不会改变,但是在定义之前,你需要做一些计算来得到它的值。这种情况下你就可以使用即时函数来包裹那些额外的计算工作,然后将它的返回值作为对象属性的值。下面是一个例子: var o = { message: (function () { @@ -544,36 +541,36 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 } }; - // usage + // 使用对象 o.getMsg(); // "call me" o.message; // "call me" -在这个例子中,o.message是一个字符串,而不是一个函数,但是它需要一个函数在脚本载入后来得到这个属性值。 +在这个例子中,`o.message`是一个字符串,而不是一个函数,但是它需要一个函数在脚本载入后通过计算得到这个属性值。 - ### 好处和用法 即时函数应用很广泛。它可以帮助我们做一些不想留下全局变量的工作。所有定义的变量都只是即时函数的本地变量,你完全不用担心临时变量会污染全局对象。 > 即时函数还有一些名字,比如“自调用函数”或者“自执行函数”,因为这些函数会在被定义后立即执行自己。 -这种模式也经常被用到书签代码中,因为书签代码会在任何一个页面运行,所以需要非常苛刻地保持全局命名空间干净。 +这种模式也经常被用到书签代码中,因为书签代码有可能会运行在任何一个页面中,所以需要非常苛刻地保持全局命名空间干净。 -这种模式也可以让你包裹一些独立的特性到一个封闭的模块中。设想你的页面是静态的,在没有JavaScript的时候工作正常,然后,本着渐进增强的精神,你给页面加入了一点增加代码。这时候,你就可以把你的代码(也可以叫“模块”或者“特性”)放到一个即时函数中并且保证页面在有没有它的时候都可以正常工作。然后你就可以加入更多的增强特性,或者对它们进行移除、进行独立测试或者允许用户禁用等等。 +这种模式也可以让你包裹一些独立的特性到一个封闭的模块中。设想你的页面是静态的,在没有JavaScript的时候工作正常,然后,本着渐进增强的精神,你给页面加入了一点增强代码。这时候,你就可以把你的代码(也可以叫“模块”或者“特性”)放到一个即时函数中并且保证页面在有没有它的时候都可以正常工作。然后你就可以加入更多的增强特性,或者对它们进行移除、进行独立测试或者允许用户禁用等等。 你可以使用下面的模板定义一段函数代码,我们叫它module1: - // module1 defined in module1.js + // 在module1.js中定义module1 (function () { - // all the module 1 code ... + // 所有module 1的代码…… }()); -套用这个模板,你就可以编写其它的模块。然后在发布到线上的时候,你就可以决定在这个时间节点上哪些特性是可以使用的,然后使用发布脚本将它们打包上线。 +你可以套用这个模板来编写其它的模块,然后在发布到线上的时候,再决定在这个时间节点上哪些特性是稳定可用的,然后使用发布脚本将它们打包上线。 +---------校对分割线--------- ## 立即初始化的对象 From f01033c8e058f3d3a9a192ed8b656845ae097829 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 19 Apr 2013 12:42:31 +0800 Subject: [PATCH 080/145] =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E5=8D=B3=E6=97=B6?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=20=E6=A0=A1=E5=AF=B9=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 0f4807d..7036fa6 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -570,48 +570,46 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 你可以套用这个模板来编写其它的模块,然后在发布到线上的时候,再决定在这个时间节点上哪些特性是稳定可用的,然后使用发布脚本将它们打包上线。 ----------校对分割线--------- - -## 立即初始化的对象 +## 对象即时初始化 -还有另外一种可以避免污染全局作用域的方法,和前面描述的即时函数相似,叫做“立即初始化的对象”模式。这种模式使用一个带有init()方法的对象来实现,这个方法在对象被创建后立即执行。初始化的工作由init()函数来完成。 +还有另外一种可以避免污染全局作用域的方法,和前面描述的即时函数相似,叫做“对象即时初始化”模式。这种模式使用一个带有`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()方法。 +在语法上,当你使用这种模式的时候就像在使用对象字面量创建一个普通对象一样。不同之处在于,需要将对象字面量用括号括起来,这样能让JavaScript引擎知道这是一个对象字面量,而不是一个代码块(`if`或者`for`循环之类)。在括号后面,紧接着就执行了`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(); -> 这种模式主要用于一些一次性的工作,并且在init()方法执行完后就无法再次访问到这个对象。如果希望在这些工作完成后保持对对象的引用,只需要简单地在init()的末尾加上return this;即可。 +> 这种模式主要用于一些一次性的工作,并且在`init()`方法执行完后就无法再次访问到这个对象。如果希望在这些工作完成后保持对对象的引用,只需要简单地在`init()`的末尾加上`return this;`即可。 +---------校对分割线--------- ## 条件初始化 From d7e13ee802563df2c9082dbfcbdd95487fd41ac2 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 19 Apr 2013 12:46:39 +0800 Subject: [PATCH 081/145] =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 7036fa6..2963999 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -609,45 +609,43 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 > 这种模式主要用于一些一次性的工作,并且在`init()`方法执行完后就无法再次访问到这个对象。如果希望在这些工作完成后保持对对象的引用,只需要简单地在`init()`的末尾加上`return this;`即可。 ----------校对分割线--------- - ## 条件初始化 条件初始化(也叫条件加载)是一种优化模式。当你知道某种条件在整个程序生命周期中都不会变化的时候,那么对这个条件的探测只做一次就很有意义。浏览器探测(或者特征检测)是一个典型的例子。 -举例说明,当你探测到XMLHttpRequest被作为一个本地对象支持时,就知道浏览器不会在程序执行过程中改变这一情况,也不会出现突然需要去处理ActiveX对象的情况。当环境不发生变化的时候,你的代码就没有必要在需要在每次XHR对象时探测一遍(并且得到同样的结果)。 +举例说明,当你探测到`XMLHttpRequest`被作为一个本地对象支持时,就知道浏览器不会在程序执行过程中改变这一情况,也不会出现突然需要去处理ActiveX对象的情况。当环境不发生变化的时候,你的代码就没有必要在需要在每次初始化XHR对象时探测一遍(并且得到同样的结果)。 -另外一些可以从条件初始化中获益的场景是获得一个DOM元素的computed styles或者是绑定事件处理函数。大部分程序员在他们的客户端编程生涯中都编写过事件绑定和取消绑定相关的组件,像下面的例子: +另外一些可以从条件初始化中获益的场景是获得一个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 + } else { // 老的浏览器 el['on' + type] = fn; } }, removeListener: function (el, type, fn) { - // pretty much the same... + // 和上面很类似的代码…… } }; -这段代码的问题就是效率不高。每当你执行utils.addListener()或者utils.removeListener()时,同样的检查都会被重复执行。 +这段代码的问题就是效率不高。每当你执行`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); @@ -671,9 +669,10 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 }; } -说到这里,要特别提醒一下关于浏览器探测的事情。当你使用这个模式的时候,不要对浏览器特性过度假设。举个例子,如果你探测到浏览器不支持window.addEventListener,不要假设这个浏览器是IE,也不要认为它不支持原生的XMLHttpRequest,虽然这个结论在整个浏览器历史上的某个点是正确的。当然,也有一些情况是可以放心地做一些特性假设的,比如.addEventListener和.removeEventListerner,但是通常来讲,浏览器的特性在发生变化时都是独立的。最好的策略就是分别探测每个特性,然后使用条件初始化,使这种探测只做一次。 +说到这里,要特别提醒一下关于浏览器探测的事情。当你使用这个模式的时候,不要对浏览器特性过度假设。举个例子,如果你探测到浏览器不支持`window.addEventListener`时,不要假设这个浏览器是IE,也不要认为它不支持原生的`XMLHttpRequest`,虽然这个结论在整个浏览器历史上的某个时间点是正确的。当然,也有一些情况是可以放心地做一些特性假设的,比如`.addEventListener`和`.removeEventListerner`,但是通常来讲,浏览器的特性在发生变化时都是独立的。最好的策略就是分别探测每个特性,然后使用条件初始化,使这种探测只做一次。 +---------校对分割线--------- ## 函数属性——Memoization模式 From 35b61d96f86562a66e82ff749325522db7d8814d Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 19 Apr 2013 12:53:43 +0800 Subject: [PATCH 082/145] =?UTF-8?q?=E8=AE=B0=E5=BF=86=E6=A8=A1=E5=BC=8F=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 2963999..a04eb68 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -672,32 +672,30 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 说到这里,要特别提醒一下关于浏览器探测的事情。当你使用这个模式的时候,不要对浏览器特性过度假设。举个例子,如果你探测到浏览器不支持`window.addEventListener`时,不要假设这个浏览器是IE,也不要认为它不支持原生的`XMLHttpRequest`,虽然这个结论在整个浏览器历史上的某个时间点是正确的。当然,也有一些情况是可以放心地做一些特性假设的,比如`.addEventListener`和`.removeEventListerner`,但是通常来讲,浏览器的特性在发生变化时都是独立的。最好的策略就是分别探测每个特性,然后使用条件初始化,使这种探测只做一次。 ----------校对分割线--------- - -## 函数属性——Memoization模式 +## 函数属性——记忆模式(Memoization) -函数也是对象,所以它们可以有属性。事实上,函数也确实本来就有一些属性。比如,对一个函数来说,不管是用什么语法创建的,它会自动拥有一个length属性来标识这个函数期待接受的参数个数: +函数也是对象,所以它们可以有属性。事实上,函数也确实本来就有一些属性。比如,对一个函数来说,不管是用什么语法创建的,它会自动拥有一个`length`属性来标识这个函数期待接受的参数个数: function func(a, b, c) {} console.log(func.length); // 3 -任何时候都可以给函数添加自定义属性。添加自定义属性的一个有用场景是缓存函数的执行结果(返回值),这样下次同样的函数被调用的时候就不需要再做一次那些可能很复杂的计算。缓存一个函数的运行结果也就是为大家所熟知的Memoization。 +任何时候都可以给函数添加自定义属性。添加自定义属性的一个有用场景是缓存函数的执行结果(返回值),这样下次同样的函数被调用的时候就不需要再做一次那些可能很复杂的计算。缓存一个函数的运行结果也就是为大家所熟知的记忆模式。 -在下面的例子中,myFunc函数创建了一个cache属性,可以通过myFunc.cache访问到。这个cache属性是一个对象(hash表),传给函数的参数会作为对象的key,函数执行结果会作为对象的值。函数的执行结果可以是任何的复杂数据结构: +在下面的例子中,`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,并且这个参数是基本类型(比如字符串)。如果你有更多更复杂的参数,则通常需要对它们进行序列化。比如,你需要将arguments对象序列化为JSON字符串,然后使用JSON字符串作为cache对象的key: +上面的代码假设函数只接受一个参数`param`,并且这个参数是原始类型(比如字符串)。如果你有更多更复杂的参数,则通常需要对它们进行序列化。比如,你需要将`arguments`对象序列化为JSON字符串,然后使用JSON字符串作为`cache`对象的key: var myFunc = function () { @@ -706,18 +704,18 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 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的严格模式中是不被允许的: +前面代码中的函数名还可以使用`arguments.callee`来替代,这样就不用将函数名硬编码。不过尽管现阶段这个办法可行,但是仍然需要注意,`arguments.callee`在ECMAScript5的严格模式中是不被允许的: var myFunc = function (param) { @@ -726,16 +724,17 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 if (!f.cache[param]) { result = {}; - // ... expensive operation ... + // ……复杂的计算…… f.cache[param] = result; } return f.cache[param]; }; - // cache storage + // 缓存 myFunc.cache = {}; +---------校对分割线--------- ## 配置对象 From d62916ddb7700f36ae06d1078c621fb1239f02f4 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 19 Apr 2013 13:05:41 +0800 Subject: [PATCH 083/145] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=AF=B9=E8=B1=A1=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index a04eb68..94dc02c 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -734,15 +734,13 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 myFunc.cache = {}; ----------校对分割线--------- - ## 配置对象 -配置对象模式是一种提供更简洁的API的方法,尤其是当你正在写一个即将被其它程序调用的类库之类的代码的时候。 +配置对象模式是一种为自己的代码提供更简洁的API的方法,如果你正在写一个即将被其它程序调用的类库之类的代码的时候就特别有用。 软件在开发和维护过程中需要不断改变是一个不争的事实。这样的事情总是以一些有限的需求开始,但是随着开发的进行,越来越多的功能会不断被加进来。 -设想一下你正在写一个名为addPerson()的函数,它接受一个姓和一个名,然后在列表中加入一个人: +设想一下你正在写一个名为`addPerson()`的函数,它接受一个姓和一个名,然后在列表中加入一个人: function addPerson(first, last) {...} @@ -754,7 +752,7 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 addPerson("Bruce", "Wayne", new Date(), null, null, "batman"); -传一大串的参数真的很不方便。一个更好的办法就是将它们替换成一个参数,并且把这个参数弄成对象;我们叫它conf,是“configuration”(配置)的缩写: +这样传一大串的参数真的很不方便。一个更好的办法就是将它们替换成一个参数对象,我们叫它`conf`,是“configuration”(配置)的缩写: addPerson(conf); @@ -779,9 +777,10 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 - 需要记住参数的名字 - 参数名字不能被压缩 -举些实例,这个模式对创建DOM元素的函数或者是给元素设定CSS样式的函数会非常实用,因为元素和CSS样式可能会有很多但是大部分可选的属性。 +在实践中,这个模式对创建DOM元素的函数或者是给元素设定CSS样式的函数会非常实用,因为元素和CSS样式可能会有很多属性,但是大部分属性是可选的。 +---------校对分割线--------- ## 柯里化 (Curry) From 92ce4d17cbe8725493bf2ecadbeea03da276a768 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 19 Apr 2013 13:29:43 +0800 Subject: [PATCH 084/145] =?UTF-8?q?=E6=9F=AF=E9=87=8C=E5=8C=96=E8=83=8C?= =?UTF-8?q?=E6=99=AF=E9=83=A8=E5=88=86=E5=BA=94=E7=94=A8=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 57 +++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 94dc02c..319fd95 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -779,35 +779,31 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 在实践中,这个模式对创建DOM元素的函数或者是给元素设定CSS样式的函数会非常实用,因为元素和CSS样式可能会有很多属性,但是大部分属性是可选的。 - ----------校对分割线--------- - ## 柯里化 (Curry) -在本章剩下的部分,我们将讨论一下关于柯里化和部分应用的话题。但是在我们开始这个话题之前,先看一下到底什么是函数应用。 +在本章剩下的部分,我们将讨论一下关于柯里化和部分应用的话题。但是在我们开始这个话题之前,先看一下什么是函数应用。 - ### 函数应用 -在一些纯粹的函数式编程语言中,对函数的描述不是被调用(called或者invoked),而是被应用(applied)。在JavaScript中也有同样的东西——我们可以使用Function.prototype.apply()来应用一个函数,因为在JavaScript中,函数实际上是对象,并且他们拥有方法。 +在一些纯粹的函数式编程语言中,对函数的描述不是被调用(`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将指向全局对象,这正是当你调用一个函数(且这个函数不是某个对象的方法)时发生的事情。 +从上面的例子中可以看出来,调用一个函数和应用一个函数有相同的结果。`apply()`接受两个参数:第一个是在函数内部绑定到`this`上的对象,第二个是一个参数数组,参数数组会在函数内部变成一个类似数组的`arguments`对象。如果第一个参数为`null`,那么`this`将指向全局对象,这正是当你调用一个函数(且这个函数不是某个对象的方法)时发生的事情。 -当一个函数是一个对象的方法时,我们不再像前面的例子一样传入null。(译注:主要是为了保证方法中的this绑定到一个有效的对象而不是全局对象。)在下面的例子中,对象被作为第一个参数传给apply(): +当一个函数是一个对象的方法时,我们不再像前面的例子一样传入`null`。(译注:主要是为了保证方法中的`this`绑定到一个有效的对象而不是全局对象。)在下面的例子中,对象被作为第一个参数传给`apply()`: var alien = { sayHi: function (who) { @@ -818,68 +814,67 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 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。) +在这个例子中,`sayHi()`中的`this`指向`alien`。而在上一个例子中,`this`是指向的全局对象。(译注:这个例子的代码有误,最后一行的`sayHi`并不能访问到`alien`的`sayHi`方法,需要使用`alien.sayHi.apply(alien, ["humans"])`才可正确运行。另外,在`sayHi`中也没有出现`this`。) -正如上面两个例子所展现出来的一样,我们将所谓的函数调用当作函数应用的一种语法糖并没有什么太大的问题。 +正如上面两个例子所展现出来的一样,我们将所谓的函数调用当作函数应用的一种语法糖来理解也没有什么太大的问题。 -需要注意的是,除了apply()之外,Function.prototype对象还有一个call()方法,但是它仍然只是apply()的一种语法糖。(译注:这两个方法的区别在于,apply()只接受两个参数,第二个参数为需要传给函数的参数数组,而call()则接受任意多个参数,从第二个开始将参数依次传给函数。)不过有种情况下使用这个语法糖会更好:当你的函数只接受一个参数的时候,你可以省去为唯一的一个元素创建数组的工作: +需要注意的是,除了`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时的计算步骤: +假设已经有了一个`add()`函数,它的工作是把`x`和`y`两个数加到一起。下面的代码片段展示了当`x`为5、`y`为4时的计算步骤: - // for illustration purposes - // not valid JavaScript + // 并不是合法的JavaScript代码,仅用于演示 - // we have this function + // 假设有一个add()函数 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替换到函数中。然后重复这个过程,直到替换掉所有的参数。 +在这个代码片段中,第一步和第二步并不是有效的JavaScript代码,但是它展示了我们手工计算的过程。首先获得第一个参数的值,然后在函数中将未知的`x`值替换为5。然后重复这个过程,直到替换掉所有的参数。 -step 1是一个所谓的部分应用的例子:我们只应用了第一个参数。当你执行一个部分应用的时候并不能获得结果(或者是解决方案),取而代之的是另一个函数。 +第一步是一个所谓的部分应用的例子:我们只应用了第一个参数。当你执行一个部分应用的时候并不能获得结果(或者是解决方案),取而代之的是另一个函数。 -下面的代码片段展示了一个虚拟的partialApply()方法的用法: +下面的代码片段展示了一个虚拟的`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)的一种语法糖。 +正如你所看到的一样,部分应用给了我们另一个函数,这个函数可以在稍后调用的时候接受其它的参数。这实际上跟`add(5)(4)`是等价的,因为`add(5)`返回了一个函数,这个函数可以使用`(4)`来调用。我们又一次看到,熟悉的`add(5, 4)`也差不多是`add(5)(4)`的一种语法糖。 -现在,让我们回到地球:并不存在这样的一个partialApply()函数,并且函数的默认表现也不会像上面的例子中那样。但是你完全可以自己去写,因为JavaScript的动态特性完全可以做到这样。 +现在,让我们回到地球:并不存在这样的一个`partialApply()`函数,并且函数的默认表现也不会像上面的例子中那样。但是你完全可以自己去写,因为JavaScript的动态特性完全可以做到这样。 让函数理解并且处理部分应用的过程,叫柯里化(Currying)。 +---------校对分割线--------- ### 柯里化(Currying) From e9b707986bc1193e3e7eb7af5af5702198f59985 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 19 Apr 2013 13:43:45 +0800 Subject: [PATCH 085/145] =?UTF-8?q?=E7=AC=AC4=E7=AB=A0=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95=20close=20#5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter4.markdown | 59 +++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/chapter4.markdown b/chapter4.markdown index 319fd95..f47c020 100644 --- a/chapter4.markdown +++ b/chapter4.markdown @@ -874,52 +874,48 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 让函数理解并且处理部分应用的过程,叫柯里化(Currying)。 ----------校对分割线--------- - ### 柯里化(Currying) -柯里化和辛辣的印度菜可没什么关系;它来自数学家Haskell Curry。(Haskell编程语言也是因他而得名。)柯里化是一个变换函数的过程。柯里化的另外一个名字也叫schönfinkelisation,来自另一位数学家——Moses Schönfinkelisation——这种变换的最初发明者。 +柯里化这个名字来自数学家Haskell Curry。(Haskell编程语言也是因他而得名。)柯里化是一个变换函数的过程。柯里化的另外一个名字也叫schönfinkelisation,来自另一位数学家——Moses Schönfinkelisation——这种变换的最初发明者。 -所以我们怎样对一个函数进行柯里化呢?其它的函数式编程语言也许已经原生提供了支持并且所有的函数已经默认柯里化了。在JavaScript中我们可以修改一下add()函数使它柯里化,然后支持部分应用。 +所以我们怎样对一个函数进行柯里化呢?其它的函数式编程语言也许已经原生提供了支持并且所有的函数已经默认柯里化了。在JavaScript中我们可以修改一下`add()`函数使它柯里化,然后支持部分应用。 来看一个例子: - // a curried add() - // accepts partial list of arguments + // 柯里化过的add()方法,可以接受部分参数 function add(x, y) { var oldx = x, oldy = y; - if (typeof oldy === "undefined") { // partial + if (typeof oldy === "undefined") { // 部分应用 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: +在这段代码中,第一次调用`add()`时,在返回的内层函数那里创建了一个闭包。这个闭包将原来的`x`和`y`的值存储到了`oldx`和`oldy`中。当内层函数执行的时候,`oldx`会被使用。如果没有部分应用,即`x`和`y`都传了值,那么这个函数会简单地将他们相加。这个`add()`函数的实现显得有些冗余,仅仅是为了更好地说明问题。下面的代码片段中展示了一个更简洁的版本,没有`oldx`和`oldy`,因为原始的`x`已经被存储到了闭包中,此外我们复用了`y`作为本地变量,而不用像之前那样新定义一个变量`newy`: - // a curried add - // accepts partial list of arguments + // 柯里化过的add()方法,可以接受部分参数 function add(x, y) { - if (typeof y === "undefined") { // partial + if (typeof y === "undefined") { // 部分应用 return function (y) { return x + y; }; } - // full application + // 完整应用 return x + y; } -在这些例子中,add()函数自己处理了部分应用。有没有可能用一种更为通用的方式来做同样的事情呢?换句话说,我们能不能对任意一个函数进行处理,得到一个新函数,使它可以处理部分参数?下面的代码片段展示了一个通用函数的例子,我们叫它schonfinkelize(),正是用来做这个的。我们使用schonfinkelize()这个名字,一部分原因是它比较难发音,另一部分原因是它听起来比较像动词(使用“curry”则不是那么明确),而我们刚好需要一个动词来表明这是一个函数转换的过程。 +在这些例子中,`add()`函数自己处理了部分应用。有没有可能用一种更为通用的方式来做同样的事情呢?换句话说,我们能不能对任意一个函数进行处理,得到一个新函数,使它可以处理部分参数?下面的代码片段展示了一个通用函数的例子,我们叫它`schonfinkelize()`,它正是用来做这个的。我们使用`schonfinkelize()`这个名字,一部分原因是它比较难发音,另一部分原因是它听起来比较像动词(使用“curry”则不是那么明确),而我们刚好需要一个动词来表明这是一个函数转换的过程。 这是一个通用的柯里化函数: @@ -933,56 +929,53 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 }; } -这个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`(也可以在闭包中访问到)即可。 现在有了通用的柯里化函数,就可以做一些测试了: - // 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()并不局限于单个参数或者单步的柯里化。这里有些更多用法的例子: +用来做函数转换的`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函数中两个重要的特性,也就是: +在JavaScript中,对开发者在函数这个话题的理解和运用的要求是比较苛刻的。在本章中,主要讨论了有关函数的一些背景知识和术语。介绍了JavaScript函数中两个重要的特性,也就是: 1. 函数是一等对象,他们可以被作为值传递,也可以拥有属性和方法。 2. 函数拥有本地作用域,而大括号不产生块级作用域。另外需要注意的是,变量的声明会被提前到本地作用域顶部。 创建一个函数的语法有: -1. 带有名字的函数表达式 -2. 函数表达式(和上一种一样,但是没有名字),也就是为大家熟知的“匿名函数” +1. 具名函数表达式 +2. 匿名函数表达式(和上一种一样,但是没有名字),也就是为大家熟知的“匿名函数” 3. 函数声明,与其它语言的函数语法相似 在介绍完背景和函数的语法后,介绍了一些有用的模式,按分类列出: @@ -1004,14 +997,14 @@ JavaScript中的回调模式已经是我们的家常便饭了,比如,如果 - 即时函数 当它们被定义后立即执行 - - 立即初始化的对象 + - 对象即时初始化 初始化工作被放入一个匿名对象,这个对象提供一个可以立即被执行的方法 - 条件初始化 使分支代码只在初始化的时候执行一次,而不是在整个程序生命周期中反复执行 3. 性能模式,这些模式帮助提高代码的执行速度,包括: - - Memoization + - 记忆模式 利用函数的属性,使已经计算过的值不用再次计算 - 自定义函数 From a75fafe9e5e809fbdea854fd4468f03af3d97687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=92=B1=E5=BC=A0=E7=9B=9B?= Date: Sat, 20 Apr 2013 14:54:35 +0800 Subject: [PATCH 086/145] Update chapter2.markdown --- chapter2.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chapter2.markdown b/chapter2.markdown index ff1decb..2f69e2d 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -58,7 +58,7 @@ JavaScript使用函数来管理作用域,在一个函数内定义的变量称 因此,为了让你的脚本和这个页面中的其他脚本和谐相处,要尽量少使用全局变量,这一点非常重要。本书随后的章节中会讲到一些减少全局变量的技巧和策略,比如使用命名空间或者即时函数等,但减少全局变量最有效的方法还是坚持使用`var`来声明变量。 -在JavaScript中有意无意地创建全局变量是件很容易的是,因为它有两个特性:首先,你可以不声明而直接使用变量,其次,JavaScirpt中具有“隐式全局对象”的概念,也就是说任何不通过`var`声明的变量都会成为全局对象的一个属性(可以把它们当作全局变量)。(译注:在ES6中可以通过`let`来声明块级作用域变量。)看一下下面这段代码: +在JavaScript中有意无意地创建全局变量是件很容易的事,因为它有两个特性:首先,你可以不声明而直接使用变量,其次,JavaScirpt中具有“隐式全局对象”的概念,也就是说任何不通过`var`声明的变量都会成为全局对象的一个属性(可以把它们当作全局变量)。(译注:在ES6中可以通过`let`来声明块级作用域变量。)看一下下面这段代码: function sum(x, y) { // 反模式:隐式全局变量 @@ -963,4 +963,4 @@ JSLint是基于JavaScript实现的(它自己的代码是可以通过JSLint检 - `for`循环、`for-in`循环、`switch`语句、“避免使用`eval()`”、不要扩充内置原型 - 遵守统一的编码规范(在任何必要的时候保持空格、缩进、花括号和分号)和命名规范(构造函数、普通函数和变量)。 -本章还讨论了其他一些和代码本身无关的实践,这些实践和编码过程紧密相关,包括写注释、写API文档、组织同事评审、不要试图去手动“压缩”(minify)代码而牺牲代码可读性、坚持使用JSLint来对代码进行检查。 \ No newline at end of file +本章还讨论了其他一些和代码本身无关的实践,这些实践和编码过程紧密相关,包括写注释、写API文档、组织同事评审、不要试图去手动“压缩”(minify)代码而牺牲代码可读性、坚持使用JSLint来对代码进行检查。 From 3f32b69037d8cffadd6a6df6f30fffad056fc8cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=92=B1=E5=BC=A0=E7=9B=9B?= Date: Sat, 20 Apr 2013 15:39:05 +0800 Subject: [PATCH 087/145] Update chapter2.markdown --- chapter2.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter2.markdown b/chapter2.markdown index 2f69e2d..286674e 100644 --- a/chapter2.markdown +++ b/chapter2.markdown @@ -292,7 +292,7 @@ JSLint提示你这样做,是因为`++`和`--`实际上降低了代码的可读 从技术上讲,`for-in`循环同样可以用于数组(JavaScript中数组也是对象),但不推荐这样做。当数组对象被扩充了自定义函数时,可能会产生逻辑错误。另外,`for-in`循环中属性的遍历顺序是不固定的,所以最好数组使用普通的`for`循环,对象使用`for-in`循环。 -可以使用对象的`hasOwnProperty()`方法过来从原型链中继承来的属性,这一点非常重要。看一下这段代码: +可以使用对象的`hasOwnProperty()`方法过滤来自原型链中继承来的属性,这一点非常重要。看一下这段代码: // 对象 var man = { From 9321145440da615115133ee6dba799b3d4228d3c Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 25 Apr 2013 10:03:19 +0800 Subject: [PATCH 088/145] =?UTF-8?q?=E5=91=BD=E5=90=8D=E7=A9=BA=E9=97=B4?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=20=E4=BB=8B=E7=BB=8D=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 48 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 7bc94f6..a547138 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -1,65 +1,65 @@ # 对象创建模式 -在JavaScript中创建对象很容易——可以通过使用对象直接量或者构造函数。本章将在此基础上介绍一些常用的对象创建模式。 +在JavaScript中创建对象是件很容易的事情,直接通过对象字面量或者构造函数就可以。本章将在此基础上介绍一些常用的对象创建模式。 -JavaScript语言本身简单、直观,通常也没有其他语言那样的语法特性:命名空间、模块、包、私有属性以及静态成员。本章将介绍一些常用的模式,以此实现这些语法特性。 +JavaScript语言本身很简单、直观,也没有其他语言的一些语言特性:命名空间、模块、包、私有属性以及静态成员。本章将介绍一些常用的模式,以此实现这些语言特性。 -我们将对命名空间、依赖声明、模块模式以及沙箱模式进行初探——它们帮助更好地组织应用程序的代码,有效地减轻全局污染的问题。除此之外,还会对包括:私有和特权成员、静态和私有静态成员、对象常量、链以及类式函数定义方式在内的话题进行讨论。 +我们将对命名空间、依赖声明、模块模式以及沙箱模式进行初探——它们可以帮助我们更好地组织应用程序的代码,有效地减少全局污染的问题。除此之外,还会讨论私有和特权成员、静态和私有静态成员、对象常量、链式调用以及一种像类式语言一样定义构造函数的方法等话题。 -## 命名空间模式(Namespace Pattern) +## 命名空间模式 -命名空间可以帮助减少全局变量的数量,与此同时,还能有效地避免命名冲突、名称前缀的滥用。 +使用命名空间可以减少全局变量的数量,与此同时,还能有效地避免命名冲突和前缀的滥用。 -JavaScript默认语法并不支持命名空间,但很容易可以实现此特性。为了避免产生全局污染,你可以为应用或者类库创建一个(通常就一个)全局对象,然后将所有的功能都添加到这个对象上,而不是到处申明大量的全局函数、全局对象以及其他全局变量。 +JavaScript没有原生的命名空间语法,但很容易可以实现这个特性。为了避免产生全局污染,你可以为应用或者类库创建一个(通常是唯一一个)全局对象,然后将所有的功能都添加到这个对象上,而不是到处声明大量的全局函数、全局对象以及其他的全局变量。 看如下例子: - // BEFORE: 5 globals - // Warning: antipattern - // constructors + // 重构前:5个全局变量 + // 注意:反模式 + // 构造函数 function Parent() {} function Child() {} - // a variable + // 一个变量 var some_var = 1; - // some objects + // 一些对象 var module1 = {}; module1.data = {a: 1, b: 2}; var module2 = {}; -可以通过创建一个全局对象(通常代表应用名)来重构上述这类代码,比方说, MYAPP,然后将上述例子中的函数和变量都变为该全局对象的属性: +可以通过创建一个全局对象(通常代表应用名)比如`MYAPP`来重构上述这类代码,然后将上述例子中的函数和变量都变为该全局对象的属性: - // AFTER: 1 global - // global object + // 重构后:一个全局变量 + // 全局对象 var MYAPP = {}; - // constructors + // 构造函数 MYAPP.Parent = function () {}; MYAPP.Child = function () {}; - // a variable + // 一个变量 MYAPP.some_var = 1; - // an object container + // 一个对象容器 MYAPP.modules = {}; - // nested objects + // 嵌套的对象 MYAPP.modules.module1 = {}; MYAPP.modules.module1.data = {a: 1, b: 2}; MYAPP.modules.module2 = {}; -这里的MYAPP就是命名空间对象,对象名可以随便取,可以是应用名、类库名、域名或者是公司名都可以。开发者经常约定全局变量都采用大写(所有字母都大写),这样可以显得比较突出(不过,要记住,一般大写的变量都用于表示常量)。 +这里的`MYAPP`就是命名空间对象,对象名可以随便取,可以是应用名、类库名、域名或者是公司名都可以。开发者经常约定全局变量都采用大写(所有字母都大写),这样可以显得比较突出(不过要记住,大写的变量也常用于表示常量)。 -这种模式是一种很好的提供命名空间的方式,避免了自身代码的命名冲突,同时还避免了同一个页面上自身代码和第三方代码(比如:JavaScript类库或者小部件)的冲突。这种模式在大多数情况下非常适用,但也有它的缺点: +这种模式是一种很好的提供命名空间的方式,避免了自身代码的命名冲突,同时还避免了同一个页面上自身代码和第三方代码(比如JavaScript类库或者widget)的冲突。这种模式在大多数情况下非常适用,但也有它的缺点: -* 代码量稍有增加;在每个函数和变量前加上这个命名空间对象的前缀,会增加代码量,增大文件大小 -* 该全局实例可以被随时修改 -* 命名的深度嵌套会减慢属性值的查询 +- 代码量稍有增加;在每个函数和变量前加上这个命名空间对象的前缀,会增加代码量,增大文件大小 +- 该全局实例可以被随时修改 +- 命名的深度嵌套会减慢属性值的查询 本章后续要介绍的沙箱模式则可以避免这些缺点。 -###通用命名空间函数 +### 通用命名空间函数 随着程序复杂度的提高,代码会分置在不同的文件中以特定顺序来加载,这样一来,就不能保证你的代码一定是第一个申明命名空间或者改变量下的属性的。甚至还会发生属性覆盖的问题。所以,在创建命名空间或者添加属性的时候,最好先检查下是否存在,如下所示: From 0b44548cc13847d184caf3c404ae4d7b0e5c7259 Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 25 Apr 2013 10:29:48 +0800 Subject: [PATCH 089/145] =?UTF-8?q?=E5=91=BD=E5=90=8D=E7=A9=BA=E9=97=B4=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index a547138..1d35ee2 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -58,33 +58,32 @@ JavaScript没有原生的命名空间语法,但很容易可以实现这个特 本章后续要介绍的沙箱模式则可以避免这些缺点。 - ### 通用命名空间函数 -随着程序复杂度的提高,代码会分置在不同的文件中以特定顺序来加载,这样一来,就不能保证你的代码一定是第一个申明命名空间或者改变量下的属性的。甚至还会发生属性覆盖的问题。所以,在创建命名空间或者添加属性的时候,最好先检查下是否存在,如下所示: +随着程序复杂度的提高,代码会被分拆在不同的文件中以按照页面需要来加载,这样一来,就不能保证你的代码一定是第一个定义命名空间或者某个属性的,甚至会发生属性覆盖的问题。所以,在创建命名空间或者添加属性的时候,最好先检查下是否存在,如下所示: - // unsafe + // 不安全的做法 var MYAPP = {}; - // better + // 更好的做法 if (typeof MYAPP === "undefined") { var MYAPP = {}; } - // or shorter + // 简写 var MYAPP = MYAPP || {}; -如上所示,不难看出,如果每次做类似操作都要这样检查一下就会有很多重复性的代码。比方说,要申明**MYAPP.modules.module2**,就要重复三次这样的检查。所以,我们需要一个重用的**namespace()**函数来专门处理这些检查工作,然后用它来创建命名空间,如下所示: +如上所示,如果每次做类似操作都要这样检查一下就会有很多重复的代码。例如,要声明`MYAPP.modules.module2`,就要重复三次这样的检查。所以,我们需要一个可复用的`namespace()`函数来专门处理这些检查工作,然后用它来创建命名空间,如下所示: - // using a namespace function + // 使用命名空间函数 MYAPP.namespace('MYAPP.modules.module2'); - // equivalent to: + // 等价于: // var MYAPP = { - // modules: { - // module2: {} - // } + // modules: { + // module2: {} + // } // }; -下面是上述namespace函数的实现案例。这种实现是无损的,意味着如果要创建的命名空间已经存在,则不会再重复创建: +下面是上述`namespace`函数的实现示例。这种实现是非破坏性的,意味着如果要创建的命名空间已经存在,则不会再重复创建: var MYAPP = MYAPP || {}; MYAPP.namespace = function (ns_string) { @@ -92,13 +91,14 @@ JavaScript没有原生的命名空间语法,但很容易可以实现这个特 parent = MYAPP, i; - // strip redundant leading global + // 去除不必要的全局变量层 + // 译注:因为namespace已经属于MYAPP if (parts[0] === "MYAPP") { parts = parts.slice(1); } for (i = 0; i < parts.length; i += 1) { - // create a property if it doesn't exist + // 如果属性不存在则创建它 if (typeof parent[parts[i]] === "undefined") { parent[parts[i]] = {}; } @@ -107,23 +107,23 @@ JavaScript没有原生的命名空间语法,但很容易可以实现这个特 return parent; }; -上述实现支持如下使用: +上述实现支持如下几种用法: - // assign returned value to a local var + // 将返回值赋给本地变量 var module2 = MYAPP.namespace('MYAPP.modules.module2'); module2 === MYAPP.modules.module2; // true - // skip initial `MYAPP` + // 省略全局命名空间`MYAPP` MYAPP.namespace('modules.module51'); - // long namespace + // 长命名空间 MYAPP.namespace('once.upon.a.time.there.was.this.long.nested.property'); -图5-1 展示了上述代码创建的命名空间对象在Firebug下的可视结果 +图5-1 展示了上述代码创建的命名空间对象在Firebug下的可视化结果 ![MYAPP命名空间在Firebug下的可视结果](./Figure/chapter5/5-1.jpg) -图5-1 MYAPP命名空间在Firebug下的可视结果 +图5-1 MYAPP命名空间在Firebug下的可视化结果 ## 声明依赖 From 9b698977751f0b7a60090b2ef5727a4b323c7791 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 26 Apr 2013 10:29:37 +0800 Subject: [PATCH 090/145] =?UTF-8?q?=E5=A3=B0=E6=98=8E=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=88=90=20=E7=A7=81=E6=9C=89?= =?UTF-8?q?=E6=88=90=E5=91=98=E5=AE=8C=E6=88=90=E4=B8=80=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 90 +++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 46 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 1d35ee2..74bef9c 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -127,27 +127,26 @@ JavaScript没有原生的命名空间语法,但很容易可以实现这个特 ## 声明依赖 -JavaScript库往往是模块化而且有用到命名空间的,这使得你可以只使用你需要的模块。比如在YUI2中,全局变量YAHOO就是一个命名空间,各个模块作为全局变量的属性,比如YAHOO.util.Dom(DOM模块)、YAHOO.util.Event(事件模块)。 +JavaScript库往往是模块化而且有用到命名空间的,这使得你可以只使用你需要的模块。比如在YUI2中,全局变量`YAHOO`就是一个命名空间,各个模块都是全局变量的属性,比如`YAHOO.util.Dom`(DOM模块)、`YAHOO.util.Event`(事件模块)。 将你的代码依赖在函数或者模块的顶部进行声明是一个好主意。声明就是创建一个本地变量,指向你需要用到的模块: var myFunction = function () { - // dependencies + // 依赖 var event = YAHOO.util.Event, dom = YAHOO.util.Dom; - // use event and dom variables - // for the rest of the function... + // 在函数后面的代码中使用event和dom…… }; 这是一个相当简单的模式,但是有很多的好处: -- 明确的声明依赖是告知你代码的用户,需要保证指定的脚本文件被包含在页面中。 +- 明确的声明依赖是告知使用你代码的开发者,需要保证指定的脚本文件被包含在页面中。 - 将声明放在函数顶部使得依赖很容易被查找和解析。 -- 本地变量(如dom)永远会比全局变量(如YAHOO)要快,甚至比全局变量的属性(如YAHOO.util.Dom)还要快,这样会有更好的性能。使用了依赖声明模式之后,全局变量的解析在函数中只会进行一次,在此之后将会使用更快的本地变量。 -- 一些高级的代码压缩工具比如YUI Compressor和Google Closure compiler会重命名本地变量(比如event可能会被压缩成一个字母,如A),这会使代码更精简,但这个操作不会对全局变量进行,因为这样做不安全。 +- 本地变量(如`dom`)永远会比全局变量(如`YAHOO`)要快,甚至比全局变量的属性(如`YAHOO.util.Dom`)还要快,这样会有更好的性能。使用了依赖声明模式之后,全局变量的解析在函数中只会进行一次,在此之后将会使用更快的本地变量。 +- 一些高级的代码压缩工具比如YUI Compressor和Google Closure compiler会重命名本地变量(比如`event`可能会被压缩成一个字母,如`A`),这会使代码更精简,但这个操作不会对全局变量进行,因为这样做不安全。 -下面的代码片段是关于是否使用依赖声明模式对压缩影响的展示。尽管使用了依赖声明模式的test2()看起来复杂,因为需要更多的代码行数和一个额外的变量,但在压缩后它的代码量却会更小,意味着用户只需要下载更少的代码: +下面的代码片段是关于是否使用依赖声明模式对压缩影响的展示。尽管使用了依赖声明模式的`test2()`看起来复杂,因为需要更多的代码行数和一个额外的变量,但在压缩后它的代码量却会更小,意味着用户只需要下载更少的代码: function test1() { alert(MYAPP.modules.m1); @@ -156,7 +155,7 @@ JavaScript库往往是模块化而且有用到命名空间的,这使得你可 } /* - minified test1 body: + test1()压缩后的函数体: alert(MYAPP.modules.m1);alert(MYAPP.modules.m2);alert(MYAPP.modules.m51) */ @@ -168,7 +167,7 @@ JavaScript库往往是模块化而且有用到命名空间的,这使得你可 } /* - minified test2 body: + test2()压缩后的函数体: var a=MYAPP.modules;alert(a.m1);alert(a.m2);alert(a.m51) */ @@ -183,8 +182,8 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 return this.myprop; } }; - console.log(myobj.myprop); // `myprop` is publicly accessible - console.log(myobj.getProp()); // getProp() is public too + console.log(myobj.myprop); // myprop是公有的 + console.log(myobj.getProp()); // getProp()也是公有的 当你使用构造函数创建对象的时候也是一样的,所有的成员都是公有的: @@ -195,60 +194,60 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 }; } var toy = new Gadget(); - console.log(toy.name); // `name` is public - console.log(toy.stretch()); // stretch() is public + console.log(toy.name); // name是公有的 + console.log(toy.stretch()); // stretch()也是公有的 ### 私有成员 -尽管语言并没有用于私有成员的专门语法,但你可以通过闭包来实现。在构造函数中创建一个闭包,任何在这个闭包中的部分都不会暴露到构造函数之外。但是,这些私有变量却可以被公有方法访问,也就是在构造函数中定义的并且作为返回对象一部分的那些方法。我们来看一个例子,name是一个私有成员,在构造函数之外不能被访问: +尽管语言并没有用于私有成员的专门语法,但你可以通过闭包来实现。在构造函数中创建一个闭包,任何在这个闭包中的部分都不会暴露到构造函数之外。但是,这些私有变量却可以被公有方法访问,也就是在构造函数中定义的并且作为返回对象一部分的那些方法。我们来看一个例子,`name`是一个私有成员,在构造函数之外不能被访问: function Gadget() { - // private member + // 私有成员 var name = 'iPod'; - // public function + // 公有函数 this.getName = function () { return name; }; } var toy = new Gadget(); - // `name` is undefined, it's private + // name是是私有的 console.log(toy.name); // undefined - // public method has access to `name` + // 公有方法可以访问到name console.log(toy.getName()); // "iPod" 如你所见,在JavaScript创建私有成员很容易。你需要做的只是将私有成员放在一个函数中,保证它是函数的本地变量,也就是说让它在函数之外不可以被访问。 ### 特权方法 -特权方法的概念不涉及到任何语法,它只是一个给可以访问到私有成员的公有方法的名字(就像它们有更多权限一样)。 +特权方法的概念不涉及到任何语法,它只是一个给可以访问到私有成员的公有方法的名字(就好像它们有更多权限一样)。 -在前面的例子中,getName()就是一个特权方法,因为它有访问name属性的特殊权限。 +在前面的例子中,`getName()`就是一个特权方法,因为它有访问`name`属性的特殊权限。 ### 私有成员失效 当你使用私有成员时,需要考虑一些极端情况: -- 在Firefox的一些早期版本中,允许通过给eval()传递第二个参数的方法来指定上下文对象,从而允许访问函数的私有作用域。比如在Mozilla Rhino(译注:一个JavaScript引擎)中,允许使用`__parent__`来访问私有作用域。现在这些极端情况并没有被广泛应用到浏览器中。 +- 在Firefox的一些早期版本中,允许通过给`eval()`传递第二个参数的方法来指定上下文对象,从而允许访问函数的私有作用域。比如在Mozilla Rhino(译注:一个JavaScript引擎)中,允许使用`__parent__`来访问私有作用域。这些极端情况现在并没有广泛存在于浏览器中。 - 当你直接通过特权方法返回一个私有变量,而这个私有变量恰好是一个对象或者数组时,外部的代码可以修改这个私有变量,因为它是按引用传递的。 -我们来看一下第二种情况。下面的Gadget的实现看起来没有问题: +我们来看一下第二种情况。下面的`Gadget`的实现看起来没有问题: function Gadget() { - // private member + // 私有成员 var specs = { - screen_width: 320, - screen_height: 480, - color: "white" + screen_width: 320, + screen_height: 480, + color: "white" }; - // public function + // 公有函数 this.getSpecs = function () { return specs; }; } -这里的问题是getSpecs()返回了一个specs对象的引用。这使得Gadget的使用者可以修改貌似隐藏起来的私有成员specs: +这里的问题是`getSpecs()`返回了一个`specs`对象的引用。这使得`Gadget()`的使用者可以修改貌似隐藏起来的私有成员`specs`: var toy = new Gadget(), specs = toy.getSpecs(); @@ -264,25 +263,24 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 图5-2 私有对象被修改了 -这个意外的问题的解决方法就是不要将你想保持私有的对象或者数组的引用传递出去。达到这个目标的一种方法是让getSpecs()返回一个新对象,这个新对象只包含对象的使用者感兴趣的数据。这也是众所周知的“最低授权原则”(Principle of Least Authority,简称POLA),指永远不要给出比需求更多的东西。在这个例子中,如果Gadget的使用者关注它是否适应一个特定的盒子,它只需要知道尺寸即可。所以你应该创建一个getDimensions(),用它返回一个只包含width和height的新对象,而不是把什么都给出去。也就是说,也许你根本不需要实现getSpecs()方法。 +这个问题有点出乎意料,解决方法就是不要将你想保持私有的对象或者数组的引用传递出去。达到这个目标的一种方法是让`getSpecs()`返回一个新对象,这个新对象只包含对象的使用者需要的数据。这也是众所周知的“最低授权原则”(Principle of Least Authority,简称POLA),指永远不要给出比真实需要更多的东西。在这个例子中,如果`Gadget()`的使用者关注它是否适应一个特定的盒子,它只需要知道尺寸即可。所以你应该创建一个`getDimensions()`,用它返回一个只包含`width`和`height`的新对象,而不是把什么都给出去。也就是说,也许你根本不需要实现`getSpecs()`方法。 -当你需要传递所有的数据时,有另外一种方法,就是使用通用的对象复制函数创建specs对象的一个副本。下一章提供了两个这样的函数——一个叫extend(),它会浅复制一个给定的对象(只复制顶层的成员)。另一个叫extendDeep(),它会做深复制,遍历所有的属性和嵌套的属性。 +当你需要传递所有的数据时,有另外一种方法,就是使用通用的对象复制函数创建`specs`对象的一个副本。下一章提供了两个这样的函数——一个叫`extend()`,它会浅复制一个给定的对象(只复制顶层的成员),另一个叫`extendDeep()`,它会做深复制,遍历所有的属性和嵌套的属性。 ### 对象字面量和私有成员 到目前为止,我们只看了使用构建函数创建私有成员的示例。如果使用对象字面量创建对象时会是什么情况呢?是否有可能含有私有成员? -如你前面所看到的那样,私有数据使用一个函数来包裹。所以在使用对象字面量时,你也可以使用一个立即执行的匿名函数创建的闭包。例如: +如你前面所看到的那样,私有数据使用一个函数来包裹。所以在使用对象字面量时,你也可以使用一个即时函数创建的闭包。例如: - var myobj; // this will be the object + var myobj; // 一个对象 (function () { - // private members + // 私有成员 var name = "my, oh my"; - // implement the public part - // note -- no `var` + // 实现公有部分,注意没有var myobj = { - // privileged method + // 特权方法 getName: function () { return name; } @@ -294,10 +292,10 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 还有一个原理一样但看起来不一样的实现示例: var myobj = (function () { - // private members + // 私有成员 var name = "my, oh my"; - // implement the public part + // 实现公有部分 return { getName: function () { return name; @@ -313,23 +311,23 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 使用构造函数创建私有成员的一个弊端是,每一次调用构造函数创建对象时这些私有成员都会被创建一次。 -这对在构建函数中添加到`this`的成员来说是一个问题。为了避免重复劳动,节省内存,你可以将共用的属性和方法添加到构造函数的`prototype`(原型)属性中。这样的话这些公共的部分会在使用同一个构造函数创建的所有实例中共享。你也同样可以在这些实例中共享私有成员。你可以将两种模式联合起来达到这个目的:构造函数中的私有属性和对象字面量中的私有属性。因为`prototype`属性也只是一个对象,可以使用对象字面量创建。 +这对在构建函数中添加到`this`的成员来说是一个问题。为了避免重复劳动,节省内存,你可以将共用的属性和方法添加到构造函数的`prototype`(原型)属性中。这样的话这些公共的部分会在使用同一个构造函数创建的所有实例中共享。你也同样可以在这些实例中共享私有成员,甚至可以将两种模式联合起来达到这个目的,同时使用构造函数中的私有属性和对象字面量中的私有属性。因为`prototype`属性也只是一个对象,可以使用对象字面量创建。 这是一个示例: function Gadget() { - // private member + // 私有成员 var name = 'iPod'; - // public function + // 公有函数 this.getName = function () { return name; }; } Gadget.prototype = (function () { - // private member + // 私有成员 var browser = "Mobile Webkit"; - // public prototype members + // 公有函数 return { getBrowser: function () { return browser; @@ -338,8 +336,8 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 }()); var toy = new Gadget(); - console.log(toy.getName()); // privileged "own" method - console.log(toy.getBrowser()); // privileged prototype method + console.log(toy.getName()); // 自有的特权方法 + console.log(toy.getBrowser()); // 来自原型的特权方法 ### 将私有函数暴露为公有方法 From a7c20d628c211f3deefe57d8ec2c147af243ff99 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 26 Apr 2013 16:06:16 +0800 Subject: [PATCH 091/145] =?UTF-8?q?=E6=9A=B4=E9=9C=B2=E6=A8=A1=E5=BC=8F=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 74bef9c..1d8abbc 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -341,7 +341,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 ### 将私有函数暴露为公有方法 -“暴露模式”是指将已经有的私有函数暴露为公有方法。当对对象进行操作时,所有功能代码都对这些操作很敏感,而你想尽量保护这些代码的时候很有用。(译注:指对来自外部的修改很敏感。)但同时,你又希望能提供一些功能的访问权限,因为它们会被用到。如果你把这些方法公开,就会使得它们不再健壮,你的API的使用者可能修改它们。在ECMAScript5中,你可以选择冻结一个对象,但在之前的版本中不可用。下面进入暴露模式(原来是由Christian Heilmann创造的模式,叫“暴露模块模式”)。 +“暴露模式”是指将已经有的私有函数暴露为公有方法,它在你希望尽量保护对象内的一些方法不被外部修改干扰的时候很有用。你希望能提供一些功能给外部访问,因为它们会被用到,如果你把这些方法公开,就会使得它们不再健壮,因为你的API的使用者可能修改它们。在ECMAScript5中,你可以选择冻结一个对象,但在之前的版本中这种方法不可用。下面进入暴露模式(原来是由Christian Heilmann创造的模式,叫“暴露模块模式”)。 我们来看一个例子,它建立在对象字面量的私有成员模式之上: @@ -375,7 +375,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 }()); -这里有两个私有变量和两个私有函数——`isArray()`和`indexOf()`。在包裹函数的最后,使用那些允许被从外部访问的函数填充`myarray`对象。在这个例子中,同一个私有函数 `indexOf()`同时被暴露为ECMAScript 5风格的`indexOf`和PHP风格的`inArry`。测试一下myarray对象: +这里有两个私有变量(私有函数)——`isArray()`和`indexOf()`。在包裹函数的最后,用那些允许被从外部访问的函数填充`myarray`对象。在这个例子中,同一个私有函数 `indexOf()`同时被暴露为ECMAScript5风格的`indexOf()`和PHP风格的`inArry()`。测试一下`myarray`对象: myarray.isArray([1,2]); // true myarray.isArray({0: 1}); // false From 34c134d82e38f2fb2f24b07534310d2d4a24280b Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 26 Apr 2013 16:13:17 +0800 Subject: [PATCH 092/145] =?UTF-8?q?=E6=A8=A1=E5=9D=97=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E7=AC=AC=E4=B8=80=E9=83=A8=E5=88=86=20=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 44 ++++++++++++++------------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 1d8abbc..92e2d47 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -394,7 +394,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 模块模式是我们目前讨论过的好几种模式的组合,即: - 命名空间模式 -- 立即执行的函数模式 +- 即时函数模式 - 私有和特权成员模式 - 依赖声明模式 @@ -402,7 +402,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 MYAPP.namespace('MYAPP.utilities.array'); -下一步是定义模块。使用一个立即执行的函数来提供私有作用域供私有成员使用。立即执行的函数返回一个对象,也就是带有公有接口的真正的模块,可以供其它代码使用: +下一步是定义模块。使用一个即时函数来提供私有作用域供私有成员使用。即时函数返回一个对象,也就是带有公有接口的真正的模块,可以供其它代码使用: MYAPP.utilities.array = (function () { return { @@ -423,40 +423,28 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 }; }()); -如果需要的话,你可以在立即执行的函数提供的闭包中声明私有属性和私有方法。函数顶部也是声明依赖的地方。在变量声明的下方,你可以选择性地放置辅助初始化模块的一次性代码。函数最终返回的是一个包含模块公共API的对象: +如果需要的话,你可以在即时函数提供的闭包中声明私有属性和私有方法。同样,声明依赖放置在函数顶部,在变量声明的下方可以选择性地放置辅助初始化模块的一次性代码。函数最终返回的是一个包含模块公共API的对象: MYAPP.namespace('MYAPP.utilities.array'); MYAPP.utilities.array = (function () { -<<<<<<< HEAD - // dependencies + // 声明依赖 var uobj = MYAPP.utilities.object, ulang = MYAPP.utilities.lang, - // private properties + // 私有属性 array_string = "[object Array]", ops = Object.prototype.toString; -======= - // dependencies - var uobj = MYAPP.utilities.object, - ulang = MYAPP.utilities.lang, ->>>>>>> 合并改动 - // private properties - array_string = "[object Array]", - ops = Object.prototype.toString; + // 私有方法 + // …… -<<<<<<< HEAD -======= - // private methods - // ... - // end var + // 结束变量声明 ->>>>>>> 合并改动 - // optionally one-time init procedures - // ... + // 选择性放置一次性初始化的代码 + // …… - // public API + // 公有API return { inArray: function (needle, haystack) { @@ -466,19 +454,15 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 } } }, -<<<<<<< HEAD - -======= - ->>>>>>> 合并改动 + isArray: function (a) { return ops.call(a) === array_string; } - // ... more methods and properties + // ……更多的方法和属性 }; }()); -模块模式被广泛使用,这是一种值得强烈推荐的模式,它可以帮助组织代码,尤其是代码量在不断增长的时候。 +模块模式被广泛使用,是一种值得强烈推荐的模式,它可以帮助我们组织代码,尤其是代码量在不断增长的时候。 ### 暴露模块模式 From 3a7d8afdce47273672ce51e3c22ec14a38d8c0cc Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 26 Apr 2013 16:20:51 +0800 Subject: [PATCH 093/145] =?UTF-8?q?=E6=A8=A1=E5=9D=97=E6=A8=A1=E5=BC=8F=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 55 ++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 92e2d47..65a97ec 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -472,25 +472,25 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 MYAPP.utilities.array = (function () { - // private properties + // 私有属性 var array_string = "[object Array]", ops = Object.prototype.toString, - // private methods - inArray = function (haystack, needle) { - for (var i = 0, max = haystack.length; i < max; i += 1) { - if (haystack[i] === needle) { - return i; + // 私有方法 + inArray = function (haystack, needle) { + for (var i = 0, max = haystack.length; i < max; i += 1) { + if (haystack[i] === needle) { + return i; + } } - } - return −1; - }, - isArray = function (a) { - return ops.call(a) === array_string; - }; - // end var + return −1; + }, + isArray = function (a) { + return ops.call(a) === array_string; + }; + // 结束变量定义 - // revealing public API + // 暴露公有API return { isArray: isArray, indexOf: inArray @@ -499,7 +499,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 ### 创建构造函数的模块 -前面的例子创建了一个对象`MYAPP.utilities.array`,但有时候使用构造函数来创建对象会更方便。你也可以同样使用模块模式来做。唯一的区别是包裹模块的立即执行的函数会在最后返回一个函数,而不是一个对象。 +前面的例子创建了一个对象`MYAPP.utilities.array`,但有时候使用构造函数来创建对象会更方便。你也可以同样使用模块模式来做。唯一的区别是包裹模块的即时函数会在最后返回一个函数,而不是一个对象。 看下面的模块模式的例子,创建了一个构造函数`MYAPP.utilities.Array`: @@ -507,23 +507,23 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 MYAPP.utilities.Array = (function () { - // dependencies + // 声明依赖 var uobj = MYAPP.utilities.object, ulang = MYAPP.utilities.lang, - // private properties and methods... - Constr; + // 私有属性和方法…… + Constr; - // end var + // 结束变量定义 - // optionally one-time init procedures - // ... + // 选择性放置一次性初始化代码 + // …… - // public API -- constructor + // 公有API——构造函数 Constr = function (o) { this.elements = this.toArray(o); }; - // public API -- prototype + // 公有API——原型 Constr.prototype = { constructor: MYAPP.utilities.Array, version: "2.0", @@ -535,8 +535,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 } }; - // return the constructor - // to be assigned to the new namespace + // 返回构造函数 return Constr; }()); @@ -547,13 +546,11 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 ### 在模块中引入全局上下文 -作为这种模式的一个常见的变种,你可以给包裹模块的立即执行的函数传递参数。你可以传递任何值,但通常会传递全局变量甚至是全局对象本身。引入全局上下文可以加快函数内部的全局变量的解析,因为引入之后会作为函数的本地变量: +作为这种模式的一个常见的变种,你可以给包裹模块的即时函数传递参数。你可以传递任何值,但通常情况下会传递全局变量甚至是全局对象本身。引入全局上下文可以加快函数内部的全局变量的解析,因为引入之后会作为函数的本地变量: MYAPP.utilities.module = (function (app, global) { - // references to the global object - // and to the global app namespace object - // are now localized + // 全局对象和全局命名空间都作为本地变量存在 }(MYAPP, this)); From 98ac65bd753bffba8e1503eab4e9e1672a5adce5 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 26 Apr 2013 16:31:26 +0800 Subject: [PATCH 094/145] =?UTF-8?q?=E6=B2=99=E7=AE=B1=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E4=BB=8B=E7=BB=8D=E5=92=8C=E4=BD=BF=E7=94=A8=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 65a97ec..7fd31b9 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -558,12 +558,12 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 沙箱模式主要着眼于命名空间模式的短处,即: -- 依赖一个全局变量成为应用的全局命名空间。在命名空间模式中,没有办法在同一个页面中运行同一个应用或者类库的不同版本,在为它们都会需要同一个全局变量名,比如`MYAPP`。 +- 依赖一个全局变量成为应用的全局命名空间。在命名空间模式中,没有办法在同一个页面中运行同一个应用或者类库的不同版本,因为它们都会需要同一个全局变量名,比如`MYAPP`。 - 代码中以点分隔的名字比较长,无论写代码还是解析都需要处理这个很长的名字,比如`MYAPP.utilities.array`。 顾名思义,沙箱模式为模块提供了一个环境,模块在这个环境中的任何行为都不会影响其它的模块和其它模块的沙箱。 -这个模式在YUI3中用得很多,但是需要记住的是,下面的讨论只是一些示例实现,并不讨论YUI3中的消息箱是如何实现的。 +这个模式在YUI3中用得很多,但是需要记住的是,下面的讨论只是一些示例实现,并不讨论YUI3中的沙箱是如何实现的。 ### 全局构造函数 @@ -572,19 +572,19 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 使用沙箱模式是像这样: new Sandbox(function (box) { - // your code here... + // 你的代码…… }); `box`对象和命名空间模式中的`MYAPP`类似,它包含了所有你的代码需要用到的功能。 我们要多做两件事情: -- 通过一些手段(第3章中的强制使用new的模式),你可以在创建对象的时候不要求一定有new。 +- 通过一些手段(第3章中的强制使用`new`的模式),你可以在创建对象的时候不要求一定有`new`。 - 让`Sandbox()`构造函数可以接受一个(或多个)额外的配置参数,用于指定这个对象需要用到的模块名字。我们希望代码是模块化的,因此绝大部分`Sandbox()`提供的功能都会被包含在模块中。 有了这两个额外的特性之后,我们来看一下实例化对象的代码是什么样子。 -你可以在创建对象时省略`new`并像这样使用已有的“ajax”和“event”模块: +你可以在创建对象时省略`new`并像这样使用已有的`ajax`和`event`模块: Sandbox(['ajax', 'event'], function (box) { // console.log(box); @@ -596,7 +596,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 // console.log(box); }); -使用通配符“*”来表示“使用所有可用的模块”如何?为了方便,我们也假设没有任何模块传入时,沙箱使用“*”。所以有两种使用所有可用模块的方法: +使用通配符“*”来表示“使用所有可用的模块”是个不错的想法,为了方便,我们也假设没有任何模块传入时,沙箱使用“*”。所以有两种使用所有可用模块的方法: Sandbox('*', function (box) { // console.log(box); @@ -606,23 +606,22 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 // console.log(box); }); -下面的例子展示了如何实例化多个消息箱对象,你甚至可以将它们嵌套起来而互不影响: +下面的例子展示了如何实例化多个沙箱对象,你甚至可以将它们嵌套起来而互不影响: Sandbox('dom', 'event', function (box) { - // work with dom and event + // 使用dom和event模块 Sandbox('ajax', function (box) { - // another sandboxed "box" object - // this "box" is not the same as - // the "box" outside this function + // 另一个沙箱中的box,这个box和外面的box不一样 //... - // done with Ajax + // 使用ajax模块的代码到此为止 + }); - // no trace of Ajax module here + // 这里的代码与ajax模块无关 }); 从这些例子中看到,使用沙箱模式可以通过将代码包裹在回调函数中的方式来保护全局命名空间。 From c030d6657116c055d601efed47fc999d9fad8698 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 26 Apr 2013 16:39:52 +0800 Subject: [PATCH 095/145] =?UTF-8?q?=E6=B2=99=E7=AE=B1=E6=A8=A1=E5=BC=8F=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 7fd31b9..4a8c4ed 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -5,7 +5,7 @@ JavaScript语言本身很简单、直观,也没有其他语言的一些语言特性:命名空间、模块、包、私有属性以及静态成员。本章将介绍一些常用的模式,以此实现这些语言特性。 我们将对命名空间、依赖声明、模块模式以及沙箱模式进行初探——它们可以帮助我们更好地组织应用程序的代码,有效地减少全局污染的问题。除此之外,还会讨论私有和特权成员、静态和私有静态成员、对象常量、链式调用以及一种像类式语言一样定义构造函数的方法等话题。 - +/Users/TooBug/github/javascript.patterns/chapter5.markdown ## 命名空间模式 使用命名空间可以减少全局变量的数量,与此同时,还能有效地避免命名冲突和前缀的滥用。 @@ -634,7 +634,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 ### 添加模块 -在动手实现构造函数之前,我们来看一下如何添加模块。 +在动手实现构造函数之前,我们先来看一下如何添加模块。 `Sandbox()`构造函数也是一个对象,所以可以给它添加一个`modules`静态属性。这个属性也是一个包含名值(key-value)对的对象,其中key是模块的名字,value是模块的功能实现。 @@ -647,7 +647,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 }; Sandbox.modules.event = function (box) { - // access to the Sandbox prototype if needed: + // 如果有需要的话可以访问Sandbox的原型 // box.constructor.prototype.m = "mmm"; box.attachEvent = function () {}; box.dettachEvent = function () {}; @@ -658,34 +658,33 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 box.getResponse = function () {}; }; -在这个例子中我们添加了`dom`、`event`和`ajax`模块,这些都是在每个类库或者复杂的web应用中很常见的代码片段。 +在这个例子中我们添加了`dom`、`event`和`ajax`模块,这些模块在每个类库或者复杂的web应用中都很常见。 -实现每个模块功能的函数接受一个实例`box`作为参数,并给这个实例添加属性和方法。 +每个模块功能函数接受一个实例`box`作为参数,并给这个实例添加属性和方法。 ### 实现构造函数 最后,我们来实现`Sandbox()`构造函数(你可能会很自然地想将这类构造函数命名为对你的类库或者应用有意义的名字): function Sandbox() { - // turning arguments into an array + // 将参数转换为数组 var args = Array.prototype.slice.call(arguments), - // the last argument is the callback + // 最后一个参数是回调函数 callback = args.pop(), - // modules can be passed as an array or as individual parameters + // 参数可以作为数组或者单独的参数传递 modules = (args[0] && typeof args[0] === "string") ? args : args[0], i; - // make sure the function is called - // as a constructor + // 保证函数是作为构造函数被调用 if (!(this instanceof Sandbox)) { return new Sandbox(modules, callback); } - // add properties to `this` as needed: + // 根据需要给this添加属性 this.a = 1; this.b = 2; - // now add modules to the core `this` object - // no modules or "*" both mean "use all modules" + // 给this对象添加模块 + // 未指明模块或者*都表示“使用所有模块” if (!modules || modules === '*') { modules = []; for (i in Sandbox.modules) { @@ -695,16 +694,16 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 } } - // initialize the required modules + // 初始化指定的模块 for (i = 0; i < modules.length; i += 1) { Sandbox.modules[modules[i]](this); } - // call the callback + // 调用回调函数 callback(this); } - // any prototype properties as needed + // 需要添加在原型上的属性 Sandbox.prototype = { name: "My Application", version: "1.0", @@ -715,7 +714,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 这个实现中的一些关键点: -- 有一个检查`this`是否是`Sandbox`实例的过程,如果不是(也就是调用`Sandbox()`时没有加`new`),我们将这个函数作为构造函数再调用一次。 +- 有一个检查`this`是否是`Sandbox()`实例的过程,如果不是(也就是调用`Sandbox()`时没有加`new`),我们将这个函数作为构造函数再调用一次。 - 你可以在构造函数中给`this`添加属性,也可以给构造函数的原型添加属性。 - 被依赖的模块可以以数组的形式传递,也可以作为单独的参数传递,甚至以`*`通配符(或者省略)来表示加载所有可用的模块。值得注意的是,我们在这个示例实现中并没有考虑从外部文件中加载模块,但明显这是一个值得考虑的事情。比如YUI3就支持这种情况,你可以只加载最基本的模块(作为“种子”),其余需要的任何模块都通过将模块名和文件名对应的方式从外部文件中加载。 - 当我们知道依赖的模块之后就初始化它们,也就是调用实现每个模块的函数。 From ef8ce2d14f2fae74a2d253b7c6bbd338de35fc28 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 26 Apr 2013 16:57:59 +0800 Subject: [PATCH 096/145] =?UTF-8?q?=E5=85=AC=E6=9C=89=E9=9D=99=E6=80=81?= =?UTF-8?q?=E6=88=90=E5=91=98=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 4a8c4ed..ffca775 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -722,33 +722,33 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 ## 静态成员 -静态属性和方法是指那些在所有的实例中保持一致的成员。在基于类的语言中,静态成员是用专门的语法来创建,使用时就像是类自己的成员一样。比如`MathUtils`类的`max()`方法会被像这样调用:`MathUtils.max(3, 5)`。这是一个公有静态成员的示例,即可以在不实例化类的情况下使用。同样也可以有私有的静态方法,即对类的使用者不可见,而在类的所有实例间是共享的。我们来看一下如何在JavaScript中实现公有和私有静态成员。 +静态属性和方法是指那些在所有的实例中都一样的成员。在基于类的语言中,静态成员是用专门的语法来创建,使用时就像是类自己的成员一样。比如`MathUtils`类的`max()`方法会被像这样调用:`MathUtils.max(3, 5)`。这是一个公有静态成员的示例,即可以在不实例化类的情况下使用。同样也可以有私有的静态方法,即对类的使用者不可见,而在类的所有实例间是共享的。我们来看一下如何在JavaScript中实现公有和私有静态成员。 ### 公有静态成员 -在JavaScript中没有专门用于静态成员的语法。但通过给构造函数添加属性的方法,可以拥有和基于类的语言一样的使用语法。之所以可以这样做是因为构造函数和其它的函数一样,也是对象,可以拥有属性。前一章讨论过的Memoization模式也使用了同样的方法,即给函数添加属性。 +在JavaScript中没有专门用于静态成员的语法。但通过给构造函数添加属性的方法,可以拥有和基于类的语言一样的使用语法。之所以可以这样做是因为构造函数和其它的函数一样,也是对象,可以拥有属性。前一章讨论过的记忆模式也使用了同样的方法,即给函数添加属性。 -下面的例子定义了一个构造函数`Gadget`,它有一个静态方法`isShiny()`和一个实例方法`setPrice()`。`isShiny()`是一个静态方法,因为它不需要指定一个对象才能工作(就像你不需要先指定一个工具(gadget)才知道所有的工具是不是有光泽的(shiny))。但setPrice()却需要一个对象,因为工具可能有不同的定价: +下面的例子定义了一个构造函数`Gadget()`,它有一个静态方法`isShiny()`和一个实例方法`setPrice()`。`isShiny()`是一个静态方法,因为它不需要指定一个具体的对象就能工作(你不需要先拿到一个特定的小工具(gadget)才知道所有小工具是不是有光泽的(shiny))。但setPrice()却需要一个对象,因为小工具可能有不同的定价: - // constructor + // 构造函数 var Gadget = function () {}; - // a static method + // 静态方法 Gadget.isShiny = function () { return "you bet"; }; - // a normal method added to the prototype + // 添加到原型的普通方法 Gadget.prototype.setPrice = function (price) { this.price = price; }; -现在我们来调用这些方法。静态方法`isShiny()`可以直接在构造函数上调用,但其它的常规方法需要一个实例: +现在我们来调用这些方法。静态方法`isShiny()`可以直接在构造函数上调用,但其它的方法需要一个实例: - // calling a static method + // 调用静态方法 Gadget.isShiny(); // "you bet" - // creating an instance and calling a method + // 创建实例并调用方法 var iphone = new Gadget(); iphone.setPrice(500); @@ -764,28 +764,28 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 在这种情况下,你需要很小心地处理静态方法内的`this`。当你运行`Gadget.isShiny()`时,在`isShiny()`内部的`this`指向`Gadget`构造函数。而如果你运行`iphone.isShiny()`,那么`this`会指向`iphone`。 -最后一个例子展示了同一个方法被静态调用和非静态调用时明显不同的行为,这取决于调用的方式。这里的`instanceof`用于获方法是如何被调用的: +下面的例子展示了同一个方法被静态调用和非静态调用时明显不同的行为,这取决于调用的方式。这里的`instanceof`用于获取方法是如何被调用的: - // constructor + // 构造函数 var Gadget = function (price) { this.price = price; }; - // a static method + // 静态方法 Gadget.isShiny = function () { - // this always works + // 这句始终正常工作 var msg = "you bet"; if (this instanceof Gadget) { - // this only works if called non-statically + // 这句只有在非静态方式调用时正常工作 msg += ", it costs $" + this.price + '!'; } return msg; }; - // a normal method added to the prototype + // 原型上添加的方法 Gadget.prototype.isShiny = function () { return Gadget.isShiny.call(this); }; From b04ff343e19c93f13f0fc47fc1242ac75c962453 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 26 Apr 2013 17:04:49 +0800 Subject: [PATCH 097/145] =?UTF-8?q?=E7=A7=81=E6=9C=89=E9=9D=99=E6=80=81?= =?UTF-8?q?=E6=88=90=E5=91=98=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index ffca775..9e1c1ed 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -806,51 +806,49 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 - 被所有由同一构造函数创建的对象共享 - 不允许在构造函数外部访问 -我们来看一个例子,`counter`是`Gadget`构造函数的一个私有静态属性。在本章中我们已经讨论过私有属性,这里的做法也是一样,需要一个函数提供的闭包来包裹私有成员。然后让这个包裹函数立即执行并返回一个新的函数。将这个返回的函数赋值给`Gadget`作为构造函数。 +我们来看一个例子,`counter`是`Gadget()`构造函数的一个私有静态属性。在本章中我们已经讨论过私有属性,这里的做法也是一样,需要一个函数提供的闭包来包裹私有成员。然后让这个包裹函数立即执行并返回一个新的函数。将这个返回的函数赋值给`Gadget()`作为构造函数。 var Gadget = (function () { - // static variable/property + // 静态变量/属性 var counter = 0; - // returning the new implementation - // of the constructor + // 返回构造函数的新实现 return function () { console.log(counter += 1); }; - }()); // execute immediately + }()); // 立即执行 -这个`Gadget`构造函数只简单地增加私有的`counter`的值然后打印出来。用多个实例测试的话你会看到`counter`在实例之间是共享的: +这个`Gadget()`构造函数只简单地增加私有变量`counter`的值然后打印出来。用多个实例测试的话你会看到`counter`在实例之间是共享的: var g1 = new Gadget();// logs 1 var g2 = new Gadget();// logs 2 var g3 = new Gadget();// logs 3 -因为我们在创建每个实例的时候`counter`的值都会加1,所以它实际上成了唯一标识使用`Gadget`构造函数创建的对象的ID。这个唯一标识可能会很有用,那为什么不把它通用一个特权方法暴露出去呢?(译注:其实这里不能叫ID,只是一个记录有多少个实例的数字而已,因为如果有多个实例被创建的话,其实已经没办法取到前面实例的标识了。)下面的例子是基于前面的例子,增加了用于访问私有静态属性的`getLastId()`方法: +因为我们在创建每个实例的时候`counter`的值都会加1,所以它实际上成了唯一标识使用`Gadget`构造函数创建的对象的ID。这个唯一标识可能会很有用,那为什么不把它通过一个特权方法暴露出去呢?(译注:严格来讲,这里不能叫ID,只是一个记录有多少个实例的数字而已,因为如果有多个实例被创建的话,没有办法取到除了最后一个之外的实例的标识。)下面的例子是基于前面的例子,增加了用于访问私有静态属性的`getLastId()`方法: - // constructor + // 构造函数 var Gadget = (function () { - // static variable/property + // 静态变量/属性 var counter = 0, NewGadget; - // this will become the - // new constructor implementation + // 这将是Gadget的新实现 NewGadget = function () { counter += 1; }; - // a privileged method + // 特权方法 NewGadget.prototype.getLastId = function () { return counter; }; - // overwrite the constructor + // 重写构造函数 return NewGadget; - }()); // execute immediately + }()); // 立即执行 测试这个新的实现: From 4f4f7f622f05255c1b4ff4cf93cc9bf042fb53db Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 26 Apr 2013 17:18:21 +0800 Subject: [PATCH 098/145] =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E5=B8=B8=E9=87=8F=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 9e1c1ed..150b50b 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -863,7 +863,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 ## 对象常量 -JavaScript中是没有常量的,尽管在一些比较现代的环境中可能会提供`const`来创建常量。 +在一些比较现代的环境中可能会提供`const`来创建常量,但在其它的环境中,JavaScript是没有常量的。 一种常用的解决办法是通过命名规范,让不应该变化的变量使用全大写。这个规范实际上也用在JavaScript原生对象中: @@ -873,32 +873,34 @@ JavaScript中是没有常量的,尽管在一些比较现代的环境中可能 你自己的常量也可以用这种规范,然后将它们作为静态属性加到构造函数中: - // constructor + // 构造函数 var Widget = function () { - // implementation... + // 实现…… }; - // constants + // 常量 Widget.MAX_HEIGHT = 320; Widget.MAX_WIDTH = 480; -同样的规范也适用于使用字面量创建的对象,常量会是使用大写名字的普通名字。 +同样的规范也适用于使用字面量创建的对象,常量会是使用大写名字的属性。 如果你真的希望有一个不能被改变的值,那么可以创建一个私有属性,然后提供一个取值的方法(getter),但不给赋值的方法(setter)。这种方法在很多可以用命名规范解决的情况下可能有些矫枉过正,但不失为一种选择。 -下面是一个通过的`constant`对象的实现,它提供了这些方法: +下面是一个通用的`constant`对象的实现,它提供了这些方法: - set(name, value) 定义一个新的常量 + - isDefined(name) 检查一个常量是否存在 + - get(name) 取常量的值 -在这个实现中,只允许基本类型的值成为常量。同时还要使用`hasOwnproperty()`小心地处理那些恰好是原生属性的常量名,比如`toString`或者`hasOwnProperty`,然后给所有的常量名加上一个随机生成的前缀: +在这个实现中,只允许基本类型的值成为常量。同时还要使用`hasOwnProperty()`小心地处理那些恰好是原生属性的常量名,比如`toString`或者`hasOwnProperty`,然后给所有的常量名加上一个随机生成的前缀: var constant = (function () { var constants = {}, @@ -934,19 +936,19 @@ JavaScript中是没有常量的,尽管在一些比较现代的环境中可能 测试这个实现: - // check if defined + // 检查是否定义 constant.isDefined("maxwidth"); // false - // define + // 定义 constant.set("maxwidth", 480); // true - // check again + // 再次检查 constant.isDefined("maxwidth"); // true - // attempt to redefine + // 尝试重定义 constant.set("maxwidth", 320); // false - // is the value still intact? + // 看看这个值是否被改变 constant.get("maxwidth"); // 480 ## 链式调用模式 From 0bf54df8689ad1606c60ed82e47b6439fc7345d6 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 26 Apr 2013 17:21:27 +0800 Subject: [PATCH 099/145] =?UTF-8?q?=20=E9=93=BE=E5=BC=8F=E8=B0=83=E7=94=A8?= =?UTF-8?q?=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 150b50b..6888af2 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -957,7 +957,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 myobj.method1("hello").method2().method3("world").method4(); -当你创建了一个没有有意义的返回值的方法时,你可以让它返回this,也就是这些方法所属的对象。这使得对象的使用者可以将下一个方法的调用和前一次调用链起来: +当你创建了一个没有有意义的返回值的方法时,你可以让它返回`this`,也就是这些方法所属的对象。这使得对象的使用者可以将下一个方法的调用和前一次调用链起来: var obj = { value: 1, @@ -974,10 +974,10 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 } }; - // chain method calls + // 链式方法调用 obj.increment().add(3).shout(); // 5 - // as opposed to calling them one by one + // 单独调用每个方法 obj.increment(); obj.add(3); obj.shout(); // 5 @@ -988,7 +988,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 另外一个好处就是帮助你思考如何拆分你的函数,创建更小、更有针对性的函数,而不是一个什么都做的函数。长时间来看,这会提升代码的可维护性。 -一个弊端是调用这样写的代码会更困难。你可能知道一个错误出现在某一行,但这一行要做很多的事情。当链式调用的方法中的某一个出现问题而又没报错时,你无法知晓到底是哪一个出问题了。《代码整洁之道》的作者Robert Martion甚至叫这种模式为“train wreck”模式。(译注:直译为“火车事故”,指负面影响比较大。) +一个弊端是调试这样写的代码会更困难。你可能知道一个错误出现在某一行,但这一行要做很多的事情。当链式调用的方法中的某一个出现问题而又没报错时,你无法知晓到底是哪一个出问题了。《代码整洁之道》的作者Robert Martion甚至叫这种模式为“train wreck”模式。(译注:直译为“火车事故”,指负面影响比较大。) 不管怎样,认识这种模式总是好的,当你写的方法没有明显的有意义的返回值时,你就可以返回`this`。这个模式应用得很广泛,比如jQuery库。如果你去看DOM的API的话,你会发现它也会以这样的形式倾向于链式调用: From a2b8d044eca606aac85a523906a3723eef274f67 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 26 Apr 2013 17:29:07 +0800 Subject: [PATCH 100/145] =?UTF-8?q?=E7=AC=AC=E4=BA=94=E7=AB=A0=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95=20close=20#6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter5.markdown | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/chapter5.markdown b/chapter5.markdown index 6888af2..3844d79 100644 --- a/chapter5.markdown +++ b/chapter5.markdown @@ -125,7 +125,7 @@ JavaScript没有原生的命名空间语法,但很容易可以实现这个特 图5-1 MYAPP命名空间在Firebug下的可视化结果 -## 声明依赖 +## 依赖声明 JavaScript库往往是模块化而且有用到命名空间的,这使得你可以只使用你需要的模块。比如在YUI2中,全局变量`YAHOO`就是一个命名空间,各个模块都是全局变量的属性,比如`YAHOO.util.Dom`(DOM模块)、`YAHOO.util.Event`(事件模块)。 @@ -141,7 +141,7 @@ JavaScript库往往是模块化而且有用到命名空间的,这使得你可 这是一个相当简单的模式,但是有很多的好处: -- 明确的声明依赖是告知使用你代码的开发者,需要保证指定的脚本文件被包含在页面中。 +- 明确的依赖声明是告知使用你代码的开发者,需要保证指定的脚本文件被包含在页面中。 - 将声明放在函数顶部使得依赖很容易被查找和解析。 - 本地变量(如`dom`)永远会比全局变量(如`YAHOO`)要快,甚至比全局变量的属性(如`YAHOO.util.Dom`)还要快,这样会有更好的性能。使用了依赖声明模式之后,全局变量的解析在函数中只会进行一次,在此之后将会使用更快的本地变量。 - 一些高级的代码压缩工具比如YUI Compressor和Google Closure compiler会重命名本地变量(比如`event`可能会被压缩成一个字母,如`A`),这会使代码更精简,但这个操作不会对全局变量进行,因为这样做不安全。 @@ -423,12 +423,12 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 }; }()); -如果需要的话,你可以在即时函数提供的闭包中声明私有属性和私有方法。同样,声明依赖放置在函数顶部,在变量声明的下方可以选择性地放置辅助初始化模块的一次性代码。函数最终返回的是一个包含模块公共API的对象: +如果需要的话,你可以在即时函数提供的闭包中声明私有属性和私有方法。同样,依赖声明放置在函数顶部,在变量声明的下方可以选择性地放置辅助初始化模块的一次性代码。函数最终返回的是一个包含模块公共API的对象: MYAPP.namespace('MYAPP.utilities.array'); MYAPP.utilities.array = (function () { - // 声明依赖 + // 依赖声明 var uobj = MYAPP.utilities.object, ulang = MYAPP.utilities.lang, @@ -507,7 +507,7 @@ JavaScript不像Java或者其它语言,它没有专门的提供私有、保护 MYAPP.utilities.Array = (function () { - // 声明依赖 + // 依赖声明 var uobj = MYAPP.utilities.object, ulang = MYAPP.utilities.lang, @@ -1022,7 +1022,7 @@ JavaScript对于习惯于用类来思考的人来说可能会比较费解,这 - 新方法的名字 - 新方法的实现 -然后这个新方法被添加到`Person`“类”。新方法的实现也只是一个函数,在这个函数里面`this`指向由`Person`创建的对象,正如我们期望的那样。 +然后这个新方法被添加到`Person`“类”。新方法的实现也只是一个函数,在这个函数里面`this`指向由`Person()`创建的对象,正如我们期望的那样。 下面是使用`Person()`创建和使用新对象的代码: @@ -1041,17 +1041,14 @@ JavaScript对于习惯于用类来思考的人来说可能会比较费解,这 }; } -在`method()`的实现中,我们首先检查这个方法是否已经被实现过,如果没有则继续,将传入的参数`implementation`加到构造函数的原型中。在这里`this`指向构造函数,而我们要增加的功能正在在这个构造函数的原型上。 +在`method()`的实现中,我们首先检查这个方法是否已经被实现过,如果没有则继续,将传入的参数`implementation`加到构造函数的原型中。在这里`this`指向构造函数,而我们要增加的功能正好在这个构造函数的原型上。 ## 小结 在本章中你看到了好几种除了字面量和构造函数之外的创建对象的方法。 -你看到了使用命名空间模式来保持全局空间干净和帮助组织代码。看到了简单而又有用的依赖声明模式。然后我们详细讨论了有关私有成员的模式,包括私有成员、特权方法以及一些涉及私有成员的极端情况,还有使用对象字面量创建私有成员以及将私有方法暴露为公有方法。所有这些模式都是搭建起现在流行而强大的模块模式的积木。 +你看到了使用命名空间模式来保持全局空间干净和帮助组织代码,看到了简单而又有用的依赖声明模式。然后我们详细讨论了有关私有成员的模式,包括私有成员、特权方法以及一些涉及私有成员的极端情况,还有使用对象字面量创建私有成员以及将私有方法暴露为公有方法。所有这些模式都是搭建起现在流行而强大的模块模式的积木。 然后你看到了使用沙箱模式作为长命名空间的另一种选择,它可以为你的代码和模块提供独立的环境。 -在最后,我们深入讨论了对象常量、静态成员(公有和私有)、链式调用模式,以及神奇的`method()`方法。 - - - +在最后,我们深入讨论了对象常量、静态成员(公有和私有)、链式调用模式,以及神奇的`method()`方法。 \ No newline at end of file From 4f22d6dd973955b970593bc5c279e1c8fff9e7eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=92=B1=E5=BC=A0=E7=9B=9B?= Date: Sat, 27 Apr 2013 16:58:23 +0800 Subject: [PATCH 101/145] Update chapter3.markdown --- chapter3.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter3.markdown b/chapter3.markdown index e9963f0..68a245a 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -450,7 +450,7 @@ JavaScript中的正则表达式也是对象,可以通过两种方式创建它 var re = /pattern/gmi; -使用正则表达式字面量可以让代码更加简洁高效,比如当调用`String.prototype.prelace()`方法时,可以传入正则表达式参数: +使用正则表达式字面量可以让代码更加简洁高效,比如当调用`String.prototype.replace()`方法时,可以传入正则表达式参数: var no_letters = "abc123XYZ".replace(/[a-z]/gi, ""); console.log(no_letters); // 123 From 71b5bc8998e65c72344a11ea053a05bbd858eaa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=92=B1=E5=BC=A0=E7=9B=9B?= Date: Sat, 27 Apr 2013 16:59:21 +0800 Subject: [PATCH 102/145] Update chapter3.markdown --- chapter3.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapter3.markdown b/chapter3.markdown index e9963f0..68a245a 100644 --- a/chapter3.markdown +++ b/chapter3.markdown @@ -450,7 +450,7 @@ JavaScript中的正则表达式也是对象,可以通过两种方式创建它 var re = /pattern/gmi; -使用正则表达式字面量可以让代码更加简洁高效,比如当调用`String.prototype.prelace()`方法时,可以传入正则表达式参数: +使用正则表达式字面量可以让代码更加简洁高效,比如当调用`String.prototype.replace()`方法时,可以传入正则表达式参数: var no_letters = "abc123XYZ".replace(/[a-z]/gi, ""); console.log(no_letters); // 123 From 9754e336051193b1a565f196ac9ec54f0d17c8ad Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 3 May 2013 12:49:44 +0800 Subject: [PATCH 103/145] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF1?= =?UTF-8?q?=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 54 +++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/chapter6.markdown b/chapter6.markdown index e09321f..91fe61d 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -1,20 +1,18 @@ - # 代码复用模式 -代码复用是一个既重要又有趣的话题,因为努力在自己或者别人写的代码上写尽量少且可以复用的代码是件很自然的事情,尤其当这些代码是经过测试的、可维护的、可扩展的、有文档的时候。 +代码复用是一个既重要又有趣的话题。如果你面对自己或者别人已经写好的代码,而这些代码又是经过测试的、可维护的、可扩展的、有文档的,这时候你只想写尽量少且可以被复用的代码就是一个再自然不过的想法。 -当我们说到代码复用的时候,想到的第一件事就是继承,本章会有很大篇幅讲述这个话题。你将看到好多种方法来实现“类式(classical)”和一些其它方式的继承。但是,最最重要的事情,是你需要记住终极目标——代码复用。继承是达到这个目标的一种方法,但是不是唯一的。在本章,你将看到怎样基于其它对象来构建新对象,怎样使用混元,以及怎样在不使用继承的情况下只复用你需要的功能。 +当我们说到代码复用的时候,想到的第一件事就是继承,本章会有很大篇幅讲述这个话题,你将看到好多种方法来实现“类式(classical)”和一些其它方式的继承。但是,最最重要的事情,是你需要记住终极目标——代码复用。继承是达到这个目标的一种方法,但是不是唯一的。在本章,你将看到怎样基于其它对象来构建新对象,怎样使用混元,以及怎样在不使用继承的情况下只复用你需要的功能。 -在做代码复用的工作的时候,谨记Gang of Four 在书中给出的关于对象创建的建议:“优先使用对象创建而不是类继承”。(译注:《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software)是一本设计模式的经典书籍,该书作者为Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides,被称为“Gang of Four”,简称“GoF”。) +在做代码复用的工作的时候,谨记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的对象仅仅是简单的键值对,这些键值对都可以动态创建或者是改变。 +很多编程语言都有原生的类的概念,以此作为对象的蓝本。在这些语言中,每个对象都是一个指定类的实例(instance),并且(以Java为例)一个对象不能在不存在对应的类的情况下存在。在JavaScript中,因为没有类,所以类的实例的概念没什么意义。JavaScript的对象仅仅是简单的键值对,这些键值对都可以动态创建或者是改变。 -但是JavaScript拥有构造函数(constructor functions),并且有语法和使用类非常相似的new运算符。 +但是JavaScript拥有构造函数(constructor functions),并且有语法和使用类非常相似的`new`运算符。 在Java中你可能会这样写: @@ -24,22 +22,21 @@ var adam = new Person(); -除了Java是强类型语言需要给adam添加类型Person外,其它的语法看起来是一样的。JavaScript的创建函数调用看起来感觉Person是一个类,但事实上,Person仅仅是一个函数。语法上的相似使得非常多的开发者陷入对JavaScript类的思考,并且给出了很多模拟类的继承方案。这样的实现方式,我们叫它“类式继承”。顺便也提一下,所谓“现代”继承模式是指那些不需要你去想类这个概念的模式。 +除了Java是强类型语言需要给`adam`添加类型`Person`外,其它的语法看起来是一样的。JavaScript的构造函数调用方式看起来让人感觉`Person()`是一个类,但事实上,`Person()`仅仅是一个函数。语法上的相似使得非常多的开发者陷入对JavaScript类的思考,并且给出了很多模拟类的继承方案。这样的实现方式,我们叫它“类式继承”。顺便也提一下,所谓“现代”继承模式是指那些不需要你去想类这个概念的模式。 当需要给项目选择一个继承模式时,有不少的备选方案。你应该尽量选择那些现代继承模式,除非团队已经觉得“无类不欢”。 本章先讨论类式继承,然后再关注现代继承模式。 - ## 类式继承的期望结果 -实现类式继承的目标是基于构造函数Child()来创建一个对象,然后从另一个构造函数Parent()获得属性。 +实现类式继承的目标是基于构造函数`Child()`来创建一个对象,然后从另一个构造函数`Parent()`获得属性。 > 尽管我们是在讨论类式继承,但还是尽量避免使用“类”这个词。“构造函数”或者“constructor”虽然更长,但是更准确,不会让人迷惑。通常情况下,应该努力避免在跟团队沟通的时候使用“类”这个词,因为在JavaScript中,很可能每个人都会有不同的理解。 -下面是定义两个构造函数Parent()和Child()的例子: +下面是定义两个构造函数`Parent()`和`Child()`的例子: - //parent构造函数 + //Parent构造函数 function Parent(name) { this.name = name || 'Adam'; } @@ -49,52 +46,50 @@ return this.name; }; - //空的child构造函数 + //空的Child构造函数 function Child(name) {} //继承 inherit(Child, Parent); -上面的代码定义了两个构造函数Parent()和Child(),say()方法被添加到了Parent()构建函数的原型(prototype)中,inherit()函数完成了继承的工作。inherit()函数并不是原生提供的,需要自己实现。让我们来看一看比较大众的实现它的几种方法。 +上面的代码定义了两个构造函数`Parent()`和`Child()`,`say()`方法被添加到了`Parent()`构建函数的原型(`prototype`)中,`inherit()`函数完成了继承的工作。`inherit()`函数并不是原生提供的,需要自己实现。让我们来看一看比较常见的实现它的几种方法。 - ## 类式继承1——默认模式 -最常用的一种模式是使用Parent()构造函数来创建一个对象,然后把这个对象设为Child()的原型。这是可复用的inherit()函数的第一种实现方法: +最常用的一种模式是使用`Parent()`构造函数来创建一个对象,然后把这个对象设为`Child()`的原型。这是可复用的`inherit()`函数的第一种实现方法: function inherit(C, P) { C.prototype = new P(); } -需要强调的是原型(prototype属性)应该指向一个对象,而不是函数,所以它需要指向由父构造函数创建的实例(对象),而不是构造函数自己。换句话说,请注意new运算符,有了它这种模式才可以正常工作。 +需要强调的是原型(`prototype`属性)应该指向一个对象,而不是函数,所以它需要指向由被继承的构造函数创建的实例(对象),而不是构造函数自己。换句话说,请注意`new`运算符,有了它这种模式才可以正常工作。 -之后在应用中使用new Child()创建对象的时候,它将通过原型拥有Parent()实例的功能,像下面的例子一样: +之后在应用中使用`new Child()`创建对象的时候,它将通过原型拥有`Parent()`实例的功能,像下面的例子一样: var kid = new Child(); kid.say(); // "Adam" - ### 跟踪原型链 -在这种模式中,子对象既继承了(父对象)“自己的属性”(添加给this的实例属性,比如name),也继承了原型中的属性和方法(比如say())。 +在这种模式中,子对象既继承了(父对象的)“自有属性”(添加给`this`的实例属性,比如`name`),也继承了原型中的属性和方法(比如`say()`)。 -我们来看一下在这种继承模式中原型链是怎么工作的。为了讨论方便,我们假设对象是内存中的一块空间,它包含数据和指向其它空间的引用。当使用new Parent()创建一个对象时,这样的一块空间就被分配了(图6-1中的2号)。它保存着name属性的数据。如果你尝试访问say()方法(比如通过(new Parent).say()),2号空间中并没有这个方法。但是在通过隐藏的链接__proto__指向Parent()构建函数的原型prototype属性时,就可以访问到包含say()方法的1号空间(Parent.prototype)了。所有的这一块都是在幕后发生的,不需要任何额外的操作,但是知道它是怎样工作的以及你正在访问或者修正的数据在哪是很重要的。注意,__proto__在这里只是为了解释原型链,这个属性在语言本身中是不可用的,尽管有一些环境提供了(比如Firefox)。 +我们来看一下在这种继承模式中原型链是怎么工作的。为了讨论方便,我们假设对象是内存中的一块空间,它包含数据和指向其它空间的引用。当使用`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。 +现在我们来看一下在使用`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()对象。 +`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”。 +现在使用`kid.say()`时会发生什么?3号对象没有这个方法,所以通过原型链找到2号。2号对象也没有这个方法,所以也通过原型链找到1号,刚好有这个方法。接下来`say()`方法引用了`this.name`,这个变量也需要解析,于是沿原型链查找的过程又走了一遍。在这个例子中,`this`指向3号对象,它没有`name`属性,然后2号对象被访问,并且有`name`属性,值为“Adam”。 -最后,我们多看一点东西,假如我们有如下的代码: +最后,我们看一点额外的东西,假如我们有如下的代码: var kid = new Child(); kid.name = "Patrick"; @@ -106,18 +101,17 @@ Child()构造函数是空的,也没有属性添加到Child.prototype上,这 图6-3 继承并且给子对象添加属性后的原型链 -设定kid.name并没有改变2号对象的name属性,但是它直接在3号对象上添加了自己的name属性。当kid.say()执行时,say方法在3号对象中找,然后是2号,最后到1号,像前面说的一样。但是这一次在找this.name(和kid.name一样)时很快,因为这个属性在3号对象中就被找到了。 +设定`kid.name`并没有改变2号对象的`name`属性,但是却直接在3号对象上添加了自有的`name`属性。当`kid.say()`执行时,`say()`方法会依次在3号对象中找,然后是2号,最后到1号,像前面说的一样。但是这一次在找`this.name`(和`kid.name`一样)时很快,因为这个属性在3号对象中就被找到了。 -如果通过delete kid.name的方式移除新添加的属性,那么2号对象的name属性将暴露出来并且在查找的时候被找到。 +如果通过`delete kid.name`的方式移除新添加的属性,那么2号对象的`name`属性就将被暴露出来并且在查找的时候被找到。 - ### 这种模式的缺点 -这种模式的一个缺点是既继承了(父对象)“自己的属性”,也继承了原型中的属性。大部分情况下你可能并不需要“自己的属性”,因为它们更可能是为实例对象添加的,并不用于复用。 +这种模式的一个缺点是既继承了(父对象的)“自有属性”,也继承了原型中的属性。大部分情况下你可能并不需要“自有属性”,因为它们更可能是为实例对象添加的,并不用于复用。 > 一个在构造函数上常用的规则是,用于复用的成员(译注:属性和方法)应该被添加到原型上。 -在使用这个inherit()函数时另外一个不便是它不能够让你传参数给子构造函数,这些参数有可能是想再传给父构造函数的。考虑下面的例子: +在使用这个`inherit()`函数时另外一个不便是它不能够让你传参数给子构造函数,这些参数有可能是想再传给父构造函数的。考虑下面的例子: var s = new Child('Seth'); s.say(); // "Adam" From 7501f536f62e9e2090ca5e7f58f057c2a655aeb8 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 3 May 2013 12:57:26 +0800 Subject: [PATCH 104/145] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF2?= =?UTF-8?q?=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/chapter6.markdown b/chapter6.markdown index 91fe61d..b366a5e 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -118,18 +118,17 @@ 这并不是我们期望的结果。事实上传递参数给父构造函数是可能的,但这样需要在每次需要一个子对象时再做一次继承,很不方便,因为需要不断地创建父对象。 - ## 类式继承2——借用构造函数 -下面这种模式解决了从子对象传递参数到父对象的问题。它借用了父对象的构造函数,将子对象绑定到this,同时传入参数: +下面这种模式解决了从子对象传递参数到父对象的问题。它借用了父对象的构造函数,将子对象绑定到`this`,同时传入参数: function Child(a, c, b, d) { Parent.apply(this, arguments); } -使用这种模式时,只能继承在父对象的构造函数中添加到this的属性,不能继承原型上的成员。 +使用这种模式时,只能继承在父对象的构造函数中添加到`this`的属性,不能继承原型上的成员。 -使用借用构造函数的模式,子对象通过复制的方式继承父对象的成员,而不是像类式继承1中那样获得引用。下面的例子展示了这两者的不同: +使用借用构造函数的模式,子对象通过复制的方式继承父对象的成员,而不是像类式继承1中那样通过引用的方式。下面的例子展示了这两者的不同: //父构造函数 function Article() { @@ -153,20 +152,19 @@ alert(blog.hasOwnProperty('tags')); // false alert(page.hasOwnProperty('tags')); // true -在上面的代码片段中,Article()被两种方式分别继承。默认模式使blog可以通过原型链访问到tags属性,所以它自己并没有tags属性,hasOwnProperty()返回false。page对象有自己的tags属性,因为它是使用借用构造函数的方式继承,复制(而不是引用)了tags属性。 +在上面的代码片段中,`Article()`被用两种方式分别继承。默认模式使`blog`可以通过原型链访问到`tags`属性,所以它自己并没有`tags`属性,`hasOwnProperty()`返回`false`。`page`对象有自己的`tags`属性,因为它是使用借用构造函数的方式继承,复制(而不是引用)了`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在继承的时候是一份独立的拷贝。 +在这个例子中,`blog`对象修改了`tags`属性,同时,它也修改了父对象,因为实际上`blog.tags`和`article.tags`是引向同一个数组。而对`pages.tags`的修改并不影响父对象`article`,因为`pages.tags`在继承的时候是一份独立的拷贝。 - ### 原型链 -我们来看一下当我们使用熟悉的Parent()和Child()构造函数和这种继承模式时原型链是什么样的。为了使用这种继承模式,Child()有明显变化: +我们来看一下当我们使用熟悉的Parent()和Child()构造函数和这种继承模式时原型链是什么样的。为了使用这种继承模式,`Child()`有明显变化: //父构造函数 function Parent(name) { @@ -187,13 +185,12 @@ kid.name; // "Patrick" typeof kid.say; // "undefined" -如果看一下图6-4,就能发现new Child对象和Parent之间不再有链接。这是因为Child.prototype根本就没有被使用,它指向一个空对象。使用这种模式,kid拥有了自己的name属性,但是并没有继承say()方法,如果尝试调用它的话会出错。这种继承方式只是一种一次性地将父对象的属性复制为子对象的属性,并没有__proto__链接。 +如果看一下图6-4,就能发现`new Child()`对象和`Parent()`之间不再有链接。这是因为`Child.prototype`根本就没有被使用,它指向一个空对象。使用这种模式,`kid`拥有了自有的`name`属性,但是并没有继承`say()`方法,如果尝试调用它的话会出错。这种继承方式只是一种一次性地将父对象的属性复制为子对象的属性,并没有`__proto__`链接。 ![图6-4 使用借用构造函数模式时没有被关联的原型链](./Figure/chapter6/6-4.jpg) 图6-4 使用借用构造函数模式时没有被关联的原型链 - ### 利用借用构造函数模式实现多继承 使用借用构造函数模式,可以通过借用多个构造函数的方式来实现多继承: @@ -224,14 +221,13 @@ 图6-5 在Firebug中查看CatWings对象 - ### 借用构造函数的利与弊 这种模式的一个明显的弊端就是无法继承原型。如前面所说,原型往往是添加可复用的方法和属性的地方,这样就不用在每个实例中再创建一遍。 -这种模式的一个好处是获得了父对象自己成员的拷贝,不存在子对象意外改写父对象属性的风险。 +这种模式的一个好处是获得了父对象自有成员的拷贝,不存在子对象意外改写父对象属性的风险。 -那么,在上一个例子中,怎样使一个子对象也能够继承原型属性呢?怎样能使kid可以访问到say()方法呢?下一种继承模式解决了这个问题。 +那么,在上一个例子中,怎样使一个子对象也能够继承原型属性呢?怎样能使`kid`可以访问到`say()`方法呢?下一种继承模式解决了这个问题。 ## 类式继承3——借用并设置原型 From da31a822c6cc11977fa1caa6498f0265153f7a5d Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 3 May 2013 13:00:44 +0800 Subject: [PATCH 105/145] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF3?= =?UTF-8?q?=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/chapter6.markdown b/chapter6.markdown index b366a5e..51977aa 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -229,7 +229,6 @@ 那么,在上一个例子中,怎样使一个子对象也能够继承原型属性呢?怎样能使`kid`可以访问到`say()`方法呢?下一种继承模式解决了这个问题。 - ## 类式继承3——借用并设置原型 综合以上两种模式,首先借用父对象的构造函数,然后将子对象的原型设置为父对象的一个新实例: @@ -239,9 +238,9 @@ } Child.prototype = new Parent(); -这样做的好处是子对象获得了父对象自己的成员,也获得了父对象中可复用的(在原型中实现的)方法。子对象也可以传递任何参数给父构造函数。这种行为可能是最接近Java的,子对象继承了父对象的所有东西,同时可以安全地修改自己的属性而不用担心修改到父对象。 +这样做的好处是子对象获得了父对象的自有成员,也获得了父对象中可复用的(在原型中实现的)方法。子对象也可以传递任何参数给父构造函数。这种行为可能是最接近Java的,子对象继承了父对象的所有东西,同时可以安全地修改自己的属性而不用担心修改到父对象。 -一个弊端是父构造函数被调用了两次,所以不是很高效。最后,(父对象)自己的属性(比如这个例子中的name)也被继承了两次。 +一个弊端是父构造函数被调用了两次,所以不是很高效。最后,(父对象的)自有属性(比如这个例子中的`name`)也被继承了两次。 我们来看一下代码并做一些测试: @@ -267,7 +266,7 @@ delete kid.name; kid.say(); // "Adam" -跟前一种模式不一样,现在say()方法被正确地继承了。可以看到name也被继承了两次,在删除掉自己的拷贝后,在原型链上的另一个就被暴露出来了。 +跟前一种模式不一样,现在`say()`方法被正确地继承了。可以看到`name`也被继承了两次,在删除掉自己的拷贝后,在原型链上的另一个就被暴露出来了。 图6-6展示了这些对象之间的关系。这些关系有点像图6-3中展示的,但是获得这种关系的方法是不一样的。 @@ -275,7 +274,6 @@ 图6-6 除了继承“自己的属性”外,原型链也被保留了 - ## 类式继承4——共享原型 不像前一种类式继承模式需要调用两次父构造函数,下面这种模式根本不会涉及到调用父构造函数的问题。 From 876cc424ae7825dafb9b93cc3ca006e424192c70 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 3 May 2013 13:03:54 +0800 Subject: [PATCH 106/145] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF4?= =?UTF-8?q?=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chapter6.markdown b/chapter6.markdown index 51977aa..00b436d 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -278,15 +278,15 @@ 不像前一种类式继承模式需要调用两次父构造函数,下面这种模式根本不会涉及到调用父构造函数的问题。 -一般的经验是将可复用的成员放入原型中而不是this。从继承的角度来看,则是任何应该被继承的成员都应该放入原型中。这样你只需要设定子对象的原型和父对象的原型一样即可: +一般的经验是将可复用的成员放入原型中而不是`this`。从继承的角度来看,则是任何应该被继承的成员都应该放入原型中。这样你只需要设定子对象的原型和父对象的原型一样即可: function inherit(C, P) { C.prototype = P.prototype; } -这种模式的原型链很短并且查找很快,因为所有的对象实际上共享着同一个原型。但是这样也有弊端,那就是如果子对象或者在继承关系中的某个地方的任何一个子对象修改这个原型,将影响所有的继承关系中的父对象。(译注:这里应该是指会影响到所有从这个原型中继承的对象。) +这种模式的原型链很短并且查找很快,因为所有的对象实际上共享着同一个原型。但是这样也有弊端,那就是如果子对象或者在继承关系中的某个地方的任何一个子对象修改这个原型,将影响所有的继承关系中的父对象。(译注:指会影响到所有从这个原型中继承的对象所依赖的共享原型上的成员。) -如图6-7,子对象和父对象共享同一个原型,都可以访问say()方法。但是,子对象不继承name属性。 +如图6-7,子对象和父对象共享同一个原型,都可以访问`say()`方法。但是,子对象不继承`name`属性。 ![图6-7 (父子对象)共享原型时的关系](./Figure/chapter6/6-7.jpg) From 431361581a71b820f3b7eb20380d62a74aec3125 Mon Sep 17 00:00:00 2001 From: TooBug Date: Fri, 3 May 2013 13:16:12 +0800 Subject: [PATCH 107/145] =?UTF-8?q?=E7=B1=BB=E5=BC=8F=E7=BB=A7=E6=89=BF?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/chapter6.markdown b/chapter6.markdown index 00b436d..3042d02 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -292,12 +292,11 @@ 图6-7 (父子对象)共享原型时的关系 - ## 类式继承5——临时构造函数 下一种模式通过打断父对象和子对象原型的直接链接解决了共享原型时的问题,同时还从原型链中获得其它的好处。 -下面是这种模式的一种实现方式,F()函数是一个空函数,它充当了子对象和父对象的代理。F()的prototype属性指向父对象的原型。子对象的原型是一这个空函数的一个实例: +下面是这种模式的一种实现方式,`F()`函数是一个空函数,它充当了子对象和父对象的代理。`F()`的`prototype`属性指向父对象的原型。子对象的原型是这个空函数的一个实例: function inherit(C, P) { var F = function () {}; @@ -311,15 +310,14 @@ 图6-8 使用临时(代理)构造函数F()实现类式继承 -这种模式通常情况下都是一种很棒的选择,因为原型本来就是存放复用成员的地方。在这种模式中,父构造函数添加到this中的任何成员都不会被继承。 +这种模式通常情况下都是一种很棒的选择,因为原型本来就是存放复用成员的地方。在这种模式中,父构造函数添加到`this`中的任何成员都不会被继承。 我们来创建一个子对象并且检查一下它的行为: var kid = new Child(); -如果你访问kid.name将得到undefined。在这个例子中,name是父对象自己的属性,而在继承的过程中我们并没有调用new Parent(),所以这个属性并没有被创建。当访问kid.say()时,它在3号对象中不可用,所以在原型链中查找,4号对象也没有,但是1号对象有,它在内在中的位置会被所有从Parent()创建的构造函数和子对象所共享。 +如果你访问`kid.name`将得到`undefined`。在这个例子中,`name`是父对象自己的属性,而在继承的过程中我们并没有调用`new Parent()`,所以这个属性并没有被创建。当访问`kid.say()`时,它在3号对象中不可用,所以在原型链中查找,4号对象也没有,但是1号对象有,它在内存中的位置会被所有从`Parent()`创建的构造函数和子对象所共享。 - ### 存储父类(Superclass) 在上一种模式的基础上,还可以添加一个指向原始父对象的引用。这很像其它语言中访问超类(superclass)的情况,有时候很方便。 @@ -333,24 +331,23 @@ C.uber = P.prototype; } - ### 重置构造函数引用 -这个近乎完美的模式上还需要做的最后一件事情就是重置构造函数(constructor)的指向,以便未来在某个时刻能被正确地使用。 +这个近乎完美的模式上还需要做的最后一件事情就是重置构造函数(`constructor`)的指向,以便未来在某个时刻能被正确地使用。 -如果不重置构造函数的指向,那所有的子对象都会认为Parent()是它们的构造函数,而这个结果完全没有用。使用前面的inherit()的实现,你可以观察到这种行为: +如果不重置构造函数的指向,那所有的子对象都会认为`Parent()`是它们的构造函数,而这个结果完全没有用。使用前面的`inherit()`的实现,你可以观察到这种行为: - // parent, child, inheritance + // Parent,Child,实现继承 function Parent() {} function Child() {} inherit(Child, Parent); - // testing the waters + // 测试 var kid = new Child(); kid.constructor.name; // "Parent" kid.constructor === Parent; // true -constructor属性很少用,但是在运行时检查对象很方便。你可以重新将它指向期望的构造函数而不影响功能,因为这个属性更多是“信息性”的。(译注:即它更多的时候是在提供信息而不是参与到函数功能中。) +`constructor`属性很少被用到,但是在运行时检查对象很方便。你可以重新将它指向期望的构造函数而不影响功能,因为这个属性更多是“信息性”的。(译注:即它更多的时候是在提供信息而不是参与到函数功能中。) 最终,这种类式继承的Holy Grail版本看起来是这样的: @@ -366,7 +363,7 @@ constructor属性很少用,但是在运行时检查对象很方便。你可以 > “代理函数”或者“代理构造函数”也是指这种模式,因为临时构造函数是被用作获取父构造函数原型的代理。 -一种常见的对Holy Grail模式的优化是避免每次需要继承的时候都创建一个临时(代理)构造函数。事实上创建一次就足够了,以后只需要修改它的原型即可。你可以用一个立即执行的函数来将代理函数存储到闭包中: +一种常见的对Holy Grail模式的优化是避免每次需要继承的时候都创建一个临时(代理)构造函数。事实上创建一次就足够了,以后只需要修改它的原型即可。你可以用一个即时函数来将代理函数存储到闭包中: var inherit = (function () { var F = function () {}; @@ -378,7 +375,6 @@ constructor属性很少用,但是在运行时检查对象很方便。你可以 } }()); - ## Klass 有很多JavaScript类库模拟了类,创造了新的语法糖。具体的实现方式可能会不一样,但是基本上都有一些共性,包括: From 9bf28bae8073671c6ce28c27470610b89b974af1 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sat, 4 May 2013 17:02:47 +0800 Subject: [PATCH 108/145] =?UTF-8?q?Klass=20=E6=A0=A1=E5=AF=B9=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 | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/chapter6.markdown b/chapter6.markdown index 3042d02..dab19c2 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -377,15 +377,15 @@ ## Klass -有很多JavaScript类库模拟了类,创造了新的语法糖。具体的实现方式可能会不一样,但是基本上都有一些共性,包括: +有很多JavaScript类库模拟了类,创造了新的语法糖。这些类库具体的实现方式可能会不一样,但是基本上都有一些共性,包括: -- 有一个约定好名字的方法,如initialize、_init或者其它相似的名字,会被自动调用,来充当类的构造函数。 +- 有一个约定好的方法,如`initialize`、`_init`或者其它相似的名字,会被自动调用,来充当类的构造函数 - 类可以从其它类继承 - 在子类中可以访问到父类(superclass) -> 我们在这里做一下变化,在本章的这部分自由地使用“class”单词,因为主题就是模拟类。 +> 我们在这里做一点变化,在本章的这部分自由地使用“class”这个词,因为主题就是模拟类。 -为避免讨论太多细节,我们来看一下JavaScript中一种模拟类的实现。首先,这种解决方案从客户的角度来看将如何被使用? +为避免讨论太多细节,我们来看一下JavaScript中一种模拟类的实现。首先,看一下这种方案将如何被使用? var Man = klass(null, { __construct: function (what) { @@ -397,14 +397,14 @@ } }); -这种语法糖的形式是一个名为klass()的函数。在一些实现方式中,它可能是Klass()构造函数或者是增强的Object.prototype,但是在这个例子中,我们让它只是一个简单的函数。 +这种语法糖的形式是一个名为`klass()`的函数。在一些其它的实现方式中,它可能是`Klass()`构造函数或者是增强的`Object.prototype`,但是在这个例子中,我们让它只是一个简单的函数。 -这个函数接受两个参数:一个被继承的类和通过对象字面量提供的新类的实现。受PHP的影响,我们约定类的构造函数必须是一个名为\_\_construct的方法。在前面的代码片段中,建立了一个名为Man的新类,并且它不继承任何类(意味着继承自Object)。Man类有一个在\_\_construct建立的自己的属性name和一个方法getName()。这个类是一个构造函数,所以下面的代码将正常工作(并且看起来像类实例化的过程): +这个函数接受两个参数:一个被继承的类和通过对象字面量提供的新类的实现。受PHP的影响,我们约定类的构造函数必须是一个名为`__construct()`的方法。在前面的代码片段中,建立了一个名为`Man`的新类,并且它不继承任何类(意味着继承自`Object`)。`Man`类有一个在`__construct()`建立的自有属性`name`和一个方法`getName()`。这个类是一个构造函数,所以下面的代码将正常工作(并且看起来像类实例化的过程): var first = new Man('Adam'); // logs "Man's constructor" first.getName(); // "Adam" -现在我们来扩展这个类,创建一个SuperMan类: +现在我们来扩展这个类,创建一个`SuperMan`类: var SuperMan = klass(Man, { __construct: function (what) { @@ -416,26 +416,25 @@ } }); -这里,klass()的第一个参数是将被继承的Man类。值得注意的是,在getName()中,父类的getName()方法首先通过SuperMan类的uber静态属性被调用。我们来测试一下: +这里,`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”。在一些语言中,父类的构造函数在子类构造函数被调用的时候会自动执行,这个特性也可以模拟。 +第一行在console中记录了“Man's constructor”,然后是“Superman's constructor”,在一些语言中,父类的构造函数在子类构造函数被调用的时候会自动执行,这个特性也被模拟了。 -用instanceof运算符测试返回希望的结果: +用`instanceof`运算符测试返回希望的结果: clark instanceof Man; // true clark instanceof SuperMan; // true -最后,我们来看一下klass()函数是怎样实现的: +最后,我们来看一下`klass()`函数是怎样实现的: var klass = function (Parent, props) { var Child, F, i; - // 1. - // new constructor + // 1. 构造函数 Child = function () { if (Child.uber && Child.uber.hasOwnProperty("__construct")) { Child.uber.__construct.apply(this, arguments); @@ -445,8 +444,7 @@ } }; - // 2. - // inherit + // 2. 继承 Parent = Parent || Object; F = function () {}; F.prototype = Parent.prototype; @@ -454,25 +452,24 @@ Child.uber = Parent.prototype; Child.prototype.constructor = Child; - // 3. - // add implementation methods + // 3. 添加方法实现 for (i in props) { if (props.hasOwnProperty(i)) { Child.prototype[i] = props[i]; } } - // return the "class" + // 返回“类” return Child; }; -这个klass()实现有三个明显的部分: +这个`klass()`实现有三个明显的部分: -1. 创建Child()构造函数,这也是最后返回的将被作为类使用的函数。在这个函数里面,如果\_\_construct方法存在的话将被调用。同样是在父类的\_\_construct(如果存在)被调用前使用静态的uber属性。也可能存在uber没有定义的情况——比如从Object继承,因为它是在Man类中被定义的。 -2. 第二部分主要完成继承。只是简单地使用前面章节讨论过的Holy Grail类式继承模式。只有一个东西是新的:如果Parent没有传值的话,设定Parent为Object。 -3. 最后一部分是类真正定义的地方,循环需要实现的方法(如例子中的\_\_constructt和getName),并将它们添加到Child的原型中。 +1. 创建`Child()`构造函数,这也是最后返回的将被作为类使用的函数。在这个函数里面,如果`__construct()`方法存在的话将被调用,同样,如果父类的`__construct()`存在,也将被调用(通过使用静态属性`uber`)。也可能存在`uber`没有定义的情况——比如从`Object`继承,前例中`Man`类即是如此。 +2. 第二部分主要完成继承。只是简单地使用前面章节讨论过的Holy Grail类式继承模式。只有一个东西是新的:如果`Parent`没有传值的话,设定`Parent`为`Object`。 +3. 最后一部分是真正定义类的地方,遍历需要实现的方法(如例子中的`__constructor()`和`getName()`),并将它们添加到`Child()`的原型中。 -什么时候使用这种模式?其实,最好是能避免则避免,因为它带来了在这门语言中不存在的完整的类的概念,会让人疑惑。使用它需要学习新的语法和新的规则。也就是说,如果你或者你的团队对类感到习惯并且同时对原型感到不习惯,这种模式可能是一个可以探索的方向。这种模式允许你完全忘掉原型,好处就是你可以将语法变种得像其它你所喜欢的语言一样。 +什么时候使用这种模式呢?其实,最好是能避免则避免,因为它带来了在这门语言中不存在的完整的类的概念,会让人疑惑。使用它需要学习新的语法和新的规则,也就是说,如果你或者你的团队习惯于使用类并且对原型感到不习惯,这种模式可能是一个可以探索的方向。这种模式允许你完全忘掉原型,好处就是你可以使用像其它语言那样的(变种)语法。 ## 原型继承 From 3cc7f8d78197ab9a622f5c277e5aa839b0d6a255 Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 5 May 2013 22:44:28 +0800 Subject: [PATCH 109/145] =?UTF-8?q?=E5=8E=9F=E5=9E=8B=E7=BB=A7=E6=89=BF=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=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, 26 insertions(+), 30 deletions(-) diff --git a/chapter6.markdown b/chapter6.markdown index dab19c2..1714952 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -471,25 +471,24 @@ 什么时候使用这种模式呢?其实,最好是能避免则避免,因为它带来了在这门语言中不存在的完整的类的概念,会让人疑惑。使用它需要学习新的语法和新的规则,也就是说,如果你或者你的团队习惯于使用类并且对原型感到不习惯,这种模式可能是一个可以探索的方向。这种模式允许你完全忘掉原型,好处就是你可以使用像其它语言那样的(变种)语法。 - ## 原型继承 -现在,让我们从一个叫作“原型继承”的模式来讨论没有类的现代继承模式。在这种模式中,没有任何类进来,在这里,一个对象继承自另外一个对象。你可以这样理解它:你有一个想复用的对象,然后你想创建第二个对象,并且获得第一个对象的功能。下面是这种模式的用法: +现在,让我们从一个叫作“原型继承”的模式来讨论没有类的现代继承模式。在这种模式中,没有任何类牵涉进来,一个对象继承自另外一个对象。你可以这样理解它:你有一个想复用的对象,然后你想创建第二个对象,并且获得第一个对象的功能。下面是这种模式的用法: - //需要继承的对象 + // 需要继承的对象 var parent = { name: "Papa" }; - //新对象 + // 新对象 var child = object(parent); - //测试 + // 测试 alert(child.name); // "Papa" -在这个代码片段中,有一个已经存在的使用对象字面量创建的对象叫parent,我们想创建一个和parent有相同的属性和方法的对象叫child。child对象使用object()函数创建。这个函数在JavaScript中并不存在(不要与构造函数Object()混淆),所以我们来看看怎样定义它。 +在这个代码片段中,有一个已经存在的使用对象字面量创建的对象叫`parent`,我们想创建一个和`parent`有相同的属性和方法的对象叫`child`。`child`对象使用`object()`函数创建。这个函数在JavaScript中并不存在(不要与构造函数`Object()`混淆),所以我们来看看怎样定义它。 -与Holy Grail类式继承相似,可以使用一个空的临时构造函数F(),然后设定F()的原型为parent对象。最后,返回一个临时构造函数的新实例。 +与Holy Grail类式继承相似,可以使用一个空的临时构造函数`F()`,然后设定`F()`的原型为`parent`对象。最后,返回一个临时构造函数的新实例。 function object(o) { function F() {} @@ -497,69 +496,66 @@ return new F(); } -图6-9展示了使用原型继承时的原型链。在这里child总是以一个空对象开始,它没有自己的属性但通过原型链(\_\_proto\_\_)拥有父对象的所有功能。 +图6-9展示了使用原型继承时的原型链。这样创建的`child`总是一个空对象,它没有自有属性但通过原型链(`__proto__`)拥有父对象的所有功能。 ![图6-9 原型继承模式](./Figure/chapter6/6-9.jpg) 图6-9 原型继承模式 - ### 讨论 -在原型继承模式中,parent不需要使用对象字面量来创建。(尽管这是一种更常用的方式。)可以使用构造函数来创建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 + // 使用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 + typeof kid.getName; // "function",因为它在原型中 + typeof kid.name; // "undefined",因为只有原型中的成员被继承了 - -###例外的ECMAScript 5 +### ECMAScript5中的原型继承 -在ECMAScript 5中,原型继承已经正式成为语言的一部分。这种模式使用Object.create方法来实现。换句话说,你不再需要自己去写类似object()的函数,它是语言原生的了: +在ECMAScript5中,原型继承已经正式成为语言的一部分。这种模式使用`Object.create()`方法来实现。换句话说,你不再需要自己去写类似`object()`的函数,它是语言原生的部分了: var child = Object.create(parent); -Object.create()接收一个额外的参数——一个对象。这个额外对象中的属性将被作为自己的属性添加到返回的子对象中。这让我们可以很方便地将继承和创建子对象在一个方法调用中实现。例如: +`Object.create()`接收一个额外的参数——一个对象。这个额外对象中的属性将被作为自有属性添加到返回的子对象中。这让我们可以很方便地将继承和创建子对象在一个方法调用中实现。例如: var child = Object.create(parent, { - age: { value: 2 } // ECMA5 descriptor + age: { value: 2 } // ES5中的属性描述符 }); child.hasOwnProperty("age"); // true -你可能也会发现原型继承模式已经在一些JavaScript类库中实现了,比如,在YUI3中,它是Y.Object()方法: +你可能也会发现原型继承模式已经在一些JavaScript类库中实现了,比如,在YUI3中,它是`Y.Object()`方法: YUI().use('*', function (Y) { var child = Y.Object(parent); From 22df729b57192d457d968b9cb01ce5766dd34a7b Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 5 May 2013 22:51:30 +0800 Subject: [PATCH 110/145] =?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=E5=92=8C=20=E6=B7=B7?= =?UTF-8?q?=E5=85=83=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/chapter6.markdown b/chapter6.markdown index 1714952..86cfeae 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -561,10 +561,9 @@ var child = Y.Object(parent); }); - ## 通过复制属性继承 -让我们来看一下另外一种继承模式——通过复制属性继承。在这种模式中,一个对象通过简单地复制另一个对象来获得功能。下面是一个简单的实现这种功能的extend()函数: +让我们来看一下另外一种继承模式——通过复制属性继承。在这种模式中,一个对象通过简单地复制另一个对象来获得功能。下面是一个简单的实现这种功能的`extend()`函数: function extend(parent, child) { var i; @@ -574,16 +573,16 @@ child[i] = parent[i]; } } - return child; + return child; } -这是一个简单的实现,仅仅是遍历了父对象的成员然后复制它们。在这个实现中,child是可选参数,如果它没有被传入一个已有的对象,那么一个全新的对象将被创建并被返回: +这是一个简单的实现,仅仅是遍历了父对象的成员然后复制它们。在这个实现中,`child`是可选参数,如果它没有被传入一个已有的对象,那么一个全新的对象将被创建并返回: var dad = {name: "Adam"}; var kid = extend(dad); kid.name; // "Adam" -上面给出的实现叫作对象的“浅拷贝”(shallow copy)。另一方面,“深拷贝”是指检查准备复制的属性本身是否是对象或者数组,如果是,也遍历它们的属性并复制。如果使用浅拷贝的话(因为在JavaScript中对象是按引用传递),如果你改变子对象的一个属性,而这个属性恰好是一个对象,那么你也会改变父对象。实际上这对方法来说可能很好(因为函数也是对象,也是按引用传递),但是当遇到其它的对象和数组的时候可能会有些意外情况。考虑这种情况: +上面给出的实现叫作对象的“浅拷贝”(shallow copy),与之相对,“深拷贝”是指检查准备复制的属性本身是否是对象或者数组,如果是,也遍历它们的属性并复制。如果使用浅拷贝的话(因为在JavaScript中对象是按引用传递),如果你改变子对象的一个属性,而这个属性恰好是一个对象,那么你也会改变父对象。实际上这对方法来说可能很好(因为函数也是对象,也是按引用传递),但是当遇到其它的对象和数组的时候可能会有些意外情况。考虑这种情况: var dad = { counts: [1, 2, 3], @@ -594,7 +593,7 @@ dad.counts.toString(); // "1,2,3,4" dad.reads === kid.reads; // true -现在让我们来修改一下extend()函数以便做深拷贝。所有你需要做的事情只是检查一个属性的类型是否是对象,如果是,则递归遍历它的属性。另外一个需要做的检查是这个对象是真的对象还是数组。我们可以使用第3章讨论过的数组检查方式。最终深拷贝版的extend()是这样的: +现在让我们来修改一下`extend()`函数以便实现深拷贝。你需要做的事情只是检查一个属性的类型是否是对象,如果是,则递归遍历它的属性。另外一个需要做的检查是这个对象是真的对象还是数组,可以使用第三章讨论过的数组检查方式。最终深拷贝版的`extend()`是这样的: function extendDeep(parent, child) { var i, @@ -633,11 +632,10 @@ kid.reads.web = true; dad.reads.paper; // true -通过复制属性继承的模式很简单且应用很广泛。例如Firebug(JavaScript写的Firefox扩展)有一个方法叫extend()做浅拷贝,jQuery的extend()方法做深拷贝。YUI3提供了一个叫作Y.clone()的方法,它创建一个深拷贝并且通过绑定到子对象的方式复制函数。(本章后面将有更多关于绑定的内容。) +通过复制属性继承的模式很简单且应用很广泛。例如Firebug(JavaScript写的Firefox扩展)有一个方法叫`extend()`做浅拷贝,jQuery的`extend()`方法做深拷贝。YUI3提供了一个叫作`Y.clone()`的方法,它创建一个深拷贝并且通过绑定到子对象的方式复制函数。(本章后面将有更多关于绑定的内容。) 这种模式并不高深,因为根本没有原型牵涉进来,而只跟对象和它们的属性有关。 - ## 混元(Mix-ins) 既然谈到了通过复制属性来继承,就让我们顺便多说一点,来讨论一下“混元”模式。除了前面说的从一个对象复制,你还可以从任意多数量的对象中复制属性,然后将它们混在一起组成一个新对象。 @@ -665,13 +663,13 @@ {sugar: "sure!"} ); -图6-10展示了在Firebug的控制台中用console.dir(cake)展示出来的混元后cake对象的属性。 +图6-10展示了在Firebug的控制台中用`console.dir(cake)`展示出来的混元后`cake`对象的属性。 ![图6-10 在Firebug中查看cake对象](./Figure/chapter6/6-10.jpg) 图6-10 在Firebug中查看cake对象 -> 如果你习惯了某些将混元作为原生部分的语言,那么你可能期望修改一个或多个父对象时也影响子对象。但在这个实现中这是不会发生的事情。这里我们只是简单地遍历、复制自己的属性,并没有与父对象的链接。 +> 如果你习惯了某些将混元作为原生部分的语言,那么你可能期望修改一个或多个父对象时也影响子对象。但在这个实现中这是不会发生的事情。这里我们只是简单地遍历、复制自有属性,并没有与父对象有任何链接。 ## 借用方法 From 64f74c6a5ef2f4acc715cf2a0f8f4f715ff570f7 Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 6 May 2013 09:11:42 +0800 Subject: [PATCH 111/145] =?UTF-8?q?=E5=80=9F=E7=94=A8=E6=96=B9=E6=B3=95=20?= =?UTF-8?q?=E4=B9=8B=20=E4=BB=8E=E6=95=B0=E7=BB=84=E5=80=9F=E7=94=A8=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/chapter6.markdown b/chapter6.markdown index 86cfeae..4f77c95 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -671,38 +671,36 @@ > 如果你习惯了某些将混元作为原生部分的语言,那么你可能期望修改一个或多个父对象时也影响子对象。但在这个实现中这是不会发生的事情。这里我们只是简单地遍历、复制自有属性,并没有与父对象有任何链接。 - ## 借用方法 -有时候会有这样的情况:你希望使用某个已存在的对象的一两个方法,你希望能复用它们,但是又真的不希望和那个对象产生继承关系,因为你只希望使用你需要的那一两个方法,而不继承那些你永远用不到的方法。受益于函数方法call()和apply(),通过借用方法模式,这是可行的。在本书中,你其实已经见过这种模式了,甚至在本章extendDeep()的实现中也有用到。 +有时候会有这样的情况:你希望使用某个已存在的对象的一两个方法,你希望能复用它们,但是又真的不希望和那个对象产生继承关系,因为你只希望使用你需要的那一两个方法,而不继承那些你永远用不到的方法。得益于函数的`call()`和`apply()`方法,可以通过借用方法模式实现它。在本书中,你其实已经见过这种模式了,甚至在本章`extendDeep()`的实现中也有用到。 -如你所熟知的一样,在JavaScript中函数也是对象,它们有一些有趣的方法,比如call()和apply()。这两个方法的唯一区别是后者接受一个参数数组以传入正在调用的方法,而前者只接受一个一个的参数。你可以使用这两个方法来从已有的对象中借用方法: +在JavaScript中函数也是对象,它们有一些有趣的方法,比如`call()`和`apply()`。这两个方法的唯一区别是后者接受一个参数数组以传入正在调用的方法,而前者只接受一个一个的参数。你可以使用这两个方法来从已有的对象中借用方法: - //call() example + // call()示例 notmyobj.doStuff.call(myobj, param1, p2, p3); - // apply() example + // apply()示例 notmyobj.doStuff.apply(myobj, [param1, p2, p3]); -在这个例子中有一个对象myobj,而且notmyobj有一个用得着的方法叫doStuff()。你可以简单地临时借用doStuff()方法,而不用处理继承然后得到一堆myobj中你永远不会用的方法。 +在这个例子中有一个对象`myobj`,而且`notmyobj`有一个用得着的方法叫`doStuff()`。你可以简单地临时借用`doStuff()`方法,而不用处理继承然后得到一堆`myobj`中无关的方法。 -你传一个对象和任意的参数,这个被借用的方法会将this绑定到你自己的对象上。简单地说,你的对象会临时假装成另一个对象以使用它的方法。这就像实际上获得了继承但又免除了“继承税”(指你不需要的属性和方法)。 +你传一个对象和任意的参数,这个被借用的方法会将`this`绑定到你传递的对象上。简单地说,你的对象会临时假装成另一个对象以使用它的方法。这就像实际上获得了继承但又免除了“继承税”(译注:指不需要的属性和方法)。 - ### 例:从数组借用 这种模式的一种常见用法是从数组借用方法。 -数组有很多很有用但是一些“类数组”对象(如arguments)不具备的方法。所以arguments可以借用数组的方法,比如slice()。这是一个例子: +数组有很多很有用但是一些“类数组”对象(如`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(...)。这种方法代码更长一些,但是不用创建一个空数组。 +在这个例子中,有一个空数组被创建了,因为要借用它的方法。也可以使用一种看起来代码更长的方法来做,那就是直接从数组的原型中借用方法,使用`Array.prototype.slice.call(...)`。这种方法代码更长一些,但是不用创建一个空数组。 ### 借用并绑定 From 599b16c1de59f0e2256372694e03065e0dd7b252 Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 6 May 2013 12:59:03 +0800 Subject: [PATCH 112/145] =?UTF-8?q?=E7=AC=AC=E5=85=AD=E7=AB=A0=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95=20close=20#7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter6.markdown | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/chapter6.markdown b/chapter6.markdown index 4f77c95..f149d9f 100644 --- a/chapter6.markdown +++ b/chapter6.markdown @@ -702,12 +702,11 @@ 在这个例子中,有一个空数组被创建了,因为要借用它的方法。也可以使用一种看起来代码更长的方法来做,那就是直接从数组的原型中借用方法,使用`Array.prototype.slice.call(...)`。这种方法代码更长一些,但是不用创建一个空数组。 - ### 借用并绑定 -当借用方法的时候,不管是通过call()/apply()还是通过简单的赋值,方法中的this指向的对象都是基于调用的表达式来决定的。但是有时候最好的使用方式是将this的值锁定或者提前绑定到一个指定的对象上。 +当借用方法的时候,不管是通过`call()`/`apply()`还是通过简单的赋值,方法中的`this`指向的对象都是基于调用的表达式来决定的。但是有时候最好的使用方式是将`this`的值锁定或者提前绑定到一个指定的对象上。 -我们来看一个例子。这是一个对象one,它有一个say()方法: +我们来看一个例子。这是一个对象`one`,它有一个`say()`方法: var one = { name: "object", @@ -716,10 +715,10 @@ } }; - // test + // 测试 one.say('hi'); // "hi, object" -现在另一个对象two没有say()方法,但是它可以从one借用: +现在另一个对象`two`没有`say()`方法,但是它可以从one借用: var two = { name: "another object" @@ -727,14 +726,13 @@ one.say.apply(two, ['hello']); // "hello, another object" -在这个例子中,say()方法中的this指向了two,this.name是“another object”。但是如果在某些场景下你将函数赋值给了全局变量或者是将这个函数作为回调,会发生什么?在客户端编程中有非常多的事件和回调,所以这种情况经常发生: +在这个例子中,`say()`方法中的`this`指向了`two`,`this.name`是“another object”。但是如果在某些场景下你将函数赋值给了全局变量或者是将这个函数作为回调,会发生什么?在客户端编程中有非常多的事件和回调,所以这种情况经常发生: - // assigning to a variable - // `this` will point to the global object + // 赋值给变量,this会指向全局对象 var say = one.say; say('hoho'); // "hoho, undefined" - // passing as a callback + // 作为回调 var yetanother = { name: "Yet another object", method: function (callback) { @@ -743,7 +741,7 @@ }; yetanother.method(one.say); // "Holla, undefined" -在这两种情况中say()中的this都指向了全局对象,所以代码并不像我们想象的那样正常工作。要修复(换言之,绑定)一个方法的对象,我们可以用一个简单的函数,像这样: +在这两种情况中`say()`中的`this`都指向了全局对象,所以代码并不像我们想象的那样正常工作。要修复(绑定)一个方法的对象,我们可以用一个简单的函数,像这样: function bind(o, m) { return function () { @@ -751,25 +749,24 @@ }; } -这个bind()函数接受一个对象o和一个方法m,然后把它们绑定在一起,再返回另一个函数。返回的函数通过闭包可以访问到o和m。也就是说,即使在bind()返回之后,内层的函数仍然可以访问到o和m,而o和m会始终指向原始的对象和方法。让我们用bind()来创建一个新函数: +这个`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。 +正如你看到的,尽管`twosay()`是作为一个全局函数被创建的,但`this`并没有指向全局对象,而是指向了通过`bind()`传入的对象`two`。不论如何调用`twosay()`,`this`将始终指向`two`。 绑定是奢侈的,你需要付出的代价是一个额外的闭包。 - ### Function.prototype.bind() -ECMAScript5在Function.prototype中添加了一个方法叫bind(),使用时和apply和call()一样简单。所以你可以这样写: +ECMAScript5在`Function.prototype`中添加了一个方法叫`bind()`,使用时和`apply()`/`call()`一样简单。所以你可以这样写: var newFunc = obj.someFunc.bind(myobj, 1, 2, 3); -这意味着将someFunc()和myobj绑定了,并且还传入了someFunc()的前三个参数。这也是一个在第4章讨论过的部分应用的例子。 +这意味着将`someFunc()`和`myobj`绑定了,并且还传入了`someFunc()`的前三个参数。这也是一个在第4章讨论过的部分应用的例子。 -让我们来看一下当你的程序跑在低于ES5的环境中时如何实现Function.prototype.bind(): +让我们来看一下当你的程序跑在低于ES5的环境中时如何实现`Function.prototype.bind()`: if (typeof Function.prototype.bind === "undefined") { Function.prototype.bind = function (thisArg) { @@ -783,22 +780,20 @@ ECMAScript5在Function.prototype中添加了一个方法叫bind(),使用时和 }; } -这个实现可能看起来有点熟悉,它使用了部分应用,将传入bind()的参数串起来(除了第一个参数),然后在被调用时传给bind()返回的新函数。这是用法示例: +这个实现可能看起来有点熟悉,它使用了部分应用,将传入`bind()`的参数串起来(除了第一个参数),然后在被调用时传给`bind()`返回的新函数。这是用法示例: var twosay2 = one.say.bind(two); twosay2('Bonjour'); // "Bonjour, another object" -在这个例子中,除了绑定的对象外,我们没有传任何参数给bind()。下一个例子中,我们来传一个用于部分应用的参数: +在这个例子中,除了绑定的对象外,我们没有传任何参数给`bind()`。下一个例子中,我们来传一个用于部分应用的参数: var twosay3 = one.say.bind(two, 'Enchanté'); twosay3(); // "Enchanté, another object" - ##小结 -在JavaScript中,继承有很多种方案可以选择。学习和理解不同的模式是有好处的,因为这可以增强你对这门语言的掌握能力。在本章中你看到了很多类式继承和现代继承的方案。 - -但是,也许在开发过程中继承并不是你经常面对的一个问题。这一部分是因为这个问题已经被使用某种方式或者某个你使用的类库解决了,另一部分是因为你不需要在JavaScript中建立很长很复杂的继承链。在静态强类型语言中,继承可能是唯一可以利用代码的方法,但在JavaScript中你可能有更多更简单更优化的方法,包括借用方法、绑定、复制属性、混元等。 +在JavaScript中,继承有很多种方案可以选择,在本章中你看到了很多类式继承和现代继承的方案。学习和理解不同的模式是有好处的,因为这可以增强你对这门语言的掌握能力。 -记住,代码复用才是目标,继承只是达成这个目标的一种手段。 +但是,也许在开发过程中继承并不是你经常面对的一个问题。一部分是因为这个问题已经被使用某种方式或者某个你使用的类库解决了,另一部分是因为你不需要在JavaScript中建立很长很复杂的继承链。在静态强类型语言中,继承可能是唯一可以复用代码的方法,但在JavaScript中有更多更简单更优化的方法,包括借用方法、绑定、复制属性、混元等。 +记住,代码复用才是目标,继承只是达成这个目标的一种手段。 \ No newline at end of file From 4251240d0d45473475a7530ed1d77a38f40d68ca Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 6 May 2013 13:30:20 +0800 Subject: [PATCH 113/145] =?UTF-8?q?=E5=8D=95=E4=BE=8B=E6=A8=A1=E5=BC=8F=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 114 +++++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 61 deletions(-) diff --git a/chapter7.markdown b/chapter7.markdown index 140bbe7..d2f3ce0 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -1,15 +1,13 @@ - # 设计模式 在GoF(Gang of Four)的书中提出的设计模式为面向对象的软件设计中遇到的一些普遍问题提供了解决方案。它们已经诞生很久了,而且被证实在很多情况下是很有效的。这正是你需要熟悉它的原因,也是我们要讨论它的原因。 尽管这些设计模式跟语言和具体的实现方式无关,但它们多年来被关注到的方面仍然主要是在强类型静态语言比如C++和Java中的应用。 -JavaScript作为一种基于原型的弱类型动态语言,使得有些时候实现某些模式时相当简单,甚至不费吹灰之力。 +JavaScript作为一种基于原型的弱类型动态语言,有些时候实现某些模式时相当简单,甚至不费吹灰之力。 让我们从第一个例子——单例模式——来看一下在JavaScript中和静态的基于类的语言有什么不同。 - ## 单例 单例模式的核心思想是让指定的类只存在唯一一个实例。这意味着当你第二次使用相同的类去创建对象的时候,你得到的应该和第一次创建的是同一个对象。 @@ -28,16 +26,15 @@ JavaScript作为一种基于原型的弱类型动态语言,使得有些时候 obj === obj2; // false obj == obj2; // false -所以你可以说当你每次使用对象字面量创建一个对象的时候就是在创建一个单例,并没有特别的语法迁涉进来。 +所以你可以说当你每次使用对象字面量创建一个对象的时候就是在创建一个单例,并没有什么特别的语法牵涉进来。 -> 需要注意的是,有的时候当人们在JavaScript中提出“单例”的时候,它们可能是在指第5章讨论过的“模块模式”。 +> 需要注意的是,有的时候当人们在JavaScript中提出“单例”的时候,它们可能是在指第五章讨论过的“模块模式”。 - ### 使用new -JavaScript没有类,所以一字一句地说单例的定义并没有什么意义。但是JavaScript有使用new、通过构造函数来创建对象的语法,有时候你可能需要这种语法下的一个单例实现。这也就是说当你使用new、通过同一个构造函数来创建多个对象的时候,你应该只是得到同一个对象的不同引用。 +JavaScript没有类,所以一字一句地说单例的定义并没有什么意义。但是JavaScript有使用`new`、通过构造函数来创建对象的语法,有时候你可能需要这种语法下的一个单例实现。这也就是说当你使用`new`、通过同一个构造函数来创建多个对象的时候,你应该只是得到同一个对象的不同引用。 -> 温馨提示:从一个实用模式的角度来说,下面的讨论并不是那么有用,只是更多地在实践模拟一些语言中关于这个模式的一些问题的解决方案。这些语言主要是(静态强类型的)基于类的语言,在这些语言中,函数并不是“一等公民”。 +> 温馨提示:从一个实用模式的角度来说,下面的讨论并不是那么有用,只是更多地在模拟一些语言中关于这个模式的一些问题的解决方案。这些语言主要是(静态强类型的)基于类的语言,在这些语言中,函数并不是“一等公民”。 下面的代码片段展示了期望的结果(假设你忽略了多元宇宙的设想,接受了只有一个宇宙的观点): @@ -45,126 +42,122 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 var uni2 = new Universe(); uni === uni2; // true -在这个例子中,uni只在构造函数第一次被调用时创建。第二次(以及后续更多次)调用时,同一个uni对象被返回。这就是为什么uni === uni2的原因——因为它们实际上是同一个对象的两个引用。那么怎么在JavaScript达到这个效果呢? +在这个例子中,`uni`只在构造函数第一次被调用时创建。第二次(以及后续更多次)调用时,同一个`uni`对象被返回。这就是为什么`uni === uni2`的原因——因为它们实际上是同一个对象的两个引用。那么怎么在JavaScript达到这个效果呢? -当对象实例this被创建时,你需要在Universe构造函数中缓存它,以便在第二次调用的时候返回。有几种选择可以达到这种效果: +当对象实例`this`被创建时,你需要在`Universe()`构造函数中缓存它,以便在第二次调用的时候返回。有几种选择可以达到这种效果: - 你可以使用一个全局变量来存储实例。不推荐使用这种方法,因为通常我们认为使用全局变量是不好的。而且,任何人都可以改写全局变量的值,甚至可能是无意中改写。所以我们不再讨论这种方案。 -- 你也可以将对象实例缓存在构造函数的属性中。在JavaScript中,函数也是对象,所以它们也可以有属性。你可以写一些类似Universe.instance的属性来缓存对象。这是一种漂亮干净的解决方案,不足之处是instance属性仍然是可以被公开访问的,别人写的代码可能修改它,这样就会失去这个实例。 +- 你也可以将对象实例缓存在构造函数的属性中。在JavaScript中,函数也是对象,所以它们也可以有属性。你可以写一些类似`Universe.instance`的属性来缓存对象。这是一种漂亮干净的解决方案,不足之处是`instance`属性仍然是可以被公开访问的,别人写的代码可能修改它,这样就会失去这个实例。 - 你可以将实例包裹在闭包中。这可以保持实例是私有的,不会在构造函数之外被修改,代价是一个额外的闭包。 让我们来看一下第二种和第三种方案的实现示例。 - ### 将实例放到静态属性中 -下面是一个将唯一的实例放入Universe构造函数的一个静态属性中的例子: +下面是一个将唯一的实例放入`Universe()`构造函数的一个静态属性中的例子: function Universe() { - // do we have an existing instance? - if (typeof Universe.instance === "object") { - return Universe.instance; - } - - // proceed as normal - this.start_time = 0; - this.bang = "Big"; - - // cache - Universe.instance = this; - - // implicit return: - // return this; + // 实例是否已经存在? + if (typeof Universe.instance === "object") { + return Universe.instance; + } + + // 处理普通逻辑 + this.start_time = 0; + this.bang = "Big"; + + // 缓存实例 + Universe.instance = this; + + // 隐式return: + // return this; } - // testing + // 测试 var uni = new Universe(); var uni2 = new Universe(); uni === uni2; // true -如你所见,这是一种直接有效的解决方案,唯一的缺陷是instance是可被公开访问的。一般来说它被其它代码误删改的可能是很小的(起码比全局变量instance要小得多),但是仍然是有可能的。 +如你所见,这是一种直接有效的解决方案,唯一的缺陷是`instance`是可被公开访问的。一般来说它被其它代码误删改的可能是很小的(起码比全局变量`instance`要小得多),但是仍然是有可能的。 - ### 将实例放到闭包中 -另一种实现基于类的单例模式的方法是使用一个闭包来保护这个唯一的实例。你可以通过第5章讨论过的“私有静态成员模式”来实现。唯一的秘密就是重写构造函数: +另一种实现基于类的单例模式的方法是使用一个闭包来保护这个唯一的实例。你可以通过第五章讨论过的“私有静态成员模式”来实现。唯一的秘密就是重写构造函数: function Universe() { - // the cached instance + // 缓存实例 var instance = this; - // proceed as normal + // 处理普通逻辑 this.start_time = 0; this.bang = "Big"; - // rewrite the constructor + // 重写构造函数 Universe = function () { return instance; }; } - // testing + // 测试 var uni = new Universe(); var uni2 = new Universe(); uni === uni2; // true -第一次调用时,原始的构造函数被调用并且正常返回this。在后续的调用中,被重写的构造函数被调用。被重写怕这个构造函数可以通过闭包访问私有的instance变量并且将它返回。 +第一次调用时,原来的构造函数被调用并且正常返回`this`。在后续的调用中,被重写的构造函数被调用。被重写的这个构造函数可以通过闭包访问私有的`instance`变量并且将它返回。 -这个实现实际上也是第4章讨论的自定义函数的又一个例子。如我们讨论过的一样,这种模式的缺点是被重写的函数(在这个例子中就是构造函数Universe())将丢失那些在初始定义和重新定义之间添加的属性。在这个例子中,任何添加到Universe()的原型上的属性将不会被链接到使用原来的实现创建的实例上。(注:这里的“原来的实现”是指实例是由未被重写的构造函数创建的,而Universe()则是被重写的构造函数。) +这个实现实际上也是第四章讨论的重定义函数的又一个例子。如我们讨论过的一样,这种模式的缺点是被重写的函数(在这个例子中就是构造函数`Universe()`)将丢失那些在初始定义和重新定义之间添加的属性。在这个例子中,任何添加到`Universe()`的原型上的属性将不会被链接到使用原来的实现创建的实例上。(注:这里的“原来的实现”是指实例是由未被重写的构造函数创建的,而`Universe()`则是被重写的构造函数。) 下面我们通过一些测试来展示这个问题: - // adding to the prototype + // 添加成员到原型 Universe.prototype.nothing = true; var uni = new Universe(); - // again adding to the prototype - // after the initial object is created + // 在创建一个对象后再添加成员到原型 Universe.prototype.everything = true; var uni2 = new Universe(); - Testing: - // only the original prototype was - // linked to the objects + // 测试: + // 只有原始的原型被链接到对象上 uni.nothing; // true uni2.nothing; // true uni.everything; // undefined uni2.everything; // undefined - // that sounds right: + // constructor看起来是对的 uni.constructor.name; // "Universe" - // but that's odd: + // 但其实不然 uni.constructor === Universe; // false -uni.constructor不再和Universe()相同的原因是uni.constructor仍然是指向原来的构造函数,而不是被重新定义的那个。 +`uni.constructor`不再和`Universe()`相同的原因是`uni.constructor`仍然是指向原来的构造函数,而不是被重新定义的那个。 -如果一定被要求让prototype和constructor的指向像我们期望的那样,可以通过一些调整来做到: +如果一定要让`prototype`和`constructor`的指向像我们期望的那样,可以通过一些调整来做到: function Universe() { - // the cached instance + // 缓存实例 var instance; - // rewrite the constructor + // 重写构造函数 Universe = function Universe() { return instance; }; - // carry over the prototype properties + // 重写prototype属性 Universe.prototype = this; - // the instance + // 创建实例 instance = new Universe(); - // reset the constructor pointer + // 重写constructor属性 instance.constructor = Universe; - // all the functionality + // 其它的功能代码 instance.start_time = 0; instance.bang = "Big"; @@ -173,24 +166,23 @@ uni.constructor不再和Universe()相同的原因是uni.constructor仍然是指 现在所有的测试结果都可以像我们期望的那样了: - // update prototype and create instance + // 修改原型,创建对象 Universe.prototype.nothing = true; // true var uni = new Universe(); Universe.prototype.everything = true; // true var uni2 = new Universe(); - // it's the same single instance + // 它们是同一个实例 uni === uni2; // true - // all prototype properties work - // no matter when they were defined + // 所有的原型上的属性都正常工作,不管是什么时候在哪添加的 uni.nothing && uni.everything && uni2.nothing && uni2.everything; // true - // the normal properties work + // 普通成员也可以正常工作 uni.bang; // "Big" - // the constructor points correctly + // constructor指向正确 uni.constructor === Universe; // true -另一种可选的解决方案是将构造函数和实例包在一个立即执行的函数中。当构造函数第一次被调用的时候,它返回一个对象并且将私有的instance指向它。在后续调用时,构造函数只是简单地返回这个私有变量。在这种新的实现下,前面所有的测试代码也会和期望的一样: +另一种可选的解决方案是将构造函数和实例包在一个即时函数中。当构造函数第一次被调用的时候,它返回一个对象并且将私有的`instance`指向它。在后续调用时,构造函数只是简单地返回这个私有变量。在这种新的实现下,前面所有的测试代码也会和期望的一样: var Universe; @@ -206,7 +198,7 @@ uni.constructor不再和Universe()相同的原因是uni.constructor仍然是指 instance = this; - // all the functionality + // 功能代码 this.start_time = 0; this.bang = "Big"; From 3ea8ab2949aa60d116c1d6efc2fce5adfb52482c Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 7 May 2013 12:47:01 +0800 Subject: [PATCH 114/145] =?UTF-8?q?=E5=B7=A5=E5=8E=82=E6=A8=A1=E5=BC=8F=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/chapter7.markdown b/chapter7.markdown index d2f3ce0..6c51f78 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -206,7 +206,6 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 }()); - ## 工厂模式 使用工厂模式的目的就是创建对象。它通常被在类或者类的静态方法中实现,目的是: @@ -214,15 +213,15 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 - 执行在建立相似的对象时进行的一些重复操作 - 让工厂的使用者在编译阶段创建对象时不必知道它的特定类型(类) -第二点在静态的基于类的语言中更重要,因为在(编译阶段)提前不知道类的情况下,创建类的实例是不普通的行为。但在JavaScript中,这部分的实现却是相当容易的事情。 +第二点在静态的基于类的语言中更重要,因为在(编译阶段)提前不知道类的情况下,创建类的实例是一件多少有些特殊的行为。但在JavaScript中,这部分的实现却是相当容易的事情。 -使用工厂方法(或类)创建的对象被设计为从同一个父对象继承;它们是特定的实现一些特殊功能的子类。有些时候这个共同的父对象就是包含工厂方法的同一个类。 +使用工厂方法(或类)创建的对象被设计为从同一个父对象继承;它们是实现一些特定的功能的子类。有些时候这个共同的父对象就是包含工厂方法的同一个类。 我们来看一个示例实现,我们有: -- 一个共同的父构造函数CarMaker。 -- CarMaker的一个静态方法叫factory(),用来创建car对象。 -- 特定的从CarMaker继承而来的构造函数CarMaker.Compact,CarMaker.SUV,CarMaker.Convertible。它们都被定义为父构造函数的静态属性以便保持全局空间干净,同时在需要的时候我们也知道在哪里找到它们。 +- 一个共同的父构造函数`CarMaker()`。 +- `CarMaker()`的一个静态方法叫`factory()`,用来创建`car`对象。 +- 特定的从`CarMaker()`继承而来的构造函数`CarMaker.Compact()`,`CarMaker.SUV()`,`CarMaker.Convertible()`。它们都被定义为父构造函数的静态属性以便保持全局空间干净,同时在需要的时候我们也知道在哪里找到它们。 我们来看一下已经完成的实现会怎么被使用: @@ -237,24 +236,24 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 var corolla = CarMaker.factory('Compact'); -可能是工厂模式中最知名的。你有一个方法可以在运行时接受一个表示类型的字符串,然后它创建并返回了一个和请求的类型一样的对象。这里没有使用new的构造函数,也没有看到任何对象字面量,仅仅只有一个函数根据一个字符串指定的类型创建了对象。 +可能是工厂模式中最为人熟知的。你有一个方法可以在运行时接受一个表示类型的字符串,然后它创建并返回了一个和请求的类型一样的对象。这里没有使用`new`的构造函数,也没有看到任何对象字面量,仅仅只有一个函数根据一个字符串指定的类型创建了对象。 这里是一个工厂模式的示例实现,它能让上面的代码片段工作: - // parent constructor + // 父构造函数 function CarMaker() {} - // a method of the parent + // 父构造函数的方法 CarMaker.prototype.drive = function () { return "Vroom, I have " + this.doors + " doors"; }; - // the static factory method + // 静态工厂方法factory CarMaker.factory = function (type) { var constr = type, - newcar; + newcar; - // error if the constructor doesn't exist + // 如果指定类型的构造函数不存在则报错 if (typeof CarMaker[constr] !== "function") { throw { name: "Error", @@ -262,18 +261,18 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 }; } - // at this point the constructor is known to exist - // let's have it inherit the parent but only once + // 现在我们确认要用到的构造函数是存在的了 + // 让它继承自父构造函数,但只继承一次 if (typeof CarMaker[constr].prototype.drive !== "function") { CarMaker[constr].prototype = new CarMaker(); } - // create a new instance + // 创建一个新实例 newcar = new CarMaker[constr](); - // optionally call some methods and then return... + // 这里可以选择性地调用一些方法,然后返回实例 return newcar; }; - // define specific car makers + // 创建特定类型的构造函数 CarMaker.Compact = function () { this.doors = 4; }; @@ -284,27 +283,26 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 this.doors = 24; }; -工厂模式的实现中没有什么是特别困难的。你需要做的仅仅是寻找请求类型的对象的构造函数。在这个例子中,使用了一个简单的名字转换以便映射对象类型和创建对象的构造函数。继承的部分只是一个公共的重复代码片段的示例,它可以被放到工厂方法中而不是被每个构造函数的类型重复。(译注:指通过原型继承的代码可以在factory方法以外执行,而不是放到factory中每调用一次都要执行一次。) +工厂模式的实现中没有什么是特别困难的,你需要做的仅仅是寻找请求类型的对象构造函数。在这个例子中,使用了一个简单的名字转换以便映射对象类型和创建对象的构造函数。继承的部分只是一个公共的重复代码片段的示例,它可以被放到工厂方法中而不是被每个构造函数的类型所重复。(译注:指原型继承的代码可以在`factory()`方法以外执行,而不是放到`factory()`中每调用一次都要执行一次。) - ### 内置对象工厂 -作为一个“野生的工厂”的例子,我们来看一下内置的全局构造函数Object()。它的行为很像工厂,因为它根据不同的输入创建不同的对象。如果传入一个数字,它会使用Number()构造函数创建一个对象。在传入字符串和布尔值的时候也会发生同样的事情。任何其它的值(包括空值)将会创建一个正常的对象。 +为了说明工厂模式应用之广泛,我们来看一下内置的全局构造函数`Object()`。它的行为很像工厂,因为它根据不同的输入创建不同的对象。如果传入一个数字,它会使用`Number()`构造函数创建一个对象。在传入字符串和布尔值的时候也会发生类似的事情。任何其它的值(包括空值)将会创建一个正常的对象。 -下面是这种行为的例子和测试,注意Object调用时可以不用加new: +下面是这种行为的例子和测试,注意`Object()`调用时可以不用加`new`: var o = new Object(), n = new Object(1), s = Object('1'), b = Object(true); - // test + // 测试 o.constructor === Object; // true n.constructor === Number; // true s.constructor === String; // true b.constructor === Boolean; // true -Object()也是一个工厂这一事实可能没有太多实际用处,仅仅是觉得值得作为一个例子提一下,告诉我们工厂模式是随处可见的。 +`Object()`也是一个工厂这一事实可能没有太多实际用处,仅仅是觉得值得作为一个例子提一下,告诉我们工厂模式是随处可见的。 From 07d5e5584c2d9d3add6b41bad8b8eb2cc5ee3cfb Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 7 May 2013 12:49:22 +0800 Subject: [PATCH 115/145] =?UTF-8?q?=E8=BF=AD=E4=BB=A3=E5=99=A8=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/chapter7.markdown b/chapter7.markdown index 6c51f78..53836fb 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -304,26 +304,24 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 `Object()`也是一个工厂这一事实可能没有太多实际用处,仅仅是觉得值得作为一个例子提一下,告诉我们工厂模式是随处可见的。 - - ## 迭代器 在迭代器模式中,你有一些含有有序聚合数据的对象。这些数据可能在内部用一种复杂的结构存储着,但是你希望提供一种简单的方法来访问这种结构中的每个元素。数据的使用者不需要知道你是怎样组织你的数据的,他们只需要操作一个个独立的元素。 -在迭代器模式中,你的对象需要提供一个next()方法。按顺序调用next()方法必须返回序列中的下一个元素,但是“下一个”在你的特定的数据结构中指什么是由你自己来决定的。 +在迭代器模式中,你的对象需要提供一个`next()`方法。按顺序调用`next()`方法必须返回序列中的下一个元素,但是“下一个”在你的特定的数据结构中指什么是由你自己来决定的。 -假设你的对象叫agg,你可以通过简单地在循环中调用next()来访问每个数据元素,像这样: +假设你的对象叫`agg`,你可以通过简单地在循环中调用`next()`来访问每个数据元素,像这样: var element; while (element = agg.next()) { - // do something with the element ... + // 访问element…… console.log(element); } -在迭代器模式中,聚合对象通常也会提供一个方便的方法hasNext(),这样对象的使用者就可以知道他们已经获取到你数据的最后一个元素。当使用另一种方法——hasNext()——来按顺序访问所有元素时,是像这样的: +在迭代器模式中,聚合对象通常也会提供一个方便的方法`hasNext()`,这样对象的使用者就可以知道他们已经获取到你数据的最后一个元素。当使用`hasNext()`来按顺序访问所有元素时,是像这样的: while (agg.hasNext()) { - // do something with the next element... + // 访问element…… console.log(agg.next()); } From d36dc03e7f826db001c1c28dd0416fcd355597c5 Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 9 May 2013 13:00:55 +0800 Subject: [PATCH 116/145] =?UTF-8?q?=E8=A3=85=E9=A5=B0=E5=99=A8=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 54 ++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/chapter7.markdown b/chapter7.markdown index 53836fb..a383cf0 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -325,39 +325,36 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 console.log(agg.next()); } - ## 装饰器 在装饰器模式中,一些额外的功能可以在运行时被动态地添加到一个对象中。在静态的基于类的语言中,处理这个问题可能是个挑战,但是在JavaScript中,对象本来就是可变的,所以给一个对象添加额外的功能本身并不是什么问题。 -装饰器模式的一个很方便的特性是可以对我们需要的特性进行定制和配置。刚开始时,我们有一个拥有基本功能的对象,然后可以从可用的装饰器中去挑选一些需要用到的去增加这个对象,甚至如果顺序很重要的话,还可以指定增强的顺序。 +装饰器模式的一个很方便的特性是可以对我们需要的特性进行定制和配置。刚开始时,我们有一个拥有基本功能的对象,然后可以从可用的装饰器中去挑选一些需要用到的去增强这个对象,如果有必要的话,还可以指定增强的顺序。 - ### 用法 -我们来看一下这个模式的示例用法。假设你正在做一个卖东西的web应用,每个新交易是一个新的sale对象。这个对象“知道”交易的价格并且可以通过调用sale.getPrice()方法返回。根据环境的不同,你可以开始用一些额外的功能来装饰这个对象。假设一个场景是这笔交易是发生在加拿大的一个省Québec,在这种情况下,购买者需要付联邦税和Québec省税。根据装饰器模式的用法,你需要指明使用联邦税装饰器和Québec省税装饰器来装饰这个对象。然后你还可以给这个对象装饰一些价格格式的功能。这个场景的使用方式可能是像这样: +我们来看一下这个模式的用法示例。假设你正在做一个卖东西的web应用,每个新交易是一个新的`sale`对象。这个对象“知道”交易的价格并且可以通过调用`sale.getPrice()`方法返回。根据环境的不同,你可以开始用一些额外的功能来装饰这个对象。假设一个场景是这笔交易是发生在加拿大的一个省Québec,在这种情况下,购买者需要付联邦税和Québec省税。根据装饰器模式的用法,你需要指明使用联邦税装饰器和Québec省税装饰器来装饰这个对象。然后你还可以给这个对象装饰一些价格格式的功能。这个场景的使用方式可能是像这样: - var sale = new Sale(100); // the price is 100 dollars - sale = sale.decorate('fedtax'); // add federal tax - sale = sale.decorate('quebec'); // add provincial tax - sale = sale.decorate('money'); // format like money + var sale = new Sale(100); // 价格是100美元 + sale = sale.decorate('fedtax'); // 加上联邦税 + sale = sale.decorate('quebec'); // 加上省税 + sale = sale.decorate('money'); // 格式化 sale.getPrice(); // "$112.88" 在另一种场景下,购买者在一个不需要交省税的省,并且你想用加拿大元的格式来显示价格,你可以这样做: - var sale = new Sale(100); // the price is 100 dollars - sale = sale.decorate('fedtax'); // add federal tax - sale = sale.decorate('cdn'); // format using CDN + var sale = new Sale(100); // 价格是100美元 + sale = sale.decorate('fedtax'); // 加上联邦税 + sale = sale.decorate('cdn'); // 用加拿大元格式化 sale.getPrice(); // "CDN$ 105.00" -如你所见,这是一种在运行时很灵活的方法来添加功能和调整对象。我们来看一下如何来实现这种模式。 +如你所见,这种方法可以在运行时很灵活地添加功能和调整对象。我们来看一下如何来实现这种模式。 - ### 实现 一种实现装饰器模式的方法是让每个装饰器成为一个拥有应该被重写的方法的对象。每个装饰器实际上是继承自已经被前一个装饰器增强过的对象。装饰器的每个方法都会调用父对象(继承自的对象)的同名方法并取得值,然后做一些额外的处理。 -最终的效果就是当你在第一个例子中调用sale.getPrice()时,实际上是在调用money装饰器的方法(图7-1)。但是因为每个装饰器会先调用父对象的方法,money的getPrice()先调用quebec的getPrice(),而它又会去调用fedtax的getPrice()方法,依次类推。这个链会一直走到原始的未经装饰的由Sale()构造函数实现的getPrice()。 +最终的效果就是当你在第一个例子中调用`sale.getPrice()`时,实际上是在调用`money`装饰器的方法(图7-1)。但是因为每个装饰器会先调用父对象的方法,`money`的`getPrice()`先调用`quebec`的`getPrice()`,而它又会去调用`fedtax`的`getPrice()`方法,依次类推。这个链会一直走到原始的未经装饰的由`Sale()`构造函数实现的`getPrice()`。 ![图7-1 装饰器模式的实现](./Figure/chapter7/7-1.jpg) 图7-1 装饰器模式的实现 @@ -375,7 +372,7 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 Sale.decorators = {}; -我们来看一个装饰器的例子。这是一个对象,实现了一个自定义的getPrice()方法。注意这个方法首先从父对象的方法中取值然后修改这个值: +我们来看一个装饰器的例子。这是一个对象,实现了一个自定义的`getPrice()`方法。注意这个方法首先从父对象的方法中取值然后修改这个值: Sale.decorators.fedtax = { getPrice: function () { @@ -385,7 +382,7 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 } }; -使用类似的方法我们可以实现任意多个需要的其它装饰器。他们的实现方式像插件一样来扩展核心的Sale()的功能。他们甚至可以被放到额外的文件中,被第三方的开发者来开发和共享: +使用类似的方法我们可以实现任意多个需要的装饰器。它们的实现方式像插件一样来扩展核心的`Sale()`的功能。它们甚至可以被放到额外的文件中,被第三方的开发者来开发和共享: Sale.decorators.quebec = { getPrice: function () { @@ -407,11 +404,11 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 } }; -最后我们来看decorate()这个神奇的方法,它把所有上面说的片段都串起来了。记得它是这样被调用的: +最后我们来看`decorate()`这个神奇的方法,它把所有上面说的片段都串起来了。记住它是这样被调用的: sale = sale.decorate('fedtax'); -字符串'fedtax'对应在Sale.decorators.fedtax中实现的对象。被装饰过的最新的对象newobj将从现在有的对象(也就是this对象,它要么是原始的对象,要么是经过最后一个装饰器装饰过的对象)中继承。实现这一部分需要用到前面章节中提到的临时构造函数模式。我们也设置一个uber属性给newobj以便子对象可以访问到父对象。然后我们从装饰器中复制所有额外的属性到被装饰的对象newobj中。最后,在我们的例子中,newobj被返回并且成为被更新过的sale对象。 +字符串`'fedtax'`对应在`Sale.decorators.fedtax`中实现的对象。被装饰过的最新的对象`newobj`将从现在有的对象(也就是`this`对象,它要么是原始的对象,要么是经过最后一个装饰器装饰过的对象)中继承。实现这一部分需要用到前面章节中提到的临时构造函数模式。我们也设置一个`uber`属性给`newobj`以便子对象可以访问到父对象。然后我们从装饰器中复制所有额外的属性到被装饰的对象`newobj`中。最后,在我们的例子中,`newobj`被返回并且成为被更新过的`sale`对象。 Sale.prototype.decorate = function (decorator) { var F = function () {}, @@ -428,29 +425,28 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 return newobj; }; - ### 使用列表实现 -我们来看另一个明显不同的实现方法,受益于JavaScript的动态特性,它完全不需要使用继承。同时,我们也可以简单地将前一个方面的结果作为参数传给下一个方法,而不需要每一个方法都去调用前一个方法。 +我们来看另一个明显不同的实现方法,得益于JavaScript的动态特性,它完全不需要使用继承。同时,我们也可以简单地将前一个方面的结果作为参数传给下一个方法,而不需要每一个方法都去调用前一个方法。 这样的实现方法还允许很容易地反装饰(undecorating)或者撤销一个装饰,这仅仅需要从一个装饰器列表中移除一个条目。 -用法示例也会明显简单一些,因为我们不需要将decorate()的返回值赋值给对象。在这个实现中,decorate()不对对象做任何事情,它只是简单地将装饰器加入到一个列表中: +用法示例也会明显简单一些,因为我们不需要将`decorate()`的返回值赋值给对象。在这个实现中,`decorate()`不对对象做任何事情,它只是简单地将装饰器加入到一个列表中: - var sale = new Sale(100); // the price is 100 dollars - sale.decorate('fedtax'); // add federal tax - sale.decorate('quebec'); // add provincial tax - sale.decorate('money'); // format like money + var sale = new Sale(100); // 价格是100美元 + sale.decorate('fedtax'); // 加上联邦税 + sale.decorate('quebec'); // 加上省税 + sale.decorate('money'); // 格式化 sale.getPrice(); // "$112.88" -Sale()构造函数现在有了一个作为自己属性的装饰器列表: +`Sale()`构造函数现在有了一个作为自己属性存在的装饰器列表: function Sale(price) { this.price = price || 100; this.decorators_list = []; } -可用的装饰器仍然被实现为Sale.decorators的属性。注意getPrice()方法现在更简单了,因为它们不需要调用父对象的getPrice()来获取结果,结果已经作为参数传递给它们了: +可用的装饰器仍然被实现为`Sale.decorators`的属性。注意`getPrice()`方法现在更简单了,因为它们不需要调用父对象的`getPrice()`来获取结果,结果已经作为参数传递给它们了: Sale.decorators = {}; @@ -472,7 +468,7 @@ Sale()构造函数现在有了一个作为自己属性的装饰器列表: } }; -最有趣的部分发生在父对象的decorate()和getPrice()方法上。在前一种实现方式中,decorate()还是多少有些复杂,而getPrice()十分简单。在这种实现方式中事情反过来了:decorate()只需要往列表中添加条目而getPrice()做了所有的工作。这些工作包括遍历现在添加的装饰器的列表,然后调用它们的getPrice()方法,并将结果传递给前一个: +最有趣的部分发生在父对象的`decorate()`和`getPrice()`方法上。在前一种实现方式中,`decorate()`还是多少有些复杂,而`getPrice()`十分简单。在这种实现方式中事情反过来了:`decorate()`只需要往列表中添加条目而`getPrice()`做了其它所有的工作,包括遍历现在添加的装饰器的列表,然后调用它们的`getPrice()`方法并将结果传递下去: Sale.prototype.decorate = function (decorator) { this.decorators_list.push(decorator); @@ -490,7 +486,7 @@ Sale()构造函数现在有了一个作为自己属性的装饰器列表: return price; }; -装饰器模式的第二种实现方式更简单一些,并且没有引入继承。装饰的方法也会简单。所有的工作都由“同意”被装饰的方法来做。在这个示例实现中,getPrice()是唯一被允许装饰的方法。如果你想有更多可以被装饰的方法,那遍历装饰器列表的工作就需要由每个方法重复去做。但是,这可以很容易地被抽象到一个辅助方法中,给它传一个方法然后使这个方法“可被装饰”。如果这样实现的话,decorators_list属性就应该是一个对象,它的属性名字是方法名,值是装饰器对象的数组。 +装饰器模式的第二种实现方式更简单一些,并且没有引入继承。装饰的方法也会简单。所有的工作都由“同意”被装饰的方法来做。在这个示例实现中,`getPrice()`是唯一被允许装饰的方法。如果你想有更多可以被装饰的方法,那遍历装饰器列表的工作就需要由每个方法重复去做。但是,这可以很容易地被抽象到一个辅助方法中,给它传一个方法然后使这个方法“可被装饰”。如果这样实现的话,`decorators_list`属性就应该是一个对象,它的属性名字是方法名,值是装饰器对象的数组。 ## 策略模式 From ce36b39c4d263f6417e4bb3549ae393044fd94a8 Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 9 May 2013 13:17:16 +0800 Subject: [PATCH 117/145] =?UTF-8?q?=E7=AD=96=E7=95=A5=E6=A8=A1=E5=BC=8F=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 293 +++++++++++++++++++++++----------------------- 1 file changed, 145 insertions(+), 148 deletions(-) diff --git a/chapter7.markdown b/chapter7.markdown index a383cf0..58789e1 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -488,42 +488,40 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 装饰器模式的第二种实现方式更简单一些,并且没有引入继承。装饰的方法也会简单。所有的工作都由“同意”被装饰的方法来做。在这个示例实现中,`getPrice()`是唯一被允许装饰的方法。如果你想有更多可以被装饰的方法,那遍历装饰器列表的工作就需要由每个方法重复去做。但是,这可以很容易地被抽象到一个辅助方法中,给它传一个方法然后使这个方法“可被装饰”。如果这样实现的话,`decorators_list`属性就应该是一个对象,它的属性名字是方法名,值是装饰器对象的数组。 - ## 策略模式 策略模式允许在运行的时候选择算法。你的代码的使用者可以在处理特定任务的时候根据即将要做的事情的上下文来从一些可用的算法中选择一个。 -使用策略模式的一个例子是解决表单验证的问题。你可以创建一个validator对象,有一个validate()方法。这个方法被调用时不用区分具体的表单类型,它总是会返回同样的结果——一个没有通过验证的列表和错误信息。 +使用策略模式的一个例子是解决表单验证的问题。你可以创建一个`validator`对象,有一个`validate()`方法。这个方法被调用时不用区分具体的表单类型,它总是会返回同样的结果——一个没有通过验证的列表和错误信息。 -但是根据具体的需要验证的表单和数据,你代码的使用者可以选择进行不同类别的检查。你的validator选择最佳的策略来处理这个任务,然后将具体的数据检查工作交给合适的算法去做。 +但是根据具体的需要验证的表单和数据,你代码的使用者可以选择进行不同类别的检查。你的`validator`选择最佳的策略来处理这个任务,然后将具体的数据检查工作交给合适的算法去做。 - ### 数据验证示例 假设你有一个下面这样的数据,它可能来自页面上的一个表单,你希望验证它是不是有效的数据: var data = { - first_name: "Super", - last_name: "Man", - age: "unknown", - username: "o_O" + first_name: "Super", + last_name: "Man", + age: "unknown", + username: "o_O" }; -对这个例子中的validator,它需要知道哪个是最佳策略,因此你需要先配置它,给它设定好规则以确定哪些是有效的数据。 +对这个例子中的`validator`而言,它需要知道哪个是最佳策略,因此你需要先配置它,给它设定好规则以确定哪些是有效的数据。 假设你不需要姓,名字可以接受任何内容,但要求年龄是一个数字,并且用户名只允许包含字母和数字。配置可能是这样的: validator.config = { - first_name: 'isNonEmpty', - age: 'isNumber', - username: 'isAlphaNum' + first_name: 'isNonEmpty', + age: 'isNumber', + username: 'isAlphaNum' }; -现在validator对象已经有了用来处理数据的配置,你可以调用validate()方法,然后将任何验证错误打印到控制台上: +现在`validator`对象已经有了用来处理数据的配置,你可以调用`validate()`方法,然后将验证错误打印到控制台上: validator.validate(data); if (validator.hasErrors()) { - console.log(validator.messages.join("\n")); + console.log(validator.messages.join("\n")); } 它可能会打印出这样的信息: @@ -531,89 +529,88 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 Invalid value for *age*, the value can only be a valid number, e.g. 1, 3.14 or 2010 Invalid value for *username*, the value can only contain characters and numbers, no special symbols -现在我们来看一下这个validator是如何实现的。所有可用的用来检查的逻辑都是拥有一个validate()方法的对象,它们还有一行辅助信息用来显示错误信息: +现在我们来看一下这个`validator`是如何实现的。所有可用的用来验证的逻辑都是拥有一个`validate()`方法的对象,它们还有一行辅助信息用来显示错误信息: - // checks for non-empty values + // 验证空值 validator.types.isNonEmpty = { - validate: function (value) { - return value !== ""; - }, - instructions: "the value cannot be empty" + validate: function (value) { + return value !== ""; + }, + instructions: "the value cannot be empty" }; - // checks if a value is a number + // 验证数字 validator.types.isNumber = { - validate: function (value) { - return !isNaN(value); - }, - instructions: "the value can only be a valid number, e.g. 1, 3.14 or 2010" + validate: function (value) { + return !isNaN(value); + }, + instructions: "the value can only be a valid number, e.g. 1, 3.14 or 2010" }; - // checks if the value contains only letters and numbers + // 验证是否只包含字母和数字 validator.types.isAlphaNum = { - validate: function (value) { - return !/[^a-z0-9]/i.test(value); - }, - instructions: "the value can only contain characters and numbers, no special symbols" + validate: function (value) { + return !/[^a-z0-9]/i.test(value); + }, + instructions: "the value can only contain characters and numbers, no special symbols" }; -最后,validator对象的核心是这样的: +最后,`validator`对象的核心是这样的: var validator = { - // all available checks - types: {}, - - // error messages in the current - // validation session - messages: [], - - // current validation config - // name: validation type - config: {}, - - // the interface method - // `data` is key => value pairs - validate: function (data) { - - var i, msg, type, checker, result_ok; - - // reset all messages - this.messages = []; - for (i in data) { - - if (data.hasOwnProperty(i)) { - - type = this.config[i]; - checker = this.types[type]; - - if (!type) { - continue; // no need to validate - } - if (!checker) { // uh-oh - throw { - name: "ValidationError", - message: "No handler to validate type " + type - }; - } - - result_ok = checker.validate(data[i]); - if (!result_ok) { - msg = "Invalid value for *" + i + "*, " + checker.instructions; - this.messages.push(msg); - } - } - } - return this.hasErrors(); - }, - - // helper - hasErrors: function () { - return this.messages.length !== 0; - } + // 所有可用的验证类型 + types: {}, + + // 本次验证所有的错误消息 + messages: [], + + // 本次验证的配置,格式为: + // name: validation type + config: {}, + + // 接口方法 + // `data` 是名值对 + validate: function (data) { + + var i, msg, type, checker, result_ok; + + // 重置所有的错误消息 + this.messages = []; + for (i in data) { + + if (data.hasOwnProperty(i)) { + + type = this.config[i]; + checker = this.types[type]; + + if (!type) { + continue; // 不需要验证 + } + if (!checker) { // 没有对应的验证类型 + throw { + name: "ValidationError", + message: "No handler to validate type " + type + }; + } + + result_ok = checker.validate(data[i]); + if (!result_ok) { + msg = "Invalid value for *" + i + "*, " + checker.instructions; + this.messages.push(msg); + } + } + } + return this.hasErrors(); + }, + + // 辅助方法 + hasErrors: function () { + return this.messages.length !== 0; + } }; -如你所见,validator对象是通用的,在所有的需要验证的场景下都可以保持这个样子。改进它的办法就是增加更多类型的检查。如果你将它用在很多页面上,每快你就会有一个非常好的验证类型的集合。然后在每个新的使用场景下你需要做的仅仅是配置validator然后调用validate()方法。 +如你所见,`validator`对象是通用的,在所有的需要验证的场景下都可以保持这个样子。改进它的办法就是增加更多类型的检查。如果你将它用在很多页面上,那么很快你就会有一个非常好的验证类型的集合。然后在新的使用场景下使用时你需要做的仅仅是配置`validator`然后调用`validate()`方法。 ## 外观模式 @@ -632,35 +629,35 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 这是两个有不同目的的相互独立的方法,他们也应该被保持独立,但与此同时,他们也经常被一起调用。所以为了不在应用中到处重复调用这两个方法,你可以创建一个外观方法来调用它们: var myevent = { - // ... - stop: function (e) { - e.preventDefault(); - e.stopPropagation(); - } - // ... + // ... + stop: function (e) { + e.preventDefault(); + e.stopPropagation(); + } + // ... }; 外观模式也适用于一些浏览器脚本的场景,即将浏览器的差异隐藏在一个外观方法下面。继续前面的例子,你可以添加一些处理IE中事件API的代码: var myevent = { - // ... - stop: function (e) { - // others - if (typeof e.preventDefault === "function") { - e.preventDefault(); - } - if (typeof e.stopPropagation === "function") { - e.stopPropagation(); - } - // IE - if (typeof e.returnValue === "boolean") { - e.returnValue = false; - } - if (typeof e.cancelBubble === "boolean") { - e.cancelBubble = true; - } - } - // ... + // ... + stop: function (e) { + // others + if (typeof e.preventDefault === "function") { + e.preventDefault(); + } + if (typeof e.stopPropagation === "function") { + e.stopPropagation(); + } + // IE + if (typeof e.returnValue === "boolean") { + e.returnValue = false; + } + if (typeof e.cancelBubble === "boolean") { + e.cancelBubble = true; + } + } + // ... }; 外观模式在做一些重新设计和重构工作时也很有用。当你想用一个不同的实现来替换某个对象的时候,你可能需要工作相当长一段时间(一个复杂的对象),与此同时,一些使用这个新对象的代码也在被同步编写。你可以先想好新对象的API,然后使用新的API创建一个外观方法在旧的对象前面。使用这种方式,当你完全替换到旧的对象的时候,你只需要修改少量客户代码,因为新的客户代码已经是在使用新的API了。 @@ -852,48 +849,48 @@ proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后 下面是proxy对象的代码: var proxy = { - ids: [], - delay: 50, - timeout: null, - callback: null, - context: null, - makeRequest: function (id, callback, context) { - // add to the queue - this.ids.push(id); - - this.callback = callback; - this.context = context; - - // set up timeout - if (!this.timeout) { - this.timeout = setTimeout(function () { - proxy.flush(); - }, this.delay); - } - }, - flush: function () { - - http.makeRequest(this.ids, "proxy.handler"); - - // clear timeout and queue - this.timeout = null; - this.ids = []; - - }, - handler: function (data) { - var i, max; - - // single video - if (parseInt(data.query.count, 10) === 1) { - proxy.callback.call(proxy.context, data.query.results.Video); - return; - } - - // multiple videos - for (i = 0, max = data.query.results.Video.length; i < max; i += 1) { - proxy.callback.call(proxy.context, data.query.results.Video[i]); - } - } + ids: [], + delay: 50, + timeout: null, + callback: null, + context: null, + makeRequest: function (id, callback, context) { + // add to the queue + this.ids.push(id); + + this.callback = callback; + this.context = context; + + // set up timeout + if (!this.timeout) { + this.timeout = setTimeout(function () { + proxy.flush(); + }, this.delay); + } + }, + flush: function () { + + http.makeRequest(this.ids, "proxy.handler"); + + // clear timeout and queue + this.timeout = null; + this.ids = []; + + }, + handler: function (data) { + var i, max; + + // single video + if (parseInt(data.query.count, 10) === 1) { + proxy.callback.call(proxy.context, data.query.results.Video); + return; + } + + // multiple videos + for (i = 0, max = data.query.results.Video.length; i < max; i += 1) { + proxy.callback.call(proxy.context, data.query.results.Video[i]); + } + } }; 了解代理模式后就在只简单地改动一下原来的代码的情况下,将多个web service请求合并为一个。 From 08ca72c28dfd66fd4867ceba15b429ee9b38c433 Mon Sep 17 00:00:00 2001 From: TooBug Date: Thu, 9 May 2013 13:21:44 +0800 Subject: [PATCH 118/145] =?UTF-8?q?=E5=A4=96=E8=A7=82=E6=A8=A1=E5=BC=8F=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/chapter7.markdown b/chapter7.markdown index 58789e1..55cb4e6 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -612,37 +612,36 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 如你所见,`validator`对象是通用的,在所有的需要验证的场景下都可以保持这个样子。改进它的办法就是增加更多类型的检查。如果你将它用在很多页面上,那么很快你就会有一个非常好的验证类型的集合。然后在新的使用场景下使用时你需要做的仅仅是配置`validator`然后调用`validate()`方法。 - ## 外观模式 -外观模式是一种很简单的模式,它只是为对象提供了更多的可供选择的接口。使方法保持短小而不是处理太多的工作是一种很好的实践。在这种实践的指导下,你会有一大堆的方法,而不是一个有着非常多参数的uber方法。有些时候,两个或者更多的方法会经常被一起调用。在这种情况下,创建另一个将这些重复调用包裹起来的方法就变得意义了。 +外观模式是一种很简单的模式,它只是为对象提供了更多的可供选择的接口。使方法保持短小而不是处理太多的工作是一种很好的实践。在这种实践的指导下,你会有一大堆的方法,而不是一个有着非常多参数的`uber`方法。有些时候,两个或者更多的方法会经常被一起调用。在这种情况下,创建另一个将这些重复调用包裹起来的方法就变得意义了。 -例如,在处理浏览器事件的时候,有以下的事件: +例如,在处理浏览器事件的时候,有以下的方法: -- stopPropagation() +- `stopPropagation()` 阻止事件冒泡到父节点 -- preventDefault() +- `preventDefault()` 阻止浏览器执行默认动作(如打开链接或者提交表单) 这是两个有不同目的的相互独立的方法,他们也应该被保持独立,但与此同时,他们也经常被一起调用。所以为了不在应用中到处重复调用这两个方法,你可以创建一个外观方法来调用它们: var myevent = { - // ... + // …… stop: function (e) { e.preventDefault(); e.stopPropagation(); } - // ... + // …… }; 外观模式也适用于一些浏览器脚本的场景,即将浏览器的差异隐藏在一个外观方法下面。继续前面的例子,你可以添加一些处理IE中事件API的代码: var myevent = { - // ... + // …… stop: function (e) { - // others + // 其它浏览器 if (typeof e.preventDefault === "function") { e.preventDefault(); } @@ -657,10 +656,10 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 e.cancelBubble = true; } } - // ... + // …… }; -外观模式在做一些重新设计和重构工作时也很有用。当你想用一个不同的实现来替换某个对象的时候,你可能需要工作相当长一段时间(一个复杂的对象),与此同时,一些使用这个新对象的代码也在被同步编写。你可以先想好新对象的API,然后使用新的API创建一个外观方法在旧的对象前面。使用这种方式,当你完全替换到旧的对象的时候,你只需要修改少量客户代码,因为新的客户代码已经是在使用新的API了。 +外观模式在做一些重新设计和重构工作时也很有用。当你想用一个不同的实现来替换某个对象的时候,你可能需要花相当长一段时间才能完成(一个复杂的对象),与此同时,一些使用这个新对象的代码也在被同步编写。你可以先想好新对象的API,然后在旧的对象前面使用新的API创建一个外观方法。使用这种方式,当你完全替换掉旧的对象的时候,你只需要修改少量的调用代码,因为新的代码已经是在使用新的API了。 ## 代理模式 From dd37788bcabc6e3e33a48ae1a446a4b99dd58c8d Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 12 May 2013 18:35:10 +0800 Subject: [PATCH 119/145] =?UTF-8?q?=E5=88=86=E7=A6=BB=20=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter8.markdown | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/chapter8.markdown b/chapter8.markdown index 57006b3..5b224bb 100644 --- a/chapter8.markdown +++ b/chapter8.markdown @@ -1,6 +1,6 @@ # DOM和浏览器中的模式 -在本书的前面几章中,我们主要关注了JavaScript核心(ECMAScript),并没有涉及太多关于在浏览器中使用JavaScript的内容。在本章,我们将探索一些在浏览器环境中的模式,因为这是最常见的JavaScript程序环境。浏览器脚本编程也是大部分不喜欢JavaScript的人对这门语言的认知。这当然是可以理解,因为在浏览器中有非常多不一致的宿主对象和DOM实现。很明显,任何能够减轻客户端脚本编程的痛楚的最佳初中都是大有益处的。 +在本书的前面几章中,我们主要关注了JavaScript核心(ECMAScript),并没有涉及太多关于在浏览器中使用JavaScript的内容。在本章,我们将探索一些在浏览器环境中的模式,因为这是最常见的JavaScript程序环境。浏览器脚本编程也是大部分不喜欢JavaScript的人对这门语言的认知。这当然是可以理解,因为在浏览器中有非常多不一致的宿主对象和DOM实现。很明显,任何能够减轻客户端脚本编程的痛楚的最佳实践都是大有益处的。 在本章中,你会看到一些零散的模式,包括DOM编程、事件处理、远程脚本、页面脚本的加载策略以及将JavaScript部署到生产环境的步骤。 @@ -20,7 +20,7 @@ JavaScript,用来处理用户交互和页面的动态变化 -尽可能地将这三者分离可以加强应用在各种用户代理(译注:user agent,即为用户读取页面并呈现的软件,一般指浏览器)的可到达性(译注:delivery,指可被用户代理接受并理解的程度),比如图形浏览器、纯文本浏览器、用于残障人士的辅助技术、移动设备等等。分离常常是和渐进增强的思想一起实现的,我们从一个给最简单的用户代理的最基础的体验(纯HTML)开始,当用户代理的兼容性提升时再添加更多的可以为体验加分的东西。如果浏览器支持CSS,那么用户会看到文档更好的呈现。如果浏览器支持JavaScript,那文档会更像一个应用,提供更多的特性来增强用户体验。 +尽可能地将这三者分离可以加强应用在各种用户代理(译注:user agent,即为用户读取页面并呈现的软件,一般指浏览器)的可到达性(译注:delivery,指可被用户代理接受并理解的程度),比如图形浏览器、纯文本浏览器、用于残障人士的辅助技术、移动设备等等。分离常常是和渐进增强的思想一起实现的,我们从一个最基础的体验(纯HTML)开始,它将被用于最简单的用户代理,当用户代理的兼容性提升时再添加更多的可以为体验加分的东西。如果浏览器支持CSS,那么用户会看到文档更好的呈现。如果浏览器支持JavaScript,那文档会更像一个应用,有更多用来增强用户体验的特性。 在实践中,分离意味者: @@ -29,21 +29,21 @@ - 不要使用内联的事件处理(如onclick)或者是内联的style属性,因为它们不属于内容层 - 使用语义化的HTML元素,比如头部和列表等 -JavaScript(行为)层的地位不应该很显赫,也就是说它不应该成为页面正常工作必须的东西,不应该使得用户在使用不支持的浏览器操作时存在障碍。它只应该被用来增强页面。 +JavaScript(行为)层的地位不应该很显赫,也就是说它不应该成为页面正常工作必须依赖的东西,不应该使得用户在使用不支持的浏览器操作时存在障碍。它只应该被用来增强页面。 -通常比较优雅的用来处理浏览器差异的方法是特性检测。它的思想是你不应该使用浏览器类型检测来决定代码的逻辑,而是应该检测在当前环境中你需要使用的某个方法或者是属性是否存在。浏览器检测一般认为是一种“反模式”(译注:anitpattern,指不好的模式)。虽然有的情况下不可避免要使用,但它应该是最后考虑的选择,并且应该只在特性检测没有办法给出明确答案(或者造成明显性能问题)的时候使用: +通常比较优雅的用来处理浏览器差异的方法是特性检测,它的思想是你不应该使用浏览器类型检测来决定代码的逻辑,而是应该检测在当前环境中你需要使用的某个方法或者是属性是否存在。浏览器检测一般认为是一种“反模式”,虽然有的情况下不可避免要使用,但它应该是最后才考虑的选择,并且应该只在特性检测没有办法给出明确答案(或者造成明显性能问题)的时候使用: - // antipattern + // 反模式 if (navigator.userAgent.indexOf('MSIE') !== −1) { document.attachEvent('onclick', console.log); } - // better + // 更好的方式 if (document.attachEvent) { document.attachEvent('onclick', console.log); } - // or even more specific + // 或者还可以再具体一点 if (typeof document.attachEvent !== "undefined") { document.attachEvent('onclick', console.log); } From 5d7c98d1e96e93b6827f3ddaef4d3bcc71a3360e Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 12 May 2013 19:46:50 +0900 Subject: [PATCH 120/145] =?UTF-8?q?DOM=E7=BC=96=E7=A8=8B=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter8.markdown | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/chapter8.markdown b/chapter8.markdown index 5b224bb..69a9488 100644 --- a/chapter8.markdown +++ b/chapter8.markdown @@ -52,9 +52,9 @@ JavaScript(行为)层的地位不应该很显赫,也就是说它不应该 ## DOM编程 -操作页面的DOM树是在客户端JavaScript编程中最普遍的动作。这也是导致开发者头疼的最主要原因(这也导致了JavaScript名声不好),因为DOM方法在不同的浏览器中实现得有很多差异。这也是为什么使用一个抽象了浏览器差异的JavaScript库能显著提高开发速度的原因。 +操作页面的DOM树是在客户端JavaScript编程中最普遍的行为。这也是导致开发者头疼的最主要原因(这也导致了JavaScript名声不好),因为DOM方法在不同的浏览器中实现得有很多差异。这也是为什么使用一个抽象了浏览器差异的JavaScript库能显著提高开发速度的原因。 -我们来看一些在访问和修改DOM树时推荐的模式,主要考虑点是性能方面。 +我们来看一些在访问和修改DOM树时推荐的模式,主要考虑性能方面。 ### DOM访问 @@ -62,31 +62,32 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 一个原则就是DOM访问的次数应该被减少到最低,这意味者: -- 避免在环境中访问DOM +- 避免在循环中访问DOM - 将DOM引用赋给本地变量,然后操作本地变量 - 当可能的时候使用selectors API -- 遍历HTML collections时缓存length(见第2章) +- 遍历HTML collections时缓存`length`(见第二章) -看下面例子中的第二个(better)循环,尽管它看起来更长一些,但却要快上几十上百倍(取决于具体浏览器): +看下面例子中的第二个循环,尽管它看起来更长一些,但却要快上几十上百倍(取决于具体浏览器): - // antipattern + // 反模式 for (var i = 0; i < 100; i += 1) { document.getElementById("result").innerHTML += i + ", "; } - // better - update a local variable var i, content = ""; + // 更好的方式 - 更新本地变量 + var i, content = ""; for (i = 0; i < 100; i += 1) { content += i + ","; } document.getElementById("result").innerHTML += content; -在下一个代码片段中,第二个例子(使用了本地变量style)更好,尽管它需要多写一行代码,还需要多定义一个变量: +在下一个代码片段中,第二个例子(使用了本地变量`style`)更好,尽管它需要多写一行代码,还需要多定义一个变量: - // antipattern + // 反模式 var padding = document.getElementById("result").style.padding, margin = document.getElementById("result").style.margin; - // better + // 更好的方式 var style = document.getElementById("result").style, padding = style.padding, margin = style.margin; @@ -96,22 +97,22 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 document.querySelector("ul .selected"); document.querySelectorAll("#widget .class"); -这两个方法接受一个CSS选择器字符串,返回匹配这个选择器的DOM列表(译注:querySelector只返回第一个匹配的DOM)。selectors API在现代浏览器(以及IE8+)可用,它总是会比你使用其它DOM方法来做同样的选择要快。主流的JavaScript库的最近版本都已经使用了这个API,所以你有理由去检查你的项目,确保使用的是最新版本。 +这两个方法接受一个CSS选择器字符串,返回匹配这个选择器的DOM列表(译注:`querySelector`只返回第一个匹配的DOM)。selectors API在现代浏览器(以及IE8+)中可用,它总是会比你使用其它DOM方法来做同样的选择要快。主流的JavaScript库的最新版本都已经使用了这个API,所以你应该去检查你的项目,确保使用的是最新版本。 -给你经常访问的元素加上一个id属性也是有好处的,因为document.getElementById(myid)是找到一个DOM元素最容易也是最快的方法。 +给你经常访问的元素加上一个`id`属性也是有好处的,因为`document.getElementById(myid)`是找到一个DOM元素最容易也是最快的方法。 ### DOM操作 -除了访问DOM元素之外,你可能经常需要改变它们、删除其中的一些或者是添加新的元素。更新DOM会导致浏览器重绘(repaint)屏幕,也经常导致重排(reflow)(重新计算元素的位置),这些操作代价是很高的。 +除了访问DOM元素之外,你可能经常需要改变它们、删除其中的一些或者是添加新的元素。更新DOM会导致浏览器重绘(repaint)屏幕,也经常导致重排(reflow,重新计算元素的位置),这些操作代价是很高的。 -再说一次,通用的原则仍然是尽量少地更新DOM,这意味着我们可以将变化集中到一起,然后在“活动的”(live)文档树之外去执行这些变化。 +还是那句话,原则是尽量少地更新DOM,这意味着我们可以将变化集中到一起,然后在“活动的”(live)文档树之外去执行这些变化。 当你需要添加一棵相对较大的子树的时候,你应该在完成这棵树的构建之后再放到文档树中。为了达到这个目的,你可以使用文档碎片(document fragment)来包含你的节点。 不要这样添加节点: - // antipattern - // appending nodes as they are created + // 反模式 + // 在节点创建后就插入文档 var p, t; @@ -145,16 +146,16 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 document.body.appendChild(frag); -这个例子和前面例子中每段更新一次相比,文档树只被更新了一下,只导致一次重排/重绘。 +这个例子和前面例子中每段更新一次相比,文档树只被更新了一次,只导致一次重排/重绘。 当你添加新的节点到文档中时,文档碎片很有用。当你需要更新已有的节点时,你也可以将这些变化集中。你可以将你要修改的子树的父节点克隆一份,然后对克隆的这份做修改,完成之后再去替换原来的元素。 var oldnode = document.getElementById('result'), clone = oldnode.cloneNode(true); - // work with the clone... + // 修改克隆后的节点…… - // when you're done: + // 结束修改之后: oldnode.parentNode.replaceChild(clone, oldnode); ## 事件 From b0052791cab69d86a70ca276bdcd700642ab140d Mon Sep 17 00:00:00 2001 From: TooBug Date: Sun, 12 May 2013 19:00:54 +0800 Subject: [PATCH 121/145] =?UTF-8?q?=E4=BA=8B=E4=BB=B6=20=E6=A0=A1=E5=AF=B9?= =?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 --- chapter8.markdown | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/chapter8.markdown b/chapter8.markdown index 69a9488..154f414 100644 --- a/chapter8.markdown +++ b/chapter8.markdown @@ -160,13 +160,13 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 ## 事件 -在浏览器脚本编程中,另一块充满兼容性问题并且带来很多不愉快的区域就是浏览器事件,比如click,mouseover等等。同样的,一个JavaScript库可以解决支持IE(9以下)和W3C标准实现的双倍工作量。 +在浏览器脚本编程中,另一块充满兼容性问题并且带来很多不愉快的区域就是浏览器事件,比如`click`,`mouseover`等等。同样的,一个JavaScript库可以解决支持IE(9以下)和W3C标准实现带来的双倍工作量。 我们来看一下一些主要的点,因为你在做一些简单的页面或者快速开发的时候可能不会使用已有的库,当然,也有可能你正在写你自己的库。 ### 事件处理 -麻烦是从给元素绑定事件开始的。假设你有一个按钮,点击它的时候增加计数器的值。你可以添加一个内联的onclick属性,这在所有的浏览器中都能正常工作,但是会违反分离和渐进增强的思想。所以你应该尽力在JavaScript中来做绑定,而不是在标签中。 +麻烦是从给元素绑定事件开始的。假设你有一个按钮,点击它的时候增加计数器的值。你可以添加一个内联的`onclick`属性,这在所有的浏览器中都能正常工作,但是会违反分离和渐进增强的思想。所以你应该尽量在JavaScript中来做绑定,而不是在标签中。 假设你有下面的标签: @@ -174,7 +174,7 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 你可以将一个函数赋给节点的onclick属性,但你只能这样做一次: - // suboptimal solution + // 不好的解决方案 var b = document.getElementById('clickme'), count = 0; @@ -183,20 +183,20 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 b.innerHTML = "Click me: " + count; }; -如果你希望在按钮点击的时候执行好几个函数,那么在维持松耦合的情况下就不能用这种方法来做绑定。从技术上讲,你可以检测onclick是否已经包含一个函数,如果已经包含,就将它加到你自己的函数中,然后替换onclick的值为你的新函数。但是一个更干净的解决方案是使用addEventListener()方法。这个方法在IE8及以下版本中不存在,在这些浏览器需要使用attachEvent()。 +如果你希望在按钮点击的时候执行好几个函数,那么在保持松耦合的情况下就不能用这种方法来做绑定。从技术上讲,你可以检测`onclick`是否已经包含一个函数,如果已经包含,就将它加到你自己的函数中,然后替换`onclick`的值为你的新函数。但是一个更干净的解决方案是使用`addEventListener()`方法。这个方法在IE8及以下版本中不存在,在这些浏览器中需要使用`attachEvent()`。 -当我们回头看条件初始化模式(第4章)时,会发现一个示例实现是一个很好的解决跨浏览器事件监听的套件。现在我们不讨论细节,只看一下如何给我们的按钮绑定事件: +当我们回头看条件初始化模式(第四章)时,会发现其中的一个示例实现就是一个很好的解决跨浏览器事件监听的套件。现在我们不讨论细节,只看一下如何给我们的按钮绑定事件: var b = document.getElementById('clickme'); if (document.addEventListener) { // W3C b.addEventListener('click', myHandler, false); } else if (document.attachEvent) { // IE b.attachEvent('onclick', myHandler); - } else { // last resort + } else { // 为保险起见…… b.onclick = myHandler; } -现在当按钮被点击时,myHandler会被执行。让我们来让这个函数实现增加按钮文字“Click me: 0”中的数字的功能。为了更有趣一点,我们假设有好几个按钮,一个myHandler()函数来处理所有的按钮点击。如果我们可以从每次点击的事件对象中获取节点和节点对应的计数器值,那为每个按钮保持一个引用和计数器就显得不高效了。 +现在当按钮被点击时,`myHandler()`会被执行。我们来让这个函数实现增加按钮文字“Click me: 0”中的数字的功能。为了更有趣一点,我们假设有好几个按钮,一个`myHandler()`函数来处理所有的按钮点击。如果我们可以从每次点击的事件对象中获取节点和节点对应的计数器值,那为每个按钮保持一个引用和计数器就显得不高效了。 我们先看一下解决方案,稍后再来做些评论: @@ -204,16 +204,16 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 var src, parts; - // get event and source element + // 获取事件对象和事件来源 e = e || window.event; src = e.target || e.srcElement; - // actual work: update label + // 真正工作的部分:更新文字 parts = src.innerHTML.split(": "); parts[1] = parseInt(parts[1], 10) + 1; src.innerHTML = parts[0] + ": " + parts[1]; - // no bubble + // 阻止冒泡 if (typeof e.stopPropagation === "function") { e.stopPropagation(); } @@ -221,7 +221,7 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 e.cancelBubble = true; } - // prevent default action + // 阻止默认行为 if (typeof e.preventDefault === "function") { e.preventDefault(); } @@ -231,24 +231,26 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 } -一个在线的例子可以在找到。 +在线的例子可以在找到。 在这个事件处理函数中,有四个部分: -- 首先,我们需要访问事件对象,它包含事件的一些信息以及触发这个事件的页面元素。事件对象会被传到事件处理回调函数中,但是使用onclick属性时需要使用全局属性window.event来获取。 +- 首先,我们需要访问事件对象,它包含事件的一些信息以及触发这个事件的页面元素。事件对象会被传到事件处理回调函数中,但是使用`onclick`属性时需要使用全局属性`window.event`来获取 - 第二部分是真正用于更新文字的部分 -- 接下来是阻止事件冒泡。在这个例子中它不是必须的,但通常情况下,如果你不阻止的话,事件会一直冒泡到文档根元素甚至window对象。同样的,我们也需要用两种方法来阻止冒泡:W3C标准方式(stopPropagation())和IE的方式(使用cancelBubble) -- 最后,如果需要的话,阻止默认行为。有一些事件(点击链接、提交表单)有默认的行为,但你可以使用preventDefault()(IE是通过设置returnValue的值为false的方式)来阻止这些默认行为。 +- 接下来是阻止事件冒泡。在这个例子中它不是必须的,但通常情况下,如果你不阻止的话,事件会一直冒泡到文档根元素甚至`window`对象。同样的,我们也需要用两种方法来阻止冒泡:W3C标准方式(`stopPropagation()`)和IE的方式(使用`cancelBubble`) +- 最后,如果需要的话,阻止默认行为。有一些事件(点击链接、提交表单)有默认的行为,但你可以使用`preventDefault()`(IE是通过设置`returnValue`的值为`false`的方式)来阻止这些默认行为 -如你所见,这里涉及到了很多重复性的工作,所以使用第7章讨论过的外观模式创建自己的事件处理套件是很有意义的。 +如你所见,这里涉及到了很多重复性的工作,所以使用第七章讨论过的外观模式创建自己的事件处理套件是很有意义的。 ### 事件委托 -事件委托是通过事件冒泡来实现的,它可以减少分散到各个节点上的事件处理函数的数量。如果有10个按钮在一个div元素中,你可以给div绑定一个事件处理函数,而不是给每个按钮都绑定一个。 +事件委托是通过事件冒泡来实现的,它可以减少分散到各个节点上的事件处理函数的数量。如果有10个按钮在一个`div`元素中,你可以给`div`绑定一个事件处理函数,而不是给每个按钮都绑定一个。 -我们来的睦一个实例,三个按钮放在一个div元素中(图8-1)。你可以在看到这个事件委托的实例。 +我们来看一个实例,三个按钮放在一个`div`元素中(图8-1)。你可以在看到这个事件委托的实例。 -> (译注: 上面的URL中的例子在IE下单击会没有反应,问题在于使用document.attachEvernt时传递的第一个参数应该是'onclick',而不是'click'.) +> 译注: 上面的URL中的例子在IE下单击会没有反应,问题在于使用`document.attachEvernt()`时传递的第一个参数应该是`'onclick'`,而不是`'click'`。 + +------校对分隔线----- ![图8-1 事件委托示例:三个在点击时增加计数器值的按钮](./Figure/chapter8/8-1.jpg) From c18c2cd85035f5bc23413c78847eebd374826914 Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 13 May 2013 13:26:21 +0800 Subject: [PATCH 122/145] =?UTF-8?q?=E4=BB=A3=E7=90=86=E6=A8=A1=E5=BC=8F=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 74 +++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/chapter7.markdown b/chapter7.markdown index 55cb4e6..f6d5a03 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -661,25 +661,23 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 外观模式在做一些重新设计和重构工作时也很有用。当你想用一个不同的实现来替换某个对象的时候,你可能需要花相当长一段时间才能完成(一个复杂的对象),与此同时,一些使用这个新对象的代码也在被同步编写。你可以先想好新对象的API,然后在旧的对象前面使用新的API创建一个外观方法。使用这种方式,当你完全替换掉旧的对象的时候,你只需要修改少量的调用代码,因为新的代码已经是在使用新的API了。 - ## 代理模式 -在代理设计模式中,一个对象充当了另一个对象的接口的角色。它和外观模式不一样,外观模式带来的方便仅限于将几个方法调用联合起来。而代理对象位于某个对象和它的客户之间,可以保护对对象的访问。 +在代理模式中,一个对象充当了另一个对象的接口的角色。它和外观模式不一样,外观模式带来的方便仅限于将几个方法调用联合起来。而代理对象位于某个对象和它的使用者之间,可以保护对对象的访问。 -这个模式看起来开销有点大,但在出于性能考虑时非常有用。代理对象可以作为对象(也叫“真正的主体”)的保护者,让真正的主体对象做尽量少的工作。 +这个模式看起来开销有点大,但在出于性能考虑时非常有用。代理对象可以作为目标对象的保护者,让目标对象做尽量少的工作。 -一种示例用法是我们称之为“懒初始化”(延迟初始化)的东西。假设初始化真正的主体是开销很大的,并且正好客户代码将它初始化后并不真正使用它。在这种情况下,代理对象可以作为真正的主体的接口起到帮助作用。代理对象接收到初始化请求,但在真正的主体真正被使用之前都不会将它传递过去。 +一种示例用法是“懒初始化”(延迟初始化)。假设负责初始化的对象是开销很大的,并且正好使用者将它初始化后并不真正使用它。在这种情况下,代理对象可以作为目标对象的接口起到帮助作用。代理对象接收到初始化请求,但在目标对象真正被使用之前都不会将请求传递过去。 -图7-2展示了这个场景,当客户代码发出初始化请求时,代理对象回复一切就绪,但并没有将请求传递过去,只有在客户代码真正需要真正的主体做些工作的时候才将两个请求一起传递过去。 +图7-2展示了这个场景,当使用目标对象的代码发出初始化请求时,代理对象回复一切就绪,但并没有将请求传递过去,只有在真正需要目标对象做些工作的时候才将两个请求一起传递过去。 -![图7-2 通过代理对象时客户代码与真正的主体的关系](./Figure/chapter7/7-2.jpg) +![图7-2 通过代理对象时目标对象与使用者的关系](./Figure/chapter7/7-2.jpg) -图7-2 通过代理对象时客户代码与真正的主体的关系 +图7-2 通过代理对象时目标对象与使用者的关系 - ### 一个例子 -在真正的主体做某件工作开销很大时,代理模式很有用处。在web应用中,开销最大的操作之一就是网络请求,此时尽可能地合并HTTP请求是有意义的。我们来看一个这种场景下应用代理模式的实例。 +在目标对象做某件工作开销很大时,代理模式很有用处。在web应用中,开销最大的操作之一就是网络请求,此时尽可能地合并HTTP请求是有意义的。我们来看一个这种场景下应用代理模式的实例。 #### 一个视频列表(expando) @@ -697,14 +695,14 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 这个应用中最主要的角色是两个对象: -- videos +- `videos` - 负责对信息区域展开/收起(videos.getInfo()方法)和播放视频的响应(videos.getPlayer()方法) -- http + 负责对信息区域展开/收起(`videos.getInfo()`方法)和播放视频的响应(`videos.getPlayer()`方法) +- `http` - 负责通过http.makeRequest()方法与服务端通讯 + 负责通过`http.makeRequest()`方法与服务端通讯 -当没有代理对象的时候,videos.getInfo()会为每个视频调用一次http.makeRequest()方法。当我们添加代理对象proxy后,它将位于vidoes和http中间,接手对makeRequest()的调用,并在可能的时候合并请求。 +当没有代理对象的时候,`videos.getInfo()`会为每个视频调用一次`http.makeRequest()`方法。当我们添加代理对象`proxy`后,它将位于`vidoes`和`http`中间,接手对`makeRequest()`的调用,并在可能的时候合并请求。 我们首先看一下没有代理对象的代码,然后添加代理对象来提升应用的响应速度。 @@ -730,13 +728,13 @@ HTML代码仅仅是一个链接列表: #### 事件处理 -现在我们来看一下事件处理的逻辑。首先我们定义一个方便的快捷函数$: +现在我们来看一下事件处理的逻辑。首先我们定义一个方便的快捷函数`$`: var $ = function (id) { return document.getElementById(id); }; -使用事件代理(第8章有更多关于这个模式的内容),我们将所有id="vids"的条目上的点击事件统一放到一个函数中处理: +使用事件代理(第八章有更多关于这个模式的内容),我们将所有`id="vids"`的条目上的点击事件统一放到一个函数中处理: $('vids').onclick = function (e) { var src, id; @@ -764,19 +762,19 @@ HTML代码仅仅是一个链接列表: videos.getInfo(id); }; -#### videos对象 +#### `videos`对象 -videos对象有三个方法: +`videos`对象有三个方法: -- getPlayer() +- `getPlayer()` 返回播放视频需要的HTML代码(跟我们讨论的无关) -- updateList() +- `updateList()` - 网络请求的回调函数,接受从服务器返回的数据,然后生成用于视频详细信息的HTML代码。这一部分也没有什么太有趣的事情。 -- getInfo() + 网络请求的回调函数,接受从服务器返回的数据,然后生成用于视频详细信息的HTML代码。这一部分也没有什么需要关注的事情。 +- `getInfo()` - 这个方法切换视频信息的可视状态,同时也调用http对象的方法,并传递updaetList()作为回调函数。 + 这个方法切换视频信息的可视状态,同时也调用`http`对象的方法,并传递`updaetList()`作为回调函数。 下面是这个对象的代码片段: @@ -803,9 +801,9 @@ videos对象有三个方法: } }; -#### http对象 +#### `http`对象 -http对象只有一个方法,它向Yahoo!的YQL服务发起一个JSONP请求: +`http`对象只有一个方法,它向Yahoo!的YQL服务发起一个JSONP请求: var http = { makeRequest: function (ids, callback) { @@ -829,23 +827,23 @@ http对象只有一个方法,它向Yahoo!的YQL服务发起一个JSONP请求 当所有的六个视频都被选中后,将会向服务端发起六个独立的像这样的YQL请求: -select * from music.video.id where ids IN ("2158073") + select * from music.video.id where ids IN ("2158073") #### 代理对象 -前面的代码工作得很正常,但我们可以让它工作得更好。proxy对象就在这样的场景中出现,并接管了http和videos对象之间的通讯。它将使用一个简单的逻辑来尝试合并请求:50ms的延迟。videos对象并不直接调用后台接口,而是调用proxy对象的方法。proxy对象在转发这个请求前将会等待一段时间,如果在等待的50ms内有另一个来自videos的调用,则它们将被合并为同一个请求。50ms的延迟对用户来说几乎是无感知的,但是却可以用来合并请求以提升点击“toggle”时的体验,一次展开多个视频。它也可以显著降低服务器的负载,因为web服务器只需要处理更少量的请求。 +前面的代码工作得很好,但我们可以让它工作得更好。`proxy`对象就在这样的场景中出现,并接管了`http`和`videos`对象之间的通讯。它将使用一个简单的逻辑来尝试合并请求:50ms的延迟。`videos`对象并不直接调用后台接口,而是调用`proxy`对象的方法。`proxy`对象在转发这个请求前将会等待一段时间,如果在等待的50ms内有另一个来自`videos`的调用,则它们将被合并为同一个请求。50ms的延迟对用户来说几乎是无感知的,但是却可以用来合并请求以提升点击“toggle”时的体验,一次展开多个视频。它也可以显著降低服务器的负载,因为web服务器只需要处理更少量的请求。 合并后查询两个视频信息的YQL大概是这样: select * from music.video.id where ids IN ("2158073", "123456") -在修改后的代码中,唯一的变化是videos.getInfo()现在调用的是proxy.makeRequest()而不是http.makeRequest(),像这样: +在修改后的代码中,唯一的变化是`videos.getInfo()`现在调用的是`proxy.makeRequest()`而不是`http.makeRequest()`,像这样: proxy.makeRequest(id, videos.updateList, videos); -proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后将这个队列传递给http对象,并提供回调函数,因为videos.updateList()只能处理一次接收到的数据。(译注:指每次传入的回调函数只能处理当次接收到的数据。) +`proxy`对象创建了一个队列来收集50ms之内接受到的视频ID,然后将这个队列传递给`http`对象,并提供回调函数,因为`videos.updateList()`只能处理一个接收到的视频信息。 -下面是proxy对象的代码: +下面是`proxy`对象的代码: var proxy = { ids: [], @@ -854,13 +852,13 @@ proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后 callback: null, context: null, makeRequest: function (id, callback, context) { - // add to the queue + // 添加到队列 this.ids.push(id); this.callback = callback; this.context = context; - // set up timeout + // 设置延时 if (!this.timeout) { this.timeout = setTimeout(function () { proxy.flush(); @@ -871,7 +869,7 @@ proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后 http.makeRequest(this.ids, "proxy.handler"); - // clear timeout and queue + // 清除延时和队列 this.timeout = null; this.ids = []; @@ -879,20 +877,20 @@ proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后 handler: function (data) { var i, max; - // single video + // 单个视频 if (parseInt(data.query.count, 10) === 1) { proxy.callback.call(proxy.context, data.query.results.Video); return; } - // multiple videos + // 多个视频 for (i = 0, max = data.query.results.Video.length; i < max; i += 1) { proxy.callback.call(proxy.context, data.query.results.Video[i]); } } }; -了解代理模式后就在只简单地改动一下原来的代码的情况下,将多个web service请求合并为一个。 +使用代理模式可以在只改动一处原来代码的情况下,将多个web service请求合并为一个。 图7-4和7-5展示了使用代理模式将与服务器三次数据交互(不用代理模式时)变为一次交互的过程。 @@ -905,9 +903,9 @@ proxy对象创建了一个队列来收集50ms之内接受到的视频ID,然后 图7-5 通过一个代理对象合并请求,减少与服务器数据交互 -#### 使用代理对象做缓存 +### 使用代理对象做缓存 -在这个例子中,客户对象(videos)已经可以做到不对同一个对象重复发出请求。但现实情况中并不总是这样。这个代理对象还可以通过缓存之前的请求结果到cache属性中来进一步保护真正的主体http对象(图7-6)。然后当videos对象需要对同一个ID的视频请求第二次时,proxy对象可以直接从缓存中取出,从而避免一次网络交互。 +在这个例子中,目标对象的使用者(`videos`)已经可以做到不对同一个对象重复发出请求,但现实情况中并不总是这样。其实这个代理对象还可以通过缓存之前的请求结果到`cache`属性中来进一步保护`http`对象(图7-6)。然后当`videos`对象需要对同一个ID的视频请求第二次时,`proxy`对象可以直接从缓存中取出,从而避免一次网络交互。 ![图7-6 代理缓存](./Figure/chapter7/7-6.jpg) From 7f971a4a05b9138db2b1b4398b82491b4bbc2161 Mon Sep 17 00:00:00 2001 From: TooBug Date: Mon, 13 May 2013 13:34:09 +0800 Subject: [PATCH 123/145] =?UTF-8?q?=E4=B8=AD=E4=BB=8B=E8=80=85=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=20=E6=A0=A1=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/chapter7.markdown b/chapter7.markdown index f6d5a03..1d20dad 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -911,30 +911,28 @@ HTML代码仅仅是一个链接列表: 图7-6 代理缓存 - ## 中介者模式 -一个应用不论大小,都是由一些彼此独立的对象组成的。所有的对象都需要一个通讯的方式来保持可维护性,即你可以安全地修改应用的一部分而不破坏其它部分。随着应用的开发和维护,会有越来越多的对象。然后,在重构代码的时候,对象可能会被移除或者被重新安排。当对象知道其它对象的太多信息并且直接通讯(直接调用彼此的方法或者修改属性)时,会导致我们不愿意看到的紧耦合。当对象耦合很紧时,要修改一个对象而不影响其它的对象是很困难的。此时甚至连一个最简单的修改都变得不那么容易,甚至连一个修改需要用多长时间都难以评估。 +一个应用不论大小,都是由一些彼此独立的对象组成的。所有的对象都需要一个通讯方式来保持可维护性,即你可以安全地修改应用的一部分而不破坏其它部分。随着应用的开发和维护,会有越来越多的对象。然后,在重构代码的时候,对象可能会被移除或者被重新设计。当对象知道其它对象的太多信息并且直接通讯(直接调用彼此的方法或者修改属性)时,会导致我们不愿意看到的紧耦合。当对象耦合很紧时,要修改一个对象而不影响其它的对象是很困难的。此时甚至连一个最简单的修改都变得不那么容易,甚至连一个修改需要用多长时间都难以评估。 -中介者模式就是一个缓解此问题的办法,它通过解耦来提升代码的可维护性(见图7-7)。在这个模式中,各个彼此合作的对象并不直接通讯,而是通过一个mediator(中介者)对象通讯。当一个对象改变了状态后,它就通知中介者,然后中介者再将这个改变告知给其它应该知道这个变化的对象。 +中介者模式就是一个缓解此问题的办法,它通过解耦来提升代码的可维护性(见图7-7)。在这个模式中,各个彼此合作的对象并不直接通讯,而是通过一个`mediator`(中介者)对象通讯。当一个对象改变了状态后,它就通知中介者,然后中介者再将这个改变告知给其它应该知道这个变化的对象。 ![图7-7 中介者模式中的对象关系](./Figure/chapter7/7-7.jpg) 图7-7 中介者模式中的对象关系 - ### 中介者示例 我们来看一个使用中介者模式的实例。这个应用是一个游戏,它的玩法是比较两位游戏者在半分钟内按下按键的次数,次数多的获胜。玩家1需要按的是1,玩家2需要按的是0(这样他们的手指不会搅在一起)。当前分数会显示在一个计分板上。 对象列表如下: -- Player 1 -- Player 2 -- Scoreboard -- Mediator +- `Player1` +- `Player2` +- `Scoreboard` +- `Mediator` -中介者Mediator知道所有的对象。它与输入设备(键盘)打交道,处理keypress事件,决定现在是哪位玩家玩的,然后通知这个玩家(见图7-8)。玩家负责玩(即给自己的分数加一分),然后通知中介者他这一轮已经玩完。中介者再告知计分板最新的分数,计分板更新显示。 +中介者`Mediator`知道所有的对象,它与输入设备(键盘)打交道,处理`keypress`事件,决定现在是哪位玩家玩的,然后通知这个玩家(见图7-8)。玩家负责玩(即给自己的分数加一分),然后通知中介者他这一轮已经玩完。中介者再告知计分板最新的分数,计分板更新显示。 除了中介者之外,其它的对象都不知道有别的对象存在。这样就使得更新这个游戏变得很简单,比如要添加一位玩家或者是添加另外一个显示剩余时间的地方。 @@ -944,7 +942,7 @@ HTML代码仅仅是一个链接列表: 图7-8 游戏涉及的对象 -玩家对象是通过Player()构造函数来创建的,有自己的points和name属性。原型上的play()方法负责给自己加一分然后通知中介者: +玩家对象是通过`Player()`构造函数来创建的,有自己的`points`和`name`属性。原型上的`play()`方法负责给自己加一分然后通知中介者: function Player(name) { this.points = 0; @@ -955,14 +953,14 @@ HTML代码仅仅是一个链接列表: mediator.played(); }; -scoreboard对象(计分板)有一个update()方法,它会在每次玩家玩完后被中介者调用。计分板根本不知道玩家的任何信息,也不保存分数,它只负责显示中介者给过来的分数: +`scoreboard`对象(计分板)有一个`update()`方法,它会在每次玩家玩完后被中介者调用。计分板根本不知道玩家的任何信息,也不保存分数,它只负责显示中介者给过来的分数: var scoreboard = { - // HTML element to be updated + // 被更新的HTML元素 element: document.getElementById('results'), - // update the score display + // 更新分数显示 update: function (score) { var i, msg = ''; @@ -978,14 +976,14 @@ scoreboard对象(计分板)有一个update()方法,它会在每次玩家 } }; -现在我们来看一下mediator对象(中介者)。在游戏初始化的时候,在setup()方法中创建游戏者,然后放后players属性以便后续使用。played()方法会被游戏者在每轮玩完后调用,它更新score哈希然表然后将它传给scoreboard用于显示。最后一个方法是keypress(),负责处理键盘事件,决定是哪位玩家玩的,并且通知它: +现在我们来看一下`mediator`对象(中介者)。在游戏初始化的时候,在`setup()`方法中创建玩家,然后放入`players`属性以便后续使用。`played()`方法会被玩家在每轮玩完后调用,它更新`score`哈希然表然后将它传给`scoreboard`用于显示。最后一个方法是`keypress()`,负责处理键盘事件,决定是哪位玩家玩的,并且通知它: var mediator = { - // all the players + // 所有的玩家 players: {}, - // initialization + // 初始化 setup: function () { var players = this.players; players.home = new Player('Home'); @@ -993,7 +991,7 @@ scoreboard对象(计分板)有一个update()方法,它会在每次玩家 }, - // someone plays, update the score + // 玩家玩完后更新分数 played: function () { var players = this.players, score = { @@ -1004,14 +1002,14 @@ scoreboard对象(计分板)有一个update()方法,它会在每次玩家 scoreboard.update(score); }, - // handle user interactions + // 处理用户交互 keypress: function (e) { e = e || window.event; // IE - if (e.which === 49) { // key "1" + if (e.which === 49) { // 按键“1” mediator.players.home.play(); return; } - if (e.which === 48) { // key "0" + if (e.which === 48) { // 按键“0” mediator.players.guest.play(); return; } @@ -1020,11 +1018,11 @@ scoreboard对象(计分板)有一个update()方法,它会在每次玩家 最后一件事是初始化和结束游戏: - // go! + // 开始 mediator.setup(); window.onkeypress = mediator.keypress; - // game over in 30 seconds + // 游戏在30秒后结束 setTimeout(function () { window.onkeypress = null; alert('Game over!'); From ceae4a15eebff8e453d8203d48360d94ab054997 Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 14 May 2013 20:13:00 +0800 Subject: [PATCH 124/145] =?UTF-8?q?=E7=AC=AC=E4=B8=83=E7=AB=A0=20=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter7.markdown | 104 ++++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 54 deletions(-) diff --git a/chapter7.markdown b/chapter7.markdown index 1d20dad..4e8d4c0 100644 --- a/chapter7.markdown +++ b/chapter7.markdown @@ -304,11 +304,11 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 `Object()`也是一个工厂这一事实可能没有太多实际用处,仅仅是觉得值得作为一个例子提一下,告诉我们工厂模式是随处可见的。 -## 迭代器 +## 遍历模式 -在迭代器模式中,你有一些含有有序聚合数据的对象。这些数据可能在内部用一种复杂的结构存储着,但是你希望提供一种简单的方法来访问这种结构中的每个元素。数据的使用者不需要知道你是怎样组织你的数据的,他们只需要操作一个个独立的元素。 +在遍历模式中,你有一些含有有序聚合数据的对象。这些数据可能在内部用一种复杂的结构存储着,但是你希望提供一种简单的方法来访问这种结构中的每个元素。数据的使用者不需要知道你是怎样组织你的数据的,他们只需要操作一个个独立的元素。 -在迭代器模式中,你的对象需要提供一个`next()`方法。按顺序调用`next()`方法必须返回序列中的下一个元素,但是“下一个”在你的特定的数据结构中指什么是由你自己来决定的。 +在遍历模式中,你的对象需要提供一个`next()`方法。按顺序调用`next()`方法必须返回序列中的下一个元素,但是“下一个”在你的特定的数据结构中指什么是由你自己来决定的。 假设你的对象叫`agg`,你可以通过简单地在循环中调用`next()`来访问每个数据元素,像这样: @@ -318,22 +318,22 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 console.log(element); } -在迭代器模式中,聚合对象通常也会提供一个方便的方法`hasNext()`,这样对象的使用者就可以知道他们已经获取到你数据的最后一个元素。当使用`hasNext()`来按顺序访问所有元素时,是像这样的: +在遍历模式中,聚合对象通常也会提供一个方便的方法`hasNext()`,这样对象的使用者就可以知道他们已经获取到你数据的最后一个元素。当使用`hasNext()`来按顺序访问所有元素时,是像这样的: while (agg.hasNext()) { // 访问element…… console.log(agg.next()); } -## 装饰器 +## 装饰模式 -在装饰器模式中,一些额外的功能可以在运行时被动态地添加到一个对象中。在静态的基于类的语言中,处理这个问题可能是个挑战,但是在JavaScript中,对象本来就是可变的,所以给一个对象添加额外的功能本身并不是什么问题。 +在装饰模式中,一些额外的功能可以在运行时被动态地添加到一个对象中。在静态的基于类的语言中,处理这个问题可能是个挑战,但是在JavaScript中,对象本来就是可变的,所以给一个对象添加额外的功能本身并不是什么问题。 -装饰器模式的一个很方便的特性是可以对我们需要的特性进行定制和配置。刚开始时,我们有一个拥有基本功能的对象,然后可以从可用的装饰器中去挑选一些需要用到的去增强这个对象,如果有必要的话,还可以指定增强的顺序。 +装饰模式的一个很方便的特性是可以对我们需要的特性进行定制和配置。刚开始时,我们有一个拥有基本功能的对象,然后可以从可用的装饰中去挑选一些需要用到的去增强这个对象,如果有必要的话,还可以指定增强的顺序。 ### 用法 -我们来看一下这个模式的用法示例。假设你正在做一个卖东西的web应用,每个新交易是一个新的`sale`对象。这个对象“知道”交易的价格并且可以通过调用`sale.getPrice()`方法返回。根据环境的不同,你可以开始用一些额外的功能来装饰这个对象。假设一个场景是这笔交易是发生在加拿大的一个省Québec,在这种情况下,购买者需要付联邦税和Québec省税。根据装饰器模式的用法,你需要指明使用联邦税装饰器和Québec省税装饰器来装饰这个对象。然后你还可以给这个对象装饰一些价格格式的功能。这个场景的使用方式可能是像这样: +我们来看一下这个模式的用法示例。假设你正在做一个卖东西的web应用,每个新交易是一个新的`sale`对象。这个对象“知道”交易的价格并且可以通过调用`sale.getPrice()`方法返回。根据环境的不同,你可以开始用一些额外的功能来装饰这个对象。假设一个场景是这笔交易是发生在加拿大的一个省Québec,在这种情况下,购买者需要付联邦税和Québec省税。根据装饰模式的用法,你需要指明使用联邦税装饰器和Québec省税装饰器来装饰这个对象。然后你还可以给这个对象装饰一些价格格式的功能。这个场景的使用方式可能是像这样: var sale = new Sale(100); // 价格是100美元 sale = sale.decorate('fedtax'); // 加上联邦税 @@ -352,12 +352,12 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 ### 实现 -一种实现装饰器模式的方法是让每个装饰器成为一个拥有应该被重写的方法的对象。每个装饰器实际上是继承自已经被前一个装饰器增强过的对象。装饰器的每个方法都会调用父对象(继承自的对象)的同名方法并取得值,然后做一些额外的处理。 +一种实现装饰模式的方法是让每个装饰器成为一个拥有应该被重写的方法的对象。每个装饰器实际上是继承自已经被前一个装饰器增强过的对象。装饰器的每个方法都会调用父对象(继承自的对象)的同名方法并取得值,然后做一些额外的处理。 最终的效果就是当你在第一个例子中调用`sale.getPrice()`时,实际上是在调用`money`装饰器的方法(图7-1)。但是因为每个装饰器会先调用父对象的方法,`money`的`getPrice()`先调用`quebec`的`getPrice()`,而它又会去调用`fedtax`的`getPrice()`方法,依次类推。这个链会一直走到原始的未经装饰的由`Sale()`构造函数实现的`getPrice()`。 -![图7-1 装饰器模式的实现](./Figure/chapter7/7-1.jpg) -图7-1 装饰器模式的实现 +![图7-1 装饰模式的实现](./Figure/chapter7/7-1.jpg) +图7-1 装饰模式的实现 这个实现以一个构造函数和一个原型方法开始: @@ -486,7 +486,7 @@ JavaScript没有类,所以一字一句地说单例的定义并没有什么意 return price; }; -装饰器模式的第二种实现方式更简单一些,并且没有引入继承。装饰的方法也会简单。所有的工作都由“同意”被装饰的方法来做。在这个示例实现中,`getPrice()`是唯一被允许装饰的方法。如果你想有更多可以被装饰的方法,那遍历装饰器列表的工作就需要由每个方法重复去做。但是,这可以很容易地被抽象到一个辅助方法中,给它传一个方法然后使这个方法“可被装饰”。如果这样实现的话,`decorators_list`属性就应该是一个对象,它的属性名字是方法名,值是装饰器对象的数组。 +装饰模式的第二种实现方式更简单一些,并且没有引入继承。装饰的方法也会简单。所有的工作都由“同意”被装饰的方法来做。在这个示例实现中,`getPrice()`是唯一被允许装饰的方法。如果你想有更多可以被装饰的方法,那遍历装饰器列表的工作就需要由每个方法重复去做。但是,这可以很容易地被抽象到一个辅助方法中,给它传一个方法然后使这个方法“可被装饰”。如果这样实现的话,`decorators_list`属性就应该是一个对象,它的属性名字是方法名,值是装饰器对象的数组。 ## 策略模式 @@ -1029,44 +1029,42 @@ HTML代码仅仅是一个链接列表: }, 30000); - ## 观察者模式 -观察者模式被广泛地应用于JavaScript客户端编程中。所有的浏览器事件(mouseover,keypress等)都是使用观察者模式的例子。这种模式的另一个名字叫“自定义事件”,意思是这些事件是被编写出来的,和浏览器触发的事件相对。它还有另外一个名字叫“订阅者/发布者”模式。 +观察者模式被广泛地应用于JavaScript客户端编程中。所有的浏览器事件(`mouseover`,`keypress`等)都是使用观察者模式的例子。这种模式的另一个名字叫“自定义事件”,意思是这些事件是被编写出来的,和浏览器触发的事件相对。它还有另外一个名字叫“订阅者/发布者”模式(Pub/Sub)。 -使用这个模式的最主要目的就是促进代码触解耦。在观察者模式中,一个对象订阅另一个对象的指定活动并得到通知,而不是调用另一个对象的方法。订阅者也被叫作观察者,被观察的对象叫作发布者或者被观察者(译注:subject,不知道如何翻译,第一次的时候译为“主体”,第二次译时觉得不妥,还是直接叫被观察者好了)。当一个特定的事件发生的时候,发布者会通知(调用)所有的订阅者,同时还可能以事件对象的形式传递一些消息。 +使用这个模式的最主要目的就是促进代码解耦。在观察者模式中,一个对象订阅另一个对象的指定活动并得到通知,而不是调用另一个对象的方法。订阅者也被叫作观察者,被观察的对象叫作发布者或者被观察者。当一个特定的事件发生的时候,发布者会通知(调用)所有的订阅者,同时还可能以事件对象的形式传递一些消息。 - ### 例1:杂志订阅 -为了理解观察者模式的实现方式,我们来看一个具体的例子。我们假设有一个发布者paper,它发行一份日报和一份月刊。无论是日报还是月刊发行,有一个名叫joe的订阅者都会收到通知。 +为了理解观察者模式的实现方式,我们来看一个具体的例子。我们假设有一个发布者`paper`,它发行一份日报和一份月刊。无论是日报还是月刊发行,有一个名叫`joe`的订阅者都会收到通知。 -paper对象有一个subscribers属性,它是一个数组,用来保存所有的订阅者。订阅的过程就仅仅是将订阅者放到这个数组中而已。当一个事件发生时,paper遍历这个订阅者列表,然后通知它们。通知的意思也就是调用订阅者对象的一个方法。因此,在订阅过程中,订阅者需要提供一个方法给paper对象的subscribe()。 +`paper`对象有一个`subscribers`属性,它是一个数组,用来保存所有的订阅者。订阅的过程就仅仅是将订阅者放到这个数组中而已。当一个事件发生时,`paper`遍历这个订阅者列表,然后通知它们。通知的意思也就是调用订阅者对象的一个方法。因此,在订阅过程中,订阅者需要提供一个方法给`paper`对象的`subscribe()`。 -paper对象也可以提供unsubscribe()方法,它可以将订阅者从数组中移除。paper对象的最后一个重要的方法是publish(),它负责调用订阅者的方法。总结一下,一个发布者对象需要有这些成员: +`paper`对象也可以提供`unsubscribe()`方法,它可以将订阅者从数组中移除。`paper`对象的最后一个重要的方法是`publish()`,它负责调用订阅者的方法。总结一下,一个发布者对象需要有这些成员: -- subscribers +- `subscribers` 一个数组 -- subscribe() +- `subscribe()` 将订阅者加入数组 -- unsubscribe() +- `unsubscribe()` 从数组中移除订阅者 -- publish() +- `publish()` 遍历订阅者并调用它们订阅时提供的方法 -所有三个方法都需要一个type参数,因为一个发布者可能触发好几种事件(比如同时发布杂志和报纸),而订阅者可以选择性地订阅其中的一种或几种。 +所有三个方法都需要一个`type`参数,因为一个发布者可能触发好几种事件(比如同时发布杂志和报纸),而订阅者可以选择性地订阅其中的一种或几种。 -因为这些成员对任何对象来说都是通用的,因此将它们作为独立对象的一部分提取出来是有意义的。然后,我们可以(通过混元模式)将它们复制到任何一个对象中,将这些对象转换为订阅者。 +因为这些成员对任何对象来说都是通用的,因此将它们作为一个单独的对象提取出来是有意义的。然后,我们可以(通过混元模式)将它们复制到任何一个对象中,将这些对象转换为订阅者。 -下面是这些发布者通用功能的一个示例实现,它定义了上面列出来的所有成员,还有一个辅助的visitSubscribers()方法: +下面是这些发布者通用功能的一个示例实现,它定义了上面列出来的所有成员,还有一个辅助的`visitSubscribers()`方法: var publisher = { subscribers: { - any: [] // event type: subscribers + any: [] // 对应事件类型的订阅者 }, subscribe: function (fn, type) { type = type || 'any'; @@ -1099,7 +1097,7 @@ paper对象也可以提供unsubscribe()方法,它可以将订阅者从数组 } }; -下面这个函数接受一个对象作为参数,并通过复制通用的发布者的方法将这个对象转变成发布者: +下面这个函数接受一个对象作为参数,并通过复制通用发布者的方法将这个对象转变成发布者: function makePublisher(o) { var i; @@ -1111,7 +1109,7 @@ paper对象也可以提供unsubscribe()方法,它可以将订阅者从数组 o.subscribers = {any: []}; } -现在我们来实现paper对象,它能做的事情就是发布日报和月刊: +现在我们来实现`paper`对象,它能做的事情就是发布日报和月刊: var paper = { daily: function () { @@ -1122,11 +1120,11 @@ paper对象也可以提供unsubscribe()方法,它可以将订阅者从数组 } }; -将paper对象变成发布者: +将`paper`对象变成发布者: makePublisher(paper); -现在我们有了一个发布者,让我们再来看一下订阅者对象joe,它有两个方法: +现在我们有了一个发布者,让我们再来看一下订阅者对象`joe`,它有两个方法: var joe = {
 drinkCoffee: function (paper) { @@ -1137,12 +1135,12 @@ paper对象也可以提供unsubscribe()方法,它可以将订阅者从数组 } }; -现在让joe来订阅paper: +现在让`joe`来订阅`paper`: paper.subscribe(joe.drinkCoffee); paper.subscribe(joe.sundayPreNap, 'monthly'); -如你所见,joe提供了一个当默认的any事件发生时被调用的方法,还提供了另一个当monthly事件发生时被调用的方法。现在让我们来触发一些事件: +如你所见,`joe`提供了一个当默认的`any`事件发生时被调用的方法,还提供了另一个当`monthly`事件发生时被调用的方法。现在让我们来触发一些事件: paper.daily(); paper.daily(); @@ -1156,23 +1154,23 @@ paper对象也可以提供unsubscribe()方法,它可以将订阅者从数组 Just read big news today About to fall asleep reading this interesting analysis -这里值得称道的地方就是paper对象并没有硬编码写上joe,而joe也同样没有硬编码写上paper。这里也没有知道所有事情的中介者对象。所有涉及到的对象都是松耦合的,而且在不修改代码的前提下,我们可以给paper添加更多的订阅者,同时joe也可以在任何时候取消订阅。 +这里值得称道的地方就是`paper`对象并没有硬编码写上`joe`,而`joe`也同样没有硬编码写上`paper`。这里也没有知道所有事情的中介者对象。所有涉及到的对象都是松耦合的,而且在不修改代码的前提下,我们可以给`paper`添加更多的订阅者,同时`joe`也可以在任何时候取消订阅。 -让我们更进一步,将joe也变成一个发布者。(毕竟,在博客和微博上,任何人都可以是发布者。)这样,joe变成发布者之后就可以在Twitter上更新状态: +让我们更进一步,将`joe`也变成一个发布者。(毕竟,在博客和微博上,任何人都可以是发布者。)这样,`joe`变成发布者之后就可以在Twitter上更新状态: makePublisher(joe); joe.tweet = function (msg) { this.publish(msg); }; -现在假设paper的公关部门准备通过Twitter收集读者反馈,于是它订阅了joe,提供了一个方法readTweets(): +现在假设`paper`的公关部门准备通过`Twitter`收集读者反馈,于是它订阅了`joe`,提供了一个方法`readTweets()`: paper.readTweets = function (tweet) { alert('Call big meeting! Someone ' + tweet); }; joe.subscribe(paper.readTweets); -这样每当joe发出消息时,paper就会弹出警告窗口: +这样每当`joe`发出消息时,`paper`就会弹出警告窗口: joe.tweet("hated the paper today"); @@ -1180,20 +1178,19 @@ paper对象也可以提供unsubscribe()方法,它可以将订阅者从数组 你可以在看到完整的源代码,并且在控制台中运行这个实例。 - ### 例2:按键游戏 -我们来看另一个例子。我们将实现一个和中介者模式的示例一样的按钮游戏,但这次使用观察者模式。为了让它看起来更高档,我们允许接受无限个玩家,而不限于2个。我们仍然保留用来产生玩家的Player()构造函数,也保留scoreboard对象。只有mediator会变成game对象。 +我们来看另一个例子。我们将实现一个和中介者模式的示例一样的按钮游戏,但这次使用观察者模式。为了让它看起来更高档,我们允许接受无限个玩家,而不限于2个。我们仍然保留用来产生玩家的`Player()`构造函数,也保留`scoreboard`对象,只有`mediator`会变成`game`对象。 -在中介者模式中,mediator对象知道所有涉及到的对象,并且调用它们的方法。而观察者模式中的game对象不是这样,它会让对象来订阅它们感兴趣的事件。比如,scoreboard会订阅game对象的scorechange事件。 +在中介者模式中,`mediator`对象知道所有涉及到的对象,并且调用它们的方法。而观察者模式中的`game`对象不是这样,它会让对象来订阅它们感兴趣的事件。比如,`scoreboard`会订阅`game`对象的`scorechange`事件。 -首先我们重新看一下通用的publisher对象,并且将它的接口做一点小修改以更贴近浏览器的情况: +首先我们重新看一下通用的`publisher`对象,并且将它的接口做一点小修改以更贴近浏览器的情况: -- 将publish(),subscribe(),unsubscribe()分别改为fire(),on(),remove() -- 事件的type每次都会被用到,所以把它变成三个方法的第一个参数 -- 可以给订阅者的方法额外加一个context参数,以便回调方法可以用this指向它自己所属的对象 +- 将`publish()`,`subscribe()`,`unsubscribe()`分别改为`fire()`,`on()`,`remove()` +- 事件的`type`每次都会被用到,所以把它变成三个方法的第一个参数 +- 可以给订阅者的方法额外加一个`context`参数,以便回调方法可以用`this`指向它自己所属的对象 -新的publisher对象是这样: +新的`publisher`对象是这样: var publisher = { subscribers: { @@ -1233,7 +1230,7 @@ paper对象也可以提供unsubscribe()方法,它可以将订阅者从数组 }; -新的Player()构造函数是这样: +新的`Player()`构造函数是这样: function Player(name, key) { this.points = 0; @@ -1247,11 +1244,11 @@ paper对象也可以提供unsubscribe()方法,它可以将订阅者从数组 this.fire('play', this); }; -变动的部分是这个构造函数接受key,代表这个玩家在键盘上用来按之后得分的按键。(这些键预先被硬编码过。)每次创建一个新玩家的时候,一个newplayer事件也会被触发。类似的,每次有一个玩家玩的时候,会触发play事件。 +变动的部分是这个构造函数接受`key`,代表这个玩家在键盘上用来按之后得分的按键。(这些键预先被硬编码过。)每次创建一个新玩家的时候,一个`newplayer`事件也会被触发。类似的,每次有一个玩家玩的时候,会触发`play`事件。 -scoreboard对象和原来一样,它只是简单地将当前分数显示出来。 +`scoreboard`对象和原来一样,它只是简单地将当前分数显示出来。 -game对象会关注所有的玩家,这样它就可以给出分数并且触发scorechange事件。它也会订阅浏览吕中所有的keypress事件,这样它就会知道按钮对应的玩家: +`game`对象会关注所有的玩家,这样它就可以给出分数并且触发`scorechange`事件。它也会订阅浏览器中所有的·keypress·事件,这样它就会知道按钮对应的玩家: var game = { @@ -1283,19 +1280,19 @@ game对象会关注所有的玩家,这样它就可以给出分数并且触发s } }; -用于将任意对象转变为订阅者的makePublisher()还是和之前一样。game对象会变成发布者(这样它才可以触发scorechange事件),Player.prototype也会变成发布者,以使得每个玩家对象可以触发play和newplayer事件: +用于将任意对象转变为订阅者的`makePublisher()`还是和之前一样。`game`对象会变成发布者(这样它才可以触发`scorechange`事件),`Player.prototype`也会变成发布者,以使得每个玩家对象可以触发`play`和`newplayer`事件: makePublisher(Player.prototype); makePublisher(game); -game对象订阅play和newplayer事件(以及浏览器的keypress事件),scoreboard订阅scorechange事件: +`game`对象订阅`play`和`newplayer`事件(以及浏览器的`keypress`事件),`scoreboard`订阅`scorechange`事件: Player.prototype.on("newplayer", "addPlayer", game); Player.prototype.on("play", "handlePlay", game); game.on("scorechange", scoreboard.update, scoreboard); window.onkeypress = game.handleKeypress; -如你所见,on()方法允许订阅者通过函数(scoreboard.update)或者是字符串("addPlayer")来指定回调函数。当有提供context(如game)时,才能通过字符串来指定回调函数。 +如你所见,`on()`方法允许订阅者通过函数(`scoreboard.update`)或者是字符串(`"addPlayer"`)来指定回调函数。当有提供`context`(如`game`)时,才能通过字符串来指定回调函数。 初始化的最后一点工作就是动态地创建玩家对象(以及它们对象的按键),用户想要多少个就可以创建多少个: @@ -1317,9 +1314,8 @@ game对象订阅play和newplayer事件(以及浏览器的keypress事件),s 这就是游戏的全部。你可以在看到完整的源代码并且试玩一下。 -值得注意的是,在中介者模式中,mediator对象必须知道所有的对象,然后在适当的时机去调用对应的方法。而这个例子中,game对象会显得笨一些(译注:指知道的信息少一些),游戏依赖于对象去观察特写的事件然后触发相应的动作:如scoreboard观察scorechange事件。这使得对象之间的耦合更松了(对象间知道彼此的信息越少越好),而代价则是弄清事件和订阅者之间的对应关系会更困难一些。在这个例子中,所有的订阅行为都发生在代码中的同一个地方,而随着应用规模的境长,on()可能会被在各个地方调用(如在每个对象的初始化代码中)。这使得调试更困难一些,因为没有一个集中的地方来看这些代码并理解正在发生什么事情。在观察者模式中,你将不再能看到那种从开头一直跟到结尾的顺序执行方式。 +值得注意的是,在中介者模式中,`mediator`对象必须知道所有的对象,然后在适当的时机去调用对应的方法。而这个例子中,`game`对象会显得笨一些(译注:指知道的信息少一些),游戏依赖于对象去观察特定的事件然后触发相应的动作:如`scoreboard`观察`scorechange`事件。这使得对象之间的耦合更松了(对象间知道彼此的信息越少越好),而代价则是弄清事件和订阅者之间的对应关系会更困难一些。在这个例子中,所有的订阅行为都发生在代码中的同一个地方,而随着应用规模的境长,`on()`可能会被在各个地方调用(如在每个对象的初始化代码中)。这使得调试更困难一些,因为没有一个集中的地方来看这些代码并理解正在发生什么事情。在观察者模式中,你将不再能看到那种从开头一直跟到结尾的顺序执行方式。 - ## 小结 在这章中你学习到了若干种流行的设计模式,并且也知道了如何在JavaScript中实现它们。我们讨论过的设计模式有: From 50bbc6a1a170445488ee9847cf6f0214f31abc09 Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 14 May 2013 20:17:04 +0800 Subject: [PATCH 125/145] =?UTF-8?q?=E4=BA=8B=E4=BB=B6=20=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter8.markdown | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/chapter8.markdown b/chapter8.markdown index 154f414..352fa36 100644 --- a/chapter8.markdown +++ b/chapter8.markdown @@ -250,8 +250,6 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 > 译注: 上面的URL中的例子在IE下单击会没有反应,问题在于使用`document.attachEvernt()`时传递的第一个参数应该是`'onclick'`,而不是`'click'`。 -------校对分隔线----- - ![图8-1 事件委托示例:三个在点击时增加计数器值的按钮](./Figure/chapter8/8-1.jpg) 图8-1 事件委托示例:三个在点击时增加计数器值的按钮 @@ -264,12 +262,12 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 -你可以给包裹按钮的div绑定一个事件处理函数,而不是给每个按钮绑定一个。然后你可以使用和前面的示例中一样的myHandler()函数,但需要修改一个小地方:你需要将你不感兴趣的点击排除掉。在这个例子中,你只关注按钮上的点击,而在同一个div中产生的其它的点击应该被忽略掉。 +你可以给包裹按钮的`div`绑定一个事件处理函数,而不是给每个按钮绑定一个。然后你可以使用和前面的示例中一样的`myHandler()`函数,但需要修改一个小地方:你需要将你不感兴趣的点击排除掉。在这个例子中,你只关注按钮上的点击,而在同一个`div`中产生的其它的点击应该被忽略掉。 -myHandler()的改变就是检查事件来源的nodeName是不是“button”: +`myHandler()`的改变就是检查事件来源的`nodeName`是不是`"button"`: - // ... - // get event and source element + // …… + // 获取事件对象和事件来源 e = e || window.event; src = e.target || e.srcElement; @@ -280,7 +278,7 @@ myHandler()的改变就是检查事件来源的nodeName是不是“button”: 事件委托的坏处是筛选容器中感兴趣的事件使得代码看起来更多了,但好处是性能的提升和更干净的代码,这个好处明显大于坏处,因此这是一种强烈推荐的模式。 -主流的JavaScript库通过提供方便的API的方式使得使用事件委托变得很容易。比如YUI3中有Y.delegate()方法,它允许你指定一个用来匹配包裹容器的CSS选择器和一个用于匹配你感兴趣的节点的CSS选择器。这很方便,因为如果事件发生在你不关心的元素上时,你的事件处理回调函数不会被调用。在这种情况下,绑定一个事件处理函数很简单: +主流的JavaScript库通过提供方便的API的方式使得使用事件委托变得很容易。比如YUI3中有`Y.delegate()`方法,它允许你指定两个CSS选择器,一个用来匹配包裹容器,一个用来匹配你感兴趣的节点。这很方便,因为如果事件发生在你不关心的元素上时,你的事件处理回调函数不会被调用。在这种情况下,绑定一个事件处理函数很简单: Y.delegate('click', myHandler, "#click-wrap", "button"); From 6a9b70be0653dd0c2682813a1b19287d8214f19a Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 14 May 2013 20:20:42 +0800 Subject: [PATCH 126/145] =?UTF-8?q?=E9=95=BF=E6=97=B6=E9=97=B4=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E7=9A=84=E8=84=9A=E6=9C=AC=20=20=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E5=AE=8C=E6=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter8.markdown | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/chapter8.markdown b/chapter8.markdown index 352fa36..626bcf9 100644 --- a/chapter8.markdown +++ b/chapter8.markdown @@ -300,11 +300,11 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 ## 长时间运行的脚本 -你可能注意到过,有时候浏览器会提示脚本运行时间过长,询问用户是否要停止执行。这种情况你当然不希望发生在自己的应用中,不管它有多复杂。 +你可能注意到过,有时候浏览器会提示脚本运行时间过长,询问用户是否要停止执行。不管应用有多复杂,你都不希望这种情况发生在自己的应用中。 同时,如果脚本运行时间太长的话,浏览器的UI将变得没有响应,用户不能点击任何东西。这是一种很差的用户体验,应该尽量避免。 -在JavaScript中没有线程,但你可以在浏览器中使用setTimeout来模拟,或者在现代浏览器中使用web workers。 +在JavaScript中没有线程,但你可以在浏览器中使用`setTimeout()`来模拟,或者在现代浏览器中使用web workers。 ### setTimeout() @@ -314,7 +314,7 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 ### Web Workers -现代浏览器为长时间运行的脚本提供了另一种解决方案:web workers。web workers在浏览器内部提供了后台线程支持,你可以将计算量很大的部分放到一个单独的文件中,比如my_web_worker.js,然后从主程序(页面)中这样调用它: +现代浏览器为长时间运行的脚本提供了另一种解决方案:web workers。web workers在浏览器内部提供了后台线程支持,你可以将计算量很大的部分放到一个单独的文件中,比如`my_web_worker.js`,然后从主程序(页面)中这样调用它: var ww = new Worker('my_web_worker.js'); ww.onmessage = function (event) { @@ -331,14 +331,14 @@ DOM操作性能不好,这是影响JavaScript性能的最主要原因。性能 while (end) { end -= 1; tmp += end; - if (end === 5e7) { // 5e7 is the half of 1e8 + if (end === 5e7) { // 5e7是1e8的一半 postMessage('halfway there, `tmp` is now ' + tmp); } } postMessage('all done'); -web worker使用postMessage()来和调用它的程序通讯,调用者通过onmessage事件来接受更新。onmessage事件处理函数接受一个事件对象作为参数,这个对象含有一个由web worker传过来data属性。类似的,调用者(在这个例子中)也可以使用ww.postMessage()来给web worker传递数据,web worker可以通过一个onmessage事件处理函数来接受这些数据。 +web worker使用`postMessage()`来和调用它的程序通讯,调用者通过`onmessage`事件来接受更新。`onmessage`事件处理函数接受一个事件对象作为参数,这个对象含有一个由web worker传过来`data`属性。类似的,调用者(在这个例子中)也可以使用`ww.postMessage()`来给web worker传递数据,web worker可以通过一个`onmessage`事件处理函数来接受这些数据。 上面的例子会在浏览器中打印出: From 038808d37a83ef9cf0ce27e8c80b01e0f76fb76a Mon Sep 17 00:00:00 2001 From: TooBug Date: Tue, 14 May 2013 22:55:55 +0800 Subject: [PATCH 127/145] =?UTF-8?q?=E8=BF=9C=E7=A8=8B=E8=84=9A=E6=9C=AC=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E5=AE=8C=E4=B8=80=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chapter8.markdown | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/chapter8.markdown b/chapter8.markdown index 626bcf9..835e846 100644 --- a/chapter8.markdown +++ b/chapter8.markdown @@ -351,9 +351,9 @@ web worker使用`postMessage()`来和调用它的程序通讯,调用者通过` ### XMLHttpRequest -现在,XMLHttpRequest是一个特别的对象(构造函数),绝大多数浏览器都可以用,它使得我们可以从JavaScript来发送HTTP请求。发送一个请求有以下三步: +现在,`XMLHttpRequest`是一个特别的对象(构造函数),绝大多数浏览器都可以用,它使得我们可以从JavaScript来发送HTTP请求。发送一个请求有以下三步: -1. 初始化一个XMLHttpRequest对象(简称XHR) +1. 初始化一个`XMLHttpRequest`对象(简称XHR) 2. 提供一个回调函数,供请求对象状态改变时调用 3. 发送请求 @@ -363,11 +363,11 @@ web worker使用`postMessage()`来和调用它的程序通讯,调用者通过` 但是在IE7之前的版本中,XHR的功能是使用ActiveX对象实现的,所以需要做一下兼容处理。 -第二步是给readystatechange事件提供一个回调函数: +第二步是给`readystatechange`事件提供一个回调函数: xhr.onreadystatechange = handleResponse; -最后一步是使用open()和send()两个方法触发请求。open()方法用于初始化HTTP请求的方法(如GET,POST)和URL。send()方法用于传递POST的数据,如果是GET方法,则是一个空字符串。open()方法的最后一个参数用于指定这个请求是不是异步的。异步是指浏览器在等待响应的时候不会阻塞,这明显是更好的用户体验,因此除非必须要同步,否则异步参数应该使用true: +最后一步是使用`open()`和`send()`两个方法触发请求。`open()`方法用于初始化HTTP请求的方法(如GET,POST)和URL。`send()`方法用于传递POST的数据,如果是GET方法,则是一个空字符串。`open()`方法的最后一个参数用于指定这个请求是不是异步的。异步是指浏览器在等待响应的时候不会阻塞,这明显是更好的用户体验,因此除非必须要同步,否则异步参数应该使用true: xhr.open("GET", "page.html", true); xhr.send(); @@ -382,7 +382,7 @@ web worker使用`postMessage()`来和调用它的程序通讯,调用者通过` if (typeof XMLHttpRequest === "function") { // native XHR xhr = new XMLHttpRequest(); - } else { // IE before 7 + } else { // IE7以下 for (i = 0; i < activeXids.length; i += 1) { try { xhr = new ActiveXObject(activeXids[i]); @@ -406,14 +406,14 @@ web worker使用`postMessage()`来和调用它的程序通讯,调用者通过` 代码中的一些说明: -- 因为IE6及以下版本中,创建XHR对象有一点复杂,所以我们通过一个数组列出ActiveX的名字,然后遍历这个数组,使用try-catch块来尝试创建对象。 -- 回调函数会检查xhr对象的readyState属性。这个属性有0到4一共5个值,4代表“complete”(完成)。如果状态还没有完成,我们就继续等待下一次readystatechange事件。 -- 回调函数也会检查xhr对象的status属性。这个属性和HTTP状态码对应,比如200(OK)或者是404(Not found)。我们只对状态码200感兴趣,而将其它所有的都报为错误(为了简化示例,否则需要检查其它不代表出错的状态码)。 +- 因为IE6及以下版本中,创建XHR对象有一点复杂,所以我们通过一个数组列出ActiveX的名字,然后遍历这个数组,使用`try-catch`块来尝试创建对象。 +- 回调函数会检查`xhr`对象的`readyState`属性。这个属性有0到4一共5个值,4代表“complete”(完成)。如果状态还没有完成,我们就继续等待下一次`readystatechange`事件。 +- 回调函数也会检查xhr对象的`status`属性。这个属性和HTTP状态码对应,比如200(OK)或者是404(Not found)。我们只对状态码200感兴趣,而将其它所有的都报为错误(为了简化示例,否则需要检查其它不代表出错的状态码)。 - 上面的代码会在每次创建XHR对象时检查一遍支持情况。你可以使用前面提到过的模式(如条件初始化)来重写上面的代码,使得只需要做一次检查。 ### JSONP -JSONP(JSON with padding)是另一种发起远程请求的方式。与XHR不同,它不受浏览器同源策略的限制,所以考虑到加载第三方站点的安全影响的问题,使用它时应该很谨慎。 +JSONP(JSON with padding)是另一种发起远程请求的方式。与XHR不同,它不受浏览器同源策略的限制,所以考虑到加载第三方站点内容的安全问题,使用它时应该很谨慎。 一个XHR请求的返回可以是任何类型的文档: @@ -422,26 +422,24 @@ JSONP(JSON with padding)是另一种发起远程请求的方式。与XHR不 - JSON数据(轻量、方便) - 简单的文本文件及其它 -使用JSONP的话,数据经常是被包裹在一个函数中的JSON,函数名称在请求的时候提供。 +而使用JSONP的话,返回的数据格式经常是被一个函数包裹的JSON,具体的函数名称在请求的时候提供。 JSONP的请求URL通常是像这样: http://example.org/getdata.php?callback=myHandler -getdata.php可以是任何类型的页面或者脚本。callback参数指定用来处理响应的JavaScript函数。 +`getdata.php`可以是任何类型的页面或者脚本。`callback`参数指定用来处理响应的JavaScript函数(译注:也就是前面提到的包裹JSON的函数)。 -这个URL会被放到一个动态生成的\元素中,像这样: +这个URL会被放到一个动态生成的` - // option 2 + // 第二种选择 但是,当你的目标是要构建一个高性能的web应用的时候,有些模式和考虑点还是应该知道的。 -作为题外话,来看一些比较常见的开发者会用在\元素上的属性: +作为题外话,来看一些比较常见的开发者会用在` - ... + …… @@ -666,7 +666,7 @@ script元素会阻塞页面的下载。浏览器会同时下载好几个组件 - ... + …… @@ -678,32 +678,32 @@ script元素会阻塞页面的下载。浏览器会同时下载好几个组件 My App - ... + …… ### HTTP分块 -HTTP协议支持“分块编码”。它允许将页面分成一块一块发送。所以如果你有一个很复杂的页面,你不需要将那些每个站都多多少少会有的(静态)头部信息也等到所有的服务端工作都完成后再开始发送。 +HTTP协议支持“分块编码”,它允许将页面分成一块一块发送。所以如果你有一个很复杂的页面,你不需要将那些(静态)头部信息也等到所有的服务端工作都完成后再开始发送。 -一个简单的策略是在组装页面其余部分的时候将页面\的内容作为第一块发送。也就是像这样子: +一个简单的策略是在组装页面其余部分的时候将页面``的内容作为第一块发送。也就是像这样子: My App - + - ... + …… - + -这种情况下可以做一个简单的发动,将JavaScript移回\,随着第一块一起发送。 +这种情况下可以做一个简单的改动,将JavaScript移回``,随着第一块一起发送。 -这样的话可以让服务器在拿到head区内容后就开始下载脚本文件,而此时页面的其它部分在服务端还尚未就绪: +这样的话可以让浏览器在拿到`head`区内容后就开始下载脚本文件,而此时页面的其它部分在服务端还尚未就绪: @@ -711,13 +711,13 @@ HTTP协议支持“分块编码”。它允许将页面分成一块一块发送 My App - + - ... + …… - + -一个更好的办法是使用第三块内容,让它在页面尾部,只包含脚本。如果有一些每个页面都用到的静态的头部,也可以将这部分随和一块一起发送: +一个更好的办法是使用第三块内容,让它在页面尾部,只包含脚本。如果有一些每个页面都用到的静态的头部,也可以将这部分随第一块一起发送: @@ -727,35 +727,35 @@ HTTP协议支持“分块编码”。它允许将页面分成一块一块发送 ... - + ... The full body of the page ... - + - + 这种方法很适合使用渐进增强思想的网站(关键业务不依赖JavaScript)。当HTML的第二块发送完毕的时候,浏览器已经有了一个加载、显示完毕并且可用的页面,就像禁用JavaScript时的情况。当JavaScript随着第三块到达时,它会进一步增强页面,为页面锦上添花。 -### 动态\元素实现非阻塞下载 +### 动态` - // becomes: + // 修改后的: - + 对很多应用来说,延迟加载的部分大部分情况下会比核心部分要大,因为我们关注的“行为”(比如拖放、XHR、动画)只在用户初始化之后才会发生。 @@ -841,44 +841,44 @@ frist_script是页面中一定存在的一个script标签,script是你创建 假设你页面的侧边栏上有一些tabs。点击tab会发出一个XHR请求获取内容,然后更新tab的内容,然后有一个更新的动画。如果这是页面上唯一需要XHR和动画库的地方,而用户又不点击tab的话会怎样? -下面介绍按需加载模式。你可以创建一个require()函数或者方法,它接受一个需要被加载的脚本文件的文件名,还有一个在脚本被加载完毕后执行的回调函数。 +下面介绍按需加载模式。你可以创建一个`require()`函数或者方法,它接受一个需要被加载的脚本文件的文件名,还有一个在脚本被加载完毕后执行的回调函数。 -require()函数可以被这样使用: +`require()`函数可以被这样使用: require("extra.js", function () { functionDefinedInExtraJS(); }); -我们来看一下如何实现这样一个函数。加载脚本很简单——你只需要按照动态\元素模式做就可以了。获知脚本已经加载需要一点点技巧,因为浏览器之间有差异: +我们来看一下如何实现这样一个函数。加载脚本很简单——你只需要按照动态`