<?php
require("../../inc/header.php");

/*
		SoftName : EmpireBak Version 2010
		Author   : wm_chief
		Copyright: Powered by www.phome.net
*/

DoSetDbChar('utf8');
E_D("DROP TABLE IF EXISTS `wp_posts`;");
E_C("CREATE TABLE `wp_posts` (
  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `post_author` bigint(20) unsigned NOT NULL DEFAULT '0',
  `post_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `post_date_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `post_content` longtext NOT NULL,
  `post_title` text NOT NULL,
  `post_excerpt` text NOT NULL,
  `post_status` varchar(20) NOT NULL DEFAULT 'publish',
  `comment_status` varchar(20) NOT NULL DEFAULT 'open',
  `ping_status` varchar(20) NOT NULL DEFAULT 'open',
  `post_password` varchar(20) NOT NULL DEFAULT '',
  `post_name` varchar(200) NOT NULL DEFAULT '',
  `to_ping` text NOT NULL,
  `pinged` text NOT NULL,
  `post_modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `post_modified_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `post_content_filtered` longtext NOT NULL,
  `post_parent` bigint(20) unsigned NOT NULL DEFAULT '0',
  `guid` varchar(255) NOT NULL DEFAULT '',
  `menu_order` int(11) NOT NULL DEFAULT '0',
  `post_type` varchar(20) NOT NULL DEFAULT 'post',
  `post_mime_type` varchar(100) NOT NULL DEFAULT '',
  `comment_count` bigint(20) NOT NULL DEFAULT '0',
  PRIMARY KEY (`ID`),
  KEY `post_name` (`post_name`),
  KEY `type_status_date` (`post_type`,`post_status`,`post_date`,`ID`),
  KEY `post_parent` (`post_parent`),
  KEY `post_author` (`post_author`)
) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8");
E_D("replace into `wp_posts` values('1','1','2013-03-29 07:55:04','2013-03-29 07:55:04','欢迎使用 WordPress。这是系统自动生成的演示文章。编辑或者删除它，然后开始您的博客！','世界，你好！','','trash','open','open','','hello-world','','','2013-03-29 16:52:47','2013-03-29 08:52:47','','0','http://wap1.benbenweb.com/?p=1','0','post','','1');");
E_D("replace into `wp_posts` values('2','1','2013-03-29 07:55:04','2013-03-29 07:55:04','这是示范页面。页面和博客文章不同，它的位置是固定的，通常会在站点导航栏显示。很多用户都创建一个“关于”页面，向访客介绍自己。例如，个人博客通常有类似这样的介绍：\n\n<blockquote>欢迎！我白天是个邮递员，晚上就是个有抱负的演员。这是我的博客。我住在天朝的帝都，有条叫做杰克的狗。</blockquote>\n\n... 公司博客可以这样写：\n\n<blockquote>XYZ Doohickey 公司成立于 1971 年，自从建立以来，我们一直向社会贡献着优秀 doohicky。我们的公司总部位于天朝魔都，有着超过两千名员工，对魔都政府税收有着巨大贡献。</blockquote>\n\n您可以访问<a href=\"http://wap1.benbenweb.com/wp-admin/\">仪表盘</a>，删除本页面，然后添加您自己的内容。祝您使用愉快！','示例页面','','trash','open','open','','sample-page','','','2013-03-29 19:27:00','2013-03-29 11:27:00','','0','http://wap1.benbenweb.com/?page_id=2','0','page','','0');");
E_D("replace into `wp_posts` values('3','1','2013-03-29 15:56:37','0000-00-00 00:00:00','','自动草稿','','auto-draft','open','open','','','','','2013-03-29 15:56:37','0000-00-00 00:00:00','','0','http://wap1.benbenweb.com/?p=3','0','post','','0');");
E_D("replace into `wp_posts` values('4','1','2013-03-29 07:55:04','2013-03-29 07:55:04','欢迎使用 WordPress。这是系统自动生成的演示文章。编辑或者删除它，然后开始您的博客！','世界，你好！','','inherit','open','open','','1-revision','','','2013-03-29 07:55:04','2013-03-29 07:55:04','','1','http://wap1.benbenweb.com/?p=4','0','revision','','0');");
E_D("replace into `wp_posts` values('5','1','2013-03-29 16:53:22','2013-03-29 08:53:22','<div>页面中页面切换动画：</div>\r\n<div>jQuery Mobile可以实现页面的切换效果。在同一个Html文件里面，可以定义多个page，切换时直接使用超链接就可以了。</div>\r\n<div>&lt;body&gt;</div>\r\n<div>&lt;div  <wbr />data-role=\"page\" id=\"main\" data-transition=\"pop\"&gt; <wbr /></div>\r\n<div>&lt;a  <wbr />href=\"#page2\" &gt;链接2&lt;/a&gt;</div>\r\n<div>&lt;/div&gt;</div>\r\n<div></div>\r\n<div>&lt;div  <wbr />data-role=\"pag\" id=\"page2\"&gt; <wbr /></div>\r\n<div>&lt;/div&gt;</div>\r\n<div>&lt;/body&gt;</div>\r\n<div>系统默认显示page的内容，不显示page2的内容，点击链接后切换到page2的内容。通过“data-transition”实现动画定义。jQuery Mobile实现了上下左右滑动，淡出，弹出，反转等动画。</div>\r\n<div>如果需要转到的<a href=\"http://www.106030.com/2/HTML/\" target=\"_blank\"><span style=\"text-decoration: underline;\">HTML</span></a>是其他链接，不是内部page，需要加上rel=''external''属性，此时动画效果无效。</div>','jQuery Mobile 页面切换动画的使用 data-trans','','publish','open','open','','jquery-mobile-%e9%a1%b5%e9%9d%a2%e5%88%87%e6%8d%a2%e5%8a%a8%e7%94%bb%e7%9a%84%e4%bd%bf%e7%94%a8-data-trans','','','2013-03-29 16:53:22','2013-03-29 08:53:22','','0','http://wap1.benbenweb.com/?p=5','0','post','','0');");
E_D("replace into `wp_posts` values('6','1','2013-03-29 16:53:04','2013-03-29 08:53:04','','jQuery Mobile 页面切换动画的使用 data-trans','','inherit','open','open','','5-revision','','','2013-03-29 16:53:04','2013-03-29 08:53:04','','5','http://wap1.benbenweb.com/?p=6','0','revision','','0');");
E_D("replace into `wp_posts` values('7','1','2013-03-29 16:53:55','2013-03-29 08:53:55','Jquery Mobile页面转场有诸多特效，其中slide是滑动效果，确切说是从右向左滑动，在项目中使用时，如果有从右向左滑动，那么再加上“返回”的从左向右的滑动就有更好的体验了，由于jquery mobile没有特定的从左向右滑动的关键词，下面说下这种特效的实现方式。\r\n\r\n本来的从右向左滑动的特效的slide的写法：\r\n<div>\r\n<div>\r\n<div><b> </b></div>\r\n</div>\r\n<ol>\r\n	<li>&lt;a href=\"#day_report_3\" data-transition=\"slide\"&gt;Next&lt;/a&gt;</li>\r\n</ol>\r\n</div>\r\n或：\r\n<div>\r\n<div>\r\n<div><b> </b></div>\r\n</div>\r\n<ol>\r\n	<li>\$(''#day_report_5'').bind( ''swipeleft'', function( e ) {</li>\r\n	<li>    \$.mobile.changePage(\"#day_report_1\",{transition: \"slide\",changeHash: true});</li>\r\n	<li>    e.stopImmediatePropagation();</li>\r\n	<li>    return false;</li>\r\n	<li>   } );</li>\r\n</ol>\r\n</div>\r\n从左向右的slide特效的写法：\r\n<div>\r\n<div>\r\n<div><b> </b></div>\r\n</div>\r\n<ol>\r\n	<li>&lt;a href=\"#day_report_2\" data-transition=\"slide\" data-direction=”reverse”&gt;Next&lt;/a&gt;</li>\r\n</ol>\r\n</div>\r\n或：\r\n<div>\r\n<div>\r\n<div><b> </b></div>\r\n</div>\r\n<ol>\r\n	<li>\$(''#day_report_5'').bind( ''swiperight'', function( e ) {</li>\r\n	<li>    \$.mobile.changePage(\"#day_report_4\",{transition: \"slide\",reverse:\"true\",changeHash: true});</li>\r\n	<li>    e.stopImmediatePropagation();</li>\r\n	<li>    return false;</li>\r\n	<li>   } );</li>\r\n</ol>\r\n</div>\r\n在&lt;a&gt; 中设置data-direction=”reverse”，在\$.mobile.changePage中设置reverse:\"true\",这样设置后的结果是转场特效反向，也就实现了我们需要的效果了。','Jquery Mobile转场特效之slide','','publish','open','open','','jquery-mobile%e8%bd%ac%e5%9c%ba%e7%89%b9%e6%95%88%e4%b9%8bslide','','','2013-03-29 16:56:00','2013-03-29 08:56:00','','0','http://wap1.benbenweb.com/?p=7','0','post','','0');");
E_D("replace into `wp_posts` values('8','1','2013-03-29 16:53:45','2013-03-29 08:53:45','','Jquery Mobile转场特效之slide','','inherit','open','open','','7-revision','','','2013-03-29 16:53:45','2013-03-29 08:53:45','','7','http://wap1.benbenweb.com/?p=8','0','revision','','0');");
E_D("replace into `wp_posts` values('9','1','2013-03-29 16:54:52','2013-03-29 08:54:52','1. <a href=\"http://www.106030.com/2/HTML/\" target=\"_blank\"><span style=\"text-decoration: underline;\">HTML</span></a>5：过去、现在、未来\r\n\r\n展示了HTML5的发展路程，从1991年HTML的出现，经过多年演变和进化，2009年HTML5问世了。它超越了以往的功能，增加了web网页的表现力，同时也增加了表单、本地数据等全新功能，对于网站的建设是一个全新的体验，HTML5带给Web无穷无尽的可塑性。\r\n\r\nHTML5支持多种媒体设备和浏览器，对Web和移动的应用和浏览器都有着较高的支持性和兼容性。据IDC调查，2012年1月，使用HTML5开发的应用程序已占据应用总数的78%。而在2011年7月调查显示，在移动设备上使用HTML5浏览器的设备约有1.09亿，预计2016年将达到21亿。\r\n\r\n乔布斯认为HTML5的到来，让Web开发人员再也无需依赖第三方浏览器插件，就能开发出高品质的图片、排版、动画等。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062T215-0.jpg\" border=\"0\" />\r\n\r\n2. 什么是HTML5？\r\n\r\n该信息图标简单的介绍了HTML5的特性和功能，以及五大主流浏览器使用HTML5的程度，IE（26%）、Firefox(77%)、Chrome（86%）Safari（79%）和Opera（72%）。并从价格、效果、普遍性和能力四个方面，对比HTML5和Flash两者之间的不同。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062S021-1.jpg\" border=\"0\" />\r\n\r\n3. HTML5发展史\r\n\r\nHTML5从何而来？如何摇身一变成为今日的Web标准？答案就在下面的图表中。从2004年Web超文本应用技术工作组（WHATWG ）成立开始，直至展望2013年HMTL5预计将超过10亿用户，该信息图标详细介绍了HTML5发展历程和里程碑。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062QD8-2.jpg\" border=\"0\" />\r\n\r\n4. 为什么开发者需要HTML5\r\n\r\n长久以来，HTML5一直被遮蔽在Flash Web开发框架的阴影中，难以显露头角。但今时今日，却发生了变化。虽然Flash和IE浏览器占领着大部分市场份额，但手机系统厂商Apple和Android却都与HTML5站在同一条战线上。让我们看看，为什么桌面和移动端的浏览器、应用和开发人员都需要HTML5？\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062Vb3-3.jpg\" border=\"0\" />\r\n\r\n5. HTML5：浏览器的未来\r\n\r\n从2012年1月的数据来看，全球已有超过34%的网站使用了HTML5技术。除去IE6-8浏览器外，其他主流浏览器都支持HTML5，其中仅有iPhone/iPad不支持Flash。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062Tb3-4.jpg\" border=\"0\" />\r\n\r\n6. HTML5：移动领域的下一个阶段\r\n\r\n据IDC调查研究显示，2013年全球各地将有10亿人使用HTML5浏览器，将有200万开发人员为HTML5浏览器开发应用。HTML5在未来的5-10年中，将成为移动发展的一个重要因素。该信息图表列举HTML5在不同领域、企业中起到的重要作用。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062QW1-5.jpg\" border=\"0\" />\r\n\r\n7. HTML5的美妙世界\r\n\r\n该信息图表显示，预计2016年将有超过21亿部移动设备使用HTML5浏览器，该数据是2010年1.09亿的20倍之多。与此同时，还介绍了HTML5的新功能和特性。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062T153-6.jpg\" border=\"0\" />\r\n\r\n8. HTML5不适合游戏？\r\n\r\nHTML5在很多领域都获得了认可，但是，在游戏开发方面，HTML5又做得如何?针对这个问题，国外科技媒体采访了华盛顿州的一家游戏厂商Zipline Gmaes的CEO Tod Hooper，在访谈中，Tod Hooper并不看好HTML5在mobile games方面的应用，因为其难以达到游戏对性能的高要求。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062RZ5-7.jpg\" border=\"0\" />\r\n\r\n9. HTML5 VS Flash Games\r\n\r\n通过分配方式、CPU使用率、安全性等方面，比较HTML5和Flash在游戏方面的优与劣。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062TS1-8.jpg\" border=\"0\" />\r\n\r\n10. HTML5历史和流行文化\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062T619-9.jpg\" border=\"0\" />','HTML5：过去、现在、未来','','publish','open','open','','html5%ef%bc%9a%e8%bf%87%e5%8e%bb%e3%80%81%e7%8e%b0%e5%9c%a8%e3%80%81%e6%9c%aa%e6%9d%a5','','','2013-03-29 16:55:52','2013-03-29 08:55:52','','0','http://wap1.benbenweb.com/?p=9','0','post','','0');");
E_D("replace into `wp_posts` values('10','1','2013-03-29 16:54:20','2013-03-29 08:54:20','','HTML5：过去、现在、未来','','inherit','open','open','','9-revision','','','2013-03-29 16:54:20','2013-03-29 08:54:20','','9','http://wap1.benbenweb.com/?p=10','0','revision','','0');");
E_D("replace into `wp_posts` values('11','1','2013-03-29 16:54:52','2013-03-29 08:54:52','1. <a href=\"http://www.106030.com/2/HTML/\" target=\"_blank\"><span style=\"text-decoration: underline;\">HTML</span></a>5：过去、现在、未来\r\n\r\n展示了HTML5的发展路程，从1991年HTML的出现，经过多年演变和进化，2009年HTML5问世了。它超越了以往的功能，增加了web网页的表现力，同时也增加了表单、本地数据等全新功能，对于网站的建设是一个全新的体验，HTML5带给Web无穷无尽的可塑性。\r\n\r\nHTML5支持多种媒体设备和浏览器，对Web和移动的应用和浏览器都有着较高的支持性和兼容性。据IDC调查，2012年1月，使用HTML5开发的应用程序已占据应用总数的78%。而在2011年7月调查显示，在移动设备上使用HTML5浏览器的设备约有1.09亿，预计2016年将达到21亿。\r\n\r\n乔布斯认为HTML5的到来，让Web开发人员再也无需依赖第三方浏览器插件，就能开发出高品质的图片、排版、动画等。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062T215-0.jpg\" border=\"0\" />\r\n\r\n2. 什么是HTML5？\r\n\r\n该信息图标简单的介绍了HTML5的特性和功能，以及五大主流浏览器使用HTML5的程度，IE（26%）、Firefox(77%)、Chrome（86%）Safari（79%）和Opera（72%）。并从价格、效果、普遍性和能力四个方面，对比HTML5和Flash两者之间的不同。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062S021-1.jpg\" border=\"0\" />\r\n\r\n3. HTML5发展史\r\n\r\nHTML5从何而来？如何摇身一变成为今日的Web标准？答案就在下面的图表中。从2004年Web超文本应用技术工作组（WHATWG ）成立开始，直至展望2013年HMTL5预计将超过10亿用户，该信息图标详细介绍了HTML5发展历程和里程碑。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062QD8-2.jpg\" border=\"0\" />\r\n\r\n4. 为什么开发者需要HTML5\r\n\r\n长久以来，HTML5一直被遮蔽在Flash Web开发框架的阴影中，难以显露头角。但今时今日，却发生了变化。虽然Flash和IE浏览器占领着大部分市场份额，但手机系统厂商Apple和Android却都与HTML5站在同一条战线上。让我们看看，为什么桌面和移动端的浏览器、应用和开发人员都需要HTML5？\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062Vb3-3.jpg\" border=\"0\" />\r\n\r\n5. HTML5：浏览器的未来\r\n\r\n从2012年1月的数据来看，全球已有超过34%的网站使用了HTML5技术。除去IE6-8浏览器外，其他主流浏览器都支持HTML5，其中仅有iPhone/iPad不支持Flash。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062Tb3-4.jpg\" border=\"0\" />\r\n\r\n6. HTML5：移动领域的下一个阶段\r\n\r\n据IDC调查研究显示，2013年全球各地将有10亿人使用HTML5浏览器，将有200万开发人员为HTML5浏览器开发应用。HTML5在未来的5-10年中，将成为移动发展的一个重要因素。该信息图表列举HTML5在不同领域、企业中起到的重要作用。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062QW1-5.jpg\" border=\"0\" />\r\n\r\n7. HTML5的美妙世界\r\n\r\n该信息图表显示，预计2016年将有超过21亿部移动设备使用HTML5浏览器，该数据是2010年1.09亿的20倍之多。与此同时，还介绍了HTML5的新功能和特性。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062T153-6.jpg\" border=\"0\" />\r\n\r\n8. HTML5不适合游戏？\r\n\r\nHTML5在很多领域都获得了认可，但是，在游戏开发方面，HTML5又做得如何?针对这个问题，国外科技媒体采访了华盛顿州的一家游戏厂商Zipline Gmaes的CEO Tod Hooper，在访谈中，Tod Hooper并不看好HTML5在mobile games方面的应用，因为其难以达到游戏对性能的高要求。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062RZ5-7.jpg\" border=\"0\" />\r\n\r\n9. HTML5 VS Flash Games\r\n\r\n通过分配方式、CPU使用率、安全性等方面，比较HTML5和Flash在游戏方面的优与劣。\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062TS1-8.jpg\" border=\"0\" />\r\n\r\n10. HTML5历史和流行文化\r\n\r\n<img alt=\"\\\\\" src=\"http://www.106030.com/uploads/allimg/121018/00062T619-9.jpg\" border=\"0\" />','HTML5：过去、现在、未来','','inherit','open','open','','9-revision-2','','','2013-03-29 16:54:52','2013-03-29 08:54:52','','9','http://wap1.benbenweb.com/?p=11','0','revision','','0');");
E_D("replace into `wp_posts` values('12','1','2013-03-29 16:53:55','2013-03-29 08:53:55','Jquery Mobile页面转场有诸多特效，其中slide是滑动效果，确切说是从右向左滑动，在项目中使用时，如果有从右向左滑动，那么再加上“返回”的从左向右的滑动就有更好的体验了，由于jquery mobile没有特定的从左向右滑动的关键词，下面说下这种特效的实现方式。\r\n\r\n本来的从右向左滑动的特效的slide的写法：\r\n<div>\r\n<div>\r\n<div><b> </b></div>\r\n</div>\r\n<ol>\r\n	<li>&lt;a href=\"#day_report_3\" data-transition=\"slide\"&gt;Next&lt;/a&gt;</li>\r\n</ol>\r\n</div>\r\n或：\r\n<div>\r\n<div>\r\n<div><b> </b></div>\r\n</div>\r\n<ol>\r\n	<li>\$(''#day_report_5'').bind( ''swipeleft'', function( e ) {</li>\r\n	<li>    \$.mobile.changePage(\"#day_report_1\",{transition: \"slide\",changeHash: true});</li>\r\n	<li>    e.stopImmediatePropagation();</li>\r\n	<li>    return false;</li>\r\n	<li>   } );</li>\r\n</ol>\r\n</div>\r\n从左向右的slide特效的写法：\r\n<div>\r\n<div>\r\n<div><b> </b></div>\r\n</div>\r\n<ol>\r\n	<li>&lt;a href=\"#day_report_2\" data-transition=\"slide\" data-direction=”reverse”&gt;Next&lt;/a&gt;</li>\r\n</ol>\r\n</div>\r\n或：\r\n<div>\r\n<div>\r\n<div><b> </b></div>\r\n</div>\r\n<ol>\r\n	<li>\$(''#day_report_5'').bind( ''swiperight'', function( e ) {</li>\r\n	<li>    \$.mobile.changePage(\"#day_report_4\",{transition: \"slide\",reverse:\"true\",changeHash: true});</li>\r\n	<li>    e.stopImmediatePropagation();</li>\r\n	<li>    return false;</li>\r\n	<li>   } );</li>\r\n</ol>\r\n</div>\r\n在&lt;a&gt; 中设置data-direction=”reverse”，在\$.mobile.changePage中设置reverse:\"true\",这样设置后的结果是转场特效反向，也就实现了我们需要的效果了。','Jquery Mobile转场特效之slide','','inherit','open','open','','7-revision-2','','','2013-03-29 16:53:55','2013-03-29 08:53:55','','7','http://wap1.benbenweb.com/?p=12','0','revision','','0');");
E_D("replace into `wp_posts` values('13','1','2013-03-29 16:56:29','2013-03-29 08:56:29','<table cellspacing=\"0\" cellpadding=\"0\">\r\n<tbody>\r\n<tr>\r\n<td>\r\n<div>&lt;h1&gt;\r\nN &lt;br /&gt;E &lt;br /&gt;T &lt;br /&gt;T &lt;br /&gt;U &lt;br /&gt;T &lt;br /&gt;S\r\n&lt;/h1&gt;</div></td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n这个方法不在用 ,不专业\r\n\r\n\r\n\r\n通过这个方法，我们将每个字母包裹在一个span中，然后在<a href=\"http://www.106030.com/2/CSS/\" target=\"_blank\"><span style=\"text-decoration: underline;\">CSS</span></a>中设置它的<code>display</code>属性为<code>block</code>\r\nh1 span { display: block; }\r\n\r\n&lt;h1&gt;\r\n&lt;span&gt; N &lt;/span&gt;\r\n&lt;span&gt; E &lt;/span&gt;\r\n&lt;span&gt; T &lt;/span&gt;\r\n&lt;span&gt; T &lt;/span&gt;\r\n&lt;span&gt; U &lt;/span&gt;\r\n&lt;span&gt; T &lt;/span&gt;\r\n&lt;span&gt; S &lt;/span&gt;\r\n&lt;/h1&gt;\r\n这个方法可以用\r\n\r\n\r\n使用JavaScript\r\n<blockquote>\r\n<div>&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n&lt;meta charset=utf-8 /&gt;\r\n&lt;title&gt;Vertical Text&lt;/title&gt;\r\n\r\n&lt;style&gt;\r\nh1 span { display: block; }\r\n&lt;/style&gt;\r\n&lt;/head&gt;\r\n&lt;body&gt;\r\n&lt;h1&gt; NETTUTS &lt;/h1&gt;\r\n\r\n&lt;script&gt;\r\nvar h1 = document.getElementsByTagName(''h1'')[0];\r\nh1.inner<a href=\"http://www.106030.com/2/HTML/\" target=\"_blank\"><span style=\"text-decoration: underline;\">HTML</span></a> = ''&lt;span&gt;'' + h1.innerHTML.split('''').join(''&lt;/span&gt;&lt;span&gt;'') + ''&lt;/span&gt;'';\r\n&lt;/script&gt;\r\n&lt;/body&gt;\r\n&lt;/html&gt;</div></blockquote>\r\n<blockquote>\r\n<div>&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n&lt;meta charset=utf-8 /&gt;\r\n&lt;title&gt;Vertical Text&lt;/title&gt;\r\n\r\n&lt;style&gt;\r\nh1 {\r\nwidth: 50px;\r\nfont-size: 50px;\r\nword-wrap: break-word;\r\n}\r\n&lt;/style&gt;\r\n&lt;/head&gt;\r\n&lt;body&gt;\r\n&lt;h1&gt; NETTUTS &lt;/h1&gt;\r\n&lt;/body&gt;\r\n&lt;/html&gt;</div>\r\n<div>&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n&lt;meta charset=utf-8 /&gt;\r\n&lt;title&gt;Vertical Text&lt;/title&gt;\r\n\r\n&lt;style&gt;\r\nh1 {\r\nwidth: 50px;\r\nfont-size: 50px;\r\nword-wrap: break-word;\r\nletter-spacing: 20px; /* Set large letter-spacing as precaution */\r\n}\r\n&lt;/style&gt;\r\n&lt;/head&gt;\r\n&lt;body&gt;\r\n&lt;h1&gt; Nettuts &lt;/h1&gt;\r\n&lt;/body&gt;\r\n&lt;/html&gt;</div></blockquote>','CSS让文字竖排方法','','publish','open','open','','css%e8%ae%a9%e6%96%87%e5%ad%97%e7%ab%96%e6%8e%92%e6%96%b9%e6%b3%95','','','2013-03-29 16:57:44','2013-03-29 08:57:44','','0','http://wap1.benbenweb.com/?p=13','0','post','','0');");
E_D("replace into `wp_posts` values('14','1','2013-03-29 16:56:19','2013-03-29 08:56:19','','CSS让文字竖排方法','','inherit','open','open','','13-revision','','','2013-03-29 16:56:19','2013-03-29 08:56:19','','13','http://wap1.benbenweb.com/?p=14','0','revision','','0');");
E_D("replace into `wp_posts` values('15','1','2013-03-29 16:56:52','2013-03-29 08:56:52','&lt;!DOCTYPE html PUBLIC \"-//W3C//DTD X<a href=\"http://www.106030.com/2/HTML/\" target=\"_blank\"><span style=\"text-decoration: underline;\">HTML</span></a> 1.0 Strict//EN\" \"<span style=\"color: #6e64af;\">http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd</span>\"&gt;\r\n&lt;meta http-equiv=\"Content-Type\" content=\"text/html; charset=gb2312\" /&gt;\r\n&lt;style type=\"text/css\"&gt;\r\n&lt;!--\r\ndiv {\r\nwidth:300px;\r\nborder:1px solid red;\r\npadding:10px;\r\noverflow:hidden;\r\ntext-overflow:ellipsis;\r\nwhite-space:nowrap;\r\n}\r\n--&gt;\r\n&lt;/style&gt;\r\n&lt;div id=\"ff\"&gt;小本爱吃鸡，打死小本，这个样式不就很好嘛哈哈。&lt;/div&gt;','css防撑开表格及完美截断','','publish','open','open','','css%e9%98%b2%e6%92%91%e5%bc%80%e8%a1%a8%e6%a0%bc%e5%8f%8a%e5%ae%8c%e7%be%8e%e6%88%aa%e6%96%ad','','','2013-03-29 16:56:52','2013-03-29 08:56:52','','0','http://wap1.benbenweb.com/?p=15','0','post','','0');");
E_D("replace into `wp_posts` values('16','1','2013-03-29 16:56:44','2013-03-29 08:56:44','','css防撑开表格及完美截断','','inherit','open','open','','15-revision','','','2013-03-29 16:56:44','2013-03-29 08:56:44','','15','http://wap1.benbenweb.com/?p=16','0','revision','','0');");
E_D("replace into `wp_posts` values('17','1','2013-03-29 16:57:24','2013-03-29 08:57:24','<div id=\"main\">\r\n<pre id=\"best-content-752318730\">这个东西叫浮动。\r\n顾名思义，就是让设置的标签产生浮动效果，就是脱离原来页面的标准输出流。\r\n正常情况下，<a href=\"http://www.106030.com/2/HTML/\" target=\"_blank\"><span style=\"text-decoration: underline;\">HTML</span></a>页面中块元素都是从上倒下排列的。如果想实现左右结构。\r\nfloat的一种选择（当然还有其他方法）。\r\n比如\r\n&lt;div style=\"widht:500px\"&gt;\r\n&lt;div style=\"float:left;width:200px\"&gt;左&lt;/div&gt;\r\n&lt;div style=\"float:left;width:200px\"&gt;右&lt;/div&gt;\r\n&lt;/div&gt;\r\n这样，这个两个DIV标签就在同一行上了（同时靠左），当然有个前提，就是他们俩的父元素的宽度要至少大于400px，\r\n这样才能在同一行上。这个结果是两个DIV的右侧会空出100PX的空白\r\n&lt;div style=\"widht:500px\"&gt;\r\n&lt;div style=\"float:left;width:200px\"&gt;左&lt;/div&gt;\r\n&lt;div style=\"float:right;width:200px\"&gt;右&lt;/div&gt;\r\n&lt;/div&gt;\r\n以上写法，也是在同一行。不同的是，一个靠左，一个靠右。\r\n这里边的两个DIV的中间有100PX的空白。\r\n\r\n需要注意的是，当标签使用了float属性后，就脱离了标准输出流，就不受页面约束了。这样不方便，也不利于页面布局。\r\n所以，一般浮动之后要清除浮动。\r\n&lt;div style=\"widht:500px\"&gt;\r\n&lt;div style=\"float:left;width:200px\"&gt;左&lt;/div&gt;\r\n&lt;div style=\"float:right;width:200px\"&gt;右&lt;/div&gt;\r\n&lt;span style=\"clear:both\"&gt;&lt;/span&gt;\r\n&lt;/div&gt;\r\n这样，页面整体的布局结构就基本上没有被破坏了</pre>\r\n<pre></pre>\r\n<pre>使div可以横向布局  浮动有3元素  以这个违例子\r\n&lt;div style=\"widht:500px\"&gt;\r\n&lt;div style=\"float:left;width:200px\"&gt;左&lt;/div&gt;\r\n&lt;div style=\"float:right;width:200px\"&gt;右&lt;/div&gt;\r\n&lt;span style=\"clear:both\"&gt;&lt;/span&gt;\r\n&lt;/div&gt;\r\n<strong>首先要两个层都有浮动   一定要有宽度    一定要清楚浮动</strong></pre>\r\n<div> <img alt=\"\" src=\"http://www.106030.com/uploads/allimg/121123/0016252553-0.png\" border=\"0\" /></div>\r\n今天要实现一个将1和2 两个区域横着排列，但是不能用table，因为用table以后，页面不是很美观，经查阅资料用css float属性。。最终做成效果如图：\r\n\r\n&nbsp;\r\n\r\n<img alt=\"\" src=\"http://www.106030.com/uploads/allimg/121123/001625E36-1.png\" border=\"0\" />\r\n\r\n&nbsp;\r\n\r\n</div>\r\n<div></div>','CSS中float属性怎么用','','publish','open','open','','css%e4%b8%adfloat%e5%b1%9e%e6%80%a7%e6%80%8e%e4%b9%88%e7%94%a8','','','2013-03-29 16:57:24','2013-03-29 08:57:24','','0','http://wap1.benbenweb.com/?p=17','0','post','','0');");
E_D("replace into `wp_posts` values('18','1','2013-03-29 16:57:10','2013-03-29 08:57:10','','CSS中float属性怎么用','','inherit','open','open','','17-revision','','','2013-03-29 16:57:10','2013-03-29 08:57:10','','17','http://wap1.benbenweb.com/?p=18','0','revision','','0');");
E_D("replace into `wp_posts` values('19','1','2013-03-29 16:56:29','2013-03-29 08:56:29','<table cellspacing=\"0\" cellpadding=\"0\">\r\n<tbody>\r\n<tr>\r\n<td>\r\n<div>&lt;h1&gt;\r\nN &lt;br /&gt;E &lt;br /&gt;T &lt;br /&gt;T &lt;br /&gt;U &lt;br /&gt;T &lt;br /&gt;S\r\n&lt;/h1&gt;</div></td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n这个方法不在用 ,不专业\r\n\r\n\r\n\r\n通过这个方法，我们将每个字母包裹在一个span中，然后在<a href=\"http://www.106030.com/2/CSS/\" target=\"_blank\"><span style=\"text-decoration: underline;\">CSS</span></a>中设置它的<code>display</code>属性为<code>block</code>\r\nh1 span { display: block; }\r\n\r\n&lt;h1&gt;\r\n&lt;span&gt; N &lt;/span&gt;\r\n&lt;span&gt; E &lt;/span&gt;\r\n&lt;span&gt; T &lt;/span&gt;\r\n&lt;span&gt; T &lt;/span&gt;\r\n&lt;span&gt; U &lt;/span&gt;\r\n&lt;span&gt; T &lt;/span&gt;\r\n&lt;span&gt; S &lt;/span&gt;\r\n&lt;/h1&gt;\r\n这个方法可以用\r\n\r\n\r\n使用JavaScript\r\n<blockquote>\r\n<div>&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n&lt;meta charset=utf-8 /&gt;\r\n&lt;title&gt;Vertical Text&lt;/title&gt;\r\n\r\n&lt;style&gt;\r\nh1 span { display: block; }\r\n&lt;/style&gt;\r\n&lt;/head&gt;\r\n&lt;body&gt;\r\n&lt;h1&gt; NETTUTS &lt;/h1&gt;\r\n\r\n&lt;script&gt;\r\nvar h1 = document.getElementsByTagName(''h1'')[0];\r\nh1.inner<a href=\"http://www.106030.com/2/HTML/\" target=\"_blank\"><span style=\"text-decoration: underline;\">HTML</span></a> = ''&lt;span&gt;'' + h1.innerHTML.split('''').join(''&lt;/span&gt;&lt;span&gt;'') + ''&lt;/span&gt;'';\r\n&lt;/script&gt;\r\n&lt;/body&gt;\r\n&lt;/html&gt;</div></blockquote>\r\n<blockquote>\r\n<div>&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n&lt;meta charset=utf-8 /&gt;\r\n&lt;title&gt;Vertical Text&lt;/title&gt;\r\n\r\n&lt;style&gt;\r\nh1 {\r\nwidth: 50px;\r\nfont-size: 50px;\r\nword-wrap: break-word;\r\n}\r\n&lt;/style&gt;\r\n&lt;/head&gt;\r\n&lt;body&gt;\r\n&lt;h1&gt; NETTUTS &lt;/h1&gt;\r\n&lt;/body&gt;\r\n&lt;/html&gt;</div>\r\n<div>&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n&lt;meta charset=utf-8 /&gt;\r\n&lt;title&gt;Vertical Text&lt;/title&gt;\r\n\r\n&lt;style&gt;\r\nh1 {\r\nwidth: 50px;\r\nfont-size: 50px;\r\nword-wrap: break-word;\r\nletter-spacing: 20px; /* Set large letter-spacing as precaution */\r\n}\r\n&lt;/style&gt;\r\n&lt;/head&gt;\r\n&lt;body&gt;\r\n&lt;h1&gt; Nettuts &lt;/h1&gt;\r\n&lt;/body&gt;\r\n&lt;/html&gt;</div></blockquote>','CSS让文字竖排方法','','inherit','open','open','','13-revision-2','','','2013-03-29 16:56:29','2013-03-29 08:56:29','','13','http://wap1.benbenweb.com/?p=19','0','revision','','0');");
E_D("replace into `wp_posts` values('20','1','2013-03-29 16:58:56','2013-03-29 08:58:56','才华横溢的<a href=\"http://www.phpied.com/\">Stoyan Stefanov</a>，在他写的由<a href=\"http://oreilly.com/\">O’Reilly</a>初版的新书<a href=\"http://amzn.to/93szK7\">《JavaScript Patterns》</a>(JavaScript模式)中，我想要是为我们的读者贡献其摘要，那会是件很美妙的事情。具体一点就是编写高质量JavaScript的一些要素，例如避免全局变量，使用单变量声明，在循环中预缓存length(长度)，遵循代码阅读，以及更多。\r\n\r\n此摘要也包括一些与代码不太相关的习惯，但对整体代码的创建息息相关，包括撰写API文档、执行同行评审以及运行JSLint。这些习惯和最佳做法可以帮助你写出更好的，更易于理解和维护的代码，这些代码在几个月或是几年之后再回过头看看也是会觉得很自豪的。\r\n<h1>书写可维护的代码(Writing Maintainable Code )</h1>\r\n软件bug的修复是昂贵的，并且随着时间的推移，这些bug的成本也会增加，尤其当这些bug潜伏并慢慢出现在已经发布的软件中时。当你发现bug 的时候就立即修复它是最好的，此时你代码要解决的问题在你脑中还是很清晰的。否则，你转移到其他任务，忘了那个特定的代码，一段时间后再去查看这些代码就 需要：\r\n<ul id=\"ul\">\r\n	<li>花时间学习和理解这个问题</li>\r\n	<li>化时间是了解应该解决的问题代码</li>\r\n</ul>\r\n还有问题，特别对于大的项目或是公司，修复bug的这位伙计不是写代码的那个人（且发现bug和修复bug的不是同一个人）。因此，必须降低理解代 码花费的时间，无论是一段时间前你自己写的代码还是团队中的其他成员写的代码。这关系到底线（营业收入）和开发人员的幸福，因为我们更应该去开发新的激动 人心的事物而不是花几小时几天的时间去维护遗留代码。\r\n\r\n另一个相关软件开发生命的事实是，读代码花费的时间要比写来得多。有时候，当你专注并深入思考某个问题的时候，你可以坐下来，一个下午写大量的代码。\r\n\r\n你的代码很能很快就工作了，但是，随着应用的成熟，还会有很多其他的事情发生，这就要求你的进行进行审查，修改，和调整。例如：\r\n<ul id=\"ul\">\r\n	<li>bug是暴露的</li>\r\n	<li>新功能被添加到应用程序</li>\r\n	<li>程序在新的环境下工作（例如，市场上出现新想浏览器）</li>\r\n	<li>代码改变用途</li>\r\n	<li>代码得完全从头重新，或移植到另一个架构上或者甚至使用另一种语言</li>\r\n</ul>\r\n由于这些变化，很少人力数小时写的代码最终演变成花数周来阅读这些代码。这就是为什么创建可维护的代码对应用程序的成功至关重要。\r\n\r\n可维护的代码意味着：\r\n<ul id=\"ul\">\r\n	<li>可读的</li>\r\n	<li>一致的</li>\r\n	<li>可预测的</li>\r\n	<li>看上去就像是同一个人写的</li>\r\n	<li>已记录</li>\r\n</ul>\r\n<h1>最小全局变量(Minimizing Globals)</h1>\r\nJavaScript通过函数管理作用域。在函数内部声明的变量只在这个函数内部，函数外面不可用。另一方面，全局变量就是在任何函数外面声明的或是未声明直接简单使用的。\r\n\r\n每个JavaScript环境有一个全局对象，当你在任意的函数外面使用this的时候可以访问到。你创建的每一个全部变量都成了这个全局对象的属 性。在浏览器中，方便起见，该全局对象有个附加属性叫做window，此window(通常)指向该全局对象本身。下面的代码片段显示了如何在浏览器环境 中创建和访问的全局变量：\r\n<div>\r\n<pre>myglobal = \"hello\"; // 不推荐写法\r\nconsole.log(myglobal); // \"hello\"\r\nconsole.log(window.myglobal); // \"hello\"\r\nconsole.log(window[\"myglobal\"]); // \"hello\"\r\nconsole.log(this.myglobal); // \"hello\"</pre>\r\n</div>\r\n&nbsp;\r\n<h1>全局变量的问题</h1>\r\n全局变量的问题在于，你的JavaScript应用程序和web页面上的所有代码都共享了这些全局变量，他们住在同一个全局命名空间，所以当程序的两个不同部分定义同名但不同作用的全局变量的时候，命名冲突在所难免。\r\n\r\nweb页面包含不是该页面开发者所写的代码也是比较常见的，例如：\r\n<ul id=\"ul\">\r\n	<li>第三方的JavaScript库</li>\r\n	<li>广告方的脚本代码</li>\r\n	<li>第三方用户跟踪和分析脚本代码</li>\r\n	<li>不同类型的小组件，标志和按钮</li>\r\n</ul>\r\n比方说，该第三方脚本定义了一个全局变量，叫做result；接着，在你的函数中也定义一个名为result的全局变量。其结果就是后面的变量覆盖前面的，第三方脚本就一下子嗝屁啦！\r\n\r\n因此，要想和其他脚本成为好邻居的话，尽可能少的使用全局变量是很重要的。在书中后面提到的一些减少全局变量的策略，例如命名空间模式或是函数立即自动执行，但是要想让全局变量少最重要的还是始终使用var来声明变量。\r\n\r\n由于JavaScript的两个特征，不自觉地创建出全局变量是出乎意料的容易。首先，你可以甚至不需要声明就可以使用变量；第二，JavaScript有隐含的全局概念，意味着你不声明的任何变量都会成为一个全局对象属性。参考下面的代码：\r\n<div>\r\n<div>\r\n<pre>function sum(x, y) {\r\n   // 不推荐写法: 隐式全局变量 \r\n   result = x + y;\r\n   return result;\r\n}\r\n</pre>\r\n</div>\r\n</div>\r\n此段代码中的<code>result</code>没有声明。代码照样运作正常，但在调用函数后你最后的结果就多一个全局命名空间，这可以是一个问题的根源。\r\n\r\n经验法则是始终使用var声明变量，正如改进版的sum()函数所演示的：\r\n<div>\r\n<pre>function sum(x, y) {\r\n   var result = x + y;\r\n   return result;\r\n}</pre>\r\n</div>\r\n另一个创建隐式全局变量的反例就是使用任务链进行部分var声明。下面的片段中，<code>a</code>是本地变量但是<code>b</code>确实全局变量，这可能不是你希望发生的：\r\n<div>\r\n<pre>// 反例，勿使用 \r\nfunction foo() {\r\n   var a = b = 0;\r\n   // ...\r\n}</pre>\r\n</div>\r\n此现象发生的原因在于这个从右到左的赋值，首先，是赋值表达式<code>b = 0</code>，此情况下b是未声明的。这个表达式的返回值是0，然后这个0就分配给了通过var定义的这个局部变量a。换句话说，就好比你输入了：\r\n<div>\r\n<pre>var a = (b = 0);</pre>\r\n</div>\r\n如果你已经准备好声明变量，使用链分配是比较好的做法，不会产生任何意料之外的全局变量，如：\r\n<div>\r\n<pre>function foo() {\r\n   var a, b;\r\n   // ... a = b = 0; // 两个均局部变量\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<blockquote>然而，另外一个避免全局变量的原因是可移植性。如果你想你的代码在不同的环境下（主机下）运行，使用全局变量如履薄冰，因为你会无意中覆盖你最初环境下不存在的主机对象（所以你原以为名称可以放心大胆地使用，实际上对于有些情况并不适用）。</blockquote>\r\n<h1>忘记var的副作用(Side Effects When Forgetting var)</h1>\r\n隐式全局变量和明确定义的全局变量间有些小的差异，就是通过<code>delete</code>操作符让变量未定义的能力。\r\n<ul id=\"ul\">\r\n	<li>通过var创建的全局变量（任何函数之外的程序中创建）是不能被删除的。</li>\r\n	<li>无var创建的隐式全局变量（无视是否在函数中创建）是能被删除的。</li>\r\n</ul>\r\n这表明，在技术上，隐式全局变量并不是真正的全局变量，但它们是全局对象的属性。属性是可以通过<code>delete</code>操作符删除的，而变量是不能的：\r\n<div>\r\n<pre>// 定义三个全局变量\r\nvar global_var = 1;\r\nglobal_novar = 2; // 反面教材\r\n(function () {\r\n   global_fromfunc = 3; // 反面教材\r\n}());\r\n\r\n// 试图删除\r\ndelete global_var; // false\r\ndelete global_novar; // true\r\ndelete global_fromfunc; // true\r\n\r\n// 测试该删除\r\ntypeof global_var; // \"number\"\r\ntypeof global_novar; // \"undefined\"\r\ntypeof global_fromfunc; // \"undefined\"</pre>\r\n</div>\r\n在ES5严格模式下，未声明的变量（如在前面的代码片段中的两个反面教材）工作时会抛出一个错误。\r\n<h1>访问全局对象(Access to the Global Object)</h1>\r\n在浏览器中，全局对象可以通过<code>window</code>属性在代码的任何位置访问（除非你做了些比较出格的事情，像是声明了一个名为window的局部变量）。但是在其他环境下，这个方便的属性可能被叫做其他什么东西（甚至在程序中不可用）。如果你需要在没有硬编码的<code>window</code>标识符下访问全局对象，你可以在任何层级的函数作用域中做如下操作：\r\n<div>\r\n<pre>var global = (function () {\r\n   return this;\r\n}());</pre>\r\n</div>\r\n&nbsp;\r\n\r\n这种方法可以随时获得全局对象，因为其在函数中被当做函数调用了（不是通过<code>new</code>构造），<code>this</code>总 是指向全局对象。实际上这个病不适用于ECMAScript 5严格模式，所以，在严格模式下时，你必须采取不同的形式。例如，你正在开发一个JavaScript库，你可以将你的代码包裹在一个即时函数中，然后从 全局作用域中，传递一个引用指向this作为你即时函数的参数。\r\n<h1>单var形式（Single var Pattern）</h1>\r\n在函数顶部使用单var语句是比较有用的一种形式，其好处在于：\r\n<ul id=\"ul\">\r\n	<li>提供了一个单一的地方去寻找功能所需要的所有局部变量</li>\r\n	<li>防止变量在定义之前使用的逻辑错误</li>\r\n	<li>帮助你记住声明的全局变量，因此较少了全局变量//zxx:此处我自己是有点晕乎的…</li>\r\n	<li>少代码（类型啊传值啊单线完成）</li>\r\n</ul>\r\n单var形式长得就像下面这个样子：\r\n<div>\r\n<pre>function func() {\r\n   var a = 1,\r\n       b = 2,\r\n       sum = a + b,\r\n       myobject = {},\r\n       i,\r\n       j;\r\n   // function body...\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n您可以使用一个var语句声明多个变量，并以逗号分隔。像这种初始化变量同时初始化值的做法是很好的。这样子可以防止逻辑错误（所有未初始化但声明的变量的初始值是<code>undefined</code>）和增加代码的可读性。在你看到代码后，你可以根据初始化的值知道这些变量大致的用途，例如是要当作对象呢还是当作整数来使。\r\n\r\n你也可以在声明的时候做一些实际的工作，例如前面代码中的<code>sum = a + b</code>这个情况，另外一个例子就是当你使用DOM（文档对象模型）引用时，你可以使用单一的var把DOM引用一起指定为局部变量，就如下面代码所示的：\r\n<div>\r\n<pre>function updateElement() {\r\n   var el = document.getElementById(\"result\"),\r\n       style = el.style;\r\n   // 使用el和style干点其他什么事...\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<h1>预解析：var散布的问题(Hoisting: A Problem with Scattered vars)</h1>\r\nJavaScript中，你可以在函数的任何位置声明多个var语句，并且它们就好像是在函数顶部声明一样发挥作用，这种行为称为 hoisting（悬置/置顶解析/预解析）。当你使用了一个变量，然后不久在函数中又重新声明的话，就可能产生逻辑错误。对于JavaScript，只 要你的变量是在同一个作用域中（同一函数），它都被当做是声明的，即使是它在var声明前使用的时候。看下面这个例子：\r\n<div>\r\n<pre>// 反例\r\nmyname = \"global\"; // 全局变量\r\nfunction func() {\r\n    alert(myname); // \"undefined\"\r\n    var myname = \"local\";\r\n    alert(myname); // \"local\"\r\n}\r\nfunc();</pre>\r\n</div>\r\n&nbsp;\r\n\r\n在这个例子中，你可能会以为第一个alert弹出的是”global”，第二个弹出”loacl”。这种期许是可以理解的，因为在第一个alert 的时候，myname未声明，此时函数肯定很自然而然地看全局变量myname，但是，实际上并不是这么工作的。第一个alert会弹 出”undefined”是因为myname被当做了函数的局部变量（尽管是之后声明的），所有的变量声明当被悬置到函数的顶部了。因此，为了避免这种混 乱，最好是预先声明你想使用的全部变量。\r\n\r\n上面的代码片段执行的行为可能就像下面这样：\r\n<div>\r\n<pre>myname = \"global\"; // global variable\r\nfunction func() {\r\n   var myname; // 等同于 -&gt; var myname = undefined;\r\n   alert(myname); // \"undefined\"\r\n   myname = \"local\";\r\n   alert(myname); // \"local\"}\r\nfunc();</pre>\r\n</div>\r\n&nbsp;\r\n<blockquote>为了完整，我们再提一提执行层面的稍微复杂点的东西。代码处理分两个阶段，第一阶段是变量，函数声明，以及正常格式的参数创建，这是一个解析和进入上下文 的阶段。第二个阶段是代码执行，函数表达式和不合格的标识符（为声明的变量）被创建。但是，出于实用的目的，我们就采用了”hoisting”这个概念， 这种ECMAScript标准中并未定义，通常用来描述行为。</blockquote>\r\n<h1>for循环(for Loops)</h1>\r\n在<code>for</code>循环中，你可以循环取得数组或是数组类似对象的值，譬如<code>arguments</code>和<code>HTMLCollection</code>对象。通常的循环形式如下：\r\n<div>\r\n<pre>// 次佳的循环\r\nfor (var i = 0; i &lt; myarray.length; i++) {\r\n   // 使用myarray[i]做点什么\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n这种形式的循环的不足在于每次循环的时候数组的长度都要去获取下。这回降低你的代码，尤其当<code>myarray</code>不是数组，而是一个<code>HTMLCollection</code>对象的时候。\r\n\r\n<code>HTMLCollections</code>指的是DOM方法返回的对象，例如：\r\n<div>\r\n<pre>document.getElementsByName()\r\ndocument.getElementsByClassName()\r\ndocument.getElementsByTagName()</pre>\r\n</div>\r\n&nbsp;\r\n\r\n还有其他一些<code>HTMLCollections</code>，这些是在DOM标准之前引进并且现在还在使用的。有：\r\n<div>\r\n<pre>document.images: 页面上所有的图片元素\r\ndocument.links : 所有a标签元素\r\ndocument.forms : 所有表单\r\ndocument.forms[0].elements : 页面上第一个表单中的所有域</pre>\r\n</div>\r\n&nbsp;\r\n\r\n集合的麻烦在于它们实时查询基本文档（HTML页面）。这意味着每次你访问任何集合的长度，你要实时查询DOM，而DOM操作一般都是比较昂贵的。\r\n\r\n这就是为什么当你循环获取值时，缓存数组(或集合)的长度是比较好的形式，正如下面代码显示的：\r\n<div>\r\n<pre>for (var i = 0, max = myarray.length; i &lt; max; i++) {\r\n   // 使用myarray[i]做点什么\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n这样，在这个循环过程中，你只检索了一次长度值。\r\n\r\n在所有浏览器下，循环获取内容时缓存<code>HTMLCollections</code>的长度是更快的，2倍(Safari3)到190倍(IE7)之间。//zxx:此数据貌似很老，仅供参考\r\n\r\n注意到，当你明确想要修改循环中的集合的时候（例如，添加更多的DOM元素），你可能更喜欢长度更新而不是常量。\r\n\r\n伴随着单var形式，你可以把变量从循环中提出来，就像下面这样：\r\n<div>\r\n<pre>function looper() {\r\n   var i = 0,\r\n        max,\r\n        myarray = [];\r\n   // ...\r\n   for (i = 0, max = myarray.length; i &lt; max; i++) {\r\n      // 使用myarray[i]做点什么\r\n   }\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n这种形式具有一致性的好处，因为你坚持了单一var形式。不足在于当重构代码的时候，复制和粘贴整个循环有点困难。例如，你从一个函数复制了一个循环到另一个函数，你不得不去确定你能够把<code>i</code>和<code>max</code>引入新的函数（如果在这里没有用的话，很有可能你要从原函数中把它们删掉）。\r\n\r\n最后一个需要对循环进行调整的是使用下面表达式之一来替换<code>i++</code>。\r\n<div>\r\n<pre>i = i + 1\r\ni += 1</pre>\r\n</div>\r\n&nbsp;\r\n\r\nJSLint提示您这样做，原因是<code>++</code>和<code>–-</code>促进了“过分棘手(excessive trickiness)”。//zxx:这里比较难翻译，我想本意应该是让代码变得更加的棘手\r\n如果你直接无视它，JSLint的<code>plusplus</code>选项会是<code>false</code>（默认是default）。\r\n\r\n还有两种变化的形式，其又有了些微改进，因为：\r\n<ul id=\"ul\">\r\n	<li>少了一个变量(无max)</li>\r\n	<li>向下数到0，通常更快，因为和0做比较要比和数组长度或是其他不是0的东西作比较更有效率</li>\r\n</ul>\r\n&nbsp;\r\n<div>\r\n<pre>//第一种变化的形式：\r\n\r\nvar i, myarray = [];\r\nfor (i = myarray.length; i–-;) {\r\n   // 使用myarray[i]做点什么\r\n}\r\n\r\n//第二种使用while循环：\r\n\r\nvar myarray = [],\r\n    i = myarray.length;\r\nwhile (i–-) {\r\n   // 使用myarray[i]做点什么\r\n}</pre>\r\n</div>\r\n这些小的改进只体现在性能上，此外JSLint会对使用i–-加以抱怨。\r\n<h1>for-in循环(for-in Loops)</h1>\r\n<code>for-in</code>循环应该用在非数组对象的遍历上，使用<code>for-in</code>进行循环也被称为“枚举”。\r\n\r\n从技术上将，你可以使用<code>for-in</code>循环数组（因为JavaScript中数组也是对象），但这是不推荐的。因为如果数组对象已被自定义的功能增强，就可能发生逻辑错误。另外，在for-in中，属性列表的顺序（序列）是不能保证的。所以最好数组使用正常的for循环，对象使用for-in循环。\r\n\r\n有个很重要的<code>hasOwnProperty()</code>方法，当遍历对象属性的时候可以过滤掉从原型链上下来的属性。\r\n\r\n思考下面一段代码：\r\n<div>\r\n<pre>// 对象\r\nvar man = {\r\n   hands: 2,\r\n   legs: 2,\r\n   heads: 1\r\n};\r\n\r\n// 在代码的某个地方\r\n// 一个方法添加给了所有对象\r\nif (typeof Object.prototype.clone === \"undefined\") {\r\n   Object.prototype.clone = function () {};\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n在这个例子中，我们有一个使用对象字面量定义的名叫man的对象。在man定义完成后的某个地方，在对象原型上增加了一个很有用的名叫 clone()的方法。此原型链是实时的，这就意味着所有的对象自动可以访问新的方法。为了避免枚举man的时候出现clone()方法，你需要应用<code>hasOwnProperty()</code>方法过滤原型属性。如果不做过滤，会导致clone()函数显示出来，在大多数情况下这是不希望出现的。\r\n<div>\r\n<pre>// 1.\r\n// for-in 循环\r\nfor (var i in man) {\r\n   if (man.hasOwnProperty(i)) { // 过滤\r\n      console.log(i, \":\", man[i]);\r\n   }\r\n}\r\n/* 控制台显示结果\r\nhands : 2\r\nlegs : 2\r\nheads : 1\r\n*/\r\n// 2.\r\n// 反面例子:\r\n// for-in loop without checking hasOwnProperty()\r\nfor (var i in man) {\r\n   console.log(i, \":\", man[i]);\r\n}\r\n/*\r\n控制台显示结果\r\nhands : 2\r\nlegs : 2\r\nheads : 1\r\nclone: function()\r\n*/</pre>\r\n</div>\r\n&nbsp;\r\n\r\n另外一种使用<code>hasOwnProperty()</code>的形式是取消Object.prototype上的方法。像是：\r\n<div>\r\n<pre>for (var i in man) {\r\n   if (Object.prototype.hasOwnProperty.call(man, i)) { // 过滤\r\n      console.log(i, \":\", man[i]);\r\n   }\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n其好处在于在man对象重新定义hasOwnProperty情况下避免命名冲突。也避免了长属性查找对象的所有方法，你可以使用局部变量“缓存”它。\r\n<div>\r\n<pre>var i, hasOwn = Object.prototype.hasOwnProperty;\r\nfor (i in man) {\r\n    if (hasOwn.call(man, i)) { // 过滤\r\n        console.log(i, \":\", man[i]);\r\n    }\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<blockquote>严格来说，不使用<code>hasOwnProperty()</code>并不是一个错误。根据任务以及你对代码的自信程度，你可以跳过它以提高些许的循环速度。但是当你对当前对象内容（和其原型链）不确定的时候，添加<code>hasOwnProperty()</code>更加保险些。</blockquote>\r\n格式化的变化（通不过JSLint）会直接忽略掉花括号，把if语句放到同一行上。其优点在于循环语句读起来就像一个完整的想法（每个元素都有一个自己的属性”X”，使用”X”干点什么）：\r\n<div>\r\n<pre>// 警告： 通不过JSLint检测\r\nvar i, hasOwn = Object.prototype.hasOwnProperty;\r\nfor (i in man) if (hasOwn.call(man, i)) { // 过滤\r\n    console.log(i, \":\", man[i]);\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<h1>（不）扩展内置原型((Not) Augmenting Built-in Prototypes)</h1>\r\n扩增构造函数的prototype属性是个很强大的增加功能的方法，但有时候它太强大了。\r\n\r\n增加内置的构造函数原型（如Object(), Array(), 或Function()）挺诱人的，但是这严重降低了可维护性，因为它让你的代码变得难以预测。使用你代码的其他开发人员很可能更期望使用内置的 JavaScript方法来持续不断地工作，而不是你另加的方法。\r\n\r\n另外，属性添加到原型中，可能会导致不使用hasOwnProperty属性时在循环中显示出来，这会造成混乱。\r\n\r\n因此，不增加内置原型是最好的。你可以指定一个规则，仅当下面的条件均满足时例外：\r\n<ul id=\"ul\">\r\n	<li>可以预期将来的ECMAScript版本或是JavaScript实现将一直将此功能当作内置方法来实现。例如，你可以添加ECMAScript 5中描述的方法，一直到各个浏览器都迎头赶上。这种情况下，你只是提前定义了有用的方法。</li>\r\n	<li>如果您检查您的自定义属性或方法已不存在——也许已经在代码的其他地方实现或已经是你支持的浏览器JavaScript引擎部分。</li>\r\n	<li>你清楚地文档记录并和团队交流了变化。</li>\r\n</ul>\r\n如果这三个条件得到满足，你可以给原型进行自定义的添加，形式如下：\r\n<div>\r\n<pre>if (typeof Object.protoype.myMethod !== \"function\") {\r\n   Object.protoype.myMethod = function () {\r\n      // 实现...\r\n   };\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<h1>switch模式(switch Pattern)</h1>\r\n你可以通过类似下面形式的switch语句增强可读性和健壮性：\r\n<div>\r\n<pre>var inspect_me = 0,\r\n    result = '''';\r\nswitch (inspect_me) {\r\ncase 0:\r\n   result = \"zero\";\r\n   break;\r\ncase 1:\r\n   result = \"one\";\r\n   break;\r\ndefault:\r\n   result = \"unknown\";\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n这个简单的例子中所遵循的风格约定如下：\r\n<ul id=\"ul\">\r\n	<li>每个case和switch对齐（花括号缩进规则除外）</li>\r\n	<li>每个case中代码缩进</li>\r\n	<li>每个case以break清除结束</li>\r\n	<li>避免贯穿（故意忽略break）。如果你非常确信贯穿是最好的方法，务必记录此情况，因为对于有些阅读人而言，它们可能看起来是错误的。</li>\r\n	<li>以default结束switch：确保总有健全的结果，即使无情况匹配。</li>\r\n</ul>\r\n<h1>避免隐式类型转换(Avoiding Implied Typecasting )</h1>\r\nJavaScript的变量在比较的时候会隐式类型转换。这就是为什么一些诸如：false == 0 或 “” == 0 返回的结果是true。为避免引起混乱的隐含类型转换，在你比较值和表达式类型的时候始终使用===和!==操作符。\r\n<div>\r\n<pre>var zero = 0;\r\nif (zero === false) {\r\n   // 不执行，因为zero为0, 而不是false\r\n}\r\n\r\n// 反面示例\r\nif (zero == false) {\r\n   // 执行了...\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n还有另外一种思想观点认为==就足够了===是多余的。例如，当你使用typeof你就知道它会返回一个字符串，所以没有使用严格相等的理由。然而，JSLint要求严格相等，它使代码看上去更有一致性，可以降低代码阅读时的精力消耗。（“==是故意的还是一个疏漏？”）\r\n<h1>避免(Avoiding) eval()</h1>\r\n如果你现在的代码中使用了eval()，记住该咒语“eval()是魔鬼”。此方法接受任意的字符串，并当作JavaScript代码来处理。当有 问题的代码是事先知道的（不是运行时确定的），没有理由使用eval()。如果代码是在运行时动态生成，有一个更好的方式不使用eval而达到同样的目 标。例如，用方括号表示法来访问动态属性会更好更简单：\r\n<div>\r\n<pre>// 反面示例\r\nvar property = \"name\";\r\nalert(eval(\"obj.\" + property));\r\n\r\n// 更好的\r\nvar property = \"name\";\r\nalert(obj[property]);</pre>\r\n</div>\r\n&nbsp;\r\n\r\n使用eval()也带来了安全隐患，因为被执行的代码（例如从网络来）可能已被篡改。这是个很常见的反面教材，当处理Ajax请求得到的JSON 相应的时候。在这些情况下，最好使用JavaScript内置方法来解析JSON相应，以确保安全和有效。若浏览器不支持JSON.parse()，你可 以使用来自JSON.org的库。\r\n\r\n同样重要的是要记住，给setInterval(), setTimeout()和Function()构造函数传递字符串，大部分情况下，与使用eval()是类似的，因此要避免。在幕后，JavaScript仍需要评估和执行你给程序传递的字符串：\r\n<div>\r\n<pre>// 反面示例\r\nsetTimeout(\"myFunc()\", 1000);\r\nsetTimeout(\"myFunc(1, 2, 3)\", 1000);\r\n\r\n// 更好的\r\nsetTimeout(myFunc, 1000);\r\nsetTimeout(function () {\r\n   myFunc(1, 2, 3);\r\n}, 1000);</pre>\r\n</div>\r\n&nbsp;\r\n\r\n使用新的Function()构造就类似于eval()，应小心接近。这可能是一个强大的构造，但往往被误用。如果你绝对必须使用eval()，你 可以考虑使用new Function()代替。有一个小的潜在好处，因为在新Function()中作代码评估是在局部函数作用域中运行，所以代码中任何被评估的通过var 定义的变量都不会自动变成全局变量。另一种方法来阻止自动全局变量是封装eval()调用到一个即时函数中。\r\n\r\n考虑下面这个例子，这里仅<code>un</code>作为全局变量污染了命名空间。\r\n<div>\r\n<pre>console.log(typeof un);    // \"undefined\"\r\nconsole.log(typeof deux); // \"undefined\"\r\nconsole.log(typeof trois); // \"undefined\"\r\n\r\nvar jsstring = \"var un = 1; console.log(un);\";\r\neval(jsstring); // logs \"1\"\r\n\r\njsstring = \"var deux = 2; console.log(deux);\";\r\nnew Function(jsstring)(); // logs \"2\"\r\n\r\njsstring = \"var trois = 3; console.log(trois);\";\r\n(function () {\r\n   eval(jsstring);\r\n}()); // logs \"3\"\r\n\r\nconsole.log(typeof un); // number\r\nconsole.log(typeof deux); // \"undefined\"\r\nconsole.log(typeof trois); // \"undefined\"</pre>\r\n</div>\r\n&nbsp;\r\n\r\n另一间eval()和Function构造不同的是eval()可以干扰作用域链，而Function()更安分守己些。不管你在哪里执行 Function()，它只看到全局作用域。所以其能很好的避免本地变量污染。在下面这个例子中，eval()可以访问和修改它外部作用域中的变量，这是 Function做不来的（注意到使用Function和new Function是相同的）。\r\n<div>\r\n<pre>(function () {\r\n   var local = 1;\r\n   eval(\"local = 3; console.log(local)\"); // logs \"3\"\r\n   console.log(local); // logs \"3\"\r\n}());\r\n\r\n(function () {\r\n   var local = 1;\r\n   Function(\"console.log(typeof local);\")(); // logs undefined\r\n}());</pre>\r\n</div>\r\n&nbsp;\r\n<h1>parseInt()下的数值转换(Number Conversions with parseInt())</h1>\r\n使用parseInt()你可以从字符串中获取数值，该方法接受另一个基数参数，这经常省略，但不应该。当字符串以”0″开头的时候就有可能会出问 题，例如，部分时间进入表单域，在ECMAScript 3中，开头为”0″的字符串被当做8进制处理了，但这已在ECMAScript 5中改变了。为了避免矛盾和意外的结果，总是指定基数参数。\r\n<div>\r\n<pre>var month = \"06\",\r\n    year = \"09\";\r\nmonth = parseInt(month, 10);\r\nyear = parseInt(year, 10);</pre>\r\n</div>\r\n&nbsp;\r\n\r\n此例中，如果你忽略了基数参数，如parseInt(year)，返回的值将是0，因为“09”被当做8进制（好比执行 parseInt( year, 8 )），而09在8进制中不是个有效数字。\r\n\r\n替换方法是将字符串转换成数字，包括：\r\n<div>\r\n<pre>+\"08\" // 结果是 8\r\nNumber(\"08\") // 8</pre>\r\n</div>\r\n&nbsp;\r\n\r\n这些通常快于parseInt()，因为parseInt()方法，顾名思意，不是简单地解析与转换。但是，如果你想输入例如“08 hello”，parseInt()将返回数字，而其它以NaN告终。\r\n<h1>编码规范(Coding Conventions)</h1>\r\n建立和遵循编码规范是很重要的，这让你的代码保持一致性，可预测，更易于阅读和理解。一个新的开发者加入这个团队可以通读规范，理解其它团队成员书写的代码，更快上手干活。\r\n\r\n许多激烈的争论发生会议上或是邮件列表上，问题往往针对某些代码规范的特定方面（例如代码缩进，是Tab制表符键还是space空格键）。如果你是 你组织中建议采用规范的，准备好面对各种反对的或是听起来不同但很强烈的观点。要记住，建立和坚定不移地遵循规范要比纠结于规范的细节重要的多。\r\n<h1>缩进(Indentation)</h1>\r\n代码没有缩进基本上就不能读了。唯一糟糕的事情就是不一致的缩进，因为它看上去像是遵循了规范，但是可能一路上伴随着混乱和惊奇。重要的是规范地使用缩进。\r\n\r\n一些开发人员更喜欢用tab制表符缩进，因为任何人都可以调整他们的编辑器以自己喜欢的空格数来显示Tab。有些人喜欢空格——通常四个，这都无所谓，只要团队每个人都遵循同一个规范就好了。这本书，例如，使用四个空格缩进，这也是JSLint中默认的缩进。\r\n\r\n什么应该缩进呢？规则很简单——花括号里面的东西。这就意味着函数体，循环 (do, while, for, for-in)，if，switch，以及对象字面量中的对象属性。下面的代码就是使用缩进的示例：\r\n<div>\r\n<pre>function outer(a, b) {\r\n    var c = 1,\r\n        d = 2,\r\n        inner;\r\n    if (a &gt; b) {\r\n        inner = function () {\r\n            return {\r\n                r: c - d\r\n            };\r\n        };\r\n    } else {\r\n        inner = function () {\r\n            return {\r\n                r: c + d\r\n            };\r\n        };\r\n    }\r\n    return inner;\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<h1>花括号{}(Curly Braces)</h1>\r\n花括号（亦称大括号，下同）应总被使用，即使在它们为可选的时候。技术上将，在in或是for中如果语句仅一条，花括号是不需要的，但是你还是应该总是使用它们，这会让代码更有持续性和易于更新。\r\n\r\n想象下你有一个只有一条语句的for循环，你可以忽略花括号，而没有解析的错误。\r\n<div>\r\n<pre>// 糟糕的实例\r\nfor (var i = 0; i &lt; 10; i += 1)\r\n   alert(i);</pre>\r\n</div>\r\n&nbsp;\r\n\r\n但是，如果，后来，主体循环部分又增加了行代码？\r\n<div>\r\n<pre>// 糟糕的实例\r\nfor (var i = 0; i &lt; 10; i += 1)\r\n   alert(i);\r\n   alert(i + \" is \" + (i % 2 ? \"odd\" : \"even\"));</pre>\r\n</div>\r\n&nbsp;\r\n\r\n第二个alert已经在循环之外，缩进可能欺骗了你。为了长远打算，最好总是使用花括号，即时值一行代码：\r\n<div>\r\n<pre>// 好的实例\r\nfor (var i = 0; i &lt; 10; i += 1) {\r\n   alert(i);\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\nif条件类似：\r\n<div>\r\n<pre>// 坏\r\nif (true)\r\n   alert(1);\r\nelse\r\n   alert(2);\r\n\r\n// 好\r\nif (true) {\r\n   alert(1);\r\n} else {\r\n   alert(2);\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<h1>左花括号的位置(Opening Brace Location)</h1>\r\n开发人员对于左大括号的位置有着不同的偏好——在同一行或是下一行。\r\n<div>\r\n<pre>if (true) {\r\n   alert(\"It''s TRUE!\");\r\n}\r\n\r\n//或\r\n\r\nif (true)\r\n{\r\n   alert(\"It''s TRUE!\");\r\n}</pre>\r\n</div>\r\n这个实例中，仁者见仁智者见智，但也有个案，括号位置不同会有不同的行为表现。这是因为分号插入机制(semicolon insertion mechanism)——JavaScript是不挑剔的，当你选择不使用分号结束一行代码时JavaScript会自己帮你补上。这种行为可能会导致麻 烦，如当你返回对象字面量，而左括号却在下一行的时候：\r\n<div>\r\n<pre>// 警告： 意外的返回值\r\nfunction func() {\r\n   return\r\n  // 下面代码不执行\r\n   {\r\n      name : \"Batman\"\r\n   }\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n如果你希望函数返回一个含有name属性的对象，你会惊讶。由于隐含分号，函数返回undefined。前面的代码等价于：\r\n<div>\r\n<pre>// 警告： 意外的返回值\r\nfunction func() {\r\n   return undefined;\r\n  // 下面代码不执行\r\n   {\r\n      name : \"Batman\"\r\n   }\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n总之，总是使用花括号，并始终把在与之前的语句放在同一行：\r\n<div>\r\n<pre>function func() {\r\n   return {\r\n      name : \"Batman\"\r\n   };\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<blockquote>关于分号注：就像使用花括号，你应该总是使用分号，即使他们可由JavaScript解析器隐式创建。这不仅促进更科学和更严格的代码，而且有助于解决存有疑惑的地方，就如前面的例子显示。</blockquote>\r\n<h1>空格(White Space)</h1>\r\n空格的使用同样有助于改善代码的可读性和一致性。在写英文句子的时候，在逗号和句号后面会使用间隔。在JavaScript中，你可以按照同样的逻辑在列表模样表达式（相当于逗号）和结束语句（相对于完成了“想法”）后面添加间隔。\r\n\r\n适合使用空格的地方包括：\r\n<ul id=\"ul\">\r\n	<li>for循环分号分开后的的部分：如<code>for (var i = 0; i &lt; 10; i += 1) {...}</code></li>\r\n	<li>for循环中初始化的多变量(i和max)：<code>for (var i = 0, max = 10; i &lt; max; i += 1) {...}</code></li>\r\n	<li>分隔数组项的逗号的后面：<code>var a = [1, 2, 3];</code></li>\r\n	<li>对象属性逗号的后面以及分隔属性名和属性值的冒号的后面：<code>var o = {a: 1, b: 2};</code></li>\r\n	<li>限定函数参数：<code>myFunc(a, b, c)</code></li>\r\n	<li>函数声明的花括号的前面：<code>function myFunc() {}</code></li>\r\n	<li>匿名函数表达式function的后面：<code>var myFunc = function () {};</code></li>\r\n</ul>\r\n使用空格分开所有的操作符和操作对象是另一个不错的使用，这意味着在<code>+, -, *, =, &lt;, &gt;, &lt;=, &gt;=, ===, !==, &amp;&amp;, ||, +=</code>等前后都需要空格。\r\n<div>\r\n<pre>// 宽松一致的间距\r\n// 使代码更易读\r\n// 使得更加“透气”\r\nvar d = 0,\r\n    a = b + 1;\r\nif (a &amp;&amp; b &amp;&amp; c) {\r\n    d = a % c;\r\n    a += d;\r\n}\r\n\r\n// 反面例子\r\n// 缺失或间距不一\r\n// 使代码变得疑惑\r\nvar d = 0,\r\n    a = b + 1;\r\nif (a&amp;&amp;b&amp;&amp;c) {\r\n    d=a % c;\r\n    a+= d;\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n最后需要注意的一个空格——花括号间距。最好使用空格：\r\n<ul id=\"ul\">\r\n	<li>函数、if-else语句、循环、对象字面量的左花括号的前面({)</li>\r\n	<li>else或while之间的右花括号(})</li>\r\n</ul>\r\n空格使用的一点不足就是增加了文件的大小，但是压缩无此问题。\r\n<blockquote>有一个经常被忽略的代码可读性方面是垂直空格的使用。你可以使用空行来分隔代码单元，就像是文学作品中使用段落分隔一样。</blockquote>\r\n<h3>命名规范(Naming Conventions)</h3>\r\n另一种方法让你的代码更具可预测性和可维护性是采用命名规范。这就意味着你需要用同一种形式给你的变量和函数命名。\r\n\r\n下面是建议的一些命名规范，你可以原样采用，也可以根据自己的喜好作调整。同样，遵循规范要比规范是什么更重要。\r\n<h3>以大写字母写构造函数(Capitalizing Constructors)</h3>\r\nJavaScript并没有类，但有new调用的构造函数：\r\n<div>\r\n<pre>var adam = new Person();</pre>\r\n</div>\r\n&nbsp;\r\n\r\n因为构造函数仍仅仅是函数，仅看函数名就可以帮助告诉你这应该是一个构造函数还是一个正常的函数。\r\n\r\n命名构造函数时首字母大写具有暗示作用，使用小写命名的函数和方法不应该使用new调用：\r\n<div>\r\n<pre>function MyConstructor() {...}\r\nfunction myFunction() {...}</pre>\r\n</div>\r\n&nbsp;\r\n<h1>分隔单词(Separating Words)</h1>\r\n当你的变量或是函数名有多个单词的时候，最好单词的分离遵循统一的规范，有一个常见的做法被称作“驼峰(Camel)命名法”，就是单词小写，每个单词的首字母大写。\r\n\r\n对于构造函数，可以使用大驼峰式命名法(upper camel case)，如<code>MyConstructor()</code>。对于函数和方法名称，你可以使用小驼峰式命名法(lower camel case)，像是<code>myFunction(), calculateArea()</code>和<code>getFirstName()</code>。\r\n\r\n要是变量不是函数呢？开发者通常使用小驼峰式命名法，但还有另外一种做法就是所有单词小写以下划线连接：例如，<code>first_name, favorite_bands, </code>和<code> old_company_name</code>，这种标记法帮你直观地区分函数和其他标识——原型和对象。\r\n\r\nECMAScript的属性和方法均使用Camel标记法，尽管多字的属性名称是罕见的（正则表达式对象的lastIndex和ignoreCase属性）。\r\n<h1>其它命名形式(Other Naming Patterns)</h1>\r\n有时，开发人员使用命名规范来弥补或替代语言特性。\r\n\r\n例如，JavaScript中没有定义常量的方法（尽管有些内置的像Number, MAX_VALUE），所以开发者都采用全部单词大写的规范来命名这个程序生命周期中都不会改变的变量，如：\r\n<div>\r\n<pre>// 珍贵常数，只可远观\r\nvar PI = 3.14,\r\n    MAX_WIDTH = 800;</pre>\r\n</div>\r\n&nbsp;\r\n\r\n还有另外一个完全大写的惯例：全局变量名字全部大写。全部大写命名全局变量可以加强减小全局变量数量的实践，同时让它们易于区分。\r\n\r\n另外一种使用规范来模拟功能的是私有成员。虽然可以在JavaScript中实现真正的私有，但是开发者发现仅仅使用一个下划线前缀来表示一个私有属性或方法会更容易些。考虑下面的例子：\r\n<div>\r\n<pre>var person = {\r\n    getName: function () {\r\n        return this._getFirst() + '' '' + this._getLast();\r\n    },\r\n\r\n    _getFirst: function () {\r\n        // ...\r\n    },\r\n    _getLast: function () {\r\n        // ...\r\n    }\r\n};</pre>\r\n</div>\r\n&nbsp;\r\n\r\n在此例中，<code>getName()</code>就表示公共方法，部分稳定的API。而<code>_getFirst()</code>和<code>_getLast()</code>则表明了私有。它们仍然是正常的公共方法，但是使用下划线前缀来警告person对象的使用者这些方法在下一个版本中时不能保证工作的，是不能直接使用的。注意，JSLint有些不鸟下划线前缀，除非你设置了noman选项为:false。\r\n\r\n下面是一些常见的_private规范：\r\n<ul id=\"ul\">\r\n	<li>使用尾下划线表示私有，如name_和getElements_()</li>\r\n	<li>使用一个下划线前缀表_protected（保护）属性，两个下划线前缀表示__private （私有）属性</li>\r\n	<li>Firefox中一些内置的变量属性不属于该语言的技术部分，使用两个前下划线和两个后下划线表示，如：__proto__和__parent__。</li>\r\n</ul>\r\n<h1>注释(Writing Comments)</h1>\r\n你必须注释你的代码，即使不会有其他人向你一样接触它。通常，当你深入研究一个问题，你会很清楚的知道这个代码是干嘛用的，但是，当你一周之后再回来看的时候，想必也要耗掉不少脑细胞去搞明白到底怎么工作的。\r\n\r\n很显然，注释不能走极端：每个单独变量或是单独一行。但是，你通常应该记录所有的函数，它们的参数和返回值，或是任何不寻常的技术和方法。要想到注 释可以给你代码未来的阅读者以诸多提示；阅读者需要的是（不要读太多的东西）仅注释和函数属性名来理解你的代码。例如，当你有五六行程序执行特定的任务， 如果你提供了一行代码目的以及为什么在这里的描述的话，阅读者就可以直接跳过这段细节。没有硬性规定注释代码比，代码的某些部分（如正则表达式）可能注释 要比代码多。\r\n<blockquote>最重要的习惯，然而也是最难遵守的，就是保持注释的及时更新，因为过时的注释比没有注释更加的误导人。</blockquote>\r\n<h1>关于作者（About the Author ）</h1>\r\nStoyan Stefanov是Yahoo!web开发人员，多个O''Reilly书籍的作者、投稿者和技术评审。他经常在会议和他的博客<a href=\"http://www.zhangxinxu.com/wordpress/2010/10/%e7%bf%bb%e8%af%91-%e9%ab%98%e8%b4%a8%e9%87%8fjavascript%e4%bb%a3%e7%a0%81%e4%b9%a6%e5%86%99%e5%9f%ba%e6%9c%ac%e8%a6%81%e7%82%b9/www.phpied.com\" target=\"_blank\">www.phpied.com</a>上发表web开发主题的演讲。Stoyan还是smush.it图片优化工具的创造者，YUI贡献者，雅虎性能优化工具YSlow 2.0的架构设计师。\r\n\r\n&nbsp;\r\n\r\n本文转自：http://www.zhangxinxu.com/wordpress/?p=1173\r\n\r\n英文原文：http://net.tutsplus.com/tutorials/javascript-ajax/the-essentials-of-writing-high-quality-javascript/\r\n<h1>同步与结束语</h1>\r\n本文已同步至目录索引：<a href=\"http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html\" target=\"_blank\">深入理解JavaScript系列</a>\r\n\r\n深入理解JavaScript系列文章，包括了原创，翻译，转载等各类型的文章，如果对你有用，请推荐支持一把，给大叔写作的动力。','深入理解JavaScript系列（1）：编写高质量JavaScript代码的基本要点','','publish','open','open','','%e6%b7%b1%e5%85%a5%e7%90%86%e8%a7%a3javascript%e7%b3%bb%e5%88%97%ef%bc%881%ef%bc%89%ef%bc%9a%e7%bc%96%e5%86%99%e9%ab%98%e8%b4%a8%e9%87%8fjavascript%e4%bb%a3%e7%a0%81%e7%9a%84%e5%9f%ba%e6%9c%ac','','','2013-03-29 17:03:17','2013-03-29 09:03:17','','0','http://wap1.benbenweb.com/?p=20','0','post','','0');");
E_D("replace into `wp_posts` values('21','1','2013-03-29 16:58:21','2013-03-29 08:58:21','','深入理解JavaScript系列（1）：编写高质量JavaScript代码的基本要点','','inherit','open','open','','20-revision','','','2013-03-29 16:58:21','2013-03-29 08:58:21','','20','http://wap1.benbenweb.com/?p=21','0','revision','','0');");
E_D("replace into `wp_posts` values('22','1','2013-03-29 17:01:08','2013-03-29 09:01:08','<h1>前言</h1>\r\n网上还没用发现有人对命名函数表达式进去重复深入的讨论，正因为如此，网上出现了各种各样的误解，本文将从原理和实践两个方面来探讨JavaScript关于命名函数表达式的优缺点。\r\n\r\n简单的说，命名函数表达式只有一个用户，那就是在Debug或者Profiler分析的时候来描述函数的名称，也可以使用函数名实现递归，但很快你就会发现其实是不切实际的。当然，如果你不关注调试，那就没什么可担心的了，否则，如果你想了解兼容性方面的东西的话，你还是应该继续往下看看。\r\n\r\n我们先开始看看，什么叫函数表达式，然后再说一下现代调试器如何处理这些表达式，如果你已经对这方面很熟悉的话，请直接跳过此小节。\r\n<h1>函数表达式和函数声明</h1>\r\n在ECMAScript中，创建函数的最常用的两个方法是函数表达式和函数声明，两者期间的区别是有点晕，因为ECMA规范只明确了一点：函数声明必须带有标示符（Identifier）（就是大家常说的函数名称），而函数表达式则可以省略这个标示符：\r\n\r\n函数声明:\r\n\r\nfunction 函数名称 <strong>(</strong>参数：可选<strong>){</strong> 函数体<strong> }</strong>\r\n\r\n函数表达式：\r\n\r\nfunction 函数名称（可选）<strong>(</strong>参数：可选<strong>){</strong> 函数体 <strong>}</strong>\r\n\r\n所以，可以看出，如果不声明函数名称，它肯定是表达式，可如果声明了函数名称的话，如何判断是函数声明还是函数表达式呢？ECMAScript是通过上下文来区分的，如果function foo(){}是作为赋值表达式的一部分的话，那它就是一个函数表达式，如果function foo(){}被包含在一个函数体内，或者位于程序的最顶部的话，那它就是一个函数声明。\r\n<div>\r\n<pre>  function foo(){} // 声明，因为它是程序的一部分\r\n  var bar = function foo(){}; // 表达式，因为它是赋值表达式的一部分\r\n\r\n  new function bar(){}; // 表达式，因为它是new表达式\r\n\r\n  (function(){\r\n    function bar(){} // 声明，因为它是函数体的一部分\r\n  })();</pre>\r\n</div>\r\n还有一种函数表达式不太常见，就是被括号括住的(function foo(){})，他是表达式的原因是因为括号 ()是一个分组操作符，它的内部只能包含表达式，我们来看几个例子：\r\n<div>\r\n<pre>  function foo(){} // 函数声明\r\n  (function foo(){}); // 函数表达式：包含在分组操作符内\r\n  \r\n  try {\r\n    (var x = 5); // 分组操作符，只能包含表达式而不能包含语句：这里的var就是语句\r\n  } catch(err) {\r\n    // SyntaxError\r\n  }</pre>\r\n</div>\r\n你可以会想到，在使用eval对JSON进行执行的时候，JSON字符串通常被包含在一个圆括号里：eval(''('' + json + '')'')，这样做的原因就是因为分组操作符，也就是这对括号，会让解析器强制将JSON的花括号解析成表达式而不是代码块。\r\n<div>\r\n<pre>  try {\r\n    { \"x\": 5 }; // \"{\" 和 \"}\" 做解析成代码块\r\n  } catch(err) {\r\n    // SyntaxError\r\n  }\r\n  \r\n  ({ \"x\": 5 }); // 分组操作符强制将\"{\" 和 \"}\"作为对象字面量来解析</pre>\r\n</div>\r\n表达式和声明存在着十分微妙的差别，首先，函数声明会在任何表达式被解析和求值之前先被解析和求值，即使你的声明在代码的最后一行，它也会在同作用域内第一个表达式之前被解析/求值，参考如下例子，函数fn是在alert之后声明的，但是在alert执行的时候，fn已经有定义了：\r\n<div>\r\n<pre>  alert(fn());\r\n\r\n  function fn() {\r\n    return ''Hello world!'';\r\n  }</pre>\r\n</div>\r\n另外，还有一点需要提醒一下，函数声明在条件语句内虽然可以用，但是没有被标准化，也就是说不同的环境可能有不同的执行结果，所以这样情况下，最好使用函数表达式：\r\n<div>\r\n<pre>  // 千万别这样做！\r\n  // 因为有的浏览器会返回first的这个function，而有的浏览器返回的却是第二个\r\n\r\n  if (true) {\r\n    function foo() {\r\n      return ''first'';\r\n    }\r\n  }\r\n  else {\r\n    function foo() {\r\n      return ''second'';\r\n    }\r\n  }\r\n  foo();\r\n\r\n  // 相反，这样情况，我们要用函数表达式\r\n  var foo;\r\n  if (true) {\r\n    foo = function() {\r\n      return ''first'';\r\n    };\r\n  }\r\n  else {\r\n    foo = function() {\r\n      return ''second'';\r\n    };\r\n  }\r\n  foo();</pre>\r\n</div>\r\n函数声明的实际规则如下：\r\n\r\n<em>函数声明</em>只能出现在<em>程序</em>或<em>函数体</em>内。从句法上讲，它们 不能出现在Block（块）（{ ... }）中，例如不能出现在 if、while 或 for 语句中。因为 Block（块） 中只能包含Statement语句， 而不能包含<em>函数声明</em>这样的源元素。另一方面，仔细看一看规则也会发现，唯一可能让<em>表达式</em>出现在Block（块）中情形，就是让它作为<em>表达式语句</em>的一部分。但是，规范明确规定了<em>表达式语句</em>不能以关键字function开头。而这实际上就是说，<em>函数表达式</em>同样也不能出现在Statement语句或Block（块）中（因为Block（块）就是由Statement语句构成的）。\r\n<h1>函数语句</h1>\r\n在ECMAScript的语法扩展中，有一个是函数语句，目前只有基于Gecko的浏览器实现了该扩展，所以对于下面的例子，我们仅是抱着学习的目的来看，一般来说不推荐使用（除非你针对Gecko浏览器进行开发）。\r\n\r\n1.一般语句能用的地方，函数语句也能用，当然也包括Block块中：\r\n<div>\r\n<pre>  if (true) {\r\n    function f(){ }\r\n  }\r\n  else {\r\n    function f(){ }\r\n  }</pre>\r\n</div>\r\n2.函数语句可以像其他语句一样被解析，包含基于条件执行的情形\r\n<div>\r\n<pre>  if (true) {\r\n    function foo(){ return 1; }\r\n  }\r\n  else {\r\n    function foo(){ return 2; }\r\n  }\r\n  foo(); // 1\r\n  // 注：其它客户端会将foo解析成函数声明 \r\n  // 因此，第二个foo会覆盖第一个，结果返回2，而不是1</pre>\r\n</div>\r\n3.函数语句不是在变量初始化期间声明的，而是在运行时声明的——与函数表达式一样。不过，函数语句的标识符一旦声明能在函数的整个作用域生效了。标识符有效性正是导致函数语句与函数表达式不同的关键所在（下一小节我们将会展示命名函数表达式的具体行为）。\r\n<div>\r\n<pre>  // 此刻，foo还没用声明\r\n  typeof foo; // \"undefined\"\r\n  if (true) {\r\n    // 进入这里以后，foo就被声明在整个作用域内了\r\n    function foo(){ return 1; }\r\n  }\r\n  else {\r\n    // 从来不会走到这里，所以这里的foo也不会被声明\r\n    function foo(){ return 2; }\r\n  }\r\n  typeof foo; // \"function\"\r\n</pre>\r\n</div>\r\n不过，我们可以使用下面这样的符合标准的代码来模式上面例子中的函数语句：\r\n<div>\r\n<pre>  var foo;\r\n  if (true) {\r\n    foo = function foo(){ return 1; };\r\n  }\r\n  else {\r\n    foo = function foo() { return 2; };\r\n  }</pre>\r\n</div>\r\n4.函数语句和函数声明（或命名函数表达式）的字符串表示类似，也包括标识符：\r\n<div>\r\n<pre>  if (true) {\r\n    function foo(){ return 1; }\r\n  }\r\n  String(foo); // function foo() { return 1; }</pre>\r\n</div>\r\n5.另外一个，早期基于Gecko的实现（Firefox 3及以前版本）中存在一个bug，即函数语句覆盖函数声明的方式不正确。在这些早期的实现中，函数语句不知何故不能覆盖函数声明：\r\n<div>\r\n<pre>  // 函数声明\r\n  function foo(){ return 1; }\r\n  if (true) {\r\n    // 用函数语句重写\r\n    function foo(){ return 2; }\r\n  }\r\n  foo(); // FF3以下返回1，FF3.5以上返回2\r\n  \r\n  // 不过，如果前面是函数表达式，则没用问题\r\n  var foo = function(){ return 1; };\r\n  if (true) {\r\n    function foo(){ return 2; }\r\n  }\r\n  foo(); // 所有版本都返回2</pre>\r\n</div>\r\n再次强调一点，上面这些例子只是在某些浏览器支持，所以推荐大家不要使用这些，除非你就在特性的浏览器上做开发。\r\n<h1>命名函数表达式</h1>\r\n函数表达式在实际应用中还是很常见的，在web开发中友个常用的模式是基于对某种特性的测试来伪装函数定义，从而达到性能优化的目的，但由于这种方式都是在同一作用域内，所以基本上一定要用函数表达式：\r\n<div>\r\n<pre>  // 该代码来自Garrett Smith的APE Javascript library库(http://dhtmlkitchen.com/ape/) \r\n  var contains = (function() {\r\n    var docEl = document.documentElement;\r\n\r\n    if (typeof docEl.compareDocumentPosition != ''undefined'') {\r\n      return function(el, b) {\r\n        return (el.compareDocumentPosition(b) &amp; 16) !== 0;\r\n      };\r\n    }\r\n    else if (typeof docEl.contains != ''undefined'') {\r\n      return function(el, b) {\r\n        return el !== b &amp;&amp; el.contains(b);\r\n      };\r\n    }\r\n    return function(el, b) {\r\n      if (el === b) return false;\r\n      while (el != b &amp;&amp; (b = b.parentNode) != null);\r\n      return el === b;\r\n    };\r\n  })();</pre>\r\n</div>\r\n提到命名函数表达式，理所当然，就是它得有名字，前面的例子var bar = function foo(){};就是一个有效的命名函数表达式，但有一点需要记住：这个名字只在新定义的函数作用域内有效，因为规范规定了标示符不能在外围的作用域内有效：\r\n<div>\r\n<pre>  var f = function foo(){\r\n    return typeof foo; // foo是在内部作用域内有效\r\n  };\r\n  // foo在外部用于是不可见的\r\n  typeof foo; // \"undefined\"\r\n  f(); // \"function\"</pre>\r\n</div>\r\n既然，这么要求，那命名函数表达式到底有啥用啊？为啥要取名？\r\n\r\n正如我们开头所说：给它一个名字就是可以让调试过程更方便，因为在调试的时候，如果在调用栈中的每个项都有自己的名字来描述，那么调试过程就太爽了，感受不一样嘛。\r\n<h1>调试器中的函数名</h1>\r\n如果一个函数有名字，那调试器在调试的时候会将它的名字显示在调用的栈上。有些调试器（Firebug）有时候还会为你们函数取名并显示，让他们和那些应用该函数的便利具有相同的角色，可是通常情况下，这些调试器只安装简单的规则来取名，所以说没有太大价格，我们来看一个例子：\r\n<div>\r\n<pre>  function foo(){\r\n    return bar();\r\n  }\r\n  function bar(){\r\n    return baz();\r\n  }\r\n  function baz(){\r\n    debugger;\r\n  }\r\n  foo();\r\n\r\n  // 这里我们使用了3个带名字的函数声明\r\n  // 所以当调试器走到debugger语句的时候，Firebug的调用栈上看起来非常清晰明了 \r\n  // 因为很明白地显示了名称\r\n  baz\r\n  bar\r\n  foo\r\n  expr_test.html()</pre>\r\n</div>\r\n通过查看调用栈的信息，我们可以很明了地知道foo调用了bar, bar又调用了baz（而foo本身有在expr_test.html文档的全局作用域内被调用），不过，还有一个比较爽地方，就是刚才说的Firebug为匿名表达式取名的功能：\r\n<div>\r\n<pre>  function foo(){\r\n    return bar();\r\n  }\r\n  var bar = function(){\r\n    return baz();\r\n  }\r\n  function baz(){\r\n    debugger;\r\n  }\r\n  foo();\r\n\r\n  // Call stack\r\n  baz\r\n  bar() //看到了么？ \r\n  foo\r\n  expr_test.html()</pre>\r\n</div>\r\n然后，当函数表达式稍微复杂一些的时候，调试器就不那么聪明了，我们只能在调用栈中看到问号：\r\n<div>\r\n<pre>  function foo(){\r\n    return bar();\r\n  }\r\n  var bar = (function(){\r\n    if (window.addEventListener) {\r\n      return function(){\r\n        return baz();\r\n      };\r\n    }\r\n    else if (window.attachEvent) {\r\n      return function() {\r\n        return baz();\r\n      };\r\n    }\r\n  })();\r\n  function baz(){\r\n    debugger;\r\n  }\r\n  foo();\r\n\r\n  // Call stack\r\n  baz\r\n  (?)() // 这里可是问号哦\r\n  foo\r\n  expr_test.html()</pre>\r\n</div>\r\n另外，当把函数赋值给多个变量的时候，也会出现令人郁闷的问题：\r\n<div>\r\n<pre>  function foo(){\r\n    return baz();\r\n  }\r\n  var bar = function(){\r\n    debugger;\r\n  };\r\n  var baz = bar;\r\n  bar = function() { \r\n    alert(''spoofed'');\r\n  };\r\n  foo();\r\n\r\n  // Call stack:\r\n  bar()\r\n  foo\r\n  expr_test.html()</pre>\r\n</div>\r\n这时候，调用栈显示的是foo调用了bar，但实际上并非如此，之所以有这种问题，是因为baz和另外一个包含alert(''spoofed'')的函数做了引用交换所导致的。\r\n\r\n归根结底，只有给函数表达式取个名字，才是最委托的办法，也就是使用<strong>命名函数表达式</strong>。我们来使用带名字的表达式来重写上面的例子（注意立即调用的表达式块里返回的2个函数的名字都是bar）：\r\n<div>\r\n<pre>  function foo(){\r\n    return bar();\r\n  }\r\n  var bar = (function(){\r\n    if (window.addEventListener) {\r\n      return function bar(){\r\n        return baz();\r\n      };\r\n    }\r\n    else if (window.attachEvent) {\r\n      return function bar() {\r\n        return baz();\r\n      };\r\n    }\r\n  })();\r\n  function baz(){\r\n    debugger;\r\n  }\r\n  foo();\r\n\r\n  // 又再次看到了清晰的调用栈信息了耶!\r\n  baz\r\n  bar\r\n  foo\r\n  expr_test.html()</pre>\r\n</div>\r\nOK，又学了一招吧？不过在高兴之前，我们再看看不同寻常的JScript吧。\r\n<h1>JScript的Bug</h1>\r\n比较恶的是，IE的ECMAScript实现JScript严重混淆了命名函数表达式，搞得现很多人都出来反对命名函数表达式，而且即便是最新的一版（IE8中使用的5.8版）仍然存在下列问题。\r\n\r\n下面我们就来看看IE在实现中究竟犯了那些错误，俗话说知已知彼，才能百战不殆。我们来看看如下几个例子：\r\n\r\n<strong>例1：函数表达式的标示符泄露到外部作用域</strong>\r\n<div>\r\n<pre>    var f = function g(){};\r\n    typeof g; // \"function\"</pre>\r\n</div>\r\n上面我们说过，命名函数表达式的标示符在外部作用域是无效的，但JScript明显是违反了这一规范，上面例子中的标示符g被解析成函数对象，这就乱了套了，很多难以发现的bug都是因为这个原因导致的。\r\n\r\n<em>注：IE9貌似已经修复了这个问题</em>\r\n\r\n<strong>例2：将命名函数表达式同时当作函数声明和函数表达式</strong>\r\n<div>\r\n<pre>    typeof g; // \"function\"\r\n    var f = function g(){};</pre>\r\n</div>\r\n特性环境下，函数声明会优先于任何表达式被解析，上面的例子展示的是JScript实际上是把命名函数表达式当成函数声明了，因为它在实际声明之前就解析了g。\r\n\r\n这个例子引出了下一个例子。\r\n<strong>例3：命名函数表达式会创建两个截然不同的函数对象！</strong>\r\n<div>\r\n<pre>    var f = function g(){};\r\n    f === g; // false\r\n\r\n    f.expando = ''foo'';\r\n    g.expando; // undefined</pre>\r\n</div>\r\n看到这里，大家会觉得问题严重了，因为修改任何一个对象，另外一个没有什么改变，这太恶了。通过这个例子可以发现，创建2个不同的对象，也就是说如果你想修改f的属性中保存某个信息，然后想当然地通过引用相同对象的g的同名属性来使用，那问题就大了，因为根本就不可能。\r\n\r\n再来看一个稍微复杂的例子：\r\n\r\n<strong>例4：仅仅顺序解析函数声明而忽略条件语句块</strong>\r\n<div>\r\n<pre>    var f = function g() {\r\n      return 1;\r\n    };\r\n    if (false) {\r\n      f = function g(){\r\n        return 2;\r\n      };\r\n    }\r\n    g(); // 2</pre>\r\n</div>\r\n这个bug查找就难多了，但导致bug的原因却非常简单。首先，g被当作函数声明解析，由于JScript中的函数声明不受条件代码块约束，所以在这个很恶的if分支中，g被当作另一个函数function g(){ return 2 }，也就是又被声明了一次。然后，所有“常规的”表达式被求值，而此时f被赋予了另一个新创建的对象的引用。由于在对表达式求值的时候，永远不会进入“这个可恶if分支，因此f就会继续引用第一个函数function g(){ return 1 }。分析到这里，问题就很清楚了：假如你不够细心，在f中调用了g，那么将会调用一个毫不相干的g函数对象。\r\n\r\n你可能会文，将不同的对象和arguments.callee相比较时，有什么样的区别呢？我们来看看：\r\n<div>\r\n<pre> var f = function g(){\r\n    return [\r\n      arguments.callee == f,\r\n      arguments.callee == g\r\n    ];\r\n  };\r\n  f(); // [true, false]\r\n  g(); // [false, true]</pre>\r\n</div>\r\n可以看到，arguments.callee的引用一直是被调用的函数，实际上这也是好事，稍后会解释。\r\n\r\n还有一个有趣的例子，那就是在不包含声明的赋值语句中使用命名函数表达式：\r\n<div>\r\n<pre>  (function(){\r\n    f = function f(){};\r\n  })();</pre>\r\n</div>\r\n按照代码的分析，我们原本是想创建一个全局属性f（注意不要和一般的匿名函数混淆了，里面用的是带名字的生命），JScript在这里捣乱了一把，首先他把表达式当成函数声明解析了，所以左边的f被声明为局部变量了（和一般的匿名函数里的声明一样），然后在函数执行的时候，f已经是定义过的了，右边的function f(){}则直接就赋值给局部变量f了，所以f根本就不是全局属性。\r\n\r\n&nbsp;\r\n\r\n了解了JScript这么变态以后，我们就要及时预防这些问题了，首先<strong>防范标识符泄漏带外部作用域</strong>，其次，应该永远<strong>不引用被用作函数名称的标识符</strong>；还记得前面例子中那个讨人厌的标识符g吗？——如果我们能够当g不存在，可以避免多少不必要的麻烦哪。因此，关键就在于始终要通过f或者arguments.callee来引用函数。如果你使用了命名函数表达式，那么应该只在调试的时候利用那个名字。最后，还要记住一点，一定要把<strong>命名函数表达式声明期间错误创建的函数清理干净</strong>。\r\n\r\n对于，上面最后一点，我们还得再解释一下。\r\n<h1>JScript的内存管理</h1>\r\n知道了这些不符合规范的代码解析bug以后，我们如果用它的话，就会发现内存方面其实是有问题的，来看一个例子：\r\n<div>\r\n<pre>  var f = (function(){\r\n    if (true) {\r\n      return function g(){};\r\n    }\r\n    return function g(){};\r\n  })();</pre>\r\n</div>\r\n我们知道，这个匿名函数调用返回的函数（带有标识符g的函数），然后赋值给了外部的f。我们也知道，命名函数表达式会导致产生多余的函数对象，而该对象与返回的函数对象不是一回事。所以这个多余的g函数就死在了返回函数的闭包中了，因此内存问题就出现了。这是因为if语句内部的函数与g是在同一个作用域中被声明的。这种情况下 ，除非我们显式断开对g函数的引用，否则它一直占着内存不放。\r\n<div>\r\n<pre>  var f = (function(){\r\n    var f, g;\r\n    if (true) {\r\n      f = function g(){};\r\n    }\r\n    else {\r\n      f = function g(){};\r\n    }\r\n    // 设置g为null以后它就不会再占内存了\r\n    g = null;\r\n    return f;\r\n  })();</pre>\r\n</div>\r\n通过设置g为null，垃圾回收器就把g引用的那个隐式函数给回收掉了，为了验证我们的代码，我们来做一些测试，以确保我们的内存被回收了。\r\n\r\n测试\r\n\r\n测试很简单，就是命名函数表达式创建10000个函数，然后把它们保存在一个数组中。等一会儿以后再看这些函数到底占用了多少内存。然后，再断开这些引用并重复这一过程。下面是测试代码：\r\n<div>\r\n<pre>  function createFn(){\r\n    return (function(){\r\n      var f;\r\n      if (true) {\r\n        f = function F(){\r\n          return ''standard'';\r\n        };\r\n      }\r\n      else if (false) {\r\n        f = function F(){\r\n          return ''alternative'';\r\n        };\r\n      }\r\n      else {\r\n        f = function F(){\r\n          return ''fallback'';\r\n        };\r\n      }\r\n      // var F = null;\r\n      return f;\r\n    })();\r\n  }\r\n\r\n  var arr = [ ];\r\n  for (var i=0; i&lt;10000; i++) {\r\n    arr[i] = createFn();\r\n  }</pre>\r\n</div>\r\n通过运行在Windows XP SP2中的任务管理器可以看到如下结果：\r\n<div>\r\n<pre>  IE6:\r\n\r\n    without `null`:   7.6K -&gt; 20.3K\r\n    with `null`:      7.6K -&gt; 18K\r\n\r\n  IE7:\r\n\r\n    without `null`:   14K -&gt; 29.7K\r\n    with `null`:      14K -&gt; 27K</pre>\r\n</div>\r\n如我们所料，显示断开引用可以释放内存，但是释放的内存不是很多，10000个函数对象才释放大约3M的内存，这对一些小型脚本不算什么，但对于大型程序，或者长时间运行在低内存的设备里的时候，这是非常有必要的。\r\n\r\n&nbsp;\r\n\r\n关于在Safari 2.x中JS的解析也有一些bug，但介于版本比较低，所以我们在这里就不介绍了，大家如果想看的话，请仔细查看英文资料。\r\n<h1>SpiderMonkey的怪癖</h1>\r\n大家都知道，命名函数表达式的标识符只在函数的局部作用域中有效。但包含这个标识符的局部作用域又是什么样子的吗？其实非常简单。在命名函数表达式被求值时，会<strong>创建一个特殊的对象</strong>，该对象的唯一目的就是保存一个属性，而这个属性的名字对应着函数标识符，属性的值对应着那个函数。这个对象会被注入到当前作用域链的前端。然后，被“扩展”的作用域链又被用于初始化函数。\r\n\r\n在这里，有一点十分有意思，那就是ECMA-262定义这个（保存函数标识符的）“特殊”对象的方式。标准说<strong>“像调用new Object()表达式那样”</strong>创建这个对象。如果从字面上来理解这句话，那么这个对象就应该是全局<code>Object</code>的一个实例。然而，只有一个实现是按照标准字面上的要求这么做的，这个实现就是SpiderMonkey。因此，在SpiderMonkey中，扩展<code>Object.prototype</code>有可能会干扰函数的局部作用域：\r\n<div>\r\n<pre>  Object.prototype.x = ''outer'';\r\n  \r\n  (function(){\r\n    \r\n    var x = ''inner'';\r\n    \r\n    /*\r\n      函数foo的作用域链中有一个特殊的对象——用于保存函数的标识符。这个特殊的对象实际上就是{ foo: &lt;function object&gt; }。\r\n      当通过作用域链解析x时，首先解析的是foo的局部环境。如果没有找到x，则继续搜索作用域链中的下一个对象。下一个对象\r\n      就是保存函数标识符的那个对象——{ foo: &lt;function object&gt; }，由于该对象继承自Object.prototype，所以在此可以找到x。\r\n      而这个x的值也就是Object.prototype.x的值（outer）。结果，外部函数的作用域（包含x = ''inner''的作用域）就不会被解析了。\r\n    */\r\n    \r\n    (function foo(){\r\n      \r\n      alert(x); // 提示框中显示：outer\r\n    \r\n    })();\r\n  })();</pre>\r\n</div>\r\n不过，更高版本的SpiderMonkey改变了上述行为，原因可能是认为那是一个安全漏洞。也就是说，“特殊”对象不再继承Object.prototype了。不过，如果你使用Firefox 3或者更低版本，还可以“重温”这种行为。\r\n\r\n另一个把内部对象实现为全局Object对象的是黑莓（Blackberry）浏览器。目前，它的<em>活动对象</em>（Activation Object）仍然继承Object.prototype。可是，ECMA-262并没有说<em>活动对象</em>也要“像调用new Object()表达式那样”来创建（或者说像创建保存NFE标识符的对象一样创建）。 人家规范只说了<em>活动对象</em>是规范中的一种机制。\r\n\r\n那我们就来看看黑莓里都发生了什么：\r\n<div>\r\n<pre>  Object.prototype.x = ''outer'';\r\n  \r\n  (function(){\r\n    \r\n    var x = ''inner'';\r\n    \r\n    (function(){\r\n      \r\n      /*\r\n      在沿着作用域链解析x的过程中，首先会搜索局部函数的活动对象。当然，在该对象中找不到x。\r\n      可是，由于活动对象继承自Object.prototype，因此搜索x的下一个目标就是Object.prototype；而\r\n      Object.prototype中又确实有x的定义。结果，x的值就被解析为——outer。跟前面的例子差不多，\r\n      包含x = ''inner''的外部函数的作用域（活动对象）就不会被解析了。\r\n      */\r\n      \r\n      alert(x); // 显示：outer\r\n      \r\n    })();\r\n  })();</pre>\r\n</div>\r\n不过神奇的还是，函数中的变量甚至会与已有的<code>Object.prototype</code>的成员发生冲突，来看看下面的代码：\r\n<div>\r\n<pre>  (function(){\r\n    \r\n    var constructor = function(){ return 1; };\r\n    \r\n    (function(){\r\n      \r\n      constructor(); // 求值结果是{}（即相当于调用了Object.prototype.constructor()）而不是1\r\n      \r\n      constructor === Object.prototype.constructor; // true\r\n      toString === Object.prototype.toString; // true\r\n      \r\n      // ……\r\n      \r\n    })();\r\n  })();</pre>\r\n</div>\r\n要避免这个问题，要避免使用Object.prototype里的属性名称，如toString, valueOf, hasOwnProperty等等。\r\n\r\n&nbsp;\r\n\r\nJScript解决方案\r\n<div>\r\n<pre>  var fn = (function(){\r\n\r\n    // 声明要引用函数的变量\r\n    var f;\r\n\r\n    // 有条件地创建命名函数\r\n    // 并将其引用赋值给f\r\n    if (true) {\r\n      f = function F(){ }\r\n    }\r\n    else if (false) {\r\n      f = function F(){ }\r\n    }\r\n    else {\r\n      f = function F(){ }\r\n    }\r\n\r\n    // 声明一个与函数名（标识符）对应的变量，并赋值为null\r\n    // 这实际上是给相应标识符引用的函数对象作了一个标记，\r\n    // 以便垃圾回收器知道可以回收它了\r\n    var F = null;\r\n\r\n    // 返回根据条件定义的函数\r\n    return f;\r\n  })();</pre>\r\n</div>\r\n最后我们给出一个应用上述技术的应用实例，这是一个跨浏览器的addEvent函数代码：\r\n<div>\r\n<pre>  // 1) 使用独立的作用域包含声明\r\n  var addEvent = (function(){\r\n\r\n    var docEl = document.documentElement;\r\n\r\n    // 2) 声明要引用函数的变量\r\n    var fn;\r\n\r\n    if (docEl.addEventListener) {\r\n\r\n      // 3) 有意给函数一个描述性的标识符\r\n      fn = function addEvent(element, eventName, callback) {\r\n        element.addEventListener(eventName, callback, false);\r\n      }\r\n    }\r\n    else if (docEl.attachEvent) {\r\n      fn = function addEvent(element, eventName, callback) {\r\n        element.attachEvent(''on'' + eventName, callback);\r\n      }\r\n    }\r\n    else {\r\n      fn = function addEvent(element, eventName, callback) {\r\n        element[''on'' + eventName] = callback;\r\n      }\r\n    }\r\n\r\n    // 4) 清除由JScript创建的addEvent函数\r\n    //    一定要保证在赋值前使用var关键字\r\n    //    除非函数顶部已经声明了addEvent\r\n    var addEvent = null;\r\n\r\n    // 5) 最后返回由fn引用的函数\r\n    return fn;\r\n  })();</pre>\r\n</div>\r\n<h1>替代方案</h1>\r\n其实，如果我们不想要这个描述性名字的话，我们就可以用最简单的形式来做，也就是在函数内部声明一个函数（而不是函数表达式），然后返回该函数：\r\n<div>\r\n<pre>  var hasClassName = (function(){\r\n\r\n    // 定义私有变量\r\n    var cache = { };\r\n\r\n    // 使用函数声明\r\n    function hasClassName(element, className) {\r\n      var _className = ''(?:^|\\\\\\\\s+)'' + className + ''(?:\\\\\\\\s+|\$)'';\r\n      var re = cache[_className] || (cache[_className] = new RegExp(_className));\r\n      return re.test(element.className);\r\n    }\r\n\r\n    // 返回函数\r\n    return hasClassName;\r\n  })();</pre>\r\n</div>\r\n显然，当存在多个分支函数定义时，这个方案就不行了。不过有种模式貌似可以实现：那就是提前使用函数声明来定义所有函数，并分别为这些函数指定不同的标识符：\r\n<div>\r\n<pre>  var addEvent = (function(){\r\n\r\n    var docEl = document.documentElement;\r\n\r\n    function addEventListener(){\r\n      /* ... */\r\n    }\r\n    function attachEvent(){\r\n      /* ... */\r\n    }\r\n    function addEventAsProperty(){\r\n      /* ... */\r\n    }\r\n\r\n    if (typeof docEl.addEventListener != ''undefined'') {\r\n      return addEventListener;\r\n    }\r\n    elseif (typeof docEl.attachEvent != ''undefined'') {\r\n      return attachEvent;\r\n    }\r\n    return addEventAsProperty;\r\n  })();</pre>\r\n</div>\r\n虽然这个方案很优雅，但也不是没有缺点。第一，由于使用不同的标识符，导致丧失了命名的一致性。且不说这样好还是坏，最起码它不够清晰。有人喜欢使用相同的名字，但也有人根本不在乎字眼上的差别。可毕竟，不同的名字会让人联想到所用的不同实现。例如，在调试器中看到attachEvent，我们就知 道<code>addEvent</code>是基于<code>attachEvent</code>的实现。当 然，基于实现来命名的方式也不一定都行得通。假如我们要提供一个API，并按照这种方式把函数命名为inner。那么API用户的很容易就会被相应实现的 细节搞得晕头转向。\r\n\r\n要解决这个问题，当然就得想一套更合理的命名方案了。但关键是不要再额外制造麻烦。我现在能想起来的方案大概有如下几个：\r\n<div>\r\n<pre>  ''addEvent'', ''altAddEvent'', ''fallbackAddEvent''\r\n  // 或者\r\n  ''addEvent'', ''addEvent2'', ''addEvent3''\r\n  // 或者\r\n  ''addEvent_addEventListener'', ''addEvent_attachEvent'', ''addEvent_asProperty''</pre>\r\n</div>\r\n另外，这种模式还存在一个小问题，即增加内存占用。提前创建N个不同名字的函数，等于有N-1的函数是用不到的。具体来讲，如果<code>document.documentElement</code> 中包含<code>attachEvent</code>，那么<code>addEventListener</code> 和<code>addEventAsProperty</code>则根本就用不着了。可是，他们都占着内存哪；而且，这些内存将永远都得不到释放，原因跟JScript臭哄哄的命名表达式相同——这两个函数都被“截留”在返回的那个函数的闭包中了。\r\n\r\n不过，增加内存占用这个问题确实没什么大不了的。如果某个库——例如Prototype.js——采用了这种模式，无非也就是多创建一两百个函数而已。只要不是（在运行时）重复地创建这些函数，而是只（在加载时）创建一次，那么就没有什么好担心的。\r\n<h1 id=\"webkit-displayName\">WebKit的displayName</h1>\r\nWebKit团队在这个问题采取了有点儿另类的策略。介于匿名和命名函数如此之差的表现力，WebKit引入了一个“特殊的”<code>displayName</code>属性（本质上是一个字符串），如果开发人员为函数的这个属性赋值，则该属性的值将在调试器或性能分析器中被显示在函数“名称”的位置上。<a href=\"http://www.alertdebugging.com/2009/04/29/building-a-better-javascript-profiler-with-webkit/\" target=\"_blank\">Francisco Tolmasky详细地解释了这个策略的原理和实现</a>。\r\n\r\n&nbsp;\r\n<h1>未来考虑</h1>\r\n将来的ECMAScript-262第5版（目前还是草案）会引入所谓的<strong>严格模式（strict mode）</strong>。开启严格模式的实现会禁用语言中的那些不稳定、不可靠和不安全的特性。据说出于安全方面的考虑，<code>arguments.callee</code>属性将在严格模式下被“封杀”。因此，在处于严格模式时，访问<code></code><code>arguments.callee</code>会导致<code>TypeError</code>（参见ECMA-262第5版的10.6节）。而我之所以在此提到严格模式，是因为如果在基于第5版标准的实现中无法使用<code>arguments.callee</code>来执行递归操作，那么使用命名函数表达式的可能性就会大大增加。从这个意义上来说，理解命名函数表达式的语义及其bug也就显得更加重要了。\r\n<div>\r\n<pre>  // 此前，你可能会使用arguments.callee\r\n  (function(x) {\r\n    if (x &lt;= 1) return 1;\r\n    return x * arguments.callee(x - 1);\r\n  })(10);\r\n  \r\n  // 但在严格模式下，有可能就要使用命名函数表达式\r\n  (function factorial(x) {\r\n    if (x &lt;= 1) return 1;\r\n    return x * factorial(x - 1);\r\n  })(10);\r\n  \r\n  // 要么就退一步，使用没有那么灵活的函数声明\r\n  function factorial(x) {\r\n    if (x &lt;= 1) return 1;\r\n    return x * factorial(x - 1);\r\n  }\r\n  factorial(10);</pre>\r\n</div>\r\n<h1>致谢</h1>\r\n<strong><em>理查德· 康福德（Richard Cornford）</em></strong>，是他率先<a href=\"http://groups.google.com/group/comp.lang.javascript/msg/5b508b03b004bce8\" target=\"_blank\">解释了JScript中命名函数表达式所存在的bug</a>。理查德解释了我在这篇文章中提及的大多数bug，所以我强烈建议大家去看看他的解释。我还要感谢<strong><em>Yann-Erwan Perio</em></strong>和<strong><em>道格拉斯·克劳克佛德（Douglas Crockford）</em></strong>，他们早在2003年就在<a href=\"http://groups.google.com/group/comp.lang.javascript/msg/03d53d114d176323\" target=\"_blank\">comp.lang.javascript论坛中提及并讨论NFE问题了</a>。\r\n\r\n<strong><em>约翰-戴维·道尔顿（John-David Dalton）</em></strong>对“最终解决方案”提出了很好的建议。\r\n\r\n<strong><em>托比·兰吉</em></strong>的点子被我用在了“替代方案”中。\r\n\r\n<strong><em>盖瑞特·史密斯（Garrett Smith）</em></strong>和<strong><em>德米特里·苏斯尼科（Dmitry Soshnikov）</em></strong>对本文的多方面作出了补充和修正。\r\n英文原文：http://kangax.github.com/nfe/\r\n\r\n参考译文：<a href=\"http://www.cn-cuckoo.com/main/wp-content/uploads/2009/12/named-function-expressions-demystified.html#jscript-memory-management\" target=\"_blank\">连接访问</a> (SpiderMonkey的怪癖之后的章节参考该文)\r\n<h1>同步与推荐</h1>\r\n本文已同步至目录索引：<a href=\"http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html\" target=\"_blank\">深入理解JavaScript系列</a>\r\n\r\n深入理解JavaScript系列文章，包括了原创，翻译，转载等各类型的文章，如果对你有用，请推荐支持一把，给大叔写作的动力。','深入理解JavaScript系列（2）：揭秘命名函数表达式','','publish','open','open','','%e6%b7%b1%e5%85%a5%e7%90%86%e8%a7%a3javascript%e7%b3%bb%e5%88%97%ef%bc%882%ef%bc%89%ef%bc%9a%e6%8f%ad%e7%a7%98%e5%91%bd%e5%90%8d%e5%87%bd%e6%95%b0%e8%a1%a8%e8%be%be%e5%bc%8f','','','2013-03-29 17:03:11','2013-03-29 09:03:11','','0','http://wap1.benbenweb.com/?p=22','0','post','','0');");
E_D("replace into `wp_posts` values('23','1','2013-03-29 17:00:41','2013-03-29 09:00:41','','深入理解JavaScript系列（2）：揭秘命名函数表达式','','inherit','open','open','','22-revision','','','2013-03-29 17:00:41','2013-03-29 09:00:41','','22','http://wap1.benbenweb.com/?p=23','0','revision','','0');");
E_D("replace into `wp_posts` values('24','1','2013-03-29 17:02:00','2013-03-29 09:02:00','<h1>简介</h1>\r\nModule模式是JavaScript编程中一个非常通用的模式，一般情况下，大家都知道基本用法，本文尝试着给大家更多该模式的高级使用方式。\r\n\r\n首先我们来看看Module模式的基本特征：\r\n<ol>\r\n	<li>模块化，可重用</li>\r\n	<li>封装了变量和function，和全局的namaspace不接触，松耦合</li>\r\n	<li>只暴露可用public的方法，其它私有方法全部隐藏</li>\r\n</ol>\r\n关于Module模式，最早是由YUI的成员Eric Miraglia在4年前提出了这个概念，我们将从一个简单的例子来解释一下基本的用法（如果你已经非常熟悉了，请忽略这一节）。\r\n<h1>基本用法</h1>\r\n先看一下最简单的一个实现，代码如下：\r\n<div>\r\n<pre>var Calculator = function (eq) {\r\n    //这里可以声明私有成员\r\n\r\n    var eqCtl = document.getElementById(eq);\r\n\r\n    return {\r\n        // 暴露公开的成员\r\n        add: function (x, y) {\r\n            var val = x + y;\r\n            eqCtl.innerHTML = val;\r\n        }\r\n    };\r\n};</pre>\r\n</div>\r\n我们可以通过如下的方式来调用：\r\n<div>\r\n<pre>var calculator = new Calculator(''eq'');\r\ncalculator.add(2, 2);</pre>\r\n</div>\r\n大家可能看到了，每次用的时候都要new一下，也就是说每个实例在内存里都是一份copy，如果你不需要传参数或者没有一些特殊苛刻的要求的话，我们可以在最后一个}后面加上一个括号，来达到自执行的目的，这样该实例在内存中只会存在一份copy，不过在展示他的优点之前，我们还是先来看看这个模式的基本使用方法吧。\r\n<h2><strong>匿名闭包</strong></h2>\r\n匿名闭包是让一切成为可能的基础，而这也是JavaScript最好的特性，我们来创建一个最简单的闭包函数，函数内部的代码一直存在于闭包内，在整个运行周期内，该闭包都保证了内部的代码处于私有状态。\r\n<div>\r\n<pre>(function () {\r\n    // ... 所有的变量和function都在这里声明，并且作用域也只能在这个匿名闭包里\r\n    // ...但是这里的代码依然可以访问外部全局的对象\r\n}());</pre>\r\n</div>\r\n注意，匿名函数后面的括号，这是JavaScript语言所要求的，因为如果你不声明的话，JavaScript解释器默认是声明一个function函数，有括号，就是创建一个函数表达式，也就是自执行，用的时候不用和上面那样在new了，当然你也可以这样来声明：\r\n<div>\r\n<pre>(function () {/* 内部代码 */})();</pre>\r\n</div>\r\n不过我们推荐使用第一种方式，关于函数自执行，我后面会有专门一篇文章进行详解，这里就不多说了。\r\n<h2><strong>引用全局变量</strong></h2>\r\nJavaScript有一个特性叫做隐式全局变量，不管一个变量有没有用过，JavaScript解释器反向遍历作用域链来查找整个变量的var声明，如果没有找到var，解释器则假定该变量是全局变量，如果该变量用于了赋值操作的话，之前如果不存在的话，解释器则会自动创建它，这就是说在匿名闭包里使用或创建全局变量非常容易，不过比较困难的是，代码比较难管理，尤其是阅读代码的人看着很多区分哪些变量是全局的，哪些是局部的。\r\n\r\n不过，好在在匿名函数里我们可以提供一个比较简单的替代方案，我们可以将全局变量当成一个参数传入到匿名函数然后使用，相比隐式全局变量，它又清晰又快，我们来看一个例子：\r\n<div>\r\n<pre>(function (\$, YAHOO) {\r\n    // 这里，我们的代码就可以使用全局的jQuery对象了，YAHOO也是一样\r\n} (jQuery, YAHOO));</pre>\r\n</div>\r\n现在很多类库里都有这种使用方式，比如jQuery源码。\r\n\r\n不过，有时候可能不仅仅要使用全局变量，而是也想声明全局变量，如何做呢？我们可以通过匿名函数的返回值来返回这个全局变量，这也就是一个基本的Module模式，来看一个完整的代码：\r\n<div>\r\n<pre>var blogModule = (function () {\r\n    var my = {}, privateName = \"博客园\";\r\n\r\n    function privateAddTopic(data) {\r\n        // 这里是内部处理代码\r\n    }\r\n\r\n    my.Name = privateName;\r\n    my.AddTopic = function (data) {\r\n        privateAddTopic(data);\r\n    };\r\n\r\n    return my;\r\n} ());</pre>\r\n</div>\r\n上面的代码声明了一个全局变量blogModule，并且带有2个可访问的属性：blogModule.AddTopic和blogModule.Name，除此之外，其它代码都在匿名函数的闭包里保持着私有状态。同时根据上面传入全局变量的例子，我们也可以很方便地传入其它的全局变量。\r\n<h1>高级用法</h1>\r\n上面的内容对大多数用户已经很足够了，但我们还可以基于此模式延伸出更强大，易于扩展的结构，让我们一个一个来看。\r\n<h2><strong>扩展</strong></h2>\r\nModule模式的一个限制就是所有的代码都要写在一个文件，但是在一些大型项目里，将一个功能分离成多个文件是非常重要的，因为可以多人合作易于开发。再回头看看上面的全局参数导入例子，我们能否把blogModule自身传进去呢？答案是肯定的，我们先将blogModule传进去，添加一个函数属性，然后再返回就达到了我们所说的目的，上代码：\r\n<div>\r\n<pre>var blogModule = (function (my) {\r\n    my.AddPhoto = function () {\r\n        //添加内部代码  \r\n    };\r\n    return my;\r\n} (blogModule));</pre>\r\n</div>\r\n这段代码，看起来是不是有C#里扩展方法的感觉？有点类似，但本质不一样哦。同时尽管var不是必须的，但为了确保一致，我们再次使用了它，代码执行以后，blogModule下的AddPhoto就可以使用了，同时匿名函数内部的代码也依然保证了私密性和内部状态。\r\n<h2><strong>松耦合扩展</strong></h2>\r\n上面的代码尽管可以执行，但是必须先声明blogModule，然后再执行上面的扩展代码，也就是说步骤不能乱，怎么解决这个问题呢？我们来回想一下，我们平时声明变量的都是都是这样的：\r\n<div>\r\n<pre>var cnblogs = cnblogs || {} ;</pre>\r\n</div>\r\n这是确保cnblogs对象，在存在的时候直接用，不存在的时候直接赋值为{}，我们来看看如何利用这个特性来实现Module模式的任意加载顺序：\r\n<div>\r\n<pre>var blogModule = (function (my) {\r\n\r\n    // 添加一些功能   \r\n    \r\n    return my;\r\n} (blogModule || {}));</pre>\r\n</div>\r\n通过这样的代码，每个单独分离的文件都保证这个结构，那么我们就可以实现任意顺序的加载，所以，这个时候的var就是必须要声明的，因为不声明，其它文件读取不到哦。\r\n<h2><strong>紧耦合扩展</strong></h2>\r\n虽然松耦合扩展很牛叉了，但是可能也会存在一些限制，比如你没办法重写你的一些属性或者函数，也不能在初始化的时候就是用Module的属性。紧耦合扩展限制了加载顺序，但是提供了我们重载的机会，看如下例子：\r\n<div>\r\n<pre>var blogModule = (function (my) {\r\n    var oldAddPhotoMethod = my.AddPhoto;\r\n\r\n    my.AddPhoto = function () {\r\n        // 重载方法，依然可通过oldAddPhotoMethod调用旧的方法\r\n    };\r\n\r\n    return my;\r\n} (blogModule));</pre>\r\n</div>\r\n通过这种方式，我们达到了重载的目的，当然如果你想在继续在内部使用原有的属性，你可以调用oldAddPhotoMethod来用。\r\n<h2><strong>克隆与继承</strong></h2>\r\n<div>\r\n<pre>var blogModule = (function (old) {\r\n    var my = {},\r\n        key;\r\n\r\n    for (key in old) {\r\n        if (old.hasOwnProperty(key)) {\r\n            my[key] = old[key];\r\n        }\r\n    }\r\n\r\n    var oldAddPhotoMethod = old.AddPhoto;\r\n    my.AddPhoto = function () {\r\n        // 克隆以后，进行了重写，当然也可以继续调用oldAddPhotoMethod\r\n    };\r\n\r\n    return my;\r\n} (blogModule));</pre>\r\n</div>\r\n这种方式灵活是灵活，但是也需要花费灵活的代价，其实该对象的属性对象或function根本没有被复制，只是对同一个对象多了一种引用而已，所以如果老对象去改变它，那克隆以后的对象所拥有的属性或function函数也会被改变，解决这个问题，我们就得是用递归，但递归对function函数的赋值也不好用，所以我们在递归的时候eval相应的function。不管怎么样，我还是把这一个方式放在这个帖子里了，大家使用的时候注意一下就行了。\r\n<h2><strong>跨文件共享私有对象</strong></h2>\r\n通过上面的例子，我们知道，如果一个module分割到多个文件的话，每个文件需要保证一样的结构，也就是说每个文件匿名函数里的私有对象都不能交叉访问，那如果我们非要使用，那怎么办呢？ 我们先看一段代码：\r\n<div>\r\n<pre>var blogModule = (function (my) {\r\n    var _private = my._private = my._private || {},\r\n        \r\n        _seal = my._seal = my._seal || function () {\r\n            delete my._private;\r\n            delete my._seal;\r\n            delete my._unseal;\r\n            \r\n        },\r\n        \r\n        _unseal = my._unseal = my._unseal || function () {\r\n            my._private = _private;\r\n            my._seal = _seal;\r\n            my._unseal = _unseal;\r\n        };\r\n        \r\n    return my;\r\n} (blogModule || {}));</pre>\r\n</div>\r\n任何文件都可以对他们的局部变量_private设属性，并且设置对其他的文件也立即生效。一旦这个模块加载结束，应用会调用 blogModule._seal()\"上锁\"，这会阻止外部接入内部的_private。如果这个模块需要再次增生，应用的生命周期内，任何文件都可以调用_unseal() ”开锁”，然后再加载新文件。加载后再次调用 _seal()”上锁”。\r\n<h2><strong>子模块</strong></h2>\r\n最后一个也是最简单的使用方式，那就是创建子模块\r\n<div>\r\n<pre>blogModule.CommentSubModule = (function () {\r\n    var my = {};\r\n    // ...\r\n\r\n    return my;\r\n} ());</pre>\r\n</div>\r\n尽管非常简单，我还是把它放进来了，因为我想说明的是子模块也具有一般模块所有的高级使用方式，也就是说你可以对任意子模块再次使用上面的一些应用方法。\r\n<h1>总结</h1>\r\n上面的大部分方式都可以互相组合使用的，一般来说如果要设计系统，可能会用到松耦合扩展，私有状态和子模块这样的方式。另外，我这里没有提到性能问题，但我认为Module模式效率高，代码少，加载速度快。使用松耦合扩展允许并行加载，这更可以提升下载速度。不过初始化时间可能要慢一些，但是为了使用好的模式，这是值得的。\r\n\r\n参考文章：\r\n\r\nhttp://yuiblog.com/blog/2007/06/12/module-pattern/\r\nhttp://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth\r\n<h1>同步与推荐</h1>\r\n本文已同步至目录索引：<a href=\"http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html\" target=\"_blank\">深入理解JavaScript系列</a>\r\n\r\n深入理解JavaScript系列文章，包括了原创，翻译，转载等各类型的文章，如果对你有用，请推荐支持一把，给大叔写作的动力。','深入理解JavaScript系列（3）：全面解析Module模式','','publish','open','open','','%e6%b7%b1%e5%85%a5%e7%90%86%e8%a7%a3javascript%e7%b3%bb%e5%88%97%ef%bc%883%ef%bc%89%ef%bc%9a%e5%85%a8%e9%9d%a2%e8%a7%a3%e6%9e%90module%e6%a8%a1%e5%bc%8f','','','2013-03-29 17:03:03','2013-03-29 09:03:03','','0','http://wap1.benbenweb.com/?p=24','0','post','','0');");
E_D("replace into `wp_posts` values('25','1','2013-03-29 17:01:33','2013-03-29 09:01:33','','深入理解JavaScript系列（3）：全面解析Module模式','','inherit','open','open','','24-revision','','','2013-03-29 17:01:33','2013-03-29 09:01:33','','24','http://wap1.benbenweb.com/?p=25','0','revision','','0');");
E_D("replace into `wp_posts` values('26','1','2013-03-29 17:02:00','2013-03-29 09:02:00','<h1>简介</h1>\r\nModule模式是JavaScript编程中一个非常通用的模式，一般情况下，大家都知道基本用法，本文尝试着给大家更多该模式的高级使用方式。\r\n\r\n首先我们来看看Module模式的基本特征：\r\n<ol>\r\n	<li>模块化，可重用</li>\r\n	<li>封装了变量和function，和全局的namaspace不接触，松耦合</li>\r\n	<li>只暴露可用public的方法，其它私有方法全部隐藏</li>\r\n</ol>\r\n关于Module模式，最早是由YUI的成员Eric Miraglia在4年前提出了这个概念，我们将从一个简单的例子来解释一下基本的用法（如果你已经非常熟悉了，请忽略这一节）。\r\n<h1>基本用法</h1>\r\n先看一下最简单的一个实现，代码如下：\r\n<div>\r\n<pre>var Calculator = function (eq) {\r\n    //这里可以声明私有成员\r\n\r\n    var eqCtl = document.getElementById(eq);\r\n\r\n    return {\r\n        // 暴露公开的成员\r\n        add: function (x, y) {\r\n            var val = x + y;\r\n            eqCtl.innerHTML = val;\r\n        }\r\n    };\r\n};</pre>\r\n</div>\r\n我们可以通过如下的方式来调用：\r\n<div>\r\n<pre>var calculator = new Calculator(''eq'');\r\ncalculator.add(2, 2);</pre>\r\n</div>\r\n大家可能看到了，每次用的时候都要new一下，也就是说每个实例在内存里都是一份copy，如果你不需要传参数或者没有一些特殊苛刻的要求的话，我们可以在最后一个}后面加上一个括号，来达到自执行的目的，这样该实例在内存中只会存在一份copy，不过在展示他的优点之前，我们还是先来看看这个模式的基本使用方法吧。\r\n<h2><strong>匿名闭包</strong></h2>\r\n匿名闭包是让一切成为可能的基础，而这也是JavaScript最好的特性，我们来创建一个最简单的闭包函数，函数内部的代码一直存在于闭包内，在整个运行周期内，该闭包都保证了内部的代码处于私有状态。\r\n<div>\r\n<pre>(function () {\r\n    // ... 所有的变量和function都在这里声明，并且作用域也只能在这个匿名闭包里\r\n    // ...但是这里的代码依然可以访问外部全局的对象\r\n}());</pre>\r\n</div>\r\n注意，匿名函数后面的括号，这是JavaScript语言所要求的，因为如果你不声明的话，JavaScript解释器默认是声明一个function函数，有括号，就是创建一个函数表达式，也就是自执行，用的时候不用和上面那样在new了，当然你也可以这样来声明：\r\n<div>\r\n<pre>(function () {/* 内部代码 */})();</pre>\r\n</div>\r\n不过我们推荐使用第一种方式，关于函数自执行，我后面会有专门一篇文章进行详解，这里就不多说了。\r\n<h2><strong>引用全局变量</strong></h2>\r\nJavaScript有一个特性叫做隐式全局变量，不管一个变量有没有用过，JavaScript解释器反向遍历作用域链来查找整个变量的var声明，如果没有找到var，解释器则假定该变量是全局变量，如果该变量用于了赋值操作的话，之前如果不存在的话，解释器则会自动创建它，这就是说在匿名闭包里使用或创建全局变量非常容易，不过比较困难的是，代码比较难管理，尤其是阅读代码的人看着很多区分哪些变量是全局的，哪些是局部的。\r\n\r\n不过，好在在匿名函数里我们可以提供一个比较简单的替代方案，我们可以将全局变量当成一个参数传入到匿名函数然后使用，相比隐式全局变量，它又清晰又快，我们来看一个例子：\r\n<div>\r\n<pre>(function (\$, YAHOO) {\r\n    // 这里，我们的代码就可以使用全局的jQuery对象了，YAHOO也是一样\r\n} (jQuery, YAHOO));</pre>\r\n</div>\r\n现在很多类库里都有这种使用方式，比如jQuery源码。\r\n\r\n不过，有时候可能不仅仅要使用全局变量，而是也想声明全局变量，如何做呢？我们可以通过匿名函数的返回值来返回这个全局变量，这也就是一个基本的Module模式，来看一个完整的代码：\r\n<div>\r\n<pre>var blogModule = (function () {\r\n    var my = {}, privateName = \"博客园\";\r\n\r\n    function privateAddTopic(data) {\r\n        // 这里是内部处理代码\r\n    }\r\n\r\n    my.Name = privateName;\r\n    my.AddTopic = function (data) {\r\n        privateAddTopic(data);\r\n    };\r\n\r\n    return my;\r\n} ());</pre>\r\n</div>\r\n上面的代码声明了一个全局变量blogModule，并且带有2个可访问的属性：blogModule.AddTopic和blogModule.Name，除此之外，其它代码都在匿名函数的闭包里保持着私有状态。同时根据上面传入全局变量的例子，我们也可以很方便地传入其它的全局变量。\r\n<h1>高级用法</h1>\r\n上面的内容对大多数用户已经很足够了，但我们还可以基于此模式延伸出更强大，易于扩展的结构，让我们一个一个来看。\r\n<h2><strong>扩展</strong></h2>\r\nModule模式的一个限制就是所有的代码都要写在一个文件，但是在一些大型项目里，将一个功能分离成多个文件是非常重要的，因为可以多人合作易于开发。再回头看看上面的全局参数导入例子，我们能否把blogModule自身传进去呢？答案是肯定的，我们先将blogModule传进去，添加一个函数属性，然后再返回就达到了我们所说的目的，上代码：\r\n<div>\r\n<pre>var blogModule = (function (my) {\r\n    my.AddPhoto = function () {\r\n        //添加内部代码  \r\n    };\r\n    return my;\r\n} (blogModule));</pre>\r\n</div>\r\n这段代码，看起来是不是有C#里扩展方法的感觉？有点类似，但本质不一样哦。同时尽管var不是必须的，但为了确保一致，我们再次使用了它，代码执行以后，blogModule下的AddPhoto就可以使用了，同时匿名函数内部的代码也依然保证了私密性和内部状态。\r\n<h2><strong>松耦合扩展</strong></h2>\r\n上面的代码尽管可以执行，但是必须先声明blogModule，然后再执行上面的扩展代码，也就是说步骤不能乱，怎么解决这个问题呢？我们来回想一下，我们平时声明变量的都是都是这样的：\r\n<div>\r\n<pre>var cnblogs = cnblogs || {} ;</pre>\r\n</div>\r\n这是确保cnblogs对象，在存在的时候直接用，不存在的时候直接赋值为{}，我们来看看如何利用这个特性来实现Module模式的任意加载顺序：\r\n<div>\r\n<pre>var blogModule = (function (my) {\r\n\r\n    // 添加一些功能   \r\n    \r\n    return my;\r\n} (blogModule || {}));</pre>\r\n</div>\r\n通过这样的代码，每个单独分离的文件都保证这个结构，那么我们就可以实现任意顺序的加载，所以，这个时候的var就是必须要声明的，因为不声明，其它文件读取不到哦。\r\n<h2><strong>紧耦合扩展</strong></h2>\r\n虽然松耦合扩展很牛叉了，但是可能也会存在一些限制，比如你没办法重写你的一些属性或者函数，也不能在初始化的时候就是用Module的属性。紧耦合扩展限制了加载顺序，但是提供了我们重载的机会，看如下例子：\r\n<div>\r\n<pre>var blogModule = (function (my) {\r\n    var oldAddPhotoMethod = my.AddPhoto;\r\n\r\n    my.AddPhoto = function () {\r\n        // 重载方法，依然可通过oldAddPhotoMethod调用旧的方法\r\n    };\r\n\r\n    return my;\r\n} (blogModule));</pre>\r\n</div>\r\n通过这种方式，我们达到了重载的目的，当然如果你想在继续在内部使用原有的属性，你可以调用oldAddPhotoMethod来用。\r\n<h2><strong>克隆与继承</strong></h2>\r\n<div>\r\n<pre>var blogModule = (function (old) {\r\n    var my = {},\r\n        key;\r\n\r\n    for (key in old) {\r\n        if (old.hasOwnProperty(key)) {\r\n            my[key] = old[key];\r\n        }\r\n    }\r\n\r\n    var oldAddPhotoMethod = old.AddPhoto;\r\n    my.AddPhoto = function () {\r\n        // 克隆以后，进行了重写，当然也可以继续调用oldAddPhotoMethod\r\n    };\r\n\r\n    return my;\r\n} (blogModule));</pre>\r\n</div>\r\n这种方式灵活是灵活，但是也需要花费灵活的代价，其实该对象的属性对象或function根本没有被复制，只是对同一个对象多了一种引用而已，所以如果老对象去改变它，那克隆以后的对象所拥有的属性或function函数也会被改变，解决这个问题，我们就得是用递归，但递归对function函数的赋值也不好用，所以我们在递归的时候eval相应的function。不管怎么样，我还是把这一个方式放在这个帖子里了，大家使用的时候注意一下就行了。\r\n<h2><strong>跨文件共享私有对象</strong></h2>\r\n通过上面的例子，我们知道，如果一个module分割到多个文件的话，每个文件需要保证一样的结构，也就是说每个文件匿名函数里的私有对象都不能交叉访问，那如果我们非要使用，那怎么办呢？ 我们先看一段代码：\r\n<div>\r\n<pre>var blogModule = (function (my) {\r\n    var _private = my._private = my._private || {},\r\n        \r\n        _seal = my._seal = my._seal || function () {\r\n            delete my._private;\r\n            delete my._seal;\r\n            delete my._unseal;\r\n            \r\n        },\r\n        \r\n        _unseal = my._unseal = my._unseal || function () {\r\n            my._private = _private;\r\n            my._seal = _seal;\r\n            my._unseal = _unseal;\r\n        };\r\n        \r\n    return my;\r\n} (blogModule || {}));</pre>\r\n</div>\r\n任何文件都可以对他们的局部变量_private设属性，并且设置对其他的文件也立即生效。一旦这个模块加载结束，应用会调用 blogModule._seal()\"上锁\"，这会阻止外部接入内部的_private。如果这个模块需要再次增生，应用的生命周期内，任何文件都可以调用_unseal() ”开锁”，然后再加载新文件。加载后再次调用 _seal()”上锁”。\r\n<h2><strong>子模块</strong></h2>\r\n最后一个也是最简单的使用方式，那就是创建子模块\r\n<div>\r\n<pre>blogModule.CommentSubModule = (function () {\r\n    var my = {};\r\n    // ...\r\n\r\n    return my;\r\n} ());</pre>\r\n</div>\r\n尽管非常简单，我还是把它放进来了，因为我想说明的是子模块也具有一般模块所有的高级使用方式，也就是说你可以对任意子模块再次使用上面的一些应用方法。\r\n<h1>总结</h1>\r\n上面的大部分方式都可以互相组合使用的，一般来说如果要设计系统，可能会用到松耦合扩展，私有状态和子模块这样的方式。另外，我这里没有提到性能问题，但我认为Module模式效率高，代码少，加载速度快。使用松耦合扩展允许并行加载，这更可以提升下载速度。不过初始化时间可能要慢一些，但是为了使用好的模式，这是值得的。\r\n\r\n参考文章：\r\n\r\nhttp://yuiblog.com/blog/2007/06/12/module-pattern/\r\nhttp://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth\r\n<h1>同步与推荐</h1>\r\n本文已同步至目录索引：<a href=\"http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html\" target=\"_blank\">深入理解JavaScript系列</a>\r\n\r\n深入理解JavaScript系列文章，包括了原创，翻译，转载等各类型的文章，如果对你有用，请推荐支持一把，给大叔写作的动力。','深入理解JavaScript系列（3）：全面解析Module模式','','inherit','open','open','','24-revision-2','','','2013-03-29 17:02:00','2013-03-29 09:02:00','','24','http://wap1.benbenweb.com/?p=26','0','revision','','0');");
E_D("replace into `wp_posts` values('27','1','2013-03-29 17:01:08','2013-03-29 09:01:08','<h1>前言</h1>\r\n网上还没用发现有人对命名函数表达式进去重复深入的讨论，正因为如此，网上出现了各种各样的误解，本文将从原理和实践两个方面来探讨JavaScript关于命名函数表达式的优缺点。\r\n\r\n简单的说，命名函数表达式只有一个用户，那就是在Debug或者Profiler分析的时候来描述函数的名称，也可以使用函数名实现递归，但很快你就会发现其实是不切实际的。当然，如果你不关注调试，那就没什么可担心的了，否则，如果你想了解兼容性方面的东西的话，你还是应该继续往下看看。\r\n\r\n我们先开始看看，什么叫函数表达式，然后再说一下现代调试器如何处理这些表达式，如果你已经对这方面很熟悉的话，请直接跳过此小节。\r\n<h1>函数表达式和函数声明</h1>\r\n在ECMAScript中，创建函数的最常用的两个方法是函数表达式和函数声明，两者期间的区别是有点晕，因为ECMA规范只明确了一点：函数声明必须带有标示符（Identifier）（就是大家常说的函数名称），而函数表达式则可以省略这个标示符：\r\n\r\n函数声明:\r\n\r\nfunction 函数名称 <strong>(</strong>参数：可选<strong>){</strong> 函数体<strong> }</strong>\r\n\r\n函数表达式：\r\n\r\nfunction 函数名称（可选）<strong>(</strong>参数：可选<strong>){</strong> 函数体 <strong>}</strong>\r\n\r\n所以，可以看出，如果不声明函数名称，它肯定是表达式，可如果声明了函数名称的话，如何判断是函数声明还是函数表达式呢？ECMAScript是通过上下文来区分的，如果function foo(){}是作为赋值表达式的一部分的话，那它就是一个函数表达式，如果function foo(){}被包含在一个函数体内，或者位于程序的最顶部的话，那它就是一个函数声明。\r\n<div>\r\n<pre>  function foo(){} // 声明，因为它是程序的一部分\r\n  var bar = function foo(){}; // 表达式，因为它是赋值表达式的一部分\r\n\r\n  new function bar(){}; // 表达式，因为它是new表达式\r\n\r\n  (function(){\r\n    function bar(){} // 声明，因为它是函数体的一部分\r\n  })();</pre>\r\n</div>\r\n还有一种函数表达式不太常见，就是被括号括住的(function foo(){})，他是表达式的原因是因为括号 ()是一个分组操作符，它的内部只能包含表达式，我们来看几个例子：\r\n<div>\r\n<pre>  function foo(){} // 函数声明\r\n  (function foo(){}); // 函数表达式：包含在分组操作符内\r\n  \r\n  try {\r\n    (var x = 5); // 分组操作符，只能包含表达式而不能包含语句：这里的var就是语句\r\n  } catch(err) {\r\n    // SyntaxError\r\n  }</pre>\r\n</div>\r\n你可以会想到，在使用eval对JSON进行执行的时候，JSON字符串通常被包含在一个圆括号里：eval(''('' + json + '')'')，这样做的原因就是因为分组操作符，也就是这对括号，会让解析器强制将JSON的花括号解析成表达式而不是代码块。\r\n<div>\r\n<pre>  try {\r\n    { \"x\": 5 }; // \"{\" 和 \"}\" 做解析成代码块\r\n  } catch(err) {\r\n    // SyntaxError\r\n  }\r\n  \r\n  ({ \"x\": 5 }); // 分组操作符强制将\"{\" 和 \"}\"作为对象字面量来解析</pre>\r\n</div>\r\n表达式和声明存在着十分微妙的差别，首先，函数声明会在任何表达式被解析和求值之前先被解析和求值，即使你的声明在代码的最后一行，它也会在同作用域内第一个表达式之前被解析/求值，参考如下例子，函数fn是在alert之后声明的，但是在alert执行的时候，fn已经有定义了：\r\n<div>\r\n<pre>  alert(fn());\r\n\r\n  function fn() {\r\n    return ''Hello world!'';\r\n  }</pre>\r\n</div>\r\n另外，还有一点需要提醒一下，函数声明在条件语句内虽然可以用，但是没有被标准化，也就是说不同的环境可能有不同的执行结果，所以这样情况下，最好使用函数表达式：\r\n<div>\r\n<pre>  // 千万别这样做！\r\n  // 因为有的浏览器会返回first的这个function，而有的浏览器返回的却是第二个\r\n\r\n  if (true) {\r\n    function foo() {\r\n      return ''first'';\r\n    }\r\n  }\r\n  else {\r\n    function foo() {\r\n      return ''second'';\r\n    }\r\n  }\r\n  foo();\r\n\r\n  // 相反，这样情况，我们要用函数表达式\r\n  var foo;\r\n  if (true) {\r\n    foo = function() {\r\n      return ''first'';\r\n    };\r\n  }\r\n  else {\r\n    foo = function() {\r\n      return ''second'';\r\n    };\r\n  }\r\n  foo();</pre>\r\n</div>\r\n函数声明的实际规则如下：\r\n\r\n<em>函数声明</em>只能出现在<em>程序</em>或<em>函数体</em>内。从句法上讲，它们 不能出现在Block（块）（{ ... }）中，例如不能出现在 if、while 或 for 语句中。因为 Block（块） 中只能包含Statement语句， 而不能包含<em>函数声明</em>这样的源元素。另一方面，仔细看一看规则也会发现，唯一可能让<em>表达式</em>出现在Block（块）中情形，就是让它作为<em>表达式语句</em>的一部分。但是，规范明确规定了<em>表达式语句</em>不能以关键字function开头。而这实际上就是说，<em>函数表达式</em>同样也不能出现在Statement语句或Block（块）中（因为Block（块）就是由Statement语句构成的）。\r\n<h1>函数语句</h1>\r\n在ECMAScript的语法扩展中，有一个是函数语句，目前只有基于Gecko的浏览器实现了该扩展，所以对于下面的例子，我们仅是抱着学习的目的来看，一般来说不推荐使用（除非你针对Gecko浏览器进行开发）。\r\n\r\n1.一般语句能用的地方，函数语句也能用，当然也包括Block块中：\r\n<div>\r\n<pre>  if (true) {\r\n    function f(){ }\r\n  }\r\n  else {\r\n    function f(){ }\r\n  }</pre>\r\n</div>\r\n2.函数语句可以像其他语句一样被解析，包含基于条件执行的情形\r\n<div>\r\n<pre>  if (true) {\r\n    function foo(){ return 1; }\r\n  }\r\n  else {\r\n    function foo(){ return 2; }\r\n  }\r\n  foo(); // 1\r\n  // 注：其它客户端会将foo解析成函数声明 \r\n  // 因此，第二个foo会覆盖第一个，结果返回2，而不是1</pre>\r\n</div>\r\n3.函数语句不是在变量初始化期间声明的，而是在运行时声明的——与函数表达式一样。不过，函数语句的标识符一旦声明能在函数的整个作用域生效了。标识符有效性正是导致函数语句与函数表达式不同的关键所在（下一小节我们将会展示命名函数表达式的具体行为）。\r\n<div>\r\n<pre>  // 此刻，foo还没用声明\r\n  typeof foo; // \"undefined\"\r\n  if (true) {\r\n    // 进入这里以后，foo就被声明在整个作用域内了\r\n    function foo(){ return 1; }\r\n  }\r\n  else {\r\n    // 从来不会走到这里，所以这里的foo也不会被声明\r\n    function foo(){ return 2; }\r\n  }\r\n  typeof foo; // \"function\"\r\n</pre>\r\n</div>\r\n不过，我们可以使用下面这样的符合标准的代码来模式上面例子中的函数语句：\r\n<div>\r\n<pre>  var foo;\r\n  if (true) {\r\n    foo = function foo(){ return 1; };\r\n  }\r\n  else {\r\n    foo = function foo() { return 2; };\r\n  }</pre>\r\n</div>\r\n4.函数语句和函数声明（或命名函数表达式）的字符串表示类似，也包括标识符：\r\n<div>\r\n<pre>  if (true) {\r\n    function foo(){ return 1; }\r\n  }\r\n  String(foo); // function foo() { return 1; }</pre>\r\n</div>\r\n5.另外一个，早期基于Gecko的实现（Firefox 3及以前版本）中存在一个bug，即函数语句覆盖函数声明的方式不正确。在这些早期的实现中，函数语句不知何故不能覆盖函数声明：\r\n<div>\r\n<pre>  // 函数声明\r\n  function foo(){ return 1; }\r\n  if (true) {\r\n    // 用函数语句重写\r\n    function foo(){ return 2; }\r\n  }\r\n  foo(); // FF3以下返回1，FF3.5以上返回2\r\n  \r\n  // 不过，如果前面是函数表达式，则没用问题\r\n  var foo = function(){ return 1; };\r\n  if (true) {\r\n    function foo(){ return 2; }\r\n  }\r\n  foo(); // 所有版本都返回2</pre>\r\n</div>\r\n再次强调一点，上面这些例子只是在某些浏览器支持，所以推荐大家不要使用这些，除非你就在特性的浏览器上做开发。\r\n<h1>命名函数表达式</h1>\r\n函数表达式在实际应用中还是很常见的，在web开发中友个常用的模式是基于对某种特性的测试来伪装函数定义，从而达到性能优化的目的，但由于这种方式都是在同一作用域内，所以基本上一定要用函数表达式：\r\n<div>\r\n<pre>  // 该代码来自Garrett Smith的APE Javascript library库(http://dhtmlkitchen.com/ape/) \r\n  var contains = (function() {\r\n    var docEl = document.documentElement;\r\n\r\n    if (typeof docEl.compareDocumentPosition != ''undefined'') {\r\n      return function(el, b) {\r\n        return (el.compareDocumentPosition(b) &amp; 16) !== 0;\r\n      };\r\n    }\r\n    else if (typeof docEl.contains != ''undefined'') {\r\n      return function(el, b) {\r\n        return el !== b &amp;&amp; el.contains(b);\r\n      };\r\n    }\r\n    return function(el, b) {\r\n      if (el === b) return false;\r\n      while (el != b &amp;&amp; (b = b.parentNode) != null);\r\n      return el === b;\r\n    };\r\n  })();</pre>\r\n</div>\r\n提到命名函数表达式，理所当然，就是它得有名字，前面的例子var bar = function foo(){};就是一个有效的命名函数表达式，但有一点需要记住：这个名字只在新定义的函数作用域内有效，因为规范规定了标示符不能在外围的作用域内有效：\r\n<div>\r\n<pre>  var f = function foo(){\r\n    return typeof foo; // foo是在内部作用域内有效\r\n  };\r\n  // foo在外部用于是不可见的\r\n  typeof foo; // \"undefined\"\r\n  f(); // \"function\"</pre>\r\n</div>\r\n既然，这么要求，那命名函数表达式到底有啥用啊？为啥要取名？\r\n\r\n正如我们开头所说：给它一个名字就是可以让调试过程更方便，因为在调试的时候，如果在调用栈中的每个项都有自己的名字来描述，那么调试过程就太爽了，感受不一样嘛。\r\n<h1>调试器中的函数名</h1>\r\n如果一个函数有名字，那调试器在调试的时候会将它的名字显示在调用的栈上。有些调试器（Firebug）有时候还会为你们函数取名并显示，让他们和那些应用该函数的便利具有相同的角色，可是通常情况下，这些调试器只安装简单的规则来取名，所以说没有太大价格，我们来看一个例子：\r\n<div>\r\n<pre>  function foo(){\r\n    return bar();\r\n  }\r\n  function bar(){\r\n    return baz();\r\n  }\r\n  function baz(){\r\n    debugger;\r\n  }\r\n  foo();\r\n\r\n  // 这里我们使用了3个带名字的函数声明\r\n  // 所以当调试器走到debugger语句的时候，Firebug的调用栈上看起来非常清晰明了 \r\n  // 因为很明白地显示了名称\r\n  baz\r\n  bar\r\n  foo\r\n  expr_test.html()</pre>\r\n</div>\r\n通过查看调用栈的信息，我们可以很明了地知道foo调用了bar, bar又调用了baz（而foo本身有在expr_test.html文档的全局作用域内被调用），不过，还有一个比较爽地方，就是刚才说的Firebug为匿名表达式取名的功能：\r\n<div>\r\n<pre>  function foo(){\r\n    return bar();\r\n  }\r\n  var bar = function(){\r\n    return baz();\r\n  }\r\n  function baz(){\r\n    debugger;\r\n  }\r\n  foo();\r\n\r\n  // Call stack\r\n  baz\r\n  bar() //看到了么？ \r\n  foo\r\n  expr_test.html()</pre>\r\n</div>\r\n然后，当函数表达式稍微复杂一些的时候，调试器就不那么聪明了，我们只能在调用栈中看到问号：\r\n<div>\r\n<pre>  function foo(){\r\n    return bar();\r\n  }\r\n  var bar = (function(){\r\n    if (window.addEventListener) {\r\n      return function(){\r\n        return baz();\r\n      };\r\n    }\r\n    else if (window.attachEvent) {\r\n      return function() {\r\n        return baz();\r\n      };\r\n    }\r\n  })();\r\n  function baz(){\r\n    debugger;\r\n  }\r\n  foo();\r\n\r\n  // Call stack\r\n  baz\r\n  (?)() // 这里可是问号哦\r\n  foo\r\n  expr_test.html()</pre>\r\n</div>\r\n另外，当把函数赋值给多个变量的时候，也会出现令人郁闷的问题：\r\n<div>\r\n<pre>  function foo(){\r\n    return baz();\r\n  }\r\n  var bar = function(){\r\n    debugger;\r\n  };\r\n  var baz = bar;\r\n  bar = function() { \r\n    alert(''spoofed'');\r\n  };\r\n  foo();\r\n\r\n  // Call stack:\r\n  bar()\r\n  foo\r\n  expr_test.html()</pre>\r\n</div>\r\n这时候，调用栈显示的是foo调用了bar，但实际上并非如此，之所以有这种问题，是因为baz和另外一个包含alert(''spoofed'')的函数做了引用交换所导致的。\r\n\r\n归根结底，只有给函数表达式取个名字，才是最委托的办法，也就是使用<strong>命名函数表达式</strong>。我们来使用带名字的表达式来重写上面的例子（注意立即调用的表达式块里返回的2个函数的名字都是bar）：\r\n<div>\r\n<pre>  function foo(){\r\n    return bar();\r\n  }\r\n  var bar = (function(){\r\n    if (window.addEventListener) {\r\n      return function bar(){\r\n        return baz();\r\n      };\r\n    }\r\n    else if (window.attachEvent) {\r\n      return function bar() {\r\n        return baz();\r\n      };\r\n    }\r\n  })();\r\n  function baz(){\r\n    debugger;\r\n  }\r\n  foo();\r\n\r\n  // 又再次看到了清晰的调用栈信息了耶!\r\n  baz\r\n  bar\r\n  foo\r\n  expr_test.html()</pre>\r\n</div>\r\nOK，又学了一招吧？不过在高兴之前，我们再看看不同寻常的JScript吧。\r\n<h1>JScript的Bug</h1>\r\n比较恶的是，IE的ECMAScript实现JScript严重混淆了命名函数表达式，搞得现很多人都出来反对命名函数表达式，而且即便是最新的一版（IE8中使用的5.8版）仍然存在下列问题。\r\n\r\n下面我们就来看看IE在实现中究竟犯了那些错误，俗话说知已知彼，才能百战不殆。我们来看看如下几个例子：\r\n\r\n<strong>例1：函数表达式的标示符泄露到外部作用域</strong>\r\n<div>\r\n<pre>    var f = function g(){};\r\n    typeof g; // \"function\"</pre>\r\n</div>\r\n上面我们说过，命名函数表达式的标示符在外部作用域是无效的，但JScript明显是违反了这一规范，上面例子中的标示符g被解析成函数对象，这就乱了套了，很多难以发现的bug都是因为这个原因导致的。\r\n\r\n<em>注：IE9貌似已经修复了这个问题</em>\r\n\r\n<strong>例2：将命名函数表达式同时当作函数声明和函数表达式</strong>\r\n<div>\r\n<pre>    typeof g; // \"function\"\r\n    var f = function g(){};</pre>\r\n</div>\r\n特性环境下，函数声明会优先于任何表达式被解析，上面的例子展示的是JScript实际上是把命名函数表达式当成函数声明了，因为它在实际声明之前就解析了g。\r\n\r\n这个例子引出了下一个例子。\r\n<strong>例3：命名函数表达式会创建两个截然不同的函数对象！</strong>\r\n<div>\r\n<pre>    var f = function g(){};\r\n    f === g; // false\r\n\r\n    f.expando = ''foo'';\r\n    g.expando; // undefined</pre>\r\n</div>\r\n看到这里，大家会觉得问题严重了，因为修改任何一个对象，另外一个没有什么改变，这太恶了。通过这个例子可以发现，创建2个不同的对象，也就是说如果你想修改f的属性中保存某个信息，然后想当然地通过引用相同对象的g的同名属性来使用，那问题就大了，因为根本就不可能。\r\n\r\n再来看一个稍微复杂的例子：\r\n\r\n<strong>例4：仅仅顺序解析函数声明而忽略条件语句块</strong>\r\n<div>\r\n<pre>    var f = function g() {\r\n      return 1;\r\n    };\r\n    if (false) {\r\n      f = function g(){\r\n        return 2;\r\n      };\r\n    }\r\n    g(); // 2</pre>\r\n</div>\r\n这个bug查找就难多了，但导致bug的原因却非常简单。首先，g被当作函数声明解析，由于JScript中的函数声明不受条件代码块约束，所以在这个很恶的if分支中，g被当作另一个函数function g(){ return 2 }，也就是又被声明了一次。然后，所有“常规的”表达式被求值，而此时f被赋予了另一个新创建的对象的引用。由于在对表达式求值的时候，永远不会进入“这个可恶if分支，因此f就会继续引用第一个函数function g(){ return 1 }。分析到这里，问题就很清楚了：假如你不够细心，在f中调用了g，那么将会调用一个毫不相干的g函数对象。\r\n\r\n你可能会文，将不同的对象和arguments.callee相比较时，有什么样的区别呢？我们来看看：\r\n<div>\r\n<pre> var f = function g(){\r\n    return [\r\n      arguments.callee == f,\r\n      arguments.callee == g\r\n    ];\r\n  };\r\n  f(); // [true, false]\r\n  g(); // [false, true]</pre>\r\n</div>\r\n可以看到，arguments.callee的引用一直是被调用的函数，实际上这也是好事，稍后会解释。\r\n\r\n还有一个有趣的例子，那就是在不包含声明的赋值语句中使用命名函数表达式：\r\n<div>\r\n<pre>  (function(){\r\n    f = function f(){};\r\n  })();</pre>\r\n</div>\r\n按照代码的分析，我们原本是想创建一个全局属性f（注意不要和一般的匿名函数混淆了，里面用的是带名字的生命），JScript在这里捣乱了一把，首先他把表达式当成函数声明解析了，所以左边的f被声明为局部变量了（和一般的匿名函数里的声明一样），然后在函数执行的时候，f已经是定义过的了，右边的function f(){}则直接就赋值给局部变量f了，所以f根本就不是全局属性。\r\n\r\n&nbsp;\r\n\r\n了解了JScript这么变态以后，我们就要及时预防这些问题了，首先<strong>防范标识符泄漏带外部作用域</strong>，其次，应该永远<strong>不引用被用作函数名称的标识符</strong>；还记得前面例子中那个讨人厌的标识符g吗？——如果我们能够当g不存在，可以避免多少不必要的麻烦哪。因此，关键就在于始终要通过f或者arguments.callee来引用函数。如果你使用了命名函数表达式，那么应该只在调试的时候利用那个名字。最后，还要记住一点，一定要把<strong>命名函数表达式声明期间错误创建的函数清理干净</strong>。\r\n\r\n对于，上面最后一点，我们还得再解释一下。\r\n<h1>JScript的内存管理</h1>\r\n知道了这些不符合规范的代码解析bug以后，我们如果用它的话，就会发现内存方面其实是有问题的，来看一个例子：\r\n<div>\r\n<pre>  var f = (function(){\r\n    if (true) {\r\n      return function g(){};\r\n    }\r\n    return function g(){};\r\n  })();</pre>\r\n</div>\r\n我们知道，这个匿名函数调用返回的函数（带有标识符g的函数），然后赋值给了外部的f。我们也知道，命名函数表达式会导致产生多余的函数对象，而该对象与返回的函数对象不是一回事。所以这个多余的g函数就死在了返回函数的闭包中了，因此内存问题就出现了。这是因为if语句内部的函数与g是在同一个作用域中被声明的。这种情况下 ，除非我们显式断开对g函数的引用，否则它一直占着内存不放。\r\n<div>\r\n<pre>  var f = (function(){\r\n    var f, g;\r\n    if (true) {\r\n      f = function g(){};\r\n    }\r\n    else {\r\n      f = function g(){};\r\n    }\r\n    // 设置g为null以后它就不会再占内存了\r\n    g = null;\r\n    return f;\r\n  })();</pre>\r\n</div>\r\n通过设置g为null，垃圾回收器就把g引用的那个隐式函数给回收掉了，为了验证我们的代码，我们来做一些测试，以确保我们的内存被回收了。\r\n\r\n测试\r\n\r\n测试很简单，就是命名函数表达式创建10000个函数，然后把它们保存在一个数组中。等一会儿以后再看这些函数到底占用了多少内存。然后，再断开这些引用并重复这一过程。下面是测试代码：\r\n<div>\r\n<pre>  function createFn(){\r\n    return (function(){\r\n      var f;\r\n      if (true) {\r\n        f = function F(){\r\n          return ''standard'';\r\n        };\r\n      }\r\n      else if (false) {\r\n        f = function F(){\r\n          return ''alternative'';\r\n        };\r\n      }\r\n      else {\r\n        f = function F(){\r\n          return ''fallback'';\r\n        };\r\n      }\r\n      // var F = null;\r\n      return f;\r\n    })();\r\n  }\r\n\r\n  var arr = [ ];\r\n  for (var i=0; i&lt;10000; i++) {\r\n    arr[i] = createFn();\r\n  }</pre>\r\n</div>\r\n通过运行在Windows XP SP2中的任务管理器可以看到如下结果：\r\n<div>\r\n<pre>  IE6:\r\n\r\n    without `null`:   7.6K -&gt; 20.3K\r\n    with `null`:      7.6K -&gt; 18K\r\n\r\n  IE7:\r\n\r\n    without `null`:   14K -&gt; 29.7K\r\n    with `null`:      14K -&gt; 27K</pre>\r\n</div>\r\n如我们所料，显示断开引用可以释放内存，但是释放的内存不是很多，10000个函数对象才释放大约3M的内存，这对一些小型脚本不算什么，但对于大型程序，或者长时间运行在低内存的设备里的时候，这是非常有必要的。\r\n\r\n&nbsp;\r\n\r\n关于在Safari 2.x中JS的解析也有一些bug，但介于版本比较低，所以我们在这里就不介绍了，大家如果想看的话，请仔细查看英文资料。\r\n<h1>SpiderMonkey的怪癖</h1>\r\n大家都知道，命名函数表达式的标识符只在函数的局部作用域中有效。但包含这个标识符的局部作用域又是什么样子的吗？其实非常简单。在命名函数表达式被求值时，会<strong>创建一个特殊的对象</strong>，该对象的唯一目的就是保存一个属性，而这个属性的名字对应着函数标识符，属性的值对应着那个函数。这个对象会被注入到当前作用域链的前端。然后，被“扩展”的作用域链又被用于初始化函数。\r\n\r\n在这里，有一点十分有意思，那就是ECMA-262定义这个（保存函数标识符的）“特殊”对象的方式。标准说<strong>“像调用new Object()表达式那样”</strong>创建这个对象。如果从字面上来理解这句话，那么这个对象就应该是全局<code>Object</code>的一个实例。然而，只有一个实现是按照标准字面上的要求这么做的，这个实现就是SpiderMonkey。因此，在SpiderMonkey中，扩展<code>Object.prototype</code>有可能会干扰函数的局部作用域：\r\n<div>\r\n<pre>  Object.prototype.x = ''outer'';\r\n  \r\n  (function(){\r\n    \r\n    var x = ''inner'';\r\n    \r\n    /*\r\n      函数foo的作用域链中有一个特殊的对象——用于保存函数的标识符。这个特殊的对象实际上就是{ foo: &lt;function object&gt; }。\r\n      当通过作用域链解析x时，首先解析的是foo的局部环境。如果没有找到x，则继续搜索作用域链中的下一个对象。下一个对象\r\n      就是保存函数标识符的那个对象——{ foo: &lt;function object&gt; }，由于该对象继承自Object.prototype，所以在此可以找到x。\r\n      而这个x的值也就是Object.prototype.x的值（outer）。结果，外部函数的作用域（包含x = ''inner''的作用域）就不会被解析了。\r\n    */\r\n    \r\n    (function foo(){\r\n      \r\n      alert(x); // 提示框中显示：outer\r\n    \r\n    })();\r\n  })();</pre>\r\n</div>\r\n不过，更高版本的SpiderMonkey改变了上述行为，原因可能是认为那是一个安全漏洞。也就是说，“特殊”对象不再继承Object.prototype了。不过，如果你使用Firefox 3或者更低版本，还可以“重温”这种行为。\r\n\r\n另一个把内部对象实现为全局Object对象的是黑莓（Blackberry）浏览器。目前，它的<em>活动对象</em>（Activation Object）仍然继承Object.prototype。可是，ECMA-262并没有说<em>活动对象</em>也要“像调用new Object()表达式那样”来创建（或者说像创建保存NFE标识符的对象一样创建）。 人家规范只说了<em>活动对象</em>是规范中的一种机制。\r\n\r\n那我们就来看看黑莓里都发生了什么：\r\n<div>\r\n<pre>  Object.prototype.x = ''outer'';\r\n  \r\n  (function(){\r\n    \r\n    var x = ''inner'';\r\n    \r\n    (function(){\r\n      \r\n      /*\r\n      在沿着作用域链解析x的过程中，首先会搜索局部函数的活动对象。当然，在该对象中找不到x。\r\n      可是，由于活动对象继承自Object.prototype，因此搜索x的下一个目标就是Object.prototype；而\r\n      Object.prototype中又确实有x的定义。结果，x的值就被解析为——outer。跟前面的例子差不多，\r\n      包含x = ''inner''的外部函数的作用域（活动对象）就不会被解析了。\r\n      */\r\n      \r\n      alert(x); // 显示：outer\r\n      \r\n    })();\r\n  })();</pre>\r\n</div>\r\n不过神奇的还是，函数中的变量甚至会与已有的<code>Object.prototype</code>的成员发生冲突，来看看下面的代码：\r\n<div>\r\n<pre>  (function(){\r\n    \r\n    var constructor = function(){ return 1; };\r\n    \r\n    (function(){\r\n      \r\n      constructor(); // 求值结果是{}（即相当于调用了Object.prototype.constructor()）而不是1\r\n      \r\n      constructor === Object.prototype.constructor; // true\r\n      toString === Object.prototype.toString; // true\r\n      \r\n      // ……\r\n      \r\n    })();\r\n  })();</pre>\r\n</div>\r\n要避免这个问题，要避免使用Object.prototype里的属性名称，如toString, valueOf, hasOwnProperty等等。\r\n\r\n&nbsp;\r\n\r\nJScript解决方案\r\n<div>\r\n<pre>  var fn = (function(){\r\n\r\n    // 声明要引用函数的变量\r\n    var f;\r\n\r\n    // 有条件地创建命名函数\r\n    // 并将其引用赋值给f\r\n    if (true) {\r\n      f = function F(){ }\r\n    }\r\n    else if (false) {\r\n      f = function F(){ }\r\n    }\r\n    else {\r\n      f = function F(){ }\r\n    }\r\n\r\n    // 声明一个与函数名（标识符）对应的变量，并赋值为null\r\n    // 这实际上是给相应标识符引用的函数对象作了一个标记，\r\n    // 以便垃圾回收器知道可以回收它了\r\n    var F = null;\r\n\r\n    // 返回根据条件定义的函数\r\n    return f;\r\n  })();</pre>\r\n</div>\r\n最后我们给出一个应用上述技术的应用实例，这是一个跨浏览器的addEvent函数代码：\r\n<div>\r\n<pre>  // 1) 使用独立的作用域包含声明\r\n  var addEvent = (function(){\r\n\r\n    var docEl = document.documentElement;\r\n\r\n    // 2) 声明要引用函数的变量\r\n    var fn;\r\n\r\n    if (docEl.addEventListener) {\r\n\r\n      // 3) 有意给函数一个描述性的标识符\r\n      fn = function addEvent(element, eventName, callback) {\r\n        element.addEventListener(eventName, callback, false);\r\n      }\r\n    }\r\n    else if (docEl.attachEvent) {\r\n      fn = function addEvent(element, eventName, callback) {\r\n        element.attachEvent(''on'' + eventName, callback);\r\n      }\r\n    }\r\n    else {\r\n      fn = function addEvent(element, eventName, callback) {\r\n        element[''on'' + eventName] = callback;\r\n      }\r\n    }\r\n\r\n    // 4) 清除由JScript创建的addEvent函数\r\n    //    一定要保证在赋值前使用var关键字\r\n    //    除非函数顶部已经声明了addEvent\r\n    var addEvent = null;\r\n\r\n    // 5) 最后返回由fn引用的函数\r\n    return fn;\r\n  })();</pre>\r\n</div>\r\n<h1>替代方案</h1>\r\n其实，如果我们不想要这个描述性名字的话，我们就可以用最简单的形式来做，也就是在函数内部声明一个函数（而不是函数表达式），然后返回该函数：\r\n<div>\r\n<pre>  var hasClassName = (function(){\r\n\r\n    // 定义私有变量\r\n    var cache = { };\r\n\r\n    // 使用函数声明\r\n    function hasClassName(element, className) {\r\n      var _className = ''(?:^|\\\\\\\\s+)'' + className + ''(?:\\\\\\\\s+|\$)'';\r\n      var re = cache[_className] || (cache[_className] = new RegExp(_className));\r\n      return re.test(element.className);\r\n    }\r\n\r\n    // 返回函数\r\n    return hasClassName;\r\n  })();</pre>\r\n</div>\r\n显然，当存在多个分支函数定义时，这个方案就不行了。不过有种模式貌似可以实现：那就是提前使用函数声明来定义所有函数，并分别为这些函数指定不同的标识符：\r\n<div>\r\n<pre>  var addEvent = (function(){\r\n\r\n    var docEl = document.documentElement;\r\n\r\n    function addEventListener(){\r\n      /* ... */\r\n    }\r\n    function attachEvent(){\r\n      /* ... */\r\n    }\r\n    function addEventAsProperty(){\r\n      /* ... */\r\n    }\r\n\r\n    if (typeof docEl.addEventListener != ''undefined'') {\r\n      return addEventListener;\r\n    }\r\n    elseif (typeof docEl.attachEvent != ''undefined'') {\r\n      return attachEvent;\r\n    }\r\n    return addEventAsProperty;\r\n  })();</pre>\r\n</div>\r\n虽然这个方案很优雅，但也不是没有缺点。第一，由于使用不同的标识符，导致丧失了命名的一致性。且不说这样好还是坏，最起码它不够清晰。有人喜欢使用相同的名字，但也有人根本不在乎字眼上的差别。可毕竟，不同的名字会让人联想到所用的不同实现。例如，在调试器中看到attachEvent，我们就知 道<code>addEvent</code>是基于<code>attachEvent</code>的实现。当 然，基于实现来命名的方式也不一定都行得通。假如我们要提供一个API，并按照这种方式把函数命名为inner。那么API用户的很容易就会被相应实现的 细节搞得晕头转向。\r\n\r\n要解决这个问题，当然就得想一套更合理的命名方案了。但关键是不要再额外制造麻烦。我现在能想起来的方案大概有如下几个：\r\n<div>\r\n<pre>  ''addEvent'', ''altAddEvent'', ''fallbackAddEvent''\r\n  // 或者\r\n  ''addEvent'', ''addEvent2'', ''addEvent3''\r\n  // 或者\r\n  ''addEvent_addEventListener'', ''addEvent_attachEvent'', ''addEvent_asProperty''</pre>\r\n</div>\r\n另外，这种模式还存在一个小问题，即增加内存占用。提前创建N个不同名字的函数，等于有N-1的函数是用不到的。具体来讲，如果<code>document.documentElement</code> 中包含<code>attachEvent</code>，那么<code>addEventListener</code> 和<code>addEventAsProperty</code>则根本就用不着了。可是，他们都占着内存哪；而且，这些内存将永远都得不到释放，原因跟JScript臭哄哄的命名表达式相同——这两个函数都被“截留”在返回的那个函数的闭包中了。\r\n\r\n不过，增加内存占用这个问题确实没什么大不了的。如果某个库——例如Prototype.js——采用了这种模式，无非也就是多创建一两百个函数而已。只要不是（在运行时）重复地创建这些函数，而是只（在加载时）创建一次，那么就没有什么好担心的。\r\n<h1 id=\"webkit-displayName\">WebKit的displayName</h1>\r\nWebKit团队在这个问题采取了有点儿另类的策略。介于匿名和命名函数如此之差的表现力，WebKit引入了一个“特殊的”<code>displayName</code>属性（本质上是一个字符串），如果开发人员为函数的这个属性赋值，则该属性的值将在调试器或性能分析器中被显示在函数“名称”的位置上。<a href=\"http://www.alertdebugging.com/2009/04/29/building-a-better-javascript-profiler-with-webkit/\" target=\"_blank\">Francisco Tolmasky详细地解释了这个策略的原理和实现</a>。\r\n\r\n&nbsp;\r\n<h1>未来考虑</h1>\r\n将来的ECMAScript-262第5版（目前还是草案）会引入所谓的<strong>严格模式（strict mode）</strong>。开启严格模式的实现会禁用语言中的那些不稳定、不可靠和不安全的特性。据说出于安全方面的考虑，<code>arguments.callee</code>属性将在严格模式下被“封杀”。因此，在处于严格模式时，访问<code></code><code>arguments.callee</code>会导致<code>TypeError</code>（参见ECMA-262第5版的10.6节）。而我之所以在此提到严格模式，是因为如果在基于第5版标准的实现中无法使用<code>arguments.callee</code>来执行递归操作，那么使用命名函数表达式的可能性就会大大增加。从这个意义上来说，理解命名函数表达式的语义及其bug也就显得更加重要了。\r\n<div>\r\n<pre>  // 此前，你可能会使用arguments.callee\r\n  (function(x) {\r\n    if (x &lt;= 1) return 1;\r\n    return x * arguments.callee(x - 1);\r\n  })(10);\r\n  \r\n  // 但在严格模式下，有可能就要使用命名函数表达式\r\n  (function factorial(x) {\r\n    if (x &lt;= 1) return 1;\r\n    return x * factorial(x - 1);\r\n  })(10);\r\n  \r\n  // 要么就退一步，使用没有那么灵活的函数声明\r\n  function factorial(x) {\r\n    if (x &lt;= 1) return 1;\r\n    return x * factorial(x - 1);\r\n  }\r\n  factorial(10);</pre>\r\n</div>\r\n<h1>致谢</h1>\r\n<strong><em>理查德· 康福德（Richard Cornford）</em></strong>，是他率先<a href=\"http://groups.google.com/group/comp.lang.javascript/msg/5b508b03b004bce8\" target=\"_blank\">解释了JScript中命名函数表达式所存在的bug</a>。理查德解释了我在这篇文章中提及的大多数bug，所以我强烈建议大家去看看他的解释。我还要感谢<strong><em>Yann-Erwan Perio</em></strong>和<strong><em>道格拉斯·克劳克佛德（Douglas Crockford）</em></strong>，他们早在2003年就在<a href=\"http://groups.google.com/group/comp.lang.javascript/msg/03d53d114d176323\" target=\"_blank\">comp.lang.javascript论坛中提及并讨论NFE问题了</a>。\r\n\r\n<strong><em>约翰-戴维·道尔顿（John-David Dalton）</em></strong>对“最终解决方案”提出了很好的建议。\r\n\r\n<strong><em>托比·兰吉</em></strong>的点子被我用在了“替代方案”中。\r\n\r\n<strong><em>盖瑞特·史密斯（Garrett Smith）</em></strong>和<strong><em>德米特里·苏斯尼科（Dmitry Soshnikov）</em></strong>对本文的多方面作出了补充和修正。\r\n英文原文：http://kangax.github.com/nfe/\r\n\r\n参考译文：<a href=\"http://www.cn-cuckoo.com/main/wp-content/uploads/2009/12/named-function-expressions-demystified.html#jscript-memory-management\" target=\"_blank\">连接访问</a> (SpiderMonkey的怪癖之后的章节参考该文)\r\n<h1>同步与推荐</h1>\r\n本文已同步至目录索引：<a href=\"http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html\" target=\"_blank\">深入理解JavaScript系列</a>\r\n\r\n深入理解JavaScript系列文章，包括了原创，翻译，转载等各类型的文章，如果对你有用，请推荐支持一把，给大叔写作的动力。','深入理解JavaScript系列（2）：揭秘命名函数表达式','','inherit','open','open','','22-revision-2','','','2013-03-29 17:01:08','2013-03-29 09:01:08','','22','http://wap1.benbenweb.com/?p=27','0','revision','','0');");
E_D("replace into `wp_posts` values('28','1','2013-03-29 16:58:56','2013-03-29 08:58:56','才华横溢的<a href=\"http://www.phpied.com/\">Stoyan Stefanov</a>，在他写的由<a href=\"http://oreilly.com/\">O’Reilly</a>初版的新书<a href=\"http://amzn.to/93szK7\">《JavaScript Patterns》</a>(JavaScript模式)中，我想要是为我们的读者贡献其摘要，那会是件很美妙的事情。具体一点就是编写高质量JavaScript的一些要素，例如避免全局变量，使用单变量声明，在循环中预缓存length(长度)，遵循代码阅读，以及更多。\r\n\r\n此摘要也包括一些与代码不太相关的习惯，但对整体代码的创建息息相关，包括撰写API文档、执行同行评审以及运行JSLint。这些习惯和最佳做法可以帮助你写出更好的，更易于理解和维护的代码，这些代码在几个月或是几年之后再回过头看看也是会觉得很自豪的。\r\n<h1>书写可维护的代码(Writing Maintainable Code )</h1>\r\n软件bug的修复是昂贵的，并且随着时间的推移，这些bug的成本也会增加，尤其当这些bug潜伏并慢慢出现在已经发布的软件中时。当你发现bug 的时候就立即修复它是最好的，此时你代码要解决的问题在你脑中还是很清晰的。否则，你转移到其他任务，忘了那个特定的代码，一段时间后再去查看这些代码就 需要：\r\n<ul id=\"ul\">\r\n	<li>花时间学习和理解这个问题</li>\r\n	<li>化时间是了解应该解决的问题代码</li>\r\n</ul>\r\n还有问题，特别对于大的项目或是公司，修复bug的这位伙计不是写代码的那个人（且发现bug和修复bug的不是同一个人）。因此，必须降低理解代 码花费的时间，无论是一段时间前你自己写的代码还是团队中的其他成员写的代码。这关系到底线（营业收入）和开发人员的幸福，因为我们更应该去开发新的激动 人心的事物而不是花几小时几天的时间去维护遗留代码。\r\n\r\n另一个相关软件开发生命的事实是，读代码花费的时间要比写来得多。有时候，当你专注并深入思考某个问题的时候，你可以坐下来，一个下午写大量的代码。\r\n\r\n你的代码很能很快就工作了，但是，随着应用的成熟，还会有很多其他的事情发生，这就要求你的进行进行审查，修改，和调整。例如：\r\n<ul id=\"ul\">\r\n	<li>bug是暴露的</li>\r\n	<li>新功能被添加到应用程序</li>\r\n	<li>程序在新的环境下工作（例如，市场上出现新想浏览器）</li>\r\n	<li>代码改变用途</li>\r\n	<li>代码得完全从头重新，或移植到另一个架构上或者甚至使用另一种语言</li>\r\n</ul>\r\n由于这些变化，很少人力数小时写的代码最终演变成花数周来阅读这些代码。这就是为什么创建可维护的代码对应用程序的成功至关重要。\r\n\r\n可维护的代码意味着：\r\n<ul id=\"ul\">\r\n	<li>可读的</li>\r\n	<li>一致的</li>\r\n	<li>可预测的</li>\r\n	<li>看上去就像是同一个人写的</li>\r\n	<li>已记录</li>\r\n</ul>\r\n<h1>最小全局变量(Minimizing Globals)</h1>\r\nJavaScript通过函数管理作用域。在函数内部声明的变量只在这个函数内部，函数外面不可用。另一方面，全局变量就是在任何函数外面声明的或是未声明直接简单使用的。\r\n\r\n每个JavaScript环境有一个全局对象，当你在任意的函数外面使用this的时候可以访问到。你创建的每一个全部变量都成了这个全局对象的属 性。在浏览器中，方便起见，该全局对象有个附加属性叫做window，此window(通常)指向该全局对象本身。下面的代码片段显示了如何在浏览器环境 中创建和访问的全局变量：\r\n<div>\r\n<pre>myglobal = \"hello\"; // 不推荐写法\r\nconsole.log(myglobal); // \"hello\"\r\nconsole.log(window.myglobal); // \"hello\"\r\nconsole.log(window[\"myglobal\"]); // \"hello\"\r\nconsole.log(this.myglobal); // \"hello\"</pre>\r\n</div>\r\n&nbsp;\r\n<h1>全局变量的问题</h1>\r\n全局变量的问题在于，你的JavaScript应用程序和web页面上的所有代码都共享了这些全局变量，他们住在同一个全局命名空间，所以当程序的两个不同部分定义同名但不同作用的全局变量的时候，命名冲突在所难免。\r\n\r\nweb页面包含不是该页面开发者所写的代码也是比较常见的，例如：\r\n<ul id=\"ul\">\r\n	<li>第三方的JavaScript库</li>\r\n	<li>广告方的脚本代码</li>\r\n	<li>第三方用户跟踪和分析脚本代码</li>\r\n	<li>不同类型的小组件，标志和按钮</li>\r\n</ul>\r\n比方说，该第三方脚本定义了一个全局变量，叫做result；接着，在你的函数中也定义一个名为result的全局变量。其结果就是后面的变量覆盖前面的，第三方脚本就一下子嗝屁啦！\r\n\r\n因此，要想和其他脚本成为好邻居的话，尽可能少的使用全局变量是很重要的。在书中后面提到的一些减少全局变量的策略，例如命名空间模式或是函数立即自动执行，但是要想让全局变量少最重要的还是始终使用var来声明变量。\r\n\r\n由于JavaScript的两个特征，不自觉地创建出全局变量是出乎意料的容易。首先，你可以甚至不需要声明就可以使用变量；第二，JavaScript有隐含的全局概念，意味着你不声明的任何变量都会成为一个全局对象属性。参考下面的代码：\r\n<div>\r\n<div>\r\n<pre>function sum(x, y) {\r\n   // 不推荐写法: 隐式全局变量 \r\n   result = x + y;\r\n   return result;\r\n}\r\n</pre>\r\n</div>\r\n</div>\r\n此段代码中的<code>result</code>没有声明。代码照样运作正常，但在调用函数后你最后的结果就多一个全局命名空间，这可以是一个问题的根源。\r\n\r\n经验法则是始终使用var声明变量，正如改进版的sum()函数所演示的：\r\n<div>\r\n<pre>function sum(x, y) {\r\n   var result = x + y;\r\n   return result;\r\n}</pre>\r\n</div>\r\n另一个创建隐式全局变量的反例就是使用任务链进行部分var声明。下面的片段中，<code>a</code>是本地变量但是<code>b</code>确实全局变量，这可能不是你希望发生的：\r\n<div>\r\n<pre>// 反例，勿使用 \r\nfunction foo() {\r\n   var a = b = 0;\r\n   // ...\r\n}</pre>\r\n</div>\r\n此现象发生的原因在于这个从右到左的赋值，首先，是赋值表达式<code>b = 0</code>，此情况下b是未声明的。这个表达式的返回值是0，然后这个0就分配给了通过var定义的这个局部变量a。换句话说，就好比你输入了：\r\n<div>\r\n<pre>var a = (b = 0);</pre>\r\n</div>\r\n如果你已经准备好声明变量，使用链分配是比较好的做法，不会产生任何意料之外的全局变量，如：\r\n<div>\r\n<pre>function foo() {\r\n   var a, b;\r\n   // ... a = b = 0; // 两个均局部变量\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<blockquote>然而，另外一个避免全局变量的原因是可移植性。如果你想你的代码在不同的环境下（主机下）运行，使用全局变量如履薄冰，因为你会无意中覆盖你最初环境下不存在的主机对象（所以你原以为名称可以放心大胆地使用，实际上对于有些情况并不适用）。</blockquote>\r\n<h1>忘记var的副作用(Side Effects When Forgetting var)</h1>\r\n隐式全局变量和明确定义的全局变量间有些小的差异，就是通过<code>delete</code>操作符让变量未定义的能力。\r\n<ul id=\"ul\">\r\n	<li>通过var创建的全局变量（任何函数之外的程序中创建）是不能被删除的。</li>\r\n	<li>无var创建的隐式全局变量（无视是否在函数中创建）是能被删除的。</li>\r\n</ul>\r\n这表明，在技术上，隐式全局变量并不是真正的全局变量，但它们是全局对象的属性。属性是可以通过<code>delete</code>操作符删除的，而变量是不能的：\r\n<div>\r\n<pre>// 定义三个全局变量\r\nvar global_var = 1;\r\nglobal_novar = 2; // 反面教材\r\n(function () {\r\n   global_fromfunc = 3; // 反面教材\r\n}());\r\n\r\n// 试图删除\r\ndelete global_var; // false\r\ndelete global_novar; // true\r\ndelete global_fromfunc; // true\r\n\r\n// 测试该删除\r\ntypeof global_var; // \"number\"\r\ntypeof global_novar; // \"undefined\"\r\ntypeof global_fromfunc; // \"undefined\"</pre>\r\n</div>\r\n在ES5严格模式下，未声明的变量（如在前面的代码片段中的两个反面教材）工作时会抛出一个错误。\r\n<h1>访问全局对象(Access to the Global Object)</h1>\r\n在浏览器中，全局对象可以通过<code>window</code>属性在代码的任何位置访问（除非你做了些比较出格的事情，像是声明了一个名为window的局部变量）。但是在其他环境下，这个方便的属性可能被叫做其他什么东西（甚至在程序中不可用）。如果你需要在没有硬编码的<code>window</code>标识符下访问全局对象，你可以在任何层级的函数作用域中做如下操作：\r\n<div>\r\n<pre>var global = (function () {\r\n   return this;\r\n}());</pre>\r\n</div>\r\n&nbsp;\r\n\r\n这种方法可以随时获得全局对象，因为其在函数中被当做函数调用了（不是通过<code>new</code>构造），<code>this</code>总 是指向全局对象。实际上这个病不适用于ECMAScript 5严格模式，所以，在严格模式下时，你必须采取不同的形式。例如，你正在开发一个JavaScript库，你可以将你的代码包裹在一个即时函数中，然后从 全局作用域中，传递一个引用指向this作为你即时函数的参数。\r\n<h1>单var形式（Single var Pattern）</h1>\r\n在函数顶部使用单var语句是比较有用的一种形式，其好处在于：\r\n<ul id=\"ul\">\r\n	<li>提供了一个单一的地方去寻找功能所需要的所有局部变量</li>\r\n	<li>防止变量在定义之前使用的逻辑错误</li>\r\n	<li>帮助你记住声明的全局变量，因此较少了全局变量//zxx:此处我自己是有点晕乎的…</li>\r\n	<li>少代码（类型啊传值啊单线完成）</li>\r\n</ul>\r\n单var形式长得就像下面这个样子：\r\n<div>\r\n<pre>function func() {\r\n   var a = 1,\r\n       b = 2,\r\n       sum = a + b,\r\n       myobject = {},\r\n       i,\r\n       j;\r\n   // function body...\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n您可以使用一个var语句声明多个变量，并以逗号分隔。像这种初始化变量同时初始化值的做法是很好的。这样子可以防止逻辑错误（所有未初始化但声明的变量的初始值是<code>undefined</code>）和增加代码的可读性。在你看到代码后，你可以根据初始化的值知道这些变量大致的用途，例如是要当作对象呢还是当作整数来使。\r\n\r\n你也可以在声明的时候做一些实际的工作，例如前面代码中的<code>sum = a + b</code>这个情况，另外一个例子就是当你使用DOM（文档对象模型）引用时，你可以使用单一的var把DOM引用一起指定为局部变量，就如下面代码所示的：\r\n<div>\r\n<pre>function updateElement() {\r\n   var el = document.getElementById(\"result\"),\r\n       style = el.style;\r\n   // 使用el和style干点其他什么事...\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<h1>预解析：var散布的问题(Hoisting: A Problem with Scattered vars)</h1>\r\nJavaScript中，你可以在函数的任何位置声明多个var语句，并且它们就好像是在函数顶部声明一样发挥作用，这种行为称为 hoisting（悬置/置顶解析/预解析）。当你使用了一个变量，然后不久在函数中又重新声明的话，就可能产生逻辑错误。对于JavaScript，只 要你的变量是在同一个作用域中（同一函数），它都被当做是声明的，即使是它在var声明前使用的时候。看下面这个例子：\r\n<div>\r\n<pre>// 反例\r\nmyname = \"global\"; // 全局变量\r\nfunction func() {\r\n    alert(myname); // \"undefined\"\r\n    var myname = \"local\";\r\n    alert(myname); // \"local\"\r\n}\r\nfunc();</pre>\r\n</div>\r\n&nbsp;\r\n\r\n在这个例子中，你可能会以为第一个alert弹出的是”global”，第二个弹出”loacl”。这种期许是可以理解的，因为在第一个alert 的时候，myname未声明，此时函数肯定很自然而然地看全局变量myname，但是，实际上并不是这么工作的。第一个alert会弹 出”undefined”是因为myname被当做了函数的局部变量（尽管是之后声明的），所有的变量声明当被悬置到函数的顶部了。因此，为了避免这种混 乱，最好是预先声明你想使用的全部变量。\r\n\r\n上面的代码片段执行的行为可能就像下面这样：\r\n<div>\r\n<pre>myname = \"global\"; // global variable\r\nfunction func() {\r\n   var myname; // 等同于 -&gt; var myname = undefined;\r\n   alert(myname); // \"undefined\"\r\n   myname = \"local\";\r\n   alert(myname); // \"local\"}\r\nfunc();</pre>\r\n</div>\r\n&nbsp;\r\n<blockquote>为了完整，我们再提一提执行层面的稍微复杂点的东西。代码处理分两个阶段，第一阶段是变量，函数声明，以及正常格式的参数创建，这是一个解析和进入上下文 的阶段。第二个阶段是代码执行，函数表达式和不合格的标识符（为声明的变量）被创建。但是，出于实用的目的，我们就采用了”hoisting”这个概念， 这种ECMAScript标准中并未定义，通常用来描述行为。</blockquote>\r\n<h1>for循环(for Loops)</h1>\r\n在<code>for</code>循环中，你可以循环取得数组或是数组类似对象的值，譬如<code>arguments</code>和<code>HTMLCollection</code>对象。通常的循环形式如下：\r\n<div>\r\n<pre>// 次佳的循环\r\nfor (var i = 0; i &lt; myarray.length; i++) {\r\n   // 使用myarray[i]做点什么\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n这种形式的循环的不足在于每次循环的时候数组的长度都要去获取下。这回降低你的代码，尤其当<code>myarray</code>不是数组，而是一个<code>HTMLCollection</code>对象的时候。\r\n\r\n<code>HTMLCollections</code>指的是DOM方法返回的对象，例如：\r\n<div>\r\n<pre>document.getElementsByName()\r\ndocument.getElementsByClassName()\r\ndocument.getElementsByTagName()</pre>\r\n</div>\r\n&nbsp;\r\n\r\n还有其他一些<code>HTMLCollections</code>，这些是在DOM标准之前引进并且现在还在使用的。有：\r\n<div>\r\n<pre>document.images: 页面上所有的图片元素\r\ndocument.links : 所有a标签元素\r\ndocument.forms : 所有表单\r\ndocument.forms[0].elements : 页面上第一个表单中的所有域</pre>\r\n</div>\r\n&nbsp;\r\n\r\n集合的麻烦在于它们实时查询基本文档（HTML页面）。这意味着每次你访问任何集合的长度，你要实时查询DOM，而DOM操作一般都是比较昂贵的。\r\n\r\n这就是为什么当你循环获取值时，缓存数组(或集合)的长度是比较好的形式，正如下面代码显示的：\r\n<div>\r\n<pre>for (var i = 0, max = myarray.length; i &lt; max; i++) {\r\n   // 使用myarray[i]做点什么\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n这样，在这个循环过程中，你只检索了一次长度值。\r\n\r\n在所有浏览器下，循环获取内容时缓存<code>HTMLCollections</code>的长度是更快的，2倍(Safari3)到190倍(IE7)之间。//zxx:此数据貌似很老，仅供参考\r\n\r\n注意到，当你明确想要修改循环中的集合的时候（例如，添加更多的DOM元素），你可能更喜欢长度更新而不是常量。\r\n\r\n伴随着单var形式，你可以把变量从循环中提出来，就像下面这样：\r\n<div>\r\n<pre>function looper() {\r\n   var i = 0,\r\n        max,\r\n        myarray = [];\r\n   // ...\r\n   for (i = 0, max = myarray.length; i &lt; max; i++) {\r\n      // 使用myarray[i]做点什么\r\n   }\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n这种形式具有一致性的好处，因为你坚持了单一var形式。不足在于当重构代码的时候，复制和粘贴整个循环有点困难。例如，你从一个函数复制了一个循环到另一个函数，你不得不去确定你能够把<code>i</code>和<code>max</code>引入新的函数（如果在这里没有用的话，很有可能你要从原函数中把它们删掉）。\r\n\r\n最后一个需要对循环进行调整的是使用下面表达式之一来替换<code>i++</code>。\r\n<div>\r\n<pre>i = i + 1\r\ni += 1</pre>\r\n</div>\r\n&nbsp;\r\n\r\nJSLint提示您这样做，原因是<code>++</code>和<code>–-</code>促进了“过分棘手(excessive trickiness)”。//zxx:这里比较难翻译，我想本意应该是让代码变得更加的棘手\r\n如果你直接无视它，JSLint的<code>plusplus</code>选项会是<code>false</code>（默认是default）。\r\n\r\n还有两种变化的形式，其又有了些微改进，因为：\r\n<ul id=\"ul\">\r\n	<li>少了一个变量(无max)</li>\r\n	<li>向下数到0，通常更快，因为和0做比较要比和数组长度或是其他不是0的东西作比较更有效率</li>\r\n</ul>\r\n&nbsp;\r\n<div>\r\n<pre>//第一种变化的形式：\r\n\r\nvar i, myarray = [];\r\nfor (i = myarray.length; i–-;) {\r\n   // 使用myarray[i]做点什么\r\n}\r\n\r\n//第二种使用while循环：\r\n\r\nvar myarray = [],\r\n    i = myarray.length;\r\nwhile (i–-) {\r\n   // 使用myarray[i]做点什么\r\n}</pre>\r\n</div>\r\n这些小的改进只体现在性能上，此外JSLint会对使用i–-加以抱怨。\r\n<h1>for-in循环(for-in Loops)</h1>\r\n<code>for-in</code>循环应该用在非数组对象的遍历上，使用<code>for-in</code>进行循环也被称为“枚举”。\r\n\r\n从技术上将，你可以使用<code>for-in</code>循环数组（因为JavaScript中数组也是对象），但这是不推荐的。因为如果数组对象已被自定义的功能增强，就可能发生逻辑错误。另外，在for-in中，属性列表的顺序（序列）是不能保证的。所以最好数组使用正常的for循环，对象使用for-in循环。\r\n\r\n有个很重要的<code>hasOwnProperty()</code>方法，当遍历对象属性的时候可以过滤掉从原型链上下来的属性。\r\n\r\n思考下面一段代码：\r\n<div>\r\n<pre>// 对象\r\nvar man = {\r\n   hands: 2,\r\n   legs: 2,\r\n   heads: 1\r\n};\r\n\r\n// 在代码的某个地方\r\n// 一个方法添加给了所有对象\r\nif (typeof Object.prototype.clone === \"undefined\") {\r\n   Object.prototype.clone = function () {};\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n在这个例子中，我们有一个使用对象字面量定义的名叫man的对象。在man定义完成后的某个地方，在对象原型上增加了一个很有用的名叫 clone()的方法。此原型链是实时的，这就意味着所有的对象自动可以访问新的方法。为了避免枚举man的时候出现clone()方法，你需要应用<code>hasOwnProperty()</code>方法过滤原型属性。如果不做过滤，会导致clone()函数显示出来，在大多数情况下这是不希望出现的。\r\n<div>\r\n<pre>// 1.\r\n// for-in 循环\r\nfor (var i in man) {\r\n   if (man.hasOwnProperty(i)) { // 过滤\r\n      console.log(i, \":\", man[i]);\r\n   }\r\n}\r\n/* 控制台显示结果\r\nhands : 2\r\nlegs : 2\r\nheads : 1\r\n*/\r\n// 2.\r\n// 反面例子:\r\n// for-in loop without checking hasOwnProperty()\r\nfor (var i in man) {\r\n   console.log(i, \":\", man[i]);\r\n}\r\n/*\r\n控制台显示结果\r\nhands : 2\r\nlegs : 2\r\nheads : 1\r\nclone: function()\r\n*/</pre>\r\n</div>\r\n&nbsp;\r\n\r\n另外一种使用<code>hasOwnProperty()</code>的形式是取消Object.prototype上的方法。像是：\r\n<div>\r\n<pre>for (var i in man) {\r\n   if (Object.prototype.hasOwnProperty.call(man, i)) { // 过滤\r\n      console.log(i, \":\", man[i]);\r\n   }\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n其好处在于在man对象重新定义hasOwnProperty情况下避免命名冲突。也避免了长属性查找对象的所有方法，你可以使用局部变量“缓存”它。\r\n<div>\r\n<pre>var i, hasOwn = Object.prototype.hasOwnProperty;\r\nfor (i in man) {\r\n    if (hasOwn.call(man, i)) { // 过滤\r\n        console.log(i, \":\", man[i]);\r\n    }\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<blockquote>严格来说，不使用<code>hasOwnProperty()</code>并不是一个错误。根据任务以及你对代码的自信程度，你可以跳过它以提高些许的循环速度。但是当你对当前对象内容（和其原型链）不确定的时候，添加<code>hasOwnProperty()</code>更加保险些。</blockquote>\r\n格式化的变化（通不过JSLint）会直接忽略掉花括号，把if语句放到同一行上。其优点在于循环语句读起来就像一个完整的想法（每个元素都有一个自己的属性”X”，使用”X”干点什么）：\r\n<div>\r\n<pre>// 警告： 通不过JSLint检测\r\nvar i, hasOwn = Object.prototype.hasOwnProperty;\r\nfor (i in man) if (hasOwn.call(man, i)) { // 过滤\r\n    console.log(i, \":\", man[i]);\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<h1>（不）扩展内置原型((Not) Augmenting Built-in Prototypes)</h1>\r\n扩增构造函数的prototype属性是个很强大的增加功能的方法，但有时候它太强大了。\r\n\r\n增加内置的构造函数原型（如Object(), Array(), 或Function()）挺诱人的，但是这严重降低了可维护性，因为它让你的代码变得难以预测。使用你代码的其他开发人员很可能更期望使用内置的 JavaScript方法来持续不断地工作，而不是你另加的方法。\r\n\r\n另外，属性添加到原型中，可能会导致不使用hasOwnProperty属性时在循环中显示出来，这会造成混乱。\r\n\r\n因此，不增加内置原型是最好的。你可以指定一个规则，仅当下面的条件均满足时例外：\r\n<ul id=\"ul\">\r\n	<li>可以预期将来的ECMAScript版本或是JavaScript实现将一直将此功能当作内置方法来实现。例如，你可以添加ECMAScript 5中描述的方法，一直到各个浏览器都迎头赶上。这种情况下，你只是提前定义了有用的方法。</li>\r\n	<li>如果您检查您的自定义属性或方法已不存在——也许已经在代码的其他地方实现或已经是你支持的浏览器JavaScript引擎部分。</li>\r\n	<li>你清楚地文档记录并和团队交流了变化。</li>\r\n</ul>\r\n如果这三个条件得到满足，你可以给原型进行自定义的添加，形式如下：\r\n<div>\r\n<pre>if (typeof Object.protoype.myMethod !== \"function\") {\r\n   Object.protoype.myMethod = function () {\r\n      // 实现...\r\n   };\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<h1>switch模式(switch Pattern)</h1>\r\n你可以通过类似下面形式的switch语句增强可读性和健壮性：\r\n<div>\r\n<pre>var inspect_me = 0,\r\n    result = '''';\r\nswitch (inspect_me) {\r\ncase 0:\r\n   result = \"zero\";\r\n   break;\r\ncase 1:\r\n   result = \"one\";\r\n   break;\r\ndefault:\r\n   result = \"unknown\";\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n这个简单的例子中所遵循的风格约定如下：\r\n<ul id=\"ul\">\r\n	<li>每个case和switch对齐（花括号缩进规则除外）</li>\r\n	<li>每个case中代码缩进</li>\r\n	<li>每个case以break清除结束</li>\r\n	<li>避免贯穿（故意忽略break）。如果你非常确信贯穿是最好的方法，务必记录此情况，因为对于有些阅读人而言，它们可能看起来是错误的。</li>\r\n	<li>以default结束switch：确保总有健全的结果，即使无情况匹配。</li>\r\n</ul>\r\n<h1>避免隐式类型转换(Avoiding Implied Typecasting )</h1>\r\nJavaScript的变量在比较的时候会隐式类型转换。这就是为什么一些诸如：false == 0 或 “” == 0 返回的结果是true。为避免引起混乱的隐含类型转换，在你比较值和表达式类型的时候始终使用===和!==操作符。\r\n<div>\r\n<pre>var zero = 0;\r\nif (zero === false) {\r\n   // 不执行，因为zero为0, 而不是false\r\n}\r\n\r\n// 反面示例\r\nif (zero == false) {\r\n   // 执行了...\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n还有另外一种思想观点认为==就足够了===是多余的。例如，当你使用typeof你就知道它会返回一个字符串，所以没有使用严格相等的理由。然而，JSLint要求严格相等，它使代码看上去更有一致性，可以降低代码阅读时的精力消耗。（“==是故意的还是一个疏漏？”）\r\n<h1>避免(Avoiding) eval()</h1>\r\n如果你现在的代码中使用了eval()，记住该咒语“eval()是魔鬼”。此方法接受任意的字符串，并当作JavaScript代码来处理。当有 问题的代码是事先知道的（不是运行时确定的），没有理由使用eval()。如果代码是在运行时动态生成，有一个更好的方式不使用eval而达到同样的目 标。例如，用方括号表示法来访问动态属性会更好更简单：\r\n<div>\r\n<pre>// 反面示例\r\nvar property = \"name\";\r\nalert(eval(\"obj.\" + property));\r\n\r\n// 更好的\r\nvar property = \"name\";\r\nalert(obj[property]);</pre>\r\n</div>\r\n&nbsp;\r\n\r\n使用eval()也带来了安全隐患，因为被执行的代码（例如从网络来）可能已被篡改。这是个很常见的反面教材，当处理Ajax请求得到的JSON 相应的时候。在这些情况下，最好使用JavaScript内置方法来解析JSON相应，以确保安全和有效。若浏览器不支持JSON.parse()，你可 以使用来自JSON.org的库。\r\n\r\n同样重要的是要记住，给setInterval(), setTimeout()和Function()构造函数传递字符串，大部分情况下，与使用eval()是类似的，因此要避免。在幕后，JavaScript仍需要评估和执行你给程序传递的字符串：\r\n<div>\r\n<pre>// 反面示例\r\nsetTimeout(\"myFunc()\", 1000);\r\nsetTimeout(\"myFunc(1, 2, 3)\", 1000);\r\n\r\n// 更好的\r\nsetTimeout(myFunc, 1000);\r\nsetTimeout(function () {\r\n   myFunc(1, 2, 3);\r\n}, 1000);</pre>\r\n</div>\r\n&nbsp;\r\n\r\n使用新的Function()构造就类似于eval()，应小心接近。这可能是一个强大的构造，但往往被误用。如果你绝对必须使用eval()，你 可以考虑使用new Function()代替。有一个小的潜在好处，因为在新Function()中作代码评估是在局部函数作用域中运行，所以代码中任何被评估的通过var 定义的变量都不会自动变成全局变量。另一种方法来阻止自动全局变量是封装eval()调用到一个即时函数中。\r\n\r\n考虑下面这个例子，这里仅<code>un</code>作为全局变量污染了命名空间。\r\n<div>\r\n<pre>console.log(typeof un);    // \"undefined\"\r\nconsole.log(typeof deux); // \"undefined\"\r\nconsole.log(typeof trois); // \"undefined\"\r\n\r\nvar jsstring = \"var un = 1; console.log(un);\";\r\neval(jsstring); // logs \"1\"\r\n\r\njsstring = \"var deux = 2; console.log(deux);\";\r\nnew Function(jsstring)(); // logs \"2\"\r\n\r\njsstring = \"var trois = 3; console.log(trois);\";\r\n(function () {\r\n   eval(jsstring);\r\n}()); // logs \"3\"\r\n\r\nconsole.log(typeof un); // number\r\nconsole.log(typeof deux); // \"undefined\"\r\nconsole.log(typeof trois); // \"undefined\"</pre>\r\n</div>\r\n&nbsp;\r\n\r\n另一间eval()和Function构造不同的是eval()可以干扰作用域链，而Function()更安分守己些。不管你在哪里执行 Function()，它只看到全局作用域。所以其能很好的避免本地变量污染。在下面这个例子中，eval()可以访问和修改它外部作用域中的变量，这是 Function做不来的（注意到使用Function和new Function是相同的）。\r\n<div>\r\n<pre>(function () {\r\n   var local = 1;\r\n   eval(\"local = 3; console.log(local)\"); // logs \"3\"\r\n   console.log(local); // logs \"3\"\r\n}());\r\n\r\n(function () {\r\n   var local = 1;\r\n   Function(\"console.log(typeof local);\")(); // logs undefined\r\n}());</pre>\r\n</div>\r\n&nbsp;\r\n<h1>parseInt()下的数值转换(Number Conversions with parseInt())</h1>\r\n使用parseInt()你可以从字符串中获取数值，该方法接受另一个基数参数，这经常省略，但不应该。当字符串以”0″开头的时候就有可能会出问 题，例如，部分时间进入表单域，在ECMAScript 3中，开头为”0″的字符串被当做8进制处理了，但这已在ECMAScript 5中改变了。为了避免矛盾和意外的结果，总是指定基数参数。\r\n<div>\r\n<pre>var month = \"06\",\r\n    year = \"09\";\r\nmonth = parseInt(month, 10);\r\nyear = parseInt(year, 10);</pre>\r\n</div>\r\n&nbsp;\r\n\r\n此例中，如果你忽略了基数参数，如parseInt(year)，返回的值将是0，因为“09”被当做8进制（好比执行 parseInt( year, 8 )），而09在8进制中不是个有效数字。\r\n\r\n替换方法是将字符串转换成数字，包括：\r\n<div>\r\n<pre>+\"08\" // 结果是 8\r\nNumber(\"08\") // 8</pre>\r\n</div>\r\n&nbsp;\r\n\r\n这些通常快于parseInt()，因为parseInt()方法，顾名思意，不是简单地解析与转换。但是，如果你想输入例如“08 hello”，parseInt()将返回数字，而其它以NaN告终。\r\n<h1>编码规范(Coding Conventions)</h1>\r\n建立和遵循编码规范是很重要的，这让你的代码保持一致性，可预测，更易于阅读和理解。一个新的开发者加入这个团队可以通读规范，理解其它团队成员书写的代码，更快上手干活。\r\n\r\n许多激烈的争论发生会议上或是邮件列表上，问题往往针对某些代码规范的特定方面（例如代码缩进，是Tab制表符键还是space空格键）。如果你是 你组织中建议采用规范的，准备好面对各种反对的或是听起来不同但很强烈的观点。要记住，建立和坚定不移地遵循规范要比纠结于规范的细节重要的多。\r\n<h1>缩进(Indentation)</h1>\r\n代码没有缩进基本上就不能读了。唯一糟糕的事情就是不一致的缩进，因为它看上去像是遵循了规范，但是可能一路上伴随着混乱和惊奇。重要的是规范地使用缩进。\r\n\r\n一些开发人员更喜欢用tab制表符缩进，因为任何人都可以调整他们的编辑器以自己喜欢的空格数来显示Tab。有些人喜欢空格——通常四个，这都无所谓，只要团队每个人都遵循同一个规范就好了。这本书，例如，使用四个空格缩进，这也是JSLint中默认的缩进。\r\n\r\n什么应该缩进呢？规则很简单——花括号里面的东西。这就意味着函数体，循环 (do, while, for, for-in)，if，switch，以及对象字面量中的对象属性。下面的代码就是使用缩进的示例：\r\n<div>\r\n<pre>function outer(a, b) {\r\n    var c = 1,\r\n        d = 2,\r\n        inner;\r\n    if (a &gt; b) {\r\n        inner = function () {\r\n            return {\r\n                r: c - d\r\n            };\r\n        };\r\n    } else {\r\n        inner = function () {\r\n            return {\r\n                r: c + d\r\n            };\r\n        };\r\n    }\r\n    return inner;\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<h1>花括号{}(Curly Braces)</h1>\r\n花括号（亦称大括号，下同）应总被使用，即使在它们为可选的时候。技术上将，在in或是for中如果语句仅一条，花括号是不需要的，但是你还是应该总是使用它们，这会让代码更有持续性和易于更新。\r\n\r\n想象下你有一个只有一条语句的for循环，你可以忽略花括号，而没有解析的错误。\r\n<div>\r\n<pre>// 糟糕的实例\r\nfor (var i = 0; i &lt; 10; i += 1)\r\n   alert(i);</pre>\r\n</div>\r\n&nbsp;\r\n\r\n但是，如果，后来，主体循环部分又增加了行代码？\r\n<div>\r\n<pre>// 糟糕的实例\r\nfor (var i = 0; i &lt; 10; i += 1)\r\n   alert(i);\r\n   alert(i + \" is \" + (i % 2 ? \"odd\" : \"even\"));</pre>\r\n</div>\r\n&nbsp;\r\n\r\n第二个alert已经在循环之外，缩进可能欺骗了你。为了长远打算，最好总是使用花括号，即时值一行代码：\r\n<div>\r\n<pre>// 好的实例\r\nfor (var i = 0; i &lt; 10; i += 1) {\r\n   alert(i);\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\nif条件类似：\r\n<div>\r\n<pre>// 坏\r\nif (true)\r\n   alert(1);\r\nelse\r\n   alert(2);\r\n\r\n// 好\r\nif (true) {\r\n   alert(1);\r\n} else {\r\n   alert(2);\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<h1>左花括号的位置(Opening Brace Location)</h1>\r\n开发人员对于左大括号的位置有着不同的偏好——在同一行或是下一行。\r\n<div>\r\n<pre>if (true) {\r\n   alert(\"It''s TRUE!\");\r\n}\r\n\r\n//或\r\n\r\nif (true)\r\n{\r\n   alert(\"It''s TRUE!\");\r\n}</pre>\r\n</div>\r\n这个实例中，仁者见仁智者见智，但也有个案，括号位置不同会有不同的行为表现。这是因为分号插入机制(semicolon insertion mechanism)——JavaScript是不挑剔的，当你选择不使用分号结束一行代码时JavaScript会自己帮你补上。这种行为可能会导致麻 烦，如当你返回对象字面量，而左括号却在下一行的时候：\r\n<div>\r\n<pre>// 警告： 意外的返回值\r\nfunction func() {\r\n   return\r\n  // 下面代码不执行\r\n   {\r\n      name : \"Batman\"\r\n   }\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n如果你希望函数返回一个含有name属性的对象，你会惊讶。由于隐含分号，函数返回undefined。前面的代码等价于：\r\n<div>\r\n<pre>// 警告： 意外的返回值\r\nfunction func() {\r\n   return undefined;\r\n  // 下面代码不执行\r\n   {\r\n      name : \"Batman\"\r\n   }\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n总之，总是使用花括号，并始终把在与之前的语句放在同一行：\r\n<div>\r\n<pre>function func() {\r\n   return {\r\n      name : \"Batman\"\r\n   };\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n<blockquote>关于分号注：就像使用花括号，你应该总是使用分号，即使他们可由JavaScript解析器隐式创建。这不仅促进更科学和更严格的代码，而且有助于解决存有疑惑的地方，就如前面的例子显示。</blockquote>\r\n<h1>空格(White Space)</h1>\r\n空格的使用同样有助于改善代码的可读性和一致性。在写英文句子的时候，在逗号和句号后面会使用间隔。在JavaScript中，你可以按照同样的逻辑在列表模样表达式（相当于逗号）和结束语句（相对于完成了“想法”）后面添加间隔。\r\n\r\n适合使用空格的地方包括：\r\n<ul id=\"ul\">\r\n	<li>for循环分号分开后的的部分：如<code>for (var i = 0; i &lt; 10; i += 1) {...}</code></li>\r\n	<li>for循环中初始化的多变量(i和max)：<code>for (var i = 0, max = 10; i &lt; max; i += 1) {...}</code></li>\r\n	<li>分隔数组项的逗号的后面：<code>var a = [1, 2, 3];</code></li>\r\n	<li>对象属性逗号的后面以及分隔属性名和属性值的冒号的后面：<code>var o = {a: 1, b: 2};</code></li>\r\n	<li>限定函数参数：<code>myFunc(a, b, c)</code></li>\r\n	<li>函数声明的花括号的前面：<code>function myFunc() {}</code></li>\r\n	<li>匿名函数表达式function的后面：<code>var myFunc = function () {};</code></li>\r\n</ul>\r\n使用空格分开所有的操作符和操作对象是另一个不错的使用，这意味着在<code>+, -, *, =, &lt;, &gt;, &lt;=, &gt;=, ===, !==, &amp;&amp;, ||, +=</code>等前后都需要空格。\r\n<div>\r\n<pre>// 宽松一致的间距\r\n// 使代码更易读\r\n// 使得更加“透气”\r\nvar d = 0,\r\n    a = b + 1;\r\nif (a &amp;&amp; b &amp;&amp; c) {\r\n    d = a % c;\r\n    a += d;\r\n}\r\n\r\n// 反面例子\r\n// 缺失或间距不一\r\n// 使代码变得疑惑\r\nvar d = 0,\r\n    a = b + 1;\r\nif (a&amp;&amp;b&amp;&amp;c) {\r\n    d=a % c;\r\n    a+= d;\r\n}</pre>\r\n</div>\r\n&nbsp;\r\n\r\n最后需要注意的一个空格——花括号间距。最好使用空格：\r\n<ul id=\"ul\">\r\n	<li>函数、if-else语句、循环、对象字面量的左花括号的前面({)</li>\r\n	<li>else或while之间的右花括号(})</li>\r\n</ul>\r\n空格使用的一点不足就是增加了文件的大小，但是压缩无此问题。\r\n<blockquote>有一个经常被忽略的代码可读性方面是垂直空格的使用。你可以使用空行来分隔代码单元，就像是文学作品中使用段落分隔一样。</blockquote>\r\n<h3>命名规范(Naming Conventions)</h3>\r\n另一种方法让你的代码更具可预测性和可维护性是采用命名规范。这就意味着你需要用同一种形式给你的变量和函数命名。\r\n\r\n下面是建议的一些命名规范，你可以原样采用，也可以根据自己的喜好作调整。同样，遵循规范要比规范是什么更重要。\r\n<h3>以大写字母写构造函数(Capitalizing Constructors)</h3>\r\nJavaScript并没有类，但有new调用的构造函数：\r\n<div>\r\n<pre>var adam = new Person();</pre>\r\n</div>\r\n&nbsp;\r\n\r\n因为构造函数仍仅仅是函数，仅看函数名就可以帮助告诉你这应该是一个构造函数还是一个正常的函数。\r\n\r\n命名构造函数时首字母大写具有暗示作用，使用小写命名的函数和方法不应该使用new调用：\r\n<div>\r\n<pre>function MyConstructor() {...}\r\nfunction myFunction() {...}</pre>\r\n</div>\r\n&nbsp;\r\n<h1>分隔单词(Separating Words)</h1>\r\n当你的变量或是函数名有多个单词的时候，最好单词的分离遵循统一的规范，有一个常见的做法被称作“驼峰(Camel)命名法”，就是单词小写，每个单词的首字母大写。\r\n\r\n对于构造函数，可以使用大驼峰式命名法(upper camel case)，如<code>MyConstructor()</code>。对于函数和方法名称，你可以使用小驼峰式命名法(lower camel case)，像是<code>myFunction(), calculateArea()</code>和<code>getFirstName()</code>。\r\n\r\n要是变量不是函数呢？开发者通常使用小驼峰式命名法，但还有另外一种做法就是所有单词小写以下划线连接：例如，<code>first_name, favorite_bands, </code>和<code> old_company_name</code>，这种标记法帮你直观地区分函数和其他标识——原型和对象。\r\n\r\nECMAScript的属性和方法均使用Camel标记法，尽管多字的属性名称是罕见的（正则表达式对象的lastIndex和ignoreCase属性）。\r\n<h1>其它命名形式(Other Naming Patterns)</h1>\r\n有时，开发人员使用命名规范来弥补或替代语言特性。\r\n\r\n例如，JavaScript中没有定义常量的方法（尽管有些内置的像Number, MAX_VALUE），所以开发者都采用全部单词大写的规范来命名这个程序生命周期中都不会改变的变量，如：\r\n<div>\r\n<pre>// 珍贵常数，只可远观\r\nvar PI = 3.14,\r\n    MAX_WIDTH = 800;</pre>\r\n</div>\r\n&nbsp;\r\n\r\n还有另外一个完全大写的惯例：全局变量名字全部大写。全部大写命名全局变量可以加强减小全局变量数量的实践，同时让它们易于区分。\r\n\r\n另外一种使用规范来模拟功能的是私有成员。虽然可以在JavaScript中实现真正的私有，但是开发者发现仅仅使用一个下划线前缀来表示一个私有属性或方法会更容易些。考虑下面的例子：\r\n<div>\r\n<pre>var person = {\r\n    getName: function () {\r\n        return this._getFirst() + '' '' + this._getLast();\r\n    },\r\n\r\n    _getFirst: function () {\r\n        // ...\r\n    },\r\n    _getLast: function () {\r\n        // ...\r\n    }\r\n};</pre>\r\n</div>\r\n&nbsp;\r\n\r\n在此例中，<code>getName()</code>就表示公共方法，部分稳定的API。而<code>_getFirst()</code>和<code>_getLast()</code>则表明了私有。它们仍然是正常的公共方法，但是使用下划线前缀来警告person对象的使用者这些方法在下一个版本中时不能保证工作的，是不能直接使用的。注意，JSLint有些不鸟下划线前缀，除非你设置了noman选项为:false。\r\n\r\n下面是一些常见的_private规范：\r\n<ul id=\"ul\">\r\n	<li>使用尾下划线表示私有，如name_和getElements_()</li>\r\n	<li>使用一个下划线前缀表_protected（保护）属性，两个下划线前缀表示__private （私有）属性</li>\r\n	<li>Firefox中一些内置的变量属性不属于该语言的技术部分，使用两个前下划线和两个后下划线表示，如：__proto__和__parent__。</li>\r\n</ul>\r\n<h1>注释(Writing Comments)</h1>\r\n你必须注释你的代码，即使不会有其他人向你一样接触它。通常，当你深入研究一个问题，你会很清楚的知道这个代码是干嘛用的，但是，当你一周之后再回来看的时候，想必也要耗掉不少脑细胞去搞明白到底怎么工作的。\r\n\r\n很显然，注释不能走极端：每个单独变量或是单独一行。但是，你通常应该记录所有的函数，它们的参数和返回值，或是任何不寻常的技术和方法。要想到注 释可以给你代码未来的阅读者以诸多提示；阅读者需要的是（不要读太多的东西）仅注释和函数属性名来理解你的代码。例如，当你有五六行程序执行特定的任务， 如果你提供了一行代码目的以及为什么在这里的描述的话，阅读者就可以直接跳过这段细节。没有硬性规定注释代码比，代码的某些部分（如正则表达式）可能注释 要比代码多。\r\n<blockquote>最重要的习惯，然而也是最难遵守的，就是保持注释的及时更新，因为过时的注释比没有注释更加的误导人。</blockquote>\r\n<h1>关于作者（About the Author ）</h1>\r\nStoyan Stefanov是Yahoo!web开发人员，多个O''Reilly书籍的作者、投稿者和技术评审。他经常在会议和他的博客<a href=\"http://www.zhangxinxu.com/wordpress/2010/10/%e7%bf%bb%e8%af%91-%e9%ab%98%e8%b4%a8%e9%87%8fjavascript%e4%bb%a3%e7%a0%81%e4%b9%a6%e5%86%99%e5%9f%ba%e6%9c%ac%e8%a6%81%e7%82%b9/www.phpied.com\" target=\"_blank\">www.phpied.com</a>上发表web开发主题的演讲。Stoyan还是smush.it图片优化工具的创造者，YUI贡献者，雅虎性能优化工具YSlow 2.0的架构设计师。\r\n\r\n&nbsp;\r\n\r\n本文转自：http://www.zhangxinxu.com/wordpress/?p=1173\r\n\r\n英文原文：http://net.tutsplus.com/tutorials/javascript-ajax/the-essentials-of-writing-high-quality-javascript/\r\n<h1>同步与结束语</h1>\r\n本文已同步至目录索引：<a href=\"http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html\" target=\"_blank\">深入理解JavaScript系列</a>\r\n\r\n深入理解JavaScript系列文章，包括了原创，翻译，转载等各类型的文章，如果对你有用，请推荐支持一把，给大叔写作的动力。','深入理解JavaScript系列（1）：编写高质量JavaScript代码的基本要点','','inherit','open','open','','20-revision-2','','','2013-03-29 16:58:56','2013-03-29 08:58:56','','20','http://wap1.benbenweb.com/?p=28','0','revision','','0');");
E_D("replace into `wp_posts` values('29','1','2013-03-29 17:06:20','0000-00-00 00:00:00','','自动草稿','','auto-draft','open','open','','','','','2013-03-29 17:06:20','0000-00-00 00:00:00','','0','http://wap1.benbenweb.com/?p=29','0','post','','0');");
E_D("replace into `wp_posts` values('30','1','2013-03-29 07:55:04','2013-03-29 07:55:04','这是示范页面。页面和博客文章不同，它的位置是固定的，通常会在站点导航栏显示。很多用户都创建一个“关于”页面，向访客介绍自己。例如，个人博客通常有类似这样的介绍：\n\n<blockquote>欢迎！我白天是个邮递员，晚上就是个有抱负的演员。这是我的博客。我住在天朝的帝都，有条叫做杰克的狗。</blockquote>\n\n... 公司博客可以这样写：\n\n<blockquote>XYZ Doohickey 公司成立于 1971 年，自从建立以来，我们一直向社会贡献着优秀 doohicky。我们的公司总部位于天朝魔都，有着超过两千名员工，对魔都政府税收有着巨大贡献。</blockquote>\n\n您可以访问<a href=\"http://wap1.benbenweb.com/wp-admin/\">仪表盘</a>，删除本页面，然后添加您自己的内容。祝您使用愉快！','示例页面','','inherit','open','open','','2-revision','','','2013-03-29 07:55:04','2013-03-29 07:55:04','','2','http://wap1.benbenweb.com/?p=30','0','revision','','0');");

require("../../inc/footer.php");
?>