颜海镜
2024-03-19T06:53:19+00:00
http://yanhaijing.com
颜海镜
yanhaijing@yeah.net
《现代JavaScript库开发》一年回顾:收获与反思
2024-03-18T00:00:00+00:00
http://yanhaijing.com/web/2024/03/18/jslib-book-1y
<p>从2013年至今,我陆续创作了多个库,关于如何开发JavaScript库,我也做了一些总结和分享,借这个机会,下面做个简要回顾。</p>
<p>2015年,我发布了zepto.fullpage,一款轻量级的全屏滚动插件。</p>
<p>2016年,我发布了前端模板template.js。</p>
<p>2018年是一个里程碑,我给公司搭建了Util.js,同时开源了jslib-base v1,并撰写了《如何写一个现代的JavaScript库》一文,开始将自己的经验分享给大家。</p>
<p>2019年,我一边搭建jsmini,一边开始着手撰写<a href="https://u.jd.com/Rq2EZjH">《现代JavaScript库开发:原理、技术与实战》</a>。</p>
<p>2021年,我给公司搭建了jssdk,作为Util.js的升级版。</p>
<p>2023年1月,<a href="https://u.jd.com/Rq2EZjH">《现代JavaScript库开发:原理、技术与实战》</a>正式出版。</p>
<p>2023年10月,jslib-base 发布了v2版本,带来重要升级。</p>
<p>时光飞逝,<a href="https://u.jd.com/Rq2EZjH">《现代JavaScript库开发:原理、技术与实战》</a>已经出版一年了,本文主要内容包括,书籍影响回顾,读者问题答疑和我自己的一些总结和感悟。</p>
<h2 id="书籍影响回顾">书籍影响回顾</h2>
<p>初稿完成后,我就发给很多朋友试读,也收获了很多前端专家的推荐语,这些都记录在书籍开头了,这里不再展开,再次感谢为本书出版带来帮助的所有朋友。</p>
<p>自本书出版以来,就受到广大读者们的欢迎,下面我从三个方面梳理记录下:</p>
<h3 id="1-销量">1. 销量:</h3>
<p>自书籍面世以来,它迅速登上了京东新书榜首,并在飙升榜上一马当先。</p>
<p><img src="/blog/595.png" width="200" /></p>
<p>在当当网,它也荣登销量榜首位,显示出它在读者中的受欢迎程度。</p>
<p><img src="/blog/596.jpeg" width="200" /></p>
<p>这些成绩不仅反映了市场对本书的认可,也证明了JavaScript库开发领域的热度和重要性,当然也要非常感谢出版社运营老师,真的非常专业。</p>
<h3 id="2-奖项">2. 奖项:</h3>
<p>在个人荣誉方面,我有幸连续两年(2022年和2023年)被评为“优秀作者”,这是对我多年来在技术写作和开源贡献上不懈努力的认可。</p>
<p><img src="/blog/597.jpg" width="200" /></p>
<p>2023年,我还被授予“技术领路人”称号,这不仅是对我的个人成就的肯定,也是对本书影响力的一个证明。</p>
<p><img src="/blog/598.jpg" width="200" /></p>
<h3 id="3-评价">3. 评价:</h3>
<p>在读者评价方面,本书在豆瓣、京东以及微信读书等平台上都收获了极高的评价。读者们赞赏书中对JavaScript库开发的深入剖析和实用指导,认为它不仅适合初学者入门,也适合资深开发者深入研究。这些正面的反馈是对我的工作最好的鼓励,也激励我继续在技术领域探索和创新。</p>
<p>豆瓣评分一直稳定在8.5分上下,节选如下:</p>
<p><img src="/blog/601.png" width="500" /></p>
<p>京东好评率95%,这是读者真金白银给出的评分,节选如下:</p>
<p><img src="/blog/602.png" width="600" /></p>
<p>微信读书好评如潮,节选如下:</p>
<p><img src="/blog/603.png" width="250" /></p>
<p>本书目前也出版了繁体版,你觉得哪个设计更好看呢。</p>
<p><img src="/blog/604.png" width="400" /></p>
<h2 id="问题答疑">问题答疑</h2>
<p>一本书籍让所有人都满意,显然是不太现实的,毕竟重口难调,我认真汇总并分析了网友的意见反馈,最主要的问题是一些网友反馈指出书中所用工具的版本较为陈旧。这个问题的产生主要是因为写作过程跨越了四年的时间,而技术领域的发展日新月异。</p>
<p>对此,我想说明几点:</p>
<p>首先,书中所写的工具和技术都是我在实际开发过程中所使用和验证过的。在我的众多开源库中,都是用的这一版,它们虽然可能不是最新版本,但都是经过实战检验的成熟方案。</p>
<p>其次,书中对于工具的版本选择是有意为之。我在书中固定了版本号,目的是为了避免因工具升级带来的不一致问题。这样可以确保读者在跟随书中示例学习时,能够获得和我当初写作时相同的体验和结果。</p>
<p>最后,我也意识到技术的更新换代是不可避免的。因此,我在近期对jslib进行了升级,推出了jslib v2,其中包含了对工具和库的最新版本更新。这样一来,读者可以在学习书中的基础原理的同时,也能接触到更现代的工具和技术。</p>
<p>总之,我希望读者能够理解书中的内容是基于当时的技术环境所编写的。虽然具体的工具和版本可能会有所变化,但书中所讲述的原理和思想是恒久不变的。同时,我也会继续关注技术发展,不断更新和完善自己的工作,以便为读者提供最新的学习资源。</p>
<h2 id="我的收获">我的收获</h2>
<p>在本书的写作过程中,我收获了许多宝贵的经验和深刻的感悟。</p>
<p>首先,在写作初期,我采用了自己开发的gitbook-boilerplate,这让我能够使用熟悉的Markdown进行写作,并利用Git来管理版本。整个过程中,我提交了273个commit,这个数字见证了我对每一个细节的不懈追求。交稿后,书籍经历了三审三校的严格把关,主要借助WPS的审阅功能,期间我做了超过2万次的细节修改,这个过程虽然漫长且辛苦,但也是提升书籍质量的关键步骤。</p>
<p>在写书的过程中,有些人喜欢在一个主题下不断地添加内容,进行“加法”。而我则倾向于“减法”,即在JavaScript库开发这个主题上,我努力将内容精简到不能再减少。这种做法让书籍更加聚焦和精炼,是我在写作过程中学到的一个重要道理。</p>
<p>关于写书,最关键的是要确定一个清晰的大纲,这通常是通过对主题进行深入思考和反复推敲来实现的。一旦大纲确立,接下来的工作就是按照小节、篇章去填充内容。每一小节的写作量大致相当于撰写一篇博文,因此,坚持下去是成功的关键。如果你也有写书的梦想,欢迎联系我交流经验。</p>
<p>最后,我还学到了一个重要的道理:在写书的同时,也要兼顾好工作和家庭。写书最大的挑战之一是时间管理。我通常给自己设定一些小目标,比如每次只写300字或者一段话。你会发现,一旦开始坐下来写,成果往往会超出预期。</p>
<p>总之,写书是一段充满挑战但也极具成就感的旅程。通过这次经历,我不仅提升了自己的技术和写作能力,也收获了对生活和工作的新认识。</p>
<h2 id="未来展望">未来展望</h2>
<p>在未来的JavaScript库开发领域,我预见到几个重要的趋势和期望:</p>
<ul>
<li>
<p>构建工具Rust化:随着Web应用的复杂度不断增加,对性能的要求也越来越高,前端构建打包lint工具,都在经历Rust重写,知识目前阶段还不成熟。</p>
</li>
<li>
<p>TypeScript的普及: 随着TypeScript的普及,越来越多的JavaScript库将支持或直接使用TypeScript进行开发,以提高代码的可靠性和开发效率。</p>
</li>
<li>
<p>新技术的融合:我们将看到更多的JavaScript库整合新兴技术,如WebAssembly等,以拓展其应用范围和性能极限。</p>
</li>
</ul>
<p>针对这些趋势,我最近发布了jslib-base 2.0,旨在支持旧仓库的一键升级,并引入了一系列改进,包括:</p>
<ul>
<li>升级全部工具到最新版</li>
<li>TypeScript库接入Babel工具</li>
<li>添加Prettier、Commitlint和Husky</li>
<li>添加一个本地server</li>
<li>支持Node exports condition</li>
<li>迁移Travis到GitHub Action</li>
</ul>
<p>按照本书内容新建的库,可以支持一键升级,也算解决了网友反馈的书中工具版本陈旧问题。</p>
<p>我相信,通过这些更新和改进,jslib-base 2.0将为JavaScript库开发提供更强大、更灵活的基础设施,帮助开发者更高效地构建和维护他们的项目。</p>
<h2 id="结语">结语</h2>
<p>在过去的五年里,我倾注了无数的心血和热情于<a href="https://u.jd.com/Rq2EZjH">《现代JavaScript库开发:原理、技术与实战》</a>这本书的打磨和关注中。四年的写作,加上一年的出版后维护,每一步都凝聚了我的努力和对技术的热爱。这本书的每一个字、每一个例子,都是我对JavaScript库开发领域的深刻理解和实践经验的体现。</p>
<p>我非常感激每一位读者的支持和鼓励,是你们的肯定让这本书能够发光发热,也是你们的反馈和建议让我不断进步。这篇文章,我献给所有支持我的人,也献给我自己,作为这五年旅程的一个总结和纪念。</p>
<p>现在,是时候展望未来了。JavaScript库开发领域仍然充满了挑战和机遇,我期待着能够继续在这个领域探索和前进。如果有机会,我愿意撰写这本书的第二版,以分享更多的经验、技术和思考。</p>
<p>再次感谢大家的支持,让我们一起期待未来的可能性!</p>
<h2 id="资源汇总">资源汇总</h2>
<ul>
<li><a href="https://github.com/yanhaijing/zepto.fullpage">zepto.fullpage</a>,专注于移动端的fullPage.js。</li>
<li><a href="https://github.com/yanhaijing/template.js">template.js</a>,一款 javascript 模板引擎,简单,好用。</li>
<li><a href="https://github.com/yanhaijing/jslib-base/">jslib-base</a>,最好用的 JS|TS 第三方库脚手架,10 秒快速搭建一个新库的基础框架。</li>
<li><a href="https://github.com/jsmini">jsmini</a>,Simple and beautifu library for JavaScript.</li>
<li><a href="https://github.com/yanhaijing/gitbook-boilerplate">gitbook-boilerplate</a>,一个基于gitbook快速写电子书的模版。</li>
</ul>
《现代JavaScript库开发:原理、技术与实战》克军推荐序
2023-01-01T00:00:00+00:00
http://yanhaijing.com/web/2023/01/01/jslib-book-preface3
<p>2023年1月,我和侯策老师共同打磨的书籍《现代JavaScript库开发:原理、技术与实战》终于正式出版了!</p>
<p><img src="/blog/593.jpeg" width="200" /></p>
<p>在书籍定稿之际,我把底稿寄给了克军老师,感谢克军老师在百忙之中抽时间阅读了底稿,并为我写了推荐序,一下是推荐序的内容。</p>
<h2 id="克军老师推荐序">克军老师推荐序</h2>
<p>我们普遍觉得,在团队里负责开发和维护基础库的工程师都是“高手”。毕竟,能位于团队上游的人总会有种莫名的“优越感”。</p>
<p>编写一个JavaScript库很难吗?不就是先把一段通用的代码抽离出来,再按照某种范式封装一下嘛!其实,要想真正回答这个问题,你可能需要先想想以下问题:</p>
<ul>
<li>为什么有些人写的库大受欢迎,而有些人写的库却没人使用?</li>
<li>你为什么愿意使用某个库,你到底看重它什么?</li>
<li>流行的库有什么共同点?</li>
<li>所谓写“好”一个库,到底要符合什么条件?</li>
<li>你有过“踩坑”经历吗,当时是什么心情?</li>
<li>别人为什么愿意为你的项目贡献代码?</li>
<li>怎么让自己写的库日后不成为“债”?</li>
</ul>
<p>如果你只是在自己的项目中抽离一些可复用的代码并将其封装成一个库,这个库可能只适用于比较单一的应用场景。但如果你希望更多的人也能用到这个库,那就要好好设计一番了。</p>
<p>你要考虑稳定性、可维护性、安全性,编写一些攻击性测试用例,还要注重代码的可读性、易理解性。如果想扩大影响力,希望更多人参与项目维护,你必须重视库的架构设计、接囗设计、文档撰写、注释情况、代码风格等。不仅如此,你所用的工具也必须是当前最主流、最酷的。你要为库的使用者提供开发、调试、测试、构建和提交等多方面的顺滑体验。如果你能把上述一切都做得很到位,那么别人一定能从中学到很多东西,也就愿意为你的项目贡献代码了。团队内部的技术共建也是类似的,并非为了彰显什么,而是为了技术交流和价值共创。</p>
<p>近些年,我看到越来越多的国人投身开源社区,成为一些知名项目的维护者和贡献者,也产生了一批优秀的国产开源项目。我相信未来的前端领域中会涌现出更多像Vue.js、Ant Design这样具有国际影响力的库和框架。</p>
<p>通过代码与工程师交流能加速自身成长,进而创造个人价值。作为一名开发者,不能只是开源库的使用者,要成为贡献者,甚至创造者。</p>
<p>《现代JavaScript库开发:原理、技术与实战》这本书将会影响一些人,使他们从开源库的使用者变成创造者。这本书构建了一条栈道,沿着它走下去,你会走进一个新世界。它也能启发另一批有经验的人,进一步完备自己的知识体系。书中涉及的开发工具未来也许会过期,但其中的开发思路、工程化的专业做法永远不会过时。书中介绍的工具和技术也都是当前最主流的,能成为主流说明具有一定的先进性,如果你能透过工具表面的用法进一步去追究其背后的哲学,你将会有更多的收获。</p>
<p>本书的实操性很强,边阅读边动手写代码,你会有更深的体会。市面上比较成熟的工具和库都是经过长期打磨形成的,其中很多设计细节只有在使用时才能感受到。当你自己开发一个库时,这些都是你灵感的源泉。</p>
<p>前面也提到,你编写一个库是希望更多人能用到它,并非标榜自己。就像做产品要考虑用户体验一样,库的作者要时刻考虑使用者的体验,要时刻提醒自己站在使用者的角度进行设计。所有恰到好处的设计都是打磨出来的,也是独具匠心的。一个库其实也是一个技术产品,如果你能够做好它,其价值将远远超越解决问题本身。愿大家能从这本书中获得设计和开发JavaScript库的价值。</p>
<div style="text-align: right">
——蚂蚁集团OceanBase部门体验技术团队负责人
<br />
克军
</div>
<h2 id="总结">总结</h2>
<p>本书主要涵盖三部分内容,可以满足读者不同阶段的学习诉求。</p>
<ul>
<li><strong>第1~5章</strong> 介绍如何开发和开源一个现代JavaScript库,这部分内容可以帮助读者快速达成库开发目标。</li>
<li><strong>第6~7章</strong> 介绍现代JavaScript库的设计最佳实践和安全最佳实践,这部分内容可以极大提高读者开发JavaScript库的质量。</li>
<li><strong>第8~11章</strong> 为实战部分,本书精选了9个典型库作为案例,带领读者了解不同类型的JavaScript库的开发要点。</li>
</ul>
<p><img src="/blog/594.png" /></p>
<p>如今,本书已全面上线,如果你也想开发属于自己的JavaScript库,提升开发技能,精进自身开发技术,一定不可以错过本书哦~~</p>
<p>有兴趣的读者可以点击下面的链接购买,再次感谢各位的支持与鼓励!恳请各位批评指正!</p>
<p>我还给大家准备了签名版,免费赠送直播课和周边,想要签名版的可以通过微信公众号联系我:<strong>颜海镜</strong></p>
<p>京东:<a href="https://u.jd.com/Subw5HP" target="_blank">https://u.jd.com/Subw5HP</a></p>
<p>当当:<a href="http://product.dangdang.com/29494845.html" target="_blank">http://product.dangdang.com/29494845.html</a></p>
《现代JavaScript库开发:原理、技术与实战》狼叔推荐序
2023-01-01T00:00:00+00:00
http://yanhaijing.com/web/2023/01/01/jslib-book-preface2
<p>2023年1月,我和侯策老师共同打磨的书籍《现代JavaScript库开发:原理、技术与实战》终于正式出版了!</p>
<p><img src="/blog/593.jpeg" width="200" /></p>
<p>在书籍定稿之际,我把底稿寄给了狼叔,感谢狼叔在百忙之中抽时间阅读了底稿,并为我写了推荐序,一下是推荐序的内容。</p>
<h2 id="狼叔推荐序">狼叔推荐序</h2>
<p>我和海镜、侯策认识很久了,他们之前写的那本《React状态管理与同构实战》是新手入门React的好书,我非常喜欢。</p>
<p>海镜不仅是大厂工程师、技术博主,还是开源爱好者。他开源了很多JavaScript库,如zepto.fullpage、template.js等。他搞的jsmini可圈可点,尤其难能可贵的是,他还编写了jslib-base——一个可以帮助开发者编写JavaScript库的工具库,这个库的特性涵盖了库开发的各个方面,非常实用。</p>
<p>我对海镜很熟悉,对他做的事也比较熟悉,所以当我得知他正在写这本书的时候,我是非常开心且放心的。开心是因为,目前前端领域和Node.js领域都缺少这样的专精内容,我在《狼书》里是写过如何开发JavaScript库的,但限于篇幅未能深入介绍,这本书弥补了我的遗憾。放心是因为,他一直是一线的、热爱开源的前端专家,无论是能力、眼界、判断力还是协作能力,都非常不错,鉴于他之前所写的那本《React状态管理与同构实战》的情况,我相信他能够将JavaScript库开发技术讲清楚。</p>
<p>事实上,本书的初稿也确实和我想的一样,章节分布清楚,内容详略得当,基本覆盖了所有读者想要看到的知识点,甚至还有扩展。</p>
<p>很多人在学习编写代码时都很迷茫,对此,我给的建议是:每天看10个npm模块(JavaScript库)。对于学习大前端(含Node.js)相关技术时感到迷茫的人来说,学习JavaScript库是消除迷茫的最好方式。当你不知道如何做时,可以通过学习JavaScript库积累对以后实际开发有益处的技能。与其不知道学什么,不如先通过学习JavaScript库每天积累几个技巧。只要坚持每天积累几个库开发技巧,并累计学习一万小时,你的个人编程能力一定会有质的飞跃。</p>
<p>当你掌握了很多开发技巧后,就会慢慢地想要自己去实现JavaScript库,这是一个创造的过程,也是一个自我实现的过程,这个过程非常容易带给人成就感。你编写的JavaScript库,可能是React这样的大框架或Vite这样的大型构建工具,也可能是is-number、debug这样的小模块。对于个人成长来说,无论模块大小,都能使人进步。当然,如果你编写的JavaScript库能够获得更多开发者和使用者的认可,那将是更值得开心的事。</p>
<p>以上就是我对开发和开源JavaScript库的简单理解,其实,我个人也是这样一步一步走过来的。</p>
<p>海镜和侯策写的这本书从多个维度介绍了JavaScript库开发和开源的技巧及注意事项,并列举了几个非常典型的库辅以实战,内容非常实用。希望大家能够通过这本书掌握更多的JavaScript库开发技巧,并通过刻意练习自我提高,成为自己想成为的人——技术大牛!</p>
<div style="text-align: right">
——Node.js布道者、《狼书》系列图书作者
<br />
桑世龙(狼叔)
</div>
<h2 id="总结">总结</h2>
<p>本书主要涵盖三部分内容,可以满足读者不同阶段的学习诉求。</p>
<ul>
<li><strong>第1~5章</strong> 介绍如何开发和开源一个现代JavaScript库,这部分内容可以帮助读者快速达成库开发目标。</li>
<li><strong>第6~7章</strong> 介绍现代JavaScript库的设计最佳实践和安全最佳实践,这部分内容可以极大提高读者开发JavaScript库的质量。</li>
<li><strong>第8~11章</strong> 为实战部分,本书精选了9个典型库作为案例,带领读者了解不同类型的JavaScript库的开发要点。</li>
</ul>
<p><img src="/blog/594.png" /></p>
<p>如今,本书已全面上线,如果你也想开发属于自己的JavaScript库,提升开发技能,精进自身开发技术,一定不可以错过本书哦~~</p>
<p>有兴趣的读者可以点击下面的链接购买,再次感谢各位的支持与鼓励!恳请各位批评指正!</p>
<p>我还给大家准备了签名版,免费赠送直播课和周边,想要签名版的可以通过微信公众号联系我:<strong>颜海镜</strong></p>
<p>京东:<a href="https://u.jd.com/Subw5HP" target="_blank">https://u.jd.com/Subw5HP</a></p>
<p>当当:<a href="http://product.dangdang.com/29494845.html" target="_blank">http://product.dangdang.com/29494845.html</a></p>
十年磨一剑(我的新书自序)
2023-01-01T00:00:00+00:00
http://yanhaijing.com/web/2023/01/01/jslib-book-preface
<p>2023年1月,我和侯策老师共同打磨的书籍《现代JavaScript库开发:原理、技术与实战》终于正式出版了!</p>
<p><img src="/blog/593.jpeg" width="200" /></p>
<p>在书籍定稿之际,有太多话想和大家说一下,思绪万千,不知从何说起。最终决定写一篇自序,记录一下这本书的由来,以及我在写作过程中的一些感悟。</p>
<h2 id="十年磨一剑">十年磨一剑</h2>
<p>十年,弹指一挥间。</p>
<p>回首过去十年,我一直致力于开源库的开发和维护,一路走来,我也从这个领域的“小白”慢慢成长为“专家”。这十年,支撑我坚持在库开发领域耕耘的原因是热爱分享,我特别希望能把自己做的东西分享给别人,分享的内容既可以是课程、博客文章,也可以是代码。在我看来,一份分享出去的代码片段,就是一个开源库。</p>
<p>十年来,前端技术推陈出新,新的开源库如雨后春笋般涌现,相信大部分读者都曾从这些开源库中受益。平日里,我们更多关注的是库的使用,很少关注库开发技术。其实,JavaScript库开发技术在这十年中也经历了快速发展,其中基于新的技术标准开发而成的库,我将其称为“现代JavaScript库”。</p>
<p>由于前端技术发展迅速,如今开发一个现代JavaScript库并不容易,其中涉及非常多的知识、工具和经验。比如,库如何兼容日益复杂的前端环境,库如何使用打包工具,库的单元测试如何做,等等。正因为这种复杂性,目前npm上的开源库并不都是现代JavaScript库,很多开源库还在使用十几年前的相对比较原始的技术。</p>
<p>除了依赖开发技术,将一个库开源还需要很多准备工作。一个库开源后的运营和维护也涉及很多知识。由于缺乏经验,很多库开源后并没有被推广开来。</p>
<p>总之,开发和开源一个现代JavaScript库并非易事,上述困难阻碍了很多读者开发自己的JavaScript库,我也曾被这些困难深深折磨过。经过十年的摸爬滚打,我不禁想:如果能有一个师傅手把手教我该多好,那我当初能少走多少弯路!基于此,我终于下定决心写一本现代JavaScript库开发领域的图书,将自己十年的经验总结沉淀,希望能够手把手教各位读者快速掌握现代JavaScript库开发技术。</p>
<h2 id="人人都可以开发自己的javascript库">人人都可以开发自己的JavaScript库</h2>
<p>有人可能会问,为什么要学习JavaScript库开发技术呢?学会开发JavaScript库有什么好处呢?其实,开发JavaScript库能够带来非常多的好处。</p>
<p>我现身说法,开发和开源库不仅可以帮助他人解决问题,也能给自己带来很多成长。开发库的特殊要求,极大提升了我的技术深度;开发库涉及的技术非常多,极大拓宽了我的知识面;开源库使我融入了开源社区,在那里获得了很多技术之外的东西。总之,开发和开源现代JavaScript库可以带来非常大的收获,我希望每一个前端开发者都不要错过这个机会。</p>
<p>其实,我有一个愿望,那就是,人人都可以开发自己的JavaScript库。</p>
<p>再小的个体也应该有机会在社区中发声,社区不应该只要月亮的光辉,漫天繁星同样是美好世界的重要组成,只要我们愿意,每个人都可以开发属于自己的JavaScript库。</p>
<p>每一个前端开发者都身处两个世界,即业务世界和开源世界。大部分人熟悉业务世界,但对开源世界了解不多。所谓“技多不压身”,多了解开源世界,融入开源世界,你一定会有更多收获。</p>
<h2 id="总结">总结</h2>
<p>本书主要涵盖三部分内容,可以满足读者不同阶段的学习诉求。</p>
<ul>
<li><strong>第1~5章</strong> 介绍如何开发和开源一个现代JavaScript库,这部分内容可以帮助读者快速达成库开发目标。</li>
<li><strong>第6~7章</strong> 介绍现代JavaScript库的设计最佳实践和安全最佳实践,这部分内容可以极大提高读者开发JavaScript库的质量。</li>
<li><strong>第8~11章</strong> 为实战部分,本书精选了9个典型库作为案例,带领读者了解不同类型的JavaScript库的开发要点。</li>
</ul>
<p><img src="/blog/594.png" /></p>
<p>如今,本书已全面上线,如果你也想开发属于自己的JavaScript库,提升开发技能,精进自身开发技术,一定不可以错过本书哦~~</p>
<p>有兴趣的读者可以点击下面的链接购买,再次感谢各位的支持与鼓励!恳请各位批评指正!</p>
<p>我还给大家准备了签名版,免费赠送直播课和周边,想要签名版的可以通过微信公众号联系我:<strong>颜海镜</strong></p>
<p>京东:<a href="https://u.jd.com/Subw5HP" target="_blank">https://u.jd.com/Subw5HP</a></p>
<p>当当:<a href="http://product.dangdang.com/29494845.html" target="_blank">http://product.dangdang.com/29494845.html</a></p>
如何在JavaScript中判断两个值相等
2022-07-10T00:00:00+00:00
http://yanhaijing.com/javascript/2022/07/10/js-equal
<blockquote>
<p>本文已收录到《<a href="https://yanhaijing.com/tags/#%E9%9D%A2%E8%AF%95%E7%9F%A5%E8%AF%86-ref">面试知识系列</a>》</p>
</blockquote>
<p>在 JavaScript 中如何判断两个值相等,这个问题看起来非常简单,但并非如此,在 JavaScript 中存在 4 种不同的相等逻辑,如果你不知道他们的区别,或者认为判断相等非常简单,那么本文非常适合你阅读。</p>
<p>ECMAScript 是 JavaScript 的语言规范,在<a href="https://262.ecma-international.org/8.0/#sec-samevalue">ECMAScript 规范</a>中存在四种相等算法,如下图所示:</p>
<p><img src="/blog/590.png" alt="" /></p>
<p>上图中四种算法对应的中文名字如下,大部分前端应该熟悉严格相等和非严格相等,但对于同值零和同值却不熟悉,下面我们分别介绍这四种算法。</p>
<ul>
<li>同值</li>
<li>同值零</li>
<li>非严格相等</li>
<li>严格相等</li>
</ul>
<h3 id="非严格相等">非严格相等</h3>
<p>非严格相等使用两个等号,也就是我们熟悉的双等,非严格相等表示语义相等,不要求类型一样,非严格相等在比较前会先将比较参数类型转换为一致,再进行比较,代码示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1</span> <span class="o">==</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// true</span>
<span class="mi">1</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">1</span><span class="dl">'</span><span class="p">;</span> <span class="c1">// true 类型不同,不影响比较结果</span>
</code></pre></div></div>
<p>非严格相等有非常复杂的转换规则,非常难以记忆,社区中<a href="https://dorey.github.io/JavaScript-Equality-Table/">有人</a>将上面的规则总结成了图片,一图胜千言,如下图所示:</p>
<p><img src="/blog/591.png" alt="" /></p>
<p>为了方便记住非严格相等的的转换逻辑,作者将非对象值,可以总结为如下三条规则:</p>
<ul>
<li>Undefined 只和 Null 相等</li>
<li>和 Number 比较时,另一个值会自动转换为 Number</li>
<li>和 Boolean 比较时,另一个值会转换为 Number</li>
</ul>
<p>如果值为对象,会使用内部的 ToPrimitive 转换,可以通过自定义 Symbol.toPrimitive 改变返回值,需要注意的是在相等的判断中 Symbol.toPrimitive 接受的 hint 参数都是 default。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{</span>
<span class="p">[</span><span class="nb">Symbol</span><span class="p">.</span><span class="nx">toPrimitive</span><span class="p">](</span><span class="nx">hint</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">hint</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">hint</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">number</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">hint</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="dl">'</span><span class="s1">yan</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">},</span>
<span class="p">};</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span> <span class="o">==</span> <span class="mi">1</span><span class="p">);</span> <span class="c1">// obj 返回 true</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">1</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// obj 返回 true</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">obj</span> <span class="o">==</span> <span class="kc">true</span><span class="p">);</span> <span class="c1">// obj 返回 true</span>
</code></pre></div></div>
<p>非严格相等并非带来了很多便利,通过隐式的自动转换,简化了部分场景的工作,比如 Number 和 String 的自动转换,简化了前端从表单,url 参数中获取值的比较问题,但自动转换带来的问题比便利还多。</p>
<p>隐式转换的规则,大部分情况下难以驾驭,现在主流的观点已经不建议使用,作者建议只在判断 undefined 和 null 的场景下可以使用非严格相等。</p>
<h2 id="严格相等">严格相等</h2>
<p>严格相等是另一种比较算法,其和非严格想等的区别是不会进行类型转换,类型不一致时直接返回 false,严格相等对应===操作符,因为使用三个等号,也被称作三等或者全等,严格相等示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1</span> <span class="o">===</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// true</span>
<span class="mi">1</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">1</span><span class="dl">'</span><span class="p">;</span> <span class="c1">// false 类型不同,影响比较结果</span>
</code></pre></div></div>
<p>不同类型值判断规则如下,和前面的非严格相等对比,严格相等更符合直觉。</p>
<p><img src="/blog/592.png" alt="" /></p>
<p>严格相等解决了非严格相等中隐式转换带来的问题,但也丢失了隐式转换带来的便利,对于类型可能不一致的情况下,比如从表单中获取的值都是字符串,保险的做法是,在比较前手动类型转换,代码示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1</span> <span class="o">===</span> <span class="nb">Number</span><span class="p">(</span><span class="dl">'</span><span class="s1">1</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// true 手动类型转换,类型防御</span>
</code></pre></div></div>
<p>严格相等几乎总是正确的,但也有例外情况,比如 NaN 和正负 0 的问题。</p>
<p>Number 类型有个特殊的值 NaN,用来表示计算错误的情概况,比较常见是非 Number 类型和 Number 类型计算时,会得到 NaN 值,代码示例如下所示,这是从表单和接口请求获取数据时很容易出现的问题。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">0</span> <span class="o">/</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// NaN</span>
<span class="kd">const</span> <span class="nx">b</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">a</span><span class="dl">'</span> <span class="o">/</span> <span class="mi">1</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">c</span> <span class="o">=</span> <span class="kc">undefined</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">// NaN</span>
</code></pre></div></div>
<p>在严格相等中,NaN 是不等于自己的,NaN 是(x !== x) 成立的唯一情况,在某些场景下其实是希望能够判断 NaN 的,可以使用 isNaN 进行判断,ECMAScript 2015 引入了新的 Number.isNaN,和 isNaN 的区别是不会对传入的参数做类型转换,建议使用语义更清晰的 Number.isNaN,但是要注意兼容性问题,判断 NaN 代码示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kc">NaN</span> <span class="o">===</span> <span class="kc">NaN</span><span class="p">;</span> <span class="c1">// false</span>
<span class="nb">isNaN</span><span class="p">(</span><span class="kc">NaN</span><span class="p">);</span> <span class="c1">// true</span>
<span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="kc">NaN</span><span class="p">);</span> <span class="c1">// true</span>
<span class="nb">isNaN</span><span class="p">(</span><span class="dl">'</span><span class="s1">aaa</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// true 自动转换类型 'aaa'转换为Number为NaN</span>
<span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span><span class="p">(</span><span class="dl">'</span><span class="s1">aaa</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// false 不进行转换,类型不为Number,直接返回false</span>
</code></pre></div></div>
<p>严格相等另一个例外情况是,无法区分+0 和-0,代码示例如下,在一些数学计算场景中是要区分语义的。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">+</span><span class="mi">0</span> <span class="o">===</span> <span class="o">-</span><span class="mi">0</span><span class="p">;</span> <span class="c1">// true</span>
</code></pre></div></div>
<p>JavaScript 中很多系统函数都使用严格相等,比如数组的 indexOf,lastIndexOf 和 switch-case 等,需要注意,这些对于 NaN 无法返回正确结果,代码示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="kc">NaN</span><span class="p">].</span><span class="nx">indexOf</span><span class="p">(</span><span class="kc">NaN</span><span class="p">);</span> <span class="c1">// -1 数组中其实存在NaN</span>
<span class="p">[</span><span class="kc">NaN</span><span class="p">].</span><span class="nx">lastIndexOf</span><span class="p">(</span><span class="kc">NaN</span><span class="p">);</span> <span class="c1">// -1</span>
</code></pre></div></div>
<h2 id="同值零">同值零</h2>
<p>同值零是另一种相等算法,名字来源于规范的直译,规范中叫做 SameValueZero,同值零和严格相等功能一样,除了处理 NaN 的方式,同值零认为 NaN 和 NaN 相等,这在判断 NaN 是否在集合中的语义下是非常合理的。</p>
<p>ECMAScript 2016 引入的 includes 使用此算法,此外 Map 的键去重和 Set 的值去重,使用此算法,代码示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="kc">NaN</span><span class="p">].</span><span class="nx">incdudes</span><span class="p">(</span><span class="kc">NaN</span><span class="p">);</span> <span class="c1">// true 注意和indexOf的区别,incdudes的语义更合理</span>
<span class="k">new</span> <span class="nb">Set</span><span class="p">([</span><span class="kc">NaN</span><span class="p">,</span> <span class="kc">NaN</span><span class="p">]);</span> <span class="c1">// [NaN] set中只会有个一个NaN,如果 NaN !== NaN的话,应该是[NaN, NaN]</span>
<span class="k">new</span> <span class="nb">Map</span><span class="p">([</span>
<span class="p">[</span><span class="kc">NaN</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span>
<span class="p">[</span><span class="kc">NaN</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span>
<span class="p">]);</span> <span class="c1">// {NaN => 2} 如果 NaN !== NaN的话,应该是 {NaN => 1, NaN => 2}</span>
</code></pre></div></div>
<h2 id="同值">同值</h2>
<p>同值是最后一种相等算法,其和同值零类似,但认为 +0 不等于 -0,ECMAScript 2015 带来的 Object.is 使用同值算法,代码示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">Object</span><span class="p">.</span><span class="nx">is</span><span class="p">(</span><span class="kc">NaN</span><span class="p">,</span> <span class="kc">NaN</span><span class="p">);</span> <span class="c1">// true</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">is</span><span class="p">(</span><span class="o">+</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">0</span><span class="p">);</span> <span class="c1">// false 📢 注意这里</span>
</code></pre></div></div>
<p>同值算法的使用场景是,确定两个值是否在任何情况下功能上是相同的,比较不常用,defineProperty 使用此算法确认键是否存在,例如,将存在的只读属性值-0 修改为+0 时会报错,如果设置为同样的-0 将执行正常,代码示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">test</span><span class="p">()</span> <span class="p">{</span>
<span class="dl">'</span><span class="s1">use strict</span><span class="dl">'</span><span class="p">;</span> <span class="c1">// 需要开启严格模式</span>
<span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="dl">'</span><span class="s1">a1</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
<span class="na">value</span><span class="p">:</span> <span class="o">-</span><span class="mi">0</span><span class="p">,</span>
<span class="na">writable</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">configurable</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="na">enumerable</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="p">});</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="dl">'</span><span class="s1">a1</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
<span class="na">value</span><span class="p">:</span> <span class="o">-</span><span class="mi">0</span><span class="p">,</span>
<span class="p">});</span> <span class="c1">// 正常执行</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="dl">'</span><span class="s1">a1</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
<span class="na">value</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
<span class="p">});</span> <span class="c1">// Uncaught TypeError: Cannot redefine property: a1</span>
<span class="p">}</span>
<span class="nx">test</span><span class="p">();</span>
</code></pre></div></div>
<p>对于数组判断是否存在的场景,如果想区分+0 和-0,可以使用 ECMAScript 2015 引入的 find 方法,自行控制判断逻辑,代码示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">includes</span><span class="p">(</span><span class="o">-</span><span class="mi">0</span><span class="p">);</span> <span class="c1">// 不能区分-0</span>
<span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">find</span><span class="p">((</span><span class="nx">val</span><span class="p">)</span> <span class="o">=></span> <span class="nb">Object</span><span class="p">.</span><span class="nx">is</span><span class="p">(</span><span class="nx">val</span><span class="p">,</span> <span class="o">-</span><span class="mi">0</span><span class="p">));</span> <span class="c1">// 能区分+0和-0</span>
</code></pre></div></div>
<h2 id="对比四种算法">对比四种算法</h2>
<p>下面对比下四种算法的区别,区别如下表所示:</p>
<table>
<thead>
<tr>
<th> </th>
<th>隐式转换</th>
<th>NaN 和 NaN</th>
<th>+0 和 -0</th>
</tr>
</thead>
<tbody>
<tr>
<td>非严格相等(==)</td>
<td>是</td>
<td>false</td>
<td>true</td>
</tr>
<tr>
<td>严格相等(===)</td>
<td>否</td>
<td>false</td>
<td>true</td>
</tr>
<tr>
<td>同值零(includes 等)</td>
<td>否</td>
<td>true</td>
<td>true</td>
</tr>
<tr>
<td>同值(Object.is)</td>
<td>否</td>
<td>true</td>
<td>false</td>
</tr>
</tbody>
</table>
<h2 id="number-类型的坑">Number 类型的坑</h2>
<p>Number 类型真的有太多坑了,除了上面提到的 NaN 和正负零的问题,还存在其他语言都存在的小数问题,小数问题是前端比较容易踩坑的地方,如果想对比两个小数是否相同,可能会违反直觉,比如 0.1+0.2 并不和 0.3 全等,代码示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">a</span> <span class="o">=</span> <span class="mf">0.1</span> <span class="o">+</span> <span class="mf">0.2</span><span class="p">;</span> <span class="c1">// 0.30000000000000004</span>
<span class="nx">a</span> <span class="o">===</span> <span class="mf">0.3</span><span class="p">;</span> <span class="c1">// false</span>
</code></pre></div></div>
<p>如果要理解上面的原因,需要知道 JavaScript 是如何存储小数的,我之前曾经写个两篇文章,专门介绍这个问题:</p>
<ul>
<li><a href="https://yanhaijing.com/javascript/2016/07/20/binary-in-js/">聊聊 JavaScript 中的二进制数</a></li>
<li><a href="https://yanhaijing.com/javascript/2014/03/14/what-every-javascript-developer-should-know-about-floating-points/">每一个 JavaScript 开发者应该了解的浮点知识</a></li>
</ul>
<p>简单来说 JavaScript 使用 IEEE-754 规范存储浮点数,这意味着每个浮点数占 64 位,具体含义如下图所示:</p>
<p><img src="/blog/75.png" alt="" /></p>
<p>因此 JavaScript 中的最小数字 2-52,对应的十进制约等于 2.2204460492503130808472633361816E-16,这个数字比较难以记忆,ECMAScript 2015 引入了 Number.EPSILON 常量表示这个数字,使用方法如下</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nb">Number</span><span class="p">.</span><span class="nx">EPSILON</span><span class="p">);</span> <span class="c1">// 2.220446049250313e-16</span>
</code></pre></div></div>
<p>对于小数的比较,一般都是让两个数字做减法,如果其差值,小于 Number.EPSILON,就认为其相等,代码示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="mf">0.1</span> <span class="o">+</span> <span class="mf">0.2</span><span class="p">;</span> <span class="c1">// 0.30000000000000004</span>
<span class="nx">a</span> <span class="o">-</span> <span class="mf">0.3</span> <span class="o"><</span> <span class="nb">Number</span><span class="p">.</span><span class="nx">EPSILON</span><span class="p">;</span> <span class="c1">// true 可认为 a === 0.3</span>
</code></pre></div></div>
<h2 id="结构相等">结构相等</h2>
<p>前面介绍了各种判断相等的办法,都只能用于基本类型,如果有两个内容一样的对象,使用上面的方法都会返回 false,在 JavaScript 中缺少判断两个引用类型结构相等的功能,比如如下的 a1 和 a2,并不相等:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">a1</span> <span class="o">=</span> <span class="p">{</span> <span class="na">a</span><span class="p">:</span> <span class="mi">1</span> <span class="p">};</span>
<span class="kd">const</span> <span class="nx">a2</span> <span class="o">=</span> <span class="p">{</span> <span class="na">a</span><span class="p">:</span> <span class="mi">1</span> <span class="p">};</span>
<span class="nx">a1</span> <span class="o">==</span> <span class="nx">a2</span><span class="p">;</span> <span class="c1">// false</span>
<span class="nx">a1</span> <span class="o">===</span> <span class="nx">a2</span><span class="p">;</span> <span class="c1">// false</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">is</span><span class="p">(</span><span class="nx">a1</span><span class="p">,</span> <span class="nx">a2</span><span class="p">);</span> <span class="c1">// false</span>
</code></pre></div></div>
<p>通过将对象序列化,可以将结构相等,转换为字符串相等,在 JavaScript 中序列化需要用到 JSON.stringify,使用 JSON.stringify 判断结构相等的示例代码如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">a1</span> <span class="o">=</span> <span class="p">{</span> <span class="na">a</span><span class="p">:</span> <span class="mi">1</span> <span class="p">};</span>
<span class="kd">const</span> <span class="nx">a2</span> <span class="o">=</span> <span class="p">{</span> <span class="na">a</span><span class="p">:</span> <span class="mi">1</span> <span class="p">};</span>
<span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">a1</span><span class="p">)</span> <span class="o">===</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">a2</span><span class="p">);</span> <span class="c1">// true</span>
</code></pre></div></div>
<p>大部分同学可能就是使用的,但其实这种方法是有缺陷的,比如某些值,在序列化后会丢失,从而导致判断逻辑错误,比如下面的值都会有问题:</p>
<ul>
<li>NaN 序列化后和 null 无法区分;</li>
<li>+0 和-0 在序列化后也无法区分;</li>
<li>溢出的数字和 null 无法区分;</li>
</ul>
<p>比如如下两个对象,结构并不相等,但序列化后值是一样的:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">a1</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">a</span><span class="p">:</span> <span class="kc">NaN</span><span class="p">,</span>
<span class="p">};</span>
<span class="kd">const</span> <span class="nx">a2</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">a</span><span class="p">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="p">};</span>
<span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">a1</span><span class="p">);</span> <span class="c1">// '{"a":null}'</span>
<span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">a2</span><span class="p">);</span> <span class="c1">// '{"a":null}'</span>
</code></pre></div></div>
<p>此外,还有一些值不能被序列化,比如 undefined 和 symbol,序列化后就丢失了,代码示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">a</span><span class="p">:</span> <span class="kc">undefined</span><span class="p">,</span>
<span class="na">b</span><span class="p">:</span> <span class="nb">Symbol</span><span class="p">(</span><span class="dl">''</span><span class="p">),</span>
<span class="p">};</span>
<span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span> <span class="c1">// '{}' 值丢失了</span>
</code></pre></div></div>
<p>由于 JSON.stringify 的方法走不通,另一种思路是自己写代码判断结构相等,其原理是依次遍历递归比较两个树是否相等,也可以使用社区中别人写好的库,比如<a href="https://github.com/jsmini/isequal">isequal</a>。</p>
<p>鉴于篇幅,本文不在给出递归判断结构相等的代码,在下一篇文章中,给大家带来 isequal 的源码分析。</p>
<p>最后,欢迎大家阅读本文,如果对本文有任何疑问,欢迎在评论区交流。</p>
如何回答面试中的JavaScript获取变量类型问题
2022-07-09T00:00:00+00:00
http://yanhaijing.com/javascript/2022/07/09/js-type
<blockquote>
<p>本文已收录到《<a href="https://yanhaijing.com/tags/#%E9%9D%A2%E8%AF%95%E7%9F%A5%E8%AF%86-ref">面试知识系列</a>》</p>
</blockquote>
<p><strong>划重点,这是一道面试必考题,我就问过很多面试者这个问题,✧(≖ ◡ ≖✿)嘿嘿</strong></p>
<p>JavaScript 是一个动态类型语言,在运行时获取变量类型是常用操作,由于 JavaScript 设计的问题,看似简单的问题,在 JavaScript 中可能并不简单,比如在社区中流传的下图,仔细看一下这些坑,即便是 JavaScript 老司机也经常翻车。</p>
<p><img src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/917d1aaea43d44cdbe18aed57c11f985~tplv-k3u1fbpfcp-watermark.image?" alt="WechatIMG23927.jpeg" /></p>
<p>上图中<code class="language-plaintext highlighter-rouge">typeof NaN</code>会返回<code class="language-plaintext highlighter-rouge">number</code>,这可能和你想的不一样,在 JavaScript 准确的获取变量类型,并不简单,正因为如此,这个问题经常被用来考察面试者,由于程序=数据+算法,而基本数据是数据的基础,所以面试中考察类型也是合理的。</p>
<p>如果面试中你只回答使用 typeof 获取类型,那大概率是会减分的,那么该如何回答这道题呢?本文将全面系统的介绍如何在 JavaScript 中判断类型,阅读本文,可以帮你,在工作中,避开类型判断雷区,如果在面试中你回答本文的内容,那么面试官将惊呼,这是高手,比我知道的都多,然后自然是好评喽。</p>
<p>下面先从最简单的例子开始,并一步一步提升难度,扩展思路,先来看第一个例子:</p>
<p>在工作中,对于数据为空的情况,经常要做防御式编程,误区之一是使用非运算符直接判断。但这样做是可能有坑的,比如这会把很多徦值计算在内,常见的徦值有<code class="language-plaintext highlighter-rouge">0</code>, <code class="language-plaintext highlighter-rouge">''</code>, <code class="language-plaintext highlighter-rouge">false</code>, <code class="language-plaintext highlighter-rouge">null</code>, <code class="language-plaintext highlighter-rouge">undefined</code>等。例如如下的 double 函数,需要对参数做为空的防御,这里使用非空运算符。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">double</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 0会被错误计算</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">NaN</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">x</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>对于判空,另一种写法是直接和<code class="language-plaintext highlighter-rouge">null</code>和<code class="language-plaintext highlighter-rouge">undefined</code>作比较,示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nx">x</span> <span class="o">===</span> <span class="kc">null</span> <span class="o">||</span> <span class="nx">x</span> <span class="o">===</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">NaN</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>虽然逻辑看起来非常正确,但这种写法有一个比较严重的问题,在 JavaScript 中<code class="language-plaintext highlighter-rouge">undefined</code>并不是关键字,而是 window 上的一个属性,在 ECMAScript 5 之前这个属性可写的,如果<code class="language-plaintext highlighter-rouge">undefined</code>被重新复制,在过时浏览器中会导致判断失效,示例如下:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">window</span><span class="p">.</span><span class="kc">undefined</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="c1">// 判断不能生效</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">x</span> <span class="o">===</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="mi">111</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>虽然在现代浏览器中不会有这个 bug,但是如果文法作用域中存在名字为<code class="language-plaintext highlighter-rouge">undefined</code>的变量还是会有问题,这被称作<code class="language-plaintext highlighter-rouge">undefiined</code>变量覆盖,例如如下代码中,<code class="language-plaintext highlighter-rouge">undefined</code>被 1 覆盖了。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="kc">undefined</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="c1">// 判断不能生效</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">x</span> <span class="o">===</span> <span class="kc">undefined</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="mi">111</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">})();</span>
</code></pre></div></div>
<p>关于判空还有比较巧妙的方法,可以只和<code class="language-plaintext highlighter-rouge">null</code>判断相等,借助隐式转换达到同样的效果,<code class="language-plaintext highlighter-rouge">null</code>是关键字,没有<code class="language-plaintext highlighter-rouge">undefined</code>的问题。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// null 和 undefined都会判断</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">x</span> <span class="o">==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
<span class="p">}</span>
</code></pre></div></div>
<p>在全等是最佳实践的背景下,这种做法并不被鼓励,建议使用 typeof 来判断<code class="language-plaintext highlighter-rouge">undefined</code>,typeof 通过内部类型判断,不存在<code class="language-plaintext highlighter-rouge">undefined</code>变量覆盖的问题。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nx">x</span> <span class="o">===</span> <span class="kc">null</span> <span class="o">||</span> <span class="k">typeof</span> <span class="nx">x</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">undefined</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="p">}</span>
</code></pre></div></div>
<p>对于 number 类型,有个需要注意的地方,在 JavaScript 中有个特殊的值叫做 NaN,NaN 的类型也是 number,编码中很少直接使用到 NaN,通常都是在计算失败时会得到这个值。</p>
<p>但将 NaN 作为 number 使用时就会报错,比如调用 NaN 上的<code class="language-plaintext highlighter-rouge">toFixed</code>方法,更好的做法是添加 isNaN 的判断,需要注意 number 类型的特殊逻辑。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">sqrt</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">);</span> <span class="c1">// NaN</span>
<span class="c1">// 注意这里的isNaN判断</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">x</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">number</span><span class="dl">'</span> <span class="o">&&</span> <span class="o">!</span><span class="nb">isNaN</span><span class="p">(</span><span class="nx">x</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">x</span><span class="p">.</span><span class="nx">toFixed</span><span class="p">(</span><span class="mi">2</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>
<p>也可以使用 ECMAScript 2015 新增的<code class="language-plaintext highlighter-rouge">Number.isNaN</code>,和全局函数 isNaN 的区别是,Number.isNaN 不会自行将参数转换成数字,<code class="language-plaintext highlighter-rouge">Number.isNaN</code>的逻辑下面的代码类似,<code class="language-plaintext highlighter-rouge">Number.isNaN</code>是更好的建议,但是需要注意兼容性的问题</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">Number</span><span class="p">.</span><span class="nb">isNaN</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">typeof</span> <span class="nx">value</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">number</span><span class="dl">'</span> <span class="o">&&</span> <span class="nb">isNaN</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span>
<span class="p">};</span>
</code></pre></div></div>
<p>typeof 只能判断基本类型,对于引用类型的到的值都是<code class="language-plaintext highlighter-rouge">object</code></p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typeof</span> <span class="p">[];</span> <span class="c1">// 'object'</span>
<span class="k">typeof</span> <span class="p">{};</span> <span class="c1">// 'object'</span>
<span class="k">typeof</span> <span class="nx">c</span><span class="p">;</span> <span class="c1">// 'object'</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">instanceof</code> 可以用来检测引用类型,其原理是检测 <code class="language-plaintext highlighter-rouge">constructor.prototype</code> 是否存在于参数 object 的原型链上</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{}</span> <span class="k">instanceof</span> <span class="nb">Object</span> <span class="c1">// true</span>
<span class="p">[]</span> <span class="k">instanceof</span> <span class="nb">Array</span> <span class="c1">// true</span>
<span class="o">/</span><span class="nx">reg</span><span class="o">/</span> <span class="k">instanceof</span> <span class="nb">RegExp</span> <span class="c1">// true</span>
</code></pre></div></div>
<p>instanceof 存在的一个问题是不够准确,原型链上存在的都会返回 true</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[]</span> <span class="k">instanceof</span> <span class="nb">Array</span> <span class="c1">// true</span>
<span class="p">[]</span> <span class="k">instanceof</span> <span class="nb">Object</span> <span class="c1">// true 注意这里</span>
</code></pre></div></div>
<p>使用 instanceof 做类型判断时,一定要注意顺序问题,如果顺序错误,可能会得不到正确的结果</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">type</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">x</span> <span class="k">instanceof</span> <span class="nb">Object</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Array永远得不到正确的类型哦</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">x</span> <span class="k">instanceof</span> <span class="nb">Array</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="dl">'</span><span class="s1">array</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>instanceof 另一个冷门的问题是存在多个 iframe 时,其判断可能会返回错误的结果,这个问题一般会在多从窗口之间从传递值时发生</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[]</span> <span class="k">instanceof</span> <span class="nb">window</span><span class="p">.</span><span class="nx">frames</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nb">Array</span> <span class="c1">// 返回false</span>
<span class="p">[]</span> <span class="k">instanceof</span> <span class="nb">window</span><span class="p">.</span><span class="nb">Array</span> <span class="c1">// 返回true</span>
</code></pre></div></div>
<p>对于数组的判断,更好的办法是使用 ECMAScript 5 带来的新方法<code class="language-plaintext highlighter-rouge">Array.isArray</code>,这个在任何情况下都可以得到可靠的结果</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">([]);</span> <span class="c1">// true</span>
<span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="c1">// false</span>
</code></pre></div></div>
<p>另一种常用的判断类型方式是使用,获取内部类型的方法,借助<code class="language-plaintext highlighter-rouge">Object.prototype.toString</code>可以获取内部类型的字符串结果</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">toString</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="p">;</span>
<span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">({});</span> <span class="c1">// [object Object]</span>
<span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span> <span class="c1">// [object Null]</span>
<span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="sr">/reg/</span><span class="p">);</span> <span class="c1">// [object RegExp]</span>
</code></pre></div></div>
<p>需要注意的是,在 ECMAScript 5 之前,undefined 和 null 并不能返回正确的值,如果有兼容性需求,需要注意这个问题</p>
<p>ECMAScript 2015 引入了<code class="language-plaintext highlighter-rouge">Symbol.toStringTag</code>可以修改内部类型的值,这会影响<code class="language-plaintext highlighter-rouge">toString</code>的返回值,但是需要注意兼容性问题</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">toString</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">obj</span><span class="p">);</span> <span class="c1">// '[object Object]'</span>
<span class="nx">obj</span><span class="p">[</span><span class="nb">Symbol</span><span class="p">.</span><span class="nx">toStringTag</span><span class="p">]</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">MyObject</span><span class="dl">'</span><span class="p">;</span> <span class="c1">// 修改内部类型</span>
<span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">obj</span><span class="p">);</span> <span class="c1">// '[object MyObject]'</span>
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>至此,本文介绍了在 JavaScript 中判断变量类型的各种方法,可以看到在正确的场景使用正确的方式并不容易,这里推荐大家使用作者维护的<a href="https://github.com/jsmini/type">type 库</a>,type 使用的正是本文介绍的知识,其提供了开箱即用的判断函数,经过了很多项目的检验,欢迎大家体验。</p>
<p>最后,欢迎大家阅读本文,如果对本文有任何疑问,欢迎在评论区交流。</p>
图解Git分支和命令
2022-07-09T00:00:00+00:00
http://yanhaijing.com/git/2022/07/09/git-branch
<p>Git的杀手锏是带来了轻量级分支,如果你使用svn分支,就会被Git新建分支和切换分支时的速度所震惊。</p>
<p>操作分支在Git中非常高频,学好分支操作是学好Git的基础,但在实际工作中,我发现很多同学并不熟悉Git的分支操作,本文试图通过图解的方式,讲解常用分支操作命令,和命令背后的Git分支模型。</p>
<h2 id="主分支">主分支</h2>
<p>在Git中新建一个项目后,默认有一个分支,即主分支。主分支一般表示项目的稳定版本,主分支应该包含稳定没有 Bug 的代码,并保持随时可以发布的状态,对于小型项目来说,只有一个主分支就够用了,每次我们提交都会创建一个commit节点。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s2">"c1"</span>
<span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s2">"c2"</span>
<span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s2">"c3"</span>
</code></pre></div></div>
<p>上面的命令会创建三个commit节点,此时master分支如下图所示:</p>
<p><img src="/blog/580.png" alt="image.png" /></p>
<p>主分支上应该只包合并提交,所有的迭代应该都在分支上进行,如果是简单的改动,直接在主分支修改也是可以的,如果功能较复杂,且需要多次提交,不建议在主分支直接修改。</p>
<h2 id="功能分支">功能分支</h2>
<p>当有新的功能要开发时,应该新建一个功能分支,命令如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git checkout <span class="nt">-b</span> feature/a
</code></pre></div></div>
<p>接下来在分支上创建两个提交,命令如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s2">"c4"</span>
<span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s2">"c5"</span>
</code></pre></div></div>
<p>此时提交树如下图所示:</p>
<p><img src="/blog/581.png" alt="image.png" /></p>
<p>当功能分支开发完成后,需要合并回主分支,合并回主分支有两种选择,快速合并和非快速合并,二者的区别在于是否创建提交节点,命令如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git merge feature/a <span class="c"># 快速合并</span>
<span class="nv">$ </span>git merge <span class="nt">--no-ff</span> feature/a <span class="c"># 非快速合并</span>
</code></pre></div></div>
<p>快速合并的结果,会直接将 master 指向了 feature/a,如下图所示:</p>
<p><img src="/blog/582.png" alt="image.png" /></p>
<p>非快速合并的结果,会在 master 创建合并提交节点,如下图所示:</p>
<p><img src="/blog/583.png" alt="image.png" /></p>
<p>两种合并方式都可以,如果选择快速合并,需要保证每个提交都是独立且完整的,如果不满足要求,Git 支持修改提交历史,需要修改后再次合并。</p>
<p>修改历史可以使用 rebase 命令,下面的命令可以修改最近三个提交,将第二个提交的 pick 改为 squash,可以合并第一个和第二个提交,将第三个提交的 pick 改为 edit,可以修改第三个提交的提交信息。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git rebase <span class="nt">-i</span> HEAD~3
pick d24b753 feat: update ci
squash f56ef2f feat: up ci
edit 6c91961 feat: up
<span class="c"># Rebase 50ece5c..6c91961 onto 50ece5c (3 commands)</span>
<span class="c"># Commands:</span>
<span class="c"># p, pick <commit> = use commit</span>
<span class="c"># r, reword <commit> = use commit, but edit the commit message</span>
<span class="c"># e, edit <commit> = use commit, but stop for amending</span>
<span class="c"># s, squash <commit> = use commit, but meld into previous commit</span>
<span class="c"># f, fixup <commit> = like "squash", but discard this commit's log message</span>
<span class="c"># x, exec <command> = run command (the rest of the line) using shell</span>
<span class="c"># b, break = stop here (continue rebase later with 'git rebase --continue')</span>
<span class="c"># d, drop <commit> = remove commit</span>
</code></pre></div></div>
<p>在创建当前分支之后,主分支可能又有新的提交,如下图所示:</p>
<p><img src="/blog/584.png" alt="image.png" /></p>
<p>在合并之前,建议先将主分支新的提交合并到当前分支,有两种策略可以选择,合并和变基,合并操作更简单,变基操作提交树更清晰,建议使用变基的方式。</p>
<p>先来看下合并操作的过程,命令如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git merge master
<span class="nv">$ </span>git checkout master
<span class="nv">$ </span>git merge feature/a
</code></pre></div></div>
<p>合并操作后的提交树如下图所示:</p>
<p><img src="/blog/585.png" alt="image.png" /></p>
<p>变基会修改feature/a的历史,就像 feature/a 是在 master 之后开发的一样,变基命令如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git rebase master
<span class="nv">$ </span>git checkout master
<span class="nv">$ </span>git merge feature/a
</code></pre></div></div>
<p>变基操作后的提交树如下图所示,虚线的提交是feature/a变基之前的状态,在变基后,虚线的提交不再有分支指向,但并不会删除,而是变成Git中的游离节点,在Git执行GC(垃圾清理)操作后,节点才会彻底删除。</p>
<p><img src="/blog/586.png" alt="image.png" /></p>
<h2 id="故障分支">故障分支</h2>
<p>如果发现存在 Bug,要尽快修复,此时可以基于主分支新建故障分支,命令如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git checkout <span class="nt">-b</span> bugfix/b
</code></pre></div></div>
<p>当验证没问题后,故障分支需要合并回主分支,并在主分支上发布新的补丁版本,命令如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git checkout master
<span class="nv">$ </span>git merge <span class="nt">--no-ff</span> bugfix/b
<span class="c"># 测试 构建 打标签 发布到npm</span>
</code></pre></div></div>
<p>主分支更新后,下游的公共分支要及时同步变更,建议使用变基进行同步,命令如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git checkout feature/a
<span class="nv">$ </span>git rebase master
</code></pre></div></div>
<p>故障分支模型如下图所示,bugfix/b 分支合并到 master 后,feature/a 分支进行了变基操作。</p>
<p><img src="/blog/587.png" alt="image.png" /></p>
<h2 id="标签与历史">标签与历史</h2>
<p>每次发布新版本时都要打标签,版本号需要符合第四章介绍的语义化版本规范,一般功能分支发布次版本号,故障分支发布修订版本号,使用Git添加tag的命令如下所示:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 假设当前版本是 1.1.0</span>
<span class="nv">$ </span>git tag 1.1.1 <span class="c"># 修改次版本号</span>
<span class="nv">$ </span>git tag 1.2.0 <span class="c"># 修改主版本</span>
</code></pre></div></div>
<p>Git 的版本号,还可以添加 v 前缀,两种风格都可以,建议在一个项目里面保持统一,添加v前缀的版本示例如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 假设当前版本是 v1.1.0</span>
<span class="nv">$ </span>git tag v1.1.1 <span class="k">*</span><span class="c"># 修改次版本号</span>
<span class="nv">$ </span>git tag v1.2.0 <span class="k">*</span><span class="c"># 修改主版本号</span>
</code></pre></div></div>
<p>添加标签后,提交树示例如下图所示。</p>
<p><img src="/blog/588.png" alt="image.png" /></p>
<p>现在假设最新版本是 v1.2.0 了,突然用户反馈了 v1.0.0 版本存在 bug,如果是比较小的问题,一般我们会建议用户升级到最新版本 ,但如果用户不能升级怎么办,比如 1.x 到 2.x 存在大版本变化。</p>
<p>出于各种原因,存在需要维护历史版本的需求,对于还有用户使用需求的历史版本,需要提供 bug 修复的支持。</p>
<p>此时创建的标签就起作用了,可以基于标签新建一个版本分支,并在版本分支上修复 bug,且发布新的版本,历史版本分支,不需要再次合并回主干分支,创建标签分支的示例如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git checkout <span class="nt">-b</span> v1.0.x v1.0.0
</code></pre></div></div>
<p>此时历史分支的示例如下图所示。</p>
<p><img src="/blog/589.png" alt="image.png" />
s</p>
<h2 id="总结">总结</h2>
<p>本文介绍了Git常见的分支命令和分支模型,希望能够帮助大家更好的掌握Git原理,如果你觉得本文对你有帮助,那就点赞加关注作者吧,如果对本文有任何疑问,欢迎在评论区交流。</p>
多子类型业务架构演进
2021-07-22T00:00:00+00:00
http://yanhaijing.com/program/2021/07/22/decoupling-the-large-subtype
<p>最近文章写得少了,都是因为在填耦合架构的坑,人生苦短,填坑不完,o(╥﹏╥)o,对如何治理耦合架构感兴趣,可以看我之前的文章——<a href="https://yanhaijing.com/program/2021/07/17/coupling-and-composition/">比耦合架构更好的架构</a></p>
<p><img src="/blog/551.png" width="300" /></p>
<p>好不容易从耦合坑里爬出来,又踩到了多子类型的坑,大坑套小坑,放眼望去,我太难了o(╥﹏╥)o</p>
<p><img src="/blog/557.png" width="300" /></p>
<p>本文结合真实项目经验,介绍多子类型的架构演进,将会介绍四种架构的优缺点和适应场景,分别是耦合架构,正交架构,低代码架构,低组件架构</p>
<p>故事是这样的,某一天小颜同学接🤚了一个业务M,小颜同学后来才知道,这个业务在团队内部被称为“屎山”,也就是传说中的多年迭代,多人接手,多种组合,缺少文档的烫手山芋,简称三多一少</p>
<p>M系统是典型的多子类型,M系统管理M资源,M资源有100多个子类型,每个子类型都有自己的逻辑,大家有类似业务同学可以感同身受一下</p>
<p>M系统的现状是100+子类型,2万行代码,但这2万行代码是写在一起的,这个文件内部有2000个分支,OMG,每次打开这文件,编辑器都要卡一会,就给某个子类型加一个逻辑,看了一天还不知道该往哪加,下图是小颜同学下班时的心情</p>
<p><img src="/blog/558.jpeg" width="200" /></p>
<p>就这样熬了一周,小颜同学深感写代码就像在扫雷,每天都在想,应该改哪里,改了会不会触雷,我的天啊,代码成功了</p>
<p><img src="/blog/559.png" width="300" /></p>
<p>比代码出错了不知道为什么,更可怕的是代码成功了,也不知道为什么;那么问题来了,如果是你面对这种情况该怎么办呢?</p>
<ul>
<li>相似又不同的类型</li>
<li>快速增长的业务</li>
<li>我的代码该如何写?</li>
</ul>
<h2 id="耦合架构">耦合架构</h2>
<p>像这种把子类型的逻辑都写在一起的模式,我称其为耦合架构,需要注意的是耦合也十分级别的,同样是耦合松耦合和紧耦合也是不同的,如果对于耦合的分类感兴趣,可以看下我的另一篇文章——<a href="https://yanhaijing.com/program/2016/09/01/about-coupling/">图解7种耦合关系</a></p>
<blockquote>
<p>PS:我的另一篇那文章也提到了耦合架构,感兴趣的可以看看——<a href="https://yanhaijing.com/program/2021/07/17/coupling-and-composition/">比耦合架构更好的架构</a></p>
</blockquote>
<p>一般听到耦合,第一反应就是解耦,但是先思考一个问题,我们的代码是如何失控的?</p>
<p>系统很有可能是这样演进的,在业务开始时</p>
<ul>
<li>PM提了一个子类型需求M1,M1包含标题和音频</li>
<li>PM提了一个新的子类型M2,在M1的基础上多了一个视频</li>
<li>PM提了一个新的子类型M3,在M1的基础上多了一个图片</li>
</ul>
<p>写下第一行代码的同学很快就完成了M1的开发,当面对M2和M3时,优先会考虑复用M1的逻辑,极有可能选择在M1中加上M2和M3的逻辑</p>
<p><img src="/blog/562.png" alt="" /></p>
<p>整个系统就这样慢慢的演进,随着时间的积累,慢慢走向失控</p>
<ul>
<li>PM提了一个子类型M4</li>
<li>…</li>
<li>PM提了一个子类型M50</li>
<li>…</li>
<li>PM提了一个子类型M100</li>
</ul>
<p>终于有一天变成了现在这样,也许早就有同学发现了系统的问题,但未能及时解决问题,相信不少业务会有这种年久失修的问题</p>
<p><img src="/blog/563.png" alt="" /></p>
<p>现在整个系统紧紧耦合在一起,巨量代码和逻辑交织在一起,整个系统蕴藏着巨大危机,开发效率低下,几乎无法并行开发,因为没法解决代码冲突啊;维护风险很高,真正的牵一发而动全身</p>
<p>整个系统已经到了岌岌可危的处境,急需一个人挽狂澜于既倒,扶大厦于将倾</p>
<p><img src="/blog/552.jpeg" width="200" /></p>
<p>如果想解决问题,首先我们要理清问题,这个系统目前存在如下问题:</p>
<ul>
<li>新人上手成本(看不懂)</li>
<li>PM让我修改旧类型(改不动)</li>
<li>PM说这个可以复用另一个(理不清)</li>
</ul>
<p>但是这些都只是问题导致的结果,导致这些问题的根源是,一个类型的需求,要面对全部类型的逻辑,系统复杂度:O(n^2)</p>
<p>其实前人们也一直在努力解决问题,只是努力的方向是,在现有架构下修修补补,比如总结文档,代码上面进行归类抽象等</p>
<h2 id="正交架构">正交架构</h2>
<p>正交架构的思想其实非常简单,既然要解耦,那就直接把每个子类型的逻辑分开实现不就好了吗,代码上彻底隔离</p>
<p>正交架构其实就是分治思想,分治思想提倡将大的问题,分离成多个小问题,从而分别解决,在多子类型系统中,子类型就是一个绝佳的分治媒介</p>
<p><img src="/blog/564.png" alt="" /></p>
<p>正交架构带来的直接好处就是,子类型解耦了,子类型内部逻辑是自治的,相互之间没有关联,从而使每个子类型的复杂度降低了,虽然系统里的逻辑和代码量并没有减少,但系统的整体复杂度降低了,在我们这里例子里面,用正交架构替换耦合架构收益如下:</p>
<ul>
<li>复杂度 n^2 => n,增加子类型时,系统复杂度线性增长</li>
<li>代码量 2个数量级,修改一个子类型时,代码量由万行级别 => 百行级别</li>
<li>分支数量 3个数量级,系统内部逻辑分支由千级别 => 个位数</li>
</ul>
<p>既然正交架构整么好,那为何不用正交架构替代耦合架构呢?没错,如果是新系统的话,我建议你从一开始就选择正交架构,但对于存量系统就很麻烦,因为有巨大的历史包袱,在这种情况下,如果时间紧迫,我建议先从新类型切到正交架构,历史包袱先保留不动,正可能需要一点小的代码设计才能实现,但我相信难不倒你的</p>
<p><img src="/blog/565.png" alt="" /></p>
<p>接下来说说正交架构的问题,通过把子类型的代码分开,带来一个明显的问题,在耦合架构中,类型之间的公共逻辑和组件是复用的,但在正交架构中是重复的,特别是对于复杂的逻辑和复杂的组件,问题尤为明显,一旦这部分功能要统一修改时,那可能要在每个子类型都要修改下</p>
<p><img src="/blog/566.png" alt="" /></p>
<p>这个问题可以通过将公共组件和公共逻辑抽象出来的方法来解决,一般如果2个子类型重复的部分,就应该提取出来,在业务迭代中,如果你想复用另一个类型的功能时,就是抽象的合适时机,一般PM会提醒你这个事情的,PM可能的对话如下</p>
<blockquote>
<p>小颜同学,这个新类型的这块就和之前的一样,我就不用在描述了吧 —— 传说中的一句话需求</p>
</blockquote>
<p><img src="/blog/567.png" alt="" /></p>
<p>关于公共组件化,还有一个问题不得不提,有两种抽象公共组件思路,一种是,组件提供配置,不同子类型使用组件时,配置不同开关;一种是,把子类型作为参数,传递到公共组件,组件内部判断子类型实现不同逻辑;对于前一种,我称为纯组件,对于后一种,我称为非纯组件</p>
<p>我发现不少同学在提取组件时,会写出来非纯组件,其实非纯组件只是把代码物理隔离了,逻辑上并没有隔离,在你的公共组件里其实还是一个小型的耦合架构,所以建议大家选择纯组件</p>
<p><img src="/blog/568.png" alt="" /></p>
<p>除了上面的非纯组件问题,正交架构还有个最大的问题就是组件化是可选的,这其实给拷贝代码提供了可能,让重复代码有继续存在的温床,提取组件这个事情就是一个最佳实践,属于弱约束的事情,而弱约束一般只能通过代码评审发现……</p>
<p>后面可能发生的事情大家都懂了吧</p>
<h2 id="低代码架构">低代码架构</h2>
<p>整个系统在正交架构下跑了一段时间,顶住了业务的压力,但我一直在思考有咩有更好的架构,最近低代码如火如荼,多子类型有咩有转低代码的可能?</p>
<p>说来也巧,刚好业务要对系统进行大的改造重构,在这个过程中,我落地了低代码架构,对于多子类型业务,如果其类型之间存在一些相似或重复部分,那么可以考虑低代码架构</p>
<p>对于单个子类型来说,只需要将正交架构中的代码,全部替换为配置,配置可能会对应一套DSL语言,需要搭配一套渲染器,渲染器读取配置,将每个配置渲染成组件库里的组件,就形成了完整的闭环</p>
<p><img src="/blog/569.png" alt="" /></p>
<p>我发现低代码刚好解决了正交架构系统可能存在的两个问题,低代码架构强制系统必须全部组件化,这就避免了正交架构可选的组件化可能带来的同样逻辑,实现不同问题;同时低代码架构下,强制要求组件面向配置开发,这恰巧杜绝了非纯组件的问题</p>
<p>低代码这一套铺下来,整个系统的复杂度和子类型数量的解耦,整体收益如下:</p>
<ul>
<li>复杂度:O(N) => O(常数),N是子类型数量,常数和组件数量相关</li>
<li>开发效率:80% ↑,新类型大概率不需要开发了</li>
<li>强制纯组件化</li>
</ul>
<p>但低代码也不是没有缺点的,首先整个系统的架构会比正交架构复杂,开发难度更大;低代码系统会增加额外的开发工作,比如配置系统,DSL语言和渲染器的开发等;如果遇到系统不支持的组件,需要额外的开发成本,且开发成本会大于这个组件在正交架构下的开发</p>
<p>低代码架构中有几个关键的技术难点,这里简单提一下</p>
<ul>
<li>绑定数据能力</li>
<li>联动能力</li>
<li>组件可扩展能力</li>
<li>DSL可扩展能力</li>
<li>校验如何设计</li>
<li>组件设计原则</li>
</ul>
<p>如果对低代码架构的实现细节感兴趣,可以继续关注我的后续文章,您的回复和打赏,是我继续写下去的动力</p>
<h2 id="低组件架构">低组件架构</h2>
<p>整个系统在低代码架构美好的演进了一段时间,但很快遇到了一些困难</p>
<p>首先是业务的多样性,导致了某些组件的配置爆炸式增加,有几个典型的组件有几十个开关,其自身的复杂度也需要关注了</p>
<p>有些子类型其功能不具有复用性,对于这种,我们扩展了类型组件的支持,也就是给这个子类型开发一个组件,其逻辑都在一起,就和正交架构的实现区别不大了</p>
<p>最大的问题,有些类型,其校验逻辑和联动关系非常复杂,通过配置化来实现这些联动和校验时越来越困难,维护的同学表示很难受</p>
<p>关于上面的问题,我思考了许久,提出了低组件架构,这个名词是我发明的,低组件架构其实是融合了正交架构和低代码架构,博取两家之长,避开两家之短</p>
<p>拿表单来举个例子,我们的程序其实是分成两部分的,首先是组件的渲染,也可以理解为静态UI的展示,这一部分其实是比较容易通过DSL来表示的;在UI背后还有逻辑层,包括组件的联动,校验等逻辑,这一部分逻辑存在DSL困境</p>
<p>其实我们写的代码,比如js,html等就是通用的DSL,既然如此不如换个思路?一个子类型包含哪些组件,组件包含哪些参数,通过低代码架构来实现;组件背后的逻辑层,通过正交架构来实现</p>
<p>低组件架构,融合了两种架构,避免了正交架构中的组件发散问题,同时也避免了低代码架构中的DSL爆炸问题</p>
<p>不过这个架构,我只是做了纸面上的推演,并未落地,纸上得来终觉浅,希望能给同学们一起启发,如果有同学有尝试,欢迎交流</p>
<h2 id="总结">总结</h2>
<p>综上,我们介绍了多子类型系统的4种架构,其实4中架构都能实现需求,但其思想却有很大不同,下面同不同层面做个对比</p>
<p>首先从子类型数量和系统复杂度方面来对比下,低组件架构其实是低代码架构的一个变种,所以此处不单独列出</p>
<p><img src="/blog/570.png" width="300" /></p>
<p>再来对比下,开发人员面对不同架构时的心智模型</p>
<p><img src="/blog/571.png" width="300" /></p>
<p>通过上面的介绍和对比,相信同学们对4种架构的定义和区别都有了自己的认识,其实架构没有好坏之分,只有适合不适合,下面从我的认知,给大家总结下不同架构适合的不同业务场景</p>
<table>
<thead>
<tr>
<th>架构</th>
<th>适应场景</th>
</tr>
</thead>
<tbody>
<tr>
<td>耦合架构</td>
<td>无,除非你想离职了,不过害人终害己,天道好轮回</td>
</tr>
<tr>
<td>正交架构</td>
<td>子类型小于30个的场景,子类型之间区别很大时</td>
</tr>
<tr>
<td>低代码架构</td>
<td>逻辑不重,子类型较多时,子类型迭代较快</td>
</tr>
<tr>
<td>低组件架构</td>
<td>同上,但逻辑较重时</td>
</tr>
</tbody>
</table>
<p>最后,希望本文的内容,可以帮助大家在多子类型业务中找到出路,感谢大家阅读,^_^</p>
<p><img src="/blog/560.jpeg" width="150" /></p>
比耦合架构更好的架构
2021-07-17T00:00:00+00:00
http://yanhaijing.com/program/2021/07/17/coupling-and-composition
<p>最近文章写得少了,都是因为在填耦合架构的坑,人生苦短,填坑不完,o(╥﹏╥)o</p>
<p><img src="/blog/551.png" width="300" /></p>
<p>在不断踩坑和填坑的过程中,我不禁感叹,怎么能写出这么糟糕的架构,其实比耦合架构更好怕的是,设计耦合架构,以及维护耦合架构的人意识不到问题的存在,坑是填不完的,要解决挖坑的人,所以我决定把这些经验分享出来,希望可以帮助大家</p>
<p><img src="/blog/550.jpeg" width="300" /></p>
<p>本文的标题稍微有些标题党,本文提到的架构是代码层面的架构,本文通过一个例子,向大家介绍耦合架构,拷贝架构,超类架构和组合架构的区别和优劣</p>
<h2 id="背景">背景</h2>
<p>本文用上传组件作为例子,一般业务中的上传组件都有自己的逻辑,现在假设我们有如下多个不同的上传组件:</p>
<ul>
<li>文件上传组件,通用的上传组件</li>
<li>图片上传组件,有自己的展示逻辑和图片处理等</li>
<li>音频上传组件,有自己的展示逻辑和音频处理等</li>
<li>路径上传组件,有自己的展示逻辑且参数也不同</li>
</ul>
<p>现在让我们想一下,实现这些逻辑,代码该如何设计,在往下看之前,建议先看一下我的另一篇文章——<a href="https://yanhaijing.com/program/2016/09/01/about-coupling/">图解7种耦合关系</a>,下面会用到这里面提到的知识</p>
<h2 id="耦合架构">耦合架构</h2>
<p>耦合架构就是把代码都写在一起,这样就实现了公共逻辑只存在一份;然后内部存在很多条件判断,来实现不同的分支逻辑</p>
<p><img src="/blog/553.png" alt="" /></p>
<p>相信大家都能看出来耦合架构的问题,心里也都有自己的答案,但耦合架构是怎么形成的呢?耦合架构大部分都是业务演进的结果,开始时业务只有一种,当来了新的业务时,可能开人员偷懒了,也可能是缺乏重构的勇气,就慢慢积累了下来,然后尾大不掉,渐渐失去了所有人的控制</p>
<p>耦合架构的优点大概只有节省设计时间了,因为不用设计,业务线性演变即可</p>
<p>耦合架构的问题在于难以维护,不同业务的逻辑紧紧交织在一起,系统复杂度会随着分支的数量指数级增长,一般随着时间的积累,很可能会逐渐失控,演变成下图这样</p>
<p><img src="/blog/552.jpeg" width="200" /></p>
<p>耦合架构对于改动非常不友好,极有可能牵一发而动全身,改不动,理不清;耦合架构对于修改Bug非常不友好,如同盲人摸象,难以排查;耦合架构对于阅读非常不友好,理清逻辑犹如管中窥豹,错综复杂</p>
<p>耦合架构演进到最后,很可能被弃坑,特别是人员变更,恰巧来了一个新的功能时,极有可能放弃前人的积累,另起炉灶</p>
<h2 id="拷贝架构">拷贝架构</h2>
<p>耦合架构演进到最后很可能变为拷贝架构,这也是我在项目中看到的问题,同学们告诉我,这个有点复杂哦,建议你拷一份,别修改之前的代码</p>
<p>拷贝架构就是各个组件完全自成一份,每个都包含完成的功能,在写业务之前,就把之前的拷贝一份</p>
<p><img src="/blog/554.png" alt="" /></p>
<p>拷贝架构的优点就是相互隔离,业务之间的演进不会相互影响</p>
<p>拷贝架构的缺点也很明显,公共逻辑被复制了多份,违反了DRY原则;特别是如果公共部分自身改动的话,可能会有研发效率加倍,或者逻辑不一致问题</p>
<h2 id="超类架构">超类架构</h2>
<p>面对前面的问题,大家会做出不同的改进,超类架构是一个典型的方向,面向对象思维建议用继承来解决泛化问题,通过抽象一个上传基类,可以解决公共逻辑的问题,不同业务的差异,可以通过子类来泛化实现</p>
<p><img src="/blog/555.png" alt="" /></p>
<p>超类的优点在于解决了泛化和公共的问题,通过抽象继承隔离了两部分逻辑</p>
<p>超类的问题在于,继承本身就脆弱,大部分前端的OOP能力也良莠不齐,存在四不像设计和啥都往超类塞的问题;超类虽然隔离了公共逻辑,但还是会将公共逻辑已接口的方式暴露给子类,子类需要感知父类的设计;父类很难设计的良好,面对发散的业务,后面父类的迭代会越来越痛苦</p>
<p>超类架构依赖于良好的抽象,如果抽象不好,也会存在弃坑的问题,很有可能拷贝一份而放弃了超类的维护</p>
<h2 id="组合架构">组合架构</h2>
<p>组合架构是更适合前端UI组件的架构,通过将页面拆分成功能独立的组件,再将组件组合到一起从而实现页面功能,在我们的例子中,可以将公共逻辑提取为一个功能单一的上传组件即可,其只包含上传逻辑处理,对外提供属性和回调函数</p>
<p><img src="/blog/556.png" alt="" /></p>
<p>从架构图上来看和超类是相似的,但继承和组合是两个概念,组合是自下而上的,可逆的,上下层组件之间的耦合是松散的;继承是自顶而下的,不可逆的,父子组件之间的耦合是紧密的</p>
<p>组合架构依赖于设计良好的组件,需要遵从单一职责和开闭原则,设计糟糕的组件会让系统变的脆弱,组件要做到高内聚,低耦合,组件要做到面向参数设计,剥离业务</p>
<h2 id="总结">总结</h2>
<p>本文通过一个例子,介绍了前端的4种设计模式,希望能够帮助大家,在解耦合的道路上有更多的选择</p>
<p>一般一个模块人人都觉得复杂,却又没人提出改进,那往往是设计存在问题,一般好的设计都是简单的</p>
如何回答面试中的JavaScript原型链问题
2021-03-13T00:00:00+00:00
http://yanhaijing.com/javascript/2021/03/13/javascript-prototype-chain
<blockquote>
<p>本文已收录到《<a href="https://yanhaijing.com/tags/#%E9%9D%A2%E8%AF%95%E7%9F%A5%E8%AF%86-ref">面试知识系列</a>》</p>
</blockquote>
<p><strong>划重点,这是一道面试必考题,我就问过很多面试者这个问题,✧(≖ ◡ ≖✿)嘿嘿</strong></p>
<p>又到了金三银四的季节,相信最近有很多同学会出来看看机会,JS中的原型是面试中的必考题,很多面试官都喜欢考察原型相关的知识,没错,我就是其中之一</p>
<p><strong>猿辅导,急聘!!!1年以上经验的前端,后端,客户端,<a target="_blank" href="https://yanhaijing.com//job/">点击查看</a></strong></p>
<p>原型作为JS中的核心知识点,可以考察的知识点和细节非常多,当然面试不是为了难住大家,而是希望了解大家的知识边界在哪里,原型知识可以反应大家对JS的了解程度,对技术的兴趣,是否喜欢钻研等</p>
<p>原型的考察,绕不过原型链,面试中如果能把原型链画清楚,一定会加分不少,但我发现大部分面试者在面试中很难画好原型链,于是我不禁陷入深深的思考</p>
<p>站在面试官的角度,我希望面试者能清晰、快速、无误的画出原型链,本文是我思考良久后,总结的快速绘制原型链的方法</p>
<h2 id="题目一">题目一</h2>
<p>面试中,我经常会考察如何实现原型继承,再下一步才是考察原型链,一般我会写出下面的代码,然后让大家绘制原型链</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">A</span> <span class="p">{}</span>
<span class="kd">class</span> <span class="nx">B</span> <span class="kd">extends</span> <span class="nx">A</span> <span class="p">{}</span>
<span class="kd">const</span> <span class="nx">b</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">B</span><span class="p">();</span>
</code></pre></div></div>
<p>面对这个题,我们先来绘制b和B的原型链,这里面涉及到三个对象分别是<code class="language-plaintext highlighter-rouge">b</code>,<code class="language-plaintext highlighter-rouge">B</code>和<code class="language-plaintext highlighter-rouge">B.prototype</code>,相信大部分同学能够画清楚这几个的关系,由于是面试中只有笔和纸,又要清晰快速,所以我们可以像下面这样画,箭头代表关系,箭头上面的字代表属性名字</p>
<p><img src="/blog/546.png" alt="" /></p>
<p>上面包括<code class="language-plaintext highlighter-rouge">__proto__</code>,<code class="language-plaintext highlighter-rouge">constructor</code>和<code class="language-plaintext highlighter-rouge">prototype</code>三个部分,能画出来上面的只能算是不及格,接下来我们在把A的部分加进去</p>
<p><img src="/blog/547.png" alt="" /></p>
<p>接下来我们把Function加进去,这一步是大家比较容易忽略的,Function比较特殊的地方就是<code class="language-plaintext highlighter-rouge">Function.__proto__</code>指向自己的<code class="language-plaintext highlighter-rouge">Function.prototype</code>,图中红色的线</p>
<p><img src="/blog/548.png" alt="" /></p>
<p>最后再把Object加进去,我们的原型链就大工告成了,可以大家可以慢慢消化一下(<em>^▽^</em>)</p>
<p><img src="/blog/549.png" alt="" /></p>
<h2 id="题目二">题目二</h2>
<p>基本上面试中能在5分钟内画出来上面的原型链,那么应该能够让面试官满意了,但我一般会再考一些原型链相关的题目,比如下面这个,这道题乍一看容易被绕进去,其实是考察大家对instanceof机制的理解,再结合上面的原型链,就很假单了</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 下面两行语句的结果是,为什么</span>
<span class="nb">Function</span> <span class="k">instanceof</span> <span class="nb">Object</span>
<span class="nb">Object</span> <span class="k">instanceof</span> <span class="nb">Function</span>
</code></pre></div></div>
<p>再比如下面的写法和上面写法的有什么区别?该如何弥补?</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">A</span><span class="p">()</span> <span class="p">{}</span>
<span class="kd">function</span> <span class="nx">B</span><span class="p">()</span> <span class="p">{}</span>
<span class="nx">B</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">A</span><span class="p">.</span><span class="nx">prototype</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">b</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">B</span><span class="p">();</span>
</code></pre></div></div>
<p>再比如,如何不通过类和函数实现继承?等等,希望大家能够举一反三,灵活应对</p>
<h2 id="总结">总结</h2>
<p>本文围绕面试,给大家讲解了原型链的问题,除了原型链,相关的继承知识也是考察的重点,下面是相关文章</p>
<ul>
<li><a href="https://yanhaijing.com/javascript/2014/07/18/javascript-prototype/">JavaScript原型之路</a></li>
<li><a href="https://yanhaijing.com/javascript/2016/07/24/prototype-and-inheritance-of-js/">详解JavaScript中的原型和继承 </a></li>
<li><a href="https://yanhaijing.com/javascript/2013/08/23/javascript-inheritance-how-to-shoot-yourself-in-the-foot-with-prototypes/">Javascript继承-原型的陷阱</a></li>
<li><a href="https://yanhaijing.com/javascript/2014/11/09/object-inherit-of-js/">JavaScript对象继承一瞥</a></li>
<li><a href="https://yanhaijing.com/javascript/2014/05/15/a-code-explain-javascript-oop/">一段代码详解JavaScript面向对象</a></li>
</ul>
Git在rebase时如何保留merge commit
2020-09-23T00:00:00+00:00
http://yanhaijing.com/git/2020/09/23/git-rebase-merge
<p>rebase是git中一个神奇的命令,可以让并行的提交,变得像线性开发的一样,最近发现rebase在遇到merge commit时可能和认知不太一样,本文通过一个例子来讲解下git rebase遇到merge commit时该如何应对</p>
<h2 id="经典rebase">经典rebase</h2>
<p>先来看下rebase的典型使用场景,假设我们有如下一个提交记录</p>
<p><img src="/blog/543.png" alt="" /></p>
<p>当dev想同步上游(master)的修改时,有两种方案,一种是merge,一种是rebase</p>
<p>merge会创建一个commit节点,并且能够保留分支关系</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git merge master
</code></pre></div></div>
<p><img src="/blog/544.png" alt="" /></p>
<p>但是一般上游到下游的操作,是不需要保留分支关系的,rebase正是为了这个场景设计的,rebase会将dev上的节点,重新在master上重新创建一遍,看起来就是是在master分支最后开发的一样</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git rebase master
</code></pre></div></div>
<p><img src="/blog/545.png" alt="" /></p>
<p>至此就介绍完了rebase常规的使用场景</p>
<h2 id="merge-rebase">merge rebase</h2>
<p>如果被rebase的分支上有merge commit,会发生什么呢?假设我们有如下的log tree(请原谅我懒得画图了^_^)</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">*</span> 31ef4ec <span class="o">(</span>HEAD -> master<span class="o">)</span> e
| <span class="k">*</span> b554f2d <span class="o">(</span>dev<span class="o">)</span> Merge branch <span class="s1">'test'</span> into dev
| |<span class="se">\ </span>
| | <span class="k">*</span> 853aaf6 <span class="o">(</span><span class="nb">test</span><span class="o">)</span> c
| <span class="k">*</span> | 1af86fa d
| |/
| <span class="k">*</span> c1b49a5 b
|/
<span class="k">*</span> 763a350 a
</code></pre></div></div>
<p>master分支在dev分支后面又有了新的提交,dev分支合并了test分支,现在dev分支想同步master分支,继续使用rebase操作</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">*</span> 8924fda <span class="o">(</span>HEAD -> dev<span class="o">)</span> c
<span class="k">*</span> 06201b2 d
<span class="k">*</span> e0c6b78 b
<span class="k">*</span> 31ef4ec <span class="o">(</span>master<span class="o">)</span> e
| <span class="k">*</span> 853aaf6 <span class="o">(</span><span class="nb">test</span><span class="o">)</span> c
| <span class="k">*</span> c1b49a5 b
|/
<span class="k">*</span> 763a350 a
</code></pre></div></div>
<p>可以rebase将merge commit抛弃了,同时test分支上的提交<code class="language-plaintext highlighter-rouge">c</code>的内容被patch到了dev分支上</p>
<p>这可能和我的认知不太一样,我希望的效果是继续保留merge commit,rebase 为我们提供<code class="language-plaintext highlighter-rouge">--rebase-merges</code>参数来这个问题</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git rebase <span class="nt">--rebase-merges</span> master
</code></pre></div></div>
<p>看下log tree,变基成功了,merge commit被保留了,但test分支也被变基了,test分支上的commit还是被复制了一份</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">*</span> 88e6cf7 <span class="o">(</span>HEAD -> dev<span class="o">)</span> Merge branch <span class="s1">'test'</span> into dev
|<span class="se">\ </span>
| <span class="k">*</span> e2fae10 c
<span class="k">*</span> | 65869a2 d
|/
<span class="k">*</span> e61592d b
<span class="k">*</span> 31ef4ec <span class="o">(</span>master<span class="o">)</span> e
| <span class="k">*</span> 853aaf6 <span class="o">(</span><span class="nb">test</span><span class="o">)</span> c
| <span class="k">*</span> c1b49a5 b
|/
<span class="k">*</span> 763a350 a
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>最后建议大家merge和rebase尽量使用一种即可,即rebase的代码,不要包含merge commit;使用了merge操作,就不要在rebase了</p>
<h2 id="参考文档">参考文档</h2>
<ul>
<li><a href="https://mtyurt.net/post/2019/git-rebase-merges-option.html">git: Preserving merge commits while rebasing</a></li>
</ul>
Git常用低频底层命令
2020-09-23T00:00:00+00:00
http://yanhaijing.com/git/2020/09/23/git-plumbing-cmd
<p>在一些git脚本中,或者git hook中,经常会见到一些不认识的git命令,比如rev-parse, hash-object, cat-file等,这些命令是干什么的?</p>
<p>别急,我们需要先了解一下Git中的内部文件类型,git中最基本的单元是文件,多个文件组成文件树,文件树会被提交对象引用,提交对象会被分支引用,如图所示</p>
<p><img src="/blog/468.png" alt="" /></p>
<p>需要注意一点的是,Git中所有这些文件都被拉平,通过一个魔法key来区分不同的文件,Key是通过文件内容自动计算出来的,文件内容不同Key就会不一样,引用关系被存在了文件内容里</p>
<p><img src="/blog/466.png" alt="" /></p>
<blockquote>
<p>关于git中内部对象的详细介绍,可以看我的另一篇文章<a href="https://yanhaijing.com/git/2017/02/08/deep-git-3/">起底Git-Git内部原理</a></p>
</blockquote>
<p>Git给每类文件都提供了相关的底层命令,用来直接操作git中的文件,一般要写一些git工具,就绕不开这些命令,本文我们来介绍下Git中比较常用的底层命令</p>
<h2 id="blob操作">Blob操作</h2>
<p>我们本地的文件,存入Git中就变成了Blob文件,<code class="language-plaintext highlighter-rouge">hash-object</code>命令可以直接向git中写入一个文件,同时会输入文件的魔法key</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo</span> <span class="s1">'test content'</span> | git hash-object <span class="nt">--stdin</span>
<span class="c"># d670460b4b4aece5915caf5c68d12f560a9fe3e4</span>
<span class="nv">$ </span><span class="nb">echo</span> <span class="s1">'test content'</span> <span class="o">></span> a
<span class="nv">$ </span>git hash-object a <span class="c"># 和效果上面一样</span>
<span class="c"># d670460b4b4aece5915caf5c68d12f560a9fe3e4</span>
</code></pre></div></div>
<p>上面并没有真正将文件放入git中,只是计算了下文件的key,如果要将文件真正写入git中,需要加上-w参数</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo</span> <span class="s1">'test content'</span> | git hash-object <span class="nt">-w</span> <span class="nt">--stdin</span>
</code></pre></div></div>
<p>怎么确定写入git中了呢?可以用上面的key去git的文件目录中查看,<strong>注意d6后面的/</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cat .git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4
# xK??OR04f(I-.QH??+I?+?K?
</code></pre></div></div>
<p>通过cat直接读取,读出来的是乱码,git提供了<code class="language-plaintext highlighter-rouge">cat-file</code>命令,可以通过key读取文件内容和文件信息</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git cat-file <span class="nt">-p</span> d670460b4b4aece5915caf5c68d12f560a9fe3e4
<span class="c"># test content</span>
<span class="nv">$ </span>git cat-file <span class="nt">-t</span> d670460b4b4aece5915caf5c68d12f560a9fe3e4 <span class="c"># 查看文件类型</span>
<span class="c"># blob</span>
<span class="nv">$ </span>git cat-file <span class="nt">-s</span> d670460b4b4aece5915caf5c68d12f560a9fe3e4 <span class="c"># 查看文件size</span>
<span class="c"># 13</span>
</code></pre></div></div>
<h2 id="tree操作">Tree操作</h2>
<p>现在我们工作区的结构如下,该如何存入git中呢?</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo </span>a <span class="o">></span> a.txt
<span class="nv">$ </span><span class="nb">mkdir </span>b
<span class="nv">$ </span><span class="nb">echo </span>b <span class="o">></span> b/b.txt
<span class="nv">$ </span>tree
<span class="c"># .</span>
<span class="c"># ├── a.txt</span>
<span class="c"># └── b</span>
<span class="c"># └── b.txt</span>
</code></pre></div></div>
<p>git提供了write-tree和read-tree两个命令,这两个命令都只能对索引区进行读取,所以我们需要将我们的文件先放入索引区,可以使用update-index命令</p>
<p>这里需要用到递归,首先我们将b文件夹放入索引</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git hash-object <span class="nt">-w</span> b/b.txt
<span class="c"># 63d8dbd40c23542e740659a7168a0ce3138ea748</span>
<span class="nv">$ </span>git update-index <span class="nt">--add</span> b/b.txt
<span class="nv">$ </span>git write-tree
<span class="c"># a14f1d9c64fb11590f86de876f3cd45f24ecfb4a</span>
</code></pre></div></div>
<p>write-tree会返回tree文件的key,可以通过cat-file来查看tree对象</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git cat-file <span class="nt">-p</span> a14f1d9c64fb11590f86de876f3cd45f24ecfb4a
<span class="c"># 040000 tree bc4d1181aca5a33673d7c5d4c209d09ce1cfabd7 b</span>
</code></pre></div></div>
<p>下面我们再来把根目录写入</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git hash-object <span class="nt">-w</span> a.txt
<span class="c"># 2e65efe2a145dda7ee51d1741299f848e5bf752e</span>
<span class="nv">$ </span>git update-index <span class="nt">--add</span> a.txt
<span class="nv">$ </span>git write-tree
<span class="c"># ba78921ed5403ef643a55125e56f21f7bd2206eb</span>
<span class="nv">$ </span>git cat-file <span class="nt">-p</span> ba78921ed5403ef643a55125e56f21f7bd2206eb
<span class="c"># 100644 blob 2e65efe2a145dda7ee51d1741299f848e5bf752e a.txt</span>
<span class="c"># 040000 tree bc4d1181aca5a33673d7c5d4c209d09ce1cfabd7 b</span>
</code></pre></div></div>
<p>通过read-tree可以将文件对象读到index区,在通过checkout-index就可以复制到工作区</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git read-tree <span class="nt">--prefix</span><span class="o">=</span>newb a14f1d9c64fb11590f86de876f3cd45f24ecfb4a
<span class="nv">$ </span>git checkout-index <span class="nt">-a</span>
<span class="nv">$ </span><span class="nb">ls</span>
<span class="c"># a.txt b newb</span>
</code></pre></div></div>
<h2 id="commit操作">Commit操作</h2>
<p>git中的commit被存储为commit对象,可以通过commit-tree将树对象提交,参数为树对象的key,还可以指定一个父提交</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo</span> <span class="s1">'first commit'</span> | git commit-tree ba78921ed5403ef643a55125e56f21f7bd2206eb
<span class="c"># 1c620dd0179c5ecd32ea6a8aaf7ce7218cf11c7b</span>
<span class="nv">$ </span>git cat-file <span class="nt">-p</span> 1c620dd0179c5ecd32ea6a8aaf7ce7218cf11c7b
<span class="c"># tree ba78921ed5403ef643a55125e56f21f7bd2206eb</span>
<span class="c"># author yanhaijing <yanhaijing@yeah.net> 1601380098 +0800</span>
<span class="c"># committer yanhaijing <yanhaijing@yeah.net> 1601380098 +0800</span>
<span class="c"># </span>
<span class="c"># first commit</span>
</code></pre></div></div>
<h2 id="ref操作">Ref操作</h2>
<p>ref被设计出来方便大家使用commit,有了ref就可以不用去记录commit key了,可以通过update-ref命令来更新ref</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git update-ref refs/heads/master 1c620dd0179c5ecd32ea6a8aaf7ce7218cf11c7b
<span class="nv">$ </span>git show-ref master
<span class="c"># 1c620dd0179c5ecd32ea6a8aaf7ce7218cf11c7b refs/heads/master</span>
</code></pre></div></div>
<h2 id="其他">其他</h2>
<p>rev-parse命令,可以将对人友好的参数,转换成对git友好的参数,在写git工具时会非常有用</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 输出git目录</span>
<span class="nv">$ </span>git rev-parse <span class="nt">--git-dir</span>
<span class="c"># .git </span>
<span class="nv">$ </span>git rev-parse HEAD <span class="c"># 输出HEAD的key</span>
</code></pre></div></div>
<h2 id="参考文章">参考文章</h2>
<ul>
<li><a href="https://www.bookstack.cn/read/git-doc-zh/docs-77.md">Git 中文参考</a></li>
<li><a href="https://git-scm.com/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-Git-%E5%AF%B9%E8%B1%A1">Git 内部原理</a></li>
</ul>
Git新命令switch和restore
2020-09-17T00:00:00+00:00
http://yanhaijing.com/git/2020/09/17/git-switch-and-restore
<p>最近发现git在修改完文件后,提示恢复修改的命令是restore,如下所示,印象中应该是checkout,所以就研究了下,总结一下分享给大家</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git status
Changes to be committed:
<span class="o">(</span>use <span class="s2">"git restore --staged <file>..."</span> to unstage<span class="o">)</span>
modified: _posts/git/2020-8-24-how-to-transfer-a-git-repo.md
</code></pre></div></div>
<p>原来是git中的checkout命令承载了分支操作和文件恢复的部分功能,有点复杂,并且难以使用和学习,所以社区解决将这两部分功能拆分开,在git 2.23.0中引入了两个新的命令switch和restore用来取代checkout</p>
<p>下面分别来说说分支操作和文件恢复,如果你对git还不太熟悉,可以先阅读我的git入门文章</p>
<ul>
<li><a href="https://yanhaijing.com/git/2017/01/19/deep-git-0/">起底Git</a></li>
<li><a href="https://yanhaijing.com/git/2014/11/01/my-git-note/">我的git笔记</a></li>
<li><a href="https://yanhaijing.com/git/2017/07/14/four-method-for-git-merge/">图解4种git合并分支方法</a></li>
</ul>
<h2 id="分支操作">分支操作</h2>
<p>原来git有两个命令用来操作分支,分别是branch和checkout</p>
<p>其中branch用来管理分支</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git branch <span class="c">## 查看当前所在分支</span>
<span class="nv">$ </span>git branch aaa <span class="c"># 新建分支aaa</span>
<span class="nv">$ </span>git branch <span class="nt">-d</span> aaa <span class="c"># 删除分支aaa</span>
</code></pre></div></div>
<p>checkout用来切换分支,切换分支时,也可以新建分支</p>
<p>由于git中分支仅仅是一个commit id的别名,所以checkout也可以切换到一个commit id</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git checkout aaa <span class="c"># 切换到 aaa分支</span>
<span class="nv">$ </span>git checkout <span class="nt">-b</span> aaa <span class="c"># 创建aaa,然后切换到 aaa分支</span>
<span class="nv">$ </span>git checkout commitid <span class="c"># 切换到某个commit id</span>
</code></pre></div></div>
<p>checkout命令会用仓库中的文件,覆盖索引区(staged or index)和工作目录(work tree)</p>
<p>新的switch命令用来接替checkout的功能,但switch不能切换到commit id</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git switch aaa <span class="c"># 切换到 aaa分支</span>
<span class="nv">$ </span>git switch <span class="nt">-c</span> aaa <span class="c"># 创建aaa,然后切换到 aaa分支</span>
</code></pre></div></div>
<p>下面来总结对比下</p>
<table>
<thead>
<tr>
<th>操作</th>
<th>2.23-</th>
<th>2.23+</th>
</tr>
</thead>
<tbody>
<tr>
<td>管理分支</td>
<td>git branch</td>
<td>git branch</td>
</tr>
<tr>
<td>切换分支</td>
<td>git checkout</td>
<td>git switch</td>
</tr>
<tr>
<td>新建+切换分支</td>
<td>git checkout -b</td>
<td>git switch -c</td>
</tr>
<tr>
<td>切换到commit id</td>
<td>git checkout</td>
<td>git checkout</td>
</tr>
</tbody>
</table>
<h2 id="文件恢复">文件恢复</h2>
<p>原来git中文件恢复涉及到两个命令,一个是checkout,一个是reset,reset除了重置分支之外,还提供了恢复文件的能力</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git checkout <span class="nt">--</span> aaa <span class="c"># 从staged中恢复aaa到worktree</span>
<span class="nv">$ </span>git reset <span class="nt">--</span> aaa <span class="c"># 从repo中恢复aaa到staged</span>
<span class="nv">$ </span>git checkout <span class="nt">--</span> HEAD aaa <span class="c"># 从repo中恢复aaa到staged和worktree</span>
<span class="nv">$ </span>git reset <span class="nt">--hard</span> <span class="nt">--</span> aaa <span class="c"># 同上</span>
</code></pre></div></div>
<p>一图胜千言系列</p>
<p><img src="/blog/541.png" alt="" /></p>
<p>新的restore命令专门用来恢复staged和worktree的文件</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git restore <span class="o">[</span><span class="nt">--worktree</span><span class="o">]</span> aaa <span class="c"># 从staged中恢复aaa到worktree</span>
<span class="nv">$ </span>git restore <span class="nt">--staged</span> aaa <span class="c"># 从repo中恢复aaa到staged</span>
<span class="nv">$ </span>git restore <span class="nt">--staged</span> <span class="nt">--worktree</span> aaa <span class="c"># 从repo中恢复aaa到staged和worktree</span>
<span class="nv">$ </span>git restore <span class="nt">--source</span> dev aaa <span class="c"># 从指定commit中恢复aaa到worktree</span>
</code></pre></div></div>
<p>一图胜千言系列</p>
<p><img src="/blog/542.png" alt="" /></p>
<p>可以看到restore提供checkout和reset两个命令才能提供的文件恢复能力,也提供了更好的语义</p>
<h2 id="总结">总结</h2>
<p>大家可以继续使用自己熟悉的命令,毕竟熟悉的才好用,本文希望能够帮助大家理清这个问题,满足大家的好奇心</p>
<p>git这样一个成熟的项目,一直处在不停的演进进化中,我们作为开发者也要保持持续学习演进的能力,共勉👊</p>
如何迁移一个Git仓库
2020-08-24T00:00:00+00:00
http://yanhaijing.com/git/2020/08/24/how-to-transfer-a-git-repo
<p>最近要把 Git 仓库迁移,看有些同学迁移中不太顺利,自己研究总结了下,分享给大家,本文将向大家介绍迁移 Git 仓库的的方法</p>
<p>如果你对git还不太熟悉,可以先阅读我的git入门文章</p>
<ul>
<li><a href="https://yanhaijing.com/git/2017/01/19/deep-git-0/">起底Git</a></li>
<li><a href="https://yanhaijing.com/git/2014/11/01/my-git-note/">我的git笔记</a></li>
<li><a href="https://yanhaijing.com/git/2017/07/14/four-method-for-git-merge/">图解4种git合并分支方法</a></li>
</ul>
<h2 id="直接push">直接PUSH</h2>
<p>有同学说,这不是很简答么,像下面这样不就好了?</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git remote add remote2 xxx
<span class="nv">$ </span>git push <span class="nt">--all</span> remote2
</code></pre></div></div>
<p>上面做法的问题在于只能把本地存在的分支推送到remote2,一般公共仓库的话,我们本地都不会有全部远端分支的引用</p>
<p>那怎么办?别急,这事其实还不太简单,下面先来研究下push命令,平时推送分支,我们都是直接 push</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git push
</code></pre></div></div>
<p>其实 git 在 push 时会自动填充填充缺省参数,比如上面的命令完整命令应该是下面这样</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git push origin branch <span class="c"># 自动填充源 origin 以及当前分支 branch</span>
</code></pre></div></div>
<p>git在push时除了自动填充参数,还会自动展开分支,上面的命令展开后如下</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git push origin refs/heads/branch:refs/heads/branch
</code></pre></div></div>
<p>查看下.git下的refs目录,就会发现git会把远端的分支存放在remotes目录</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.git/refs/remotes/origin/xxx
</code></pre></div></div>
<p>那我们可以用下面的命令进行仓库迁移</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git push remote2 refs/remotes/origin/<span class="k">*</span>:refs/heads/<span class="k">*</span>
</code></pre></div></div>
<p>但这样做还有个问题不能解决,就是tag引用还是丢失了,下面介绍的方法可以解决这个问题</p>
<h2 id="裸仓库">裸仓库</h2>
<p>git有一个裸仓库的概念,只要在克隆时建一个–bare参数即可</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone <span class="nt">--bare</span> xxx
</code></pre></div></div>
<p>裸仓库是没有工作目录的,克隆完的仓库只有.git下的内容,同时也不能再做更新工作</p>
<p>但裸仓库可以再次push到另一个源,刚好可以完成我们仓库迁移的任务</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git remote add remote2 xxx
<span class="nv">$ </span>git push <span class="nt">--mirror</span> remote2
</code></pre></div></div>
<h2 id="镜像仓库">镜像仓库</h2>
<p>镜像仓库和裸仓库类似,但裸仓库不能更新,镜像仓库可以实现后续的更新工作</p>
<p>镜像仓库的创建只需要在克隆的时候加上–mirror参数即可</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone <span class="nt">--mirror</span> xxx
</code></pre></div></div>
<p>镜像仓库的push操作和裸仓库一样,也可以完成我们仓库迁移的任务</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git remote add remote2 xxx
<span class="nv">$ </span>git push <span class="nt">--mirror</span> remote2
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>本文总结了git迁移仓库的不同方法和区别,迁移源的问题,推荐大家使用裸仓库的方法,希望同学们遇到问题可以多思考多总结</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="https://stackoverflow.com/questions/37884832/git-push-all-branches-from-one-remote-to-another-remote">git push all branches from one remote to another remote</a></li>
<li><a href="https://stackoverflow.com/questions/3959924/whats-the-difference-between-git-clone-mirror-and-git-clone-bare">What’s the difference between git clone –mirror and git clone –bare</a></li>
</ul>
深拷贝的终极探索
2018-10-10T00:00:00+00:00
http://yanhaijing.com/javascript/2018/10/10/clone-deep
<blockquote>
<p>本文已收录到《<a href="https://yanhaijing.com/tags/#jsmini-ref">jsmini系列文章</a>》</p>
</blockquote>
<p><strong>划重点,这是一道面试必考题,我就问过很多面试者这个问题,✧(≖ ◡ ≖✿)嘿嘿</strong></p>
<p>首先这是一道非常棒的面试题,可以考察面试者的很多方面,比如基本功,代码能力,逻辑能力,而且进可攻,退可守,针对不同级别的人可以考察不同难度,比如漂亮妹子就出1☆题,(*^__^*) 嘻嘻……</p>
<p>一般在面试者回答出问题后,我总能够潇洒的再抛出一些问题,看着面试者露出惊异的眼神,默默一转身,深藏功与名</p>
<p>本文我将给大家破解深拷贝的谜题,由浅入深,环环相扣,总共涉及4种深拷贝方式,每种方式都有自己的特点和个性</p>
<h2 id="深拷贝-vs-浅拷贝">深拷贝 VS 浅拷贝</h2>
<p>再开始之前需要先给同学科普下什么是深拷贝,和深拷贝有关系的另个一术语是浅拷贝又是什么意思呢?如果对这部分部分内容了解的同学可以跳过</p>
<p>其实深拷贝和浅拷贝都是针对的引用类型,JS中的变量类型分为值类型(基本类型)和引用类型;对值类型进行复制操作会对值进行一份拷贝,而对引用类型赋值,则会进行地址的拷贝,最终两个变量指向同一份数据</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 基本类型</span>
<span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span>
<span class="nx">a</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">);</span> <span class="c1">// 2, 1 ,a b指向不同的数据</span>
<span class="c1">// 引用类型指向同一份数据</span>
<span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">{</span><span class="na">c</span><span class="p">:</span> <span class="mi">1</span><span class="p">};</span>
<span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">c</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">.</span><span class="nx">c</span><span class="p">,</span> <span class="nx">b</span><span class="p">.</span><span class="nx">c</span><span class="p">);</span> <span class="c1">// 2, 2 全是2,a b指向同一份数据</span>
</code></pre></div></div>
<p>对于引用类型,会导致a b指向同一份数据,此时如果对其中一个进行修改,就会影响到另外一个,有时候这可能不是我们想要的结果,如果对这种现象不清楚的话,还可能造成不必要的bug</p>
<p>那么如何切断a和b之间的关系呢,可以拷贝一份a的数据,根据拷贝的层级不同可以分为浅拷贝和深拷贝,浅拷贝就是只进行一层拷贝,深拷贝就是无限层级拷贝</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">a1</span> <span class="o">=</span> <span class="p">{</span><span class="na">b</span><span class="p">:</span> <span class="p">{</span><span class="na">c</span><span class="p">:</span> <span class="p">{}};</span>
<span class="kd">var</span> <span class="nx">a2</span> <span class="o">=</span> <span class="nx">shallowClone</span><span class="p">(</span><span class="nx">a1</span><span class="p">);</span> <span class="c1">// 浅拷贝</span>
<span class="nx">a2</span><span class="p">.</span><span class="nx">b</span><span class="p">.</span><span class="nx">c</span> <span class="o">===</span> <span class="nx">a1</span><span class="p">.</span><span class="nx">b</span><span class="p">.</span><span class="nx">c</span> <span class="c1">// true</span>
<span class="kd">var</span> <span class="nx">a3</span> <span class="o">=</span> <span class="nx">clone</span><span class="p">(</span><span class="nx">a1</span><span class="p">);</span> <span class="c1">// 深拷贝</span>
<span class="nx">a3</span><span class="p">.</span><span class="nx">b</span><span class="p">.</span><span class="nx">c</span> <span class="o">===</span> <span class="nx">a1</span><span class="p">.</span><span class="nx">b</span><span class="p">.</span><span class="nx">c</span> <span class="c1">// false</span>
</code></pre></div></div>
<p>浅拷贝的实现非常简单,而且还有多种方法,其实就是遍历对象属性的问题,这里只给出一种,如果看不懂下面的方法,或对其他方法感兴趣,可以看我的<a href="https://yanhaijing.com/javascript/2015/05/09/diff-between-keys-getOwnPropertyNames-forin/">这篇文章</a></p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">shallowClone</span><span class="p">(</span><span class="nx">source</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">target</span> <span class="o">=</span> <span class="p">{};</span>
<span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="k">in</span> <span class="nx">source</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">source</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">i</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">target</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="nx">source</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">target</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="最简单的深拷贝">最简单的深拷贝</h2>
<p>深拷贝的问题其实可以分解成两个问题,浅拷贝+递归,什么意思呢?假设我们有如下数据</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">a1</span> <span class="o">=</span> <span class="p">{</span><span class="na">b</span><span class="p">:</span> <span class="p">{</span><span class="na">c</span><span class="p">:</span> <span class="p">{</span><span class="na">d</span><span class="p">:</span> <span class="mi">1</span><span class="p">}};</span>
</code></pre></div></div>
<p>只需稍加改动上面浅拷贝的代码即可,注意区别</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">clone</span><span class="p">(</span><span class="nx">source</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">target</span> <span class="o">=</span> <span class="p">{};</span>
<span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="k">in</span> <span class="nx">source</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">source</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">i</span><span class="p">))</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">source</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">target</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="nx">clone</span><span class="p">(</span><span class="nx">source</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span> <span class="c1">// 注意这里</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">target</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="nx">source</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">target</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>大部分人都能写出上面的代码,但当我问上面的代码有什么问题吗?就很少有人答得上来了,聪明的你能找到问题吗?</p>
<p>其实上面的代码问题太多了,先来举几个例子吧</p>
<ul>
<li>没有对参数做检验</li>
<li>判断是否对象的逻辑不够严谨</li>
<li>没有考虑数组的兼容</li>
</ul>
<p>(⊙o⊙),下面我们来看看各个问题的解决办法,首先我们需要抽象一个判断对象的方法,其实比较常用的判断对象的方法如下,其实下面的方法也有问题,但如果能够回答上来那就非常不错了,如果完美的解决办法感兴趣,不妨看看<a href="https://github.com/jsmini/type/blob/master/src/index.js">这里吧</a></p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">isObject</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">toString</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">[object Object]</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>函数需要校验参数,如果不是对象的话直接返回</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">clone</span><span class="p">(</span><span class="nx">source</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isObject</span><span class="p">(</span><span class="nx">source</span><span class="p">))</span> <span class="k">return</span> <span class="nx">source</span><span class="p">;</span>
<span class="c1">// xxx</span>
<span class="p">}</span>
</code></pre></div></div>
<p>关于第三个问题,嗯,就留给大家自己思考吧,本文为了减轻大家的负担,就不考虑数组的情况了,其实ES6之后还要考虑set, map, weakset, weakmap,/(ㄒoㄒ)/~~</p>
<p>其实吧这三个都是小问题,其实递归方法最大的问题在于爆栈,当数据的层次很深是就会栈溢出</p>
<p>下面的代码可以生成指定深度和每层广度的代码,这段代码我们后面还会再次用到</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">createData</span><span class="p">(</span><span class="nx">deep</span><span class="p">,</span> <span class="nx">breadth</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">data</span> <span class="o">=</span> <span class="p">{};</span>
<span class="kd">var</span> <span class="nx">temp</span> <span class="o">=</span> <span class="nx">data</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">deep</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">temp</span> <span class="o">=</span> <span class="nx">temp</span><span class="p">[</span><span class="dl">'</span><span class="s1">data</span><span class="dl">'</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">j</span> <span class="o"><</span> <span class="nx">breadth</span><span class="p">;</span> <span class="nx">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">temp</span><span class="p">[</span><span class="nx">j</span><span class="p">]</span> <span class="o">=</span> <span class="nx">j</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">data</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">createData</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span> <span class="c1">// 1层深度,每层有3个数据 {data: {0: 0, 1: 1, 2: 2}}</span>
<span class="nx">createData</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="c1">// 3层深度,每层有0个数据 {data: {data: {data: {}}}}</span>
</code></pre></div></div>
<p>当clone层级很深的话就会栈溢出,但数据的广度不会造成溢出</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">clone</span><span class="p">(</span><span class="nx">createData</span><span class="p">(</span><span class="mi">1000</span><span class="p">));</span> <span class="c1">// ok</span>
<span class="nx">clone</span><span class="p">(</span><span class="nx">createData</span><span class="p">(</span><span class="mi">10000</span><span class="p">));</span> <span class="c1">// Maximum call stack size exceeded</span>
<span class="nx">clone</span><span class="p">(</span><span class="nx">createData</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">100000</span><span class="p">));</span> <span class="c1">// ok 广度不会溢出</span>
</code></pre></div></div>
<p>其实大部分情况下不会出现这么深层级的数据,但这种方式还有一个致命的问题,就是循环引用,举个例子</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">a</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span>
<span class="nx">clone</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="c1">// Maximum call stack size exceeded 直接死循环了有没有,/(ㄒoㄒ)/~~</span>
</code></pre></div></div>
<p>关于循环引用的问题解决思路有两种,一直是循环检测,一种是暴力破解,关于循环检测大家可以自己思考下;关于暴力破解我们会在下面的内容中详细讲解</p>
<h2 id="一行代码的深拷贝">一行代码的深拷贝</h2>
<p>有些同学可能见过用系统自带的JSON来做深拷贝的例子,下面来看下代码实现</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">cloneJSON</span><span class="p">(</span><span class="nx">source</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">source</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>
<p>其实我第一次简单这个方法的时候,由衷的表示佩服,其实利用工具,达到目的,是非常聪明的做法</p>
<p>下面来测试下cloneJSON有没有溢出的问题,看起来cloneJSON内部也是使用递归的方式</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">cloneJSON</span><span class="p">(</span><span class="nx">createData</span><span class="p">(</span><span class="mi">10000</span><span class="p">));</span> <span class="c1">// Maximum call stack size exceeded</span>
</code></pre></div></div>
<p>既然是用了递归,那循环引用呢?并没有因为死循环而导致栈溢出啊,原来是JSON.stringify内部做了循环引用的检测,正是我们上面提到破解循环引用的第一种方法:循环检测</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">a</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span>
<span class="nx">cloneJSON</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="c1">// Uncaught TypeError: Converting circular structure to JSON</span>
</code></pre></div></div>
<h2 id="破解递归爆栈">破解递归爆栈</h2>
<p>其实破解递归爆栈的方法有两条路,第一种是消除尾递归,但在这个例子中貌似行不通,第二种方法就是干脆不用递归,改用循环,当我提出用循环来实现时,基本上90%的前端都是写不出来的代码的,这其实让我很震惊</p>
<p>举个例子,假设有如下的数据结构</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">a1</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="na">a2</span><span class="p">:</span> <span class="p">{</span>
<span class="na">b1</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="na">b2</span><span class="p">:</span> <span class="p">{</span>
<span class="na">c1</span><span class="p">:</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这不就是一个树吗,其实只要把数据横过来看就非常明显了</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> a
/ \
a1 a2
| / \
1 b1 b2
| |
1 c1
|
1
</code></pre></div></div>
<p>用循环遍历一棵树,需要借助一个栈,当栈为空时就遍历完了,栈里面存储下一个需要拷贝的节点</p>
<p>首先我们往栈里放入种子数据,<code class="language-plaintext highlighter-rouge">key</code>用来存储放哪一个父元素的那一个子元素拷贝对象</p>
<p>然后遍历当前节点下的子元素,如果是对象就放到栈里,否则直接拷贝</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">cloneLoop</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">root</span> <span class="o">=</span> <span class="p">{};</span>
<span class="c1">// 栈</span>
<span class="kd">const</span> <span class="nx">loopList</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span>
<span class="na">parent</span><span class="p">:</span> <span class="nx">root</span><span class="p">,</span>
<span class="na">key</span><span class="p">:</span> <span class="kc">undefined</span><span class="p">,</span>
<span class="na">data</span><span class="p">:</span> <span class="nx">x</span><span class="p">,</span>
<span class="p">}</span>
<span class="p">];</span>
<span class="k">while</span><span class="p">(</span><span class="nx">loopList</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 深度优先</span>
<span class="kd">const</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">loopList</span><span class="p">.</span><span class="nx">pop</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">parent</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">parent</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">key</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">key</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
<span class="c1">// 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素</span>
<span class="kd">let</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">parent</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">key</span> <span class="o">!==</span> <span class="dl">'</span><span class="s1">undefined</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">res</span> <span class="o">=</span> <span class="nx">parent</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
<span class="p">}</span>
<span class="k">for</span><span class="p">(</span><span class="kd">let</span> <span class="nx">k</span> <span class="k">in</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">k</span><span class="p">))</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">data</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 下一次循环</span>
<span class="nx">loopList</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
<span class="na">parent</span><span class="p">:</span> <span class="nx">res</span><span class="p">,</span>
<span class="na">key</span><span class="p">:</span> <span class="nx">k</span><span class="p">,</span>
<span class="na">data</span><span class="p">:</span> <span class="nx">data</span><span class="p">[</span><span class="nx">k</span><span class="p">],</span>
<span class="p">});</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">res</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span> <span class="o">=</span> <span class="nx">data</span><span class="p">[</span><span class="nx">k</span><span class="p">];</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">root</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>改用循环后,再也不会出现爆栈的问题了,但是对于循环引用依然无力应对</p>
<h2 id="破解循环引用">破解循环引用</h2>
<p>有没有一种办法可以破解循环应用呢?别着急,我们先来看另一个问题,上面的三种方法都存在的一个问题就是引用丢失,这在某些情况下也许是不能接受的</p>
<p>举个例子,假如一个对象a,a下面的两个键值都引用同一个对象b,经过深拷贝后,a的两个键值会丢失引用关系,从而变成两个不同的对象,o(╯□╰)o</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="p">{};</span>
<span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">{</span><span class="na">a1</span><span class="p">:</span> <span class="nx">b</span><span class="p">,</span> <span class="na">a2</span><span class="p">:</span> <span class="nx">b</span><span class="p">};</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">a1</span> <span class="o">===</span> <span class="nx">a</span><span class="p">.</span><span class="nx">a2</span> <span class="c1">// true</span>
<span class="kd">var</span> <span class="nx">c</span> <span class="o">=</span> <span class="nx">clone</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span>
<span class="nx">c</span><span class="p">.</span><span class="nx">a1</span> <span class="o">===</span> <span class="nx">c</span><span class="p">.</span><span class="nx">a2</span> <span class="c1">// false</span>
</code></pre></div></div>
<p>如果我们发现个新对象就把这个对象和他的拷贝存下来,每次拷贝对象前,都先看一下这个对象是不是已经拷贝过了,如果拷贝过了,就不需要拷贝了,直接用原来的,这样我们就能够保留引用关系了,✧(≖ ◡ ≖✿)嘿嘿</p>
<p>但是代码怎么写呢,o(╯□╰)o,别急往下看,其实和循环的代码大体一样,不一样的地方我用<code class="language-plaintext highlighter-rouge">// ==========</code>标注出来了</p>
<p>引入一个数组<code class="language-plaintext highlighter-rouge">uniqueList</code>用来存储已经拷贝的数组,每次循环遍历时,先判断对象是否在<code class="language-plaintext highlighter-rouge">uniqueList</code>中了,如果在的话就不执行拷贝逻辑了</p>
<p><code class="language-plaintext highlighter-rouge">find</code>是抽象的一个函数,其实就是遍历<code class="language-plaintext highlighter-rouge">uniqueList</code></p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 保持引用关系</span>
<span class="kd">function</span> <span class="nx">cloneForce</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// =============</span>
<span class="kd">const</span> <span class="nx">uniqueList</span> <span class="o">=</span> <span class="p">[];</span> <span class="c1">// 用来去重</span>
<span class="c1">// =============</span>
<span class="kd">let</span> <span class="nx">root</span> <span class="o">=</span> <span class="p">{};</span>
<span class="c1">// 循环数组</span>
<span class="kd">const</span> <span class="nx">loopList</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span>
<span class="na">parent</span><span class="p">:</span> <span class="nx">root</span><span class="p">,</span>
<span class="na">key</span><span class="p">:</span> <span class="kc">undefined</span><span class="p">,</span>
<span class="na">data</span><span class="p">:</span> <span class="nx">x</span><span class="p">,</span>
<span class="p">}</span>
<span class="p">];</span>
<span class="k">while</span><span class="p">(</span><span class="nx">loopList</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 深度优先</span>
<span class="kd">const</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">loopList</span><span class="p">.</span><span class="nx">pop</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">parent</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">parent</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">key</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">key</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">data</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
<span class="c1">// 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素</span>
<span class="kd">let</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">parent</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">key</span> <span class="o">!==</span> <span class="dl">'</span><span class="s1">undefined</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">res</span> <span class="o">=</span> <span class="nx">parent</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="p">{};</span>
<span class="p">}</span>
<span class="c1">// =============</span>
<span class="c1">// 数据已经存在</span>
<span class="kd">let</span> <span class="nx">uniqueData</span> <span class="o">=</span> <span class="nx">find</span><span class="p">(</span><span class="nx">uniqueList</span><span class="p">,</span> <span class="nx">data</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">uniqueData</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">parent</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="nx">uniqueData</span><span class="p">.</span><span class="nx">target</span><span class="p">;</span>
<span class="k">continue</span><span class="p">;</span> <span class="c1">// 中断本次循环</span>
<span class="p">}</span>
<span class="c1">// 数据不存在</span>
<span class="c1">// 保存源数据,在拷贝数据中对应的引用</span>
<span class="nx">uniqueList</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
<span class="na">source</span><span class="p">:</span> <span class="nx">data</span><span class="p">,</span>
<span class="na">target</span><span class="p">:</span> <span class="nx">res</span><span class="p">,</span>
<span class="p">});</span>
<span class="c1">// =============</span>
<span class="k">for</span><span class="p">(</span><span class="kd">let</span> <span class="nx">k</span> <span class="k">in</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">k</span><span class="p">))</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">data</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 下一次循环</span>
<span class="nx">loopList</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
<span class="na">parent</span><span class="p">:</span> <span class="nx">res</span><span class="p">,</span>
<span class="na">key</span><span class="p">:</span> <span class="nx">k</span><span class="p">,</span>
<span class="na">data</span><span class="p">:</span> <span class="nx">data</span><span class="p">[</span><span class="nx">k</span><span class="p">],</span>
<span class="p">});</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">res</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span> <span class="o">=</span> <span class="nx">data</span><span class="p">[</span><span class="nx">k</span><span class="p">];</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">root</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">find</span><span class="p">(</span><span class="nx">arr</span><span class="p">,</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span><span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">source</span> <span class="o">===</span> <span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>下面来验证一下效果,amazing</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="p">{};</span>
<span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">{</span><span class="na">a1</span><span class="p">:</span> <span class="nx">b</span><span class="p">,</span> <span class="na">a2</span><span class="p">:</span> <span class="nx">b</span><span class="p">};</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">a1</span> <span class="o">===</span> <span class="nx">a</span><span class="p">.</span><span class="nx">a2</span> <span class="c1">// true</span>
<span class="kd">var</span> <span class="nx">c</span> <span class="o">=</span> <span class="nx">cloneForce</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span>
<span class="nx">c</span><span class="p">.</span><span class="nx">a1</span> <span class="o">===</span> <span class="nx">c</span><span class="p">.</span><span class="nx">a2</span> <span class="c1">// true</span>
</code></pre></div></div>
<p>接下来再说一下如何破解循环引用,等一下,上面的代码好像可以破解循环引用啊,赶紧验证一下</p>
<p>惊不惊喜,(*^__^*) 嘻嘻……</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">a</span><span class="p">.</span><span class="nx">a</span> <span class="o">=</span> <span class="nx">a</span><span class="p">;</span>
<span class="nx">cloneForce</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span>
</code></pre></div></div>
<p>看起来完美的<code class="language-plaintext highlighter-rouge">cloneForce</code>是不是就没问题呢?<code class="language-plaintext highlighter-rouge">cloneForce</code>有两个问题</p>
<p>第一个问题,所谓成也萧何,败也萧何,如果保持引用不是你想要的,那就不能用<code class="language-plaintext highlighter-rouge">cloneForce</code>了;</p>
<p>第二个问题,<code class="language-plaintext highlighter-rouge">cloneForce</code>在对象数量很多时会出现很大的问题,如果数据量很大不适合使用<code class="language-plaintext highlighter-rouge">cloneForce</code></p>
<h2 id="性能对比">性能对比</h2>
<p>上边的内容还是有点难度,下面我们来点更有难度的,对比一下不同方法的性能</p>
<p>我们先来做实验,看数据,影响性能的原因有两个,一个是深度,一个是每层的广度,我们采用固定一个变量,只让一个变量变化的方式来测试性能</p>
<p>测试的方法是在指定的时间内,深拷贝执行的次数,次数越多,证明性能越好</p>
<p>下面的<code class="language-plaintext highlighter-rouge">runTime</code>是测试代码的核心片段,下面的例子中,我们可以测试在2秒内运行<code class="language-plaintext highlighter-rouge">clone(createData(500, 1)</code>的次数</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">runTime</span><span class="p">(</span><span class="nx">fn</span><span class="p">,</span> <span class="nx">time</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">stime</span> <span class="o">=</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span> <span class="o">-</span> <span class="nx">stime</span> <span class="o"><</span> <span class="nx">time</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">fn</span><span class="p">();</span>
<span class="nx">count</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">count</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">runTime</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="nx">clone</span><span class="p">(</span><span class="nx">createData</span><span class="p">(</span><span class="mi">500</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="p">},</span> <span class="mi">2000</span><span class="p">);</span>
</code></pre></div></div>
<p>下面来做第一个测试,将广度固定在100,深度由小到大变化,记录1秒内执行的次数</p>
<table>
<thead>
<tr>
<th>深度</th>
<th>clone</th>
<th>cloneJSON</th>
<th>cloneLoop</th>
<th>cloneForce</th>
</tr>
</thead>
<tbody>
<tr>
<td>500</td>
<td>351</td>
<td>212</td>
<td>338</td>
<td>372</td>
</tr>
<tr>
<td>1000</td>
<td>174</td>
<td>104</td>
<td>175</td>
<td>143</td>
</tr>
<tr>
<td>1500</td>
<td>116</td>
<td>67</td>
<td>112</td>
<td>82</td>
</tr>
<tr>
<td>2000</td>
<td>92</td>
<td>50</td>
<td>88</td>
<td>69</td>
</tr>
</tbody>
</table>
<p>将上面的数据做成表格可以发现,一些规律</p>
<ul>
<li>随着深度变小,相互之间的差异在变小</li>
<li>clone和cloneLoop的差别并不大</li>
<li>cloneLoop > cloneForce > cloneJSON</li>
</ul>
<p><img src="/blog/539.png" alt="" /></p>
<p>我们先来分析下各个方法的时间复杂度问题,各个方法要做的相同事情,这里就不计算,比如循环对象,判断是否为对象</p>
<ul>
<li>clone时间 = 创建递归函数 + 每个对象处理时间</li>
<li>cloneJSON时间 = 循环检测 + 每个对象处理时间 * 2 (递归转字符串 + 递归解析)</li>
<li>cloneLoop时间 = 每个对象处理时间</li>
<li>cloneForce时间 = 判断对象是否缓存中 + 每个对象处理时间</li>
</ul>
<p>cloneJSON的速度只有clone的50%,很容易理解,因为其会多进行一次递归时间</p>
<p>cloneForce由于要判断对象是否在缓存中,而导致速度变慢,我们来计算下判断逻辑的时间复杂度,假设对象的个数是n,则其时间复杂度为O(n2),对象的个数越多,cloneForce的速度会越慢</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1 + 2 + 3 ... + n = n^2/2 - 1
</code></pre></div></div>
<p>关于clone和cloneLoop这里有一点问题,看起来实验结果和推理结果不一致,其中必有蹊跷</p>
<p>接下来做第二个测试,将深度固定在10000,广度固定为0,记录2秒内执行的次数</p>
<table>
<thead>
<tr>
<th>宽度</th>
<th>clone</th>
<th>cloneJSON</th>
<th>cloneLoop</th>
<th>cloneForce</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>13400</td>
<td>3272</td>
<td>14292</td>
<td>989</td>
</tr>
</tbody>
</table>
<p>排除宽度的干扰,来看看深度对各个方法的影响</p>
<ul>
<li>随着对象的增多,cloneForce的性能低下凸显</li>
<li>cloneJSON的性能也大打折扣,这是因为循环检测占用了很多时间</li>
<li>cloneLoop的性能高于clone,可以看出递归新建函数的时间和循环对象比起来可以忽略不计</li>
</ul>
<p>下面我们来测试一下cloneForce的性能极限,这次我们测试运行指定次数需要的时间</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">data1</span> <span class="o">=</span> <span class="nx">createData</span><span class="p">(</span><span class="mi">2000</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">data2</span> <span class="o">=</span> <span class="nx">createData</span><span class="p">(</span><span class="mi">4000</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">data3</span> <span class="o">=</span> <span class="nx">createData</span><span class="p">(</span><span class="mi">6000</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">data4</span> <span class="o">=</span> <span class="nx">createData</span><span class="p">(</span><span class="mi">8000</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">data5</span> <span class="o">=</span> <span class="nx">createData</span><span class="p">(</span><span class="mi">10000</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="nx">cloneForce</span><span class="p">(</span><span class="nx">data1</span><span class="p">)</span>
<span class="nx">cloneForce</span><span class="p">(</span><span class="nx">data2</span><span class="p">)</span>
<span class="nx">cloneForce</span><span class="p">(</span><span class="nx">data3</span><span class="p">)</span>
<span class="nx">cloneForce</span><span class="p">(</span><span class="nx">data4</span><span class="p">)</span>
<span class="nx">cloneForce</span><span class="p">(</span><span class="nx">data5</span><span class="p">)</span>
</code></pre></div></div>
<p>通过测试发现,其时间成指数级增长,当对象个数大于万级别,就会有300ms以上的延迟</p>
<p><img src="/blog/540.png" alt="" /></p>
<h2 id="总结">总结</h2>
<p>尺有所短寸有所长,无关乎好坏优劣,其实每种方法都有自己的优缺点,和适用场景,人尽其才,物尽其用,方是真理</p>
<p>下面对各种方法进行对比,希望给大家提供一些帮助</p>
<table>
<thead>
<tr>
<th> </th>
<th>clone</th>
<th>cloneJSON</th>
<th>cloneLoop</th>
<th>cloneForce</th>
</tr>
</thead>
<tbody>
<tr>
<td>难度</td>
<td>☆☆</td>
<td>☆</td>
<td>☆☆☆</td>
<td>☆☆☆☆</td>
</tr>
<tr>
<td>兼容性</td>
<td>ie6</td>
<td>ie8</td>
<td>ie6</td>
<td>ie6</td>
</tr>
<tr>
<td>循环引用</td>
<td>一层</td>
<td>不支持</td>
<td>一层</td>
<td>支持</td>
</tr>
<tr>
<td>栈溢出</td>
<td>会</td>
<td>会</td>
<td>不会</td>
<td>不会</td>
</tr>
<tr>
<td>保持引用</td>
<td>否</td>
<td>否</td>
<td>否</td>
<td>是</td>
</tr>
<tr>
<td>适合场景</td>
<td>一般数据拷贝</td>
<td>一般数据拷贝</td>
<td>层级很多</td>
<td>保持引用关系</td>
</tr>
</tbody>
</table>
<p>本文的灵感都来自于<a href="https://github.com/jsmini/clone">@jsmini/clone</a>,如果大家想使用文中的4种深拷贝方式,可以直接使用@jsmini/clone这个库</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// npm install --save @jsmini/clone</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">clone</span><span class="p">,</span> <span class="nx">cloneJSON</span><span class="p">,</span> <span class="nx">cloneLoop</span><span class="p">,</span> <span class="nx">cloneForce</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">@jsmini/clone</span><span class="dl">'</span><span class="p">;</span>
</code></pre></div></div>
<p>本文为了简单和易读,示例代码中忽略了一些边界情况,如果想学习生产中的代码,请阅读<a href="https://github.com/jsmini/clone">@jsmini/clone</a>的源码</p>
<p>@jsmini/clone孵化于<a href="https://github.com/jsmini">jsmini</a>,jsmini致力于为大家提供一组小而美,无依赖的高质量库</p>
<p>jsmini的诞生离不开<a href="https://github.com/yanhaijing/jslib-base">jslib-base</a>,感谢jslib-base为jsmini提供了底层技术</p>
<p>最后感谢你阅读了本文,相信现在你能够驾驭任何深拷贝的问题了,如果有什么疑问,欢迎和我讨论</p>
React一线问题十问十答
2018-09-12T00:00:00+00:00
http://yanhaijing.com/web/2018/09/12/react-qa
<p>最近在开源中国搞了个问答活动,收到了不少网友关于React的提问,本文挑选出一些比较典型的问题总结一下,对问答感兴趣的同学可以移步<a href="https://www.oschina.net/question/2720166_2285981">这里</a></p>
<h2 id="问答">问答</h2>
<blockquote>
<p>问:React如何快速的入手,有什么学习方法推荐的吗?</p>
</blockquote>
<p>答:快速上手的话,建议阅读一些入门教程,比如阮一峰老师的博客,接下来要实践一下,比如简单实现一个小程序,接下来就是在学习和实践中循环了</p>
<p>另外React的官网是非常不错的资料,主要作用可用来查阅资料,如果入门了以后,想要对React有深入的了解,可以关注下我的新书《<a href="https://item.jd.com/12403508.html">React状态管理与同构实战</a>》</p>
<blockquote>
<p>问:问一下React和Vue的区别?React和Vue等框架未来的发展趋势?</p>
<p>从开发效率和学习成本来看,未来会不会出现比现有前端工程化更简洁的前端框架或开发模式呢?</p>
</blockquote>
<p>答:关于React和Vue的区别网上有大把大把的资料,这里就不展开回答了,简单来说Vue是MVVM框架,React是一个View层的抽象解决方案</p>
<p>我认为未来React和Vue会长期并存,都会有很多的工作机会,其实在React和Vue之前,框架也是多个并存的情况,另外个人建议不要太局限于框架,在我看来一个功能用Vue和用React来实现,并没有太大的开发效率上的区别</p>
<p>从开发效率和学习成本来看,未来可能会出现更高效的情况,但从原理上来说,要颠覆现在框架的原理,可能需要很长时间的发展</p>
<p>框架都是用来解决具体问题的,未来不会再出现一个新的react了,颠覆react的一定不是react的效仿者,目前来看react能够坚持很久,但是react也有很多不适合的场景,比如动画就非常不友好,前端框架的发展方向应该是围绕解决问题,提升效率来的</p>
<blockquote>
<p>问:现在框架很多,原始的js这些还没学完,React跟vue这些已经铺天盖地了,而且框架性的东西会要求大家按照他们的语法或者逻辑来做,对于新人重新开始可能没问题,对于老手来说切换的成本大,容易出错,是否有好的办法?</p>
</blockquote>
<p>答:我对于框架的态度一直是工作中用到了才回去真正学习,否则不会去深入了解,所以面对繁多的新技术不要畏惧,要感到高兴,因为这才能证明前端在发展,另外如果原始js还没学完,怎么能自称为老手?</p>
<p>框架只是改变了UI的写法,组件的写法,其实涉及到逻辑部分,都还是原生的js,所以原生js和框架两个都要学好;
现在前端工程师必须有很强的学习能力,不能面向技术编程,而是要面向解决问题编程,不管什么技术,只要能解决问题就是好的,否则一个技术展被淘汰了,还是要被迫的去学习</p>
<p>我现在觉得更大的学习压力是对于新手的,而不是老手的,老手其实只要学一个框架就好了,新手要学的东西远比老手多,所以现在前端的入门越来越难了</p>
<blockquote>
<p>问:刚好最近也在学习React,也接触了Redux这个状态管理工具,对于React的设计思想稍微有了些自己的理解——精简而又复杂,复杂指的是很容易“过度设计”,显得整个项目有点臃肿庞大,对于这个问题,有没有什么组件设计建议呢?</p>
</blockquote>
<p>答:React中一切都是基于组件,但是组件的粒度确认一个问题,到底是该每个按钮设计一个组件,无比细粒度呢?还是很大模块的组粒度呢?</p>
<p>我觉得对于不同场景,应该有不同的设计理念,对于组件库,那么应该设计的竟可能细粒度,这样才能方便组合;
对于业务代码,则不应该设计得过细,会浪费精力,也不宜过大,过大了维护起来也是个问题,还是老规矩,每个组件不要超过300行挺好</p>
<blockquote>
<p>问:怎么正确的理解React的生命周期呢?</p>
</blockquote>
<p>答:每一个软件都会诞生和死亡的一天,React组件也有自己的生命周期,每一个React组件都会经历出生、存在和消亡的过程,在React的世界里,这三个生命周期被称为,挂载(Mounting)、更新(Updating)和卸载(Unmounting)</p>
<p>React为每个过程提供了一些回调函数,称作钩子函数,让我们可以自定义一些事情,如果想了解更多的内容,可以关注下我的新书《<a href="https://item.jd.com/12403508.html">React状态管理与同构实战</a>》</p>
<blockquote>
<p>问:React在什么时候比较适合SSR呢?</p>
</blockquote>
<p>答:React再有node中间层的时候比较适合做SSR,其实是否SSR应该是业务决定的,比如如果你需要做SEO那你就需要SSR,比如新闻网站,内容类网站;对于不需要SEO的系统,比如后端系统,webapp,都是不需要SSR的</p>
<p>想了解更多SSR的内容,可以关注我的新书</p>
<blockquote>
<p>问:1、React在表单处理上有没有比较好的解决方案?目前在一些复杂的表单处理上,需要些大量的handleXXXChange方法,Vue中的v-model要简单许多。此外还有表单校验,包括antd在内的ui框架,在这方面的处理都显得相当复杂,不易维护。</p>
<p>2、在render中使用箭头函数和bind都是不推荐的,但是对于列表遍历中传递当前对象:</p>
<ul>
{items.map((item, index) => (
<li onClick={(e) => this.handleItemClick(e, item, index)}>{item.title}</li>
)}
</ul>
<p>如果不使用箭头函数,有什么比较好的解决方案吗?</p>
</blockquote>
<p>答:</p>
<ol>
<li>
<p>可以写一个babel插件,给react扩展v-model的功能哈</p>
</li>
<li>表单校验,也可以封装一些高阶函数吧</li>
<li>在原生标签的函数中,使用bind或者剪头函数没什么问题哈,或者可以把值放到dom属性中,这样的性能还不如多一个函数快</li>
</ol>
<blockquote>
<p>问:react redux 怎么处理多线程文档,管理多个请求并发问题?</p>
<p>你怎么看待这种用worker的方式启动新线程的?</p>
<p>var worker = new Worker(js file path);</p>
</blockquote>
<p>答:js中是没有多线程的,但是却可以做到请求并发,如果想要多个请求都返回时才处理,可以使用Promise.all</p>
<p>在有密集计算,又不希望卡顿主线程的情况下,原来只能用setTimeout分片,现在可以用worker了,这种方式非常棒,有实际的使用场景</p>
<blockquote>
<p>问:做技术选型如何考量react在开发应用的优略?学习成本?也就是说react实际技术落地的技术点?</p>
</blockquote>
<p>答:这其实就是技术选型的问题,我将回答react到底适合什么场景,技术栈是否应该统一</p>
<p>如果你的页面交互比较简单,其实使用react,并不能比使用jq提升多少效率,对于这种业务,用不用react是无所谓的;
如果你的页面是面向c端的页面,并且需要做seo,那么就要掂量掂量了,因为你使用react的话就需要使用ssr了</p>
<p>对于一个团队来说技术栈肯定是统一更好的,但是还是要看业务是否统一,因为面向c端的和面向内部的系统不统一也可以;
如果你的页面仅仅是内部系统,那么选择react+antdesign是非常好的选择;
如果你的业务是面向c端的,然后页面又比较简单那么react就不是必须的了,也不是最好的选择;
如果你的页面有面向c端的,也有面向内部的,我认为是可以保持两套技术栈的;
两套技术栈就意味着浪费效率,基建可能要做两套,这也是一个问题</p>
<p>所以要综合考虑,结合业务场景,如果你个团队同时存在jq和react是可以接收的,但同时存在react和vue就是不能接受的了</p>
<h2 id="总结">总结</h2>
<p>本文都是网友反馈的问题,包括React入门进阶,React具体细节,技术选型等,如果你也有疑问,欢迎在评论区交流,最后给大家推荐几本不错的React书籍</p>
<ul>
<li><a href="https://item.jd.com/12403508.html">React状态管理与同构实战</a> 我的新书,React入门与进阶首选</li>
<li><a href="https://amazon.cn/gp/product/B01MF8VAAR/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=yanhaijing-23&creative=3200&linkCode=as2&creativeASIN=B01MF8VAAR&linkId=9bf42b4afa4afe15d2942840b492d21a">React全栈</a> 非常不错React进阶书籍</li>
<li><a href="https://amazon.cn/gp/product/B01MQIE77V/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=yanhaijing-23&creative=3200&linkCode=as2&creativeASIN=B01MQIE77V&linkId=54e36a194a1cb98d87bd37d6170aa5b7">深入React技术栈</a> 深入理解,适合深入阅读</li>
</ul>
React最佳实践
2018-09-11T00:00:00+00:00
http://yanhaijing.com/javascript/2018/09/11/react-best-practice
<p>本文介绍React中一些问题的最佳解决办法,和一些常见的错误用法</p>
<h2 id="组件">组件</h2>
<p>React的组件有好几种,对于一个组件该如何归类,应该遵守如下的顺序:</p>
<ul>
<li>Functional Component</li>
<li>PureComponent</li>
<li>Component</li>
</ul>
<p>如果一个组件没有自身状态,应该使用Functional Component;</p>
<p>如果组件是纯组件(属性都是简单值),那么应该使用PureComponent;</p>
<p>否则应该使用Component;</p>
<p>Functional Component</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">Hello</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p"><</span><span class="nt">div</span><span class="p">></span><span class="si">{</span><span class="nx">props</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="p"></</span><span class="nt">div</span><span class="p">></span>
<span class="p">}</span>
<span class="nx">Hello</span><span class="p">.</span><span class="nx">propTypes</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="nx">React</span><span class="p">.</span><span class="nx">PropTypes</span><span class="p">.</span><span class="nx">string</span>
<span class="p">};</span>
<span class="nx">Hello</span><span class="p">.</span><span class="nx">defaultProps</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">yan</span><span class="dl">'</span>
<span class="p">};</span>
</code></pre></div></div>
<p>PureComponent</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">Demo1</span> <span class="kd">extends</span> <span class="nx">Component</span> <span class="p">{</span>
<span class="nx">shouldComponentUpdate</span><span class="p">(</span><span class="nx">nextProps</span><span class="p">,</span> <span class="nx">nextState</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">const</span> <span class="p">{</span> <span class="nx">props</span><span class="p">,</span> <span class="nx">state</span><span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">shallowCompare</span><span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">a</span> <span class="o">===</span> <span class="nx">b</span> <span class="o">||</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">a</span><span class="p">).</span><span class="nx">every</span><span class="p">(</span><span class="nx">k</span> <span class="o">=></span> <span class="nx">a</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span> <span class="o">===</span> <span class="nx">b</span><span class="p">[</span><span class="nx">k</span><span class="p">]);</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">shallowCompare</span><span class="p">(</span><span class="nx">nextProps</span><span class="p">,</span> <span class="nx">props</span><span class="p">)</span> <span class="o">&&</span> <span class="nx">shallowCompare</span><span class="p">(</span><span class="nx">nextState</span><span class="p">,</span> <span class="nx">state</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">Demo2</span> <span class="kd">extends</span> <span class="nx">PureComponent</span> <span class="p">{}</span>
</code></pre></div></div>
<h2 id="属性与状态">属性与状态</h2>
<p>不要把全部属性放到state!不要把全部属性放到state!</p>
<p>不要给属性赋值!不要给属性赋值!不要给属性赋值!</p>
<p>属性应该尽可能使用简单值,最好不要使用复杂数据结构</p>
<p>每个属性都应该有类型检测,PropType不可省略</p>
<p>属性应该提供默认值,对于必须传参的属性,应该添加isRequired</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">Hello</span> <span class="kd">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
<span class="kd">static</span> <span class="nx">propTypes</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="nx">React</span><span class="p">.</span><span class="nx">PropTypes</span><span class="p">.</span><span class="nx">string</span><span class="p">.</span><span class="nx">isRequired</span><span class="p">,</span>
<span class="na">sex</span><span class="p">:</span> <span class="nx">React</span><span class="p">.</span><span class="nx">PropTypes</span><span class="p">.</span><span class="nx">number</span>
<span class="p">}</span>
<span class="kd">static</span> <span class="nx">defaultProps</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">sex</span><span class="p">:</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="事件和回调">事件和回调</h2>
<p>对于绑定的事件和传递给子组件的回调函数,统一使用onXXX前缀</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">User</span> <span class="p">{</span>
<span class="c1">// good</span>
<span class="nx">onClick</span><span class="p">()</span> <span class="p">{</span>
<span class="p">}</span>
<span class="c1">// bad</span>
<span class="nx">handleClick</span><span class="p">()</span> <span class="p">{</span>
<span class="p">}</span>
<span class="c1">// bad</span>
<span class="nx">saveCallback</span><span class="p">()</span> <span class="p">{</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>事件和回调的this绑定应该在constructor中完成,可以提升新能</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">User</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// good</span>
<span class="k">this</span><span class="p">.</span><span class="nx">onClick</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">onClick</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">onClick2</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{}</span>
<span class="nx">onClick</span><span class="p">()</span> <span class="p">{}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// bad</span>
<span class="k">return</span> <span class="p"><</span><span class="nt">div</span> <span class="na">onClick</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">onClick</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">)</span><span class="si">}</span><span class="p">></</span><span class="nt">div</span><span class="p">></span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>如果在render中需要给回调函数绑定部分参数,那么应该把这个过程下放到子组件完成</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">User</span> <span class="p">{</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="nx">list</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">item</span> <span class="o">=></span> <span class="p">(</span>
<span class="c1">// bad</span>
<span class="p"><</span><span class="nc">UserDetail</span> <span class="na">onCallback</span><span class="p">=</span><span class="si">{</span><span class="p">()</span> <span class="o">=></span> <span class="k">this</span><span class="p">.</span><span class="nx">onCallback</span><span class="p">(</span><span class="nx">item</span><span class="p">.</span><span class="nx">name</span><span class="p">)</span><span class="si">}</span> <span class="p">/></span>
<span class="p">)))</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">User</span> <span class="p">{</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="nx">list</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">item</span> <span class="o">=></span> <span class="p">(</span>
<span class="c1">// good</span>
<span class="p"><</span><span class="nc">UserDetail</span> <span class="na">name</span><span class="p">=</span><span class="si">{</span><span class="nx">item</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span> <span class="na">onCallback</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">onCallback</span><span class="si">}</span> <span class="p">/></span>
<span class="p">)))</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="反模式">反模式</h2>
<h3 id="key的问题">key的问题</h3>
<p>循环中的组件都要添加key,key不应该是每次生成的guid或index,应该是数据的id</p>
<p>如果没有id,初始化时可以填充guid</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="nx">list</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">item</span> <span class="o">=></span> <span class="p">(</span>
<span class="c1">// good</span>
<span class="p"><</span><span class="nc">ListItem</span> <span class="na">key</span><span class="p">=</span><span class="si">{</span><span class="nx">item</span><span class="p">.</span><span class="nx">id</span><span class="si">}</span><span class="p">/></span>
<span class="p">)))</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="nx">list</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">item</span><span class="p">,</span> <span class="nx">index</span><span class="p">)</span> <span class="o">=></span> <span class="p">(</span>
<span class="c1">// bad</span>
<span class="p"><</span><span class="nc">ListItem</span> <span class="na">key</span><span class="p">=</span><span class="si">{</span><span class="nx">index</span><span class="si">}</span><span class="p">/></span>
<span class="p">)))</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="nx">list</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">item</span> <span class="o">=></span> <span class="p">(</span>
<span class="c1">// bad</span>
<span class="p"><</span><span class="nc">ListItem</span> <span class="p">/></span>
<span class="p">)))</span>
<span class="p">}</span>
<span class="c1">// good</span>
<span class="kd">const</span> <span class="nx">list</span> <span class="o">=</span> <span class="nx">list</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">x</span> <span class="o">=></span> <span class="p">({...</span><span class="nx">x</span><span class="p">,</span> <span class="na">id</span><span class="p">:</span> <span class="nx">guid</span><span class="p">()}))</span>
</code></pre></div></div>
<h3 id="属性同步问题">属性同步问题</h3>
<p>不应该把属性拷贝到state,除非是默认值</p>
<h2 id="总结">总结</h2>
<p>正确的使用React能够事半功倍,也能避免一些问题</p>
如何写一个现代的JavaScript库
2018-08-17T00:00:00+00:00
http://yanhaijing.com/javascript/2018/08/17/2020-js-lib
<blockquote>
<p>本文已收录到《<a href="https://yanhaijing.com/tags/#jsmini-ref">jsmini系列文章</a>》</p>
</blockquote>
<p>我写过一些<a href="https://github.com/yanhaijing">开源项目</a>,在开源方面有一些经验,最近看到了阮老师的微博(好吧确实是几年前看到的了),深有感触,现在一个开源项目涉及的东西确实挺多的,特别是对于新手来说非常不友好</p>
<p><img src="/blog/529.png" alt="" /></p>
<p>最近我写了一个<a href="https://github.com/yanhaijing/jslib-base">jslib-base</a>,旨在从多方面快速帮大家搭建一个标准的js库,本文将以jslib-base为例,介绍写一个开源库的知识</p>
<blockquote>
<p>jslib-base 最好用的js第三方库脚手架,赋能js第三方库开源,让开发一个js库更简单,更专业</p>
</blockquote>
<h2 id="文档">文档</h2>
<p>所谓代码未动,文档先行,文档对于一个项目非常重要,一个项目的文档包括</p>
<ul>
<li>README.md</li>
<li>TODO.md</li>
<li>CHANGELOG.md</li>
<li>LICENSE</li>
<li>doc</li>
</ul>
<h3 id="readmemd">README.md</h3>
<p>README是一个项目的门面,应该简单明了的呈现用户最关心的问题,一个开源库的用户包括使用者和贡献者,所以一个文档应该包括项目简介,使用者指南,贡献者指南三部分</p>
<p>项目简介用该简单介绍项目功能,使用场景,兼容性的相关知识,这里重点介绍下徽章,相信大家都见过别人项目中的徽章,如下所示</p>
<p><img src="/blog/530.png" alt="" /></p>
<p>徽章通过更直观的方式,将更多的信息呈现出来,还能够提高颜值,有一个网站专门制作各种徽章,可以看<a href="https://shields.io/#/">这里</a></p>
<p>这里有一个README的<a href="https://github.com/yanhaijing/jslib-base/blob/master/README.md">完整的例子</a></p>
<h3 id="todomd">TODO.md</h3>
<p>TODO应该记录项目的未来计划,这对于贡献者和使用者都有很重要的意义,下面是TODO的例子</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- [X] 已完成
- [ ] 未完成
</code></pre></div></div>
<h3 id="changelogmd">CHANGELOG.md</h3>
<p>CHANGELOG记录项目的变更日志,对项目使用者非常重要,特别是在升级使用版本时,CHANGELOG需要记录项目的版本,发版时间和版本变更记录</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>## 0.1.0 / 2018-10-6
- 新增xxx功能
- 删除xxx功能
- 更改xxx功能
</code></pre></div></div>
<h3 id="license">LICENSE</h3>
<p>开源项目必须要选择一个协议,因为没有协议的项目是没有人敢使用的,关于不同协议的区别可以看下面这张图(出自阮老师博客),我的建议是选择MIT或者BSD协议</p>
<p><img src="/blog/531.png" alt="" /></p>
<h3 id="doc">doc</h3>
<p>开源项目还应该提供详细的使用文档,一份详细文档的每个函数介绍都应该包括如下信息:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>函数简单介绍
函数详细介绍
函数参数和返回值(要遵守下面的例子的规则)
- param {string} name1 name1描述
- return {string} 返回值描述
举个例子(要包含代码用例)
// 代码
特殊说明,比如特殊情况下会报错等
</code></pre></div></div>
<h2 id="构建">构建</h2>
<p>理想的情况如下:</p>
<ul>
<li>库开发者美滋滋的写ES6+的代码;</li>
<li>库使用者能够运行在浏览器(ie6-11)和node(0.12-10)中</li>
<li>库使用者能够使用AMD或CMD模块方案</li>
<li>库使用者能够使用webpack、rollup或fis等预编译工具</li>
</ul>
<p>理想很丰满,现实很。。。,如何才能够让开发者和使用者都能够开心呢,jslib-base通过babel+rollup提供了解决方案</p>
<p><img src="/blog/534.png" alt="" /></p>
<h3 id="编译">编译</h3>
<p>通过babel可以把ES6+的代码编译成ES5的代码,babel经历了5到6的进化,下面一张图总结了babel使用方式的变迁</p>
<p><img src="/blog/535.png" alt="" /></p>
<p>本文不讨论babel的进化史(后面会单独开一片博文介绍),而是选择最现代化的<code class="language-plaintext highlighter-rouge">babel-preset-env</code>方案,babel-preset-env可以通过提供提供兼容环境,而决定要编译那些ES特性</p>
<p>其原理大概如下,首先通过ES的特性和<a href="http://kangax.github.io/compat-table/es6/">特性的兼容列表</a>计算出每个特性的兼容性信息,再通过给定兼容性要求,计算出要使用的babel插件</p>
<p><img src="/blog/536.png" alt="" /></p>
<p>首先需要安装<code class="language-plaintext highlighter-rouge">babel-preset-env</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm i <span class="nt">--save-dev</span> babel-preset-env
</code></pre></div></div>
<p>然后新增一个.babelrc文件,添加下面的内容</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
"presets": [
["env",
{
"targets": {
"browsers": "last 2 versions, > 1%, ie >= 6, Android >= 4, iOS >= 6, and_uc > 9",
"node": "0.10"
},
"modules": false,
"loose": false
}]
]
}
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">targets</code>中配置需要兼容的环境,关于浏览器配置对应的浏览器列表,可以从<a href="http://browserl.ist/">browserl.ist</a>上查看</p>
<p><code class="language-plaintext highlighter-rouge">modules</code>表示编出输出的模块类型,支持”amd”,”umd”,”systemjs”,”commonjs”,false这些选项,false表示不输出任何模块类型</p>
<p><code class="language-plaintext highlighter-rouge">loose</code>代表松散模式,将loose设置为true,能够更好地兼容ie8以下环境,下面是一个例子(ie8不支持<code class="language-plaintext highlighter-rouge">Object.defineProperty</code>)</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 源代码</span>
<span class="kd">const</span> <span class="nx">aaa</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="k">export</span> <span class="k">default</span> <span class="nx">aaa</span><span class="p">;</span>
<span class="c1">// loose false</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">exports</span><span class="p">,</span> <span class="dl">'</span><span class="s1">__esModule</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
<span class="na">value</span><span class="p">:</span> <span class="kc">true</span>
<span class="p">});</span>
<span class="kd">var</span> <span class="nx">aaa</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="nx">exports</span><span class="p">.</span><span class="k">default</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="c1">// loose true</span>
<span class="nx">exports</span><span class="p">.</span><span class="nx">__esModule</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">aaa</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="nx">exports</span><span class="p">.</span><span class="k">default</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">babel-preset-env</code>解决了语法新特性的兼容问题,如果想使用api新特性,在babel中一般通过babel-polyfill来解决,babel-polyfill通过引入一个polyfill文件来解决问题,这对于普通项目很实用,但对于库来说就不太友好了</p>
<p>babel给库开发者提供的方案是<code class="language-plaintext highlighter-rouge">babel-transform-runtime</code>,runtime提供类似程序运行时,可以将全局的polyfill沙盒化</p>
<p>首先需要安装<code class="language-plaintext highlighter-rouge">babel-transform-runtime</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm i <span class="nt">--save-dev</span> babel-plugin-transform-runtime
</code></pre></div></div>
<p>在.babelrc增加下面的配置</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"plugins": [
["transform-runtime", {
"helpers": false,
"polyfill": false,
"regenerator": false,
"moduleName": "babel-runtime"
}]
]
</code></pre></div></div>
<p>transform-runtime,支持三种运行时,下面是polyfill的例子</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 源代码</span>
<span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="c1">// 编译后的代码</span>
<span class="kd">var</span> <span class="nx">_promise</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">babel-runtime/core-js/promise</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="nx">_promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="c1">// Promise被替换为_promise</span>
</code></pre></div></div>
<p>虽然虽然可以优雅的解决问题,但是引入的文件非常之大,比如只用了ES6中数组的find功能,可能就会引入一个几千行的代码,我的建议对于库来说能不用最好不用</p>
<h3 id="打包">打包</h3>
<p>编译解决了ES6到ES5的问题,打包可以把多个文件合并成一个文件,对外提供统一的文件入口,打包解决的是依赖引入的问题</p>
<h4 id="rollup-vs-webpack">rollup vs webpack</h4>
<p>我选择的rollup作为打包工具,rollup号称下一代打包方案,其有如下功能</p>
<ul>
<li>依赖解析,打包构建</li>
<li>仅支持ES6模块</li>
<li>Tree shaking</li>
</ul>
<p>webpack作为最流行的打包方案,rollup作为下一代打包方案,其实一句话就可以总结二者的区别:库使用rollup,其他场景使用webpack</p>
<p>为什么我会这么说呢?下面通过例子对比下webpack和rollup的区别</p>
<p>假设我们有两个文件,index.js和bar.js,其代码如下</p>
<p>bar.js对外暴漏一个函数<code class="language-plaintext highlighter-rouge">bar</code></p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="nx">bar</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">bar</span><span class="dl">'</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>index.js引用bar.js</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">bar</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./bar</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">bar</span><span class="p">()</span>
</code></pre></div></div>
<p>下面是webpack的配置文件webpack.config.js</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">path</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">entry</span><span class="p">:</span> <span class="dl">'</span><span class="s1">./src/index.js</span><span class="dl">'</span><span class="p">,</span>
<span class="na">output</span><span class="p">:</span> <span class="p">{</span>
<span class="na">path</span><span class="p">:</span> <span class="nx">path</span><span class="p">.</span><span class="nx">resolve</span><span class="p">(</span><span class="nx">__dirname</span><span class="p">,</span> <span class="dl">'</span><span class="s1">dist</span><span class="dl">'</span><span class="p">),</span>
<span class="na">filename</span><span class="p">:</span> <span class="dl">'</span><span class="s1">bundle.js</span><span class="dl">'</span>
<span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>
<p>下面来看一下webpack打包输出的内容,o(╯□╰)o,别着急,我们的代码在最下面的几行,上面这一大片代码其实是webpack生成的简易模块系统,webpack的方案问题在于会生成很多冗余代码,这对于业务代码来说没什么问题,但对于库来说就不太友好了</p>
<p><em>注意:下面的代码基于webpack3,webpack4增加了scope hoisting,已经把多个模块合并到一个匿名函数中</em></p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/******/</span>
<span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">modules</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// webpackBootstrap</span>
<span class="cm">/******/</span> <span class="c1">// The module cache</span>
<span class="cm">/******/</span>
<span class="kd">var</span> <span class="nx">installedModules</span> <span class="o">=</span> <span class="p">{};</span>
<span class="cm">/******/</span>
<span class="cm">/******/</span> <span class="c1">// The require function</span>
<span class="cm">/******/</span>
<span class="kd">function</span> <span class="nx">__webpack_require__</span><span class="p">(</span><span class="nx">moduleId</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/******/</span>
<span class="cm">/******/</span> <span class="c1">// Check if module is in cache</span>
<span class="cm">/******/</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">installedModules</span><span class="p">[</span><span class="nx">moduleId</span><span class="p">])</span> <span class="p">{</span>
<span class="cm">/******/</span>
<span class="k">return</span> <span class="nx">installedModules</span><span class="p">[</span><span class="nx">moduleId</span><span class="p">].</span><span class="nx">exports</span><span class="p">;</span>
<span class="cm">/******/</span>
<span class="p">}</span>
<span class="cm">/******/</span> <span class="c1">// Create a new module (and put it into the cache)</span>
<span class="cm">/******/</span>
<span class="kd">var</span> <span class="nx">module</span> <span class="o">=</span> <span class="nx">installedModules</span><span class="p">[</span><span class="nx">moduleId</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
<span class="cm">/******/</span>
<span class="na">i</span><span class="p">:</span> <span class="nx">moduleId</span><span class="p">,</span>
<span class="cm">/******/</span>
<span class="na">l</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="cm">/******/</span>
<span class="na">exports</span><span class="p">:</span> <span class="p">{}</span>
<span class="cm">/******/</span>
<span class="p">};</span>
<span class="cm">/******/</span>
<span class="cm">/******/</span> <span class="c1">// Execute the module function</span>
<span class="cm">/******/</span>
<span class="nx">modules</span><span class="p">[</span><span class="nx">moduleId</span><span class="p">].</span><span class="nx">call</span><span class="p">(</span><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span><span class="p">,</span> <span class="nx">module</span><span class="p">,</span> <span class="nx">module</span><span class="p">.</span><span class="nx">exports</span><span class="p">,</span> <span class="nx">__webpack_require__</span><span class="p">);</span>
<span class="cm">/******/</span>
<span class="cm">/******/</span> <span class="c1">// Flag the module as loaded</span>
<span class="cm">/******/</span>
<span class="nx">module</span><span class="p">.</span><span class="nx">l</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="cm">/******/</span>
<span class="cm">/******/</span> <span class="c1">// Return the exports of the module</span>
<span class="cm">/******/</span>
<span class="k">return</span> <span class="nx">module</span><span class="p">.</span><span class="nx">exports</span><span class="p">;</span>
<span class="cm">/******/</span>
<span class="p">}</span>
<span class="cm">/******/</span>
<span class="cm">/******/</span>
<span class="cm">/******/</span> <span class="c1">// expose the modules object (__webpack_modules__)</span>
<span class="cm">/******/</span>
<span class="nx">__webpack_require__</span><span class="p">.</span><span class="nx">m</span> <span class="o">=</span> <span class="nx">modules</span><span class="p">;</span>
<span class="cm">/******/</span>
<span class="cm">/******/</span> <span class="c1">// expose the module cache</span>
<span class="cm">/******/</span>
<span class="nx">__webpack_require__</span><span class="p">.</span><span class="nx">c</span> <span class="o">=</span> <span class="nx">installedModules</span><span class="p">;</span>
<span class="cm">/******/</span>
<span class="cm">/******/</span> <span class="c1">// define getter function for harmony exports</span>
<span class="cm">/******/</span>
<span class="nx">__webpack_require__</span><span class="p">.</span><span class="nx">d</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">exports</span><span class="p">,</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">getter</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/******/</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">__webpack_require__</span><span class="p">.</span><span class="nx">o</span><span class="p">(</span><span class="nx">exports</span><span class="p">,</span> <span class="nx">name</span><span class="p">))</span> <span class="p">{</span>
<span class="cm">/******/</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">exports</span><span class="p">,</span> <span class="nx">name</span><span class="p">,</span> <span class="p">{</span>
<span class="cm">/******/</span>
<span class="na">configurable</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="cm">/******/</span>
<span class="na">enumerable</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="cm">/******/</span>
<span class="na">get</span><span class="p">:</span> <span class="nx">getter</span>
<span class="cm">/******/</span>
<span class="p">});</span>
<span class="cm">/******/</span>
<span class="p">}</span>
<span class="cm">/******/</span>
<span class="p">};</span>
<span class="cm">/******/</span>
<span class="cm">/******/</span> <span class="c1">// getDefaultExport function for compatibility with non-harmony modules</span>
<span class="cm">/******/</span>
<span class="nx">__webpack_require__</span><span class="p">.</span><span class="nx">n</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">module</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/******/</span>
<span class="kd">var</span> <span class="nx">getter</span> <span class="o">=</span> <span class="nx">module</span> <span class="o">&&</span> <span class="nx">module</span><span class="p">.</span><span class="nx">__esModule</span> <span class="p">?</span>
<span class="cm">/******/</span>
<span class="kd">function</span> <span class="nx">getDefault</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">module</span><span class="p">[</span><span class="dl">'</span><span class="s1">default</span><span class="dl">'</span><span class="p">];</span> <span class="p">}</span> <span class="p">:</span>
<span class="cm">/******/</span>
<span class="kd">function</span> <span class="nx">getModuleExports</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">module</span><span class="p">;</span> <span class="p">};</span>
<span class="cm">/******/</span>
<span class="nx">__webpack_require__</span><span class="p">.</span><span class="nx">d</span><span class="p">(</span><span class="nx">getter</span><span class="p">,</span> <span class="dl">'</span><span class="s1">a</span><span class="dl">'</span><span class="p">,</span> <span class="nx">getter</span><span class="p">);</span>
<span class="cm">/******/</span>
<span class="k">return</span> <span class="nx">getter</span><span class="p">;</span>
<span class="cm">/******/</span>
<span class="p">};</span>
<span class="cm">/******/</span>
<span class="cm">/******/</span> <span class="c1">// Object.prototype.hasOwnProperty.call</span>
<span class="cm">/******/</span>
<span class="nx">__webpack_require__</span><span class="p">.</span><span class="nx">o</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">object</span><span class="p">,</span> <span class="nx">property</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">object</span><span class="p">,</span> <span class="nx">property</span><span class="p">);</span> <span class="p">};</span>
<span class="cm">/******/</span>
<span class="cm">/******/</span> <span class="c1">// __webpack_public_path__</span>
<span class="cm">/******/</span>
<span class="nx">__webpack_require__</span><span class="p">.</span><span class="nx">p</span> <span class="o">=</span> <span class="dl">""</span><span class="p">;</span>
<span class="cm">/******/</span>
<span class="cm">/******/</span> <span class="c1">// Load entry module and return exports</span>
<span class="cm">/******/</span>
<span class="k">return</span> <span class="nx">__webpack_require__</span><span class="p">(</span><span class="nx">__webpack_require__</span><span class="p">.</span><span class="nx">s</span> <span class="o">=</span> <span class="mi">0</span><span class="p">);</span>
<span class="cm">/******/</span>
<span class="p">})</span>
<span class="cm">/************************************************************************/</span>
<span class="cm">/******/</span>
<span class="p">([</span>
<span class="cm">/* 0 */</span>
<span class="cm">/***/</span>
<span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">module</span><span class="p">,</span> <span class="nx">__webpack_exports__</span><span class="p">,</span> <span class="nx">__webpack_require__</span><span class="p">)</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">use strict</span><span class="dl">"</span><span class="p">;</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">defineProperty</span><span class="p">(</span><span class="nx">__webpack_exports__</span><span class="p">,</span> <span class="dl">"</span><span class="s2">__esModule</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">value</span><span class="p">:</span> <span class="kc">true</span> <span class="p">});</span>
<span class="cm">/* harmony import */</span>
<span class="kd">var</span> <span class="nx">__WEBPACK_IMPORTED_MODULE_0__bar__</span> <span class="o">=</span> <span class="nx">__webpack_require__</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="nb">Object</span><span class="p">(</span><span class="nx">__WEBPACK_IMPORTED_MODULE_0__bar__</span><span class="p">[</span><span class="dl">"</span><span class="s2">a</span><span class="dl">"</span> <span class="cm">/* default */</span> <span class="p">])()</span>
<span class="cm">/***/</span>
<span class="p">}),</span>
<span class="cm">/* 1 */</span>
<span class="cm">/***/</span>
<span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">module</span><span class="p">,</span> <span class="nx">__webpack_exports__</span><span class="p">,</span> <span class="nx">__webpack_require__</span><span class="p">)</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">use strict</span><span class="dl">"</span><span class="p">;</span>
<span class="cm">/* harmony export (immutable) */</span>
<span class="nx">__webpack_exports__</span><span class="p">[</span><span class="dl">"</span><span class="s2">a</span><span class="dl">"</span><span class="p">]</span> <span class="o">=</span> <span class="nx">bar</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">bar</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">//</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">bar</span><span class="dl">'</span><span class="p">)</span>
<span class="p">}</span>
<span class="cm">/***/</span>
<span class="p">})</span>
<span class="cm">/******/</span>
<span class="p">]);</span>
</code></pre></div></div>
<p>下面来看看rollup的结果,rollup的配置和webpack类似</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="k">default</span> <span class="p">{</span>
<span class="na">input</span><span class="p">:</span> <span class="dl">'</span><span class="s1">src/index.js</span><span class="dl">'</span><span class="p">,</span>
<span class="na">output</span><span class="p">:</span> <span class="p">{</span>
<span class="na">file</span><span class="p">:</span> <span class="dl">'</span><span class="s1">dist/bundle2.js</span><span class="dl">'</span><span class="p">,</span>
<span class="na">format</span><span class="p">:</span> <span class="dl">'</span><span class="s1">cjs</span><span class="dl">'</span>
<span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>
<p>下面看看rollup的产出,简直完美有没有,模块完全消失了,rollup通过顺序引入到同一个文件来解决模块依赖问题,rollup的方案如果要做拆包的话就会有问题,因为模块完全透明了,但这对于库开发者来说简直就是最完美的方案</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="dl">'</span><span class="s1">use strict</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">bar</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">//</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">bar</span><span class="dl">'</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">bar</span><span class="p">();</span>
</code></pre></div></div>
<h4 id="模块化方案">模块化方案</h4>
<p>在ES6模块化之前,JS社区探索出了一些模块系统,比如node中的commonjs,浏览器中的AMD,还有可以同时兼容不同模块系统的UMD,如果对这部分内容感兴趣,可以看我之前的一篇文章《<a href="https://yanhaijing.com/javascript/2015/03/28/js-module/">JavaScript模块的前世今生</a>》</p>
<p>对于浏览器原生,预编译工具和node,不同环境中的模块化方案也不同;由于浏览器环境不能够解析第三方依赖,所以浏览器环境需要把依赖也进行打包处理;不同环境下引用的文件也不相同,下面通过一个表格对比下</p>
<table>
<thead>
<tr>
<th> </th>
<th>浏览器(script,AMD,CMD)</th>
<th>预编译工具(webpack,rollup,fis)</th>
<th>Node</th>
</tr>
</thead>
<tbody>
<tr>
<td>引用文件</td>
<td>index.aio.js</td>
<td>index.esm.js</td>
<td>index.js</td>
</tr>
<tr>
<td>模块化方案</td>
<td>UMD</td>
<td>ES Module</td>
<td>commonjs</td>
</tr>
<tr>
<td>自身依赖</td>
<td>打包</td>
<td>打包</td>
<td>打包</td>
</tr>
<tr>
<td>第三方依赖</td>
<td>打包</td>
<td>不打包</td>
<td>不打包</td>
</tr>
</tbody>
</table>
<p><em>注意: legacy模式下的模块系统可以兼容ie6-8,但由于rollup的一个<a href="https://github.com/rollup/rollup/issues/2088">bug</a>(这个bug是我发现的,但rollup并不打算修复,╮(╯▽╰)╭哎),legacy模式下,不可同时使用 export 与 export default</em></p>
<h4 id="tree-shaking">tree shaking</h4>
<p>rollup是天然支持tree shaking,tree shaking可以剔除依赖模块中没有被使用的部分,这对于第三方依赖(node_moduels依赖)非常有帮助,可以极大的降低包的体积</p>
<p>举个例子,假设index.js只是用了第三方包is.js中的一个函数<code class="language-plaintext highlighter-rouge">isString</code>,没有treeshaking会将is.js全部引用进来</p>
<p><img src="/blog/537.png" alt="" /></p>
<p>而使用了treeshaking的话则可以将is.js中的其他函数剔除,仅保留<code class="language-plaintext highlighter-rouge">isString</code>函数</p>
<p><img src="/blog/538.png" alt="" /></p>
<h2 id="规范">规范</h2>
<p>无规矩不成方圆,特别是对于开源项目,由于会有多人参与,所以大家遵守一份规范会事半功倍</p>
<h3 id="编辑器规范">编辑器规范</h3>
<p>首先可以通过<code class="language-plaintext highlighter-rouge">.editorconfig</code>来保证缩进、换行的一致性,目前绝大部分浏览器都已经支持,可以看<a href="http://editorconfig.org/">这里</a></p>
<p>下面的配置设置在js,css和html中都用空格代替tab,tab为4个空格,使用unix换行符,使用utf8字符集,每个文件结尾添加一个空行</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>root = true
[{*.js,*.css,*.html}]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
insert_final_newline = true
[{package.json,.*rc,*.yml}]
indent_style = space
indent_size = 2
</code></pre></div></div>
<h3 id="代码风格">代码风格</h3>
<p>其次可以通过eslint来保证代码风格一致,关于eslint的安装和配置这里不再展开解释了,在jslib-base中只需要运行下面的命令就可以进行代码校验了,eslint的配置文件位于<code class="language-plaintext highlighter-rouge">config/.eslintrc.js</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>npm run lint
</code></pre></div></div>
<h3 id="设计规范">设计规范</h3>
<p>eslint只能够保证代码规范,却不能保证提供优秀的接口设计,关于函数接口设计有一些指导规则</p>
<p>参数数量</p>
<ul>
<li>函数的参数个数最多不要超过5个</li>
</ul>
<p>可选参数</p>
<ul>
<li>可选参数应该放到后面</li>
<li>可选参数数量超过三个时,可以使用对象传入</li>
<li>可选参数,应该提供默认值</li>
</ul>
<p>参数校验与类型转换</p>
<ul>
<li>必传参数,如果不传要报错</li>
<li>对下列类型要做强制检验,类型不对要报错(object, array, function)</li>
<li>对下列类型要做自动转换(number, string, boolean)</li>
<li>对于复合类型的内部数据,也要做上面的两个步骤</li>
<li>对于number转换后如果为NaN,要做特殊处理(有默认值的赋值为默认值,无默认值的要报错)</li>
</ul>
<p>参数类型</p>
<ul>
<li>参数尽量使用值类型(简单类型)</li>
<li>参数尽量不要使用复杂类型(避免副作用)</li>
<li>使用复杂类型时,层级不要过深</li>
<li>使用复杂数据类型时,应该进行深拷贝(避免副作用)</li>
</ul>
<p>函数返回值</p>
<ul>
<li>返回值可返回操作结果(获取接口),操作是否成功(保存接口)</li>
<li>
<p>返回值的类型要保持一致</p>
</li>
<li>返回值尽量使用值类型(简单类型)</li>
<li>返回值尽量不要使用复杂类型(避免副作用)</li>
</ul>
<h3 id="版本规范">版本规范</h3>
<p>版本应该遵守开源社区通用的<a href="https://semver.org/lang/zh-CN/">语义化版本</a></p>
<p>版本号格式:x.y.z</p>
<ul>
<li>x 主版本号,不兼容的改动</li>
<li>y 次版本号,兼容的改动</li>
<li>z 修订版本号,bug修复</li>
</ul>
<h3 id="git-commit规范">Git commit规范</h3>
<p>代码的提交应该遵守规范,这里推荐一个<a href="https://yanhaijing.com/git/2016/02/17/my-commit-message/">我的规范</a></p>
<h2 id="测试">测试</h2>
<p>没有单元测试的库都是耍流氓,单元测试能够保证每次交付都是有质量保证的,业务代码由于一次性和时间成本可以不做单元测试,但开源库由于需要反复迭代,对质量要求又极高,所以单元测试是必不可少的</p>
<p>关于单元测试有很多技术方案,其中一种选择是<a href="https://mochajs.org/">mocha</a>+<a href="http://www.chaijs.com/">chai</a>,mocha是一个单元测试框架,用来组织、运行单元测试,并输出测试报告;chai是一个断言库,用来做单元测试的断言功能</p>
<p>由于chai不能够兼容ie6-8,所以选择了另一个断言库——<a href="http://chaijs.com/">expect.js</a>,expect是一个BDD断言库,兼容性非常好,所以我选择的是mocha+expect.js</p>
<p><em>关于BDD与TDD的区别这里不再赘述,感兴趣的同学可以自行查阅相关资料</em></p>
<p>有了测试的框架,还需要写单元测试的代码,下面是一个例子</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">expect</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">expect.js</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">base</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">../dist/index.js</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">单元测试</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">功能1</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">相等</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">expect</span><span class="p">(</span><span class="mi">1</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">equal</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>
<p>然后只需运行下面的命令,mocha会自动运行test目录下面的js文件</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>mocha
</code></pre></div></div>
<p>mocha支持在node和浏览器中测试,但上面的框架在浏览器下有一个问题,浏览器没法支持<code class="language-plaintext highlighter-rouge">require('expect.js')</code>,我用了一个比较hack的方法解决问题,早浏览器中重新定义了require的含义</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><script </span><span class="na">src=</span><span class="s">"../../node_modules/mocha/mocha.js"</span><span class="nt">></script></span>
<span class="nt"><script </span><span class="na">src=</span><span class="s">"../../node_modules/expect.js/index.js"</span><span class="nt">></script></span>
<span class="nt"><script></span>
<span class="kd">var</span> <span class="nx">libs</span> <span class="o">=</span> <span class="p">{</span>
<span class="dl">'</span><span class="s1">expect.js</span><span class="dl">'</span><span class="p">:</span> <span class="nx">expect</span><span class="p">,</span>
<span class="dl">'</span><span class="s1">../dist/index.js</span><span class="dl">'</span><span class="p">:</span> <span class="nx">jslib_base</span>
<span class="p">};</span>
<span class="kd">var</span> <span class="nx">require</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">path</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">libs</span><span class="p">[</span><span class="nx">path</span><span class="p">];</span>
<span class="p">}</span>
<span class="nt"></script></span>
</code></pre></div></div>
<p>下面是用mocha生成测试报告的例子,左边是在node中,右边是在浏览器中</p>
<p><img src="/blog/533.png" alt="" /></p>
<h2 id="可持续集成">可持续集成</h2>
<p>没有可持续集成的库都是原始人,如果每次push都能够自动运行单元测试就好了,这样就省去了手动运行的繁琐,好在<a href="https://www.travis-ci.org/">travis-ci</a>已经为我们提供了这个功能</p>
<p>用GitHub登录travis-ci,就可以看到自己在GitHub上的项目了,然后需要打开下项目的开关,才能够打开自动集成功能</p>
<p><img src="/blog/532.png" alt="" /></p>
<p>第二步,还需要在项目中添加一个文件<code class="language-plaintext highlighter-rouge">.travis.yml</code>,内容如下,这样就可以在每次push时自动在node 4 6 8版本下运行<code class="language-plaintext highlighter-rouge">npm test</code>命令,从而实现自动测试的目的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>language: node_js
node_js:
- "8"
- "6"
- "4"
</code></pre></div></div>
<h2 id="其他内容">其他内容</h2>
<p>开源库希望得到用户的反馈,如果对用户提的issue有要求,可以设置一个模版,用来规范github上用户反馈的issue需要制定一些信息</p>
<p>通过提供<code class="language-plaintext highlighter-rouge">.github/ISSUE_TEMPLATE</code>文件可以给issue提供模版,下面是一个例子,用户提issue时会自动带上如下的提示信息</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>### 问题是什么
问题的具体描述,尽量详细
### 环境
- 手机: 小米6
- 系统:安卓7.1.1
- 浏览器:chrome 61
- jslib-base版本:0.2.0
- 其他版本信息
### 在线例子
如果有请提供在线例子
### 其他
其他信息
</code></pre></div></div>
<h2 id="jsmini">jsmini</h2>
<p><a href="https://github.com/jsmini">jsmini</a>是基于jslib-base的一系列库,jsmini的理念是小而美,并且无第三方依赖,开源了很多能力,能够
助力库开发者</p>
<h2 id="总结">总结</h2>
<p>五年弹指一挥间,本文总结了自己做开源项目的一些经验,希望能够帮助大家,所有介绍的内容都可以在<a href="https://github.com/yanhaijing/jslib-base">jslib-base</a>里面找到</p>
<p>jslib-base是一个拿来即用脚手架,赋能js第三方库开源,快速开源一个标准的js库</p>
<p>最后再送给大家一句话,开源一个项目,<code class="language-plaintext highlighter-rouge">重在开始,贵在坚持</code></p>
解密传统组件间通信与React组件间通信
2018-08-16T00:00:00+00:00
http://yanhaijing.com/javascript/2018/08/16/react-component-communication
<p>在React中最小的逻辑单元是组件,组件之间如果有耦合关系就会进行通信,本文将会介绍React中的组件通信的不同方式</p>
<p>通过归纳范,可以将任意组件间的通信归类为四种类型的组件间通信,分别是父子组件,爷孙组件,兄弟组件和任意组件,
需要注意的是前三个也可以算作任意组件的范畴,所以最后一个是万能方法</p>
<h3 id="父子组件">父子组件</h3>
<p>父子组件间的通信分为父组件向子组件通信和子组件向父组件通信两种情况,下面先来介绍父组件向子组件通信,
传统做法分为两种情况,分别是初始化时的参数传递和实例阶段的方法调用,例子如下</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">Child</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 获取dom引用</span>
<span class="k">this</span><span class="p">.</span><span class="nx">$div</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">#wp</span><span class="dl">'</span><span class="p">);</span>
<span class="c1">// 初始化时传入name</span>
<span class="k">this</span><span class="p">.</span><span class="nx">updateName</span><span class="p">(</span><span class="nx">name</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">updateName</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 对外提供更新的api</span>
<span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span>
<span class="c1">// 更新dom</span>
<span class="k">this</span><span class="p">.</span><span class="nx">$div</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">name</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">Parent</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 初始化阶段</span>
<span class="k">this</span><span class="p">.</span><span class="nx">child</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Child</span><span class="p">(</span><span class="dl">'</span><span class="s1">yan</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">setTimeout</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="c1">// 实例化阶段</span>
<span class="k">this</span><span class="p">.</span><span class="nx">child</span><span class="p">.</span><span class="nx">updateName</span><span class="p">(</span><span class="dl">'</span><span class="s1">hou</span><span class="dl">'</span><span class="p">);</span>
<span class="p">},</span> <span class="mi">2000</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>在React中将两个情况统一处理,全部通过属性来完成,之所以能够这样是因为React在属性更新时会自动重新渲染子组件,
下面的例子中,2秒后子组件会自动重新渲染,并获取新的属性值</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">Child</span> <span class="kd">extends</span> <span class="nx">Component</span> <span class="p">{</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p"><</span><span class="nt">div</span><span class="p">></span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="p"></</span><span class="nt">div</span><span class="p">></span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">Parent</span> <span class="kd">extends</span> <span class="nx">Component</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 初始化阶段</span>
<span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="p">{</span><span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">yan</span><span class="dl">'</span><span class="p">};</span>
<span class="nx">setTimeout</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="c1">// 实例化阶段</span>
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span><span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">hou</span><span class="dl">'</span><span class="p">})</span>
<span class="p">},</span> <span class="mi">2000</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p"><</span><span class="nc">Child</span> <span class="na">name</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span> <span class="p">/></span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>下面来看一下子组件如何向父组件通信,传统做法有两种,一种是回调函数,另一种是为子组件部署消息接口</p>
<p>先来看回调函数的例子,回调函数的优点是非常简单,缺点就是必须在初始化的时候传入,并且不可撤回,并且只能传入一个函数</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">Child</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">(</span><span class="nx">cb</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 调用父组件传入的回调函数,发送消息</span>
<span class="nx">setTimeout</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span> <span class="nx">cb</span><span class="p">()</span> <span class="p">},</span> <span class="mi">2000</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">Parent</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 初始化阶段,传入回调函数</span>
<span class="k">this</span><span class="p">.</span><span class="nx">child</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Child</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">child update</span><span class="dl">'</span><span class="p">)</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>下面来看看消息接口方法,首先需要一个可以发布和订阅消息的基类,比如下面实现了一个简单的<code class="language-plaintext highlighter-rouge">EventEimtter</code>,实际生产中可以直接使用别人写好的类库,比如<a href="https://github.com/jsmini/event">@jsmini/event</a>,子组件继承消息基类,就有了发布消息的能力,然后父组件订阅子组件的消息,即可实现子组件向父组件通信的功能</p>
<p>消息接口的优点就是可以随处订阅,并且可以多次订阅,还可以取消订阅,缺点是略显麻烦,需要引入消息基类</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 消息接口,订阅发布模式,类似绑定事件,触发事件</span>
<span class="kd">class</span> <span class="nx">EventEimtter</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">eventMap</span> <span class="o">=</span> <span class="p">{};</span>
<span class="p">}</span>
<span class="nx">sub</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">cb</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">eventList</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">eventMap</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">eventMap</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="o">||</span> <span class="p">{};</span>
<span class="nx">eventList</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">cb</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">pub</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="p">...</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">eventMap</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="o">||</span> <span class="p">[]).</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">cb</span> <span class="o">=></span> <span class="nx">cb</span><span class="p">(...</span><span class="nx">data</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">Child</span> <span class="kd">extends</span> <span class="nx">EventEimtter</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="k">super</span><span class="p">();</span>
<span class="c1">// 通过消息接口发布消息</span>
<span class="nx">setTimeout</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">pub</span><span class="p">(</span><span class="dl">'</span><span class="s1">update</span><span class="dl">'</span><span class="p">)</span> <span class="p">},</span> <span class="mi">2000</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">Parent</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 初始化阶段,传入回调函数</span>
<span class="k">this</span><span class="p">.</span><span class="nx">child</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Child</span><span class="p">();</span>
<span class="c1">// 订阅子组件的消息</span>
<span class="k">this</span><span class="p">.</span><span class="nx">child</span><span class="p">.</span><span class="nx">sub</span><span class="p">(</span><span class="dl">'</span><span class="s1">update</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">child update</span><span class="dl">'</span><span class="p">)</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Backbone.js就同时支持回调函数和消息接口方式,但React中选择了比较简单的回调函数模式,下面来看一下React的例子</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">Child</span> <span class="kd">extends</span> <span class="nx">Component</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">(</span><span class="nx">props</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">setTimeout</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">cb</span><span class="p">()</span> <span class="p">},</span> <span class="mi">2000</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p"><</span><span class="nt">div</span><span class="p">></</span><span class="nt">div</span><span class="p">></span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">Parent</span> <span class="kd">extends</span> <span class="nx">Component</span> <span class="p">{</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p"><</span><span class="nc">Child</span> <span class="na">cb</span><span class="p">=</span><span class="si">{</span><span class="p">()</span> <span class="o">=></span> <span class="p">{</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">update</span><span class="dl">'</span><span class="p">)}</span><span class="si">}</span> <span class="p">/></span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="爷孙组件">爷孙组件</h3>
<p>父子组件其实可以算是爷孙组件的一种特例,这里的爷孙组件不光指爷爷和孙子,而是泛指祖先与后代组件通信,可能隔着很多层级,我们已经解决了父子组件通信的问题,根据化归法,很容易得出爷孙组件的答案,那就是层层传递属性么,把爷孙组件通信分解为多个父子组件通信的问题</p>
<p>层层传递的优点是非常简单,用已有知识就能解决,问题是会浪费很多代码,非常繁琐,中间作为桥梁的组件会引入很多不属于自己的属性</p>
<p>在React中,通过context可以让祖先组件直接把属性传递到后代组件,有点类似星际旅行中的虫洞一样,通过context这个特殊的桥梁,可以跨越任意层次向后代组件传递消息</p>
<p>怎么在需要通信的组件之间开启这个虫洞呢?需要双向声明,也就是在祖先组件声明属性,并在后代组件上再次声明属性,然后在祖先组件上放上属性就可以了,就可以在后代组件读取属性了,下面看一个例子</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">PropTypes</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">prop-types</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">class</span> <span class="nx">Child</span> <span class="kd">extends</span> <span class="nx">Component</span> <span class="p">{</span>
<span class="c1">// 后代组件声明需要读取context上的数据</span>
<span class="kd">static</span> <span class="nx">contextTypes</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">text</span><span class="p">:</span> <span class="nx">PropTypes</span><span class="p">.</span><span class="nx">string</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 通过this.context 读取context上的数据</span>
<span class="k">return</span> <span class="p"><</span><span class="nt">div</span><span class="p">></span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">context</span><span class="p">.</span><span class="nx">text</span><span class="si">}</span><span class="p"></</span><span class="nt">div</span><span class="p">></span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">Ancestor</span> <span class="kd">extends</span> <span class="nx">Component</span> <span class="p">{</span>
<span class="c1">// 祖先组件声明需要放入context上的数据</span>
<span class="kd">static</span> <span class="nx">childContextTypes</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">text</span><span class="p">:</span> <span class="nx">PropTypes</span><span class="p">.</span><span class="nx">string</span>
<span class="p">}</span>
<span class="c1">// 祖先组件往context放入数据</span>
<span class="nx">getChildContext</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span><span class="na">text</span><span class="p">:</span> <span class="dl">'</span><span class="s1">yanhaijing</span><span class="dl">'</span><span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>context的优点是可以省去层层传递的麻烦,并且通过双向声明控制了数据的可见性,对于层数很多时,不失为一种方案;但缺点也很明显,就像全局变量一样,如果不加节制很容易造成混乱,而且也容易出现重名覆盖的问题</p>
<p>个人的建议是对一些所有组件共享的只读信息可以采用context来传递,比如登录的用户信息等</p>
<p><em>小贴士:React Router路由就是通过context来传递路由属性的</em></p>
<h3 id="兄弟组件">兄弟组件</h3>
<p>如果两个组件是兄弟关系,可以通过父组件作为桥梁,来让两个组件之间通信,这其实就是主模块模式</p>
<p>下面的例子中,两个子组件通过父组件来实现显示数字同步的功能</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">Parent</span> <span class="kd">extends</span> <span class="nx">Component</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">onChange</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">num</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span><span class="nx">num</span><span class="p">})</span>
<span class="p">}.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span>
<span class="p"><</span><span class="nt">div</span><span class="p">></span>
<span class="p"><</span><span class="nc">Child1</span> <span class="na">num</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">num</span><span class="si">}</span> <span class="na">onChange</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">onChange</span><span class="si">}</span><span class="p">></span>
<span class="p"><</span><span class="nc">Child2</span> <span class="na">num</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">num</span><span class="si">}</span> <span class="na">onChange</span><span class="p">=</span><span class="si">{</span><span class="k">this</span><span class="p">.</span><span class="nx">onChange</span><span class="si">}</span><span class="p">></span>
<span class="p"></</span><span class="nt">div</span><span class="p">></span>
);
}
}
</code></pre></div></div>
<p>主模块模式的优点就是解耦,把两个子组件之间的耦合关系,解耦成子组件和父组件之间的耦合,把分散的东西收集在一起好处非常明显,能带来更好的可维护性和可扩展性</p>
<h3 id="任意组件">任意组件</h3>
<p>任意组件包括上面的三种关系组件,上面三种关系应该优先使用上面介绍的方法,对于任意的两个组件间通信,总共有三种办法,分别是共同祖先法,消息中间件和状态管理</p>
<p>基于我们上面介绍的爷孙组件和兄弟组件,只要找到两个组件的共同祖先,就可以将任意组件之间的通信,转化为任意组件和共同祖先之间的通信,这个方法的好处就是非常简单,已知知识就能搞定,缺点就是上面两种模式缺点的叠加,除了临时方案,不建议使用这种方法</p>
<p>另一种比较常用的方法是消息中间件,就是引入一个全局消息工具,两个组件通过这个全局工具进行通信,这样两个组件间的通信,就通过全局消息媒介完成了</p>
<p>还记得上面介绍的消息基类吗?下面的例子中,组件1和组件2通过全局event进行通信</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">EventEimtter</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">eventMap</span> <span class="o">=</span> <span class="p">{};</span>
<span class="p">}</span>
<span class="nx">sub</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="nx">cb</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">eventList</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">eventMap</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">eventMap</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="o">||</span> <span class="p">{};</span>
<span class="nx">eventList</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">cb</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">pub</span><span class="p">(</span><span class="nx">name</span><span class="p">,</span> <span class="p">...</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">eventMap</span><span class="p">[</span><span class="nx">name</span><span class="p">]</span> <span class="o">||</span> <span class="p">[]).</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">cb</span> <span class="o">=></span> <span class="nx">cb</span><span class="p">(...</span><span class="nx">data</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// 全局消息工具</span>
<span class="kd">const</span> <span class="nx">event</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">EventEimtter</span><span class="p">;</span>
<span class="c1">// 一个组件</span>
<span class="kd">class</span> <span class="nx">Element1</span> <span class="kd">extends</span> <span class="nx">Component</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 订阅消息</span>
<span class="nx">event</span><span class="p">.</span><span class="nx">sub</span><span class="p">(</span><span class="dl">'</span><span class="s1">element2update</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">element2 update</span><span class="dl">'</span><span class="p">)});</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// 另一个组件。</span>
<span class="kd">class</span> <span class="nx">Element2</span> <span class="kd">extends</span> <span class="nx">Component</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 发布消息</span>
<span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="nx">event</span><span class="p">.</span><span class="nx">pub</span><span class="p">(</span><span class="dl">'</span><span class="s1">element2update</span><span class="dl">'</span><span class="p">)</span> <span class="p">},</span> <span class="mi">2000</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>消息中间件的模式非常简单,利用了观察者模式,将两个组件之间的耦合解耦成了组件和消息中心+消息名称的耦合,但为了解耦却引入全局消息中心和消息名称,消息中心对组件的侵入性很强,和第三方组件通信不能使用这种方式</p>
<p>小型项目比较适合使用这种方式,但随着项目规模的扩大,达到中等项目以后,消息名字爆炸式增长,消息名字的维护成了棘手的问题,重名概率极大,没有人敢随便删除消息信息,消息的发布者找不到消息订阅者的信息等</p>
<p>其实上面的问题也不是没有解决办法,重名的问题可以通过制定规范,消息命名空间等方式来极大降低冲突,其他问题可以通过把消息名字统一维护到一个文件,通过对消息的中心化管理,可以让很多问题都很容易解决</p>
<p>如果你的项目非常大,上面两种方案都不合适,那你可能需要一个状态管理工具,通过状态管理工具把组件之间的关系,和关系的处理逻辑从组建中抽象出来,并集中化到统一的地方来处理,Redux就是一个非常不错的状态管理工具</p>
<p>除了Redux,还有Mobx,Rematch,reselect等工具,本文不展开介绍,有机会后面单独成文,这些都是用来解决不同问题的,只要根据自己的场景选择合适的工具就好了</p>
<h3 id="总结">总结</h3>
<p>组件间的关系千变万化,都可以用上面介绍的方法解决,对于不同规模的项目,应该选择适合自己的技术方案,上面介绍的不同方式解耦的程度是不一样的,关于不同耦合关系的好坏,可以看我之前的文章《<a href="http://yanhaijing.com/program/2016/09/01/about-coupling/">图解7种耦合关系</a>》</p>
<p>本文节选自我的新书《React 状态管理与同构实战》,感兴趣的同学可以继续阅读本书,这本书由我和前端自身技术侯策合力打磨,凝结了我们在学习、实践 React 框架过程中的积累和心得。除了 React 框架使用介绍以外,着重剖析了状态管理以及服务端渲染同构应用方面的内容。同时吸取了社区大量优秀思想,进行归纳比对。</p>
<p>本书受到百度公司副总裁沈抖、百度高级前端工程师<a href="https://www.zhihu.com/people/dong-rui-24/activities">董睿</a>,以及知名JavaScript语言专家<a href="http://www.ruanyifeng.com/home.html">阮一峰</a>、Node.js布道者<a href="https://www.zhihu.com/people/i5ting/activities">狼叔</a>、Flarum中文社区创始人 <a href="http://justjavac.com/">justjavac</a>、新浪移动前端技术专家<a href="https://www.zhihu.com/people/xiao-jue-83/activities">小爝</a>、知乎知名博主<a href="https://www.zhihu.com/people/justineo/activities">顾轶灵</a>等前端圈众多专家大咖的联合力荐。</p>
<p>有兴趣的读者可以点击下面的链接购买,再次感谢各位的支持与鼓励!恳请各位批评指正!</p>
<p>京东:<a href="https://item.jd.com/12403508.html" target="_blank">https://item.jd.com/12403508.html</a></p>
<p>当当:<a href="http://product.dangdang.com/25308679.html" target="_blank">http://product.dangdang.com/25308679.html</a></p>
《React状态管理与同构实战》限量签名版来啦
2018-08-14T00:00:00+00:00
http://yanhaijing.com/web/2018/08/14/my-react-book
<p>从去年起,我和知名技术专家<a href="https://www.zhihu.com/people/lucas-hc">侯策</a>开始了合著之旅,今年我们共同打磨的书籍《React 状态管理与同构实战》终于正式出版了!</p>
<p><img src="/img/react-book.jpg" width="700" /></p>
<p>下面是本书的详细信息,有兴趣的同学可以了解一下</p>
<h2 id="购买方式">购买方式</h2>
<p>为大家准备了<strong>限量签名版</strong>,可指定寄语,附赠小惊喜,目前可通过两种方式购买</p>
<p>第一种,扫面下方二维码,通过微店购买</p>
<p><img src="/img/weidian2.png" width="170" /></p>
<p>第二种,也可以扫面下方二维码,添加我的微信购买</p>
<p><img src="/img/weixin.jpg" width="170" /></p>
<p>不想要签名版的同学可以通过京东或当当购买</p>
<p>京东:<a href="https://item.jd.com/12403508.html" target="_blank">https://item.jd.com/12403508.html</a></p>
<p>当当:<a href="http://product.dangdang.com/25308679.html" target="_blank">http://product.dangdang.com/25308679.html</a></p>
<p>下面是本书的豆瓣主页,欢迎大家讨论或反馈问题</p>
<p>豆瓣:<a href="https://book.douban.com/subject/30290509/" target="_blank">https://book.douban.com/subject/30290509/</a></p>
<h2 id="本书特色">本书特色</h2>
<p><strong>一部颠覆前端历史的框架
</strong></p>
<p>React的横空出世颠覆了以往的前端开发方式,本书深入浅出React与Redux相关原理,重点讲解前沿同构技术,揭秘SSR实战要领!
</p>
<p><strong>两位知名技术博主的沉淀
</strong></p>
<p>本书的两位作者都是前端圈内非常活跃的技术博主,拥有丰富的实战经验,经历近十年的沉淀,将鲜活的经验通过本书分享给各位读者!
</p>
<p><strong>众多圈内技术大咖的力荐
</strong></p>
<p>百度公司副总裁沈抖、百度公司资深前端工程师<a href="https://www.zhihu.com/people/dong-rui-24/activities">董睿</a>、前端圈知名技术专家<a href="http://www.ruanyifeng.com/home.html">阮一峰</a>、<a href="https://www.zhihu.com/people/i5ting/activities">狼叔</a>、<a href="http://justjavac.com/">迷渡(justjavac)</a>、<a href="https://www.zhihu.com/people/xiao-jue-83/activities">小爝</a>、<a href="https://www.zhihu.com/people/justineo/activities">顾轶灵</a>联合力荐!</p>
<h2 id="内容简介">内容简介</h2>
<p>React自开源以来,便以革命性的设计理念迅速颠覆了前端开发的传统意义,其倡导的组件化、状态管理、虚拟DOM等思想极大提高了前端开发效率。为了更加高效地维护React应用的数据状态,以Redux为代表的数据管理模式横空出世。</p>
<p>本书以React技术栈为核心,在介绍React用法的基础上,从源码层面分析了Redux思想,同时着重介绍了服务端渲染和同构应用的架构模式。书中包含许多项目实例,不仅为用户打开了React技术栈的大门,更能提升读者对前沿领域的整体认知。</p>
<h2 id="本书收到的赞誉">本书收到的赞誉</h2>
<blockquote>
<p>本书以React为中心,讲解相关技术栈的同时,深刻剖析了隐藏在其后的编程思想,希望更多的开发者能够以这本书为起点,深入把握前端开发技术,活学活用,打造良好的用户体验,为新移动时代创造更多优秀的产品。
<br />
—— 百度公司副总裁,沈抖</p>
</blockquote>
<blockquote>
<p>本书针对React进行了专题研究,其中还包含Redux用法的详细介绍、源码解读、中间件的实现原理,以及前后端同构的解决方案等内容,适合初学者进阶学习React的相关知识,掌握实战技能。建议各位读者按照源码上机练习,达到更好的学习效果。
<br />
—— 资深JavaScript语言专家、知名技术博客作者,<a href="http://www.ruanyifeng.com/home.html">阮一峰</a></p>
</blockquote>
<blockquote>
<p>这本书系统讲解了Redux和同构技术,是一本在垂直领域中非常优秀的专业书籍。本书简明扼要,重视实践,尤其适合初学者。学会Redux可以让你在前端开发中更加游刃有余,同构开发对于拓宽前端开发领域也有着极其重要的意义,建议大家深入学习。
<br />
—— Node.js布道者、Cnode社区管理员,<a href="https://www.zhihu.com/people/i5ting/activities">狼叔(i5ting)</a></p>
</blockquote>
<blockquote>
<p>本书深入React技术,涉及方方面面,从入门到高阶实例,从状态管理到同构应用技巧,无论是在技术实现原理上,还是在实战经验上,本书都能帮助读者对React形成全貌理解。无论你是React新手还是资深工程师,相信都能从本书中获得启发。
<br />
—— 新浪移动前端技术专家,<a href="https://www.zhihu.com/people/xiao-jue-83/activities">付强(小爝)</a></p>
</blockquote>
<blockquote>
<p>本书不仅覆盖了以React、Redux、Next.js等为核心的组件化开发流程,还深入源码解读了技术细节,通过对设计思路的阐述帮助读者增强系统性的理解和认知。相信本书可以帮助广大开发者更好地掌握React体系的精髓,创造出体验更佳的产品。
<br />
—— 百度资深工程师,<a href="https://www.zhihu.com/people/justineo/activities">顾轶灵</a></p>
</blockquote>
<h2 id="阅读答疑">阅读答疑</h2>
<p><strong>1、本书适合什么人阅读?</strong></p>
<p>答:本书主要适合具有一定JavaScript基础的前端工程师,以及对前端开发感兴趣的相关从业人员阅读。</p>
<p><strong>2、阅读前要安装什么样的环境?</strong></p>
<p>答:书中的示例代码基于React v16以上版本,需安装Node v8.9.4。</p>
<p><strong>3、如何获取书中的示例代码?</strong></p>
<p>答:扫描本书前言中“读者服务”部分给出的二维码,即可获得本书示例代码的下载方式。</p>
<p><strong>4、读完本书能够对同构技术掌握到什么程度?</strong></p>
<p>答:可以独立使用React框架搭建服务端渲染架构,完成代码同构设计。</p>
<h2 id="作者简介">作者简介</h2>
<p>侯策,硕士毕业于法国国立高等电信学校。曾任职于BePATIENT集团、法国能源和苏伊士集团。2015年回国加入百度知识搜索部,负责多个产品线的大型技术迭代。</p>
<p>颜海镜(我),知名技术博主,开源达人,常以歪脖无脸男形象作为头像,经过多年沉淀,专注Web前端开发,先后任职于金山、百度、美团点评,负责前端开发工作。</p>
<h2 id="总结">总结</h2>
<p>《React 状态管理与同构实战》这本书由我和前端知名技术专家<a href="https://www.zhihu.com/people/lucas-hc">侯策</a>合力打磨,凝结了我们在学习、实践 React 框架过程中的积累和心得。</p>
<p>最后送上一组实拍图,再次感谢各位的支持与鼓励!恳请各位批评指正!</p>
<p><img src="/blog/527.jpg" width="400" /></p>
人生需要勇气(我的新书自序)
2018-08-13T00:00:00+00:00
http://yanhaijing.com/web/2018/08/13/my-react-book-preface
<p>从去年起,我和知名技术专家<a href="https://www.zhihu.com/people/lucas-hc">侯策</a>开始了合著之旅,今年我们共同打磨的书籍《React 状态管理与同构实战》终于正式出版了!</p>
<p><img src="/blog/526.png" width="150" /></p>
<p>在书籍定稿之际,思前想后,我写了下面这篇自序</p>
<h2 id="人生需要勇气">人生需要勇气</h2>
<p>人类的每次一进步,技术的每一次发展,都源自对未知世界的探索,探索让我们发现更大的世界,有人说探索需要好奇心,有人说探索需要想象力,而我觉得最重要的是勇气,面对未知,勇敢的人才能迈出一步</p>
<p>在前端的世界里,从来不缺乏有勇气的人,这些勇士们引领着前端的不断变革,技术更新了一代又一代,从以jQuery为代表的的操作DOM时代,到以Backbone为代表的的MVC框架,再到以AngularJS为代表的MVVM框架,再到以React为代表的的新一代技术,前端得到了空前的繁荣,颇有百家争鸣,百花齐放的局面,而这一切都源自勇气</p>
<p>如果你对上面提到的名词不了解也没关系,如果你对当前爆炸的技术感觉迷茫也不要担心,我也曾经害怕恐惧,对新技术畏手畏脚,其实人的天性就是喜欢熟悉的环境,幸运的是我突破了自己,曾经陌生的名词,如今都被我驾驭的很好,是勇气给了我力量</p>
<p>关于我和React的故事,要感谢本书的另一位作者——候策,也是我最志同道合的同事和朋友,他还是一位非常优秀的勇士,他最先研究React,并给我介绍了很多React的知识,也进行了很多探讨,最开始仅仅是做一些比较小的内部系统,现在React已经变成我主要的技术栈了,极大提供了我的工作效率,这次帮我战胜未知的除了勇气还有友谊,关于我对React理解,你可以阅读本书后面的内容,我已经把我的想法总结好等你阅读了</p>
<p>关于我和这本书的故事,还是要感谢候策,你之所以能看到这本书,是因为他是一位勇气值爆棚的真正勇士,最开始候策和我说要写一本书的时候,我是拒绝的,虽然我写了很多博文,也阅读了很多书籍,但我从来没写过一本书,面对未知我有点犹豫,最后我还是决定要试一下,感谢勇气和友谊,最终我又一次挑战了自己,也欢迎大家关注我的博客(<a href="http://yanhaijing.com">yanhaijing.com</a>),在那里有更多关于我的故事</p>
<p>写书和写博客还是有很大差别的,整个写作过程就是挑战和突破的过程,因此书中难免有纰漏之处,也欢迎得到大家的斧正,如果说面对未知,勇气让我们卖出第一步,接下来靠的就是坚持了,做任何事情,都是重在开始,贵在坚持</p>
<p>颜海镜
2018 写于北京</p>
<h2 id="总结">总结</h2>
<p>《React 状态管理与同构实战》这本书由我和前端知名技术专家<a href="https://www.zhihu.com/people/lucas-hc">侯策</a>合力打磨,凝结了我们在学习、实践 React 框架过程中的积累和心得。除了 React 框架使用介绍以外,着重剖析了状态管理以及服务端渲染同构应用方面的内容。同时吸取了社区大量优秀思想,进行归纳比对。</p>
<p>本书受到百度公司副总裁沈抖、百度高级前端工程师<a href="https://www.zhihu.com/people/dong-rui-24/activities">董睿</a>,以及知名JavaScript语言专家<a href="http://www.ruanyifeng.com/home.html">阮一峰</a>、Node.js布道者<a href="https://www.zhihu.com/people/i5ting/activities">狼叔</a>、Flarum中文社区创始人 <a href="http://justjavac.com/">justjavac</a>、新浪移动前端技术专家<a href="https://www.zhihu.com/people/xiao-jue-83/activities">小爝</a>、知乎知名博主<a href="https://www.zhihu.com/people/justineo/activities">顾轶灵</a>等前端圈众多专家大咖的联合力荐。</p>
<p>有兴趣的读者可以点击下面的链接购买,再次感谢各位的支持与鼓励!恳请各位批评指正!</p>
<p>京东:<a href="https://item.jd.com/12403508.html" target="_blank">https://item.jd.com/12403508.html</a></p>
<p>当当:<a href="http://product.dangdang.com/25308679.html" target="_blank">http://product.dangdang.com/25308679.html</a></p>
《React 状态管理与同构实战》董睿推荐序
2018-08-13T00:00:00+00:00
http://yanhaijing.com/web/2018/08/13/my-react-book-preface-3
<p>从去年起,我和知名技术专家<a href="https://www.zhihu.com/people/lucas-hc">侯策</a>开始了合著之旅,今年我们共同打磨的书籍《React 状态管理与同构实战》终于正式出版了!</p>
<p><img src="/blog/526.png" width="150" /></p>
<p>在书籍定稿之际,我把底稿寄给了董睿,感谢董睿在百忙之中抽时间阅读了底稿,并为我写了推荐序,一下是推荐序的内容</p>
<h2 id="董睿推荐序">董睿推荐序</h2>
<p>对于前端这样一个重界面开发的领域来说,组件化几乎是真理了。但是不同的场景、不同的人对组件化的理解可能很不相同。我见过许多不同的组件形态,有 ExtJS 这种高度封装的富组件、JQuery UI 这种与页面 DOM 强相关的组件、有 Bootstrap 这种重样式轻行为的组件、以及各种不知名的公司内团队内自己的组件实现等等。</p>
<p>而从来没有一个组件框架能够像 React 一样那么多的被谈论,它很大程度上改变了前端的开发方式。毫无疑问 React 是成功的,为什么呢?也许是因为它的组件模型足够简单易于理解;也许因为它允许开发者把视图写在 JSX 中;也许因为它为很多前端工程师带来了新的理念(虽然有些不是它创造的),JSX、V-DOM、Flux、immutable、HOC、Fiber…;也许因为它对同构的支持;也许因为 React Native;也许,都有。无论如何,这些理由都不重要了。我看到在我的周围,无论是不是使用 React 的同学,都在谈论和学习它,在框架选型的时候它都是无法回避的选项之一。</p>
<p>在我们平时做项目中,框架的选型仅仅是一个开始。真正考验工程师能力的,是如何在框架的理念以及玩法下,驾驭越来越复杂的业务复杂度。React 上手难度并不低。我们需要对其理念和设计思想有足够深入的了解,结合我们自身的业务看哪些是真正有用的,而不会陷入过度设计;我们需要对其实现原理有足够深入的了解,避免陷入性能的瓶颈。在本书中,作者对设计思想、框架实现、和应用实践上都有较多的剖析,相信读者能够快速的将其中的知识应用到项目开发中。而我更希望看到的,是读者在阅读完后,在应用上更深入的思考与交流。比如什么东西是应该作为应用状态管理的、什么样的场景下应该使用怎样的组件间通信模式、相同的应用场景下不用 Redux 换成 Mobx 应该怎么玩等等。融会贯通到忘记一本书,才是真的收获。</p>
<p>百度资深前端工程师、EFE核心成员、San框架主要开发人员 <a href="https://www.zhihu.com/people/dong-rui-24/activities">董睿</a></p>
<h2 id="总结">总结</h2>
<p>《React 状态管理与同构实战》这本书由我和前端知名技术专家<a href="https://www.zhihu.com/people/lucas-hc">侯策</a>合力打磨,凝结了我们在学习、实践 React 框架过程中的积累和心得。除了 React 框架使用介绍以外,着重剖析了状态管理以及服务端渲染同构应用方面的内容。同时吸取了社区大量优秀思想,进行归纳比对。</p>
<p>本书受到百度公司副总裁沈抖、百度高级前端工程师<a href="https://www.zhihu.com/people/dong-rui-24/activities">董睿</a>,以及知名JavaScript语言专家<a href="http://www.ruanyifeng.com/home.html">阮一峰</a>、Node.js布道者<a href="https://www.zhihu.com/people/i5ting/activities">狼叔</a>、Flarum中文社区创始人 <a href="http://justjavac.com/">justjavac</a>、新浪移动前端技术专家<a href="https://www.zhihu.com/people/xiao-jue-83/activities">小爝</a>、知乎知名博主<a href="https://www.zhihu.com/people/justineo/activities">顾轶灵</a>等前端圈众多专家大咖的联合力荐。</p>
<p>有兴趣的读者可以点击下面的链接购买,再次感谢各位的支持与鼓励!恳请各位批评指正!</p>
<p>京东:<a href="https://item.jd.com/12403508.html" target="_blank">https://item.jd.com/12403508.html</a></p>
<p>当当:<a href="http://product.dangdang.com/25308679.html" target="_blank">http://product.dangdang.com/25308679.html</a></p>
《React 状态管理与同构实战》迷渡(justjavac)推荐序
2018-08-13T00:00:00+00:00
http://yanhaijing.com/web/2018/08/13/my-react-book-preface-2
<p>从去年起,我和知名技术专家<a href="https://www.zhihu.com/people/lucas-hc">侯策</a>开始了合著之旅,今年我们共同打磨的书籍《React 状态管理与同构实战》终于正式出版了!</p>
<p><img src="/blog/526.png" width="150" /></p>
<p>在书籍定稿之际,我把底稿寄给了<a href="http://justjavac.com/">justjavac</a>,感谢<a href="http://justjavac.com/">justjavac</a>在百忙之中抽时间阅读了底稿,并为我写了推荐序,一下是推荐序的内容</p>
<h2 id="迷渡justjavac推荐序">迷渡(justjavac)推荐序</h2>
<p>1995年,Brendan Eich花了10天时间开发出一门脚本语言,取名为Mocha,并将其集成到了Netscape浏览器中,不久后这门语言被改名为LiveScript,意思是可以让网页充满动力。同年年底,网景公司和SUN公司达成协议并获得了Java商标的使用权,于是正式将这门语言更名为JavaScript。</p>
<p>历史选择了JavaScript,使其成为了目前浏览器唯一内置支持的语言。时至今日,JavaScript已经不仅仅局限于为网页开发实现特效了,而是真正发展成了一门全功能的编程语言。</p>
<p>我从2005年开始接触网页开发,经历了Web开发的“上古时代”。在Web 1.0时期,我们开发出来的网页是给人“看”的,此时流行jQuery这种用来处理浏览器兼容性的库,以及像Dojo、YUI、Extjs这种用来做UI的库。</p>
<p>随着计算机和浏览器性能的提升,JavaScript的功能开始不再局限于实现简单的网页开发,特别是Ajax的使用更是显著地提升了用户体验,这个时期被称为Web 2.0。站在开发者和使用者的角度,Web 2.0时期开发出来的网页是给人“用”的。此时的JavaScript程序无论从代码数量上还是代码复杂程度上,都是前所未有的。于是开发者们开始借鉴后端流行的MVC框架的思想,随后又根据前端自身的特点改进了传统MVC模式,并发展出了MVP、MVVM等新架构,其中比较有代表性的有Knockout.js、Backbone.js、Ember.js等。</p>
<p>后来,React发布了,自那时起我成为了一名坚定的React使用者。React不仅仅是一个全新的框架,更是一种新的思想。React重新定义了前端View层的开发模式:v = f(s)</p>
<p>其中s代表引用的状态(state),v代表View,而f则是一个把状态映射为View的纯函数。这个简单的公式代表了前端开发的一种模式:View就是对于状态(state)的展示,对于同一个f而言,相同的状态永远对应相同的视图。</p>
<p>React就是这里的f,React生态的不同库则代表着不同的f,比如react-naitve、react-art、react-canvas、react-svg等。</p>
<p>当我们写<TextBox color="red">时,它既可以被react-dom渲染为一个div标签,也可以在服务器端被渲染为一个字符串,还可以被react-native渲染为原生的控件,甚至可以被渲染为Word中的一行文本、Excel中的一个表格等。而这一切的魔法就源自React的思想。</TextBox></p>
<p>但是React只是一个专注于View层的框架,它只负责把状态映射为视图,并不关注状态的来源和转换,因此在实际开发中,我们还需要关注“React全家桶”中的Redux。另外,同构应用可以让开发者只编写一套代码便可以既运行在服务端,又运行在客户端,充分结合两者的优势,并有效避免两者的不足。这也是React的一大优势。</p>
<p>虽然市面上关于React的书已经数不胜数,但是大多都是围绕着React框架本身的使用方法来讲解的,对于深入讲解React状态管理与同构应用的书却寥寥无几,而侯策和颜海镜的这本书正好可以弥补这一方面的不足。</p>
<p>几年前,由于GitHub上的机缘巧合,我认识了本书的作者之一颜海镜。颜海镜不仅是开源的狂热爱好者,也是国内最早学习并实践React的开发者之一。从我认识他起,他就一直在关注各种前端新技术,并开源了很多前端开发工具和库,这一点真的非常难能可贵!
如果你想实战React同构应用,或者想要深入全面地了解有关React状态管理的知识,相信这本《React状态管理与同构实战》一定会给你给多启发。强烈建议各位读者细细品读。</p>
<p><a href="http://justjavac.com/">迷渡(justjavac)</a></p>
<p>Flarum中文社区创始人</p>
<p>2018年6月于天津</p>
<h2 id="总结">总结</h2>
<p>《React 状态管理与同构实战》这本书由我和前端知名技术专家<a href="https://www.zhihu.com/people/lucas-hc">侯策</a>合力打磨,凝结了我们在学习、实践 React 框架过程中的积累和心得。除了 React 框架使用介绍以外,着重剖析了状态管理以及服务端渲染同构应用方面的内容。同时吸取了社区大量优秀思想,进行归纳比对。</p>
<p>本书受到百度公司副总裁沈抖、百度高级前端工程师<a href="https://www.zhihu.com/people/dong-rui-24/activities">董睿</a>,以及知名JavaScript语言专家<a href="http://www.ruanyifeng.com/home.html">阮一峰</a>、Node.js布道者<a href="https://www.zhihu.com/people/i5ting/activities">狼叔</a>、Flarum中文社区创始人 <a href="http://justjavac.com/">justjavac</a>、新浪移动前端技术专家<a href="https://www.zhihu.com/people/xiao-jue-83/activities">小爝</a>、知乎知名博主<a href="https://www.zhihu.com/people/justineo/activities">顾轶灵</a>等前端圈众多专家大咖的联合力荐。</p>
<p>有兴趣的读者可以点击下面的链接购买,再次感谢各位的支持与鼓励!恳请各位批评指正!</p>
<p>京东:<a href="https://item.jd.com/12403508.html" target="_blank">https://item.jd.com/12403508.html</a></p>
<p>当当:<a href="http://product.dangdang.com/25308679.html" target="_blank">http://product.dangdang.com/25308679.html</a></p>
多环境管理终极方案
2018-05-13T00:00:00+00:00
http://yanhaijing.com/tool/2018/05/13/switch-environment
<p>我最近很苦恼,因为我们团队负责很多很多个系统,我们每个系统又分为不同的环境,举个例子吧,假设我们有10个系统,每个系统有开发,测试,线上三个环境,那就是30个域名,这些域名还没什么规律可循,我差不多是个废人了/(ㄒoㄒ)/~~</p>
<p>先来交代下背景,如果你没有这样的问题,那么可以只看看背景,或者以后遇到了再来看具体的方案</p>
<p>有时候我的系统会有一个链接跳转到别人负责的系统,但我并不知道他的系统不同环境是什么,如果我想知道我还得问他。。。</p>
<p>每次给QA提测,明明只改了一个页面,我却要写好几个url发给他。。。</p>
<p>脑补一下我从开发环境切换到测试环境的画面,首先我先找到测试环境的域名,粘贴到浏览器,再去赋值后面的路径,再次粘贴过来,按下回车,大概我要操作十几个按键,我去。。。</p>
<p>总结一下问题就是,多个系统多套环境造成的问题:</p>
<ul>
<li>每个人的记忆成本</li>
<li>项目交接成本</li>
<li>如何高效切换环境</li>
<li>不熟悉人的成本,比如其他项目成员,测试人员,产品等</li>
</ul>
<h2 id="解决方案">解决方案</h2>
<p>那么怎么解决这个问题呢,我最开始的做法应该和大部分人差不多,我把每个系统,每个环境都放到了浏览器收藏夹,可以脑补一下我的收藏夹多么多么壮观</p>
<p>收藏夹解决了一些问题,比如记忆成本,但也只解决了这一个问题,切换环境还是稍微有点复杂</p>
<p>我还想到了另一个绝妙的办法,整理文档,一个常常的文档收集了全部的系统,全部的环境,文档弥补了收藏夹的一些缺点,比如交接成本</p>
<p>文档+收藏夹可以一起使用,文档收录全部的,收藏夹只收藏自己负责的系统,看起来完美了,一般人可能会这么认为,但是切换的效率并没有解决,那么到底有没有办法解决呢!</p>
<p>一个程序员给出了他的终极解决方案,一个浏览器插件,一个会自动识别当前环境是什么环境,并把当前环境对应的其他环境都展示出来的插件,终极解决方案完美的解决了所有的问题</p>
<ul>
<li>支持全部系统</li>
<li>数据一处维护</li>
<li>屏蔽各种细节</li>
<li>自动拼接</li>
</ul>
<h2 id="插件开发">插件开发</h2>
<p>关于插件开发有很多非常不错的资料,当然实时性最好的资料是谷歌官方的资料,但对于英文不好的同学,给大家推荐一个本图灵的书《<a href="http://www.ituring.com.cn/book/1421">Chrome扩展及应用开发</a>》,一个学生写的,完全免费,大赞</p>
<p>关于插件的知识这里不再展开,这里开发的是一个插件类,并且是对全部页面展现的</p>
<p><img src="/blog/522.png" alt="" /></p>
<h2 id="系统设计">系统设计</h2>
<p>麻雀虽小五脏俱全,这么一个小小的插件,主要包含三部分,服务端、客户端和反馈系统,整体架构图如下:</p>
<p><img src="/blog/523.png" alt="" /></p>
<p>数据的设计非常简单,如下所示,version和changeLog用来做升级提示,data中是数据</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.0.0"</span><span class="p">,</span><span class="w">
</span><span class="nl">"changeLog"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"完成域名切换与展示功能"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"dev"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://dev.yanhaijing.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"test"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://test.yanhaijing.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"beta"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://beta.yanhaijing.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"st"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://st.yanhaijing.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"online"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://yanhaijing.com"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>程序执行的流程图如下,首先是获取数据,需要处理各种异常:</p>
<p><img src="/blog/524.png" alt="" /></p>
<p>由于没有放到Chrome的应用商店,所以升级的提示需要自己做,思路也很简单,在程序里面写死当前的版本号,如果当前版本号和接口返回罪行版本号不一致,就提示升级</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">renderUpdate</span><span class="p">(</span><span class="nx">version</span><span class="p">,</span> <span class="nx">changeLog</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">curVersion</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">1.2.0</span><span class="dl">'</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">version</span> <span class="o">===</span> <span class="nx">curVersion</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
<span class="c1">// xxxxxx</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="问题与思考">问题与思考</h2>
<p>下面来看看做的过程中遇到了一些问题和一些思考</p>
<p>先来说说权限的问题,这个对没有经验的人是一个坑吧,比如想获取当前页面url需要配置下面的权限</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"permissions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"https://yanhaijing.com/"</span><span class="p">,</span><span class="w">
</span><span class="s2">"https://*.yanhaijing.com/"</span><span class="p">,</span><span class="w">
</span><span class="s2">"tabs"</span><span class="p">,</span><span class="w">
</span><span class="s2">"activeTab"</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>想使用eval需要配置下面的权限,注意<code class="language-plaintext highlighter-rouge">unsafe-eval</code></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"content_security_policy"</span><span class="p">:</span><span class="w"> </span><span class="s2">"script-src 'self' 'unsafe-eval'; object-src 'self'"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>另一个问题就是如果某一环境有多个域名的问题,比如dev环境有多个域名怎么办?大体上有两条思路,一个就是复用现在的数据,将key的定义放宽,这样成本最小</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"dev1"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://dev.yanhaijing.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"dev2"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://dev.yanhaijing.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"dev3"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://dev.yanhaijing.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"dev4"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://dev.yanhaijing.com"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>另一个思路就是数据再加一层,数据上的设计如下</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"dev"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"dev1"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://dev.yanhaijing.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"dev2"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://dev.yanhaijing.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"dev3"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://dev.yanhaijing.com"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"test"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://test.yanhaijing.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"beta"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://beta.yanhaijing.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"st"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://st.yanhaijing.com"</span><span class="p">,</span><span class="w">
</span><span class="nl">"online"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://yanhaijing.com"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>这样搞的话,需要程序上做出很大改动,并且旧版本都不能够兼容,所以需要两个版本的数据,可以再添加一个data2</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"data": [
{
"dev1": "https://dev.yanhaijing.com",
"dev2": "https://dev.yanhaijing.com",
"dev3": "https://dev.yanhaijing.com"
}
]
"data2": [
{
"dev": {
"dev1": "https://dev.yanhaijing.com",
"dev2": "https://dev.yanhaijing.com",
"dev3": "https://dev.yanhaijing.com"
}
}
]
</code></pre></div></div>
<p>关于如何选择就根据实际情况来吧,如果时间紧迫就选简单的方案</p>
<h2 id="总结">总结</h2>
<p>说了这么,都是思路和方法,下面提供一个实际的<a href="https://github.com/yanhaijing/switch-env">仓库</a>,只是个demo哦</p>
<p>最后再来看下效果图吧</p>
<p><img src="/blog/525.gif" alt="" /></p>
函数式编程的一点实战
2018-03-01T00:00:00+00:00
http://yanhaijing.com/javascript/2018/03/01/functional-programming-practice
<p>函数式编程太火了,我也想学!!!</p>
<p>函数式编程太难了,该怎么入门???</p>
<p>嗯,函数式编程这么火,是一定要学的,但是怎么学呢?本文通过一个例子来介绍函数式编程的用法,先声明本文没有复杂的概念,也没有各种定义,只有一个由浅入深的例子</p>
<h2 id="背景">背景</h2>
<p>假设有一个如下的数据结构,type 是 1-n,flag 表示当前元素的状态,页面大概是一个列表,可以通过类型和选择状态来过滤列表内容</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">list</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="na">flag</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
<span class="na">flag</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
<span class="na">flag</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">];</span>
</code></pre></div></div>
<p>可能的需求有下面这些:</p>
<ul>
<li>获取全部的列表</li>
<li>获取一个或多个类型的列表</li>
<li>获取否定类型的列表</li>
<li>获取指定状态的列表</li>
<li>获取指定状态,指定类型的列表</li>
</ul>
<p>呃呃呃,有点懵是不是,思考下这个程序该怎么写?如果写一个函数满足上面所有的需求呢?</p>
<h2 id="获取全部的列表">获取全部的列表</h2>
<p>下面先来写第一个需求,需要用到数组的 filter 和函数的默认值,就是这么简单</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getList</span><span class="p">(</span>
<span class="nx">filter</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">list</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">filter</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>调用<code class="language-plaintext highlighter-rouge">getList</code>就能获取全部的列表,但上面的参数默认值其实可以提取出一个公共函数</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 高阶函数</span>
<span class="kd">function</span> <span class="nx">bool</span><span class="p">(</span><span class="nx">flag</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="o">!!</span><span class="nx">flag</span><span class="p">;</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="nx">bool</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span> <span class="c1">// function { return true; }</span>
<span class="nx">bool</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span> <span class="c1">// function { return false; }</span>
</code></pre></div></div>
<p>快来用 bool 来重写上面的代码吧,更简洁了是不是</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getList</span><span class="p">(</span><span class="nx">filter</span> <span class="o">=</span> <span class="nx">bool</span><span class="p">(</span><span class="kc">true</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">list</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">filter</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>抽象出来的 bool 是一个高阶函数,bool 让返回布尔值的函数变得可以复用,高阶函数让函数可以被复用</p>
<h2 id="获取一个或多个类型的列表">获取一个或多个类型的列表</h2>
<p>如果想获取指定类型的列表,最简单的写法如下</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getList</span><span class="p">(</span><span class="nx">filter</span> <span class="o">=</span> <span class="nx">bool</span><span class="p">(</span><span class="kc">true</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">list</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">filter</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">data</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">data</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">data</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="mi">3</span><span class="p">;</span>
<span class="p">});</span>
</code></pre></div></div>
<p>上面代码中多次调用<code class="language-plaintext highlighter-rouge">data.type</code>,我们把获取对象属性的功能抽象出来</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">pick</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">prop</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">[</span><span class="nx">prop</span><span class="p">];</span>
<span class="p">}</span>
<span class="nx">pick</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="dl">'</span><span class="s1">type</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// data.type</span>
</code></pre></div></div>
<p>下面用 pick 函数改写上面的代码</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">pick</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="dl">'</span><span class="s1">type</span><span class="dl">'</span><span class="p">)</span> <span class="o">===</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">pick</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="dl">'</span><span class="s1">type</span><span class="dl">'</span><span class="p">)</span> <span class="o">===</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">pick</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="dl">'</span><span class="s1">type</span><span class="dl">'</span><span class="p">)</span> <span class="o">===</span> <span class="mi">3</span><span class="p">;</span>
<span class="p">});</span>
</code></pre></div></div>
<p>好像更复杂了。。。要是能把 type 参数提前绑定呢?绑定函数参数好像是函数柯里化,下面的 currying 函数</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">func</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">func</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">add</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">y</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">add10</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">add</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
<span class="nx">add10</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="c1">// 11</span>
<span class="nx">add10</span><span class="p">(</span><span class="mi">5</span><span class="p">);</span> <span class="c1">// 15</span>
</code></pre></div></div>
<p>但 currying 的绑定顺序是从左到右的,上面我们希望的先绑定第二个参数,如果能反转一下参数顺序就好了</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">reverseArgs</span><span class="p">(...</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">args</span><span class="p">.</span><span class="nx">reverse</span><span class="p">();</span>
<span class="p">}</span>
<span class="nx">reverseArgs</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span> <span class="c1">// [3, 2, 1]</span>
</code></pre></div></div>
<p>怎么才能把 reverseArgs 和我们的函数结合起来呢?下面介绍一个神奇的高阶函数</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 返回一个函数A,A在被执行时会依次执行传入的函数参数</span>
<span class="c1">// compose(fn1, fn2, fn3)(1) 相当于fn3(...fn2(...fn1(1)))</span>
<span class="kd">function</span> <span class="nx">compose</span><span class="p">(...</span><span class="nx">fns</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">function</span> <span class="p">(...</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">fns</span><span class="p">.</span><span class="nx">reduce</span><span class="p">((</span><span class="nx">prev</span><span class="p">,</span> <span class="nx">fn</span><span class="p">)</span> <span class="o">=></span> <span class="nx">fn</span><span class="p">(...[].</span><span class="nx">concat</span><span class="p">(</span><span class="nx">prev</span><span class="p">)),</span> <span class="nx">args</span><span class="p">);</span>
<span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>
<p>有了 currying 和反转参数的代码再来改造下我们上面的代码,豁然开朗有木有</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">reversePick</span> <span class="o">=</span> <span class="nx">compose</span><span class="p">(</span><span class="nx">reverseArgs</span><span class="p">,</span> <span class="nx">pick</span><span class="p">);</span> <span class="c1">// 参数顺序被反转 reversePick('type', data)</span>
<span class="kd">const</span> <span class="nx">pickType</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">reversePick</span><span class="p">,</span> <span class="dl">'</span><span class="s1">type</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// 先绑定第一个参数 pickType(data)</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">pickType</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="o">===</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">pickType</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="o">===</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">pickType</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="o">===</span> <span class="mi">3</span><span class="p">;</span>
<span class="p">});</span>
</code></pre></div></div>
<p>但上面的代码并不能执行,由于数组 filter 函数,传给每个 filter 的参数并不是一个,而是三个,这就影响到了我们的 reverseArgs,操作,随意需要先进性裁切操作,还需要引入一个函数</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">sliceArgs</span><span class="p">(</span><span class="nx">num</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">args</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">num</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">sliceArgs2</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">sliceArgs</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">reversePick</span> <span class="o">=</span> <span class="nx">compose</span><span class="p">(</span><span class="nx">sliceArgs2</span><span class="p">,</span> <span class="nx">reverseArgs</span><span class="p">,</span> <span class="nx">pick</span><span class="p">);</span>
</code></pre></div></div>
<p>上面的代码中的另一个问题是存在多次判断的代码,下面提取出来</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">isEqual</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">x</span> <span class="o">===</span> <span class="nx">y</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">isType1</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">isEqual</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">isType2</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">isEqual</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">isType3</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">isEqual</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">isType1</span><span class="p">(</span><span class="nx">pickType</span><span class="p">(</span><span class="nx">data</span><span class="p">));</span>
<span class="p">});</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">isType2</span><span class="p">(</span><span class="nx">pickType</span><span class="p">(</span><span class="nx">data</span><span class="p">));</span>
<span class="p">});</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">isType3</span><span class="p">(</span><span class="nx">pickType</span><span class="p">(</span><span class="nx">data</span><span class="p">));</span>
<span class="p">});</span>
</code></pre></div></div>
<p>显然代码好像更复杂了,别急继续往下看,上面代码重复写了三个函数,利用前面的<code class="language-plaintext highlighter-rouge">compose</code>可以把这个函数也消除,是不是很神奇</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// compose(pickType, isType1(data) = function () { isType1(pickType(data)) }</span>
<span class="nx">getList</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">pickType</span><span class="p">,</span> <span class="nx">isType1</span><span class="p">));</span>
<span class="nx">getList</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">pickType</span><span class="p">,</span> <span class="nx">isType2</span><span class="p">));</span>
<span class="nx">getList</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">pickType</span><span class="p">,</span> <span class="nx">isType3</span><span class="p">));</span>
</code></pre></div></div>
<p>上面解决了一个类型的判断,如果相判断 1 或 2 该怎么做呢?还需要一个或操作的高阶函数</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">or</span><span class="p">(...</span><span class="nx">fns</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">function</span> <span class="p">(...</span><span class="nx">agrs</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">fns</span><span class="p">.</span><span class="nx">some</span><span class="p">(</span><span class="nx">fn</span> <span class="o">=></span> <span class="nx">fn</span><span class="p">(...</span><span class="nx">args</span><span class="p">))</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nx">getList</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">pickType</span><span class="p">,</span> <span class="nx">or</span><span class="p">(</span><span class="nx">isType1</span><span class="p">,</span> <span class="nx">isType2</span><span class="p">))</span>
</code></pre></div></div>
<h2 id="获取否定类型的列表">获取否定类型的列表</h2>
<p>下面来看看如何获取否定类型的列表,比如获取所有 type 非 1 的,看起来需要一个取非的函数</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function not(fn) {
return function (...args) {
return !fn(...args)
}
}
getList(compose(pickType, not(isType1))
</code></pre></div></div>
<h2 id="获取指定状态的列表">获取指定状态的列表</h2>
<p>下面来看看如何获取指定类型的列表,其实参考上面的过程就很容易得出</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">isFlagTrue</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">isEqual</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">isFlagFalse</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">isEqual</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">pickFlag</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">sliceArgs2</span><span class="p">,</span> <span class="nx">reverseArgs</span><span class="p">,</span> <span class="nx">pick</span><span class="p">),</span> <span class="dl">'</span><span class="s1">flag</span><span class="dl">'</span><span class="p">);</span> <span class="c1">// currying reverseArgs 见上面</span>
<span class="nx">getList</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">pickFlag</span><span class="p">,</span> <span class="nx">isFlagTrue</span><span class="p">));</span> <span class="c1">// 获取flag为true的列表</span>
<span class="nx">getList</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">pickFlag</span><span class="p">,</span> <span class="nx">isFlagFalse</span><span class="p">));</span> <span class="c1">// 获取flag为false的列表</span>
</code></pre></div></div>
<h2 id="获取指定状态指定类型的列表">获取指定状态,指定类型的列表</h2>
<p>有了获取指定状态和指定类型的代码,那么如何指定两个呢?我们需要一个并操作的高阶函数</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">and</span><span class="p">(...</span><span class="nx">fns</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">function</span> <span class="p">(...</span><span class="nx">agrs</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">fns</span><span class="p">.</span><span class="nx">every</span><span class="p">((</span><span class="nx">fn</span><span class="p">)</span> <span class="o">=></span> <span class="nx">fn</span><span class="p">(...</span><span class="nx">args</span><span class="p">));</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="c1">// 获取类型为1,flag为true的列表</span>
<span class="nx">getList</span><span class="p">(</span><span class="nx">and</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">pickType</span><span class="p">,</span> <span class="nx">isType1</span><span class="p">),</span> <span class="nx">compose</span><span class="p">(</span><span class="nx">pickFlag</span><span class="p">,</span> <span class="nx">isFlagTrue</span><span class="p">)));</span>
<span class="c1">// 获取类型不为1,flag为true的列表</span>
<span class="nx">getList</span><span class="p">(</span><span class="nx">and</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">pickType</span><span class="p">,</span> <span class="nx">not</span><span class="p">(</span><span class="nx">isType1</span><span class="p">)),</span> <span class="nx">compose</span><span class="p">(</span><span class="nx">pickFlag</span><span class="p">,</span> <span class="nx">isFlagTrue</span><span class="p">)));</span>
<span class="c1">// 获取类型为1或2,并且flag为true的列表</span>
<span class="nx">getList</span><span class="p">(</span>
<span class="nx">and</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">pickType</span><span class="p">,</span> <span class="nx">or</span><span class="p">(</span><span class="nx">isType1</span><span class="p">,</span> <span class="nx">isType2</span><span class="p">)),</span> <span class="nx">compose</span><span class="p">(</span><span class="nx">pickFlag</span><span class="p">,</span> <span class="nx">isFlagTrue</span><span class="p">))</span>
<span class="p">);</span>
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>整个过程我们抽象了很多通用函数,这些函数都是可以复用的</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">bool</span><span class="p">(</span><span class="nx">flag</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="o">!!</span><span class="nx">flag</span><span class="p">;</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">pick</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">prop</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">obj</span><span class="p">[</span><span class="nx">prop</span><span class="p">];</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">func</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">func</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="kc">null</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">reverseArgs</span><span class="p">(...</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">args</span><span class="p">.</span><span class="nx">reverse</span><span class="p">();</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">sliceArgs</span><span class="p">(</span><span class="nx">num</span><span class="p">,</span> <span class="p">...</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">args</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">num</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">compose</span><span class="p">(...</span><span class="nx">fns</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">function</span> <span class="p">(...</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">fns</span><span class="p">.</span><span class="nx">reduce</span><span class="p">((</span><span class="nx">prev</span><span class="p">,</span> <span class="nx">fn</span><span class="p">)</span> <span class="o">=></span> <span class="nx">fn</span><span class="p">(...[].</span><span class="nx">concat</span><span class="p">(</span><span class="nx">prev</span><span class="p">)),</span> <span class="nx">args</span><span class="p">);</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">isEqual</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">x</span> <span class="o">===</span> <span class="nx">y</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">or</span><span class="p">(...</span><span class="nx">fns</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">function</span> <span class="p">(...</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">fns</span><span class="p">.</span><span class="nx">some</span><span class="p">((</span><span class="nx">fn</span><span class="p">)</span> <span class="o">=></span> <span class="nx">fn</span><span class="p">(...</span><span class="nx">args</span><span class="p">));</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">not</span><span class="p">(</span><span class="nx">fn</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">function</span> <span class="p">(...</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="o">!</span><span class="nx">fn</span><span class="p">(...</span><span class="nx">args</span><span class="p">);</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">and</span><span class="p">(...</span><span class="nx">fns</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">function</span> <span class="p">(...</span><span class="nx">args</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">fns</span><span class="p">.</span><span class="nx">every</span><span class="p">((</span><span class="nx">fn</span><span class="p">)</span> <span class="o">=></span> <span class="nx">fn</span><span class="p">(...</span><span class="nx">args</span><span class="p">));</span>
<span class="p">};</span>
<span class="p">}</span>
</code></pre></div></div>
<p>完整的业务代码如下,有了这些函数,代码变得非常简单,易读,并且非常灵活,可以随意组合</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">sliceArgs2</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">sliceArgs</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">pickType</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">sliceArgs2</span><span class="p">,</span> <span class="nx">reverseArgs</span><span class="p">,</span> <span class="nx">pick</span><span class="p">),</span> <span class="dl">'</span><span class="s1">type</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">isType1</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">isEqual</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">isType2</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">isEqual</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">isType3</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">isEqual</span><span class="p">,</span> <span class="mi">3</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">pickFlag</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">sliceArgs2</span><span class="p">,</span> <span class="nx">reverseArgs</span><span class="p">,</span> <span class="nx">pick</span><span class="p">),</span> <span class="dl">'</span><span class="s1">flag</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">isFlagTrue</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">isEqual</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">isFlagFalse</span> <span class="o">=</span> <span class="nx">currying</span><span class="p">(</span><span class="nx">isEqual</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">list</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="na">flag</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
<span class="na">flag</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">{</span>
<span class="na">type</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
<span class="na">flag</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
<span class="p">},</span>
<span class="p">];</span>
<span class="kd">function</span> <span class="nx">getList</span><span class="p">(</span><span class="nx">filter</span> <span class="o">=</span> <span class="nx">bool</span><span class="p">(</span><span class="kc">true</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">list</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">filter</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// 获取类型为1,flag为true的列表</span>
<span class="nx">getList</span><span class="p">(</span><span class="nx">and</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">pickType</span><span class="p">,</span> <span class="nx">isType1</span><span class="p">),</span> <span class="nx">compose</span><span class="p">(</span><span class="nx">pickFlag</span><span class="p">,</span> <span class="nx">isFlagTrue</span><span class="p">)));</span>
<span class="c1">// 获取类型不为1,flag为true的列表</span>
<span class="nx">getList</span><span class="p">(</span><span class="nx">and</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">pickType</span><span class="p">,</span> <span class="nx">not</span><span class="p">(</span><span class="nx">isType1</span><span class="p">)),</span> <span class="nx">compose</span><span class="p">(</span><span class="nx">pickFlag</span><span class="p">,</span> <span class="nx">isFlagTrue</span><span class="p">)));</span>
<span class="c1">// 获取类型为1或2,并且flag为true的列表</span>
<span class="nx">getList</span><span class="p">(</span>
<span class="nx">and</span><span class="p">(</span><span class="nx">compose</span><span class="p">(</span><span class="nx">pickType</span><span class="p">,</span> <span class="nx">or</span><span class="p">(</span><span class="nx">isType1</span><span class="p">,</span> <span class="nx">isType2</span><span class="p">)),</span> <span class="nx">compose</span><span class="p">(</span><span class="nx">pickFlag</span><span class="p">,</span> <span class="nx">isFlagTrue</span><span class="p">))</span>
<span class="p">);</span>
</code></pre></div></div>
<p>下面给出对应的非函数式写法的代码,对比一下,大量的过程式代码,区别还是很大的,如果是你,你会使用那种方式呢?</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">getList</span><span class="p">(</span><span class="nx">filter</span> <span class="o">=</span> <span class="nx">bool</span><span class="p">(</span><span class="kc">true</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">list</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">filter</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// 获取类型为1,flag为true的列表</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">data</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="mi">1</span> <span class="o">&&</span> <span class="nx">data</span><span class="p">.</span><span class="nx">flag</span> <span class="o">===</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">});</span>
<span class="c1">// 获取类型不为1,flag为true的列表</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">data</span><span class="p">.</span><span class="nx">type</span> <span class="o">!==</span> <span class="mi">1</span> <span class="o">&&</span> <span class="nx">data</span><span class="p">.</span><span class="nx">flag</span> <span class="o">===</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">});</span>
<span class="c1">// 获取类型为1或2,并且flag为true的列表</span>
<span class="nx">getList</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="mi">1</span> <span class="o">||</span> <span class="nx">data</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="mi">2</span><span class="p">)</span> <span class="o">&&</span> <span class="nx">data</span><span class="p">.</span><span class="nx">flag</span> <span class="o">===</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">});</span>
</code></pre></div></div>
<p>最后给大家推荐一本 javascript 函数式编程的书籍《<a href="https://amazon.cn/gp/product/B01264FOY4/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=yanhaijing-23&creative=3200&linkCode=as2&creativeASIN=B01264FOY4&linkId=b05428b19333f7f958a427562757de10">JavaScript 函数式编程</a>》,如果对函数式感兴趣的话就赶紧入手吧</p>
CSS实现水平垂直居中的1010种方式
2018-01-17T00:00:00+00:00
http://yanhaijing.com/css/2018/01/17/horizontal-vertical-center
<p>划重点,这是一道面试必考题,很多面试官都喜欢问这个问题,我就被问过好几次了</p>
<p><img src="/blog/528.png" alt="" /></p>
<p>要实现上图的效果看似很简单,实则暗藏玄机,本文总结了一下CSS实现水平垂直居中的方式大概有下面这些,本文将逐一介绍一下,我将本文整理成了一个<a href="https://github.com/yanhaijing/vertical-center">github仓库</a>,欢迎大家star</p>
<p>仅居中元素定宽高适用</p>
<ul>
<li>absolute + 负margin</li>
<li>absolute + margin auto</li>
<li>absolute + calc</li>
</ul>
<p>居中元素不定宽高</p>
<ul>
<li>absolute + transform</li>
<li>lineheight</li>
<li>writing-mode</li>
<li>table</li>
<li>css-table</li>
<li>flex</li>
<li>grid</li>
</ul>
<h2 id="absolute--负margin">absolute + 负margin</h2>
<p>为了实现上面的效果先来做些准备工作,假设HTML代码如下,总共两个元素,父元素和子元素</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"wp"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"box size"</span><span class="nt">></span>123123<span class="nt"></div></span>
<span class="nt"></div></span>
</code></pre></div></div>
<p>wp是父元素的类名,box是子元素的类名,因为有定宽和不定宽的区别,size用来表示指定宽度,下面是所有效果都要用到的公共代码,主要是设置颜色和宽高</p>
<p><strong>注意:后面不在重复这段公共代码,只会给出相应提示</strong></p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* 公共代码 */</span>
<span class="nc">.wp</span> <span class="p">{</span>
<span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="no">red</span><span class="p">;</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">300px</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">300px</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.box</span> <span class="p">{</span>
<span class="nl">background</span><span class="p">:</span> <span class="no">green</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.box.size</span><span class="p">{</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">100px</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">100px</span><span class="p">;</span>
<span class="p">}</span>
<span class="c">/* 公共代码 */</span>
</code></pre></div></div>
<p>绝对定位的百分比是相对于父元素的宽高,通过这个特性可以让子元素的居中显示,但绝对定位是基于子元素的左上角,期望的效果是子元素的中心居中显示</p>
<p>为了修正这个问题,可以借助外边距的负值,负的外边距可以让元素向相反方向定位,通过指定子元素的外边距为子元素宽度一半的负值,就可以让子元素居中了,css代码如下</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* 此处引用上面的公共代码 */</span>
<span class="c">/* 此处引用上面的公共代码 */</span>
<span class="c">/* 定位代码 */</span>
<span class="nc">.wp</span> <span class="p">{</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">relative</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.box</span> <span class="p">{</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span><span class="err">;</span>
<span class="nl">top</span><span class="p">:</span> <span class="m">50%</span><span class="p">;</span>
<span class="nl">left</span><span class="p">:</span> <span class="m">50%</span><span class="p">;</span>
<span class="nl">margin-left</span><span class="p">:</span> <span class="m">-50px</span><span class="p">;</span>
<span class="nl">margin-top</span><span class="p">:</span> <span class="m">-50px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这是我比较常用的方式,这种方式比较好理解,兼容性也很好,缺点是需要知道子元素的宽高</p>
<p>点击查看<a href="http://yanhaijing.com/vertical-center/absolute1.html">完整DEMO</a></p>
<h2 id="absolute--margin-auto">absolute + margin auto</h2>
<p>这种方式也要求居中元素的宽高必须固定,HTML代码如下</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"wp"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"box size"</span><span class="nt">></span>123123<span class="nt"></div></span>
<span class="nt"></div></span>
</code></pre></div></div>
<p>这种方式通过设置各个方向的距离都是0,此时再讲margin设为auto,就可以在各个方向上居中了</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* 此处引用上面的公共代码 */</span>
<span class="c">/* 此处引用上面的公共代码 */</span>
<span class="c">/* 定位代码 */</span>
<span class="nc">.wp</span> <span class="p">{</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">relative</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.box</span> <span class="p">{</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span><span class="err">;</span>
<span class="nl">top</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">left</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">right</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">bottom</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">margin</span><span class="p">:</span> <span class="nb">auto</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这种方法兼容性也很好,缺点是需要知道子元素的宽高</p>
<p>点击查看<a href="http://yanhaijing.com/vertical-center/absolute2.html">完整DEMO</a></p>
<h2 id="absolute--calc">absolute + calc</h2>
<p>这种方式也要求居中元素的宽高必须固定,所以我们为box增加size类,HTML代码如下</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"wp"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"box size"</span><span class="nt">></span>123123<span class="nt"></div></span>
<span class="nt"></div></span>
</code></pre></div></div>
<p>感谢css3带来了计算属性,既然top的百分比是基于元素的左上角,那么在减去宽度的一半就好了,代码如下</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* 此处引用上面的公共代码 */</span>
<span class="c">/* 此处引用上面的公共代码 */</span>
<span class="c">/* 定位代码 */</span>
<span class="nc">.wp</span> <span class="p">{</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">relative</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.box</span> <span class="p">{</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span><span class="err">;</span>
<span class="nl">top</span><span class="p">:</span> <span class="n">calc</span><span class="p">(</span><span class="m">50%</span> <span class="n">-</span> <span class="m">50px</span><span class="p">);</span>
<span class="nl">left</span><span class="p">:</span> <span class="n">calc</span><span class="p">(</span><span class="m">50%</span> <span class="n">-</span> <span class="m">50px</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这种方法兼容性依赖calc的兼容性,缺点是需要知道子元素的宽高</p>
<p>点击查看<a href="http://yanhaijing.com/vertical-center/absolute3.html">完整DEMO</a></p>
<h2 id="absolute--transform">absolute + transform</h2>
<p>还是绝对定位,但这个方法不需要子元素固定宽高,所以不再需要size类了,HTML代码如下</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"wp"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"box"</span><span class="nt">></span>123123<span class="nt"></div></span>
<span class="nt"></div></span>
</code></pre></div></div>
<p>修复绝对定位的问题,还可以使用css3新增的transform,transform的translate属性也可以设置百分比,其是相对于自身的宽和高,所以可以讲translate设置为-50%,就可以做到居中了,代码如下</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* 此处引用上面的公共代码 */</span>
<span class="c">/* 此处引用上面的公共代码 */</span>
<span class="c">/* 定位代码 */</span>
<span class="nc">.wp</span> <span class="p">{</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">relative</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.box</span> <span class="p">{</span>
<span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span>
<span class="nl">top</span><span class="p">:</span> <span class="m">50%</span><span class="p">;</span>
<span class="nl">left</span><span class="p">:</span> <span class="m">50%</span><span class="p">;</span>
<span class="nl">transform</span><span class="p">:</span> <span class="n">translate</span><span class="p">(</span><span class="m">-50%</span><span class="p">,</span> <span class="m">-50%</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这种方法兼容性依赖translate2d的兼容性</p>
<p>点击查看<a href="http://yanhaijing.com/vertical-center/absolute4.html">完整DEMO</a></p>
<h2 id="lineheight">lineheight</h2>
<p>利用行内元素居中属性也可以做到水平垂直居中,HTML代码如下</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"wp"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"box"</span><span class="nt">></span>123123<span class="nt"></div></span>
<span class="nt"></div></span>
</code></pre></div></div>
<p>把box设置为行内元素,通过<code class="language-plaintext highlighter-rouge">text-align</code>就可以做到水平居中,但很多同学可能不知道通过通过<code class="language-plaintext highlighter-rouge">vertical-align</code>也可以在垂直方向做到居中,代码如下</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* 此处引用上面的公共代码 */</span>
<span class="c">/* 此处引用上面的公共代码 */</span>
<span class="c">/* 定位代码 */</span>
<span class="nc">.wp</span> <span class="p">{</span>
<span class="nl">line-height</span><span class="p">:</span> <span class="m">300px</span><span class="p">;</span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">0px</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.box</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">16px</span><span class="p">;</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
<span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">middle</span><span class="p">;</span>
<span class="nl">line-height</span><span class="p">:</span> <span class="n">initial</span><span class="p">;</span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">left</span><span class="p">;</span> <span class="c">/* 修正文字 */</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这种方法需要在子元素中将文字显示重置为想要的效果</p>
<p>点击查看<a href="http://yanhaijing.com/vertical-center/lineheight.html">完整DEMO</a></p>
<h2 id="writing-mode">writing-mode</h2>
<p>很多同学一定和我一样不知道<code class="language-plaintext highlighter-rouge">writing-mode</code>属性,感谢@张鑫旭老师的反馈,简单来说writing-mode可以改变文字的显示方向,比如可以通过writing-mode让文字的显示变为垂直方向</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"div1"</span><span class="nt">></span>水平方向<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"div2"</span><span class="nt">></span>垂直方向<span class="nt"></div></span>
</code></pre></div></div>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.div2</span> <span class="p">{</span>
<span class="nl">writing-mode</span><span class="p">:</span> <span class="n">vertical-lr</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>显示效果如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>水平方向
垂
直
方
向
</code></pre></div></div>
<p>更神奇的是所有水平方向上的css属性,都会变为垂直方向上的属性,比如<code class="language-plaintext highlighter-rouge">text-align</code>,通过<code class="language-plaintext highlighter-rouge">writing-mode</code>和<code class="language-plaintext highlighter-rouge">text-align</code>就可以做到水平和垂直方向的居中了,只不过要稍微麻烦一点</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"wp"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"wp-inner"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"box"</span><span class="nt">></span>123123<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"></div></span>
</code></pre></div></div>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* 此处引用上面的公共代码 */</span>
<span class="c">/* 此处引用上面的公共代码 */</span>
<span class="c">/* 定位代码 */</span>
<span class="nc">.wp</span> <span class="p">{</span>
<span class="nl">writing-mode</span><span class="p">:</span> <span class="n">vertical-lr</span><span class="p">;</span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.wp-inner</span> <span class="p">{</span>
<span class="nl">writing-mode</span><span class="p">:</span> <span class="n">horizontal-tb</span><span class="p">;</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.box</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
<span class="nl">margin</span><span class="p">:</span> <span class="nb">auto</span><span class="p">;</span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">left</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这种方法实现起来和理解起来都稍微有些复杂</p>
<p>点击查看<a href="http://yanhaijing.com/vertical-center/writing-mode.html">完整DEMO</a></p>
<h2 id="table">table</h2>
<p>曾经table被用来做页面布局,现在没人这么做了,但table也能够实现水平垂直居中,但是会增加很多冗余代码</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><table></span>
<span class="nt"><tbody></span>
<span class="nt"><tr></span>
<span class="nt"><td</span> <span class="na">class=</span><span class="s">"wp"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"box"</span><span class="nt">></span>123123<span class="nt"></div></span>
<span class="nt"></td></span>
<span class="nt"></tr></span>
<span class="nt"></tbody></span>
<span class="nt"></table></span>
</code></pre></div></div>
<p>tabel单元格中的内容天然就是垂直居中的,只要添加一个水平居中属性就好了</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.wp</span> <span class="p">{</span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.box</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这种方法就是代码太冗余,而且也不是table的正确用法</p>
<p>点击查看<a href="http://yanhaijing.com/vertical-center/table.html">完整DEMO</a></p>
<h2 id="css-table">css-table</h2>
<p>css新增的table属性,可以让我们把普通元素,变为table元素的现实效果,通过这个特性也可以实现水平垂直居中</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"wp"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"box"</span><span class="nt">></span>123123<span class="nt"></div></span>
<span class="nt"></div></span>
</code></pre></div></div>
<p>下面通过css属性,可以让div显示的和table一样</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.wp</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="nb">table-cell</span><span class="p">;</span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">middle</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.box</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>这种方法和table一样的原理,但却没有那么多冗余代码,兼容性也还不错</p>
<p>点击查看<a href="http://yanhaijing.com/vertical-center/css-table.html">完整DEMO</a></p>
<h2 id="flex">flex</h2>
<p>flex作为现代的布局方案,颠覆了过去的经验,只需几行代码就可以优雅的做到水平垂直居中</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"wp"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"box"</span><span class="nt">></span>123123<span class="nt"></div></span>
<span class="nt"></div></span>
</code></pre></div></div>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.wp</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span>
<span class="nl">justify-content</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="nl">align-items</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>目前在移动端已经完全可以使用flex了,PC端需要看自己业务的兼容性情况</p>
<p>点击查看<a href="http://yanhaijing.com/vertical-center/flex.html">完整DEMO</a></p>
<h2 id="grid">grid</h2>
<p>感谢@一丝姐 反馈的这个方案,css新出的网格布局,由于兼容性不太好,一直没太关注,通过grid也可以实现水平垂直居中</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"wp"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"box"</span><span class="nt">></span>123123<span class="nt"></div></span>
<span class="nt"></div></span>
</code></pre></div></div>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.wp</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">grid</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.box</span> <span class="p">{</span>
<span class="nl">align-self</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="py">justify-self</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>代码量也很少,但兼容性不如flex,不推荐使用</p>
<p>点击查看<a href="http://yanhaijing.com/vertical-center/grid.html">完整DEMO</a></p>
<h2 id="总结">总结</h2>
<p>下面对比下各个方式的优缺点,肯定又双叒叕该有同学说回字的写法了,简单总结下</p>
<ul>
<li>PC端有兼容性要求,宽高固定,推荐absolute + 负margin</li>
<li>PC端有兼容要求,宽高不固定,推荐css-table</li>
<li>PC端无兼容性要求,推荐flex</li>
<li>移动端推荐使用flex</li>
</ul>
<p><strong>小贴士:</strong>关于flex的兼容性决方案,请看这里《<a href="//yanhaijing.com/css/2016/08/21/flex-practice-on-mobile/">移动端flex布局实战</a>》</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>居中元素定宽高固定</th>
<th>PC兼容性</th>
<th>移动端兼容性</th>
</tr>
</thead>
<tbody>
<tr>
<td>absolute + 负margin</td>
<td>是</td>
<td>ie6+, chrome4+, firefox2+</td>
<td>安卓2.3+, iOS6+</td>
</tr>
<tr>
<td>absolute + margin auto</td>
<td>是</td>
<td>ie6+, chrome4+, firefox2+</td>
<td>安卓2.3+, iOS6+</td>
</tr>
<tr>
<td>absolute + calc</td>
<td>是</td>
<td>ie9+, chrome19+, firefox4+</td>
<td>安卓4.4+, iOS6+</td>
</tr>
<tr>
<td>absolute + transform</td>
<td>否</td>
<td>ie9+, chrome4+, firefox3.5+</td>
<td>安卓3+, iOS6+</td>
</tr>
<tr>
<td>writing-mode</td>
<td>否</td>
<td>ie6+, chrome4+, firefox3.5+</td>
<td>安卓2.3+, iOS5.1+</td>
</tr>
<tr>
<td>lineheight</td>
<td>否</td>
<td>ie6+, chrome4+, firefox2+</td>
<td>安卓2.3+, iOS6+</td>
</tr>
<tr>
<td>table</td>
<td>否</td>
<td>ie6+, chrome4+, firefox2+</td>
<td>安卓2.3+, iOS6+</td>
</tr>
<tr>
<td>css-table</td>
<td>否</td>
<td>ie8+, chrome4+, firefox2+</td>
<td>安卓2.3+, iOS6+</td>
</tr>
<tr>
<td>flex</td>
<td>否</td>
<td>ie10+, chrome4+, firefox2+</td>
<td>安卓2.3+, iOS6+</td>
</tr>
<tr>
<td>grid</td>
<td>否</td>
<td>ie10+, chrome57+, firefox52+</td>
<td>安卓6+, iOS10.3+</td>
</tr>
</tbody>
</table>
<p>最近发现很多同学都对css不够重视,这其实是不正确的,比如下面的这么简单的问题都有那么多同学不会,我也是很无语</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"red blue"</span><span class="nt">></span>123<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"blue red"</span><span class="nt">></span>123<span class="nt"></div></span>
</code></pre></div></div>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.red</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="no">red</span>
<span class="p">}</span>
<span class="nc">.blue</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="no">blue</span>
<span class="p">}</span>
</code></pre></div></div>
<p>问两个div的颜色分别是什么,竟然只有40%的同学能够答对,这40%中还有很多同学不知道为什么,希望这些同学好好补习下CSS基础,下面给大家推荐几本CSS的书籍</p>
<ul>
<li><a href="https://amazon.cn/gp/product/B00M2DKZ1W/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=yanhaijing-23&creative=3200&linkCode=as2&creativeASIN=B00M2DKZ1W&linkId=b7b8130f4f0da8be122691521b9bae47">CSS设计指南</a>(最好的入门书)</li>
<li><a href="https://amazon.cn/gp/product/B00LHL3DV4/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=yanhaijing-23&creative=3200&linkCode=as2&creativeASIN=B00LHL3DV4&linkId=1fcb124655bbe765eecd9ca84af6f6ba">图解CSS3</a>(最好的CSS3入门)</li>
<li><a href="https://amazon.cn/gp/product/B0788XRYGF/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=yanhaijing-23&creative=3200&linkCode=as2&creativeASIN=B0788XRYGF&linkId=d6295dbff4749b7584891ba004214edb">CSS世界</a>(CSS进阶)</li>
<li><a href="https://amazon.cn/gp/product/B01ET3FO86/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=yanhaijing-23&creative=3200&linkCode=as2&creativeASIN=B01ET3FO86&linkId=bc697a9006483f55e36256f5458df5e8">CSS揭秘</a>(CSS高手)</li>
</ul>
<p>喜欢看网络资料同学,可以看看MDN的这个<a href="https://developer.mozilla.org/zh-CN/docs/Web/Guide/CSS/Getting_started">CSS入门教程</a>,强烈推荐,英语好的同学建议看英文版</p>
缩放布局的那些事儿
2018-01-11T00:00:00+00:00
http://yanhaijing.com/css/2018/01/11/scale-layout
<p>本文将讲解缩放布局的相关知识</p>
正则表达式教程——原理篇
2017-11-29T00:00:00+00:00
http://yanhaijing.com/javascript/2017/11/29/regexp-principle
<p>本系列前两篇文章,介绍了正则表达式的<a href="http://yanhaijing.com/javascript/2017/08/06/regexp-syntax/">语法</a>和正则表达式在js中的<a href="http://yanhaijing.com/javascript/2017/08/26/regexp-practice/">实践</a></p>
<p>本文计划内容如下:</p>
<ul>
<li>将讲解正则表达式的原理</li>
<li>计划开发一个正则引擎</li>
<li>计划开发正则匹配动画展示系统</li>
</ul>
<p>本文正在众筹!!!</p>
<p>你的打赏,是我写下去的动力!!!待续。。。</p>
Rem布局的原理解析
2017-09-29T00:00:00+00:00
http://yanhaijing.com/css/2017/09/29/principle-of-rem-layout
<p>面试中发现很多人对rem布局的原理不是很清楚,只停留在会使用的阶段,或者理解完全是错误的,本文将给大家讲清楚rem布局的原理,使用方案等知识</p>
<h2 id="什么是rem">什么是Rem</h2>
<p>rem和em很容易混淆,其实两个都是css的单位,并且也都是相对单位,现有的em,css3才引入的rem,在介绍rem之前,我们先来了解下em</p>
<blockquote>
<p>em作为font-size的单位时,其代表父元素的字体大小,em作为其他属性单位时,代表自身字体大小——MDN</p>
</blockquote>
<p>我在面试时经常问会一道和em有关的题,来看一下面试者对css细节的了解程度,如下,问s1、s2、s5、s6的<code class="language-plaintext highlighter-rouge">font-size</code>和<code class="language-plaintext highlighter-rouge">line-height</code>分别是多少px,先来想一想,结尾处有答案和解释</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"p1"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"s1"</span><span class="nt">></span>1<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"s2"</span><span class="nt">></span>1<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"p2"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"s5"</span><span class="nt">></span>1<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"s6"</span><span class="nt">></span>1<span class="nt"></div></span>
<span class="nt"></div></span>
</code></pre></div></div>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.p1</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">16px</span><span class="p">;</span> <span class="nl">line-height</span><span class="p">:</span> <span class="m">32px</span><span class="p">;}</span>
<span class="nc">.s1</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">2em</span><span class="p">;}</span>
<span class="nc">.s2</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">2em</span><span class="p">;</span> <span class="nl">line-height</span><span class="p">:</span> <span class="m">2em</span><span class="p">;}</span>
<span class="nc">.p2</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">16px</span><span class="p">;</span> <span class="nl">line-height</span><span class="p">:</span> <span class="m">2</span><span class="p">;}</span>
<span class="nc">.s5</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">2em</span><span class="p">;}</span>
<span class="nc">.s6</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">2em</span><span class="p">;</span> <span class="nl">line-height</span><span class="p">:</span> <span class="m">2em</span><span class="p">;}</span>
</code></pre></div></div>
<p>em可以让我们的页面更灵活,更健壮,比起到处写死的px值,em似乎更有张力,改动父元素的字体大小,子元素会等比例变化,这一变化似乎预示了无限可能</p>
<p>有些人提出用em来做弹性布局页面,但其复杂的计算让人诟病,甚至有人专门做了个px和em的<a href="https://vasilis.nl/nerd/code/emcalc/">计算器</a>,不同节点像素值对应的em值,o(╯□╰)o</p>
<p><img src="/blog/519.png" alt="" /></p>
<p>em做弹性布局的缺点还在于牵一发而动全身,一旦某个节点的字体大小发生变化,那么其后代元素都得重新计算,X﹏X</p>
<blockquote>
<p>rem作用于非根元素时,相对于根元素字体大小;rem作用于根元素字体大小时,相对于其出初始字体大小——MDN</p>
</blockquote>
<p>rem取值分为两种情况,设置在根元素时和非根元素时,举个例子</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* 作用于根元素,相对于原始大小(16px),所以html的font-size为32px*/</span>
<span class="nt">html</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">2rem</span><span class="p">}</span>
<span class="c">/* 作用于非根元素,相对于根元素字体大小,所以为64px */</span>
<span class="nt">p</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">2rem</span><span class="p">}</span>
</code></pre></div></div>
<p>rem有rem的优点,em有em的优点,尺有所短,寸有所长,我一直不觉得技术没有什么对错,只有适合不适合,有对错的是使用技术的人,杰出与优秀的区别就在于能否选择合适的技术,并让其发挥优势</p>
<p>我一直觉得em就是为字体和行高而生的,有些时候子元素字体就应该相对于父元素,元素行高就应该相对于字体大小;而rem的有点在于统一的参考系</p>
<h2 id="rem布局原理">Rem布局原理</h2>
<p>rem布局的本质是什么?这是我问过很多人的一个问题,但得到的回答都差强人意。</p>
<p>其实rem布局的本质是等比缩放,一般是基于宽度,试想一下如果UE图能够等比缩放,那该多么美好啊</p>
<p>假设我们将屏幕宽度平均分成100份,每一份的宽度用x表示,<code class="language-plaintext highlighter-rouge">x = 屏幕宽度 / 100</code>,如果将x作为单位,x前面的数值就代表屏幕宽度的百分比</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">p</span> <span class="p">{</span><span class="nl">width</span><span class="p">:</span> <span class="m">50</span><span class="n">x</span><span class="p">}</span> <span class="c">/* 屏幕宽度的50% */</span>
</code></pre></div></div>
<p>如果想要页面元素随着屏幕宽度等比变化,我们需要上面的x单位,不幸的是css中并没有这样的单位,幸运的是在css中有rem,通过rem这个桥梁,可以实现神奇的x</p>
<p>通过上面对rem的介绍,可以发现,如果子元素设置rem单位的属性,通过更改html元素的字体大小,就可以让子元素实际大小发生变化</p>
<pre><code class="language-Css">html {font-size: 16px}
p {width: 2rem} /* 32px*/
html {font-size: 32px}
p {width: 2rem} /*64px*/
</code></pre>
<p>如果让html元素字体的大小,恒等于屏幕宽度的1/100,那1rem和1x就等价了</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">html</span> <span class="p">{</span><span class="py">fons-size</span><span class="p">:</span> <span class="n">width</span> <span class="p">/</span> <span class="m">100</span><span class="p">}</span>
<span class="nt">p</span> <span class="p">{</span><span class="nl">width</span><span class="p">:</span> <span class="m">50rem</span><span class="p">}</span> <span class="c">/* 50rem = 50x = 屏幕宽度的50% */</span>
</code></pre></div></div>
<p>如何让html字体大小一直等于屏幕宽度的百分之一呢? 可以通过js来设置,一般需要在页面dom ready、resize和屏幕旋转中设置</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">fontSize</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">.</span><span class="nx">clientWidth</span> <span class="o">/</span> <span class="mi">100</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">px</span><span class="dl">'</span><span class="p">;</span>
</code></pre></div></div>
<p>那么如何把UE图中的获取的像素单位的值,转换为已rem为单位的值呢?公式是<code class="language-plaintext highlighter-rouge">元素宽度 / UE图宽度 * 100</code>,让我们举个例子,假设UE图尺寸是640px,UE图中的一个元素宽度是100px,根据公式<code class="language-plaintext highlighter-rouge">100/640*100 = 15.625</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>p {width: 15.625rem}
</code></pre></div></div>
<p>下面来验证下上面的计算是否正确,下面的表格是UE图等比缩放下,元素的宽度</p>
<table>
<thead>
<tr>
<th>UE图宽度</th>
<th>UE图中元素宽度</th>
</tr>
</thead>
<tbody>
<tr>
<td>640px</td>
<td>100px</td>
</tr>
<tr>
<td>480px</td>
<td>75px</td>
</tr>
<tr>
<td>320px</td>
<td>50px</td>
</tr>
</tbody>
</table>
<p>下面的表格是通过我们的元素在不同屏幕宽度下的计算值</p>
<table>
<thead>
<tr>
<th>页面宽度</th>
<th>html字体大小</th>
<th>p元素宽度</th>
</tr>
</thead>
<tbody>
<tr>
<td>640px</td>
<td>640/100 = 6.4px</td>
<td>15.625*6.4=100px</td>
</tr>
<tr>
<td>480px</td>
<td>480/100=4.8px</td>
<td>15.625*4.8=75px</td>
</tr>
<tr>
<td>320px</td>
<td>320/100=3.2px</td>
<td>15.625*3.2=50px</td>
</tr>
</tbody>
</table>
<p>可以发现,UE图宽度和屏幕宽度相同时,两边得出的元素宽度是一致的</p>
<p>上面的计算过程有些繁琐,可以通过预处理的function来简化过程,下面是sass的例子,less类似</p>
<div class="language-scss highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ue-width</span><span class="p">:</span> <span class="m">640</span><span class="p">;</span> <span class="cm">/* ue图的宽度 */</span>
<span class="k">@function</span> <span class="nf">px2rem</span><span class="p">(</span><span class="nv">$px</span><span class="p">)</span> <span class="p">{</span>
<span class="k">@return</span> <span class="si">#{</span><span class="nv">$px</span><span class="o">/</span><span class="nv">$ue-width</span><span class="o">*</span><span class="m">100</span><span class="si">}</span><span class="n">rem</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">p</span> <span class="p">{</span>
<span class="nl">width</span><span class="p">:</span> <span class="nf">px2rem</span><span class="p">(</span><span class="m">100</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>上面的代码编译完的结果如下</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">p</span> <span class="p">{</span><span class="nl">width</span><span class="p">:</span> <span class="m">15.625rem</span><span class="p">}</span>
</code></pre></div></div>
<p>其实有了postcss后,这个过程应该放到postcss中,源代码如下</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">p</span> <span class="p">{</span><span class="nl">width</span><span class="p">:</span> <span class="m">100</span><span class="n">px2rem</span><span class="p">}</span>
</code></pre></div></div>
<p>postcss会对px2rem这个单位进行处理,处理后的结果如下,感兴趣的话来写一个px2rem的postcss插件吧</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">p</span> <span class="p">{</span><span class="nl">width</span><span class="p">:</span> <span class="m">15.625rem</span><span class="p">}</span>
</code></pre></div></div>
<h2 id="比rem更好的方案">比Rem更好的方案</h2>
<p>上面提到想让页面元素随着页面宽度变化,需要一个新的单位x,x等于屏幕宽度的百分之一,css3带来了rem的同时,也带来了vw和vh</p>
<blockquote>
<p>vw —— 视口宽度的 1/100;vh —— 视口高度的 1/100 —— MDN</p>
</blockquote>
<p>聪明的你也许一经发现,这不就是单位x吗,没错根据定义可以发现1vw=1x,有了vw我们完全可以绕过rem这个中介了,下面两种方案是等价的,可以看到vw比rem更简单,毕竟rem是为了实现vw么</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* rem方案 */</span>
<span class="nt">html</span> <span class="p">{</span><span class="py">fons-size</span><span class="p">:</span> <span class="n">width</span> <span class="p">/</span> <span class="m">100</span><span class="p">}</span>
<span class="nt">p</span> <span class="p">{</span><span class="nl">width</span><span class="p">:</span> <span class="m">15.625rem</span><span class="p">}</span>
<span class="c">/* vw方案 */</span>
<span class="nt">p</span> <span class="p">{</span><span class="nl">width</span><span class="p">:</span> <span class="m">15.625vw</span><span class="p">}</span>
</code></pre></div></div>
<p>vw还可以和rem方案结合,这样计算html字体大小就不需要用js了</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">html</span> <span class="p">{</span><span class="py">fons-size</span><span class="p">:</span> <span class="m">1vw</span><span class="p">}</span> <span class="c">/* 1vw = width / 100 */</span>
<span class="nt">p</span> <span class="p">{</span><span class="nl">width</span><span class="p">:</span> <span class="m">15.625rem</span><span class="p">}</span>
</code></pre></div></div>
<p>虽然vw各种优点,但是vw也有缺点,首先vw的兼容性不如rem好,使用之前要看下</p>
<table>
<thead>
<tr>
<th>兼容性</th>
<th>Ios</th>
<th>安卓</th>
</tr>
</thead>
<tbody>
<tr>
<td>rem</td>
<td>4.1+</td>
<td>2.1+</td>
</tr>
<tr>
<td>vw</td>
<td>6.1+</td>
<td>4.4+</td>
</tr>
</tbody>
</table>
<p>另外,在使用弹性布局时,一般会限制最大宽度,比如在pc端查看我们的页面,此时vw就无法力不从心了,因为除了width有max-width,其他单位都没有,而rem可以通过控制html根元素的font-size最大值,而轻松解决这个问题</p>
<h2 id="rem不是银弹">Rem不是银弹</h2>
<p>rem是弹性布局的一种实现方式,弹性布局可以算作响应式布局的一种,但响应式布局不是弹性布局,弹性布局强调等比缩放,100%还原;响应式布局强调不同屏幕要有不同的显示,比如媒体查询</p>
<blockquote>
<p>用户选择大屏幕有两个几个出发点,有些人想要更大的字体,更大的图片,比如老花眼的我;有些人想要更多的内容,并不想要更大的图标;有些人想要个镜子。。。——颜海镜</p>
</blockquote>
<p>我认为一般内容型的网站,都不太适合使用rem,因为大屏用户可以自己选择是要更大字体,还是要更多内容,一旦使用了rem,就剥夺了用户的自由,比如百度知道,百度经验都没有使用rem布局;一些偏向app类的,图标类的,图片类的,比如淘宝,活动页面,比较适合使用rem,因为调大字体时并不能调大图标的大小</p>
<p>rem可以做到100%的还原度,但同事rem的制作成本也更大,同时使用rem还有一些问题,下面我们一一列举下:</p>
<p>首先是字体的问题,字体大小并不能使用rem,字体的大小和字体宽度,并不成线性关系,这个函数我还没算出来,所以字体大小不能使用rem;由于设置了根元素字体的大小,会影响所有没有设置字体大小的元素,因为字体大小是会继承的,难道要每个元素都显示设置字体大小???</p>
<p>我们可以在body上做字体修正,比如把body字体大小设置为16px,但如果用户自己设置了更大的字体,此时用户的设置将失效,比如合理的方式是,将其设置为用户的默认字体大小</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">html</span> <span class="p">{</span><span class="py">fons-size</span><span class="p">:</span> <span class="n">width</span> <span class="p">/</span> <span class="m">100</span><span class="p">}</span>
<span class="nt">body</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">16px</span><span class="p">}</span>
</code></pre></div></div>
<p>那字体的大小如何实现响应式呢?可以通过修改body字体的大小来实现,同时所有设置字体大小的地方都是用em单位,对就是em,因为只有em才能实现,同步变化,我早就说过em就是为字体而生的</p>
<p>当然不同屏幕字体大小相同也是非常合理和不错的效果,需要你自己做决策</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@media</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">min-width</span><span class="p">:</span> <span class="m">320px</span><span class="p">)</span> <span class="p">{</span>
<span class="nt">body</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">16px</span><span class="p">}</span>
<span class="p">}</span>
<span class="k">@media</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">min-width</span><span class="p">:</span> <span class="m">481px</span><span class="p">)</span> <span class="n">and</span> <span class="p">(</span><span class="n">max-width</span><span class="p">:</span><span class="m">640px</span><span class="p">)</span> <span class="p">{</span>
<span class="nt">body</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">18px</span><span class="p">}</span>
<span class="p">}</span>
<span class="k">@media</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">min-width</span><span class="p">:</span> <span class="m">641px</span><span class="p">)</span> <span class="p">{</span>
<span class="nt">body</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">20px</span><span class="p">}</span>
<span class="p">}</span>
<span class="nt">p</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">1.2em</span><span class="p">}</span>
<span class="nt">p</span> <span class="nt">a</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">1.2em</span><span class="p">}</span>
</code></pre></div></div>
<p>第二,如果用户在PC端浏览,页面过宽怎么办?一般我们都会设置一个最大宽度,大于这个宽度的话页面居中,两边留白</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">clientWidth</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">.</span><span class="nx">clientWidth</span><span class="p">;</span>
<span class="nx">clientWidth</span> <span class="o">=</span> <span class="nx">clientWidth</span> <span class="o"><</span> <span class="mi">780</span> <span class="p">?</span> <span class="nx">clientWidth</span> <span class="p">:</span> <span class="mi">780</span><span class="p">;</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">fontSize</span> <span class="o">=</span> <span class="nx">clientWidth</span> <span class="o">/</span> <span class="mi">100</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">px</span><span class="dl">'</span><span class="p">;</span>
</code></pre></div></div>
<p>设置body的宽度为100rem,并水平居中</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">body</span> <span class="p">{</span>
<span class="nl">margin</span><span class="p">:</span> <span class="nb">auto</span><span class="p">;</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">100rem</span>
<span class="p">}</span>
</code></pre></div></div>
<p>第三,如果用户禁用了js怎么破?其实这种用户真不多了,要不放弃吧。。。</p>
<p>首先可以添加noscript标签提示用户</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><noscript></span>开启JavaScript,获得更好的体验<span class="nt"></noscript></span>
</code></pre></div></div>
<p>给html添加一个320时的默认字体大小,保证页面可以显示</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">html</span> <span class="p">{</span><span class="py">fons-size</span><span class="p">:</span> <span class="m">3.2px</span><span class="p">}</span>
</code></pre></div></div>
<p>如果你想要更好的体验,不如添加媒体查询吧</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@media</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">min-width</span><span class="p">:</span> <span class="m">320px</span><span class="p">)</span> <span class="p">{</span>
<span class="nt">html</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">3.2px</span><span class="p">}</span>
<span class="p">}</span>
<span class="k">@media</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">min-width</span><span class="p">:</span> <span class="m">481px</span><span class="p">)</span> <span class="n">and</span> <span class="p">(</span><span class="n">max-width</span><span class="p">:</span><span class="m">640px</span><span class="p">)</span> <span class="p">{</span>
<span class="nt">html</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">4.8px</span><span class="p">}</span>
<span class="p">}</span>
<span class="k">@media</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">min-width</span><span class="p">:</span> <span class="m">641px</span><span class="p">)</span> <span class="p">{</span>
<span class="nt">html</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">6.4px</span><span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>rem不是银弹,这个世上也没有银弹,每个方案都有其优点,也有其缺点,学会做出选择和妥协</p>
<p>rem仅能做到内容的缩放,但是对于非矢量资源,比如图片放大时的失真,并无法解决,这个以后有缘再讨论。</p>
<h2 id="rem布局方案">Rem布局方案</h2>
<p>通过上面可以得出实现缩放布局,共有四种方案,下面做一个对比</p>
<table>
<thead>
<tr>
<th>缩放布局</th>
<th>用户体验</th>
<th>兼容性</th>
<th>依赖js</th>
<th>超大屏幕</th>
<th>修正字体</th>
</tr>
</thead>
<tbody>
<tr>
<td>rem+media-query</td>
<td>可</td>
<td>IOS4.1 AN2.1</td>
<td>√</td>
<td>√</td>
<td>×</td>
</tr>
<tr>
<td>rem+js</td>
<td>良</td>
<td>IOS4.1 AN2.1</td>
<td>×</td>
<td>√</td>
<td>×</td>
</tr>
<tr>
<td>rem+vw</td>
<td>优</td>
<td>IOS6.1 AN4.4</td>
<td>√</td>
<td>√</td>
<td>×</td>
</tr>
<tr>
<td>vw</td>
<td>优</td>
<td>IOS6.1 AN4.4</td>
<td>√</td>
<td>×</td>
<td>√</td>
</tr>
</tbody>
</table>
<p>如果要求兼容性,建议rem+js方案,需要解决的问题如下:</p>
<ul>
<li>
<p>修正body字体大小</p>
</li>
<li>浏览器禁用js(可选)</li>
<li>宽度限制,超大屏幕居中(可选)</li>
<li>字体缩放(可选)</li>
</ul>
<p>如果兼容性满足,建议使用rem+vw方案,需要解决的问题如下:</p>
<ul>
<li>修正body字体大小</li>
<li>宽度限制,超大屏幕居中(可选)</li>
<li>字体缩放(可选)</li>
</ul>
<p>但是上面的方案还有个问题,就是分成100份的话,假设屏幕宽度320,此时html大小是3.2px,但浏览器支持最小字体大小是12px,怎么办?那就分成10份呗,只要把上面的100都换成10就好了</p>
<p>下面给一个rem+js方案的完整例子,css的计算没有使用预处理器,这个很简单</p>
<p>html代码如下</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html</span> <span class="na">lang=</span><span class="s">"en"</span><span class="nt">></span>
<span class="nt"><head></span>
<span class="nt"><meta</span> <span class="na">charset=</span><span class="s">"UTF-8"</span><span class="nt">></span>
<span class="nt"><meta</span> <span class="na">name=</span><span class="s">"viewport"</span> <span class="na">content=</span><span class="s">"width=device-width, initial-scale=1, maximum-scale=1"</span><span class="nt">></span>
<span class="nt"><title></span>rem布局——rem+js<span class="nt"></title></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="nt"><noscript></span>开启JavaScript,获得更好的体验<span class="nt"></noscript></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"p1"</span><span class="nt">></span>
宽度为屏幕宽度的50%,字体大小1.2em
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"s1"</span><span class="nt">></span>
字体大小1.2.em
<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"p2"</span><span class="nt">></span>
宽度为屏幕宽度的40%,字体大小默认
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"s2"</span><span class="nt">></span>
字体大小1.2em
<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre></div></div>
<p>css代码如下</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">html</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">32px</span><span class="p">;</span> <span class="c">/* 320/10 */</span>
<span class="p">}</span>
<span class="nt">body</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">16px</span><span class="p">;</span> <span class="c">/* 修正字体大小 */</span>
<span class="c">/* 防止页面过宽 */</span>
<span class="nl">margin</span><span class="p">:</span> <span class="nb">auto</span><span class="p">;</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">10rem</span><span class="p">;</span>
<span class="c">/* 防止页面过宽 */</span>
<span class="nl">outline</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">dashed</span> <span class="no">green</span><span class="p">;</span>
<span class="p">}</span>
<span class="c">/* js被禁止的回退方案 */</span>
<span class="k">@media</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">min-width</span><span class="p">:</span> <span class="m">320px</span><span class="p">)</span> <span class="p">{</span>
<span class="nt">html</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">32px</span><span class="p">}</span>
<span class="nt">body</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">16px</span><span class="p">;}</span>
<span class="p">}</span>
<span class="k">@media</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">min-width</span><span class="p">:</span> <span class="m">481px</span><span class="p">)</span> <span class="n">and</span> <span class="p">(</span><span class="n">max-width</span><span class="p">:</span><span class="m">640px</span><span class="p">)</span> <span class="p">{</span>
<span class="nt">html</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">48px</span><span class="p">}</span>
<span class="nt">body</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">18px</span><span class="p">;}</span>
<span class="p">}</span>
<span class="k">@media</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">min-width</span><span class="p">:</span> <span class="m">641px</span><span class="p">)</span> <span class="p">{</span>
<span class="nt">html</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">64px</span><span class="p">}</span>
<span class="nt">body</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">20px</span><span class="p">;}</span>
<span class="p">}</span>
<span class="nt">noscript</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="nb">block</span><span class="p">;</span>
<span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="m">#d6e9c6</span><span class="p">;</span>
<span class="nl">padding</span><span class="p">:</span> <span class="m">3px</span> <span class="m">5px</span><span class="p">;</span>
<span class="nl">background</span><span class="p">:</span> <span class="m">#dff0d8</span><span class="p">;</span>
<span class="nl">color</span><span class="p">:</span> <span class="m">#3c763d</span><span class="p">;</span>
<span class="p">}</span>
<span class="c">/* js被禁止的回退方案 */</span>
<span class="nc">.p1</span><span class="o">,</span> <span class="nc">.p2</span> <span class="p">{</span>
<span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="no">red</span><span class="p">;</span>
<span class="nl">margin</span><span class="p">:</span> <span class="m">10px</span> <span class="m">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.p1</span> <span class="p">{</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">5rem</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">5rem</span><span class="p">;</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">1.2em</span><span class="p">;</span> <span class="c">/* 字体使用em */</span>
<span class="p">}</span>
<span class="nc">.s1</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">1.2em</span><span class="p">;</span> <span class="c">/* 字体使用em */</span>
<span class="p">}</span>
<span class="nc">.p2</span> <span class="p">{</span>
<span class="nl">width</span><span class="p">:</span> <span class="m">4rem</span><span class="p">;</span>
<span class="nl">height</span><span class="p">:</span> <span class="m">4rem</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.s2</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">1.2em</span> <span class="c">/* 字体使用em */</span>
<span class="p">}</span>
</code></pre></div></div>
<p>js代码如下</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">documentElement</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">documentElement</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">callback</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">clientWidth</span> <span class="o">=</span> <span class="nx">documentElement</span><span class="p">.</span><span class="nx">clientWidth</span><span class="p">;</span>
<span class="c1">// 屏幕宽度大于780,不在放大</span>
<span class="nx">clientWidth</span> <span class="o">=</span> <span class="nx">clientWidth</span> <span class="o"><</span> <span class="mi">780</span> <span class="p">?</span> <span class="nx">clientWidth</span> <span class="p">:</span> <span class="mi">780</span><span class="p">;</span>
<span class="nx">documentElement</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">fontSize</span> <span class="o">=</span> <span class="nx">clientWidth</span> <span class="o">/</span> <span class="mi">10</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">px</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">DOMContentLoaded</span><span class="dl">'</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">orientationchange</span><span class="dl">'</span> <span class="k">in</span> <span class="nb">window</span> <span class="p">?</span> <span class="dl">'</span><span class="s1">orientationchange</span><span class="dl">'</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">resize</span><span class="dl">'</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span>
</code></pre></div></div>
<p>完整的例子如下</p>
<ul>
<li><a href="http://yanhaijing.com/rem/rem-and-js.html">rem+js的例子</a></li>
<li><a href="http://yanhaijing.com/rem/rem-and-vw.html">rem+vw的例子</a></li>
<li><a href="http://yanhaijing.com/rem/vw.html">vw的例子</a></li>
</ul>
<p>页面效果如下</p>
<p><img src="/blog/520.png" alt="" /></p>
<h2 id="总结">总结</h2>
<p>如果对本文有什么疑问,欢迎留言讨论;如果觉得本文对你有帮助,那就赶紧赞赏吧,^_^</p>
<p>最后填一下开头埋的雷吧,demo在<a href="http://yanhaijing.com/rem/demo.html">这里</a>,代码如下</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"p1"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"s1"</span><span class="nt">></span>1<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"s2"</span><span class="nt">></span>1<span class="nt"></div></span>
<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"p2"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"s5"</span><span class="nt">></span>1<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"s6"</span><span class="nt">></span>1<span class="nt"></div></span>
<span class="nt"></div></span>
</code></pre></div></div>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.p1</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">16px</span><span class="p">;</span> <span class="nl">line-height</span><span class="p">:</span> <span class="m">32px</span><span class="p">;}</span>
<span class="nc">.s1</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">2em</span><span class="p">;}</span>
<span class="nc">.s2</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">2em</span><span class="p">;</span> <span class="nl">line-height</span><span class="p">:</span> <span class="m">2em</span><span class="p">;}</span>
<span class="nc">.p2</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">16px</span><span class="p">;</span> <span class="nl">line-height</span><span class="p">:</span> <span class="m">2</span><span class="p">;}</span>
<span class="nc">.s5</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">2em</span><span class="p">;}</span>
<span class="nc">.s6</span> <span class="p">{</span><span class="nl">font-size</span><span class="p">:</span> <span class="m">2em</span><span class="p">;</span> <span class="nl">line-height</span><span class="p">:</span> <span class="m">2em</span><span class="p">;}</span>
</code></pre></div></div>
<p>先来看第一组的答案</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>p1:font-size: 16px; line-height: 32px
s1:font-size: 32px; line-height: 32px
s2:font-size: 32px; line-height: 64px
</code></pre></div></div>
<p>和你的答案一样吗?下面来解释下</p>
<ul>
<li>p1 无需解释</li>
<li>s1 em作为字体单位,相对于父元素字体大小;line-height继承父元素计算值</li>
<li>s2 em作为行高单位时,相对于自身字体大小</li>
</ul>
<p>再来看看第二组的答案</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>p2:font-size: 16px; line-height: 32px
s5:font-size: 32px; line-height: 64px
s6:font-size: 32px; line-height: 64px
</code></pre></div></div>
<p>意不意外?惊不惊喜?下面来解释下</p>
<ul>
<li>p2 <code class="language-plaintext highlighter-rouge">line-height: 2</code>自身字体大小的两倍</li>
<li>s5 数字无单位行高,继承原始值,s5的line-height继承的2,自身字体大小的两倍</li>
<li>s6 无需解释</li>
</ul>
<h2 id="相关资料">相关资料</h2>
<ul>
<li><a href="https://zhuanlan.zhihu.com/p/23968868">使用CSS3 REM 和 VW 打造等比例响应式页面的便捷工作流</a></li>
<li><a href="http://www.cnblogs.com/lyzg/p/4877277.html">从网易与淘宝的font-size思考前端设计稿与工作流</a></li>
<li><a href="https://www.nihaoshijie.com.cn/index.php/archives/593/">移动web适配之rem</a></li>
<li><a href="http://harttle.com/2016/12/20/rem-and-accessibility.html">使用 rem 提供一致的字体大小</a></li>
<li><a href="http://www.w3cplus.com/css/vw-for-layout.html">再聊移动端页面的适配</a></li>
<li><a href="http://www.w3cplus.com/mobile/lib-flexible-for-html5-layout.html">使用Flexible实现手淘H5页面的终端适配</a></li>
</ul>
正则表达式教程——实践篇
2017-08-26T00:00:00+00:00
http://yanhaijing.com/javascript/2017/08/26/regexp-practice
<p><a href="http://yanhaijing.com/javascript/2017/08/06/regexp-syntax/">上一篇文章</a>介绍了正则的语法,本文来介绍下在js中如何使用正则</p>
<p>在js中创建正则有两种办法,字面量和new,和创建其他类型变量一样</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var reg = /abc/g // 字面量
var reg = new RegExp('abc', 'g') // new方式,意思和上面一样
</code></pre></div></div>
<p>js中用到正则的地方有两个入口,正则的api和字符串的api,<code class="language-plaintext highlighter-rouge">RegExp#test</code>等于<code class="language-plaintext highlighter-rouge">RegExp.prototype.test</code></p>
<ul>
<li>RegExp#test</li>
<li>RegExp#exec</li>
<li>String#search</li>
<li>String#match</li>
<li>String#split</li>
<li>String#replace</li>
</ul>
<h2 id="regexptest">RegExp#test</h2>
<p>每个正则实例都有test方法,test的参数是字符串,返回值是布尔值,表示当前正则是否能匹配指定的字符串</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/abc/.test('abc') // true
/abc/.test('abd') // false
</code></pre></div></div>
<h2 id="regexpexec">RegExp#exec</h2>
<p>exec使用方法和test一样,只是返回值并不是布尔值,而是返回匹配的结果</p>
<p>匹配成功返回一个数组,数组第一项是匹配结果,后面一次是捕获的分组</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/abc(d)/.exec('abcd') // ["abcd", "d", index: 0, input: "abcd"]
</code></pre></div></div>
<p>此数组还有另外两个参数,input是输入的字符串,index表示匹配成功的序列在输入字符串中的索引位置</p>
<p>如果有全局参数(g),第二次匹配时将从上次匹配结束时继续</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var r1 = /ab/
r1.exec('ababab') // ['ab', index: 0]
r1.exec('ababab') // ['ab', index: 0]
var r2 = /ab/g
r2.exec('ababab') // ['ab', index: 0]
r2.exec('ababab') // ['ab', index: 2]
r2.exec('ababab') // ['ab', index: 4]
</code></pre></div></div>
<p>这一特性可以被用于循环匹配,比如统计字符串中abc的次数</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var reg = /abc/g
var str = 'abcabcabcabcabc'
var num = 0;
var match = null;
while((match = reg.exec(str)) !== null) {
num++
}
console.log(num) // 5
</code></pre></div></div>
<p>如果匹配失败则返回null</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/abc(d)/.exec('abc') // null
</code></pre></div></div>
<h2 id="stringsearch">String#search</h2>
<p>search方法返回匹配成功位置的索引,参数是字符串或正则,结果是索引</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'abc'.search(/abc/) // 0
'abc'.search(/c/) // 2
</code></pre></div></div>
<p>如果匹配失败则返回-1</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'abc'.search(/d/) // -1
'abc'.search(/d/) !== -1 // false 转换为布尔值
</code></pre></div></div>
<h2 id="stringmatch">String#match</h2>
<p>match方法也会返回匹配的结果,匹配结果和exec类似</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'abc'.match(/abc/) // ['abc', index: 0, input: abc]
'abc'.match(/abd/) // null
</code></pre></div></div>
<p>如果有全局参数(g),match会返回所有的结果,并且没有index和input属性</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'abcabcabc'.match(/abc/g) // ['abc', 'abc', 'abc']
</code></pre></div></div>
<h2 id="stringsplit">String#split</h2>
<p>字符串的split方法,可以用指定符号分隔字符串,并返回数据</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'a,b,c'.split(',') // [a, b, c]
</code></pre></div></div>
<p>其参数也可以使一个正则,如果分隔符有多个时,就必须使用正则</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'a,b.c'.split(/,|\./) // [a, b, c]
</code></pre></div></div>
<h2 id="stringreplace">String#replace</h2>
<p>字符串的replace方法,可以将字符串的匹配字符,替换成另外的指定字符</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'abc'.replace('a', 'b') // 'bbc'
</code></pre></div></div>
<p>其第一个参数可以是正则表达式,如果想全局替换需添加全局参数</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'abc'.replace(/[abc]/, 'y') // ybc
'abc'.replace(/[abc]/g, 'y') // yyy 全局替换
</code></pre></div></div>
<p>在第二个参数中,也可以引用前面匹配的结果</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'abc'.replace(/a/, '$&b') // abbc $& 引用前面的匹配字符
'abc'.replace(/(a)b/, '$1a') // aac &n 引用前面匹配字符的分组
'abc'.replace(/b/, '$\'') // aac $` 引用匹配字符前面的字符
'abc'.replace(/b/, "$'") // acc $' 引用匹配字符后面的字符
</code></pre></div></div>
<p>replace的第二个参数也可以是函数,其第一个参数是匹配内容,后面的参数是匹配的分组</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'abc'.replace(/\w/g, function (match, $1, $2) {
return match + '-'
})
// a-b-c-
</code></pre></div></div>
<h2 id="regexp">RegExp</h2>
<p>RegExp是一个全局函数,可以用来创建动态正则,其自身也有一些属性</p>
<ul>
<li>$_</li>
<li>$n</li>
<li>input</li>
<li>length</li>
<li>lastMatch</li>
</ul>
<p>来个例子</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/a(b)/.exec('abc') // ["ab", "b", index: 0, input: "abc"]
RegExp.$_ // abc 上一次匹配的字符串
RegExp.$1 // b 上一次匹配的捕获分组
RegExp.input // abc 上一次匹配的字符串
RegExp.lastMatch // ab 上一次匹配成功的字符
RegExp.length // 2 上一次匹配的数组长度
</code></pre></div></div>
<h2 id="实例属性">实例属性</h2>
<p>正则表达式的实例上也有一些属性</p>
<ul>
<li>flags</li>
<li>ignoreCase</li>
<li>global</li>
<li>multiline</li>
<li>source</li>
<li>lastIndex</li>
</ul>
<p>还是看例子</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var r = /abc/igm;
r.flags // igm
r.ignoreCase // true
r.global // true
r.multiline // true
r.source // abc
</code></pre></div></div>
<p>lastIndex比较有意思,表示上次匹配成功的是的索引</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var r = /abc/igm;
r.exec('abcabcabc')
r.lastIndex // 3
r.exec('abcabcabc')
r.lastIndex // 6
</code></pre></div></div>
<p>可以更改lastIndex让其重新开始</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var r = /abc/igm;
r.exec('abcabcabc') // ["abc", index: 0]
r.exec('abcabcabc') // ["abc", index: 3]
r.lastIndex = 0
r.exec('abcabcabc') // ["abc", index: 0]
</code></pre></div></div>
<h2 id="实战实例">实战实例</h2>
<p>来几个常用的例子</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/(?:0\d{2,3}-)?\d{7}/ // 电话号 010-xxx xxx
/^1[378]\d{9}$/ // 手机号 13xxx 17xxx 18xxx
/^[0-9a-zA-Z_]+@[0-9a-zA-Z]+\.[z-z]+$/ // 邮箱
</code></pre></div></div>
<p>去除字符串前后空白</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>str = str.replace(/^\s*|\s*$/g, '')
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>到这里你已经学会了正则的语法,并且学会了在js中使用正则的方法,接下来快去实战吧,要想学会正则必须多加练习,正所谓拳不离手曲不离口吗</p>
<p>在<a href="http://yanhaijing.com/javascript/2017/11/29/regexp-principle/">下一篇文章</a>中,准备讲一讲正则的实现原理,难度略大o(╯□╰)o</p>
<p>本文灵感来源于《<a href="https://www.amazon.cn/gp/product/B016DWSEWO/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B016DWSEWO&linkCode=as2&tag=yanhaijing-23">JavaScript忍者秘籍</a>》这本书,如果你想成为高手的话,我也非常推荐看下这本书</p>
正则表达式教程——语法篇
2017-08-06T00:00:00+00:00
http://yanhaijing.com/javascript/2017/08/06/regexp-syntax
<p>正则表达式,名字听上去就没有吸引力,我发现很多前端对正则表达式都很难做到如数家珍,每次能够运行全凭运气,更有甚者完全靠复制粘贴,其实这样并不好</p>
<p>正则表达式其实并不难,语法就那么多,而且一旦掌握在某些时候能够给解决问题提供捷径,更重要的是面试可能会被问到,要是不会那就尴尬了</p>
<p>本文主要介绍正则表达式的语法部分,下面将正则表达式简称为正则</p>
<h2 id="正则是啥">正则是啥?</h2>
<p>同学你可以出门右转了,下面是我自己的理解</p>
<blockquote>
<p>正则就是用有限的符号,表达无限的序列,殆已!</p>
</blockquote>
<p>正则表达式的语法一般如下(js),两条斜线中间是正则主体,这部分可以有很多字符组成;<code class="language-plaintext highlighter-rouge">i</code>部分是修饰符,<code class="language-plaintext highlighter-rouge">i</code>的意思表示忽略大小写</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/^abc/i
</code></pre></div></div>
<p>正则定义了很多特殊意义的字符,有名词,量词,谓词等,下面逐一介绍</p>
<h2 id="简单字符">简单字符</h2>
<p>没有特殊意义的字符都是简单字符,简单字符就代表自身,绝大部分字符都是简单字符,举个例子</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/abc/ // 匹配 abc
/123/ // 匹配 123
/-_-/ // 匹配 -_-
/海镜/ // 匹配 海镜
</code></pre></div></div>
<h2 id="转义字符">转义字符</h2>
<p><code class="language-plaintext highlighter-rouge">\</code>是转义字符,其后面的字符会代表不同的意思,转义字符主要有三个作用:</p>
<p>第一种,是为了匹配不方便显示的特殊字符,比如换行,tab符号等</p>
<p>第二种,正则中预先定义了一些代表特殊意义的字符,比如<code class="language-plaintext highlighter-rouge">\w</code>等</p>
<p>第三种,在正则中某些字符有特殊含义(比如下面说到的),转义字符可以让其显示自身的含义</p>
<p>下面是常用转义字符列表:</p>
<table class="table table-bordered">
<tr><td>\n</td><td>匹配换行符</td></tr>
<tr><td>\r</td><td>匹配回车符</td></tr>
<tr><td>\t</td><td>匹配制表符,也就是tab键</td></tr>
<tr><td>\v</td><td>匹配垂直制表符</td></tr>
<tr><td>\x20</td><td>20是2位16进制数字,代表对应的字符</td></tr>
<tr><td>\u002B</td><td>002B是4位16进制数字,代表对应的字符</td></tr>
<tr><td>\u002B</td><td>002B是4位16进制数字,代表对应的字符</td></tr>
<tr><td>\w</td><td>匹配任何一个字母或者数字或者下划线</td></tr>
<tr><td>\W</td><td>匹配任何一个字母或者数字或者下划线以外的字符</td></tr>
<tr><td>\s</td><td>匹配空白字符,如空格,tab等</td></tr>
<tr><td>\S</td><td>匹配非空白字符</td></tr>
<tr><td>\d</td><td>匹配数字字符,0~9</td></tr>
<tr><td>\D</td><td>匹配非数字字符</td></tr>
<tr><td>\b</td><td>匹配单词的边界</td></tr>
<tr><td>\B</td><td>匹配非单词边界</td></tr>
<tr><td>\\</td><td>匹配\本身</td></tr>
</table>
<h2 id="字符集和">字符集和</h2>
<p>有时我们需要匹配一类字符,字符集可以实现这个功能,字符集的语法用<code class="language-plaintext highlighter-rouge">[</code><code class="language-plaintext highlighter-rouge">]</code>分隔,下面的代码能够匹配a或b或c</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[abc]
</code></pre></div></div>
<p>如果要表示字符很多,可以使用<code class="language-plaintext highlighter-rouge">-</code>表示一个范围内的字符,下面两个功能相同</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[0123456789]
[0-9]
</code></pre></div></div>
<p>在前面添加<code class="language-plaintext highlighter-rouge">^</code>,可表示非的意思,下面的代码能够匹配<code class="language-plaintext highlighter-rouge">a</code><code class="language-plaintext highlighter-rouge">b</code><code class="language-plaintext highlighter-rouge">c</code>之外的任意字符</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[^abc]
</code></pre></div></div>
<p>其实正则还内置了一些字符集,在上面的转义字符有提到,下面给出内置字符集对应的自定义字符集</p>
<ul>
<li>. 匹配除了换行符(\n)以外的任意一个字符 = [^\n]</li>
<li>\w = [0-9a-zA-Z_]</li>
<li>\W = [^0-9a-zA-Z_]</li>
<li>\s = [ \t\n\v]</li>
<li>\S = [^ \t\n\v]</li>
<li>\d = [0-9]</li>
<li>\D = [^0-9]</li>
</ul>
<h2 id="量词">量词</h2>
<p>如果我们有三个苹果,我们可以说自己有个3个苹果,也可以说有一个苹果,一个苹果,一个苹果,每种语言都有量词的概念</p>
<p>如果需要匹配多次某个字符,正则也提供了量词的功能,正则中的量词有多个,如<code class="language-plaintext highlighter-rouge">?</code>、<code class="language-plaintext highlighter-rouge">+</code>、<code class="language-plaintext highlighter-rouge">*</code>、<code class="language-plaintext highlighter-rouge">{n}</code>、<code class="language-plaintext highlighter-rouge">{m,n}</code>、<code class="language-plaintext highlighter-rouge">{m,}</code></p>
<p><code class="language-plaintext highlighter-rouge">{n}</code>匹配n次,比如<code class="language-plaintext highlighter-rouge">a{2}</code>,匹配<code class="language-plaintext highlighter-rouge">aa</code></p>
<p><code class="language-plaintext highlighter-rouge">{m, n}</code>匹配m-n次,优先匹配n次,比如<code class="language-plaintext highlighter-rouge">a{1,3}</code>,可以匹配<code class="language-plaintext highlighter-rouge">aaa</code>、<code class="language-plaintext highlighter-rouge">aa</code>、<code class="language-plaintext highlighter-rouge">a</code></p>
<p><code class="language-plaintext highlighter-rouge">{m,}</code>匹配m-∞次,优先匹配∞次,比如<code class="language-plaintext highlighter-rouge">a{1,}</code>,可以匹配<code class="language-plaintext highlighter-rouge">aaaa...</code></p>
<p><code class="language-plaintext highlighter-rouge">?</code>匹配0次或1次,优先匹配1次,相当于<code class="language-plaintext highlighter-rouge">{0,1}</code></p>
<p><code class="language-plaintext highlighter-rouge">+</code>匹配1-n次,优先匹配n次,相当于<code class="language-plaintext highlighter-rouge">{1,}</code></p>
<p><code class="language-plaintext highlighter-rouge">*</code>匹配0-n次,优先匹配n次,相当于<code class="language-plaintext highlighter-rouge">{0,}</code></p>
<p>正则默认和人心一样是贪婪的,也就是常说的贪婪模式,凡是表示范围的量词,都优先匹配上限而不是下限</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a{1, 3} // 匹配字符串'aaa'的话,会匹配aaa而不是a
</code></pre></div></div>
<p>有时候这不是我们想要的结果,可以在量词后面加上<code class="language-plaintext highlighter-rouge">?</code>,就可以开启非贪婪模式</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a{1, 3}? // 匹配字符串'aaa'的话,会匹配a而不是aaa
</code></pre></div></div>
<h2 id="字符边界">字符边界</h2>
<p>有时我们会有边界的匹配要求,比如以xxx开头,以xxx结尾</p>
<p><code class="language-plaintext highlighter-rouge">^</code>在<code class="language-plaintext highlighter-rouge">[]</code>外表示匹配开头的意思</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>^abc // 可以匹配abc,但是不能匹配aabc
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">$</code>表示匹配结尾的意思</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>abc$ // 可以匹配abc,但是不能匹配abcc
</code></pre></div></div>
<p>上面提到的<code class="language-plaintext highlighter-rouge">\b</code>表示单词的边界</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>abc\b // 可以匹配 abc ,但是不能匹配 abcc
</code></pre></div></div>
<h2 id="选择表达式">选择表达式</h2>
<p>有时我们想匹配x或者y,如果x和y是单个字符,可以使用字符集,<code class="language-plaintext highlighter-rouge">[abc]</code>可以匹配<code class="language-plaintext highlighter-rouge">a</code>或<code class="language-plaintext highlighter-rouge">b</code>或<code class="language-plaintext highlighter-rouge">c</code>,如果x和y是多个字符,字符集就无能为力了,此时就要用到分组</p>
<p>正则中用<code class="language-plaintext highlighter-rouge">|</code>来表示分组,<code class="language-plaintext highlighter-rouge">a|b</code>表示匹配<code class="language-plaintext highlighter-rouge">a</code>或者<code class="language-plaintext highlighter-rouge">b</code>的意思</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>123|456|789 // 匹配 123 或 456 或 789
</code></pre></div></div>
<h2 id="分组与引用">分组与引用</h2>
<p>分组是正则中非常强大的一个功能,可以让上面提到的量词作用于一组字符,而非单个字符,分组的语法是圆括号包裹<code class="language-plaintext highlighter-rouge">(xxx)</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(abc){2} // 匹配abcabc
</code></pre></div></div>
<p>分组不能放在<code class="language-plaintext highlighter-rouge">[]</code>中,分组中还可以使用选择表达式</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(123|456){2} // 匹配 123123、456456、123456、456123
</code></pre></div></div>
<p>和分组相关的概念还有一个捕获分组和非捕获分组,分组默认都是捕获的,在分组的<code class="language-plaintext highlighter-rouge">(</code>后面添加<code class="language-plaintext highlighter-rouge">?:</code>可以让分组变为非捕获分组,非捕获分组可以提高性能和简化逻辑</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'123'.match(/(?:123)/) // 返回 ['123']
'123'.match(/(123)/) // 返回 ['123', '123']
</code></pre></div></div>
<p>和分组相关的另一个概念是引用,比如在匹配html标签时,通常希望<code class="language-plaintext highlighter-rouge"><xxx></xxx></code>后面的<code class="language-plaintext highlighter-rouge">xxx</code>能够和前面保持一致</p>
<p>引用的语法是<code class="language-plaintext highlighter-rouge">\数字</code>,数字代表引用前面第几个捕获分组,注意非捕获分组不能被引用</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><([a-z]+)><\/\1> // 可以匹配 `<span></span>` 或 `<div></div>`等
</code></pre></div></div>
<h2 id="预搜索">预搜索</h2>
<p>如果你想匹配xxx前不能是yyy,或者xxx后不能是yyy,那就要用到预搜索</p>
<p>js只支持正向预搜索,也就是xxx后面必须是yyy,或者xxx后面不能是yyy</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1(?=2) // 可以匹配12,不能匹配22
1(?!2) // 可有匹配22,不能匹配12
</code></pre></div></div>
<h2 id="修饰符">修饰符</h2>
<p>默认正则是区分大小写,这可能并不是我们想要的,正则提供了修饰符的功能,修复的语法如下</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/xxx/gi // 最后面的g和i就是两个修饰符
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">g</code>正则遇到第一个匹配的字符就会结束,加上全局修复符,可以让其匹配到结束</p>
<p><code class="language-plaintext highlighter-rouge">i</code>正则默认是区分大小写的,<code class="language-plaintext highlighter-rouge">i</code>可以忽略大小写</p>
<p><code class="language-plaintext highlighter-rouge">m</code>正则默认情况下,^和$只能匹配字符串的开始和结尾,m修饰符可以让^和$匹配行首和行尾,不理解就看例子</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/jing$/ // 能够匹配 'yanhaijing,不能匹配 'yanhaijing\n'
/jing$/m // 能够匹配 'yanhaijing, 能够匹配 'yanhaijing\n'
/^jing/ // 能够匹配 'jing',不能匹配 '\njing'
/^jing/m // 能够匹配 'jing',能够匹配 '\njing'
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>刻意练习,方能游刃有余,知己知彼,方能百战百胜,正则是前端的一个武器,技多不压身</p>
<p>有时我们会遇到特别复杂的正则,有时候可能不太直观,下面推荐一个图形化展示的<a href="https://jex.im/regulex">工具</a>,我们把涉及到的语法罗列一下</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/^[a-z]*[^\d]{1,10}?(aaa|bbb)(?:ccc)$/
</code></pre></div></div>
<p>可以看到工具能够更快的帮我们理清头绪</p>
<p><img src="/blog/505.png" alt="" /></p>
<p>本文仅讲述了语法,<a href="http://yanhaijing.com/javascript/2017/08/26/regexp-practice/">下一篇文章</a>将详细介绍如何在js中使用正则,推荐一下<a href="https://github.com/isaacs/node-glob">glob</a>,号称给人看的正则,比正则的语法简单多了,也是平时开发的神器</p>
异步编程那些事
2017-08-02T00:00:00+00:00
http://yanhaijing.com/javascript/2017/08/02/talk-async
<p>世界在进步,宣扬了这么多年的前端金科玉律被推翻,这是要变天啊,前端飞速发展,而我们只有保持不断学习,才能不那么迷茫</p>
<p>本文通过一个实例,讲解了异步编程的发展历史和 6 种异步编程方法,实践出真知</p>
<h2 id="前言">前言</h2>
<p>就在不久前(15 年以前),说起异步还只有 callback,后来 Promise 来的时候我是拒绝的,generator 来的时候我是决绝的,当 async 来的时候我想我是不是落后了,嗯我该学习了</p>
<p>作为一个切图人员,遇到的异步场景实在太少,思来想去也就只有动画了,本文将用不同方式实现下面的动画,红色方块每次向右移动 100 像素,完整的 demo 在<a href="https://github.com/yanhaijing/async-demo">这里</a></p>
<p><img src="/blog/503.gif" alt="" /></p>
<h2 id="关于异步">关于异步</h2>
<p>异步是一个关于现在和将来的问题,现在执行的代码和将来执行的代码,举个例子来说一下异步是如何复杂和反人类</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">b</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">foo</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">a</span> <span class="o">=</span> <span class="nx">b</span> <span class="o">+</span> <span class="mi">3</span><span class="p">;</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">bar</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">b</span> <span class="o">=</span> <span class="nx">a</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">ajax</span><span class="p">(</span><span class="dl">"</span><span class="s2">url1</span><span class="dl">"</span><span class="p">,</span> <span class="nx">foo</span><span class="p">);</span>
<span class="nx">ajax</span><span class="p">(</span><span class="dl">"</span><span class="s2">url2</span><span class="dl">"</span><span class="p">,</span> <span class="nx">bar</span><span class="p">);</span>
</code></pre></div></div>
<p>上面的代码如果是同步的,那么打印 a 的地方就是确定的;但要是异步的,a 的值就有两种可能(哪个请求先回来),这还是只是最简单的 case,你可以思考下,自己写程序时有咩有考虑过这种问题</p>
<h2 id="异步编程方法">异步编程方法</h2>
<p>js 中的异步编程共有下面这些方法,好吧以前我就知道前两个,其中事件监听和观察者有些类似,下面我们将分别介绍</p>
<ul>
<li>回调函数</li>
<li>事件监听</li>
<li>观察者</li>
<li>Promise</li>
<li>Generator</li>
<li>async/await</li>
</ul>
<p>先来一张形象的图</p>
<p><img src="/blog/504.jpg" alt="" /></p>
<h2 id="回调函数">回调函数</h2>
<p>callback 是我们最熟悉的方式,上面提到的动画的例子,用 callback 实现代码如下</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">moveTo</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">moveTo</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">moveTo</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">moveTo</span><span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="c1">// 无限不循环</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">});</span>
</code></pre></div></div>
<p>快看,这就是传说中的回调地狱吗?是,也不是。。。</p>
<p>其实我在工作中并没遇到过这么多异步,可能我写的业务比较简单吧,O(∩_∩)O 哈哈~</p>
<p>有同学可能会问 moveTo 函数是如何实现的?答案如下</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">moveTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">cb</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{})</span> <span class="p">{</span>
<span class="nx">move</span><span class="p">(</span><span class="dl">"</span><span class="s2">.box</span><span class="dl">"</span><span class="p">).</span><span class="nx">x</span><span class="p">(</span><span class="nx">x</span><span class="p">).</span><span class="nx">y</span><span class="p">(</span><span class="nx">y</span><span class="p">).</span><span class="nx">end</span><span class="p">(</span><span class="nx">cb</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>下面来列举回调的 N 大罪状</p>
<ul>
<li>违反直觉</li>
<li>错误追踪</li>
<li>模拟同步</li>
<li>回调地狱</li>
<li>并发执行</li>
<li>信任问题(多次调用)</li>
</ul>
<p>违反直觉,并不是说缩进,缩进其实可以通过拆分函数来解决,而是对人友好的顺序执行,现在要跳来跳去</p>
<p>错误追踪,异步让<code class="language-plaintext highlighter-rouge">try catch</code>直接跪了,为了能够捕获到异步的错误,有两种方案,分离回调和 first error</p>
<p>jquery 的 ajax 就是典型的分离回调</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">success</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">error</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">$</span><span class="p">.</span><span class="nx">ajax</span><span class="p">({},</span> <span class="nx">success</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
</code></pre></div></div>
<p>Node 采用的是 first error,所有系统异步接口第一个参数都是 error 对象</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">callback</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 出错</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 成功</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">async</span><span class="p">(</span><span class="dl">"</span><span class="s2">url</span><span class="dl">"</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span>
</code></pre></div></div>
<p>模拟同步,node 的 io 相关的 api 都是异步的,我只是想要 python 那种同步的个脚本,险些精神分裂,o(╯□╰)o</p>
<p>一段很容易理解的同步代码</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="o">++</span><span class="nx">i</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="nx">sync</span><span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>然而如果是异步的话要这么写,/(ㄒ o ㄒ)/~~</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="kd">function</span> <span class="nx">next</span><span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="nx">len</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">i</span> <span class="o"><</span> <span class="nx">len</span><span class="p">)</span> <span class="p">{</span>
<span class="k">async</span><span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">],</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
<span class="nx">next</span><span class="p">(</span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">len</span><span class="p">,</span> <span class="nx">callback</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">callback</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">})(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="c1">// All array items have processed.</span>
<span class="p">});</span>
</code></pre></div></div>
<p>关于回调地狱,并发执行和信任问题,我建议你阅读文章结尾处推荐的资料,因为我实在才疏学浅,o(╯□╰)o</p>
<h2 id="观察者">观察者</h2>
<p>观察者模式需要一个 pub 和 sub 函数,或者其他类似工具,一定有同学说,这不还是回调吗?请看清楚,回调是因为观察者模式,而不是异步。其实回调的问题,观察者模式并没有解决</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">sub</span><span class="p">(</span><span class="dl">"</span><span class="s2">1</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">moveTo</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">pub</span><span class="p">(</span><span class="dl">"</span><span class="s2">2</span><span class="dl">"</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="nx">sub</span><span class="p">(</span><span class="dl">"</span><span class="s2">2</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">moveTo</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">pub</span><span class="p">(</span><span class="dl">"</span><span class="s2">3</span><span class="dl">"</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="nx">sub</span><span class="p">(</span><span class="dl">"</span><span class="s2">3</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">moveTo</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">pub</span><span class="p">(</span><span class="dl">"</span><span class="s2">4</span><span class="dl">"</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="c1">// 无限不循环</span>
</code></pre></div></div>
<p>pub 和 sub 函数的是怎么实现的呢?最简单的实现如下</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">eventMap</span> <span class="o">=</span> <span class="p">{};</span>
<span class="kd">function</span> <span class="nx">pub</span><span class="p">(</span><span class="nx">msg</span><span class="p">,</span> <span class="p">...</span><span class="nx">rest</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">eventMap</span><span class="p">[</span><span class="nx">msg</span><span class="p">]</span> <span class="o">&&</span>
<span class="nx">eventMap</span><span class="p">[</span><span class="nx">msg</span><span class="p">].</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">cb</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">cb</span><span class="p">(...</span><span class="nx">rest</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">sub</span><span class="p">(</span><span class="nx">msg</span><span class="p">,</span> <span class="nx">cb</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">eventMap</span><span class="p">[</span><span class="nx">msg</span><span class="p">]</span> <span class="o">=</span> <span class="nx">eventMap</span><span class="p">[</span><span class="nx">msg</span><span class="p">]</span> <span class="o">||</span> <span class="p">[];</span>
<span class="nx">eventMap</span><span class="p">[</span><span class="nx">msg</span><span class="p">].</span><span class="nx">push</span><span class="p">(</span><span class="nx">cb</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="promise">Promise</h2>
<p>如果你还不了解 Promise,那么建议先阅读<a href="http://yanhaijing.com/javascript/2015/09/16/es6-promise/">这篇文章</a>,下面是 Promise 实现上面的例子</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">moveTo</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">moveTo</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">})</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">moveTo</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">})</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">moveTo</span><span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">})</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="c1">// 无限不循环</span>
<span class="p">});</span>
</code></pre></div></div>
<p>再来看一下,moveTo 函数有何不同,看出来没?</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">moveTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">move</span><span class="p">(</span><span class="dl">"</span><span class="s2">.box</span><span class="dl">"</span><span class="p">).</span><span class="nx">x</span><span class="p">(</span><span class="nx">x</span><span class="p">).</span><span class="nx">y</span><span class="p">(</span><span class="nx">y</span><span class="p">).</span><span class="nx">end</span><span class="p">(</span><span class="nx">resolve</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>
<p>其实 Promise 开始我是拒绝的,这不还是回调吗!!!请再次注意,这里的回调不是为了异步,而是 Promise 协议</p>
<p>Promise 其实是一种控制反转,举个例子,就是原来我们要给异步函数传入一个回调函数,现在变成了异步函数返回一个 Promise 对象,堪称神来之笔,而 Promise 就是实现这种反转的工具,Promise 是一个双方约定的契约(规范)</p>
<p>其实 Promise 还有很多优点,要知道 Promise 是后面新技术的基础,堪称一切异步方案的粘合剂,没有 Promise,可能就不会有 generator,那么为什么说是可能呢?请看 Generator 一节</p>
<p>Promise 解决了回调的一些问题,但并没有全部解决,比如 Promise 有很好的错误追踪,避免了回调地狱,对并发执行很友好,因为 Promise 只决议一次,就很好的解决了信任问题</p>
<p>但 Promise 对违反直觉并不友好,回调变成了长长的 Promise 链</p>
<p>看一下模拟同步的代码,貌似成功解决了</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">chain</span> <span class="o">=</span> <span class="nb">Promise</span><span class="p">.</span><span class="nx">resolve</span><span class="p">();</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">arr</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="o">++</span><span class="nx">i</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">chain</span> <span class="o">=</span> <span class="nx">chain</span><span class="p">.</span><span class="nx">then</span><span class="p">(()</span> <span class="o">=></span> <span class="k">async</span><span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]).</span><span class="nx">then</span><span class="p">((</span><span class="nx">x</span><span class="p">)</span> <span class="o">=></span> <span class="p">(</span><span class="nx">arr</span><span class="p">[</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="nx">x</span><span class="p">)));</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="generator">Generator</h2>
<p>Generator 是一个革命性特性,es2015(es6)中引入,让原本必须一次执行完毕函数,现在可以在中间暂停,并在下次继续执行,这就让 js 可以模拟协程的概念</p>
<p>其实关于 Generator 我开始也是拒绝的,这东西本来不是为异步而生,是为了产生持续生成的数据,和迭代器是天生一对,
而把他用在异步上优点类似那 float 做布局(float 本来只是为了实现文字环绕的效果),有点绕,不是很好理解</p>
<p>动画的例子用 generator 实现如下,就好像是同步的一样</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span><span class="o">*</span> <span class="nx">gen</span><span class="p">()</span> <span class="p">{</span>
<span class="k">yield</span> <span class="nx">moveTo</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="k">yield</span> <span class="nx">moveTo</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="k">yield</span> <span class="nx">moveTo</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="k">yield</span> <span class="nx">moveTo</span><span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="c1">// 无限不循环</span>
<span class="p">}</span>
<span class="nx">run</span><span class="p">(</span><span class="nx">gen</span><span class="p">);</span>
</code></pre></div></div>
<p>moveTo 函数如下</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">moveTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">move</span><span class="p">(</span><span class="dl">"</span><span class="s2">.box</span><span class="dl">"</span><span class="p">).</span><span class="nx">x</span><span class="p">(</span><span class="nx">x</span><span class="p">).</span><span class="nx">y</span><span class="p">(</span><span class="nx">y</span><span class="p">).</span><span class="nx">end</span><span class="p">(</span><span class="nx">resolve</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>
<p>看吧,多么简洁,简直完美,但我发誓当你看到 run 函数后你就疯了</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">run</span><span class="p">(</span><span class="nx">fn</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">gen</span> <span class="o">=</span> <span class="nx">fn</span><span class="p">();</span>
<span class="kd">function</span> <span class="nx">next</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">gen</span><span class="p">.</span><span class="nx">next</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">done</span><span class="p">)</span> <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">res</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">next</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nx">next</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>生成器要实现异步,必须得有启动函数,也就是 run,好在社区有封装好的 run 函数,比如 co 这个库</p>
<p>生成器能很好的解决 Promise 不能解决的问题,如违反直觉,繁琐的代码,但也有其自身的问题,比如不太好理解,蹩脚,需要启动器</p>
<p>Promise 那一节,我说如果没有 Promise 就可能没有 generator,而不是必须呢?因为 generator 实现异步除了基于 Promise,还可以基于 thunk 函数,感兴趣的你自行查阅吧,祝好</p>
<h2 id="asyncawait">async/await</h2>
<p>那么有没有一种完美方案呢?还真有啊,es2017(es8)给我们带来了异步函数,这家伙简直就是自带启动器的生成器函数,好吧他就是给予 generator 的</p>
<p>动画的例子用异步函数来实现</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="kd">function</span> <span class="nx">run</span><span class="p">()</span> <span class="p">{</span>
<span class="k">await</span> <span class="nx">moveTo</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="k">await</span> <span class="nx">moveTo</span><span class="p">(</span><span class="mi">200</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="k">await</span> <span class="nx">moveTo</span><span class="p">(</span><span class="mi">300</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="k">await</span> <span class="nx">moveTo</span><span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
<span class="c1">// 无限不循环</span>
<span class="p">}</span>
<span class="nx">run</span><span class="p">();</span>
</code></pre></div></div>
<p>moveTo 函数如下</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="kd">function</span> <span class="nx">moveTo</span><span class="p">(</span><span class="nx">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="k">await</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">move</span><span class="p">(</span><span class="dl">"</span><span class="s2">.box</span><span class="dl">"</span><span class="p">).</span><span class="nx">x</span><span class="p">(</span><span class="nx">x</span><span class="p">).</span><span class="nx">y</span><span class="p">(</span><span class="nx">y</span><span class="p">).</span><span class="nx">end</span><span class="p">(</span><span class="nx">resolve</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>
<p>完美同步般的代码,自带启动器,世界终于重归美好,终极异步方案,还学什么 generator 啊</p>
<h2 id="总结">总结</h2>
<p>上面只是介绍了 js 中异步编程的全部方法,如果你想深入了解异步编程,那么我推荐一本书《<a href="https://www.amazon.cn/gp/product/B01LMYXGAI/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B01LMYXGAI&linkCode=as2&tag=yanhaijing-23">你不知道的 JavaScript</a>》中卷,这本书能解答你全部的疑惑</p>
一个Mac小白的自我修养
2017-07-19T00:00:00+00:00
http://yanhaijing.com/mac/2017/07/19/my-mac-note
<p>作为一个前端,坚守了 9 年的 windows 平台,也是惭愧,但主要还是因为穷,最近终于换上了 mac,但却是各种不习惯,各种折腾,本文记录下自己遇到的问题和解决办法,希望能够帮助 mac 新同学们</p>
<h2 id="mac-编年史">Mac 编年史</h2>
<p>一直理不清 mac 的历史,下面简单总结下,最开始的苹果电脑叫做 Apple,比如 Apple I,Apple II,其操作系统叫做 System,大概从 1984-1997</p>
<ul>
<li>System1.0 - 5.0</li>
<li>System6.0 彩色</li>
<li>System7.0</li>
<li>System7.5</li>
<li>System7.6</li>
</ul>
<p>后来苹果推出新的 Mac 电脑,Mac OS 系统诞生了</p>
<ul>
<li>Mac OS 8</li>
<li>Mac OS 8.5</li>
<li>Mac OS 9</li>
</ul>
<p>苹果将 Mac OS10 改名为 OS X,并给每个版本命名一个大型猫科动物</p>
<table>
<thead>
<tr>
<th>系统</th>
<th>代号</th>
<th>时间</th>
</tr>
</thead>
<tbody>
<tr>
<td>Mac OS X 10.0</td>
<td>猎豹 Cheetah</td>
<td>2001.3.24</td>
</tr>
<tr>
<td>Mac OS X 10.1</td>
<td>美洲狮 Puma</td>
<td>2001.9.25</td>
</tr>
<tr>
<td>Mac OS X 10.2</td>
<td>美洲虎 Jaguar</td>
<td>2002.8.24</td>
</tr>
<tr>
<td>Mac OS X 10.3</td>
<td>黑豹 Panther</td>
<td>2003.10.24</td>
</tr>
<tr>
<td>Mac OS X 10.4</td>
<td>虎 Tiger</td>
<td>2005.4.29</td>
</tr>
<tr>
<td>Mac OS X 10.5</td>
<td>花豹 Leopard</td>
<td>2007.10.26</td>
</tr>
<tr>
<td>Mac OS X 10.6</td>
<td>雪豹 Snow Leopard</td>
<td>2008.6.9</td>
</tr>
<tr>
<td>Mac OS X 10.7</td>
<td>狮子 Lion</td>
<td>2011.6.7</td>
</tr>
</tbody>
</table>
<p>苹果为了整合 iphone 和 mac 将 Mac OS X 改为名 OS X,猫科动物也快用完了,10.8 以后就改用地名了。。。</p>
<table>
<thead>
<tr>
<th>系统</th>
<th>代号</th>
<th>时间</th>
</tr>
</thead>
<tbody>
<tr>
<td>OS X 10.8</td>
<td>山狮 Mountain Lion</td>
<td>2012.2.16</td>
</tr>
<tr>
<td>OS X 10.9</td>
<td>巨浪 Mavericks</td>
<td>2013.6.10</td>
</tr>
<tr>
<td>OS X 10.10</td>
<td>优胜美地 Yosemite</td>
<td>2014.6.3</td>
</tr>
<tr>
<td>OS X 10.11</td>
<td>酋长岩 El Capitan</td>
<td>2015</td>
</tr>
</tbody>
</table>
<p>看起来整合 iphone 和 mac 的计划失败了,苹果将 mac 的系统改名为 macOS</p>
<table>
<thead>
<tr>
<th>系统</th>
<th>代号</th>
<th>时间</th>
</tr>
</thead>
<tbody>
<tr>
<td>macOS 10.12</td>
<td>内华达山脉 Sierra</td>
<td>2016.6.14</td>
</tr>
<tr>
<td>macOS 10.13</td>
<td>内华达高脊山脉 High Sierra</td>
<td>2017.6.5</td>
</tr>
<tr>
<td>macOS 10.14</td>
<td>莫哈维沙漠 Mojave</td>
<td>2018.9.24</td>
</tr>
<tr>
<td>macOS 10.15</td>
<td>圣卡塔利娜岛 Catalina</td>
<td>2019.10.7</td>
</tr>
<tr>
<td>macOS 11.x</td>
<td>大瑟尔 Big Sur</td>
<td>2020.11.12</td>
</tr>
<tr>
<td>macOS 12.x</td>
<td>蒙特利湾 Monterey</td>
<td>2021.7.1</td>
</tr>
<tr>
<td>macOS 13.x</td>
<td>范朵拉 Ventura</td>
<td>2022.6.6</td>
</tr>
</tbody>
</table>
<h2 id="mac-基础">Mac 基础</h2>
<p>初次接触 mac 会很不习惯,开始菜单呢?桌面上怎么没有软件?怎么安装软件?别慌,试着忘掉 windows 中的概念,先来了解下 mac 中的功能</p>
<h3 id="dock">Dock</h3>
<p>Dock 是码头的意思,Dock 位于屏幕的底部,打开的 app 会在上面显示,类似 windows 底部的任务栏,可以把常用 app 设置为在 Dock 中常驻,这样非常方便</p>
<h3 id="launchpad">Launchpad</h3>
<p>windows 中会在桌面上放置软件的快捷方式,非常方便,mac 类似的功能就是 Launchpad,打开 Launchpad 会看到所有的安装的软件,顶部的搜索框可以用来搜索 app,非常方便</p>
<h3 id="spotlight">Spotlight</h3>
<p>Spotlight 是聚光灯的意思,可以快速找到电脑上的软件和文件,这是一个神器,大概相当于 windows 上的开始菜单搜索和文件全局搜索,通过 command+空格键打开,如果记得 app 的名字,通过这个打开 app 会比 Launchpad 快很多,Spotlight 开可以用来搜索文件,只要记得文件名字就行</p>
<p>除了上面提到的功能,还有很多功能,比如快速计算,换算单位。。。</p>
<h3 id="finder">Finder</h3>
<p>mac 下没有 windows 下的文件管理器,类似的功能是 Finder,但是功能比 windows 弱很多,Finder 的本意是访问并达到,而不是文件管理</p>
<p>打开 Finder,哎呦我去 C 盘,D 盘,E 盘哪去了?嗯 mac 下就一个磁盘,那以前 D 盘放软件,E 盘放学习资料,F 盘放娱乐资料的习惯怎么破?你可以通过目录来解决,系统默认帮你建好了一些目录,比如:</p>
<ul>
<li>图片</li>
<li>文稿</li>
<li>下载</li>
<li>。。。</li>
</ul>
<p>那重装系统是安装到 C 盘,不会覆盖其他盘文件怎么破?这个我还没研究明白 o(╯□╰)o 据说 mac 不用重装系统。。。</p>
<p>Finder 中选中文件回车是修改文件名,如果想预览文件,可以按空格键,如果想打开文件可以 command+o,这点和 windows 很不一样</p>
<p>Finder 默认的设置非常难用,需要进行一些自定义设置才能好用点,如下:</p>
<ul>
<li>新标签页打开文件,默认是新窗口</li>
<li>左侧显示内容太少,根本不够用</li>
<li>搜索时搜索当前目录,默认是全局搜索</li>
<li>显示状态栏,显示路径栏,显示预览,默认全都不显示</li>
</ul>
<h3 id="多桌面">多桌面</h3>
<p>mac 下的桌面存在感很弱,我基本用不到,除了设置好看的壁纸之外,o(╯□╰)o</p>
<p>但多桌面是一个非常好用的功能,windows10 也有,简单来说就是多个工作空间互不影响,我设置了两个桌面,一个工作,一个生活,这样工作和生活就能不互相干扰了</p>
<h3 id="mission-control">Mission Control</h3>
<p>一般切换程序是使用 command+tab,但如果一个程序双开的话,command+tab 就不灵了,还有些弹出窗口一不小心就不见了怎么破?Mission Control 可以让你找到所有的界面,只要四指向上滑动就可以了,就是这么简单</p>
<h3 id="软件界面">软件界面</h3>
<p>mac 中的软件关闭按钮在左边,不在右边。。。三个按钮分别是关闭,最小化和最大化,不过一般没什么用,关闭的快捷键是 command+w,最小化的快捷键是 command+h,双击三个按钮旁边的位置可以让软件自适应大小</p>
<p>mac 中的软件菜单栏也是分离的,这个比较个性。。。</p>
<h3 id="退出程序">退出程序</h3>
<p>mac 中左上角的关闭(command+w),其实并没有退出程序,感觉和最小化差不多,mac 中退出程序有两种方式:</p>
<ul>
<li>Dock 中在软件上右键退出</li>
<li>command+q</li>
</ul>
<p>有时候程序可能卡死,需要强制退出,强制退出也有两种方法:</p>
<ul>
<li>按住 option 键,Dock 中在软件上右键强制退出</li>
<li>command+option+esc,然后弹出的界面中退出,类似 windows 的任务管理器</li>
</ul>
<h2 id="软件系统">软件系统</h2>
<p>Mac 下的软件都安装在了 Applications 目录下,很多 xxx.app 结尾的就是一个一个软件,直接点击就可以打开,但其实这些都不是一个文件,而是一个文件夹,右键-> 显示包含内容,就能看到里面的内容,里面包括软件资源和可执行文件,xxx.app 可以理解为软件的安装目录</p>
<p>下面来说说安装软件,安装软件可以通过 AppStore 来进行安装,但有时候很多软件里面都没有,需要自己下载软件来安装,下载的时候可能下载到.app、.dmg、.pkg 结尾,下面介绍下区别</p>
<p>.app 的直接打开就行,首次打开会提示你拷贝到 Applications 目录去,就是这么简单</p>
<p>dmg 是苹果的压缩镜像文件(类似 Windows 下的 iso ),Mac 应用软件通用的打包格式,里面一般包含 <code class="language-plaintext highlighter-rouge">应用程序.app</code> 的图标和一个应用程序文件夹(<code class="language-plaintext highlighter-rouge">/Applications</code>)快捷方式,直接将 <code class="language-plaintext highlighter-rouge">应用程序.app</code> 拖曳至应用程序文件夹即可完成安装,相当于绿色软件,卸载就是直接删除就行</p>
<p>pkg 相当于 win 下面的大型安装包,pkg 安装一般要求 sudo 授权,会对系统进行修改,卸载的话会麻烦一点</p>
<h2 id="命令行">命令行</h2>
<p>命令行才是程序员的最爱,mac 下的命令行几乎和 linux 一样好用,比 windows 好用太多,下面介绍一些命令行的知识</p>
<h3 id="配置文件">配置文件</h3>
<p>如果想设置环境变量,修改 PATH,自定义别名都涉及到对 shell 进行配置,网上搜的话还是比较混乱的,有的说<code class="language-plaintext highlighter-rouge">.bash_profile</code>,有的说<code class="language-plaintext highlighter-rouge">.bashrc</code>,怎么我的 mac 没有.bashrc?下面给大家分享下自己的认识</p>
<p>需要注意我说的都是<code class="language-plaintext highlighter-rouge">~</code>目录下的配置文件,不涉及<code class="language-plaintext highlighter-rouge">/etc/</code>下的配置文件</p>
<p><code class="language-plaintext highlighter-rouge">.bash_profile</code>是为 bash 的配置文件,由于历史原因 shell 是有很多分支的,比如 bshell,kshell,zshell,通过下面的命令可以查看系统支持的全部 shell</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cat</span> /etc/shells
/bin/bash
/bin/csh
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh
</code></pre></div></div>
<p>如果当前使用 bash,mac 中每次打开命令终端,或者新开 tab 页都会加载<code class="language-plaintext highlighter-rouge">.bash_profile</code>文件,mac 下没有<code class="language-plaintext highlighter-rouge">.bashrc</code>文件,可以自己新建一个,但还需要在<code class="language-plaintext highlighter-rouge">.bash_profile</code>手动加载<code class="language-plaintext highlighter-rouge">.bashrc</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vi ~/.bash_profile
<span class="c"># 环境变量</span>
<span class="c"># PATH设置</span>
<span class="c"># 如果当前是bash,则手动加载.bashrc</span>
<span class="k">if</span> <span class="o">[</span> <span class="nt">-f</span> ~/.bashrc <span class="o">]</span> <span class="o">&&</span> <span class="o">[</span> <span class="nv">$SHELL</span> <span class="o">=</span> <span class="s1">'/bin/bash'</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">source</span> ~/.bashrc
<span class="k">fi</span>
</code></pre></div></div>
<p>一般在<code class="language-plaintext highlighter-rouge">.bash_profile</code>中设置 path,环境变量等;在<code class="language-plaintext highlighter-rouge">.bashrc</code>中设置 bash 自己私有的东西,比如 bash 下的别名</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vi ~/.bashrc
<span class="c"># bash shell私有设置</span>
<span class="nb">alias </span><span class="nv">ll</span><span class="o">=</span><span class="nb">ls</span> <span class="nt">-l</span>
</code></pre></div></div>
<p>一句话总结,<code class="language-plaintext highlighter-rouge">.bash_profile</code>中的内容会和其他 shell 共享,<code class="language-plaintext highlighter-rouge">.bashrc</code>中的内容仅仅 bash 会加载</p>
<h3 id="添加-path">添加 PATH</h3>
<p>程序员经常和环境变量打交道,下面来介绍下 mac 下如何设置环境变量,总的来说有两种方法</p>
<p>下载了一个可执行程序,想放到环境变量的最简单方法就是通过软链接连接到<code class="language-plaintext highlighter-rouge">/usr/local/bin</code>目录下,这里需要注意的就是必须要写绝对路径,不然可能出错</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">ln</span> <span class="nt">-s</span> /Users/yan/adb /usr/local/bin
</code></pre></div></div>
<p>如果想把一个目录加到 PATH,上面的方法就行不通了,但是可以再<code class="language-plaintext highlighter-rouge">~/.bash_profile</code>修改 path,下面把 platform-tools 添加到 PATH 中</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vi ~/.bash_profile
<span class="c"># 环境变量</span>
<span class="c"># PATH设置</span>
<span class="nb">export </span><span class="nv">ANDROID_HOME</span><span class="o">=</span>~/Library/Android/sdk
<span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="nv">$PATH</span>:<span class="nv">$ANDROID_HOME</span>/tools:<span class="nv">$ANDROID_HOME</span>/platform-tools
</code></pre></div></div>
<h3 id="zsh">zsh</h3>
<p>有人说 zsh 是终极 shell,确实 z 是最后一个字母了 o(╯□╰)o,zsh 配置很复杂,搞不好好不如不用,不过这么复杂事情已经有人给搞好了,<a href="http://ohmyz.sh/">Oh My ZSH</a>让 zsh 可以开箱即用,下面赶紧来使用 zsh 吧</p>
<p>mac 下自带 zsh,仅需一个命令就可以切换到 zsh 了</p>
<pre><code class="language-Bash">$ chsh -s /bin/zsh
</code></pre>
<p>下面还得安装 oh my zsh,安装 oh my zsh 需要先安装 git,好在 mac 自带了 git,oh my zsh 官网有安装的命令,就一行</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>sh <span class="nt">-c</span> <span class="s2">"</span><span class="si">$(</span>curl <span class="nt">-fsSL</span> https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh<span class="si">)</span><span class="s2">"</span>
</code></pre></div></div>
<p>zsh 的配置文件位于<code class="language-plaintext highlighter-rouge">~/.zshrc</code>,zsh 不会加载<code class="language-plaintext highlighter-rouge">.bash_profile</code>,这可麻烦了,之前设置的环境变量怎么办?简单只需要在<code class="language-plaintext highlighter-rouge">.zshrc</code>中手动加载<code class="language-plaintext highlighter-rouge">.bash_profile</code>就行了</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>vi ~/.zshrc
<span class="c"># 加载 .bash_profile</span>
<span class="nb">source</span> ~/.bash_profile
</code></pre></div></div>
<p>oh my zsh 进行了很多配置,让 zsh 比 bash 好用很多,比如大量 alias 的设置,完整的别名列表见<a href="https://github.com/robbyrussell/oh-my-zsh/wiki/Cheatsheet">这里</a></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.. # 等同于 cd ..
... # 等同于 cd ../..
~ # 等同于 cd ~
</code></pre></div></div>
<p>oh my zsh 有很多功能,比如换肤,这里就不折腾了,你要是喜欢就自己折腾吧</p>
<p>我安装了如下几个插件</p>
<pre><code class="language-Bash">$ vi ~/.zshrc
plugins=(git sublime code z zsh-autosuggestions zsh-syntax-highlighting)
</code></pre>
<ul>
<li>git 插件可以让你的命令行显示出来分支名,工作区状态,非常好用;</li>
<li>sublime 插件会添加一个全局的<code class="language-plaintext highlighter-rouge">st</code>命令,通过这个命令可以通过命令行用 sublime 打开任何文件</li>
<li>vscode 的插件,同 sublime 插件的作用</li>
<li>z 提供类似 jump 的功能,可以进入过得目录,快速跳转</li>
<li>zsh-syntax-highlighting 是一个非常有用的命令,可以提示输入的命令是否正确</li>
<li>zsh-autosuggestions 会根据你的历史记录,提供自动提示功能,非常好用</li>
</ul>
<h3 id="安装命令">安装命令</h3>
<p>下面来介绍下 mac 如何安装第三方命令,在开始介绍之前,先介绍一点基本知识,mac 中程序一般位于三个目录:</p>
<ul>
<li>/bin 系统程序存放处</li>
<li>/usr/bin mac 自动第三方程序存放处,如 git python ruby</li>
<li>/usr/local/bin 用户安装第三方程序存放处</li>
</ul>
<p>其中覆盖优先级是<code class="language-plaintext highlighter-rouge">/usr/local/bin </code> > <code class="language-plaintext highlighter-rouge">/usr/bin</code> > <code class="language-plaintext highlighter-rouge">/bin</code>,优先级其实是由 PATH 中的设置决定的,上面的顺序是系统默认的设置</p>
<p>mac 下安装命令最简单的方式就是手动下载安装,比如手动下载 git 的安装包,但缺点很多,就不介绍了</p>
<p>mac 下有两个安装命令的工具一个是<a href="http://www.macports.org/">MacPorts</a>,另一个是<a href="http://brew.sh/">Homebrew</a>,下面主要介绍下 Homebrew</p>
<p>Homebrew(简称 brew)是 Mac 不可或缺的软件管理工具,让 Mac 拥有类似 apt-get 的功能,用以简化软件的安装、升级和卸载过程</p>
<p>brew 会下载源代码,然后执行 <code class="language-plaintext highlighter-rouge">./configure</code> && <code class="language-plaintext highlighter-rouge">make install</code> ,将软件安装到单独的目录(<code class="language-plaintext highlighter-rouge">/usr/local/Cellar</code>)下,然后软链(symlink)到 <code class="language-plaintext highlighter-rouge">/usr/local/bin</code> 目录下,同时会自动检测下载相关依赖库,并自动配置好各种环境变量,这简直不能太好用了^_^</p>
<p>brew 的安装也非常简单,去官网拷贝安装代码即可,其中 ruby 和 curl 都是 mac 的自带程序</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>/usr/bin/ruby <span class="nt">-e</span> <span class="s2">"</span><span class="si">$(</span>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/master/install<span class="si">)</span><span class="s2">"</span>
</code></pre></div></div>
<p>安装好后就可以通过 brew 来安装程序了</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>brew <span class="nb">install </span>wget <span class="c"># 安装wget</span>
<span class="nv">$ </span>brew <span class="nb">install </span>git <span class="c"># 安装git</span>
<span class="nv">$ </span>brew <span class="nb">install </span>node <span class="c"># 安装node</span>
</code></pre></div></div>
<p>brew 比较常用的命令如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>brew install xxx # 安装
brew uninstall xxx # 卸载
brew upgrade xxx # 升级程序
brew list # 写出本地安装程序
brew search xxx # 查询可以用程序
brew info xxx # 查看制定程序的信息
</code></pre></div></div>
<p>可以通过 brew 安装系统已经存在的程序,比如 git,python 等,brew 安装的程序会覆盖系统安装的程序,但由于安装目录不同,仍可通过绝对路径访问系统自带程序</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git <span class="c"># brew安装git</span>
<span class="nv">$ </span>/usr/bin/git <span class="c"># 系统自带git</span>
<span class="nv">$ </span>/usr/local/bin/git <span class="c"># brew安装git</span>
</code></pre></div></div>
<h3 id="安装-python">安装 python</h3>
<p>系统自带的 python 是 2.7.10,但没有自带 pip,python 从 2.7.13 开始自带 pip,由于要用 pip 所以我想在安装一个 python,就可以通过 brew 安装</p>
<p>搜索 python,看到有两个</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>brew search python
python python3 ...
</code></pre></div></div>
<p>我想安装 python2,应该是第一个,下面通过 info 看下具体信息,会出现很多信息,关注版本就好了</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>brew info python
python: stable 2.7.14 <span class="o">(</span>bottled<span class="o">)</span>, HEAD
...
</code></pre></div></div>
<p>下面安装 python</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>brew <span class="nb">install </span>python
...
</code></pre></div></div>
<p>brew 会告诉你安装到了哪里,修改了 PATH,怎么调用 pip,可以发现现在 python 已经指向新安装的 python 了,但,需要通过 pip2 来使用 pip 功能</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python <span class="nt">--version</span>
Python 2.7.13
<span class="nv">$ </span>pip
zsh: <span class="nb">command </span>not found: pip
<span class="nv">$ </span>pip2 <span class="nt">--version</span>
pip 9.0.1 from /usr/local/lib/python2.7/site-packages <span class="o">(</span>python 2.7<span class="o">)</span>
</code></pre></div></div>
<p>除了通过升级 python 的方式安装 pip,也可以单独安装 pip</p>
<p>首先需要手动下载 pip 安装文件,下载 get-pip.py(https://bootstrap.pypa.io/get-pip.py)</p>
<p>然后运行下面的命令即可</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>python get-pip.py
</code></pre></div></div>
<p>安装好 pip 以后,就可以通过 pip 来进行自身的升级</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>pip <span class="nb">install</span> <span class="nt">--upgrade</span> pip
</code></pre></div></div>
<h3 id="安装-ruby">安装 ruby</h3>
<p>mac 自带的 ruby 是 2.3,我的博客需要用到 jekyll,jekyll 已经不支持 2.3 了,需要安装更高版本的 ruby,可以通过 brew 直接安装一个,但 ruby 有自己的多版本管理工具<a href="https://rvm.io/">RVM</a>,RVM 是一个命令行工具,可以提供一个便捷的多版本 Ruby 环境的管理和切换</p>
<p>rvm 的官网有安装程序的命令</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="se">\c</span>url <span class="nt">-sSL</span> https://get.rvm.io | bash <span class="nt">-s</span> stable
</code></pre></div></div>
<p>下面来安装指定版本的 ruby</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>rvm list known <span class="c"># 列出ruby所有版本</span>
<span class="o">[</span>ruby-]2.4[.1]
...
<span class="nv">$ </span>rvm <span class="nb">install </span>2.4.1 <span class="c"># 安装指定版本</span>
<span class="nv">$ </span>ruby <span class="nt">-version</span>
ruby 2.4.1p111 <span class="o">(</span>2017-03-22 revision 58053<span class="o">)</span> <span class="o">[</span>x86_64-darwin16]
</code></pre></div></div>
<p>rvm 安装的 ruby 位于<code class="language-plaintext highlighter-rouge">~yan/.rvm/rubies</code>目录下,不会和系统的 ruby 冲突,系统自带 ruby 位于<code class="language-plaintext highlighter-rouge">/usr/bin/ruby</code></p>
<p>如果很长时间安装不上,可能是 rvm 的下载源不稳定,可以尝试切换为<a href="https://cache.ruby-china.org/">淘宝的源</a></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"ruby_url=https://cache.ruby-china.org/pub/ruby"</span> <span class="o">></span> ~/.rvm/user/db
</code></pre></div></div>
<p>rvm 常用命令如下:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>rvm list <span class="c"># 列出本地版本</span>
<span class="nv">$ </span>rvm use 2.4.1 <span class="c"># 如果本地安装了多个版本,可切换到指定版本</span>
<span class="nv">$ </span>rvm remove 2.4.1 <span class="c"># 卸载指定版本</span>
</code></pre></div></div>
<p>rvm 还有很多其他的功能,不过我就用到这么多,自己摸索吧</p>
<h2 id="常用软件">常用软件</h2>
<p>下面整理下自己常用的软件</p>
<ul>
<li>draw.io 跨平台的 viso,主要用来画一些流程图,线框图,结构图</li>
<li>OmniGraffle 画各种图,基本用不到</li>
<li>OmniPlan 画甘特图,项目管理神器</li>
<li>typora 所见即所得的 md 工具,一用就会爱上</li>
<li>cheatsheet 一键查看当前工具的快捷键神器,再也不怕忘记快捷键了</li>
<li>Charles mac 下的 fiddler,用来抓取 http 请求</li>
<li>Foxit Reader 一款 pdf 阅读器,跨平台,免费的</li>
<li>iZip Unarchiver 用来在 mac 下接呀 rar 压缩包</li>
<li>LICEcap 用来录制屏幕 gif 图</li>
<li>Read CHM 用来阅读 chm 文件</li>
<li>SwitchHosts! 用来管理 host</li>
<li>VirtualBox 虚拟机软件,跨平台,用来安装 windows 和 linux</li>
<li>AppCleaner 查看软件的文件路径,还能清理软件,适合有洁癖的人使用</li>
<li>Dr. Cleaner mac 清理工具,不怎么用得到</li>
<li>Beyond Compare 跨平台的文件比较工具</li>
<li>CatchMouse 如果你有三个显示器的,在不同显示器之间快速切换鼠标的工具</li>
<li>百度 PPT 遥控器 通过手机代替遥控笔,适合会议室没有遥控笔的情况</li>
<li>RescueTime 事件追踪软件,只顾过数据都会上报,不适合公司电脑使用</li>
<li>AnyDesk 一个小而美的远程控制软件,跨平台</li>
</ul>
<h2 id="总结">总结</h2>
<p>最后推荐大家阅读池建强老师的《<a href="https://amazon.cn/gp/product/B00ID5UV30/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=yanhaijing-23&creative=3200&linkCode=as2&creativeASIN=B00ID5UV30&linkId=2058fdf9c81d13e245a8e85ab48b022c">MacTalk·人生元编程</a>》,书中介绍了很多 mac 知识和 mac 技巧,并且包含了很多受益匪浅的人生哲理,非常值得阅读</p>
图解4种git合并分支方法
2017-07-14T00:00:00+00:00
http://yanhaijing.com/git/2017/07/14/four-method-for-git-merge
<p>有时候我们会后悔,有时候我们会想回到过去,有时候我们想改变历史,然而在我们这个世界,目前来看是无法回到过去改变历史的</p>
<p>但在git的世界里,一切皆有可能,我们可以在多维空间里任意切换,随意改变一个宇宙的时间线,只要我们愿意,git的分支就是这么神奇</p>
<p>然而很多时候你以为你改变了历史,不为人知,那个宇宙并没有消失,而是遗失在了git的世界里,有能力的人便能找到</p>
<p>彼此分开的世界也能随时交叉合并,世界就这样开开合合,偶会需要解决合并冲突</p>
<p>git中的分支非常的轻量,其实就是一个文件,里面记录了分支所指向的commit id,下图中有两个分支分别是master和test,他们都指向了A2这个提交,HEAD是一个特殊的指针,他永远指向你当前所在的位置;有时候你可能不在某一个分支上,不要惊慌,你随时有权利去你想去的分支,git赋予了你新建,切换分支的能力</p>
<p><img src="/blog/496.png" alt="" /></p>
<p>然后有时候世界并不总如上图那般美好,面对分叉的两个分支,git新手总是一脸茫然,本文我将讲述git中合并分支的方法</p>
<p>在git中合并分支有三种方法,分别是merge,rebase,cherry-pick,而其中merge又有三种区别,下面将一一介绍</p>
<h2 id="fast-forward">fast-forward</h2>
<p>如果待合并的分支在当前分支的下游,也就是说没有分叉时,会发生快速合并,从test分支切换到master分支,然后合并test分支</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout master
git merge test
</code></pre></div></div>
<p>这种方法相当于直接把master分支移动到test分支所在的地方,并移动HEAD指针</p>
<p><img src="/blog/498.gif" alt="" /></p>
<h2 id="no-ff">no-ff</h2>
<p>如果我们不想要快速合并,那么我们可以强制指定为非快速合并,只需加上<code class="language-plaintext highlighter-rouge">--no-ff</code>参数</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout master
git merge –no-ff test
</code></pre></div></div>
<p>这种合并方法会在master分支上新建一个提交节点,从而完成合并</p>
<p><img src="/blog/499.gif" alt="" /></p>
<h2 id="squash">squash</h2>
<p>svn的在合并分支时采用的就是这种方式,squash会在当前分支新建一个提交节点</p>
<p>squash和no-ff非常类似,区别只有一点不会保留对合入分支的引用</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout master
git merge –squash test
</code></pre></div></div>
<p><img src="/blog/500.gif" alt="" /></p>
<h2 id="rebase">rebase</h2>
<p>当要合并两个分叉的分支时,merge的方式是将待合入分支和当前分支不同的部分,在当前分支新建节点,如下图所示</p>
<p><img src="/blog/501.png" alt="" /></p>
<p>rebase与merge不同,rebase会将合入分支上超前的节点在待合入分支上重新提交一遍,如下图,B1 B2会变为B1’ B2’,看起来会变成线性历史</p>
<p><img src="/blog/502.png" alt="" /></p>
<h2 id="cherry-pick">cherry-pick</h2>
<p>这命令简直就是神器,给你自由,你想把那个节点merge过来就把那个节点merge过来,其合入的不是分支而是提交节点</p>
<h2 id="总结">总结</h2>
<p>只有知道了这些合并方式的区别,才能git在手,天下我有,任你分支在凌乱,我自岿然不动</p>
<h2 id="继续学习">继续学习</h2>
<ul>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-0/">起底Git-开篇</a></li>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-1/">起底Git-版本控制简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-2/">起底Git-Git简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/08/deep-git-3/">起底Git-Git内部原理</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-4/">起底Git-Git基础</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-5/">起底Git-Git进阶</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-6/">起底Git-Git开发流程</a></li>
<li><a href="http://yanhaijing.com/git/2014/11/01/my-git-note/">起底Git-Git常用命令总结</a></li>
</ul>
多说评论系统迁移记
2017-04-11T00:00:00+00:00
http://yanhaijing.com/other/2017/04/11/trans-duoshuo-comment
<p>多说评论厂倒闭了,倒闭了,倒闭了!!!</p>
<p>作为一个使用了3年的重度用户,面对这个消息,满是感伤,说好的不离不弃呢,我只能默默的送上祝福</p>
<h2 id="抉择">抉择</h2>
<p>摆在我面前有两条路,要么迁移,干掉评论,我选择了前者</p>
<p>解决了纠结,又有一个大大岔路口摆在面前,众多评论系统中选择哪个呢???</p>
<ul>
<li>一大波国产评论系统</li>
<li>disqus</li>
<li><a href="https://commentit.io/">commentit</a></li>
<li><a href="https://github.com/imsun/gitment">gitment</a></li>
</ul>
<p>先来说结果,最后我选择了commentit,原因是我乐意,首先类多说这种评论系统(有言,disqus)被我排除在外了,毕竟伤口还疼呢</p>
<p>gitment通过github api把文章评论托管在github issues,通过github issues做精细化定制,非常有意思,并且也是国人开发的,优点是只依赖github api,祈祷github 不下线这个功能就行了</p>
<h2 id="comm-entit">Comm (ent|it)</h2>
<p>我选择的是commentit,一个法国小孩开发的,依赖法国小孩的服务系统,将评论发pull request到自己的仓库,然在在自己仓库存储评论</p>
<p>法国小孩在<a href="https://commentit.io/tos">警告</a>里说自己不对系统稳定性和时间做任何保证,o(╯□╰)o</p>
<p>接入过程非常简单,官方的<a href="https://commentit.io/getting-started">Getting started</a>介绍的非常清楚了</p>
<p>开始老是报错,提交不上去,错误信息如下,后来在<a href="https://github.com/guilro/commentit/issues/9">github</a>和作者交流才发现是我的仓库名字是<code class="language-plaintext highlighter-rouge">yanhaijing.github.com</code>的问题,重命名为<code class="language-plaintext highlighter-rouge">yanhaijing.github.io</code>就好了</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Error: could not commit the comment to github (repository or file not found)
</code></pre></div></div>
<p>commentit提供了两种评论存储方案,存储在博文的meta数据,或者存储在单独外部文件,我选择了后者,原因是方便迁移历史评论数据,而且我也觉得在.md里存储一大推评论有点恶心,具体接入可以直接参看我的<a href="https://github.com/yanhaijing/yanhaijing.github.io/blob/master/_includes/JB/comments-providers/commentit">github</a></p>
<p>整体体验还是不错的,登录账号可以使用推特,脸谱和github</p>
<h2 id="历史数据问题">历史数据问题</h2>
<p>好吧我最想说的是这部分,我大概有几百个评论,实在舍不得丢弃,最后写了一个脚本将多说导出的数据,转换成了commentit的数据</p>
<p>先来看下多说导出的数据</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"threads": [
{
"thread_id": 1366309223767474177,
"created_at": "2014-01-17T16:59:41+08:00",
"url": "http://yanhaijing.com/css/2014/01/css/"
}
],
"posts": [
{
"thread_id": 1366309223767474198,
"message": "这题太发散了~~",
"created_at": "2014-01-22T11:54:42+08:00",
"author_name": "修远兮",
"author_url": "http://weibo.com/bai80",
}
]
</code></pre></div></div>
<p>再来看一下<a href="https://github.com/yanhaijing/yanhaijing.github.io/blob/master/_data/comments.yml">commentit</a>要求的格式:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>encapsulation-of-javascript:
- author:
type: full
displayName: 修远兮
url: http://weibo.com/bai80
picture: /img/default-avatar.png
content: 这题太发散了~~
date: 2014-01-22T11:54:42+08:00
</code></pre></div></div>
<p>思路是,把评论按照文章进行聚合,代码如下</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let threadsMap = {};
ret.threads.forEach(function (thread) {
// 过滤指定url
if (/yanhaijing\.com\/\w*\/\d\d\d\d\/\d\d\/\d\d/.test(thread.url)) {
threadsMap[thread.thread_id] = thread;
console.log(thread.thread_id, thread.url)
}
});
let commentMap = {};
ret.posts.forEach(function (post) {
if (!threadsMap[post.thread_id]) {
return;
}
let threadUrl = threadsMap[post.thread_id].url;
let arr = threadUrl.split('?')[0].split('/');
let threadName = arr.pop() || arr.pop();
if (!/^[a-zA-Z0-9_\-]+$/.test(threadName)) {
return;
}
if (!commentMap[threadName]) {
commentMap[threadName] = [];
}
commentMap[threadName].push(post);
})
let text = '';
Object.keys(commentMap).forEach(function (key) {
text += `${key}:\n`;
commentMap[key].forEach(function (comment) {
let msg = comment.message;
if (msg.indexOf('\n') !== -1) {
msg = ' |-\n' + msg;
msg = msg.replace(/\n/g, '\n ');
}
msg = msg.replace(/[\[\]:@"\b]/g, ' ');
let name = comment.author_name.replace(/@/g, '');
let picture = '/img/default-avatar.png';
if (name === '颜海镜') {
picture = '/img/facelessman.png'
}
text += ` - author:\n`;
text += ` type: full\n`;
text += ` displayName: ${name}\n`;
text += ` url: ${comment.author_url}\n`;
text += ` picture: ${picture}\n`;
text += ` content: ${msg}\n`;
text += ` date: ${comment.created_at}\n`;
});
});
console.log(text);
</code></pre></div></div>
<p>遇到的一个问题就是头像,头像数据我只能弄了一个默认头像,所以历史评论全变成了一样的头像,好在用户名和url还有,<a href="http://yanhaijing.com/tool/2016/06/30/my-sublime3/">demo</a></p>
<h2 id="总结">总结</h2>
<p>总结了一些经验和遇到的一些问题,希望能够帮到迁移的人,现在我只祈祷法国小哥别弃坑,o(╯□╰)o</p>
<p>如果你是hexo我不建议迁移commenit,因为commentit不支持提交到指定分支,而hexo一般都是有两个分支source和master,我建议看下上面提到的gitment</p>
windows常用网络命令
2017-04-09T00:00:00+00:00
http://yanhaijing.com/windows/2017/04/09/some-command-of-cmd
<p>作为没用过mac的温到死(windows)狗,每次都要百度也是够了,本文总结下自己常用的网络命令</p>
<ul>
<li>ping</li>
<li>ipconfig</li>
<li>netstat</li>
<li>route</li>
<li>nslookup</li>
<li>tracert</li>
<li>arp</li>
</ul>
<h2 id="ping">ping</h2>
<p>测试本机与指定机器是否联通</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ping jingyan.baidu.com
$ ping -t jingyan.baidu.com # ping 1000万次
$ ping -n 10 jingyan.baidu.com # ping 指定次数
</code></pre></div></div>
<h2 id="ipconfig">ipconfig</h2>
<p>网卡相关操作</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ipconfig # 查看网卡信息,如ip地址
$ ipconfig /all # 查看mac地址
$ ipconfig /displaydns # 查看dns缓存内容
$ ipconfig /flushdns # 清除dns缓存
</code></pre></div></div>
<h2 id="netstat">netstat</h2>
<p>了解整体网络情况及连接情况</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ netstat # 实时查看
$ netstat -n # 数组形式显示,ip地址代替域名
$ netstat -r # 显示路由表
</code></pre></div></div>
<h2 id="route">route</h2>
<p>查看和配置路由</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ route print # 显示路由表
</code></pre></div></div>
<h2 id="nslookup">nslookup</h2>
<p>查询dns信息</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ nslookup jingyan.baidu.com # 查询指定域名的dns信息
</code></pre></div></div>
<h2 id="tracert">tracert</h2>
<p>跟踪路由信息</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ tracert jingyan.baidu.com
$ tracert -h 10 jingyan.baidu.com # 指定最大跳跃次数
</code></pre></div></div>
<h2 id="arp">arp</h2>
<p>查看本地计算机或另一台计算机的ARP高速缓存中的内容</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ arp -a # 查看arp缓冲内容
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>本文总结了自己常用的网络命令,以后再也不用到处去找百度了O(∩_∩)O~~</p>
红蓝墨水之谜?
2017-03-23T00:00:00+00:00
http://yanhaijing.com/math/2017/03/23/red-blue-ink
<p>先来划重点:这是一道百度校招面试题!!!</p>
<h2 id="背景">背景</h2>
<p>那位同学先别走,我知道你早已工作多年,现在社招也有这道题了,(<em>^__^</em>) 嘻嘻……</p>
<p>真实的故事是这样的,遥想当年博主参加百度校招,就挂在了这道题上</p>
<p>最近在面试中也有问这道题,发现大部分人都回答不上来,今天写出来给大家分享下</p>
<h2 id="干货">干货</h2>
<p>题:有一瓶红墨水,一瓶蓝墨水,从红墨水舀一勺到蓝瓶里,搅匀后再舀一勺到红瓶里。</p>
<p>问:此时红墨水里含蓝墨水的量和蓝墨水里含红墨水的量关系是什么?大于、小于、等于?</p>
<p><img src="/blog/485.png" alt="" /></p>
<p>建议大家先自己思考下思路,实在没思路再看答案。</p>
<h2 id="答案">答案</h2>
<p>相信大家看到题目应该和博主一样毫无头绪,其实按照经验这道题的答案应该是等于,对这道题的答案就是等于。</p>
<p>结题方法应该有很多种,比如方程组,x,y啥的,但我们可以考虑一种极限情况,加入勺子足够大,和瓶子一样大,明显是相等的。</p>
<p>但你说极限,明显不能糊弄过面试官的,这道题有一个其实有一个障眼法,不要关注第一次,我们只关注第二次那一勺。然后回答下面的问题</p>
<p>红里面有的蓝墨水=一勺蓝红混合体-一勺里含的红</p>
<p>蓝里面有的红墨水=一勺红墨水-第二次一勺里含的红</p>
<p>好了,现在明显蓝里的红和红里的蓝是一样一样的啊</p>
<h2 id="总结">总结</h2>
<p>好了,觉得有意思的评论吧;觉得无聊的拿板砖拍吧</p>
模块化的一些感悟
2017-03-15T00:00:00+00:00
http://yanhaijing.com/javascript/2017/03/15/some-of-module
<p>最近看到尼古拉斯大神的一篇分享——<a href="https://www.slideshare.net/nzakas/scalable-javascript-application-architecture">Scalable Javascript Application Architecture</a>,里面基于YUI讲了构建web应用的一些思想,现在看有些已经过时了,但思想永不过时,强烈建议读一读</p>
<p>其中对模块的介绍感觉很经典,加上一点自己的见解,总结分享一下</p>
<h2 id="模块新说">模块新说</h2>
<p>前端模块由HTML CSS JS三部分组成,模块应该具备包含自身的全部功能,不依赖外部环境</p>
<p>低耦合可以让模块互不影响,随意插拔</p>
<p>模块很像拼图的碎片,拼图碎片不需要知道完整的图片长什么样,仅仅保证自己能够被拼接即可</p>
<p>程序的成功创建,其实就是让每个模块按部就班</p>
<p>模块像小孩子一样,需要严格的一组规则约束,来保证避免问题</p>
<h2 id="模块心法">模块心法</h2>
<p>我在之前写过一篇<a href="http://yanhaijing.com/program/2016/09/01/about-coupling/">图解7种耦合关系 </a>,里面讲解了不同的耦合程度;对于模块我们希望做到高内聚,低耦合</p>
<p>下面是高内聚的心法</p>
<ul>
<li>仅处理自己
<ul>
<li>仅调用自己的方法</li>
<li>不能访问外部dom节点</li>
<li>不能访问全局变量</li>
</ul>
</li>
<li>
<p>仅发出消息</p>
</li>
<li>不要制造垃圾
<ul>
<li>不能创建全局变量</li>
</ul>
</li>
<li>不要和陌生人说话
<ul>
<li>不要直接和其他模块通信</li>
</ul>
</li>
</ul>
<h2 id="总结">总结</h2>
<p>好的模块应该做到对外界无感知,仅接收数据,暴漏API和回调</p>
起底Git-Git开发流程
2017-02-09T00:00:00+00:00
http://yanhaijing.com/git/2017/02/09/deep-git-6
<p>这是<a href="http://yanhaijing.com/git/2017/01/19/deep-git-0/">起底Git系列</a>的第六篇,本篇我们来介绍一下Git的开发流程。</p>
<p>Git非常灵活,可以适用各种开发环境,你可以定义自己的开发流程,网上也有各种各样的流程,这里我们介绍两种常用的流程,分别是集中式开发流程和集成管理者工作流</p>
<h2 id="集中式开发流程">集中式开发流程</h2>
<p>大家都访问同一个远端仓库,传统的svn就是这种模式</p>
<p><img src="/blog/471.png" alt="" /></p>
<p>在GitHub上有两种方式可以实现这种模式,一种是给自己的仓库添加其他开发者的key,也就是授权给其他开发者,这种模式比较适合三三两两的自由开发者</p>
<p><img src="/blog/472.png" alt="" /></p>
<p>另一种模式是利用github的组织(organization),类似于群,可以邀请别人加入组织,组织可以进行精细的权限控制,这种模式适合小型团队</p>
<p><img src="/blog/473.png" alt="" /></p>
<h2 id="集成管理者工作流">集成管理者工作流</h2>
<p>这种模式下每个开发者有自己远端仓库,开发者开发完后会给管理员发请求,管理员自己选择是否合并到项目仓库</p>
<p><img src="/blog/474.png" alt="" /></p>
<p>GitHub中的fork+pull request就是这种模式,这种模式非常适合小型开源项目,接受未知第三方的贡献</p>
<h2 id="总结">总结</h2>
<p>如果你有任何疑问的话,欢迎留言讨论;如果本系列文章对你有帮助的话,那我很荣幸,别忘了打赏哦,O(∩_∩)O哈哈~</p>
<p>最后感谢你的阅读,O(∩_∩)O哈哈~</p>
<h2 id="继续学习">继续学习</h2>
<ul>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-1/">版本控制简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-2/">Git简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/08/deep-git-3/">Git内部原理</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-4/">Git基础</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-5/">Git进阶</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-6/">Git开发流程</a></li>
<li><a href="http://yanhaijing.com/git/2014/11/01/my-git-note/">Git常用命令总结</a></li>
</ul>
起底Git-Git进阶
2017-02-09T00:00:00+00:00
http://yanhaijing.com/git/2017/02/09/deep-git-5
<p>这是<a href="http://yanhaijing.com/git/2017/01/19/deep-git-0/">起底 Git 系列</a>的第六篇,本篇我们来介绍一下 Git 的进阶技巧。</p>
<ul>
<li>选择版本</li>
<li>搜索调试</li>
<li>重写历史</li>
<li>重置揭秘</li>
<li>高级合并</li>
</ul>
<h2 id="选择版本">选择版本</h2>
<p>假设当前版本库如下图所示,有时我们可能先找到当前提交的父提交和祖先提交,^和~可以满足我们的需求</p>
<p>^和~都匹配当前提交的父提交,^^和~~匹配父提交的父提交,^和~后面跟数字的时候意义是不同的,具体可以看下面的例子</p>
<p><img src="/blog/475.png" alt="" /></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git log HEAD^
A2
<span class="nv">$ </span>git log HEAD^^
A1
<span class="nv">$ </span>git log HEAD^2
B1
<span class="nv">$ </span>git log HEAD~
A2
<span class="nv">$ </span>git log HEAD~~
A1
<span class="nv">$ </span>git log HEAD~2
A1
</code></pre></div></div>
<p>有时候我们可能会想选择一个区间,比如 A1,A2,A3,下面通过例子说明..,…和^的区别</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git log master..test
C0 C1
<span class="nv">$ </span>git log ^master <span class="nb">test
</span>C0 C1
<span class="nv">$ </span>git log master…test
A1 A2 A3 C0 C1
</code></pre></div></div>
<h2 id="搜索调试">搜索调试</h2>
<p>A:设想这样一种情况,摸个分支 test,开发完后被删除了,怎么找回这个分支呢?</p>
<p>其实 git 会在本地记录每次 HEAD 的变化,通过 reflog 命令可以拿到这些记录</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git reflog
0e94c5b HEAD@<span class="o">{</span>0<span class="o">}</span>: commit: 222
7e07aa7 HEAD@<span class="o">{</span>1<span class="o">}</span>: commit: 111
c5aba97 HEAD@<span class="o">{</span>2<span class="o">}</span>: commit: 000
</code></pre></div></div>
<p>比如 111 是 test 分支最后一个提交,我们可以去 111 这个提交,然后在新建一个分支就 ok 了</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git checkout 7e07aa7 <span class="c"># 或者git checkout HEAD@{1}</span>
<span class="nv">$ </span>git checkout <span class="nt">-b</span> <span class="nb">test</span>
</code></pre></div></div>
<p>B:设想这样一种情况,某天你突然发现某行代码写错了,你想快速找到这个 bug 的始作俑者?</p>
<p>blame 可以快速显示文件的每一行最后一次修改是谁</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git blame README.md
f6ffa8f4 <span class="o">(</span>yanhaijing 2016-08-03 19:54:42 +0800 1<span class="o">)</span> 123
f6ffa8f4 <span class="o">(</span>yanhaijing 2016-08-03 19:54:42 +0800 1<span class="o">)</span> 456
</code></pre></div></div>
<p>blame 时可以指定范围</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git blame <span class="nt">-L10</span>,15 README.md <span class="c"># 查看10-15行的信息</span>
</code></pre></div></div>
<p>C:设想这样一种情况,你想在 Git 的某个历史提交中进行搜索?</p>
<p>grep 只能搜索工作目录,git grep 可以在指定提交中进行搜索</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git <span class="nb">grep </span>yanhaijing HEAD~27 fis-conf.js
HEAD~27:fis-conf.js: <span class="k">*</span> @author yanhaijing.com
</code></pre></div></div>
<p>D:设想这样一种情况,你想在 Git 的整个历史中进行搜索?git log 可以实现这个功能</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git log <span class="nt">-Syanhaijing</span> <span class="nt">--oneline</span>
0a191c message aaa
</code></pre></div></div>
<p>E:设想这样一种情况,某一天你突然发现线上代码挂了,但你找不到原因,你想快速找到是哪一个版本引入的 bug?</p>
<p>git bisect 是一个非常有用的调试工具,它通过自动进行一个二分查找来找到哪一个特定的提交是导致 bug 或者问题的第一个提交</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git bisect start <span class="c"># 开始</span>
<span class="nv">$ </span>git bisect bad <span class="c"># 标记为好的</span>
<span class="nv">$ </span>git bisect good <span class="c"># 标记为坏的</span>
<span class="nv">$ </span>git bisect reset <span class="c"># 结束</span>
</code></pre></div></div>
<h2 id="重写历史">重写历史</h2>
<p>假设你提交完后发现忘记了一些东西,打算更改上次提交,在 git 中可以使用追加提交,假设现在仓库状态如下所示</p>
<p><img src="/blog/476.png" alt="" /></p>
<p>修改完后可以再次提交</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git add <span class="nb">.</span>
<span class="nv">$ </span>git commit <span class="nt">--amend</span>
</code></pre></div></div>
<p>就可以修改上次提交,需要注意的是上一次提交并没有被删除,只是没有分支引用,变成了游离状态,在未来的某个时间会被 git 自动回收</p>
<p><img src="/blog/477.png" alt="" /></p>
<p>如果你进行了几次提交后后悔了,想重写之前的好几次提交,那就只能用 rebase 了,假设目前状态如下</p>
<p><img src="/blog/478.png" alt="" /></p>
<p>假设你想重写 A1 和 A2</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git rebase <span class="nt">-i</span> HEAD~2
</code></pre></div></div>
<p>需要注意的是已经 push 到远端的提交,就不要再重写了,不然世界人民会恨你,因为你需要<code class="language-plaintext highlighter-rouge">git push -f</code></p>
<p><img src="/blog/479.png" alt="" /></p>
<h2 id="重置揭秘">重置揭秘</h2>
<p>重置有两种方法,reset 和 checkout,这两个方法非常容易混淆,两个命令都分为全局模式和文件模式</p>
<p>reset 全局模式可以用下图总结</p>
<p><img src="/blog/483.png" alt="" /></p>
<p>reset 的文件模式可以覆盖索引区,一个副作用就是用来取消暂存</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git reset xxx – file
</code></pre></div></div>
<p>checkout 的全局模式可以用下图总结</p>
<p><img src="/blog/484.png" alt="" /></p>
<p>checkout 的文件模式会覆盖索引区和工作区,可以用来丢弃修改,属于不可逆转的操作</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout xxx – file
</code></pre></div></div>
<p>其实下图就总结两个命令的区别</p>
<p><img src="/blog/482.png" alt="" /></p>
<h2 id="高级合并">高级合并</h2>
<p>合并分支时,很多人非常害怕遇到冲突,其实冲突并不可怕</p>
<p>A:git 默认的冲突显示包括 our 和 their,如果想得到和 svn 那样包含 base+our+their 的代码,可以检出冲突</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git checkout <span class="nt">--conflict</span><span class="o">=</span>diff3 hello.rb
</code></pre></div></div>
<p>B:如果在冲突时想想 svn 一样得到,base+our+their 三个文件的代码</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git show :1:xxx <span class="o">></span> xxx.base
<span class="nv">$ </span>git show :2:xxx <span class="o">></span> xxx.our
<span class="nv">$ </span>git show :3:xxx <span class="o">></span> xxx.their
</code></pre></div></div>
<p>C:合并冲突一团乱麻,想撤销合并</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git merge <span class="nt">--abort</span>
</code></pre></div></div>
<p>D:合并后后悔了?想撤消合并?分为两种情况</p>
<p>假如还没推送到远端,可以 reset 掉</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git reset <span class="nt">--hard</span> HEAD~
</code></pre></div></div>
<p><img src="/blog/480.png" alt="" /></p>
<p>如果已经推动到远端,可以用 revert</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git revert <span class="nt">-m</span> 1 HEAD
</code></pre></div></div>
<p><img src="/blog/481.png" alt="" /></p>
<h2 id="总结">总结</h2>
<p>如果你有任何疑问的话,欢迎留言讨论;如果本系列文章对你有帮助的话,那我很荣幸,别忘了打赏哦,O(∩_∩)O 哈哈~</p>
<p>最后感谢你的阅读,O(∩_∩)O 哈哈~</p>
<h2 id="继续学习">继续学习</h2>
<ul>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-1/">版本控制简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-2/">Git 简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/08/deep-git-3/">Git 内部原理</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-4/">Git 基础</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-5/">Git 进阶</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-6/">Git 开发流程</a></li>
<li><a href="http://yanhaijing.com/git/2014/11/01/my-git-note/">Git 常用命令总结</a></li>
</ul>
起底Git-Git基础
2017-02-09T00:00:00+00:00
http://yanhaijing.com/git/2017/02/09/deep-git-4
<p>这是<a href="http://yanhaijing.com/git/2017/01/19/deep-git-0/">起底 Git 系列</a>的第五篇,本篇我们来介绍一下 Git 基础。</p>
<h2 id="安装">安装</h2>
<p>学习 git 前,你需要先安装 git,git 安装流程请移步<a href="https://git-scm.com/downloads">这里</a>,里面有各个平台的安装方法</p>
<h2 id="基础">基础</h2>
<p>学习命令行工具要学会的第一步就是查看帮助文档,可以用下面的命令查看 git 的帮助信息</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git <span class="nb">help
</span>git <span class="nb">help </span>xxx
git xxx <span class="nt">--help</span>
git xxx <span class="nt">-h</span>
</code></pre></div></div>
<p>接下来就是一些自定义信息了,比如配置用户名和快捷命令(别名)</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git config <span class="nt">--global</span> user.name yanhaijing
git config <span class="nt">--global</span> user.email yanhaijing@yeah.net
git config <span class="nt">--global</span> alias.st status <span class="c">#git st</span>
git config <span class="nt">--global</span> alias.co checkout <span class="c">#git co</span>
git config <span class="nt">--global</span> alias.br branch <span class="c">#git br</span>
git config <span class="nt">--global</span> alias.ci commit <span class="c">#git ci</span>
</code></pre></div></div>
<p>值得一提的就是配置换行符了,windows 上的换行符和 mac 和类 unix 不一样,在跨平台时就痛苦了,为了统一,可以将提交到仓库的换行符同配置成 unix 格式</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> git config <span class="nt">--global</span> core.autocrlf input
</code></pre></div></div>
<p>如果想把 git 仓库提交到服务器,可能还要配置秘钥,如果你不想每次都输入密码的话</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh-keygen <span class="nt">-t</span> rsa <span class="nt">-C</span> yanxuefeng@baidu.com <span class="c"># 生成秘钥</span>
<span class="nb">cat</span> ~/.ssh/id_rsa.pub <span class="c"># 获取公钥,配置到github 的sshkey</span>
ssh <span class="nt">-T</span> git@github.com <span class="c"># 测试是否生效</span>
</code></pre></div></div>
<p>玩转 git 的第一步,你需要有一个仓库,获取仓库总共有两种办法</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git init <span class="c"># 初始化一个仓库</span>
git clone url <span class="c"># 克隆一个已有仓库</span>
</code></pre></div></div>
<p>有时候我们不想把某些文件提交到仓库里,比如编译产生的临时文件,此时.gitignore 能够发挥作用,.gitignore 的规则如下</p>
<ul>
<li># 代表注释</li>
<li>每行是一个 path,glob 模式匹配</li>
<li>!代表取非</li>
<li>*代表 0 个或多个字符</li>
<li>?代表一个字符</li>
<li>[]代表集合</li>
</ul>
<p>.gitattributes 这个还没想好怎么讲,挖个坑坑坑坑</p>
<p>git 仓库的工作流如下</p>
<p><img src="/blog/486.png" alt="" /></p>
<p>git 的文件状态如下</p>
<p><img src="/blog/487.png" alt="" /></p>
<p>想查看当前的仓库状态可以使用<code class="language-plaintext highlighter-rouge">git status</code>,这个命令能够给出很多提示信息,建议经常使用</p>
<p>关于 commit 的提交,每个 commit 必须是独立,完整的功能,保持适当粒度,不要过大也不要过小;关于提交信息的描述可以参考下开源届的规范,也可以看下我之前整理的<a href="http://yanhaijing.com/git/2016/02/17/my-commit-message/">提交规范</a>,下面是一个提交的例子</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Header <span class="o">=</span> <span class="nb">type</span>: subject(feat fix style refactor)
Body <span class="o">=</span> list
feat: 添加分享功能
给每篇博文添加分享功能Œ
- 添加分享到微博
- 添加分享到微信
</code></pre></div></div>
<p>在 git 内部一个 commit 的信息如下</p>
<p><img src="/blog/488.png" alt="" /></p>
<p>但后面我们会进行如下的抽象</p>
<p><img src="/blog/489.png" alt="" /></p>
<h3 id="分支">分支</h3>
<p>git 中的分支非常轻量,就是对 commit 的一个引用,默认 git 会新建一个 master 分支,git 中有一个特殊的引用讲座 HEAD,他只想当前所在的位置</p>
<p><img src="/blog/495.png" alt="" /></p>
<p>有时我们需要新建或删除分支</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git branch <span class="nb">test</span> <span class="c"># 新家分支test</span>
git branch –d <span class="nb">test</span> <span class="c"># 删除test分支,当前HEAD必须不能指向test分支</span>
</code></pre></div></div>
<p><img src="/blog/496.png" alt="" /></p>
<p>建好分之后,就该切换到那个分支去了</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout <span class="nb">test</span> <span class="c"># 切换到test分支</span>
</code></pre></div></div>
<p>新建并切换分支,可以合并成一个命令,下面的命令等同于上面两条命令</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout –b <span class="nb">test</span>
</code></pre></div></div>
<p><img src="/blog/497.png" alt="" /></p>
<p>在新建的分支上开发一段时间后,你可能需要将其合并到另一个分支去,而合并分支共有 4 中不同的方法,关于这四种方法的区别和场景请看这篇文章<a href="http://yanhaijing.com/git/2017/07/14/four-method-for-git-merge/">图解 4 种 git 合并分支方法</a></p>
<h3 id="远端">远端</h3>
<p>一张图概括远端和本地的关系,以及相应的操作命令</p>
<p><img src="/blog/494.png" alt="" /></p>
<p>其实 git 在远端是一个完整的仓库,和本地其实一样,也有 HEAD,也有 master;但我们本地为了防止冲突,都将远端的引用名字放到了 origin 下(origin 也可是别的名字)</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Remote HEAD -> xxx/HEAD(origin/HEAD)
Remote master -> xxx/master(origin/master)
</code></pre></div></div>
<p>在.git 下面的 config 文件里面又对应规则的配置信息,fetch 哪一行,设置将远端<code class="language-plaintext highlighter-rouge">refs/heads</code>下的放到本地的<code class="language-plaintext highlighter-rouge">refs/remotes/origin</code>下</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>remote <span class="s2">"origin"</span><span class="o">]</span>
url <span class="o">=</span> git@github.com:yanhaijing/yanhaijing.github.io.git
fetch <span class="o">=</span> +refs/heads/<span class="k">*</span>:refs/remotes/origin/<span class="k">*</span>
</code></pre></div></div>
<p>对于远端最常用的操作就是更新和推送</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git fetch origin <span class="c"># 将本地的远端和远端进行同步</span>
git merge origin/master <span class="c"># 将本地的远端合并到本地分支</span>
git pull origin <span class="c"># 这相当于上面两条命令</span>
git push origin master <span class="c"># 将本地推送到远程分支</span>
</code></pre></div></div>
<p>关于 push 的必须得说一下引用展开,下面这些命令是一样的,git 内部都会自动展开成最后一条</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git push origin master
git push origin master:master
git push origin heads/master:heads/master
git push origin refs/heads/master:refs/heads/master
</code></pre></div></div>
<p>有时候我们可能会查看远端的信息,比如看下远端地址啥的</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git remote –v <span class="c"># 查看全部远端的摘要信心</span>
git remote show xxx <span class="c"># 查看具体远端的详细信息</span>
</code></pre></div></div>
<p>有时候我们也会增加或删除远端</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git remote add xxx url <span class="c"># 廷加一个新的远端</span>
git remote remove xxx <span class="c"># 删除一个远端</span>
</code></pre></div></div>
<h3 id="日志">日志</h3>
<p>有时我们需要查看 git 的提交日志,可以使用<code class="language-plaintext highlighter-rouge">git log</code>,log 有大量参数,感兴趣的同学可以自行探索,但常用的就下面这么几个</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git log <span class="nt">-number</span> <span class="c"># 显示最近几条</span>
git log <span class="nt">--oneline</span>(--abbrev-commit <span class="nt">--pretty</span><span class="o">=</span>oneline)# 单行显示,显示简短commit <span class="nb">id
</span>git log <span class="nt">--graph</span> <span class="c"># 以树形展示</span>
git log <span class="nt">--decorate</span> <span class="c"># 显示分支名等</span>
git log <span class="nt">--first-parent</span> <span class="c"># 显示第一父元素(不显示merge进来的树形结构)</span>
git log <span class="nt">--all</span> <span class="c"># 显示全部分支</span>
</code></pre></div></div>
<p>除了<code class="language-plaintext highlighter-rouge">git log</code>我们还可以使用 gitk 这个工具,这是 git 2.0 以后加入的图形化工具,可以以更友好等方式查看 log 树,常用的有两个参数</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gitk –all <span class="c"># 显示全部分支</span>
gitk <span class="nt">--first-parent</span> <span class="c"># 显示第一父元素(不显示merge进来的树形结构)</span>
</code></pre></div></div>
<p>下图是<code class="language-plaintext highlighter-rouge">git log</code>和 gitk 的命令复杂度和显示效果</p>
<p><img src="/blog/491.png" alt="" /></p>
<p><img src="/blog/492.png" alt="" /></p>
<p>注:如果你在 mac 下使用 gitk,可能会发现显示非常模糊,这是因为 retina 屏幕的问题,可以尝试下下面的方法</p>
<p>首先安装 retinizer,如果你没安装 brew 可能需要安装 homebrew</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>brew cask <span class="nb">install </span>retinizer
</code></pre></div></div>
<p>然后打开 gitk 所在的目录</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>open /System/Library/Frameworks/Tk.framework/Versions/Current/Resources/
</code></pre></div></div>
<p>打开 retinizer,并将 Wish.app 拖拽到 retiniaer 的界面,然后点一下那个按钮,然后就 ok 了</p>
<p>下面是带不带<code class="language-plaintext highlighter-rouge">--first-parent</code>的区别</p>
<p><img src="/blog/493.png" alt="" /></p>
<p>有人用 git 的 commit 只做了台北的公交线路图,我表示给跪了,感兴趣<a href="http://gugod.org/2009/12/git-graphing/">猛戳这里</a></p>
<h3 id="diff">diff</h3>
<p>diff 命令常用的命令有两个</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git diff file <span class="c">#查看指定文件的差异</span>
git diff <span class="nt">--stat</span> <span class="c">#查看简单的diff结果</span>
</code></pre></div></div>
<p>下图总结了 diff 全部情况</p>
<p><img src="/blog/489.png" alt="" /></p>
<h3 id="常用命令总结">常用命令总结</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>config
<span class="nb">help </span>status log
clone init
add commit
branch checkout diff
merge rebase
fetch pull push remote
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>如果你有任何疑问的话,欢迎留言讨论;如果本系列文章对你有帮助的话,那我很荣幸,别忘了打赏哦,O(∩_∩)O 哈哈~</p>
<p>最后感谢你的阅读,O(∩_∩)O 哈哈~</p>
<h2 id="继续学习">继续学习</h2>
<ul>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-1/">版本控制简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-2/">Git 简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/08/deep-git-3/">Git 内部原理</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-4/">Git 基础</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-5/">Git 进阶</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-6/">Git 开发流程</a></li>
<li><a href="http://yanhaijing.com/git/2014/11/01/my-git-note/">Git 常用命令总结</a></li>
</ul>
起底Git-Git内部原理
2017-02-08T00:00:00+00:00
http://yanhaijing.com/git/2017/02/08/deep-git-3
<p>这是<a href="http://yanhaijing.com/git/2017/01/19/deep-git-0/">起底Git系列</a>的第三篇,本篇我们来介绍一下Git的内部工作原理。</p>
<h2 id="what-is-git">What is Git?</h2>
<p>你可能听说过Git是版本控制系统、微型文件系统、内容寻址系统。Git的底层是一个微型内容寻址系统Git在1.5版本是一个分水岭,1.5之前更偏向文件系统,1.5版本后更偏向版本控制系统</p>
<p><img src="/blog/463.png" alt="" /></p>
<p>Git常用命令共有30多个,可运行<code class="language-plaintext highlighter-rouge">git help</code>查看;但Git总共有130多个命令,可以通过<code class="language-plaintext highlighter-rouge">git help -a</code>查看,这些命令可以分为高层命令和底层命令,底层命令被设计成unix风格,不常用</p>
<p><img src="/blog/464.png" alt="" /></p>
<p>Git仓库下有一个.git目录,里面存储了git全部的秘密,一般包括下面的内容:</p>
<ul>
<li>config</li>
<li>index</li>
<li>HEAD</li>
<li>hooks/</li>
<li>logs/</li>
<li>refs/</li>
<li>objects/</li>
</ul>
<p>下面会详细介绍没个部分都是什么</p>
<h2 id="config">config</h2>
<p>config是仓库的配置文件,一个典型的配置文件如下,我们创建的远端,分支都在配置文件里有表现;
fetch操作的行为也是在这里配置的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
[remote "origin"]
url = git@github.com:yanhaijing/zepto.fullpage.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
[branch "dev"]
remote = origin
merge = refs/heads/dev
</code></pre></div></div>
<h2 id="objects">objects</h2>
<p>git通过一种算法可以得到任意文件的指纹(40位16进制数字),然后通过文件指纹存取数据,存取的数据都位于objects目录</p>
<p><img src="/blog/465.png" alt="" /></p>
<p>objects目录下有3种类型的数据:</p>
<ul>
<li>Blob</li>
<li>Tree</li>
<li>Commit</li>
</ul>
<p>文件都被存储为blob类型的文件,可以通过内部命令<code class="language-plaintext highlighter-rouge">hash-object</code>写入数据</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4
</code></pre></div></div>
<p>然后通过<code class="language-plaintext highlighter-rouge">cat-file</code>取出数据</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content
</code></pre></div></div>
<p>文件夹被存储为tree类型的文件,文件内容如下所示</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0
100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b simplegit.rb
</code></pre></div></div>
<p>一般我们系统中的目录,在git中会像下面这样存储</p>
<p><img src="/blog/466.png" alt="" /></p>
<p>创建的提交节点被存储为commit类型数据,commit文件的内容如下</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ git cat-file -p fdf4fc3
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author Scott Chacon <schacon@gmail.com> 1243040974 -0700
committer Scott Chacon <schacon@gmail.com> 1243040974 -0700
first commit
</code></pre></div></div>
<p>有三个提交的Git仓库可简化为下图所示</p>
<p><img src="/blog/467.png" alt="" /></p>
<h2 id="refs">refs</h2>
<p>refs目录存储都是引用文件,如本地分支,远端分支,标签等</p>
<ul>
<li>refs/heads/xxx 本地分支</li>
<li>refs/remotes/origin/xxx 远端分支</li>
<li>refs/tags/xxx 本地tag</li>
</ul>
<p>引用文件的内容都是40位commit</p>
<p><img src="/blog/470.png" alt="" /></p>
<p>上面只有提交的图补上分支后,如下所示</p>
<p><img src="/blog/468.png" alt="" /></p>
<h2 id="head">HEAD</h2>
<p>HEAD文件存储的是当前所在的位置,其内容可以使分支名字,40位commit ID</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cat HEAD
refs/heads/master
</code></pre></div></div>
<p>上面的图补上HEAD后,如下所示:</p>
<p><img src="/blog/469.png" alt="" /></p>
<h2 id="总结">总结</h2>
<p>如果你有任何疑问的话,欢迎留言讨论;如果本系列文章对你有帮助的话,那我很荣幸,别忘了打赏哦,O(∩_∩)O哈哈~</p>
<p>最后感谢你的阅读,O(∩_∩)O哈哈~</p>
<h2 id="继续学习">继续学习</h2>
<ul>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-1/">版本控制简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-2/">Git简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/08/deep-git-3/">Git内部原理</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-4/">Git基础</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-5/">Git进阶</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-6/">Git开发流程</a></li>
<li><a href="http://yanhaijing.com/git/2014/11/01/my-git-note/">Git常用命令总结</a></li>
</ul>
起底Git-Git简史
2017-01-19T00:00:00+00:00
http://yanhaijing.com/git/2017/01/19/deep-git-2
<p>这是<a href="http://yanhaijing.com/git/2017/01/19/deep-git-0/">起底Git系列</a>的第二篇,本篇我们来介绍一下Git的历史。</p>
<blockquote>
<p>温故而知新可以为师矣 —— 孔子</p>
</blockquote>
<h2 id="git之父">Git之父</h2>
<p>提到Git就绕不开一个人,那就是<a href="https://github.com/torvalds">linus</a>,用linus的话说他这辈子就做成了两件事,创造了linux和Git,神一样存在,linus也经常爆出来一些语录</p>
<p>广告时间,如果你是一个有追求的人,我建议你看下这本书——《<a href="https://www.amazon.cn/gp/product/B00MB51SAI/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B00MB51SAI&linkCode=as2&tag=yanhaijing-23">只是为了好玩:Linux之父林纳斯自传</a>》,里面也提到了Git的诞生</p>
<p><a href="https://www.amazon.cn/gp/product/B00MB51SAI/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B00MB51SAI&linkCode=as2&tag=yanhaijing-23"><img src="/blog/459.jpg" alt="" /></a></p>
<h2 id="git诞生记">Git诞生记</h2>
<p>这是一个传奇故事,时间定格到2005年,当时linux再使用一个叫做BitKeeper的版本控制工具,BitKeeper开发商在这一年决定不再免费提供给linux社区使用,linus当即伸出了中指,于是两周后Git诞生了</p>
<p><img src="/blog/460.png" alt="" /></p>
<p>Git经过10多年的发展,内部设计基本没变化,而这一切都是linus用两周时间创造出来的</p>
<h2 id="git使命">Git使命</h2>
<p>Git在设计之初就是为了搞定linux内核这种巨无霸而设计的,所以制定了自己的使命</p>
<ul>
<li>速度</li>
<li>简单的设计</li>
<li>对非线性开发模式的强力支持(允许成千上万个并行开发的分支)</li>
<li>完全分布式</li>
<li>有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)</li>
</ul>
<h2 id="git优点">Git优点</h2>
<p>Git作为分布式版本控制的代表,其优点不言而喻</p>
<ul>
<li>快</li>
<li>本地仓库</li>
<li>轻量级分支</li>
<li>分布式</li>
<li>各种工作流</li>
</ul>
<h2 id="谁在使用">谁在使用</h2>
<p>经过10多年的发展,目前绝大部分开源项目都在使用Git,目前Git已经处于霸主地位</p>
<ul>
<li>Git</li>
<li>Linux Kernel</li>
<li>Eclipse</li>
<li>Perl</li>
<li>Android</li>
<li>TypeScript</li>
<li>webkit</li>
</ul>
<h2 id="存储方式">存储方式</h2>
<p>世界上的版本控制总共有两种存储方式,一种是存储差异,另一种是存储快照</p>
<p>存储差异:存储base文件,以后每次存储base文件的更改,SVN就是这种方式</p>
<p><img src="/blog/461.png" alt="" /></p>
<p>存储快照:每次更改都存储一个新文件,Git是这种方式</p>
<p><img src="/blog/462.png" alt="" /></p>
<h2 id="总结">总结</h2>
<p>如果你有任何疑问的话,欢迎留言讨论;如果本系列文章对你有帮助的话,那我很荣幸,别忘了打赏哦,O(∩_∩)O哈哈~</p>
<p>最后感谢你的阅读,O(∩_∩)O哈哈~</p>
<h2 id="继续学习">继续学习</h2>
<ul>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-1/">版本控制简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-2/">Git简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/08/deep-git-3/">Git内部原理</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-4/">Git基础</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-5/">Git进阶</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-6/">Git开发流程</a></li>
<li><a href="http://yanhaijing.com/git/2014/11/01/my-git-note/">Git常用命令总结</a></li>
</ul>
起底Git-版本控制简史
2017-01-19T00:00:00+00:00
http://yanhaijing.com/git/2017/01/19/deep-git-1
<p>这是<a href="http://yanhaijing.com/git/2017/01/19/deep-git-0/">起底Git系列</a>的第一篇,本篇先来介绍一下版本控制的历史。</p>
<blockquote>
<p>以史为鉴可以明得失 —— 李世民</p>
</blockquote>
<h2 id="解决的问题">解决的问题</h2>
<p>我们在开发中可能会有下面的需求,亟待解决,而这正是版本控制所做的事</p>
<ul>
<li>回到过去</li>
<li>改变历史</li>
<li>古今对比</li>
<li>并行开发</li>
<li>谁动了我的代码</li>
</ul>
<p>如果用一张图来总结的话,我想就是下面这张</p>
<p><img src="/blog/454.jpg" alt="" /></p>
<h2 id="分类">分类</h2>
<p>版本控制大致可以下面三类,其每一类都是一个时代的产物,按照历史规律,新生事物总会取代陈旧事物,现在处在DVCS时代,但你可能还在陈旧的公司代码见过CVCS,我们很幸运生活在这个时代</p>
<ul>
<li>LVCS(Local VCS)</li>
<li>CVCS(Central VCS)</li>
<li>DVCS(Distributed VCS)</li>
</ul>
<p>如果你不知道上面三个分类的意思,那就往下看吧,下面聊一聊每一个的历史</p>
<h2 id="史前时代">史前时代</h2>
<p>不说话,写过论文的应该都懂下面这张图,在没有版本控制工具之前,聪明的我们通过手动复制解决</p>
<p><img src="/blog/455.jpg" alt="" /></p>
<h2 id="lvcslocal-vcs">LVCS(Local VCS)</h2>
<p>本地版本控制原理很简单,就是在本地建一个仓库,记录每一个版本和版本之间的关系,这就解决了我们上面手动维护的尴尬</p>
<p>这个时代的代表是rcs,自行百度吧</p>
<p>但本地版本控制有一个很大的问题,就是没法多人协作</p>
<p><img src="/blog/456.png" alt="" /></p>
<h2 id="cvcscentral-vcs">CVCS(Central VCS)</h2>
<p>为了解决写作的问题,聪明的程序员想了个办法,大家共用一个仓库over了吗,于是乎把仓库搬到了远端机器,大家都访问同一个远程仓库,这种模式就成为中心仓库模式</p>
<p>这个时代的代表工具就是SVN,这也是大部分公司还在用的工具</p>
<p>但这种模式有一个致命的缺点,就是中心的单点,如果服务器恰好坏了,那代码全部丢失,所以大公司一般都有很好的容灾机制</p>
<p><img src="/blog/457.png" alt="" /></p>
<h2 id="dvcsdistributed-vcs">DVCS(Distributed VCS)</h2>
<p>随着开源运动的爆发,中心仓库模式很难适应全球多人写作的模式,于是乎分布式版本控制诞生了</p>
<p>DVCS结合了LVCS和CVCS两者的优点,本地仓库让一切都在本地,同时分布式的设计又让每一个节点都能成为远端</p>
<p>DVCS的缺点也是不容忽视的,本地仓库会导致首次clone变慢,其学习曲线优点陡峭(相对而言)</p>
<p><img src="/blog/458.png" alt="" /></p>
<h2 id="总结">总结</h2>
<p>如果你有任何疑问的话,欢迎留言讨论;如果本系列文章对你有帮助的话,那我很荣幸,别忘了打赏哦,O(∩_∩)O哈哈~</p>
<p>最后感谢你的阅读,O(∩_∩)O哈哈~</p>
<h2 id="继续学习">继续学习</h2>
<ul>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-1/">版本控制简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-2/">Git简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/08/deep-git-3/">Git内部原理</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-4/">Git基础</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-5/">Git进阶</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-6/">Git开发流程</a></li>
<li><a href="http://yanhaijing.com/git/2014/11/01/my-git-note/">Git常用命令总结</a></li>
</ul>
起底Git-开篇
2017-01-19T00:00:00+00:00
http://yanhaijing.com/git/2017/01/19/deep-git-0
<p>最近整理了一份git的分享,从如下六个方面对git进行了讲解,我在内部试着讲了一下效果还不错,
感兴趣的可以约我进行团队培训(价格不菲),建议大家自学。</p>
<p>在开始阅读下面的文章之前,我强烈建议你先看完本文的内容。</p>
<ul>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-1/">版本控制简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/01/19/deep-git-2/">Git简史</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/08/deep-git-3/">Git内部原理</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-4/">Git基础</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-5/">Git进阶</a></li>
<li><a href="http://yanhaijing.com/git/2017/02/09/deep-git-6/">Git开发流程</a></li>
<li><a href="http://yanhaijing.com/git/2014/11/01/my-git-note/">Git常用命令总结</a></li>
</ul>
<p>作为一个git老司机,终于可以贡献一下了,也算是功德圆满了。</p>
<p>我在2013年1月份开始使用git,算起来也刚好4年了,下面是我的<a href="https://github.com/yanhaijing/">github</a>截图,还没follow的同学要抓紧了。</p>
<p><img src="/blog/450.png" alt="" /></p>
<h2 id="读音">读音</h2>
<p>先来考大家一个问题,Git的读音是什么?!!!正确发音如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[ɡɪt]
</code></pre></div></div>
<p>那linux呢!!!,聪明的你知道我为什么把这两个单词放一起吗?</p>
<h2 id="冰山一角">冰山一角</h2>
<p>学习一个新事物会经历,不了解,入门,“精通”,随着会得越多会发现不会的也越来越多,学习Git也是类似的。</p>
<p>开始我觉得我掌握了常用的30几个命令后,我觉得我学会Git了,但是后来当我发现还有100多个不常用命令时我的世界观崩塌了,再后来我发现几乎每个命令都有几十个,甚至上百个可选项的时候,我再也不会说自己精通Git了,也期望你不要走入误区,够用就好,用到的时候在学,学习本质,而不是表面</p>
<p>如果将Git比作下面黄色的圆的话,这个系列所讲的知识,可能只有绿色的圆那么大,这只是冰山一角,还有更多的知识等待着你去探索,学完本系列只是开始而不是结束,加油吧少年。</p>
<p><img src="/blog/451.png" alt="" /></p>
<h2 id="推荐一本书">推荐一本书</h2>
<p>一定有些同学是想我一样爱好阅读的,非要我推荐一本书,学习Git我只推荐一本书,那就是<a href="https://git-scm.com/book/zh/v2">ProGit</a>,目前这本书已经是第二版了,说起这本书,我前前后后读了有4遍,如果你想系统学习Git的话我是强烈建议你阅读的。</p>
<p><img src="/blog/452.png" alt="" /></p>
<h2 id="gui-vs-cli">GUI vs CLI</h2>
<p>在开始之前我要先把这个问题说清楚,这其实是一个哲学问题,各自有各自的优缺点,也都有各自擅长的方面</p>
<p>在SVN时代,我们其实是被GUI惯坏了,我相信大部分人都不怎么了解svn原理,也不清楚svn命令行如何使用o(╯□╰)o</p>
<p>但是我想告诉你Git不是这样的,你必须学习一点Git的基础知识,也必须使用命令行,因为Git就是为命令行设计的,任何GUI都是命令行的一个子集,
所以从开始就是用命令行,除了两个例外(log tree 和 diff tool)</p>
<p><img src="/blog/453.png" alt="" /></p>
<h2 id="其他推荐资料">其他推荐资料</h2>
<p>下面针对不同人群,推荐了不同类型的学习资料,对号入座吧;其中大部分也是本系列的参考文献。</p>
<ul>
<li><a href="http://rogerdudler.github.io/git-guide/index.zh.html">Git简明指南</a>—— 快速入门</li>
<li><a href="http://marklodato.github.io/visual-git-guide/index-zh-cn.html">图解Git</a> —— 搞清原理</li>
<li><a href="https://git-scm.com/book/zh/v2">ProGit</a> ——系统,进阶</li>
<li><a href="http://www.slideshare.net/ihower/git-tutorial-13695342">一个台湾小哥的PPT</a>—— 一篇很棒的ppt</li>
</ul>
<h2 id="总结">总结</h2>
<p>如果你有任何疑问的话,欢迎留言讨论;如果本系列文章对你有帮助的话,那我很荣幸,别忘了打赏哦,O(∩_∩)O哈哈~</p>
<p>最后感谢你的阅读,新年快乐O(∩_∩)O~~</p>
Nodejs实战——实现一个资源分析系统
2016-11-11T00:00:00+00:00
http://yanhaijing.com/nodejs/2016/11/11/some-practice-of-nodejs
<p>最近经验要迁移到https,需要排查页面中需要用到的全部资源,我用nodejs+phantomjs做了一个自动收集页面资源的程序,本文分享下自己的一点经验,注意文章结尾有干货</p>
<p>如果全文只能说一句话,那我想说的是,“一定要打破自身局限性,要走出舒适区,身为前端不能被前端局限住视野”,前端虽美,可不要贪杯哦</p>
<p><img src="/blog/506.jpg" alt="" /></p>
<h2 id="调研">调研</h2>
<p>我最先想到的方案就是手动收集依赖,但这种方式明显不可取,但在一番曲折之后我发现了<a href="http://www.softwareishard.com/blog/har-12-spec/">HAR</a>这种神奇的东西,一句话概括,HAR是一种数据格式,对人家还有专门的组织定义规范了,起作用是定义网络请求的数据规范,以便在不同工具中共享数据</p>
<p><img src="/blog/508.jpg" alt="" /></p>
<p>感兴趣的同学可以自己导出数据看下格式,上面从chrome面板导出数据,让后放到<a href="http://www.softwareishard.com/har/viewer/">HAR Viewer</a>工具就可以还原了,似不似很神奇,O(∩_∩)O哈哈~</p>
<p><img src="/blog/509.jpg" alt="" /></p>
<p><em>小技巧:</em>如果想收集一个页面依赖了哪些域名,可以这样操作</p>
<p><img src="/blog/507.jpg" alt="" /></p>
<p>目标是自动化,我打算用node来写逻辑,一番调研后发现phantomjs是一个无头浏览器,可以被node调用,真个程序的架构就清晰了,涉及node,phantomjs和HAR</p>
<p><img src="/blog/510.jpg" alt="" /></p>
<p>理想很丰满,现实很骨感,我其实没用过phantomjs,也没写过node,o(╯□╰)o</p>
<h2 id="phantomjs">phantomjs</h2>
<p>phantomjs是一个没有界面的,可通过JavaScript api编程的Webkit,快速并且原生支持多种web标准:DOM处理,CSS选择器,JSON,Canvas和SVG,这是官网对phantomjs的介绍</p>
<p>phantomjs的整体架构如下,我们可以调用phantomjs暴漏的api,从而实现自己想要的功能</p>
<p><img src="/blog/512.jpg" alt="" /></p>
<p>phantomjs的主要功能有4大块,而网络监控正是我需要的</p>
<ul>
<li>headless website testing 无界面网站测试</li>
<li>screen capture 页面截图</li>
<li>page automation 页面自动化测试</li>
<li>network monitoring 网络监控</li>
</ul>
<p>网络上关于phantom的教程要么停留在v1,从v2开始phantom有了独立的可执行程序,在windows下就是exe,需要单独安装,可以在<a href="http://phantomjs.org/download.html">这里下载</a>对应的安装程序,安装好后还需添加到path路径,在node中也会调用这个可执行程序</p>
<p>在命令行输入<code class="language-plaintext highlighter-rouge">phantomjs -v</code>,验证是否安装成功</p>
<p><img src="/blog/513.png" alt="" /></p>
<p>新建一个test.js</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var page = require('webpage').create();
page.open('http://example.com', function(status) {
console.log("Status: " + status);
if(status === "success") {
page.render('example.png');
}
phantom.exit();
});
</code></pre></div></div>
<p>然后用phantom运行这个js,就可以得到对应页面的截图,是不是很简单,注意这个test.js是用phantom运行的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>phantomjs ./test.js
</code></pre></div></div>
<h2 id="nodejs">nodejs</h2>
<p>Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,其架构如下</p>
<p><img src="/blog/514.jpg" alt="" /></p>
<p>作为一个没怎么用过node的前端老司机,我给前端的建议是不要怕,node其实并不难,或者说用node完成自己想做的事情并不难</p>
<p>学习node必须先了解这四个基本概念,commonjs、package、npm、package.json</p>
<p>由于js本身缺少模块的功能(es6已经有了原生支持),为解决这个问题,社区创造了commonjs,node中的模块都是commonjs模块,一个模块可以暴漏接口供其他模块使用,也可以引用其他模块暴漏的接口</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var q = require('q'); // 依赖模块q
exoprts.a = q; // 暴漏给其他模块使用
</code></pre></div></div>
<p>有时候我们会想吧几个js文件给别人,让别人使用,这是package的概念,package是一堆功能类似的模块的集合,每个package的根目录都有一个package.json文件,用来记录一些自己的信息,比如名字,版本号等</p>
<p>我们自己的node项目也会有一个package.json文件,主要是用来记录依赖了哪些第三方包</p>
<p>node的开源包一般都在npm这个社区上,npm同时可以安装包的一个命令行工具,比如我们再npm网站上找到了<code class="language-plaintext highlighter-rouge">bootstrap</code>这个包,就可以在命令行用npm安装这个包,安装好后,可以将这个依赖写到package.json,方便以后安装</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install bootstrap
</code></pre></div></div>
<p>node作为一个有了对系统完整操作的功能,所有有众多内置模块,带星号的是我用到的模块,看到这么多不要慌,你可能只会用到20%</p>
<ul>
<li>Buffer 二进制文件相关</li>
<li>*Child Processes 处理子进程的模块</li>
<li>*Console 控制台输出</li>
<li>Events 事件系统</li>
<li>*File System 文件系统</li>
<li>*Globals 全局变量</li>
<li>HTTP http请求相关</li>
<li>Utilites 内置工具函数</li>
<li>*URI URL相关操作</li>
<li>Stream 文件流相关</li>
<li>Query String</li>
<li>*Process 当前进程相关</li>
<li>*Path 文件路径相关</li>
<li>Modules 模块相关</li>
</ul>
<h2 id="技术细节">技术细节</h2>
<p>为了可扩展性,我将整个系统设计成四个部分,配置系统,获取HAR系统,解析系统,可视化系统</p>
<p><img src="/blog/515.png" alt="" /></p>
<p><code class="language-plaintext highlighter-rouge">config.js</code>是配置文件,我将配置做成网站加页面的维度,这样就可以统计多个网站的多个页面了</p>
<p><code class="language-plaintext highlighter-rouge">get-website.js</code>读取<code class="language-plaintext highlighter-rouge">config.js</code>,然后解析成网站和对应的页面</p>
<p><code class="language-plaintext highlighter-rouge">get-page.js</code>里是获取每个页面网络数据,因为要执行子进程,所以要用到<code class="language-plaintext highlighter-rouge">execFile</code></p>
<p><code class="language-plaintext highlighter-rouge">parse-website.js</code>负责将数据按照页面,网站收集,分类,整理</p>
<p><code class="language-plaintext highlighter-rouge">print-website.js</code>负责将数据打印输出,涉及文件读写</p>
<p>整个程序用到了<code class="language-plaintext highlighter-rouge">child_process</code>,调用phantom子进程</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>childProcess.execFile(binPath, childArgs, {maxBuffer: 1000*1024}, function(err, stdout, stderr) {
if (err) {
reject(stdout || stderr || err);
return;
}
// handle results
resolve(stdout);
});
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">process</code>获取当前进程的信息,如获取当前程序的路径,环境变量</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>process.cwd()
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">path</code>路径相关操作,如拼接读写文件的路径</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var path = require('path')
path.join(__dirname, 'phantomjs-script.js')
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">fs</code>读写文件,如将最终数据写入文件</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fs.writeFile(filepath, content, function (err) {
if (err) {
reject(err);
return;
}
resolve(name);
});
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>整个系统可以实现对配置文件中的页面依赖的资源进行收集整理和分析,终于把手工的工作变成了自动的,作为一个程序员一定要有自动化的意识,重复的工作让机器来做</p>
<p>这种脚本类型的任务,node的异步会让程序更复杂,o(╯□╰)o</p>
<p>如果然我推荐一篇学习nodejs的文章的话,那我推荐《<a href="http://nqdeng.github.io/7-days-nodejs/">七天学会nodejs</a>》,如果让我推荐一本书的话,我推荐《<a href="https://www.amazon.cn/gp/product/B00K4RUZHW/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B00K4RUZHW&linkCode=as2&tag=yanhaijing-23">nodejs实战</a>》</p>
调研webp图片格式
2016-10-19T00:00:00+00:00
http://yanhaijing.com/web/2016/10/19/study-webp
<p>最近在调研为经验引入webp的可能性,下面总结一下最近的调研成果。</p>
<h2 id="简介">简介</h2>
<p>webp是谷歌在2010年开源的一种新的图片格式,目前在谷歌浏览器(9+)和安卓(4.0+)里面都有很好的兼容性。</p>
<p>根据谷歌官方给出的数据,无损压缩webp图片比png图片小26%,有损压缩的webp可以比jpeg小25-34%,下面测试案例中有具体的demo,大家可以亲眼查看效果。</p>
<p>鉴于webp的优点,推荐引入webp格式。</p>
<p><strong>注意:webp格式会增加解码时间,是png的4-5倍(毫秒级)</strong></p>
<p>谷歌官方给了几种向后兼容使用webp的方案:</p>
<ul>
<li>服务器端检测</li>
<li>js监测</li>
<li>html5 picture</li>
</ul>
<h2 id="pc端方案">PC端方案</h2>
<p>通过<a href="http://caniuse.com/#search=webp">caniuse</a>,我们可以看到opera(11+)和chrome(9+)支持webp格式,但其他浏览器几乎全军覆没。</p>
<p><img src="/blog/443.png" alt="" /></p>
<p>通过百度流量研究院给出的<a href="http://tongji.baidu.com/data/browser">浏览器数据</a>看,chrome占比非常高,你应该根据自己网站的流量统计作出具体决定。</p>
<p><img src="/blog/445.png" alt="" /></p>
<p>pc端建议的方案是html5的<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/picture">picture</a>,<a href="http://caniuse.com/#search=picture">caniuse</a>显示picture和webp兼容性(在chrome上)几乎是一样的。</p>
<p><img src="/blog/444.png" alt="" /></p>
<p>一个picture的简单demo如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><picture>
<source srcset="xxx.webp" type="image/webp">
<img src="xxx.png" alt="test">
</picture>
</code></pre></div></div>
<h2 id="移动端方案">移动端方案</h2>
<p>webp在安卓上的兼容性非常好,但ios完全不支持,<a href="http://caniuse.com/#search=webp">caniuse</a>显示兼容安卓4.0以上版本</p>
<p><img src="/blog/446.png" alt="" /></p>
<p>通过百度统计出来的流量看,移动端安卓占比为70%+,你应该根据自己网站的流量统计作出具体决定。</p>
<p><img src="/blog/447.png" alt="" /></p>
<p>由于picture在移动端兼容性不好,所以这种方案不太可取,可以采用js监测方案,js方案要求图片必须是异步加载,下面是谷歌官方给出的代码示例</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// check_webp_feature:
// 'feature' can be one of 'lossy', 'lossless', 'alpha' or 'animation'.
// 'callback(feature, result)' will be passed back the detection result (in an asynchronous way!)
function check_webp_feature(feature, callback) {
var kTestImages = {
lossy: "UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA",
lossless: "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==",
alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",
animation: "UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"
};
var img = new Image();
img.onload = function () {
var result = (img.width > 0) && (img.height > 0);
callback(feature, result);
};
img.onerror = function () {
callback(feature, false);
};
img.src = "data:image/webp;base64," + kTestImages[feature];
}
</code></pre></div></div>
<p>目前是基于回调,可以封装成promise。</p>
<p>我写了一组测试的demo,可以看这里:https://github.com/yanhaijing/webp</p>
<h2 id="相关工具">相关工具</h2>
<ul>
<li><a href="https://isparta.github.io/">iSparta 图片压缩与格式转换工具</a></li>
<li><a href="http://zhitu.isux.us/">智图 高效优质的图片优化平台</a></li>
</ul>
<h2 id="测试案例">测试案例</h2>
<ul>
<li><a href="https://developers.google.com/speed/webp/gallery">官方demo</a></li>
<li><a href="https://isparta.github.io/compare-webp/index.html#12345">isparta静态图片</a></li>
<li><a href="https://isparta.github.io/compare-webp/index_a.html#12">isparta动图</a></li>
</ul>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="https://developers.google.com/speed/webp/">webp官网</a></li>
<li><a href="https://isux.tencent.com/introduction-of-webp.html">WebP 探寻之路</a></li>
<li><a href="http://zhitu.isux.us/index.php/preview/webp">WebP使用方案</a></li>
<li><a href="https://davidwalsh.name/webp-images-performance">WebP Images & Performance</a></li>
</ul>
详解函数参数和arguments的区别
2016-10-08T00:00:00+00:00
http://yanhaijing.com/javascript/2016/10/08/diff-between-param-and-arguments
<p>最近我在看《<a href="https://www.amazon.cn/gp/product/B01LMYXGAI/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B01LMYXGAI&linkCode=as2&tag=yanhaijing-23">你不知道的JavaScript</a>》中卷,在这里我也强烈建议你也读一读这本书,其中提到了一个函数参数和arguments的区别,在此做一个记录。</p>
<p>本文将用代码来说明两者的区别。</p>
<h2 id="准备工作">准备工作</h2>
<p>如果你不知道arguments是什么,我建议你看看<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments">mdn的介绍</a>。</p>
<p>ES5引入了<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode">严格模式</a>,在严格模式中改变了arguments和函数参数之间的关系,本文将分为两种情况分别说明。</p>
<h2 id="一般模式">一般模式</h2>
<p>直接上代码,先来看调用时缺省参数的情况</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function a1(x) {
x = 2;
console.log(x, arguments[0]);
}
a1(); // 2 undefined
function a2(x) {
arguments[0] = 2;
console.log(x, arguments[0]);
}
a2(); // undefined 2
</code></pre></div></div>
<p>再来看调用时传入参数的情况</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function a3(x) {
x = 2;
console.log(x, arguments[0]);
}
a3(1); // 2 2
function a4(x) {
arguments[0] = 2;
console.log(x, arguments[0]);
}
a4(1); // 2 2
</code></pre></div></div>
<p>可以看到如果缺省参数,arguments和参数是隔离开的;如果传入参数,arguments和参数是双向绑定的。</p>
<h2 id="严格模式">严格模式</h2>
<p>再来看看严格模式,直接上代码</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function b1(x) {
'use strict';
x = 2;
console.log(x, arguments[0]);
}
b1(); // 2 undefined
function b2(x) {
'use strict';
arguments[0] = 2;
console.log(x, arguments[0]);
}
b2(); // undefined 2
function b3(x) {
'use strict';
x = 2;
console.log(x, arguments[0]);
}
b3(1); // 2 1
function b4(x) {
'use strict';
arguments[0] = 2;
console.log(x, arguments[0]);
}
b4(1); // 1 2
</code></pre></div></div>
<p>在严格模式下,无论参数是否缺省,arguments和参数都是隔离开的。</p>
<h2 id="总结">总结</h2>
<p>在严格和非严格模式下行为会有不同,大家小心别踩坑,祝好。</p>
分享一套校招前端笔试题
2016-09-14T00:00:00+00:00
http://yanhaijing.com/web/2016/09/14/a-fe-question-of-toutiao
<p>我说我去面试了,你信不信?这是头条的校招前端笔试题,如果侵权了,请联系我。</p>
<h2 id="html">HTML</h2>
<ol>
<li>
<p>针对移动浏览器端开发页面,不期望用户放大屏幕,且要求“视口(viewport)”宽度等于屏幕宽度,视口高度等于设备高度,如何设置?</p>
</li>
<li>
<p>data-xxx 属性的作用是什么?</p>
</li>
<li>
<p>请描述一下cookies,sessionStorage和localStorage的区别?</p>
</li>
<li>
<p>什么时候浏览器的标准模式(standards mode)和怪异模式(quirks mode)</p>
</li>
</ol>
<h2 id="css">CSS</h2>
<ol>
<li>
<p>解释一下box-model:全部属性,各个属性取值类型,范围,计算值方式,负值作用,box-sizing概念。</p>
</li>
<li>
<p>BFC(Block Formatting Context)是什么?有哪些应用?</p>
</li>
<li>
<p>如何要求容器在宽度自由很缩的情况下,A/B/C的宽度始终是1:1:1,如何实现,写出两种方法。</p>
</li>
</ol>
<p><img src="/blog/441.png" alt="" /></p>
<ol>
<li>如图,A若宽高已知,如何实现水平、垂直均相对于父元素居中?若A高度未知呢?</li>
</ol>
<p><img src="/blog/442.png" alt="" /></p>
<h2 id="javascript">JAVASCRIPT</h2>
<ol>
<li>
<p>函数中的arguments是什么?是数组吗?若不是,如何将它转化为真正的数组?</p>
</li>
<li>
<p>列举JavaScript中typeof操作符的可能结果,如何区分:{}和[]类型?</p>
</li>
<li>
<p>Function中的call、apply、bind的区别是什么?请针对每一个写出一个代码示例。</p>
</li>
<li>
<p>使用jQuery,找到id位<code class="language-plaintext highlighter-rouge">selector</code>的select标签中有用<code class="language-plaintext highlighter-rouge">data-target</code>属性为<code class="language-plaintext highlighter-rouge">isme</code>的option的值?</p>
</li>
<li>
<p>请优化下段代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> for (var i = 0; i < document.getElementsByTagName('a').length; i++) {
document.getElementsByTagName('a')[i].onmouseover = function () {
this.style.color = 'red';
}
document.getElementsByTagName('a')[i].onmouseout = function () {
this.style.color = '';
}
}
</code></pre></div> </div>
</li>
</ol>
<h2 id="总结">总结</h2>
<p>整套题比较基础,属于初级工程师水平。</p>
如何做到ES6 free
2016-09-10T00:00:00+00:00
http://yanhaijing.com/javascript/2016/09/10/es6-free
<p>你的打赏,是我写下去的动力!!!待续。。。</p>
经验无线步骤页改版总结
2016-09-07T00:00:00+00:00
http://yanhaijing.com/program/2016/09/07/exp-wap-step
<p>最近对经验无线步骤页进行了改版,今天在组内做了改版总结的分享,本文是这次分享的文字版,大纲如下:</p>
<ul>
<li>简介</li>
<li>架构</li>
<li>新技术</li>
</ul>
<p>我之前写过一个经验详情页的改版分享——《<a href="http://yanhaijing.com/program/2016/04/14/how-to-reconstruct-a-large-historical-project/">如何重构一个大型历史项目——经验详情页改版总结</a>》,很多和上次一样的东西这次就没做分享,有兴趣的同学可以看下(移动端或开模拟器)。</p>
<h2 id="简介">简介</h2>
<p>经验步骤页是经验最重要的页面之一,<a href="http://jingyan.baidu.com/album/fea4511a71963bf7bb912580.html?stepindex=2&stepGroup=E1">点击这里</a>查看效果,页面的截图如下:</p>
<p><img src="/blog/425.png" alt="" /></p>
<p>页面大体可分为: 最上面的bar、经验头部(听语音)、主题内容等,其中主题内容部分可左右和上下滑动,左右滑动会切换到上一步,下一步,同时会更新经验头部。</p>
<p>本次改版总共有两个FE参加,我主要负责页面基础架构,主要逻辑的开发,另一个同学主要负责,最后一页的交互和点击查看大图的功能。这次做的点击查看大图是非常亮点的一个特色,大家有兴趣可以试一下,关于查看大图介绍我等另一位FE写完,再贴到这里。</p>
<h2 id="架构">架构</h2>
<h3 id="一个模块">一个模块</h3>
<p>这次改版需要小流量,新旧版页面需要在线上同时存在一段时间,以前的做法我们都是重新拉一个前端的模块,这次我们是在同一个模块完成的这个工作,新版的内容全部放到了v2路径下。</p>
<p>一个非常小的点是fis打包的配置,为了能够自动把v2和v1的打包文件区分开我们用到了正则的<code class="language-plaintext highlighter-rouge">?!</code>技巧,关于正则我也打算写一篇博文《<a href="http://yanhaijing.com/javascript/2017/08/06/regexp-syntax">详解JavaScript正则表达式</a>》。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pack: {
'xxx/step2.css': [
/\/widget\/v2\/(?!css\/).*\.less/
],
'xxx/step.css': [
/\/widget\/(?!v2\/).*\.less/,
'/widget/**.css'
]
}
</code></pre></div></div>
<h3 id="耦合">耦合</h3>
<p>关于其中耦合的关系,我在之前一篇博文里面做过介绍了,<a href="http://yanhaijing.com/program/2016/09/01/about-coupling/">点这里</a>查看。</p>
<p>在我们项目中,还有一种独特的方式,我称其为消息中心,通过消息中心将模块之间的耦合变成了模块与消息中心的耦合。</p>
<p><img src="/blog/427.png" alt="" /></p>
<p>这种模式的缺点就是可能会消息爆炸,并且消息的订阅是散落在各个模块的,将来只能对消息扩展,而不能删除消息中的信息。</p>
<p>下面举个栗子来说说消息中心和非直接耦合的区别,假设模块A B C D E F 如下图所示,线条代表模块之间的关系。</p>
<p><img src="/blog/428.png" alt="" /></p>
<p>消息中心模式的结构图如下所示,A与B之间的关系解耦成了A与消息中心的关系,但A还是要知道B的消息名称,我称其为消息名耦合,也就是说A对B还是有感知的。</p>
<p><img src="/blog/429.png" alt="" /></p>
<p>主模块模式的结构图如下所示,A与B之间的关系解耦合成A与主模块之间的关系,A完全不知道B的存在,消息中心模式中的消息名耦合被放到了主模块中。</p>
<p><img src="/blog/430.png" alt="" /></p>
<h3 id="fcontext">F.context</h3>
<p>我们项目中有一个全局的数据中心,叫做F.context,全局数据中心对应上面耦合中的数据耦合,属于耦合比较紧的一种模块,我原来对其有偏见,重新认识一下F.context,明确一下F.context应该放一下全局性的数据,并且一次赋值,尽量不要对其值进行修改。</p>
<p>特别注意不要用F.context来代替传参,我们项目中有很多这种反模式。</p>
<p><img src="/blog/434.png" alt="" /></p>
<h2 id="新技术">新技术</h2>
<p>这次改版中我们尝试了引入了三个新技术,包括ES6,postcss,flex,下面分别来介绍介绍这几个技术。</p>
<h3 id="es6">ES6</h3>
<p>在之前我们都是在活动页面尝试使用ES6,在积累了经验后我们决定在这个页面尝试使用。</p>
<p>如果你对ES6不了解,可以查看我之前写的一系列文章:</p>
<ul>
<li><a href="http://yanhaijing.com/javascript/2015/09/04/try-es2015/">快来使用ECMAScript 2015吧</a></li>
<li><a href="http://yanhaijing.com/javascript/2015/09/11/learn-es2015/">ECMAScript 2015 简易教程</a></li>
<li><a href="http://yanhaijing.com/javascript/2015/09/16/es6-promise/">快来使用ES2015的Promise吧</a></li>
<li><a href="http://yanhaijing.com/javascript/2016/04/27/es2015-practice/">ES2015实战——面向未来编程</a></li>
</ul>
<p>关于ES6其实有<a href="http://espadrine.github.io/New-In-A-Spec/es2015/">一系列功能</a>,但这次我们主要用到了三个:module, class, let。</p>
<p>我们的ES6最后都会使用bable编译成ES5来执行,所以我们代码的兼容性就是ES5的兼容,而ES5在wap端<a href="http://kangax.github.io/compat-table/es5/">兼容性</a>非常好。</p>
<p><img src="/blog/435.png" alt="" /></p>
<p>后续我准备写一篇如何用ES5来代替ES6的博文《<a href="/program/2016/09/10/es6-free/">如何做到ES6 free</a>》。</p>
<p>下面上一段步骤页滑动模块的js</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import {UIBase} from 'base.es';
class Slider extends UIBase {
constructor(index) {
super();
this.bindEvent();
}
bindEvent() {}
move(x) {}
}
export {Slider};
</code></pre></div></div>
<h3 id="postcss">postcss</h3>
<p><a href="http://postcss.org/">postcss</a>是最近出来的新东西,学名是css后处理器,也就是处理标准化的css,和sass等预处理器的区别需要区分开,sass生成css,postcss处理标准css。</p>
<p><img src="/blog/436.png" alt="" /></p>
<p>目前已经有很多大公司在用,包括google,Facebook等。</p>
<p>打开postcss的官网,可以看到介绍了4个特色——添加前缀,css变量,命名空间,语法验证。</p>
<p>post是一套css的语法解析工具,可以通过插件来实现不同的功能,其插件可以分为下面这些分类。</p>
<p><img src="/blog/437.png" alt="" /></p>
<p>我们主要使用的是添加前缀功能,在fis中实现这个功能主要有两个插件,<code class="language-plaintext highlighter-rouge">fis-preprocessor-autoprefixer</code>,<code class="language-plaintext highlighter-rouge">fis-postprocessor-autoprefixer</code>,其中推荐使用前一个,这是fis官方维护的插件,在fis中有两个节点可以插入后处理器的功能;fis的编程流程如下,分为单文件编译和打包两个过程。</p>
<p><img src="/blog/438.png" alt="" /></p>
<p><a href="https://github.com/postcss/autoprefixer">autoprefixer</a>有很多配置项</p>
<ul>
<li>Browsers 浏览器列表 默认 > 1%, last 2 versions, Firefox ESR</li>
<li>Cascade 是否级联 默认 true</li>
<li>Add 是否添加前缀 默认true</li>
<li>Remove 是否移除无用的前缀 默认true</li>
<li>Supports 是否适配 @support 默认true</li>
<li>Flexbox 是否支持flex 默认true</li>
<li>Grid 是否支持grid 默认true</li>
<li>Stats 自定义地区浏览器</li>
</ul>
<p>其中最主要的的配置项是第一个,其详细配置项在<a href="https://github.com/ai/browserslist">这里</a>,这里有一个<a href="http://browserl.ist/">在线版</a>,比较直观。</p>
<p>我们的配置数据如下:<code class="language-plaintext highlighter-rouge">'Android >= 4', 'iOS >= 6', 'and_uc > 9'</code>,兼容的浏览器顾名思义。</p>
<p>postcss支持的css3属性列表在<a href="https://github.com/postcss/autoprefixer/wiki/support-list">这里</a>,我们整理出一个常用的子集如下所示:</p>
<p><img src="/blog/439.png" alt="" /></p>
<p><strong>注意:</strong>需要注意的就是postcss并不能解决浏览器不兼容css的问题,而是可以帮我们磨平前缀,所以使用新的css3属性前,还是要先看<a href="http://caniuse.com/">caniuse</a>。在下面的例子中,如果要兼容安卓4.3以下,就不能使用<code class="language-plaintext highlighter-rouge">calc</code>。</p>
<p><img src="/blog/440.png" alt="" /></p>
<h3 id="flex">flex</h3>
<p>在这个项目中我们大规模使用了flex,其中也踩了不少坑,关于flex我打算在另一篇博文里详细介绍——《<a href="http://yanhaijing.com/css/2016/08/21/flex-practice-on-mobile/">移动端flex布局实战经验</a>》。</p>
<h2 id="总结">总结</h2>
<p>最后感谢大家浏览本文,如果你有任何疑问可以在下面留言和我交流。</p>
我的hexo笔记
2016-09-01T00:00:00+00:00
http://yanhaijing.com/linux/2016/09/01/my-hexo
<p>你的打赏,是我写下去的动力!!!待续。。。</p>
一个博客由什么组成
2016-09-01T00:00:00+00:00
http://yanhaijing.com/thinking/2016/09/01/a-blog-need-something
<p>你的打赏,是我写下去的动力!!!待续。。。</p>
图解7种耦合关系
2016-09-01T00:00:00+00:00
http://yanhaijing.com/program/2016/09/01/about-coupling
<p>之前组内同学问我耦合的关系,我没给对方讲清楚,今天借这个机会来深入讲讲模块之间的耦合关系这个事情。</p>
<p>本文将用图文详细讲解七种耦合的不同之处。</p>
<h2 id="高内聚与低耦合">高内聚与低耦合</h2>
<p>高内聚与低耦合是每个软件开发者追求的目标,那么内聚和耦合分别是什么意思呢?</p>
<p><img src="/blog/416.png" alt="" /></p>
<blockquote>
<p>内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。</p>
</blockquote>
<blockquote>
<p>耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。</p>
</blockquote>
<h2 id="耦合">耦合</h2>
<p>不同模块之间的关系就是耦合,根据耦合程度可以分为 7 种,耦合度依次变低。</p>
<ul>
<li>内容耦合</li>
<li>公共耦合</li>
<li>外部耦合</li>
<li>控制耦合</li>
<li>标记耦合</li>
<li>数据耦合</li>
<li>非直接耦合</li>
</ul>
<p>下面我们来说说每种耦合是什么,开始之前先来说下要实现的功能。m1 和 m2 是两个独立的模块,其中 m2 种会显示 m1 的输入,m1 会显示 m2 的输入。</p>
<p><img src="/blog/417.png" alt="" /></p>
<p>很显然,m1 和 m2 两个模块之间会有一些联系(耦合),你也可以想想如何实现这个功能,下面用 7 种不同的方式来实现这个功能。</p>
<p><strong>注:</strong>项目的代码我放到了<a href="https://github.com/yanhaijing/coupling">github</a>,项目的 demo,可以在<a href="http://yanhaijing.com/coupling/">这里查看</a>。</p>
<h3 id="内容耦合">内容耦合</h3>
<p>内容耦合是最紧的耦合程度,一个模块直接访问另一模块的内容,则称这两个模块为内容耦合。</p>
<p><img src="/blog/418.png" alt="" /></p>
<p>为了实现功能,我们将 m1 的输入放到 m2.m1input 上,将 m2 的输入放到 m1.m2input 上。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// m1.js</span>
<span class="nx">root</span><span class="p">.</span><span class="nx">m2</span><span class="p">.</span><span class="nx">m1input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">m2</span><span class="p">.</span><span class="nx">update</span><span class="p">();</span>
<span class="c1">// m2.js</span>
<span class="nx">root</span><span class="p">.</span><span class="nx">m1</span><span class="p">.</span><span class="nx">m2input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">m1</span><span class="p">.</span><span class="nx">update</span><span class="p">();</span>
</code></pre></div></div>
<p><strong>PS:</strong>不知道谁会这么写代码,除了我为了做演示之外。。。</p>
<p>查看<a href="https://github.com/yanhaijing/coupling/tree/gh-pages/demo1">完整代码</a>和<a href="http://yanhaijing.com/coupling/demo1/demo.html">demo</a>。</p>
<h3 id="公共耦合">公共耦合</h3>
<p>一组模块都访问同一个全局数据结构,则称之为公共耦合。</p>
<p><img src="/blog/419.png" alt="" /></p>
<p>在这种 case 中,m1 和 m2 将自己的输入放到全局的 data 上。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// m1.js</span>
<span class="nx">root</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">m1input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">m2</span><span class="p">.</span><span class="nx">update</span><span class="p">();</span>
<span class="c1">// m2.js</span>
<span class="nx">root</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">m2input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">m1</span><span class="p">.</span><span class="nx">update</span><span class="p">();</span>
</code></pre></div></div>
<p>查看<a href="https://github.com/yanhaijing/coupling/tree/gh-pages/demo2">完整代码</a>和<a href="http://yanhaijing.com/coupling/demo2/demo.html">demo</a>。</p>
<h3 id="外部耦合">外部耦合</h3>
<p>一组模块都访问同一全局简单变量,而且不通过参数表传递该全局变量的信息,则称之为外部耦合。外部耦合和公共耦合很像,区别就是一个是简单变量,一个是复杂数据结构。</p>
<p><img src="/blog/420.png" alt="" /></p>
<p>在这种 case 中,m1 和 m2 都将自己的输入放到全局上。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// m1.js</span>
<span class="nx">root</span><span class="p">.</span><span class="nx">m1input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">m2</span><span class="p">.</span><span class="nx">update</span><span class="p">();</span>
<span class="c1">// m2.js</span>
<span class="nx">root</span><span class="p">.</span><span class="nx">m2input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">m1</span><span class="p">.</span><span class="nx">update</span><span class="p">();</span>
</code></pre></div></div>
<p>查看<a href="https://github.com/yanhaijing/coupling/tree/gh-pages/demo3">完整代码</a>和<a href="http://yanhaijing.com/coupling/demo3/demo.html">demo</a>。</p>
<h3 id="控制耦合">控制耦合</h3>
<p>模块之间传递的不是数据信息,而是控制信息例如标志、开关量等,一个模块控制了另一个模块的功能。</p>
<p>从控制耦合开始,模块的数据就放在自己内部了,不同模块之间通过接口互相调用。</p>
<p><img src="/blog/421.png" alt="" /></p>
<p>在这个 case 中,得增加一个需求,就是当 m1 的输入为空时,隐藏 m2 的显示信息。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// m1.js</span>
<span class="nx">root</span><span class="p">.</span><span class="nx">m1input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">m2</span><span class="p">.</span><span class="nx">update</span><span class="p">();</span>
<span class="nx">m2</span><span class="p">.</span><span class="nx">toggle</span><span class="p">(</span><span class="o">!!</span><span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span> <span class="c1">// 传递flag</span>
</code></pre></div></div>
<p>上面的代码中 m1 直接控制了 m2 的显示和隐藏。</p>
<p>查看<a href="https://github.com/yanhaijing/coupling/tree/gh-pages/demo4">完整代码</a>和<a href="http://yanhaijing.com/coupling/demo4/demo.html">demo</a>。</p>
<h3 id="标记耦合">标记耦合</h3>
<p>调用模块和被调用模块之间传递数据结构而不是简单数据,同时也称作特征耦合。</p>
<p><img src="/blog/422.png" alt="" /></p>
<p>在这个 case 中,m1 传给 m2 的是一个对象。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// m1.js</span>
<span class="nx">me</span><span class="p">.</span><span class="nx">m1input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">m2</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">me</span><span class="p">);</span> <span class="c1">// 传递引用</span>
<span class="c1">// m2.js</span>
<span class="nx">me</span><span class="p">.</span><span class="nx">m2input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">m1</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">me</span><span class="p">);</span>
</code></pre></div></div>
<p>查看<a href="https://github.com/yanhaijing/coupling/tree/gh-pages/demo5">完整代码</a>和<a href="http://yanhaijing.com/coupling/demo5/demo.html">demo</a>。</p>
<h3 id="数据耦合">数据耦合</h3>
<p>调用模块和被调用模块之间只传递简单的数据项参数。相当于高级语言中的值传递。</p>
<p><img src="/blog/423.png" alt="" /></p>
<p>在这个 case 中,m1 传给 m2 的是一个简单数据结构。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// m1.js</span>
<span class="nx">me</span><span class="p">.</span><span class="nx">m1input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">m2</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">me</span><span class="p">.</span><span class="nx">m1input</span><span class="p">);</span> <span class="c1">// 传递值</span>
<span class="c1">// m2.js</span>
<span class="nx">me</span><span class="p">.</span><span class="nx">m2input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">m1</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">me</span><span class="p">.</span><span class="nx">m2input</span><span class="p">);</span>
</code></pre></div></div>
<p>查看<a href="https://github.com/yanhaijing/coupling/tree/gh-pages/demo6">完整代码</a>和<a href="http://yanhaijing.com/coupling/demo6/demo.html">demo</a>。</p>
<h3 id="非直接耦合">非直接耦合</h3>
<p>两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。耦合度最弱,模块独立性最强。</p>
<p>子模块无需知道对方的存在,子模块之间的联系,全部变成子模块和主模块之间的联系。</p>
<p><img src="/blog/424.png" alt="" /></p>
<p>在这个 case 种,增加一个 index.js 作为主模块。</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// index.js</span>
<span class="kd">var</span> <span class="nx">m1</span> <span class="o">=</span> <span class="nx">root</span><span class="p">.</span><span class="nx">m1</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">m2</span> <span class="o">=</span> <span class="nx">root</span><span class="p">.</span><span class="nx">m2</span><span class="p">;</span>
<span class="nx">m1</span><span class="p">.</span><span class="nx">init</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">m2</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">str</span><span class="p">);</span>
<span class="p">});</span>
<span class="nx">m2</span><span class="p">.</span><span class="nx">init</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">m1</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">str</span><span class="p">);</span>
<span class="p">});</span>
<span class="c1">// m1.js</span>
<span class="nx">me</span><span class="p">.</span><span class="nx">m1input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">inputcb</span><span class="p">(</span><span class="nx">me</span><span class="p">.</span><span class="nx">m1input</span><span class="p">);</span> <span class="c1">// inputcb是回调函数</span>
<span class="c1">// m2.js</span>
<span class="nx">me</span><span class="p">.</span><span class="nx">m2input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">value</span><span class="p">;</span>
<span class="nx">inputcb</span><span class="p">(</span><span class="nx">me</span><span class="p">.</span><span class="nx">m2input</span><span class="p">);</span>
</code></pre></div></div>
<p>查看<a href="https://github.com/yanhaijing/coupling/tree/gh-pages/demo7">完整代码</a>和<a href="http://yanhaijing.com/coupling/demo7/demo.html">demo</a>。</p>
<h2 id="内聚">内聚</h2>
<p>其实关于内聚也分为很多种,如下所示,如果你感兴趣可以自己研究研究,我们下次再来分享内聚的问题。</p>
<ul>
<li>偶然内聚</li>
<li>逻辑内聚</li>
<li>时间内聚</li>
<li>通信内聚</li>
<li>顺序内聚</li>
<li>功能内聚</li>
</ul>
<h2 id="总结">总结</h2>
<p>希望你看完上面的文章,搞懂了耦合的种类,也希望你以后能使用非直接耦合这种方式来写代码,祝好。</p>
我的vim笔记
2016-09-01T00:00:00+00:00
http://yanhaijing.com/linux/2016/09/01/my-vim
<p>你的打赏,是我写下去的动力!!!待续。。。</p>
<p><a href="http://blog.csdn.net/niushuai666/article/details/7275406">Vim简明教程【CoolShell】</a>
<a href="http://sheet.shiar.nl/vi">vi/vim cheat sheet</a></p>
一个纯前端实现的页面生成工具
2016-08-21T00:00:00+00:00
http://yanhaijing.com/web/2016/08/21/a-pure-front-end-act-template
<p>你的打赏,是我写下去的动力!!!待续。。。</p>
<h2 id="作品展示">作品展示</h2>
<p>记得用手机或者开模拟器哦</p>
<ul>
<li><a href="http://jingyan.baidu.com/zt/fangshai2016/wap/index.html">百度经验:防晒的正确姿势</a></li>
<li><a href="http://jingyan.baidu.com/zt/UVA/index.html">百度经验:专业解密无人机的拍摄飞行技巧</a></li>
<li><a href="http://jingyan.baidu.com/zt/run/index.html">专业跑步教练王晓刚 带你科学跑步_百度经验</a></li>
<li><a href="http://jingyan.baidu.com/zt/oppo/index.html">小欧教你玩转R9</a></li>
<li><a href="http://jingyan.baidu.com/zt/YCW1/index.html">关于狗狗的一生,你必须知道的事</a></li>
<li><a href="http://jingyan.baidu.com/zt/YCWZT2/index.html">宠物,你愿和谁相伴?</a></li>
<li><a href="http://jingyan.baidu.com/zt/PGZC/wap/index.html">苹果在此,我来削皮</a></li>
<li><a href="http://jingyan.baidu.com/zt/kouqiang/wap/index.html">儿童口腔健康指南_百度经验</a></li>
<li><a href="http://jingyan.baidu.com/zt/mate9/wap/index.html">华为Mate 9 进步,更进一步</a></li>
</ul>
<h2 id="三个系统">三个系统</h2>
<h3 id="拼装系统">拼装系统</h3>
<h3 id="预览系统">预览系统</h3>
<h3 id="生成系统">生成系统</h3>
<h2 id="一个已经存在的系统">一个已经存在的系统</h2>
<h2 id="目标">目标</h2>
<h2 id="设计">设计</h2>
<h2 id="实战">实战</h2>
<h2 id="总结">总结</h2>
手把手教你在虚拟机里安装linux
2016-08-21T00:00:00+00:00
http://yanhaijing.com/linux/2016/08/21/how-to-install-linux-on-vm
<p>对于想学习linux的同学,第一步当然是要有一个linux环境,而我建议你在虚拟机里安装,本文将一步步教你如何在虚拟机里安装linux。</p>
<p>整个过程可以分为两步,安装虚拟机和安装linux。</p>
<h2 id="安装虚拟机">安装虚拟机</h2>
<p>我推荐大家使用<a href="https://www.virtualbox.org/">Oracle VM VirtualBox</a>,这是一个跨平台的虚拟机,下载对应的平台安装包,安装过程不在赘述。</p>
<p>(1) VirtualBox的界面如下图所示,点击新建。</p>
<p><img src="/blog/298.png" alt="" /></p>
<p>(2) 然后输入名称,我输入的是centos6.6-2,选择linux系统,并选择redhat版本,点击下一步</p>
<p><img src="/blog/299.png" alt="" /></p>
<p>(3) 这里将会给linux系统分配内存,在不开启图形界面的情况下512M就足够了,如果需要图形界面需要大于1GB内存,这里我选择512M,然后点击下一步</p>
<p>(4) 接下来选择虚拟硬盘,这一步直接点创建即可</p>
<p>(5) 接下来让你选择虚拟硬盘文件类型,这一步保持默认,点击下一步</p>
<p>(6) 接下来存储在物理硬盘上的选项选择动态分配,点击下一步</p>
<p>(7) 接下来可以选择在硬盘上的存储位置,选择好后点击创建</p>
<p><img src="/blog/300.png" alt="" /></p>
<h2 id="安装linux">安装linux</h2>
<p>安装linux之前需要先下载镜像,我推荐centos6.x Minimal版,可以来<a href="https://wiki.centos.org/Download">这里</a>下载。</p>
<p>(1) 接下来就该安装linux了,我们通过虚拟光驱来安装linux,点击设置按钮</p>
<p><img src="/blog/301.png" alt="" /></p>
<p>然后点击左侧的“存储”,选中“没有磁片”,在点击右侧的小光盘图标,然后点击“选择一个虚拟光盘”,然后找到我们上面下载系统镜像文件。</p>
<p><img src="/blog/302.png" alt="" /></p>
<p>(2) 接下来就可以开始安装linux了,点击启动按钮</p>
<p><img src="/blog/303.png" alt="" /></p>
<p>(2) 接下来我们选择第二个安装选项</p>
<p><img src="/blog/304.png" alt="" /></p>
<p>(4) 等待硬件检测完成,询问我们是否检查光盘,选择跳过</p>
<p><img src="/blog/305.png" alt="" /></p>
<p>(5) 接下来问我们安装过程选择什么语言,保持默认下一步,然后选择US</p>
<p><img src="/blog/306.png" alt="" /></p>
<p>(6) 接下来问我们是否格式化磁盘,下面的选项</p>
<p><img src="/blog/307.png" alt="" /></p>
<p>(7) 接下来市选择时区,我们选择上海时区</p>
<p><img src="/blog/308.png" alt="" /></p>
<p>(8) 接下来输入root的密码,这个密码要记住</p>
<p><img src="/blog/309.png" alt="" /></p>
<p>(9) 接下来问我们安装到那里,选择使用整个磁盘</p>
<p><img src="/blog/310.png" alt="" /></p>
<p>这里应该还有一个分区的过程,linux系统分区的原则是:</p>
<ul>
<li>/boot/ 100MB</li>
<li>swap内存大小两倍,不高于16GB</li>
<li>/ 20GB</li>
<li>/data/ 剩余空间</li>
</ul>
<p>(10) 接下来等待几分钟,安装完成后选择reboot</p>
<p><img src="/blog/311.png" alt="" /></p>
<p>(11) 接下来输入用户名密码进入linux系统,至此大功告成</p>
<p><img src="/blog/312.png" alt="" /></p>
<h2 id="总结">总结</h2>
<p>不同的linux版本,不同的centos版本,安装界面可能会不同,但整个过程大同小异,相信你一定会举一反三的。</p>
移动端flex布局实战
2016-08-21T00:00:00+00:00
http://yanhaijing.com/css/2016/08/21/flex-practice-on-mobile
<p>做过移动端的同学都知道移动端布局太难了,终端太多了,传统的布局方式已经力不从心,各种新的布局方式被发明</p>
<p>在flex之前,传统布局有流式布局(就是默认的方式),绝对定位布局,弹性布局(em),和浮动布局,其中浮动布局并不是为布局而设计的,使用起来略显繁琐</p>
<p>2009年,对前端来说是不平凡的一年,html5定稿,es5.1发布,flex应运而生,天生响应式,生而为布局,使用及其简单</p>
<p>但是理想很丰满,现实很骨感,flex三改其规范,浏览器实现不一,各种神坑,本文将总结2017年移动端使用flex的最佳实践和经验</p>
<h2 id="兼容性">兼容性</h2>
<p>2017年9月份,现在来看下flex的<a href="http://caniuse.com/#search=flex">兼容性</a>,可以发现绝大部分都是绿色</p>
<p><img src="/blog/516.png" alt="" /></p>
<p>上图中红色箭头代表我们应该兼容的浏览器情况,在国内,UC和QQ浏览器的份额不容忽视,上图中的 1 2 3 其实代表flex的三版语法,flex有09年版语法,11年版语法和标准语法;右上角带黄色小方块的代表需要添加-webkit-前缀</p>
<p>将上图总结一下,移动端需要的兼容情况如下:</p>
<table>
<thead>
<tr>
<th>安卓</th>
<th>2.1-4.3</th>
<th>4.4-∞</th>
<th>UC 11.4</th>
<th>QQ 1.2-∞</th>
</tr>
</thead>
<tbody>
<tr>
<td>语法版本</td>
<td>09版</td>
<td>标准</td>
<td>09版</td>
<td>标准</td>
</tr>
<tr>
<td>是否前缀</td>
<td>是</td>
<td>否</td>
<td>是</td>
<td>否</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>iOS Safari</th>
<th>3.2-6.1</th>
<th>7.1-8.4</th>
<th>9.2–∞</th>
</tr>
</thead>
<tbody>
<tr>
<td>语法版本</td>
<td>09版</td>
<td>标准</td>
<td>标准</td>
</tr>
<tr>
<td>是否前缀</td>
<td>是</td>
<td>是</td>
<td>否</td>
</tr>
</tbody>
</table>
<p>再来看一下百度给出的移动设备的<a href="https://mtj.baidu.com/data/mobile/device">统计情况</a>,分别是安卓和ios,可以发现现在需要兼容安卓4.1+,IOS7+,这里百度给出的数据,当然你应该根据自己产品的统计情况来确定兼容情况</p>
<p><img src="/blog/517.png" alt="" /></p>
<p><img src="/blog/518.png" alt="" /></p>
<p>总结一下本文各处的最佳实践,兼容性目标是安卓4.1+,IOS7+,UC和qq浏览器</p>
<h2 id="属性对照">属性对照</h2>
<p>通过上面的目标和caniuse,很容易得出我们需要写09和标准两版语法,只有在两版语法中都存在属性才能使用,下面给出两版语法的对照关系,注意这不是语法指南,语法指南请看结尾处的推荐资料</p>
<h3 id="容器的属性">容器的属性</h3>
<p>容器属性包括:</p>
<ul>
<li>
<p>display</p>
</li>
<li>flex-direction</li>
<li>flex-wrap</li>
<li>flex-flow</li>
<li>justify-content</li>
<li>align-items</li>
<li>align-content</li>
</ul>
<p>display</p>
<table>
<thead>
<tr>
<th>标准版</th>
<th>09版</th>
</tr>
</thead>
<tbody>
<tr>
<td>display: flex</td>
<td>display: box</td>
</tr>
<tr>
<td>display: inline-flex</td>
<td>display: inline-box</td>
</tr>
</tbody>
</table>
<p>flex-direction</p>
<table>
<thead>
<tr>
<th>标准版</th>
<th>09版</th>
</tr>
</thead>
<tbody>
<tr>
<td>flex-direction: row</td>
<td>box-orient: horizontal; box-direction: normal</td>
</tr>
<tr>
<td>flex-direction: row-reverse</td>
<td>box-orient: horizontal; box-direction: reverse</td>
</tr>
<tr>
<td>flex-direction: column</td>
<td>box-orient: vertical; box-direction: normal</td>
</tr>
<tr>
<td>flex-direction: column-reverse</td>
<td>box-orient: vertical; box-direction: reverse</td>
</tr>
</tbody>
</table>
<p>flex-wrap</p>
<table>
<thead>
<tr>
<th>标准版</th>
<th>09版</th>
</tr>
</thead>
<tbody>
<tr>
<td>flex-wrap: nowrap</td>
<td>box-lines: single</td>
</tr>
<tr>
<td>flex-wrap: wrap</td>
<td>box-lines: multiple</td>
</tr>
<tr>
<td>flex-wrap: wrap-reverse</td>
<td>无</td>
</tr>
</tbody>
</table>
<p>flex-flow是flex-direction和flex-wrap两个属性的简写,09版无对应属性,09版可以分开写两条属性</p>
<p>justify-content</p>
<table>
<thead>
<tr>
<th>标准版</th>
<th>09版</th>
</tr>
</thead>
<tbody>
<tr>
<td>justify-content: flex-start</td>
<td>box-pack: start</td>
</tr>
<tr>
<td>justify-content: flex-end</td>
<td>box-pack: end</td>
</tr>
<tr>
<td>justify-content: center</td>
<td>box-pack: center</td>
</tr>
<tr>
<td>justify-content: space-between</td>
<td>box-pack: justify</td>
</tr>
<tr>
<td>justify-content: space-around</td>
<td>无</td>
</tr>
</tbody>
</table>
<p>align-items</p>
<table>
<thead>
<tr>
<th>标准版</th>
<th>09版</th>
</tr>
</thead>
<tbody>
<tr>
<td>align-items: flex-start</td>
<td>box-align: start</td>
</tr>
<tr>
<td>align-items: flex-end</td>
<td>box-align: end</td>
</tr>
<tr>
<td>align-items: center</td>
<td>box-align: center</td>
</tr>
<tr>
<td>align-items: baseline</td>
<td>box-align: baseline</td>
</tr>
<tr>
<td>align-items: stretch</td>
<td>box-align: stretch</td>
</tr>
</tbody>
</table>
<p>align-content,09版无对应属性</p>
<h3 id="项目的属性">项目的属性</h3>
<p>项目属性包括:</p>
<ul>
<li>order</li>
<li>flex-grow</li>
<li>flex-shrink</li>
<li>flex-basis</li>
<li>flex</li>
<li>align-self</li>
</ul>
<p>order</p>
<table>
<thead>
<tr>
<th>标准版</th>
<th>09版</th>
</tr>
</thead>
<tbody>
<tr>
<td>order: number</td>
<td>box-ordinal-group: number</td>
</tr>
</tbody>
</table>
<p>flex-grow,09版无对应属性</p>
<p>flex-shrink,09版无对应属性</p>
<p>flex-basis,09版无对应属性</p>
<p>flex,标准版的flex是一个复合属性,09版的box-flex仅支持配置数字</p>
<table>
<thead>
<tr>
<th>标准版</th>
<th>09版</th>
</tr>
</thead>
<tbody>
<tr>
<td>flex: flex-grow flex-shrink flex-basis</td>
<td>box-flex: number</td>
</tr>
</tbody>
</table>
<p>align-self,09版无对应属性</p>
<p><strong>09版的语法对flex项目的可配置功能非常弱,仅能调整顺序和伸缩性</strong></p>
<h2 id="采坑经验">采坑经验</h2>
<p>一般来说只要09版语法有对应功能,就可以使用了,但是移动端还有一些坑,导致某些属性不能用</p>
<p>justify-content: space-around 不能用,旧版语法没有,但是可以用space-between+容器的padding模拟</p>
<p>flex-wrap: wrap 不能用,对应的旧版语法 box-lines: mutiple 大部分浏览器不支持,也就是说不能折行</p>
<p>uc span行内元素作为子项时 display 必须设置为block,最好直接使用块级元素</p>
<h2 id="实战">实战</h2>
<p>说了这么多下面给一份标准的写法,一个flex属性应该这么写</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>webkit前缀09版
webkit前缀标准版
标准版
</code></pre></div></div>
<p>举个例子,display: flex要这么写</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">display</span><span class="o">:</span> <span class="nt">-webkit-box</span><span class="o">;</span>
<span class="nt">display</span><span class="o">:</span> <span class="nt">-webkit-flex</span><span class="o">;</span>
<span class="nt">display</span><span class="o">:</span> <span class="nt">flex</span><span class="o">;</span>
</code></pre></div></div>
<p>一定有同学说这也太麻烦了,有没有啥简单的办法呢?还真有,共有三种办法,感谢前辈</p>
<p>第一种,编辑器插件,有一个叫做autoprefix插件,sublime就可以安装,你只需写标准属性,然后按一下快捷键就能够自动填充前缀属性。这种方法用起来最简单,但这种方法后面会不太好维护,比如有一天不需要09版语法了怎么破???一个一个去改吧,o(╯□╰)o</p>
<p>第二种,预处理器mixin,如果你用过less或者sass的话,一定知道mixin,下面已less 2.x为例,sass大同小异</p>
<pre><code class="language-less">.display-flex(@display: flex) {
& when (@display=flex) {
display: -webkit-box;
}
& when (@display=inline-flex) {
display: -webkit-inline-box;
}
display: e("-webkit-@{display}");
display: @display;
}
.flex-direction(@direction) {
& when (@direction=row) {
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
}
& when (@direction=row-reverse) {
-webkit-box-orient: horizontal;
-webkit-box-direction: reverse;
}
& when (@direction=column) {
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
}
& when (@direction=column-reverse) {
-webkit-box-orient: vertical;
-webkit-box-direction: reverse;
}
-webkit-flex-direction: @direction;
flex-direction: @direction;
}
.flex-wrap(@wrap) {
& when (@wrap=nowrap) {
-webkit-box-lines: single;
}
& when (@wrap=wrap) {
-webkit-box-lines: multiple;
}
-webkit-flex-wrap: @wrap;
flex-wrap: @wrap;
}
.justify-content(@justify-content) {
& when (@justify-content=flex-start) {
-webkit-box-pack: start;
}
& when (@justify-content=flex-end) {
-webkit-box-pack: end;
}
& when (@justify-content=center) {
-webkit-box-pack: center;
}
& when (@justify-content=space-between) {
-webkit-box-pack: justify;
}
-webkit-justify-content: @justify-content;
justify-content: @justify-content;
}
.align-items(@align-items) {
& when (@align-items=flex-start) {
-webkit-box-align: start;
}
& when (@align-items=flex-end) {
-webkit-box-align: end;
}
& when (@align-items=center) {
-webkit-box-align: center;
}
& when (@align-items=baseline) {
-webkit-box-align: baseline;
}
& when (@align-items=stretch) {
-webkit-box-align: stretch;
}
-webkit-align-items: @align-items;
align-items: @align-items;
}
.order(@order) {
-webkit-box-ordinal-group: @order;
-webkit-order: @order;
order: @order;
}
.flex(@flex) {
-webkit-box-flex: @flex;
-webkit-flex: @flex;
flex: @flex;
}
</code></pre>
<p>在使用的只要一行就行了</p>
<pre><code class="language-less">.container {
.display-flex;
.flex-direction(row);
.justify-content(center);
}
</code></pre>
<p>上面的代码less编译完的结果如下</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.container</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">-webkit-box</span><span class="p">;</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">-webkit-flex</span><span class="p">;</span>
<span class="nl">display</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span>
<span class="nl">-webkit-box-orient</span><span class="p">:</span> <span class="n">horizontal</span><span class="p">;</span>
<span class="nl">-webkit-box-direction</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span>
<span class="nl">-webkit-flex-direction</span><span class="p">:</span> <span class="n">row</span><span class="p">;</span>
<span class="nl">flex-direction</span><span class="p">:</span> <span class="n">row</span><span class="p">;</span>
<span class="nl">-webkit-box-pack</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="nl">-webkit-justify-content</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="nl">justify-content</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>有同学说这么麻烦,我不想写啊?其实应该有人已经写好了,比如<a href="https://github.com/Igosuki/compass-mixins/blob/master/lib/compass/css3/_flexbox.scss">compass</a>,可以参考一下</p>
<p>这种方式的前提就是已经使用了css预处理器,可维护性比第一种方法更好;但是以我的经验来说,其实大部分项目的mixin未必是有人维护的,比如可能有一天不需要前缀版本了,但是并一定会有人去更新的</p>
<p>第三种,css后处理器,其实自从postcss出来之后,自动加前缀的活就该交给postcss来做了,有了这个插件我们只需要配置要兼容的浏览器版本就可以了,加前缀的事情后处理器自动帮你解决,最近babel也出了一个类似的babel-env</p>
<p>fis中可以使用<code class="language-plaintext highlighter-rouge">fis-postprocessor-autoprefixer</code>这个插件,我在之前的文章《<a href="http://yanhaijing.com/program/2016/09/07/exp-wap-step/">经验无线步骤页改版总结</a>》中有介绍</p>
<p>webpack中可以使用<a href="https://github.com/postcss/postcss-loader">postcss-loader</a>这个loader</p>
<p>终于可以和浏览器前缀愉快的玩耍了^_^</p>
<p><strong>普及一个小科普知识,css后面的实验室性不会再以加前缀的方式进行了,而是会通过浏览器的设置方式来显示开启实验属性,因为前缀的方式不够优雅。。。这锅主要还是怪前端开发者,因为我们啊,只写webkit前缀,都不写标准属性,o(╯□╰)o</strong></p>
<h2 id="总结">总结</h2>
<p>希望本文能够帮助你更好的使用flex,少踩一些坑,现在在移动端已经可以任性的使用flex了,但pc端还不行,ie8。。。如果没有兼容性问题,那就快来使用这一好用的布局方式吧</p>
<p>最后我强烈建议大家阅读大漠老师的《<a href="https://amazon.cn/gp/product/B00LHL3DV4/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&tag=yanhaijing-23&camp=536&creative=3200&linkCode=as2&creativeASIN=B00LHL3DV4&linkId=ce75459043755ec9e78830fa6e65f2be">图解CSS3</a>》,这是我见过讲css3讲的最好的书了</p>
<h2 id="相关资料">相关资料</h2>
<ul>
<li><a href="http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html">Flex 布局教程:语法篇</a></li>
<li><a href="http://www.ruanyifeng.com/blog/2015/07/flex-examples.html">Flex 布局教程:实例篇</a></li>
</ul>
教你如何在windows上安装Java
2016-08-20T00:00:00+00:00
http://yanhaijing.com/java/2016/08/20/how-to-install-java-on-win
<p>最近够倒霉的,电脑硬盘坏了,重新做了个系统,各种环境全都没了,/(ㄒoㄒ)/~~</p>
<p>然后我发现自己在重新安装各种环境的时候,有些东西竟然还需要去查,所以决定把这些环境的配置都写成博客记录下来。</p>
<p>今天就教大家如何在windows上安装java,和如何配置jdk环境。</p>
<h2 id="下载java">下载java</h2>
<p>首先需要下载java的安装包,点击<a href="http://www.oracle.com/technetwork/cn/java/javase/downloads/index.html">这里下载</a>(链接可能过期,如果链接不对,可自行百度),在这个界面我们选择java这个。</p>
<p><img src="/blog/405.png" alt="" /></p>
<p>接下来选择对应的版本,我选择windows 32位版,然后勾上同意协议,点击下载链接</p>
<p><img src="/blog/406.png" alt="" /></p>
<h2 id="安装java">安装java</h2>
<p>找到下载的java安装包,我已经下载好了</p>
<p><img src="/blog/407.png" alt="" /></p>
<p>接下来就是windows程序的通用安装过程了,一路下一步,其中可以选择安装的路径</p>
<p><img src="/blog/408.png" alt="" /></p>
<p>整个过程分为安装安装jre(java运行时)和jdk(java开发者工具)两个过程,如果你更改了安装路径,需要注意把两个改成一样的</p>
<h2 id="配置环境">配置环境</h2>
<p>安装好后,打开命令行,输入<code class="language-plaintext highlighter-rouge">java -version</code>,就可以看到下面的输出</p>
<p><img src="/blog/409.png" alt="" /></p>
<p>现在java会自动把jre的环境设置好,所以才能直接输入java命令</p>
<p><img src="/blog/410.png" alt="" /></p>
<p>我们下面来配置jdk环境,这里我配置到用户变量,第一步,配置<code class="language-plaintext highlighter-rouge">JAVA_HOME</code>,其值是jdk的安装目录,比如我的是<code class="language-plaintext highlighter-rouge">C:\Program Files (x86)\Java\jdk1.8.0_45</code>。</p>
<p><img src="/blog/411.png" alt="" /></p>
<p>第二步,将jdk加入到PATH中,在用户的PATH最前面加入下面的语句,<code class="language-plaintext highlighter-rouge">%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;</code></p>
<p><img src="/blog/412.png" alt="" /></p>
<p>我们可以键入<code class="language-plaintext highlighter-rouge">javac -version</code>来验证</p>
<p><img src="/blog/414.png" alt="" /></p>
<p>第三步,配置CLASSPATH,新建变量<code class="language-plaintext highlighter-rouge">CLASSPATH</code>,键入下面的语句<code class="language-plaintext highlighter-rouge">.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar</code></p>
<p><img src="/blog/413.png" alt="" /></p>
<h2 id="验证一下">验证一下</h2>
<p>下面来写一个小程序验证一下,新建一个Hello.java,键入下面的代码</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>public class Hello {
public static void main(String[] args) {
System.out.println("Hello World!!!");
}
}
</code></pre></div></div>
<p>然后运行<code class="language-plaintext highlighter-rouge">javac Hello</code>编译,在用<code class="language-plaintext highlighter-rouge">java</code>执行</p>
<p><img src="/blog/415.png" alt="" /></p>
<h2 id="总结">总结</h2>
<p>按照上面的步骤我们就将环境配置好了。</p>
xshell命令集的使用方法
2016-08-10T00:00:00+00:00
http://yanhaijing.com/tool/2016/08/10/xshell-commond
<p>最近在学习后端的知识,我一直在用xshell终端,今天来说说如何使用xshell的命令集来加快登录线上机器的速度。</p>
<p>首先打开菜单-工具-快速命令集,会看到如下图所示的窗口</p>
<p><img src="/blog/400.png" alt="" /></p>
<p>然后点新建,创建一个新的命令集,比如我已经创建好了一个my,这个可以用来分组命令</p>
<p>新建完后,选中命令集,点击编辑,会弹出如下图所示的窗口</p>
<p><img src="/blog/401.png" alt="" /></p>
<p>然后可以点击添加添加,会得到下图所示的窗口</p>
<p><img src="/blog/402.png" alt="" /></p>
<p>在最上面的输入框输入命令的名字,我们重点关注发送文本,和执行下面的脚本这两个。</p>
<p>先来说说文本,这个很简单就是发送文本到终端里,可以保存一些复杂文本进来,还可以是密码什么的;可以选择粘贴文本后是否添加回车。</p>
<p>重点说说如何通过执行脚本来方便我们登录线上机器,在本地保存下面的文本内容,保存为.vbs,将其中的机器和密码替换为你对应的机器和密码即可,然后选择添加脚本,然后一路确定就ok了。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Sub Main
' *** Send ***
xsh.Screen.Send("ssh user@机器")
xsh.Screen.Send(VbCr)
' *** WaitForString ***
xsh.Screen.WaitForString("password:")
' *** Send ***
xsh.Screen.Send("密码")
xsh.Screen.Send(VbCr)
End Sub
</code></pre></div></div>
<p>这时候我们需要显示快速命令条,默认是不显示的,菜单-查看-快速命令 勾上,这时会在最下面显示命令条,在最右边选择显示你自己的命令集</p>
<p><img src="/blog/403.png" alt="" /></p>
<p>就会在命令条看到快速命令了</p>
<p><img src="/blog/404.png" alt="" /></p>
<p>然后点击这个按钮就可以快速登录线上机器了,O(∩_∩)O哈哈~</p>
<p>还可以设置一个复制ssh渠道的快捷键,比如我设置的是alt+t,只需两步,就可快速登录线上机器。</p>
详解JavaScript中的原型和继承
2016-07-24T00:00:00+00:00
http://yanhaijing.com/javascript/2016/07/24/prototype-and-inheritance-of-js
<p>最近组内的童鞋和我说的自己一直搞不太清楚js原型这一块的东西,我想了想觉得这东西也不是一两句话就能解释清楚的,所以我决定来解释解释js中的原型机制,希望也能帮到你。</p>
<p>本文将会介绍面向对象,继承,原型等相关知识,涉及的知识点如下:</p>
<ul>
<li>面向对象与继承</li>
<li>CEOC</li>
<li>OLOO</li>
<li>臃肿的对象</li>
<li>原型与原型链</li>
<li>修改原型的方式</li>
</ul>
<h2 id="面向对象与继承">面向对象与继承</h2>
<p>最近学习了下python,还写了篇博文《<a href="http://yanhaijing.com/program/2016/06/28/my-python/">重拾编程乐趣——我的Python笔记</a>》,加深了我对面向对象的一些理解。</p>
<p>我们会对我们写的程序进行抽象,而不同的语言都提供了不同的抽象工具,比如各种语言里面的数组,集合(键值数组,哈希表,字典等)等提供了对数据的抽象;而VB里面的子程序,类C语言里面的函数,提供了抽象代码段的能力。</p>
<p>有时我们希望将数据和对数据的操作封装到一起,这被称作对象,是一种更高唯独的抽象工具,而这种抽象工具——对象可以对现实世界进行建模。</p>
<p>现实世界中的事物都有一些联系,比如我们可以抽象出来猫,然后又公猫,母猫,显然公猫应该拥有猫的特性,这也就是继承,细分的事物应该有高纬度事物的特点。</p>
<p>想要实现对象和继承这套思维,目前有两种实现方法,分别是CEOC和OLOO。</p>
<h2 id="ceoc">CEOC</h2>
<p>CEOC(class extend other class)是一套基于类和实例的实现方式,这种方式实用的比较广泛,大部分流星的面向对象语言都在使用,比如java,python等。</p>
<p>类作为对象的抽象描述,对象是类的实例,也称作类的泛化,泛化和抽象对应。最恰当的类比就是盖房子了,类就相当于房子的图纸,图纸是对房子属性和功能的描述,根据图纸可以盖很多个类似的房子;另一种类比就是磨具和零件,磨具相当于类,零件相当于对象,通过磨具我们可以造出来很多零件。</p>
<p>其实与其说是面向对象还不如说是面向类编程,在这种机制中解决上面提到的继承逻辑时,是在类上实现的,子类可以继承父类。</p>
<p>在类的机制中更像是对工厂里通过磨具造零件机制的模拟,而非动物世界这种繁衍继承机制的模拟,下面介绍的OLOO则是对世界更好的模拟。</p>
<h2 id="oloo">OLOO</h2>
<p>OLOO(object link other object)是一套基于对象和原型的实现方式,这种方式唯一实现广泛语言就是js了,熟悉类机制的同学,初次接触这个,可能会觉得不是很好理解,而很多前端同学理解的也不是很明白,之前写过一篇原型的文章,推荐大家阅读《<a href="http://yanhaijing.com/javascript/2014/07/18/javascript-prototype/">JavaScript原型之路</a>》。</p>
<p>其实面向类的机制有些多此一举了,因为最后使用的是对象,而不是类,那么我们直接让对象可以集成对象不就行了,在这种机制中可以通过某种操作让对象和对象之间可以建立继承的关系,当继承的对象(子对象)中没有某些属性时可以去被继承的对象(父对象)中去查找。</p>
<p>在OLOO中,父对象也可以成为子对象的原型,这有些类似我们的人类的繁衍,每一个人都是一个对象,都是父母所生,而不是用模版建造出来的,每一个人都有一个父亲,孩子和父亲之间有一种特殊的关系,成为父子。</p>
<h2 id="臃肿的对象">臃肿的对象</h2>
<p>来说说JS中的对象机制,JS中的对象显得有些臃肿,JS中的对象承接了两个功能,一是面向对象机制中的对象,另一个是数据抽象中的集合——其他语言中称为键值数组,哈希表或字典。</p>
<p>其实在其它语言中的对象都不耦合集合的功能,比如python中有字典,php中有键值数组,好在es2015中引入新的map类型,来把这个功能解耦出去,但我相信很多人都习惯这么使用了o(╯□╰)o</p>
<p>而本文所说的对象不包括后面的这一部分功能,特指js中面向对象机制中的对象。</p>
<h2 id="原型与原型链">原型与原型链</h2>
<p>js语言是基于原型的语言,在js中几乎一切都是对象,每个对象都有原型,而原型也是一个对象,也有自己的原型,从而形成原型链。</p>
<p>当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依此层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。</p>
<p>js中提到原型可能有不同的意思,我们得先区分清楚,需要清除到底是哪个原型:</p>
<ol>
<li>js是基于原型的语言中的prototype</li>
<li>每个对象都有自己的原型中的prototype</li>
<li>每个函数都有一个原型属性中的prototype</li>
</ol>
<p>第一点中的prototype是一个概念,一种机制,而不是具体的什么东西。</p>
<p>第二点中的prototype是说每个对象都有自己的原型对象(父对象),下面我们用[[Prototype]]来指代这个原型对象。</p>
<p>第三点和第二点很容易混淆,每个函数都有一个原型属性,我们用prototype来指代。</p>
<p>es5带来了查看对象原型的方法——Object.getPrototypeOf,该方法返回指定对象的原型(也就是该对象内部属性[[Prototype]]的值)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>console.log(Object.getPrototypeOf({}))
>>> Object.prototype
</code></pre></div></div>
<p>es6带来了另一种查看对象原型的方法——Object.prototype.__proto__,一个对象的__proto__ 属性和自己的内部属性[[Prototype]]指向一个相同的值 (通常称这个值为原型),原型的值可以是一个对象值也可以是null(比如说Object.prototype.__proto__的值就是null)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>({}).__proto__
>>> Object.prototype
</code></pre></div></div>
<p>下面举个例子来说说原型与原型链,以及访问对象属性的时候会发生的行为:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// a ---> b 代表b是a的原型
</code></pre></div></div>
<h2 id="修改原型的方式">修改原型的方式</h2>
<p>在js中创建和修改原型的方法有很多,下面一一列举出来。</p>
<p>在下面的例子中我们将对象a的[[Prototype]]指向b。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// a ---> b
</code></pre></div></div>
<h3 id="使用普通语法创建对象">使用普通语法创建对象</h3>
<p>这是最容易被大家忽略的方法,在js中你是绕不过原型的,不经意间就创建了原型</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var o = {a: 1};
// o ---> Object.prototype ---> null
var a = [];
// a ---> Array.prototype ---> Object.prototype ---> null
function f(){}
// f ---> Function.prototype ---> Object.prototype ---> null
</code></pre></div></div>
<p>这种方法无法让a的[[Prototype]]指向b。</p>
<h3 id="使用构造器创建对象">使用构造器创建对象</h3>
<p>构造函数就是一个普通的函数,只不过这次不是直接调用函数,而是在函数前加上new关键字。</p>
<p>每个函数都有一个prototype属性,通过new关键字新建的对象的原型会指向构造函数的prototype属性,所以我们可以修改构造函数的prototype属性从而达到操作对象原型的目的。</p>
<p>为了让b继承a,需要有一个构造函数A</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var b = {};
function A() {};
A.prototype = b;
var a = new A();
Object.getPrototypeOf(a) === b;
// true
// a ---> A.prototype === b
</code></pre></div></div>
<h3 id="使用-objectcreate-创建对象">使用 Object.create 创建对象</h3>
<p>ES5带来了Object.create接口,可以让我们直接设置一个对象原型</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var b = {};
var a = Object.create(b);
Object.getPrototypeOf(a) === b;
// true
// a ---> b
</code></pre></div></div>
<h3 id="objectsetprototypeof">Object.setPrototypeOf</h3>
<p>ES6带来了另一个接口,可以绕过创建对象的过程,直接操作原型</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = {};
var b = {};
Object.setPrototypeOf(a, b);
Object.getPrototypeOf(a) === b;
// true
// a ---> b
</code></pre></div></div>
<h3 id="proto"><strong>proto</strong></h3>
<p>ES6还带来了一个属性,通过这个属性也可以直接操作原型</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = {};
var b = {};
a.__proto__ = b;
Object.getPrototypeOf(a) === b;
// true
// a ---> b
</code></pre></div></div>
<p><strong>注意</strong>这个属性在ES6规范的附录中,也就意味着不是所有的环境都会有这个属性。</p>
<h3 id="使用-class-关键字">使用 class 关键字</h3>
<p>ES6引入了以class语法糖,通过extends关键字我们也可以实现继承,但是无法直接操作对象的原型,而是要借助“类”,其实就是构造函数和函数的prototype属性。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class B {}
class A extends B {}
var a = new A();
Object.getPrototypeOf(a) === A.prototype;
// true
// a ---> A.prototype === B的实例
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>不知道看完文章你理解原型了吗?如果还有疑惑建议你阅读下面的文章。</p>
<h2 id="参考文章">参考文章</h2>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain">继承与原型链</a></li>
<li><a href="http://ued.ctrip.com/blog/javascript-the-core.html">JavaScript. The core</a></li>
</ul>
聊聊JavaScript中的二进制数
2016-07-20T00:00:00+00:00
http://yanhaijing.com/javascript/2016/07/20/binary-in-js
<p>事情的起因是这样的最近在业务代码中发现下面这样的一行代码,我看了半天没搞明白是什么意思,不知道聪明的你能不能知道是什么意思呢?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>!~location.href.search('***')
</code></pre></div></div>
<p>如果你也不知道,并且也像我一样富有好奇心那么就和我一起来学习这篇文章吧。在本文中你将学到如下知识:</p>
<ul>
<li>二进制数的表示</li>
<li>js中的二进制数整数</li>
<li>js中的位运算</li>
</ul>
<h2 id="二进制数">二进制数</h2>
<p>本文假设你知道计算机中用二进制数来存储,计算数字,并且熟悉二进制数的表示方法。</p>
<p>为了实现不同的目的,其实都是为了简化问题,二进制数在计算机中有不同的表示方法,如原码、反码、补码和移码等。</p>
<p><strong>注意:</strong>本文问了简化运算,二进制数都是用一个字节——8个二进制位来简化说明</p>
<p>先来说说真值吧,我们表示自然数包括正数,负数和0,下面是1和-1的二进制表示,我们称为真值</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+ 00000001 # +1
- 00000001 # -1
</code></pre></div></div>
<p>8位二进制数能表示的真值范围是[-2^8, +2^8]。</p>
<p>由于计算机只能存储0和1,不能存储正负,所以用8个二进制位的最高位来表示符号,0表示正,1表示负,用后七位来表示真值的绝对值,这种表示方法称为原码表示法,简称原码,上面的1和-1的原码如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 0000001 # +1
1 0000001 # -1
</code></pre></div></div>
<p>由于<code class="language-plaintext highlighter-rouge">10000000</code>的意思是-0,这个没有意义,所有这个数字被用来表示-128,所有负数就比整数多一个。</p>
<p>由于最高位被用来表示符号了,现在能表示的范围是[-2^7, +2^7-1],即[-128, +127]</p>
<p>反码是另一种表示数字的方法,其规则是整数的反码何其原码一样,负数的反码将其原码的符号位不变,其余各位按位取反</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 0000001 # +1
1 1111110 # -1
</code></pre></div></div>
<p>反码的表示范围是[-2^7, +2^7-1],即[-128, +127]</p>
<p>补码是另外一种表示方法,主要是为了简化运算,将减法变为加法而发明的数字表示法,其规则是整数的补码和原码一样,负数的补码是其反码末尾加1</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 0000001 # +1
1 1111111 # -1
</code></pre></div></div>
<p>快速计算负数补码的规则就是,由其原码低位向高位找到第一个1,1和其低位不变,1前面的高位按位取反即可,不知道聪明的你能不能想到原理。</p>
<p>8位补码表示的范围是[-2^7, +2^7-1],即[-128, +127]</p>
<h2 id="js中的二进制数整数">js中的二进制数整数</h2>
<p>再来说说js中的二进制整数表示,一名合格的jser应该支持在js中只有一种数字类型,就是浮点型,js的浮点数遵循IEEE 754规范,如果你想了解js浮点数的更多知识,我推荐你看这篇文章《<a href="http://yanhaijing.com/javascript/2014/03/14/what-every-javascript-developer-should-know-about-floating-points/">每一个JavaScript开发者应该了解的浮点知识</a>》。</p>
<p>然而在js中还有另一种类型的数据,那就是用32个比特位表示的整数,只要对js中的任何数字做位运算操作系统内部都会将其转换成整形,尝试在控制台输入下面的代码</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2.1 | 0 # 或运算
>>> 2
</code></pre></div></div>
<p>js中的这种整形是区分正负数的,我们根据上面的知识推断js中的整数的表示范围是[-2^31, +2^31-1],即[-2147483648, +2147483647],在控制台输出下面的代码来验证我们的推断</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-2147483648 | 0
>>> -2147483648
-2147483649 | 0
>>> 2147483647
2147483647 | 0
>>> 2147483647
2147483648 | 0
>>> -2147483648
</code></pre></div></div>
<p>从上面的结果可以看出,大于和小于最低和最高的值再去进行转换时都将改变正负号</p>
<h2 id="js中的位运算">js中的位运算</h2>
<p>js中的位运算符有下面这些,对数字进行这些操作时,系统内部都会讲64的浮点数转换成32位的整形</p>
<ul>
<li>& 与</li>
<li>| 或</li>
<li>~ 非</li>
<li>^ 异或</li>
<li><< 左移</li>
<li>>> 算数右移(有符号右移)</li>
<li>>>> 逻辑右移(无符号右移)</li>
</ul>
<p>下面举例子来说明每个运算符的作用,开始之前先来介绍几个会用到的知识点</p>
<h3 id="原生二进制字面量">原生二进制字面量</h3>
<p>es6中引入了原生二进制字面量,二进制数的语法是0b开头,我们将会用到这个新功能,目前chrome最新版已经支持。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0b111 // 7
0b001 // 1
</code></pre></div></div>
<h3 id="numberprototypetostring">Number.prototype.toString</h3>
<p>先来介绍下下面会用到的一个方法——<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString">Number.prototype.toString</a>方法可以讲数字转化为字符串,有一个可选的参数,用来决定将数字显示为指定的进制,下面可以查看3的二进制表示,根据这个特性,我还特意做了一个<a href="http://yanhaijing.com/radixConvert/">进制转化工具</a>。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>3..toString(2)
>> 11
</code></pre></div></div>
<h3 id="-与">& 与</h3>
<p>&按位与会将操作数和被操作数的相同为进行与运算,如果都为1则为1,如果有一个为0则为0</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>101
011
---
001
</code></pre></div></div>
<p>101和011与完的结果就是001,下面在js中进行验证</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(0b101 & 0b011).toString(2)
>>> "1"
</code></pre></div></div>
<h3 id="-或">| 或</h3>
<p>|按位或是相同的位置上只要有一个为1就是1,两个都为0则为0</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>101
001
---
101
</code></pre></div></div>
<p>101和001或完的结果是101,下面在js中进行验证</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(0b101 | 0b001).toString(2)
>>> "101"
</code></pre></div></div>
<h3 id="-非">~ 非</h3>
<p>~操作符会将操作数的每一位取反,如果是1则变为0,如果是0则边为1</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>101
---
010
</code></pre></div></div>
<p>101按位非的结果是010,下面在js中验证</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(~0b101).toString(2)
>>> "-110"
</code></pre></div></div>
<p>啊呀,怎么结果不对呢!!!上面提到了js中的数字是有符号的,我们忘记了最高位的符号了,为了简化我们将32位简化为8位,注意最高位是符号位</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 0000101
1 1111010 // 非后的结果
1 0000101 // 求反
1 0000110 // 求补
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">1 1111010</code>明显是一个负数,而且是负数的补码表示,我们的求它的原码,也就是再对它求补<code class="language-plaintext highlighter-rouge">1 0000110</code>就是这个数的真值,也就是结果显示-110,这下总算自圆其说了,O(∩_∩)O哈哈~</p>
<p>其实上面的与和或也都是会操作符号位的,不信你试试下面这两个,可以看到符号位都参与了运算</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(0b1&-0b1)
>>> 1
(0b1|-0b1)
>>> -1
</code></pre></div></div>
<h3 id="-异或">^ 异或</h3>
<p>再来说说异或,这个比较有意思,异或顾名思义看看两个位是否为异——不同,两个位不同则为1,两个位相同则为0</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>101
001
---
100
</code></pre></div></div>
<p>101和001异或的结果是100,js中验证</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(0b101^0b001).toString(2)
>>> "100"
</code></pre></div></div>
<h3 id="-左移"><< 左移</h3>
<p>左移的规则就是每一位都向左移动一位,末尾补0,其效果相当于×2,其实计算机就是用移位操作来计算乘法的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>010
---
0100
</code></pre></div></div>
<p>010左移一位就会变为100,下面在js中验证</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(0b010<<1).toString(2)
>>> "100"
</code></pre></div></div>
<h3 id="-算数右移有符号右移">>> 算数右移(有符号右移)</h3>
<p>算数右移也称为有符号右移,也就是移位的时候高位补的是其符号位,整数则补0,负数则补1</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(0b111>>1).toString(2)
>>> "11"
(-0b111>>1).toString(2)
>>> "-100"
</code></pre></div></div>
<p>负数的结果好像不太对劲,我们来看看是怎么回事</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-111 // 真值
1 0000111 // 原码
1 1111001 // 补码
1 1111100 // 算数右移
1 0000100 // 移位后的原码
-100 // 移位后的真值
</code></pre></div></div>
<h3 id="-逻辑右移无符号右移">>>> 逻辑右移(无符号右移)</h3>
<p>逻辑右移又称为无符号右移,也就是右移的时候高位始终补0,对于整数和算数右移没有区别</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(0b111>>>1).toString(2)
>>> "11"
</code></pre></div></div>
<p>对于负数则就不同了,右移后会变为正数</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(-0b111>>>1).toString(2)
>>> "1111111111111111111111111111100"
</code></pre></div></div>
<h2 id="关于开头的问题">关于开头的问题</h2>
<p>关于二进制数就说这么多吧,再来说说开头的问题,开头的问题其实可以分解为下面的问题因为search会返回-1 和找到位置的索引,也就成了下面的问题</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>!~-1
>>> ture
!~0
>>> false
!~1
>>> false
</code></pre></div></div>
<p>非运算对于数字的结果相当于改变符号,并对其值的绝对值-1</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~-1
>>> 0
~0
>>> -1
~1
>>> -2
</code></pre></div></div>
<p>其实可以看出!~x的逻辑就是判断x是否为-1,my god这逻辑真是逆天了,我还是劝大家直接写成 x === -1多好啊</p>
<h2 id="总结">总结</h2>
<p>通过这篇文章终于把当年没学明白的二进制数搞明白了,希望你和我一样,祝你好运。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://jser.it/blog/2014/07/07/numbers-in-javascript/">Numbers in JavaScript</a></li>
</ul>
我的 Sublime Text 3 笔记
2016-06-30T00:00:00+00:00
http://yanhaijing.com/tool/2016/06/30/my-sublime3
<p>今天打开<a href="http://www.sublimetext.com/">sublime</a>的官网,发现官网上已经推荐默认推荐大家使用sublime text 3了,作为追赶时代的人,我怎么能够落后呢?我决定开始使用sublime3,同时我也希望你也能够使用。</p>
<p>本文记录我是用sublime 3的笔记,涉及到方方面面,主要是方便自己,也希望能够帮到你。</p>
<p><img src="/blog/297.png" alt="" /></p>
<p><strong>注:</strong>我之前写过一篇<a href="http://yanhaijing.com/tool/2014/10/24/my-sublime/">sublime text 2的笔记</a>,取走不谢。</p>
<h2 id="安装">安装</h2>
<p>在<a href="http://www.sublimetext.com/">官网</a>下载最新版的安装文件,选择对应的平台,我用的是windows,sublime3和2是能够共存的,我就在自己电脑同时装了3和2,并准备逐步迁移到3上面。</p>
<p>没错本文就是在sublime3 上写的。</p>
<h2 id="初始化">初始化</h2>
<p>sublime3是高度定制化的,安装完成后,我做的第一件事情就是按照自己的习惯定制。</p>
<p>打开Preferences->Setting-Default,在那里面有很多默认配置选项,我们可以在这里改变默认值,但我的建议是在Setting-User里面去进行修改。</p>
<p>修改显示字体大小,我一般习惯使用14号字</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"font_size": 14, // 默认10
</code></pre></div></div>
<p>保存文件时自动在末尾添加空行(我们的项目有这样的要求)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"ensure_newline_at_eof_on_save": true, // 默认是false
</code></pre></div></div>
<p>默认使用Unix换行符,如果大家使用统一的换行符,会让事情变得简单</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"default_line_ending": "unix", // 默认是system
</code></pre></div></div>
<p>使用空格填充tab键,没有好坏之分,统一就好</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"translate_tabs_to_spaces": true,// 默认是false
</code></pre></div></div>
<p>我还把-和$两个符号从分隔符中删掉,这样在选择php变量和css选择符的时候就更爽了</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"word_separators": "./\\()\"':,.;<>~!@#%^&*|+=[]{}`~?"
</code></pre></div></div>
<p>如果你感兴趣也可以修改其他配置选项试试,快去定制属于自己的编辑器吧。</p>
<p>如果你发现你的goto anything非常卡顿,那有可能是安装了巨大的node_modules导致的,你可以修改下面的配置,注意最后三个</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> "binary_file_patterns": ["*.jpg", "*.jpeg", "*.png", "*.gif", "*.ttf", "*.tga", "*.dds", "*.ico", "*.eot", "*.pdf", "*.swf", "*.jar", "*.zip", "vendor/**", "node_modules/**/node_modules/**", ".certs/**"],
</code></pre></div></div>
<p>上面我们把node_modules目录下的node_moduels都排除在外,如果你还觉得卡,那就直接把node_moduels也排除在外吧。。。</p>
<p>解决办法,参考<a href="https://blog.michaelchen.io/goto-anything-is-super-slow-in-sublime-text/">文章</a></p>
<h2 id="快捷键">快捷键</h2>
<p>如果你还不习惯快捷键,那你真的该学习了,我越来越依赖快捷键了。</p>
<p>sublime的快捷键非常非常多,很难都记住,按照80/20原则,只有20%是常用的,下面是我常用的快捷键:</p>
<p>语法说明:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">ctrl+x</code> ctrl和x键同时按</li>
<li><code class="language-plaintext highlighter-rouge">x & y</code> x操作后,进行y操作</li>
<li><code class="language-plaintext highlighter-rouge">x | y</code> x操作或y操作</li>
<li><code class="language-plaintext highlighter-rouge">(x)</code> 分组x是一个整体</li>
<li><code class="language-plaintext highlighter-rouge">[x]</code> x是可选操作</li>
<li><code class="language-plaintext highlighter-rouge">x, y</code> x操作, y操作(两个类似操作写到一行)</li>
</ul>
<table class="table table-bordered">
<thead>
<tr><th>Mac 快捷键</th><th>Windows 快捷键</th><th>功能</th></tr>
</thead>
<tbody>
<tr><td>cmd+shift+p</td><td>ctrl+shift+p</td><td>打开命令面板</td></tr>
<tr><td>cmd+p|cmd+t</td><td>ctrl+p</td><td>搜索项目中的文件</td></tr>
<tr><td>cmd+r</td><td>ctrl+r</td><td>前往Method</td></tr>
<tr><td>ctrl+g</td><td>ctrl+g</td><td>跳转到第几行</td></tr>
<tr><td>cmd+k & cmd+b</td><td>ctrl+k & ctrl+b</td><td>切换侧边栏显示状态</td></tr>
<tr><td>cmd+del | ctrl+k</td><td>ctrl+shift+(backspace | del)</td><td>(左侧|右侧)全部删除</td></tr>
<tr><td>cmd+y</td><td>ctrl+y</td><td>重做或重复</td></tr>
<tr><td>shift+方向键</td><td>shift+方向键</td><td>移动并选择</td></tr>
<tr><td>cmd+([ | ])</td><td>ctrl+([ | ])</td><td>缩进|取消缩进</td></tr>
<tr><td>cmd+opt+([|])</td><td>ctrl+shift+([|])</td><td>(折叠|展开)代码</td></tr>
<tr><td>cmd+l</td><td>ctrl+l</td><td>选择行,重复可依次增加选择下一行</td></tr>
<tr><td>ctrl+m</td><td>ctrl+m</td><td>跳转到对应括号</td></tr>
<tr><td>ctrl+shift+m</td><td>ctrl+shift+m</td><td>选中括号间的内容</td></tr>
<tr><td>cmd+opt+.</td><td>alt+.</td><td>close tag</td></tr>
<tr><td>cmd+/</td><td>ctrl+/</td><td>当前行注释状态切换</td></tr>
<tr><td>cmd+opt+f</td><td>ctrl+h</td><td>替换</td></tr>
<tr><td>cmd+[shift]+f</td><td>ctrl+[shift]+f</td><td>[全局]查找</td></tr>
<tr><td>ctrl+[shift]+tab, cmd+num</td><td>ctrl+[shift]+tab, ctrl+pageup, alt+num</td><td>切换tab面板</td></tr>
<tr><td>cmd+shift+y</td><td>ctrl+shift+y</td><td>将光标处的表达式计算,对于数学不好的很有用</td></tr>
<tr><td>cmd+[shift]+v</td><td>ctrl+[shift]+v</td><td>[缩进]粘贴</td></tr>
<tr><td>cmd+opt+v</td><td>ctrl+k & ctrl+v</td><td>从历史记录粘贴</td></tr>
<tr><td>cmd+d</td><td>ctrl+d</td><td>选择一个选中项的下一个匹配项</td></tr>
<tr><td>ctrl+cmd+g</td><td>alt+f3</td><td>选择文件中的所有匹配项项</td></tr>
<tr><td>cmd+shift+k</td><td>ctrl+shift+’</td><td>选择所有选中项的标签</td></tr>
<tr><td>ctrl+d</td><td>ctrl+shift+a</td><td>选择当前选中项的父容器,可连续使用</td></tr>
<tr><td>ctrl+cmd+(↑|↓)</td><td>ctrl+shift+(↑|↓)</td><td>(上|下)移动一行</td></tr>
<tr><td>cmd+shift+d</td><td>ctrl+shift+d</td><td>复制行或选中项</td></tr>
<tr><td>ctrl+shift+w</td><td>alt+shift+w</td><td>用标签包裹行或选中项</td></tr>
<tr><td>, cmd+opt+(↑|↓), opt+(↑|↓)</td><td>ctrl+(↑|↓), alt+(↑|↓), alt+shift+(↑|↓)</td><td>(加|减)1, (加|减)10, (加|减)0.1</td></tr>
<tr><td>cmd+'</td><td>ctrl+shift+;</td><td>移除未闭合的容器元素</td></tr>
<tr><td>cmd+j</td><td>ctrl+j</td><td>合并选中的行(多行边一行)</td></tr>
<tr><td>cmd+kk</td><td>ctrl+kk</td><td>从光标处删除至行尾</td></tr>
<tr><td>ctrl+shift+k</td><td>ctrl+shift+k</td><td>删除整行</td></tr>
</tbody>
</table>
<h2 id="插件">插件</h2>
<p>sublime是离不开插件的,来晒晒我的插件吧。</p>
<h3 id="package-control">Package Control</h3>
<p>sublime的插件工具也是一个插件,这似乎是个悖论,需要安装的插件叫做<a href="https://sublime.wbond.net/">Package Control</a>。</p>
<p>首先打开控制台,点击sublime的菜单栏 view->show console(或者使用快捷键 ctrl+`),然后运行下的代码,等待几秒钟。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ','%20')).read())
</code></pre></div></div>
<p>或者下面的也可以</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'https://packagecontrol.io/' + pf.replace(' ','%20')).read())
</code></pre></div></div>
<p>现在在Preferences中就能看到 Package Control了,如下图所示。</p>
<p><img src="/blog/143.bmp" alt="" /></p>
<h3 id="javascript-completions">JavaScript Completions</h3>
<p>原生js提示插件,这个插件能够提供很多js语法。</p>
<h3 id="vintage">Vintage</h3>
<p>如果你习惯使用vim,那么可以安装这个插件,这个插件可以让sublime像vim一样。</p>
<h3 id="babel">Babel</h3>
<p>最近在写React,jsx用这个来做高亮显示</p>
<h3 id="smarty"><a href="https://packagecontrol.io/packages/Smarty">Smarty</a></h3>
<p>提供smarty语法的支持。Smarty插件默认的分隔符是<code class="language-plaintext highlighter-rouge">{}</code>,如果你使用的分隔符不同可以更改插件目录的Smarty.tmPreferences文件,找到其中的SMARTY_LDELIM和SMARTY_RDELIM,修改为你的分隔符即可。</p>
<h3 id="liquid">Liquid</h3>
<p>提供Liquid语法支持,如果你也写博客的话不妨试试。</p>
<h3 id="syncedsidebarbg">SyncedSidebarBg</h3>
<p>有很多强迫症同学发现自己的目录栏和编辑部分的背景颜色不一致,这个插件可以帮你解决这个让你不爽的问题</p>
<h3 id="less"><a href="https://github.com/danro/LESS-sublime">LESS</a></h3>
<p>这是一个非常棒的插件,可以让sublime支持less的语法高亮和语法提示,对于搞less的同学灰常重要,不做过多解释。</p>
<h3 id="scss">SCSS</h3>
<p>提供sass语法高亮支持,不建议安装SASS,SCSS更适合.scss语法支持。</p>
<h3 id="pretty-json">Pretty JSON</h3>
<p>提供对json文件的美化和格式化功能。</p>
<h3 id="jquery">jQuery</h3>
<p>支持jquery的只能语法提示,很赞。</p>
<h3 id="emmet">Emmet</h3>
<p>Emmet的前身是大名鼎鼎的Zen coding,如果你从事Web前端开发的话,对该插件一定不会陌生。它使用仿CSS选择器的语法来生成代码,大大提高了HTML/CSS代码编写的速度。对于html可以使用tab键,对于其他比如jsx可以使用ctrl+e</p>
<ul>
<li><a href="http://www.iteye.com/news/27580">这里是一篇演示文章</a></li>
<li><a href="http://docs.emmet.io/">Emmet官网</a></li>
</ul>
<h3 id="docblockr">DocBlockr</h3>
<p>DocBlockr 可以使你很方便地对代码建立文档。它会解析函数,变量,和参数,根据它们自动生成文档范式,你的工作就是去填充对应的说明。</p>
<p><img src="/blog/144.gif" alt="" /></p>
<h3 id="html-css-js-prettify">HTML-CSS-JS Prettify</h3>
<p>能够格式化css html 和js。</p>
<p><strong>注意:</strong>格式化的文件路径中不能有中文,不然会报找不到node的错误(windows下)。</p>
<p><img src="/blog/168.png" alt="" /></p>
<h3 id="brackethighlighter">BracketHighlighter</h3>
<p>像这些符号是成对的:花括号{}, 中括号[],括号:() ,引号“” 等。 这些符号当我们鼠标放在开始符号的位置的时候, 希望能明显看到结尾符号在哪儿sublime默认是下划线,很不明显, 想要明显一点,可以安装插件 BracketHighlighter。</p>
<h3 id="terminal">Terminal</h3>
<p>可以sublime中,打开命令行,非常方便哦。还可在自定义打开的命令行,比如我就把默认命令行改为了git-bash。只需在设置中进行如下配置即可(注意路径)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"terminal": "D:\\Program Files\\Git\\git-bash.exe"
</code></pre></div></div>
<p>Terminal会占用默认的<code class="language-plaintext highlighter-rouge">ctrl + shift + t</code>这个快捷键,这个可以打开刚刚关闭的页面,所以我将这个快捷键修改为 <code class="language-plaintext highlighter-rouge">ctrl + alt + t</code>。</p>
<h3 id="git">Git</h3>
<p>安装这个插件就可以在底部状态栏显示当前文件的git状态</p>
<h3 id="tortoisesvn">TortoiseSVN</h3>
<p>安装这个插件可以在邮件显示svn提交命令,这样我就不用打开文件夹了</p>
<h3 id="autofilename">AutoFileName</h3>
<p>以前用dreamweave的时候在引用文件的时候,可以自动补全文件名的功能,这个插件让sublime有了这个功能。</p>
<h3 id="allautocomplete">AllAutocomplete</h3>
<p>自动完成插件,可在全部打开的文件中,自动完成。</p>
<h3 id="alignment">Alignment</h3>
<p>对齐插件,强迫症患者必备,可以按等号对齐两边的变量。</p>
<h3 id="sidebarenhancements">SideBarEnhancements</h3>
<p>增强sublime的右键功能。</p>
<h3 id="multieditutils">MultiEditUtils</h3>
<p>扩展多行编辑的功能。</p>
<h3 id="markdown-preview">Markdown Preview</h3>
<p>如果你也喜欢md语法,那么安装这个插件吧,可以很方便的预览。</p>
<h3 id="htmlentity-snippets">HTMLEntity Snippets</h3>
<p>当你想输入html实体标签时,然后又记不住时,使用这个插件吧。</p>
<h3 id="converttoutf8">ConvertToUTF8</h3>
<p>这个插件可以让sublime3打开gbk编码的文件,但是不能保存,我还没找到更好的插件</p>
<h3 id="sublimelinter">SublimeLinter</h3>
<p>如果想对我们的代码风格进行验证,则可安装这个插件,这个插件只是个框架,要验证具体的语言还得安装插件</p>
<h3 id="sublimelinter-contrib-eslint">SublimeLinter-contrib-eslint</h3>
<p>js语言的验证可以安装这个插件</p>
<h3 id="package-syncing">Package Syncing</h3>
<p>最后推荐一个同步插件,这个插件可以在不同的机器同步配置信息和插件,非常方便,但鉴于国内的墙太高,我都是直接把插件给手动备份了,然后直接拖进去,或者直接去github上下载对应的包。</p>
<p><a href="https://github.com/yanhaijing/mysublime3">这里</a>是我的Package Syncing 导出来的文件。</p>
<h2 id="总结">总结</h2>
<p>sublime非常棒的,正是我喜欢的风格。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://blog.jobbole.com/82527/">Gif多图:我常用的 16 个 Sublime Text 快捷键</a></li>
<li><a href="http://segmentfault.com/a/1190000000505218">12个不可不知的Sublime Text应用技巧和诀窍</a></li>
<li><a href="http://www.jianshu.com/p/3cb5c6f2421c">如何优雅地使用Sublime Text3</a></li>
</ul>
重拾编程乐趣——我的Python笔记
2016-06-28T00:00:00+00:00
http://yanhaijing.com/program/2016/06/28/my-python
<p>早就想学学python了,但是一直没有勇气,一个偶然的机会,感谢图灵教育送了我一本《<a href="http://www.amazon.cn/gp/product/B0153174GS/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B0153174GS&linkCode=as2&tag=yanhaijing-23">父与子的编程之旅:与小卡特一起学Python</a>》,让我有激情、有动力学下去。</p>
<p>而我也很争气,很神奇的读完了这本书,要知道我现在很少读web相关外的技术书籍的,我给这本书的评价是5星,很棒的一本入门书,浅显易懂,非常适合新手来阅读,而且这不是一本讲python的书,而是一本讲编程的书。</p>
<p>感谢这本书,感谢python让我重新找回了编程的乐趣,终于又有了那种刚刚学习C语言时候的感觉了,所以我决定写下这篇文章,算是我的读书笔记吧,也是给自己做个备忘,主要是对这本书和python的总结。</p>
<p><img src="/blog/295.jpg" alt="" /></p>
<p>正文从这里开始,下面的内容都会按照书的目录结构来进行记录。</p>
<h2 id="出发吧">出发吧</h2>
<h3 id="安装python">安装python</h3>
<p>学习<a href="https://www.python.org/">python</a>的第一步是安装python,可以到python的<a href="https://www.python.org/downloads/">官网下载</a>,这里有很多版本,选择合适的版本和平台下载即可</p>
<p>我选择是2.x,我下载时最新的版本是2.7.12,windows平台的安装就是一路下一步即可安装好。</p>
<h3 id="交互模式">交互模式</h3>
<p>在开始菜单搜索或找到python安装目录,打开IDLE(Python GUI),即进入了python的交互模式,</p>
<p><img src="/blog/296.png" alt="" /></p>
<p>交互模式的意思就是,你可以输入一行代码,然后按下回车,python会立马告诉你执行结果,这个特别好用。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print "Hello World"
</code></pre></div></div>
<p>上面代码的执行结果就是打印出Hellow World,print是python的输出语句。</p>
<h3 id="第一个程序">第一个程序</h3>
<p>打开菜单->File->New File(ctrl + N),就可以进入文本编辑模式,这里可以输入多行代码,比如键入如下代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print "Hello World";
print 22 + 10;
</code></pre></div></div>
<p>然后保存到某个目录,名字为xxx.py,菜单->Run->Run Module(F5),即可执行上面的代码。</p>
<h2 id="记住内存和变量">记住内存和变量</h2>
<p>每个程序都是由下面三个部分组成:</p>
<ul>
<li>输入(input)</li>
<li>处理输入(process)</li>
<li>输出(ouput)</li>
</ul>
<h3 id="名字">名字</h3>
<p>在python中声明变量像下面这样,python中的变量都类似于引用,也是没有类型的。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>num = 123;
str = "123";
</code></pre></div></div>
<h2 id="基本数学运算">基本数学运算</h2>
<p>python可以完成基本的数学运算,当初python设计出来就是为了完成数学运算。</p>
<h3 id="四大基本运算">四大基本运算</h3>
<p>python可以完成四大基础运算,如下所示:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print 3 + 2 # 4
print 3 - 2 # 1
print 3 * 2 # 6
print 3 / 2 # 1
print 3.0 / 2 # 1.5
</code></pre></div></div>
<p>在前面的<code class="language-plaintext highlighter-rouge">3 + 2</code>中,3 和 2称为操作数,+ 称为操作符。</p>
<p>python的运算顺序和数学中一样,乘除高于加减,如果要改变运算顺序,和数学中一样的,需要添加括号。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print 3 + 1 * 2 # 6
print (3 + 1) * 2 # 8
</code></pre></div></div>
<h3 id="指数和求余数">指数和求余数</h3>
<p>在python中指数可以像下面这样表示</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print 3 ** 5 # 3 * 3 * 3 * 3 * 3 = 243
</code></pre></div></div>
<p>求余数使用%,像下面这样,注意和除法区分开</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print 7 % 2 # 1
print 7 / 2 # 3
</code></pre></div></div>
<h3 id="自增和自减">自增和自减</h3>
<p>python中没有想c++中的自增和自减(i++),要实现一个数的自增的像下面这样</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>num = 2
num += 1 # 3
num -= 1 # 2
</code></pre></div></div>
<p>上面的代码和下面的代码效果是一样的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>num = 2
num = num + 1 # 3
num = num - 1 # 2
</code></pre></div></div>
<h2 id="数据的类型">数据的类型</h2>
<p>python中的变量虽然没有类型,但是值是有类型的。</p>
<h3 id="改变类型">改变类型</h3>
<p>可以通过下面的函数来改变数据类型</p>
<ul>
<li>float() 从一个字符串或整数创建浮点数</li>
<li>int() 从一个字符串或浮点数创建一个新的整数</li>
<li>str() 从一个书或其他任何类型创建一个新的字符串</li>
</ul>
<p>三个函数都会返回一个新的值,而不会改变原来的值,因为值是不可变的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a = 24
b = float(a) # 24.0
c = 38.0
d = int(c) # 38
</code></pre></div></div>
<h3 id="类型检查">类型检查</h3>
<p>python中的变量是没有类型,那么如何检查一个变量当前的值是什么类型的呢?答案就是type函数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a = 24
type(a) # <type 'int'>
b = 24.0
type(b) # <type 'float'>
c = 'str'
type(c) # <type 'str'>
</code></pre></div></div>
<h2 id="输入">输入</h2>
<p>对一个程序来说最重要的一点就是输入了,如果电脑不能响应输入,那么就一点用处都没有了。</p>
<h3 id="raw_input">raw_input</h3>
<p>raw_input函数可以从用户哪里得到一个字符串</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print "请输入:"
some = raw_input();
print "你的输入是:", some
</code></pre></div></div>
<p>上面的程序会输出你输入的任何字符</p>
<p>raw_input还可以接受一个参数,这个参数会被作为输出,上面的程序可以简化为下面这样</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>some = raw_input("请输入:")
print "你的输入是:", some
</code></pre></div></div>
<h3 id="print">print</h3>
<p>print用来给打印输出,简单点说就是向控制台打印一些东西</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print "yan"
print "yan"
print "yan"
</code></pre></div></div>
<p>上面的三条语句会得到下面的输出</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yan
yan
yan
</code></pre></div></div>
<p>如果想在同一行打印的话可以在结尾加上逗号</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print "yan",
print "yan",
print "yan"
</code></pre></div></div>
<p>上面的语句会得到下面的输出</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yan yan yan
</code></pre></div></div>
<p>中间会有一个空格,print还支持多个参数,中间用逗号分隔</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print "yan", "yan", "yan"
</code></pre></div></div>
<p>上面的语句会得到下面的输出,中间也有一个空格</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>yan yan yan
</code></pre></div></div>
<h3 id="来自互联网的输入">来自互联网的输入</h3>
<p>可以python的内置库,可以获得从互联网的而输入,下面的代码可以输出我的博客内容</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import urllib2
file = urllib2.urlopen("http://yanhaijing.com")
msg = file.read()
print msg
</code></pre></div></div>
<h2 id="判断在判断">判断在判断</h2>
<p>每次程序执行都输出一样的东西没什么意思,需要有判断分支才有意思。</p>
<h3 id="测试测试">测试,测试</h3>
<p>人之所以有智能是因为人能够思考,面对选择是能做出选择,那么如何让计算机也能够做出选择呢?那就是测试,我们需要做一些测试,然后决定接下来做什么。这些测试可能包括如下问题:</p>
<ul>
<li>这两个东西相等吗?</li>
<li>其中一个是不是小于另一个?</li>
<li>其中一个是不是大于另一个?</li>
</ul>
<p>完成测试并更根据结果做出测试称之为分支,在python中使用if来进行测试:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if a == 1
</code></pre></div></div>
<p>python中的代码块比较奇葩,不是靠大括号而是缩进来确定范围的,声明一个代码块时就加一个冒号,就像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if a == 1:
print "a等于1"
print "代码块"
print "这里不是代码块了"
</code></pre></div></div>
<p>python中的elseif和else像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if a == 1:
print "a等于1"
elif a == 2:
print "a等于2"
else:
print "a等于其他值"
</code></pre></div></div>
<h3 id="比较操作符">比较操作符</h3>
<p>python中的比较操作符不只有等于还有其他,比如大于小于等,完整的列表如下</p>
<ul>
<li>== (等于)</li>
<li>< (小于)</li>
<li>> (大于)</li>
<li><= (小于等于)</li>
<li>>= (大于等于)</li>
<li>!= (不等于)</li>
</ul>
<h3 id="逻辑运算符">逻辑运算符</h3>
<p>python中也可以表达逻辑演算中的与 或 非,分别用下面的符号表示</p>
<ul>
<li>and</li>
<li>or</li>
<li>not</li>
</ul>
<p>逻辑运算符的用例如下</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if a != 1 and a != 2:
print "a不等于1和2"
</code></pre></div></div>
<h2 id="转圈圈">转圈圈</h2>
<p>来说说循环,循环分为两种,计数循环和条件循环。</p>
<h2 id="计数循环">计数循环</h2>
<p>python中的计数循环使用for in,语法如下所示,和c语言有很大差别</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for looper in [1, 2, 3, 4]:
print looper
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">[1, 2, 3, 4]</code>是列表,python中类似数组的东西,如果循环次数很多,可以用range来代替</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for looper in range(1, 5):
print looper
</code></pre></div></div>
<p>range还可以之传入一个参数,比如<code class="language-plaintext highlighter-rouge">range(5)</code>等同于 <code class="language-plaintext highlighter-rouge">range(0, 5)</code></p>
<p>如果改变循环的步长,可以传入第三个参数,<code class="language-plaintext highlighter-rouge">range(0, 6, 2)</code>等同于 <code class="language-plaintext highlighter-rouge">[0, 2, 4]</code>,其中2就是步长</p>
<p>步长还可以是负数,<code class="language-plaintext highlighter-rouge">range(6, 0, -2)</code>,等同于<code class="language-plaintext highlighter-rouge">[6, 4, 2]</code></p>
<h2 id="条件循环">条件循环</h2>
<p>条件循环的意思就是判断某个条件是否为真,如果为真就一直循环下去,使用while关键字</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>while 1 == 1:
print "永远也不会停下来"
</code></pre></div></div>
<h3 id="跳出循环">跳出循环</h3>
<p>跳出循环可以使用break和continue,分别是退出循环和退出本次循环。</p>
<h2 id="全是为了你注释">全是为了你——注释</h2>
<p>对于程序而言注释必不可少,python的注释和类c语言不一样,是使用#号,#号后面的都被当作注释,像下面这样</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># 我是注释我是注释
print 213 # 行末注释
</code></pre></div></div>
<p>python中没有多行注释,只能用单行注视模拟多行注释,像下面这样</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># ***********
# 多行注释
# 多行注释
# ***********
</code></pre></div></div>
<h2 id="收集起来列表和字典">收集起来——列表和字典</h2>
<p>python中存取数据有两种类型的东西,一种叫做列表,一种叫做字典。</p>
<h3 id="列表">列表</h3>
<p>python中的列表和数组很像,创建列表的语法如下所示:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [1, 2, 3, 4]
print list[0] # 1
</code></pre></div></div>
<p>可以用append向列表添加一个元素</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = []
list.append(1)
print list # 1
</code></pre></div></div>
<h3 id="列表分片">列表分片</h3>
<p>python中想从列表中获取列表片段,需要用到分片(slicing)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = ['a', 'b', 'c', 'd']
print list[1:3]
>>> ['b', 'c']
</code></pre></div></div>
<p>分片的语法可以简写,可以省略冒号前后的数字</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [1, 2, 3, 4, 5]
print list[:]
>>> [1, 2, 3, 4, 5]
print list[3:]
>>> [4, 5]
print list[:3]
>>> [1, 2, 3]
</code></pre></div></div>
<p>需要注意分片会返回一个新的列表,不会修改原列表。</p>
<h3 id="in关键字">in关键字</h3>
<p>要查找摸个元素是否在列表中,可以使用in</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = ['a']
print 'a' in list
>>> True
</code></pre></div></div>
<h3 id="del关键字">del关键字</h3>
<p>del 从列表删除指定位置元素</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = ['a', 'b', 'c']
del list[0]
print list
>>> ['b', 'c']
</code></pre></div></div>
<h3 id="列表方法">列表方法</h3>
<p>列表有很多方法,上面的append就是其中之一</p>
<ul>
<li>append</li>
<li>extend</li>
<li>insert</li>
<li>remove</li>
<li>pop</li>
<li>index</li>
<li>sort</li>
<li>reverse</li>
<li>sorted</li>
</ul>
<p>append用来在列表末尾添加一个元素</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [1]
list.append(2)
print list
>>> [1, 2]
</code></pre></div></div>
<p>extend在列表末尾添加多个元素</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [1]
list.extend([2, 3])
print list
>>> [1, 2, 3]
</code></pre></div></div>
<p>insert在指定位置插入元素</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [1, 2, 3]
list.insert(1, 'a')
print list
>>> [1, 'a', 2, 3]
</code></pre></div></div>
<p>remove从列表中删除指定元素,如果删除的元素不在列表中会报错,可以和in关键字配合使用</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [1, 2, 3]
list.remove(2)
print list
>>> [1, 3]
</code></pre></div></div>
<p><strong>注意:</strong>和del区分,del用来删除指定的索引位置元素</p>
<p>pop去除列表中最后一个元素</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [1, 2, 3]
a = list.pop()
print a
print list
>>> 3
>>> [1, 2]
</code></pre></div></div>
<p>index查找指定元素在列表中的索引,如果删除的元素不在列表中会报错,可以和in关键字配合使用</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [1, 2, 3]
print list.index(3)
>>> 2
</code></pre></div></div>
<p><strong>注意:</strong>和in区分开,in会返回布尔值,index会返回索引</p>
<p>sort用来给列表排序</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [4, 2, 1]
list.sort()
print list
>>> [1, 2, 4]
</code></pre></div></div>
<p>如果想倒叙排列列表,可以给sort传一个参数</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [1, 2, 3]
list.sort(reverse = True)
print list
>>> [3, 2, 1]
</code></pre></div></div>
<p><strong>注意:</strong>sort会修改原来的列表,而不是创建一个新的列表,所以下面的操作不正确</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print list.sort() # 不正确的做法
>>> None
list.sort() # 需要分两步
print list
</code></pre></div></div>
<p>reverse翻转列表,逆序排序数组还可以用这个</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [1, 2, 3]
list.reverse()
print list
>>> [3, 2, 1]
</code></pre></div></div>
<p>sorted输一个系统函数,而不是数组函数,可以得到一个列表的有序副本,而不会影响原来的列表</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [3, 2, 1]
nlist = sorted(list)
print list
print nlist
>>> [3, 2, 1]
>>> [1, 2, 3]
</code></pre></div></div>
<h3 id="循环列表">循环列表</h3>
<p>列表可用for循环来遍历</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for i in [1, 2, 3]:
print i
>>> 1
>>> 2
>>> 3
</code></pre></div></div>
<h3 id="不可变的列表">不可变的列表</h3>
<p>pythton中的列表是可变类型,python中有一种不可变的列表——元组,元组的语法如下</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tuple = (1, 2, 3) # 不可增删改
</code></pre></div></div>
<h3 id="字典">字典</h3>
<p>python中的另一个种集合类型就是字典,类似其他语言中的关联数组或哈希表,分为键和值,通过键来存取值。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>phoneNumbers = {'yan': 133}
print phoneNumbers['yan']
>>> 133
phoneNumbers['hai'] = 666;
print phoneNumbers['hai']
>>> 666
</code></pre></div></div>
<h3 id="字典方法">字典方法</h3>
<p>字典也有一些方法</p>
<ul>
<li>keys</li>
<li>values</li>
<li>clear</li>
</ul>
<p>keys方法可以列出字典总的全部键值</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dic = {'yan': 1, 'hai': 2}
print dic.keys()
>>> ['hai', 'yan']
</code></pre></div></div>
<p>values方法返回字典中的全部值</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dic = {'yan': 1, 'hai': 2}
print dic.values()
>>> [2, 1]
</code></pre></div></div>
<p>如果需要字典返回有序的键值,可以对返回的键值列表进行排序</p>
<p>clear用来删除字典中的而所有条目</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dic = {'yan': 1, 'hai': 2}
dic.clear()
print dic
>>> {}
</code></pre></div></div>
<h3 id="del--in">del & in</h3>
<p>del用来删除字典中的数据</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dic = {'yan': 1}
del dic['yan']
print dic
>>> {}
</code></pre></div></div>
<p>检查某个关键字是否在字典中使用in</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dic = {'yan', 1}
print 'yan' in dic
>>> True
</code></pre></div></div>
<h2 id="函数">函数</h2>
<p>当程序变大以后就需要一些方法来把程序分解成更小的部分,在python中主要有三种方法:</p>
<ul>
<li>函数——就像代码的积木,可以反复的使用</li>
<li>对象——把程序中的各个部分描述为自包含的单元</li>
<li>模块——包含程序各部分的独立文件</li>
</ul>
<p>创建一个函数需要使用def关键字</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def hello():
print 'hello world'
hello()
>>> 'hello world'
</code></pre></div></div>
<p>函数可以有多个参数,中间用逗号分隔</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def hello(who):
print 'hello', who
hello()
>>> hello yan
</code></pre></div></div>
<p>函数可以有返回值</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def add(x, y):
return x + y
add(1, 2)
>>> 3
</code></pre></div></div>
<p>函数内部的变量都是局部变量,在函数外部不能访问</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def test():
x = 1
print x
x = 10
print x
test()
>>> 10
>>> 1
</code></pre></div></div>
<p>在函数中可以访问全局变量,但不可修改,修改会创建一个局部变量</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def test2():
y = 10
print y
y = 1
test2()
print y
>>> 10
>>> 1
</code></pre></div></div>
<p>在函数内部想强制访问全局变量,需要使用global</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>def test3():
global z
z = 10
print z
z = 1
test3()
print z
>>> 1
>>> 10
>>> 10
</code></pre></div></div>
<h2 id="对象">对象</h2>
<p>对象可以把函数和数据收集在一起。对象可以包含属性和动作。</p>
<p>举个例子就是球,可以操作一个球,踢球,捡球,这些操作成为动作;球的颜色,大小称为球的属性。</p>
<p>python中称为对象的属性和方法。</p>
<h3 id="创建对象">创建对象</h3>
<p>python中创建对象分为两步,第一步是创建对象的描述——类;第二步使用类来创建一个真正的对象。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># 定义对象描述
# __init__ 初始化对象方法
# setColor 对象的方法
class Ball:
def __init__(self, color):
self.color = color
def setColor(self, color):
self.color = color
# 实例化对象
ball = Ball('red');
print ball.color
ball.setColor('green')
print ball.color
# python中的对象可动态添加属性,这一点和java不一样
ball.size = 'big'
>>> red
>>> green
</code></pre></div></div>
<h3 id="魔法方法">“魔法”方法</h3>
<p>python中对象有一些魔法方法,上面的__init__就是,还有一个魔法方法是__str__,可以自定义对象的打印文本</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class Ball:
def __str__():
return 'yanhaijing.com'
ball = Ball()
print ball
>>> yanhaijing.com
</code></pre></div></div>
<h3 id="self">self</h3>
<p>python的对象方法调用时,的第一个参数表示调用的对象,self是约定俗成的做法,不是必须的。</p>
<h3 id="隐藏数据">隐藏数据</h3>
<p>python也不支持私有的属性,这一点和js一样。</p>
<h3 id="继承">继承</h3>
<p>在面向对象编程中,类可以从其他类继承属性和方法。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class People:
__init__(self, name):
self.name = name
talk():
# pass作为占位符,可以表示为空函数
pass
# 继承People
class Teacher(People):
__init__(self, name):
# 调用父类方法
People.__init__(self, name)
</code></pre></div></div>
<h2 id="模块">模块</h2>
<p>如果一个东西可以由几部分组成,我们就可以说这个东西是模块化的。乐高积木是最直观的例子了。</p>
<p>python中每个文件都是一个模块,模块意味着分治,可以把一个复杂的系统拆分成由简单的部分组成,这在大型程序中是非常有意义的。</p>
<p>模块是更高维度的抽象,可以把一组功能类似的函数,都放到一个模块中,一个复杂的模块也可以由更多简单的模块组成。</p>
<h3 id="创建模块">创建模块</h3>
<p>模块就是一个文件,文件中的函数,变量都可以被别的模块引用,也就是说模块的一切的公开的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># module1.py
qiu = 1
def hello():
return 'hello'
</code></pre></div></div>
<p>上面定义了一个模块,有一个公开的属性qiu和一个公开的函数hello。</p>
<h3 id="使用模块">使用模块</h3>
<p>在其他文件中可以用import关键字引用模块。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import module1
print module1.qiu
print module1.hello()
>>> 1
>>> hello
</code></pre></div></div>
<p>还可以换一种写法</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>from module1 import hello
print hello()
>>> hello
</code></pre></div></div>
<h3 id="标准模块">标准模块</h3>
<p>python中内置了很多系统函数,要使用这些函数需要引用相应的模块,比如:</p>
<ul>
<li>time</li>
<li>随机数</li>
</ul>
<p>time模块能够获取你的计算机时钟的信息,其中比较常用的就是sleep可以用来让程序暂停一会</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import time
time.sleep(1)
</code></pre></div></div>
<p>random模块用于生成随机数</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import random
print random.randint(0, 100) # 1-100之间随机数
print random.random() # 0-1之间随机数
>>> 4
>>> 0.5456454545
</code></pre></div></div>
<h2 id="打印格式化与字符串">打印格式化与字符串</h2>
<p>这一章讲了如何打印出格式化的内容。</p>
<h3 id="换行">换行</h3>
<p>可以在字符串中插入换行</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print "hello \nworld"
>>> hello
>>> world
</code></pre></div></div>
<h3 id="水平间隔制表符">水平间隔——制表符</h3>
<p>看了这本书我才知道制表符还有这个功能,一个制表符会占据8个字符的间隔,但是会和前面的字符串合并</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print 'aaa\txyz'
print 'aaaa\txyz'
print 'aaaaa\txyz'
>>> aaa xyz
>>> aaaa xyz
>>> aaaaaa xyz
</code></pre></div></div>
<h3 id="字符串插入变量">字符串插入变量</h3>
<p>python中在字符串中插入变量称为格式化字符串,使用百分号(%)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>str = '3'
print '1%s3' % str
>>> 123
</code></pre></div></div>
<p>%s表示插入字符串,还可以用%i用来表示整数,%f表示浮点数,都成为格式化字符串,用来指示如何显示 变量。</p>
<h3 id="数字格式化">数字格式化</h3>
<p>数字格式化可能是最复杂的部分了</p>
<p>%d和%i用来格式化整数,如果传入的是小数,则会进行截断</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>number = 12.55
print '%i' % numver
print '%d' % numver
>>> 12
>>> 12
</code></pre></div></div>
<p>%f或%F用来格式化浮点数</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>number = 12.1234
print '%f' % number # 默认显示六位小数
>>> 12.123400
print '%.2f' # 显示两位小数,末尾四舍五入
>>> 12.12
print '%+f' % number #强制显示证号
>>> +12.123400
# 如果希望正负数的列表对齐,但是整数前没有正号,可使用空格
number1 = -98.12
number = 98.12
print '% .2f' % number1
print '% .2f' % number2
>>> -98.12
>>> 98.12
</code></pre></div></div>
<p>%e和%E用来格式化E记法的数字</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>number = 12.1234
print '%e' % number
>>> 1.212340e+01
print '%.2e'
>>>> 1.2e+01
</code></pre></div></div>
<p>%g和%G用来自动表示为浮点数或E记法</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>number1 = 12
number2 = 1234567890
print '%g' % number1
print '%g' % number2
>>> 12
>>> 1.23456e+09
</code></pre></div></div>
<p>如果有多个格式化的字符串,需要使用元组</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>n1 = 1
n2 = 2
print '%i-%i' % (n1, n2)
>>> 1-2
</code></pre></div></div>
<p>还可以把格式化的字符串赋值给变量</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>str = '%.2f' % 12.123
print str
>>> 12.12
</code></pre></div></div>
<h3 id="格式化新方法">格式化新方法</h3>
<p>python2.6中引入了格式化的新方法,format函数</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print '{0:.1f}-{1:.1f}'.format(12.12, 11.12)
>> 12.1-11.1
</code></pre></div></div>
<p>格式化的符号用大括号括起来,冒号前面代表参数的索引。</p>
<h3 id="更多字符串处理">更多字符串处理</h3>
<p>如果想分解字符串使用split</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = '1,2,3,4'.split(',')
print list
>>> ['1', '2', '3', '4']
</code></pre></div></div>
<p>连接字符串使用join方法</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [1, 2, 3, 4]
str = ''.join(list)
print str
>>> 1 2 3 4
</code></pre></div></div>
<p>搜索字符串的方法有很多</p>
<ul>
<li>startswith()</li>
<li>endswith()</li>
<li>in</li>
<li>index()</li>
</ul>
<p>下面来距离各个方法的用法</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'abc'.startswith('a')
>>> True
'abc'.endswith(c)
>>> True
'b' in 'abc'
>>> True
'abc'.index('b')
>>> 1
</code></pre></div></div>
<p>删除字符串的一部分使用strip</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>str = 'abcdef'
print str.strip('a')
>>> bcdef
str = ' abc '
print str.strip() # 没有参数删除空格
>>> abc
</code></pre></div></div>
<p>改变大小写使用lower和upper</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>print 'abc'.upper()
>>> 'ABC'
print 'ABC'.lower()
>>> 'abc'
</code></pre></div></div>
<h2 id="文件输入与输出">文件输入与输出</h2>
<p>这一章讲了python如何读写文件。</p>
<p>文件的属性包括文字,类型,位置和大小等。</p>
<p>将一组文件组织在一起得东西称为目录,目录还可以组织目录,称为子目录。</p>
<h3 id="打开文件">打开文件</h3>
<p>对文件进行操作之前需要先打开文件</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>file = open('test.txt', 'r'); # 读文件
file = open('test.txt', 'w'); # 写文件
file = open('test.txt', 'a'); # 追加文件
</code></pre></div></div>
<h3 id="读文件">读文件</h3>
<p>读文件之前需要先打开文件,然后使用readlines将文件读到列表中,列表的每一项是文件中的一行</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>file = open('test.txt', 'r'); # 读文件
lines = file.readlines()
print lines
file.close() # 一定不要忘记关闭文件
>>> ['line1\n', 'line2\n', 'line3']
</code></pre></div></div>
<p>还可以使用readline每次读取一行文件</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>file = open('test.txt', 'r'); # 读文件
line1 = file.readline()
line2 = file.readline()
print line1
print line2
file.close()
>>> line1
>>> line2
</code></pre></div></div>
<p>如果想改变文件当前读到了第几行,可以使用seek</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>file = open('test.txt', 'r'); # 读文件
line1 = file.readline()
file.seek()
line2 = file.readline()
print line1
print line2
file.close()
>>> line1
>>> line1
</code></pre></div></div>
<h3 id="文本文件和二进制文件">文本文件和二进制文件</h3>
<p>文件分为文本文件和二进制文件,打开文本文件和二进制文件的方式有些区别</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>file1 = open('text.txt', 'r') # 打开文本文件
file2 = open('music.mp3', 'rb') # 打开二进制文件
</code></pre></div></div>
<h3 id="写文件">写文件</h3>
<p>写文件两种方式,第一种是覆盖现有文件,第二种是追加现有文件。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>file1 = open('text.txt', 'w') # 打开文件,后续可写
file2 = open('text.txt', 'a') # 打开文本,后续可追加
</code></pre></div></div>
<p>打开文件后可以使用write函数来往文件写内容</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>file = open('text.txt', 'w')
file.write('\nline1')
file.write('\nline1')
file.close()
</code></pre></div></div>
<p>还可以用print函数进行写文件</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>file = open('text.txt', 'w')
print >> file, "\nline1"
file.close()
</code></pre></div></div>
<p>上面的<code class="language-plaintext highlighter-rouge">>></code>符号的意思是重定向输出,即把输出重定向到文件,而不是显示器</p>
<h3 id="在文件中保存内容pickle">在文件中保存内容:pickle</h3>
<p>python中为在文件中存取对象提供了一个方便的模块——pickle</p>
<p>存储文件的过程如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import pickle
list = ['12', '12', '12', '12']
file = open('temp.pkl', 'w')
pickle.dump(list, file);
file.close()
</code></pre></div></div>
<p>还原的过程如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import pickle
file = open('temp.pkl', 'r')
list = pickle.load(file)
file.close()
print list
>>> ['12', '12', '12', '12']
</code></pre></div></div>
<h2 id="游戏">游戏</h2>
<p>书中还提到了很多小游戏,其中也介绍了很多制作游戏和UI的库,比如easyui,pygame等</p>
<p>举一个猜拳游戏的小例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import random
secret = random.randint(1, 99)
guess = 0
tries = 0
print "helow, guess"
print "number from 1 to 99, 6 times"
while guess != secret and tries < 6:
guess = input("guess is:")
if guess < secret:
print "too low"
elif guess > secret:
print "too high"
tries = tries + 1
if guess == secret:
print "success"
else:
print "fail"
print "secret is ", secret
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>这是我读《<a href="http://www.amazon.cn/gp/product/B0153174GS/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B0153174GS&linkCode=as2&tag=yanhaijing-23">父与子的编程之旅:与小卡特一起学Python</a>》后记录总结的一些笔记,书中还介绍了很多其他内容,如果你没看过我强烈建议你看一看这本书,如果看过了可以看这篇笔记用来快速回忆里面的内容。</p>
<p>最后感谢你的阅读。</p>
我的linux笔记
2016-06-28T00:00:00+00:00
http://yanhaijing.com/linux/2016/06/28/my-linux
<p>作为一个前端工程师,<a href="http://www.linux.org/">Linux</a>的知识是必不可少的,这块是我的弱项,我终于决定恶补一下了,最近老大也给我们下达了要会后端的死命令。</p>
<p>本文就是我学习linux的笔记,主要是方便自己记忆,希望也能帮到你。</p>
<p>我选择了《<a href="http://www.amazon.cn/gp/product/B00NT1IY3W/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B00NT1IY3W&linkCode=as2&tag=yanhaijing-23">跟阿铭学Linux</a>》这本书来学习,本文也是这本书的学习笔记。</p>
<p>我高度向大家推荐这本书,如果你是一个不太懂但很想学linux的人我建议你买这本书来学习,这本书的定位就是让小白和女友都能轻松学会的Linux教程,而且还有配套的视频教程,本人看完感觉浅显易懂,收获颇多。</p>
<h2 id="学习之初">学习之初</h2>
<p>万事开头难,兴趣是最好的老师</p>
<h3 id="linux简介">Linux简介</h3>
<p>Linux是有linus大神和社区开发出来一款开源操作系统(大神还开发了Git哦),很多地方和Unix很像</p>
<p>Linux的读音是[‘lɪnəks],中文谐音是“李呢克斯”</p>
<p>Linux有很多发行版本,其中比较有名的有Debian(1993)和Ubuntu(2004),Ubuntu实在Debain的基础上发展来的;Slackware(1993)和SUSE(1994),SUSE是Slackware的分支</p>
<p>还有RedHat系列,包括RedHat(1994),Fedora和CentOS,fedora是redhat的实验版本(测试版),centos(2003)和rhel几乎一样,本书推荐用的版本就是centos。</p>
<h3 id="linux管理员要养成的习惯">Linux管理员要养成的习惯</h3>
<p>学习linux要养成如下习惯:</p>
<ul>
<li>习惯使用命令行</li>
<li>操作要严谨</li>
</ul>
<h3 id="学习建议">学习建议</h3>
<p>linux内容多,命令多,学习的关键就是别急慢慢来,一点一点学,多记笔记,多练习,多用。</p>
<h2 id="安装linux">安装Linux</h2>
<p>安装linux的过程我建议大家看这篇文章《<a href="http://yanhaijing.com/linux/2016/08/21/how-to-install-linux-on-vm/">手把手教你在虚拟机里安装linux</a>》。</p>
<p>这里强调一下linux系统的分区原则:</p>
<ul>
<li>/boot/ 100MB</li>
<li>swap内存大小两倍,不高于16GB</li>
<li>/ 20GB</li>
<li>/data/ 剩余空间</li>
</ul>
<h3 id="设置网络">设置网络</h3>
<h2 id="远程登录">远程登录</h2>
<h2 id="linux文件和目录管理">Linux文件和目录管理</h2>
<h2 id="linux系统用户与用户组管理">Linux系统用户与用户组管理</h2>
<h2 id="lixnu磁盘管理">Lixnu磁盘管理</h2>
<h2 id="文本编辑工具vim">文本编辑工具Vim</h2>
<h2 id="文档的压缩和打包">文档的压缩和打包</h2>
<h2 id="安装rpm包和源码包">安装RPM包和源码包</h2>
<h2 id="shell基础知识">shell基础知识</h2>
<h2 id="正则表达式">正则表达式</h2>
<h2 id="shell脚本">shell脚本</h2>
<h2 id="linux系统日常管理">Linux系统日常管理</h2>
<h2 id="lamp环境搭建">LAMP环境搭建</h2>
<h2 id="lnmp环境配置">LNMP环境配置</h2>
<h2 id="常用mysql操作">常用MySql操作</h2>
<h2 id="总结">总结</h2>
Icon图片素材收藏集合
2016-06-19T00:00:00+00:00
http://yanhaijing.com/design/2016/06/19/icon-lib-website
<p>今天翻阅自己的浏览器收藏夹,突然发现一个叫做icon的目录,点击去发现收藏了很多icon素材的网站,╮(╯▽╰)╭,谁让我是松鼠癌晚期了,看见什么就喜欢屯着,然后顺手整理了一下。</p>
<p>本文将给大家分享一些查看图片素材的网站,希望大家喜欢,能够给大家的工作带来些方便。</p>
<h2 id="iconfinder"><a href="https://www.iconfinder.com/">iconfinder</a></h2>
<p>首先给大家推荐的就是icon finder,这网站的素材很多,很全,而且每个素材都有多种格式可选,并且都是矢量图,可以调整图片的尺寸。</p>
<p>但有一些是需要付费的,我们可以选中左边的free选项,这样搜出来的就都是免费的了。</p>
<h2 id="iconseeker"><a href="http://www.iconseeker.com/">iconseeker</a></h2>
<p>其次个大家推荐icon seeker,这里面的内容稍微少一些,但是都是免费的,搜索时可以按不同的尺寸搜索,图片都是位图,不能调整尺寸。</p>
<h2 id="iconarchive"><a href="http://www.iconarchive.com/">iconarchive</a></h2>
<p>icon archive和icon seeker很相似,功能大体相同。但素材更多一点。</p>
<h2 id="simpleicons"><a href="http://simpleicons.org/">simpleicons</a></h2>
<p>最后给大家介绍一下 simpleicon,这里面内容比较少,但都是svg格式的图片。</p>
<h2 id="iconfont"><a href="http://www.iconfont.cn/">iconfont</a></h2>
<p>感谢网友推荐的阿里巴巴矢量图标库,这个上面都是矢量资源,内容也很多,很不错。</p>
<h2 id="总结">总结</h2>
<p>有了这篇文章,终于可以把自己的收藏夹给清空了,好happy啊,如果你也有一些自己的私货,那么拿出来和大家分享吧。</p>
Web前端设计原则
2016-06-17T00:00:00+00:00
http://yanhaijing.com/web/2016/06/17/design-principles-of-web-front
<p>最近看了@<a href="http://yuguo.us/">余果</a> 的《<a href="https://www.amazon.cn/%E5%9B%BE%E4%B9%A6/dp/B0141BI0D2?ie=UTF8&camp=536&creative=3200&creativeASIN=B0141BI0D2&linkCode=as2&ref_=as_li_qf_sp_asin_il_tl&tag=yanhaijing-23">Web全栈工程师的自我修养</a>》颇有一些收获,刚好最近在制定经验前端的规范化工作,制定了几条前端设计原则,分享给大家。</p>
<p>下面就是经验的前端指导原则:</p>
<p>设计原则是指导性原则,比较抽象,可扩展</p>
<ul>
<li>KISS原则</li>
<li>DRY原则</li>
<li>最少知道原则</li>
<li>分治原则</li>
</ul>
<h2 id="kiss原则keep-it-simple-and-stupid">KISS原则(Keep it Simple and Stupid)</h2>
<p>一切尽可能简单,简单,简单!!!
简单就是美,能简单的绝不复杂</p>
<h2 id="dry原则dont-repeat-yourself">DRY原则(Don’t Repeat Yourself)</h2>
<p>顾名思义,任何一段代码如果需要复制第三次,需要进行抽象</p>
<h2 id="最少知道原则">最少知道原则</h2>
<p>高内聚,低耦合,任何事物应该保持对外界的最少知道原则</p>
<h2 id="分治原则">分治原则</h2>
<p>复杂的问题,负责的功能,应该拆分,由更小的部分组成</p>
<h2 id="总结">总结</h2>
<p>如果你对上面的规则有什么疑问或者建议,那就在评论区和我讨论吧,期待你的反馈哦。</p>
ES2015实战——面向未来编程
2016-04-27T00:00:00+00:00
http://yanhaijing.com/javascript/2016/04/27/es2015-practice
<p>昨天在知识体系做了一次分享,主题是ES2015实战,本文将分享内容整理成博文,帮助没记住,没听懂和错过的同学来看,同时也分享给大家,分享给更多的人。</p>
<p>本文基本是对分享内容的总结,但可能略有差异,或者说文字内容都是经过精雕细琢的,不会有分享现场的错误,所以说绝对值得一看。</p>
<p>开始之前警告一下,内容不少,建议略作准备,让我们开始我们的探险之旅吧,let’s go。</p>
<p>本文的大纲如下,和分享内容一致:</p>
<ul>
<li>开场</li>
<li>道歉</li>
<li>关于我</li>
<li>历史</li>
<li>新特性</li>
<li>实战</li>
<li>未来</li>
<li>总结</li>
<li>选择</li>
<li>参考资料</li>
<li>问答</li>
<li>彩蛋</li>
</ul>
<p>注:蛋疼的markdown并不支持锚点,还得手动写html,所以我就不加了,我是纯粹主义者,不能接受在markdown里插入html(其实是太懒),大家顺着往下看吧。</p>
<p>说明:每个大标题相当于上面的大纲,每个二级标题相当于ppt的一页(这把ppt搞成文章还真是不太容易)。</p>
<h2 id="开场">开场</h2>
<p>为了等大家的到来(总有人喜欢迟到),避免先到同学的无聊,我专门给大家准备了一个开场小游戏。</p>
<p><img src="/blog/256.jpg" alt="" /></p>
<p>相关链接:</p>
<ul>
<li>游戏:<a href="http://yanhaijing.com/color/">http://yanhaijing.com/color/</a></li>
<li>源代码:<a href="https://github.com/yanhaijing/color">https://github.com/yanhaijing/color</a></li>
</ul>
<h2 id="道歉">道歉</h2>
<p>先给大家道个歉,这个分享本来打算赶在15年分享,因为15年刚好是Web诞生25周年,也刚刚好是我的25周岁,我竟然和web同岁(沾沾嘻嘻),但拖延症很严重的我,一直拖到现在,直到开讲前的半小时我还在改ppt,我希望给大家最好的分享。</p>
<p>PS:其实我还专门买了本《战胜拖延症》的书,但一直拖到现在还没时间看,o(╯□╰)o</p>
<h2 id="关于我">关于我</h2>
<p>先做个自我介绍,这里不展开了,感兴趣的同学直接<a href="http://yanhaijing.com/yan_about/">点这里</a>。</p>
<h3 id="我的前端启蒙">我的前端启蒙</h3>
<p>10年开始接触前端,一路走来到现在刚好5年多一点,说起我的前端入门就像笑话一样,一路自己摸索过来。</p>
<p>开始时为了给安装包写说明文档,我想让我的说明文档更漂亮,所以用到了html,对没错就是这么low,如下图中的许可协议。</p>
<p><img src="/blog/257.jpg" alt="" /></p>
<p>我其实是像下面这样写的,现在看low到爆,但我当时觉得简直就是完美啊。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><font size="24">许可协议</font>
</code></pre></div></div>
<p><img src="/blog/258.jpg" alt="" /></p>
<h3 id="我的前端启蒙书">我的前端启蒙书</h3>
<p>为了制作安装包的文档,我觉得我得学学HTML,这个东西看起来不错啊,比C语言那个黑框框好多了,然后我就去了图书馆挑来挑去我就调到了下面这本书,我之所以挑下面是这本书,是因为这本书是彩色的,比较好看,当时也不懂,也没想那么多。</p>
<p>当时也看到了网页三剑客之类,如果但是入了三剑客的门可能就不能在这里给大家分享了,我可能去做flash了。</p>
<p><img src="/blog/259.jpg" alt="" /></p>
<p>好像也很low,囧。</p>
<p>相关链接:</p>
<ul>
<li><a href="https://book.douban.com/subject/3027532/">新编HTML网页设计</a></li>
</ul>
<h3 id="我的处女作">我的处女作</h3>
<p>前段时间我在微博晒了我的处女作,引发了很多晒出自己的第一个作品,今天在整理这个ppt的时候,我发现了我更早的一个前端作品。</p>
<p><img src="/blog/260.jpg" alt="" /></p>
<p>三个frame,也是不能更low了,我放这个当然不是让大家看我都low,而是想告诉大家web强大的兼容能力,html5中frame已经废弃了,但是浏览器仍然能够支持,20年前的网页如今依然能看,这么强大的平台,承担人类知识的传播者,简直就是再适合不过了。</p>
<p>强大的兼容性,得已于html和css的设计理念,设计之初就是向后兼容的,不认识的标签识别为行内元素,不认识的css属性直接忽略,而不是报错。</p>
<h3 id="我的前端路">我的前端路</h3>
<p>再来说说我的前端路,我10年接触前端,因为我主要是看学校图书馆的书来学习前端的,虽说09年HTML5已经定稿,但国内的环境其实会之后1年,然后国内图书一般会之后2-3年,然后图书馆的书一般会再落后1年,这一下就落后了5年,也就是说我开始时并不知道哪些书特别好,所以我最看是看到的都是03年左右的书,后来看得多了才慢慢知道了选择哪些书,所以我10年入门却还是经历了很多技术上的变革。</p>
<p>写着写着突然发现这部分简直可以单独写一篇文章了,╮(╯▽╰)╭。</p>
<p>先来说说我经历HTML变革如下:</p>
<ul>
<li>HTML 4</li>
<li>xhtml 1.1</li>
<li>html 5</li>
<li>html</li>
<li>html 5.1</li>
</ul>
<p>我最开始接触的是html4,我特别不能认识大写的英文,比如strong我认识,但是STRONG我要先写成小写才能认识,所以我特别讨厌大写的标签;后来接触了xhtml,感觉这个真是好啊,至少要求全部小写啊,这么严格的要求我特别喜欢,并奉为至尊。</p>
<p>再到后来xhtml2 失败,html5更空出世充当救世主,坐在我对面的舍友告诉我html5来了,标签又能大写了,也不用闭合,属性也不用双引号了,我当时就懵逼了,这简直是历史的倒退啊。</p>
<p>后来我接受了html5,因为我了解了她,她是那么美,语意标签,不再强迫开发者,慢慢理解了html5的思想,接受现实,因为浏览器就是这么做的,xhtml的理想在现实面前无法落地,散落了一地悲伤。</p>
<p>再后来whatwg将html5改为html,并标榜为活着的语言,还是因为浏览器就是这么做的,我也激动了,web本该如此,早怎么没想到呢。</p>
<p>知道最近w3c说要发布html5.1了,虽然html没有了版本的概念,但是w3c必须要整理出版本来,不然后面想做html解析器的人估计就懵逼了。</p>
<p>说完了html再来说说css,如下:</p>
<ul>
<li>css2.1</li>
<li>css3</li>
<li>css module</li>
</ul>
<p>开始接触的也是css2.1,感觉这个东西挺简单啊,但简直就是高大上啊,比c语言那个黑窗口厉害多了,我要学会全部的选择器,全部的属性,后来我真的做到了,每一个属性都如数家珍,横扫了图书馆每一本书籍。</p>
<p>再后来css3来了,我心里说真是什么鬼,css已经够用了,请不要再添加垃圾,虽然我极力反对,但我无能为力,没有人能听到我的呐喊,我除了接受什么都做不了,好吧我又被强奸了,但还高潮了,我爱上了css3,简直棒棒的,我也曾想过全部掌握每一个属性,后来发现my god,臣妾做不到啊。</p>
<p>再后来在知乎上被@winter老师点醒,css3其实也是不存在的,css2.1之后css就拆分了,因为它太大了,必须拆分,就想html是一样的。</p>
<p>说完了css再来说说js,如下:</p>
<ul>
<li>js</li>
<li>es3</li>
<li>es5</li>
<li>es6</li>
<li>es*</li>
</ul>
<p>历史的发展总是相似的,上面的经历,注定我的js入门也是悲剧的,先学js,后来学es3,再后来es5来了,我也是抵制的,js已经够用了,请不要再添加了,我的嗓子都嘶哑了,你们能不能听听我的呼声,开发规范的老古董能不能考虑考虑开发者的声音呢。</p>
<p>结果可想而知,我又爱上了es5,知道去年es6来了,真是日了狗了,class,proxies,generators,reflect轻饶了js吧,给js一条生路号码,js不需要这些垃圾的东西,这是java程序员才需要的狗屎。</p>
<p>结果吗当然就是今天这篇文章啦,我爱上了es6,并且我想推荐给你,让你也爱上。</p>
<p>再来说说我的浏览器,如下:</p>
<ul>
<li>ie6</li>
<li>ie7</li>
<li>ie8</li>
<li>firefox</li>
<li>opera</li>
<li>chrome</li>
</ul>
<p>说起来可能是悲剧,但我最开始开发用的浏览器竟然是ie6,后来基本在倒腾ie 6 7 8,可能我入门就选择最难用的浏览器(对开发来说),后来改用firefox,感觉这个加上firebug简直就是神器。</p>
<p>再后来很崇拜opera,因为我感觉这款浏览器简单,好用,后来有了chrome我就放弃了其他浏览器了,这个简直就是开发,生活全好用啊,特别是翻墙后就是无敌了。</p>
<p>相关链接:</p>
<ul>
<li><a href="http://yanhaijing.com/tool/2015/03/29/chrome-mutil-user/">Chrome多账户的妙用</a></li>
<li><a href="http://yanhaijing.com/tool/2015/03/30/my-chrome-plugin/">我的Chrome插件</a></li>
</ul>
<p>最后来说说编辑器之旅,工欲善其事必先利其器,一个程序员必须有一个好的编辑器,虽然熟能生巧,但你用notepad和sublime比都不在同一个级别里,你再怎么熟料也是比不过人家的,我的编辑器之旅如下:</p>
<ul>
<li>notepad</li>
<li>frontpage</li>
<li>dreamweaver</li>
<li>aptana</li>
<li>sublime</li>
</ul>
<p>从notepad一路走来,说多了都是泪,现在我推荐大家用sublime。</p>
<p>从写代码,到所见即所得,再到继续写代码,编辑器也在跟着换,目前sublime是我最喜欢的编辑器了,因为我喜欢他的速度。</p>
<p>相关链接:</p>
<ul>
<li><a href="http://yanhaijing.com/tool/2014/10/24/my-sublime/">我的 Sublime Text 2 笔记</a></li>
</ul>
<h2 id="历史">历史</h2>
<blockquote>
<p>以史为鉴可以明得失 —— 李世民
温故而知新可以为师矣 —— 孔子
学而不思则罔思而不学则殆 —— 孔子</p>
</blockquote>
<p>古人早就告诉我们历史的重要性,鉴于很多同学其实不太清楚js的历史,所在在开始学习新知识之前我们先来连接下js的历史。</p>
<p><img src="/blog/261.jpg" alt="" /></p>
<h3 id="网景">网景</h3>
<p>提到js的历史不得不提网景公司,有兴趣的同学可以自行查阅资料。</p>
<p><img src="/blog/262.jpg" alt="" /></p>
<p>这家公司开发了一个牛逼的软件,就是网景浏览器,网景浏览器风靡一时,成为了人们通往因特网的唯一入口,后来为了给浏览器扩展本地验证表的等的功能,于是乎创造了javascript。</p>
<p><img src="/blog/263.jpg" alt="" /></p>
<h3 id="mozilla">Mozilla</h3>
<p>后来在和IE的竞争中失败了,虽然失败了,但是还是很有钱和技术,于是乎成立了Mozilla基金会。</p>
<p><img src="/blog/264.png" alt="" /></p>
<p>Mozilla决定将自己的浏览器开源,于是乎有了firefox,火狐浏览器凭借出色的用户体验和丰富而插件,以及开源终于卧薪尝胆,改变了IE一家独大的局面,才有了后面的chrome横空出世,从而形成今天三分天下的局面。</p>
<p><img src="/blog/265.png" alt="" /></p>
<p>Mozilla带给我们web开发者最大礼物除了firefox还有开发者社区MDN,我现在经常在上面查阅自己不知道的知识。</p>
<p>相关链接:</p>
<ul>
<li><a href="https://developer.mozilla.org/zh-CN/">MDN</a></li>
</ul>
<h3 id="js之父">JS之父</h3>
<p>Brendan Eich花了10天时间设计了javascript,借鉴了c的语法,函数借鉴了scheme,原型借鉴了self。</p>
<p><img src="/blog/266.jpg" alt="" /></p>
<p>相关链接:</p>
<ul>
<li><a href="http://yanhaijing.com/javascript/2013/06/22/javascript-designing-a-language-in-10-days/">Javascript:10天设计一门语言</a></li>
<li><a href="https://brendaneich.com/">JS之父博客</a></li>
</ul>
<h3 id="js进化史">JS进化史</h3>
<p>下面的一张图其实完美呈现了javascript的进化史。</p>
<p><img src="/blog/267.jpg" alt="" /></p>
<p>开始时js其实叫做livescript,当时java正火,便打上了java的顺飞车,改名叫做javascript,开始时一直伴随Netscape发布,后来ie也加入进了战争,并开发了自己的JScript,网景便去ECMA提交了自己的语言,从此ECMA负责制定js语言的标准,因为刚好是第262号标准,所以变叫做 ecma-262,因为java的版权在java的公司sun手里面,所指便ecma便给自己的规范改名为ecmascript,下文简称ES。</p>
<p>1999年制定了ES3的规范,将近10年时间语言层面没有大的改动,知道08年结尾,筹划了10年之久的ES4被废弃了,而是推出了更少改动,更友好的的ES5。</p>
<p>2011年对ES5进修了一次修订,一直到2015年6月份,ES6的规发布了,并规定以后的ES规范以年份命名,每年发布一个小版本,再不会像ES6这种一下发布很多功能了。</p>
<p>下图是我从网上找到的一个关于重大事情发生的关键时间点的图,可以来看一下。</p>
<p><img src="/blog/268.jpg" alt="" /></p>
<p>相关链接:</p>
<ul>
<li><a href="https://zh.wikipedia.org/wiki/Ecma%E5%9B%BD%E9%99%85">Ecma国际</a></li>
</ul>
<h3 id="esjs">ES&JS</h3>
<p>需要区分ES(ECMAScript)和JS(JavaScript)的区别,我们常说的js可能包括es+宿主环境,当然有时候js也可指代es,sun只把js的名称使用权限售给了Mozilla。一般说es指代规范,js指代具体的实现。</p>
<p><img src="/blog/269.jpg" alt="" /></p>
<h3 id="方言">方言</h3>
<p>其实除了JS外还有一些其他的ES方言,比较出名的ActionScript 3,除此之外还有一些,比如:QtScript
和DMDScript等(我都没听过,囧)。其实如果可以的话你自己也可以按照规范写一个ES的方言。</p>
<p><img src="/blog/270.jpg" alt="" /></p>
<h3 id="tc39">TC39</h3>
<p>推进 JavaScript 发展的委员会</p>
<ul>
<li>会员都是公司</li>
<li>定期召开会议</li>
<li>会议由会员公司的代表与特邀专家出席</li>
</ul>
<p>TC39 实行的是协商一致的原则:通过一项决议必须得到<strong>每一位</strong>会员(公司代表)的赞成</p>
<p>下图是TC39的一些会员和其在github上的<a href="https://github.com/tc39">TC39 team</a>的公开成员。</p>
<p><img src="/blog/277.jpg" alt="" /></p>
<h3 id="tc39流程">TC39流程</h3>
<p>TC39的<a href="https://tc39.github.io/process-document/">工作流程</a>其实也可用下面一张图来总结:</p>
<p><img src="/blog/278.jpg" alt="" /></p>
<h3 id="ecma262-状态">ECMA262 状态</h3>
<p>可以在<a href="https://github.com/tc39/ecma262">这里</a>查看目前处于各个状态的ecmascript未来版本的特性。其中stage1-4分别对应相面的1-4。</p>
<h3 id="问题">问题</h3>
<p>最后抛给大家一个问题,这个问题没有答案,需要大家自己思考,就是TC39这种方式的优缺点,一个语言有一个团队制定规范和个人开发者制定规范的优缺点,可以参考xhtml2.0之死。</p>
<h2 id="新特性">新特性</h2>
<p><img src="/blog/276.jpg" alt="" /></p>
<h3 id="有哪些">有哪些</h3>
<p>我想初次了解ES6的人,一定会懵逼的,就像看到下面的图片一样,我勒个去了,这都是什么,其实ES6改动之多简直不亚于重新设计了一门语言,不说新增的新特性,可以说是原有的几乎每一项都有所修改,改动点不下于数百个之多。</p>
<p>面对如此之多的新特性,那么我们从何入手呢?改如何学起?</p>
<p><img src="/blog/279.jpg" alt="" /></p>
<p>相关链接:</p>
<ul>
<li><a href="http://yanhaijing.com/javascript/2015/09/11/learn-es2015/">ECMAScript 2015 简易教程</a></li>
</ul>
<h3 id="es规范">ES规范</h3>
<p>其实根据80 20原则,我们可以知道,这么多特性中常用的可能只有20%,就像之前的js,其中的精粹部分也只有100多页。</p>
<p>其实ES6中有很多特型都是为node而生,实际上这其中我们能够在平时常用到的,并且好用的可能也就是20%不到,所以大家不要有那么多恐惧。</p>
<p><img src="/blog/280.jpg" alt="" /></p>
<h3 id="模块系统">模块系统</h3>
<p>我们最先介绍的是模块系统,因为模块系统是社区做的最多探索的一块,也诞生了很多很多私有的模块系统,我在我的博文《<a href="http://yanhaijing.com/javascript/2015/03/28/js-module/">JavaScript模块的前世今生</a>》中有过一些阐述。</p>
<p>常见的模块系统有下面这些:</p>
<ul>
<li>AMD</li>
<li>CMD</li>
<li>commonjs</li>
<li>UMD</li>
<li>。。。</li>
</ul>
<p>如果你一个也不知道那么也没关系,因为从今天开始你就不需要这些,因为ES6为我们带来了全新的模块系统。</p>
<p>ES6模块系统的特性:</p>
<ul>
<li>静态模块(模块名不能是变量)</li>
<li>声明式语法</li>
</ul>
<p>可以像下面这样引入模块:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import {$} from 'jquery.js'; // es6
var $ = require('jquery.js')['$']; // amd
</code></pre></div></div>
<p>像下面这样导出模块:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export {$}; // es6
export.$ = $; // amd
</code></pre></div></div>
<p><strong>注意:从这里开始,负责任的我在每一个ES6代码下面都写上了对等的ES5实现方法</strong>
<strong>注意:从这里开始,负责任的我在每一个ES6代码下面都写上了对等的ES5实现方法</strong>
<strong>注意:从这里开始,负责任的我在每一个ES6代码下面都写上了对等的ES5实现方法</strong></p>
<p>ES6模块系统和AMD相比有一些不一样的思想,总结一下就是:</p>
<ul>
<li>按需引入 vs 全局引入</li>
<li>多点暴漏 vs 全局暴漏</li>
</ul>
<p>模块思想代码解释:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import {each, ...} from 'underscore.js'; // es6
var _ = require('underscore.js'); // amd
export {each, map, ...}; // es6
module.exports = _; // amd
</code></pre></div></div>
<p>需要注意的是目前还没有任何一款浏览器支持ES6模块系统,这主要是因为ES规范仅仅定义了一套引入和导出的规范,而没有定义具体如何实现,其实现方式在浏览器端和node端都有很多大争议,在ES7以及未来的规范中很可能解决这个问题,目前社区也在做积极的探索。详细内容可以参考这篇文章《<a href="https://segmentfault.com/a/1190000004940294">为何 ES Module 如此姗姗来迟</a>》。</p>
<p>所以目前为止我们想使用ES6的模块系统可以有两种方案:</p>
<ul>
<li><a href="https://github.com/systemjs/systemjs">SystemJS</a></li>
<li>transpiler(转换器),如ES6 module transpiler,babel,Traceur</li>
</ul>
<h3 id="推荐特性">推荐特性</h3>
<p>所以我们今天并不打算介绍每一个特型,不然这篇文章就写不完了,如果你想了解ES6的方方面面可以查看下面的参考链接,这里只会介绍我在之前文章中总结的我个人认为对于前端比较适合的特性。</p>
<ul>
<li>字符串</li>
<li>解构赋值</li>
<li>对象</li>
<li>数组</li>
<li>函数</li>
<li>Class</li>
<li>Promise</li>
</ul>
<p>相关链接:</p>
<ul>
<li><a href="http://yanhaijing.com/javascript/2015/09/04/try-es2015/">快来使用ECMAScript 2015吧</a></li>
</ul>
<h3 id="字符串">字符串</h3>
<p>ES6对字符串做了扩展,引入了一种新的字符串,可以支持字符串插值和多行字符串。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 字符串
`yanhaijing ${abc}`; // es6
'yanhaijing' + abc;
`yanhaijing
.
com`; // es6
'yanhaijing' +
'.' +
'com'; // es5
</code></pre></div></div>
<p>其中字符串插值还可配合模版一起使用,同时现在支持全部Unicode码了,另外还增加了一组字符串接口,感兴趣的同学可以深入了解,个人感觉比较实用的就是字符串插值和多行字符串了。</p>
<h3 id="解构">解构</h3>
<p>ES6带来了解构的功能,解构支持下面的一些场景:</p>
<ul>
<li>数组解构</li>
<li>对象解构</li>
<li>字符串结构</li>
<li>数值和布尔值的解构赋值</li>
<li>函数参数的解构赋值</li>
</ul>
<p>代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 解构
var arr = [1, 2, 3, 4];
var [first, second] = arr; // es6
var first = arr[0]; // es5
var second = arr[1]; // es5
var obj = {a: 1, b: 2};
var {a, b} = obj; // es6
var a = obj.a; // es5
var b = obj.b; // es5
function add([x, y]){
return x + y;
}
add([1, 2]) // 3
</code></pre></div></div>
<p>关于解构还有一些其他的黑魔法,感兴趣的同学可自行了解。</p>
<h3 id="对象">对象</h3>
<p>ES6对对象字面量进行了扩展,支持属性同名变量简写,属性名求值,属性为函数的简写等:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 对象
var a = 1;
var obj = {
a,
[a + 1]: 3,
add() {}
} // es6
obj[a+1] = 3; // es5
</code></pre></div></div>
<p>同时ES6还扩展了一批对象的接口,感兴趣的同学可自行了解。</p>
<h3 id="spread">Spread</h3>
<p>Spread是数组的一个新功能,可以快速实现数组的拷贝,需要注意的是这里是对直接子元素的浅拷贝。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 数组
var arr1 = [1, 2, 3];
var arr2 = [...arr1]; //es6 浅拷贝
var arr2 = [].concat(arr1); // es5
var arr2 = arr1.slice(0);
min(...arr2);
</code></pre></div></div>
<h3 id="函数">函数</h3>
<p>ES6的函数也带来了很多新特性,这里主要介绍下面三点:</p>
<ul>
<li>箭头函数</li>
<li>rest参数</li>
<li>默认值</li>
</ul>
<p>先来说说箭头函数吧,这个是为lamada表达式而生,个人感觉和数组方法结合起来特别好用。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 箭头函数
[1, 2, 3].map(x => x + 1); // es6
[1, 2, 3].map(function(x) {
return x + 1;
}.bind(this)); // es5
[1, 2, 3].filter(x => {return x > 2});
(x, y, z) => {***}
</code></pre></div></div>
<p><strong>注意:</strong>箭头函数中的this是绑定的。</p>
<p>再来说说rest参数,在之前可以通过arguments参数来模拟rest参数,但现在有了真正的rest参数了。</p>
<p>rest必须放在最后,代表后面的传入参数,其是一个真正的数组。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// rest参数
function aaa(...args) {
return args.join(',');
} // es6
function aaa() {
return [].slice.call(arguments, 0).join(',');
} // es5
function bbb(x, y, ...args) {
}
bbb(1, 2, ...[3, 4, 5]); // 和数组的spread结合起来简直完美
</code></pre></div></div>
<p>再来说说函数参数的默认值吧,有了这个就省了不少事了。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 默认值
function f(a = 1) {} // es6
function f(a) {
a = typeof a === 'undefined' ? 1: a; // es5
}
</code></pre></div></div>
<h3 id="class">Class</h3>
<p>ES6之前想要模拟一个class真是费了牛劲了,我们一般通过new 构造函数的方式来模拟,社区也是有很多种方法,光学会这些方法都得花费很多精力。</p>
<p>class典型的特色如下:</p>
<ul>
<li>公有共享属性/方法</li>
<li>公有静态属性/方法</li>
<li>公有特权方法(访问私有成员)</li>
<li>公有成员</li>
<li>私有静态成员/方法</li>
<li>私有成员/方法</li>
</ul>
<p>现在ES6为我们带来了原生的class,终于让世界重归一统。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// class
class Child extends Parent {
constructor() {
super();
this.value = 1;
}
get() {
return this.value;
}
}
</code></pre></div></div>
<p>关于这个问题我之前还写过两篇文章可以参考:</p>
<ul>
<li><a href="http://yanhaijing.com/javascript/2014/11/09/object-inherit-of-js/">JavaScript对象继承一瞥</a></li>
<li><a href="http://yanhaijing.com/javascript/2014/05/15/a-code-explain-javascript-oop/">一段代码详解JavaScript面向对象</a></li>
</ul>
<h3 id="promise">Promise</h3>
<p>Promise的典型状态如下,这里不做过多介绍,如果你感兴趣可以看我的这篇文章《<a href="http://yanhaijing.com/javascript/2015/09/16/es6-promise/">快来使用ES2015的Promise吧</a>》。</p>
<p>关于Promise我想说的就是思想的转变,以前我们是传入一个回调函数,现在返回给我们一个将来会改变的值。</p>
<p>Promise的兼容性目前还有很大问题,所以必须使用pollyfill库,具体可以查看我上面介绍的文章。</p>
<p><img src="/blog/281.jpg" alt="" /></p>
<p>我们一般像下面这样使用Promise</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// Promise
function async() {
return new Promise((resolve, reject) => {
window.setTimeout(() => {resolve(123);}, 1000);
});
}
async().then(function() {
// xxx
});
</code></pre></div></div>
<p>对应的回调函数代码如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// es5
function async(cb) {
window.setTimeout(function() {
cb();
}, 1000);
}
async(function() {
// ***
})
</code></pre></div></div>
<p>一个典型的回调地狱的例子如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function delay(time, cb) {
window.setTimeout(function() {cb()}, time);
}
delay(100, function() {
delay(200, function() {
delay(300, function() {
delay(400, function() {
delay(500, function() {
delay(600, function() {
delay(700, function() {
delay(800, function() {
console.log('yanhaijing.com');
})
})
})
})
})
})
})
});
</code></pre></div></div>
<p>用Promise后的典型代码如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function delay(time) {
return new Promise(function (resolve, reject) {
window.setTimeout(function() {resolve()}, time);
});
}
delay(100).then(function() {
return delay(200);
}).then(function() {
return delay(300);
}).then(function() {
return delay(400);
}).then(function() {
return delay(500);
}).then(function() {
return delay(600);
}).then(function() {
return delay(700);
}).then(function() {
return delay(800);
}).then(function() {
console.log('yanhaijing.com');
});
</code></pre></div></div>
<p>一个常见的误区我们不要像下面这样使用Promise:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>delay(100).then(function() {
delay(200).then(function() {
delay(300).then(function() {
delay(400).then(function() {
delay(500).then(function() {
delay(600).then(function() {
delay(700).then(function() {
delay(800).then(function() {
console.log('yanhaijing.com');
})
})
})
})
})
})
})
});
</code></pre></div></div>
<p>关于Promise的更多误区我们可以查看这篇文章《<a href="http://fex.baidu.com/blog/2015/07/we-have-a-problem-with-promises/">We have a problem with promises</a>》。</p>
<p>相关链接:</p>
<ul>
<li><a href="http://liubin.org/promises-book/">JavaScript Promise迷你书(中文版)</a></li>
</ul>
<h3 id="其他">其他</h3>
<p>这里还有很多其他新特性我们没有介绍到,如果你感兴趣可以自行了解,文章结尾的相关资料是个不错的参考:</p>
<ul>
<li>generators</li>
<li>unicode</li>
<li>module loaders</li>
<li>map + set + weakmap + weakset</li>
<li>proxies</li>
<li>symbols</li>
<li>subclassable built-ins</li>
<li>math + number + string + array + object APIs</li>
<li>binary and octal literals</li>
<li>reflect api</li>
<li>tail calls</li>
</ul>
<h2 id="实战">实战</h2>
<p><img src="/blog/275.jpg" alt="" /></p>
<h3 id="es5">ES5</h3>
<p>先来看看ES5目前的<a href="http://kangax.github.io/compat-table/es5/">兼容性</a>,从下图看兼容性还是很好的。</p>
<p><img src="/blog/282.jpg" alt="" /></p>
<h3 id="兼容性">兼容性</h3>
<p>再来看看ES6的<a href="http://kangax.github.io/compat-table/es6/">兼容性</a>,简直惨不忍睹啊。</p>
<p><img src="/blog/283.jpg" alt="" /></p>
<h3 id="我为什么支持抵制es6">我为什么支持/抵制ES6</h3>
<p>作为一个来自ES3时代的老古董,坚决反对ES4,经历并拥护ES5的变革的我,起初我对ES6是抵制的,对ES6是抵制的,对ES6是抵制的。</p>
<p>ES6在搞什么鬼,竟然不能向后兼容,这简直违背Web的设计理念,HTML,css都是向后兼容的,你竟然不向后兼容,对不起我不能使用你。滚,滚,滚。。。</p>
<h3 id="编译器转换器编码器">编译器/转换器/编码器</h3>
<p>其实直到我了解了编译器,才转变了我的想法,并开始支持和使用ES6。</p>
<p>编译器的灵感其实其实并不是凭空而生的,想coffeescript/typescript中早就开始使用了,我们可以把ES5比喻成汇编语言(目标语言),而降ES5+比喻成C语言(源语言),从而完成下面的转换。</p>
<p>这是一场思想的转变,ES5 是Web时代的汇编语言,编译不可避免。</p>
<p><img src="/blog/284.png" alt="" /></p>
<h3 id="无处不在的编译器">无处不在的编译器</h3>
<p>其实对于编译器,我们早就开始使用了,不信你看下面的列表,你肯定使用过其中一个呢。</p>
<ul>
<li>masm</li>
<li>gcc</li>
<li>javac</li>
<li>coffeescript/typescript</li>
<li>less/sass</li>
</ul>
<h3 id="转换器">转换器</h3>
<p>那么将我们的ES6转换成ES5的转换器有哪些呢?其实也是有不少的,比如:</p>
<ul>
<li>babel</li>
<li>Traceur(google)</li>
</ul>
<h3 id="babel">Babel</h3>
<p>我要给大家分享的就<a href="https://babeljs.io/">Babel</a>,通过babel我们在今天就可以使用ES6,甚至是ES7。</p>
<p><img src="/blog/285.jpg" alt="" /></p>
<p>Babel是一个杰出的编译器,前身是大名鼎鼎的6to5,后来为了顺应未来ES7,便改名为babel,不再局限于6to5。</p>
<h3 id="babel-5vs6">Babel 5vs6</h3>
<p>Babel最近推出了v6版本,关于版本的选择也是个艺术问题,我一般是推荐选择最新版的。</p>
<p>6相对于5最大的变化就是拥抱ES*,每个ES属性都是一个插件,而不是像之前那样都揉在了一起,我们可以自由拼装自己想要的特性,自由组合,每个团队都可以有自己的的选择权。</p>
<p>但目前6在npm2的架构下,安装起来太大了,而且问题也是蛮多的,我们可以坐等其成成熟了再来使用,现在可以先选择相对稳定的5.x,当然如果是尝鲜的话还是推荐6的,这个版本一旦选择了,以后就要谨慎更新,最好是写死不要更新。</p>
<h3 id="如何使用">如何使用</h3>
<p>Babel提供了一个在线的编译器,方便大家上手体验,感兴趣的同学请<a href="https://babeljs.io/repl/">猛戳这里</a>。</p>
<p>真正到项目中我们还是得解构构建工具来使用,Babel提供了各种构建工具的使用方法,可以<a href="https://babeljs.io/docs/setup/">查看这里</a>。</p>
<p>下面我们主要介绍如何在fis中使用babel。</p>
<h3 id="fis插件">fis插件</h3>
<p>如果你不了解fis可以先猛戳<a href="http://fis.baidu.com/">这里</a>进行了解,简单说就是一个前端集成构建工具,在fis中我们想使用babel,需要用到下面两个插件:</p>
<ul>
<li><a href="https://www.npmjs.com/package/fis-parser-babel-5.x">fis-parser-babel-5.x</a></li>
<li><a href="https://www.npmjs.com/package/fis-parser-babel-6.x">fis-parser-babel-6.x</a></li>
</ul>
<h3 id="fis配置">fis配置</h3>
<p>配置这个简单了,你想用哪个插件都写得很清楚了,这里就不展开了。</p>
<h3 id="兼容老代码">兼容老代码</h3>
<p>我们遇到最大的一个问题就是历史代码绝对不能用babel编译,不然可能各种报错,这里提供两种解决这个问题的思路:</p>
<ul>
<li>.es</li>
<li>.es.js</li>
</ul>
<p>可以将需要编译的代码后缀名设置为.es,或者.es.js,或者自定义的xxx.js。</p>
<h3 id="推荐规范">推荐规范</h3>
<p>我推荐大家使用babel-5.x这个插件,并且将需要编译的后缀名设置为.es。</p>
<p>这里唯一的一个缺点就是.es会绕过百度代码检查,这可能是一把双刃剑,长期来说不利于代码质量。</p>
<h3 id="优点">优点</h3>
<p>先来说说Babel的优点,Babel可以让我们现在就是用ES6,如果等着浏览器都支持恐怕要到2020年了,甚至现在就能让我们使用ESnext中的一些特性。</p>
<h3 id="缺点">缺点</h3>
<p>Babel的缺点也是显而易见的,比如需要引入编译流程,对于没有构建工具的团队是一大挑战,对于已经迈入前端工程化的团队也会带来额外的编译时间。</p>
<p>同时线上代码的排错问题也变得更难了,因为线上都是编译后的代码,虽然sourcemap能一定程度上解决这个问题。</p>
<p>Babel作为一个编译工具,其自身的问题也是不容忽略的,5升级6都是有风险的,起稳定性和兼容性是最大的问题,远没有gcc这种工具稳定得多。前段时间发生的left-pad事件对于我们的功能都是有威胁的。</p>
<p>Babel的局限性也是个问题,Babel只能解决语法层面的问题,api相关的还是需要shim来解决,还有一些是Babel和shim都不能解决的,我们只能坐等浏览器更新了,好消息是现在浏览器更新速度越来越快了,O(∩_∩)O哈哈~</p>
<h3 id="广告1">广告1</h3>
<p>终于来到我喜欢的广告时间了,如果你想体验es6,而又自己懒得搭环境,那么没关系我都给你搭好了,你可以适用我的<a href="https://github.com/yanhaijing/fis3-base">fis3-base</a>,拿来即用的fis3脚手架。</p>
<h3 id="广告2">广告2</h3>
<p>如果你原来适用baidutemplate,那么用了es6后,你一定会郁闷的,因为es6会全部是严格模式,而baidutemplate在严格模式下报错,我推荐你用我的<a href="https://github.com/yanhaijing/template.js">template.js</a>,和baidutemplate 100%兼容,同时支持严格模式。</p>
<p>你可能踩过的坑,我都踩过啦,O(∩_∩)O哈哈~</p>
<h2 id="未来">未来</h2>
<p>未来已经到来。</p>
<p><img src="/blog/274.jpg" alt="" /></p>
<h3 id="更多es规范">更多ES规范</h3>
<p>TC39和浏览器厂商现在打成了一致,会加快规范的发布速度,再也不会出现以前10年一个版本的问题,以后每年一个版本,ES7会在今年发布,ES8会在明年发布,每年的夏天6月份都会发布新的规范,同时规范将会以年份命名,比如今年夏天将会发布ES2016,会带来乘方运算符等新特性。</p>
<p>以后每个新版规范带来的特性都会变得更少,再也不会出现ES6这种一下这么多新特性的规范了,历史终将远去,并不会再次上演。</p>
<h3 id="精粹">精粹</h3>
<p>现在的 迫切希望能有个ES6语言精粹,因为我偏执的认为,新特性一定也有糟粕,坐等了,如果没人动手,我可能会尝试来做这个事情。</p>
<p>我还想要的是ES的前端子集,ES6有太多东西都是为了Node而生的,并不适合前端,这对于前端工程师学习成本还是蛮大的,我希望大家都能用最少的精力,做更多的事情,而不是恰恰相反,特别是对于团队内部的普及,这将起到事半功倍的效果,今天的这篇文章中提到的部分,可能就是我认为的精粹和前端子集部分。</p>
<h3 id="浏览器">浏览器</h3>
<p>目前主流的浏览器有chrome,firefox,ie,safari,现在的浏览器厂商已经达成一致,没6周会发布一个版本,ie6 10年一个版本的时代再也不会有了,ie团队更是激进每4周会发一个大版本。</p>
<p>这让我看到了js无限的潜能,感觉有些无限演进的意思,这个平台的活力真正还是爆发了。</p>
<h3 id="我们怎么做">我们怎么做</h3>
<p>首先需要说明下,我们特指前端FE,babel必不可少,我们可以定制自己的babel插件,然后等那个属性没有兼容性问题了就删掉这个插件。</p>
<p>未来已经到来,我们应该面向未来。</p>
<h2 id="总结">总结</h2>
<p>坚持看到这里的同学,我想先给你们一个鼓励,你们真是棒棒的,我想大家此刻的脑子里可能下下面这幅画一样,晕晕的,乱乱的,下面我们就来做个总结,帮你理理思路。</p>
<p><img src="/blog/273.jpg" alt="" /></p>
<h3 id="优点-1">优点</h3>
<p>ES6的优点非常明显,总结起来大概有如下:</p>
<ul>
<li>官方规范,趋势,面向未来,未来会原生支持</li>
<li>代码变现力更强了,代码行数更少</li>
<li>同时开发效率大大提升</li>
<li>减少对第三方库的依赖,比如underscore,promise等</li>
</ul>
<h3 id="缺点-1">缺点</h3>
<p>ES6没有缺点吗?没有,但是还是可以总结出来一些:</p>
<ul>
<li>ES6很多特性面向node.js,对前端无用</li>
<li>需要我们学习新东西,对某些人可能是挑战</li>
<li>js变得更复杂了,新人生手难度更大</li>
<li>这么多东西糟粕在所难免,需要去取精华</li>
</ul>
<h3 id="收益">收益</h3>
<p>借用babel在线编辑器提供的例子来看一下收益,实现同样逻辑功能的代码ES6是100行,而ES5却要200行,这收益简直就是100%啊。</p>
<p><img src="/blog/286.jpg" alt="" /></p>
<p>然而我们实际测试发现并没有这么完美,实际中大概能节省30%左右的代码量(工作量)。</p>
<p>其次更少的意味着逻辑更清晰,可维护性更好,一个人能完成的工作量也就更多了,哈哈。</p>
<p>开发时间可以大幅减少,开发消息大大提升。</p>
<h3 id="为什么">为什么</h3>
<p>下面我们来说说为什么要学习ES6,我想能看到这里的同学多半是有兴趣的,所以我给大家解惑一下,大概可以有一下几点:</p>
<ul>
<li>迟早要学(区别ts,cs),因为是规范,所以你迟早要学的,被动不如主动,晚学不如早学,规范即标准,你迟早要学</li>
<li>社区的趋势,和你团队的趋势都会想规范靠拢,目前的浏览器也是如此,大趋势不可违背</li>
<li>ES6的表现力更强,这往往意味着可维护性更高</li>
<li>学习成本很小,相信我只要你用心3天足够了,塌下心来尝试一下吧</li>
<li>编程激情,如果你有黑客精神,那么你应该学习,相信我ES6重新燃起了我编程的激情</li>
<li>如果你也不想加班,那么你该学习ES6,他会让你写代码变得更快</li>
<li>面试中的加分项,如果你想在出去面试,ES6会成为你的加分项</li>
</ul>
<h2 id="做出选择">做出选择</h2>
<p>下图出自黑客帝国,neo需要做出选择,是回到dream world,还是real world,关键是选择,但关键的关键是做出选择,用于不用就看你了。</p>
<p><img src="/blog/272.jpg" alt="" /></p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://www.ecma-international.org/ecma-262/6.0/">http://www.ecma-international.org/ecma-262/6.0/</a></li>
<li><a href="http://es6.ruanyifeng.com/">http://es6.ruanyifeng.com/</a></li>
<li><a href="http://www.infoq.com/cn/es6-in-depth/">http://www.infoq.com/cn/es6-in-depth/</a></li>
<li><a href="http://es6-features.org/">http://es6-features.org/</a></li>
<li><a href="https://github.com/lukehoban/es6features">https://github.com/lukehoban/es6features</a></li>
<li><a href="https://github.com/es6-org/exploring-es6">https://github.com/es6-org/exploring-es6</a></li>
<li><a href="http://yanhaijing.com/es5/">http://yanhaijing.com/es5/</a></li>
</ul>
<h2 id="相关资料">相关资料</h2>
<ul>
<li><a href="http://gank.io/post/564151c1f1df1210001c9161">给 JavaScript 初心者的 ES2015 实战</a></li>
<li><a href="https://www.h5jun.com/post/six-nifty-es6-tricks.html">六个漂亮的 ES6 技巧</a></li>
</ul>
<h2 id="问答">问答</h2>
<p>悲催的是没有一个人提问,╮(╯▽╰)╭,连我的托都听懵逼了。。。</p>
<h2 id="彩蛋">彩蛋</h2>
<p>一般来说到这里就该结束了,但作为一个负责人的步道师,我还是给大家准备了彩蛋。</p>
<p><img src="/blog/271.jpg" alt="" /></p>
<h3 id="值得关注的项目">值得关注的项目</h3>
<ul>
<li><a href="http://lebab.io/">lebab</a></li>
<li><a href="http://postcss.org/">postcss</a></li>
<li><a href="http://cssnext.io/">cssnext</a></li>
</ul>
<h3 id="我的一些开源库">我的一些开源库</h3>
<ul>
<li><a href="https://github.com/yanhaijing/zepto.fullpage">zepto.fullpage</a></li>
<li><a href="https://github.com/yanhaijing/data.js">data.js</a></li>
<li><a href="https://github.com/yanhaijing/console.js">console.js</a></li>
<li><a href="https://github.com/yanhaijing/loveTimeline">loveTimeline</a></li>
</ul>
<h3 id="我写的小游戏">我写的小游戏</h3>
<ul>
<li><a href="http://yanhaijing.com/fan1xia/">翻一下</a></li>
<li><a href="http://yanhaijing.com/inverter/">变色方块</a></li>
<li><a href="http://yanhaijing.com/linklink/new.html">连连看</a></li>
<li><a href="http://yanhaijing.com/snake/">贪吃蛇</a></li>
<li><a href="http://yanhaijing.com/Painter/">Painter</a></li>
</ul>
如何重构一个大型历史项目——经验详情页改版总结
2016-04-14T00:00:00+00:00
http://yanhaijing.com/program/2016/04/14/how-to-reconstruct-a-large-historical-project
<p>挺长时间没写博客了,你以为我去玩了吗?其实我是去做了一个大项目。</p>
<p>开场之前先来个热身吧,最近撸了一个小游戏《<a href="http://yanhaijing.com/color/">看你有多色</a>》,源代码在<a href="https://github.com/yanhaijing/color">这里</a>,欢迎大家交流。</p>
<p>项目做完后在内部做了次分享,本文就是这次分享的文字版,警告:本文略长,建议备好咖啡,来一段愉快的阅读之旅吧,我保证不会让你失望的。</p>
<p>本文将会介绍如何重构一个大型历史项目的方方面面。</p>
<p>本文将会按照下面的目录展开分享,本文仅讨论技术。</p>
<ul>
<li>简介</li>
<li>准备</li>
<li>时间</li>
<li>团队</li>
<li>技术</li>
<li>架构</li>
<li>边界</li>
<li>收益</li>
<li>总结</li>
</ul>
<h2 id="简介">简介</h2>
<p>这次要改版的是经验最重要的页面之一——<a href="http://jingyan.baidu.com/article/fdffd1f850e750f3e88ca169.html">经验wap详情页</a>,流量巨大,历史悠久,就导致这必然是一个复杂的问题。</p>
<h2 id="准备">准备</h2>
<p>项目上的准备就是PM提供的MRD和设计师提供的PSD,前端的准备基本是先熟悉旧版页面的逻辑,我本人其实也不熟悉这个页面。</p>
<h2 id="时间">时间</h2>
<p>项目开始前肯定要给出排期,那么接下来就说排期和时间管理的问题。</p>
<h3 id="预估时间">预估时间</h3>
<p>前端项目的时间预估最准确的就是从UE图预估,拿到UE图后将其拆分成各个组件,然后评估每个模块的开发时间,其实思想就是分治,将不可评估任务拆分成可评估任务。</p>
<p>这次的项目因为是就模块改版,还有一个维度就是看就模块老代码,这次老模块是一个X,工作量未知,所以留了一些buffer,结果还真派上了用场,在老代码里跌倒了几次。/(ㄒoㄒ)/~~</p>
<p>举个例子:</p>
<ul>
<li>导航 2h</li>
<li>头部 3h</li>
<li>尾部 4h</li>
<li>。。。</li>
</ul>
<h3 id="风险">风险</h3>
<p>这次遇到可能影响进度的风险点主要都是来自经验的老代码,历史数据结构,各种边界case。</p>
<p>有些东西,你不看老模块的代码是不可能知道的,所以还是要把老代码全部看懂才行。</p>
<h3 id="合作">合作</h3>
<p>本次两个人一起开发,就涉及到如何合作的问题,在开始之前我制定了一些规则。</p>
<p>才学了语义化的提交信息,我准备尝试一下,我指定的规范在这里《<a href="http://yanhaijing.com/git/2016/02/17/my-commit-message/">我的提交信息规范</a>》。</p>
<p>最后我们一共提交了100多个commit,基本上等同于100多个小功能,我提倡勤提交,这样方便两个人同步进度。</p>
<p>因为页面被拆分成了模块,而我们的项目中支持模块化开发,所以两个人合作起来还是挺容易的,每个人负责自己的模块,互不干扰。</p>
<h3 id="效率">效率</h3>
<p>根据人月神话来说,团队人越多可能效率越低,那么如何保证两个人的效率和一个人时是一样的呢?我们有如下利器来保证:</p>
<ul>
<li>模块化开发</li>
<li>良好的基础框架</li>
<li>合理的项目(功能)拆分</li>
</ul>
<h2 id="技术">技术</h2>
<p>接下来说说我们这次改版的技术细节吧。</p>
<h3 id="指导原则">指导原则</h3>
<p>技术上大体可以分为以下几类:</p>
<ul>
<li>新技术</li>
<li>陈旧技术</li>
<li>成熟技术</li>
<li>稳定技术</li>
</ul>
<p>对于新技术我们还是持敬畏的态度,这次的选型就是选择成熟稳定的技术。</p>
<h3 id="技术栈">技术栈</h3>
<p>来说说我们这次的技术栈吧</p>
<ul>
<li>fis-plus</li>
<li>sass</li>
<li><del>es6(新技术)</del></li>
<li><del>promise(新技术)</del></li>
<li>template</li>
<li>zepto,gmu</li>
</ul>
<h2 id="架构">架构</h2>
<p>架构是个最大的问题,我们从下面四个方面来说说:</p>
<ul>
<li>目录</li>
<li>结构</li>
<li>样式</li>
<li>行为</li>
</ul>
<h3 id="目录">目录</h3>
<p>我们的目录规范就是fisp的目录规范,主要目录如下:</p>
<ul>
<li>page 模版文件</li>
<li>test 测试数据</li>
<li>widget 组成页面的组件</li>
</ul>
<h3 id="模版">模版</h3>
<p>我们用的是smarty模版,对应的就是结构——HTML,比如我们的模版文件是index.tpl,其内部有各个组件拼装而成。</p>
<p>模版可以继承,我们的业务模版都继承通用模版,这样的好处就是方便给所有页面添加通用的一些东西。</p>
<p>还可以在父模版挖一些坑,在自模版填,这个功能叫做block。</p>
<p>举个例子吧,比如如下图我们在父模版挖一个title的坑,因为每个页面的title可能都不一样,在子页面我们会向填坑</p>
<p>再来说说widget,一般的一个页面会被拆分成一个个模块(组件),然后又每个模块去拼装页面。</p>
<p>举个例子,下面左边就是拆分完的页面,右边是伪代码。</p>
<p><img src="/blog/289.png" alt="" /></p>
<h3 id="样式">样式</h3>
<p>这次改版我们的样式决定使用sass这个预处理器。</p>
<p>我们将样式精心分类,总共有以下三类:</p>
<ul>
<li>公用css</li>
<li>组件css</li>
<li>mixin</li>
</ul>
<p>我们定义了两个mixin:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@mixin clearfix() {
&::after {
content: "";
display: table;
clear: both;
}
}
@mixin size($width, $height: $width) {
width: $width;
height: $height;
}
</code></pre></div></div>
<p>_variables.scss里面我们只定义了几个能够抽取成公共的颜色变量</p>
<p>再来说说组件样式,一个典型的组件代码大概如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@import "mixin";
@import "variables";
.wgt-step-read {
background: $bg-color;
a {
color: $color-primary;
}
.arrow {
@include size(5px, 9px);
}
}
</code></pre></div></div>
<p>其编译后的css如下所示</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.wgt-step-read {background: #fff;}
.wgt-step-read a {color: #333;}
.wgt-step-read .arrow {width: 5px; height: 9px}
</code></pre></div></div>
<p>用了sass后我们可能会忽视嵌套问题,任意嵌套,我们规定嵌套不能超过三层。</p>
<p><strong>小贴士</strong>:这里介绍一个css选择符的两种逻辑,我推荐的其实是中庸思想,不走极端。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.wgt-step-read .arrow // 嵌套
.wgt-step-read-arrow // 组合类名
.wgt-step-read ul p span // bad
.wgt-step-read .pv // 添加类名,解决上面嵌套层级的问题
</code></pre></div></div>
<p>再来说说我们样式的指导原则:</p>
<ul>
<li>wgt前缀(区分模块和其他类)</li>
<li>嵌套不要超过三层(添加类名解决)</li>
<li>合理使用类名(语义)</li>
<li>合理使用mixin(区分extend)</li>
<li>DRY</li>
</ul>
<p>最后说说布局之争,项目开始时我们有如下两种布局可以选择:</p>
<ul>
<li>px</li>
<li>rem</li>
</ul>
<p>最终我们选的还是传统的px,因为我们为我们是知识型页面,页面多为文字;其次px比rem更简单。</p>
<p>在我的逻辑看起来我用大屏是未来看到更多的文字,而不是更大的字体,如果我想看更大的字体我自己调整字体就好了。</p>
<h3 id="行为">行为</h3>
<p>行为这里展开讲的地方不太多,因为按照模块拆分以后,每个js都不太大,强调的一点就是不要再拼接字符串了,而是改用前端模版。</p>
<h2 id="边界">边界</h2>
<p>边界这块在内部分享中介绍了很多内容,这里绝大部分都不能展开叙述,因为可能会涉及到一些内部的数据;当然我会将一些比较通用的知识,不涉及经验的具体细节。</p>
<h3 id="起源">起源</h3>
<p>对于经验而言,经过了多年的沉淀,已经没有人能够熟悉的知道他的方方面面,那么我们如何理清楚,经验的一些属性、分类、边界情况呢?</p>
<p>答案就是追本溯源,而不是舍本逐末,我开始的出发方向就错了,我一头跳进了某一个case中,还好后来及时修正了这种错误的思想。</p>
<p>举个例子,我们是如何追本溯源的呢?大概从以下一些方面:</p>
<ul>
<li>产生经验的地方</li>
<li>后端数据</li>
<li>老代码</li>
</ul>
<p>最直观的其实就是生产经验的地方,也就是说每个case都能在这里找到出处。</p>
<p>其次就是看模版数据,再次就是看经验的老代码。</p>
<p>总而言之,经验的复杂程度超乎我的想象。</p>
<h3 id="一条线的变迁">一条线的变迁</h3>
<p>有太多内容都不能公开给大家,想来想去就说这个吧,还比较有意思。</p>
<p>对你没看错,就是下面这条线,就是她!!!</p>
<p><img src="/blog/292.jpg" alt="" /></p>
<p>看看我是怎么实现的吧,如下图,对你们看错,separator-line就是这条线</p>
<p><img src="/blog/293.jpg" alt="" /></p>
<p>不知道你会不会鄙视我,我选择了一种不太优雅的实现方式,为了装饰性增加了多余的html。</p>
<p>不知道你会如何实现这条线,来说说我的实现方式吧。</p>
<p>最开始时,我把它放在了tag-wp的bodder-bottom上,后来我发现tag-wp竟然可能不存在,o(╯□╰)o</p>
<p>我想那我就放在abstract-wp上吧,也许聪明的你猜到了解决,是的,这个元素也可能没有。</p>
<p>最后我只能搞一个单独元素出来了,o(╯□╰)o</p>
<p>这个问题其实我们也可以换个思路,就峰回路转了,我们可以不把这条线考虑成装饰性元素,而是考虑成分割线,是不是就好理解了呢,分割线就是该用一个元素来表示(hr),虽然我这里没有用hr,但是内心总是不那么忐忑了,谁让我是强迫症呢,不写出优雅的代码我会吃不下,睡不着的。</p>
<h2 id="收益">收益</h2>
<p>最后来说说这次改版的收益吧。</p>
<h3 id="可维护性">可维护性</h3>
<p>旧的代码,陈年失修,而且一直在打补丁,自我感觉改版后,可维护性上了一个量级,这里就不能给大家贴代码展示了。</p>
<p>不过有一个数据还是可以拿来展示一些的,其实新旧版都要处理同样的逻辑的,同样的逻辑旧版用了107行,而新版只用了73行。</p>
<h2 id="总结">总结</h2>
<p>这次改版让我明白了一个道理,页面中的任何元素都可能没有,需要考虑到容错处理;设计不能一蹴而就,往往需要反复打磨,反复修改,或者推翻重来;凭空的设计完美却不实用,好的设计必须结合产品内容,项目细节。</p>
<h3 id="优点">优点</h3>
<p>这次改版我认为在下面几点上都是做的不错的地方:</p>
<ul>
<li>架构</li>
<li>新技术</li>
<li>团队合作</li>
</ul>
<h3 id="缺点">缺点</h3>
<p>如果说优缺点的话,我认为唯一的缺点就是时间的预估了,时间预估上的失误还是很明显的。如果时间不能准确预估需要留好buffer。</p>
<h3 id="表扬">表扬</h3>
<p>我需要表扬我队友的效率,开发真的很快。比我快太多。</p>
<h3 id="批评与自我批评">批评与自我批评</h3>
<p>我要批评我队友的效率,复制粘贴了太多老代码,自己也没看,而且代码规范都没遵守。</p>
<p>我也要批评我自己的效率,想得太多,做的太少,应该提高自己的效率。</p>
<h2 id="附">附</h2>
<p>本文的<a href="http://naotu.baidu.com/file/5a45700dae2df3912c8587a8491b27fe?token=86b24083b15e144d">百度脑图</a>。</p>
我希望的世界
2016-04-06T00:00:00+00:00
http://yanhaijing.com/other/2016/04/06/i-hope-world
<p>最近在知乎上看到这个问题《<a href="https://www.zhihu.com/question/40914324/answer/89331468">同性爱人被农村的父母胁迫结婚,已被软禁,疑似遭遇强奸。我该怎么办?</a>》,我很是震惊,本来想着回答好多好多,最后我只回答了一句话:</p>
<blockquote>
<p>如果没有自由,活着还有什么意义</p>
</blockquote>
<p>那就来说说我希望的世界是什么样子吧。一个充满感性的诗人,一个无比理性的程序员。</p>
<h2 id="自由">自由</h2>
<blockquote>
<p>生命诚可贵,爱情价更高,若为自由故,两者皆可抛。</p>
</blockquote>
<p>在我看来自由是最重要的,如果一个人没有自由,那真的是活着没什么意义了。当然这里说的自由肯定是遵守法律的,你的自由不能夺走别人自由。</p>
<p>在我看来自由大概有几种:</p>
<ul>
<li>人身自由</li>
<li>财富自由</li>
<li>精神自由</li>
</ul>
<p>现在的我们其实都拥有人身自由,这点可能比古代稍好点,比如奴隶社会的奴隶可能没有人身自由(我认为我们大多数人和古时的奴隶处在同一个阶级),当然现在也有某些人没有人身自由,比如监狱里的人,他们都是影响了别人的自由,才会被夺走自己的自由。</p>
<blockquote>
<p>有钱的人想要什么?——更多的钱。</p>
</blockquote>
<p>每个人都想实现财务自由,财务自由了就不会有那么多烦恼了,然而是这样吗?其实你没有了现在的烦恼,你还会有其他烦恼,当然钱多点自然是好事,我个人也向往财务自由。</p>
<p>最后来说说精神自由,我其实觉得我们是有精神自由的,是这样吗?我认为是的,虽然我们接受的外界信息被控制着,但我认为我们还是有精神自由的,哪些人没有精神自由?想想吧。</p>
<p>最后还是强调,你的自由,不能建立在剥夺别人的自由之上。</p>
<h2 id="平等">平等</h2>
<p>我认为人和人之间应该是平等的,虽然这并不容易,我所认为的平等,并不是阶级地位的平等(阶级地位应该永远也不会平等),我所指的是另一个维度,人与人之间的平等,平等的前提是互相尊重。</p>
<h2 id="和平">和平</h2>
<p>《黑客帝国》里neo想要的就是和平,这个世界真的太需要和平了,和平的前提其实是平等,和平最大的敌人其实是利益,因为利益让人们有了私心,然后便是占有,战争。</p>
<p>国与国之间,人与人之间都需要和平,和平不易,须珍惜。</p>
<h2 id="总结">总结</h2>
<p>这就是我希望的世界,自由,平等,和平。</p>
HTML5视频的那些事儿
2016-03-12T00:00:00+00:00
http://yanhaijing.com/html/2016/03/12/html5-video
<p>最近在研究HTML5播放视频的事情,发现这个东西真是据复杂无比啊,我勒个去了,然后给团队做了个技术分享。</p>
<p>本文将总结下自己对视频研究的结果,做个记录,同时也能方便后来人的学习,涉及视频的方方面面。</p>
<p>刚刚突然发现了自己800年前做的一个<a href="http://yanhaijing.com/player/localPlayer.html">视频播放器</a>,才想起来当时好像也研究过一点。</p>
<h2 id="视频">视频</h2>
<p>其实视频早于Web而存在的,1990年Web诞生,但早在此之前视频就已经存在了,视频技术这么多年的发展,其历史复杂性超乎你的想象。同 时富文本格式本来就是一件复杂的事情,更是让视频变得复杂无比。</p>
<h3 id="常用格式">常用格式</h3>
<p>我们生活场可能遇到过下面这些格式的视频:</p>
<ul>
<li>rm/rmvb/mkv</li>
<li>avi(Audio Video Interleave)</li>
<li>flv(Flash Video)</li>
<li>mp4(MPEG-4 Part 14)</li>
<li>ogv</li>
<li>webm</li>
</ul>
<h3 id="视频组成">视频组成</h3>
<p>如果拍脑袋想的话,一个完整的视频大概有下面一些部分组成:</p>
<ul>
<li>meta信息</li>
<li>视频</li>
<li>音频</li>
<li>字幕</li>
<li>缩略图</li>
<li>其他信息</li>
</ul>
<p>视屏的组成大概如下图所示:</p>
<p><img src="/blog/249.png" alt="" /></p>
<p>下面三幅图都是mp4格式,但其视频编码确实不同的。</p>
<p><img src="/blog/250.png" alt="" /></p>
<p><img src="/blog/251.png" alt="" /></p>
<p><img src="/blog/252.png" alt="" /></p>
<h3 id="三种格式">三种格式</h3>
<p>我们平时说的mp4到底是什么?其实对于一个视频有三种格式,如下:</p>
<ul>
<li>容器格式</li>
<li>视频编解码器</li>
<li>音频编解码器</li>
</ul>
<p>以后再有人问你视频是什么格式的时候,一定要先问问对方问的是那种格式。</p>
<p>容器格式规定了一个视屏文件的视频编码器,音频编码器,字幕,缩略图等信息的格式,常用的视频格式如下,括号中是对应的文件后缀名。</p>
<ul>
<li>Ogg容器(.ogv)</li>
<li>MPEG4-容器(.mp4)</li>
<li>Webm容器(.webm)</li>
<li>Matroska容器(.mkv)</li>
</ul>
<p>再来说说视频编解码器,视频编解码器规定了压缩视频和播放视频时使用的算法,常用的视频编码如下:</p>
<ul>
<li>H.264(AVC Advanced Video Codec)</li>
<li>VP8</li>
<li>Ogg Theora</li>
</ul>
<p><strong>注意:AVC就是H.264。</strong></p>
<p>音频编解码器和视屏编解码器类似,规定了音频的压缩和播放的算法,常用的音频编码如下:</p>
<ul>
<li>AAC(Advanced Audio Coding)</li>
<li>MPEG-3</li>
<li>Ogg Vorbis</li>
</ul>
<h3 id="视频总结">视频总结</h3>
<p>说了这么多,总结起来就是下面的一张表格,下面列出了常用的视频文件格式和其对应的视频格式。</p>
<p><img src="/blog/253.png" alt="" /></p>
<h2 id="html5-video">HTML5 Video</h2>
<p>理清了视频的历史,该轮到今天的主角上场了,再来说说HTML5的Video,HTML5的Video包括如下的一堆内容:</p>
<ul>
<li>img audio video 富媒体标签</li>
<li>一堆属性</li>
<li>DOM接口</li>
<li>DOM事件</li>
<li>向后兼容</li>
<li>字幕</li>
</ul>
<h3 id="embed--flash">embed & flash</h3>
<p>在开始介绍Video之前先来介绍介绍在之前是如何在网页中播放视频的,在最开始的时候我们使用embed标签来嵌入插件的方式来播放的,其会调用系统上的原生播放器,如windows上的 media player,mac上的quick time等。</p>
<p>这样做的缺点就是这块区域完全是黑盒,无法和播放器进行通信,甚至不知道是否在播放。</p>
<p>使用的方式相对简单,只需下面的一行代码即可,其中src是视屏的路径。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><embed src="media/helloworld.swf" />
</code></pre></div></div>
<p>由于embed的缺陷,flash来了,感谢flash带来了很棒的体验,并且其装机量能够达到99%,这种做法优点是能够和播放器交互,也能定制皮肤等;但缺点也很明显,需要开发单独的播放器插件(虽然可以用别人开发好的),并且其使用方式略显复杂,需要依赖第三方插件。</p>
<p>播放flash的代码如下,我想没人能够记得住吧,就想XHTML的doctype一样,囧。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><object id="flowplayer" width="704" height="400" data="media/flowplayer-3.2.16.swf" type="application/x-shockwave-flash">
<param name="movie" value="media/flowplayer-3.2.16.swf" />
<param name="flashvars" value='config={"clip":"media/beach.mp4"}' />
</object>
</code></pre></div></div>
<h3 id="video标签">video标签</h3>
<p>video标签的灵感来源于img标签,都是富媒体,既然能用img引入图片,那么为何不能用video引入vide呢?于是便有了video标签。</p>
<p>在页面中引入一个视频的代码和引入图片一样简单。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><video src="media/butterfly.mp4" controls>
您的浏览器不支持 video 标签。
</video>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">src</code>是视频的路径,controls表示显示视频播放控件,默认是不显示的。标签之间的文字会出现在不支持video标签的浏览器中,作为后备内容出现,用来兼容不支持video标签的浏览器。</p>
<p>怎么样是不是很简单,其实video标签还有一些属性可以配置。属性列表可以<a href="http://www.w3school.com.cn/tags/tag_video.asp">查看这里</a>。</p>
<h3 id="video-dom">Video DOM</h3>
<p>video标签对应有Video对象,可以通过js进行操作。Video对象有一组属性和方法,同时还包括一组事件。</p>
<p>比如可以读取一个视频的时长和当前播放的时间,同时还能设置当前播放的时间,可以在视频暂停的时候添加自定义事件等。</p>
<ul>
<li><a href="http://www.w3school.com.cn/jsref/dom_obj_video.asp">属性列表</a></li>
<li><a href="http://www.w3school.com.cn/jsref/dom_obj_video.asp">方法列表</a></li>
<li><a href="http://www.w3school.com.cn/tags/html_ref_eventattributes.asp">事件列表</a></li>
</ul>
<h3 id="兼容性">兼容性</h3>
<p>浏览器对视频格式的支持各不相同,小一点的浏览器厂商比如firefox和opera不愿支持商业的视频格式(mp4),因为需要支付专利费,而大一点的厂商如微软苹果等,不愿支持开源的格式,因为可能有专利问题。</p>
<p>好消息是现在firefox也支持mp4了,而opera从12版本后就换成了webkit内核也不存在这个问题了。</p>
<p>下面看一下PC上的浏览器对视频格式的支持情况。</p>
<p><img src="/blog/254.png" alt="" /></p>
<p>再来看看手机上的兼容情况。</p>
<p><img src="/blog/255.png" alt="" /></p>
<h3 id="source">Source</h3>
<p>未解决兼容性的问题,HTML5也给出了解决办法,那就是source标签。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><video controls autoplay>
<source src="media/butterfly.mp4" type="video/mp4">
<source src="media/butterfly.webm" type="video/webm">
<source src="media/butterfly.ogv" type="video/ogg">
</video>
</code></pre></div></div>
<p>浏览器会最先尝试播放第一个视频,如果发现不支持会播放第二个,依次类推直到找到一个可以播放的,或者全部能播放。。。</p>
<p><strong>注意:浏览器支持video标签,不能播放视频的情况下是不会显示后备方案的,需要和不支持video标签的情况区分开。</strong></p>
<h3 id="字幕">字幕</h3>
<p>字幕也是一个复杂的问题,简单的一个字幕就可能有下面的需求:格式,换行,颜色,卡拉OK等。所以现存的字幕格式就有50多种。</p>
<p>使用字幕的方式和使用source的方式类似,同时可指定多个字幕文件,用来指代不同语言的字幕,用户可以自己选择想要的字幕。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><video controls loop autoplay>
<source src="media/butterfly.mp4" type="video/mp4">
<source src="media/butterfly.webm" type="video/webm">
<track src="media/butterfly.vtt" srclang="en" kind="subtitles" label="English" default>
<track src="media/butterfly_fr.vtt" srclang="fr" kind="subtitles" label="French">
</video>
</code></pre></div></div>
<p>vtt格式如下所示,标记了每个字幕开始出现的时间和消失的时间。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>WEBVTT
00:00:01.000 --> 00:00:03.000
Butterflies are lovely.
00:00:04.000 --> 00:00:08.000
Don't you think?
</code></pre></div></div>
<h3 id="一套方案">一套方案</h3>
<p>这里提供一套完整的方案,支持video的浏览器优先使用video,否则退化为使用flash,如果也不支持flash则退化为提示文案。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><video controls width="704" height="400">
<source src="media/beach.mp4" type="video/mp4">
<source src="media/beach.webm" type="video/webm">
<object id="flowplayer" width="704" height="400" data="flowplayer-3.2.16.swf" type="application/x-shockwave-flash">
<param name="movie" value="flowplayer-3.2.16.swf" />
<param name="flashvars" value='config={"clip":"media/beach.mp4"}' />
<p>您的浏览器不支持此视频</p>
</object>
</video>
</code></pre></div></div>
<p>当然整个流程其实也可以反过来,即优先使用flash。</p>
<h2 id="播放器">播放器</h2>
<p>很多时候我们不能使用video标签的播放控件,可能有种种原因,比如自定义皮肤。这时候需要我们自己写一个播放器。</p>
<p>头脑风暴一下我们大概需要实现以下功能:</p>
<ul>
<li>兼容性</li>
<li>自定义一套皮肤</li>
<li>各种api</li>
<li>各种消息(事件)</li>
</ul>
<p>如果你感兴趣可以自己尝试下,其实自己写一个完整的播放器还是很有挑战性的,好在社区已经为我们写好了,推荐两个不错的播放器插件。</p>
<ul>
<li><a href="http://videojs.com/">VideoJS</a></li>
<li><a href="https://www.jwplayer.com/">jwplayer</a>(收费)</li>
</ul>
<p>如果上面介绍的两个插件还不能满足你的需求,你可能需要更多的视频插件,可以<a href="https://www.awesomes.cn/repos/Media/Video">查看这里</a>。</p>
<h2 id="总结">总结</h2>
<p>好了到这里本文就结束了,如果你还有什么疑惑或建议那么在下面的评论区给我留言讨论吧。</p>
<p>本文提到的所有代码都可以在<a href="https://github.com/yanhaijing/video-demo">这里找到</a>。</p>
<h2 id="相关资料">相关资料</h2>
<ul>
<li>[移动端HTML5<video>视频播放优化实践](http://www.xuanfengge.com/html5-video-play.html)</video></li>
<li><a href="http://taobaofed.org/blog/2016/05/23/video-player/">视频播放的那些事</a></li>
</ul>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://www.amazon.cn/gp/product/B00VDSW71S/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B00VDSW71S&linkCode=as2&tag=yanhaijing-23">《HTML5秘籍》第二版</a></li>
<li><a href="http://www.amazon.cn/gp/product/B0081E9X0K/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B0081E9X0K&linkCode=as2&tag=yanhaijing-23">《HTML5程序设计》第二版</a></li>
<li><a href="https://zh.wikipedia.org/">维基百科</a></li>
<li><a href="http://www.w3school.com.cn/index.html">w3school</a></li>
</ul>
我为什么开通微信公众号
2016-02-29T00:00:00+00:00
http://yanhaijing.com/other/2016/02/29/why-i-open-weixin-public
<p>故事源于和伪原创网站代表——<a href="http://www.kuqin.com/shuoit/20150729/347289.html">酷勤网</a>的一次撕逼,深深的感觉到无力抵抗这些聚合网站,作为一个小小原创博主,付出了努力,但应该获得的回报却被别人掠夺了。</p>
<p>然而庆幸的你遇到的问题,别人多半早已遇到过,并且已经总结出经验,感谢社区,读了<a href="https://www.phodal.com">Phodal</a>的这篇文章《<a href="https://www.phodal.com/blog/fighting-for-origin-creative/">从个人博客到公众号:与聚合网站抗争的无奈辛酸史</a>》,让我有一种醍醐灌顶的感觉。</p>
<p>我开始了解并搭建了自己的微信公众号:<code class="language-plaintext highlighter-rouge">颜海镜</code>,欢迎关注哦,本文将介绍自己对微信公众号的一些理解。</p>
<p><img src="/blog/248.png" alt="" /></p>
<h2 id="抵触情绪">抵触情绪</h2>
<p>作为一个90后,QQ伴随我成长起来,当大家都在用微信的时候,我是抵触的,我觉得没有什么用啊,有一个就够了,直到后来工作后发现周围的同事都在用,才开始接触微信,开始觉得也没什么特别,渐渐成为重度用户后才发现微信的和QQ比的优点。</p>
<p>对于微信公众号,我也是抵触的,作为个人博客站长,我认为这个东西对我没有什么好处,远不及在微博这类社交媒体能带给我流量和知名度,是在想不出来这个东西对我有一点点的用处。</p>
<p>如果你也和我有同样的困惑,或者和我有类似的想法,不妨往下看吧。</p>
<h2 id="开始">开始</h2>
<p>我注册了<code class="language-plaintext highlighter-rouge">颜海镜</code>这个作为自己的公众号,为了让大家好搜索,我希望注册 <code class="language-plaintext highlighter-rouge">yanhaijing</code>这个微信号,但是发现已经被注册了。。。</p>
<p>注册了个人类型,简单搭好了环境,搭环境这个其实还是很简单的。</p>
<h2 id="为什么">为什么</h2>
<p>我最近才想明白微信公众号的意义——连接人与信息源,建立人与感兴趣的信息源之间的通道。这其实类似微博的大V与粉丝的关系。</p>
<p>人就是广大的微信用户,这个基数太大了,大到令人吃惊。而信息源是个广泛的定义,任何发出信息的点都可以是信息源,这从注册微信公众号时的分类也能看出来,简单列举一些:</p>
<ul>
<li>个人</li>
<li>公司</li>
<li>商家</li>
<li>…</li>
</ul>
<p>其实挺久以前在吉野家关注吉野家公众号的时候我就发现了这个问题,举个例子吧,对于吉野家这种商家,会希望将自己的优惠活动信息推送给自己的用户,怎么才能做到呢?自己做个app,然后让用户安装其实是挺让人讨厌的,而且成本略高(肯德基就是这么做的),关注的成本明显低多了。</p>
<p>然而既然信息能够送到自己的用户手里,谁又会关心是通过什么传递的呢,但是微信做了这件事,感觉百度的直达号就不是这么一个概念。</p>
<p>然而我以前对信息源的理解狭义了,最近才发现,原来我也可以是信息源,我的博客就是信息源,而对我博客敢兴趣的人就是信息接收者,通过微信公众号,可以第一时间看到我博客的内容更新了。</p>
<p>为了鼓励大家订阅,这个推送可以是优先的,也就是推送一段时间后才同步到博客,当然我还是希望耕耘自己的博客,而且会一直做下去,不希望经营微信公众号,占用太多的时间。</p>
<p>再次感谢<a href="https://www.phodal.com">Phodal</a>,他后来建了自媒体博主群,并把我拉了进去,让我能和大家交换更多知识。</p>
<h2 id="总结">总结</h2>
<p>希望这篇文章能带给你一些东西,如果你和我有同样的困惑,那么快快来开通微信公众号吧。下次有时间说说对微博的理解。</p>
<p>好了,你都读到这了,在不说出自己的公众号都感觉不好意思了;你都读到这了,在不关注也说不过去了吧,下面是我的微信公众号,快快关注吧,或者微信搜索:<code class="language-plaintext highlighter-rouge">颜海镜</code>。</p>
<p><img src="/blog/247.jpg" alt="" /></p>
我的jekyll笔记
2016-02-21T00:00:00+00:00
http://yanhaijing.com/jekyll/2016/02/21/my-jekyll
<p>我的博客用的就是用<a href="http://jekyllrb.com/">jekyll</a>搭建的,有几年历史了,同时《<a href="http://yanhaijing.com/basejs/">JavaScript简易教程</a>》也是基于jekyll搭建的,可以说我使用jekyll很长时间了,也算是比较熟悉了,最近我打算基于jekyll搭建一个新的东西,但却发现自己平时只是使用而且,竟然记不清搭建的流程,这篇博客就是由此产生。</p>
<p>本文介绍jekyll的安装,使用的方法,记录下来供自己和大家使用。</p>
<h2 id="简介">简介</h2>
<p><a href="http://jekyllrb.com/">Jekyll</a>是一个静态站点生成器,它会根据网页源码生成静态文件。它提供了模板、变量、插件等功能,所以实际上可以用来编写整个网站。</p>
<p><img src="/blog/217.png" alt="" /></p>
<h2 id="安装">安装</h2>
<p>jekyll是基于ruby开发的,所以依赖ruby环境,并且需要通过gem进行安装,具体过程我在下面两篇文章中有详细介绍,这里不在展开写了。</p>
<ul>
<li><a href="http://yanhaijing.com/jekyll/2011/12/30/run-jekyll-on-window/">在Windows系统配置Jekyll</a></li>
<li><a href="http://yanhaijing.com/tool/2015/08/25/my-gem-note/">我的gem笔记</a></li>
</ul>
<p>ruby环境安装好后,可以通过下面的命令安装jekyll</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem install jekyll # 或下面的命令
</code></pre></div></div>
<p>也可以指定安装版本</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem install jekyll --version=2.5.3
</code></pre></div></div>
<p>安装过程可能很慢,墙的问题在上面的文章中有解决办法,安装好后在命令行输入下面的命令可以查看是否安装成功</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jekyll --version
</code></pre></div></div>
<p>一般你会看到类似下面的输出,则代表你安装成功了,否则可能需要重装</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jekyll x.x.x # x.x.x 代表你安装的版本
</code></pre></div></div>
<h2 id="流程">流程</h2>
<p>第一步创建项目</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jekyll new myjekyll
</code></pre></div></div>
<p>切换到myjekyll目录,运行下面的命令即可</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jekyll server
</code></pre></div></div>
<p>然后打开浏览器的<code class="language-plaintext highlighter-rouge">127.0.0.1:4000</code>,即可查看网站效果。</p>
<h2 id="常用命令">常用命令</h2>
<p>记录一下常用的命令。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>jekyll help # 查看帮助
jekyll help subcommand # 查看子命令的帮助信息
jekyll new site-name # 创建一个新的
jekyll build # 构建
jekyll server # 开启本地服务器查看效果
jekyll server -P 4001 # 指定端口
jekyll server -w # 文件发生变化时,自动重新编译
</code></pre></div></div>
<h2 id="技术储备">技术储备</h2>
<p>如果你不了解<a href="http://wowubuntu.com/markdown/">markdown</a>和<a href="https://github.com/shopify/liquid/wiki/liquid-for-designers">liquid</a>,你可能需要先了解下,我这里简单介绍一下。</p>
<p><a href="http://wowubuntu.com/markdown/">markdown</a>是写文章的神器,可以用简单的文本格式代替html标记。</p>
<p>比如我们想写一个列表,只需像下面这样即可</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- 列表项
- 列表项
</code></pre></div></div>
<p>最终会编译成下面的html</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><li>列表项</li>
<li>列表项</li>
</code></pre></div></div>
<p>如果你想写博客那么可能需要掌握这个,可以专注写文章,而不是写那么冗余的html标记,markdown几乎支持常用的html标签,具体的语法可以查看相关链接里给出的网址。</p>
<p><a href="https://github.com/shopify/liquid/wiki/liquid-for-designers">liquid</a>是一个模版语言,是jekyll支持的一种,有点类似smarty,只不过是静态的模版语言,只能在编译的过程中进行替换。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><h2>我的jekyll笔记</h2>
</code></pre></div></div>
<p>上面的代码最终会编译成下面的代码,假设<code class="language-plaintext highlighter-rouge">page.title = 标题</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><h2>标题</h2>
</code></pre></div></div>
<p>liquid除了支持变量替换外还支持逻辑语法,具体可以查看相关链接中的链接。</p>
<h2 id="相关链接">相关链接</h2>
<ul>
<li><a href="http://jekyllrb.com/">jekyll官网</a></li>
<li><a href="http://jekyllcn.com/">jekyll中文站</a></li>
<li><a href="http://wowubuntu.com/markdown/">Markdown语法</a></li>
<li><a href="https://github.com/shopify/liquid/wiki/liquid-for-designers">Liquid模板语言</a></li>
</ul>
我的提交信息规范
2016-02-17T00:00:00+00:00
http://yanhaijing.com/git/2016/02/17/my-commit-message
<p>最近维护<a href="https://github.com/yanhaijing">GitHub</a>上的一些开源项目,一直对git的提交信息格式一直是个头疼的问题,一直在思索如何组织好这个提交信息。</p>
<p>最近看了阮一峰老师的一篇关于提交信息规范的文章(见文章结尾参考资料),才让我恍然大悟。一般来说,commit message 应该清晰明了,说明本次提交的目的。</p>
<p>格式化的提交信息有诸多好处,本文将整理一套自己用的提交信息格式,并在以后的项目中应用起来。</p>
<p><img src="/blog/218.jpg" alt="" /></p>
<h2 id="格式">格式</h2>
<p>提交信息包括三个部分:<code class="language-plaintext highlighter-rouge">Header</code>,<code class="language-plaintext highlighter-rouge">Body</code> 和 <code class="language-plaintext highlighter-rouge">Footer</code>。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><Header>
<Body>
<Footer>
</code></pre></div></div>
<p>其中,Header 是必需的,Body 和 Footer 可以省略。</p>
<h3 id="header">Header</h3>
<p>Header部分只有一行,包括俩个字段:<code class="language-plaintext highlighter-rouge">type</code>(必需)和<code class="language-plaintext highlighter-rouge">subject</code>(必需)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><type>: <subject>
</code></pre></div></div>
<h4 id="type">type</h4>
<p>type用于说明 commit 的类别,可以使用如下类别:</p>
<ul>
<li>feat:新功能(feature)</li>
<li>fix:修补bug</li>
<li>doc:文档(documentation)</li>
<li>style: 格式(不影响代码运行的变动)</li>
<li>refactor:重构(即不是新增功能,也不是修改bug的代码变动)</li>
<li>test:增加测试</li>
<li>chore:构建过程或辅助工具的变动</li>
</ul>
<h4 id="subject">subject</h4>
<p>subject是 commit 目的的简短描述。</p>
<ul>
<li>以动词开头,使用第一人称现在时,比如change,而不是changed或changes</li>
<li>第一个字母小写</li>
<li>结尾不加句号(。)</li>
</ul>
<h3 id="body">Body</h3>
<p>Body 部分是对本次 commit 的详细描述,可以分成多行。下面是一个范例。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>More detailed explanatory text, if necessary. Wrap it to
about 72 characters or so.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Use a hanging indent
</code></pre></div></div>
<p><strong>注意:</strong>应该说明代码变动的动机,以及与以前行为的对比。</p>
<h3 id="footer">Footer</h3>
<p>Footer 部分只用于两种情况:</p>
<ul>
<li>关联 Issue</li>
<li>关闭 Issue</li>
</ul>
<h3 id="关联-issue">关联 Issue</h3>
<p>本次提交如果和摸个issue有关系则需要写上这个,格式如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Issue #1, #2, #3
</code></pre></div></div>
<h3 id="关闭-issue">关闭 Issue</h3>
<p>如果当前提交信息解决了某个issue,那么可以在 Footer 部分关闭这个 issue,关闭的格式如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Close #1, #2, #3
</code></pre></div></div>
<h2 id="例子">例子</h2>
<p>说了半天不给个例子都是瞎扯淡,下面是一个完整的例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>feat: 添加了分享功能
给每篇博文添加了分享功能
- 添加分享到微博功能
- 添加分享到微信功能
- 添加分享到朋友圈功能
Issue #1, #2
Close #1
</code></pre></div></div>
<p>上面的提交信息应该能够自解释自己的意思了。</p>
<h2 id="总结">总结</h2>
<p>上面的规范基本够自己用了,我会根据自己的需要,扩展这份规范,你也可以直接使用或者制定自己的规范,快来让我们的提交会说话吧。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://www.ruanyifeng.com/blog/2016/01/commit_message_change_log.html">Commit message 和 Change log 编写指南</a></li>
</ul>
我的2015年总结
2016-02-05T00:00:00+00:00
http://yanhaijing.com/work/2016/02/05/my-2015
<blockquote>
<p>逝者如斯夫,不舍昼夜</p>
</blockquote>
<p>愿与不愿,选与不选2015年已经过去,如果给自己的15年打分的话,我给自己打给70分。</p>
<p>本文将总结自己的2015年的过往,今年还是和去年一样,分为工作和个人。然后再计划一下16年自己想做的事情。</p>
<p>赶在15年的尾巴写下这篇文章。</p>
<h2 id="工作">工作</h2>
<p>15年我为百度贡献了15万行代码,平均一个月1.25万+。</p>
<p>今年上半年主要负责知道这边的业务,下半年转战师傅,年底转战经验。</p>
<h3 id="百度知道">百度知道</h3>
<p>这一年总共做了11个专题活动,完善了NAbase。</p>
<p>去年做了半年活动,终于等到技术熟练了,并且<a href="http://zhidao.baidu.com/daily">日报</a>准备改版,完成了两期的日报改版,这其实是我到百度后自己负责的第一个大型项目。</p>
<p>后来又完成了<a href="http://zhidao.baidu.com/liuyan">真相问答机</a>的改版工作。</p>
<p>知识这边的less一直用的都是1.3.x,已经非常陈旧,我将其升级到了1.7.5,并整理了1.3到1.7的变化,并修复了升级过程中的问题。</p>
<p><img src="/blog/211.png" alt="" /></p>
<h3 id="百度师傅">百度师傅</h3>
<p>8月份我来到了<a href="http://shifu.baidu.com/">百度师傅</a>,主要负责百度师傅这边的一些工作,显示完成了选择服务按钮联动的改版,后来完成了我的地址和支付功能的开发。</p>
<p>期间完成了商家mis的开发。</p>
<p><img src="/blog/212.png" alt="" /></p>
<h3 id="百度经验">百度经验</h3>
<p>今年年底我开始接触经验这边的开发工作,先后开发了<a href="http://jingyan.baidu.com/z/spring-dinner/index.html">年夜饭</a>和<a href="http://jingyan.baidu.com/z/spring2016-custom/index.html">春节习俗</a>专题,年前完成了<a href="http://jingyan.baidu.com/tag?tagName=linux">经验tag页</a>的改版工作。</p>
<p><img src="/blog/213.png" alt="" /></p>
<h2 id="个人">个人</h2>
<p>15年是自己成长最快的一年,也经历了一些事情。</p>
<h3 id="技术">技术</h3>
<p>15年在技术方面自己接触了es2015,sass,fis3,mocha等,并用gitbook制作了自己的第一个诗集。</p>
<p><img src="/blog/214.png" alt="" /></p>
<h3 id="博客">博客</h3>
<p>15年共写了25篇博客,平均每个月两篇,写博客的收获很多,还会继续坚持下去。</p>
<p><img src="/blog/215.png" alt="" /></p>
<h3 id="开源项目">开源项目</h3>
<p>开源项目方面,可以查看我的<a href="https://github.com/yanhaijing">GitHub</a>,<a href="https://github.com/yanhaijing/zepto.fullpage">zepto.fullpage</a>支持起了知识体系10多个活动,和无数网友的需求。</p>
<p><a href="https://github.com/yanhaijing/template.js">template.js</a>已经应用到百度师傅和百度经验的项目中,用来代替baidutemplate。</p>
<p>将fis3应用到活动的开发中,并搭建了<a href="https://github.com/yanhaijing/fis3-base">fis3-base</a>。</p>
<p><img src="/blog/216.png" alt="" /></p>
<h3 id="健康">健康</h3>
<p>15年终于下定决心做了以前不敢做的事情,做了一个小手术,解决了尽10年来的负担,成大事者不纠结,虽然现在身体还没完全回复,祝自己16年健健康康。</p>
<h3 id="阅读">阅读</h3>
<p>15年阅读了14本书籍,包括技术,社交等,我将我的书单记录在了<a href="http://www.douban.com/people/yanhaijing/">豆瓣</a>。</p>
<h2 id="2016">2016</h2>
<p>我给自己安排了好多工作,努力做吧,不一定都能做完。见附里的脑图。</p>
<h2 id="总结">总结</h2>
<p>光阴荏苒,日月如梭,时间不等人,15年已然过去,或得或失,16年已经开始,继续加油吧。</p>
<p>感谢<a href="http://naotu.baidu.com/">百度脑图</a>,真是一个很好用的工具。</p>
<h2 id="附">附</h2>
<p>符完整脑图的链接</p>
<ul>
<li><a href="http://naotu.baidu.com/file/e47d0a9fc45c65a37a51e6441f059561?token=acfcd94a1b4f2abc">我的2015</a></li>
<li><a href="http://naotu.baidu.com/file/0175834048c7628b785f1f897002e9e2?token=b9ab855366716928">我的2016规划</a></li>
</ul>
奶糖日记
2016-02-01T00:00:00+00:00
http://yanhaijing.com/life/2016/02/01/naitang-note
<p>2015年7月12日,星期日,搬到了新家,一切收拾就绪后发现不能上网,就联系了修网线的师傅过来,由于网线断了,师傅去26楼接网线的时候发现了一只小兔基,女盆友看见后超喜欢(其实女朋友一直想要一只小兔基),修网线的师傅打算拿回家炖了,在我们的要求下便给了我们——这便是奶糖的来历。</p>
<p>下图是女盆友第一次抱着奶糖时的照片。</p>
<p><img src="/blog/182.jpg" alt="" /></p>
<h2 id="关于名字">关于名字</h2>
<p>关于给小兔基起名字,也是伤透了脑筋的事情,后来我一度想干脆叫它小兔基算了,有一段时间我们一直以叫狗的方式叫它,这个习惯一直延续到现在,也是它唯一能听的懂的名字,但这毕竟是个小名,后来女盆友想了一个好名字,我们决定叫它奶糖,来源就是大白兔奶糖,并且它收起爪子趴在地上时确实像个大奶糖。</p>
<p><img src="/blog/204.jpg" alt="" /></p>
<h2 id="日记">日记</h2>
<p>下面会记录奶糖日常生活中比较有意思的事情。</p>
<h3 id="纸窝窝">纸窝窝</h3>
<p>奶糖来的第一天特别的乖巧听话,为什么这么说呢,因为后面实在是太不听话了,我们找了一个纸箱子给他做窝窝,刚刚来到陌生环境的奶糖胆子特别小,一天时间都没出这个纸箱子,o(∩_∩)o 哈哈。</p>
<p>我们在箱子底下垫一些纸,每天更换,这样硬是撑了好久。</p>
<p><img src="/blog/205.jpg" alt="" /></p>
<h3 id="搬进新家喽">搬进新家喽</h3>
<p>后来我们发现纸箱子实在撑不下去了,因为奶糖会从箱子里跳出来,后来我们把箱子盖子竖起来,用胶带粘住,这样高度会加高一截,防止奶糖跳出来,然而道高一尺魔高一丈,这样也并不起作用了。</p>
<p>后来我们干脆把奶糖关到自己的卧室里,爱怎么跑怎么跑去吧,这样一直坚持到几天后,奶糖竟然蹦上了床,并且在奶糖第二次在床上便便和尿尿后,我们终于决定给奶糖买个新家了。</p>
<p>买这种东西当然是上淘宝了,在快递在路上的几天我们眼巴巴的盼着快递快点到,一边盼着奶糖别再上床了,实在没有床单可换了。</p>
<p>终于到了,手忙脚乱搭建起新家,然后把奶糖关了进去,失去了自由的奶糖,脸上写满了不高兴,然而我终于可以高兴起来了。</p>
<p><img src="/blog/206.jpg" alt="" /></p>
<p>奶糖的新家还是很赞的,有食槽和水槽,并且下面有底盘,屎和尿的处理就方便了很多,后悔的是没有买防侧漏的,奶糖经常会把尿尿到外面的地上。</p>
<p>自从住进来新家后,奶糖就有些忧郁,即便放出来时,脸上也是写满了不开心。</p>
<p><img src="/blog/207.jpg" alt="" /></p>
<h3 id="溜兔子">溜兔子</h3>
<p>听说过遛狗的,你听说过溜兔子吗?也是心血来潮,在给奶糖买新家的时候,淘宝店主送了一个栓兔子的绳子,但个人感觉更像是拴狗的,栓兔子的套太粗了,弄到最紧,奶糖还是进出自如,只能想办法把绳子系了个扣,总算跑不出来了。</p>
<p>出门啦,兔子还是和狗狗不一样,并不会顺着你牵着它的方向走,为了加快速度只能抱着它进电梯,出电梯,抱它的时间稍微久一点就开始<br />
蹬人了,奶糖的后腿也是惹不起的,蹬一下还是很疼的。</p>
<p>到了楼下奶糖就开始撒欢了,完全不听话,还好有绳子制约着,不然可能追都追不上啊,显示在楼下的花园里吃了些花,然后来到了停车场的草坪上,吃到了鲜草,兔子吃草只吃草尖,挑剔的很啊,最后放一张照片吧。</p>
<p><img src="/blog/208.jpg" alt="" /></p>
<h3 id="草">草</h3>
<p>开始我们给奶糖吃的是馒头,饼,黄瓜等,反正都是人吃的,后来发现兔子不吃草是不行的,刚好楼下的小区有草坪,便每天去楼下拔草,奶糖只爱吃下面图片中的那种草。</p>
<p>话说大家一定想不到一只兔子的杀伤力啊,没过多久楼下的校区就被吃光了,再后来方圆500米之内的草都吃光了。</p>
<p>开始没有发现,后来才发现,有一些狗和人在草里尿尿拉屎,真是恶心啊,每次下完雨是最开心的了,不然拔回来的草还要用水洗一遍,o(︶︿︶)o 唉</p>
<p><img src="/blog/209.jpg" alt="" /></p>
<h3 id="电线杀手">电线杀手</h3>
<p>没养过兔兔的人肯定不知道兔兔竟然是电线杀手啊,奶糖也没有逃离兔兔的天性,开始并没有发现奶糖的这个特点,后来不知道怎么就开始咬电线了,家里的好多线都被咬破了。</p>
<ul>
<li>手机充电线</li>
<li>ipad充电线</li>
<li>电脑充电线</li>
<li>路由器电线</li>
<li>网线</li>
<li>冰箱电线</li>
<li>插线板</li>
<li>。。。</li>
</ul>
<p>其实此刻我的内心是崩溃的,无数次拿起刀和锅,但都没下去手,不是舍不得,是不会剥皮,不会剥皮,不会剥皮。</p>
<p>说真的电线咬破了也没啥,主要是担心漏电,这个太危险了,后来电线能藏的都藏起来了,不能藏起来的也都用胶带粘起来了,奶糖便开始吃泡沫,塑料,。。。后来我把这些都藏起来了,奶糖便没得吃了。现在的奶糖已经进化为吃纸箱子了。</p>
<p>有一次特别有意思,一根橡皮筋,刚刚蹦到地上,我还没来得及捡,奶糖变抢先了一步,吃到了嘴里,我赶紧想从嘴里掏出来,但是还是晚了,当时我就想把它脑袋拧下来。</p>
<h3 id="可怜的绿萝">可怜的绿萝</h3>
<p>女票是个花花草草控,家里样了好多肉肉,各种各样的植物,其中女友样了一盆绿萝,说起这盆绿萝还是很有来历的,不展开说了,女票一只想拿这盆绿萝去冲吉尼斯世界纪录,已经养了快两米长了,结果没想到有一天被奶糖把尖给吃了,要知道蛇无头不行,尖没了,就不长了,问题是同样的事情发生了好几次。</p>
<p>每次犯了错误,奶糖都一脸无辜的样子,就好像不是它干的一样,关键是奶糖的智商根本不知道你在说它,打它它也不知道,越打它越往上凑,也是醉了。</p>
<h3 id="天生爱打洞">天生爱打洞</h3>
<p>说起兔子爱打洞那真是天性啊,成语里就有狡兔三窟的说法,奶糖面对到处都是水泥的墙,只能另辟蹊径了,家里的箱子,放衣服的袋子,全给打成洞了,(/ □ \)。</p>
<p>下面是奶糖钻袋子的一张照片,可不是只有喵星人才喜欢钻袋子哦。</p>
<p><img src="/blog/210.jpg" alt="" /></p>
<h3 id="垃圾桶">垃圾桶</h3>
<p>奶糖对垃圾桶是情有独钟的,不把垃圾桶弄倒了誓不罢休,立起来弄到了,这个能玩一天了。</p>
<p>而且不管任何东西只要放到垃圾桶里就喜欢吃了,平时不吃的东西也会吃的津津有味,也不知道为何放到垃圾桶里就变得好吃了。</p>
<h2 id="总结">总结</h2>
<p>如果你想获得关于奶糖的最新信息,那么可以关注微博<a href="http://weibo.com/p/1005053963363396">@爱猫族的快乐生活</a>,我会在这里每天发布奶糖的生活照。</p>
我的mocha笔记
2016-01-18T00:00:00+00:00
http://yanhaijing.com/tool/2016/01/18/my-mocha
<p>最近(<a href="https://github.com/zry656565">Jerry Zou</a>)提议将<a href="https://github.com/yanhaijing/data.js">data.js</a>的单元测试工具替换为<a href="http://mochajs.org/">mocha</a>(原来的是<a href="http://qunitjs.com/">qunit</a>),并且主导进行了整个过程,感谢社区,让我有动力了解这个优秀的工具。</p>
<blockquote>
<p>data.js 是带有消息通知的数据中心,我称其为会说话的数据。旨在让编程变得简单,世界变得美好</p>
</blockquote>
<p>本文将记录一些学习的笔记和使用过程中遇到的问题。</p>
<h2 id="关于">关于</h2>
<p>我理解mocha是一个测试框架,特点是支持node和浏览器端,断言工具自由化,并有很多扩展。</p>
<p><img src="/blog/219.png" alt="" /></p>
<p>从前用qunit做测试,node需要用nodeunit,需要维护两套测试代码,非常不方便,有了mocha后就可以只维护一套了。</p>
<h2 id="断言">断言</h2>
<p>mocha只是一个测试框架,只提供了测试套件,具体的断言工具还需要在挑选,比较流行的是<a href="http://chaijs.com/">chai</a>,chai有很多选择,可以选择tdd,bdd,除此之外你还可以在<a href="http://mochajs.org/#assertions">这里</a>挑选更多的断言工具。</p>
<p>我们开始使用的是chai,后来发现在ie9一下不能run起来,就只能放弃了,换成了<a href="https://github.com/Automattic/expect.js">expect.js</a>。</p>
<h2 id="安装">安装</h2>
<p>全局安装mocha</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install -g mocha@~2.3.4 # 安装mocha
</code></pre></div></div>
<p>本地安装</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install -g mocha@~2.3.4 --save-dev
</code></pre></div></div>
<p>安装expect.js</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install -g expect.js@~0.3.1 --save-dev
</code></pre></div></div>
<h2 id="运行测试">运行测试</h2>
<p>写好单元测试后运行下面的命令运行测试:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mocha test
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>代码例子可以查看<a href="https://github.com/yanhaijing/data.js">data.js</a>,整体下来感觉mocha还是很赞的,用起来也比较舒服,回头有时间把项目的测试框架全部换成mocha,o(∩_∩)o 哈哈。</p>
<p>我相信你看完我的笔记肯定学不会mocha,没关系看看参考资料的内容吧,我也是看着这个学习的。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html">测试框架 Mocha 实例教程</a></li>
</ul>
我的Bower笔记
2016-01-18T00:00:00+00:00
http://yanhaijing.com/tool/2016/01/18/my-bower-note
<p>Bower是一款针对浏览器端的库管理工具,本文将记录bower的一些常用内容。</p>
<p><img src="/blog/230.jpg" alt="" /></p>
<h2 id="安装">安装</h2>
<p>Bower基于node.js,所以安装之前,必须先确保已安装node.js</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install bower -g
</code></pre></div></div>
<p>安装好后运行下面的命令可以看到版本号</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bower -v
</code></pre></div></div>
<p>查看帮助文档</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bower help
</code></pre></div></div>
<h2 id="常用操作">常用操作</h2>
<p>接下来记录一些常用操作命令</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bower init # 初始化
bower install # 安装bower依赖
bower install backbone[#1.11.3] # 安装一个具体的库[指定版本]
bower uninstall jquery # 卸载
bower update jquery # 更新
bower list # 列出全部的库
bower search jquery # 搜索
bower info jquery # 查看一个库的具体信息
bower register jquery git://github.com/jquery/jquery # 注册一个包
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>目前bower基本已经过时了,记录一下供自己使用吧。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://javascript.ruanyifeng.com/tool/bower.html">Bower:客户端库管理工具</a></li>
<li><a href="http://blog.fens.me/nodejs-bower-intro/">bower解决js的依赖管理</a></li>
</ul>
津游记
2016-01-02T00:00:00+00:00
http://yanhaijing.com/life/2016/01/02/one-day-in-tianjin
<p>2016年的第一天,去了趟天津,一为游玩,二为看人,本文将记录下所见,所闻,所感,文体类似游记,不如命名为《津游记》吧(其实就是天津一日游(/ □ \))。o(∩_∩)o 哈哈</p>
<h2 id="准备">准备</h2>
<p>提前一天买了火车票,是城际列车,北京南站到天津站,54.5¥,30分钟车程,按捺不住❤中的小激动。</p>
<p>出发的前一天晚上,准备了一个背包,两个苹果清洗干净,两根香蕉,给充电宝充上电(出门已不能不带这个了,不然都不敢玩手机),准备好口罩(据说明天爆表啊(/ □ \))。</p>
<p>洗了个澡,然后订好闹钟,早早躺在床上,做了个好梦啊。</p>
<p><img src="/blog/190.png" alt="" /></p>
<h2 id="启程">启程</h2>
<p>叮铃铃,叮铃铃……,起床啦,收拾好行装出门啦,电梯里发生了一点小摩擦,后来没赶上提前预定的车次,改签了车次,也没了座位(┬_┬)</p>
<p><img src="/blog/191.gif" alt="" /></p>
<h2 id="火车站">火车站</h2>
<p>下了车最先来到的是站前广场,说实话还是有一点震撼的,好大的广场啊,有图有真相。</p>
<p><img src="/blog/192.jpg" alt="" /></p>
<h2 id="世纪之钟">世纪之钟</h2>
<p>首先来到是世纪之钟,第一感觉就是这东西好奇怪啊,好多齿轮,还有个有很多大大的钢珠的轮子,建议感兴趣的同学可以去看一看。</p>
<p><img src="/blog/195.jpg" alt="" /></p>
<h2 id="海河">海河</h2>
<p>接下来来到的是海河,后面还沿着海河走了一段路,河里是冻起来的的,然后又不是冻得很平整,一块一块的冰拼接在了一起,像是被人打碎过,又被大自然动起来一样。</p>
<p>冰面上有很多红色的小飞机,上面还有字,大概承载别人的祝福吧。</p>
<p>沿着河岸走了一段,竟然发现这边的河面没有结冰,看着水漫上冰岸,然后又缩回去,我也懂得一哆嗦,河面上飘着几只鸟,应该是海鸥吧,也有在天上飞的,滑翔而过。</p>
<p>走着走着河岸便没了护栏,往河里看了一眼,看不到底,吓得我一缩脖了,不由得想着掉下去坑定会上不来吧,心理想着,这没有护栏的多危险啊,我还是离远远的比较有安全感。</p>
<p><img src="/blog/199.jpg" alt="" /></p>
<h2 id="解放桥">解放桥</h2>
<p>就是踏着这座桥走过了海河宽阔的湖面,桥的中间是机动车道,两边是行人道,行人道使用木板铺成的,踩上去咯吱咯吱的响,随时会断的感觉。</p>
<h2 id="彩虹桥大沽桥">彩虹桥(大沽桥)</h2>
<p>这座桥看起来有点奇怪,两个半圆,一大一小,白天看也没什么特别,可当我晚上回来的时候路过,发现真的是美轮美奂啊,有图有真相,见过最美的桥没有之一。</p>
<p><img src="/blog/196.jpg" alt="" /></p>
<h2 id="天津环球金融中心">天津环球金融中心</h2>
<p>这个其实算是灯塔了,出了火车站,就一直朝着它的方向走来,有点擎天博玉柱的姿势,笔直的直冲云霄,外面有好多棱面,整齐划一。</p>
<p><img src="/blog/198.jpg" alt="" /></p>
<h2 id="滨江道">滨江道</h2>
<p>这个是主角了,就是来这里逛得,最先映入眼帘的一棵高高大大的圣诞树,足有10米高,见下图</p>
<p><img src="/blog/197.jpg" alt="" /></p>
<p>街道两旁各种店铺,有买衣服的,有卖鞋的,有卖吃的的,琳琅满目,真可以用一段鼠来宝来形容:</p>
<blockquote>
<p>打竹板,进街来,买卖铺子两边排,也有买来也有卖,也有幌子和招牌——天津交通广播电台</p>
</blockquote>
<p>不得不说的就是人了,人真是太多了,川流不息啊,接踵擦肩。</p>
<p>去了美特斯邦威店,给女票挑了一件枣红色的外套,然后又去逛了森马店,然后为了给下午听相声预留时间,赶紧去吃饭了,吃了个奇石咕噜鱼,吃货快来膜拜吧。</p>
<p><img src="/blog/200.jpg" alt="" /></p>
<p>晚上又去了小吃一条街,吃的真是太多了,吃货的竟然不知道选哪个,最终选择大鱿鱼和煎饼果子,又要发图啦!!!</p>
<p><img src="/blog/201.jpg" alt="" /></p>
<h2 id="名流茶馆多伦道店">名流茶馆(多伦道店)</h2>
<p>吃过午饭就赶来茶馆听相声了,去的是名流茶馆,名流茶馆历史悠久,名字还是相声泰斗马三立马老给提的名字。</p>
<p>买了门票,80¥一张好贵的感觉,不过作为还行坐在第一排</p>
<p><img src="/blog/202.jpg" alt="" /></p>
<p>老规矩,第一段是快板书,说的是传统快板绕口令,年轻演员,感觉词倒是都对啊,就是不太溜,下来,换我上去,<(︶︿︶)_╭∩╮╭∩╮</p>
<p>接下来的相声都是传统相声,没有太大意思,开头的两个没啥意思都是年轻演员,后面的还行,还能记得节目清单如下:</p>
<ul>
<li>绕口令</li>
<li>典当论</li>
<li>拴娃娃</li>
<li>。。。</li>
</ul>
<p><img src="/blog/203.jpg" alt="" /></p>
<h2 id="返程">返程</h2>
<p>返程啦,原路返回的,还是两张车票,109¥,30分钟车程,累的不行了,回到家倒头便睡。</p>
<h2 id="线路图">线路图</h2>
<p>好了来看看我的线路图吧,能给你那个鸟瞰图。</p>
<p><img src="/blog/193.png" alt="" /></p>
<p>然而一张图并放不下,再来一张吧,接着上面那张哦。</p>
<p><img src="/blog/194.png" alt="" /></p>
<h2 id="总结">总结</h2>
<p><strong>注:</strong>部分图片来源于网络。</p>
北京医保异地就诊报销问题
2015-12-29T00:00:00+00:00
http://yanhaijing.com/life/2015/12/29/medicare
<p>最近本人在外地发生了一次就医,了解了下北京医保异地就诊报销的所需的资料和流程,整理在此,方便他人和自己。</p>
<h2 id="前提">前提</h2>
<blockquote>
<p>本市参保人员因公外出和探亲期间,在本市行政区域外突发疾病不能回京治疗的,可在当地一家县级(含)以上基本医疗保险定点医疗机构就医,医疗费用按本市基本医疗保险有关规定审核支付——《北京市基本医疗保险参保人员就医管理暂行办法》第十二条规定</p>
</blockquote>
<p>重点关键字就是<strong>急诊</strong>。</p>
<h2 id="所需资料">所需资料</h2>
<p>据悉,需保存的材料清单包括:</p>
<ul>
<li>本人社保卡</li>
<li>急诊诊断证明</li>
<li>急诊处方底方</li>
<li>收费票据,以及检查、治疗、化验费用的明细</li>
</ul>
<p>如果在异地发生急诊需要留院观察,除上述材料外,参保人还需准备外埠</p>
<ul>
<li>住院费用汇总清单</li>
<li>出院诊断证明</li>
<li>本次住院的全部病例复印件</li>
</ul>
<h2 id="总结">总结</h2>
<p>希望上面的才能能够帮到你,我查了好多资料才找到这些信息的。</p>
<p>好吧,最终我还是没能报销,因为我所在公司是走商保的而非医保,商保给的结果是既往病史不给报销(也就是在投保之前就有的病),o(︶︿︶)o 唉,不开心。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://zm12.sm-img2.com/?src=http%3A%2F%2Fwww.hzins.com%2Fstudy%2Fdetal-136412.html&uid=aacfb85ae7383ddc5ae16e35b1426490&hid=4776f2db8b9473483b4ff7f3e7156ec4&pos=5&cid=9&time=1443399639828&from=click&restype=1&pagetype=0000004002000402&bu=web&query=%E5%8C%97%E4%BA%AC%E5%8C%BB%E4%BF%9D%E5%BC%82%E5%9C%B0%E6%80%A5%E8%AF%8A%E6%8A%A5%E9%94%80&mode=&uc_param_str=dnntnwvepffrgibijbprsvpi">北京异地医保报销流程及所需资料</a></li>
<li><a href="http://beijing.qianlong.com/3825/2015/07/21/2500@10423583.htm">异地急诊就医可回京报销</a></li>
</ul>
不可错过的javascript迷你库
2015-12-29T00:00:00+00:00
http://yanhaijing.com/javascript/2015/12/29/mini-js-lib
<p>最近看着下自己的<a href="https://github.com/yanhaijing">github star</a>,把我吓坏了,手贱党,收藏癖的我都收藏了300+个仓库了,是时候整理一下了。</p>
<p>Unix主张kiss,小而美被实践是最好用的,本文将介绍笔者收集的一些非常赞的开源库。</p>
<p>这些库的共性是非常小,而且功能单一。</p>
<p><img src="/blog/231.gif" alt="" /></p>
<h2 id="cookiejs"><a href="https://github.com/js-coder/cookie.js">cookie.js</a></h2>
<p>如果你操作过cookie的接口,那么你一定会感觉这东西的规范真的是太复杂了,根本记不住啊,其实你是对的,因为cookie的接口设计的是有问题的,也就是说设计的太底层了,根本不友好,那么来试试这个js库吧。</p>
<h2 id="storejs"><a href="https://github.com/marcuswestin/store.js">store.js</a></h2>
<p>再来说说浏览器的localStore吧,这东西太赞了,可惜尼玛每个浏览器都实现的各不相同,我也是醉了,如果你也有同样的烦恼,不如来试试这个迷你库,它有更简单的api,最重要的是他解决了跨浏览器问题,甚至解决了低版本浏览器(ie6)不支持localStore的问题。</p>
<h2 id="datajs"><a href="https://github.com/yanhaijing/data.js">data.js</a></h2>
<blockquote>
<p>data.js 是带有消息通知的数据中心,我称其为会说话的数据。旨在让编程变得简单,世界变得美好</p>
</blockquote>
<p>如果你使用模块化编程,或者在node环境下的话,你一定纠结过不同模块间到底如何共享数据的问题(虽然这是反模式),全局变量。。。那么试试这个迷你库吧,简单可以来,会让你消除上面的烦恼问题,同时他还支持消息,当数据更新时,会发出消息。</p>
<h2 id="templatejs"><a href="https://github.com/yanhaijing/template.js">template.js</a></h2>
<blockquote>
<p>template.js 一款javascript模板引擎,简单,好用。</p>
</blockquote>
<h2 id="favicojs"><a href="http://lab.ejci.net/favico.js/">favico.js</a></h2>
<p>在favico上添加数字,是不是很nice,点击下面的官网查看效果,这肯定要逼死强迫症了。</p>
<p><a href="http://lab.ejci.net/favico.js/">官网</a>。</p>
<h2 id="modernizr"><a href="http://modernizr.com/">Modernizr</a></h2>
<p>这个就不过多解释了,各种html css js检测器,功能检测哦。</p>
<h2 id="movejs"><a href="http://visionmedia.github.io/move.js/">Move.js</a></h2>
<p>如果你操作过css3的属性,一定会觉得非常痛苦的,那不如来试试这个,css3动画瞬间变得简单了。</p>
<h2 id="keypress"><a href="http://dmauro.github.io/Keypress/">Keypress</a></h2>
<p>一定记不住键盘上每个键的键位码吧,来试试这个,直观的展示,再也不需要记忆了。</p>
<h2 id="devicejs"><a href="http://matthewhudson.me/projects/device.js/">device.js</a></h2>
<p>你想检测用户的设备,试试这个吧,比jq.browser全面多了。</p>
<h2 id="isjs"><a href="http://arasatasaygin.github.io/is.js/">is.js</a></h2>
<p>迷你检查库,这个几乎涵盖了全部的各种检测,如果嫌弃这个太大,很多功能用不到,那就使用更迷你的<a href="https://github.com/yanhaijing/is.js">is2.js</a>吧</p>
<h2 id="es5-shim"><a href="https://github.com/es-shims/es5-shim">es5-shim</a></h2>
<p>还没使用es5,只能鄙视你了,担心兼容性,用这个吧,主要是为了es6打基础啊。</p>
<h2 id="es6-promise"><a href="https://github.com/jakearchibald/es6-promise">es6-promise</a></h2>
<p>promise太好用了,兼容性问题靠这个全解决了。</p>
<h2 id="parallax"><a href="https://github.com/wagerfield/parallax">parallax</a></h2>
<p>先来看个视差效果的<a href="http://zhidao.baidu.com/s/10year/index.html">demo</a>,是不是很赞,如果你也想实现这个效果,那么来试试这个吧。</p>
<h2 id="notiejs"><a href="https://github.com/jaredreich/notie.js">notie.js</a></h2>
<p>还在使用弹窗通知用户,太low了,快来试试这款非阻塞式,小清新的通知插件吧,对移动端有好,界面好到爆炸啊。</p>
<h2 id="sharejs"><a href="https://github.com/overtrue/share.js">share.js</a></h2>
<blockquote>
<p>一键分享到微博、QQ空间、QQ好友、微信、腾讯微博、豆瓣、Facebook、Twitter、Linkedin、Google+、点点等社交网站。</p>
</blockquote>
<p>如果你受够了分享组件的烦恼,那么来试试这个对移动端有好的分享组件吧,界面优美,看起来很赞。</p>
<p><a href="http://overtrue.me/share.js/">demo</a></p>
<h2 id="mathjs"><a href="https://github.com/josdejong/mathjs">mathjs</a></h2>
<p>js自带的数学运算不能满足你的需求了,那试试这个,扩展了很多数学运算。</p>
<p>这里是<a href="http://mathjs.org/">官网</a>。</p>
<p><img src="https://raw.github.com/josdejong/mathjs/master/img/mathjs.png" alt="" /></p>
<h2 id="momentjs"><a href="http://momentjs.cn/">momentjs</a></h2>
<p>这个处理时间的工具,你想要的都有了,太赞了,感谢评论区的推荐。</p>
<h2 id="spacetime"><a href="https://github.com/smallwins/spacetime">spacetime</a></h2>
<p>moment太大,那试试这个吧,超小,功能还超全的一个时间处理库</p>
<h2 id="inotify"><a href="https://github.com/jaywcjlove/iNotify">iNotify</a></h2>
<p>浏览器通知各种兼容性的坑,试试这个库吧,磨平各个浏览器的差异,同时还新增多种通知</p>
<h2 id="jsmini"><a href="https://github.com/jsmini">jsmini</a></h2>
<p>还没过瘾,jsmini library满足你,一组小而美的JavaScript迷你库,功能众多</p>
<h2 id="总结">总结</h2>
<p>本文介绍的只是作者收集的一小部分而已,作者将会保持时时更新的,如果你有什么推荐的欢迎反馈给我。</p>
<p>最后向大家推荐依稀<a href="http://microjs.com/">microjs</a>,这里收集了太多小而美的库,自己来淘宝吧。</p>
我的Browsersync笔记
2015-12-26T00:00:00+00:00
http://yanhaijing.com/tool/2015/12/26/my-browsersync
<p><a href="http://www.browsersync.io/">BrowserSync</a>——省时的浏览器同步测试工具。</p>
<blockquote>
<p><a href="http://www.browsersync.io/">Browsersync</a>能让浏览器实时、快速响应您的文件更改(html、js、css、sass、less等)并自动刷新页面。更重要的是<strong><a href="http://www.browsersync.io/">Browsersync</a>可以同时在PC、平板、手机等设备下进项调试</strong>。您可以想象一下:“假设您的桌子上有pc、ipad、iphone、android等设备,同时打开了您需要调试的页面,当您使用<a href="http://www.browsersync.io/">browsersync</a>后,您的任何一次代码保存,以上的设备都会同时显示您的改动”。无论您是前端还是后端工程师,使用它将提高您30%的工作效率。</p>
</blockquote>
<p><img src="/blog/232.jpg" alt="" /></p>
<p>大约半年前有位大牛想我推荐这个工具,但一直没有实践,今天准备好好研究研究,并记录在此。</p>
<h2 id="安装">安装</h2>
<p>安装Node后,通过npm安装BrowserSync:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ npm install -g browser-sync
</code></pre></div></div>
<p>安装完后在命令行输入下面的命令,查看是否安装成功:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ browser-sync --version
</code></pre></div></div>
<p>若安装成功能看到一个三位版本号,如下图所示:</p>
<p><img src="/blog//187.png" alt="" /></p>
<p>安装好后输入下面的命令可以查看帮助文档:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ browser-sync --help
</code></pre></div></div>
<h2 id="glob">glob</h2>
<p>开始之前先让我们来简单了解下glob语法,glob就是精简版的正则表达式,主要用来匹配文件,语法规则如下:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">*</code> 匹配多个除了 <code class="language-plaintext highlighter-rouge">/</code> 以外的字符</li>
<li><code class="language-plaintext highlighter-rouge">?</code> 匹配单个除了 <code class="language-plaintext highlighter-rouge">/</code> 以外的字符</li>
<li><code class="language-plaintext highlighter-rouge">**</code> 匹配多个字符包括 <code class="language-plaintext highlighter-rouge">/</code></li>
<li><code class="language-plaintext highlighter-rouge">{}</code> 可以让多个规则用 <code class="language-plaintext highlighter-rouge">,</code> 逗号分隔,起到<code class="language-plaintext highlighter-rouge">或者</code>的作用</li>
<li><code class="language-plaintext highlighter-rouge">!</code> 出现在规则的开头,表示<code class="language-plaintext highlighter-rouge">取反</code>。即匹配不命中后面规则的文件</li>
</ul>
<p>更多内容请<a href="https://github.com/isaacs/node-glob">查看这里</a>。</p>
<h2 id="本地开发">本地开发</h2>
<p>接下来我们看看如何在本地开发时使用,比如我本地有一个目录,我想监视这个目录的css修改,那么先切换到这个目录,然后执行下面的命令即可:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>browser-sync start --server --files "**.css"
</code></pre></div></div>
<p>请注意这个命令里的start –server,这其实是BrowserSync自己启动了一个小型服务器,然后我们通过下面的url访问我们的网站(http://localhost:3000)。</p>
<p>注意:3000端口可能被占用,如果被占用可以用下面的命令指定一个端口:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>browser-sync start --server --port 8081 --files "**.css"
</code></pre></div></div>
<p>典型的效果图如下所示:</p>
<p><img src="/blog//188.gif" alt="" /></p>
<p>也可以像下面这样匹配所有文件的改动:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>browser-sync start --server --files "**"
</code></pre></div></div>
<p>此时,BrowserSync仍然会正确地判断文件变化是否是css,若是其他文件发生变化则刷新页面。</p>
<h2 id="代理模式">代理模式</h2>
<p>很多时候我们往往会在本地大家一个服务器用来开发,这时我们需要使用代理模式:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>browser-sync start --proxy "localhost:8080" --files "**"
</code></pre></div></div>
<h2 id="gulp">gulp</h2>
<p><a href="http://gulpjs.com/">Gulp</a>是现在流行的自动化工具,但BrowserSync并没有Gulp插件版,因为并不需要。BrowserSync有自己独立的API,将它注册为gulp的一个task即可。下面是一段gulpfile.js的示例:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var gulp = require('gulp');
var browserSync = require('browser-sync');
gulp.task('browser-sync', function() {
browserSync({
files: "**",
server: {
baseDir: "./"
}
});
});
gulp.task('default', ["browser-sync"]);
</code></pre></div></div>
<p>这时候运行gulp将等同于前文的browser-sync start –server –files “**“。更多的用法示例请查看<a href="https://github.com/BrowserSync/gulp-browser-sync">gulp-browser-sync</a>。</p>
<h2 id="ui界面及其他">UI界面及其他</h2>
<p>BrowserSync提供的一个简易控制面板。BrowserSync最常用的几个配置选项,都可以在这个面板里调整。如果没有指定打开<code class="language-plaintext highlighter-rouge">http://localhost:3001/</code>会看到下面的界面。</p>
<p><img src="/blog//189.png" alt="" /></p>
<p>这里提供了不少功能,值得一提的是内置了weinre,这些可以抛弃weinre了,这个比那个用起来简单多了,o(∩_∩)o 哈哈。</p>
<h2 id="总结">总结</h2>
<p>好了就先写这么多吧,如果你有更多需求请查看<a href="https://www.browsersync.io/docs/">官方文档</a>,或者下面列出的参考资料。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://www.browsersync.cn/">Browsersync中文网</a></li>
<li><a href="http://segmentfault.com/a/1190000002607627">BrowserSync,迅捷从免F5开始</a></li>
</ul>
快来使用ES2015的Promise吧
2015-09-16T00:00:00+00:00
http://yanhaijing.com/javascript/2015/09/16/es6-promise
<p>接触<a href="https://promisesaplus.com/">Promise</a>很久了,但一直也没用过,感觉很陌生,但是ES2015来了,是时候使用了,本文将介绍Promise的方方面面,同时也是自己学习的过程。</p>
<h2 id="阅读须知">阅读须知</h2>
<p>本文将会尽可能使用ES2015的语法,这可能需要你使用一款现代浏览器,如果你还不了解可以阅读下<a href="http://yanhaijing.com/javascript/2015/09/11/learn-es2015">这篇文章</a>。</p>
<h2 id="兼容性">兼容性</h2>
<p>Promise的浏览器兼容性如下,你需要选一款兼容的浏览器才可以,你也可以<a href="http://caniuse.com/#search=Promises">点击这里查看</a>。</p>
<p><img src="/blog/184.png" alt="" /></p>
<h2 id="什么是promise">什么是Promise</h2>
<p>Promise其实是Node社区中诞生的产物,如果你写过Node你肯定会知道为了异步,写了那么多的回调,而Promise就是比回调更有好的方式。</p>
<p>所谓Promise,就是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的API,可供进一步处理。</p>
<p><img src="/blog/183.png" alt="" /></p>
<p>如果你有兴趣可以阅读一下<a href="https://promisesaplus.com/">Promise的规范</a>,但我不太感兴趣,我将从实践的角度来学习它。</p>
<h2 id="new-promise">new Promise</h2>
<p>Promise是一个构造函数(类),可以使用new运算符新建一个实例,然后就可以使用了,构造函数接受一个函数作为参数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var p = new Promise((resolve, reject) => {
window.setTimeout(() => {resolve(123);}, 1000);
});
p.then((data) => {
console.log('p success', data);
});
</code></pre></div></div>
<p>上面的代码输出如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=> p success 123
</code></pre></div></div>
<p>新建Promise对象时传入的函数,接受两个参数,resolve和reject,分别用来改变Promise的状态,创建好,调用resolve和reject时,可以传入参数,这个参数会自动传递个后面的回调函数中。</p>
<p>创建好promise后,可以通过then方法定制状态变化后的回调函数。</p>
<p>好了这就是使用Promise的全部代码了,剩下的部分就全靠你的发挥了,下面将会介绍常用的API。</p>
<h2 id="promiseprototype-属性">Promise.prototype 属性</h2>
<p>打开浏览器的控制台,输入如下代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.getOwnPropertyNames(Promise.prototype).sort().forEach(function (val) {console.log(val, '\n')});
</code></pre></div></div>
<p>在我的浏览器中上面的代码会有如下输出,可能不同浏览器会不一样。</p>
<p><img src="/blog/185.png" alt="" /></p>
<p>这里我们将重点关注如下接口:</p>
<ul>
<li>catch</li>
<li>then</li>
</ul>
<h3 id="promiseprototypecatch">Promise.prototype.catch</h3>
<p>catch() 方法只处理Promise被拒绝的情况,并返回一个Promise。该方法的行为和调用Promise.prototype.then(undefined, onRejected)相同。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>p.catch((reason) => {
// 拒绝
});
</code></pre></div></div>
<p>更多信息,<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch">请点击这里查看</a>。</p>
<h3 id="promiseprototypethen">Promise.prototype.then</h3>
<p>then()方法返回一个Promise。它有两个参数,分别为Promise在 success 和 failure 情况下的回调函数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>p.then((value) => {
// 满足
}, (reason) => {
// 拒绝
});
</code></pre></div></div>
<p>更多信息,<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/then">请点击这里查看</a>。</p>
<h2 id="promise-属性">Promise 属性</h2>
<p>我们也先来看看浏览器支持哪些接口,在控制台输入如下代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.getOwnPropertyNames(Promise).sort().forEach(function (val) {console.log(val, '\n')});
</code></pre></div></div>
<p>会看到如下的输出:</p>
<p><img src="/blog/186.png" alt="" /></p>
<p>我们将重点关注一下属性:</p>
<ul>
<li>all</li>
<li>race</li>
<li>reject</li>
<li>resolve</li>
</ul>
<h3 id="promiseall">Promise.all</h3>
<p>Promise.all(iterable) 方法返回一个promise,该promise会在iterable参数内的所有promise都被解决后被解决。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Promise.all(iterable);
</code></pre></div></div>
<p>iterable是一个可迭代对象,比如Array。</p>
<p>更多信息,<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all">请点击这里查看</a>。</p>
<h3 id="promiserace">Promise.race</h3>
<p>Promise.race(iterable)方法返回一个promise,这个promise在iterable中的任意一个promise被解决或拒绝后,立刻以相同的解决值被解决或以相同的拒绝原因被拒绝。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Promise.race(iterable);
</code></pre></div></div>
<p>iterable是一个可迭代对象,比如Array。</p>
<p>更多信息,<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/race">请点击这里查看</a>。</p>
<h3 id="promisereject">Promise.reject</h3>
<p>Promise.reject(reason)方法返回一个用reason拒绝的Promise。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Promise.reject(reason);
</code></pre></div></div>
<p>reason Promise被拒绝的原因。</p>
<p>更多信息,<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject">请点击这里查看</a>。</p>
<h3 id="promiseresolve">Promise.resolve</h3>
<p>Promise.resolve(value)方法返回一个以给定值resolve掉的Promise对象。但如果这个值是thenable的(就是说带有then方法),返回的promise会“追随”这个thenable的对象,接收它的最终状态(指resolved/rejected/pendding/settled);否则这个被返回的promise对象会以这个值被fulfilled。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Promise.resolve(value);
Promise.resolve(promise);
Promise.resolve(thenable);
</code></pre></div></div>
<p>value 用来resolve待返回的promise对象的参数。既可以是一个Promise对象也可以是一个thenable。</p>
<p>更多信息,<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve">请点击这里查看</a>。</p>
<h2 id="实战">实战</h2>
<p>我们构造一段下面的代码,基本上用到了上面的全部知识:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 1000ms 后success
var p1 = new Promise((resolve, reject) => {
window.setTimeout(() => {resolve(123);}, 1000);
});
p1.then((data) => {
console.log('p1 success', data);
});
// 2000ms 后success
var p2 = new Promise((resolve, reject) => {
window.setTimeout(() => {resolve(456);}, 2000);
});
p2.then((data) => {
console.log('p2 success', data);
});
var pa = Promise.all([p1, p2]);
var pr = Promise.race([p1, p2]);
pa.then((data) => {
console.log('pa success', data);
});
pr.then((data) => {
console.log('pr success', data);
});
</code></pre></div></div>
<p>上面的代码输出如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 1000ms
p1 success 123
pr success 123
// 2000ms
p2 success 456
pa success [123, 456]
</code></pre></div></div>
<h2 id="polyfill">Polyfill</h2>
<p>如果要兼容旧的浏览器,又要使用新特性,那么只能使用Polyfill了,类似的库很多,我推荐使用<a href="https://github.com/jakearchibald/es6-promise">es6-promise</a></p>
<p>es6-promise是一个兼容 ES6 Promises 的Polyfill类库。 它基于 <a href="https://github.com/tildeio/rsvp.js">RSVP.js</a> 这个兼容 Promises/A+ 的类库, 它只是 RSVP.js 的一个子集,只实现了Promises 规定的 API。</p>
<p>关于使用方法和注意事项这个库的文档都写得很详细了,在此不再详细介绍了。</p>
<h2 id="总结">总结</h2>
<p>是时候使用Promise了,上面已经介绍了ES2015 Promise的全部内容了,如果你还想阅读更多资料,可查看下面的参考资料,如果你有任何疑问或建议,欢迎在下面的评论区和我讨论。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="https://promisesaplus.com/">Promises/A+规范</a></li>
<li><a href="http://liubin.github.io/promises-book/">JavaScript Promise迷你书(中文版)</a></li>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise">MDN Promise</a></li>
</ul>
我的GitBook笔记
2015-09-12T00:00:00+00:00
http://yanhaijing.com/tool/2015/09/12/my-gitbook-note
<p>最近准备写一本书,所以研究研究<a href="https://www.gitbook.com">GitBook</a>,本文对学习做个记录,主要目就是以后日常使用,就不用去翻看别的资料了。</p>
<h2 id="什么是gitbook">什么是GitBook</h2>
<blockquote>
<p>GitBook 是一个基于 Node.js 的命令行工具,可使用 Github/Git 和 Markdown 来制作精美的电子书。</p>
</blockquote>
<p><img src="/blog/233.png" alt="" /></p>
<ul>
<li><a href="http://www.gitbook.io">GitBook项目官网</a></li>
<li><a href="https://github.com/GitbookIO/gitbook">GitBook Github地址</a></li>
</ul>
<p>GitBook需要使用markdown格式编写,如果你不了解可以看看<a href="https://help.gitbook.com/format/markdown.html">这里</a>。</p>
<h2 id="安装">安装</h2>
<p>需要先安装<a href="http://nodejs.org/">Node</a>,安装步骤网上教程非常多,在此不详细介绍,安装好node后就可以使用npm安装GitBook了。</p>
<h3 id="gitbook-cli">gitbook-cli</h3>
<p>需要先安装gitbook-cli,这个工具是用来管理gitbook工具的,这有点类似容器的意思,通过gitbook-cli可以在本地安装多个gitbook工具的不同版本。</p>
<p>使用如下命令安装GitBook。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> $ npm install gitbook-cli -g
</code></pre></div></div>
<p>安装完之后,你可以检验下是否安装成功。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gitbook -V
</code></pre></div></div>
<h3 id="安装gitbook">安装gitbook</h3>
<p>安装完gitbook-cli后,要使用gitbook还需要安装gitbook工具,可以通过如下命令安装。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gitbook versions:install
</code></pre></div></div>
<p>安装好后可以通过如下命令查看是否安装成功。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gitbook versions
</code></pre></div></div>
<p>都安装好后接下来我们就可以做点有意思的事情了。</p>
<h2 id="常用命令">常用命令</h2>
<p>再开始做有意思的事情之前,先来熟悉下常用命令。</p>
<h3 id="gitbook-cli常用命令">gitbook-cli常用命令</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gitbook -h # 查看帮助信息
gitbook versions # 查看本地安装的gitbook版本
gitbook versions:install # 安装最新版gitbook
gitbook versions:install 2.3.3 # 安装指定版本
gitbook versions:uninstall # 卸载当前选中版本
gitbook versions:uninstall 2.3.3 # 卸载指定版本
gitbook versions:link # 指定当前文件夹使用当前选中版本
gitbook versions:link 2.3.3 # 指定当前文件夹使用指定版本
gitbook versions:link path # 指定path使用指定版本
gitbook -v 2.3.3 # 指定当前使用哪个版本的gitbook
gitbook --gitbook 2.3.3 # 同上
</code></pre></div></div>
<h3 id="gitbook常用命令">gitbook常用命令</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gitbook init # 初始化一个仓库
$ gitbook install # 安装插件
$ gitbook serve [book] # 本地预览
$ gitbook serve --port 8001 # 指定端口
$ gitbook build repository PATH # 输出一个静态网站
$ gitbook pdf book pdf # 生成pdf文件
$ gitbook help # 查看帮助
</code></pre></div></div>
<p><strong>顺便吐槽一下gitbook命令设计是有问题,两个不同的命令耦合在了一起。</strong></p>
<h2 id="图书项目结构">图书项目结构</h2>
<p>README.md和SUMMARY.md是Gitbook项目必备的两个文件,也就是一本最简单的GitBook也必须含有这两个文件,它们在一本Gitbook中具有不同的用处。</p>
<h3 id="readmemd">README.md</h3>
<p>这个文件相当于一本Gitbook的简介。</p>
<h3 id="summarymd">SUMMARY.md</h3>
<p>这个文件是一本书的目录结构,使用Markdown语法,一个简单的例子如下所示。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># Summary
* [Part I](part1/README.md)
* [Writing is nice](part1/writing.md)
* [GitBook is nice](part1/gitbook.md)
* [Part II](part2/README.md)
* [We love feedback](part2/feedback_please.md)
* [Better tools for authors](part2/better_tools.md)
</code></pre></div></div>
<p>更多信息请看<a href="https://help.gitbook.com/format/chapters.html">这里</a>。</p>
<h3 id="bookjson">book.json</h3>
<p>自从GitBook 2.0.0开始支持自定义简介文件,在book.json中定义,这样README.md就可以用作项目的简介。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
"structure": {
"readme": "myIntro.md"
}
}
</code></pre></div></div>
<p>更多信息请看<a href="https://help.gitbook.com/format/introduction.html">这里</a>。</p>
<p>book.json还有自定义更多的信息,比如网页的title,description等,全部可配置信息请查看<a href="https://help.gitbook.com/format/configuration.html">这里</a>。</p>
<h2 id="发布">发布</h2>
<p>使用gitbook可以很方便的发布到很多平台下面举几个常用的例子。</p>
<h3 id="发布到github">发布到GitHub</h3>
<p>源代码保存到master分支,build出来的上传到gh-pages分支,就这么简单的搞定了。如果你还不会使用git和github,那么不妨读读我的另一篇文章《<a href="http://yanhaijing.com/git/2014/11/01/my-git-note">我的git笔记</a>》。</p>
<h3 id="发布pdf">发布PDF</h3>
<p>这里已windows平台为例子,需要先安装<a href="http://calibre-ebook.com/">calibre@2.38.0</a>(其实只是需要ebook-convert这个工具),安装好后将其安装目录配置到PATH。</p>
<p>然后就可以使用下面的命令生成pdf了。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ gitbook pdf . ../temp.pdf # 将当前目录,生成到父目录下的temp.pdf
</code></pre></div></div>
<h3 id="发布到gitbook">发布到GitBook</h3>
<p>还没想好,想好了再写。</p>
<h2 id="总结">总结</h2>
<p>关于<a href="https://www.gitbook.com">GitBook</a>的更多资料可查看官网的<a href="https://help.gitbook.com">帮助文档</a>,哪里的介绍比较全面,也会保持时时更新。</p>
<p>我把这些配置整理了个<a href="https://github.com/yanhaijing/gitbook-boilerplate">仓库</a>,可以开箱即用,最重要的是添加了对docx格式的支持,我的<a href="http://yanhaijing.com/spring/">青丝集</a>就是用的这个模版,墙裂推荐!</p>
<blockquote>
<p><a href="https://github.com/yanhaijing/gitbook-boilerplate">gitbook-boilerplate</a> —— 一个基于gitbook快速写电子书的模版,支持html、pdf、<strong>docx</strong>、epub、mobi</p>
</blockquote>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://gitbook-zh.wanqingwong.com/">Gitbook 使用入门</a></li>
</ul>
ECMAScript 2015 简易教程
2015-09-11T00:00:00+00:00
http://yanhaijing.com/javascript/2015/09/11/learn-es2015
<h2 id="背景">背景</h2>
<p>本文最初源自<a href="http://git.io/es6features">es6features</a>,你可以到github上去加星。你可以使用<a href="https://babeljs.io/repl">REPL</a>在线预览这些特性。</p>
<h2 id="简介">简介</h2>
<blockquote>
<p>ECMAScript 6 是ECMAScript标准的最新版本,于2015年6月批准通过。ES2015是对语言的一次重大更新,是自从2009年ES5标准化后的第一次重大更新。主要的JavaScript引擎正在逐步实现这些特性,<a href="http://kangax.github.io/compat-table/es6/">点击这里</a>查看浏览器的兼容情况。</p>
</blockquote>
<p>查看<a href="http://www.ecma-international.org/ecma-262/6.0/index.html">ES2015 标准</a>,了解关于ECMAScript 6语言的完整规范。</p>
<h2 id="ecmascript-6-新特性">ECMAScript 6 新特性</h2>
<h3 id="箭头函数和静态thisarrows-and-lexical-this">箭头函数和静态this(Arrows and Lexical This)</h3>
<p>箭头函数使用胖箭头(=>)语法,与C#,Java 8和CoffeeScript类似。支持表达式和语句作为函数体。不像普通函数,箭头函数的this是和文法作用域绑定的。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 表达式作为函数体
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);
// 语句作为函数体
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
// 静态this
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
};
</code></pre></div></div>
<h3 id="类classes">类(Classes)</h3>
<p>ES2015中的类是对基于原型面向对象模式的一个简单语法糖。有一个单一的声明形式可以使类模式更容易使用,并鼓励互操作性。类支持基于原型的继承,超类调用(super),实例方法,静态方法和构造函数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class SkinnedMesh extends THREE.Mesh {
constructor(geometry, materials) {
super(geometry, materials);
this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
update(camera) {
//...
super.update();
}
static defaultMatrix() {
return new THREE.Matrix4();
}
}
</code></pre></div></div>
<h3 id="增强对象字面量enhanced-object-literals">增强对象字面量(Enhanced Object Literals)</h3>
<p>对象字面量被扩展支持直接设置原型,简洁属性赋值和方法,超类调用。这也让对象字面量和类声明的关系更密切,并让基于对象的设计更便利。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var obj = {
// __proto__
__proto__: theProtoObj,
// 下面的写法不会设置内部原型
'__proto__': somethingElse,
// ‘handler: handler’的简写
handler,
// 方法
toString() {
// 调用父对象的方法
return "d " + super.toString();
},
// 属性名是一个表达式
[ "prop_" + (() => 42)() ]: 42
};
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">__proto__</code>属性需要原生支持,这一属性在ECMAScript前一个版本中一度被废弃。目前为止,大部分引擎支持这一属性。同时需要注意,仅仅web浏览器需要实现这一属性,在Node中现在就可使用。</p>
<h3 id="模版字符串template-strings">模版字符串(Template Strings)</h3>
<p>模版字符串提供构建字符串的语法糖。这类似Perl,Python等其他语言中的字符串插值。可以选择性添加一个标签,允许对字符串构建的定制化,避免注入攻击或其他需求。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 普通字符串
`This is a pretty little template string.`
// 多行字符串
`In ES5 this is
not legal.`
// 字符串中嵌入变量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
// 模版标签
String.raw`In ES5 "\n" is a line-feed.`
// 下面构造一个HTTP请求头,来解释差值替换和构造
GET`http://foo.org/bar?a=${a}&b=${b}
Content-Type: application/json
X-Credentials: ${credentials}
{ "foo": ${foo},
"bar": ${bar}}`(myOnReadyStateChangeHandler);
</code></pre></div></div>
<h3 id="解构destructuring">解构(Destructuring)</h3>
<p>按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。解构失败是静默的,类似标准的对象属性查找foo[“bar”],找不到值则为undefined。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 列表匹配
var [a, , b] = [1,2,3];
// 对象匹配
var { op: a, lhs: { op: b }, rhs: c }
= getASTNode()
// 对象匹配简写
// 绑定作用域中的 `op`, `lhs` 和 `rhs`
var {op, lhs, rhs} = getASTNode()
// 函数参数
function g({name: x}) {
console.log(x);
}
g({name: 5})
// 解构失败,赋值为undefined
var [a] = [];
a === undefined;
// 如果有默认值,则为默认值
var [a = 1] = [];
a === 1;
</code></pre></div></div>
<h3 id="默认值--rest参数--扩展运算符default--rest--spread">默认值 + Rest参数 + 扩展运算符(Default + Rest + Spread)</h3>
<p>现在函数调用可以传递默认参数了;扩展运算符允许将数组转化为函数的参数传入;rest参数,用于获取函数的多余参数,这样就不需要使用arguments对象了。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function f(x, y=12) {
// 如果不传递y或传递undefined,y的值为12
return x + y;
}
f(3) == 15
function f(x, ...y) {
// y是一个数组
return x * y.length;
}
f(3, "hello", true) == 6
function f(x, y, z) {
return x + y + z;
}
// 将数组扩展为三个参数
f(...[1,2,3]) == 6
</code></pre></div></div>
<h3 id="let--const">Let + Const</h3>
<p>let类似于var,但是所声明的变量,只在let命令所在的代码块内有效。const也用来声明变量,但是声明的是常量。一旦声明,常量的值就不能改变。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function f() {
{
let x;
{
// 块作用域
const x = "sneaky";
// const常量重新赋值会报错
x = "foo";
}
// let变量可以再次赋值
x = "bar";
// 在块作用域中重复声明将会报错
let x = "inner";
}
}
</code></pre></div></div>
<h3 id="iterator和forof循环iterators--forof">Iterator和for..of循环(Iterators + For..Of)</h3>
<p>遍历器对象能够自定义遍历行为,这很像Java的Iterable。新增的for..of用来代替for..in。并不需要是数组,任何数据结构只要部署Iterator接口,就可以完成遍历操作。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>let fibonacci = {
[Symbol.iterator]() {
let pre = 0, cur = 1;
return {
next() {
[pre, cur] = [cur, pre + cur];
return { done: false, value: cur }
}
}
}
}
for (var n of fibonacci) {
if (n > 1000)
break;
console.log(n);
}
</code></pre></div></div>
<p>遍历器基于弱类型接口(下面是使用TypeScript语法的展示):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>interface IteratorResult {
done: boolean;
value: any;
}
interface Iterator {
next(): IteratorResult;
}
interface Iterable {
[Symbol.iterator](): Iterator
}
</code></pre></div></div>
<h4 id="需要腻子脚本support-via-polyfill">需要腻子脚本(Support via polyfill)</h4>
<p>使用遍历器需要引用Babel<a href="https://babeljs.io/docs/usage/polyfill">腻子脚本</a>。</p>
<h3 id="generator-函数generators">Generator 函数(Generators)</h3>
<p>Generator函数是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同。Generator函数有多种理解角度。从语法上,首先可以把它理解成,Generator函数是一个状态机,封装了多个内部状态。</p>
<p>执行Generator函数会返回一个遍历器对象,也就是说,Generator函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态。</p>
<p>形式上,Generator函数是一个普通函数,但是有两个特征。一是,function命令与函数名之间有一个星号;二是,函数体内部使用yield语句,定义不同的内部状态(yield语句在英语里的意思就是“产出”)。</p>
<p><strong>注意:</strong>也可以使用‘await’——类似异步编程,参见ES7 await <a href="https://github.com/lukehoban/ecmascript-asyncawait">提案</a>。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var fibonacci = {
[Symbol.iterator]: function*() {
var pre = 0, cur = 1;
for (;;) {
var temp = pre;
pre = cur;
cur += temp;
yield cur;
}
}
}
for (var n of fibonacci) {
// 截断1000以后的数据
if (n > 1000)
break;
console.log(n);
}
</code></pre></div></div>
<p>generator接口原理如下(使用TypeScript语法的模拟):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>interface Generator extends Iterator {
next(value?: any): IteratorResult;
throw(exception: any);
}
</code></pre></div></div>
<h4 id="需要腻子脚本support-via-polyfill-1">需要腻子脚本(Support via polyfill)</h4>
<p>为了使用Generator,需要引用Babel的<a href="https://babeljs.io/docs/usage/polyfill">腻子脚本</a>。</p>
<h3 id="unicode">Unicode</h3>
<p>ES6增强了对Unicode的支持,包括新的unicode字面量和新的正则匹配参数u模式,以及新的API来处理超过两个字节的字符。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// ES5.1中
"𠮷".length == 2
// 新的正则参数 u
"𠮷".match(/./u)[0].length == 2
// 新的表示法
"\u{20BB7}" == "𠮷" == "\uD842\uDFB7"
// 新字符串方法
"𠮷".codePointAt(0) == 0x20BB7
// for-of
for(var c of "𠮷") {
console.log(c);
}
</code></pre></div></div>
<h3 id="模块modules">模块(Modules)</h3>
<p>语言级支持定义模块和组件。新模块的设计汇总了流行JavaScript模块加载器(AMD,CommonJS)的优点。模块的实现机制由运行时环境实现。隐式异步模型——模块加载完成之前代码不会执行。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
alert("2π = " + sum(pi, pi));
</code></pre></div></div>
<p>可以设置导出默认值和导出全部:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
// app.js
import exp, {pi, e} from "lib/mathplusplus";
alert("2π = " + exp(pi, e));
</code></pre></div></div>
<h4 id="模块格式化">模块格式化</h4>
<p>Babel可以将ES2015的模块转译成几种不同的模块格式,包括Common.js,AMD,System和UMD。你甚至可以创建自己的模块格式,更多细节可以查看<a href="https://babeljs.io/docs/usage/modules">模块的文档</a>。</p>
<h3 id="map--set--weakmap--weakset">Map + Set + WeakMap + WeakSet</h3>
<p>专为常见算法设计的高效数据结构。WeakMaps提供对象的弱引用作为键名的索引表。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;
// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;
// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined
// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });
// Because the added object has no other references, it will not be held in the set
</code></pre></div></div>
<h4 id="需要使用polyfill">需要使用polyfill</h4>
<p>为了使用这些新数据结构,你必须引入babel的<a href="https://babeljs.io/docs/usage/polyfill">腻子脚本</a>。</p>
<h3 id="代理proxies">代理(Proxies)</h3>
<p>Proxy可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 代理对象
var target = {};
var handler = {
get: function (receiver, name) {
return `Hello, ${name}!`;
}
};
var p = new Proxy(target, handler);
p.world === "Hello, world!";
// 代理函数
var target = function () { return "I am the target"; };
var handler = {
apply: function (receiver, ...args) {
return "I am the proxy";
}
};
var p = new Proxy(target, handler);
p() === "I am the proxy";
</code></pre></div></div>
<p>下面是Proxy支持的拦截操作一览:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var handler =
{
// target.prop
get: ...,
// target.prop = value
set: ...,
// 'prop' in target
has: ...,
// delete target.prop
deleteProperty: ...,
// target(...args)
apply: ...,
// new target(...args)
construct: ...,
// Object.getOwnPropertyDescriptor(target, 'prop')
getOwnPropertyDescriptor: ...,
// Object.defineProperty(target, 'prop', descriptor)
defineProperty: ...,
// Object.getPrototypeOf(target), Reflect.getPrototypeOf(target),
// target.__proto__, object.isPrototypeOf(target), object instanceof target
getPrototypeOf: ...,
// Object.setPrototypeOf(target), Reflect.setPrototypeOf(target)
setPrototypeOf: ...,
// for (let i in target) {}
enumerate: ...,
// Object.keys(target)
ownKeys: ...,
// Object.preventExtensions(target)
preventExtensions: ...,
// Object.isExtensible(target)
isExtensible :...
}
</code></pre></div></div>
<h4 id="不支持的特性unsupported-feature">不支持的特性(Unsupported feature)</h4>
<p>由于ES5的局限性,Proxy不能被转换和填补。<a href="http://kangax.github.io/compat-table/es6/#Proxy">点击这里</a>查看浏览器支持情况。</p>
<h3 id="symbols">Symbols</h3>
<p>Symbol 可以作为对象的键值,是一种新的原始类型。Symbol函数可以接受一个字符串作为参数,表示对Symbol实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。Symbol是独一无二的,但可以通过Object.getOwnPropertySymbols获取到。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function() {
// 块级作用域
var key = Symbol("key");
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function() {
... this[key] ...
}
};
// Babel不能支持下面的操作,需要原生支持
typeof key === "symbol"
})();
var c = new MyClass("hello")
c["key"] === undefined
</code></pre></div></div>
<h4 id="需要腻子脚本不完备limited-support-via-polyfill">需要腻子脚本(不完备)(Limited support via polyfill)</h4>
<p>需要使用Babel的<a href="https://babeljs.io/docs/usage/polyfill">腻子脚本</a>,但无法全部支持。由于语言的限制,有些特性无法被转义和填补。更多细节请查看core.js的<a href="https://github.com/zloirock/core-js#caveats-when-using-symbol-polyfill">相关说明</a>。</p>
<h3 id="继承内建父类subclassable-built-ins">继承内建父类(Subclassable Built-ins)</h3>
<p>在ES2015中,语言内置父类如Array,Date和Dom元素能够被子类继承。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 子类继承Array
class MyArray extends Array {
constructor(...args) { super(...args); }
}
var arr = new MyArray();
arr[1] = 12;
arr.length == 2
</code></pre></div></div>
<h4 id="部分支持partial-support">部分支持(Partial support)</h4>
<p>能够子类化的父类必须是基于类的,比如HTMLElement能被子类继承,然后由于ES5引擎的限制许多父类不能被子类化,如Date,Array和Error等。</p>
<h3 id="math--number--string--object-apis">Math + Number + String + Object APIs</h3>
<p>ES2015扩展了很多新的API,包括核心数学函数,数组转换接口和实现对象复制功能的Object.assign。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false
Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"
Array.from(document.querySelectorAll("*")) // 返回一个真正的数组
Array.of(1, 2, 3) // 类似new Array(...),但只有一个参数时语义不变
[0, 0, 0].fill(7, 1) // [0,7,7]
[1,2,3].findIndex(x => x == 2) // 1
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"
Object.assign(Point, { origin: new Point(0,0) })
</code></pre></div></div>
<h4 id="需要使用腻子脚本不能完全模拟limited-support-from-polyfill">需要使用腻子脚本(不能完全模拟)(Limited support from polyfill)</h4>
<p>通过Babel的<a href="https://babeljs.io/docs/usage/polyfill">腻子脚本</a>,能够支持大部分API,然而,由于各种原因(例如,String.prototype.normalize需要添加大量代码)并未全部支持。你可以<a href="https://github.com/addyosmani/es6-tools#polyfills">点击这里</a>找到更多腻子脚本。</p>
<h3 id="二进制和八进制字面量binary-and-octal-literals">二进制和八进制字面量(Binary and Octal Literals)</h3>
<p>新增了二进制和八进制字面量表示法(注意和原来的ES5废弃的八进制表示法不同)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0b111110111 === 503 // true
0o767 === 503 // true
</code></pre></div></div>
<h4 id="仅支持字面量形式only-supports-literal-form">仅支持字面量形式(Only supports literal form)</h4>
<p>Babel仅能转换0o767,不支持<code class="language-plaintext highlighter-rouge">Numver("0o767")</code>。</p>
<h3 id="promises">Promises</h3>
<p>所谓Promise,就是一个对象,用来传递异步操作的消息。它代表了某个未来才会知道结果的事件(通常是一个异步操作),并且这个事件提供统一的API,可供进一步处理。目前JavaScript库已在使用中。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function timeout(duration = 0) {
return new Promise((resolve, reject) => {
setTimeout(resolve, duration);
})
}
var p = timeout(1000).then(() => {
return timeout(2000);
}).then(() => {
throw new Error("hmm");
}).catch(err => {
return Promise.all([timeout(100), timeout(200)]);
})
</code></pre></div></div>
<h3 id="需要使用腻子脚本support-via-polyfill">需要使用腻子脚本(Support via polyfill)</h3>
<p>要想使用Promises需要引用Babel<a href="https://babeljs.io/docs/usage/polyfill">腻子脚本</a>。</p>
<h3 id="反射reflect-api">反射(Reflect API)</h3>
<p>Reflect对象与Proxy对象一样,也是ES6为了操作对象而提供的新API。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var O = {a: 1};
Object.defineProperty(O, 'b', {value: 2});
O[Symbol('c')] = 3;
Reflect.ownKeys(O); // ['a', 'b', Symbol(c)]
function C(a, b){
this.c = a + b;
}
var instance = Reflect.construct(C, [20, 22]);
instance.c; // 42
</code></pre></div></div>
<h4 id="需要使用腻子脚本support-via-polyfill-1">需要使用腻子脚本(Support via polyfill)</h4>
<p>Reflect API需要引用babel <a href="https://babeljs.io/docs/usage/polyfill">腻子脚本</a>。</p>
<h3 id="尾调用tail-calls">尾调用(Tail Calls)</h3>
<p>尾调用(Tail Call)是函数式编程的一个重要概念,本身非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。</p>
<p>对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function factorial(n, acc = 1) {
"use strict";
if (n <= 1) return acc;
return factorial(n - 1, n * acc);
}
// 现在的实现大部分都会移除,但在ES2015中不会
factorial(100000)
</code></pre></div></div>
<h2 id="原文链接">原文链接</h2>
<p>https://babeljs.io/docs/learn-es2015/</p>
<h2 id="更多资料">更多资料</h2>
<ul>
<li><a href="http://es6.ruanyifeng.com/">ECMAScript 6 入门</a></li>
<li><a href="http://www.infoq.com/cn/es6-in-depth/">深入浅出ES6</a></li>
</ul>
我的svn笔记
2015-09-08T00:00:00+00:00
http://yanhaijing.com/tool/2015/09/08/my-svn-note
<p>最开始接触的版本控制系统就是svn,但因为一直找不到好的托管系统,google code实在太慢(目前已死),后来偶尔接触了github,便踏入了git的大门,从而一发不可收拾。</p>
<p>由于没怎么用过,并且身为git粉,又对svn嗤之以鼻,所以一直停留在只会用小乌龟,ci和co的水平,最近我痛定思痛决定好好研究下svn,及其命令行工具。</p>
<p>本文主要记录些svn的常用命令,以备自己日后使用。</p>
<p><img src="/blog/234.png" alt="" /></p>
<h2 id="初始化">初始化</h2>
<p>如何开始使用svn呢,可以checkout一个已有项目。</p>
<p>检出已有项目:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>svn checkout|co URL # 检出项目到当前目录
svn checkout|co URL PATH # 检出项目到已有目录
svn checkout|co –r 3 # 检出制定版本
</code></pre></div></div>
<h2 id="常用命令">常用命令</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>svn status|st # 查看当前目录状态
svn status|st PATH # 查看指定目录状态
svn update|up # 更新
svn update|up -r 3 # 更新到指定版本
svn update PATH # 更新指定的路径
svn add PATH # 添加指定路径 纳入版本控制
svn commit|ci -m "commit" # 提交
svn diff # 查看当前目录的变化
svn diff PATH # 查看指定目录的变化
svn diff -r 3 # 查看当前目录和指定版本的变化
svn diff -r 3:4 # 查看当前目录版本 3和版本4的变化
svn revert PATH # 取消指定目录的修改
svn blame TARGET # 显示某个已受控文件的每一行的最后修改版本和作者
svn info # 查看当前目录的svn信息
svn log # 查看当前目录的历史信息
svn log -v # 显示详细历史信息
svn log PATH # 查看指定目录历史信息
</code></pre></div></div>
<h2 id="树冲突">树冲突</h2>
<p>主干上删除了一个文件,而你又对这个文件做了修改;主干上对文件做了修改,而你删除了这个文件的时候,都会发生树冲突</p>
<p>树冲突并不可怕,当发生树冲突时有两种解决办法:</p>
<p>其一,保留你的修改,远程的修改不算数</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>svn resolved –accept working path
</code></pre></div></div>
<p>其二,保留远程的修改,用下面的命令删除文件后在提交就ok了</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>svn delete path
</code></pre></div></div>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://w3cboy.com/post/2015/09/those-common-svn-and-git-commands/#0-tsina-1-96627-397232819ff9a47a7b7e80a40613cfe1">那些常用的svn和git命令</a></li>
</ul>
快来使用ECMAScript 2015吧
2015-09-04T00:00:00+00:00
http://yanhaijing.com/javascript/2015/09/04/try-es2015
<p><a href="http://www.ecma-international.org/ecma-262/6.0/">ECMAScript 6</a>(以下简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了。标准的制定者有计划,以后每年发布一次标准,使用年份作为标准的版本。因为当前版本的ES6是在2015年发布的,所以又称<a href="http://www.ecma-international.org/ecma-262/6.0/">ECMAScript 2015</a>。</p>
<p>如果你还不知道ECMAScript,那么你有可能知道javascript,那么可自行查询js和es的关系,如果你也不知道javascript可以忽略这篇文章了。</p>
<p><a href="http://www.ecma-international.org/ecma-262/6.0/">ES6</a>是<a href="http://yanhaijing.com/es5/">ES5.1</a>的下一个版本,和ES5不一样,ES6的改动非常大,而且无法用shim脚本兼容陈旧的浏览器,我一开始认为这是有违web理论的,web得以繁荣昌盛,得益其强大的兼容性,HTML5的理念也是向后兼容,css3的理念也是向后兼容,我一直认为ES6会步ES4的后尘,就像xhtml2.0的结局一样。</p>
<p>直到我遇见了<a href="https://babeljs.io/">babel</a>,transpiler 的理念真是太棒了,当我蓦然回首,发现自己早已深处工程化前端之列,预编译已经融入到工作中了,如果你还处在前端的蛮荒时代(没有编译流程),那么可以先考虑引入工程化流程,再来接触ES6,目前比较流行的有<a href="http://gruntjs.com/">grunt</a>,<a href="http://gulpjs.com/">gulp</a>,<a href="http://webpack.github.io/">webpack</a>等。当然如果是node的话,那就没什么压力了。</p>
<p><a href="https://babeljs.io/">babel</a>支持的平台非常多,可以参看<a href="https://babeljs.io/docs/setup/">这里</a>,点击相应平台,下面会给出使用的例子。</p>
<p>至此我真的想说,前端朋友们,是时候使用<a href="http://www.ecma-international.org/ecma-262/6.0/">ES6</a>了,大胆尝试吧,真的非常棒。</p>
<p>本文将介绍在fis中借助babel使用ES6的过程,包括环境搭建,基本语法,模块系统,优秀特性等。</p>
<h2 id="环境搭建">环境搭建</h2>
<p>作为前端开发者,我非常幸运能使用<a href="http://fis.baidu.com/">fis</a>这么高大上的工具,fis在前不久刚刚发布了fis3,我们用的还是fis2,作为国产工具最大的杯具就是babel不提供默认支持,好的方面是fis团队已经开发出了插件支持——<a href="https://github.com/fex-team/fis-parser-babel-5.x">fis-parser-babel-5.x</a>。</p>
<p>借助这个插件就搞定了编译问题,先来说说我的思路吧,我只希望给固定的js引入编译过程,我的思路是这样的,后缀为.es或.es.js的文件才引入编译流程,这样就可以共存了。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>test.js // 普通js文件
test.es // es文件
test.es.js //es文件
</code></pre></div></div>
<p>我使用的是fis2,配置的js也非常容易,所以在此就不列举了。</p>
<p>babel会将es6代码编译为es5代码,所以其代码需要在支持es5语法的浏览器里运行,如果你支持的浏览器都是现代浏览器可以忽略这个问题,如果你要兼容一些陈旧浏览器,比如ie8那么我建议使用<a href="https://github.com/es-shims/es5-shim">es5-shim</a>,需要引入es5-shim.js和es5-sham.js。</p>
<p>很多编辑器其实还不支持es6的语法,我使用的编辑器是sublime,借助JavaScriptNext - ES6 Syntax这个插件,可以让sublime支持es6语法。</p>
<p>如果你的编辑器没有类似的功能,那么推荐你用subliem吧,我总结了一些使用subliem的经验,推荐给你《<a href="http://yanhaijing.com/tool/2014/10/24/my-sublime/">我的 Sublime Text 2 笔记</a>》。</p>
<h2 id="基本语法">基本语法</h2>
<p>ES6带来了很多新的语法,参考资料里面列举了一些资料可以参考,我看的是阮一峰老师的ECMAScript 6 入门,新知识点还是蛮多的,边扫语法边实验,看看babel编译完的es5语法是什么样子,我建议你也这么做,这样效率极高,而且能知道babel干了些什么。</p>
<p>babel有一个<a href="https://babeljs.io/repl/">try is out</a>,可以时时预览原语法和编译后的语法。本文下面的部分就将用这个作为演示系统。</p>
<p>需要注意的是babel并不支持全部的es6语法,有些并没有实现,babel首页有个清单,babel还不止支持es6,还支持部分es7的语法。</p>
<h2 id="模块系统">模块系统</h2>
<p>先来说说模块系统吧,如果你还未使用模块开发(AMD OR CMD),那可以跳过这个章节,或者看看我的另一篇文章《<a href="http://yanhaijing.com/javascript/2015/03/28/js-module/">JavaScript模块的前世今生</a>》,了解下javascript模块的历史,ES6的模块系统和现有模块系统是兼容的,也就是说你既可以在AMD中引用ES6中的模块,也可以在ES6中引用AMD中的模块。</p>
<p>在ES6模块系统中,引用其他模块系统可以用下面的代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import $ from 'jquery.js';
$('#test');
</code></pre></div></div>
<p>babel编译完的代码如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'use strict';
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var _jqueryJs = require('jquery.js');
var _jqueryJs2 = _interopRequireDefault(_jqueryJs);
(0, _jqueryJs2['default'])('#test');
</code></pre></div></div>
<p>babel默认使用的模块系统是commonjs,上面编译完的代码就是commonjs的代码。</p>
<p>再来看看如何导出能被其他模块系统引用的模块。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var $ = '';
export default $;
</code></pre></div></div>
<p>上面的代码,导出了默认导出模块,下面看看babel编译的结果:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var $ = '';
exports['default'] = $;
module.exports = exports['default'];
</code></pre></div></div>
<p>编译完还是符合commonjs规范的模块。好了对于其他导入和导出已发,不妨自己试试吧。</p>
<p><strong>注意:</strong>babel编译完的代码会好使用严格模式。</p>
<p>关于模块的更多细节可以参考这里:</p>
<ul>
<li><a href="http://segmentfault.com/a/1190000003410285">ES6 的模块系统</a></li>
</ul>
<h2 id="优秀特性">优秀特性</h2>
<p>模块系统介绍完了,来看看ES6中一些其他优秀特性,babel并未对ES6的全部语法提供支持,下面我列举一些自己认为优秀的特性,并可立即使用的特性列举出来。</p>
<h3 id="字符串">字符串</h3>
<p>动态字符串使用反引号。并且支持模块变量和多行模式。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 源码
`yanhaijing ${abc}`;
// babel编译完的代码
"yanhaijing " + abc;
</code></pre></div></div>
<p>后面只列举源码,babel编译完的结构可自行查看。</p>
<h3 id="解构赋值">解构赋值</h3>
<p>使用数组和对象成员对变量赋值,优先使用解构赋值。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 数组结构赋值
var arr = [1, 2, 3, 4];
var [first, second] = arr;
// 对象结构赋值
var obj = {a: 1, b: 2};
var {a, b} = obj;
</code></pre></div></div>
<h3 id="对象">对象</h3>
<p>ES6扩展了对象字面量的语法。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = 1;
var obj = {
a,
[a + 1]: 3,
add() {}
}
</code></pre></div></div>
<h3 id="数组">数组</h3>
<p>使用扩展运算符(…)拷贝数组。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var arr1 = [1, 2, 3];
var arr2 = [...arr1];
</code></pre></div></div>
<h3 id="函数">函数</h3>
<p>箭头函数:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(() => {
console.log('Welcome to the Internet.');
})();
</code></pre></div></div>
<p>不要在函数体内使用arguments变量,使用rest运算符(…)代替:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function concatenateAll(...args) {
return args.join('');
}
</code></pre></div></div>
<p>使用默认值语法设置函数参数的默认值:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function f(a = 1) {}
</code></pre></div></div>
<h3 id="class">Class</h3>
<p>总是用class,取代需要prototype操作。因为class的写法更简洁,更易于理解</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class Child extends Parent {
constructor() {
super();
this.value = 1;
}
get() {
return this.value;
}
}
</code></pre></div></div>
<p>更多优秀特性,请参看<a href="http://es6.ruanyifeng.com/#docs/style">这里</a>。</p>
<h2 id="总结">总结</h2>
<p>好了这就是本文的全部内容了,读完本文按捺不住心中的小激动就快快来适用吧,es6真的很棒,babel非常重要,ES6还有很多特性等待你去挖掘哦,另外babel也有很多功能,你自己去发现吧。</p>
<p>如果你有什么疑问或建议在评论区和我一起讨论吧。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://yanhaijing.com/es5/">ECMAScript 5.1</a></li>
<li><a href="http://es6.ruanyifeng.com/">ECMAScript 6 入门</a></li>
<li><a href="http://es6-features.org/">ECMAScript 6 — New Features: Overview & Comparison</a></li>
<li><a href="https://github.com/lukehoban/es6features">es6features</a></li>
<li><a href="http://www.open-open.com/lib/view/open1425130804218.html">现在开始使用 ES6</a></li>
<li><a href="http://kangax.github.io/compat-table/es6/">es6 compat-table</a></li>
<li><a href="https://hacks.mozilla.org/category/es6-in-depth/">es6-in-depth</a></li>
<li><a href="http://www.infoq.com/cn/articles/es6-in-depth-an-introduction">深入浅出ECMAScript 6(上面链接的中文版)</a></li>
</ul>
我的npm笔记
2015-09-01T00:00:00+00:00
http://yanhaijing.com/tool/2015/09/01/my-npm-note
<p>本文记录一些npm的使用技巧,主要包括自己常用的命令,做个备忘。</p>
<h2 id="npm-是什么">NPM 是什么?</h2>
<p>NPM是NodeJS的包管理工具,现在已经进一步发展,致力于为很多其他平台提供包管理工具,其核心思想就是让包的安装更简洁,并自动处理依赖的问题。</p>
<p><img src="/blog/235.png" alt="" /></p>
<p>它的主要功能就是管理node包,包括:安装、卸载、更新、查看、搜索、发布等。</p>
<ul>
<li><a href="https://npmjs.org/">npm官网</a></li>
<li><a href="https://npmjs.org/doc/README.html">npm官方文档</a></li>
</ul>
<p>如果上面的服务挂了(原因你懂得),可以访问下面的链接备用:</p>
<ul>
<li><a href="http://npm.taobao.org/">淘宝 NPM 镜像</a></li>
</ul>
<h2 id="配置代理">配置代理</h2>
<p>npm的服务有时会挂掉,就算不挂掉,速度也是龟速的,这需要我们设置下代理:</p>
<p>npm默认从npm上下载安装包资源:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://registry.npmjs.org/
</code></pre></div></div>
<p>安装时临时指定代理:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install data_js --registry=https://registry.npm.taobao.org
</code></pre></div></div>
<p>全局配置:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm config set registry https://registry.npm.taobao.org
npm config list ## 查看刚才的配置是否生效
</code></pre></div></div>
<p><strong>注意:</strong> 如果你要发布npm包,你需要在改为npm的官方网址。</p>
<p>如果你使用的是linux也可以使用别名的方式:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>alias cnpm="npm --registry=https://registry.npm.taobao.org \
--cache=$HOME/.npm/.cache/cnpm \
--disturl=https://npm.taobao.org/dist \
--userconfig=$HOME/.cnpmrc"
</code></pre></div></div>
<p>或者直接安装<a href="http://cnpmjs.org/">cnpm</a>。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install -g cnpm
cnpm install *** # 安装后用cnpm代替npm
</code></pre></div></div>
<p>npm默认install时会安装<code class="language-plaintext highlighter-rouge">^a.b.c</code>,这其实非常不好,你可以做下面的配置,将<code class="language-plaintext highlighter-rouge">^</code>改为<code class="language-plaintext highlighter-rouge">~</code>或<code class="language-plaintext highlighter-rouge">""</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm config set save-prefix "^" # ^a.b.c
npm config set save-prefix "~" # ~a.b.c
npm config set save-prefix "" # a.b.c
</code></pre></div></div>
<h2 id="常用命令">常用命令</h2>
<p>上面配置好代理后,我们就可以任性的玩,下面记录一下常用的命令。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm <-h> # 列出帮助信息
npm -l #列出全部可用命令
npm -v #列出npm版本号
npm config set <key> <value> # 设置配置
npm config delete <key> # 删除配置
npm config list #查看全部配置
npm <cmd> -h # 查找可用的命令
npm help <term> # 查看帮助信息
npm login #登录
npm whoami #查看当前用户
npm publish #发布项目
npm unpublish <name>[@<version>]#取消发布项目
npm search pkg # 查看指定包是否存在
npm install [-g] <pkg>[@<version>] # 安装指定包
npm uninstall [-g] <pkg>[@<version>] # 卸载指定包
npm ls# 查看当前目录下安装了那些包
npm ls <pkg># 查看特定package的信息
npm root # 查看当前包的安装路径
npm root -g #查看全局npm安装的路径
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>目前差不多就能用到这么多,以后如果用到更多东西再慢慢更新。</p>
IOS上给body绑定click事件的bug
2015-08-27T00:00:00+00:00
http://yanhaijing.com/mobile/2015/08/27/click-bug-on-body-on-ios
<p>最近做师傅的按钮拆分项目,掉了一个ios的大坑,记录下面,避免后人掉坑。</p>
<h2 id="描述">描述</h2>
<p>这个bug只在IOS上有,包括ihone,ipad,由于ios浏览器都用的safari内核,所以ios浏览器全部中枪。</p>
<ul>
<li>window,document,body 绑定click事件时,点击body不会触发</li>
</ul>
<p><a href="http://yanhaijing.com/webtest/mobile/bug/ios-body-click.html">demo</a></p>
<p>你可能会说谁会往body绑定事件啊,答案就是我这种喜欢把事件都绑定到body的人,然后利用事件冒泡机制,这样做有诸多好处,如果你不知道请自行百度。</p>
<h2 id="解决">解决</h2>
<p>解决办法也很简单,大概有几个思路:</p>
<ul>
<li>改用touch事件(如果是弹出层的话,会有点透问题)</li>
<li>如何避免bug触发:不要委托到body结点上,委托到任意指定父元素都可以,或者使用原生具有该事件的元素,如使用click事件触发就用a标签包一层。</li>
<li>已触发如何修补:safari对事件的解析非常特殊,如果一个事件曾经被响应过,则会一直冒泡(捕获)到根结点,所以对于已大规模触发的情况,只需要在body元素的所有子元素绑定一个空事件就好了,如:
(“body > *”).on(“click”, function(){};);</li>
</ul>
<h2 id="总结">总结</h2>
<p>但愿你不会和我一样掉坑里,摔得好惨好惨。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://am-team.github.io/amg/dev-exp-doc.html#ios-safari-bug-总结">iOS safari BUG 总结</a></li>
</ul>
Photoshop简史
2015-08-25T00:00:00+00:00
http://yanhaijing.com/tool/2015/08/25/photoshop-history
<p>众所周知photoshop的公开版本号一直比较随性,就想windows一样,我本人也搞不清楚这些版本之间的关系,作为一个喜欢新版的,第一步就是要搞清楚哪个版本是最新版。</p>
<p><img src="/blog/236.jpg" alt="" /></p>
<p>本文带你领略photoshop的历史,从此你再也不会认为photoshop8 比cs3更高级的想法。</p>
<table class="table table-bordered table-striped">
<thead>
<th>版本</th>
<th>操作系统</th>
<th>开发代号</th>
<th>发售日期</th>
<th>新特性</th>
</thead>
<tbody>
<tr>
<td>
0.63
</td>
<td>
Macintosh
</td>
<td>
</td>
<td>
1988年10月
</td>
<td>
</td>
</tr>
<tr>
<td>
1.0
</td>
<td>
Macintosh
</td>
<td>
</td>
<td>
1990年2月
</td>
<td>
</td>
</tr>
<tr>
<td>
2.0
</td>
<td>
Macintosh
</td>
<td>
快速漩涡
</td>
<td>
1991年6月
</td>
<td>
路径
</td>
</tr>
<tr>
<td>
2.5
</td>
<td>
Macintosh
</td>
<td>
隼
</td>
<td>
1992年1月
</td>
<td>
</td>
</tr>
<tr>
<td>
</td>
<td>
Windows
</td>
<td>
硫磺
</td>
<td>
1992年1月
</td>
<td>
</td>
</tr>
<tr>
<td>
</td>
<td>
IRIX/Solaris
</td>
<td>
</td>
<td>
1993年11月
</td>
<td>
</td>
</tr>
<tr>
<td>
3.0
</td>
<td>
Macintosh
</td>
<td>
虎山
</td>
<td>
1994年9月
</td>
<td>
调色板标签、图层
</td>
</tr>
<tr>
<td>
</td>
<td>
Windows/IRIX/Solaris
</td>
<td>
</td>
<td>
1994年11月
</td>
<td>
</td>
</tr>
<tr>
<td>
4.0
</td>
<td>
Macintosh/Windows
</td>
<td>
大电猫
</td>
<td>
1996年11月
</td>
<td>
可调整的图层,可编辑类型
</td>
</tr>
<tr>
<td>
5.0
</td>
<td>
Macintosh/Windows
</td>
<td>
奇怪货物
</td>
<td>
1998年5月
</td>
<td>
多次撤销(历史面板),色彩管理
</td>
</tr>
<tr>
<td>
5.5
</td>
<td>
Macintosh/Windows
</td>
<td>
</td>
<td>
1999年2月
</td>
<td>
与Image Ready同梱,储存为网页用,吸取,向量图象
</td>
</tr>
<tr>
<td>
6.0
</td>
<td>
Macintosh/Windows
</td>
<td>
毛发中的维纳斯
</td>
<td>
2000年9月
</td>
<td>
更新用户界面,“溶解” 滤镜,图层模式,混合图层
</td>
</tr>
<tr>
<td>
7.0
</td>
<td>
Mac Classic/OS X/Windows
</td>
<td>
流动的天空
</td>
<td>
2002年3月
</td>
<td>
文本全部向量化,修复笔刷,新绘画引擎
</td>
</tr>
<tr>
<td>
7.0.1
</td>
<td>
Mac Classic/OS X/Windows
</td>
<td>
</td>
<td>
2002年8月
</td>
<td>
支持相机 RAW 1.x (可选插件)
</td>
</tr>
<tr>
<td>
8.0(CS)
</td>
<td>
Mac OS X/Windows
</td>
<td>
暗物质
</td>
<td>
2003年10月
</td>
<td>
支持相机 RAW 2.x ,Highly modified "Slice Tool",阴影/高光 命令,颜色匹配命令,"镜头模糊" 滤镜,实时柱状图,Detection and refusal to print scanned images of various banknotes,使用Safecast的DRM复制保护技术,支持JavaScript脚本语言及其他语言
</td>
</tr>
<tr>
<td>
9.0(CS2)
</td>
<td>
Mac OS X/Windows
</td>
<td>
空间猴子
</td>
<td>
2005年4月
</td>
<td>
支持相机 RAW 3.x,智慧对象, PHOTOSHOP 图像扭曲,点恢复笔刷,红眼工具,镜头校正滤镜,智慧锐化,Smart Guides,消失点,改善64-bit PowerPC G5 Macintosh计算机运行Mac OS X 10.4时的内存管理,支持高动态范围成像 (High Dynamic Range Imaging),More smudging options, such as "Scattering",改善图层选取 (可选取多于一个图层)
</td>
</tr>
<tr>
<td>
CS3
</td>
<td>
所有Mac OS X/Windows
</td>
<td>
Red Pill
</td>
<td>
2007年4月
</td>
<td>
可以使用于英特尔的麦金塔平台,增进对Windows Vista的支持,全新的用户界面,Feature additions to Adobe Camera RAW,快速选取工具,曲线、消失点、色版混合器、亮度和对比度、打印对话窗的改进 ,黑白转换调整,自动合并和自动混合,智慧(无损)滤镜,移动器材的图像支持,Improvements to cloning and healing,更完整的32 bit / HDR 支持 (图层, 绘图, 更多滤镜与调整),快速启动
</td>
</tr>
<tr>
<td>
CS4
</td>
<td>
所有Mac OS X/Windows
</td>
<td>
巨石群
</td>
<td>
2008年9月23日
</td>
<td>
Adobe CS4套装拥有一百多项创新,并特别注重简化工作流程、提高设计效率,Photoshop CS4支持基于内容的智能缩放,支持64位操作系统、更大容量内存,基于OpenGL的GPGPU通用计算加速
</td>
</tr>
<tr>
<td>
CS5
</td>
<td>
Mac OS X 10.5.7 / 10.6 /Windows
</td>
<td>
</td>
<td>
2010年4月12日
</td>
<td>
拥有多项全新功能,自动镜头校正、 支持HDR(High Dynamic Range,即高动态范围)调节 、区域删除、先进的选择工具、Puppet Warp(新功能)、64位Mac OS X支持、全新笔刷系统、处理高管相机中的RAW文件。
</td>
</tr>
<tr>
<td>
CS6
</td>
<td>
Mac OS X 10.6.8 / 10.7 /WindowsXP SP3/ Windows7 SP1
</td>
<td>
</td>
<td>
2013年3月23日
</td>
<td>
Photoshop在前几代加入了GPUOpenGL加速、内容填充等新特性,此代会加强3D图像编辑,采用新的暗色调用户界面,其他改进还有整合Adobe云服务、改进文件搜索等。
</td>
</tr>
<tr>
<td>
CC
</td>
<td>
Mac OS X 10.7/10.8 /Windows vista/7,8,8.1
</td>
<td>
</td>
<td>
2013年7月
</td>
<td>
在Photoshop CS6功能的基础上,Photoshop CC新增相机防抖动功能、CameraRAW功能改进、图像提升采样、属性面板改进、Behance集成等功能,以及Creative Cloud,即云功能。
</td>
</tr>
</tbody>
</table>
<h2 id="总结">总结</h2>
<p>以上是到目前为止photoshop的版本历史,如果你在网上看到不再这些版本里面的,那么可以搜索下相关信息,很有可能是假的。</p>
<p>我会维护这份博客的更新,希望photoshop越来越好。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://www.zhujiangroad.com/design/photoshop/27612.html">图解:1988-2011 Photoshop发展历史回顾</a></li>
</ul>
我的gem笔记
2015-08-25T00:00:00+00:00
http://yanhaijing.com/tool/2015/08/25/my-gem-note
<p>由于写博客要用到jekyll,而jekyll是基于ruby的,而gem是ruby的包管理器,所以我需要用到gem这个工具。</p>
<p><img src="/blog/237.png" alt="" /></p>
<p>本文将记录一些常用gem命令,以备不时之需(每次都查还烦人)。</p>
<h2 id="更换淘宝镜像">更换淘宝镜像</h2>
<p>由于国内网络原因(你懂的),导致 rubygems.org 存放在 Amazon S3 上面的资源文件间歇性连接失败。这里要更换默认镜像为<a href="https://gems.ruby-china.com/">ruby-china的镜像</a>。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem sources --remove https://rubygems.org/
gem sources -a https://gems.ruby-china.com/
gem sources -l
*** CURRENT SOURCES ***
https://gems.ruby-china.com/
# 请确保只有 https://gems.ruby-china.com/
</code></pre></div></div>
<h2 id="常用命令">常用命令</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem -v # 查看RubyGems软件的版本
gem help #显示RubyGem使用帮助
gem help example #列出RubyGem命令一些使用范例
gem install [gemname] # 安装指定gem包,程序先从本机查找gem包并安装,如果本地没有,则从远程gem安装。
gem install -l [gemname] # 仅从本机安装gem包
gem install -r [gemname] # 仅从远程安装gem包
gem install [gemname] --version=[ver] # 安装指定版本的gem包
gem uninstall [gemname] # 删除指定的gem包,注意此命令将删除所有已安装的版本
gem uninstall [gemname] --version=[ver] # 删除某指定版本gem
gem update --system # 更新升级RubyGems软件自身
gem update [gemname] #更新所有|指定已安装的gem包
gem list # 查看本机已安装的所有gem包 #显示RubyGem使用帮助
</code></pre></div></div>
<h2 id="深入阅读">深入阅读</h2>
<ul>
<li><a href="http://guides.rubygems.org/command-reference/">Guides</a></li>
</ul>
番茄工作法实战
2015-08-24T00:00:00+00:00
http://yanhaijing.com/work/2015/08/24/pomodoro-practice
<p>极度拖延的我,最近收获了一本《<a href="http://www.amazon.cn/gp/product/B004O9F71K/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B004O9F71K&linkCode=as2&tag=yanhaijing-23">图解番茄工作法</a>》,如获至宝,准备自己实践一下,希望能有所收获。</p>
<p><img src="/blog/238.jpg" alt="" /></p>
<p>本文并不会详细介绍番茄工作法是什么,如果你还不了解番茄工作法,可以阅读上面提到的书籍。</p>
<p>本文可以算是我的一篇读后感,或者说是对书中内容的总结,或者说是番茄工作法的要点,主要是目的是为了给自己实施番茄工作法已指导,这样就不用每次去翻书了,这里介绍的是原版的番茄工作法核心。</p>
<h2 id="核心思想">核心思想</h2>
<p>一次只做一件事,计划-承诺-总结,选择-行动-修复。</p>
<h2 id="目标">目标</h2>
<p>番茄工作法是一套简单的工具和流程,用以提升个人和所在团队的生产力,从而做到以下目标:</p>
<ul>
<li>减轻时间焦虑</li>
<li>提升集中力和注意力,减少中断</li>
<li>增强决策意识</li>
<li>唤醒激励和持久激励</li>
<li>巩固达成目标的决心</li>
<li>晚上预估流程,精确地保质保量</li>
<li>改进工作,学习流程</li>
<li>强化决断力,快到斩乱麻</li>
</ul>
<h2 id="所需工具">所需工具</h2>
<ul>
<li>番茄钟</li>
<li>一支铅笔+橡皮</li>
<li>纸质表格三张(白纸即可)
<ul>
<li>活动清单</li>
<li>今日待办(承诺)</li>
<li>记录表格</li>
</ul>
</li>
</ul>
<h2 id="基本方法">基本方法</h2>
<p>将要完成的活动全部填入“活动清单”表格,每天造成从其中选出今日要完成的活动,填入“今日待办”表格。</p>
<p>然后开始工作:</p>
<ul>
<li>在“今日代办”中选择一项最重要的活动</li>
<li>启动番茄钟,时间设定为25分钟</li>
<li>开始工作,直到番茄钟响</li>
<li>在“今日代办”表格该活动右侧标一个X</li>
<li>若该活动已完成,则在“今日代办”表格上换掉它</li>
<li>休息片刻5分钟</li>
<li>重复上面的步骤</li>
<li>每四个番茄钟后,休息20分钟</li>
</ul>
<p>每天工作结束后,要进行记录,刚开始使用番茄工作法时,只记录每天完成的番茄钟数就好了。记录表格第一列是日期,第二列是番茄钟数</p>
<h2 id="规则和技巧">规则和技巧</h2>
<ul>
<li>番茄钟不可分割</li>
<li>如果一项活动要话费超过5-7个番茄钟,应该进行拆分</li>
<li>如果一项活动的花费不足一个番茄钟,则可与其他活动合并</li>
<li>一旦番茄钟启动,就必须走到响铃</li>
</ul>
<h2 id="中断">中断</h2>
<p>应用番茄工作法时,中断是难以避免的问题。下面分别介绍不同中断的处理处理方法。</p>
<p>中断的番茄钟要作废,不能记录到今日代办表格。</p>
<h3 id="内部中断">内部中断</h3>
<ul>
<li>每次你想要做其他活动,在今日代办的当前任务后面画一个撇号(`)</li>
<li>在今日代办表格的计划外紧急区域记下这项活动,今天完成。</li>
<li>或在活动清单表格中记下这项活动,改天完成(标”U”代表计划外,如果需要还可写上最后期限)</li>
<li>再接再厉完成当前番茄钟,直到番茄钟响铃</li>
</ul>
<h3 id="外部中断">外部中断</h3>
<ul>
<li>每次外部中断,在当前任务的后面画一个减号(-)</li>
<li>采取“告知-协商-回电”策略,与中断者沟通</li>
<li>应用与上面内部中断相同的步骤记录相关活动,并继续完成当前番茄钟</li>
<li>多数看似紧急的外部中断,其实可以推迟25分钟或2个小时(4个番茄钟)再来处理</li>
<li>番茄钟期间,可关闭电子邮件和聊天软件</li>
</ul>
<h2 id="预估">预估</h2>
<p>番茄工作法预估的核心是花时间做回顾,将“认为自己能做的”和“实际做到的”相对应。一定要跟踪和记录。</p>
<h3 id="活动清单中的预估">活动清单中的预估</h3>
<p>预估番茄钟数写在“活动清单”表格每个条目的右边,当然是用铅笔。</p>
<h3 id="今日代办中的预估">今日代办中的预估</h3>
<p>今日代办表格中每项任务后面画方格子,代表自己预估的番茄数,没花吃一个番茄,则在对应方格画X,如果方格满了还没完成工作,要做重新预付,并在后面画圆,代表二次预估的番茄数,依次类推。</p>
<h3 id="记录表格中的预估">记录表格中的预估</h3>
<p>记录表格新增一列,如果一项活动被高估了三个番茄钟(+3),若被低估了则是-N。把数字相加,“今日代办”表格中预留的小方格比实际用量多了5个,即总体误差为+5。</p>
<p>记录表格中再新增一列,用来记录重新预估的次数。</p>
<p>我们的目标是让两列都为0。</p>
<h2 id="我的番茄工作法">我的番茄工作法</h2>
<p>我决定从今天起开始使用番茄工作法,早晨我去公司领了如下文具:</p>
<ul>
<li>黑、蓝、红色笔,铅笔+橡皮</li>
<li>两个comix 螺旋装订本 A5</li>
<li>安卓APP-番茄时钟</li>
</ul>
<p><img src="/blog//177.jpg" alt="" /></p>
<p>铅笔用来写一些临时可能会修改的数据,黑色笔用来书写大部分数据。</p>
<p>蓝色的本子是每日清单,绿色的本子正面是活动清单,从后往前是记录清单,这样我只要两个本子就可以解决三张表格的问题,虽然本子不要钱,但还是要节约。</p>
<p>这种本子本身就是带横格的,所以就省去了制作表格的麻烦,而且还装订在一起了,省的丢,并且这种本子想撕下来一页非常容易,这是我再三思考的选择。</p>
<h2 id="总结">总结</h2>
<p>看完了书,感觉这真是一个不错的工具,即便你不使用番茄工作法,三张表格对每个人来说也非常重要,那么欢迎你也加入番茄工作法吧,如果有任何疑问可以在评论区和我讨论。</p>
<h2 id="附件">附件</h2>
<ul>
<li><a href="http://wenku.baidu.com/link?url=_cTNP2WkI_8aTh1Wy-FsBeO-FH09cFdfPRIEjcr9VgwbglPP5MYyJJnexdPZzf5KsANV328KAW74R9p8rPwsj9sFulonZLvRdZo-Pf30WyK">番茄工作法实战表格</a></li>
</ul>
列举webpack的几大特色
2015-08-22T00:00:00+00:00
http://yanhaijing.com/tool/2015/08/22/webpack-features
<p><a href="http://webpack.github.io/">webpack</a>是一款优秀的静态资源打包工具,本文将会介绍其一些特色。</p>
<p><img src="/blog/239.png" alt="" /></p>
<h2 id="插件">插件</h2>
<p><a href="http://webpack.github.io/">webpack</a>有丰富的插件接口。内部插件使用这些接口完成了大部分特色。这些接口使webpack非常灵活。</p>
<h2 id="性能">性能</h2>
<p><a href="http://webpack.github.io/">webpack</a>使用异步I/O,并且有多级缓存机制。这让webpack速度非常快,和让人难以置信的增量编译速度。</p>
<h2 id="加载器">加载器</h2>
<p><a href="http://webpack.github.io/">webpack</a>通过加载器机制支持文件的预处理。webpack支持打包任何静态资源,而不仅仅是javascript。你也可以很容易的编写自己的插件。</p>
<h2 id="支持">支持</h2>
<p><a href="http://webpack.github.io/">webpack</a>支持<a href="http://webpack.github.io/docs/amd.html">ADM</a>和<a href="http://webpack.github.io/docs/commonjs.html">CommonJs</a>模块风格。webpack对代码执行聪明的静态语法分析,甚至有一个评估引擎用来评估简单表达式。这使webpack对大部分现存的库的能很好的支持。</p>
<h2 id="代码分割模块">代码分割(模块)</h2>
<p>webpack支持代码<a href="http://webpack.github.io/docs/code-splitting.html">分割</a>成块(模块),块可按需加载,减少初始加载时间。</p>
<h2 id="优化">优化</h2>
<p>webpack会进行很多<a href="http://webpack.github.io/docs/optimization.html">优化</a>工作,来减少文件的大小,对<a href="http://webpack.github.io/docs/long-term-caching.html">访问缓存</a>也有很好的支持——哈希值。</p>
<h2 id="开发工具">开发工具</h2>
<p>webpack支持SourceUrls和SourceMaps,可用于简单的调试。也可监控文件,通过中间件和服务器实现自动重载。</p>
<h2 id="多平台">多平台</h2>
<p>webpack是为web而生的,但也支持<a href="https://github.com/webpack/worker-loader">webworks</a>和node.js。</p>
<h2 id="更多资料">更多资料</h2>
<ul>
<li><a href="http://segmentfault.com/a/1190000002551952">Webpack 入门指迷</a></li>
<li><a href="http://segmentfault.com/a/1190000002552008">Webpack 怎么用</a></li>
</ul>
分享几个制作loading效果的生成工具
2015-07-09T00:00:00+00:00
http://yanhaijing.com/tool/2015/07/09/loading-gif-generator
<p>今天给大家分享几个自己收藏的制作loading gif图的网站,虽然现在css3也能做出同样的效果,但在考虑到兼容性的时候,gif可以说是最快速的方法。</p>
<h2 id="ajaxloadinfo"><a href="http://www.ajaxload.info/" title="ajaxload.info">ajaxload.info</a></h2>
<p>这是我最常用的一个,支持多种形状,前景色,背景色,透明背景,非常方便。</p>
<h2 id="loadingapngcom"><a href="http://loadingapng.com/" title="loadingapng.com">loadingapng.com</a></h2>
<p>很精美的页面,只是我发现不能下载不知道为什么,支持两种图片类型,多种形状,前景色,背景色,透明背景,自定义大小。</p>
<h2 id="loadinggifcom"><a href="http://www.loadinggif.com/" title="loadinggif.com">loadinggif.com</a></h2>
<p>简单实用派,支持多种形状,前景色,背景色,透明背景。</p>
<h2 id="preloadersnet"><a href="http://preloaders.net/" title="preloaders.net">preloaders.net</a></h2>
<p>支持多种形状,两种格式,前景色,背景色,透明别净,动画速度,自定义尺寸等</p>
<h2 id="总结">总结</h2>
<p>介绍这么多不进行对比就是耍流氓,几个工具的对比如下:</p>
<table class="table table-bordered">
<tbody>
<thead>
<td>名称</td>
<td>支持格式</td>
<td>多种形状</td>
<td>前景色</td>
<td>背景色</td>
<td>透明背景</td>
<td>大小</td>
<td>动画速度</td>
</thead>
<tbody>
<tr>
<td>[ajaxload.info][1]</td>
<td>gif</td>
<td>39</td>
<td>√</td>
<td>√</td>
<td>√</td>
<td></td>
<td></td>
</tr>
<tr>
<td>[loadingapng.com][2]</td>
<td>apng, gif</td>
<td>45</td>
<td>√</td>
<td>√</td>
<td>√</td>
<td>√</td>
<td></td>
</tr>
<tr>
<td>[loadinggif.com][3]</td>
<td>gif</td>
<td>37</td>
<td>√</td>
<td>√</td>
<td>√</td>
<td></td>
<td></td>
</tr>
<tr>
<td>[preloaders.net][4]</td>
<td>apng, gif</td>
<td>无数</td>
<td>√</td>
<td>√</td>
<td>√</td>
<td>√</td>
<td>√</td>
</tr>
</tbody>
</tbody>
</table>
<p>最后分享一下<a href="http://spin.js.org/" title="spin">spin.js</a>和<a href="http://cssload.net/">cssload</a>,一个是用js,一个使用css都可以生成动画。</p>
<p>如果你也有好的资料,那么在评论区和我一起讨论吧。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://www.cnblogs.com/lxblog/p/3425599.html">为大家分享一个 Ajax Loading —— spin.js</a></li>
</ul>
奇虎360Web前端开发面试经历
2015-06-26T00:00:00+00:00
http://yanhaijing.com/work/2015/06/26/find-job-of-360
<p>最近有幸参加了一次360的面试,下面把面试题总结记录一下:</p>
<p>下了公交车远远就看见了360的大楼,挺气派。</p>
<p><img src="/blog/179.jpg" alt="" /></p>
<p>围着楼绕了半圈才找到入口,不容易啊,入口在内部,走进大楼映入眼帘的是为人民服务的标语。</p>
<p><img src="/blog/180.jpg" alt="" /></p>
<p>喝水的一次性杯子都很精致。</p>
<p><img src="/blog/181.jpg" alt="" /></p>
<h2 id="一面">一面</h2>
<ul>
<li>js考考察了基本语法,正则表达式,this,原型,作用域</li>
<li>html和css考察了兼容性,布局,事件,和逻辑部分</li>
</ul>
<h2 id="二面">二面</h2>
<p>二面是大名鼎鼎的@月影(<a href="http://weibo.com/silverna">十年踪迹</a>),鸭梨好大啊。</p>
<p>二面考了两道比较简单的几何和概率</p>
<h2 id="三面">三面</h2>
<p>HR,不想多说了,竟然遇到了异父异母的亲兄弟。</p>
<h2 id="总结">总结</h2>
<p>面试题做的很烂,不想多说了。</p>
<h2 id="相关资料">相关资料</h2>
<ul>
<li><a href="http://javascript-puzzlers.herokuapp.com/">JavaScript Puzzlers!</a></li>
</ul>
比较LESS预处理器1.3.3到1.7.5的变化
2015-06-18T00:00:00+00:00
http://yanhaijing.com/less/2015/06/18/less-log
<p><img src="/blog/240.jpg" alt="" /></p>
<h2 id="新功能">新功能</h2>
<ul>
<li>extend <1.4.0 Beta 1 & 2></li>
<li>支持属性中的命名空间和选择器插值 <1.4.0 Beta 1 & 2></li>
<li>新增导入选项功能(reference, inline…)</li>
<li>允许属性合并通过 +: 语法 <1.5.0></li>
<li>支持属性差值,例如:@{prefix}-property: value; <1.6.0></li>
<li>支持^和^^ shadow dom 选择器 <1.6.1></li>
<li>支持属性合并,属性间隔为空格时使用+_ <1.7.0></li>
<li>支持深度组合 <1.7.5></li>
</ul>
<h2 id="新增函数">新增函数</h2>
<ul>
<li>data-uri, extract, luma, hsvhue, hsvsaturation, hsvvalue, pow, pi, mod, tan, sin, cos, atan, asin, acos, sqrt, convert <1.4.0 Beta 1 & 2></li>
<li>isunit <1.4.0 Beta 3></li>
<li>svg-gradient, length, min, max <1.5.0></li>
</ul>
<h2 id="变化">变化</h2>
<ul>
<li>数学运算现在仅发生在括号内 <1.4.0 Beta 1 & 2></li>
<li>单位参加计算 <1.4.0 Beta 1 & 2></li>
<li>mixin中的变量不再泄漏到调用他们的作用域 <1.4.0 Beta 1 & 2></li>
<li>rgba声明现在是收缩的。例如:rgba(-1, 258, 258, -1)将变为 rgba(0m 255, 255, 0) <1.6.0></li>
<li>展开& when() {,而不是复制 <1.6.2></li>
<li>支持比较引号不同的值。例如:现在”test” === ‘test’ <1.7.1></li>
<li>置顶@charsets 到正确的位置 <1.7.5></li>
</ul>
<h2 id="废弃">废弃</h2>
<ul>
<li>(~”@var”) 选择器插值被移除。<1.4.0 Beta 1 & 2></li>
<li>@import-once 已经被移除 <1.4.0 Beta 1 & 2></li>
</ul>
<h2 id="250">2.5.0</h2>
<p>2015-04-03</p>
<ul>
<li>supports the scoped <code class="language-plaintext highlighter-rouge">@plugin</code> directive to load function plugins</li>
<li>All directives are bubbled (e.g. supports), not just media</li>
<li>Performance improvements to the parser - should help non-chrome browsers with very large less files to be a lot quicker.</li>
<li>the image size function respects include paths like other file functions</li>
<li>colour functions take a relative argument that applies percentages relatively instead of absolutely</li>
<li>include paths now allows : as a separator on windows (recognising and not splitting drive names by the backslash)</li>
<li><code class="language-plaintext highlighter-rouge">@import (css)</code> does not pull the directive above comments</li>
<li>Fix for import statements without quotes sometimes causing issues</li>
<li>replace supports dimensions and colours</li>
<li>the browser field is set in the package.json for use with browserify</li>
<li>another fix to support paths being passed as a string instead of an array</li>
<li>detached rulesets can be used as default arguments</li>
<li>Fix a lot of false warnings about extends</li>
<li>errors written to stderr more consistently</li>
<li>consistently keep units if strict units is off</li>
<li>Better support for comments in function all arguments</li>
</ul>
<h2 id="240">2.4.0</h2>
<p>2015-02-07</p>
<ul>
<li>Support for plugins that pre-process (to add libraries silently etc.)</li>
<li>Empty sourcemaps now work</li>
<li>Extract and Length functions now ignore comments in a list (more work to come to fix the general problem)</li>
<li>fragment urls are treated absolute since they refer to the html document</li>
<li>Extends on a selector joined with <code class="language-plaintext highlighter-rouge">&</code> now work better</li>
<li>Nested mixins work better with !important (regression in 2.3.0)</li>
<li>The promise dependency is now actually optional (introduced in 2.0.0)</li>
<li>Files with just <code class="language-plaintext highlighter-rouge">\r</code> newlines now process ok (regression in 2.0.0)</li>
<li>When strict units is off and the unit is 1/x, (e.g. 1 / 12px) the unit output is x, previously nothing (regression in 2.0.0)</li>
</ul>
<h2 id="231">2.3.1</h2>
<p>2015-01-28</p>
<ul>
<li>Fix depends option (regression in 2.3.0)</li>
<li>Support parent selector (<code class="language-plaintext highlighter-rouge">&</code>) used in sub element expression (e.g. <code class="language-plaintext highlighter-rouge">:not(.c_&)</code>)</li>
</ul>
<h2 id="230">2.3.0</h2>
<p>2015-01-27</p>
<ul>
<li>add <code class="language-plaintext highlighter-rouge">isruleset</code> function</li>
<li>add optional import option, causing less to not fail if file not found</li>
<li>Fix browsers-side cache.</li>
<li>Many fixes to import reference - support <code class="language-plaintext highlighter-rouge">@support</code> and keyframe</li>
<li>Selectors now interpolate pseudo selectors (e.g. <code class="language-plaintext highlighter-rouge">:@{hover}</code>)</li>
<li>Fix comments missed off if they were at the end of the file</li>
<li>Fix !important used with parametric mixins</li>
<li>Emits warnings for extends when the target is not found</li>
<li>include-path now works on data-uri</li>
<li>variables and function calls work for path in data-uri</li>
<li>Fix absolute paths not working on imports sometimes.</li>
<li>Unicode BOM removed again</li>
<li>Misc. bug fixes</li>
</ul>
<h2 id="220">2.2.0</h2>
<p>2015-01-04</p>
<ul>
<li>do not apply relative paths to svg-gradient and data-uri functions data-uri output</li>
<li>using import filename interpolation and import inline together now works</li>
<li>deprecate the compression option (still works, but outputs a warning unless silent)</li>
<li>The node version of less now has image-size, image-width, image-height which return the image dimensions of a file</li>
<li>Fixed an issue that could cause the parse to occur more than once and the callback be called multiple times</li>
<li>if you are outputting to the console, lessc defaults to silent so warnings do not end up in output</li>
<li><code class="language-plaintext highlighter-rouge">isunit</code> function supports <code class="language-plaintext highlighter-rouge">''</code> to test if a dimension has no unit</li>
<li>data-uri function now counts characters after base64 encoding instead of bytes before encoding to determine ie8 support</li>
<li>fix bug effecting guards on pseudo class selectors</li>
<li>do not cache on the browser when used with modifyVars</li>
<li>detection if less does not parse last character in file</li>
<li>detection of whether a file is css now requires <code class="language-plaintext highlighter-rouge">/css</code>, <code class="language-plaintext highlighter-rouge">.css</code>, <code class="language-plaintext highlighter-rouge">?css</code>, <code class="language-plaintext highlighter-rouge">&css</code> instead of just <code class="language-plaintext highlighter-rouge">css</code>. You can still tell less the type of file using import options.</li>
<li>remove extra new line added to sourcemap entry inline file</li>
<li>support safari extension</li>
<li>less.parse now exposes a way to get the AST. We do not recommend you use this unless you need to.</li>
</ul>
<h2 id="212">2.1.2</h2>
<p>2014-12-20</p>
<ul>
<li>Fix for use with requirejs</li>
<li>Fixes for data-uri function</li>
</ul>
<h2 id="211">2.1.1</h2>
<p>2014-11-27</p>
<ul>
<li>Improved keyword and anonymous usage with the replace function</li>
<li>Added <code class="language-plaintext highlighter-rouge">getCSSAppendage</code> to sourcemap builder to avoid duplication in plugins</li>
<li>Fix problem with plugins when used with the promises version of render</li>
<li>If the render callback throws an exception it now propogates instead of calling the callback again with an error</li>
</ul>
<h2 id="210">2.1.0</h2>
<p>2014-11-23</p>
<ul>
<li>Fixed <code class="language-plaintext highlighter-rouge">isSync</code> option, it was using sync file operations but promises are guaranteed to call back async. We now support promises as a feature rather than the 1st class way of doing things.</li>
<li>Browser code is now synchronous again, like in v1, meaning it blocks the site until less is compiled</li>
<li>Some fixes for variable imports which affected filemanagers when synchronous</li>
<li>Fixed lessc makefile dependencies option</li>
<li>output now reports back a imports field with an array of imported files</li>
<li>relative path test for drive names (so windows only) is now case insensitive</li>
<li>Fix for IE7 - use getChar instead of indexing array</li>
<li>variables using !important now output !important, which bubbles up to affect the rule</li>
<li>livereload cache buster is now treated specially</li>
<li>upgrade dependencies</li>
</ul>
<h2 id="200">2.0.0</h2>
<p>2014-11-09</p>
<ul>
<li>Fixed multiplication in non strict units mode to take the left operand unit, in the case that the unit cannot be resolved</li>
<li>Some fixes for browser cross-compatibility</li>
<li>browser tests now pass in IE 8-11 and FF</li>
<li>added index.js and browser.js in root as shortcuts</li>
<li>fixed some local variable spellings</li>
<li>support for <code class="language-plaintext highlighter-rouge">@counter-style</code> directive</li>
</ul>
<h2 id="200-b3">2.0.0-b3</h2>
<p>2014-11-01</p>
<ul>
<li>some refactoring of browser structure to allow use of api vs normal browser bundle</li>
<li>browser bundle no longer leaks require</li>
<li>browser can now be scoped with just window</li>
<li>browser <code class="language-plaintext highlighter-rouge">useFileCache</code> defaults to <code class="language-plaintext highlighter-rouge">true</code>, but file cache is now cleared when refreshing or in watch mode</li>
</ul>
<h2 id="200-b2">2.0.0-b2</h2>
<p>2014-10-26</p>
<ul>
<li>Imports are now sequenced and so are consistent (previously some complex projects might end up with occasional different orderings)</li>
<li>Imports with variables are better supported - variables can be specified in sub imports</li>
<li>Support for rebeccapurple</li>
<li>Browser can now accept options as attributes on the script tag and the link tags e.g. <code class="language-plaintext highlighter-rouge"><script data-verbose="false" src="less.js"...</code></li>
<li>adding a .less file extension is done in the abstract file manager so it the behaviour can be overridden by certain file managers</li>
<li>Fixed a bug where unquoted urls beginning <code class="language-plaintext highlighter-rouge">//</code> e.g. <code class="language-plaintext highlighter-rouge">url(//file/file.less)</code> would be incorrectly interpreted (bug introduced in b-1)</li>
<li>lessc plugins can be a function, used as a constructor as well as an object - this to allow the plugin more flexibility to be used programattically</li>
</ul>
<h2 id="200-b1">2.0.0-b1</h2>
<p>2014-10-19</p>
<ul>
<li>Public Beta / Release Candidate - Feedback welcome
For a guide to breaking changes see <a href="http://lesscss.org/usage/## v2-upgrade-guide">the v2 upgrade guide</a></li>
<li>no longer including old versions of less in the repo or npm</li>
<li>not including test less and gradle files in npm</li>
<li>colours now output in the format they are added, so yellow will output yellow, not its hex counterpart</li>
<li>better parsing - better comment support and comments in brackets can now contain comments including quotes.</li>
<li>Removal of dependency on clean-css - install less-plugin-clean-css and use –clean-css to reference plugin</li>
<li>Environment Support - less is now separate from its node and browser environment implementations and adding support for another javascript environment should be straight forward.</li>
<li>Plugin Support - it is now straight forward to add AST manipulations (see less-plugin-inline-images), file managers (see less-plugin-npm-import) and post processors (see less-plugin-clean-css and less-plugin-autoprefix).</li>
<li>We now recommend using less.render and using the parser directly is not in the same way as in v2. It is possible but it would require changes and we do not guarantee it will not be broken in minor version releases.</li>
<li>In the browser, less.pageLoadFinished will be a promise, resolved when less has finished its initial processing. less.refresh and less.modifyVars also return promises.</li>
<li>In the browser, as before less is used as options, however this is now copied to less.options if you need to access after less has run</li>
<li>In the browser, the cache can be overwritten by setting less.cache before less loads. After load less.cache will be the default implementation.</li>
<li>less.js now uses browserify to generate its browser side component</li>
<li>default values for the sourcemap options have been re-done and improved to hopefully mean creating sourcemaps is easier</li>
<li>Many smaller bugfixes and API changes. Please let us know if something you relied on has disappeared or an area should be better documented.</li>
</ul>
<h2 id="175">1.7.5</h2>
<p>2014-09-03</p>
<ul>
<li>Allow comments in keyframe (complete comment support coming in 2.0)</li>
<li>允许在keyframe中存在注释</li>
<li>pass options to parser from less.render</li>
<li>从less。render传递参数给解析器</li>
<li>Support /deep/ combinator</li>
<li>支持深度组合</li>
<li>handle fragments in data-uri’s</li>
<li>处理 data-uri 片段</li>
<li>float @charsets to the top correctly</li>
<li>置顶@charsets 到正确的位置</li>
<li>updates to some dependencies</li>
<li>更新部分依赖</li>
<li>Fix interpolated import in media query</li>
<li>修复媒体查询中的import插值</li>
<li>A few other various small corrections</li>
<li>其他一些小问题修复</li>
</ul>
<h2 id="174">1.7.4</h2>
<p>2014-07-27</p>
<ul>
<li>Handle uppercase paths in browser</li>
<li>处理浏览器中中路径中的大写字母</li>
<li>Show error if an empty selector is used in extend</li>
<li>如果对空选择器使用extend将会报错</li>
<li>Fix property merging in directives</li>
<li>修复directives中的属性合并问题</li>
<li>Fix ordering of charset and import directives</li>
<li>修复字符集和导入指令的顺序问题</li>
<li>Fix race condition that caused a rules is undefined error sometimes if you had a complex import strategy</li>
<li>Better error message for imports missing semi-colons or malformed</li>
<li>导入确实分号将会有更好的错误消息</li>
<li>Do not use util.print to avoid deprecate warnings in node 0.11</li>
<li>不再使用 util.print,node0.11中将会废弃</li>
</ul>
<h2 id="173">1.7.3</h2>
<p>2014-06-22</p>
<ul>
<li>Include dist files, missing from 1.7.2</li>
<li>引入dist文件,1.7.2中丢失的</li>
<li>Do not round the results of color functions, like lightness, hue, luma etc.</li>
<li>不再取舍颜色函数的结果,例如:lightness, hue, luma等</li>
<li>Support cover and contain keywords in background definitions</li>
<li>支持背景中的cover和contain关键字</li>
</ul>
<h2 id="172">1.7.2</h2>
<p>2014-06-19</p>
<ul>
<li>Allow paths option to be a string (in 1.7.1 less started throwing an exception instead of incorrectly processing the string as an array of chars)</li>
<li>允许路径选项是一个字符串</li>
<li>Do not round numbers when used with javascript (introduced 1.7.0)</li>
<li>使用javascript时,不再修改数字精度</li>
</ul>
<h2 id="171">1.7.1</h2>
<p>2014-06-08</p>
<ul>
<li>Fix detection of recursive mixins</li>
<li>修复对mixin循环引用的检测</li>
<li>Fix the paths option for later versions of node (0.10+)</li>
<li>修复path参数</li>
<li>Fix paths joining bug</li>
<li>修复路径加入问题</li>
<li>Fix a number precision issue on some versions of node</li>
<li>解决部分node版本中,数字精度问题</li>
<li>Fix an IE8 issue with importing css files</li>
<li>修复IE8中导入css文件的一个问题</li>
<li>Fix IE11 detection for xhr requests</li>
<li>修复IE11检测xhr请求问题</li>
<li>Modify var works if the last line of a less file is a comment.</li>
<li>Better detection of valid hex colour codes</li>
<li>更好的检测十六进制颜色值</li>
<li>Some stability fixes to support a low number of available file handles</li>
<li>Support comparing values with different quote types e.g. “test” now === ‘test’</li>
<li>支持比较引号不同的值。例如:现在”test” === ‘test’</li>
<li>Give better error messages if accessing a url that returns a non 200 status code</li>
<li>对于返回非200状态码的url,给出更好的错误提示</li>
<li>Fix the e() function when passed empty string</li>
<li>修复给e()函数传递空字符串的错误</li>
<li>Several minor bug fixes</li>
<li>几个小错误修复</li>
</ul>
<h2 id="170">1.7.0</h2>
<p>2014-02-27</p>
<ul>
<li>Add support for rulesets in variables and passed to mixins to allow wrapping</li>
<li>支持变量中的规则集,并且可以传递给mixin</li>
<li>Change luma to follow the w3c spec, luma is available as luminance. Contrast still uses luma so you may see differences if your threshold % is close to the existing calculated luma.</li>
<li>更改luma函数,遵循w3c规范,luma现在可以作为luminance使用。</li>
<li>Upgraded clean css which means the –selectors-merge-mode is now renamed –compatibility</li>
<li>升级clean css,–selectors-merge-mode现在更名为–compatibility</li>
<li>Add support for using variables with @keyframes, @namespace, @charset</li>
<li>添加对如下变量的支持:@keyframes, @namespace, @charset</li>
<li>Support property merging with +_ when spaces are needed and keep + for comma separated</li>
<li>支持属性合并,属性间隔为空格时使用+_,间隔为逗号时使用+</li>
<li>Imports now always import once consistently - a race condition meant previously certain configurations would lead to a different ordering of files</li>
<li>Fix support for <code class="language-plaintext highlighter-rouge">.mixin(@args...)</code> when called with no args (e.g. <code class="language-plaintext highlighter-rouge">.mixin();</code>)</li>
<li>当没有参数时,修复对<code class="language-plaintext highlighter-rouge">.mixin(@args...)</code>的支持(例如:<code class="language-plaintext highlighter-rouge">.mixin();</code>)</li>
<li>Do unit conversions with min and max functions. Don’t pass through if not understood, throw an error</li>
<li>min和max函数将进行单位转换。不能识别的单位将会报错</li>
<li>Allow % to be passed on its own to the unit function e.g. <code class="language-plaintext highlighter-rouge">unit(10, %)</code></li>
<li>unit函数支持传入%。例如:<code class="language-plaintext highlighter-rouge">unit(10, %)</code></li>
<li>Fix a bug when comparing a unit value to a non-unit value if the unit-value was the multiple of another unit (e.g. cm, mm, deg etc.)</li>
<li>修复有单位值和无单位值比较时的一个错误(例如:cm,mm,deg等)</li>
<li>Fix mixins with media queries in import reference files not being put into the output (they now output, they used to incorrectly not)</li>
<li>修import refrence文件中的媒体查询中的mixin不被输出问题(尼玛太拗口了)</li>
<li>Fix lint mode - now reports all errors</li>
<li>修复lint模式——现在会报告全部的错误</li>
<li>Fixed a small scope issue with & {} selector rulesets incorrectly making mixins visible - regression from 1.6.2</li>
<li>修复一个小的作用域问题——1.6.2中引入,& {}选择器会引起mixin可见</li>
<li>Browser - added log level “debug” at 3 to get less logging, The default has changed so unless you set the value to the default you won’t see a difference</li>
<li>Browser - logLevel takes effect regardless of the environment (production/dev)</li>
<li>Browser - added postProcessor option, a function called to post-process the css before adding to the page</li>
<li>浏览器——添加postProcessor选项,</li>
<li>Browser - use the right request for file access in IE</li>
<li>浏览器——在IE中使用正确的文件访问请求</li>
</ul>
<h2 id="163">1.6.3</h2>
<p>2014-02-08</p>
<ul>
<li>Fix issue with calling toCSS twice not working in some situations (like with bootstrap 2)</li>
<li>修复在某些情况下调用两次toCSS导致不工作的情况(像bootstrap 2)</li>
</ul>
<h2 id="162">1.6.2</h2>
<p>2014-02-02</p>
<ul>
<li>The Rhino release is fixed!</li>
<li>修复RHino版本问题(实在不知道如何翻译)</li>
<li>ability to use uppercase colours</li>
<li>可以使用大写颜色</li>
<li>Fix a nasty bug causing syntax errors when selector interpolation is preceded by a long comment (and some other cases)</li>
<li>修复一个会引起语法错误的严重的问题——当选择器差值之前有一段很长评论时</li>
<li>Fix a major bug with the variable scope in guards on selectors (e.g. not mixins)</li>
<li>修复选择器守护中变量作用域的一个重大错误</li>
<li>Fold in <code class="language-plaintext highlighter-rouge">& when () {</code> to the current selector rather than duplicating it</li>
<li>展开<code class="language-plaintext highlighter-rouge">& when() {</code>,而不是复制</li>
<li>fix another issue with array prototypes</li>
<li>修复数组原型的一些其他问题</li>
<li>add a url-args option which adds a value to all urls (for cache busting)</li>
<li>添加 url-args 参数,可以给所有url添加值</li>
<li>Round numbers to 8 decimal places - thereby stopping javascript precision errors</li>
<li>整数保留8位小数,避免javascript精度问题</li>
<li>some improvements to the default() function in more complex scenarios</li>
<li>改进default函数在更复杂环境下的表现</li>
<li>improved missing ‘{‘ and ‘(‘ detection</li>
<li>改善丢失’{‘和’(‘的检测</li>
</ul>
<h2 id="161">1.6.1</h2>
<p>2014-01-12</p>
<ul>
<li>support ^ and ^^ shadow dom selectors</li>
<li>支持^和^^ shadow dom 选择器</li>
<li>fix sourcemap selector (used to report end of the element or selector) and directive position (previously not supported)</li>
<li>修复sourcemap 选择器</li>
<li>fix parsing empty less files</li>
<li>修复解析空less文件的问题</li>
<li>error on (currently) ambiguous guards on multiple css selectors</li>
<li>older environments - protect against typeof regex returning function</li>
<li>老版本浏览器中,防止typeof 正则表达式 返回函数</li>
<li>Do not use default keyword</li>
<li>不再使用默认关键字</li>
<li>use innerHTML in tests, not innerText</li>
<li>在测试中使用 innerHTML代替innerText</li>
<li>protect for-in in case Array and Object prototypes have custom fields</li>
<li>保护for-in引用原型属性的问题</li>
</ul>
<h2 id="160">1.6.0</h2>
<p>2014-01-01</p>
<ul>
<li>Properties can be interpolated, e.g. @{prefix}-property: value;</li>
<li>支持属性差值,例如:@{prefix}-property: value;</li>
<li>a default function has been added only valid in mixin definitions to determine if no other mixins have been matched</li>
<li>Added a plugins option that allows specifying an array of visitors run on the less AST</li>
<li>添加一个插件参数,允许指定一个数组运行less AST</li>
<li>Performance improvements that may result in approx 20-40% speed up</li>
<li>性能改进,提速20-40%</li>
<li>Javascript evaluations returning numbers can now be used in calculations/functions</li>
<li>javascripteval返回值现在可以用来计算</li>
<li>fixed issue when adding colours, taking the alpha over 1 and breaking when used in colour functions</li>
<li>修复颜色函数的错误</li>
<li>when adding together 2 colours with non zero alpha, the alpha will now be combined rather than added</li>
<li>当两个颜色的非零alpha相遇时,不再简单相加,而是取其较大者</li>
<li>the advanced colour functions no longer ignore transparency, they blend that too</li>
<li>更高级的颜色函数,不再忽略透明度</li>
<li>Added –clean-option and cleancssOptions to allow passing in clean css options</li>
<li>添加 –clean-option 和 cleancssOptions 允许传入clean-css的参数</li>
<li>rgba declarations are now always clamped e.g. rgba(-1,258,258, -1) becomes rgba(0, 255, 255, 0)</li>
<li>rgba声明现在是收缩的。例如:rgba(-1, 258, 258, -1)将变为 rgba(0m 255, 255, 0)</li>
<li>Fix possible issue with import reference not bringing in styles (may not be a bugfix, just a code tidy)</li>
<li>修复import reference的一些问题</li>
<li>Fix some issues with urls() being prefixed twice and unquoted urls in mixins being processed each time they are called</li>
<li>修复urls函数的一些问题</li>
<li>Fixed error messages for undefined variables in javascript evaluation</li>
<li>修复javascript eval中变量未定义的错误消息</li>
<li>Fixed line/column numbers from math errors</li>
<li>修复数学运算中行/列数字错误</li>
</ul>
<h2 id="151">1.5.1</h2>
<p>2013-11-17</p>
<ul>
<li>Added source-map-URL option</li>
<li>添加 source-map-URL 参数</li>
<li>Fixed a bug which meant the minimised 1.5.0 browser version was not wrapped, meaning it interfered with require js</li>
<li>Fixed a bug where the browser version assume port was specified</li>
<li>修复一个bug,浏览器版本假定指定了端口</li>
<li>Added the ability to specify variables on the command line</li>
<li>添加在命令行中指定变量的能力</li>
<li>Upgraded clean-css and fixed it from trying to import</li>
<li>升级clean-css</li>
<li>correct a bug meaning imports weren’t synchronous (syncImport option available for full synchronous behaviour)</li>
<li>纠正一个imports 不同步的问题(syncImport选项可以解决这个问题)</li>
<li>better mixin matching behaviour with calling multiple classes e.g. .a.b.c;</li>
<li>对于调用多个类是,更好的mixin匹配行为。例如: .a.b.c;</li>
</ul>
<h2 id="150">1.5.0</h2>
<p>2013-10-21</p>
<ul>
<li>sourcemap support</li>
<li>支持 sourcemap(一直可以把编译后的样式和源文件对应起来的技术)</li>
<li>support for import inline option to include css that you do NOT want less to parse e.g. <code class="language-plaintext highlighter-rouge">@import (inline) "file.css";</code></li>
<li>支持内联导入选项,用来包含不想用less解析的css文件。<code class="language-plaintext highlighter-rouge">@import (inline) "file.css"</code></li>
<li>better support for modifyVars (refresh styles with new variables, using a file cache), is now more resiliant</li>
<li>更好支持 modifyVars(使用新变量刷新风格,使用文件缓存),现在更resiliant</li>
<li>support for import reference option to reference external css, but not output it. Any mixin calls or extend’s will be output.</li>
<li>支持导入 reference 选项,来实现引用css,但不会输出。mixin和extends将会输出。</li>
<li>support for guards on selectors (currently only if you have a single selector)</li>
<li>支持在选择符中使用guard(当前仅支持单个选择符)</li>
<li>allow property merging through the +: syntax</li>
<li>允许属性合并通过 +: 语法</li>
<li>Added min/max functions</li>
<li>添加 min/max 函数</li>
<li>Added length function and improved extract to work with comma separated values</li>
<li>添加 length 函数,改进 extract函数支持逗号分隔符</li>
<li>when using import multiple, sub imports are imported multiple times into final output</li>
<li>当使用 import multiple,子导入最后将被输出多次</li>
<li>fix bad spaces between namespace operators</li>
<li>修复命名空间操作符之间的空格问题</li>
<li>do not compress comment if it begins with an exclamation mark</li>
<li>感叹号开头的评论将不会被压缩(这是非常有用)</li>
<li>Fix the saturate function to pass through when using the CSS syntax</li>
<li>当使用css语法是,修复saturate函数传递问题</li>
<li>Added svg-gradient function</li>
<li>添加 svg-gradient 函数</li>
<li>Added no-js option to lessc (in browser, use javascriptEnabled: false) which disallows JavaScript in less files</li>
<li>lessc添加no-js参数(在浏览器中,使用 javascriptEnabled: false),进制在less文件中使用javascript</li>
<li>switched from the little supported and buggy cssmin (previously ycssmin) to clean-css</li>
<li>从小众且bug百出的cssmin(前身是ycssmin)切换到clean-css</li>
<li>support transparent as a color, but not convert between rgba(0, 0, 0, 0) and transparent</li>
<li>支持透明颜色,但不能使用convert转换 rgba(0, 0, 0, 0)和transparent</li>
<li>remove sys.puts calls to stop deprecation warnings in future node.js releases</li>
<li>删除 sys.puts 调用,在node.js未来版本中将会废弃</li>
<li>Browser: added logLevel option to control logging (2 = everything, 1 = errors only, 0 = no logging)</li>
<li>浏览器:添加 logLevel 选项,用来控制日志输出(2=全部,1=仅错误,0=不输出)</li>
<li>Browser: added errorReporting option which can be “html” (default) or “console” or a function</li>
<li>浏览器:添加 errorReporting 选项——可以是”html”(默认)或”console”或一个函数。</li>
<li>Now uses grunt for building and testing</li>
<li>现在使用 grunt 构建和测试</li>
<li>A few bug fixes for media queries, extends, scoping, compression and import once.</li>
<li>修复媒体查询,extends,作用域,压缩和import once相关的一些问题</li>
</ul>
<h2 id="142">1.4.2</h2>
<p>2013-07-20</p>
<ul>
<li>if you don’t pass a strict maths option, font size/line height options are output correctly again</li>
<li>如果没传递严格数学选项,字体 尺寸/行高 可以正确输出了。</li>
<li>npmignore now include .gitattributes</li>
<li>将.gitattributres加入npmignore</li>
<li>property names may include capital letters</li>
<li>属性名可能包括大写字母</li>
<li>various windows path fixes (capital letters, multiple // in a path)</li>
<li>修复各种windows路径(路径中包含大写字母,多个//)</li>
</ul>
<h2 id="141">1.4.1</h2>
<p>2013-07-05</p>
<ul>
<li>fix syncImports and yui-compress option, as they were being ignored</li>
<li>修复 syncImports 和 yui-compress 选项,由于他们被忽略。</li>
<li>fixed several global variable leaks</li>
<li>修复几个全局变量泄漏问题</li>
<li>handle getting null or undefined passed as the options object</li>
<li>处理获取传递参数对象为null或undefined的情况</li>
</ul>
<h2 id="140">1.4.0</h2>
<p>2013-06-05</p>
<ul>
<li>fix passing of strict maths option</li>
<li>修复传递严格数学选项</li>
</ul>
<h2 id="140-beta-4">1.4.0 Beta 4</h2>
<p>2013-05-04</p>
<ul>
<li>change strictMaths to strictMath. Enable this with –strict-math=on in lessc and strictMath:true in JavaScript.</li>
<li>更改 strictMaths 为 strictMath。启用这个在lessc中使用 –strict-math=on,在javascript中使用 strictMath:true</li>
<li>change lessc option for strict units to –strict-units=off</li>
<li>改变lessc严格单位选项为 –strict-units=off</li>
</ul>
<h2 id="140-beta-3">1.4.0 Beta 3</h2>
<p>2013-04-30</p>
<ul>
<li>strictUnits now defaults to false and the true case now gives more useful but less correct results, e.g. 2px/1px = 2px</li>
<li>严格单位现在默认是false,并且true的情况下,会给出更有用但不正确的结果,例如: 2px/1px = 2px</li>
<li>Process ./ when having relative paths</li>
<li>当有相对路径时处理./</li>
<li>add isunit function for mixin guards and non basic units</li>
<li>添加 isunit 函数——用在mixin guard中和非基本单位中</li>
<li>extends recognise attributes</li>
<li>扩展识别属性</li>
<li>exception errors extend the JavaScript Error</li>
<li>remove es-5-shim as standard from the browser</li>
<li>从浏览器中,移除 es-5-shim。</li>
<li>Fix path issues with windows/linux local paths</li>
<li>修复 windows/linux 的本地路径问题</li>
</ul>
<h2 id="140-beta-1--2">1.4.0 Beta 1 & 2</h2>
<p>2013-03-07</p>
<ul>
<li>support for <code class="language-plaintext highlighter-rouge">:extend()</code> in selectors (e.g. <code class="language-plaintext highlighter-rouge">input:extend(.button) {}</code>) and <code class="language-plaintext highlighter-rouge">&:extend();</code> in ruleset (e.g. <code class="language-plaintext highlighter-rouge">input { &:extend(.button all); }</code>)</li>
<li>支持在选择符中使用<code class="language-plaintext highlighter-rouge">:extend()</code>(例如: <code class="language-plaintext highlighter-rouge">input:extend(.button) {}</code>)和在规则集中使用 <code class="language-plaintext highlighter-rouge">&:extend()</code> (例如: <code class="language-plaintext highlighter-rouge">input {&:extend(.button all);}</code>)</li>
<li>maths is now only done inside brackets. This means font: statements, media queries and the calc function can use a simpler format without being escaped. Disable this with –strict-maths-off in lessc and strictMaths:false in JavaScript.</li>
<li>数学运算现在仅发生在括号内。这意味着字体属性,媒体查询和calc函数可以使用简单格式,而不用转义。关闭这个功能——lessc中使用 –stract-math-off,在javascript中使用 strictMaths: false</li>
<li>units are calculated, e.g. 200cm+1m = 3m, 3px/1px = 3. If you use units inconsistently you will get an error. Suppress this error with –strict-units-off in lessc or strictUnits:false in JavaScript</li>
<li>单位参加计算,例如:200cm+1m = 3m,3px/1px = 3。如果你使用的单位不一致将会出错。关闭这个错误可以在less中使用 –strict-units-off或在js中使用 strictUnits:false</li>
<li><code class="language-plaintext highlighter-rouge">(~"@var")</code> selector interpolation is removed. Use @{var} in selectors to have variable selectors</li>
<li><code class="language-plaintext highlighter-rouge">(~"@var")</code> 选择器插值被移除。请使用 <code class="language-plaintext highlighter-rouge">@{var}</code>。</li>
<li>default behaviour of import is to import each file once. <code class="language-plaintext highlighter-rouge">@import-once</code> has been removed.</li>
<li>默认导入行为是每个文件指导入一次。<code class="language-plaintext highlighter-rouge">@import-once</code> 已经被移除</li>
<li>You can specify options on imports to force it to behave as css or less <code class="language-plaintext highlighter-rouge">@import (less) "file.css"</code> will process the file as less</li>
<li>你可以指定import时的参数,强制作为css或less来处理。<code class="language-plaintext highlighter-rouge">@import (less) "file.css"</code> 将被作为less文件</li>
<li>variables in mixins no longer ‘leak’ into their calling scope</li>
<li>mixin中的变量不再泄漏到调用他们的作用域</li>
<li>added data-uri function which will inline an image into the output css. If ieCompat option is true and file is too large, it will fallback to a url()</li>
<li>添加 data-uri 函数,可以在输出的css中嵌入图片。</li>
<li>significant bug fixes to our debug options</li>
<li>修复调试参数的重大问题</li>
<li>other parameters can be used as defaults in mixins e.g. .a(@a, @b:@a)</li>
<li>mixin中支持使用其他参数作为默认参数。例如:<code class="language-plaintext highlighter-rouge">.a(@a, @b:@a)</code></li>
<li>an error is shown if properties are used outside of a ruleset</li>
<li>如果使用规则其意外的属性,将会报错</li>
<li>added extract function which picks a value out of a list, e.g. extract(12 13 14, 3) => 14</li>
<li>添加 extract 函数——从列表中选出一个值,例如:<code class="language-plaintext highlighter-rouge">extract(12 12 14, 3)</code> => 14</li>
<li>added luma, hsvhue, hsvsaturation, hsvvalue functions</li>
<li>添加luma,hsvhue,hsvsaturation,hsvvalue函数</li>
<li>added pow, pi, mod, tan, sin, cos, atan, asin, acos and sqrt math functions</li>
<li>添加如下数学函数:pow,pi,mod,tan,sin,cons,atan,asin,acos和sqrt。</li>
<li>added convert function, e.g. convert(1rad, deg) => value in degrees</li>
<li>添加convert函数,例如:<code class="language-plaintext highlighter-rouge">convert(1rad, deg)</code> => 转换为角度</li>
<li>lessc makes output directories if they don’t exist</li>
<li>如果不存在,lessc输出文件夹</li>
<li>lessc <code class="language-plaintext highlighter-rouge">@import</code> supports https and 301’s</li>
<li>lessc <code class="language-plaintext highlighter-rouge">@import</code> 支持https和301(http状态吗)。</li>
<li>lessc “-depends” option for lessc writes out the list of import files used in makefile format</li>
<li>lessc “-depends”选项让lessc输出makefile格式的导入文件列表</li>
<li>lessc “-lint” option just reports errors</li>
<li>lessc “-lint”参数仅报告错误</li>
<li>support for namespaces in attributes and selector interpolation in attributes</li>
<li>支持属性中的命名空间和选择器插值</li>
<li>other bug fixes</li>
<li>其他问题修复</li>
</ul>
<h2 id="133">1.3.3</h2>
<p>2012-12-30</p>
<ul>
<li>Fix critical bug with mixin call if using multiple brackets</li>
<li>修复调用mixin时,存在多个括号时的循环问题</li>
<li>when using the filter contrast function, the function is passed through if the first argument is not a color 使用filter contrast</li>
<li>函数时,如果第一个参数不是颜色值,将被忽略</li>
</ul>
<h2 id="132">1.3.2</h2>
<p>2012-12-28</p>
<ul>
<li>browser and server url re-writing is now aligned to not re-write (previous lessc behaviour)</li>
<li>浏览器和服务器现的url重写机制更改为不重写(上一版本lessc的行为)</li>
<li>url-rewriting can be made to re-write to be relative to the entry file using the relative-urls option (less.relativeUrls option)</li>
<li>使用relative-urls参数,url重写将可以对相对文件开启重写</li>
<li>rootpath option can be used to add a base path to every url</li>
<li>rootpath参数将会应用到每一个url</li>
<li>Support mixin argument separator of ‘;’ so you can pass comma separated values. e.g. <code class="language-plaintext highlighter-rouge">.mixin(23px, 12px;);</code></li>
<li>支持参数分隔使用’;’,现在可以像下面这样传递参数,例如:<code class="language-plaintext highlighter-rouge">.mixin(32px, 12px);</code></li>
<li>Fix lots of problems with named arguments in corner cases, not behaving as expected</li>
<li>修复大量命名参数边界情况——不符合直觉</li>
<li>hsv, hsva, unit functions</li>
<li>hsv,hsva,unit 函数</li>
<li>fixed lots more bad error messages</li>
<li>修复大量糟糕的错误消息</li>
<li>fix <code class="language-plaintext highlighter-rouge">@import-once</code> to use the full path, not the relative one for determining if an import has been imported already</li>
<li>修复在<code class="language-plaintext highlighter-rouge">@import-once</code>中使用绝对路径</li>
<li>support <code class="language-plaintext highlighter-rouge">:not(:nth-child(3))</code></li>
<li>支持<code class="language-plaintext highlighter-rouge">:not(:nth-child(3))</code></li>
<li>mixin guards take units into account</li>
<li>mixin guards引入对单位的支持</li>
<li>support unicode descriptors (<code class="language-plaintext highlighter-rouge">U+00A1-00A9</code>)</li>
<li>支持unicode描述符(<code class="language-plaintext highlighter-rouge">U+00A1-00A9</code>)</li>
<li>support calling mixins with a stack when using <code class="language-plaintext highlighter-rouge">&</code> (broken in 1.3.1)</li>
<li>当使用<code class="language-plaintext highlighter-rouge">&</code>时,支持mixin调用堆栈</li>
<li>support <code class="language-plaintext highlighter-rouge">@namespace</code> and namespace combinators</li>
<li>支持 <code class="language-plaintext highlighter-rouge">@namespace</code> 和命名空间组合</li>
<li>when using % with colour functions, take into account a colour is out of 256</li>
<li>当在颜色函数中使用%时,颜色值参考为256</li>
<li>when doing maths with a % do not divide by 100 and keep the unit</li>
<li>数学运算时 a % 不再除以100,而是保留单位</li>
<li>allow url to contain % (e.g. %20 for a space)</li>
<li>允许url中包含%(例如,%20)</li>
<li>if a mixin guard stops execution a default mixin is not required</li>
<li>如果mixin guard 通知执行时不再需要默认mixin</li>
<li>units are output in strings (use the unit function if you need to get the value without unit)</li>
<li>在字符串中输出单位(如果你需要或得没有单位的值请使用unit函数)</li>
<li>do not infinite recurse when mixins call mixins of the same name</li>
<li>当mixin调用自身时,不再无限递归</li>
<li>fix issue on important on mixin calls</li>
<li>修复mixin调用时important的问题</li>
<li>fix issue with multiple comments being confused</li>
<li>修复多个评论冲突的问题</li>
<li>tolerate multiple semi-colons on rules</li>
<li>容忍规则后添加多个分号</li>
<li>ignore subsequant <code class="language-plaintext highlighter-rouge">@charset</code></li>
<li>忽略后面的 <code class="language-plaintext highlighter-rouge">@charset</code></li>
<li>syncImport option for node.js to read files syncronously</li>
<li>suncImport参数可以使node.js同步读取文件</li>
<li>write the output directory if it is missing</li>
<li>文件丢失输出目录</li>
<li>change dependency on cssmin to ycssmin</li>
<li>将cssmin更改为ycssmin</li>
<li>lessc can load files over http</li>
<li>lessc 支持通过http加载文件</li>
<li>allow calling less.watch() in non dev mode</li>
<li>允许在非开发者模式中调用 less.watch()</li>
<li>don’t cache in dev mode</li>
<li>开发模式中禁用缓存</li>
<li>less files cope with query parameters better</li>
<li>更好的文件处理参数</li>
<li>sass debug statements are now chrome compatible</li>
<li>在chrome中兼容sass调试语句</li>
<li>modifyVars function added to re-render with different root variables</li>
<li></li>
</ul>
<h2 id="131">1.3.1</h2>
<p>2012-10-18</p>
<ul>
<li>Support for comment and @media debugging statements</li>
<li>支持注释和@media调试语句</li>
<li>bug fix for async access in chrome extensions</li>
<li>修复chrome扩展中的异步访问</li>
<li>new functions tint, shade, multiply, screen, overlay, hardlight, difference, exclusion, average, negation, softlight, red, green, blue, contrast</li>
<li>添加新函数tint, shade, multiply, screen, overlay, hardlight, difference, exclusion, average, negation, softlight, red, green, blue, contrast</li>
<li>allow escaped characters in attributes</li>
<li>允许在属性中使用转义字符</li>
<li>in selectors support @{a} directly, e.g. .a.@{a} { color: black; }</li>
<li>支持在选择符中直接使用 @{a},例如,.a.@{a} {color: black}</li>
<li>add fraction parameter to round function</li>
<li>round函数添加部分参数</li>
<li>much better support for & selector</li>
<li>支持更好的 & 选择符</li>
<li>preserve order of link statements client side</li>
<li>维护客户端连接语句的顺序</li>
<li>lessc has better help</li>
<li>lessc引入更好的帮助信息</li>
<li>rhino version fixed</li>
<li>修复rhino版本</li>
<li>fix bugs in clientside error handling</li>
<li>修复clientside中的错误处理问题</li>
<li>support dpi, vmin, vm, dppx, dpcm units</li>
<li>支持 dpi,vmin,vm,dppx,dpcm 等单位</li>
<li>Fix ratios in media statements</li>
<li>修复媒体查询条件中的百分比</li>
<li>in mixin guards allow comparing colors and strings</li>
<li>在mixin guards 中支持颜色和字符串的比较</li>
<li>support for -*-keyframes (for -khtml but now supports any)</li>
<li>支持 _*_keyframes</li>
<li>原来仅支持-khtml,现在支持全部前缀</li>
<li>in mix function, default weight to 50%</li>
<li>在混入函数中,默认宽度改为50%</li>
<li>support @import-once</li>
<li>支持 @import-once</li>
<li>remove duplicate rules in output</li>
<li>删除输出中的重复规则</li>
<li>implement named parameters when calling mixins</li>
<li>实现调用 mixins 时的命名参数</li>
<li>many numerous bug fixes</li>
<li>修复大量bug</li>
</ul>
<h2 id="130">1.3.0</h2>
<p>2012-03-10</p>
<ul>
<li>@media bubbling</li>
<li>媒体查询嵌套</li>
<li>Support arbitrary entities as selectors</li>
<li>支持任意实体作为选择器</li>
<li><a href="https://gist.github.com/1933613">Variadic argument support</a></li>
<li>支持可变参数</li>
<li>Behaviour of zero-arity mixins has <a href="https://gist.github.com/1933613">changed</a></li>
<li>Allow <code class="language-plaintext highlighter-rouge">@import</code> directives in any selector</li>
<li>允许在任何选择符中使用<code class="language-plaintext highlighter-rouge">@import</code>指令</li>
<li>Media-query features can now be a variable</li>
<li>现在媒体查询功能支持使用变量</li>
<li>Automatic merging of media-query conditions</li>
<li>自动合并媒体查询条件</li>
<li>Fix global variable leaks</li>
<li>修复全局变量泄漏</li>
<li>Fix error message on wrong-arity call</li>
<li>修复调用时参数数量错误的错误消息</li>
<li>Fix an <code class="language-plaintext highlighter-rouge">@arguments</code> behaviour bug</li>
<li>修复<code class="language-plaintext highlighter-rouge">@arguments</code>的一个行为错误</li>
<li>Fix <code class="language-plaintext highlighter-rouge">::</code> selector output</li>
<li>修复<code class="language-plaintext highlighter-rouge">::</code>选择符输出</li>
<li>Fix a bug when using @media with mixins</li>
<li>修复在混入中使用@media时的一个问题</li>
</ul>
<h2 id="121">1.2.1</h2>
<p>2012-01-15</p>
<ul>
<li>Fix imports in browser</li>
<li>修复浏览器中的导入</li>
<li>Improve error reporting in browser</li>
<li>改善浏览器中的错误报告</li>
<li>Fix Runtime error reports from imported files</li>
<li>修复来自导入文件的运行时错误报告</li>
<li>Fix <code class="language-plaintext highlighter-rouge">File not found</code> import error reporting</li>
<li>修复导入时<code class="language-plaintext highlighter-rouge">文件丢失</code>错误报告</li>
</ul>
<h2 id="120">1.2.0</h2>
<p>2012-01-07</p>
<ul>
<li>Mixin guards</li>
<li>引入混入保护</li>
<li>New function <code class="language-plaintext highlighter-rouge">percentage</code></li>
<li>全新的<code class="language-plaintext highlighter-rouge">percentage</code>函数</li>
<li>New <code class="language-plaintext highlighter-rouge">color</code> function to parse hex color strings</li>
<li>全新的<code class="language-plaintext highlighter-rouge">color</code>函数,转换十六进制颜色字符串</li>
<li>New type-checking stylesheet functions</li>
<li>全新的类型检查样式函数</li>
<li>Fix Rhino support</li>
<li>修复对 Rhino 的支持</li>
<li>Fix bug in string arguments to mixin call</li>
<li>修复mixin调用时,字符串参数的问题</li>
<li>Fix error reporting when index is 0</li>
<li>修复当index为零时的错误报告</li>
<li>Fix browser support in WebKit and IE</li>
<li>修复对Webkit和IE浏览器的支持应该指在浏览器中引入less.js的情况</li>
<li>Fix string interpolation bug when var is empty</li>
<li>修复当变量为空时,字符串差值错误</li>
<li>Support <code class="language-plaintext highlighter-rouge">!important</code> after mixin calls</li>
<li>支持 mixni调用后面的 <code class="language-plaintext highlighter-rouge">!important</code></li>
<li>Support vanilla @keyframes directive</li>
<li>支持@keyframes 指定</li>
<li>Support variables in certain css selectors, like <code class="language-plaintext highlighter-rouge">nth-child</code></li>
<li>支持在特定的css选择器中使用变量,比如<code class="language-plaintext highlighter-rouge">nth-child</code></li>
<li>Support @media and @import features properly</li>
<li>完整支持 @meida 和 @import 特性</li>
<li>Improve @import support with media features</li>
<li>改进@import,支持媒体查询特性</li>
<li>Improve error reports from imported files</li>
<li>改善导入文件错误报告</li>
<li>Improve function call error reporting</li>
<li>改善函数调用错误报告</li>
<li>Improve error-reporting</li>
<li>改善错误报告</li>
</ul>
工作的三种状态
2015-06-05T00:00:00+00:00
http://yanhaijing.com/work/2015/06/05/status-of-work
<p>今天突然想到这个问题(PS:可能是前几天老大找谈话的结果),我理解中工作分为三种状态,分别如下:</p>
<ul>
<li>不能胜任</li>
<li>可以胜任</li>
<li>不局限</li>
</ul>
<p>本文是自己的一点随笔,不关心的同学可以略过。</p>
<h2 id="不能胜任">不能胜任</h2>
<p>其实不能胜任工作有很多种,譬如刚刚入职,能力不够……,总结起来都可以归为不能够胜任工作。</p>
<p>在这个状态的人要有自知之明,认清原因,努力一下,或者换个工作,一直这样下去是很糟糕的。</p>
<p>大家千万不要处在这个层面哈。</p>
<p>譬如给我一个国家主席当当,我想我会拒绝的((/ □ ))。</p>
<h2 id="可以胜任">可以胜任</h2>
<p>我想大部分人都处于这个阶段,自己的工作都能按时按量做好,我也是这样,这其实也没什么不好,公司需要这样的员工,但长久下去,会慢慢平庸下去,沦为常人,放眼望去,如芸芸众生。</p>
<h2 id="不局限">不局限</h2>
<p>其实我们应该往前一步,不仅视为工作,而是做好一件事,而且要做的更好,不要把工作当成工作,我很希望自己能到这个阶段,并且一直在努力,如果你有什么好办法,快快告诉我吧。</p>
<h2 id="总结">总结</h2>
<p>随笔,不喜勿碰。</p>
碉堡了!ECMAScript 5的Object.create方法
2015-05-12T00:00:00+00:00
http://yanhaijing.com/javascript/2015/05/12/extend-of-object-create
<p>一直对<a href="http://yanhaijing.com/es5/" title="es5">ES5</a>处于了解的阶段,没有深入实践过,最近研究发现<code class="language-plaintext highlighter-rouge">Object.create</code>方法有些缺陷,呵呵。</p>
<p>什么!!!你还不知道这个方法,难道你还停留在上个世纪,骚年是时候学习<a href="http://yanhaijing.com/es5/" title="es5">ES5</a>了,因为<a href="http://yanhaijing.com/es5/" title="es5">ES6</a>马上出来的。如果你记不清楚这个方法的语法了,那么我建议你阅读一下<a href="http://yanhaijing.com/javascript/2015/05/08/member-of-object/">这篇文章</a>。</p>
<h2 id="前言">前言</h2>
<p>好了言归正传,先来看看Object.create的语法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.create(proto [, propertiesObject ])
</code></pre></div></div>
<p>期第二个参数的涉及我很不满意,其必须为属性描述符,众所周知属性描述符这东西,写起来有些蹩脚,而且,咱又不写公共库,有些特性也用不到,我希望其能接受普通对象,并且接受多个对象,有点类似mixin的意思,所以我扩展了一下create,姑且就放到Object.creates上吧,如果你不赞成这种方法,可以随意放置。</p>
<h2 id="语法">语法</h2>
<p>Object.creates的语法如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.creates(proto, [object...])
</code></pre></div></div>
<p>第一个参数和Object.create的第一个参数一样,后面有一个或多个,对象,其可以将后面对象的属性extend到前面去,是不是有点jq的extend的意思,差不多就是这个意思。</p>
<h2 id="实现">实现</h2>
<p>借助Object.getOwnPropertyDescriptor和Object.defineProperty我们可以获取对象属性的属性描述符,然后赋给目标对象,从而实现只传入对象的目的,具体代码如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.creates = function (proto) {
var obj = Object.create(proto);
//如果只有一个参数
if (arguments.length < 2) return obj;
[].slice.call(arguments, 1).forEach(function (copy) {
Object.getOwnPropertyNames(copy).forEach(function (prop) {
Object.defineProperty(obj, prop, Object.getOwnPropertyDescriptor(copy, prop));
});
});
return obj;
}
</code></pre></div></div>
<p>代码使用了<a href="http://yanhaijing.com/es5/" title="es5">es5</a>中很多数组相关的方法,如果你还不了解,可以看下<a href="http://yanhaijing.com/javascript/2014/01/17/fun-with-javascript-native-array-functions/">这篇文章</a>。</p>
<h2 id="实例">实例</h2>
<p>下面是一个例子,可以看出比Object.create简单多了。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.creates(Object.prototype, {a: 1, b: 2}, {c: 3}, {a: 4});
// > Object {a: 4, b: 2, c: 3}
</code></pre></div></div>
<p>从例子中可以看到,后面的对象的属性,覆盖了前面对象的属性,这符合第一直觉。</p>
<h2 id="总结">总结</h2>
<p>其实我开始比较抵制es5的create方法,因为我觉的它很繁琐,要定义那么多其实不那么常用的东西,所以我写了这个方法。我觉得它很好用,如果你有什么想法,那在评论里和我一起讨论吧。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://yanhaijing.com/javascript/2015/05/08/member-of-object/">细说JavaScript中对象的属性和方法</a></li>
<li><a href="http://yanhaijing.com/javascript/2014/11/09/object-inherit-of-js">JavaScript对象继承一瞥</a></li>
<li><a href="http://yanhaijing.com/javascript/2014/07/18/javascript-prototype">JavaScript原型之路</a></li>
</ul>
详解forin,Object.keys和Object.getOwnPropertyNames的区别
2015-05-09T00:00:00+00:00
http://yanhaijing.com/javascript/2015/05/09/diff-between-keys-getOwnPropertyNames-forin
<p>以前笔者一直搞不太清楚三者之间的区别,最近再看这块的内容,顺便理清一下思路。</p>
<p>本文将用一个例子说明三者之间的区别,如果你还不知道这些方法,那么不放看一下<a href="http://yanhaijing.com/javascript/2015/05/08/member-of-object/" title="细说JavaScript中对象的属性和方法">这篇文章</a>。</p>
<h2 id="前言">前言</h2>
<p>这三个方法,都可以用来遍历对象,这非常有用,其中后两个都是<a href="http://yanhaijing.com/es5/" title="es5">es5</a>中新增的方法。</p>
<p>本文会用到一些es5的对象知识,如果你不了解,可以看一下开头提到的文章,首先我们需要一个父对象。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var parent = Object.create(Object.prototype, {
a: {
value: 1,
writable: true,
enumerable: true,
configurable: true
}
});
</code></pre></div></div>
<p>parent继承自Object.prototype,有一个可枚举的属性a。下面我们在创建一个继承自parent的对象child。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var child = Object.create(parent, {
b: {
value: 2,
writable: true,
enumerable: true,
configurable: true
},
c: {
value: 3,
writable: true,
enumerable: false,
configurable: true
}
});
</code></pre></div></div>
<p>child有两个属性b和c,其中b为可枚举属性,c为不可枚举属性。</p>
<p>下面我们将用四种方法遍历child对象,来比较四种方法的不同。如下的代码代表程序的输出。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>console.log('yanhaijing is God');
// > yanhaijing is God
</code></pre></div></div>
<p><strong>注</strong>:⑤代表<a href="http://yanhaijing.com/es5/" title="es5">es5</a>中新增的方法,你可能需要一款现代浏览器来访问。</p>
<h2 id="for-in">for in</h2>
<p>for in是<a href="http://yanhaijing.com/es5/" title="es5">es3</a>中就存在,最早用来遍历对象(集合)的方法。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for (var key in child) {
console.log(key);
}
// > b
// > a
</code></pre></div></div>
<p>从输出可以看出,for in会输出自身以及原型链上可枚举的属性。</p>
<p><strong>注意</strong>:不同的浏览器对for in属性输出的顺序可能不同。</p>
<p>如果仅想输出自身的属性可以借助 <a href="http://yanhaijing.com/javascript/2015/05/08/member-of-object/" title="细说JavaScript中对象的属性和方法">hasOwnProperty</a>。可以过滤掉原型链上的属性。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for (var key in child) {
if (child.hasOwnProperty(key)) {
console.log(key);
}
}
// > b
</code></pre></div></div>
<p>上面的代码,仅输出了child自己的可枚举属性b,而没有输出原型parent中的属性。</p>
<h2 id="objectkeys">Object.keys⑤</h2>
<p><a href="http://yanhaijing.com/javascript/2015/05/08/member-of-object/" title="细说JavaScript中对象的属性和方法">Object.keys</a>是<a href="http://yanhaijing.com/es5/" title="es5">es5</a>中新增的方法,用来获取对象自身可枚举的属性键。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>console.log(Object.keys(child));
// > ["b"]
</code></pre></div></div>
<p>可以看出Object.keys的效果和for in+hasOwnProperty的效果是一样的。</p>
<h2 id="objectgetownpropertynames">Object.getOwnPropertyNames⑤</h2>
<p><a href="http://yanhaijing.com/javascript/2015/05/08/member-of-object/" title="细说JavaScript中对象的属性和方法">Object.getOwnPropertyNames</a>也是<a href="http://yanhaijing.com/es5/" title="es5">es5</a>中新增的方法,用来获取对象自身的全部属性名。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>console.log(Object.getOwnPropertyNames(child));
// > ["b", "c"]
</code></pre></div></div>
<p>从输出可以看出其和Object.keys的区别。</p>
<h2 id="总结">总结</h2>
<p>相信看完后,搞不清楚的同学应该明白了,在es3中,我们不能定义属性的枚举性,所以也不需要那么多方法,有了keys和getOwnPropertyNames后基本就用不到for in了。</p>
<p>如果你想在老旧浏览器中也是用这些方法,那试试<a href="https://github.com/es-shims/es5-shim">es5shim</a>吧。</p>
<h2 id="相关资料">相关资料</h2>
<ul>
<li><a href="http://yanhaijing.com/javascript/2015/05/08/member-of-object/" title="细说JavaScript中对象的属性和方法">细说JavaScript中对象的属性和方法</a></li>
</ul>
细说JavaScript中对象的属性和方法
2015-05-08T00:00:00+00:00
http://yanhaijing.com/javascript/2015/05/08/member-of-object
<p>最近在回家的路上读了尼古拉斯的新书《<a href="http://www.amazon.cn/gp/product/B00VDSW6X2/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B00VDSW6X2&linkCode=as2&tag=yanhaijing-23">JavaScript面向对象精要</a>》,发现自己对对象的属性和方法不是很熟悉,特别是es5新增的部分,特写此文总结一下,同时也与大家共勉。</p>
<p>本文分为两部分,分别介绍Object和Object.prototype上的一些常用方法。主要参考了<a href="https://developer.mozilla.org" title="MDN">MDN</a>,每个方法都给出了<a href="https://developer.mozilla.org" title="MDN">MDN</a>的链接。</p>
<h2 id="前言">前言</h2>
<p>查看一个对象属性的最好方法,不是去百度,也不是去google,而是用下面的方法(来自《<a href="http://www.amazon.cn/gp/product/B00JWXDB52/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B00JWXDB52&linkCode=as2&tag=yanhaijing-23">DOM启蒙</a>》):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.getOwnPropertyNames(Object).sort().forEach(function (val) {console.log(val, '\n')});
</code></pre></div></div>
<p>上面的代码会有如下的输出,我的环境是Chrome(52.0.2743.82 m),如果你的环境不同输出可能不一样:</p>
<p><img src="/blog/169.png" alt="" /></p>
<p>如果不支持<code class="language-plaintext highlighter-rouge">getOwnPropertyNames</code>的浏览器就用<code class="language-plaintext highlighter-rouge">for in</code>吧,请自行解决。</p>
<h2 id="object">Object</h2>
<p>从上面的输出中挑选出一些常用方法和属性,会得到下面的列表:</p>
<ul>
<li>create⑤</li>
<li>defineProperty⑤</li>
<li>defineProperties⑤</li>
<li>getPrototypeOf⑤</li>
<li>getOwnPropertyDescriptor⑤</li>
<li>keys⑤</li>
<li>getOwnPropertyNames⑤</li>
<li>preventExtensions⑤</li>
<li>isExtensible⑤</li>
<li>seal⑤</li>
<li>isSealed⑤</li>
<li>freeze⑤</li>
<li>isFrozen⑤</li>
<li>assign⑥</li>
<li>getOwnPropertySymbols⑥</li>
<li>is⑥</li>
<li>setPrototypeOf⑥</li>
<li>values⑧</li>
<li>entries⑧</li>
<li>getOwnPropertyDescriptors⑧</li>
</ul>
<p><strong>注解:⑤为ES5新增的方法;⑥为ES6(ES2015)新增的方法;⑧为ES8(ES2017)新增的方法</strong></p>
<p>下面将会一一介绍上面的方法。</p>
<h3 id="objectcreate">Object.create</h3>
<blockquote>
<p>Object.create() 方法创建一个拥有指定原型和若干个指定属性的对象。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.create(proto [, propertiesObject ])
</code></pre></div></div>
<ul>
<li>proto 为新创建对象的原型对象,设置为null可创建没有原型的空对象。</li>
<li>propertiesObject 包涵若干个属性的描述符和defineProperties的第二个参数一样。</li>
</ul>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.create(Object.prototype, {
a: {
value: 1,
writable: true,
configurable: true
}
});
</code></pre></div></div>
<p>创建一个继承自Object.prototype的对象,有一个属性a,其可写,可配置,不可枚举,值为1。</p>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create">更多详情</a>。</p>
<h3 id="objectdefineproperty">Object.defineProperty</h3>
<blockquote>
<p>Object.defineProperty() 方法直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.defineProperty(obj, prop, descriptor)
</code></pre></div></div>
<p>descriptor 可包含4个属性,如下:</p>
<ul>
<li>configurable 当且仅当这个属性描述符值为 true 时,该属性可能会改变,也可能会被从相应的对象删除。默认为 false。</li>
<li>enumerable true 当且仅当该属性出现在相应的对象枚举属性中。默认为 false。</li>
<li>value 属性的值</li>
<li>writable 定义属性值是否可写。</li>
<li>get 一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。方法将返回用作属性的值。默认为 undefined。</li>
<li>set 同get一起使用,功能互补。</li>
</ul>
<p>其中value和writable一组,get和set一组,不可同时出现。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 显式
Object.defineProperty(obj, "key", {
enumerable: false,
configurable: false,
writable: false,
value: "static"
});
</code></pre></div></div>
<p>上面给obj对象定义了一个属性key,其不可枚举,不可配置,只读,值为static。</p>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty">更多详情</a>。</p>
<h3 id="objectdefineproperties">Object.defineProperties</h3>
<blockquote>
<p>Object.defineProperties() 方法在一个对象上添加或修改一个或者多个自有属性,并返回该对象。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.defineProperties(obj, props)
</code></pre></div></div>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties">更多详情</a>。</p>
<h3 id="objectgetprototypeof">Object.getPrototypeOf</h3>
<blockquote>
<p>Object.getPrototypeOf() 方法返回指定对象的原型(也就是该对象内部属性[[Prototype]]的值)。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.getPrototypeOf(object)
</code></pre></div></div>
<p>可以用来获取对象的原型。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.getPrototypeOf({}) === Object.prototype
// > true
</code></pre></div></div>
<p>在es5之前,要达到上面同样的方法,只能使用 constructor。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>({}).constructor.prototype === Object.prototype
// > true
</code></pre></div></div>
<p>但对于自定义的构造函数,如果复写了prototype,可能导致获取的constructor不正确,如何解决这个问题,可以看<a href="http://yanhaijing.com/js/2014/11/09/object-inherit-of-js/">这篇文章</a>。</p>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/GetPrototypeOf">更多详情</a>。</p>
<h3 id="objectgetownpropertydescriptor">Object.getOwnPropertyDescriptor</h3>
<blockquote>
<p>Object.getOwnPropertyDescriptor() 返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.getOwnPropertyDescriptor(obj, prop)
</code></pre></div></div>
<p>可用来获取或查看对象属性的特性。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var obj = {a: 1};
Object.getOwnPropertyDescriptor(obj, 'a');
// > Object {value: 1, writable: true, enumerable: true, configurable: true}
</code></pre></div></div>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor">更多详情</a>。</p>
<h3 id="objectkeys">Object.keys</h3>
<blockquote>
<p>Object.keys() 方法会返回一个由给定对象的所有可枚举自身属性的属性名组成的数组,数组中属性名的排列顺序和使用for-in循环遍历该对象时返回的顺序一致(两者的主要区别是 for-in 还会遍历出一个对象从其原型链上继承到的可枚举属性)。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.keys(obj)
</code></pre></div></div>
<p>典型的用法如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var obj = {a: 1, b: 2};
console.log(Object.keys(obj));
// > ["a", "b"]
</code></pre></div></div>
<p>keys可以用来代替原来的for in循环,借助<a href="http://yanhaijing.com/javascript/2014/01/17/fun-with-javascript-native-array-functions/">es5新增的数组方法</a>,可提升代码可读性。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.keys(obj).forEach(function (val) {console.log(val)});
</code></pre></div></div>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/keys">更多详情</a>。</p>
<h3 id="objectgetownpropertynames">Object.getOwnPropertyNames</h3>
<blockquote>
<p>Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性)组成的数组。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.getOwnPropertyNames(obj)
</code></pre></div></div>
<p>其和Object.keys的区别就是能够获取自身的全部属性,包括不可枚举属性。</p>
<h3 id="objectpreventextensions">Object.preventExtensions</h3>
<blockquote>
<p>Object.preventExtensions() 方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.preventExtensions(obj)
</code></pre></div></div>
<p>需要注意的是不可扩展的对象的属性通常仍然可以被删除。</p>
<p>尝试给一个不可扩展对象添加新属性的操作将会失败,在非严格模式下是静默的,在<a href="https://developer.mozilla.org/zh-CN/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode" title="严格模式">严格模式</a>下会抛出<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypeError" title="TypeError">TypeError</a>异常。</p>
<p>Object.preventExtensions 只能阻止一个对象不能再添加新的自身属性,仍然可以为该对象的原型添加属性。</p>
<p>在 ECMAScript 5 中可扩展的对象可以变得不可扩展,但反过来不行。</p>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions">更多详情</a>。</p>
<h3 id="objectisextensible">Object.isExtensible</h3>
<blockquote>
<p>Object.isExtensible() 方法判断一个对象是否是可扩展的(是否可以在它上面添加新的属性)。</p>
</blockquote>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible">更多详情</a>。</p>
<h3 id="objectseal">Object.seal</h3>
<blockquote>
<p>Object.seal() 方法可以让一个对象密封,并返回被密封后的对象。密封对象是指那些不能添加新的属性,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性,但可能可以修改已有属性的值的对象。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.seal(obj)
</code></pre></div></div>
<p>密封一个对象会让这个对象变的变为不可扩展的,且所有已有属性会变的不可配置。属性不可配置的效果就是属性变的不可删除,以及一个数据属性不能被重新定义成为访问器属性,或者反之。但属性的值仍然可以修改。</p>
<p>尝试删除一个密封对象的属性或者将某个密封对象的属性从数据属性转换成访问器属性,结果会静默失败或抛出<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/TypeError" title="TypeError">TypeError</a>异常(<a href="https://developer.mozilla.org/zh-CN/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode" title="严格模式">严格模式</a>)。</p>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/seal">更多详情</a>。</p>
<h3 id="objectissealed">Object.isSealed</h3>
<blockquote>
<p>Object.isSealed() 方法判断一个对象是否是密封的(sealed)。</p>
</blockquote>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed">更多详情</a>。</p>
<h3 id="objectfreeze">Object.freeze</h3>
<blockquote>
<p>Object.freeze() 方法可以冻结一个对象。冻结对象是指那些不能添加新的属性,不能修改已有属性的值,不能删除已有属性,以及不能修改已有属性的可枚举性、可配置性、可写性的对象。也就是说,这个对象永远是不可变的。该方法返回被冻结的对象。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.freeze(obj)
</code></pre></div></div>
<p>冻结对象是不可扩展的,密封的,同时期值属性的writable会被设置为false,set也将失效,总之会变为不可更改。任何尝试修改该对象的操作都会失败,可能是静默失败,也可能会抛出异常(<a href="https://developer.mozilla.org/zh-CN/docs/JavaScript/Reference/Functions_and_function_scope/Strict_mode" title="严格模式">严格模式</a>)。</p>
<h3 id="objectisfrozen">Object.isFrozen</h3>
<blockquote>
<p>Object.isFrozen() 方法判断一个对象是否被冻结(frozen)。</p>
</blockquote>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen">更多详情</a>。</p>
<h3 id="objectassign">Object.assign</h3>
<blockquote>
<p>Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。</p>
</blockquote>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign">更多详情</a></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.assign(target, ...sources)
</code></pre></div></div>
<h3 id="objectgetownpropertysymbols">Object.getOwnPropertySymbols</h3>
<blockquote>
<p>Object.getOwnPropertySymbols() 方法会返回一个数组,该数组包含了指定对象自身的(非继承的)所有 symbol 属性键。</p>
</blockquote>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols">更多详情</a></p>
<h3 id="objectis">Object.is</h3>
<blockquote>
<p>Object.is() 方法用来判断两个值是否是同一个值。</p>
</blockquote>
<p>Object.is与严格比较运算符(===)的行为基本一致,不同之处只有两个:一是+0不等于-0,二是NaN等于自身。</p>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is">更多详情</a></p>
<h3 id="objectsetprototypeof">Object.setPrototypeOf</h3>
<blockquote>
<p>将一个指定的对象的原型设置为另一个对象或者null(既对象的[[Prototype]]内部属性)。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.setPrototypeOf(obj, prototype)
</code></pre></div></div>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf">更多详情</a></p>
<h2 id="objectprototype">Object.prototype</h2>
<p>Object.prototype上的方法,都是实例方法,必须在对象实例上调用。</p>
<ul>
<li>hasOwnProperty</li>
<li>isPrototypeOf⑤</li>
<li>propertyIsEnumerable⑤</li>
<li>__proto__ ⑥</li>
</ul>
<h3 id="objecthasownproperty">Object#hasOwnProperty</h3>
<blockquote>
<p>hasOwnProperty() 方法用来判断某个对象是否含有指定的自身属性。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>obj.hasOwnProperty(prop)
</code></pre></div></div>
<p>在没有Object.keys之前,借助hasOwnProperty,可以让for in 达到类似的效果,代码如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for(var key in obj) {
if (obj.hasOwnProperty(key)) {
//过滤掉原型上的方法
}
}
</code></pre></div></div>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty">更多详情</a>。</p>
<h3 id="objectisprototypeof">Object#isPrototypeOf</h3>
<blockquote>
<p>isPrototypeOf() 方法测试一个对象是否存在于另一个对象的原型链上。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>prototype.isPrototypeOf(object)
</code></pre></div></div>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/isPrototypeOf">更多详情</a>。</p>
<h3 id="objectpropertyisenumerable">Object#propertyIsEnumerable</h3>
<blockquote>
<p>propertyIsEnumerable() 方法返回一个布尔值,表明指定的属性名是否是当前对象可枚举的自身属性。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>obj.propertyIsEnumerable(prop)
</code></pre></div></div>
<p>从原型链上继承的属性,所以该方法会返回false。</p>
<p>如果对象没有指定的属性,该方法返回 false。</p>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/propertyIsEnumerable">更多详情</a>。</p>
<h3 id="object__proto__">Object#__proto__</h3>
<blockquote>
<p>一个对象的__proto__ 属性和自己的内部属性[[Prototype]]指向一个相同的值 (通常称这个值为原型),原型的值可以是一个对象值也可以是null(比如说Object.prototype.__proto__的值就是null).该属性可能会引发一些错误,因为用户可能会不知道该属性的特殊性,而给它赋值,从而改变了这个对象的原型。 如果需要访问一个对象的原型,应该使用方法Object.getPrototypeOf。
__proto__ 属性已经被添加在了ES6草案 §B.3.1中。</p>
</blockquote>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto">更多详情</a>。</p>
<h2 id="总结">总结</h2>
<p>除了上面介绍的方法,还有一些实验方法,和不常用的方法,可以在<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">这里</a>找到。</p>
<h2 id="相关文章">相关文章</h2>
<ul>
<li><a href="http://yanhaijing.com/javascript/2014/01/17/fun-with-javascript-native-array-functions/">有趣的JavaScript原生数组函数</a></li>
</ul>
我的Chrome插件
2015-03-30T00:00:00+00:00
http://yanhaijing.com/tool/2015/03/30/my-chrome-plugin
<p>本人是一枚Web前端工程师,下面分享一些自己平时常用的插件。主要分为两部分,生活必备和工作必备。</p>
<p>如果你还不知道怎么安装chrome插件<a href="http://jingyan.baidu.com/article/e5c39bf56286ae39d6603374.html">请看这里</a></p>
<p>其中标星的为常用。</p>
<p><img src="/blog/241.png" alt="" /></p>
<h2 id="生活">生活</h2>
<h3 id="惠惠购物助手">惠惠购物助手*</h3>
<p>如果你网购的话,你会发现这个真的太重要了,无良商家经常标出假的打折信息,使用这个插件便能避免上当了。</p>
<p>更多信息<a href="http://zhushou.huihui.cn/">请看这里</a>。</p>
<h3 id="adblock-plus">Adblock Plus*</h3>
<p>现在有些无良网站,比如*狐,广告实在太多了,这个插件可以屏蔽广告,但这样做其实会伤害Web,毕竟网站是需要生存的么,不过我还是选择使用。</p>
<h3 id="ghostery">Ghostery</h3>
<p>14年315爆出cookie跟踪的问题,一时间让大家对上网的隐私有了重视,这个插件可以保护你,可以查看谁在跟踪你。</p>
<h3 id="proxy-switchysharp">Proxy SwitchySharp*</h3>
<p>在天朝有时需要看看外面风景的话,这个插件就派上用场了,我主要用来访问一些外国网站。</p>
<h3 id="印象笔记剪藏">印象笔记·剪藏*</h3>
<p>当看到一篇好的博文,或者网页,我通常会收藏起来。</p>
<h3 id="有道词典chrome鼠标取词插件">有道词典Chrome鼠标取词插件*</h3>
<p>英文不好,所以需要借助这个工具。</p>
<h3 id="open-seo-statsformerly-pagerank-status">Open SEO Stats(Formerly: PageRank Status)</h3>
<p>这个插件也是很棒的,能看到网站的seo各种信息。</p>
<h2 id="工作">工作</h2>
<h3 id="web-developer">Web Developer*</h3>
<p>这个应该不需要介绍了,前端人员必备,内容量巨大。</p>
<h3 id="web前端助手fehelper">WEB前端助手(FeHelper)*</h3>
<p>国人开发的一款工具,很牛的,补充了web developer没有的功能,推荐使用。</p>
<h3 id="colorzilla">ColorZilla*</h3>
<p>前端开发尝尝需要获取页面上的颜色值,如果每次都选取元素,然后从css面板去查看,效率不高,这个插件可以一键从页面上取色,还有很多其他功能,快来尝试一下吧。</p>
<p>其提供的<a href="http://www.colorzilla.com/gradient-editor/">css gradient generator</a>也是我常用的功能。</p>
<h3 id="prettyprint">PrettyPrint*</h3>
<p>如果你查看别人网站的源代码的话,现在稍微专业点的网站,</p>
<h3 id="html5-outliner">HTML5 Outliner</h3>
<p>HTML5是html的语义更丰富,如果你想看一下自己制作的页面的大纲列表,这个插件就能派上用场了,如果你还不知道什么是大纲可以看看<a href="http://www.osmn00.com/rebuild/223.html">这里</a>。</p>
<h3 id="jquery-debugger">jQuery Debugger</h3>
<p>这个插件刚刚发现的,还在体验,先保留。</p>
<h3 id="jquery-injector">jquery-injector</h3>
<p>这个插件可以在任意页面注入jquery,如果在研究别人的页面,这个插件可能派上用场,但引用的是google cdn上的js,在天朝你懂得,你可能需要用到上面的代理插件。</p>
<h3 id="pagespeed-insights-by-google">PageSpeed Insights (by Google)</h3>
<p>如果你熟悉YSlow,这个插件就是和其比肩的工具,甚至某些方面都做得更好,google推出的网站性能优化工具,你值得拥有。</p>
<h3 id="xml-tree">XML Tree</h3>
<p>xml的美化工具,xml现在不是很常用,(/ □ \)。</p>
<h3 id="check-my-links">Check My Links</h3>
<p>这个是很棒的一个工具,如果你写了一个页面,页面上有很多链接,这个工具可以用来检测页面上的链接,是否有空连接,特别是对于测试人员,非常有用。</p>
<h3 id="code-cola">Code Cola</h3>
<p>所见即所得的css工具,可在页面山直观显示元素的css样式,并做更改,我之所以收藏是被其创意感动了,而不是多么实用。</p>
<h3 id="screen-capture-by-google">Screen Capture (by Google)</h3>
<p>如果你的网页超过了一屏,而你想把页面截图发给你的客户看,怎么办,试试这个吧,可以截取整个页面的图片,很赞的。</p>
<h2 id="总结">总结</h2>
<p>以上就是我收藏的一些插件,大家都知道插件装得多,会变得很卡,我平时大部分插件都处于关闭状态,用到的时候才会开启。</p>
<p>另外我使用两个帐号将工作和生活区分开来,如果你不知道多帐号,<a href="http://yanhaijing.com/tool/2015/03/29/chrome-mutil-user">请看这里</a>。</p>
Chrome多账户的妙用
2015-03-29T00:00:00+00:00
http://yanhaijing.com/tool/2015/03/29/chrome-mutil-user
<p>本文写给chrome用户的新手们,那些还不知道chrome多账户的人。</p>
<p>生活中接触的很多人都不知道这一点,而我已经深切的体验到其给我带来的好处了。本文的目的就是给这些人打开一扇窗。</p>
<p><img src="/blog/241.png" alt="" /></p>
<h2 id="如何使用">如何使用</h2>
<p>具体可参看参考资料里面的链接,这里就不说了,相信每一个会使用计算机的人,都会轻松掌握的。</p>
<h2 id="使用场景">使用场景</h2>
<ul>
<li>一人多种需求</li>
<li>多人同一电脑</li>
<li>家长监控孩子</li>
</ul>
<p>我属于前两种,我需要区分生活和工作,同时家里的电脑又是两个人公用的,借助多帐号都可以轻松解决。</p>
<h2 id="我的方案">我的方案</h2>
<p>我是一个前端工程师,平时要装很多开发的插件,同时又要装一些生活的插件,同时开发的时候还要经常清除cookie什么的,我的方案就是两个帐号,生活一个号,工作一个号,互不干扰,还有一个临时帐号,用来测试用。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://jingyan.baidu.com/article/bea41d4351169bb4c51be618.html">如何使用Chrome的多账户功能</a></li>
</ul>
JavaScript模块的前世今生
2015-03-28T00:00:00+00:00
http://yanhaijing.com/javascript/2015/03/28/js-module
<p>如今JavaScript模块化编程的概念已经普及开来,一提起模块化,大家想到的可能是AMD,CMD,requirejs或seajs。其实还有很多其他的概念。本文将会陈述下JavaScript模块的前世今生。</p>
<p>众所周知,JavaScript由于<a href="http://yanhaijing.com/javascript/2013/06/22/javascript-designing-a-language-in-10-days/">历史的原因</a>并没有模块的概念,自从ajax带来了web2.0概念后,js代码已经和以前大不相同了,2009年HTML5兴起后,前端代码的行数已经呈现井喷式发展,随着代码量的增加,模块的缺失的缺点日益凸显,Javascript社区做了很多探索。</p>
<h2 id="模块的定义">模块的定义</h2>
<p>模块并非js语言独创,显然是借鉴其他语言的,下面是百度百科对模块的定义:</p>
<blockquote>
<p>模块,又称构件,是能够单独命名并独立地完成一定功能的程序语句的集合(即程序代码和数据结构的集合体)</p>
</blockquote>
<p>从中提炼出几个关键字就是,独立,集合,完成一定功能。</p>
<p>上面的提炼,再从其他语言的实现中借鉴下,总结起来,我们期待的模块有如下特性:</p>
<ul>
<li>独立性——能够独立完成一个功能,不受外部环境的影响</li>
<li>完整性——完成一个特定功能</li>
<li>集合性——一组语句的集合</li>
<li>依赖性——可以依赖已经存在的模块</li>
<li>被依赖——可以被其他模块依赖</li>
</ul>
<p>其实我们想要的就是一个独立的模块,并能引用依赖,及被依赖。</p>
<p>C语言的库和头文件(include),java的包(import)。这在其他语言中都是原生支持的特性,在js中却是没有的。</p>
<h2 id="原始写法">原始写法</h2>
<p>如果仅从定义入手,那么一个函数即可成为一个模块(独立,集合,完成一个功能),那我们就先从最原始的探索开始,也许不经意间,我们早已在使用模块了。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//最简单的函数,可以称作一个模块
function add(x, y) {
return x + y;
}
</code></pre></div></div>
<p>稍微了解点javascript基础的人都知道js中能创建作用域的就是函数(ES6之前),总结下社区的探索,对模块的模拟大概如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function (mod, $, _) {
mod.add = ***;
mod.sub = ***;
}((window.mod = window.mod || {}), jQuery, Underscore));
</code></pre></div></div>
<p>上面的mod模块不会重复定义,可自由定义依赖。</p>
<p>99%的人思想会止步于此,但这种实现其实并不完美,仍然需要手动维护依赖的顺序。典型的场景就是上面的jquery必须先于我们的代码引入,不然会报引用错误,这显然不是我们想要的。</p>
<p>我在写<a href="http://yanhaijing.com/Painter/">Painter</a>的时候,曾经手动维护几十个script之间的先后顺序,这种感觉很虐心,最后想加个新script很容易报错。下面介绍的</p>
<h2 id="yui">YUI</h2>
<p>前段时间雅虎宣布<a href="http://yuilibrary.com/">YUI</a>不再更新了,很是伤感,最早接触模块的概念,当属YUI了,当然不是YUI2了。</p>
<p>YUI3经过全新设计,使用了沙箱模式 + 命名空间的方式,并有了模块的概念。</p>
<p>例如在YUI3中想使用一个模块,需要如下这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//使用node模块,node模块会作为参数传入
YUI().use('node', function (Y) {
///***
}
</code></pre></div></div>
<p>YUI的模块化已经做的很好了,但对于仅想使用模块的人,要引入YUI确实有点太重了。</p>
<h2 id="cmdcommon-module-definition">CMD(Common Module Definition)</h2>
<p>说道<a href="https://github.com/cmdjs/specification">CMD</a>就不能不提<a href="http://wiki.commonjs.org/wiki/CommonJS">commonjs</a>,提到commonjs就不能不提<a href="http://nodejs.org/">node</a>。</p>
<p>CMD规范参照commonjs中的方式,定义模块的方式如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>define(function(require, exports, module) {
// The module code goes here
});
</code></pre></div></div>
<p>一个文件就是一个模块,文件名就是模块的名字,使用模块的方法也和commonjs中一致,只需require就好了,模块名字可省略后缀。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//使用event.js模块
var ec = require('event');
</code></pre></div></div>
<p>CMD的典型实现就是<a href="http://seajs.org">seajs</a>,应用的很广泛。</p>
<h2 id="amdasynchronous-module-definition">AMD(Asynchronous Module Definition)</h2>
<p><a href="https://github.com/amdjs/amdjs-api/wiki/AMD-(%E4%B8%AD%E6%96%87%E7%89%88)">AMD</a>是异步模块定义,特别适合在浏览器端使用,其规范和CMD是很像的,AMD规范中定义模块的方式如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>define(id?, dependencies?, factory);
</code></pre></div></div>
<p>同CMD一样,一个文件即一个模块,模块的使用方法如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>define(["beta"], function (beta) {
bata.***//调用模块
});
</code></pre></div></div>
<p>AMD主张依赖注入,这点和CMD不同(以来查找)。</p>
<p>AMD也支持已CMD的方式来使用依赖。</p>
<p>AMD的典型实现有<a href="http://requirejs.org/">requireJS</a>,<a href="https://github.com/fex-team/mod">modJS</a>和<strong><a href="https://github.com/yanhaijing/lodjs">lodJS</a></strong>。</p>
<h2 id="kmd">KMD</h2>
<p>KMD是<a href="http://docs.kissyui.com/1.4/docs/html/tutorials/kissy/loader/index.html">kissy</a>中提出来的,是kissy自己的一套模块化方案,具体我也不是很清楚,感兴趣的同学可自行搜索相关资料。</p>
<p>有一次同事<a href="http://weibo.com/u/1835760415">@eric曦尧</a>无意说起,KMD的意思是 kill amd and cmd,当时觉得好高打上的名字(/ □ \)。</p>
<h2 id="es6">ES6</h2>
<p><a href="http://yanhaijing.com/es5/">ES6</a>带来了语言层面的模块化支持,规范方面见<a href="https://people.mozilla.org/~jorendorff/es6-draft.html#sec-module-namespace-exotic-objects">这里</a>,文档方面见<a href="http://es6.ruanyifeng.com/#docs/class">这里</a>。</p>
<p>我们现在期待的就是ES6规范快点尘埃落定(据说今年夏天),现在还处于草案状态,还有浏览器厂商们的大力支持,还有就是在国内尽快普及开来。</p>
<h2 id="umd">UMD</h2>
<p><a href="https://github.com/umdjs/umd">UMD</a>的全称是Universal Module Definition。和它名字的意思一样,这种规范基本上可以在任何一个模块环境中工作。</p>
<p>一段典型的UMD代码如下所示:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function (root, factory) {
var Data = factory(root);
if ( typeof define === 'function' && define.amd) {
// AMD
define('data', function() {
return Data;
});
} else if ( typeof exports === 'object') {
// Node.js
module.exports = Data;
} else {
// Browser globals
var _Data = root.Data;
Data.noConflict = function () {
if (root.Data === Data) {
root.Data = _Data;
}
return Data;
};
root.Data = Data;
}
}(this, function (root) {
var Data = ...
//自己的代码
return Data;
}));
</code></pre></div></div>
<p>这是出自<a href="https://github.com/yanhaijing/data.js">data.js</a>中的一部分代码,其原理就是做个判断,不同的环境进行不同的处理。</p>
<p>我已将UMD应用到自己的项目中,瞬间感觉高大上了不少:-)。</p>
<h2 id="总结">总结</h2>
<p>比较成气候的模块化方案,当属AMD和CMD,网上关于二者比较的文章甚多,很难评价谁好谁坏,当下开来AMD的使用范围似乎更广些,而CMD的本土化方面做的更好。</p>
<p>这些模块化的探索,使前端工程化成为了可能,可以说没有模块,工程化更无从弹起,本文总结了大家在模块化方面的一些探索,下一篇文章将重点介绍下<a href="https://github.com/yanhaijing/lodjs">lodJS</a>(一款基于AMD的模块加载器)的实践和原理。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://www.ruanyifeng.com/blog/2012/10/javascript_module.html">Javascript模块化编程(一):模块的写法</a></li>
<li><a href="http://www.ituring.com.cn/article/1091">JavaScript模块化开发一瞥</a></li>
<li><a href="http://www.zhihu.com/question/20351507/answer/14859415">AMD 和 CMD 的区别有哪些?</a></li>
<li><a href="http://www.douban.com/note/283566440/">SeaJS与RequireJS最大的区别</a></li>
<li><a href="http://www.zhihu.com/question/21347409">YUI Modules 与 AMD/CMD,哪一种方式更好?</a></li>
<li><a href="http://zhuanlan.zhihu.com/FrontendMagazine/19569085">ES6的模块、构建工具及应用的发布</a></li>
<li><a href="https://blog.wilddog.com/?p=587">写了十年JS却不知道模块化为何物?</a></li>
</ul>
挥别 2014
2015-01-04T00:00:00+00:00
http://yanhaijing.com/work/2015/01/04/my-2014
<p>时间从不停留,在我写下文字的间隙又溜走一些,回首2014可以用几个关键字来总结,意外,纠结,欢乐,痛苦。</p>
<p>下面从工作和个人两方面回忆下一年来的点滴。</p>
<h2 id="工作">工作</h2>
<p>14年最大的两件事就是离开金山和入职百度了,艾玛,换工作换的太快了。</p>
<h3 id="怀旧金山">怀旧金山</h3>
<p>2013年最后一天,我来到了金山,从此称呼自己为金山人,这是我的第一家比较大型的互联网公司,入职时是紧张的,激动的,看到指纹打卡,瞬间觉得好高大上的感觉。</p>
<p>接下来的工作亦是兢兢业业,顺利通过了试用期,便有一些懈怠,在这里终于有了正规的开发流程,美工,后端,编辑和我,环环相扣。</p>
<p>在金山的第一个任务就是<a href="http://mj.xoyo.com">麻辣江湖</a>的<a href="http://mj.xoyo.com/zt/2014/01/06/index.shtml">新春专题</a>,也许是太紧张,也许是基础不够扎实,第一个任务就卡壳了,不会做啊,最后在<a href="http://weibo.com/sunxu">@旭姐</a>的帮助下顺利完成了第一个任务,在次深深的感谢旭姐在金山的日子里,对我的照顾。</p>
<p>接下来陆续做了一些游戏的专题活动,主要包括<a href="http://xd.xoyo.com">反恐行动</a>,<a href="http://mj.xoyo.com">麻辣江湖</a>和<a href="http://fox.xoyo.com">狐狸三国</a>。</p>
<ul>
<li><a href="http://xd.xoyo.com/zt/2014/gold/index.shtml">反恐行动-金牌联赛</a></li>
<li><a href="http://zt.xoyo.com/xd/tuiguang/">反恐行动-推广系统</a></li>
<li><a href="http://mj.xoyo.com/">麻辣江湖-着陆页</a></li>
</ul>
<p><strong>注:</strong>刚刚发现狐狸三国已经停服了,看着自己做的东西死了,最悲哀的事情莫过于此。</p>
<p>生活就这样继续了,做专题做到想吐,后来在移动化的浪潮下做了<a href="http://xd.m.xoyo.com/">反恐行动</a>和狐狸三国的移动端官网,第一次做手机端的东西。</p>
<p>后来在<a href="http://www.labazhou.net/">@伟哥</a>自媒体的主张下,开始尝试做些自己的东西,做了几款小游戏。</p>
<ul>
<li><a href="http://zt.xoyo.com/play/jx3sgbd/">上古宝典</a></li>
<li><a href="http://xd.xoyo.com/zt/zylm/index.html">正义联盟</a></li>
</ul>
<p>再后来我就离开金山了,命运总是充满了反讥,入职6个月后,7.25号我离开了金山。</p>
<p>最后写下些感谢的祝福,也是希望还能记住这些人。</p>
<ul>
<li>感谢 @伟哥,在迷茫的时候给我指引方向,还有一起打红警的欢乐。</li>
<li>感谢 @飞哥,总是在我意淫时,让我赶快干活。</li>
<li>感谢 @旭姐,工作中的琐事和技术上的指导。</li>
<li>感谢 @大亮,好久没有一起打红警了。</li>
<li>感谢 @娜娜,每当我抬起头,眼前都是美景(美女)。</li>
<li>感谢 @侯鹏,带给我那么多技术上的思考。</li>
<li>感谢 @甩饼,和我同姓,还夸我代码写的好,你的腰子好了吧((>﹏<))。</li>
</ul>
<h3 id="百度知道">百度知道</h3>
<p>7.30号加入了百度。这是一个意外,意外而来的面试,意外的面试通过。</p>
<p>有些事情总是相似的,但有些却不同,入职后第一感觉就是鸭梨好大,周围的同学都很优秀,终于有了可以探讨的小伙伴了,我们被称为FE。在百度的工作最大的变化,就是工作的范围更大了,html,css,js都要写,而且上线也是自己可以上线,内容也要自己填充,随时上线的感觉很爽,权力越大,责任越大。</p>
<p>主要负责活动页面的制作和主站项目的维护和更新工作,来到百度的第一个任务就是《<a href="http://zhidao.baidu.com/s/book/index.html">重酬出书</a>》,在<a href="http://blog.wuyuying.com/">@小伍</a> 的帮助下顺利完成了第一个项目,基于<a href="http://fis.baidu.com/">fis</a>,第一次用上自动化构建工具,而且还是fis这么高级的。</p>
<p>后续还做了一些其他活动和主站pc和wap端的一些功能需求,NA端的专题做的比较多,对移动端的开发技巧有了更多了解,最近做了两个比较大的PC专题,其中《<a href="http://zhidao.baidu.com/s/continue-sign/index.html">连续签到</a>》目前正在线上。</p>
<p>来到百度的变化可以用下面的话来总结:用上了自动化工具,开始了模块化开发,离不开了模板引擎,写上了less代码,工作效率更高了,不那么拖延了,遇上了一群牛掰的小伙伴。</p>
<p>我是一个有重度拖延症的人,到了百度后都快治好了::>_<::。</p>
<h2 id="个人">个人</h2>
HTML5 离线缓存-manifest简介
2014-12-28T00:00:00+00:00
http://yanhaijing.com/html/2014/12/28/html5-manifest
<p>在搞<a href="http://yanhaijing.com/Painter">Painter</a>时有涉及到HTML5离线缓存,最近想把其应用到其他项目中,发现自己已经忘得差不多了,所以写下本文,给自己做个记录。</p>
<p>本文将介绍离线缓存的方方面面,并在最后会介绍一下关于自动化的问题。</p>
<ul>
<li>起源</li>
<li>什么是Cache Manifest</li>
<li>Manifest的特点</li>
<li>浏览器支持情况</li>
<li>如何使用</li>
<li>Manifest文件</li>
<li>如何更新缓存</li>
<li>注意事项</li>
<li>自动化工具</li>
<li>参考资料</li>
</ul>
<h2 id="起源">起源</h2>
<p>html5之前的网页,都是无连接,必须联网才能访问,这其实也是web的特色,这其实对于PC是时代问题并不大,但到了移动互联网时代,设备终端位置不再固定,依赖无线信号,网络的可靠性变得降低,比如坐在火车上,过了一个隧道(15分钟),便无法访问网站,这对于web的伤害是很大的,比如对于 《<a href="http://yanhaijing.com/es5">ecmascript合集</a>》这样的为阅读而生的页面。</p>
<p>html5便引入了cache manifest 文件。那么什么是cache manifest呢,接下来会讲到。</p>
<h2 id="什么是cache-manifest">什么是Cache Manifest</h2>
<p>首先manifest是一个后缀名为minifest的文件,在文件中定义那些需要缓存的文件,支持manifest的浏览器,会将按照manifest文件的规则,像文件保存在本地,从而在没有网络链接的情况下,也能访问页面。</p>
<p>当我们第一次正确配置app cache后,当我们再次访问该应用时,浏览器会首先检查manifest文件是否有变动,如果有变动就会把相应的变得跟新下来,同时改变浏览器里面的app cache,如果没有变动,就会直接把app cache的资源返回,基本流程是这样的。</p>
<p><img src="/blog//164.png" alt="" /></p>
<h2 id="manifest的特点">Manifest的特点</h2>
<ul>
<li>离线浏览: 用户可以在离线状态下浏览网站内容。</li>
<li>更快的速度: 因为数据被存储在本地,所以速度会更快.</li>
<li>减轻服务器的负载: 浏览器只会下载在服务器上发生改变的资源。</li>
</ul>
<h2 id="浏览器支持情况">浏览器支持情况</h2>
<p>所有主流浏览器均支持应用程序缓存,除了 Internet Explorer。<a href="http://caniuse.com/#search=manifest">caniuse</a>给出的答案如下图所示。</p>
<p><img src="/blog//165.png" alt="" /></p>
<h2 id="如何使用">如何使用</h2>
<p>html新增了一个manifest属性,可以用来指定当前页面的manifest文件。</p>
<p>创建一个和html同名的manifest文件,比如页面为index.html,那么可以建一个index.manifest的文件,然后给index.html的html标签添加如下属性即可:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><html lang="en" manifest="index.manifest">
</code></pre></div></div>
<h2 id="manifest文件">Manifest文件</h2>
<p>接下来详细说说manifest的细节,一个典型的manifest文件代码结构像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CACHE MANIFEST
#version 1.3
CACHE:
test.css
NETWORK:
*
</code></pre></div></div>
<p>manifest文件,基本格式为三段: CACHE, NETWORK,与 FALLBACK,其中NETWORK和FALLBACK为可选项。</p>
<p>而第一行CACHE MANIFEST为固定格式,必须写在前面。</p>
<p>以#号开头的是注释,一般会在第二行写个版本号,用来在缓存的文件更新时,更改manifest的作用,可以是版本号,时间戳或者md5码等等。</p>
<h3 id="cache必须">CACHE:(必须)</h3>
<p>标识出哪些文件需要缓存,可以是相对路径也可以是绝对路径。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a.css
http://yanhaijing.com/a.css
</code></pre></div></div>
<p>NETWORK:(可选)</p>
<p>这一部分是要绕过缓存直接读取的文件,可以使用通配符*。</p>
<p>下面的代码 “login.asp” 永远不会被缓存,且离线时是不可用的:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NETWORK:
login.asp
</code></pre></div></div>
<p>可以使用星号来指示所有其他资源/文件都需要因特网连接:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NETWORK:
*
</code></pre></div></div>
<h3 id="fallback可选">FALLBACK:(可选)</h3>
<p>指定了一个后备页面,当资源无法访问时,浏览器会使用该页面。该段落的每条记录都列出两个 URI—第一个表示资源,第二个表示后备页面。两个 URI 都必须使用相对路径并且与清单文件同源。可以使用通配符。</p>
<p>下面的例子中,如果无法建立因特网连接,则用 “404.html” 替代 /html5/ 目录中的所有文件。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>FALLBACK:
/html5/ /404.html
</code></pre></div></div>
<p>下面的例子中,则用 “404.html” 替代所有文件。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>FALLBACK:
*.html /404.html
</code></pre></div></div>
<h2 id="如何更新缓存">如何更新缓存</h2>
<p>如下三种方式,可以更新缓存:</p>
<ul>
<li>更新manifest文件</li>
<li>通过javascript操作</li>
<li>清除浏览器缓存</li>
</ul>
<p>给manifest添加或删除文件,都可更新缓存,如果我们更改了js,而没有新增或删除,前面例子中注释中的版本号,可以很好的用来更新manifest文件。</p>
<p>html5中引入了js操作离线缓存的方法,下面的js可以手动更新本地缓存。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>window.applicationCache.update();
</code></pre></div></div>
<p>如果用户清除了浏览器缓存(手动或用其他一些工具)都会重新下载文件。</p>
<h2 id="注意事项">注意事项</h2>
<ul>
<li>浏览器对缓存数据的容量限制可能不太一样(某些浏览器设置的限制是每个站点 5MB)。</li>
<li>如果manifest文件,或者内部列举的某一个文件不能正常下载,整个更新过程都将失败,浏览器继续全部使用老的缓存。</li>
<li>引用manifest的html必须与manifest文件同源,在同一个域下。</li>
<li>FALLBACK中的资源必须和manifest文件同源。</li>
<li>当一个资源被缓存后,该浏览器直接请求这个绝对路径也会访问缓存中的资源。</li>
<li>站点中的其他页面即使没有设置manifest属性,请求的资源如果在缓存中也从缓存中访问。</li>
<li>当manifest文件发生改变时,资源请求本身也会触发更新。</li>
</ul>
<h2 id="自动化工具">自动化工具</h2>
<p>manifest文件中的cache部分不能使用通配符,必须手动指定,这实在太让人不可理解,文件一多,就成了体力活了,这里介绍的 <a href="https://www.npmjs.com/package/grunt-manifest">grunt-manifest</a>能自动生成manifest文件的目的。grunt-manifest依赖grunt,grunt是一个自动化构建工具,如果你不知道grunt,请移步<a href="http://www.gruntjs.net/">这里</a>。</p>
<p>如下的命令可以安装grunt-manifest,并加入到依赖文件。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install grunt-manifest --save-dev
</code></pre></div></div>
<p>如下的代码,可以在grunt中载入grunt-manifest,然后便可使用。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grunt.loadNpmTasks('grunt-manifest');
</code></pre></div></div>
<p>使用grunt-manifest的一个典型的配置文件如下所示:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grunt.initConfig({
manifest: {
generate: {
options: {
basePath: "../",
cache: ["js/app.js", "css/style.css"]
network: ["http://*", "https://*"],
fallback: ["/ /offline.html"],
exclude: ["js/jquery.min.js"],
preferOnline: true,
verbose: true,
timestamp: true
},
src: [
"some_files/*.html",
"js/*.min.js",
"css/*.css"
],
dest: "index.manifest"
}
}
});
</code></pre></div></div>
<p>其中options定义生成manifest的一些自定义参数,src是要生成的文件,dest是输出文件。</p>
<p>options下有很多参数,主要参数如下:</p>
<ul>
<li>basePath 设置出入文件的根目录</li>
<li>cache 手动添加缓存文件</li>
<li>network 手动添加网络文件</li>
<li>fallback 手动添加后备文件</li>
<li>exclude 设置不添加到cache的文件</li>
<li>verbose 是否添加版权信息</li>
<li>timestamp 是否添加时间戳</li>
</ul>
<p>这里有<a href="http://yanhaijing.com/basejs/">basejs</a>的<a href="https://github.com/yanhaijing/basejs/blob/gh-pages/Gruntfile.js">配置文件</a>和生成的<a href="https://github.com/yanhaijing/basejs/blob/gh-pages/index.manifest">manifest文件</a>的例子。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://www.nihaoshijie.com.cn/index.php/archives/425">HTML5离线存储之Application Cache</a></li>
<li><a href="http://www.w3school.com.cn/html5/html_5_app_cache.asp">HTML 5 应用程序缓存</a></li>
</ul>
Web编码总结
2014-12-20T00:00:00+00:00
http://yanhaijing.com/web/2014/12/20/web-charset
<p>今天遇到了一个奇怪的问题,两次ajax发送的同一个变量值,后端接收到的编码不一样……,一时间,我竟然发现自己对于编码的问题不能说的很清楚。</p>
<p>lisp主张代码即数据,其实我们写的代码也是数据(信息),数据的存储和传播都要就要涉及到编码的问题。就像我们向对方传递信息之前,先要问对方:can you spreak in english。</p>
<p><strong>小贴士:</strong>嗨,你知道吗!windows的换行符是 \r\l,linux的是 \l,mac的是 \r,这是有意还是故意的呢……</p>
<p>本文会试图说清web开发过程中,如下方面的编码问题:</p>
<ul>
<li>编码简史</li>
<li>文件编码</li>
<li>HTML编码</li>
<li>CSS编码</li>
<li>JavaScript编码</li>
<li>ajax编码</li>
</ul>
<h2 id="编码简史">编码简史</h2>
<p>如果想把编码问题说清楚,那恐怕你看到这里就会把页面关掉了,所以这里仅简要介绍下国内web开发中常用的一些编码。如果你了解或不感兴趣,可以直接跳过本部分。</p>
<ul>
<li>ASCII/EASCII(ISO/IEC 646)</li>
<li>GB2312/GBK/GB18030</li>
<li>Unicode/UTF8/UTF16</li>
</ul>
<p>进入数字时代,整个世界都要数字化,最先数字化的就是文字,老外的自我中心论,以为世上仅有abcd…… 26个字母,便发明了ASCII(美国标准信息交换码)。</p>
<p><a href="http://zh.wikipedia.org/wiki/ASCII">ASCII</a> 使用一个字节的低7位的不同组合来表示字母数字和一些其他字符,<code class="language-plaintext highlighter-rouge">2^7 = 128</code>7位二进制共可以代表128个字母。</p>
<p>后来为了表示更多的字符便将ASCII扩展为8位,称为<a href="http://zh.wikipedia.org/wiki/EASCII">EASCII</a>,并等同于国际标准<a href="http://zh.wikipedia.org/wiki/ISO/IEC_646">ISO/IEC 646</a>。</p>
<p>而我们为了让汉语也能数字化,变发明了gb2312,gb2312使用两个字节16位表示汉字,为了兼容ASCII将每个字节的高8位置为1,共有14位可用,2^14 = 16384,但GB 2312标准共收录6763个汉字,后来发现不太够用,又扩展了gbk,gb18030。</p>
<p>终于老外发明了 <a href="http://zh.wikipedia.org/wiki/Unicode">Unicode</a>,一切都解决了,Unicode有两个字节的16位的编码空间,Unicode是一个归法,比较常用的有UTF8和UTF16两种编码方式。</p>
<p><a href="http://zh.wikipedia.org/wiki/UTF-8">UTF8</a>在web领域比较常用,是一种变长的编码方式,<a href="http://zh.wikipedia.org/wiki/UTF-16">UTF16</a>是一种定长方式。</p>
<p>最后来看一下,‘回’字的不同编码,要记住哦,后面会多次用到。</p>
<p><img src="/blog/162.png" alt="" /></p>
<h2 id="文件编码">文件编码</h2>
<p>文件编码也可以说是存储的编码。</p>
<p>我们写的代码,最终会以二进制的方式,持久化到计算机的存储设备中。</p>
<p>不同编码的文件,会以不同的规则存储到磁盘中,不同的编码要有自己独特的规则,这样才能在读取的时候不发生冲突,也就是要能识别出来自己,计算机在打开文件的时候都是靠猜测来解码的。</p>
<p>如果存储的编码(算法)和打开的编码(算法)不一致,就会出现乱码的情况。</p>
<p>文件在网络上传入要声明自己的编码,我们一定要清楚源文件的编码,然后才能进行后序工作。</p>
<p>对于前端而言会涉及html,css和js源文件的编码,而国内常用的编码如下:</p>
<ul>
<li>ascii</li>
<li>gbk</li>
<li>utf-8</li>
</ul>
<p>推荐大家用utf-8,不容易出问题,但由于历史原因,国内有很多网站的编码都是gbk,下面是BAT的网站情况:</p>
<ul>
<li>qq.com gb2312</li>
<li>taobao.com gbk</li>
<li>baidu utf-8</li>
</ul>
<p>百度历史比较悠久的产品线的编码也是gbk,比如百度知道。</p>
<h2 id="html">HTML</h2>
<p>网页中HTML的编码由哪些部分决定呢?真正运行在服务器上的HTML编码由下面几部分决定</p>
<ul>
<li>HTTP头</li>
<li>meta</li>
<li>user-agent</li>
</ul>
<p>上面给出的三个因素的权重是由高到低,也就是说HTTP头会覆盖meta的信息。</p>
<p>如果你还不知道什么是HTTP头那么请自行百度,HTTP头中的编码信息如下图中红色方块圈起来的部分,代表当前页面的编码是utf8。用户代理收到这个信息后,就会用utf8编码来将收到的字节流解码。</p>
<p><img src="/blog/150.png" alt="" /></p>
<p>有些服务器会设置文件的默认编码信息,有些则不会,后端语言都可以自行设置页面的html编码,在php中设置http编码代码如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>header("Content-Type:text/html; charset=utf-8");
</code></pre></div></div>
<p>如果没有设置http头信息,或者在文件系统直接打开页面,html的meta标签就派上了用场,meta中可以设置http头信息,下面的代码和上面php的功能相同:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><meta content="text/html; charset=utf-8" http-equiv="Content-Type">
</code></pre></div></div>
<p>在html5中将上面的代码简化为如下形式:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><meta charset="utf-8">
</code></pre></div></div>
<p>如果既没有设置http头,也没有meta标签,那么用户代码会使用系统的默认设置,windows下的中文环境的默认编码一般是 gb2312,所以用户代理就会用gb2312来解码页面,如果页面的编码也刚好是gb2312那么万事ok,否则就会出现乱码。</p>
<p>用户代理一般可以设置默认的编码是什么。比如chrome打开设置下的内容网络,会看到如下的设置界面。</p>
<p><img src="/blog/151.png" alt="" /></p>
<p><strong>注:</strong>如果最终显示的编码和页面本身的编码不同就会出现乱码。</p>
<h2 id="css">CSS</h2>
<p>说完了HTML的编码再来说说CSS的编码,如果HTML文件和CSS文件的编码不一致,那么就需要单独声明才可以。</p>
<p>在html4中link有一个属性——charset可以用来指定引入css文件的编码,但这个属性在html5中已经废弃了,虽然废弃了但在浏览器中还是可以使用的,下面的代码显示指定css文件的编码为gbk。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><link rel="stylesheet" href="gbk-1.css" charset="gbk">
</code></pre></div></div>
<p>然而html5废弃了这个属性那么该怎么办呢,其实废弃这个属性,是因为把这个功能代理给了css文件,css中有一个@charset指定,可以指定页面的编码,将下面的代码放在css文件的顶部,会显示声明页面的编码为utf8。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@charset utf-8
</code></pre></div></div>
<p>那么问题来了,如果即设置了charset属性,又设置了@charset指定,结果是@charset指定会覆盖link的charset属性,charset属性已经废弃了,建议用css的@charset指令。</p>
<p><strong>注:</strong>如果我们显示指定的编码和css文件的编码不一致也会导致乱码问题。</p>
<h2 id="javascript">JavaScript</h2>
<p>说清了CSS的编码,我们再来说JavaScript的编码问题,首先还是如果HTML文件的编码和JavaScript文件的编码不一致的话就会产生乱码,这时就需要我们显示声明一下js文件的编码才可以。</p>
<p>html中的script标签有一个charset属性,用来指定引入外部jss文件的编码(字符集)。下面的代码显示声明引入的js的编码是utf8。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><script src="***.js" charset="utf-8"></script>
</code></pre></div></div>
<p><strong>注:</strong>如果我们显示指定的编码和js文件的编码不一致也会导致乱码问题。</p>
<p>其实在JavaScript中仅支持utf16编码,其实也不是utf16,而是utf16的子集——ucs-2,这导致在js中无法表示BMP之外的文字(更多信息请看<a href="http://www.ruanyifeng.com/blog/2014/12/unicode.html">这里</a>)。</p>
<p>无论js源文件的编码是什么,下面的源代码的输出结果是一样的——前提是解码js的过程正确。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'回'.charCodeAt(0)#输出 22238
</code></pre></div></div>
<p>22238的十六进制表示是56 DE,而这正是‘回’字的utf16编码。</p>
<h2 id="ajax">ajax</h2>
<p>说ajax之前,先来说说表单提交吧,下面分别是utf8页面和gbk页面的表单提交请求,其中参数name是’回’字。可以看出表单请求的编码是由页面决定的。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>http://localhost/github/webtest/charset/form.php?name=%E5%9B%9E
http://localhost/github/webtest/charset/form.php?name=%BB%D8
</code></pre></div></div>
<p>ajax发送的代码也是由页面决定的,但我们在发送参数前都会encodeURIComponent一下,encodeURIComponent不管页面编码是什么,都会返回utf8编码。</p>
<p>让我们构造一个例子,页面A的编码为gbk,有如下的代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xhr.open("GET","form.php?name=" + encodeURIComponent('回') + 'namegbk=' + '回',true);
</code></pre></div></div>
<p>我们看到发送的ajax请求如下所示:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>http://localhost/github/webtest/charset/form.php?name=%E5%9B%9Enamegbk=%BB%D8
</code></pre></div></div>
<p>这个例子巧妙的验证了上面的结论。</p>
<p>再来说说json,json只支持utf8编码,所以不要返回gbk编码的json。</p>
<p><img src="/blog/161.png" alt="" /></p>
<h2 id="总结">总结</h2>
<p>在信息交换的任何一个环节,如果编码信息和解码信息不一致都会产生乱码问题。</p>
<p>本文中的所有例子都可以从<a href="https://github.com/yanhaijing/webtest/tree/master/charset">webtest</a>下载。</p>
<p>关于编码还有很多东西可以需要学习,我强烈建议你可以阅读下参考资料里面的一些文章,同时我还建议您阅读”<a href="http://www.amazon.cn/gp/product/B009RSXIB4?ie=UTF8&camp=536&creativeASIN=B009RSXIB4&linkCode=xm2&tag=yanhaijing-23">code</a>“这本书。</p>
<p><a href="http://www.amazon.cn/gp/product/B009RSXIB4?ie=UTF8&camp=536&creativeASIN=B009RSXIB4&linkCode=xm2&tag=yanhaijing-23"><img src="/blog/163.png" alt="" /></a></p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://www.unicode.org/">http://www.unicode.org/</a></li>
<li><a href="http://www.zdic.net/">汉典</a></li>
<li><a href="http://www.unicode.org/charts/unihan.html">编码查询工具</a></li>
<li><a href="http://blog.csdn.net/fmddlmyy/article/details/372148">谈谈Unicode编码,简要解释UCS、UTF、BMP、BOM等名词</a></li>
<li><a href="http://blog.csdn.net/fmddlmyy/article/details/1510189">浅谈文字编码和Unicode(上)</a></li>
<li><a href="http://blog.csdn.net/fmddlmyy/article/details/1510193">浅谈文字编码和Unicode(下)</a></li>
<li><a href="http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html">字符编码笔记:ASCII,Unicode和UTF-8</a></li>
<li><a href="http://www.crifan.com/summary_explain_what_is_html_charset_and_common_value_of_gb2312_gbk_utf_8_iso8859_1/">【整理】关于HTML网页源码的字符编码(charset)格式(GB2312,GBK,UTF-8,ISO8859-1等)的解释</a></li>
<li><a href="http://www.ruanyifeng.com/blog/2014/12/unicode.html">Unicode与JavaScript详解</a></li>
</ul>
如何调试移动端网页
2014-12-17T00:00:00+00:00
http://yanhaijing.com/mobile/2014/12/17/web-debug-for-mobile
<p>本文将会介绍如何在真机上调试移动端的页面,和之前的《<a href="http://yanhaijing.com/web/2014/02/21/how-to-test-mobile-websit-on-pc/">如何在电脑上测试手机网站</a>》的不同之处在于真机环境。</p>
<p>本会介绍的方法如下:</p>
<ul>
<li>UC浏览器开发版</li>
<li>Chrome 远程调试</li>
<li>Firefox 远程调试</li>
<li>Opera 远程调试</li>
<li>Weinre</li>
</ul>
<h2 id="uc浏览器开发版">UC浏览器开发版</h2>
<p>感谢UC造出这么好用的浏览器,同时注重开发者,远好于国内其他厂商,UC浏览器有一个开发版非常赞。</p>
<ul>
<li><a href="http://www.uc.cn/business/developer/">UC浏览器开发版网址</a></li>
<li><a href="http://wap.uc.cn/index.php?action=PackageDown&do=ByPfid&product=UCBrowser&pfid=145&lang=zh-cn&bid=33533&direct=true&from=dev-slp-dir-pc">下载地址</a></li>
</ul>
<p>UC开发版的网站上介绍的很清楚,也有很详细的文档,这里就不再赘述,简单记录下WIFI的调试方法,保证pc和手机在同一个网段,在PC上打开Chrome或Safari,在地址栏输入:<strong>手机IP + :9998</strong>,在手机端会弹出确认按钮,点击确认后,就可在pc上看到效果了,如此简单。</p>
<h2 id="chrome浏览器">Chrome浏览器</h2>
<p>要先使用Chrome浏览器的远程调试功能需要先翻墙才可以,还需做如下准备:</p>
<ul>
<li>PC Chrome最新版</li>
<li>安卓 Chrome最新版</li>
<li>数据线一根</li>
</ul>
<p>插上数据线,打开手机里的浏览器,然后PC浏览器-菜单-更多工具-检查设备,就会看到如下界面:</p>
<p><img src="/blog/156.png" alt="" /></p>
<p>点击inspect,稍等片刻即可打开调试界面:</p>
<p><img src="/blog/157.png" alt="" /></p>
<p>功能和PC一样,异常强大。</p>
<p>更多信息请移步<a href="https://developer.chrome.com/devtools/docs/remote-debugging#installing-androidsdk">这里</a>。</p>
<h2 id="firefox浏览器">Firefox浏览器</h2>
<p>首先,需要如下几个准备工作:</p>
<ul>
<li>PC Firefox 15+</li>
<li>Android Firefox 15+</li>
<li>一根数据线</li>
<li>adb驱动</li>
</ul>
<p>上面三个就不多解释了,adb驱动我们安装个手机管家什么的,都可以自动安装上,打开命令行中断,输入adb命令,如果看到长长的输出,那么恭喜你,你已经安装了adb驱动了。</p>
<p>输入如下命令可以查看链接的设备。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adb devices
</code></pre></div></div>
<p><img src="/blog/152.png" alt="" /></p>
<p>接下来我们需要用adb在本地开一个接口来做代理接受数据:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adb forward tcp:6000 tcp:6000
</code></pre></div></div>
<p>接下来就是打开手机和电脑的远程调试开惯了,默认都是关闭的。</p>
<p>手机端Firefox点击菜单栏的”设置”->”开发者工具”,勾选”远程调试”。</p>
<p><img src="/blog/153.png" alt="" /></p>
<p>PC端Firefox打开about:config,设置devtools.debugger.remote-enabled为True。或者打开开发者工具,找到设置面板,打开里面的远程调试。</p>
<p><img src="/blog/154.png" alt="" /></p>
<p>接下来找到桌面端Firefox-菜单-工具-Web开发者-远程链接。保证端口号和上面开启的端口号一致就好了。</p>
<p><img src="/blog/155.png" alt="" /></p>
<p>如果你没有其他特别的远程调试要求,只需要使用默认值。按“确定”。手机端会弹出一个确定按钮,点击确定就可以在电脑上调试手机上的网页了。</p>
<p>这里火狐的实现有一个不好的地方,就是在调试面板里选择dom是,手机页面里不会跟着变色,这点还需改进。</p>
<p>更多信息,请移步<a href="https://developer.mozilla.org/en-US/docs/Tools/Remote_Debugging/Firefox_for_Android">这里</a>。</p>
<h2 id="opera浏览器">Opera浏览器</h2>
<p>请下载opera官方浏览器,而非中文版,中文版把这个功能阉割了(鄙视一下),在手机浏览器地址栏输入如下命令,会看到下图所示的信息。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>opera:debug
</code></pre></div></div>
<p><img src="/blog/158.png" alt="" /></p>
<p>勾上enable选项,然后在pc上命令行输入如下命令:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adb forward tcp:9222 localabstract:opera_devtools_remote
</code></pre></div></div>
<p>然后用基于chrominu的浏览器打开http://localhost:9222,已chrome为例,会看到如下界面:</p>
<p><img src="/blog/159.png" alt="" /></p>
<p>点击相应页面即可进入调试界面,非常方便。</p>
<p>这里需要注意,可能会出现空白,因为opera的调试页面使用了不安全的链接被组织了,只需点一下右上角的安全提示按钮,选择允许即可,如下图所示。</p>
<p><img src="/blog/160.png" alt="" /></p>
<p>其实在chrome浏览器的检索设备界面,也能看到opera的页面,但是点击inspect就会崩溃,无语啊。</p>
<p>更多信息,请移步<a href="https://dev.opera.com/articles/remotely-debugging-opera-for-android/">这里</a>。</p>
<h2 id="weinre">Weinre</h2>
<p>网上关于winner的介绍大多是基于java,很繁琐,其实基于node+npm会简单很多,感谢node,感谢npm。</p>
<ul>
<li><a href="https://www.npmjs.com/package/weinre">weinre在npm</a></li>
<li><a href="http://people.apache.org/~pmuellr/weinre/">weinre官网</a></li>
<li><a href="http://people.apache.org/~pmuellr/weinre/docs/latest/">weinre文档</a></li>
</ul>
<p>打开命令行输入如下命令安装npm</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm -g install weinre #win
sudo npm -g install weinre # linux
</code></pre></div></div>
<p>安装成功了后,在命令行是输入</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>weinre --httpPort 8081 --boundHost -all-
</code></pre></div></div>
<p>会开到如下的提示,上面的httpPort是指定接口,省略的话会默认8080,boundHost指定为all,才能在远程设备访问(手机)上访问。</p>
<p><img src="/blog/147.png" alt="" /></p>
<p>在浏览器打开 http://localhost:8080,会看到weinre的介绍信息如下:</p>
<p><img src="/blog/148.png" alt="" /></p>
<p>点击上图中的1会进入相应的调试页面,现在还是空的,后面会有图,点击2会进入weinre的文档。</p>
<p>在需要调试的页面添加如下js,这个js是打开的weinre页面中的js代码,就可在在pc端调试手机端页面。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><script src="http://localhost:8081/target/target-script-min.js#anonymous"></script>
</code></pre></div></div>
<p>假设电脑ip是192.168.0.100,服务器的端口8080,需要将上面代码中的localhost替换为电脑ip,然后在手机上访问电脑的上的页面,就可以通过weinre调试了。</p>
<p><img src="/blog/149.gif" alt="" /></p>
<h2 id="总结">总结</h2>
<p>还有一些其他方法还没有实验过,只是听说过,所以就不写出来了,先挖个坑吧,以后慢慢填。</p>
<p>网上的很多资料都陈旧了,浏览器已经改版了,还有些人根本自己都没有实验过,就敢写,上面的方法我都亲自实验过了。如果遇到什么问题,可以给我留言讨论。</p>
JavaScript对象继承一瞥
2014-11-09T00:00:00+00:00
http://yanhaijing.com/javascript/2014/11/09/object-inherit-of-js
<p>js创建之初,正值java大行其道,面向对象编程春风正盛,js借鉴了java的对象机制,但仅是看起来像,也就是js的构造函数,如下:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">People</span><span class="p">(</span><span class="nx">age</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">age</span> <span class="o">=</span> <span class="nx">age</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">getAge</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(){</span><span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">age</span><span class="p">};</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">p1</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">People</span><span class="p">(</span><span class="mi">20</span><span class="p">);</span><span class="c1">//People的实例1</span>
<span class="kd">var</span> <span class="nx">p2</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">People</span><span class="p">(</span><span class="mi">40</span><span class="p">);</span><span class="c1">//People的实例2</span>
</code></pre></div></div>
<p>上面的代码很像java了,通过new constructor()的方式,可以创建多个实例。</p>
<p>但上面代码问题是getAge方法会在每个People的实例中存在,如果实例多的话,会浪费很多空间,js采用了牺牲时间,获取空间的方法,js引入了原型理念,将方法放入原型中:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">People</span><span class="p">(</span><span class="nx">age</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">age</span> <span class="o">=</span> <span class="nx">age</span>
<span class="p">}</span>
<span class="nx">People</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getAge</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span><span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">age</span><span class="p">};</span>
</code></pre></div></div>
<p>本文就来总结下,如何使用构造函数来实现继承。</p>
<h2 id="场景">场景</h2>
<p>我们假设我们有一个父构造函数People和子构造函数Student,People有一个属性age和一个方法getAge,Student有一个属性num和getNum。</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">People</span><span class="p">(</span><span class="nx">age</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">age</span> <span class="o">=</span> <span class="nx">age</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">People</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getAge</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(){</span><span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">age</span><span class="p">;};</span>
<span class="kd">function</span> <span class="nx">Student</span><span class="p">(</span><span class="nx">num</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">num</span> <span class="o">=</span> <span class="nx">num</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">Student</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getNum</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span><span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">num</span><span class="p">;};</span>
</code></pre></div></div>
<p>我们要实现是Student继承People,这在js里可要费一番力气了。</p>
<h2 id="默认模式">默认模式</h2>
<p>我们可以利用js的原型机制,将子构造函数的原型属性设置为父构造函数的实例,这是js中比较常用的方式:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">Student</span><span class="p">(</span><span class="nx">num</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">num</span> <span class="o">=</span> <span class="nx">num</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">Student</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">People</span><span class="p">();</span>
<span class="nx">Student</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getNum</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span><span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">num</span><span class="p">;};</span>
<span class="kd">var</span> <span class="nx">stu1</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Student</span><span class="p">(</span><span class="dl">'</span><span class="s1">123</span><span class="dl">'</span><span class="p">);</span>
</code></pre></div></div>
<p>这样做其实基本实现了我们的需求,但如果深入思考上面的方式,其实有几个缺点:</p>
<ol>
<li>子类无法继承父类的实例属性</li>
<li>会将父类的实例属性,扩展到子类的原型上</li>
<li>修改了子类的原型属性,会导致在stu1上获取constructor属性为People,而不是Student</li>
</ol>
<p>哦!!!</p>
<h2 id="借用构造函数">借用构造函数</h2>
<p>先来看看如何解决第一个问题,我们可以巧用js的call方法,如果你还不知道这个方法,请移步<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call">这里</a>。</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">Student</span><span class="p">(</span><span class="nx">age</span><span class="p">,</span> <span class="nx">num</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">People</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">age</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">num</span> <span class="o">=</span> <span class="nx">num</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>我们在子构造函数内部,借用父构造函数,这样就巧妙地在子类中继承了父类的实例化属性。这其实类似java的super关键字。</p>
<h2 id="共享原型">共享原型</h2>
<p>再来看看如何解决第二个问题,解决这个问题,其实我们可以将子构造函数的原型更改为父构造函数的原型,而不是父构造函数的实例。</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">Student</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="nx">People</span><span class="p">.</span><span class="nx">prototype</span><span class="p">;</span>
</code></pre></div></div>
<p>这样就不会将父构造函数的实例属性扩展到子构造函数的原型上了。</p>
<p>但这样做会导致另一个问题,就是无法再在Student的原型上扩展方法了,因为会扩展同时会扩展到People的原型上。</p>
<h2 id="临时构造函数">临时构造函数</h2>
<p>为了解决上面引发的问题,和第三个问题。我们可以在子构造函数和父构造函数之间,加一层临时构造函数。</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">F</span><span class="p">()</span> <span class="p">{}</span>
<span class="nx">F</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="nx">People</span><span class="p">.</span><span class="nx">prototype</span><span class="p">;</span>
<span class="nx">Student</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">F</span><span class="p">();</span>
</code></pre></div></div>
<p>这样就可以Student的原型上扩展子构造函数的方法,同时不影响父构造函数的原形了。</p>
<p>在修复一下constructor属性就ok啦</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">Student</span><span class="p">.</span><span class="nx">prorotype</span><span class="p">.</span><span class="kd">constructor</span> <span class="o">=</span> <span class="nx">Student</span><span class="p">;</span>
</code></pre></div></div>
<h2 id="圣杯">圣杯</h2>
<p>我们将上面的几种方法综合起来,代码看起来就像下面这样子:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//继承函数</span>
<span class="kd">function</span> <span class="nx">inherit</span><span class="p">(</span><span class="nx">C</span><span class="p">,</span> <span class="nx">P</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">F</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(){};</span>
<span class="nx">F</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="nx">P</span><span class="p">.</span><span class="nx">prototype</span><span class="p">;</span>
<span class="nx">C</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">F</span><span class="p">();</span><span class="c1">//临时构造函数</span>
<span class="nx">C</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="kd">constructor</span> <span class="o">=</span> <span class="nx">C</span><span class="p">;</span><span class="c1">//修复constructor</span>
<span class="nx">C</span><span class="p">.</span><span class="nx">uper</span> <span class="o">=</span> <span class="nx">P</span><span class="p">;</span><span class="c1">//存储超类</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">People</span><span class="p">(</span><span class="nx">age</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">age</span> <span class="o">=</span> <span class="nx">age</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">People</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getAge</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(){</span><span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">age</span><span class="p">;};</span>
<span class="kd">function</span> <span class="nx">Student</span><span class="p">(</span><span class="nx">age</span><span class="p">,</span> <span class="nx">num</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">Student</span><span class="p">.</span><span class="nx">uber</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">age</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">num</span> <span class="o">=</span> <span class="nx">num</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">inherit</span><span class="p">(</span><span class="nx">Student</span><span class="p">,</span> <span class="nx">People</span><span class="p">);</span><span class="c1">//继承父构造函数</span>
<span class="nx">Student</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getNum</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span><span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">num</span><span class="p">;};</span>
<span class="kd">var</span> <span class="nx">s</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Student</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span>
<span class="nx">s</span><span class="p">.</span><span class="nx">getNum</span><span class="p">();</span> <span class="c1">// 100</span>
<span class="nx">s</span><span class="p">.</span><span class="nx">getAge</span><span class="p">();</span> <span class="c1">// 20</span>
</code></pre></div></div>
<p>如果你的环境支持ES5,对于ie等可以引入es5shim,那么可以使用Object.create来实现同样的功能</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">inherit</span><span class="p">(</span><span class="nx">C</span><span class="p">,</span> <span class="nx">P</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 等同于临时构造函数</span>
<span class="nx">C</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">P</span><span class="p">.</span><span class="nx">prototype</span><span class="p">);</span>
<span class="nx">C</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="kd">constructor</span> <span class="o">=</span> <span class="nx">C</span><span class="p">;</span> <span class="c1">// 修复constructor</span>
<span class="nx">C</span><span class="p">.</span><span class="nx">uper</span> <span class="o">=</span> <span class="nx">P</span><span class="p">;</span><span class="c1">//存储超类</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="圣杯的遗失">圣杯的遗失</h2>
<p>上面的圣杯模式接近完美了,但却漏了一点,就是类的静态属性集成的问题,举个例子</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">People</span><span class="p">.</span><span class="nx">say</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">word</span><span class="p">)</span> <span class="p">{</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">word</span><span class="p">)};</span>
<span class="nx">inherit</span><span class="p">(</span><span class="nx">Student</span><span class="p">,</span> <span class="nx">People</span><span class="p">);</span> <span class="c1">// 继承父构造函数</span>
<span class="nx">Student</span><span class="p">.</span><span class="nx">say</span><span class="p">()</span> <span class="c1">// Student应该可以继承People的静态方法,但目前还没能实现</span>
</code></pre></div></div>
<p>下面给出解决办法</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//继承函数</span>
<span class="kd">function</span> <span class="nx">inherit</span><span class="p">(</span><span class="nx">C</span><span class="p">,</span> <span class="nx">P</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 等同于临时构造函数</span>
<span class="nx">C</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">P</span><span class="p">.</span><span class="nx">prototype</span><span class="p">);</span>
<span class="nx">C</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="kd">constructor</span> <span class="o">=</span> <span class="nx">C</span><span class="p">;</span><span class="c1">//修复constructor</span>
<span class="nx">C</span><span class="p">.</span><span class="nx">uper</span> <span class="o">=</span> <span class="nx">P</span><span class="p">;</span><span class="c1">//存储超类</span>
<span class="c1">// 静态属性继承,慎用,有坑</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">setPrototypeOf</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// setPrototypeOf es6</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">setPrototypeOf</span><span class="p">(</span><span class="nx">C</span><span class="p">,</span> <span class="nx">P</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">C</span><span class="p">.</span><span class="nx">__proto__</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// __proto__ es6引入,但是部分浏览器早已支持</span>
<span class="nx">C</span><span class="p">.</span><span class="nx">__proto__</span> <span class="o">=</span> <span class="nx">P</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// 兼容ie10-等陈旧浏览器</span>
<span class="c1">// 将P上的静态属性和方法拷贝一份到C上,不会覆盖C上的方法</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">k</span> <span class="k">in</span> <span class="nx">P</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">P</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">k</span><span class="p">)</span> <span class="o">&&</span> <span class="o">!</span><span class="p">(</span><span class="nx">k</span> <span class="k">in</span> <span class="nx">C</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">C</span><span class="p">[</span><span class="nx">k</span><span class="p">]</span> <span class="o">=</span> <span class="nx">P</span><span class="p">[</span><span class="nx">k</span><span class="p">];</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>上面静态属性继承的问题,在于在陈旧浏览器中,属性和方法的继承是静态拷贝的,继承完后续父类的改动不会自动同步到子类,这是一个坑</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">People</span><span class="p">.</span><span class="nx">say</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">word</span><span class="p">)</span> <span class="p">{</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">word</span><span class="p">)};</span>
<span class="nx">inherit</span><span class="p">(</span><span class="nx">Student</span><span class="p">,</span> <span class="nx">People</span><span class="p">);</span><span class="c1">//继承父构造函数</span>
<span class="nx">People</span><span class="p">.</span><span class="nx">say</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">word</span><span class="p">)</span> <span class="p">{</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">word</span> <span class="o">+</span> <span class="mi">123</span><span class="p">)};</span>
<span class="nx">Student</span><span class="p">.</span><span class="nx">say</span><span class="p">(</span><span class="dl">'</span><span class="s1">abc</span><span class="dl">'</span><span class="p">)</span> <span class="c1">// abc 而不是 abc123</span>
</code></pre></div></div>
<h2 id="后es6时代的圣杯">后ES6时代的圣杯</h2>
<p>ES6带来了原生class,从此继承这件事终于有了简单的写法</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">People</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">(</span><span class="nx">age</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">age</span> <span class="o">=</span> <span class="nx">age</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">getAge</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">age</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">Student</span> <span class="kd">extends</span> <span class="nx">People</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">(</span><span class="nx">age</span><span class="p">,</span> <span class="nx">num</span><span class="p">)</span> <span class="p">{</span>
<span class="k">super</span><span class="p">(</span><span class="nx">age</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">num</span> <span class="o">=</span> <span class="nx">num</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">getNum</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">num</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">s</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Student</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="mi">100</span><span class="p">);</span>
<span class="nx">s</span><span class="p">.</span><span class="nx">getNum</span><span class="p">();</span> <span class="c1">// 100</span>
<span class="nx">s</span><span class="p">.</span><span class="nx">getAge</span><span class="p">();</span> <span class="c1">// 20</span>
</code></pre></div></div>
<p>但是上面的写法需要支持ES6语法的浏览器才可以执行,在旧版本浏览器中,可以使用Babel来编译代码,同样需要注意的Babel编译结果对静态类的实现中没上上面方法中最后的else分支,也就是在旧浏览器中静态属性不会继承</p>
<p>如果你使用Babel,并且需要兼容成旧浏览器,最好的做法是避免静态属性继承,或者硬编码,直接引用父类是更好的做法</p>
<h2 id="总结">总结</h2>
<p>我将本文的代码总结了一个js仓库,感兴趣的可以看<a href="https://github.com/yanhaijing/inherits.js">这里</a>。</p>
<p>本文大部分内容其实出自,《<a href="http://www.amazon.cn/gp/product/B008QTG1HS/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=536&creative=3200&creativeASIN=B008QTG1HS&linkCode=as2&tag=yanhaijing-23">javascript模式</a>》第五章 代码的复用模式。记录下来省的自己每次都要去翻书了,当然主要还是写给MM看的。</p>
<p>本文仅仅讲述了继承相关的内容,关于JavaScript面向对象相关的内容,建议阅读下面的文章:</p>
<ul>
<li><a href="http://yanhaijing.com/javascript/2014/05/15/a-code-explain-javascript-oop/">一段代码详解JavaScript面向对象</a></li>
<li><a href="http://yanhaijing.com/javascript/2014/07/18/javascript-prototype/">JavaScript原型之路</a></li>
<li><a href="http://yanhaijing.com/javascript/2016/07/24/prototype-and-inheritance-of-js/">详解JavaScript中的原型和继承</a></li>
<li><a href="http://yanhaijing.com/javascript/2013/08/23/javascript-inheritance-how-to-shoot-yourself-in-the-foot-with-prototypes/">Javascript继承-原型的陷阱</a></li>
</ul>
给你的网站添加 console.js
2014-11-03T00:00:00+00:00
http://yanhaijing.com/javascript/2014/11/03/use-console.js
<p>本文仅先给使用console调试的FE同学,如果你还不知道console是什么,或者还停留在<code class="language-plaintext highlighter-rouge">alert</code>阶段,那就不要浪费时间了,say bye bye!</p>
<p>你是否试程序的过程中用过<code class="language-plaintext highlighter-rouge">console.log(***)</code>,发现在现代浏览器里运行好好的,到了ie里却出现莫名其妙的错误,你完全不知道为什么。</p>
<p>或者你知道在ie下console不能使用,每次上线前都要注释掉console的代码,一不小心漏掉了一个。</p>
<p>如果有过上面类似的情况,和我有着同样的烦恼,那恭喜你,console.js就是为你准备的。(如果你用着非常牛逼的自动化工具,能自动过滤掉console的话,往下看下也是会有收获的)</p>
<p>你还在写类似下面这样的代码吗?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (console && console.log) {
console.log(***);
}
</code></pre></div></div>
<p>或者</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>console.log = console.log || function () {}
</code></pre></div></div>
<p>那么是时候做出改变了,console.js会帮你解决这些问题。</p>
<h2 id="consolejs是什么">console.js是什么</h2>
<p>console.js是一个微型js库,用来修复在不支持或部分支持console的浏览器下,调用<code class="language-plaintext highlighter-rouge">console.***</code>出错的问题。</p>
<p>这其实有点类似reset.css或者html5shim的做法,console.js参考了<a href="http://msdn.microsoft.com/en-us/library/ie/gg589530.aspx">MSDN</a> <a href="https://developer.mozilla.org/en-US/docs/Web/API/Console">MDN</a> <a href="http://getfirebug.com/wiki/index.php/Console_API">Firebug</a>三个文档对console的介绍。是其中提到api的超集。</p>
<p>console.js的全部代码如下,这么简单的代码,还是老规矩不解释:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>;(function(g) {
'use strict';
var _console = g.console || {};
var methods = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'exception', 'error', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn'];
var console = {version: '0.1.0'};
var key;
for(var i = 0, len = methods.length; i < len; i++) {
key = methods[i];
console[key] = function (key) {
return function () {
if (typeof _console[key] === 'undefined') {
return 0;
}
Function.prototype.apply.call(_console[key], _console, arguments);
};
}(key);
}
g.console = console;
}(window));
</code></pre></div></div>
<p><strong>小贴士:</strong>你知道最前面的分号是干嘛用的吗?</p>
<p>其实是为了防止自动化工具拼接js时,前面的js的结尾处忘记了加分号,然后拼接出来的代码就挂了。属于防御式编程。</p>
<p>例如a.js和b.js代码如下:</p>
<p>a.js</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function () {
//...
}())
</code></pre></div></div>
<p>b.js</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function () {
//...
}());
</code></pre></div></div>
<p>上面的代码被合后就会变为</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function () {
//...
}())
(function () {
//...
}());
</code></pre></div></div>
<p>这段代码执行时就会报错了,穿插一个小知识点,太小了,无法自成文章。</p>
<p>更多信息请参考console.js的<a href="https://github.com/yanhaijing/console.js#readme">文档</a>。</p>
<h2 id="仅此而已了吗">仅此而已了吗?</h2>
<p>我一直在思考还可以做哪些改进,或者功能,仅此而已了吗?当然不是,我能想到的还可以做下面的一些改进。</p>
<p>增加一个对原始console的访问接口,类似jq的noConflict,或者在现在的console上加一个对原来console的引用。</p>
<p>增加对域名的过滤功能,比如我们可能只希望log信息在调试的时候输出,而在线上时不做输出。</p>
<p>目前对不支持的接口仅简单赋值为空函数,可考虑对不支持的接口做模拟,对不支持console功能的浏览器,提供自定义模拟console。</p>
<p>当然这些功能是否应该加入console.js,是个问题,应该思考下,console.js的初衷是什么。。。</p>
<h2 id="不足">不足</h2>
<p>对于ie8 9浏览器,在首次打开控制台时,会新建console对象,现在console.js,尽在页面载入时做修复,无法解决这个问题。</p>
<p>但对于打开控制台的人,绝大多数应该不属于用户吧。($ _ $)</p>
<h2 id="总结">总结</h2>
<p>console.js 与console就想html5shim于html5,仅此而已,如此简单。</p>
<p>求个star(⊙o⊙),github地址:<a href="https://github.com/yanhaijing/console.js">https://github.com/yanhaijing/console.js</a></p>
我的git笔记
2014-11-01T00:00:00+00:00
http://yanhaijing.com/git/2014/11/01/my-git-note
<p>转眼间加入git的阵营已经快两年了,结识git,缘起github,2年前在寻找代码托管网站,当时还是用svn,起初使用google code,可是google的服务虽好,在天朝你懂得,后来发现了github,多亏了蒋鑫老师的《<a href="http://www.worldhello.net/gotgithub/">GotGitHub</a>》将我带入github的大门,如果你是个github新手,那我强烈建议你阅读这篇文章,里面讲了很多东西。</p>
<p><img src="/blog/242.png" alt="" /></p>
<p>起初的时候我是用github for windows这个客户端,在切换到多分支的时候被,自动转换换行符坑的不浅,后来越来阅读了《<a href="http://git-scm.com/book/zh/v1">Pro Git</a>》第一版,对git的了解深入了一步,并开始转到命令行上来,如今我在<a href="https://github.com/yanhaijing">github</a>上开源了70几个库,借助git,可自由在这些项目之间穿梭,同时还维护了github家园的<a href="http://weibo.com/githubchina/">微博</a>和Q群(193091696),如果你想获取关于git和github的最新消息可以关注微博,如果你有什么疑问或者问题,欢迎加群一起讨论。</p>
<p>这篇文章记录个人常用的一些命令,和记不住的一些命令。</p>
<p><img src="/blog/146.png" alt="" /></p>
<h2 id="安装">安装</h2>
<p>在 Windows 上安装 Git 同样轻松,有个叫做 msysGit 的项目提供了安装包:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://git-for-windows.github.io/
</code></pre></div></div>
<p>完成安装之后,就可以使用命令行的 git 工具(已经自带了 ssh 客户端)了,另外还有一个图形界面的 Git 项目管理工具。</p>
<h2 id="配置">配置</h2>
<p>首先是配置帐号信息</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git config -e [--global] # 编辑Git配置文件
git config --global user.name yanhaijing
git config --global user.email yanhaijing@yeah.net
git config --list #查看配置的信息
git help config #获取帮助信息
</code></pre></div></div>
<p>配置自动换行(自动转换坑太大)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git config --global core.autocrlf input #提交到git是自动将换行符转换为lf
</code></pre></div></div>
<p>配置密钥</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh-keygen -t rsa -C yanhaijing@yeah.net #生成密钥
ssh -T git@github.com #测试是否成功
</code></pre></div></div>
<p>配置别名,git的命令没有自动完成功能,有点坑哈,别名派上了用场</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git config --global alias.st status #git st
git config --global alias.co checkout #git co
git config --global alias.br branch #git br
git config --global alias.ci commit #git ci
git config --global alias.ll commit #git ci
</code></pre></div></div>
<p>笔者一般只配置配置这几个,你也可以配置其他命令。</p>
<p>我遇到一个问题,就是我工作中的邮箱和自己的邮箱不一样,这就很苦恼了,一个程序员绝不接受一个仓库一个仓库的配置吧,在网上 搜了很多方法都太繁琐。。。好在Git 2.13引入了一个新功能——条件配置,能够很简单的解决这个问题,详情请看<a href="https://blog.github.com/2017-05-10-git-2-13-has-been-released/">这里</a></p>
<p>简单来说就是现在可以指定不同的目录使用不同的配置文件,这个功能简直太赞了,下面通过我的需求来举个例子,我自己的代码都位于gihtub目录,工作的代码位于work目录</p>
<p>首先需要修改gitconfig文件,一般都是修改自己账户下的gitconfig</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vi ~/.gitconfig
</code></pre></div></div>
<p>在最下面添加如下的代码<code class="language-plaintext highlighter-rouge">includeIf</code>,效果就是work目录下会引用path中的配置文件</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[user]
name = 颜海镜
email = yanhaijing@yeah.net
[includeIf "gitdir:~/work"]
path = .gitconfig-work
</code></pre></div></div>
<p>然后在添加gitconfig-work文件</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vi ~/.gitconfig-work
</code></pre></div></div>
<p>.gitconfig-work文件内容如下</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[user]
name = 颜海镜
email = yanhaijing@meituan.com
</code></pre></div></div>
<p>配置好后,在work目录就会使用工作的邮箱,笑脸</p>
<h2 id="新建仓库">新建仓库</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git init #初始化
git status #获取状态
git add [file1] [file2] ... #.或*代表全部添加
git commit -m "message" #此处注意乱码
git remote add origin git@github.com:yanhaijing/test.git #添加源
git push -u origin master #push同事设置默认跟踪分支
</code></pre></div></div>
<h2 id="从现有仓库克隆">从现有仓库克隆</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone git://github.com/yanhaijing/data.js.git
git clone git://github.com/schacon/grit.git mypro#克隆到自定义文件夹
</code></pre></div></div>
<h2 id="本地">本地</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git add * # 跟踪新文件
git add -u [path] # 添加[指定路径下]已跟踪文件
rm *&git rm * # 移除文件
git rm -f * # 移除文件
git rm --cached * # 停止追踪指定文件,但该文件会保留在工作区
git mv file_from file_to # 重命名跟踪文件
git log # 查看提交记录
git commit # 提交更新
git commit [file1] [file2] ... # 提交指定文件
git commit -m 'message'
git commit -a # 跳过使用暂存区域,把所有已经跟踪过的文件暂存起来一并提交
git commit --amend#修改最后一次提交
git commit -v # 提交时显示所有diff信息
git reset HEAD *#取消已经暂存的文件
git reset --mixed HEAD *#同上
git reset --soft HEAD *#重置到指定状态,不会修改索引区和工作树
git reset --hard HEAD *#重置到指定状态,会修改索引区和工作树
git reset -- files#重置index区文件
git revert HEAD #撤销前一次操作
git revert HEAD~ #撤销前前一次操作
git revert commit ## 撤销指定操作
git checkout -- file#取消对文件的修改(从暂存区——覆盖worktree file)
git checkout branch|tag|commit -- file_name#从仓库取出file覆盖当前分支
git checkout -- .#从暂存区取出文件覆盖工作区
git diff file #查看指定文件的差异
git diff --stat #查看简单的diff结果
git diff #比较Worktree和Index之间的差异
git diff --cached #比较Index和HEAD之间的差异
git diff HEAD #比较Worktree和HEAD之间的差异
git diff branch #比较Worktree和branch之间的差异
git diff branch1 branch2 #比较两次分支之间的差异
git diff commit commit #比较两次提交之间的差异
git log #查看最近的提交日志
git log --pretty=oneline #单行显示提交日志
git log --graph # 图形化显示
git log --abbrev-commit # 显示log id的缩写
git log -num #显示第几条log(倒数)
git log --stat # 显示commit历史,以及每次commit发生变更的文件
git log --follow [file] # 显示某个文件的版本历史,包括文件改名
git log -p [file] # 显示指定文件相关的每一次diff
git stash #将工作区现场(已跟踪文件)储藏起来,等以后恢复后继续工作。
git stash list #查看保存的工作现场
git stash apply #恢复工作现场
git stash drop #删除stash内容
git stash pop #恢复的同时直接删除stash内容
git stash apply stash@{0} #恢复指定的工作现场,当你保存了不只一份工作现场时。
</code></pre></div></div>
<h2 id="分支">分支</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git branch#列出本地分支
git branch -r#列出远端分支
git branch -a#列出所有分支
git branch -v#查看各个分支最后一个提交对象的信息
git branch --merge#查看已经合并到当前分支的分支
git branch --no-merge#查看为合并到当前分支的分支
git branch test#新建test分支
git branch branch [branch|commit|tag] # 从指定位置出新建分支
git branch --track branch remote-branch # 新建一个分支,与指定的远程分支建立追踪关系
git branch -m old new #重命名分支
git branch -d test#删除test分支
git branch -D test#强制删除test分支
git branch --set-upstream dev origin/dev #将本地dev分支与远程dev分支之间建立链接
git checkout test#切换到test分支
git checkout -b test#新建+切换到test分支
git checkout -b test dev#基于dev新建test分支,并切换
git merge test#将test分支合并到当前分支
git merge --squash test ## 合并压缩,将test上的commit压缩为一条
git cherry-pick commit #拣选合并,将commit合并到当前分支
git cherry-pick -n commit #拣选多个提交,合并完后可以继续拣选下一个提交
git rebase master#将master分之上超前的提交,变基到当前分支
git rebase --onto master 169a6 #限制回滚范围,rebase当前分支从169a6以后的提交
git rebase --interactive #交互模式
git rebase --continue# 处理完冲突继续合并
git rebase --skip# 跳过
git rebase --abort# 取消合并
</code></pre></div></div>
<h2 id="远端">远端</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git fetch origin remotebranch[:localbranch]# 从远端拉去分支[到本地指定分支]
git merge origin/branch#合并远端上指定分支
git pull origin remotebranch:localbranch# 拉去远端分支到本地分支
git push origin branch#将当前分支,推送到远端上指定分支
git push origin localbranch:remotebranch#推送本地指定分支,到远端上指定分支
git push origin :remotebranch # 删除远端指定分支
git push origin remotebranch --delete # 删除远程分支
git branch -dr branch # 删除本地和远程分支
git checkout -b [--track] test origin/dev#基于远端dev分支,新建本地test分支[同时设置跟踪]
</code></pre></div></div>
<h2 id="源">源</h2>
<p>git是一个分布式代码管理工具,所以可以支持多个仓库,在git里,服务器上的仓库在本地称之为remote。</p>
<p>个人开发时,多源用的可能不多,但多源其实非常有用。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git remote add origin1 git@github.com:yanhaijing/data.js.git
git remote#显示全部源
git remote -v#显示全部源+详细信息
git remote rename origin1 origin2#重命名
git remote rm origin#删除
git remote show origin#查看指定源的全部信息
</code></pre></div></div>
<h2 id="标签">标签</h2>
<p>当开发到一定阶段时,给程序打标签是非常棒的功能。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git tag#列出现有标签
git tag v0.1 [branch|commit] # [从指定位置]新建标签
git tag -a v0.1 -m 'my version 1.4'#新建带注释标签
git checkout tagname#切换到标签
git push origin v1.5#推送分支到源上
git push origin --tags#一次性推送所有分支
git tag -d v0.1#删除标签
git push origin :refs/tags/v0.1#删除远程标签
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>啊哈!终于总结完了,以后不会的时候,再也不用到处去找了。</p>
<p>其实还有两个最有用的命令还未提到。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git help *#获取命令的帮助信息
git status#获取当前的状态,非常有用,因为git会提示接下来的能做的操作
</code></pre></div></div>
<p>最后再补一个救命的命令吧,如果你不小心删错了东西,就用下面的命令,可以看到你之前操作的id,大部分情况下是可以恢复的,记住git几乎不会删除东西。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git reflog # 显示最近操作的commit id
</code></pre></div></div>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://gitref.org/zh/index.html">Git参考手册</a></li>
<li><a href="http://rogerdudler.github.io/git-guide/index.zh.html">git-简明指南</a></li>
<li><a href="http://marklodato.github.io/visual-git-guide/index-zh-cn.html">图解Git</a></li>
<li><a href="http://git-scm.com/book/zh/">Pro Git</a></li>
<li><a href="http://www-cs-students.stanford.edu/~blynn/gitmagic/intl/zh_cn/">Git Magic</a></li>
<li><a href="http://pcottle.github.io/learnGitBranching/">learnGitBranching</a></li>
<li><a href="http://www.cnblogs.com/1-2-3/archive/2010/07/10/git-use-emeditor.html">让 EmEditor 成为 Git 的编辑器</a></li>
</ul>
我的 Sublime Text 2 笔记
2014-10-24T00:00:00+00:00
http://yanhaijing.com/tool/2014/10/24/my-sublime
<p>作为aptana死忠粉的我,最近由于工作需要最近开始使用sublime,初次使用,就被其秒开的启动速度,简洁的界面设计,无干扰的信息提示所这幅。</p>
<p><img src="/blog/243.png" alt="" /></p>
<p>俗话说,工欲善其事必先利其器,作为码农,在开始编码之前,必须要对自己的工具熟悉,才能事半功倍,所以开始了一番折腾,下面记录下一些笔记。</p>
<h2 id="初始化">初始化</h2>
<p>安装完编辑器后就可以使用了,但我一般会进行一些配置,希望你也按照自己的喜欢进行配置。</p>
<p>下面的配置都是打开Preferences->Setting-Default,在那里面有很多默认配置选项,我们可以在这里改变默认值。</p>
<p>修改显示字体大小,我一般习惯使用14号字</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"font_size": 14, // 默认10
</code></pre></div></div>
<p>保存文件时自动在末尾添加空行(我们的项目有这样的要求)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"ensure_newline_at_eof_on_save": true, // 默认是false
</code></pre></div></div>
<p>默认使用Unix换行符,如果大家使用统一的换行符,会让事情变得简单</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"default_line_ending": "unix", // 默认是system
</code></pre></div></div>
<p>使用空格填充tab键,没有好坏之分,统一就好</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"translate_tabs_to_spaces": true,// 默认是false
</code></pre></div></div>
<p>上面的配置项,如果你是一个完美主义者,可以在用户配置文件中配置,不修改默认配置文件。</p>
<h2 id="快捷键">快捷键</h2>
<p>作为码农,很多时间都是在敲键盘的,所以快捷键是非常重要的,sublime的快捷键非常非常多,很难都记住,按照80/20原则,只有20%是常用的,下面是我常用的快捷键:</p>
<p>语法说明:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">ctrl+x</code> ctrl和x键同时按</li>
<li><code class="language-plaintext highlighter-rouge">x & y</code> x操作后,进行y操作</li>
<li><code class="language-plaintext highlighter-rouge">x | y</code> x操作或y操作</li>
<li><code class="language-plaintext highlighter-rouge">(x)</code> 分组x是一个整体</li>
<li><code class="language-plaintext highlighter-rouge">[x]</code> x是可选操作</li>
<li><code class="language-plaintext highlighter-rouge">x, y</code> x操作, y操作(两个类似操作写到一行)</li>
</ul>
<table class="table table-bordered">
<thead>
<tr><th>快捷键</th><th>功能</th></tr>
</thead>
<tbody>
<tr><td>ctrl+shift+p</td><td>打开命令面板</td></tr>
<tr><td>ctrl+p</td><td>搜索项目中的文件</td></tr>
<tr><td>ctrl+r</td><td>前往Method</td></tr>
<tr><td>ctrl+g</td><td>跳转到第几行</td></tr>
<tr><td>ctrl+k & ctrl+b</td><td>切换侧边栏显示状态</td></tr>
<tr><td>ctrl+shift+(backspace|del)</td><td>(左侧|右侧)全部删除</td></tr>
<tr><td>ctrl+y</td><td>重做或重复</td></tr>
<tr><td>shift+方向键</td><td>移动并选择</td></tr>
<tr><td>ctrl+([|])</td><td>缩进|取消缩进</td></tr>
<tr><td>ctrl+shift+([|])</td><td>(折叠|展开)代码</td></tr>
<tr><td>ctrl+l</td><td>选择行,重复可依次增加选择下一行</td></tr>
<tr><td>ctrl+m</td><td>跳转到对应括号</td></tr>
<tr><td>ctrl+shift+m</td><td>选中括号间的内容</td></tr>
<tr><td>alt+.</td><td>close tag</td></tr>
<tr><td>ctrl+/</td><td>当前行注释状态切换</td></tr>
<tr><td>ctrl+h</td><td>替换</td></tr>
<tr><td>ctrl+[shift]+f</td><td>[全局]查找</td></tr>
<tr><td>ctrl+[shift]+tab, ctrl+pageup, alt+num</td><td>切换tab面板</td></tr>
<tr><td>ctrl+shift+y</td><td>将光标处的表达式计算,对于数学不好的很有用</td></tr>
<tr><td>ctrl+[shift]+v</td><td>[缩进]粘贴</td></tr>
<tr><td>ctrl+d</td><td>选择一个选中项的下一个匹配项</td></tr>
<tr><td>alt+f3</td><td>选择文件中的所有匹配项项</td></tr>
<tr><td>ctrl+shift+’</td><td>选择所有选中项的标签</td></tr>
<tr><td>ctrl+shift+a</td><td>选择当前选中项的父容器,可连续使用</td></tr>
<tr><td>ctrl+shift+(↑|↓)</td><td>(上|下)移动一行</td></tr>
<tr><td>ctrl+shift+d</td><td>复制行或选中项</td></tr>
<tr><td>alt+shift+w</td><td>用标签包裹行或选中项</td></tr>
<tr><td>ctrl+(↑|↓), alt+(↑|↓), alt+shift+(↑|↓)</td><td>(加|减)1, (加|减)10, (加|减)0.1</td></tr>
<tr><td>ctrl+shift+;</td><td>移除未闭合的容器元素</td></tr>
<tr><td>ctrl+j</td><td>合并选中的行(多行边一行)</td></tr>
<tr><td>ctrl+kk</td><td>从光标处删除至行尾</td></tr>
<tr><td>ctrl+shift+k</td><td>删除整行</td></tr>
</tbody>
</table>
<h2 id="插件">插件</h2>
<p>sublime的功能已经很满足大部分需求了,但还是有个别差异化的需求,无法满足,这时候sublime的插件派上用场,先来晒下我的插件。</p>
<p><img src="/blog/141.png" alt="" /></p>
<h3 id="package-control">Package Control</h3>
<p>由于sublime 2本身不带插件,所以要先安装插件管理器(<a href="https://sublime.wbond.net/">Package Control</a>),可以通过在线和离线安装。</p>
<h4 id="在线安装">在线安装</h4>
<p>首先打开控制台,点击sublime的菜单栏 view->show console(或者使用快捷键 ctrl+`)。</p>
<p>现在打开了控制台, 这个控制台有上下两栏, 上面一栏会实时显示sublime执行了什么插件,输出执行结果, 如果你安装的某个插件不能正常运行,应该先在这里看看有没有报错。下面栏是一个输入框,可以运行python代码。</p>
<p><img src="/blog/142.png" alt="" /></p>
<p>我们输入下面的代码点击回车运行, 就能安装好package control了。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>import urllib2,os,hashlib; h = '2915d1851351e5ee549c20394736b442' + '8bc59f460fa1548d1514676163dafc88'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); os.makedirs( ipp ) if not os.path.exists(ipp) else None; urllib2.install_opener( urllib2.build_opener( urllib2.ProxyHandler()) ); by = urllib2.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); open( os.path.join( ipp, pf), 'wb' ).write(by) if dh == h else None; print('Error validating download (got %s instead of %s), please try manual install' % (dh, h) if dh != h else 'Please restart Sublime Text to finish installation')
</code></pre></div></div>
<p>运行结束以后,记得重启编辑器,就能在Preferences中看到 Package Control了。</p>
<p><img src="/blog/143.bmp" alt="" /></p>
<h4 id="离线安装">离线安装</h4>
<p>如果在线安装失败,你可以试试离线安装,前提是需要一个<a href="http://sublime.wbond.net/Package%20Control.sublime-package">Package Control的安装包</a>,将该安装包替换到Sublime Text2的相关路径下即可,这里有两个路径,经验证放置到其中任意一处均可。</p>
<ul>
<li>路径一:打开Sublime Text 2,点击PreFerences –> Browse Packages,进入一个文件夹后返回该文件夹的上一级“Sublime Text2”,找到一个“Installed Packages”文件夹(如果没有该文件夹则创建个新的),将下载好的Package Control的安装包放到“Installed Packages”文件夹下即可。</li>
<li>路径二:Sublime Text 2 的安装路径,“Sublime Text 2\Pristine Packages\”文件夹下。</li>
</ul>
<p>其中路径一是官方推荐的防止路径,详细信息,请看<a href="https://sublime.wbond.net/installation#Simple">这里</a>。</p>
<p>安装完成后,按住 ctrl+shift+p。此时会输出一个输入框,即可安装,删除,更新插件了。</p>
<h3 id="vintage">Vintage</h3>
<p>如果你习惯使用vim,那么可以安装这个插件,这个插件可以让sublime像vim一样。</p>
<h3 id="smarty"><a href="https://packagecontrol.io/packages/Smarty">Smarty</a></h3>
<p>提供smarty语法的支持。Smarty插件默认的分隔符是<code class="language-plaintext highlighter-rouge">{}</code>,如果你使用的分隔符不同可以更改插件目录的Smarty.tmPreferences文件,找到其中的SMARTY_LDELIM和SMARTY_RDELIM,修改为你的分隔符即可。</p>
<ul>
<li><a href="https://github.com/amitsnyderman/sublime-smarty">github</a></li>
</ul>
<h3 id="liquid">Liquid</h3>
<p>提供Liquid语法支持,如果你也写博客的话不妨试试。</p>
<h3 id="css3_syntax">CSS3_Syntax</h3>
<p>对css语法高亮的支持,view-syntax-css3选中css3就能使用css3高亮了。必须每条属性都加上分号,并且属性必须小写,不然不会高亮,有点鸡肋啊。</p>
<h3 id="autoprefixer">Autoprefixer</h3>
<p>可以给css自动加前缀功能</p>
<h3 id="less"><a href="https://github.com/danro/LESS-sublime">LESS</a></h3>
<p>这是一个非常棒的插件,可以让sublime支持less的语法高亮和语法提示,对于搞less的同学灰常重要,不过多解释。</p>
<h3 id="scss">SCSS</h3>
<p>提供sass语法高亮支持,不建议安装SASS,SCSS更适合.scss语法支持。</p>
<h3 id="javascriptnext---es6-syntax">JavaScriptNext - ES6 Syntax</h3>
<p>提供ES6的语法支持。</p>
<h3 id="pretty-json">Pretty JSON</h3>
<p>提供对json文件的美化和格式化功能。</p>
<h3 id="jquery">jQuery</h3>
<p>支持jquery的只能语法提示,很赞。</p>
<h3 id="emmet">Emmet</h3>
<p>Emmet的前身是大名鼎鼎的Zen coding,如果你从事Web前端开发的话,对该插件一定不会陌生。它使用仿CSS选择器的语法来生成代码,大大提高了HTML/CSS代码编写的速度。</p>
<ul>
<li><a href="http://www.iteye.com/news/27580">这里是一篇演示文章</a></li>
<li><a href="http://docs.emmet.io/">Emmet官网</a></li>
</ul>
<h3 id="sublimelinter"><del>SublimeLinter<del></del></del></h3>
<p>可以验证各种语法错误,不多解释。目前这个插件已经不支持sublime2了,我还没找到代替的插件。</p>
<h3 id="docblockr">DocBlockr</h3>
<p>DocBlockr 可以使你很方便地对代码建立文档。它会解析函数,变量,和参数,根据它们自动生成文档范式,你的工作就是去填充对应的说明。</p>
<p><img src="/blog/144.gif" alt="" /></p>
<h3 id="htmlcssjs-prettify">HTML/CSS/JS Prettify</h3>
<p>能够格式化css html 和js。</p>
<p><strong>注意:</strong>格式化的文件路径中不能有中文,不然会报找不到node的错误(windows下)。</p>
<p><img src="/blog/168.png" alt="" /></p>
<h3 id="brackethighlighter">BracketHighlighter</h3>
<p>像这些符号是成对的:花括号{}, 中括号[],括号:() ,引号“” 等。 这些符号当我们鼠标放在开始符号的位置的时候, 希望能明显看到结尾符号在哪儿sublime默认是下划线,很不明显, 想要明显一点,可以安装插件 BracketHighlighter。</p>
<h3 id="gbk-encoding-support">GBK Encoding Support</h3>
<p>这个插件还是非常有用的,因为sublime 本身 不支持gbk编码,在utf8如此流行的今天,我们整站还是gbk编码,o(︶︿︶)o 唉,所以全靠这个插件了。</p>
<h3 id="terminal">Terminal</h3>
<p>可以sublime中,打开命令行,非常方便哦。还可在自定义打开的命令行,比如我就把默认命令行改为了git-bash。只需在设置中进行如下配置即可(注意路径)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"terminal": "D:\\Program Files\\Git\\git-bash.exe"
</code></pre></div></div>
<h3 id="syncedsidebar">SyncedSideBar</h3>
<p>支持当前文件在左侧面板中定位,不错。</p>
<h3 id="clipboard-history">Clipboard History</h3>
<p>可以保存粘贴的历史,很赞的功能,再也不用担心历史丢失了。ctrl+alt+v可打开历史面板,上下选择即可,安装后会和默认的ctrl+shift+v(粘贴缩进)冲突,干掉这个快捷键。</p>
<h3 id="autoprefixer-1">Autoprefixer</h3>
<p>自动添加css前缀,这个也是挺有用的。</p>
<h3 id="autofilename">AutoFileName</h3>
<p>以前用dreamweave的时候在引用文件的时候,可以自动补全文件名的功能,这个插件让sublime有了这个功能。</p>
<h3 id="allautocomplete">AllAutocomplete</h3>
<p>自动完成插件,可在全部打开的文件中,自动完成。</p>
<h3 id="aligment">Aligment</h3>
<p>对齐插件,强迫症患者必备,可以按等号对齐两边的变量。</p>
<h3 id="hexviewer">HexViewer</h3>
<p>提供十六进制文件查看功能。</p>
<h3 id="multieditutils">MultiEditUtils</h3>
<p>扩展多行编辑的功能。</p>
<h3 id="markdown-preview">Markdown Preview</h3>
<p>如果你也喜欢md语法,那么安装这个插件吧,可以很方便的预览。</p>
<h3 id="htmlentity-snippets">HTMLEntity Snippets</h3>
<p>当你想输入html实体标签时,然后又记不住时,使用这个插件吧。</p>
<h3 id="imesupport">IMESupport</h3>
<p>当你发现你的输入法在输入中文的时候输入框不在光标的位置那可能是这个问题,这其实是sublime的一个bug,试试这个吧,这里有一篇文章介绍《<a href="http://qianduanblog.com/post/sublime-text-3-plugin-imesupport.html">sublime text 插件:IMESupport</a>》。</p>
<h3 id="package-syncing">Package Syncing</h3>
<p>最后推荐一个同步插件,这个插件可以在不同的机器同步配置信息和插件,非常方便,但鉴于国内的墙太高,我都是直接把插件给手动备份了,然后直接拖进去,或者直接去github上下载对应的包。</p>
<p><a href="https://github.com/yanhaijing/mysublime">这里</a>是我的Package Syncing 导出来的文件。</p>
<h2 id="总结">总结</h2>
<p>sublime非常棒的,正是我喜欢的风格。</p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://blog.jobbole.com/82527/">Gif多图:我常用的 16 个 Sublime Text 快捷键</a></li>
<li><a href="http://segmentfault.com/a/1190000000505218">12个不可不知的Sublime Text应用技巧和诀窍</a></li>
</ul>
纯CSS3打造七巧板
2014-09-09T00:00:00+00:00
http://yanhaijing.com/css/2014/09/09/15-create-tangram-width-pure-css3
<p>最近项目上要制作一个七巧板,脑子里瞬间闪现,什么。。。七巧板不是小时候玩的吗。。。</p>
<h2 id="七巧板的由来">七巧板的由来</h2>
<p>先来个科普吧,是我在查资料过程中看到的,感觉很有意思。</p>
<p>宋朝有个叫<a href="http://www.baidu.com/s?wd=%E9%BB%84%E4%BC%AF%E6%80%9D&hl_tag=textlink&tn=SE_hldp01350_v6v6zkg6">黄伯思</a>的人,对几何图形很有研究,他热情好客,发明了一种用6张小桌子组成的“宴几”——请客吃饭的小桌子。后来有人把它改进为7张桌组成的宴几,可以根据吃饭人数的不同,把桌子拼成不同的形状,比如3人拼成三角形,4人拼成四方形,6人拼成六方形……这样用餐时人人方便,气氛更好。后来,有人把宴几缩小改变到只有七块板,用它拼图,演变成一种玩具。</p>
<p>因为它十分巧妙好玩,所以人们叫它“七巧板”。</p>
<p>今天,在世界上几乎没有人不知道七巧板和七巧图,它在国外被称为“唐图”(Tangram),意思是来自中国的拼图(不是唐代发明的图)。</p>
<p><img src="http://images.cnitblog.com/blog/460220/201409/111925058246713.png" alt="" /></p>
<p>纳尼,原来Tangram是咱们中国的,。。。</p>
<h2 id="方案">方案</h2>
<p>看完了有趣的东西,该开始正题了,就是无论使用什么技术给我整出个七巧板来。。。(在前端页面里)</p>
<p>结合自己的知识体系,思考了下大概的思路:</p>
<ol>
<li>Canvas,万能的Cavans一定可以解决问题,加上之前做过<a href="http://yanhaijing.com/Painter">Painter</a>。灵活性+扩展性满足。</li>
<li>CSS3,每个版子是一个dom元素,然后使用css3搞定。灵活性 扩展性不如canvas。</li>
<li>svg,这个应该也可以吧,但自己对这方面的知识匮乏。</li>
<li>。。。暂时未想出。</li>
</ol>
<p>考虑到时间成本(太紧了)和其他。。。原因,决定使用css3的方案。</p>
<p>开始想板子使用图片来做,但多亏自己以前写过一篇‘<a href="http://yanhaijing.com/css/2014/04/04/with-css-code-to-write-various-shapes-graphic-method/">用CSS代码写出的各种形状图形的方法</a>’,里面收录了使用CSS3制作20种图形的方法,有兴趣的同学可以看下,查了下可以满足所需的7种板子的形状。</p>
<h2 id="用到属性">用到属性</h2>
<ul>
<li>transform</li>
<li>translation</li>
</ul>
<h2 id="技术验证">技术验证</h2>
<p>开始之前先要验证下,所要用到的CSS3是否可以兼容所要需平台,这多亏http://caniuse.com/。</p>
<p>因为我要运行在移动端,查了下要用到的css3属性,在安卓2.3以上都支持,但需加前缀,所以可以放心使用。</p>
<p><img src="http://images.cnitblog.com/blog/460220/201409/111951154816974.png" alt="" /></p>
<h2 id="编码实现">编码实现</h2>
<p>首先我们需要一个容器和起个元素用来表示七块板子。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div class="wrap">
<div class="t t1 t11"></div>
<div class="t t2 t22"></div>
<div class="t t3 t33"></div>
<div class="t t4 t44"></div>
<div class="t t5 t55"></div>
<div class="t t6 t66"></div>
<div class="t t7 t77"></div>
</div>
</code></pre></div></div>
<p>其实我们总共用到的图形总共有三种,三角形、正方形行和平行四边形。这在上面提到的文章里面全有,这里涉及到的一点点数学的知识,就是这些板子之间是有一定大小关系的,只需一点点数学知识姐可以解决了。</p>
<p>至此板子的表示就不是问题了,其次还需把板子移动到指定位置才可以拼成好看的图形,这全靠css3的 transform搞定,可以实现平移、缩放、旋转、变形多种操作。</p>
<p>这里我们设置wap的position为relative。所有板子都为absolute。并设置top和left为零,这样初始化时所有的板子都位于左上角,然后将板子的transform-origin设为左上角,就实现了定位时的好计算,下面是css部分的代码。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.wrap{
position: relative;
width:300px;
height: 400px;
}
.t{
position: absolute;
top:0;
left:0;
width: 0;
height: 0;
transform-origin:0 0;
}
.t1{
border-top: 212.13203435596425732025330863145px solid red;
border-right: 212.13203435596425732025330863145px solid transparent;
transform: translate(150px, 150px) rotate(-135deg);
}
.t2{
border-top: 212.13203435596425732025330863145px solid #fdaf17;
border-right: 212.13203435596425732025330863145px solid transparent;
transform: translate(150px, 150px) rotate(135deg);
}
.t3{
width: 106.06601717798212866012665431573px;
height: 106.06601717798212866012665431573px;
background: #c3d946;
transform: translate(150px,150px) rotate(45deg);
}
.t4{
border-top: 106.06601717798212866012665431573px solid #00bdd0;
border-right: 106.06601717798212866012665431573px solid transparent;
transform: translate(150px,150px) rotate(-45deg);
}
.t5{
border-top: 106.06601717798212866012665431573px solid #5dbe79;
border-right: 106.06601717798212866012665431573px solid transparent;
transform: translate(75px,225px) rotate(45deg);
}
.t6{
width: 150px;
height: 75px;
transform: translate(300px) rotate(90deg) skew(45deg);
background: #ffdd01;
}
.t7{
border-top: 150px solid #0177bf;
border-right: 150px solid transparent;
transform: translate(300px,300px) rotate(180deg);
}
</code></pre></div></div>
<h2 id="demo">DEMO</h2>
<p>好了,七巧板终于做好了让我们来看下效果吧。</p>
<p><img src="http://images.cnitblog.com/blog/460220/201409/112011249811732.png" alt="" /></p>
<iframe width="100%" height="300" src="http://jsfiddle.net/3tf8ac6q/7/embedded/" allowfullscreen="allowfullscreen" frameborder="0"></iframe>
<h2 id="提高">提高</h2>
<p>仅此而已了吗,当然不是,还有很多可考虑的东西,这里的css都是写死的,灵活性太差,如果用LESS的话(我用的就是less),我们可以设置一个基本长度变量(less支持变量和数学运算哦),然后其他的全部基于这个变量计算,这样只要改变这个变量,就能轻松改变整个七巧板的大小,可扩展性是不是有很大进步(实际上我就是这么做的,但jsfiddle不支持less语法,所以我就写了个css版的)。</p>
<p>其实还可以通过css3变化出多种图形,据说七巧板的可以拼出的图形多大几千种之多。。。快快开动你的脑筋来制作吧,做好了记得给博主留个链接啊。</p>
<p>进一步的提高就是通过CSS3的transtion,制作动画,在配合key-frame,可以制作七巧板变形的复杂动画,效果美轮美奂啊。</p>
<p>这里只贴一个图片吧,不提供代码了。。。</p>
<p><img src="http://images.cnitblog.com/blog/460220/201409/131348130127700.gif" alt="" /></p>
<p> </p>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>CSS3动画详解(http://beiyuu.com/css3-animation/)</li>
<li>腾讯动画手册(http://ecd.tencent.com/css3/guide.html)</li>
</ul>
JavaScript原型之路
2014-07-18T00:00:00+00:00
http://yanhaijing.com/javascript/2014/07/18/javascript-prototype
<h2 id="简介">简介</h2>
<p>最近我在学习Frontend Masters 上的<a href="https://frontendmasters.com/courses/advanced-javascript/">高级JavaScript系列教程</a>,Kyle 带来了他的“OLOO”(对象链接其他对象)概念。这让我想起了Keith Peters 几年前发表的一篇博文,关于<a href="http://www.adobe.com/devnet/html5/articles/javascript-object-creation.html">学习没有“new”的世界</a>,其中解释了使用原型继承代替构造函数。两者都是纯粹的原型编码。</p>
<h2 id="标准方法the-standard-way">标准方法(The Standard Way)</h2>
<p>一直以来,我们学习的在 JavaScript 里创建对象的方法都是创建一个构造函数,然后为函数的原型对象添加方法。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function Animal(name) {
this.name = name;
}
Animal.prototype.getName = function() {
return this.name;
};
</code></pre></div></div>
<p>对于子类的解决方案是,创建一个新的构造函数,并且设置其原型为其父类的原型。调用父类的构造函数,并将this设置为其上下文对象。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function Dog(name) {
Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.speak = function() {
return "woof";
};
var dog = new Dog("Scamp");
console.log(dog.getName() + ' says ' + dog.speak());
</code></pre></div></div>
<h2 id="原型方法the-prototypal-way">原型方法(The Prototypal Way)</h2>
<p>如果你接触过任何原型语言,你会觉得上面的例子看起来很奇怪。我尝试过 <a href="http://iolanguage.org/">IO 语言</a>——一门基于原型的语言。在原型语言中,可以通过克隆对象并添加属性和方法的方式创建一个原型。然后你能克隆刚才创建的原型,从而创建一个可以使用的实例,或者克隆它来创建另一个原型。上面的例子在 IO 里,看起来像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Animal := Object clone
Animal getName := method(name)
Dog := Animal clone
Dog speak := method("woof")
dog := Dog clone
dog name := "Scamp"
writeln(dog getName(), " says ", dog speak())
</code></pre></div></div>
<h2 id="好消息the-good-news">好消息(The Good News)</h2>
<p>在JavaScript中,也可以使用这种编码方式!Object.create 函数和 IO 里的 clone 类似。下面是在JavaScript中,纯原型的实现。除了语法不同之外,和 IO 版本一样。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Animal = Object.create(Object);
Animal.getName = function() {
return this.name;
};
Dog = Object.create(Animal);
Dog.speak = function() {
return "woof";
};
var dog = Object.create(Dog);
dog.name = "Scamp";
console.log(dog.getName() + ' says ' + dog.speak());
</code></pre></div></div>
<h2 id="坏消息the-bad-news">坏消息(The Bad News)</h2>
<p><span class="Apple-converted-space"><span class="Apple-converted-space">当使用构造函数时,JavaScript 引擎会进行优化。在 <a href="http://jsperf.com/proto-vs-ctor/3">JSPerf </a>上测试两个不同的操作,显示基于原型的实现比使用构造函数的方式最多慢90多倍。</span></span></p>
<p><img src="http://jurberg.github.io/images/proto-vs-ctor.png" alt="Perf Graph" /></p>
<p><span class="Apple-converted-space">另外,如果你使用类似 <a href="https://angularjs.org/">Angular </a>的框架,当创建控制器和服务时,必须使用构造函数。</span></p>
<h2 id="引入类enter-classes">引入类(Enter Classes)</h2>
<p>ES6带来了新的 class 语法。但其只是标准构造函数方法的语法糖。新的语法看起来更像 Java 或 c#,但其幕后仍然是创建原型对象。这会让来自基于类语言的人感到迷惑,因为当创建原型时,他们希望类和他们的语言有相同的属性。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>class Animal {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class Dog extends Animal {
constructor(name) {
super(name);
}
speak() {
return "woof";
}
}
var dog = new Dog("Scamp");
console.log(dog.getName() + ' says ' + dog.speak());
</code></pre></div></div>
<h2 id="结论conclusion">结论(Conclusion)</h2>
<p>如果让我选择,我会用纯原型的风格。这更具有表现力,动态和有趣。由于虚拟机会对构造函数方法进行优化,所有框架都会选择构造函数方法,在产品代码中,我会继续使用构造函数。一旦 ES6 变得流行,我希望使用新的类语法代替古老的构造函数方法。</p>
<h2 id="注">注</h2>
<p>英文:http://jurberg.github.io/blog/2014/07/12/javascript-prototype/</p>
基于Grunt构建一个JavaScript库
2014-07-14T00:00:00+00:00
http://yanhaijing.com/tool/2014/07/14/building-a-javascript-library-with-grunt-js
<p>现在公认的JavaScript典型项目需要运行单元测试,合并压缩。有些还会使用代码生成器,代码样式检查或其他构建工具。</p>
<p>Grunt.js是一个开源工具,可以帮助你完成上面的所有步骤。它非常容易扩展,并使用JavaScript书写,所以任何为JavaScript库或项目工作的人都可以按自己的需要扩展它。</p>
<p><img src="/blog/245.png" alt="" /></p>
<p>本文解释如何使用Grunt.js构建JavaScript库。Grunt.js依赖Node.js和npm,所以第一节解释其是什么,如何安装和使用。如果你对npm有了解,那你可以跳过这一节。第四和第五节讲述如何配置Grunt和一系列典型Grunt任务。</p>
<p>本文讨论的代码例子可以在<a href="https://github.com/SomMeri/grunt-tutorial">GitHub</a>上访问。</p>
<h2 id="工具概述tool-chain-overview">工具概述(Tool Chain Overview)</h2>
<p>开始之前,我们需要三个工具:</p>
<ul>
<li><a href="http://nodejs.org/">Node.js</a>,</li>
<li><a href="http://npmjs.org/">Npm</a>,</li>
<li><a href="http://gruntjs.com/">Grunt.js</a>.</li>
</ul>
<p>Node.js是一个流行的服务器端JavaScript环境。它被用来编写和运行JavaScript服务和JavaScript命令行工具。如果你想进一步链接Node.js,你可以查看<a href="http://stackoverflow.com/a/5511507">Stack Overflow</a>上相关的资料。</p>
<p>Npm是Node.js的包管理工具。它能从中心仓库下载依赖,解决大部分依赖冲突问题。Npm仓库仅仅存储Node.js服务器段和命令行项目。它不包含用于web和移动app相关的库。我们用它来下载Grunt.js。</p>
<p>Grunt.js是一个任务运行工具,我们用起构建我们的项目。它在Node.js之上运行并且通过Npm安装。</p>
<h3 id="安装nodejs和npmnodejs-and-npm-installation">安装Node.js和Npm(Node.js and Npm Installation)</h3>
<p>你可以直接从<a href="http://nodejs.org/download/">下载页面</a>或用<a href="https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager">其它包管理工具</a>安装node.js。安装成功后在命令行输入 node -v Node.js将输出它的版本号。</p>
<p>大部分安装器和包管理工具将同时安装Npm。在命令行输入 npm -v 测试是否安装成功。如果成功将输出它的版本号。不同的系统可能安装方式略有不同。</p>
<h4 id="linux">Linux</h4>
<p>下载和使用<a href="https://npmjs.org/install.sh">安装脚本</a>。</p>
<h4 id="windows">Windows</h4>
<p>windows安装器包含npm并会添加path变量。仅在你下载Node.exe或从源代码编译Node是才需要独立安装Npm。从<a href="http://nodejs.org/dist/npm/">这里</a>下载Npm的最新版zip压缩包。解压后赋值到Node.exe的安装目录。如果你愿意,你也可以放到任何位置,将其加入path变量即可。</p>
<h4 id="osx">OSX</h4>
<p>安装包中内建Npm。</p>
<h2 id="npm基础npm-basics">Npm基础(Npm Basics)</h2>
<p>了解Npm基础操作对于使用和安装Grunt.js都有帮助。这节仅包含接触知识。更多细节可以查看<a href="https://npmjs.org/doc/">npm文档</a>。</p>
<p>本节将解释下面的东西:</p>
<ul>
<li>什么是npm;</li>
<li>npm插件本地安装和全局安装的区别;</li>
<li>package.json文件和其规范;</li>
<li>npm安装命令。</li>
</ul>
<h3 id="概要overview">概要(Overview)</h3>
<p>Npm是一个包管理工具,可以从中心仓库下载和安装JavaScript依赖。安装包能被用在Node.js项目或命令行工具。</p>
<p>项目通常在package.json文件内部列出其依赖和安装插件。此外npm库也可以从命令行安装。</p>
<h3 id="全局安装vs本地安装global-vs-local-installation">全局安装vs本地安装(Global vs Local Installation)</h3>
<p>每个包可以安装在全局或本地环境。实际的区别是存储位置和访问方式。</p>
<p>全局安装包被直接存储在Node.js安装路径。他们之所以被称为全局,是因为他们可以在任何地方直接访问。</p>
<p>本地安装将下载包安装在当前工作路径。本地安装包只能从其所在目录访问。</p>
<p>本地安装包被存储进node_mudules子目录。无论你使用什么版本控制系统,你可以将其添加仅.ignorefile 文件。</p>
<h3 id="packagejson">Package.json</h3>
<p>package.json文件包含npm项目描述。它总是位于项目的根目录,并且包含项目名称,版本,协议和其他类似元数据。最重要的是,它包含两个项目依赖列表。</p>
<p> 第一个列表包含运行所需要的依赖。任何希望使用此项目的人必须安装它们。第二个列表包含开发时需要依赖项。包括测试工具,构建工具和代码样式检测工具。</p>
<p>创建package.json最简单的方法是通过 npm install 命令。这条命令会以交互式提问一系列问题,并根据回答在当前工作目录生成基本的package.json文件。只有名字(name)和版本(version)属性是必须的。如果你不打算将你的库发布到Npm,你能忽略其余的部分。</p>
<p>下面的链接包含对package.json的详细描述:</p>
<ul>
<li><a href="http://package.json.nodejitsu.com/">package.json interactive guide</a>;</li>
<li><a href="http://docs.nodejitsu.com/articles/getting-started/npm/what-is-the-file-package-json">package.json tutorial on nodejitsu</a>;</li>
<li><a href="https://npmjs.org/doc/json.html">official documentation</a>.</li>
</ul>
<h3 id="安装命令the-install-command">安装命令(The Install Command)</h3>
<p>Npm宝可以通过npm install 命令安装。默认安装到本地。全局安装需要指定 -g开关。</p>
<p>不带参数的 npm install 将在当前目录或上层目录查找 package.json 文件。如果发现,将会在当前目录安装所有列出的依赖项。</p>
<p>可以通过 npm install <pkg_name@version> 命令安装具体的npm包。这条命令将从中心仓库找到指定版本的包,并将其安装到当前目录。</p>
<p>版本号是可选的。如果省略将下载最新稳定版。</p>
<p>最后,通过 –sace-dev开关不仅可以安装包,还会将其添加到 package.json 的开发依赖中。</p>
<h2 id="为项目添加gruntjsadding-gruntjs-to-the-project">为项目添加Grunt.js(Adding Grunt.js to the Project)</h2>
<p>我们将首先将Grunt.js添加进我们的JavaScript项目。为此我们需要安装两个Grunt.js模块:</p>
<ul>
<li>grunt-cli - 命令行接口 (CLI);</li>
<li>grunt - 任务运行器.</li>
</ul>
<p><em>提醒:最新的Grunt.js(4.0)不再兼容以前的版本。一些老的教程和文档不再适合新版Grunt.js了。</em></p>
<h3 id="概论overview">概论(Overview)</h3>
<p>所有实际的工作是由任务运行器来做。命令行接口仅解析参数和将其传递个任务运行器。如果任务运行器没有安装将不会做任何事情。</p>
<p>命令行接口应该被安装在全局环境,然而任务运行器在本地环境。全局命令行接口保证Grunt命令可以在所有路径访问。任务运行器必须是本地的,因为不同的项目可能需要不同的Grunt版本。</p>
<h3 id="安装installation">安装(Installation)</h3>
<p>安装全局Grunt命令行接口:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install -g grunt-cli
</code></pre></div></div>
<p>切换到项目根目录,通过npm init让Npm帮你生成package.json文件。它会问你些问题然后根据你的回答生成合法的package.json文件。只有名字和版本是必须的;你可以忽略其他选项。</p>
<p>将Grunt.js最新版添加到本地环境,同时添加到package.json文件的开发依赖里。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install grunt --save-dev
</code></pre></div></div>
<h3 id="packagejson-1">Package.json</h3>
<p>通过前面的命令创建的package.json文件应该类似相面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
"name": "gruntdemo",
"version": "0.0.0",
"description": "Demo project using grunt.js.",
"main": "src/gruntdemo.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": "",
"author": "Meri",
"license": "BSD",
"devDependencies": {
"grunt": "~0.4.1"
}
}
</code></pre></div></div>
<h2 id="配置gruntjsconfigure-gruntjs">配置Grunt.js(Configure Grunt.js)</h2>
<p>Grunt.js运行任务并且通过任务完成工作。然而,Grunt.js安装成功后还没有任务可以使用。任务必须从插件载入,而插件通常需要Npm安装。</p>
<p>我们使用五个插件:</p>
<ul>
<li><strong>grunt-contrib-concat</strong> - 拼接文件,</li>
<li><strong>grunt-contrib-uglify</strong> - 拼接并压缩文件(js),</li>
<li><strong>grunt-contrib-copy</strong> - 复制文件,</li>
<li><strong>grunt-contrib-qunit</strong> - 运行单元测试,</li>
<li><strong>grunt-contrib-jshint</strong> - 检查bug和JavaScript代码风格.</li>
</ul>
<p>本节将解释如何配置这些插件。我们从最简单的配置开始,然后一步步解释如何配置任务。余下的子章节将详细讲解如何配置每个插件。</p>
<h3 id="基础-不做任何配置basic-do-nothing-configuration">基础-不做任何配置(Basic Do Nothing Configuration)</h3>
<p>Grunt将配置信息写到Gruntfile.js或Gruntfile.coffee文件里。由于我们创建的JavaScript项目,我们将使用JavaScript版本。最简单的Gruntfile.js看起来像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//包装函数 有一个参数
module.exports = function(grunt) {
// 默认任务。在本例子中没有任何操作。
grunt.registerTask('default', []);
};
</code></pre></div></div>
<p>配置信息被保存在module.exports函数内部。它包含grunt对象作为其参数,并且通过调用该函数完成配置。</p>
<p>配置函数必须创建至少一个任务别名,并且配置他们。例如,上面的代码片段创建了一个“default”任务别名并且指定为空的任务列表。换句话说,默认的任务别名可以工作,但不会做任何事情。</p>
<p>用 grunt <taskAlias> 命令运行指定的 taskAlias任务。taskAlias的参数是可选的,如果省略,Grunt将使用“default”任务。</p>
<p>保存Gruntfile.js文件,在命令行运行Grunt:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grunt
</code></pre></div></div>
<p>你应该看到如下输出:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Done, without errors.
</code></pre></div></div>
<p>如果配置任务返回错误或警告Grunt将发出蜂鸣声(在命令行下输入错误的声音,译者未发现这点)。如果你不想听到蜂鸣声,你可以使用 -no-color 参数:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grunt -no-color
</code></pre></div></div>
<h3 id="grunt-npm-任务grunt-npm-tasks">Grunt Npm 任务(Grunt Npm Tasks)</h3>
<p>从插件添加任务是通过用步骤,对所有插件都是相同的。本节将概要讲述需要的过程,实际的例子将在下面的章节讲解。</p>
<h4 id="安装插件install-the-plugin">安装插件(Install the Plugin)</h4>
<p>首先,我们需要将插件添加进package.json文件的开发依赖里面,并且使用Npm进行安装:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install <plugin name> --save-dev
</code></pre></div></div>
<h4 id="配置任务configure-tasks">配置任务(Configure Tasks)</h4>
<p>任务配置必须被存储在一个对象内部,有各自的任务名,并且被传递给 grunt.initConfig方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>module.exports = function(grunt) {
grunt.initConfig({
firstTask : { /* ... 配置第一个任务 ... */ },
secondTask : { /* ... 配置第二个任务 ... */ },
// ... 其他任务 ...
lastTask : { /* ... 最后一个任务 ... */ }
});
// ... the rest ...
};
</code></pre></div></div>
<p>全面的任务配置信息解释看这里<a href="http://gruntjs.com/configuring-tasks">Grunt.js文档</a>。本节仅描述最通用,简单的例子。假设任务接受一个文件列表,并处理他们,然后生出输出文件。</p>
<p>一个简单的任务配置例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>firstTask: {
options: {
someOption: value //取决于插件
},
target: {
src: ['src/file1.js', 'src/file2.js'], //输入文件
dest: 'dist/output.js' // 输出文件
}
}
</code></pre></div></div>
<p>例子中的任务配置有两个属性。一个是任务选项,名称必须是”options“。Grunt.js不会对options的属性执行任何操作,其行为有插件决定。</p>
<p>其他项可以有任何名字,并且要包含任务目标。最常见的任务是操作和生成文件,所以他们的target有两个属性,”src“ 和 ”dest“。src包含输入的文件列表,dest包含输出的文件名字。</p>
<p>如果你配置多个任务,Grunt将依次执行。下面的任务将运行两次,一次操作src及其子目录的所有js文件,另一次操作test及其子目录下的所有js文件:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>multipleTargetsTask: {
target1: { src: ['src/**/*.js'] },
target2: { src: ['test/**/*.js']] }
}
</code></pre></div></div>
<h4 id="加载和注册任务load-and-register-tasks">加载和注册任务(Load and Register Tasks)</h4>
<p>最后,将插件载入必须使用 grunt.loadNpmTasks 函数,并且注册任务别名。</p>
<p>上面介绍的结构合起来如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>module.exports = function(grunt) {
grunt.initConfig({ /* ... tasks configuration ... */ });
grunt.loadNpmTasks('grunt-plugin-name');
grunt.registerTask('default', ['firstTask', 'secondTask', ...]);
};
</code></pre></div></div>
<h3 id="配置jshintconfigure-jshint">配置JSHint(Configure JSHint)</h3>
<p><a href="http://www.jshint.com/">JSHint</a>检查JavaScript代码中潜在的问题和错误。他被设计成可配置的,并且有合理的默认值。</p>
<p>我们将使用 <a href="https://github.com/gruntjs/grunt-contrib-jshint#readme">grunt-contrib-jshint</a> 插件,grunt-contrib开头的插件都是有Grunt官方维护的,如果你创建自己的插件千万不要以次开头。</p>
<h4 id="安装插件install-the-plugin-1">安装插件(Install the Plugin)</h4>
<p>打开命令行,在项目根目录运行 npm install grunt-contrib-jshint –save-dev。将会添加插件到package.json文件的开发依赖,并且安装到本地Npm仓库。</p>
<h4 id="jshint参数jshint-options">JSHint参数(JSHint Options)</h4>
<p>grunt-contrib-jshint插件的参数和JSHint一样。完整的参数列表可以访问<a href="http://www.jshint.com/docs/">JSHint的文档页面</a>。</p>
<p>JSHint 的参数 “eqeqeq” 会将 == 和 != 操作符报告为警告。默认是关闭的,因为这些操作符是合法的。但我建议你开启它,因为严格相等比非严格相等更安全。</p>
<p>同时我建议你开启trailing选项,将会对代码中结尾部的空白元素生成警告。结尾的空白在多行字符串中会引起奇怪的问题。</p>
<p>每一个可选项都是布尔值,设置为true将会开启相应检测。下面的例子开启了eqeqeq和trailing选项:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>options: {
eqeqeq: true,
trailing: true
}
</code></pre></div></div>
<h4 id="配置jshint任务configure-the-jshint-task">配置JSHint任务(Configure the JSHint Task)</h4>
<p>grunt-contrib-jshint插件的任务名字是“jshint”。我们将使用上一节中的配置选项,使它检测位于src和test目录下的全部JavaScript文件。</p>
<p>JSHint的配置信息必须写在名为“jshint”的属性内部。可以有两个属性,一个数参数(options)另一个是目标(target)。</p>
<p>目标可以在任何属性内部,在这里我们仅使用“target”。其必须包含待验证的JavaScript文件列表。文件列表可以放在目标的src属性中,可以使用<em>*和</em>通配符。</p>
<p>有两个自定义选项,将会验证位于src和test目录及其子目录下的所有js文件。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grunt.initConfig({
jshint: {
options: {
eqeqeq: true,
trailing: true
},
target: {
src : ['src/**/*.js', 'test/**/*.js']
}
}
});
</code></pre></div></div>
<h4 id="加载和注册load-and-register">加载和注册(Load and Register)</h4>
<p>最后需要载入和注册 grunt-contrib-jshint 任务:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.registerTask('default', ['jshint']);
</code></pre></div></div>
<h4 id="全部jshint配置选项full-jshint-configuration">全部JSHint配置选项(Full JSHint Configuration)</h4>
<p>目前为止完整的 Gruntfile.js 文件如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>module.exports = function(grunt) {
grunt.initConfig({
jshint: {
options: {
trailing: true,
eqeqeq: true
},
target: {
src : ['src/**/*.js', 'test/**/*.js']
}
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.registerTask('default', ['jshint']);
};
</code></pre></div></div>
<h3 id="拼接文件concatenate-files">拼接文件(Concatenate Files)</h3>
<p>js文件一般是分散的(如果你的项目不是几十行js的话),上线前我们必须将其打包进一个文件,并添加版本号和项目名字。在文件的开始位置应该包含库名称,版本,协议,构建时间和其他一些信息。</p>
<p>例如,像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/*! gruntdemo v1.0.2 - 2013-06-04
* License: BSD */
var gruntdemo = function() {
...
</code></pre></div></div>
<p>我们将使用 <a href="https://github.com/gruntjs/grunt-contrib-concat#readme">grunt-contrib-concat</a> 插件完成拼接任务,并生成正确的注释信息。</p>
<h4 id="安装插件install-the-plugin-2">安装插件(Install the Plugin)</h4>
<p>和前面一样,将插件写进package.json文件的开发依赖里,然后从Npm仓库安装到本地。</p>
<p>打开命令行,运行如下命令:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install grunt-contrib-concat --save-dev
</code></pre></div></div>
<h4 id="加载packagejsonload-packagejson">加载Package.json(Load Package.json)</h4>
<p>首先从package.json文件加载配置信息,并存储在pkg属性。需要使用 grunt.file.readJSON 函数:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pkg: grunt.file.readJSON('package.json'),
</code></pre></div></div>
<p>现在pkg的值是一个对象,包含全部package.json的信息。项目名被存储在pkg.name属性,版本被存储在pkg.version。版权被存储在pkg.license属性等等。</p>
<h4 id="生成页头信息和-文件名compose-banner-and-file-name">生成页头信息和 文件名(Compose Banner and File Name)</h4>
<p>Grunt提供一套<a href="http://gruntjs.com/api/grunt.template">模版系统</a>,我们可以使用它构建页头和文件名称。模版可以在字符串中嵌入JavaScript表达式,通过<%= expression %> 语法。Grunt计算表达式的值并替换模版中的表达式。</p>
<p>例如,模版中的 <%= pkg.name %> 将被替换为 pkg.name 的属性值。如果属性值是字符串,模版的行为类似字符串拼接 …’ + pkg.name + ‘…</p>
<p>模版中可以引用Grunt中的全部属性。系统提供了一个非常有帮助的日期格式化函数。我们将使用 grunt.template.today(format) 函数生成当前的时间戳。</p>
<p>让我们生成一个简单的页头,包含项目名称,版本号,版权和当前的日期。由于我们需要在 Uglify 任务中使用banner,所以我们将其存储在变量中:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var bannerContent = '/*! <%= pkg.name %> v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> \n' +
' * License: <%= pkg.license %> */\n';
</code></pre></div></div>
<p>上面的模版生成如下的页头:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/*! gruntdemo v0.0.1 - 2013-06-04
* License: BSD */
</code></pre></div></div>
<p>项目的名称和版本部分也需要在多处使用。将项目名和版本号拼在一起,存储在一个变量中:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var name = '<%= pkg.name %>-v<%= pkg.version%>';
</code></pre></div></div>
<p>生成的名字如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gruntdemo-v0.0.1
</code></pre></div></div>
<h4 id="配置目标和选项configure-target-and-options">配置目标和选项(Configure Target and Options)</h4>
<p>target必须包含需要被拼接的文件列表,和合并完成后输出文件的名字。target支持通配符和模版,所以我们使用前一节生成的模版:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>target : {
// 拼接src目录下的所有文件
src : ['src/**/*.js'],
// place the result into the dist directory,
// name variable contains template prepared in
// previous section
dest : 'distrib/' + name + '.js'
}
</code></pre></div></div>
<p>concat插件也可以通过banner属性添加banner。由于上面我们已经将banner内容赋给bannerContent变量,所以我们仅需引入即可:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>options: {
banner: bannerContent
}
</code></pre></div></div>
<h4 id="加载和注册load-and-register-1">加载和注册(Load and Register)</h4>
<p>最后不要忘记从Npm加载 grunt-contrib-concat ,并且将其注册到默认工作流:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default', ['jshint', 'concat']);
</code></pre></div></div>
<h4 id="完整的拼接配置full-concat-configuration">完整的拼接配置(Full Concat Configuration)</h4>
<p>这一节出示包含完整contat配置的Gruntfile.js文件。</p>
<p>注意pkg属性在传递给initConfig方法的参数中定义。我们不能把他放在其他地方,因为它读取模版信息,并且仅在initConfig方法的参数和grunt对象中有访问模版的权限。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>module.exports = function(grunt) {
var bannerContent = '... banner template ...';
var name = '<%= pkg.name %>-v<%= pkg.version%>';
grunt.initConfig({
// pkg is used from templates and therefore
// MUST be defined inside initConfig object
pkg : grunt.file.readJSON('package.json'),
// concat configuration
concat: {
options: {
banner: bannerContent
},
target : {
src : ['src/**/*.js'],
dest : 'distrib/' + name + '.js'
}
},
jshint: { /* ... jshint configuration ... */ }
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default', ['jshint', 'concat']);
};
</code></pre></div></div>
<h3 id="压缩minify">压缩(Minify)</h3>
<p>如果浏览器载入和解析大文件,会使页面载入变得缓慢。可能不是所有项目都会遇到这个问题,但对于移动端的app和使用非常多大型库的大型web应用程序而言,是必须要考虑的。</p>
<p>因此,我们也将我们库进行压缩。压缩会将输入的文件变小,通过去除空白元素,注释,替换变量名称等,但不会改变代码逻辑。</p>
<p>压缩功能使用 <a href="https://github.com/gruntjs/grunt-contrib-uglify#readme">grunt-contrib-uglify</a> 插件,其通过Grunt集成 <a href="http://lisperator.net/uglifyjs/">UglifyJs</a>。它通过uglify任务拼接和压缩一组文件。</p>
<h4 id="源程序映射source-maps">源程序映射(Source Maps)</h4>
<p>压缩会使生成的文件难于阅读和调试,所以我们通过生成源程序映射来简化问题。</p>
<p>源程序映射是压缩文件和源文件之间的纽带。如果浏览器支持,浏览器调试工具会显示对人友好的源文件,而不是压缩文件。仅有chrome和nightly版本的 firefox支持源代码映射。你可以在<a href="http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/">HTML5 rocks</a> 和 <a href="http://net.tutsplus.com/tutorials/tools-and-tips/source-maps-101/">Tutsplus </a>上获取更多信息,我建议你看看阮一峰老师的这篇文章:http://www.ruanyifeng.com/blog/2013/01/javascript_source_map.html</p>
<h4 id="安装插件install-the-plugin-3">安装插件(Install the Plugin)</h4>
<p>将插件添加到package.json的开发依赖里,并且安装到本地Npm仓库。</p>
<p>使用如下命令:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install grunt-contrib-uglify --save-dev
</code></pre></div></div>
<h4 id="配置目标configure-target">配置目标(Configure Target)</h4>
<p>配置uglify任务目标的方式和concat任务类似。必须要包含待压缩的javascript文件列表和输出文件的名字。</p>
<p>支持通配符和模版,所以我们可以使用前面章节中的用到的模版:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>target : {
// use all files in src directory
src : ['src/**/*.js'],
// place the result into the dist directory,
// name variable contains template prepared in
// previous sub-chapter
dest : 'distrib/' + name + '.min.js'
}
</code></pre></div></div>
<h4 id="配置选项configure-options">配置选项(Configure Options)</h4>
<p>配置banner的方式和concat一样——通过设置“banner”属性并且支持模版。因此,我们可以重复使用前面章节中准备好的 bannerContent 变量。</p>
<p>通过“sourceMap”属性生成源文件映射。包含生成文件的名字。此外,必须设置“sourceMapUrl”和“sourceMapRoot”属性。前一个包含相对于uglified文件到源文件映射文件的路径,后一个包含是源文件映射到源文件的相对路径。</p>
<p>通过bannerContent变量生成页眉,通过name变量生成源文件映射文件的名字:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>options: {
banner: bannerContent,
sourceMapRoot: '../',
sourceMap: 'distrib/'+name+'.min.js.map',
sourceMapUrl: name+'.min.js.map'
}
</code></pre></div></div>
<h4 id="加载和注册load-and-register-2">加载和注册(Load and Register)</h4>
<p>最后一步是从Npm加载 grunt-contrib-uglify,并且添加到默认任务列表:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['jshint', 'concat', 'uglify']);
</code></pre></div></div>
<h4 id="全部-uglify-配置信息full-uglify-configuration">全部 Uglify 配置信息(Full Uglify Configuration)</h4>
<p>下面是包含完整uglify配置信息的Gruntfile.js文件:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>module.exports = function(grunt) {
var bannerContent = '... banner template ...';
var name = '<%= pkg.name %>-v<%= pkg.version%>';
grunt.initConfig({
// pkg must be defined inside initConfig object
pkg : grunt.file.readJSON('package.json'),
// uglify configuration
uglify: {
options: {
banner: bannerContent,
sourceMapRoot: '../',
sourceMap: 'distrib/'+name+'.min.js.map',
sourceMapUrl: name+'.min.js.map'
},
target : {
src : ['src/**/*.js'],
dest : 'distrib/' + name + '.min.js'
}
},
concat: { /* ... concat configuration ... */ },
jshint: { /* ... jshint configuration ... */ }
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['jshint', 'concat', 'uglify']);
};
</code></pre></div></div>
<h3 id="最后的发布文件latest-release-file">最后的发布文件(Latest Release File)</h3>
<p>最后要发布的库包括两个文件,并且都在名字中含有版本号。这会给想要自动下载每个新版本的人造成不必要的困难。</p>
<p>如果想要查看是否发布了新版本和新版本的名字,必须每次都要获取和解析一个json文件。如果每次都更改名称,则必须更新下载脚本。</p>
<p>因此我们将使用 <a href="https://github.com/gruntjs/grunt-contrib-copy#readme">grunt-contrib-copy</a> 插件创建无版本号的文件。</p>
<h4 id="安装插件install-the-plugin-4">安装插件(Install the Plugin)</h4>
<p>将插件添加进package.json的开发依赖,并且从Npm仓库安装到本地。</p>
<p>使用如下命令:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install grunt-contrib-copy --save-dev
</code></pre></div></div>
<h4 id="配置插件configure-the-plugin">配置插件(Configure the Plugin)</h4>
<p>copy配置信息包括三个目标,分别对应三个发布文件。没有配置选项,基本和前一个插件配置过程一样。</p>
<p>仅有一点不一样,就是多任务。每个任务包含一对 src/dest,待拷贝的名字和待创建的名字。</p>
<p>对前面的任务配置选项稍加修改。将所有文件名字放到变量中,以便可以重复使用:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>module.exports = function(grunt) {
/* define filenames */
latest = '<%= pkg.name %>';
name = '<%= pkg.name %>-v<%= pkg.version%>';
devRelease = 'distrib/'+name+'.js';
minRelease = 'distrib/'+name+'.min.js';
sourceMapMin = 'distrib/source-map-'+name+'.min.js';
lDevRelease = 'distrib/'+latest+'.js';
lMinRelease = 'distrib/'+latest+'.min.js';
lSourceMapMin = 'distrib/source-map-'+latest+'.min.js';
grunt.initConfig({
copy: {
development: { // copy non-minified release file
src: devRelease,
dest: lDevRelease
},
minified: { // copy minified release file
src: minRelease,
dest: lMinRelease
},
smMinified: { // source map of minified release file
src: sourceMapMin,
dest: lSourceMapMin
}
},
uglify: { /* ... uglify configuration ... */ },
concat: { /* ... concat configuration ... */ },
jshint: { /* ... jshint configuration ... */ }
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'copy']);
</code></pre></div></div>
<h3 id="单元测试unit-tests">单元测试(Unit Tests)</h3>
<p>最后,配置 grunt.js 运行单元测试,测试最新发布的文件。我们将使用 grunt-contrib-qunit 插件实现目标。这个插件将在无头的 <a href="http://phantomjs.org/">PhantomJS </a>实例中运行 <a href="http://qunitjs.com/">QUnit </a>单元测试。</p>
<p>这个解决方案不能模拟不同浏览器和查找全部 bug,但对于我们来说已经足够了。如果想得到更好的配置,可以使用 js-test-driver 或 其他类似工具,然而,关于 <a href="http://code.google.com/p/js-test-driver/">js-test-dirver</a> 的配置超出了本文的范围。</p>
<h4 id="准备测试用例prepare-tests">准备测试用例(Prepare Tests)</h4>
<p>Qunit 单元测试经常要运行 src 目录里的 JavaScript 文件,由于测试是开发的一部分。如果你想测试刚刚发布的拼接压缩后的版本工作状况,需要创建一个新的 QUnit HTML 文件,并加载最后发布的文件。</p>
<p>下面是一个例子是 Qunit 的入口文件:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>QUnit Example</title>
<link rel="stylesheet" href="../libs/qunit/qunit.css">
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="../libs/qunit/qunit.js"></script>
<!-- Use latest versionless copy of current release -->
<script src="../distrib/gruntdemo.min.js"></script>
<script src="tests.js"></script>
</body>
</html>
</code></pre></div></div>
<h4 id="安装插件install-the-plugin-5">安装插件(Install the Plugin)</h4>
<p>将插件添加进package.json 的开发者依赖中,并且将其安装到本地 Npm 仓库。</p>
<p>使用如下命令:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm install grunt-contrib-qunit --save-dev
</code></pre></div></div>
<h4 id="配置插件configure-plugin">配置插件(Configure Plugin)</h4>
<p>配置 grunt-contrib-qunit 插件和配置前面的任务如出一辙。由于我们使用默认的 Qunit 配置,所以可以省略选项属性。不能忽略的是必须配置 target,指定全部的 Qunit HTML 文件。</p>
<p>接下来指定位于测试目录下的全部 HTML 文件,及其子目录应该运行 Qunit 测试:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>grunt.initConfig({
qunit:{
target: {
src: ['test/**/*.html']
}
},
// ... all previous tasks ...
});
</code></pre></div></div>
<h3 id="完整的-gruntjs-文件final-gruntjs-file">完整的 Grunt.js 文件(Final Grunt.js File)</h3>
<p>下面是完整的 Gruntfile.js 配置信息:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>module.exports = function(grunt) {
var name, latest, bannerContent, devRelease, minRelease,
sourceMap, sourceMapUrl, lDevRelease, lMinRelease,
lSourceMapMin;
latest = '<%= pkg.name %>';
name = '<%= pkg.name %>-v<%= pkg.version%>';
bannerContent = '/*! <%= pkg.name %> v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> \n' +
' * License: <%= pkg.license %> */\n';
devRelease = 'distrib/'+name+'.js';
minRelease = 'distrib/'+name+'.min.js';
sourceMapMin = 'distrib/'+name+'.min.js.map';
sourceMapUrl = name+'.min.js.map';
lDevRelease = 'distrib/'+latest+'.js';
lMinRelease = 'distrib/'+latest+'.min.js';
lSourceMapMin = 'distrib/'+latest+'.min.js.map';
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
qunit:{
target: {
src: ['test/**/*.html']
}
},
// configure copy task
copy: {
development: {
src: devRelease,
dest: lDevRelease
},
minified: {
src: minRelease,
dest: lMinRelease
},
smMinified: {
src: sourceMapMin,
dest: lSourceMapMin
}
},
// configure uglify task
uglify:{
options: {
banner: bannerContent,
sourceMapRoot: '../',
sourceMap: sourceMapMin,
sourceMappingURL: sourceMapUrl
},
target: {
src: ['src/**/*.js'],
dest: minRelease
}
},
// configure concat task
concat: {
options: {
banner: bannerContent
},
target: {
src: ['src/**/*.js'],
dest: devRelease
}
},
// configure jshint task
jshint: {
options: {
trailing: true,
eqeqeq: true
},
target: {
src: ['src/**/*.js', 'test/**/*.js']
}
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'copy', 'qunit']);
};
</code></pre></div></div>
<h2 id="结论conclusion">结论(Conclusion)</h2>
<p>现在 Grunt.js 配置好了,并且可以使用了。我们的目标是使配置尽可能简单,使用成对的 src/dest,通配符和模版。当然,Grunt.js 也提供其他更<a href="http://gruntjs.com/configuring-tasks">高级的选项</a>。</p>
<p>如果能够自动下载和管理项目依赖的库,会变得更美好。我发现两个可行的解决方案,<a href="http://bower.io/">Bower</a> 和 <a href="http://ender.jit.su/">Ender</a>。我没有试过他们,但都可以管理前端JavaScript包和其依赖。</p>
<h2 id="注">注</h2>
<p>文章有些长,拖了很久的文章终于翻译完成了,我最近打算写一本关于 Grunt 指南的书籍,会详细讲解如何构建一套前端自动化工具,如果你支持我的工作,那就给我捐助吧。</p>
<p>原文:http://flippinawesome.org/2013/07/01/building-a-javascript-library-with-grunt-js/</p>
CSS 设计理念
2014-07-09T00:00:00+00:00
http://yanhaijing.com/css/2014/07/09/12-design-principles-of-css
<p>今天整理CSS2.1的规范,发现这个,分享给大家。</p>
<p>CSS2.1 作为 CSS2 和 CSS1 的后序版本,基于一下一组设计理念:</p>
<ul>
<li>向前和向后兼容。CSS2.1 的用户代理能够理解 CSS1 的样式表。 CSS1 的用户代理能够读取 CSS2.1 的样式表,并且丢弃他们不能理解的部分。同时,对于不支持 CSS 的用户代理可以显示样式增强的文档。当然通过 CSS 增强的样式将不被渲染,但所有的内容可以被表现。</li>
<li>作为结构化文档的补充。样式表补充结构化文档(例如,HTML 和 XML应用程序),为标记文本提供样式信息。样式表应该非常容易修改,并对标记的影像甚微或没有。</li>
<li>供应商,平台和设备无关。样式表使文档保持供应商,平台和设备无关。样式表本身也是供应商和平台无关的,但CSS2.1允许你为一组设备指定一个样式表(例如,打印机)。</li>
<li>可维护性。通过在文档中指向样式表,网站管理员能简化站点的维护和保持整个站点的一致外观和感觉。例如,如果组织的背景色发生改变,仅需更改一个文件。</li>
<li>简单。CSS 是一门简单的样式语言,是对人类读写友好的。CSS的属性保持最大程度上的相互独立,一般实现一个效果只有一种方法。</li>
<li>网络性能。CSS为内容的呈现方式提供了紧凑的编码。图片或音频文件常被作者用来实现某种特定的渲染效果,样式表和其比起来体积要小的多。同时,减少网络链接的次数,进一步提高网络性能。</li>
<li>灵活性。有几种方法可以将CSS应用到内容。关键特征是不同位置的样式信息能够层叠,包括默认(用户代理)样式表,用户样式表,链接样式表,内嵌样式,和元素属性中的样式信息。渴求的某些渲染效果和设备无关相冲突,但CSS2.1</li>
<li>丰富的。为作者提供一组丰富的渲染效果,增加网站作为表达媒介的丰富性。设计师们已经对桌面版和幻灯片应用中的常见功能渴望许久。但 CSS2.1 为满足设计师的要求,向前迈了一大步。</li>
<li>可选的语言绑定。规范中描述的一组 CSS 属性使视觉和听觉格式化模型表现一致。其格式化模型可以通过CSS语言访问,但也可以绑定到其他语言。例如,在JavaScript程序中可以动态改变某个元素的‘color’属性值。</li>
<li>可访问性。一些CSS功能将使网络更方便残障用户:
<ul>
<li>控制字体外观属性允许作者消除不可访问的文图图片。</li>
<li>位置属性允许作者消除强制布局的标记技巧(例如,不可见图片)。</li>
<li>!improtan 规则的意义在于有特别演示要求的用户可以覆盖作者的样式表。</li>
<li>所有属性的‘inherit’值用来提升层叠的通用性,和更容易生成一致的风格。</li>
<li>改进媒体支持,包括媒体分组和 braille,embossed,和 tty 媒体类型,允许用户或作者为这些设备定制页面。</li>
</ul>
</li>
</ul>
<h2 id="注">注</h2>
<p>原文:http://www.w3.org/TR/CSS2/intro.html#design-principles</p>
如何使用PhoneGap打包Web App
2014-06-24T00:00:00+00:00
http://yanhaijing.com/mobile/2014/06/24/12-how-use-phonegap-package-web-app
<p>最近做了一款小游戏,定位是移动端访问,思来想去最后选择了jQuery mobile最为框架,制作差不多以后,是否可以打包成App,恰好以前对PhoneGap有耳闻,便想用这个来做打包,可以其中艰辛曲折多次让我想放弃的心情,官方提供的例子,对我这种没用过的人而言,真是无语的很,所已将配置环境和打包过程写下做个记录。</p>
<p><img src="/blog/244.jpg" alt="" /></p>
<p>因为我只弄了Andriod的环境,所以在此只以Andriod为例。</p>
<p>使用PhoneGap搭建Android开发的项目整体步骤如下:</p>
<ol>
<li>
<p>安装java环境。</p>
</li>
<li>
<p>安装ant构建工具。</p>
</li>
<li>
<p>安装android的开发环境并配置环境变量。</p>
</li>
<li>
<p>安装Node.js环境并配置环境变量。</p>
</li>
<li>
<p>安装git</p>
</li>
<li>
<p>使用npm安装PhoneGap全局环境。</p>
</li>
<li>
<p>使用PhoneGap命令创建PhoneGap项目。</p>
</li>
<li>
<p>将PhoneGap编译为android项目。</p>
</li>
<li>
<p>将上述项目导入ADT进行后续开发。</p>
</li>
<li>
<p>安装.apk文件</p>
</li>
</ol>
<p>其实官网给出的<a href="http://phonegap.com/install/">安装过程</a>忽略了很多步骤(因为这里是Andriod环境,所以才会比官网的例子多出不少步骤),像我这种前端开发人员,电脑里可是连java都没装的,下面就详细讲解这些步骤,并最终生成apk文件。</p>
<h2 id="安装java环境">安装Java环境</h2>
<p>这点不用我讲,网上一搜一大堆,而且很多程序员电脑里面都是有java环境的,需要强调的是安装java的环境要和后面下载andriod开发环境一致,不然会报错,要保证都是32位或64位,笔者就装了个64位jdk然后,安卓环境是32位的,运行不成功。</p>
<p><strong>资源</strong></p>
<p>jdk 下载:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html</p>
<p>配置java环境:http://jingyan.baidu.com/article/ed15cb1b2ed02a1be369818a.html</p>
<h2 id="安装ant构建工具">安装Ant构建工具</h2>
<p>Adobe将PhoneGap已经放到Apache名下进行开源,并且还改了个名字,ant可以apache下的构建工具,所以……需要先安装ant才可以,安装过程其实非常简单,第一个就是<a href="http://ant.apache.org/bindownload.cgi">下载</a>,选择适合自己的版本,因为我的环境是win7 所以下载zip格式的就可以了。</p>
<p><img src="http://images.cnitblog.com/i/460220/201406/241743384553923.png" alt="" /></p>
<p>然后将zip文件解压到任意目录,并添加环境变量,具体可以参看<a href="http://ant.apache.org/manual/index.html">这里</a></p>
<ol>
<li>将bin目录添加到path里面</li>
<li>添加ANT_HOME变量为ant的根目录</li>
<li>确保安装了jdk并配置好了JAVA_HOME</li>
</ol>
<p>然后保存环境变量,打开命令行输入 <code class="language-plaintext highlighter-rouge">ant -version</code> 你应该看见类似下面的输出,那恭喜成功了,可以进行下一步了,如果未成功,可百度下错误原因:</p>
<p><img src="http://images.cnitblog.com/i/460220/201406/241748446899380.png" alt="" /></p>
<h2 id="下载andriod开发环境并配置环境变量">下载Andriod开发环境并配置环境变量</h2>
<p>首先就是来<a href="http://developer.android.com/design/downloads/index.html">这里</a>下载环境,然后是安装,其实就是解压到任意目录,可以看<a href="http://developer.android.com/index.html">这里</a>,接下来需要添加环境变量,将sdk目录下的platform-tools 和 tools添加到path里。</p>
<p><img src="http://images.cnitblog.com/i/460220/201406/241754276118178.png" alt="" /></p>
<p>然后你还需要设置avd,打开AVD Manager,点击新建,然后设置一些参数即可,由于我也不是搞安卓的,所以吗你要想深入了解需自行研究。</p>
<p><img src="http://images.cnitblog.com/i/460220/201406/241902083612827.png" alt="" /></p>
<h2 id="安装git">安装git</h2>
<p>git是我非常喜欢的版本控制工具,我电脑上自带的是github for windows,只需将其git命令添加到path即可,如果你没有安装git我建议你安装mysygit,安装过程中记得勾上将git添加仅path选项。如果你安装的其他git工具,请确保将git命令加入path,因为安装phonegap过程会用到git命令。</p>
<p>来<a href="http://msysgit.github.io/">这里</a>下载mysygit,注意下载过程非常缓慢(没办法了谁让我们在天朝呢,以前mysygit在google code上的时候速度更慢,下载迁移到github速度已经快很多了)。</p>
<p>如果你对git感兴趣,我建议你加入我的群一起交流,<span class="qname" title="GitHub家园②">GitHub家园②</span><span class="Apple-converted-space"> <span id="group_number" class="group_number">193091696,由于1群已满,群共享里也有mysygit的最新pre版,下载速度会是github上的几百倍吧!!!!</span></span></p>
<h2 id="安装nodejs环境并配置环境变量">安装Node.js环境并配置环境变量</h2>
<p>来这里<a href="http://nodejs.org/download/">下载</a>你需要的版本,windows建议下载.msi安装包,自带npm,无需配置环境变量,如果你下载.exe的话下载的知识node,还需要自行配置环境变量和安装npm。现在的node安装过程真的非常简单了。</p>
<h2 id="使用npm安装phonegap全局环境">使用npm安装PhoneGap全局环境</h2>
<p>到这里就可以安装官网上的提供的教程来了,打开刚刚安装的node的命令行工具,然后输入 <code class="language-plaintext highlighter-rouge">npm install -g phonegap</code>,将会自动安装phonegap,需要注意的是安装过程非常缓慢,因为安装期间回到用到git命令去下载文件(不是git慢,而是外网慢)。安装完成后会提示安装成功,当然你也可以输入 <code class="language-plaintext highlighter-rouge">phonegap -v</code>,你将会看到如下输出,说明你安装成功了:`</p>
<p><img src="http://images.cnitblog.com/i/460220/201406/241815094551502.png" alt="" /></p>
<h2 id="使用phonegap命令创建phonegap项目">使用PhoneGap命令创建PhoneGap项目</h2>
<p>接下来将路径切到任意目录,输入<code class="language-plaintext highlighter-rouge"> <code>phonegap create my-app</code> 你将会看到如下画面:</code></p>
<p><img src="http://images.cnitblog.com/i/460220/201406/241818458611749.png" alt="" /></p>
<h2 id="将phonegap编译为android项目">将PhoneGap编译为android项目</h2>
<p>接下来先切换到myapp1目录,然后运行<code class="language-plaintext highlighter-rouge">phonegap run andriod</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cd myapp
phonegap run android
</code></pre></div></div>
<p>会出现很多构建信息,成功后会自动启动adk模拟器</p>
<p><img src="http://images.cnitblog.com/i/460220/201406/241822552363434.png" alt="" /></p>
<p><img src="http://images.cnitblog.com/i/460220/201406/241900059861008.png" alt="" /></p>
<p>如果你不想运行安卓模拟器,而只想构将项目那么可以,你只需运行 <code class="language-plaintext highlighter-rouge">phonegap build android</code> 即可。</p>
<h2 id="将上述项目导入adt进行后续开发">将上述项目导入ADT进行后续开发</h2>
<p>启动ADT中的eclipse,然后选择File-New-Project,在打开的“New Project”向导中选择Android->Android Project from Existing Code,并选择Next</p>
<p>在下一步的导航页中Root Directory选择刚才创建的my-app/platforms/android文件夹,下方Projects会出现两个项目,都勾选,但是不要勾选Copy projects into workspace选项。</p>
<p>选择Finish完成上述导入</p>
<p>话说上面的导入过程是复制粘贴的,笔者导入的时候点击finished就是不起作用,不知为何,比较郁闷,不知你是否也会遇到同样的事情。</p>
<h2 id="安装apk文件">安装.apk文件</h2>
<p>项目目录下的platforms\android\ant-build 里已经生成了对应的apk文件,将其导入手机即可安装。</p>
<p><img src="http://images.cnitblog.com/i/460220/201406/241907545029142.png" alt="" /></p>
<p>我也不知道这两个apk有什么区别,我讲第一个放到手机里试了试,挺完美的。</p>
<h2 id="结论">结论</h2>
<p>至此完成了用phonegap构建web项目的过程,是不是灰常复杂,而且网上的其他文章的安装过程,笔者尝试绝大部分未能成功,在此将自己安装过程总结下来,希望能帮到大家。</p>
<h2 id="注">注</h2>
<p>参考文献:</p>
<ul>
<li>http://zhidao.baidu.com/link?url=TQDJTCYZqa-lG9AvhAvYjbj3DqbHwZLkIbGvBlFBMsomC73s0Ro-byUvAxu9fsByVPjpSicFFFGtS2dVrVqzpYcVU2NK8ljogH0dX8XXp1i</li>
<li>http://zhaochaozcx.blog.163.com/blog/static/22000865201375102830684/</li>
</ul>
12个很少被人知道的CSS事实
2014-05-17T00:00:00+00:00
http://yanhaijing.com/css/2014/05/17/12-little-known-css-facts
<p>CSS不是一门很复杂的语言,但是即使你已经写css很多年了,也很有可能遇到一些新玩意儿-某些属性从来没用过,某些值从来未曾考虑,或者某些规范细则你从来不知道。</p>
<p>我经常会遇到一些css小细节,所以我想在这片文章中和大家分享,需要承认的是,这篇文章中的很多东西并不具有实操价值,但也许你可以留作后用。</p>
<h2 id="body上的color不只是应用于文字">body上的color不只是应用于文字</h2>
<p>让我们从最简单的开始吧,color属性是被广泛运用的属性,某些人可能不曾注意,它并不仅仅只是定义文本的颜色。</p>
<p>让我们看这个例子:</p>
<iframe allowtransparency="true" class="cp_embed_iframe" frameborder="0" height="431" id="cp_embed_CtwFG" scrolling="no" src="http://codepen.io/anon/embed/CtwFG?height=431&theme-id=5083&slug-hash=CtwFG&default-tab=result" style="width: 100%; overflow: hidden;"></iframe>
<p>请注意我们只是给body设定了color为yellow,但是你可以看到,页面里的元素都变成了黄色,他们包括:</p>
<ul>
<li>图片的alt文字的值,就是当图片源载入不了时显示的文字</li>
<li>列表元素的边框</li>
<li>无序列表的点</li>
<li>有序列表的数字</li>
<li>hr元素</li>
</ul>
<p>有趣的是,hr元素默认是不会继承color属性得,我需要强制他继承border-color: inherit,这对我来说有点怪异。</p>
<p>W3c规范是这么<a href="http://www.w3.org/TR/css3-color/#foreground">定义</a>的:</p>
<blockquote>
<p>这个属性用来描述元素的文本的前景颜色,附带被用来为其他接受颜色值的其他属性提供潜在的间接的值。</p>
</blockquote>
<p>我不是很明确地知道有哪些是被当做所谓的前景的,如果你知道的话,请在评论中告诉我。</p>
<h2 id="visibility还可以设置-collapse值">visibility还可以设置 “collapse”值</h2>
<p>你可能已经用过visibility上千遍了,最常用的是visible和hidden,用来使元素显示或者隐藏。</p>
<p>还有第三个很少被用到的值是collapse,除了在表格的行,列中使用有差异外,他和hidden的作用是等同的。</p>
<p>下面让我们看看在表格元素中,collapse是怎么工作的,不过前提是table的border-collapse需要设定成separate才会有效果哦!</p>
<iframe allowtransparency="true" class="cp_embed_iframe" frameborder="0" height="406" id="cp_embed_jeICw" scrolling="no" src="http://codepen.io/anon/embed/jeICw?height=406&theme-id=5083&slug-hash=jeICw&default-tab=result" style="width: 100%; overflow: hidden;"></iframe>
<p>CSS-Trick 网站的Almanac同学说<a href="http://css-tricks.com/almanac/properties/v/visibility/">不要用这个属性</a>,因为这个属性存在兼容问题。</p>
<p>我的测试结果是:</p>
<ul>
<li>在Chrome中,collapse和hidden的表现并无不同(请看<a href="https://code.google.com/p/chromium/issues/detail?id=174167">bug报告和讨论</a>)</li>
<li>在firefox,opera和ie11中,collpase的工作是正常的,那就是,表格的行被清除了,而且不再占据空间。</li>
</ul>
<p>我得承认,这个值可能很少会被用到,但是他确实是存在的。</p>
<h2 id="background这个简写又有了新值">background这个简写又有了新值</h2>
<p>在css2.1中,background是这5个值的缩写,background-color, background-image, background-repeat, background-attachment, background-position。现在,在css3中,又有三个成员被加进来了,现在总共有8个值,他们是:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>background: [background-color] [background-image] [background-repeat]
[background-attachment] [background-position] / [ background-size] [background-origin] [background-clip];
</code></pre></div></div>
<p>请注意那个正斜杠,和font,<a href="http://www.sitepoint.com/setting-css3-border-radius-with-slash-syntax/">border-radius</a>类似,这个正斜杠允许你在写完position后加上background-size。</p>
<p>另外,还有两个可选的值是background-origin和background-clip。</p>
<p>实操中语法会变成这个样子</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.example {
background: aquamarine url(img.png)
no-repeat
scroll
center center / 50%
content-box content-box;
}
</code></pre></div></div>
<p>让我们在demo中来一起 感受它:</p>
<iframe allowtransparency="true" class="cp_embed_iframe" frameborder="0" height="359" id="cp_embed_wdsef" scrolling="no" src="http://codepen.io/anon/embed/wdsef?height=359&theme-id=5083&slug-hash=wdsef&default-tab=result" style="width: 100%; overflow: hidden;"></iframe>
<p>这些新值在现代浏览器中工作完美,但是你可能需要为不支持的浏览器优雅降级。</p>
<h2 id="clip只对absolute元素和fixed元素有效">clip只对absolute元素和fixed元素有效</h2>
<p>上边我们谈到了backgrond-clip,现在我们谈谈clip,我们一般是这么写的:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.example {
clip: rect(110px, 160px, 170px, 60px);
}
</code></pre></div></div>
<p>我们用这个方法来剪切元素的一部分,但是它的前提是这个元素必须是absolute定位的(这里有<a href="http://www.impressivewebs.com/css-clip-property/">解释</a>),所以代码变成这样</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.example {
position: absolute;
clip: rect(110px, 160px, 170px, 60px);
}
</code></pre></div></div>
<p>你可以看到当我们切换这个元素absolute定位的时候,clip也会跟着变化:</p>
<iframe allowtransparency="true" class="cp_embed_iframe" frameborder="0" height="494" id="cp_embed_siFJu" scrolling="no" src="http://codepen.io/anon/embed/siFJu?height=494&theme-id=5083&slug-hash=siFJu&default-tab=result" style="width: 100%; overflow: hidden;"></iframe>
<p>你也可以通过设定position:fixed来让clip变得有效,因为根据<a href="http://www.w3.org/TR/CSS2/visuren.html#absolute-positioning">规范</a>,fixed元素有资格被当做absolute元素。</p>
<h2 id="垂直百分比是根据父层的宽度计算的而不是父层高度计算的">垂直百分比是根据父层的宽度计算的,而不是父层高度计算的</h2>
<p>这个说起来有<a href="http://www.impressivewebs.com/vertical-percentages-css/">一点麻烦</a>,你应该知道百分比宽度是根据父层的宽度计算的,但是如果像padding,margin这样的属性用上百分比的时候,最终的结果是根据父层的宽度计算的,而不是根据父层的高度计算。</p>
<p>大家来看这个例子:</p>
<iframe allowtransparency="true" class="cp_embed_iframe" frameborder="0" height="475" id="cp_embed_qLnpm" scrolling="no" src="http://codepen.io/anon/embed/qLnpm?height=475&theme-id=5083&slug-hash=qLnpm&default-tab=result" style="width: 100%; overflow: hidden;"></iframe>
<p>注意当我们滑动滑块的时候,只是改变了父层容器的宽度,但是padding-bottom的值却产生了变化。</p>
<h2 id="border实际上是简写属性的简写">border实际上是简写属性的简写</h2>
<p>我们都写过这样的语句:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.example {
border: solid 1px black;
}
</code></pre></div></div>
<p>border属性是border-style,border-width,border-color的简写 但是请不要忘了,这三个属性每个属性都包含有自身的简写,比如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.example {
border-width: 2px 5px 1px 0;
}
</code></pre></div></div>
<p>这样会让四个border获得不同的宽度,同理,border-color和border-style也是这样:</p>
<iframe allowtransparency="true" class="cp_embed_iframe" frameborder="0" height="302" id="cp_embed_ClKFE" scrolling="no" src="http://codepen.io/anon/embed/ClKFE?height=302&theme-id=5083&slug-hash=ClKFE&default-tab=result" style="width: 100%; overflow: hidden;"></iframe>
<p>这里的重点是,你没法用border这个属性为四条边做出不同的颜色,宽度,样式,所以属性简写再简写后,表达就变得不那么精确了。</p>
<h2 id="text-decoration实际上是三种属性的简写">text-decoration实际上是三种属性的简写</h2>
<p>我知道这篇文章所说的的可能会让你有一点点晕。</p>
<p>根据w3c规范,现在这个语句是符合标准的:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a {
text-decoration: overline aqua wavy;
}
</code></pre></div></div>
<p>text-decoration现在是这三个属性的缩写:text-decoration-line, text-decoration-color和text-decoration-style.</p>
<p>不幸的是,目前只有firefox一家支持这个新属性:</p>
<iframe id="cp_embed_HapgB" src="//codepen.io/anon/embed/HapgB?height=178&theme-id=5083&slug-hash=HapgB&default-tab=result" scrolling="no" frameborder="0" height="178" allowtransparency="true" class="cp_embed_iframe" style="width: 100%; overflow: hidden;"></iframe>
<p>在这个demo中,我们用了类似text-decoration-color这样的写法,我知道这样写很不爽,因为目前很多浏览器不支持,如果我们直接写text-decoration: overline aqua wavy;的话,无疑目前的浏览器识别不了这样的写法,于是只能不解析有,所以为了向下兼容,我们只能这么写了。</p>
<h2 id="border-width-支持关键字值">border-width 支持关键字值</h2>
<p>这个并不是那么惊天地泣鬼神,但是除了接受标准的值外(像5px或者1em),border-width同时还接受三个关键字值: medium, thin,和 thick。</p>
<p>实际上,border-width的初始值是medium,下面这个例子中用的是thick:</p>
<iframe allowtransparency="true" class="cp_embed_iframe" frameborder="0" height="216" id="cp_embed_HcaED" scrolling="no" src="http://codepen.io/anon/embed/HcaED?height=216&theme-id=5083&slug-hash=HcaED&default-tab=result" style="width: 100%; overflow: hidden;"></iframe>
<p>当浏览器渲染这些关键字值得时候,规范并没有要求他们用特定的宽度值,但是在我的测试中,所有的浏览器都是把这三个值转化成了1px,3px,和5px。</p>
<h2 id="很少有人用border-image">很少有人用border-image</h2>
<p>我曾经写过一篇关于css3的 border-image的<a href="http://www.sitepoint.com/css3-border-images">文章</a>,这个特性已经被现代浏览器很好的支持了,除了ie10及以下版本。但是有人在意吗?</p>
<blockquote>
<p>如果你不喜欢阅读英文,你可以阅读我早前写的一篇关于CSS3的border-image的基础教程。——@大漠</p>
</blockquote>
<p>它看起来是一个很优美的特性,允许你创建流动的图片边框,在这个例子中,你可以缩放窗口来观察图片边框的流动。</p>
<iframe allowtransparency="true" class="cp_embed_iframe" frameborder="0" height="384" id="cp_embed_DvhKL" scrolling="no" src="http://codepen.io/anon/embed/DvhKL?height=384&theme-id=5083&slug-hash=DvhKL&default-tab=result" style="width: 100%; overflow: hidden;"></iframe>
<p>不幸的是,border-image仍然像一个新奇事物一样并未被很多人使用。但也许我是错的。如果你知道有哪个真实案例中有使用border-image,或者你就使用过它的话,请在评论中让我知道,我会很乐意承认我错了。</p>
<h2 id="还存在empty-cells-这样一个属性">还存在empty-cells 这样一个属性</h2>
<p>这个属性是被广泛支持的,包括ie8,它写起来是这个样子的:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>table {
empty-cells: hide;
}
</code></pre></div></div>
<p>你也许已经知道了,它是用在表格中的,它告诉浏览器是否显示空的单元格。试着点击切换按钮来观察empty-cells的效果:</p>
<iframe allowtransparency="true" class="cp_embed_iframe" frameborder="0" height="300" id="cp_embed_yfhtq" scrolling="no" src="http://codepen.io/anon/embed/yfhtq?height=300&theme-id=5083&slug-hash=yfhtq&default-tab=result" style="width: 100%; overflow: hidden;"></iframe>
<p>在这个例子中,需要确保表格的边框是可见的,而且border-collapse没有被设定成 collapsed。</p>
<h2 id="font-style-还有一个值是oblique">font-style 还有一个值是“oblique”</h2>
<p>当我们使用font-style属性得时候,经常用到的两个值是normal和italic,但是你还可以给它另一个值oblique:</p>
<iframe allowtransparency="true" class="cp_embed_iframe" frameborder="0" height="282" id="cp_embed_lItBj" scrolling="no" src="http://codepen.io/anon/embed/lItBj?height=282&theme-id=5083&slug-hash=lItBj&default-tab=result" style="width: 100%; overflow: hidden;"></iframe>
<p>但是它到底是个神马意思呢?还有就是它和italic看起来一样吗?</p>
<p>规范对于oblique是<a href="http://dev.w3.org/csswg/css-fonts/#font-style-prop">这么解释</a>的:</p>
<blockquote>
<p>应用oblique样式,如果没有的话就用italic样式</p>
</blockquote>
<p>规范对于italic的解释和oblique基本上差不多,oblique这个词是一个<a href="https://en.wikipedia.org/wiki/Oblique_type">排版术语</a>,表示是在normal的基础上倾斜的字体,而不是真正的斜体。</p>
<p>由于css处理oblique的方式,其实它和italic是通用的,除非这个字体就是一个oblique字体。</p>
<p>而我从未听说过有oblique字体,但是也许我是错的。我的研究是,oblique是当一个字体没有真斜体的时候的一个仿斜体。</p>
<p>所以,如果我没有错的话,这就意味着如果一个字体没有真斜体字体,那么如果我们写了font-style:italic实际上会让字体变成font-style:oblique的形式。</p>
<blockquote>
<p>下边这个图可以很直观的知道仿斜体和真斜体的区别。灰色的是oblique仿斜体。 ——@大圆
12个很少被人知道的CSS事实</p>
</blockquote>
<p><img src="/blog/139.png" alt="" /></p>
<h2 id="word-wrap和overflow-wrap是等同的">word-wrap和overflow-wrap是等同的</h2>
<p>word-wrap不是一个被经常用到的<a href="http://www.impressivewebs.com/word-wrap-css3/">属性</a>,但在某些情况下是非常有用的,一个常见的例子是让长url换行,以免它撑开父容器,例子如下:</p>
<iframe allowtransparency="true" class="cp_embed_iframe" frameborder="0" height="206" id="cp_embed_DFqyI" scrolling="no" src="http://codepen.io/anon/embed/DFqyI?height=206&theme-id=5083&slug-hash=DFqyI&default-tab=result" style="width: 100%; overflow: hidden;"></iframe>
<p>由于这个属性是微软原创的,所以它被所有的浏览器支持了,包括ie5.5。</p>
<p>尽管它跨浏览器,而且被一贯地支持,W3C依然决定把word-wrap换成overflow-wrap- 我猜是之前的命名有点名不副实? overflow-wrap和word-wrap有着同样的值,而word-wrap被当作是overflow-wrap的一个后补语法。</p>
<p>现在有一些浏览器是支持overflow-wrap的,但这么做貌似是无意义的,因为所有旧的浏览器都把word-wrap解析得很好,而且由于历史原因所有的浏览器都被要求继续支持word-wrap。</p>
<p>我们可以在浏览器升级后开始使用overflow-wrap,但是直到现在,我依然看不到换成新语法的意义何在。</p>
<h2 id="这里边有多少是你不知道的呢">这里边有多少是你不知道的呢?</h2>
<p>不知你从这篇文章中有没有学到什么,我希望是这样,也许大多数有经验的css开发者知道很多,但是对于css新手应该会受益多一点。</p>
<p>大家都来扒一扒有几条是新发现呢?欢迎在评论中告诉我们!</p>
<h2 id="注">注</h2>
<p>英文出处:http://www.sitepoint.com/12-little-known-css-facts/</p>
<p>中文译文:http://www.w3cplus.com/css/12-little-known-css-facts.html</p>
一段代码详解JavaScript面向对象
2014-05-15T00:00:00+00:00
http://yanhaijing.com/javascript/2014/05/15/a-code-explain-javascript-oop
<p>不解释,ES3时代的经典做法</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 私有静态成员</span>
<span class="kd">var</span> <span class="nx">user</span> <span class="o">=</span> <span class="dl">""</span><span class="p">;</span>
<span class="c1">// 私有静态方法</span>
<span class="kd">function</span> <span class="nx">privateStaticMethod</span><span class="p">(){</span>
<span class="p">}</span>
<span class="nx">Box</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">value</span><span class="p">){</span>
<span class="c1">// 私有成员</span>
<span class="kd">var</span> <span class="nx">privateUser</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
<span class="c1">// 这个是私有方法</span>
<span class="kd">function</span> <span class="nx">privateMethod</span><span class="p">(){</span>
<span class="p">}</span>
<span class="c1">// 公有方法,因为能访问私有成员,也可以说是特权函数,也可以说是实例方法</span>
<span class="k">this</span><span class="p">.</span><span class="nx">getUser</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="k">return</span> <span class="nx">user</span><span class="p">;</span>
<span class="p">};</span>
<span class="c1">// 公有成员</span>
<span class="k">this</span><span class="p">.</span><span class="nx">user</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">};</span>
<span class="c1">// 公有共享方法</span>
<span class="nx">Box</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">sharedMethod</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{};</span>
<span class="c1">// 公有共享属性</span>
<span class="nx">Box</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">sharedProperty</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="c1">// 公有静态方法 </span>
<span class="nx">Box</span><span class="p">.</span><span class="nx">staticMethod</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){};</span>
<span class="c1">// 公有静态成员</span>
<span class="nx">Box</span><span class="p">.</span><span class="nx">staticProperty</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">})();</span>
</code></pre></div></div>
<p>时过境迁,下面来看看在ES6+中如何实现</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 私有静态成员</span>
<span class="kd">var</span> <span class="nx">user</span> <span class="o">=</span> <span class="dl">""</span><span class="p">;</span>
<span class="c1">// 私有静态方法</span>
<span class="kd">function</span> <span class="nx">privateStaticMethod</span><span class="p">(){</span>
<span class="p">}</span>
<span class="kd">class</span> <span class="nx">Box</span> <span class="p">{</span>
<span class="c1">// 私有成员,等同于 constructor内部,提案写法,stage-3</span>
<span class="err">#</span><span class="nx">privateUser</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="c1">// 公有成员,等同于 constructor内部,提案写法,stage-3</span>
<span class="nx">user1</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 私有成员</span>
<span class="kd">var</span> <span class="nx">privateUser</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
<span class="c1">// 这个是私有方法</span>
<span class="kd">function</span> <span class="nx">privateMethod</span><span class="p">(){</span>
<span class="p">}</span>
<span class="c1">// 公有方法,因为能访问私有成员,也可以说是特权函数,也可以说是实例方法</span>
<span class="k">this</span><span class="p">.</span><span class="nx">getUser</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
<span class="k">return</span> <span class="nx">user</span><span class="p">;</span>
<span class="p">};</span>
<span class="c1">// 公有成员</span>
<span class="k">this</span><span class="p">.</span><span class="nx">user2</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 公有共享属性</span>
<span class="nl">sharedProperty</span><span class="p">:</span> <span class="mi">1</span>
<span class="c1">// 公有共享方法</span>
<span class="nx">sharedMethod</span><span class="p">()</span> <span class="p">{}</span>
<span class="c1">// 公有静态方法</span>
<span class="kd">static</span> <span class="nx">staticMethod1</span><span class="p">()</span> <span class="p">{}</span>
<span class="c1">// 公有静态成员,提案写法,stage-3</span>
<span class="kd">static</span> <span class="nx">staticProperty1</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// 公有静态方法,和上面写法等同</span>
<span class="nx">Box</span><span class="p">.</span><span class="nx">sharedMethod2</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{};</span>
<span class="c1">// 公有静态成员,和上面写法等同</span>
<span class="nx">Box</span><span class="p">.</span><span class="nx">staticProperty2</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">})();</span>
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>更多js面向对象相关内容请看:</p>
<ul>
<li><a href="http://yanhaijing.com/javascript/2014/11/09/object-inherit-of-js/">JavaScript对象继承一瞥</a></li>
<li><a href="http://yanhaijing.com/javascript/2014/07/18/javascript-prototype/">JavaScript原型之路</a></li>
<li><a href="http://yanhaijing.com/javascript/2016/07/24/prototype-and-inheritance-of-js/">详解JavaScript中的原型和继承</a></li>
<li><a href="http://yanhaijing.com/javascript/2013/08/23/javascript-inheritance-how-to-shoot-yourself-in-the-foot-with-prototypes/">Javascript继承-原型的陷阱</a></li>
</ul>
JavaScript简易教程
2014-05-12T00:00:00+00:00
http://yanhaijing.com/javascript/2014/05/12/basic-javascript
<p>这是我所知道的最完整最简洁的JavaScript基础教程。</p>
<p>这篇文章带你尽快走进JavaScript的世界——前提是你有一些编程经验的话。本文试图描述这门语言的最小子集。我给这个子集起名叫做“JavaScript简易教程”,并推荐准备深入阅读细节和高级技巧之前的新手阅读。心急吃不了热豆腐。文章的最后提出如何进一步学习。</p>
<p><strong>警告:</strong>下面是我所描述的规则集和最佳实践。我喜欢整洁清晰(例如,你可以随时通过下面的目录快速导航)。规则是无懈可击的,但不可避免——每个人的理解不同。</p>
<h2 id="目录">目录</h2>
<ol>
<li><a href="#sect_conventions">本文约定</a></li>
<li><a href="#sect_nature">语言的性质</a></li>
<li><a href="#sect_syntax">语法</a></li>
<li><a href="#sect_variables_assignment">变量和赋值</a></li>
<li><a href="#sect_values">值</a></li>
<li><a href="#sect_booleans">布尔</a></li>
<li><a href="#sect_numbers">数字</a></li>
<li><a href="#sect_strings">字符串</a></li>
<li><a href="#sect_statements">语句</a></li>
<li><a href="#sect_functions">函数</a></li>
<li><a href="#sect_exceptions">异常处理</a></li>
<li><a href="#sect_strict_mode">严格模式</a></li>
<li><a href="#sect_var_scope_closures">变量作用域和闭包</a></li>
<li><a href="#sect_objects">对象和继承</a></li>
<li><a href="#sect_arrays">数组</a></li>
<li><a href="#sect_regexp">正则表达式</a></li>
<li><a href="#sect_math">数学</a></li>
<li><a href="#sect_standard_library">标准库的其他功能</a></li>
<li><a href="#sect_learn_next">下一步学什么?</a></li>
</ol>
<h2 id="sect_conventions">本文约定(Conventions used in this blog post)</h2>
<h3 id="命令行交互command-line-interaction">命令行交互(Command line interaction)</h3>
<p>每当我介绍一个新概念,我都会尝试通过JavaScript命令行进行演示。像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> 3 + 4
7
</code></pre></div></div>
<p>大于号后面的文本是用户输入内容。其他的都是JavaScript引擎的输出内容。此外,也可以使用console.log()来向控制台打印数据(这方法可以在大部分JavaScript引擎中工作,包括Node.js).</p>
<h3 id="查找文档finding-documentation">查找文档(Finding documentation)</h3>
<p>有时你看到一些函数或方法有超链接,你应该清楚他们的工作原理。如果没有,可以在<a href="https://developer.mozilla.org/en-US/">Mozilla Developer Network</a> (MDN)上查看细节,你也可以使用Google在MDN上查找文档。例如,下面是通过Google搜索数组的push()方法的例子:</p>
<p><a href="https://www.google.com/search?q=mdn+array+push">mdn array push</a></p>
<h2 id="sect_nature">语言的性质(The nature of the language)</h2>
<p>本节对JavaScript的性质简要介绍,以帮助你理解一些疑问。</p>
<h3 id="javascript-和-ecmascriptjavascript-versus-ecmascript">JavaScript 和 ECMAScript(JavaScript versus ECMAScript)</h3>
<p>编程语言称为JavaScript,语言标准称为ECMAScript。他们有不同名字的原因是因为“Java”已经被注册为商标(属于Oracle)。目前,只有Mozilla被正式允许使用“JavaScript”名称,因为很久以前他们等到一份许可。因此,开放的语言标准拥有不同的名字。当前的JavaScript版本是ECMAScript 5,ECMAScript 6当前是<a href="http://www.2ality.com/2012/11/guide-esnext.html">开发版</a>。</p>
<h3 id="影响influences">影响(Influences)</h3>
<p>JavaScript之父,Brendan Eich 别无选择必须<a href="http://yanhaijing.com/javascript/2013/06/22/javascript-designing-a-language-in-10-days">迅速创建一门语言</a>。(或者,更糟糕,Netscape将使用其他技术)。他借鉴了几门其他语言:</p>
<ul>
<li>JavaScript借鉴了Java的语法和如何区分<a href="#sect_prim_vs_obj">原始值和对象</a>。
JavaScript的函数设计受Scheme和AWK的启发——他们(函数)都是第一类(first-class)对象,并且在语言中广泛使用。<a href="#sect_closures">闭包</a>使他们变成强大的工具。</li>
<li>Self影响了JavaScript独一无二的面向对象编程(OOP)风格。它的<a href="http://www.2ality.com/2011/06/prototypes-as-classes.html">核心思想</a>(在这里我们没有提到)非常优雅,基于此创建的语言非常少。但一个<a href="#sect_constructors">简单的模式</a>(见后面)照顾大部分用例。JavaScript面向对象编程的杀手级特性是你可以直接创建对象。不需要先创建类或其他类似的东西。</li>
<li>Perl和Python<a href="http://www.2ality.com/2013/02/javascript-influences.html">影响了</a>JavaScript字符串,数组和正则表达式的操作。</li>
</ul>
<p>JavaScript直到ECMAScript 3才加入异常处理,这解释了为什么这门语言经常自动转换类型和经常静默失败:它最初不能抛出异常。</p>
<p>一方面,JavaScript有很多怪癖,并且确实很多功能(块级变量作用域(block-sciped variables),模块(modules)支持子类型(subtyping)等)。另一方面,它有几个非常强大的特性,允许你弥补上面的问题。在其他语言中,你要学习语言特性。在JavaScript中,你需要经常学习模式代替。</p>
<h3 id="深入阅读further-reading">深入阅读(Further reading)</h3>
<ul>
<li><a href="http://www.2ality.com/2011/03/javascript-how-it-all-began.html">JavaScript: how it all began</a></li>
<li><a href="http://www.2ality.com/2012/09/javascript-glass-half-full.html">JavaScript: the glass is half full</a> [什么让JavaScript如此吸引人?]</li>
<li><a href="http://www.2ality.com/2011/06/ecmascript.html">ECMAScript: ES.next versus ES 6 versus ES Harmony</a> [包括ECMAScript版本的一个简史]</li>
<li><a href="http://www.2ality.com/2013/02/javascript-influences.html">Perl and Python influences in JavaScript</a></li>
<li><a href="http://yanhaijing.com/javascript/2013/06/22/javascript-designing-a-language-in-10-days">Javascript:10天设计一门语言</a></li>
</ul>
<h2 id="sect_syntax">语法(Syntax)</h2>
<p>这节介绍一些JavaScript的基本语法规则。</p>
<h3 id="语句和表达式statements-versus-expressions">语句和表达式(Statements versus expressions)</h3>
<p>了解JavaScript的语法,这有助于了解(简而言之),它有两个主要的语法类型:语句和表达式。</p>
<ul>
<li>语句通常“做某些事情”。程序是一组语句序列。举个例子,下面声明(创建)一个 变量 foo:
var foo;</li>
<li>表达式产生值。他们通常位于赋值操作的邮编,函数参数,等。举个例子:
3 * 7</li>
</ul>
<p>语句和表达式之间的区别最好通过实例说明,JavaScript(像Java)有两种不同的方式实现if-then-else。一种是用语句:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var x;
if (y >= 0) {
x = y;
} else {
x = -y;
}
</code></pre></div></div>
<p>另一种是表达式:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var x = y >= 0 ? y : -y;
</code></pre></div></div>
<p>你可以将后者作为函数参数(但前者不行):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>myFunction(y >= 0 ? y : -y)
</code></pre></div></div>
<p>最后,每当JavaScript期待一个语句,你也可以用一个表达式代替。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>foo(bar(7, 1));
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">foo(...);</code>是一个语句(也叫做表达式语句),<code class="language-plaintext highlighter-rouge">bar(7, 1)</code>是一个表达式。他们都实现函数调用。</p>
<h3 id="流程控制语句和语句块control-flow-statements-and-blocks">流程控制语句和语句块(Control flow statements and blocks)</h3>
<p>流程控制语句,其语句体可以是单条语句。举两个例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (obj !== null) obj.foo();
while (x > 0) x--;
</code></pre></div></div>
<p>然而,任何语句总能被语句块代替,花括号包含零或多条语句。因此,你也可以这样写:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (obj !== null) {
obj.foo();
}
while (x > 0) {
x--;
}
</code></pre></div></div>
<p>在本文中,我们只是用后一种方式。</p>
<h3 id="分号semicolons">分号(Semicolons)</h3>
<p>分号在JavaScript中是<a href="http://www.2ality.com/2011/05/semicolon-insertion.html">可选的</a>。但省略他们可能带来意想不到的结果,所以我建议你不要那样做。</p>
<p>正如上面所看到的,分号作为语句的结尾,但语句块不需要。仅有一种情况下你将见到块后面有分号:函数表达式后面的函数体块。表达式作为语句的结尾,后面是分号:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var x = 3 * 7;
var f = function () { };
</code></pre></div></div>
<h3 id="注释comments">注释(Comments)</h3>
<p>JavaScript有两种注释方式:单行注释和多行注释。单行注释以//开头,以换行符结尾:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>x++; // 单行(single-line)注释
</code></pre></div></div>
<p>多行注释用/**/包裹</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/*
这是多行注释
多行哦
*/
</code></pre></div></div>
<h3 id="深入阅读">深入阅读</h3>
<ul>
<li><a href="http://www.2ality.com/2012/09/expressions-vs-statements.html">Expressions versus statements in JavaScript</a></li>
<li><a href="http://www.2ality.com/2011/05/semicolon-insertion.html">Automatic semicolon insertion in JavaScript</a></li>
</ul>
<h2 id="sect_variables_assignment">变量和赋值(Variables and assignment)</h2>
<p>JavaScript中的变量在使用之前必须先声明:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var foo; // 声明变量“foo”
</code></pre></div></div>
<h3 id="赋值assignment">赋值(Assignment)</h3>
<p>你可以在生命变量的同时给它赋值:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var foo = 6;
</code></pre></div></div>
<p>你也可以给已经存在的变量重新赋值:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>foo = 4; // 更改变量的值
</code></pre></div></div>
<h3 id="复合赋值操作符compount-assignment-operators">复合赋值操作符(Compount assignment operators)</h3>
<p>有很多符合赋值操作符,例如+=。下面的两个赋值操作等价:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>x += 1;
x = x + 1;
</code></pre></div></div>
<h3 id="identifiers">标识符和变量名(Identifiers and variable names)</h3>
<p>标识符就是事物的名字,在JavaScript中他们扮演不同的语法角色。例如,变量的名称是一个标识符。</p>
<p>大体上,标识符的第一个字符可以是任何Unicode字符,美元标志符($)或下划线(_)。后面的字符可以是任意字符和数字。因此,下面全是合法的标识符:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>arg0
_tmp
$elem
π
</code></pre></div></div>
<p>一些标识符是“保留关键字”——他们是语法的一部分,不能用作变量名:</p>
<blockquote>
<p>arguments break case catch class const continue debugger default delete do else enum eval export extends false finally for function if implements import in instanceof interface let new null package private protected public return static super switch this throw true try typeof var void while with yield</p>
</blockquote>
<p>从技术上讲,下面三个标识符不是保留字,但也不应该作为变量名:</p>
<blockquote>
<p>Infinity NaN undefined</p>
</blockquote>
<h3 id="深入阅读-1">深入阅读</h3>
<ul>
<li><a href="http://mathiasbynens.be/notes/javascript-identifiers">Valid JavaScript variable names</a> [by Mathias Bynens]</li>
</ul>
<h2 id="sect_values">值(Values)</h2>
<p>JavaScript有所有我们期待的编程语言值类型:布尔,数字,字符串,数组等。JavaScript中的所有值都有属性。每个属性有一个键(或名字)和一个值。考虑记录的域(fields of record)。你可以使用点(.)操作符读取属性:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>value.propKey
</code></pre></div></div>
<p>举个例子:字符串“abc”有属性lenght(长度)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > var str = 'abc';
> str.length
3
</code></pre></div></div>
<p>上面的代码也可以写成下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> 'abc'.length
3
</code></pre></div></div>
<p>点操作符也可以用来给属性赋值:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > var obj = {}; // 空对象
> obj.foo = 123; // 创建属性“foo”,设置它为123
123
> obj.foo
123
</code></pre></div></div>
<p>你也可以通过它(.)调用方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> 'hello'.toUpperCase()
'HELLO'
</code></pre></div></div>
<p>上面,我们在值“hello”上面调用方法 toUpperCase()。</p>
<h3 id="sect_prim_vs_obj">原始类型值和对象(Primitive values versus objects)</h3>
<p>JavaScript定义了不同值之间的区别:</p>
<ul>
<li>原始值包括:boolean,number,string,null和undefined,</li>
<li>所有其他的值都是对象。实际上对象被定义为——所有不为原始类型的值。</li>
</ul>
<p>两者之间的主要区别在于他们是如何被比较的:每一个对象有一个独一无二的标志,并且仅和自己相等:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var obj1 = {}; // 一个空对象
> var obj2 = {}; // 另一个空对象
> obj1 === obj2
false
> obj1 === obj1
true
</code></pre></div></div>
<p>相反,所有原始值只要编码值相同就被认为是相同的:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var prim1 = 123;
> var prim2 = 123;
> prim1 === prim2
true
</code></pre></div></div>
<p>接下来的两节介绍原始值和对象的更多细节。</p>
<h3 id="原始类型值primitive-values">原始类型值(Primitive values)</h3>
<p>下面的全是原始类型值(简称:原始值):</p>
<ul>
<li><a href="#sect_booleans">布尔类型</a>:true,false</li>
<li><a href="#sect_numbers">数字类型</a>:1736,1.351</li>
<li><a href="#sect_strings">字符串类型</a>: ‘abc’,”abc”</li>
<li>两个“<a href="#sect-non-values">无值(non-values)</a>”:undefined,null</li>
</ul>
<p>原始值的特征:</p>
<ul>
<li>
<p><strong>值做比较时:</strong>“内容”做比较。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > 3 === 3
true
> 'abc' === 'abc'
true
</code></pre></div> </div>
</li>
<li>
<p><strong>无法更改:</strong>值的属性无法更改,无法添加和移除属性。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > var str = 'abc';
> str.foo = 3; // try to create property `foo` ⇒ no effect
> str.foo // unknown property
undefined
</code></pre></div> </div>
<p>(获取未知属性总返回undefined)</p>
</li>
<li>
<p><strong>原始值的集合是固定的(fixed set of values):</strong>你不能自定义原始值。</p>
</li>
</ul>
<h3 id="对象objects">对象(Objects)</h3>
<p>所有非原始值(non-primitive)的值都是对象。最常见的几种对象类型是:</p>
<ul>
<li>
<p><a href="#sect_objects">简单对象</a>(类型是Object)能通过对象字面量创建:</p>
<p>{
firstName: ‘Jane’,
lastName: ‘Doe’
}</p>
<p>上面的对象有两个属性:firstName属性的值是“Jane”,lastName属性的值是“Doe”。</p>
</li>
<li>
<p><a href="#sect_arrays">数组</a>(类型是 Array)能通过数组字面量创建:</p>
<p>[ ‘apple’, ‘banana’, ‘cherry’ ]</p>
<p>上面的数组有三个元素,可以通过数字索引访问。例如“apple”的索引是0.</p>
</li>
<li>
<p><a href="#sect_regexp">正则表达式对象</a>(类型是 RegExp)能通过正则表达式字面量创建。</p>
<p>/^a+b+$/</p>
</li>
</ul>
<p>对象的特征:</p>
<ul>
<li>
<p><strong>比较的是引用:</strong>比较的是标识符,每个值有自己的标识符。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > {} === {} // 两个不同的空对象
false
> var obj1 = {};
> var obj2 = obj1;
> obj1 === obj2
true
</code></pre></div> </div>
</li>
<li>
<p><strong>默认可以更改。</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > var obj = {};
> obj.foo = 123;
> obj.foo
123
</code></pre></div> </div>
</li>
</ul>
<p>-** 用户可扩展(user-extensible):**你可以通过<a href="#sect_constructors">构造函数</a>定义新的对象类型。</p>
<p><a href="#sect_arrays">数组</a>所有的数据结构(如)都是对象,但并不是所有的对象都是数据结构。例如:<a href="#sect_regexp">正则表达式是对象</a>,但不是一个数据结构。</p>
<h3 id="sect-non-values">undefined 和 null(undefined and null)</h3>
<p>多少有些不必要,JavaScript有两个“无值(non-values)”:undefined 和 null。</p>
<ul>
<li>
<p>undefined的意思是“没有值(no value)”。为初始化的变量是undefined:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > var foo;
> foo
undefined
</code></pre></div> </div>
<p>如果你读取不存在的属性,将返回undefined:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > var obj = {}; // 空对象
> obj.foo
undefined
</code></pre></div> </div>
<p>未传递的参数也是undefined:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > function f(x) { return x }
> f()
undefined
</code></pre></div> </div>
</li>
<li>
<p>null的意思是“没有对象(no object)”。它被用来表示对象的无值(参数,链上的对象等)。</p>
</li>
</ul>
<p>通常情况下你应该把undefined和null看成是等价的,如果他们代表相同意义的无值的话。检查他们的一种方式是通过严格比较:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (x === undefined || x === null) {
...
}
</code></pre></div></div>
<p>另一种在实际中使用的方法是认为undefined 和 null 都是<a href="#sect_truthy_falsy">false</a>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (!x) {
...
}
</code></pre></div></div>
<p><strong>警告:</strong>false,0,NaN 和 “” 都被当作false。</p>
<h3 id="包装类型wrapper-types">包装类型(Wrapper types)</h3>
<p>对象类型的实例Foo(包括内建类型,例如Array和其他自定义类型)从对象Foo.prototype上获取方法。你可以通过读这个方法但不调用它的方式验证这点:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> [].push === Array.prototype.push
true
</code></pre></div></div>
<p>相反,原始类型是没有类型的,所以每个原始类型有一个关联类型,称之为包装类型:</p>
<ul>
<li>
<p>布尔值的包装类型是 Boolean。布尔值从Boolean.prototype上获取方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > true.toString === Boolean.prototype.toString
true
</code></pre></div> </div>
<p>注意包装类型的名字首个字母是大写的B。如果在JavaScript中布尔值的类型可以访问,那么它可能会被成为布尔对象。</p>
</li>
<li>数字值的包装类型是Number。</li>
<li>字符串值的包装类型是String。</li>
</ul>
<p>包装类型也有实例(他们的实例是对象),但不常用。相反,包装类型有其他用处:如果你将他们作为函数调用,他们可以将值转换为原始类型。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> Number('123')
123
> String(true)
'true'
</code></pre></div></div>
<h3 id="通过typeof-和-instanceof-将值分类categorizing-values-via-typeof-and-instanceof">通过typeof 和 instanceof 将值分类(Categorizing values via typeof and instanceof)</h3>
<p>有两个操作符可以用来将值分类:typeof 主要用来操作原始值,instanceof 主要用来造作对象。</p>
<p><strong>typeof</strong> 使用方法如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>typeof «value»
</code></pre></div></div>
<p>它返回描述 value “类型”的一个字符串。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> typeof true
'boolean'
> typeof 'abc'
'string'
> typeof {} // 空对象字面量
'object'
> typeof [] // 空数组字面量
'object'
</code></pre></div></div>
<p>下标列出了所有typeof的结果:</p>
<table class="table table-bordered">
<tbody>
<tr>
<td><strong>操作数</strong></td>
<td><strong>结果</strong></td>
</tr>
<tr>
<td><tt>undefined</tt></td>
<td><tt>'undefined'</tt></td>
</tr>
<tr>
<td><tt>null</tt></td>
<td><tt>'object'</tt></td>
</tr>
<tr>
<td>Boolean value</td>
<td><tt>'boolean'</tt></td>
</tr>
<tr>
<td>Number value</td>
<td><tt>'number'</tt></td>
</tr>
<tr>
<td>String value</td>
<td><tt>'string'</tt></td>
</tr>
<tr>
<td>Function</td>
<td><tt>'function'</tt></td>
</tr>
<tr>
<td>All other values</td>
<td><tt>'object'</tt></td>
</tr>
</tbody>
</table>
<p>有两个东西和我们所说的原始值和对象是矛盾的:</p>
<ul>
<li>函数的类型是“function”而不是“object”。鉴于函数(类型为“function”)是对象(类型是对象)的子类型,这不是一个错误。</li>
<li>null的类型是“object”。这是一个bug,但从没被修复,因为修复后会破坏现有的代码。</li>
</ul>
<p><strong>instanceof</strong>使用方法如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>«value» instanceof «Constr»
</code></pre></div></div>
<p>如果 value 是一个对象,并且value 是由构造函数Constr创建的(考虑:类)。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var b = new Bar(); // 通过构造函数Bar创建对象
> b instanceof Bar
true
> {} instanceof Object
true
> [] instanceof Array
true
> [] instanceof Object // 数字是对象的子类型
true
</code></pre></div></div>
<h3 id="深入阅读-2">深入阅读</h3>
<ul>
<li><a href="http://www.2ality.com/2013/01/categorizing-values.html">Categorizing values in JavaScript</a></li>
<li><a href="http://www.2ality.com/2011/11/improving-typeof.html">Improving the JavaScript typeof operator</a></li>
<li><a href="http://yanhaijing.com/javascript/2014/01/05/exploring-the-abyss-of-null-and-undefined-in-javascript">探索javascript中null和undefined的深渊</a></li>
</ul>
<h2 id="sect_booleans">布尔(Booleans)</h2>
<p>布尔类型原始值包括true和false。下面的操作符产生布尔值:</p>
<ul>
<li>
<table>
<tbody>
<tr>
<td>二元逻辑运算符:&&(与),</td>
<td> </td>
<td>(或)</td>
</tr>
</tbody>
</table>
</li>
<li>前缀逻辑运算符:!(非)</li>
<li>等值运算符:=== !== == !=</li>
<li>比较运算符(字符串或数字):> >= < <=</li>
</ul>
<h3 id="sect_truthy_falsy">真值和假值(Truthy and falsy)</h3>
<p>每当JavaScript希望一个布尔值时(例如:if语句的条件),可以使用任何值。它将被理解(转换)为true或false。下面的值被理解为false:</p>
<ul>
<li>undefined, null</li>
<li>布尔: false</li>
<li>数字: -0, NaN</li>
<li>字符串: ‘’</li>
</ul>
<p>所有其他值被认为true。被理解为false的值成为假值(falsy),被理解为true的值成为真值(truthy)。可以使用Boolean作为函数测试值被理解为什么。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> Boolean(undefined)
false
> Boolean(0)
false
> Boolean(3)
true
</code></pre></div></div>
<h3 id="二元逻辑运算符binary-logical-operators">二元逻辑运算符(Binary logical operators)</h3>
<p>JavaScript中的二元逻辑运算符是短路运算——如果第一个操作数可以确定结果,第二个操作数将不被验证。例如,在下面的代码中,函数foo()不会被调用。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>false && foo()
true || foo()
</code></pre></div></div>
<p>此外,二元逻辑运算符返回操作数中的一个——可能是一个布尔值,也可能不是。一张真值表用来决定返回哪个值:</p>
<ul>
<li>
<p>与:如果第一个操作数是假值,返回第一个。否则返回第二个操作数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > NaN && 'abc'
NaN
> 123 && 'abc'
'abc'
</code></pre></div> </div>
</li>
<li>
<p>或:如果第一个操作数是真值,返回第一个。否则,返回第二个操作数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > 'abc' || 123
'abc'
> '' || 123
123
</code></pre></div> </div>
</li>
</ul>
<h3 id="等值运算符equality-operators">等值运算符(Equality operators)</h3>
<p>在JavaScript中检测相等,你可以使用严格相等(===)和严格不等(!==)。或者你也可以使用非严格相等(==)和非严格不等(!=)。经验规则:总是用严格运算符,假装非严格运算符不存在。严格相等更安全。</p>
<h3 id="深入阅读-3">深入阅读</h3>
<ul>
<li><a href="http://www.2ality.com/2011/06/javascript-equality.html">Equality in JavaScript: === versus ==</a></li>
<li><a href="http://yanhaijing.com/javascript/2014/04/25/strict-equality-exemptions">在JavaScript中什么时候使用==是正确的?</a></li>
</ul>
<h2 id="sect_numbers">数字(Numbers)</h2>
<p>JavaScript中的所有数字都是浮点型(虽然大部分的JavaScript引擎内部也使用整数)。至于为什么这样设计,查看这里(<a href="http://yanhaijing.com/javascript/2014/03/14/what-every-javascript-developer-should-know-about-floating-points">每一个JavaScript开发者应该了解的浮点知识</a>)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> 1 === 1.0
true
</code></pre></div></div>
<p>特殊数字:</p>
<ul>
<li>
<p>NaN (“不是一个数字 not a number”): 错误值。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > Number('xyz') // 'xyz' 不能被转换为数字
NaN
</code></pre></div> </div>
</li>
<li>
<p>Infinity:也是最大错误值(溢出).</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > 3 / 0
Infinity
> Math.pow(2, 1024) // 数字太大了
Infinity
</code></pre></div> </div>
<p>Infinity 有时很有用,因为它比任何其他数字都大。同样,-Infinity 比其他任何数字都小。</p>
</li>
<li>
<p>JavaScript有<a href="http://www.2ality.com/2012/03/signedzero.html">两个零</a>,+0 和 -0。它通常不让你看到,并简单将两个零都显示为0:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> > +0
0
> -0
0
</code></pre></div> </div>
<p>因此最好假装只有一个零(正如我们看到假值时所做的那样:-0 和 +0 都是假值)。</p>
</li>
</ul>
<h3 id="运算符operators">运算符(Operators)</h3>
<p>JavaScript中有下列<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators">算数运算符</a>:</p>
<ul>
<li>加: number1 + number2</li>
<li>减: number1 - number2</li>
<li>乘: number1 * number2</li>
<li>除: number1 / number2</li>
<li>求模: number1 % number2</li>
<li>自增: ++variable, variable++</li>
<li>自减: –variable, variable–</li>
<li>负值: -value</li>
<li>转换为数字: +value</li>
</ul>
<p>全局对象<a href="#sect_math">Math</a>通过函数提供更多算数运算操作。</p>
<p>JavaScript中也有<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators">位运算符</a>(例如:位与 &)。</p>
<h3 id="深入阅读-4">深入阅读</h3>
<p>在2ality有<a href="http://www.2ality.com/search/label/numbers">一系列</a>博文介绍这些内容,例如:</p>
<ul>
<li><a href="http://www.2ality.com/2012/04/number-encoding.html">How numbers are encoded in JavaScript</a></li>
<li><a href="http://www.2ality.com/2012/03/signedzero.html">JavaScript’s two zeros</a></li>
<li><a href="http://www.2ality.com/2012/02/js-integers.html">Integers and shift operators in JavaScript</a></li>
<li><a href="http://www.2ality.com/2012/02/nan-infinity.html">NaN and Infinity in JavaScript</a></li>
<li><a href="http://www.2ality.com/2012/07/large-integers.html">Working with large integers in JavaScript</a></li>
</ul>
<h2 id="sect_strings">字符串(Strings)</h2>
<p>字符串可以直接通过字符串字面量创建。这些字面量被单引号或双引号包裹。反斜线(\)转义字符并且产生一些控制字符。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'abc'
"abc"
'Did she say "Hello"?'
"Did she say \"Hello\"?"
'That\'s nice!'
"That's nice!"
'Line 1\nLine 2' // 换行
'Backlash: \\'
</code></pre></div></div>
<p>可以通过方括号访问单个字符:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var str = 'abc';
> str[1]
'b'
</code></pre></div></div>
<p>length属性是字符串的字符数量。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> 'abc'.length
3
</code></pre></div></div>
<p><strong>提醒:</strong>字符串是不可变的,如果你想改变现有字符串,你需要创建一个行的字符串。</p>
<h3 id="字符串运算符string-operators">字符串运算符(String operators)</h3>
<p>字符串可以通过加号操作符(+)拼接,如果其中一个操作数为字符串,会将另一个操作数也转换为字符串。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var messageCount = 3;
> 'You have '+messageCount+' messages'
'You have 3 messages'
</code></pre></div></div>
<p>连续执行拼接操作可以使用 += 操作符:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var str = '';
> str += 'Multiple ';
> str += 'pieces ';
> str += 'are concatenated.';
> str
'Multiple pieces are concatenated.'
</code></pre></div></div>
<h3 id="字符串方法string-methods">字符串方法(String methods)</h3>
<p>字符串有许多有用的<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/prototype">方法</a>。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> 'abc'.slice(1) // 复制子字符串
'bc'
> 'abc'.slice(1, 2)
'b'
> '\t xyz '.trim() // 移除空白字符
'xyz'
> 'mjölnir'.toUpperCase()
'MJÖLNIR'
> 'abc'.indexOf('b') // 查找字符串
1
> 'abc'.indexOf('x')
-1
</code></pre></div></div>
<h3 id="深入阅读-5">深入阅读</h3>
<ul>
<li><a href="http://www.2ality.com/2011/10/string-concatenation.html">String concatenation in JavaScript</a></li>
<li><a href="http://www.2ality.com/2012/09/javascript-quotes.html">JavaScript: single quotes or double quotes?</a></li>
</ul>
<h2 id="sect_statements">语句(Statements)</h2>
<h3 id="条件conditionals">条件(Conditionals)</h3>
<p>if语句通过布尔条件决定执行那个分支:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (myvar === 0) {
// then
}
if (myvar === 0) {
// then
} else {
// else
}
if (myvar === 0) {
// then
} else if (myvar === 1) {
// else-if
} else if (myvar === 2) {
// else-if
} else {
// else
}
</code></pre></div></div>
<p>下面的switch语句,furit的值决定那个分支被执行。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>switch (fruit) {
case 'banana':
// ...
break;
case 'apple':
// ...
break;
default: // 所有其他情况
// ...
}
</code></pre></div></div>
<h3 id="循环loops">循环(Loops)</h3>
<p>for 循环的格式如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for(初始化; 当条件成立时循环; 下一步操作)
</code></pre></div></div>
<p>例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for (var i=0; i < arr.length; i++) {
console.log(arr[i]);
}
</code></pre></div></div>
<p>当条件成立时while循环继续循环它的循环体。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 和上面的for循环相等
var i = 0;
while (i < arr.length) {
console.log(arr[i]);
i++;
}
</code></pre></div></div>
<p>当条件成立时,do-while循环继续循环。由于条件位于循环体之后,所以循环体总是被至少至少执行一次。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>do {
// ...
} while(条件);
</code></pre></div></div>
<p><strong>在所有的循环中:</strong></p>
<ul>
<li>break中断循环</li>
<li>continue开始一个新的循环迭代</li>
</ul>
<h2 id="sect_functions">函数(Functions)</h2>
<p>定义函数的一种方法是通过函数声明:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function add(param1, param2) {
return param1 + param2;
}
</code></pre></div></div>
<p>上面的代码定义一个名称叫做add的函数,有两个参数param1和param2,并且返回参数的和。下面是你如何调用这个函数:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> add(6, 1)
7
> add('a', 'b')
'ab'
</code></pre></div></div>
<p>另一种定义add()函数的方法是通过函数表达式:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var add = function (param1, param2) {
return param1 + param2;
};
</code></pre></div></div>
<p>函数表达式产生一个值,因此可以直接将函数作为参数传递给其他函数:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>someOtherFunction(function (p1, p2) { ... });
</code></pre></div></div>
<h3 id="函数声明提升function-declarations-are-hoisted">函数声明提升(Function declarations are hoisted)</h3>
<p>函数声明会被提升,他们全被移动到当前作用域开始之处。这允许你在函数声明之前调用它们:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function foo() {
bar(); // 没问题,bar被提升
function bar() {
...
}
}
</code></pre></div></div>
<p>注意:虽然变量声明也会被<a href="#sect_var_scope_closures">提升</a>,但赋值的过程不会被提升:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function foo() {
bar(); // 有问题,bar是undefined
var bar = function () {
// ...
};
}
</code></pre></div></div>
<h3 id="特殊变量argumentsthe-special-variable-arguments">特殊变量arguments(The special variable arguments)</h3>
<p>在JavaScript中你可以调用任意函数并传递任意数量的参数——语言绝不会抱怨。那可以工作,然而,使所有参数可访问需要通过特殊变量 arguments。arguments 看起来像数组,但它没有数组的方法。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> function f() { return arguments }
> var args = f('a', 'b', 'c');
> args.length
3
> args[0] // 获取索引为0的元素
'a'
</code></pre></div></div>
<h3 id="太多或太少参数too-many-or-too-few-arguments">太多或太少参数(Too many or too few arguments)</h3>
<p>让我们通过下面的函数探索JavaScript中传递太多或太少参数时如何处理(函数 toArray在<a href="#sect_toarray">后面提到</a>)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function f(x, y) {
console.log(x, y);
console.log(toArray(arguments));
}
</code></pre></div></div>
<p>多出的参数将被忽略(可以通过arguments访问):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> f('a', 'b', 'c')
a b
[ 'a', 'b', 'c' ]
</code></pre></div></div>
<p>缺少的参数将是undefined:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> f('a')
a undefined
[ 'a' ]
> f()
undefined undefined
[]
</code></pre></div></div>
<h3 id="可选参数optional-parameters">可选参数(Optional parameters)</h3>
<p>下面是一个常见模式,给参数设置默认值:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function pair(x, y) {
x = x || 0; // (*)
y = y || 0;
return [ x, y ];
}
</code></pre></div></div>
<table>
<tbody>
<tr>
<td>在(*)这行,如果x是真值(除了:null,undefined 等),</td>
<td> </td>
<td>操作符返回x。否则,它返回第二个操作数。</td>
</tr>
</tbody>
</table>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> pair()
[ 0, 0 ]
> pair(3)
[ 3, 0 ]
> pair(3, 5)
[ 3, 5 ]
</code></pre></div></div>
<h3 id="强制数量enforcing-an-arity">强制数量(Enforcing an arity)</h3>
<p>如果你想强制参数的数量,你可以检测arguments.length:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function pair(x, y) {
if (arguments.length !== 2) {
throw new Error('Need exactly 2 arguments');
}
...
}
</code></pre></div></div>
<h3 id="sect_toarray">将arguments 转换为数组(Converting arguments to an array)</h3>
<p>arguments 不是一个数组,它仅仅是<a href="http://www.2ality.com/2013/05/quirk-array-like-objects.html">类数组</a>(array-like):它有一个length属性,并且你可以通过方括号索引方式访问它的元素。然而,你不能移除元素,或在它上面调用任何数组方法。因此,有时你需要将其转换为数组。这就是下面函数的作用。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function toArray(arrayLikeObject) {
return [].slice.call(arrayLikeObject);
}
</code></pre></div></div>
<h3 id="深入阅读-6">深入阅读</h3>
<ul>
<li><a href="http://www.2ality.com/2013/05/quirk-parameters.html">JavaScript quirk 5: parameter handling</a></li>
</ul>
<h2 id="sect_exceptions">异常处理(Exception handling)</h2>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch">异常处理</a>最常见的方式像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function throwException() {
throw new Error('Problem!');
}
try {
throwException();
} catch (e) {
console.log(e); // 错误:信息
console.log(e.stack); // 非标准,但大部分浏览器支持
}
</code></pre></div></div>
<p>try分支包裹易出错的代码,如果try分支内部抛出异常,catch分支将会执行。</p>
<h3 id="深入阅读-7">深入阅读</h3>
<ul>
<li><a href="http://www.2ality.com/2011/12/subtyping-builtins.html">Subtyping JavaScript builtins in ECMAScript 5 [especially relevant for errors]</a></li>
</ul>
<h2 id="sect_strict_mode">严格模式(Strict mode)</h2>
<p><a href="http://www.2ality.com/2011/01/javascripts-strict-mode-summary.html">严格模式</a>开启检测和一些其他措施,是JavaScript变成更整洁的语言。推荐使用严格模式。为了开启严格模式,只需在JavaScript文件或script标签第一行添加如下语句:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'use strict';
</code></pre></div></div>
<p>你也可以在每个函数上选择性开启严格模式,只需将上面的代码放在函数的开头:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function functionInStrictMode() {
'use strict';
}
</code></pre></div></div>
<p>下面的两小节看下严格模式的三大好处。</p>
<h3 id="明确错误explicit-errors">明确错误(Explicit errors)</h3>
<p>让我们看一个例子,严格模式给我们明确的错误,否则JavaScript总是静默失败:下面的函数 f() 执行一些非法操作,它试图更改所有字符串都有的只读属性——length:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function f() {
'abc'.length = 5;
}
</code></pre></div></div>
<p>当你调用上面的函数,它静默失败,赋值操作被简单忽略。让我们将 f() 在严格模式下运行:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function f_strict() {
'use strict';
'abc'.length = 5;
}
</code></pre></div></div>
<p>现在浏览器报给我们一些错误:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> f_strict()
TypeError: Cannot assign to read only property 'length' of abc
</code></pre></div></div>
<h3 id="不是方法的函数中的thisthis-in-non-method-functions">不是方法的函数中的this(this in non-method functions)</h3>
<p>在严格模式下,不作为方法的函数中的this值是undefined:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function f_strict() {
'use strict';
return this;
}
console.log(f_strict() === undefined); // true
</code></pre></div></div>
<p>在非严格模式下,this的值是被称作全局对象(global object)(在浏览器里是window):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function f() {
return this;
}
console.log(f() === window); // true
</code></pre></div></div>
<h3 id="不再自动创建全局变量no-auto-created-global-variables">不再自动创建全局变量(No auto-created global variables)</h3>
<p>在非严格模式下,如果你给不存在的变量赋值,JavaScript会自动创建一个全局变量:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> function f() { foo = 5 }
> f() // 不会报错
> foo
5
</code></pre></div></div>
<p>在严格模式下,这会产生一个错误:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> function f_strict() { 'use strict'; foo2 = 4; }
> f_strict()
ReferenceError: foo2 is not defined
</code></pre></div></div>
<h3 id="深入阅读-8">深入阅读</h3>
<ul>
<li><a href="http://www.2ality.com/2011/01/javascripts-strict-mode-summary.html">JavaScript’s strict mode: a summary</a></li>
<li><a href="http://yanhaijing.com/javascript/2013/12/28/demystifying-this-in-javascript">揭秘javascript中谜一样的this</a></li>
<li><a href="http://yanhaijing.com/javascript/2014/04/30/javascript-this-keyword">JavaScript中的this关键字</a></li>
</ul>
<h2 id="sect_var_scope_closures">变量作用域和闭包(Variable scoping and closures)</h2>
<p>在JavaScript中,你必须通过var声明变量,在你使用它们之前:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var x;
> x = 3;
> y = 4;
ReferenceError: y is not defined
</code></pre></div></div>
<p>你可以用一条var语句声明和初始化多个变量:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var x = 1, y = 2, z = 3;
</code></pre></div></div>
<p>但我建议每个变量使用一条语句。因此,我将上面的语句重写为:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var x = 1;
var y = 2;
var z = 3;
</code></pre></div></div>
<p>由于提升(见下文),最好在函数顶部声明变量。</p>
<h3 id="变量和函数作用域variables-are-function-scoped">变量和函数作用域(Variables are function-scoped)</h3>
<p>变量的作用域总是整个函数(没有块级作用域)。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function foo() {
var x = -3;
if (x < 0) { // (*)
var tmp = -x;
...
}
console.log(tmp); // 3
}
</code></pre></div></div>
<p>我们可以看到tmp变量不仅在(*)所在行的语句块,它在整个函数内都存在。</p>
<h3 id="变量提升variables-are-hoisted">变量提升(Variables are hoisted)</h3>
<p>变量声明会被提升:声明会被移到函数的顶部,但赋值过程不会。举个例子,在下面的函数中(*)行位置声明了一个变量。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function foo() {
console.log(tmp); // undefined
if (false) {
var tmp = 3; // (*)
}
}
</code></pre></div></div>
<p>在内部,上面的函数被执行像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function foo() {
var tmp; // declaration is hoisted
console.log(tmp);
if (false) {
tmp = 3; // assignment stays put
}
}
</code></pre></div></div>
<h3 id="sect_closures">闭包(Closures)</h3>
<p>每个函数保持和函数体内部变量的连接,甚至离开创建它的作用域之后。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function createIncrementor(start) {
return function () { // (*)
return start++;
}
}
</code></pre></div></div>
<p>在(*)行开始的函数在它创建时保留上下文,并在内部保存一个start活动值:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var inc = createIncrementor(5);
> inc()
5
> inc()
6
> inc()
7
</code></pre></div></div>
<p>闭包是一个函数加上和其作用域链的链接。因此,createIncrementor() 返回的是一个闭包。</p>
<h3 id="iife模拟块级作用域iife-simulating-block-scoping">IIFE:模拟块级作用域(IIFE: Simulating block scoping)</h3>
<p>有时你想模拟一个块,例如你想将变量从全局作用域隔离。完成这个工作的模式叫做 IIFE(立即执行函数表达式(Immediately Invoked Function Expression)):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function () { // 块开始
var tmp = ...; // 非全局变量
}()); // 块结束
</code></pre></div></div>
<p>上面你会看到函数表达式被立即执行。外面的括号用来阻止它被解析成函数声明;只有函数表达式能被立即调用。函数体产生一个新的作用域并使 tmp 变为局部变量。</p>
<h3 id="闭包实现变量共享inadvertent-sharing-via-closures">闭包实现变量共享(Inadvertent sharing via closures)</h3>
<p>下面是个经典问题,如果你不知道它那它会让你费尽思量。因此,先浏览下,先对问题有个大概的了解。</p>
<p>闭包保持和外部变量的连接,有时可能和你想像的行为不一致:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var result = [];
for (var i=0; i < 5; i++) {
result.push(function () { return i }); // (*)
}
console.log(result[1]()); // 5 (不是 1)
console.log(result[3]()); // 5 (不是 3)
</code></pre></div></div>
<p>(*)行的返回值总是当前的i值,而不是当函数被创建时的i值。当循环结束后,i的值是5,这是为什么数组中的所有函数的返回值总是一样的。如果你想捕获当前变量的快照,你可以使用 IIFE:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for (var i=0; i < 5; i++) {
(function (i2) {
result.push(function () { return i2 });
}(i)); // 复制当前的i
}
</code></pre></div></div>
<h3 id="深入阅读-9">深入阅读</h3>
<ul>
<li><a href="http://www.2ality.com/2012/11/var-statement-rules.html">Variable declarations: three rules you can break</a></li>
<li><a href="http://www.2ality.com/2013/05/quirk-variable-scope.html">JavaScript quirk 6: the scope of variables</a></li>
<li><a href="http://www.2ality.com/2013/05/quirk-closures.html">JavaScript quirk 7: inadvertent sharing of variables via closures</a></li>
<li><a href="http://yanhaijing.com/javascript/2013/08/30/understanding-scope-and-context-in-javascript">认识javascript中的作用域和上下文</a></li>
<li><a href="http://yanhaijing.com/javascript/2014/04/30/JavaScript-Scoping-and-Hoisting">JavaScript的作用域和提升机制</a></li>
<li><a href="http://yanhaijing.com/javascript/2014/04/29/what-is-the-execution-context-in-javascript">了解JavaScript的执行上下文</a></li>
</ul>
<h2 id="sect_objects">对象和继承(Objects and inheritance)</h2>
<p>和所有的<a href="#sect_values">值类型</a>一样,对象有属性。事实上,你可以将对象当作一组属性的集合,每个属性是一对(键和值)。键是字符串,值可以是任意JavaScript值。到目前为止,我们仅仅见过键是<a href="#identifiers">标识符</a>的属性,因为点操作符处理的键必须为标识符。在这节,你讲见到另一种方法属性的方法,能将任意字符串作为键。</p>
<h3 id="单个对象single-objects">单个对象(Single objects)</h3>
<p>在JavaScript中,你可以直接创建对象,通过对象字面量:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var jane = {
name: 'Jane',
describe: function () {
'use strict';
return 'Person named '+this.name;
}
};
</code></pre></div></div>
<p>上面的对象有两个属性:name 和 describe。你能读(“get”)和 写(“set”)属性:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> jane.name // get
'Jane'
> jane.name = 'John'; // set
> jane.newProperty = 'abc'; // 自动创建
</code></pre></div></div>
<p>属性是函数如 describe 可以被当作方法调用。当调用他们时可以在它们内部通过this引用对象。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> jane.describe() // 调用方法
'Person named John'
> jane.name = 'Jane';
> jane.describe()
'Person named Jane'
</code></pre></div></div>
<p>in 操作符用来检测一个属性是否存在:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> 'newProperty' in jane
true
> 'foo' in jane
false
</code></pre></div></div>
<p>你若你读取一个不存在的属性,你将得到undefined值。因此上面的两个检查也可以像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> jane.newProperty !== undefined
true
> jane.foo !== undefined
false
</code></pre></div></div>
<p>delete操作符用来删除一个属性:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> delete jane.newProperty
true
> 'newProperty' in jane
false
</code></pre></div></div>
<h3 id="任意键属性arbitrary-property-keys">任意键属性(Arbitrary property keys)</h3>
<p>属性的键可以是任意字符串。到目前为止,我们看到的对象字面量中的和点操作符后的属性关键字。按这种方法你只能使用<a href="#identifiers">标识符</a>。如果你想用其他任意字符串作为键名,你必须在对象字面量里加上引号,并使用方括号获取和设置属性。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var obj = { 'not an identifier': 123 };
> obj['not an identifier']
123
> obj['not an identifier'] = 456;
</code></pre></div></div>
<p>方括号允许你动态计算属性关键字:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var x = 'name';
> jane[x]
'Jane'
> jane['na'+'me']
'Jane'
</code></pre></div></div>
<h3 id="引用方法extracting-methods">引用方法(Extracting methods)</h3>
<p>如果你引用一个方法,它将失去和对象的连接。就其本身而言,函数不是方法,其中的this值为undefined(严格模式下)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var func = jane.describe;
> func()
TypeError: Cannot read property 'name' of undefined
</code></pre></div></div>
<p>解决办法是使用函数内置的bind()方法。它创建一个新函数,其this值固定为给定的值。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var func2 = jane.describe.bind(jane);
> func2()
'Person named Jane'
</code></pre></div></div>
<h3 id="方法内部的函数functions-inside-a-method">方法内部的函数(Functions inside a method)</h3>
<p>每个函数都有一个特殊变量this。如果你在方法内部嵌入函数是很不方便的,因为你不能从函数中访问方法的this。下面是一个例子,我们调用forEach循环一个数组:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var jane = {
name: 'Jane',
friends: [ 'Tarzan', 'Cheeta' ],
logHiToFriends: function () {
'use strict';
this.friends.forEach(function (friend) {
// 这里的“this”是undefined
console.log(this.name+' says hi to '+friend);
});
}
}
</code></pre></div></div>
<p>调用 logHiToFriends 会产生错误:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> jane.logHiToFriends()
TypeError: Cannot read property 'name' of undefined
</code></pre></div></div>
<p>有两种方法修复这问题。</p>
<p>#1:将this存储在不同的变量。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>logHiToFriends: function () {
'use strict';
var that = this;
this.friends.forEach(function (friend) {
console.log(that.name+' says hi to '+friend);
});
}
</code></pre></div></div>
<p>#2:forEach的第二个参数允许提供this值。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>logHiToFriends: function () {
'use strict';
this.friends.forEach(function (friend) {
console.log(this.name+' says hi to '+friend);
}, this);
}
</code></pre></div></div>
<p>在JavaScript中函数表达式经常被用作函数参数。时刻小心函数表达式中的this。</p>
<h3 id="sect_constructors">构造函数:对象工厂(Constructors: factories for objects)</h3>
<p>目前为止,你可能认为JavaScript的对象仅是键值的映射,通过JavaScript对象字面量可以得出这个观点,看起来很像其他语言中的地图/字典(map/dictionary)。然而,JavaScript对象也支持真正意义上的面向对象特性:继承(inheritance)。本节会完全讲解JavaScript中继承的工作原理,但会给你以此为开始的简单模式。如果你想得到更多知识,请查阅这篇文章“<a href="http://www.2ality.com/2012/01/js-inheritance-by-example.html">JavaScript inheritance by example</a>”。</p>
<p>除了作为“真正”的函数和方法,函数还在JavaScript中扮演第三种角色:如果通过new操作符调用,他们会变为构造函数,对象的工厂。构造函数是对其他语言中的类的粗略模拟。约定俗称,构造函数的第一个字母大写。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 设置实例数据
function Point(x, y) {
this.x = x;
this.y = y;
}
// 方法
Point.prototype.dist = function () {
return Math.sqrt(this.x*this.x + this.y*this.y);
};
</code></pre></div></div>
<p>我们看到构造函数分为两部分:首先,Point函数设置实例数据。其次,Point.prototype属性包含对象的方法。前者的数据是每个实例私有的,后面的数据是所有实例共享的。</p>
<p>我们通过new操作符调用Point:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var p = new Point(3, 5);
> p.x
3
> p.dist()
5.830951894845301
</code></pre></div></div>
<p>p是Point的一个实例:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> p instanceof Point
true
> typeof p
'object'
</code></pre></div></div>
<h3 id="深入阅读-10">深入阅读</h3>
<ul>
<li><a href="http://www.2ality.com/2012/01/objects-as-maps.html">The pitfalls of using objects as maps in JavaScript</a> [important, read soon]</li>
<li><a href="http://www.2ality.com/2012/01/js-inheritance-by-example.html">JavaScript inheritance by example</a></li>
<li><a href="http://www.2ality.com/2012/10/javascript-properties.html">Object properties in JavaScript</a> [advanced: each property has attributes that determine whether it is writable, etc.]</li>
<li><a href="http://www.2ality.com/2012/03/private-data.html">Private data for objects in JavaScript</a></li>
<li><a href="http://yanhaijing.com/javascript/2013/08/23/javascript-inheritance-how-to-shoot-yourself-in-the-foot-with-prototypes">Javascript继承 原型的陷阱</a></li>
<li><a href="http://yanhaijing.com/javascript/2013/08/30/encapsulation-of-javascript">Javascript 封装问题</a></li>
</ul>
<h2 id="sect_arrays">数组(Arrays)</h2>
<p>数组是数组元素的序列,能通过整数索引方法数组元素,数组索引从0开始。</p>
<h3 id="数组字面量array-literals">数组字面量(Array literals)</h3>
<p>数组字面量创建数组很方便:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var arr = [ 'a', 'b', 'c' ];
</code></pre></div></div>
<p>上面的数组有三个元素:分别是字符串“a”,“b”, “c”。你可以通过整数索引访问它们:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> arr[0]
'a'
> arr[0] = 'x';
> arr
[ 'x', 'b', 'c' ]
</code></pre></div></div>
<p>length属性总表示一个数组有多少项元素。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> arr.length
3
</code></pre></div></div>
<p>除此之外它也可以用来从数组上移除尾部元素:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> arr.length = 2;
> arr
[ 'x', 'b' ]
</code></pre></div></div>
<p>in操作符也可以在数组上工作。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> 1 in arr // arr在索引为1处是否有元素?
true
> 5 in arr // arr在索引为5处是否有元素?
false
</code></pre></div></div>
<p>值得注意的是数组是对象,因此可以有对象属性:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> arr.foo = 123;
> arr.foo
123
</code></pre></div></div>
<h3 id="数组方法array-methods">数组方法(Array methods)</h3>
<p>数组有许多<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype">方法</a>。举些例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> var arr = [ 'a', 'b', 'c' ];
> arr.slice(1, 2) // 复制元素
[ 'b' ]
> arr.slice(1)
[ 'b', 'c' ]
> arr.push('x') // 在末尾添加一个元素
4
> arr
[ 'a', 'b', 'c', 'x' ]
> arr.pop() // 移除最后一个元素
'x'
> arr
[ 'a', 'b', 'c' ]
> arr.shift() // 移除第一个元素
'a'
> arr
[ 'b', 'c' ]
> arr.unshift('x') // 在前面添加一个元素
3
> arr
[ 'x', 'b', 'c' ]
> arr.indexOf('b') // 查找给定项在数组中的索引,若不存在返回-1
1
> arr.indexOf('y')
-1
> arr.join('-') // 将元素拼接为一个字符串
'x-b-c'
> arr.join('')
'xbc'
> arr.join()
'x,b,c'
</code></pre></div></div>
<h3 id="遍历数组iterating-over-arrays">遍历数组(Iterating over arrays)</h3>
<p>有几种方法可以遍历数组元素。其中两个最重要的是 forEach 和 map。</p>
<p>forEach遍历整个数组,并将当前元素和它的索引传递给一个函数:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[ 'a', 'b', 'c' ].forEach(
function (elem, index) { // (*)
console.log(index + '. ' + elem);
});
</code></pre></div></div>
<p>上面代码的输出</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0. a
1. b
2. c
</code></pre></div></div>
<p>注意(*)行的函数参数是可省略的。例如:它可以只有一个参数 elem。</p>
<p>map创建一个新数组,通过给每个存在数组元素应用一个函数:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> [1,2,3].map(function (x) { return x*x })
[ 1, 4, 9 ]
</code></pre></div></div>
<h3 id="深入阅读-11">深入阅读</h3>
<ul>
<li><a href="http://www.2ality.com/2012/12/arrays.html">Arrays in JavaScript</a></li>
<li><a href="http://www.2ality.com/2013/05/quirk-array-like-objects.html">JavaScript quirk 8: array-like objects</a></li>
<li><a href="http://yanhaijing.com/javascript/2014/01/17/fun-with-javascript-native-array-functions">有趣的javascript原生数组函数</a></li>
</ul>
<h2 id="sect_regexp">正则表达式(Regular expressions)</h2>
<p>JavaScript内建支持正则表达式。他们被双斜线分隔:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/^abc$/
/[A-Za-z0-9]+/
</code></pre></div></div>
<h3 id="方法-test测试是否匹配method-test-is-there-a-match">方法 test():测试是否匹配(Method test(): is there a match?)</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> /^a+b+$/.test('aaab')
true
> /^a+b+$/.test('aaa')
false
</code></pre></div></div>
<h3 id="方法-exec匹配和捕获组method-exec-match-and-capture-groups">方法 exec():匹配和捕获组(Method exec(): match and capture groups)</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> /a(b+)a/.exec('_abbba_aba_')
[ 'abbba', 'bbb' ]
</code></pre></div></div>
<p>返回的数组第一项(索引为0)是完整匹配,捕获的第一个分组在第二项(索引为1),等。<a href="http://www.2ality.com/2011/04/javascript-overview-of-regular.html">有一种方法</a>可以反复调用获取所有匹配。</p>
<h3 id="方法-replace搜索并替换method-replace-search-and-replace">方法 replace():搜索并替换(Method replace(): search and replace)</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> '<a> <bbb>'.replace(/<(.*?)>/g, '[$1]')
'[a] [bbb]'
</code></pre></div></div>
<p>replace的第一个参数必须是正则表达式,并且开启全局搜索(/g 标记),否则仅第一个匹配项会被替换。有<a href="http://www.2ality.com/2011/04/javascript-overview-of-regular.html">一种方法</a>使用一个函数来计算替换项。</p>
<h3 id="深入阅读-12">深入阅读</h3>
<ul>
<li><a href="http://www.2ality.com/2011/04/javascript-overview-of-regular.html">JavaScript: an overview of the regular expression API</a></li>
<li><a href="http://tech.pro/tutorial/1214/javascript-regular-expression-enlightenment">JavaScript Regular Expression Enlightenment</a> [by Cody Lindley]</li>
</ul>
<h2 id="sect_math">数学(Math)</h2>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math">Math</a>是一个有算数功能的对象。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> Math.abs(-2)
2
> Math.pow(3, 2) // 3^2
9
> Math.max(2, -1, 5)
5
> Math.round(1.9)
2
> Math.cos(Math.PI) // 预定义常量π
-1
</code></pre></div></div>
<h2 id="sect_standard_library">标准库的其他功能(Other functionality of the standard library)</h2>
<p>JavaScript标准库相对简单,但有很多其他东西你可以使用:</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date">Date</a>:日期构造函数,主要功能有转换和创建日期字符串,访问日期组成部分(年,小时等)。</li>
<li><a href="http://www.2ality.com/2011/08/json-api.html">JSON</a>:一个对象,功能是转换和生成JSON数据。</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/console">console.*</a> 方法:浏览器的具体方法,不是语言成分的部分,但他们也可以在<a href="http://nodejs.org/api/stdio.html">Node.js</a>中工作。</li>
</ul>
<h2 id="sect_learn_next">下一步学什么?</h2>
<p>在你学会了这篇文章的基础教程后,你可以转到大部分章节末尾提到的高级教程。此外,我建议你看下面的资源:</p>
<ul>
<li>Style guides: I have written a <a href="http://www.2ality.com/2013/07/meta-style-guide.html">guide to style guides</a>.</li>
<li><a href="http://underscorejs.org/">Underscore.js</a>: 一个弥补JavaScript标准库缺少的功能的库</li>
<li><a href="http://jsbooks.revolunet.com/">JSbooks – free JavaScript books</a></li>
<li><a href="http://uptodate.frontendrescue.org/">Frontend rescue: how to keep up to date on frontend technologies</a></li>
<li><a href="http://yanhaijing.com/">http://yanhaijing.com</a> 当然还有我的博客也非常不错哦</li>
<li><a href="http://yanhaijing.com/es5">http://yanhaijing.com/es5 </a>如果你想成为高手,我建议阅读ecmascript 规范</li>
<li><a href="http://yanhaijing.com/javascript/2013/12/11/24-JavaScript-best-practices-for-beginners">给javascript初学者的24条最佳实践</a></li>
<li><a href="http://yanhaijing.com/javascript/2014/04/23/seven-javascript-quirks-i-wish-id-known-about">我希望我知道的七个JavaScript技巧</a></li>
</ul>
<h2 id="欢迎反馈">欢迎反馈</h2>
<p>我尝试找到JavaScript的一个最理想子集。我成功了吗?需要增删一些东西吗?如果你是一个JavaScript新手,我特别想听听你的意见:当读这篇文章的时候是非常容易理解吗?或是你在某些地方卡住?</p>
<h2 id="注">注</h2>
<p>原文:http://www.2ality.com/2013/06/basic-javascript.html</p>
JavaScript中的this关键字
2014-04-30T00:00:00+00:00
http://yanhaijing.com/javascript/2014/04/30/javascript-this-keyword
<p>“this”关键字是JavaScript中广泛应用的一种特性,但它经常也是这门语言中最容易混淆和误解的特性。“this”的实际意义是什么?它是如何求值的?</p>
<p>本文试图以清晰的方式澄清和解释这问题的答案。</p>
<p>有过其他编程经验的人对“this”关键字并不陌生,大部分时候当通过构造函数实例化一个类的实例时,它指新创建的对象。例如,如果我有一个类<code class="language-plaintext highlighter-rouge">Boat()</code>,其拥有一个<code class="language-plaintext highlighter-rouge">moveBoat()</code>方法,当在<code class="language-plaintext highlighter-rouge">moveBoat</code>方法中引用“this”的时候,我们实际上访问的基于Boat类新创建的对象。</p>
<p>在JavaScript中,当通过“new”关键字调用构造函数时,我们也有this概念,然而,这并不是唯一的规则,并且“this”经常在不同的执行上下文中引用到不同的对象。如果你不熟悉JavaScript中的执行上下文,我建议你阅读我的<a href="http://yanhaijing.com/javascript/2014/04/29/what-is-the-execution-context-in-javascript">另一篇文章</a>。说的够多了,让我们看一些JavaScript的例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 全局作用域
foo = 'abc';
alert(foo); // abc
this.foo = 'def';
alert(foo); // def
</code></pre></div></div>
<p>每当你在全局作用域中使用“this”关键字时(没在函数内部),它通常指向全局对象(global object)。现在让我们看看函数内部“this”的值:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var boat = {
size: 'normal',
boatInfo: function() {
alert(this === boat);
alert(this.size);
}
};
boat.boatInfo(); // true, 'normal'
var bigBoat = {
size: 'big'
};
bigBoat.boatInfo = boat.boatInfo;
bigBoat.boatInfo(); // false, 'big'
</code></pre></div></div>
<p>那么上面的“this”如何确定?我们看到上面的boat对象有一个<code class="language-plaintext highlighter-rouge">size</code>属性和一个<code class="language-plaintext highlighter-rouge">boatInfo</code>方法。在<code class="language-plaintext highlighter-rouge">boatInfo()</code>内部,会弹出this的值是否是<code class="language-plaintext highlighter-rouge">boat</code>对象,也会弹出this的size属性。所以我们执行<code class="language-plaintext highlighter-rouge">boat.boatInfo()</code>,我们看见this的值是<code class="language-plaintext highlighter-rouge">boat</code>对象和<code class="language-plaintext highlighter-rouge">boat</code>的<code class="language-plaintext highlighter-rouge">size</code>属性值是<code class="language-plaintext highlighter-rouge">normal</code>。</p>
<p>然后我们创建另一个对象<code class="language-plaintext highlighter-rouge">bigBoat</code>,也有一个size属性是big。然而,bigBoat对象没有boatInfo方法,所以我们从boat对象拷贝方法 <code class="language-plaintext highlighter-rouge">bigBoat.boatInfo = boat.boatInfo</code>。现在,当我们调用bigBoat.boatInfo()并进入函数时,我们看到this不等于boat,并且现在size属性值是big。为什么会这样?boatInfo()内部的this值是如何改变的?</p>
<p>你必须意识到的第一件事是函数内部this的值不是静态的,每次你调用一个函数它总是重新求值,但这一过程发生在函数代码实际执行之前。函数内部的this值实际由函数被调用的父作用域提供,更重要的是,依赖实际函数的语法。</p>
<p>当函数被调用时,我们看紧邻括号“()”的左边。如果在括号的左侧存在一个引用,传递给调用函数的“this”值是引用属于的对象,否则this的值将是全局对象。让我们看一个例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function bar() {
alert(this);
}
bar(); // global - 因为bar方法被调用时属于 global 对象
var foo = {
baz: function() {
alert(this);
}
}
foo.baz(); // foo - 因为baz()方法被调用时术语foo对象
</code></pre></div></div>
<p>如果this就这么简单,那上面的代码就足够了。我们可以进一步使事情变得复杂,通过不同的调用语法,改变相同函数内部“this”的值。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var foo = {
baz: function() {
alert(this);
}
}
foo.baz(); // foo - 因为baz被调用时属于foo对象
var anotherBaz = foo.baz;
anotherBaz(); // global - 因为anotherBaz()被调用时术语global对象
</code></pre></div></div>
<p>我们看到baz()内部的“this”值每次都不同,这是因为调用的语法不同。现在,让我们看看深度嵌套对象内部“this”的值:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var anum = 0;
var foo = {
anum: 10,
baz: {
anum: 20,
bar: function() {
console.log(this.anum);
}
}
}
foo.baz.bar(); // 20 - 因为()的左边是bar,而它被调用时属于baz对象
var hello = foo.baz.bar;
hello(); // 0 - 因为()的左边是hello,而它被调用时属于global对象
</code></pre></div></div>
<p>另一个经常被问的问题是事件处理程序内部的“this”关键字如何求值?答案是事件处理程序内部的“this”总是引用触发事件的元素。让我们看一个例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div id="test">I am an element with id #test</div>
function doAlert() {
alert(this.innerHTML);
}
doAlert(); // undefined
var myElem = document.getElementById('test');
myElem.onclick = doAlert;
alert(myElem.onclick === doAlert); // true
myElem.onclick(); // I am an element
</code></pre></div></div>
<p>我们看到当doAlert()第一次调用时,弹出的值是undefined,由于doAlert()属于global对象。然后我们写<code class="language-plaintext highlighter-rouge">myElem.onclick = doAlert</code>。这意味这当onclick被出发时,它作为myElem的一个方法,“this”的值将是myElem元素。</p>
<p>我想说的最后一点是,“this”的值也可以通过call和apply手动设置,这超过我们所讨论的范围。还感兴趣的是,当调用构造函数时,“this”引用新创建的实例对象。原因是因为构造函数前面的“new”关键字,它创建一个新对象,构造函数内部的“this”总引用新创建的对象。</p>
<h2 id="总结">总结</h2>
<p>希望今天的文章已经澄清了“this”关键字的误解,并且你总能知道“this”的正确值。现在我们知道“this”的值不是静态的,值得确定依赖于函数被如何调用。</p>
<h2 id="注">注</h2>
<p>原文 http://davidshariff.com/blog/javascript-this-keyword/</p>
JavaScript的作用域和提升机制
2014-04-30T00:00:00+00:00
http://yanhaijing.com/javascript/2014/04/30/JavaScript-Scoping-and-Hoisting
<p>你知道下面的JavaScript代码执行时会输出什么吗?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var foo = 1;
function bar() {
if (!foo) {
var foo = 10;
}
alert(foo);
}
bar();
</code></pre></div></div>
<p>答案是“10”,吃惊吗?那么下面的可能会真的让你大吃一惊:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = 1;
function b() {
a = 10;
return;
function a() {}
}
b();
alert(a);
</code></pre></div></div>
<p>这里浏览器会弹出“1”。怎么回事?这似乎看起来是奇怪,未知,让人混淆的,但这实际上是这门语言一个强大和富有表现力的特性。我不知道这一特性行为是否有标准名字,但我喜欢这个术语“提升(hoisting)”。本文试图揭示这一特性的机制,但首先让我们链接JavaScript的作用域。</p>
<h2 id="javascript中的作用域scope">JavaScript中的作用域(scope)</h2>
<p>JavaScript初学者最容易混淆的地方是作用域。实际上,不只是初学者。我遇到过许多经验丰富的JavaScript程序员,却不完全明白作用域。JavaScript的作用域如此容易混淆的原因是它看起来很像C家族的语言(类C语言)。考虑下面的C程序:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#include <stdio.h>
int main() {
int x = 1;
printf("%d, ", x); // 1
if (1) {
int x = 2;
printf("%d, ", x); // 2
}
printf("%d\n", x); // 1
}
</code></pre></div></div>
<p>程序的输出是1,2,1.这是因为C和C家族的语言有<strong>块级作用域</strong>(block-level scope)。当控制流进入一个块,比如if语句,新的变量会在块作用域里声明,不会对外面作用域产生印象。这不适用于JavaScript。在Firebug里运行下面的代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var x = 1;
console.log(x); // 1
if (true) {
var x = 2;
console.log(x); // 2
}
console.log(x); // 2
</code></pre></div></div>
<p>在这个例子中,Firebug将输出1,2,2。这是因为JavaScript有<strong>函数级作用域</strong>(function-level scope)。这一点和C家族完全不同。语句块,如if语言,不创建新的作用域。仅仅函数创建新作用域。</p>
<p>很多程序员,像C,C++,C#或Java,都不知道这点,也不希望这样。幸运的是,因为JavaScript函数的灵活性,有一个解决方案。你若你必须要在函数内部创建一个临时作用域,像下面这样做:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function foo() {
var x = 1;
if (x) {
(function () {
var x = 2;
// 此处省略一万个字
}());
}
// x 仍然是 1.
}
</code></pre></div></div>
<p>这方法实际上相当灵活,可以在你需要临时作用域的时候随意使用,不局限于块级语句内部。然而,我强烈建议你花时间去了解和欣赏JavaScript的作用域。它非常强大,是这门语言中我最喜欢的特性之一。如果你了解作用域,将更容易理解提升。</p>
<h2 id="声明名字和提升hoisting">声明,名字和提升(Hoisting)</h2>
<p>在JavaScript中,作用域中的名字(属性名)有四种基本来源:</p>
<ol>
<li><strong>语言定义:</strong>默认所有作用域都有属性名this和arguments。</li>
<li><strong>形参:</strong>函数可能有形式参数,其作用域是整个函数体内部。</li>
<li><strong>函数声明:</strong>类似于function foo() {}这种形式。</li>
<li>
<p><strong>变量声明:</strong>var foo;这种形式的代码。
函数声明和变量声明总是被JavaScript解释器无形中移动到(提升)包含他们的作用域顶部。函数参数和语言定义的名称明显总是存在。这意味着像下面的代码:</p>
<p>function foo() {
bar();
var x = 1;
}</p>
</li>
</ol>
<p>实际上被解释为像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function foo() {
var x;
bar();
x = 1;
}
</code></pre></div></div>
<p>无论包含声明的代码行是否会被执行,上面的过程都会发生。下面的两个函数是等价的:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function foo() {
if (false) {
var x = 1;
}
return;
var y = 1;
}
function foo() {
var x, y;
if (false) {
x = 1;
}
return;
y = 1;
}
</code></pre></div></div>
<p>注意变量声明中赋值的过程不会被提升。仅仅变量名字被提升了。这不适用于函数声明,整个函数体也会提升。但不要忘记有两种声明函数的方法。考虑下面的JavaScript代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function test() {
foo(); // 类型错误 “foo 不是一个函数”
bar(); // “这能运行”
var foo = function () { // 将函数表达式赋值给本地变量“foo”
alert("this won't run!");
}
function bar() { // 'bar'函数声明,分配“bar”名字
alert("this will run!");
}
}
test();
</code></pre></div></div>
<p>在这种情况下,仅仅函数声明的函数体被提升到顶部。名字“foo”被提升,但后面的函数体,在执行的时候才被指派。</p>
<p>这是全部的基本提升,看起来并不复杂和让人混淆。当然,这是JavaScript,在某些特殊性况下会更复杂一点。</p>
<h3 id="名字解析顺序">名字解析顺序</h3>
<p>需要记住的最重要的特殊情况是名字的解析顺序。记住作用域中的名字有四种来源。上面我列出他们的顺序是他们被解析的顺序。一般来说,如果一个名字已经被定义过,那么它不会在被其他有相同名字的属性重写。这意味着函数声明优先于变量声明。这并不意味着为名字赋值的过程将不工作,仅仅声明的过程会被忽略。有几个例外情况:</p>
<ul>
<li>函数的内置变量arguments比较奇怪。它看起来是在普通的函数参数之后才声明,其实是在函数声明之前。如果参数里面有名称为arguments的参数,它会比内置的那个优先级高,即使它是undefined。所以不要使用arguments作为为函数参数的名称。</li>
<li>尝试使用this作为标示符的地方都会造成一个语法错误。这是一个很好的特性。</li>
<li>如果多个参数具有相同的名字,那么最后一个参数会优先于先前的,即使它是undefined。</li>
</ul>
<h3 id="命名函数表达式">命名函数表达式</h3>
<p>你可以在函数表达式给中给函数命名,用这样的语法不能完成一个函数声明,下面有一些代码来说明我的意思:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>foo(); // TypeError "foo is not a function"
bar(); // valid
baz(); // TypeError "baz is not a function"
spam(); // ReferenceError "spam is not defined"
var foo = function () {}; // 匿名函数表达式(“foo”会被提升)
function bar() {}; // 函数声明(“bar”和函数体会被提升)
var baz = function spam() {}; // 命名函数表达式(仅“baz”会被提升)
foo(); // valid
bar(); // valid
baz(); // valid
spam(); // ReferenceError "spam is not defined"
</code></pre></div></div>
<h2 id="编码时如何使用这些知识">编码时如何使用这些知识</h2>
<p>现在你应该理解了作用域和提升(hoisting),那么我们在编写JavaScript的时候应该怎么做呢?最重要的事情就是始终用var表达式来声明你的变量。我<strong>强烈建议</strong>你使用单var模式(single var)。如果你强迫自己做到这一点,你将永远不会遇到任何与变量提升相关的混乱的问题。但是这样做也让我们很难跟踪那些在当前作用域中实际上已经声明的变量。我建议你使用<a href="http://www.jslint.com/">JSLint</a>和声明一次原则来进行实际操作,如果你这样做了,你的代码应该会看起来像这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/*jslint onevar: true [...] */
function foo(a, b, c) {
var x = 1,
bar,
baz = "something";
}
</code></pre></div></div>
<h2 id="标准给出的解释">标准给出的解释</h2>
<p>我翻了翻<a href="http://yanhaijing.com/es5/">ECMAScript标准</a>,想直接了解这些东西是如何工作的,发现效果不错。这里我不得不说关于变量声明和作用域(第12.2.2节)的内容:</p>
<blockquote>
<p>如果在一个函数中声明变量,这些变量就被定义在了在该函数的函数作用域中,见第10.1.3所述。不然它们就是被定义在全局的作用域内(即,它们被创建为全局对象的成员,见第10.1.3所述),当进入执行环境的时候,变量就被创建。一个语句块不能定义一个新的作用域。只有一个程序或者函数声明能够产生一个新的作用域。创建变量时,被初始化为undefined。如果变量声明语句里面带有赋值操作,则赋值操作只有被执行到声明语句的时候才会发生,而不是创建的时候。</p>
</blockquote>
<p>我希望这篇文章阐明了对JavaScript程序员来说最常见的迷惑问题,我试图讲的尽可能详尽,以避免造成更多的迷惑,如果我说错了或者有大的遗漏,请通知我。</p>
<h2 id="注">注</h2>
<p>原文 http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html</p>
了解JavaScript的执行上下文
2014-04-29T00:00:00+00:00
http://yanhaijing.com/javascript/2014/04/29/what-is-the-execution-context-in-javascript
<p>在这篇文章里,我将深入研究JavaScript中最基本的部分——执行上下文(execution context)。读完本文后,你应该清楚了解解释器做了什么,为什么函数和变量能在声明前使用以及他们的值是如何决定的。</p>
<h2 id="什么是执行上下文">什么是执行上下文?</h2>
<p>当JavaScript代码运行,执行环境非常重要,有下面几种不同的情况:</p>
<ul>
<li>全局代码——你的代码首次执行的默认环境。</li>
<li>函数代码——每当进入一个函数内部。</li>
<li>Eval代码——eval内部的文本被执行时。</li>
</ul>
<p>在网上你能读到许多关于作用域(scope)的资源,本文的目的是让事情变得更简单,让我们将术语执行上下文想象为当前被执行代码的环境/作用域。说的够多了,现在让我们看一个包含全局和函数上下文的代码例子。</p>
<p><img src="/blog/136.jpg" alt="" /></p>
<p>很简单的例子,我们有一个被紫色边框圈起来的全局上下文和三个分别被绿色,蓝色和橘色框起来的不同函数上下文。只有全局上下文(的变量)能被其他任何上下文访问。</p>
<p>你可以有任意多个函数上下文,每次调用函数创建一个新的上下文,会创建一个私有作用域,函数内部声明的任何变量都不能在当前函数作用域外部直接访问。在上面的例子中,函数能访问当前上下文外面的变量声明,但在外部上下文不能访问内部的变量/函数声明。为什么会发生这种情况?代码到底是如何被解释的?</p>
<h2 id="执行上下文堆栈">执行上下文堆栈</h2>
<p>浏览器里的JavaScript解释器被实现为单线程。这意味着同一时间只能发生一件事情,其他的行文或事件将会被放在叫做执行栈里面排队。下面的图是单线程栈的抽象视图:</p>
<p><img src="/blog/137.jpg" alt="" /></p>
<p>我们已经知道,当浏览器首次载入你的脚本,它将默认进入全局执行上下文。如果,你在你的全局代码中调用一个函数,你程序的时序将进入被调用的函数,并穿件一个新的执行上下文,并将新创建的上下文压入执行栈的顶部。</p>
<p>如果你调用当前函数内部的其他函数,相同的事情会在此上演。代码的执行流程进入内部函数,创建一个新的执行上下文并把它压入执行栈的顶部。浏览器将总会执行栈顶的执行上下文,一旦当前上下文函数执行结束,它将被从栈顶弹出,并将上下文控制权交给当前的栈。下面的例子显示递归函数的执行栈调用过程:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function foo(i) {
if (i === 3) {
return;
}
else {
foo(++i);
}
}(0));
</code></pre></div></div>
<p><img src="/blog/138.gif" alt="" /></p>
<p>这代码调用自己三次,每次给i的值加一。每次foo函数被调用,将创建一个新的执行上下文。一旦上下文执行完毕,它将被从栈顶弹出,并将控制权返回给下面的上下文,直到只剩全局上下文能为止。</p>
<p><strong>有5个需要记住的关键点,关于执行栈(调用栈):</strong></p>
<ul>
<li>单线程。</li>
<li>同步执行。</li>
<li>一个全局上下文。</li>
<li>无限制函数上下文。</li>
<li>每次函数被调用创建新的执行上下文,包括调用自己。</li>
</ul>
<h2 id="执行上下文的细节">执行上下文的细节</h2>
<p>我们现在已经知道没次调用函数,都会创建新的执行上下文。然而,在JavaScript解释器内部,每次调用执行上下文,分为两个阶段:</p>
<ol>
<li>创建阶段【当函数被调用,但未执行任何其内部代码之前】:
<ul>
<li>创建作用域链(<a href="http://davidshariff.com/blog/javascript-scope-chain-and-closures/">Scope Chain</a>)</li>
<li>创建变量,函数和参数。</li>
<li>求”this“的值。</li>
</ul>
</li>
<li>激活/代码执行阶段:
<ul>
<li>指派变量的值和函数的引用,解释/执行代码。</li>
</ul>
</li>
</ol>
<p>可以将每个执行上下文抽象为一个对象并有三个属性:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>executionContextObj = {
scopeChain: { /* 变量对象(variableObject)+ 所有父执行上下文的变量对象*/ },
variableObject: { /*函数 arguments/参数,内部变量和函数声明 */ },
this: {}
}
</code></pre></div></div>
<h3 id="激活变量对象aovo">激活/变量对象【AO/VO】</h3>
<p>当函数被调用是executionContextObj被创建,但在实际函数执行之前。这是我们上面提到的第一阶段,创建阶段。在此阶段,解释器扫描传递给函数的参数或arguments,本地函数声明和本地变量声明,并创建executionContextObj对象。扫描的结果将完成变量对象的创建。</p>
<p><strong>下面是解释器如果执行代码的伪逻辑:</strong></p>
<ol>
<li>查找调用函数的代码。</li>
<li>执行函数代码之前,先创建执行上下文。</li>
<li>进入创建阶段:
<ul>
<li>初始化作用域链:</li>
<li>创建变量对象:
<ul>
<li>创建arguments对象,检查上下文,初始化参数名称和值并创建引用的复制。</li>
<li>扫描上下文的函数声明:
<ul>
<li>为发现的每一个函数,在变量对象上创建一个属性——确切的说是函数的名字——其有一个指向函数在内存中的引用。</li>
<li>如果函数的名字已经存在,引用指针将被重写。</li>
</ul>
</li>
<li>扫面上下文的变量声明:
<ul>
<li>为发现的每个变量声明,在变量对象上创建一个属性——就是变量的名字,并且将变量的值初始化为<a href="http://davidshariff.com/blog/javascripts-undefined-explored/">undefined</a></li>
<li>如果变量的名字已经在变量对象里存在,将不会进行任何操作并继续扫描。</li>
</ul>
</li>
</ul>
</li>
<li>求出上下文内部“this”的值。</li>
</ul>
</li>
<li>激活/代码执行阶段:
<ul>
<li>在当前上下文上运行/解释函数代码,并随着代码一行行执行指派变量的值。</li>
</ul>
</li>
</ol>
<p>让我们看一个例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function foo(i) {
var a = 'hello';
var b = function privateB() {
};
function c() {
}
}
foo(22);
</code></pre></div></div>
<p>当调用foo(22)时,创建状态像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fooExecutionContext = {
scopeChain: { ... },
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: pointer to function c()
a: undefined,
b: undefined
},
this: { ... }
}
</code></pre></div></div>
<p>真如你看到的,创建状态负责处理定义属性的名字,不为他们指派具体的值,以及形参/实参的处理。一旦创建阶段完成,执行流进入函数并且激活/代码执行阶段,看下函数执行完成后的样子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fooExecutionContext = {
scopeChain: { ... },
variableObject: {
arguments: {
0: 22,
length: 1
},
i: 22,
c: pointer to function c()
a: 'hello',
b: pointer to function privateB()
},
this: { ... }
}
</code></pre></div></div>
<h3 id="提升hoisting">提升(Hoisting)</h3>
<p>你能在网上找到很多定义JavaScript hoisting术语的资源,解释变量和函数声明被提升到函数作用域的顶部。然而,没有人解释为什么会发生这种情况的细节,学习了上面关于解释器如何创建爱你活动对象的新知识,很容易明白为什么。看下面的例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function() {
console.log(typeof foo); // 函数指针
console.log(typeof bar); // undefined
var foo = 'hello',
bar = function() {
return 'world';
};
function foo() {
return 'hello';
}
}()); 我们能回答下面的问题:
</code></pre></div></div>
<ul>
<li><strong>为什么我们能在foo声明之前访问它?</strong>
<ul>
<li>如果我们跟随创建阶段,我们知道变量在激活/代码执行阶段已经被创建。所以在函数开始执行之前,foo已经在活动对象里面被定义了。</li>
</ul>
</li>
<li><strong>Foo被声明了两次,为什么foo显示为函数而不是undefined或字符串?</strong>
<ul>
<li>尽管foo被声明了两次,我们知道从创建阶段函数已经在活动对象里面被创建,这一过程发生在变量创建之前,并且如果属性名已经在活动对象上存在,我们仅仅更新引用。</li>
<li>因此,对foo()函数的引用首先被创建在活动对象里,并且当我们解释到var foo时,我们看见foo属性名已经存在,所以代码什么都不做并继续执行。</li>
</ul>
</li>
<li><strong>为什么bar的值是undefined?</strong>
<ul>
<li>bar实际上是一个变量,但变量的值是函数,并且我们知道变量在创建阶段被创建但他们被初始化为undefined。</li>
</ul>
</li>
</ul>
<h2 id="总结">总结</h2>
<p>希望现在你了解JavaScript解释器如何执行你的代码。了解执行上下文和堆栈,将有助于你了解背后的原因——为什么你的代码被解释为和你最初希望不同的值。</p>
<p>你想知道解释器内部的运作的开销太大,或者你的JavaScript知识的必要性?知道执行上下文相帮你写出更好的JavaScript?</p>
<p>你想知道解释器的内部工作原理,需要太多篇幅,和必要的JavaScript知识。知道执行上下文能帮你写出更好的JavaScript代码。</p>
<p><strong>注意:</strong>有些人一直在问闭包,回调,延时等问题,我将在<a href="http://davidshariff.com/blog/javascript-scope-chain-and-closures/">下一篇文章</a>里提到,更多关注域执行上下文有关的<a href="http://davidshariff.com/blog/javascript-scope-chain-and-closures/">作用域链</a>相关方面。</p>
<h2 id="深入阅读">深入阅读</h2>
<ul>
<li><a href="http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf">ECMA-262 5th Edition</a></li>
<li><a href="http://dmitrysoshnikov.com/ecmascript/chapter-2-variable-object/">ECMA-262-3 in detail. Chapter 2. Variable object</a></li>
<li><a href="http://jibbering.com/faq/notes/closures/#clIRExSc">Identifier Resolution, Execution Contexts and scope chains</a></li>
</ul>
<h2 id="注">注</h2>
<p>原文:http://davidshariff.com/blog/what-is-the-execution-context-in-javascript/</p>
在JavaScript中什么时候使用==是正确的?
2014-04-25T00:00:00+00:00
http://yanhaijing.com/javascript/2014/04/25/strict-equality-exemptions
<p>在JavaScript中什么情况下使用==是正确的?简而言之:没有。这篇文章来看五种情况下总是使用===,并且解释为什么不用==。</p>
<p>JavaScript有两种操作符用来比较两个值是否相等 [1]:</p>
<ul>
<li>严格相等 === 仅考虑相同类型的值是否相等。</li>
<li>“正常”(或非严格)相等操作符 == 在比较之前,尝试为不同类型的值进行转换,然后类似严格相等。</li>
</ul>
<p>给JavaScript初学者的建议是完全忘掉 == ,并且总是使用 ===。事实证明,后者是更符合常规的。有五种案例,表面看起来可以不遵从规则,但真的不是这样。从现在开始,我使用下面的规则:</p>
<blockquote>
<p>意图清晰的代码胜过更简洁的代码。
记住:你的代码仅写一次,但被阅读很多次——尽可能保证对阅读者友好。</p>
</blockquote>
<h2 id="例1你清楚自己在比较什么">例1:你清楚自己在比较什么</h2>
<p>例如,使用typeof操作符[2],你能确保结果是字符串。然后可以放心使用 ==,因为我们确定不会在发生类型转换。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (typeof x == "function") {
...
}
</code></pre></div></div>
<p>然而,有两个反对这样做的原因:</p>
<ul>
<li>一致性:使用==对一致性没有任何好处,那么为什么不避免使用呢?</li>
<li>简单和性能:一般来说,=== 是最简单的操作符,因为它不进行类型转换。JavaScript引擎的性能参差不齐[3],但在大部分浏览器中 === 比 == 速度更快。</li>
<li>
<h2 id="例2与undefined和null做比较">例2:与undefined和null做比较</h2>
</li>
</ul>
<p>当使用 == 时,undefined和null在结果上等价——他们彼此相等,互相相等,但没有意义(包括JavaScript中的能被转换为false的值):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> null == null
true
> undefined == null
true
> false == null
false
> 0 == null
false
</code></pre></div></div>
<p>因此,下面的if语句检测的是null或undefined。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (x == null) {
...
}
</code></pre></div></div>
<p>然而,这是否出于简洁性考虑,意图并不清晰:如果你同时也检测undefined,那么你可以这样写。然而,如果JavaScript初学者读到你的代码,他们可能认为你仅仅检测null。如果高手读到你的代码,他们可能认为你写错了,并且应该写成 ===。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (x === undefined || x === null) {
...
}
</code></pre></div></div>
<p>如果你有点懒的话,上面的代码能被精简为:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (!x) {
...
}
</code></pre></div></div>
<p>和上面一样的警告:这条件成立,如果x有否定类型的值。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>undefined
null
false
0
""
</code></pre></div></div>
<h2 id="例3比较字符串和数字">例3:比较字符串和数字</h2>
<p>场景:你正工作在用户界面代码或编码处理服务器端参数。你可能会把数字编码为字符串。如果x是一个字符串,你可以像下面这样比较:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (x == 123) {
...
}
</code></pre></div></div>
<p>但问什么不告诉其他阅读你代码的人,如果x不是数字,它将被转换为数字?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if (Number(x) === 123) {
...
}
</code></pre></div></div>
<h2 id="例4比较对象和原始值">例4:比较对象和原始值</h2>
<p>使用 == 时你可以将一个原始值和其他原始值或包装类型 [4]实例做比较:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> function isAbc(x) { return x == "abc" }
> isAbc("abc")
true
> isAbc(new String("abc"))
true
</code></pre></div></div>
<p>而使用 === 时,你不能这样做:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> new String("abc") === "abc"
false
</code></pre></div></div>
<p>左边是一个对象而右边是原始值。因为他们类型不同所以不严格相等。然而,你同样需要向阅读你代码的人解释清楚你的意图。下面是表达式:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>x == "abc"
</code></pre></div></div>
<p>你的目的是什么?</p>
<ul>
<li>你真的想让一个包装字符串和右边的字符串作比较吗?这似乎不太可能,但如果确实是这样,你应该小心翼翼并给出文档记录。</li>
<li>你想将x转换为字符串?那应该写的更明确<code class="language-plaintext highlighter-rouge">String(x) === "abc"</code></li>
<li>你想提取包装变量的原始值?那你应该考虑下面的写法<code class="language-plaintext highlighter-rouge">x.valueOf() === "abc"</code></li>
</ul>
<h2 id="例5javascript是灵活的语言我的代码也应该这样">例5:JavaScript是灵活的语言——我的代码也应该这样</h2>
<p>理由是这样的:我想我的代码像JavaScript一样灵活。== 操作符帮我实现这一目的。例如JavaScript的灵活体现在它自动转换值类型:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> "abc" + false
'abcfalse'
> 3 + true
4
> +"73"
73
</code></pre></div></div>
<p>有几个理由反驳上述假说:</p>
<p>1.即使会自动转换但并不总是按你需要的方式转换。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> !"false"
false
> 7 + "3"
'73'
> Number("")
0
</code></pre></div></div>
<p>2.非严格相等的转换规则非常复杂:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> 2 == false
false
> 2 == true
false
> Boolean(2)
true
</code></pre></div></div>
<p>3.显示转化加上严格相等的代码更具描述性。比较:灵活的非严格相等。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function is123Implicit(x) {
return x == 123;
}
> is123Implicit(123)
true
> is123Implicit(new Number(123))
true
> is123Implicit("123")
true
</code></pre></div></div>
<p>替代方案:灵活的显式转换和严格相等。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function is123Explicit(x) {
x = Number(x);
return x === 123;
}
> is123Explicit(123)
true
> is123Explicit(new Number(123))
true
> is123Explicit("123")
true
</code></pre></div></div>
<p>4.有人说您的代码缺少灵活性?可以说JavaScript的默认灵活性利大于弊(对于学习难度而言)。写防御型的代码更容易暴漏Bug。<code class="language-plaintext highlighter-rouge">is123Explicit()</code> 的防御型版本看起来像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function is123Defensive(x) {
if (typeof x !== "number") {
throw new TypeError("Not a number: "+x);
}
return x === 123;
}
</code></pre></div></div>
<p>如果你想给函数传递任何非原始数字值,你必须先进行类型转换。</p>
<h2 id="结论">结论</h2>
<p>我希望我让你确信坚持简单的规则——”不用 ==“的意义,不只是对新手。在你的代码中魔法越少,通常意味着越容易理解。</p>
<h2 id="相关阅读">相关阅读</h2>
<ol>
<li><a href="http://www.2ality.com/2011/06/javascript-equality.html">Equality in JavaScript: === versus ==</a></li>
<li><a href="http://www.2ality.com/2011/11/improving-typeof.html">Improving the JavaScript typeof operator</a></li>
<li><a href="http://jsperf.com/equ-vs-strict-equ">jsPerf: == versus ===</a></li>
<li><a href="http://www.2ality.com/2011/03/javascript-values-not-everything-is.html">JavaScript values: not everything is an object</a></li>
</ol>
<h2 id="注">注</h2>
<p>英文:http://www.2ality.com/2011/12/strict-equality-exemptions.html</p>
我希望我知道的七个JavaScript技巧
2014-04-23T00:00:00+00:00
http://yanhaijing.com/javascript/2014/04/23/seven-javascript-quirks-i-wish-id-known-about
<p>如果你是一个JavaScript新手或仅仅最近才在你的开发工作中接触它,你可能感到沮丧。所有的语言都有自己的怪癖(quirks)——但从基于强类型的服务器端语言转移过来的开发人员可能会感到困惑。我就曾经这样,几年前,当我被推到了全职JavaScript开发者的时候,有很多事情我希望我一开始就知道。在这篇文章中,我将分享一些怪癖,希望我能分享给你一些曾经令我头痛不已的经验。这不是一个完整列表——仅仅是一部分——但希望它让你看清这门语言的强大之处,可能曾经被你认为是障碍的东西。</p>
<p><strong>我们将看下列技巧:</strong></p>
<ul>
<li>相等</li>
<li>点号vs括号</li>
<li>函数上下文</li>
<li>函数声明vs函数表达式</li>
<li>命名vs匿名函数</li>
<li>立即执行函数表达式</li>
<li>typeof vs Object.prototype.toString</li>
</ul>
<h2 id="1-相等">1.) 相等</h2>
<p>C#出身的我非常熟悉==比较运算符。值类型(或字符串)当有相同值是是相等的。引用类型相等需要有相同的引用。(我们假设你没有重载==运算符,或实现你自己的等值运算和GetHashCode方法)我很惊讶为什么JavaScript有两个等值运算符:==和===。最初我的大部分代码都是用的==,所以我并不知道当我运行如下代码的时候JavaScript为我做了什么:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var x = 1;
if(x == "1") {
console.log("YAY! They're equal!");
}
</code></pre></div></div>
<p>这是黑暗魔法吗?整数1是如何和字符串”1”相等的?</p>
<p>在JavaScript中,有相等(==)和严格相等(===)之说。相等运算符将强制转换两边的操作数为相同类型后执行严格相等比较。所以在上面的例子中,字符串”1”会被转换为整数1,这个过程在幕后进行,然后与变量x进行比较。</p>
<p>严格相等不进行类型转换。如果操作数类型不同(如整数和字符串),那么他们不全等(严格相等)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var x = 1;
// 严格平等,类型必须相同
if(x === "1") {
console.log("Sadly, I'll never write this to the console");
}
if(x === 1) {
console.log("YES! Strict Equality FTW.")
}
</code></pre></div></div>
<p>你可能正在考虑可能发生强制类型转换而引起的各种恐怖问题——假设你的引用中发生了这种转换,可能导致你非常困难找到问题出在哪里。这并不奇怪,这也是为什么经验丰富的JavaScript开发者总是建议使用严格相等。</p>
<h2 id="2-点号-vs-括号">2.) 点号 vs 括号</h2>
<p>这取决于你来自其他什么语言,你可能见过或没见过这种方式(这就是废话)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 获取person对象的firstName值
var name = person.firstName;
// 获取数组的第三个元素
var theOneWeWant = myArray[2]; // remember, 0-based index不要忘了第一个元素的索引是0
</code></pre></div></div>
<p>然而,你知道它也可以使用括号引用对象的成员吗?比如说:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var name = person["firstName"];
</code></pre></div></div>
<p>为什么会这样有用吗?而你会用点符号的大部分时间,有几个实例的括号使某些方法可能无法这样做。例如,我会经常重构大开关语句到一个调度表,所以这样的事情:</p>
<p>为什么可以这样用?你以前可能对使用点更熟悉,有几个特例只能用括号表示法。例如,我经常会将switch语句重构为查找表(速度更快),其实就像这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var doSomething = function(doWhat) {
switch(doWhat) {
case "doThisThing":
// more code...
break;
case "doThatThing":
// more code...
break;
case "doThisOtherThing":
// more code....
break;
// additional cases here, etc.
default:
// default behavior
break;
}
}
</code></pre></div></div>
<p>可以转化为像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var thingsWeCanDo = {
doThisThing : function() { /* behavior */ },
doThatThing : function() { /* behavior */ },
doThisOtherThing : function() { /* behavior */ },
default : function() { /* behavior */ }
};
var doSomething = function(doWhat) {
var thingToDo = thingsWeCanDo.hasOwnProperty(doWhat) ? doWhat : "default"
thingsWeCanDo[thingToDo]();
}
</code></pre></div></div>
<p>使用switch并没有错误(并且在许多情况下,如果被迭代多次并且非常关注性能,switch可能比查找表表现更好)。然而查找表提供了一个很好的方法来组织和扩展代码,并且括号允许你的属性延时求值。</p>
<h2 id="3-函数上下文">3.) 函数上下文</h2>
<p>已经有一些伟大的博客发表了文章,正确理解了JavaScript中的this上下文(在文章的结尾我会给出一些不错的链接),但它确实应该加到“我希望我知道”的列表。它真的困难看懂代码并且自信的知道在任何位置this的值——你仅需要学习一组规则。不幸的是,我早起读到的许多解释只是增加了我的困惑。因此我试图简明扼要的做出解释。</p>
<h3 id="第一首先考虑全局情况global">第一——首先考虑全局情况(Global)</h3>
<p>默认情况下,直到某些原因改变了执行上下文,否则this的值都指向全局对象。在浏览器中,那将会是window对象(或在node.js中为global)。</p>
<h3 id="第二方法中的this值">第二——方法中的this值</h3>
<p>当你有一个对象,其有一个函数成员,冲父对象调用这方法,this的值将指向父对象。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var marty = {
firstName: "Marty",
lastName: "McFly",
timeTravel: function(year) {
console.log(this.firstName + " " + this.lastName + " is time traveling to " + year);
}
}
marty.timeTravel(1955);
// Marty McFly is time traveling to 1955
</code></pre></div></div>
<p>你可能已经知道你能引用marty对象的timeTravel方法并且创建一个其他对象的新引用。这实际上是JavaScript非常强大的特色——使我们能够在不同的实例上引用行为(调用函数)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var doc = {
firstName: "Emmett",
lastName: "Brown",
}
doc.timeTravel = marty.timeTravel;
</code></pre></div></div>
<p>所以——如果我们调用doc.timeTravel(1885)将会发生什么?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>doc.timeTravel(1885);
// Emmett Brown is time traveling to 1885
</code></pre></div></div>
<p>再次——上演黑暗魔法。嗯,并不是真的。记住,当你调用一个方法的时候,this上下文是被调用函数父的父对象。</p>
<p>当我们保存marty.TimeTravel方法的引用然后调用我们保存的引用时发生了什么?让我们看看:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var getBackInTime = marty.timeTravel;
getBackInTime(2014);
// undefined undefined is time traveling to 2014
为什么是“undefined undefined”?!而不是“Matry McFly”?
</code></pre></div></div>
<p>让我们问一个关键的问题:当我们调用我们的getBackInTime函数时父对象/容器对象是什么?当getBackIntTime函数存在于window中时,我们调用它作为一个函数,而不是一个对象的方法。当我们像这样调用一个函数——没有容器对象——this上下文将是全局对象。<a href="http://davidshariff.com/blog/javascript-this-keyword/">David Shariff</a>有一个伟大的描述关于这:</p>
<blockquote>
<p>无论何时调用一个函数,我们必须立刻查看括号的左边。如果在括号的左边存在一个引用,那么被传递个调用函数的this值确定为引用所属的对象,否则是全绝对象。</p>
</blockquote>
<p>由于getBackInTime的this上下文是window——没有firstName和lastName属性——这解释了为什么我们看见“undefined undefined”。</p>
<p>因此我们知道直接调用一个函数——没有容器对象——this上下文的结果是全局对象。然而我也说我早就知道我们的getBackInTime函数存在于window上。我是如何知道的?好的,不像上面我包裹getBackInTime在不同的上下文(我们探讨立即执行函数表达式的时候),我声明的任何变量都被添加的window。来自Chrome控制台的验证:</p>
<p><img src="/blog/125.png" alt="" /></p>
<p>是时候讨论下this的主要用武之地之一了:订阅事件处理。</p>
<h3 id="第三仅仅是2的扩展异步调用方法中的this值">第三(仅仅是#2的扩展)——异步调用方法中的this值</h3>
<p>所以,让我们假装我们想调用我们的marty.timeTravel方法当有人点击一个按钮时:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var flux = document.getElementById("flux-capacitor");
flux.addEventListener("click", marty.timeTravel);
</code></pre></div></div>
<p>在上面的代码中,当用户点击按钮是,我们会看见“undefined undefined is time traveling to [object MouseEvent]”。什么?好——首先,非常明显的问题是我们没有给我们的timeTravel方法提供year参数。反而,我们直接订阅这方法作为事件处理程序,并且MouseEvent参数被作为第一个参数传递个事件处理程序。这是很容易修复的,但真正的问题是我们再次见到“undefined undefined”。不要无望——你已经知道为什么会发生这种情况(即使你还没意识到)。让我们修改我们的timeTravel函数,输出this,从而帮助我们搞清事实:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>marty.timeTravel = function(year) {
console.log(this.firstName + " " + this.lastName + " is time traveling to " + year);
console.log(this);
};
</code></pre></div></div>
<p>现在——当我们点击这按钮,我们将类似下面的输出 在你的浏览器控制台:</p>
<p><img src="/blog/126.png" alt="" /></p>
<p>当方法被调用时,第二个console.log输出出this上下文——它实际上是我们订阅事件的按钮元素。你感到吃惊吗?就像之前——当我们将marty.timeTravel赋值给getBackInTime变量时——对marty.timeTravel的引用被保存到事件处理程序,并被调用,但容器对象不再是marty对象。在这种情况下,它将在按钮实例的<a href="http://en.wikipedia.org/wiki/Event-driven_architecture">点击事件</a>中异步调用。</p>
<p>所以——有可能将this设置为我们想要的结果吗?绝对可以!在这个例子里,解决方法非常简单。不在事件处理程序中直接订阅marty.timeTravel,而是使用匿名函数作为事件处理程序,并在匿名函数中调用marty.timeTravel。这也能修复year参数丢失的问题。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>flux.addEventListener("click", function(e) {
marty.timeTravel(someYearValue);
});
</code></pre></div></div>
<p>点击按钮将会在控制台输出类似下面的信息:</p>
<p><img src="/blog/127.png" alt="" /></p>
<p>成功了!但为什么这样可以?思考我们是如何调用timeTravel方法的。在我们按钮点击的第一个例子中,我们在事件处理程序中订阅方法自身的引用,所以它没有从父对象marty上调用。在第二个例子中,通过this为按钮元素的匿名函数,并且当我们调用marty.timeTravel时,我们从其父对象marty上调用,所以this为marty。</p>
<h3 id="第四构造函数中的this值">第四——构造函数中的this值</h3>
<p>当你用构造函数创建对象实例时,函数内部的this值就是新创建的对象。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var TimeTraveler = function(fName, lName) {
this.firstName = fName;
this.lastName = lName;
// Constructor functions return the
// newly created object for us unless
// we specifically return something else
};
var marty = new TimeTraveler("Marty", "McFly");
console.log(marty.firstName + " " + marty.lastName);
// Marty McFly
</code></pre></div></div>
<h3 id="callapply和bindcall">Call,Apply和BindCall</h3>
<p>你可能开始疑惑,上面的例子中,没有语言级别的特性允许我们在运行时指定调用函数的this值吗?你是对的。存在于函数原型上的call和apply方法允许我们调用函数并传递this值。</p>
<p>call方法的第一个参数是this,后面是被调用函数的参数序列:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>someFn.call(this, arg1, arg2, arg3);
</code></pre></div></div>
<p>apply的第一个参数也是this,后面是其余参数组成的数组:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>someFn.apply(this, [arg1, arg2, arg3]);
</code></pre></div></div>
<p>我们的doc和marty实例他们自己能时间旅行,但einstein(<a href="http://backtothefuture.wikia.com/wiki/Einstein">爱因斯坦</a>)需要他们的帮助才能完成时间旅行。所以让我们给我们的doc实例添加一个方法,以至于doc能帮助einstein完成时间旅行。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>doc.timeTravelFor = function(instance, year) {
this.timeTravel.call(instance, year);
// 如果你使用apply使用下面的语法
// this.timeTravel.apply(instance, [year]);
};
</code></pre></div></div>
<p>现在它可以传送Einstein 了:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var einstein = {
firstName: "Einstein",
lastName: "(the dog)"
};
doc.timeTravelFor(einstein, 1985);
// Einstein (the dog) is time traveling to 1985
</code></pre></div></div>
<p>我知道这个例子有些牵强,但它足以让你看到应用函数到其他对象的强大之处。</p>
<p>这种方法还有我们没有发现的另一种用处。让我们给我们的marty实例添加一个goHome方法,作为this.timeTravel(1985)的快捷方式。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>marty.goHome = function() {
this.timeTravel(1985);
}
</code></pre></div></div>
<p>然而,我们知道如果我们订阅marty.goHome作为按钮的点击事件处理程序,this的值将是按钮——并且不幸的是按钮没有timeTravel方法。我们能用上面的方法解决——用个一匿名函数作为事件处理程序,并在其内部调用上述方法——但我们有另一个选择——bind函数:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>flux.addEventListener("click", marty.goHome.bind(marty));
</code></pre></div></div>
<p>bind函数实际上会返回一个新函数,新函数的this值根据你提供的参数设置。如果你需要支持低版本浏览器(例如:ie9以下版本),你可能需要bind函数的<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind">shim</a>(或者,如果你使用jQuery你可以用$.proxy代替,underscore和lodash都提供_.bind方法)。</p>
<blockquote>
<p>记住重要一点,如果你直接使用原型上的bind方法,它将创建一个实例方法,这将绕过原型方法的优点。这不是错误,做到心里清楚就行了。我写了关于这个问题得更多信息在<a href="http://freshbrewedcode.com/jimcowart/2013/02/12/getting-into-context-binds/">这里</a>。</p>
</blockquote>
<h2 id="4-函数表达式vs函数声明">4.) 函数表达式vs函数声明</h2>
<p>函数声明不需要var关键字。事实上,如Angus Croll所说:“把他们想象成变量声明的兄弟有助于理解”。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function timeTravel(year) {
console.log(this.firstName + " " + this.lastName + " is time traveling to " + year);
} 上面例子里的函数名字timeTravel不仅在它声明的在作用域可见,同时在函数本身内部也是可见的(这对递归函数调用非常有用)。函数声明,本质上说其实就是命名函数。换句话说,上面函数的名称属性是timeTravel。
</code></pre></div></div>
<p>函数表达式定义一个函数并指派给一个变量。典型应用如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var someFn = function() {
console.log("I like to express myself...");
}; 也可以对函数表达式命名——然而,不像函数声明,命名函数表达式的名字仅在它自身函数体内可访问:
var someFn = function iHazName() {
console.log("I like to express myself...");
if(needsMoreExpressing) {
iHazName(); // 函数的名字在这里可以访问
}
};
// 你可以在这里调用someFn(),但不能调用iHazName()
someFn();
</code></pre></div></div>
<blockquote>
<p>讨论函数表达式和函数声明不能不提“hoisting(提升)”——函数和变量声明被编译器移到作用域的顶部。在这里我们无法详细解释hoisting,但你可以读<a href="http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html">Ben Cherry</a>和<a href="http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/">Angus Croll</a>两个人的伟大解释。</p>
</blockquote>
<h2 id="5-命名vs匿名函数">5.) 命名vs匿名函数</h2>
<p>基于我们刚才的讨论,你可能一进猜到“匿名”函数其实就是一个没有名字的函数。大多数JavaScript开发者能迅速识别瞎买年第一个参数为匿名函数:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>someElement.addEventListener("click", function(e) {
// I'm anonymous!
});
</code></pre></div></div>
<p>然而,同样的我们的marty.timeTravvel方法也是一个匿名函数:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var marty = {
firstName: "Marty",
lastName: "McFly",
timeTravel: function(year) {
console.log(this.firstName + " " + this.lastName + " is time traveling to " + year);
}
}
</code></pre></div></div>
<p>因为函数声明必须有一个唯一的名字,只有函数表达式可以没有名字。</p>
<h2 id="6-立即执行函数表达式">6.) 立即执行函数表达式</h2>
<p>因为我们正在谈论函数表达式,有一个东西我希望我早知道:立即执行函数表达式(IIFE)。有很多关于IIFE的好文章(我将在文章结尾出列出),但用一句话来形容,函数表达式不是通过将函数表达式赋值给一个标量,稍后再执行,而是理解执行。可以在浏览器控制台看这一过程。</p>
<p>首先——让我们先敲入一个函数表达式——但不给它指派变量——看看会发什么:</p>
<p><img src="/blog/128.png" alt="" /></p>
<p>语法错误——这被认为是函数声明,缺少函数名字。然而,为了使其变为表达式,我们仅需将其包裹在括号内:</p>
<p><img src="/blog/129.png" alt="" /></p>
<p>让其变为表达式后控制台返回给我们一个匿名函数(记住,我们没有为其指派值,但表达式会有返回值)。所以——我们知道“函数表达式”是“立即调用函数表达式”的一部分。为了等到“立即执行”的特性,我们通过在表达式后面添加另一个括号来调用返回的表达式(就像我们调用其他函数一样):</p>
<p><img src="/blog/130.png" alt="" /></p>
<p>“但是等一下,Jim!(指作者)我想我以前见过这种调用方式”。 事实上你可能见过——这是合法的语法(众所周知的是Douglas Crockford的首选语法)</p>
<p><img src="/blog/131.png" alt="" /></p>
<p>这两种方法都起作用,但是我强烈建议你读一读<a href="https://github.com/airbnb/javascript/issues/21#issuecomment-10203921">这里</a>。</p>
<p>OK,非常棒——现在我们已经知道了IIFE是什么——以及为什么要用它?</p>
<p>它帮助我们控制作用域——任何JavaScript教程中非常重要的部分!前面我们看到的许多实例都创建在全局作用域。这意味着window(假设环境是浏览器)对象将有很多属性。如果我们全部按照这种方式写我们的JavaScript代码,我们会迅速在全局作用域积累一吨(夸张)变量声明,window代码会被污染。即使在最好的情况下,在全局变量暴漏许多细节是糟糕的建议,但当变量的名字和已经存在的window属性名字相同时会发生什么呢?window属性会被重写!</p>
<p>例如,如果你最喜欢的“Amelia Earhart”网站在全局作用域声明了一个navigator变量,下面是设置之前和之后的结果:</p>
<p><img src="/blog/132.png" alt="" /></p>
<p>哎呀!</p>
<p>显而易见——全局变量被污染是糟糕的。JavaScript使用函数作用域(而不是块作用域,如果你来自C#或Java,这点非常重要!),所以保持我们的代码和全局作用域分离的办法是创建一个新作用域,我们可以使用IIFE来实现,因为它的内容在它自己的函数作用域内。在下面的例子中,我将在控制台向你显示window.navigator的值,然后我常见一个IIFE(立即执行函数表达式)去包裹Amelia Earhart的行为和数据。IIFE结束后返回一个作为我们的“程序命名空间”的对象。我在IIFE内声明的navigator变量将不会重写window.navigator的值。</p>
<p><img src="/blog/133.png" alt="" /></p>
<p>作为额外好处,我们上面创建的IIFE是JavaScript中模块模式的启蒙。我将在结尾处包括一些我浏览的模块模式的链接。</p>
<h2 id="7-typeof操作符和objectprototypetostring">7.) ‘typeof’操作符和’Object.prototype.toString’</h2>
<p>最终,可能发现在某些情况下,你需要检查传递给函数参数的类型,或其他类似的东西。typeof运算符会是显而易见的选择,但是,这并不是万能的。例如,当我们对一个对象,数组,字符串或正则表达式,调用typeof运算符时会发生什么?</p>
<p><img src="/blog/134.png" alt="" /></p>
<p>还好——至少我们可以将字符串和对象,数组,正则表达式区分开,对吗?幸运的是,我们可以得到更准确的类型信息,我们有其他不同的方法。我们将使用Object.prototype.toString方法,并且应用我们前面提到的call方法:</p>
<p><img src="/blog/135.png" alt="" /></p>
<p>为什么我们要使用Object.prototype上的toString方法?因为第三方库或你自己的代码可能重写实例的toString方法。通过Object.prototype,我们可以强制实现实例原来的toString行为。</p>
<p>如果你知道typeof将会返回什么那么你不需要进行多余的检查(例如,你仅需要知道是或不是一个字符串),此时用typeof非常好。然而,如果你需要区分数组和对象,正则表达式和对象,等等,那么使用Object.prototype.toString吧。</p>
<h2 id="接下来呢">接下来呢</h2>
<p>我已经从其他JavaScript开发者的见解中收益颇多,所以请看看下面的这些链接,并给这些人一些鼓励,他们给予了我们谆谆教诲。</p>
<ul>
<li>Axel Rauschmayer’s 非常棒的文章 <a href="http://yanhaijing.com/javascript/2014/04/25/strict-equality-exemptions">在JavaScript中什么时候使用==是正确的?</a> (提示:从不)</li>
<li><a href="http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/">Fixing the typeof Operator</a> by Angus Croll</li>
<li><a href="https://github.com/airbnb/javascript/issues/21#issuecomment-10203921">Airbnb Github Issue comment</a> that’s the single best explanation on IIFE parens placement</li>
<li><a href="http://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/">Function Declarations vs. Function Expressions</a> – by Angus Croll</li>
<li><a href="http://freshbrewedcode.com/jimcowart/2013/02/12/getting-into-context-binds/">Getting Into Context Binds</a> by yours truly</li>
<li><a href="http://benalman.com/news/2010/11/immediately-invoked-function-expression/">Immediately-Invoked Function Expression (IIFE)</a> by Ben Alman</li>
<li><a href="http://addyosmani.com/resources/essentialjsdesignpatterns/book/">Learning JavaScript Design Patterns</a> by Addy Osmani</li>
<li><a href="http://unschooled.org/2012/03/understanding-javascript-this/">Understanding the “this” keyword in JavaScript</a> by Nicholas Bergson-Shilcock</li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind">MDN – Function.prototype.bind</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply">MDN – Function.prototype.apply</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call">MDN – Function.prototype.call</a></li>
<li><a href="http://kangax.github.io/nfe/">Named function expressions demystified</a> by Juriy “kangax” Zaytsev</li>
<li><a href="http://www.2ality.com/2013/06/basic-javascript.html">Basic JavaScript for the impatient programmer</a> by Axel Rauschmayer</li>
<li><a href="http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html">JavaScript Scoping and Hoisting</a> by Ben Cherry</li>
<li><a href="http://davidshariff.com/blog/javascript-this-keyword/">JavaScript’s ‘this’ Keyword</a> by David Shariff</li>
<li><a href="http://davidshariff.com/blog/what-is-the-execution-context-in-javascript/">What is the Execution Context & Stack in JavaScript?</a> by David Shariff</li>
</ul>
<h2 id="注">注</h2>
<p>英文:http://developer.telerik.com/featured/seven-javascript-quirks-i-wish-id-known-about/</p>
12345679*81=?
2014-04-21T00:00:00+00:00
http://yanhaijing.com/math/2014/04/21/12345679mul8
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>12345679*81
=(11111111+1111111+111111+11111+1111+111+11+2)*9*9
=(99999999+9999999+999999+99999+9999+999+99+9+9)*9
=(100000000+10000000+1000000+100000+10000+1000+100+10+10-9)*9
=111111111*9
=999999999
Done.
</code></pre></div></div>
<h2 id="注">注</h2>
<p>原文:http://www.cnblogs.com/tupx/archive/2012/04/22/2464906.html</p>
仅100行的JavaScript DOM操作类库
2014-04-11T00:00:00+00:00
http://yanhaijing.com/javascript/2014/04/11/a-dom-manipulation-class-in-100-lines-of-javascript
<p>如果你构建过Web引用程序,你可能处理过很多DOM操作。访问和操作DOM元素几乎是每一个Web应用程序的通用需求。我们我们经常从不同的控件收集信息,我们需要设置value值,修改div或span标签的内容。当然有许多库能帮助处理这些行为,其中最流行的当属jQuery,已经成为事实上的标准。有事你并不需要jQuery提供每一样东西,所以在这篇文章中,我们将看看如何创建自己的类库来操作DOM元素。</p>
<h2 id="api">API</h2>
<p>身为开发者的我们每天都要做决定。我相信在测试驱动开发中,我真的非常喜欢的一个事实是它迫使你在开始实际编码之前必须做出设计决定。沿着这些思路,我想我想要的DOM操作类库的API最终看起来可能像这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//返回 DOM 元素
dom('.selector').el
//返回元素的值/内容
dom('.selector').val()
//设置元素的值/内容
dom('.selector').val('value')
</code></pre></div></div>
<p>这应该包括了大多数可能用到的操作。然而如何我们可以一次操作多个对象会显得个更好。如果能生成一个JavaScript对象,那将是伟大之举。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//生成包装DOM元素的对象
dom({
structure: {
propA: '.selector',
propB: '.selector'
},
propC: '.selector'
})
</code></pre></div></div>
<p>一旦我们将元素存下来,我们能很容易对它们执行val方法。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>//检索DOM元素的值
dom({
structure: {
propA: '.selector',
propB: '.selector'
},
propC: '.selector'
}).val()
</code></pre></div></div>
<p>这将是将数据直接从DOM转换为JavaScript对象的有效方法。</p>
<p>现在我们心理已经清楚我们的API看起来的样子,我们类库代码看起来像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var dom = function(el) {
var api = { el: null }
api.val = function(value) {
// ...
}
return api;
}
</code></pre></div></div>
<h2 id="作用域">作用域</h2>
<p>很明显,我们打算使用类似getElementById,querySelector或querySelectorAll这样的方法。通常情况下,你可以像下面这样访问DOM:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var header = document.querySelector('.header');
</code></pre></div></div>
<p>querySeletor是非常有趣的,例如,它不仅仅是document对象的方法,同时也是其他DOM元素的方法。这意味着,我们可以在特定上下文中运行查询。比如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><header>
<p>Big</p>
</header>
<footer>
<p>Small</p>
</footer>
var header = document.querySelector('header');
var footer = document.querySelector('footer');
console.log(header.querySelector('p').textContent); // Big
console.log(footer.querySelector('p').textContent); // Small
</code></pre></div></div>
<p>我们能在特定的DOM树上操作,并且我们的类库应该支持传递作用域。所以,如果它接受一个父元素选择符是非常棒的。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var dom = function(el, parent) {
var api = { el: null }
api.val = function(value) {
// ...
}
return api;
}
</code></pre></div></div>
<h2 id="查询dom元素">查询DOM元素</h2>
<p>按照我们上面所说的,我们将使用querySelector和querySelectorAll查询DOM元素。让我们为这些函数创建两个快捷方式。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var qs = function(selector, parent) {
parent = parent || document;
return parent.querySelector(selector);
};
var qsa = function(selector, parent) {
parent = parent || document;
return parent.querySelectorAll(selector);
};
</code></pre></div></div>
<p>在那之后我们应该传递el参数。通常情况下将是一个(选择符)字符串,但我们也应该支持:</p>
<ul>
<li>DOM元素——类库的val方法会非常方便,所以我们可能需要使用已经引用的元素;</li>
<li>JavaScript对象——为了创建包含多个DOM元素的JavaScript对象。</li>
</ul>
<p>下面的switch包括这两种情况:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>switch(typeof el) {
case 'string':
parent = parent && typeof parent === 'string' ? qs(parent) : parent;
api.el = qs(el, parent);
break;
case 'object':
if(typeof el.nodeName != 'undefined') {
api.el = el;
} else {
var loop = function(value, obj) {
obj = obj || this;
for(var prop in obj) {
if(typeof obj[prop].el != 'undefined') {
obj[prop] = obj[prop].val(value);
} else if(typeof obj[prop] == 'object') {
obj[prop] = loop(value, obj[prop]);
}
}
delete obj.val;
return obj;
}
var res = { val: loop };
for(var key in el) {
res[key] = dom.apply(this, [el[key], parent]);
}
return res;
}
break;
}
</code></pre></div></div>
<p>如果开发者传递字符串将执行第一个case。我们转换parent并且调用querySelector的快捷方式。第二个case将会被执行如果我们传递一个DOM元素或JavaScript对象。我们检查对象是否有nodeName属性,如果有这个属性,我们直接将它的值作为api.el的值。如果没有,那么我们遍历对象的所有属性并且为每个属性初始化为类库实例。这里有一些测试用例:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><p>text</p>
<header>
<p>Big</p>
</header>
<footer>
<p>Small</p>
</footer>
</code></pre></div></div>
<p>访问第一个段落:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dom('p').el
</code></pre></div></div>
<p>访问header节点里的段落:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dom('p', 'header').el
</code></pre></div></div>
<p>传递一个DOM元素:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dom(document.querySelector('header')).el
</code></pre></div></div>
<p>传递一个JavaScript对象:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var els = dom({
footer: 'footer',
paragraphs: {
header: 'header p',
footer: 'footer p'
}
}))
// 最后我们在此得到JavaScript对象。
// 它的属性是实际的结果
// 执行dom函数。例如,获取值
// footer是paragraphs的属性
els.paragraphs.footer.el
</code></pre></div></div>
<h2 id="获取或设置元素的值">获取或设置元素的值</h2>
<p>表单元素的值如input或select可以被很容易的检索到——我们可以使用元素的value属性。我们我们已经有一个能访问的DOM元素了——存储在api.el。然而,当我们碰到单选框或复选框是有些棘手。对于其他HTML节点像div,section或span我们获取元素的值实际上是获取textContent属性。如果textContent是undefined那么可以用innerHTML代替(相似)。让我们写出另一个switch语句:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>api.val = function(value) {
if(!this.el) return null;
var set = !!value;
var useValueProperty = function(value) {
if(set) { this.el.value = value; return api; }
else { return this.el.value; }
}
switch(this.el.nodeName.toLowerCase()) {
case 'input':
break;
case 'textarea':
break;
case 'select':
break;
default:
}
return set ? api : null;
}
</code></pre></div></div>
<p>首先我们需要确保api.el属性存在。set是布尔类型变量告诉我们是获取还是设置元素的value属性。有.value属性的元素包括一个辅助方法。switch语句将包含方法的实际逻辑。最后我们返回api本身,为了保持链式操作。当然我们这样做仅当我们使用设置器函数时。</p>
<p>让我们看看如何处理不能同类型的元素。例如input节点:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>case 'input':
var type = this.el.getAttribute('type');
if(type == 'radio' || type == 'checkbox') {
var els = qsa('[name="' + this.el.getAttribute('name') + '"]', parent);
var values = [];
for(var i=0; i<els.length; i++) {
if(set && els[i].checked && els[i].value !== value) {
els[i].removeAttribute('checked');
} else if(set && els[i].value === value) {
els[i].setAttribute('checked', 'checked');
els[i].checked = 'checked';
} else if(els[i].checked) {
values.push(els[i].value);
}
}
if(!set) { return type == 'radio' ? values[0] : values; }
} else {
return useValueProperty.apply(this, [value]);
}
break;
</code></pre></div></div>
<p>这可能是最有趣的例子了。有两种类型的元素需要不同的处理——单选框和复选框。这些元素实际上是一组,我们要牢记这点。这就是为什么我们使用querySelectorAll获取整组并找出哪个是被选择/选中的。更复杂的是,复选框可能不止被选中一个。上面的方法完美处理所有这些情况。
处理textarea元素非常简单,这要得益于我们上面写的辅助函数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>case 'textarea':
return useValueProperty.apply(this, [value]);
break;
</code></pre></div></div>
<p>下面看我们如何处理下拉列表(select):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>case 'select':
if(set) {
var options = qsa('option', this.el);
for(var i=0; i<options.length; i++) {
if(options[i].getAttribute('value') === value) {
this.el.selectedIndex = i;
} else {
options[i].removeAttribute('selected');
}
}
} else {
return this.el.value;
}
break;
</code></pre></div></div>
<p>最后是默认操作:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>default:
if(set) {
this.el.innerHTML = value;
} else {
if(typeof this.el.textContent != 'undefined') {
return this.el.textContent;
} else if(typeof this.el.innerText != 'undefined') {
return typeof this.el.innerText;
} else {
return this.el.innerHTML;
}
}
break;
</code></pre></div></div>
<p>上面这些代码我们完成了我们的val方法。这里有一个简单的HTML表单和相应的测试:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><form>
<input type="text" value="sample text" />
<input type="radio" name="options" value="A">
<input type="radio" name="options" checked value="B">
<select>
<option value="10"></option>
<option value="20"></option>
<option value="30" selected></option>
</select>
<footer>version: 0.3</footer>
</form>
</code></pre></div></div>
<p>如果我们写下面的:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dom({
name: '[type="text"]',
data: {
options: '[type="radio"]',
count: 'select'
},
version: 'footer'
}, 'form').val();
</code></pre></div></div>
<p>我们会得到:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
data: {
count: "30",
options: "B"
},
name: "sample text",
version: "version: 0.3"
}
</code></pre></div></div>
<p>这方法对于把数据冲HTML导成JavaScript对象非常有帮助。这正是我们很多人每天都很常见的任务。</p>
<h2 id="最后结果">最后结果</h2>
<p>最后完成的类库代码仅有100行代码,但它仍然满足我们所需的访问 DOM元素并且获取和设置value值/内容。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var dom = function(el, parent) {
var api = { el: null }
var qs = function(selector, parent) {
parent = parent || document;
return parent.querySelector(selector);
};
var qsa = function(selector, parent) {
parent = parent || document;
return parent.querySelectorAll(selector);
};
switch(typeof el) {
case 'string':
parent = parent && typeof parent === 'string' ? qs(parent) : parent;
api.el = qs(el, parent);
break;
case 'object':
if(typeof el.nodeName != 'undefined') {
api.el = el;
} else {
var loop = function(value, obj) {
obj = obj || this;
for(var prop in obj) {
if(typeof obj[prop].el != 'undefined') {
obj[prop] = obj[prop].val(value);
} else if(typeof obj[prop] == 'object') {
obj[prop] = loop(value, obj[prop]);
}
}
delete obj.val;
return obj;
}
var res = { val: loop };
for(var key in el) {
res[key] = dom.apply(this, [el[key], parent]);
}
return res;
}
break;
}
api.val = function(value) {
if(!this.el) return null;
var set = !!value;
var useValueProperty = function(value) {
if(set) { this.el.value = value; return api; }
else { return this.el.value; }
}
switch(this.el.nodeName.toLowerCase()) {
case 'input':
var type = this.el.getAttribute('type');
if(type == 'radio' || type == 'checkbox') {
var els = qsa('[name="' + this.el.getAttribute('name') + '"]', parent);
var values = [];
for(var i=0; i<els.length; i++) {
if(set && els[i].checked && els[i].value !== value) {
els[i].removeAttribute('checked');
} else if(set && els[i].value === value) {
els[i].setAttribute('checked', 'checked');
els[i].checked = 'checked';
} else if(els[i].checked) {
values.push(els[i].value);
}
}
if(!set) { return type == 'radio' ? values[0] : values; }
} else {
return useValueProperty.apply(this, [value]);
}
break;
case 'textarea':
return useValueProperty.apply(this, [value]);
break;
case 'select':
if(set) {
var options = qsa('option', this.el);
for(var i=0; i<options.length; i++) {
if(options[i].getAttribute('value') === value) {
this.el.selectedIndex = i;
} else {
options[i].removeAttribute('selected');
}
}
} else {
return this.el.value;
}
break;
default:
if(set) {
this.el.innerHTML = value;
} else {
if(typeof this.el.textContent != 'undefined') {
return this.el.textContent;
} else if(typeof this.el.innerText != 'undefined') {
return typeof this.el.innerText;
} else {
return this.el.innerHTML;
}
}
break;
}
return set ? api : null;
}
return api;
}
</code></pre></div></div>
<p>我创建了一个<a href="http://jsbin.com/locap/5/embed?html,js,console">jsbin</a>的例子,你可以看看类作品。</p>
<h2 id="总结">总结</h2>
<p>我上面讨论的类库是<a href="http://absurdjs.com/pages/client-side-components/">AbsurdJS客户端组件</a>的一部分。该模块的完成文档可以在<a href="http://absurdjs.com/pages/api/build-in-components/#dom">这里</a>找到。这代码的目的并非要取代jQuery或其他可以访问DOM的流行类库。函数的思想是自成一体,一个函数只做一件事并把它做好。这是<a href="http://absurdjs.com/">AbsurdJS</a>背后的主要思想,它也是基于模块化建设的,如<a href="http://absurdjs.com/pages/api/build-in-components/#router">router</a>或<a href="http://absurdjs.com/pages/api/build-in-components/#ajax">Ajax</a>模块。</p>
<h2 id="注">注</h2>
<p>原文 http://flippinawesome.org/2014/03/10/a-dom-manipulation-class-in-100-lines-of-javascript/</p>
用CSS代码写出的各种形状图形的方法
2014-04-04T00:00:00+00:00
http://yanhaijing.com/css/2014/04/04/with-css-code-to-write-various-shapes-graphic-method
<p>一共收集整理了图形20个,比较实用,同时也为了熟悉CSS的代码。整合了一下,有错误欢迎指出。</p>
<h2 id="1正方形">1.正方形</h2>
<p><img src="/blog/103.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#square {
width: 100px;
height: 100px;
background: red;
}
</code></pre></div></div>
<h2 id="2长方形">2.长方形</h2>
<p><img src="/blog/104.jpg" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#rectangle {
width: 200px;
height: 100px;
background: red;
}
</code></pre></div></div>
<h2 id="3左上三角">3.左上三角</h2>
<p><img src="/blog/105.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#triangle-topleft {
width: 0;
height: 0;
border-top: 100px solid red;
border-right: 100px solid transparent;
}
</code></pre></div></div>
<h2 id="4右上三角">4.右上三角</h2>
<p><img src="/blog/106.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#triangle-topright {
width: 0;
height: 0;
border-top: 100px solid red;
border-left: 100px solid transparent;
}
</code></pre></div></div>
<h2 id="5左下三角">5.左下三角</h2>
<p><img src="/blog/107.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#triangle-bottomleft {
width: 0;
height: 0;
border-bottom: 100px solid red;
border-right: 100px solid transparent;
}
</code></pre></div></div>
<h2 id="6右下三角">6.右下三角</h2>
<p><img src="/blog/108.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#triangle-bottomright {
width: 0;
height: 0;
border-bottom: 100px solid red;
border-left: 100px solid transparent;
}
</code></pre></div></div>
<h2 id="7平行四边形">7.平行四边形</h2>
<p><img src="/blog/109.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#parallelogram {
width: 150px;
height: 100px;
-webkit-transform: skew(20deg);
-moz-transform: skew(20deg);
-o-transform: skew(20deg);
background: red;
}
</code></pre></div></div>
<h2 id="8梯形">8.梯形</h2>
<p><img src="/blog/110.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#trapezoid {
border-bottom: 100px solid red;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
height: 0;
width: 100px;
}
</code></pre></div></div>
<h2 id="9六角星">9.六角星</h2>
<p><img src="/blog/111.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#star-six {
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-bottom: 100px solid red;
position: relative;
}
#star-six:after {
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-top: 100px solid red;
position: absolute;
content: "";
top: 30px;
left: -50px;
}
</code></pre></div></div>
<h2 id="10五角星">10.五角星</h2>
<p><img src="/blog/112.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#star-five {
margin: 50px 0;
position: relative;
display: block;
color: red;
width: 0px;
height: 0px;
border-right: 100px solid transparent;
border-bottom: 70px solid red;
border-left: 100px solid transparent;
-moz-transform: rotate(35deg);
-webkit-transform: rotate(35deg);
-ms-transform: rotate(35deg);
-o-transform: rotate(35deg);
}
#star-five:before {
border-bottom: 80px solid red;
border-left: 30px solid transparent;
border-right: 30px solid transparent;
position: absolute;
height: 0;
width: 0;
top: -45px;
left: -65px;
display: block;
content: '';
-webkit-transform: rotate(-35deg);
-moz-transform: rotate(-35deg);
-ms-transform: rotate(-35deg);
-o-transform: rotate(-35deg);
}
#star-five:after {
position: absolute;
display: block;
color: red;
top: 3px;
left: -105px;
width: 0px;
height: 0px;
border-right: 100px solid transparent;
border-bottom: 70px solid red;
border-left: 100px solid transparent;
-webkit-transform: rotate(-70deg);
-moz-transform: rotate(-70deg);
-ms-transform: rotate(-70deg);
-o-transform: rotate(-70deg);
content: '';
}
</code></pre></div></div>
<h2 id="11五边形">11.五边形</h2>
<p><img src="/blog/113.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#pentagon {
position: relative;
width: 54px;
border-width: 50px 18px 0;
border-style: solid;
border-color: red transparent;
}
#pentagon:before {
content: "";
position: absolute;
height: 0;
width: 0;
top: -85px;
left: -18px;
border-width: 0 45px 35px;
border-style: solid;
border-color: transparent transparent red;
}
</code></pre></div></div>
<h2 id="12六边形">12.六边形</h2>
<p><img src="/blog/114.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#hexagon {
width: 100px;
height: 55px;
background: red;
position: relative;
}
#hexagon:before {
content: "";
position: absolute;
top: -25px;
left: 0;
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-bottom: 25px solid red;
}
#hexagon:after {
content: "";
position: absolute;
bottom: -25px;
left: 0;
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-top: 25px solid red;
}
</code></pre></div></div>
<h2 id="13桃心">13.桃心</h2>
<p><img src="/blog/115.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#heart {
position: relative;
width: 100px;
height: 90px;
}
#heart:before,
#heart:after {
position: absolute;
content: "";
left: 50px;
top: 0;
width: 50px;
height: 80px;
background: red;
-moz-border-radius: 50px 50px 0 0;
border-radius: 50px 50px 0 0;
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
transform: rotate(-45deg);
-webkit-transform-origin: 0 100%;
-moz-transform-origin: 0 100%;
-ms-transform-origin: 0 100%;
-o-transform-origin: 0 100%;
transform-origin: 0 100%;
}
#heart:after {
left: 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
-webkit-transform-origin: 100% 100%;
-moz-transform-origin: 100% 100%;
-ms-transform-origin: 100% 100%;
-o-transform-origin: 100% 100%;
transform-origin :100% 100%;
}
</code></pre></div></div>
<h2 id="14无限大符号">14.无限大符号</h2>
<p><img src="/blog/116.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#infinity {
position: relative;
width: 212px;
height: 100px;
}
#infinity:before,
#infinity:after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 60px;
height: 60px;
border: 20px solid red;
-moz-border-radius: 50px 50px 0 50px;
border-radius: 50px 50px 0 50px;
-webkit-transform: rotate(-45deg);
-moz-transform: rotate(-45deg);
-ms-transform: rotate(-45deg);
-o-transform: rotate(-45deg);
transform: rotate(-45deg);
}
#infinity:after {
left: auto;
right: 0;
-moz-border-radius: 50px 50px 50px 0;
border-radius: 50px 50px 50px 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
</code></pre></div></div>
<p>15.蛋</p>
<p><img src="/blog/117.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#egg {
display:block;
width: 126px;
height: 180px;
background-color: red;
-webkit-border-radius: 63px 63px 63px 63px / 108px 108px 72px 72px;
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
}
</code></pre></div></div>
<h2 id="16提示对话框">16.提示对话框</h2>
<p><img src="/blog/118.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#talkbubble {
width: 120px;
height: 80px;
background: red;
position: relative;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
}
#talkbubble:before {
content:"";
position: absolute;
right: 100%;
top: 26px;
width: 0;
height: 0;
border-top: 13px solid transparent;
border-right: 26px solid red;
border-bottom: 13px solid transparent;
}
</code></pre></div></div>
<h2 id="17十二角星">17.十二角星</h2>
<p><img src="/blog/119.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#burst-8 {
background: red;
width: 80px;
height: 80px;
position: relative;
text-align: center;
-webkit-transform: rotate(20deg);
-moz-transform: rotate(20deg);
-ms-transform: rotate(20deg);
-o-transform: rotate(20eg);
transform: rotate(20deg);
}
#burst-8:before {
content: "";
position: absolute;
top: 0;
left: 0;
height: 80px;
width: 80px;
background: red;
-webkit-transform: rotate(135deg);
-moz-transform: rotate(135deg);
-ms-transform: rotate(135deg);
-o-transform: rotate(135deg);
transform: rotate(135deg);
}
</code></pre></div></div>
<h2 id="18八角星">18.八角星</h2>
<p><img src="/blog/120.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#burst-8 {
background: red;
width: 80px;
height: 80px;
position: relative;
text-align: center;
-webkit-transform: rotate(20deg);
-moz-transform: rotate(20deg);
-ms-transform: rotate(20deg);
-o-transform: rotate(20eg);
transform: rotate(20deg);
}
#burst-8:before {
content: "";
position: absolute;
top: 0;
left: 0;
height: 80px;
width: 80px;
background: red;
-webkit-transform: rotate(135deg);
-moz-transform: rotate(135deg);
-ms-transform: rotate(135deg);
-o-transform: rotate(135deg);
transform: rotate(135deg);
}
</code></pre></div></div>
<h2 id="19钻石">19.钻石</h2>
<p><img src="/blog/121.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#cut-diamond {
border-style: solid;
border-color: transparent transparent red transparent;
border-width: 0 25px 25px 25px;
height: 0;
width: 50px;
position: relative;
margin: 20px 0 50px 0;
}
#cut-diamond:after {
content: "";
position: absolute;
top: 25px;
left: -25px;
width: 0;
height: 0;
border-style: solid;
border-color: red transparent transparent transparent;
border-width: 70px 50px 0 50px;
}
</code></pre></div></div>
<h2 id="20八卦">20.八卦</h2>
<p><img src="/blog/122.png" alt="" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#yin-yang {
width: 96px;
height: 48px;
background: #eee;
border-color: red;
border-style: solid;
border-width: 2px 2px 50px 2px;
border-radius: 100%;
position: relative;
}
#yin-yang:before {
content: "";
position: absolute;
top: 50%;
left: 0;
background: #eee;
border: 18px solid red;
border-radius: 100%;
width: 12px;
height: 12px;
}
#yin-yang:after {
content: "";
position: absolute;
top: 50%;
left: 50%;
background: red;
border: 18px solid #eee;
border-radius:100%;
width: 12px;
height: 12px;
}
</code></pre></div></div>
<h2 id="注">注</h2>
<p>原文:http://www.wzsky.net/html/Website/CSS/115159.html</p>
10条影响CSS渲染速度的写法与建议
2014-04-04T00:00:00+00:00
http://yanhaijing.com/css/2014/04/04/10-influence-css-rendering-speed-method-and-suggestions
<h2 id="1-zishu--尽量避开">1、*{} #zishu *{} 尽量避开</h2>
<p>由于不同浏览器对HTML标签的解释有差异,所以最终的网页效果在不同的浏览器中可能是不一样的,为了消除这方面的风险,设计者通常会在CSS的一个始就把所有标签的默认属性全部去除,以达到所有签标属性值都统一的效果。所以就有了*通配符。*会遍历所有的标签;</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>*{margin:0; padding:0}
</code></pre></div></div>
<p><strong>建议的的解决办法:</strong></p>
<p>不要去使用生僻的标签,因为这些标签往往在不同浏览器中解释出来的效果不一样;所以你要尽可能的去使用那些常用的标签;</p>
<p>不要使用*;而是把你常用到的这些标签进行处理;例如:<code class="language-plaintext highlighter-rouge">body,li,p,h1{margin:0; padding:0}</code></p>
<h2 id="2滤镜的一些东西不要去用">2、滤镜的一些东西不要去用</h2>
<p>IE的一些滤镜在FIREFOX中不支持,往往写一些效果时你还是使用CSS HACK;而滤镜是一个非常毫资源的东西;特别是一些羽化、阴影和一个前透明的效果;</p>
<p><strong>建议的解决办法:</strong></p>
<p>能不使用就不要使用,一方面兼容问题;很多效果只能在IE中使用;</p>
<p>就本例而言,如果非要这样在的效果,建议用图片作背景;(只说优化速度,实际应用还是可以小部分用,有人可能会说,用图片还多一个HTTP请求呢,呵呵……)</p>
<p>一个非常好的例子,就是在今年512大地震时,很多网站一夜之间全部变成了灰色,他们只用了一行CSS代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>body{filter: gray;}
</code></pre></div></div>
<p>但,你会看会看到这些网页非常的慢,打开后你的CPU也会飙升,不夸张的说,如果你的电脑配置差,干死你也不为过。</p>
<h2 id="3一个页面上少用绝对定位">3、一个页面上少用绝对定位</h2>
<p>绝对定位(<code class="language-plaintext highlighter-rouge">position:absolute</code> )是网页布局中很常用到的,特别是作一些浮动效果时,也会让页面看起来非常的酷。但网页中如果使用过多的绝对定位,会让你的网页变得非常的慢,这一点上边FIREFOX表现要比IE还要差。</p>
<p><strong>建议的解决办法:</strong></p>
<p>尽可能少用,这个少用的值是多少,也没有一个非常好的值来说明;还要看绝定定位这个标签里边的内容的多少;在这里我只能说,这样写会有性能问题,少用。</p>
<p>如果能用变通实现同样的效果,就用变通的办法。</p>
<h2 id="4background-背景图片的平铺">4、background 背景图片的平铺</h2>
<p>有些网页的背景或页面中某块的背景通常要用到图片的平铺,平铺后就会有平铺次数的问题,如果是单次还好,如果是多次,就废了。</p>
<p><strong>建议的作法:</strong></p>
<p>色彩少的图片要作成gif图片;</p>
<p>平铺的图片尽可能大一些,如果是色彩少的GIF图片,图片大一些,实际大小也不会大多少;上边的两个例子就很好的证明,第一个图片非常少,第二个图大较大一些;但速度是非常不一样的;</p>
<h2 id="5让属性尽可能多的去继承">5、让属性尽可能多的去继承</h2>
<p>尽可能让一些属性子可以继承父,而不是覆盖父;</p>
<h2 id="6css的路径别太深">6、CSS的路径别太深;</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#zishu #info #tool #sidebar h2{ font-size:12px;}
</code></pre></div></div>
<h2 id="7能简写的一些就简写">7、能简写的一些就简写;</h2>
<p>这个对渲染速度没有影响;只是少几个字符;</p>
<h2 id="8别放空的的class或没有的class在html代码中">8、别放空的的class或没有的class在HTML代码中;</h2>
<h2 id="9float-的应用">9、float 的应用</h2>
<p>这个东西我的感觉是如果使用不当,百分百有性能问题,而且还非常的大,但实在不知道怎么样能弄一个例子出来;这里只能建议大家如果不是很明白float是怎么工作的,还是少使用为妙。</p>
<p>曾经因为这个把IE干死过,我写过一个例子,虽说和这个没有什么太大的关系:IE之死__原来与CSS有关</p>
<h2 id="10合理的布局">10、合理的布局</h2>
<p>为什么这么说呢,合理的布局,可以改变CSS的写法以及渲染过程。</p>
<h2 id="注">注</h2>
<p>原文:http://bbs.html5cn.org/thread-2395-1-1.html</p>
CSS3中的网格
2014-04-01T00:00:00+00:00
http://yanhaijing.com/css/2014/04/01/grids-in-css3
<p>在这篇文章中,我们将来看一些CSS3新属性,从而用HTML和CSS处理网格的时候更容易。但首先让我们讨论一点HTML和CSS网格的历史,了解清楚为什么以前很困难。</p>
<h2 id="网格简史">网格简史</h2>
<p>曾几何时,我们的布局是一团糟。表格和框架是用于创建多列布局的主要工具。虽然他们能完成工作(但其实非常糟糕)。</p>
<p>把目光投向今天。HTML和CSS已经变得非常复杂,Web设计的知名度和复杂度与日俱增。我们曾经使用的旧的布局方法显然已经out了。然而,一个历史遗留问题浮出水面:多列布局。</p>
<p>更复杂的是,我们的页面宽度不再是静态的。响应式大行其道,所以我们倾向于基于百分比的列宽。基于固定960像素宽的简单网格已经行不通——我们需要流体网格。</p>
<p>CSS2规范中用浮动解决列的方法存在一个问题。为了防止父元素破环你的布局,我们需要添加clearfix。通过这种方法,来修正父元素的高度坍塌问题(浮动元素脱离标准流,父元素会认为浮动资源不存在)。我们大部分接受这种方法,但许多人仍然认为它是一种hack(奇技淫巧)。</p>
<p>通过inline-box的方法并不常见,但仍然存在。内联元素会保持在一行,他们自然顺序排列。当一行被占满,后面的元素自然折到下一行。但因为他表现为文本特性,其行为类似文本。这意味着你必须避免HTML元素之间的空白元素(空格,tab,换行……)。Inline-block不是为这设计的,不且工作的并不十分如意。</p>
<p>在这两种方法中,浮动的方法更可靠。这就是为什么它更流行,排在第一位。然而,创建多列后,我们发现需要再次压缩内容,因为我们需要一些填充距离。这就引出最终的问题:盒模型</p>
<p>盒模型是什么,简单来说,一个元素的实际尺寸包括:高度/宽度+内边距+边的宽度。外边据并不使盒子变大,仅仅在自己和其他元素之间增加空隙。所以设置宽度时,比如25%,其盒子的实际宽度比这大得多,这意味着在一行没有足够的空间放下四个元素。</p>
<p>这烦人的问题有不同的解决方案:负外边距,嵌套元素——我知道的全部了。他们都需要额外的CSS或DOM元素,算作hack方法。让我们面对现实,CSS2中没有解决网格的好方法。</p>
<p>然而今天,CSS3提供很好的支持,规范增加了专门用于网格的几个新特性。这些特性都有哪些?我们如何使用他们?让我们看一看。</p>
<h2 id="box-sizing-border-box">box-sizing: border-box</h2>
<p>已经解决的问题之一是扩展盒模型的性质。通过设置box-sizing的值为border-box可以解决这个问题。通过减少内容宽度使边和内边距的距离也算到width属性里。</p>
<p><strong>HTML</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div class="row">
<div class="column">Col one</div>
<div class="column">Col two</div>
<div class="column">Col three</div>
<div class="column">Col four</div>
</div>
</code></pre></div></div>
<p><strong>CSS</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.row:after {
clear: both;
content: '';
display: block;
}
.column {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
float: left;
min-height: 8em;
overflow: hidden;
padding: 2em;
width: 25%;
}
.column:nth-child(1) { background-color: #9df5ba; }
.column:nth-child(2) { background-color: #9df5d7; }
.column:nth-child(3) { background-color: #9df5f5; }
.column:nth-child(4) { background-color: #9dd7f5; }
</code></pre></div></div>
<p><strong>Demo</strong></p>
<p><img src="/blog/100.png" alt="" /></p>
<p>虽然这工作的很棒,但我们仍然需要使用浮动,我们仍然需要清除浮动。此外,我们我们只能使用内边距作为元素的空间,外边距不再起作用。这意味着在快元素之间没有实际的空间,而是它的内容。虽然这对很多设计非常有用,但仍然觉得它是个小错误。</p>
<ul>
<li>Firefox 1</li>
<li>Chrome 1</li>
<li>IE 8</li>
<li>Opera 7</li>
<li>Safari 3</li>
</ul>
<h2 id="width-calc百分比--距离">width: calc(百分比 – 距离)</h2>
<p>另一个伟大的选择是使用calc()函数。他允许我们在不依赖JavaScript的情况下计算元素的真实宽度——可以是不同的单位!</p>
<p><strong>HTML</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div class="row">
<div class="column">Col one</div>
<div class="column">Col two</div>
<div class="column">Col three</div>
<div class="column">Col four</div>
</div>
</code></pre></div></div>
<p><strong>CSS</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.row { margin-left: -1em; }
.row:after {
clear: both;
content: '';
display: block;
}
.column {
float: left;
margin-left: 1em;
min-height: 8em;
padding: 1em;
width: -webkit-calc(25% - 3em);
width: -moz-calc(25% - 3em);
width: calc(25% - 3em);
}
.column:nth-child(1) { background-color: #9df5ba; }
.column:nth-child(2) { background-color: #9df5d7; }
.column:nth-child(3) { background-color: #9df5f5; }
.column:nth-child(4) { background-color: #9dd7f5; }
</code></pre></div></div>
<p><strong>Demo</strong></p>
<p><img src="/blog/101.png" alt="" /></p>
<p>重新计算实际尺寸的能力是一个伟大的选择,但不幸的的是,我们仍然需要浮动,我们也需要列的容器为负外边距。同上,一个很棒的选择,但仍然有些瑕疵。</p>
<ul>
<li>Firefox 4</li>
<li>Chrome 19</li>
<li>IE 9</li>
<li>Opera ?</li>
<li>Safari 6 (appears to be a little buggy)</li>
</ul>
<h2 id="flexbox">Flexbox</h2>
<p>伸缩布局盒是有特定配置行为的元素——有点像表格。这是真的吗?是的没错。表格的行为实际上相当棒,因为它的显示依据它的内容而变化。但现在已经不再使用表格布局,所以表格标签不是一个选项。
起初,伸缩盒看起来有待年复杂。有很多很难理解的属性,尤其像我这样不擅用英语演讲的人。幸运的是,Chirs Coyier写了一个关于伸缩盒的伟大指导,我必须提到。</p>
<p><strong>HTML</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div class="row">
<div class="column">Col one</div>
<div class="column">Col two</div>
<div class="column">Col three</div>
<div class="column">Col four</div>
</div>
</code></pre></div></div>
<p><strong>CSS</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.row {
display: -webkit-flex;
-webkit-flex-direction: row;
-webkit-flex-wrap: nowrap;
-webkit-justify-content: space-between;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between;
}
.column {
margin: 0.5em;
min-height: 8em;
padding: 1em;
width: 25%;
}
.column:nth-child(1) { background-color: #9df5ba; }
.column:nth-child(2) { background-color: #9df5d7; }
.column:nth-child(3) { background-color: #9df5f5; }
.column:nth-child(4) { background-color: #9dd7f5; }
</code></pre></div></div>
<p><strong>Demo</strong></p>
<p><img src="/blog/102.png" alt="" /></p>
<p>就这么简单!但……浏览器的支持不是很好。</p>
<ul>
<li>Firefox 18</li>
<li>Chrome 21</li>
<li>IE 10</li>
<li>Opera 12.10</li>
<li>Safari 6.1</li>
</ul>
<h2 id="结论">结论</h2>
<p>尽管CSS3带来了许多新特性并且修复了一些历史遗留问题,在我看来,伸缩盒布局是用CSS创建弹性网格的唯一非hack方法。然而,不幸的是浏览器的支持表现平平。尽管如何,其他方法丰富了表现,所以他们是一个进步,并且他们有很好的浏览器支持。</p>
<h2 id="注">注</h2>
<p>原文:http://flippinawesome.org/2014/03/03/grids-in-css3/</p>
正确使用HTML title属性
2014-03-26T00:00:00+00:00
http://yanhaijing.com/html/2014/03/26/using-the-html-title-attribute
<p>如果你想对使用手机,平板电脑和辅助技术的用户隐藏某些内容,而只对键盘用户显示,那么请使用title属性。</p>
<h2 id="细节">细节</h2>
<p><a href="http://www.w3.org/html/wg/drafts/html/master/dom.html#the-title-attribute">HTML的title</a>属性本身有问题。之所以有问题是因为它在一些重要的方面表现的不够好,尽管它陪伴我们<a href="http://www.w3.org/TR/REC-html32#anchor">超过14年了</a>。随着触摸设备的兴起,这个属性的作用进一步降低。title属性的可访问性变得很鸡肋,由于缺少浏览器的支持,屏幕阅读器的支持和制作人员的重视。</p>
<h3 id="下列情况下title属性由于缺乏支持变得多余">下列情况下title属性由于缺乏支持变得多余:</h3>
<ul>
<li>对于在手机浏览器里访问web内容信息的人。通常title属性的内容在桌面浏览器里显示为提示信息。据我所知,没有任何手机浏览器<a href="http://twitter.com/jared_w_smith/status/29095537048">支持显示</a>提示信息,并且也没有其他访问title属性内容的视觉方法。</li>
<li>对于那些无法使用鼠标的人提供信息。通常title属性的内容在桌面浏览器里显示为提示信息。虽然提示信息的行为已经有10多年历史,但一直没有浏览器实现使用键盘显示title属性的方法。</li>
<li>对于在大多数<a href="http://www.w3.org/TR/2010/WD-html-markup-20101019/elements.html#elements">HTML元素</a>上使用它为使用各种辅助技术的人提供信息。据我所知屏幕阅读器对访问title属性信息一致不支持。</li>
</ul>
<h3 id="title属性不友好用户如下">title属性不友好用户如下</h3>
<ul>
<li>手机用户</li>
<li>仅使用键盘的用户</li>
<li>使用屏幕放大器的用户</li>
<li>屏幕阅读器用户</li>
<li>精细运动技能障碍的用户</li>
<li>认知障碍的用户</li>
</ul>
<h3 id="title属性有用的例子">title属性有用的例子:</h3>
<ul>
<li>为frame或iframe元素贴上标签:
<ul>
<li><code class="language-plaintext highlighter-rouge"><frame title="navigation"></code></li>
</ul>
</li>
<li>提供需要程序才能实现的在特殊情况下才显示的标签,直接使用可见的文本标签会显得多余:
<ul>
<li><code class="language-plaintext highlighter-rouge"><input type="text" title="search"> <input type="submit" value="search"></code></li>
<li><a href="http://files.paciellogroup.com/presentations/techshare07/?slideSelect=0#slide28"> 数据表格中的标签控件</a>。
<h3 id="title属性无用或用处不大的例子">title属性无用或用处不大的例子:</h3>
</li>
</ul>
</li>
<li>为不能作为文本的链接或周围内容添加额外信息:
<ul>
<li><code class="language-plaintext highlighter-rouge"><a href="newsletter.PDF" title="PDF file, size 1 mb.">newsletter</a></code></li>
<li>相反这样的信息应该作为链接文本的部分或在链接的旁边。</li>
</ul>
</li>
<li>提供和链接文本相同的信息:
<ul>
<li><code class="language-plaintext highlighter-rouge"><a href="newsletter.PDF" title="newsletter">newsletter</a></code></li>
<li>建议不要复制链接内容作为title属性。这其实相当于什么都没做。</li>
</ul>
</li>
<li>用于图像的标题:
<ul>
<li><code class="language-plaintext highlighter-rouge"><img src="castle1858.jpeg" title="Oil-based paint on canvas. Maria Towle, 1858."
alt="The castle now has two towers and two walls."></code></li>
<li>大概标题信息是最重要的信息,应该能被所有用户默认访问。如果是这样,那么这个内容应该紧挨着图片。</li>
</ul>
</li>
<li>用来代替表单的标签,去掉可见的文本标签:
<ul>
<li><code class="language-plaintext highlighter-rouge"><input type="text" title="name"></code></li>
<li>屏幕阅读器的用户将会访问表单元素的标签,由于title属性被列入可访问性api内的属性名称(当文本标签使用标签元素时是不被支持的)。许多其他用户并不如此。建议尽可能包括一个可见的文本标签。</li>
</ul>
</li>
<li>为表单元素提供和可见的标签内容相同的信息:
<ul>
<li><code class="language-plaintext highlighter-rouge"><label for="n1">name</label> <input type="text" title="name" id="n1"></code></li>
<li>重复可见的标签文本不可能除了添加一系列的用户认知噪声。不做它。重复可见的标签文本除了添加一系列令人讨厌的认知噪声外,似乎没有其他作用,停止这种用法。</li>
</ul>
</li>
<li>为表单元素提供额外的指令:
<ul>
<li><code class="language-plaintext highlighter-rouge"><label for="n1">name</label> <input type="text" title="Please use uppercase."id="n1"></code></li>
<li>如果这指令对于正确的使用表单元素非常重要,请在元素周围提供文字信息,确保每个用户都能读到。</li>
</ul>
</li>
<li>作为缩写的扩展:
<ul>
<li><code class="language-plaintext highlighter-rouge"><abbr title="world wide web consortium">W3C</abbr></code></li>
<li>虽然abbr元素的title属性被屏幕阅读器软件所支持,但使用它仍然是有问题的,因为其他用户群无法使用。建议当缩写词在文档中首次出现时提供文本格式的全称,或提供全称形式的术语表。这并不是说不可以使用title属性,因其具有局限性,应该提供文本形式的全称。</li>
</ul>
</li>
</ul>
<h3 id="html-51-包括使用title属性的一般性建议"><a href="http://www.w3.org/html/wg/drafts/html/master/">HTML 5.1</a> 包括使用title属性的一般性建议:</h3>
<blockquote>
<p>依赖<a href="http://www.w3.org/html/wg/drafts/html/master/dom.html#attr-title">title</a>属性目前是不被鼓励的,由于许多用户代理不能按照规范的要求显示这个属性(如需要鼠标指针设备引起提示信息的显示,排除了仅使用键盘的用户和触摸屏用户)</p>
</blockquote>
<p><strong>来源: <a href="http://www.w3.org/html/wg/drafts/html/master/dom.html#the-title-attribute">HTML 5.1 – title属性</a></strong></p>
<p>用title属性代替img元素的alt属性或作为图片的标题是被禁止的</p>
<blockquote>
<p>依托title属性目前来看是被禁止的,由于许多用户代理对这属性的可访问性支持很差……</p>
</blockquote>
<p><strong>来源: <a href="http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#a-key-part-of-the-content">HTML 5.1</a></strong></p>
<h2 id="扩展阅读">扩展阅读</h2>
<ul>
<li><a href="http://www.paciellogroup.com/blog/2012/01/html5-accessibility-chops-title-attribute-use-and-abuse/">title attribute use and abuse</a></li>
<li><a href="http://blog.paciellogroup.com/2010/11/?p=37">The title attribute – what is it good for? (resurrected)</a></li>
<li><a href="http://www.rnib.org.uk/wacblog/articles/too-much-accessibility/too-much-accessibility-title-attributes/">Too much accessibility – TITLE attributes</a></li>
</ul>
<h2 id="注">注</h2>
<p>原文:http://blog.paciellogroup.com/2010/11/using-the-html-title-attribute/</p>
开始写 CSS 吧
2014-03-26T00:00:00+00:00
http://yanhaijing.com/css/2014/03/26/starting-to-write-css
<p>你是否觉得 CSS 不再跟以前一样了呢?最近几年成了热门话题,许多聪明的人也在谈论它。CSS 远不止是前端开发者应该用来美化网页的小玩意儿。我们关心性能,想要创作出更好的网站。</p>
<p>在这篇文章里,我会分享最近几个月学习 CSS 相关的知识和我个人对编写 CSS 代码确切的看法。作为程序员,我真的对每样东西的结构部分很感兴趣。我觉得编写 CSS 的方式应该改变并对此深入研究。我寻找好的处理方式,最佳准则(best principles)和新的工作流程(workflows)。</p>
<p>这篇文章就像是在 CSS 世界里旅行的总结。很多人说编写 CSS 不是真正的编程。我并不认同,它同样充满乐趣和挑战性。</p>
<h2 id="css-预处理器css-preprocessors">CSS 预处理器(CSS Preprocessors)</h2>
<p><img src="/blog/94.jpg" alt="" /></p>
<p>当一个程序员开始写 CSS 时发生了什么 当一个程序员开始写 CSS 时发生了什么:你 -> 编写代码 -> 预处理器 -> CSS 代码 -> 网页</p>
<p>好吧,让我们面对它。这世上编写纯的 CSS 不是件有趣的事情。预处理器使用一些类似 CSS 语法,神奇地生成有效的 CSS 代码。在你和最终发送给浏览器的样式之间,它添加了一个中间层。这没有听起来那么坏,因为预处理器提供了一些真正实用的特性。</p>
<h3 id="合并concatenation">合并(Concatenation)</h3>
<p>我认为能使你的文件合并在一起是最有价值的事情之一。我确信,你了解当在你的 <code class="language-plaintext highlighter-rouge">.css</code> 文件用<code class="language-plaintext highlighter-rouge"> @import</code>时,实际上告诉浏览器“麻烦你顺便也捎带这个文件”。 确实如此,发送新的请求,这有点不好,因为你可能会有非常多的文件。发送额外的请求会降低应用的性能。如果你使用 CSS 预处理器,这个问题将会解决。它们会很容易地把你所有的样式编译到单一的 <code class="language-plaintext highlighter-rouge">.css</code> 文件。</p>
<h3 id="扩展-extending">扩展 (Extending)</h3>
<p>主要有两个 CSS 预处理器 —— <a href="http://lesscss.org/">LESS</a> 和 <a href="http://sass-lang.com/">SASS</a>。它们都支持扩展。没错,工作方式略有不同,不过想法(idea)是一样的。你写一个有一串属性的基本类(通常称作 <code class="language-plaintext highlighter-rouge">mixin</code>),之后把这些属性导入到另一个选择器。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// less
.bordered(@color: #000) {
border: dotted 2px @color;
}
.header { .bordered; }
.footer { .bordered(#BADA55); }
// compiles to
.header {
border: dotted 2px #000000;
}
.footer {
border: dotted 2px #bada55;
}
</code></pre></div></div>
<p>这里有个问题,如果你定义了一个没有参数的 mixin,也就是说像这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.bordered {
border: dotted 2px #000;
}
</code></pre></div></div>
<p>它会原样编译到 CSS 文件里,不管你是否使用到。就像这样,因为这是有效的选择器。在 SASS 里,我们会多一点点灵活性。分别是 <code class="language-plaintext highlighter-rouge">mixins</code>,<code class="language-plaintext highlighter-rouge">extends</code> 和 <code class="language-plaintext highlighter-rouge">placeholders</code> (如果你想了解它们准确的不同之处,我强烈推荐<a href="http://krasimirtsonev.com/blog/article/SASS-mixins-extends-and-placeholders-differences-use-cases">这篇文章</a>。让我们看下 SASS 及其编译后的结果:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// sass
@mixin bordered($color: #000) {
border: dotted 2px $color;
}
.header { @include bordered; }
.footer { @include bordered(#BADA55); }
// compiles to
.header {
border: dotted 2px black;
}
.footer {
border: dotted 2px #bada55;
}
</code></pre></div></div>
<p>看起来几乎和 LESS 相同,但如果我们看下第二个用例,定义一个占位符(a place holder):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// sass
%bordered {
border: dotted 2px #000;
}
.header {
@extend %bordered;
}
.footer {
@extend %bordered;
}
// compiles to
.header, .footer {
border: dotted 2px #000;
}
</code></pre></div></div>
<p>有两个很好的事情发生。首先,这里不会编译 <code class="language-plaintext highlighter-rouge">.bordered</code> 类(there is no .bordered class compiled)。第二,SASS 合并了选择器,这让我们的 CSS 更短一些。</p>
<h3 id="配置-configuration">配置 (Configuration)</h3>
<p>LESS 和 SASS 都支持定义变量。你可以稍后再访问这些变量,使用它们作为属性的值。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// sass
$brand-color: #009f0A;
...
h1 {
color: $brand-color;
}
</code></pre></div></div>
<p>这是个好的特性,因为你可能会在同一个地方,存储一些像公司的颜色或网格宽度之类重要的东西。如果你想要修改,可以不用检查一边所有的代码。</p>
<p>另一个方便的用法是插入变量。下面的例子演示这种方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// sass
@mixin border($side) {
border-#{$side}: solid 1px #000;
}
.header {
@include border("left");
}
// compiles to
.header {
border-left: solid 1px #000;
}
</code></pre></div></div>
<h3 id="反对预处理器-against-the-preprocessors">反对预处理器 (Against the preprocessors)</h3>
<ul>
<li>
<p>预处理器是一个工具,也就是说,你必须多做一件事,把它添加到把你的开发环境中。你可能想要把它整合进你的应用里,当然这需要额外编写代码。</p>
</li>
<li>
<p>如果你不想让你的代码跟预处理器的弄乱,那么你很有可能需要一个监听工具。另一个用来监听你文件的工具,一旦文件有更新就会生成编译后的版本。如果是这样的话,那么每次当你开始开发项目的时候都要运行这个监听工具。也许你会优化这个过程所需的时间,但它还是需要你多留一份心。</p>
</li>
<li>
<p>许多开发者总是只盯着他们的 <code class="language-plaintext highlighter-rouge">.less</code> 或者 <code class="language-plaintext highlighter-rouge">.sass</code> 文件。但编译后的文件才是重要的。你的 SASS 代码可能很优雅并优化过的,但这并不意味着你最后得到同样优美的 CSS 代码。你可能会有真正需要关心的特定问题。因此,定期地检查编译后的版本。</p>
</li>
</ul>
<h2 id="bem">BEM</h2>
<p><img src="/blog/95.jpg" alt="" /></p>
<p>BEM 代表块 Block 元素 Element 修饰符 Modifier BEM代表块(Block),元素(Element),修饰符(Modifier)。</p>
<p>好吧,我找到一个可以玩的新工具。预处理器也许能够节省大量的时间,但是单独使用它们不能写出好的结构。我开始思考的第一件事是命名规范。让我们看下面 HTML 代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><header class="site-header">
<div class="logo"></div>
<div class="navigation"></div>
</header>
</code></pre></div></div>
<p>样式可能跟这个类似:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.site-header { ... }
.logo { ... }
.navigation { ... }
</code></pre></div></div>
<p>当然,这会奏效。但有个问题,阅读这个 CSS 你不能理解它,例如, <code class="language-plaintext highlighter-rouge">logo</code> 属于 <code class="language-plaintext highlighter-rouge">header</code>。你也许有另一个小的 <code class="language-plaintext highlighter-rouge">logo</code> 图片用在 <code class="language-plaintext highlighter-rouge">footer</code>。下一个逻辑步骤是写一个后代选择器。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.site-header .logo { ... }
</code></pre></div></div>
<p>然而使用这种选择器不是个好主意,因为这把样式紧绑到特定的标记层次(it tights styles to specific tags hierarchy)。如果我把 <code class="language-plaintext highlighter-rouge">logo</code> 移到 <code class="language-plaintext highlighter-rouge">header</code> 标签外面会怎样呢?这样式会失效。另一个你能做的是把 <code class="language-plaintext highlighter-rouge">site-header</code> 添加到 <code class="language-plaintext highlighter-rouge">logo</code> 类的名字中:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.site-header-logo { ... }
</code></pre></div></div>
<p>这很好,不言自明(self explanatory)。但这不是在所有的情况下都奏效。以后,在十二月份可能想要使用圣诞节版的 logo。那么,我不能写成:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.site-header-logo-xmas { ... }
</code></pre></div></div>
<p>因为我的逻辑是写一个选择器能够匹配嵌套在 HTML 里的标记。</p>
<p><a href="http://bem.info/method/definitions/">BEM</a> 是这种情况的解决方案。它意思是块(Block),元素(Element),修饰符(Modifier)和创建一些你可以遵循的规则。使用 BEM,我们小小的例子会变成这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.site-header { ... } /* block */
.site-header__logo { ... } /* element */
.site-header__logo--xmas { ... } /* modifier */
.site-header__navigation { ... } /* element */
</code></pre></div></div>
<p>也就是说,<code class="language-plaintext highlighter-rouge">.site-header</code> 是我们的块(our block)。logo 和 导航(navigation)这个块的元素(elements),logo 的 <code class="language-plaintext highlighter-rouge">xmas</code> 版本是修饰符(modifier)。也许这看起来简单,但这真的强大。一旦你开始使用它会发现让你更好的结构化。反对的理由主要是 BEM 的语法。没错,这看来确实有点难看,但我准备好对这个好系统的命名妥协。</p>
<p>(值得阅读:<a href="http://bem.info/method/definitions/">这里</a> 和 <a href="http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/">这里</a> )</p>
<h2 id="oocss">OOCSS</h2>
<p><img src="/blog/96.jpg" alt="" /></p>
<p>面向对象的 CSS 面向对象的 CSS</p>
<p>我找到了 BEM 就能准确地命名我的类,然后就开始思考构造(composition)。也许我第一次阅读的文章是关于<a href="https://github.com/stubbornella/oocss/wiki">面向对象的 CSS</a>。面向对象编程有时是关于添加抽象并且 CSS 能够支持它。是否使用预处理器,你都应该了解 OOCSS 。作为码农(coder),我发现这个理念真的跟平时编程很接近,例如 JavaScript 。这是两个主要原则(principles):</p>
<h3 id="结构和表层分开-separate-structure-and-skin">结构和表层分开 (Separate structure and skin)</h3>
<p>让我们用下面的例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.header {
background: #BADA55;
color: #000;
width: 960px;
margin: 0 auto;
}
.footer {
background: #BADA55;
text-align: center;
color: #000;
padding-top: 20px;
}
</code></pre></div></div>
<p>这里有些样式是重复的。我们可以把它们提取到另一个类,如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.colors-skin {
background: #BADA55;
color: #000;
}
.header {
width: 960px;
margin: 0 auto;
}
.footer {
text-align: center;
padding-top: 20px;
}
</code></pre></div></div>
<p>这样,我们就有了可扩展的 <code class="language-plaintext highlighter-rouge">colors-skin</code> 对象。HTML 代码也许跟下面的类似:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div class="header colors-skin"> ... </div>
<div class="colors-skin"> ... </div>
<div class="footer colors-skin"> ... </div>
</code></pre></div></div>
<p>这种改变有几个好处:</p>
<ul>
<li>我们有一个可以使用多次的类。</li>
<li>如果我们需要修改,只需修改一处。</li>
<li>我们移除了 CSS 文件里的重复项,这让文件更小。</li>
</ul>
<h3 id="容器和内容分开-separate-container-and-content">容器和内容分开 (Separate container and content)</h3>
<p>这里的理念是,不管每个元素放在哪里,都应该被应用同一种样式。因此,你应该避免跟下面类似的选择器用法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.header .social-widget {
width: 250px;
}
</code></pre></div></div>
<p>这是因为如果你把 <code class="language-plaintext highlighter-rouge">.social-widget</code> 移到 <code class="language-plaintext highlighter-rouge">.header</code> 容器(container)外面,宽度(width)就会不相同。一般来说这不是好的做法,尤其是给整个页面到处都使用的部分添加样式。这个原则鼓励使 CSS 模块化,我强烈建议利用充足的时间尝试下。就我个人而言,遵守这个原则意味着能够编写出更好的 CSS。</p>
<h2 id="框架-the-framework">框架 (The framework)</h2>
<p>如果你在 GitHub 打开 <a href="https://github.com/stubbornella/oocss">OOCSS repository</a> 会看到一个框架。是的,这个框架用到面向对象的 CSS 理念,并且有一堆很酷的组件可以立即使用(ready-to-use)。某些时候我并不喜欢框架。如果你思考一下的话会发现,framework 这个单词有部分 frame 和 work,你确实在条条框框(frame)里工作(work)。你确实要对框架妥协并且必须遵守它的规则。我更愿意使用微框架(micro-frameworks)或者只提供最基本特性的工具。当然我不是打算重新发明轮子,但我总是试着在两者之间取得平衡。经常这样,现成可用(ready-to-use)的解决方案会带来混乱复杂的系统。我的建议是,干一件事只为了一个特定的目的。如果你可能多地包含方方面面,你的下场将会是……你懂的,一个框架。</p>
<p>但是,我强烈推荐你查看下 OOCSS 框架。这是一个独特的知识(It’s an unique piece of knowledge),也许会满足你的需求。<a href="https://twitter.com/stubbornella">Nicole Sullivan</a> 托管这个 repository (仓库)。她是 OOCSS 的创始人(She is a pioneer in OOCSS),如果你有空的话我建议你查看下这个 <a href="http://www.youtube.com/watch?v=GhX8iPcDSsI">presentations/talks</a>。</p>
<h2 id="smacss">SMACSS</h2>
<p><img src="/blog/97.jpg" alt="" /></p>
<p>可量化和模块化 CSS 架构 Scalable and Modular Architecture for CSS 可量化和模块化的 CSS 架构</p>
<p>另一个流行的理念是 <a href="http://smacss.com/">SMACSS</a>。SMACSS 表示可量化(Scalable)和模块化(Modular)的 CSS 架构。<a href="https://twitter.com/snookca">Jonathan Snook</a> 为 CSS 开发者介绍了风格指南(introduces something like style guide for the CSS developers)。这个想法是把你的应用分成以下几种类型:</p>
<ul>
<li>基本(Base)—— 为一些简单的选择器设置默认的基本样式。例如 clearfix (清除浮动)。</li>
<li>布局(Layout)—— 定义网格(grids)。</li>
<li>模块(Module)—— 一组元素组合成模块。例如 header 和 sidebar。</li>
<li>状态(State)—— 包括了元素不同的状态。如果特定的对象是隐藏的,点击的(pressed),扩展的(expanded)等等……则定义相应的规则。</li>
<li>主题(Theme)—— 更多地面向视觉部分。跟状态类型相似。
我没有使用 SMACSS 的经验,但它非常受欢迎,并确实提倡了好的想法。它更像是一个理念而不是框架,这非常好。因此,你不会受严格的规则,类或者是组件( components)束缚。</li>
</ul>
<h2 id="原子设计-atomic-design">原子设计 (Atomic design)</h2>
<p><img src="/blog/98.jpg" alt="" /></p>
<p>原子设计 原子(Atoms) -> 分子(molecules) -> 组织(organisms) -> 模板(Templates) -> 网页(Pages)</p>
<p>了解 OOCSS 和 SMACSS 之后我寻找一个恰当的象征(metaphor),很快我看到了 <a href="http://bradfrostweb.com/blog/post/atomic-web-design/">Atomic Design</a> 这篇文章,很好地展示 原子设计( Atomic Design) 这个好的理念。作者是 <a href="http://bradfrostweb.com/">Brad Frost</a>,是个知名的 Web 开发者,主要从事<a href="http://bradfrost.github.io/this-is-responsive/index.html">自适应式</a>(responsive)和移动终端方面的工作。</p>
<p>这个理念真的很有趣。效仿一些化学技术,物质的基本单位是原子。Brad 把这移到 CSS,我们的网页是由原子构成的。一个原子是这样的:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><label>Search the site</label>
</code></pre></div></div>
<p>或者</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><input type="text" placeholder="enter keyword" />
</code></pre></div></div>
<p>也就是说,原子包含 DOM 元素一些基本的样式。例如调配颜色(color palette),字体大小或者转换(transitions)。稍后这些部分会合并成一个分子(molecules)。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><form>
<label>Search the site</label>
<input type="text" placeholder="enter keyword" />
<input type="submit" value="search" />
</form>
</code></pre></div></div>
<p>这样 <code class="language-plaintext highlighter-rouge">form</code> 元素包含了几个原子。像这样的抽象化带来灵活性,因为我们也许会用同样的原子构建另一个分子。这样(Together with that),我们能在不同的地方(contexts)重复使用同样的 <code class="language-plaintext highlighter-rouge">form</code>。</p>
<p>Brad 并没有止步于此。分子构成了组织(organisms)。按照同样的方法,我们可以写成如下,把它称作组织(organisms):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><header>
<div class="logo">
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contacts</a></li>
</ul>
</nav>
<form>
<label>Search the site</label>
<input type="text" placeholder="enter keyword" />
<input type="submit" value="search" />
</form>
</header>
</code></pre></div></div>
<p>这个理念的下一个是模板(templates)。这没有跟化学直接相关,而是放到 Web 环境中(web context)。一旦我们开始合并不同的组织,就是在构造模板。之后这些模板形成了最终的网页。</p>
<p>你可能已经使用相似的方法开发你的应用软件。然而,以合理的方式命名会带来好的架构。你和所有你的团队队员在开发中会明白一些事情。把一件东西分成原子和分子是挺重要的一部分,因为这会改善 Web 应用程序的开发过程和维护。</p>
<h2 id="organiccss">OrganicCSS</h2>
<p><img src="/blog/99.jpg" alt="" /></p>
<p>原子设计 有机的 CSS Organic CSS</p>
<p>几个月前我写了一篇关于 <a href="https://github.com/VarnaLab/node-organic">Organic</a> 的文章。这是一个很小的 JavaScript 应用程序框架。它更像是设计模式,我个人很喜欢它。我甚至在几个项目里使用了 Organic 并且它干得非常好。如果你对它感兴趣,我强烈建议你阅读这篇<a href="http://net.tutsplus.com/tutorials/javascript-ajax/organic-development/">博文</a>。</p>
<p>当我看 Brad Frost 的文章时,我已经对相似的理念非常熟悉,因为我了解 Organic 。Brad 的 <em><a href="http://bradfrostweb.com/blog/post/atomic-web-design/">Atomic Design</a></em> 十分出色,但我决定更进一步,基于原子设计(Atomic Design)理念试着写自己的微框架。我选择 SASS 作为预处理器并在 Github 创建了<a href="https://github.com/krasimir/organic-css">仓库</a></p>
<h3 id="原子-atoms">原子 (Atoms)</h3>
<p>让我们从框架最小的部分开始 —— 原子。在<a href="http://zh.wikipedia.org/wiki/%E5%8E%9F%E5%AD%90">维基百科</a>的定义是 *原子是一种元素能保持其化学性质的最小单位。*。在 CSS 的环境(context)中,我认为是一个属性和它的值。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>margin-top: 24px;
</code></pre></div></div>
<p>添加原子只是直接在类里面写样式,这种方式不是我想要的。因此如果写成下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>body {
margin-top: 24px;
}
header {
margin-top: 24px;
}
</code></pre></div></div>
<p>预处理器就会保持这个样子。我想要的最后结果是:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>body, header {
margin-top: 24px;
}
</code></pre></div></div>
<p>SASS 使用占位符(place holders)实现这个效果。也就是说:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>%margin-top-24 {
margin-top: 24px;
}
body {
@extend %margin-top-24;
}
header {
@extend %margin-top-24;
}
</code></pre></div></div>
<p>因此,我必须使用占位符(placeholders)。这也就意味着我必须有大量预定义的并且我能使用的占位符(placeholders)。就在那时,我决定这个框架将只包含原子。也许还有一些类似通常的 <code class="language-plaintext highlighter-rouge">reset.css</code>,网格定义等等。我想要为 CSS 开发写一些基本的东西(I wanted to write something which acts as a base for the CSS development)。也许一个又一个项目之后我将会看到一些可以放进核心的模式,但刚开始我想保持 repo(仓库)整洁简单。为了使一些东西始终如一的,我为定义原子创建 mixin。如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@include define-atom("block") {
display: block;
}
@include define-atom("font-family") {
font-family: Georgia;
}
</code></pre></div></div>
<p>使用这种方式我创建了一堆容易应用到每个项目的原子。你可以在<a href="https://github.com/krasimir/organic-css/tree/master/src/atoms">这里</a>查阅。我从别的框架里使用了一些最佳做法,所以并不是全归功于我。这里是个在分子里混合原子的 mixin:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@mixin header { /* <- molecule called 'header' 称作 `header` 的分子*/
@include atoms((
block,
clearfix,
font-family
));
}
</code></pre></div></div>
<h3 id="分子-molecules">分子 (Molecules)</h3>
<p>分子是需要样式的 DOM 元素,但是没有子代。或者是没有直接联系的子代。例如 <code class="language-plaintext highlighter-rouge"><img src="logo.jpg" /></code> 可以是分子。如果你觉得在网页里识别出分子,只需思考下什么是有原子构成的。如果一些元素是由其他分子构成的,那么很可能是一个(细胞器)。下面几行展示的是如何定义一个分子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@mixin login-box {
@include atoms((
block,
font-size-20,
margin-top-23,
bold
));
}
</code></pre></div></div>
<p>我面前有些有趣的东西。让我们看下 <code class="language-plaintext highlighter-rouge">body</code> 标记。这是什么?是一个分子或者其他东西吗?没错,它通过原子需要一些样式,但通常包含了其他分子。它应该是其他东西。我做出决定,CSS 应该是主角。也就是说,如果 <code class="language-plaintext highlighter-rouge">body</code> 标记需要一些原子提供样式,那就是分子,理论上,我不该给它附加上任何其他的分子。这看起来有点不切实际,但在大多数情况下,这会阻止你使用后代选择器,这个好兆头。</p>
<h3 id="细胞器-organelles">细胞器 (Organelles)</h3>
<p>一旦你能够识别出哪个 DOM 元素是分子,你将会知道什么细胞器。例如,典型的 <code class="language-plaintext highlighter-rouge">form</code> 元素是一个细胞器的好例子。它包含了分子像 <code class="language-plaintext highlighter-rouge">label</code>,<code class="language-plaintext highlighter-rouge">input</code> 或者 <code class="language-plaintext highlighter-rouge">textarea</code>。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.login-form {
@include label;
@include input;
@include textarea;
}
</code></pre></div></div>
<p>这个框架中,细胞器(Organelles)是第一个跟当前应用紧密相关的 。原子和分子在不同项目中会有所改变,而细胞器却不会。</p>
<h3 id="更多的抽象-more-abstractions">更多的抽象 (More abstractions)</h3>
<p>你也许经常想要在其他地方合并几个细胞器。如果是这种情况,添加其他抽象:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Atom → Molecule → Organelle → Cell → Tissue → Organ → Sys → Organism
</code></pre></div></div>
<p>你选择什么来让你的 CSS 结构化,这是个问题。目前为止,我只在一个项目里使用过 OrganicCSS,但我能说,它使项目变清晰了。我把不同的元素放在它们各自的目录(文件夹)里并像这样命名类,例如,分子在一个“molecules”目录(文件夹),里面的文件命名为“header_molecule.scss”我才能容易地找到正在编写的是哪一个。例如,如果有一个叫 <code class="language-plaintext highlighter-rouge">header</code> 的细胞器,我简单地改成 <code class="language-plaintext highlighter-rouge">o-header</code>。然后,当我看 HTML 代码时可以是了解到,这个元素的 CSS 样式文件在 <code class="language-plaintext highlighter-rouge">organelles</code> 文件夹。</p>
<h2 id="总结">总结</h2>
<p>这个一个有趣的旅行。我不知道是否会在将来使用 OrganicCSS,但这不是重点。我学到的东西才是重要的。我知道必须改变我的 CSS 开发过程,我做到了。我确信我们应该谈论更多 CSS 的结构。如你所见,在文章里我们有很多好的资源。我们只是必须找到它们,学习它们干了什么和如何运行的。只有我们能决定是否使用它们。更好的是,当你了解了整个项目才能创作出能更好满足需求的东西。</p>
<h2 id="注">注</h2>
<ul>
<li>原文:http://funwo.tk/2013-09-08-starting-to-write-css-cn.html</li>
<li>英文:http://davidwalsh.name/starting-css</li>
</ul>
每一个JavaScript开发者应该了解的浮点知识
2014-03-14T00:00:00+00:00
http://yanhaijing.com/javascript/2014/03/14/what-every-javascript-developer-should-know-about-floating-points
<p>在JavaScript开发者的开发生涯中的某些点,总会遇到奇怪的BUG——看似基础的数学问题,但却又觉得有些不对劲。总有一天,你会被告知JavaScript中的数字实际上是浮点数。试图了解浮点数和为什么他们如此奇怪,迎接你的将是一片又臭又长的<a href="http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html">文章</a>。本文的目的是给JavaScript开发者简单讲解浮点数。</p>
<p>本文假设读者熟悉的用二进制表示的十进制数字(即1被写成<code class="language-plaintext highlighter-rouge">1b</code>,2是<code class="language-plaintext highlighter-rouge">10b</code>,3是<code class="language-plaintext highlighter-rouge">11b</code>,4是<code class="language-plaintext highlighter-rouge">100b</code>……等)。为了使文章表达的更清楚,在本章中,“十进制”主要是指计算机内部的十进制数字表示法(例如:2.718)。“二进制”在本文中指计算机内部的表示。书面陈述将分别被称为“以十为底″和“以二为底″。</p>
<h2 id="浮点数">浮点数</h2>
<p>什么是浮点数,我们开始认为我们见过各种数字,我可可以说<code class="language-plaintext highlighter-rouge">1</code>是一个整数,因为它没有分数部分。</p>
<p>½被称为分数。这意味着,将一平均分开为二,分数是浮点运算中一个非常重要的概念。</p>
<p><code class="language-plaintext highlighter-rouge">0.5</code>通常被称为一个十进制数。然而,有一个很重要的区别必须阐明——<code class="language-plaintext highlighter-rouge">0.5</code>实际上是分数½的十进制(以十为底)表示。本文中,我们将这种表示方法称为点表示法。我们把<code class="language-plaintext highlighter-rouge">0.5</code>称为有限表示(有限小数)因为其分数表示的数字是有限的——<code class="language-plaintext highlighter-rouge">5</code>后面没有其他数字。表示⅓的<code class="language-plaintext highlighter-rouge">0.3333</code>…是无限表示的例子。这个想法在我们的讨论非常重要。</p>
<p>还存在另一种表示全部整数,分数或小数的方法。你可能已经见过。它看起来像这样:6.022×1023(注:这是阿伏伽德罗数,这是摩尔的化学溶液中的分子的数目)。它通常被称为标准形式,或科学记数法。形式可以被抽象为像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>D1.D2D3D4...Dp x BE
</code></pre></div></div>
<p>这种通用形式被称作<strong>浮点数</strong>。</p>
<p>由<code class="language-plaintext highlighter-rouge">p</code>和<code class="language-plaintext highlighter-rouge">D</code>组成的序列——<code class="language-plaintext highlighter-rouge">D1.D2D3D4...Dp</code>——被称为有效数字或尾数。<code class="language-plaintext highlighter-rouge">p</code>是有效数字的权重,通常称为精度。有效数后的<code class="language-plaintext highlighter-rouge">x</code>是符号的一部分(本文中的乘法符号,将用*表示)。其后是基数,基数后是指数。该指数可以是正或负。</p>
<p>浮点数的好处是它可以用来表示任何数值。例如,整数1可以表示为<code class="language-plaintext highlighter-rouge">1.0×10^0</code>。光的速度可以表示为<code class="language-plaintext highlighter-rouge">2.99792458×108 m/s</code>。1/2可以被表示为二进制形式<code class="language-plaintext highlighter-rouge">0.1×2^0</code>。</p>
<h2 id="移除小数点">移除小数点</h2>
<p>在上面的例子中,我们仍然保留小数点(小数点在数字里面)。当用二进制表示数值的时候,这带来了一些问题。任意给定一个浮点数,比如<code class="language-plaintext highlighter-rouge">π</code>(PI),我们可以将其表示为一个浮点数:<code class="language-plaintext highlighter-rouge">3.14159 x 100</code>。用二进制表示,它看起来像这样:<code class="language-plaintext highlighter-rouge">11.00100100 001111……</code>假设在十六位机里表示数字,这意味着数字被放在机器里会是这样的:<code class="language-plaintext highlighter-rouge">11001001000011111</code>。现在的问题是:小数点应该放在哪里?这甚至不涉及指数(我们默认基数为2)。</p>
<p>如果数字变为<code class="language-plaintext highlighter-rouge">5.14159</code>?整数部分将变为<code class="language-plaintext highlighter-rouge">101</code>而不是<code class="language-plaintext highlighter-rouge">11</code>,增加了一位。当然,我们可以指定字段的前N位属于整数部分(即小数点的左边),其余属于小数部分,但那是另一篇关于定点数的话题。</p>
<p>一旦我们移除小数点后,我们只有两件东西需要记录:指数和尾数。我们可以通过应用变换公式将小数点移除,使广义浮点数看起来像这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>D1D2D3D4...Dp / (Bp-1) x BE
</code></pre></div></div>
<p>这就是我们得到的大多数二进制浮点数。注意,现在有效数是一个整数。这使得它更易于存储一个浮点数在机器上。事实上,应用最广泛的二进制浮点数表示方法是IEEE 754标准。</p>
<h2 id="ieee-754">IEEE 754</h2>
<p>JavaScript中的浮点数采用IEEE-754格式的规定。更具体的说是一个双精度格式,这意味着每个浮点数占64位。虽然它不是二进制表示浮点数的唯一途径,但它是目前最广泛使用的格式。该格式用64位二进制表示像下面这样:</p>
<p><img src="/blog/75.png" alt="" /></p>
<p>你可能注意到机器表示的方法和约定俗成的书面表示一点不同。在64位中,1位用于标志位——用来表示一个数是正数还是负数。11位用于指数–这允许指数最大到<code class="language-plaintext highlighter-rouge">1024</code>。剩下的52位代表的尾数。如果你曾经好奇为什么JavaScript中的某些东西如<code class="language-plaintext highlighter-rouge">+0</code> 和 <code class="language-plaintext highlighter-rouge">-0</code>,标志位说明一切——JavaScript中的所有数字都有符号位。<code class="language-plaintext highlighter-rouge">Infinity</code>和<code class="language-plaintext highlighter-rouge">NaN</code>也被编码进浮点数——<code class="language-plaintext highlighter-rouge">2047</code>作为一个特殊的指数。如果尾数是<code class="language-plaintext highlighter-rouge">0</code>,它是一个正无穷或负无限。如果不是,那么它是<code class="language-plaintext highlighter-rouge">NaN</code>。</p>
<h2 id="舍入误差">舍入误差</h2>
<p>有了上面对浮点数进行介绍,现在我们进入了一个更棘手的问题–舍入误差。它是所有开发者使用浮点数开发的祸根,JavaScript开发者尤其如此,因为JavaScript开发者唯一可用的编号格式是浮点数。</p>
<p>上面提到的分数⅓不能在以10为底中有限表示。这实际上在任何数制中都存在。例如,在在以二为底的数字中,1 / 10不能有限表示。被表示为<code class="language-plaintext highlighter-rouge">0.00110011001100110011……</code>注意<code class="language-plaintext highlighter-rouge">0011</code>是无限重复的。这是因为这个特别的怪癖,舍入误差造成的。</p>
<p>先看一个舍入误差的例子。考虑一个最著名的无理数,PI:<code class="language-plaintext highlighter-rouge">3.141592653589793……</code>大多数人记得前五位(<code class="language-plaintext highlighter-rouge">3.1415</code>)非常棒——我们将使用这个例子说明舍入误差,因此可以计算舍入误差:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(R - A) / Bp-1 ……其中`R`代表圆形的半径,`A`代表一个实数。`Bp`代表以`p`为底的精度。所以谨记PI的舍入误差:`0.00009265……`。
</code></pre></div></div>
<p>虽然这看起似乎不是很严重,让我们试着用以二为底的数来检验这个想法。考虑分数1 / 10。在十进制,它被写作<code class="language-plaintext highlighter-rouge">0.1</code>。在二进制中,它是:<code class="language-plaintext highlighter-rouge">0.00011001100110011……</code>假设我们仅保留5位尾数,可以写为<code class="language-plaintext highlighter-rouge">0.0001</code>。但<code class="language-plaintext highlighter-rouge">0.0001</code>在二进制表示法中实际是1 / 16(或<code class="language-plaintext highlighter-rouge">0.0625</code>)的表示!这意味着有舍入误差为<code class="language-plaintext highlighter-rouge">0.0375</code>,这是相当大的。想象一下基本的加法运算,如<code class="language-plaintext highlighter-rouge">0.1 + 0.2</code>,答案返回<code class="language-plaintext highlighter-rouge">0.2625</code>!</p>
<p>幸运的是,浮点规范指定ECMAScript最多使用52个尾数,所以舍入误差变得很小——规范的具体细节规避了大部分的舍入误差。因为对浮点数进行算术运算的过程中误差会被放大,IEEE 754规范还包括用于数学运算的具体算法。</p>
<p>然而,应该指出的是,尽管如此,算术运算的关联属性(比如加法,减法,乘法和减法)不能得到保证在处理浮点数时,即使精度再高。我的意思是,<code class="language-plaintext highlighter-rouge">((x + y)+ A + B)</code>不一定等于<code class="language-plaintext highlighter-rouge">((x + y)+(A + B))</code>。</p>
<p>这是JavaScript开发人员的祸根。例如,在JavaScript中,<code class="language-plaintext highlighter-rouge">0.1 + 0.2 = = = 0.3</code>将返回假。我希望你现在明白这是为什么。更糟的是,事实上,舍入误差会在连续的数学运算中增加(积累)。</p>
<h2 id="在javascript处理浮点数">在JavaScript处理浮点数</h2>
<p>设计处理JavaScript数字的问题,已经存在很多的建议,好坏参半。大多数这些建议都是在算数运算之前或之后完成取舍。</p>
<p>到目前位置我见过的寥寥无几的建议就是把运算数全部存储为整数(无类型),然后格式化显示。通过一个例子可以看出,在账户中大量储存的美分而不是美元(不知道举的例子是什么账户)。这里有一个值得注意的问题——不是世界上所有的货币都是十进制的(毛里求斯币:毛里求斯卢比是毛里求斯共和国的流通货币。币值有25、50、100、200、500、1000和2000。辅币单位为分)。同时,吐槽了日元和人名币……。最终,你会重新创建浮点——有可能。</p>
<p>我见过处理浮点数最好的建议是使用库,像<a href="https://github.com/guipn/sinful.js">sinfuljs</a>或<a href="http://mathjs.org/">mathjs</a>。我个人比较喜欢mathjs(但实际上,任何和数学相关的我甚至不会使用JavaScript去做)。当需要任意精度数学计算的时候,<a href="https://github.com/dtrebbien/BigDecimal.js">BigDecimal</a>也是非常有用的。</p>
<p>另一个被多次重复的建议是使用内置的<code class="language-plaintext highlighter-rouge">toPrecision()</code>和<code class="language-plaintext highlighter-rouge">toFixed()</code>方法。使用他们时最容易犯得逻辑错误是忘记这些方法的返回值字符串。所以如果你像下面这样会得不到想要的结果:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function foo(x, y) {
return x.toPrecision() + y.toPrecision()
}
> foo(0.1, 0.2)
"0.10.2"
</code></pre></div></div>
<p>设计内置方法<code class="language-plaintext highlighter-rouge">toPrecision()</code>和<code class="language-plaintext highlighter-rouge">toFixed()</code>的目的仅是用于显示。谨慎使用!</p>
<h2 id="结论">结论</h2>
<p>JavaScript中的数字是真正的浮点数。由于二进制表示的固有缺陷,以及有限的机器空间,我们不得不面对一个充满舍入误差的规范。本文解释了为什么这些舍入误差是什么和为什么。记住使用一个很棒的库而不是自己去做一切。</p>
<h2 id="注">注</h2>
<p>原文:<em>http://flippinawesome.org/2014/02/17/what-every-javascript-developer-should-know-about-floating-points/</em></p>
重温CSS:Border属性
2014-02-22T00:00:00+00:00
http://yanhaijing.com/css/2014/02/22/css-refresher-border
<p>边界是众所周知的,有什么新的东西吗?好吧,我敢打赌,在这篇文章中,有很多你不看永远不知道的东西!</p>
<p>不仅可以用CSS3来创建圆角,使用原有CSS一样可以显示自定义图形。这是正确的(有待考究);在过去,没发现这种技术之前,我们可能使用背景图像定位来显示一个园或箭头。幸运的是,我们能放下PS图象处理软件了。</p>
<h2 id="基础">基础</h2>
<p>你可能很熟悉边的最基本用法。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>border: 1px solid black;
</code></pre></div></div>
<p>上面的代码将给元素应用1px的边。即简洁又简单;但我们也可以稍作修改。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>border-width: thick;
border-style: solid;
border-color: black;
</code></pre></div></div>
<p>除了指定具体的边框宽度值,也可以使用这三个关键词:<code class="language-plaintext highlighter-rouge">thin</code>,<code class="language-plaintext highlighter-rouge">medium</code> 和 <code class="language-plaintext highlighter-rouge">thick</code>。</p>
<p><img src="/blog/75.jpg" alt="" /></p>
<p>虽然乍看起来单个属性的方式没必要,但有极少数的情况下,当它是有利的,例如当你需要在特定的事件发生时更新边的部分属性。</p>
<p>也许你需要在用户将鼠标悬停在一个特定的元素上时改变这个元素的边框颜色。使用复合属性需要重复像素值和边的样式。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>box {
border: 1px solid red;
}
.box:hover {
border: 1px solid green;
}
</code></pre></div></div>
<p>一个更优雅的和简洁(DRY,don’t repeat yourself)的做法是只更新边的颜色属性。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.box {
border: 1px solid red;
}
.box:hover {
border-color: green;
}
</code></pre></div></div>
<p>此外,一会你会发现,这种单个属性的方式有助于通过CSS创建自定义的形状。</p>
<h2 id="圆角">圆角</h2>
<p><code class="language-plaintext highlighter-rouge">border-radius</code> CSS3中的代表——第一个在社区中得到广泛使用新属性。这意味着,除去Internet Explorer 8及更低版本,所有的浏览器可以显示圆角。</p>
<p>为了使样式能正确应用,需要为Webkit和Mozilla内核添加前缀。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
</code></pre></div></div>
<p>然而,今天我们不关心前缀,只简单坚持官方形式:<code class="language-plaintext highlighter-rouge">border-radius</code>。</p>
<p><img src="/blog/76.jpg" alt="image" /></p>
<p>如你所料,我们可以为每个角指定不同的值。</p>
<p><img src="/blog/77.jpg" alt="image" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>border-top-left-radius: 20px;
border-top-right-radius: 0;
border-bottom-right-radius: 30px;
border-bottom-left-radius: 0;
</code></pre></div></div>
<p>在上面的代码中,设置<code class="language-plaintext highlighter-rouge">border-top-right-radius</code>和<code class="language-plaintext highlighter-rouge">border-bottom-left-radius</code>为零是多余的,除非该元素有继承的值。</p>
<p>就像<code class="language-plaintext highlighter-rouge">margin</code>和<code class="language-plaintext highlighter-rouge">padding</code>一样,如果需要的话,这些设置可以合并为一个单一的属性。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/* 左上角, 右上角, 右下角, 左下角 */
border-radius: 20px 0 30px 0;
</code></pre></div></div>
<p>举个例子(网页设计师经常这样做),可以用CSS的<code class="language-plaintext highlighter-rouge">border-radius</code>属性模拟柠檬的形状,如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.lemon {
width: 200px; height: 200px;
background: #F5F240;
border: 1px solid #F0D900;
border-radius: 10px 150px 30px 150px;
}
</code></pre></div></div>
<p><img src="/blog/78.jpg" alt="image" /></p>
<h2 id="扩展知识">扩展知识</h2>
<p>许多设计师一直用的是目前为止在本章列出的知识,然而,有一些方法我们可以进一步扩展!</p>
<h3 id="多个边">多个边</h3>
<p>当给一个元素应用多个边的时候,有各种各样的技术可以参考。</p>
<h4 id="边的样式">边的样式</h4>
<p><code class="language-plaintext highlighter-rouge">solid</code>,<code class="language-plaintext highlighter-rouge">dashe</code>d和<code class="language-plaintext highlighter-rouge">dotted</code>是最常用的<code class="language-plaintext highlighter-rouge">border-style</code>属性值,还有其他几个我们可以使用的值,包括<code class="language-plaintext highlighter-rouge">groove</code>和<code class="language-plaintext highlighter-rouge">ridge</code>。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>border: 20px groove #e3e3e3;
</code></pre></div></div>
<p>或者写成单个属性形式:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>border-color: #e3e3e3;
border-width: 20px;
border-style: groove;
</code></pre></div></div>
<p><img src="/blog/79.jpg" alt="image" /></p>
<p>虽然这看起来不错,但<code class="language-plaintext highlighter-rouge">ridge</code>或<code class="language-plaintext highlighter-rouge">groove</code>效果并不是真正的多个边。</p>
<h4 id="轮廓">轮廓</h4>
<p>创建两条边的最流行的方式是利用<code class="language-plaintext highlighter-rouge">outline</code>属性。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.box {
border: 5px solid #292929;
outline: 5px solid #e3e3e3;
}
</code></pre></div></div>
<p><img src="/blog/80.jpg" alt="image" /></p>
<p>这个方法表现的非常棒,然而,最多两个边界。您应该需要创建一个层,实现渐变梯度效果,需要一种不同的方法。</p>
<h3 id="伪元素">伪元素</h3>
<p>当轮廓技术无法满足要求时,另一种方法是利用:<code class="language-plaintext highlighter-rouge">:before</code>和<code class="language-plaintext highlighter-rouge">:after</code>伪元素,并利用生成内容产生额外的边。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.box {
width: 200px; height: 200px;
background: #e3e3e3;
position: relative;
border: 10px solid green;
}
/* 创建和容器宽度相同的两个容器 */
.box:after, .box:before {
content: '';
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
}
.box:after {
border: 5px solid red;
outline: 5px solid yellow;
}
.box:before {
border: 10px solid blue;
}
</code></pre></div></div>
<p><img src="/blog/81.jpg" alt="image" /></p>
<p>这也许不是最优雅的方法,但它确实起作用了。需要注意的地方是很容易混淆边界颜色的顺序。确保正确的序列。</p>
<h4 id="对象阴影">对象阴影</h4>
<p>创建无限数量的边界更酷的方法是利用CSS3的<code class="language-plaintext highlighter-rouge">box-shadow</code>属性。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.box {
border: 5px solid red;
box-shadow:
0 0 0 5px green,
0 0 0 10px yellow,
0 0 0 15px orange;
}
</code></pre></div></div>
<p><img src="/blog/82.jpg" alt="image" /></p>
<p>在这种情况下,我们灵活使用<code class="language-plaintext highlighter-rouge">box-shadow</code>属性,这种方法,并不是css规范的本意。</p>
<p>通过设置<code class="language-plaintext highlighter-rouge">x</code>,<code class="language-plaintext highlighter-rouge">y</code>,和模糊属性为<code class="language-plaintext highlighter-rouge">0</code>,我们可以使用多个值在需要的位置创建实线边界。因为<code class="language-plaintext highlighter-rouge">box-shadow</code>属性可以叠加,通过逗号分割,可以使用无限的值。</p>
<p>这种技术能很好的运行。在不能识别<code class="language-plaintext highlighter-rouge">box-shadow</code>属性的老式浏览器中,这只会呈现单一红色<code class="language-plaintext highlighter-rouge">5px</code>边界。</p>
<blockquote>
<p>谨记:在所有浏览器里实现相同的设计是不必要的。为大部分现代浏览器书写你的CSS,然后为老式浏览器提供可行的回退版本。</p>
</blockquote>
<h2 id="自定义角度">自定义角度</h2>
<p>除了给<code class="language-plaintext highlighter-rouge">border-radius</code>传递一个值外,我们也可以提供两个——由/分隔——为水平和垂直半径指定不同的值。</p>
<p>例如……</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>border-radius: 50px / 100px; /* 水平半径, 垂直半径 */
</code></pre></div></div>
<p>……相当于:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>border-top-left-radius: 50px 100px;
border-top-right-radius: 50px 100px;
border-bottom-right-radius: 50px 100px;
border-bottom-left-radius: 50px 100px;
</code></pre></div></div>
<p>这种技术是特别有用,当你需要模拟一个平缓的,冗长的曲线,而不是一个通用的圆角。例如,下面的代码允许我们稍微变形一个正方形形状,模拟出更多卷纸一样的效果。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.box {
width: 200px; height: 200px;
background: #666;
border-top-left-radius: 15em 1em;
border-bottom-right-radius: 15em 1em;
}
</code></pre></div></div>
<p><img src="/blog/83.jpg" alt="image" /></p>
<h2 id="css形状">CSS形状</h2>
<p>也许最干脆的是直接使用边界,给宽和高为零的元素巧妙的应用边界。令人困惑,是吗?让我们看看一个演示。</p>
<p>在接下来的几个例子,假设以下标记……</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div class="box"></div>
</code></pre></div></div>
<p>……和基本样式如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.box {
width: 200px;
height: 200px;
background: black;
}
</code></pre></div></div>
<p>最常用的例子是如何使用CSS形状创建一个箭头。</p>
<p>关键是了解如何用CSS生成箭头,通过为每个边设置不同的颜色,并且将容器的宽和高都减为零。</p>
<p>假设一个有<code class="language-plaintext highlighter-rouge">arrow</code>类的<code class="language-plaintext highlighter-rouge">div</code>作为容器:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.arrow {
width: 0; height: 0;
border-top: 100px solid red;
border-right: 100px solid green;
border-bottom: 100px solid blue;
border-left: 100px solid yellow;
}
</code></pre></div></div>
<p>在本章的开始,更清洁的语法是不使用复合语法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.arrow {
width: 0; height: 0;
border: 100px solid;
border-top-color: red;
border-right-color: green;
border-bottom-color: blue;
border-left-color: yellow;
}
</code></pre></div></div>
<p>我们甚至可以进一步精简,通过合并颜色值。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.arrow {
width: 0; height: 0;
border: 100px solid;
border-color: red green blue yellow;
}
</code></pre></div></div>
<p><img src="/blog/84.jpg" alt="image" /></p>
<p>很有趣,不是吗?不过,当我们后退一步时更有趣。现在,如果我们将除了蓝边之外的所有的<code class="language-plaintext highlighter-rouge">border-color</code>设置为透明的将会怎样?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.arrow {
width: 0; height: 0;
border: 100px solid;
border-bottom-color: blue;
}
</code></pre></div></div>
<p><img src="/blog/85.jpg" alt="image" /></p>
<p>太棒了!但用<code class="language-plaintext highlighter-rouge">div</code>创建一个箭头似乎不太符合语义化。然而,通过<code class="language-plaintext highlighter-rouge">after</code>或<code class="language-plaintext highlighter-rouge">before</code>等相关伪元素可以用来创建箭头。</p>
<h3 id="创建一个气泡">创建一个气泡</h3>
<p>创建一个100%CSS的气泡,我们从下面的标记考试。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div class="speech-bubble">Hi there!</div>
</code></pre></div></div>
<p>接下来,应用一些基本样式。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.speech-bubble {
position: relative;
background-color: #292929;
width: 200px;
height: 150px;
line-height: 150px; /* 垂直居中 */
color: white;
text-align: center;
}
</code></pre></div></div>
<p><img src="/blog/86.jpg" alt="image" /></p>
<p>箭头将通过<code class="language-plaintext highlighter-rouge">after</code>伪元素实现。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.speech-bubble:after {
content: '';
}
</code></pre></div></div>
<blockquote>
<p><code class="language-plaintext highlighter-rouge">:before</code>和<code class="language-plaintext highlighter-rouge">:after</code>伪元素可以用来在元素内容之前或之后插入生成内容。
接下来,只是简单复制箭头,并定位到适当的位置。我们开始通过绝对定位的内容,重置宽度和高度,并应用边界颜色。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.speech-bubble:after {
content: '';
position: absolute;
width: 0;
height: 0;
border: 10px solid;
border-color: red green blue yellow;
}
</code></pre></div></div>
<p><img src="/blog/87.jpg" alt="image" /></p>
<p>因为我们知道我们想要向下的箭头,上面的图片表明,除了红色(或上)边境其他的都应该被省略,或者设置为透明。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.speech-bubble:after {
content: '';
position: absolute;
width: 0;
height: 0;
border: 10px solid;
border-top-color: red;
}
</code></pre></div></div>
<p><img src="/blog/88.jpg" alt="image" /></p>
<p>当创建CSS形状是,因为我们不能使用width属性来指定箭头的宽度,而是应该使用<code class="language-plaintext highlighter-rouge">border-width</code>属性。在这种情况下,箭头应该更大点;所以<code class="language-plaintext highlighter-rouge">border-width</code>可以增加到<code class="language-plaintext highlighter-rouge">15px</code>。我们将箭头定位到容器的底部居中,通过利用<code class="language-plaintext highlighter-rouge">top</code>和<code class="language-plaintext highlighter-rouge">left</code>属性。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.speech-bubble:after {
content: '';
position: absolute;
width: 0;
height: 0;
border: 15px solid;
border-top-color: red;
top: 100%;
left: 50%;
}
</code></pre></div></div>
<p><img src="/blog/89.jpg" alt="image" /></p>
<p>到这里就差不多了;最后一个步骤是更新箭头的颜色和容器的背景颜色相同。定位也需要修改,根据边界的宽度(<code class="language-plaintext highlighter-rouge">15 px</code>)。当我们在这里,我们还将应用一个微妙<code class="language-plaintext highlighter-rouge">border-radius</code>属性来使容器更像气泡。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.speech-bubble {
/* … 其他样式 */
border-radius: 10px;
}
.speech-bubble:after {
content: '';
position: absolute;
width: 0;
height: 0;
border: 15px solid;
border-top-color: #292929;
top: 100%;
left: 50%;
margin-left: -15px; /* 调整边框宽度 */
}
</code></pre></div></div>
<p><img src="/blog/90.jpg" alt="image" /></p>
<p>不错,不是吗?将这代码抽象为几个可重用的类,好应用到你将来的项目。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/*
对话气泡
用法:使用.speech-bubble和.speech-bubble-DIRECTION类
<div class="speech-bubble speech-bubble-top">Hi there</div>
*/
.speech-bubble {
position: relative;
background-color: #292929;
width: 200px;
height: 150px;
line-height: 150px; /* 垂直居中 */
color: white;
text-align: center;
border-radius: 10px;
font-family: sans-serif;
}
.speech-bubble:after {
content: '';
position: absolute;
width: 0;
height: 0;
border: 15px solid;
}
/* 箭头的位置 */
.speech-bubble-top:after {
border-bottom-color: #292929;
left: 50%;
bottom: 100%;
margin-left: -15px;
}
.speech-bubble-right:after {
border-left-color: #292929;
left: 100%;
top: 50%;
margin-top: -15px;
}
.speech-bubble-bottom:after {
border-top-color: #292929;
top: 100%;
left: 50%;
margin-left: -15px;
}
.speech-bubble-left:after {
border-right-color: #292929;
top: 50%;
right: 100%;
margin-top: -15px;
}
</code></pre></div></div>
<p><img src="/blog/91.jpg" alt="image" /></p>
<h3 id="补充更好的垂直居中">补充:更好的垂直居中</h3>
<p>使用<code class="language-plaintext highlighter-rouge">line-height</code>实现垂直居中的一个缺点是仅限于一行。当文本需要两行或两行以上时,每一行的高度将会太大。一个聪明的解决办法是设置气泡的<code class="language-plaintext highlighter-rouge">display</code>属性为<code class="language-plaintext highlighter-rouge">table</code>,和包装段落文本的<code class="language-plaintext highlighter-rouge">display</code>为<code class="language-plaintext highlighter-rouge">table-cell</code>。这就允许我们将文本设为垂直居中。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div class="speech-bubble speech-bubble-top">
<p>Text goes here.</p>
</div>
</code></pre></div></div>
<p>接下来,修改CSS。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.speech-bubble {
/* 其他样式 */
display: table;
}
.speech-bubble p {
display: table-cell;
vertical-align: middle;
}
</code></pre></div></div>
<p><img src="/blog/92.jpg" alt="image" /></p>
<blockquote>
<p>如果引用<code class="language-plaintext highlighter-rouge">display: table</code> 带来可怕的表格布局的老式回忆,别担心。这些属性是指显示一个元素的样式。</p>
</blockquote>
<p>我们不局限于三角形;CSS能产生各种各样的形状,甚至心和生物危害标志!</p>
<p><img src="/blog/93.jpg" alt="image" /></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.biohazard {
width: 0; height: 0;
border: 60px solid;
border-radius: 50%;
border-top-color: black;
border-bottom-color: black;
border-left-color: yellow;
border-right-color: yellow;
}
</code></pre></div></div>
<h2 id="总结">总结</h2>
<p>虽然最简单的<code class="language-plaintext highlighter-rouge">border:1px solid black;</code>语法很有帮助,如果我们聪明,我们可以创建各种有益的效果,图标和形状。谁会想到边界可以如此强大?关键是要记住常见的形状或对话框的样式应该只被创建一次,然后抽象出来实用的类为将来使用。</p>
<p>注</p>
<p>原文:<a href="http://www.cnblogs.com/yanhaijing/admin/%20http:/net.tutsplus.com/tutorials/html-css-techniques/css-refreshers-borders/">CSS Refreshers: Borders</a></p>
如何在电脑上测试手机网站
2014-02-21T00:00:00+00:00
http://yanhaijing.com/web/2014/02/21/how-to-test-mobile-websit-on-pc
<p>最近公司要开发网站的移动版,让我准备准备知识,话说本人开发移动网站的经验还真不多,最悲剧的事情就是我的手机是个经典的诺基亚,而且公司还不给配手机,这是有多讨厌,没办法,没有手机只能用电脑模拟了,相办法代替,查了很多资料,尝试了大部分方法,下面将这一天的努力总结下分享给大家,也让大家免去看那么多文章,以下介绍的方法,都是本人亲自测试成功的方法,测试环境winxp。</p>
<h2 id="chrome">Chrome*</h2>
<p>chrome模拟手机总共有四种方法,原理都一样,通过伪装User-Agent,将浏览器模拟成Android设备。以下标星的为推荐方法。</p>
<h3 id="1新建chrome快捷方式">1.新建Chrome快捷方式</h3>
<p>右击桌面上的Chrome浏览器图标,在弹出的右键菜单中选择“复制”,复制一个图标副本到桌面。右击该副本,选择“属性”,打开相应的对话框,在“目标”文本框的字符后面添加以下语句:“–user-agent=”Android””,如下图:</p>
<p><img src="/blog/59.png" alt="" /></p>
<p>注意user前面是两个“-”,并且“chrome.exe”与“–user”之间有一个空格。确定之后,打开这个新建的Chrome快捷方式,输入3g.qq.com就可以浏览到像手机里打开一样的页面了。</p>
<p>这时可以新建一个用户,就不影响原来用户访问的时候也是访问的手机版。</p>
<h3 id="2一次性模拟iphone和安卓手机">2.一次性模拟iPhone和安卓手机</h3>
<p>开始–运行中输入以下命令,启动浏览器:</p>
<p>模拟谷歌Android:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chrome.exe --user-agent="Mozilla/5.0 (Linux; U; Android 2.2; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"
</code></pre></div></div>
<p>模拟苹果iPhone:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chrome.exe --user-agent="Mozilla/5.0 (iPad; U; CPU OS 3_2_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B500 Safari/531.21.10"
</code></pre></div></div>
<p>这种方法仅供特殊情况下使用,因为重启Chrome将不能恢复正常User-Agent,所以是一次性。</p>
<p><img src="/blog/60.png" alt="" /></p>
<p>更多的user-agent请自行搜索。</p>
<h3 id="3安装插件">3.安装插件</h3>
<p>插件可以很方便切换各种user-agent,很方便,但是可能会稍微影像性能。</p>
<p>User-Agent Selector地址:<a href="https://chrome.google.com/webstore/detail/user-agent-selector/fnbmdojpgjpmjjmnjdnbobcdhenmmgod/related">https://chrome.google.com/webstore/detail/user-agent-selector/fnbmdojpgjpmjjmnjdnbobcdhenmmgod/related</a></p>
<p><img src="/blog/61.png" alt="" /></p>
<p>从上图可以看到,还有很多类似的插件,其实功能都大同小异。</p>
<h3 id="4自带模拟器">4:自带模拟器*</h3>
<p>打开chrome开发者工具,按F12(r32版本),然后找到右上角的齿轮按钮,打开设置面板,选择Overrides,勾上Show ‘Emulation’ view in console drawer(在控制台视图中显示“仿真”)。</p>
<p><img src="/blog/62.png" alt="" /></p>
<p>然后关闭设置面板,选择Elements面板(非Console就可以),找到右上角打开控制台面板,选择控制台面板里的Emulation面板,右边有很多选项,选择一个点击Emulate就可以了,Reset按钮能恢复到默认状态。</p>
<p><img src="/blog/63.png" alt="" /></p>
<p>打开仿真后,打开<a href="http://yanhaijing.com">http://yanhaijing.com</a>,即可看到如下的手机下的界面</p>
<p><img src="/blog/64.png" alt="" /></p>
<p>这种方法简单好用,而且不需要重启,推荐这种方法。</p>
<p><strong>注意:以上第一种和第二种方法都需要将全部打开的chrome窗口关闭,再打开才能起作用。</strong></p>
<h2 id="firefox">Firefox*</h2>
<h3 id="1修改user-agent">1.修改user-agent</h3>
<p>和chrome一样安装插件修改user-agent的方法</p>
<p>具体方法移步这里:<a href="http://blog.sina.com.cn/s/blog_645813a30100qf68.html">http://blog.sina.com.cn/s/blog_645813a30100qf68.html</a></p>
<h3 id="2火狐响应式设计修改user-agent">2.火狐响应式设计+修改user-agent*</h3>
<p>最近的火狐自己添加响应式设计功能和3D试图都很棒,打开火狐自己的控制台(非firebug),找到右上角的响应式设计按钮。</p>
<p><img src="/blog/65.png" alt="" /></p>
<p>打开后即切换到响应式设计界面</p>
<p><img src="/blog/66.png" alt="" /></p>
<p>但我们看到打开QQ的站点并未被自动引到QQ的移动页面,这样只对响应式设计的界面起作用,对想QQ这样云端判断,返回不同页面的并不适应,这里就要配合上面的方法,再改下user-agent,即可实现类似chrome的调试功能。</p>
<p><img src="/blog/67.png" alt="" /></p>
<h3 id="3firefox-os-模拟器">3.Firefox OS 模拟器</h3>
<p>安装的方法 参考这里:<a href="https://developer.mozilla.org/zh-CN/docs/Tools-840092-dup/Firefox_OS_%E6%A8%A1%E6%8B%9F%E5%99%A8#Installing">https://developer.mozilla.org/zh-CN/docs/Tools-840092-dup/Firefox_OS_%E6%A8%A1%E6%8B%9F%E5%99%A8#Installing</a></p>
<p>安装完成后可打开如下界面,可用里面的浏览器打开网站即可,但这种方法打开的是电脑网站,而不是手机网站,也就是他的user-agent不是手机的,故对响应式界面起作用,对判断user-agent的网站不起作用,访问qq,baidu等返回的都是电脑界面。</p>
<p><img src="/blog/68.png" alt="" /></p>
<h2 id="opera">Opera*</h2>
<h3 id="1修改user-agent-1">1.修改user-agent</h3>
<p>和chrome和firefox类似,可自行安装插件,自opera12之后,opera改用webkit内核,故可安装chrome的插件,也可自行在opera的商店中搜索插件</p>
<p>User Agent Changer下载: <a href="https://addons.opera.com/zh-cn/extensions/details/user-agent-changer/?display=en">https://addons.opera.com/zh-cn/extensions/details/user-agent-changer/?display=en</a></p>
<h3 id="2opera-mobile-emulator--dragonfly">2.Opera Mobile Emulator + dragonfly*</h3>
<p>下载适合自己的版本,安装完毕会开如下界面:</p>
<p><img src="/blog/69.png" alt="" /></p>
<p>左侧选择平台,右侧选择参数,选择完毕点击启动,如下的几面,用过手机opera的朋友会很熟悉,就是手机opera</p>
<p><img src="/blog/70.png" alt="" /></p>
<p>关于opera mobile emulator的更详细介绍参看文章末尾参考资料的相关内容。</p>
<p>但此时,还是只能看而已,不能调试模拟器里的网站,这里需要dragonfly配合以实现调试,由于opera12后换了内核,不能安装dragonfly了,所有你需要一款opera12的浏览器,和dragonfly的离线包,配置好后具体如何连接请参看这里<a href="http://www.opera.com/dragonfly/documentation/remote/">http://www.opera.com/dragonfly/documentation/remote/</a></p>
<p>全部设置好后即可实现在电脑上调试手机网页,如下图所示:</p>
<p><img src="/blog/71.png" alt="" /></p>
<p>opera12下载地址:<a href="http://yanhaijing.7958.com/down_10918696.html">http://yanhaijing.7958.com/down_10918696.html</a></p>
<p>dragonfly中文离线包下载地址:<a href="http://yanhaijing.7958.com/down_10918700.html">http://yanhaijing.7958.com/down_10918700.html</a></p>
<p>opera mobile emulator下载地址:<a href="http://www.opera.com/zh-cn/developer/mobile-emulator">http://www.opera.com/zh-cn/developer/mobile-emulator</a></p>
<h2 id="模拟器">模拟器*</h2>
<h3 id="1官方模拟器">1.官方模拟器*</h3>
<p>做安卓开发的肯定都知道安卓模拟器,这是谷歌官方的提供的开发环境,能模拟安卓环境,还可切换各个版本,可下载配置好的环境,然后打开eclipes,直接打开AVDM,穿件一个AVD,然后start,如下图:</p>
<p><img src="/blog/72.png" alt="" /></p>
<p>要等一大会时间,会打开模拟器,和安卓环境一样,打开里面的浏览器测试即可。但我的浏览器打不开不知道为什么,郁闷的很啊。</p>
<p><img src="/blog/73.png" alt="" /></p>
<p>下载地址:<a href="http://developer.android.com/sdk/index.html">http://developer.android.com/sdk/index.html</a></p>
<h3 id="2bluestacks">2.bluestacks</h3>
<p>这也是一款模拟器,可自行搜索,本人安装后电脑就卡死了,可能我的电脑配置不行吧,看介绍还是不错的。</p>
<h2 id="在线测试">在线测试</h2>
<p>在线只能测试界面的视觉效果,不能调试,但也是很不错的。</p>
<h3 id="1mobile-emulator">1.Mobile Emulator*</h3>
<p>非常不错,速度也很快,界面很简洁,支持多种平台。</p>
<p><a href="http://emulator.mobilewebsitesubmit.com/screenResolution.php?url=http://yanhaijing.com">http://emulator.mobilewebsitesubmit.com/</a></p>
<p><img src="/blog/74.png" alt="" /></p>
<h3 id="2opera-mini-simulator">2.opera mini simulator</h3>
<p>需要java环境支持,单一平台,opera出品,速度很快。</p>
<p><a href="http://www.opera.com/zh-cn/developer/opera-mini-simulator">http://www.opera.com/zh-cn/developer/opera-mini-simulator</a></p>
<h3 id="3webpage-mobile">3.webpage mobile</h3>
<p>说实话弄了半天也没弄出来,大大的鄙视下吧,但是能测试的平台很全面。</p>
<p><a href="http://www.webpagetest.org/mobile">http://www.webpagetest.org/mobile</a></p>
<h2 id="总结">总结</h2>
<p>以上列出了多种方法,各有利弊,希望大家选择适合自己的方法,本人推荐chrome自带模拟器和opera mobile emulator + dragonfly的方法。因为这两种方法,接近真是手机环境,又能调试css和js。</p>
<p>当然文中没有提到的还有最好的方法就是你有一台手机,那就太好了,配合远程调试,是最最理想的办法。</p>
<h2 id="参考资料">参考资料</h2>
<p>*Chrome模拟手机浏览器(iOS/Android)的三种方法,亲测无误!:http://www.ihacksoft.com/chrome-simulate-ios-android-browse.html</p>
<p>用谷歌浏览器来当手机模拟器:http://blog.s135.com/chrome_phone/</p>
<p>*关于手机网站测试的问题:http://segmentfault.com/q/1010000000143811</p>
<p>如何firefox模拟手机访问手机网站:http://blog.sina.com.cn/s/blog_645813a30100qf68.html</p>
<p>*Firefox OS 模拟器:https://developer.mozilla.org/zh-CN/docs/Tools-840092-dup/Firefox_OS_%E6%A8%A1%E6%8B%9F%E5%99%A8</p>
<p>*Opera Mobile Emulator for desktop:http://dev.opera.com/articles/view/opera-mobile-emulator/</p>
<p>设置 Opera Dragonfly 为离线版或实验版的方法:http://opera.im/archives/running%EF%BC%8Ddragonfly-offline-or-experimental-version/</p>
<p>*整理:手机端网页调试方案:http://blog.segmentfault.com/humphry/1190000000313211?page=1</p>
<p>*移动终端开发必备知识:http://isux.tencent.com/mobile-development-essential-knowledge.html/zh-hans</p>
如何离线安装GitHub for windows?
2014-02-18T00:00:00+00:00
http://yanhaijing.com/git/2014/02/18/how-to-install-github-for-windows-with-offline
<p>此文献给xp用户和被墙用户。</p>
<p><img src="/blog/246.jpg" alt="" /></p>
<p>今天群里(GitHub家园 225932282)有人说GitHub for windows安装不上,错误提示如下,看了下感觉应该是被墙了,我试了试下面的网址,没问题,所以让他访问下面的网址试试,果然打不开,群里的大牛们给出的答案多半是翻墙(墙真真讨厌)。看起来问题僵住了。</p>
<p><img src="/blog/58.jpg" alt="" /></p>
<p>话说群里那个朋友,实验了网上很多办法,都不行。</p>
<p>最近我的GitHub for windows也出问题了,我用的是xp系统,软件升级后需要.net 4.5的环境,可是xp不能安装,要vista以上版本,才可以,一直很郁闷,还好自己一直用的是命令行,用UI的时候也就是提交和比对文件的时候用,这GitHub for windows是在线安装,只能安装最新版本,卸载重装还是不行,而且也不能安装旧版本,而且还会自动升级,真真让人郁闷,看起来还是命令行的兼容性好啊。</p>
<p>后来查了很多资料,发现可以离线安装,便下载了安装包试试,果然很好用,但是版本有些旧,只需下载压缩包并解压,无需安装即可使用,但是需要先安装.net4.0,这个xp是可以安装的,分享给大家,好了废话不多说,想要的用户下载即可使用,下面有下载地址:</p>
<h2 id="注">注</h2>
<p>GitHub for windows 1.0.41.2离线安装包 下载地址:<a href="http://yanhaijing.7958.com/down_10900602.html">http://yanhaijing.7958.com/down_10900602.html</a></p>
<p>GitHub家园 225932282 git/github知识共享,经验交流,开源项目,资料下载,帐号互粉,欢迎GitHuber加入<a target="_blank" href="http://shang.qq.com/wpa/qunwpa?idkey=2abbc3c1882f4250778a56919845e48892a83a41ba8111fa48a315d008accb60"><img border="0" src="http://pub.idqqimg.com/wpa/images/group.png" alt="github 家园" title="github 家园" /></a></p>
亦得亦失,我的2013年总结
2014-01-28T00:00:00+00:00
http://yanhaijing.com/work/2014/01/28/it-also-lost
<p>愿或不愿2013以然过去,光阴荏苒,日月如梭这一年经历了很多事情,也不很顺利,在此做个总结。</p>
<h2 id="学业">学业</h2>
<p>2013顺利完成了学业,拿到了毕业证和学位证,回首想起大学时的时光,怅然若失,几忆昔上天若再给我一次上大学的机会,我会……,有得必有失,世上卖什么要的都有可就是没有卖后悔药的,看来人生还是得向前看,用心去做,成与不成在乎上天。</p>
<p>2013印象最深的画面就是在颁发学位证的现场,领导将头上的穗拨到右边,代表我顺利拿到了学士学位,和领导握手,然后双手捧着学位证,向台下鞠躬,只想说只有亲身经历,才能体会那种庄严,神圣而不可侵犯。</p>
<p>大四的下半年,辞掉了实习的工作,专心回学校做毕设,修炼内功,找工作,但有得有失,得一份安逸,失一份阅历,得一份理论,失一份实践,想着最后半年拥有学校的图书馆资源了,看了不少书,最后也拿到了优秀毕业设计,毕设网址如下,是一款益智小游戏。</p>
<p><a href="http://yanhaijing.com/fan1xia/">http://yanhaijing.com/fan1xia/</a></p>
<h2 id="工作">工作</h2>
<p>毕业后的工作很是不顺利,由于先前百度小米的面试经历,和自我感觉良好的感觉,是我有些洋洋自得,很多小公司,我看不上,而大公司又看不上我,一直也没找到合适的,后来面试的时候也没好好表现,现在想来很是遗憾,遗憾自己的太多不端正,可能因为自负吧,自命不凡,可却又很平凡,应了那句:“心比天高,命比纸薄”。</p>
<p>几经辗转,最后选择了中科北控成像这家公司,当时觉得是家国企,从事的是3D方面的工作,很酷,只是工资有些少,但也没别的选择了,所以中科就成了我毕业后的第一家公司,其实当时不是很情愿,愿与不愿,不遂心智,但想想从事的工作就很兴奋,webgl多么激动人心的工作,来到公司后有些失望,因为从事我们这块的算上项目经理和我就4个人,团队规模没达到我的理想,工作上也没进入状态,处于很浮躁的状态,没把精力全部投入工作,中间面试了几家其他公司,还是老情况,我看上的看不上我,看上我的我看不上,o(︶︿︶)o 唉,人间的杯具都是这样发生的,后来终于安下心来努力工作了,在公司也学到了不少东西,感觉上学时学的图形学的东西有了用武之地,了解了不少3d方面的东西,但总是觉得有些力不从心,o(︶︿︶)o 唉我这文学命,数学天赋不够啊。</p>
<p>后来经过将近半年的时间,离开了中科北控,来到了现在的公司,金山软件,出于很多方面的考虑吧,当时也忧郁了很久,项目经理说给我加薪让我留下,我也不觉得webgl会有前瞻性的未来,直到想通了一个问题,便豁然开朗,那就是webgl有前瞻性的未来,和我在哪个公司没关系,我在别的公司一样可以搞webgl,便毅然离开了中科北控,但还是要感谢原公司,给了我机会,让我成长了很多。</p>
<p>两次面试精力终于让我来到了金山,在新公司熟悉一个新环境,其实是我不擅长的事,但总算进了一个算是大公司的半互联网公司,工资待遇也有大幅提高,塌下心来好好工作吧,但唯一让我不爽的就是,现在的团队规模,很失望,并未达到理想的要求,我一直想经历团队开发的洗礼和大项目的实战经验,积累些团队开发经验,希望在新的一年有所改变吧,2013坎坎坷坷,祝愿2014,刚好是我的本命年,希望能策马奔腾。</p>
<h2 id="个人">个人</h2>
<p>2013个人成长了很多,技术上学习了不少知识,html,css和javascript方面都得到了加强,可真应了那句话,天外有天,人外有人,以前觉得自己很厉害,越来越发现牛人真多,真是会得越多,不会的越多,感觉自己还有很多东西要学,很长的路要走,感谢金山云的面试机会让我学到了不少知识,,由于离开了学校,失去了图书馆资源,自己又工作了有了些积蓄,还要感谢微博上的抽奖平台,陆陆续续也中了4-5本赠书了,加上自己买的图书,建立起了自己小小的图书库,算下来,每个月也得有2.5本图书入手,算是投资,算是收藏吧,我喜欢读书,不过最近一段时间受室友的影像,下班后很少读书了,被非诚勿扰,快乐大本营,爱情保卫战,等娱乐节目包围,最近更是跟着看爱情公寓4,2014不能在这样了,要合理规划下班时间,娱乐学习两不误。</p>
<p>2013有幸认识了李松峰老师,还获得了松峰老师亲笔签名的赠书,认识了justjavac大神,大神给我了很多帮助,也建起了自己的博客<a href="http://yanhaijing.com/">http://yanhaijing.com</a>,定期发些文章,自己知识不够,写不出经典文章,就翻译些文章,但自己语言中枢不够发达, 翻译的质量不是很好,但总的来说有所提高,2014原创文章要有所增加,争取每周能有一片文章的更新。</p>
<p>2013建起了github家园的QQ群,GitHub家园225932282,建了好些个家园群,本打算做个家园系列群联盟,但没有那么多精力,只做起来这个,希望能认识些志同道合的朋友,互相交流,陆陆续续也有150人了,还维护起了<a href="http://weibo.com/githubchina">@github家园</a>的微博,陆陆续续也争取到了几百个粉丝,分享写git相关的知识,每天乐在其中。</p>
<p>2013情感上也有许多事情,但不便写在此处,希望新的一年我爱的人和爱我的人都能幸福。</p>
<p>2013看了北京动物园的大熊猫,海洋馆的海豚表演,颐和园的17拱桥,香山的红叶,呵呵。</p>
<p>2013见到了偶像郭德纲,于谦,小岳岳,还获得了,小岳岳的签名书《钦口说我眼中的郭德纲》很幸福哦。</p>
<p>2013遗憾的事情是,本打算和同事一起做的webgl的中文论坛,没做起来,因为离开了原公司,我的站长计划泡汤了。</p>
<p>2013前端工程师一枚,2014希望成长为高级前端工程师,嘟嘟</p>
如何使用CSS3创建一个漂亮的图标
2014-01-25T00:00:00+00:00
http://yanhaijing.com/css/2014/01/25/create-a-document-icon-with-css3
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/1050_icon/demo.html">演示</a> <a href="http://cdn.tutsplus.com/net/uploads/legacy/1050_icon/source-code.zip">下载</a></p>
<p>今天,我想展示给你一个巧妙的花招。我们将创建一个纯CSS3文本图标。更让人震惊的是,这效果将只需要一个HTML元素。</p>
<h2 id="游戏的计划">游戏的计划</h2>
<ul>
<li>创建一个矩形盒子</li>
<li>设置圆角</li>
<li>使用伪类元素创建一个卷角效果</li>
<li>通过梯度渐变创建文本效果</li>
</ul>
<p>让我们开始吧!</p>
<h2 id="第一步创建盒子">第一步:创建盒子</h2>
<p>让我们加入唯一的HTML元素:一个锚标签。这是有理可依,因为大多数的图标同时也是一个链接。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><a class="docIcon" href="#">Document Icon</a>
</code></pre></div></div>
<p>可以给图标设置任何尺寸,我们设置为40*56px——只是演示,在实际环境中你可能需要其他尺寸。同时,我们需要添加 <code class="language-plaintext highlighter-rouge">display:block</code>,因为所有的锚标签默认情况下都是内联函数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.docIcon
{
background:#eee;
background: linear-gradient(top, #ddd 0, #eee 15%, #fff 40%, #fff 70%, #eee 100%);
border:1px solid #ccc;
display:block;
width:40px;
height:56px;
position:relative;
margin:42px auto;
}
</code></pre></div></div>
<p>注意,以上,我们设定的定位上下文是为后面伪元素服务。你会发现,我仅仅用了官方CSS3语法中的渐变。你可能会想需要使用浏览器前缀。为了加快速度,你可以用<a href="http://prefixr.com/">prefixr.com</a>(网站挂掉了,试试这个吧,<a href="http://leaverou.github.io/prefixfree/">http://leaverou.github.io/prefixfree/</a>)或在你最喜欢的代码编辑器里使用它的API。简单的复制上面的代码片段,粘贴到prefixr的文本框内,然后点击确认,它会生成各种带前缀的属性,像这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.docIcon {
background: #eee;
background: -webkit-linear-gradient(top, #ddd 0, #eee 15%, #fff 40%, #fff 70%, #eee 100%);
background: -moz-linear-gradient(top, #ddd 0, #eee 15%, #fff 40%, #fff 70%, #eee 100%);
background: -o-linear-gradient(top, #ddd 0, #eee 15%, #fff 40%, #fff 70%, #eee 100%);
background: -ms-linear-gradient(top, #ddd 0, #eee 15%, #fff 40%, #fff 70%, #eee 100%);
background: linear-gradient(top, #ddd 0, #eee 15%, #fff 40%, #fff 70%, #eee 100%);
border: 1px solid #ccc;
display: block;
width: 40px;
height: 56px;
position: relative;
margin: 42px auto;
}
</code></pre></div></div>
<p><img src="/blog/50.jpg" alt="" /></p>
<p>接下来,让我们使用CSS的<code class="language-plaintext highlighter-rouge">box-shadow</code>添加一些阴影效果。我也用<code class="language-plaintext highlighter-rouge">text-indent</code>属性来隐藏文本。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.docIcon
{
...
-webkit-box-shadow:inset rgba(255,255,255,0.8) 0 1px 1px;
-moz-box-shadow:inset rgba(255,255,255,0.8) 0 1px 1px;
box-shadow:inset rgba(255,255,255,0.8) 0 1px 1px;
text-indent:-9999em;
}
</code></pre></div></div>
<p><strong>到目前为止,我们得到如下所示:</strong></p>
<p><img src="/blog/51.png" alt="" /></p>
<h2 id="第二步设置圆角">第二步:设置圆角</h2>
<p>接下来,我们需要创建一个圆角的效果。添加下面的代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.docIcon
{
...
-webkit-border-radius:3px 15px 3px 3px;
-moz-border-radius:3px 15px 3px 3px;
border-radius:3px 15px 3px 3px;
}
</code></pre></div></div>
<p>通过这四个值,可以指定的顶部,底部,左,右半径,因此。这是类似于你设置外边据和内边距的方式。</p>
<p><strong>现在如下:</strong></p>
<p><img src="/blog/52.png" alt="" /></p>
<h2 id="第三步弯曲一个角">第三步:弯曲一个角</h2>
<blockquote>
<p>为创建弯曲角的视觉效果,我们将使用生成内容和伪类元素。</p>
</blockquote>
<p>首先,在我们的图标<code class="language-plaintext highlighter-rouge">:before</code>上添加内容。在这种情况下,我们不需要任何特定的文本。相反,我们需要创建一个15px的盒子,并应用背景渐变。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.docIcon:before {
content: "";
display: block;
position: absolute;
top: 0;
right: 0;
width: 15px;
height: 15px;
background: #ccc;
background: -webkit-linear-gradient(45deg, #fff 0, #eee 50%, #ccc 100%);
background: -moz-linear-gradient(45deg, #fff 0, #eee 50%, #ccc 100%);
background: -o-linear-gradient(45deg, #fff 0, #eee 50%, #ccc 100%);
background: -ms-linear-gradient(45deg, #fff 0, #eee 50%, #ccc 100%);
background: linear-gradient(45deg, #fff 0, #eee 50%, #ccc 100%);
-webkit-box-shadow: rgba(0,0,0,0.05) -1px 1px 1px, inset white 0 0 1px;
-moz-box-shadow: rgba(0,0,0,0.05) -1px 1px 1px, inset white 0 0 1px;
box-shadow: rgba(0,0,0,0.05) -1px 1px 1px, inset white 0 0 1px;
border-bottom: 1px solid #ccc;
border-left: 1px solid #ccc;
}
</code></pre></div></div>
<p>为给我们的生成内容设置右上方的卷角,我们必须,再次采用相同的设置。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>...
-webkit-border-radius:3px 15px 3px 3px;
-moz-border-radius:3px 15px 3px 3px;
border-radius:3px 15px 3px 3px;
</code></pre></div></div>
<p><strong>展示!</strong></p>
<p><img src="/blog/53.png" alt="" /></p>
<h2 id="第四步添加线">第四步:添加线</h2>
<p>接下来,我们将使用<code class="language-plaintext highlighter-rouge">:after</code>伪元素添加一些虚线代表抽象的文本。设置宽度为60%,左右外边据各为20%(相加为100%)。接下来,我们指定高度和位置在0,0点。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.docIcon:after
{
content:"";
display:block;
position:absolute;
left:0;
top:0;
width:60%;
margin:22px 20% 0;
height:15px;
}
</code></pre></div></div>
<p>创建一组线显得有点棘手,但是如果我们是聪明的话,我们可以使用CSS渐变达到这种效果。首先,把总高度除以五,将每一块用实心填充。请参考下面的图像,清晰的体现这一思想。要把它放进你的工具箱,它是个漂亮的技术,不是吗?</p>
<p><img src="/blog/54.png" alt="" /></p>
<p><strong>多条线的CSS渐变</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.docIcon:after
{
...
background:#ccc;
background: -webkit-linear-gradient(top, #ccc 0, #ccc 20%, #fff 20%, #fff 40%, #ccc 40%, #ccc 60%, #fff 60%, #fff 80%, #ccc 80%, #ccc 100%);
background: -moz-linear-gradient(top, #ccc 0, #ccc 20%, #fff 20%, #fff 40%, #ccc 40%, #ccc 60%, #fff 60%, #fff 80%, #ccc 80%, #ccc 100%);
background: -o-linear-gradient(top, #ccc 0, #ccc 20%, #fff 20%, #fff 40%, #ccc 40%, #ccc 60%, #fff 60%, #fff 80%, #ccc 80%, #ccc 100%);
background: -ms-linear-gradient(top, #ccc 0, #ccc 20%, #fff 20%, #fff 40%, #ccc 40%, #ccc 60%, #fff 60%, #fff 80%, #ccc 80%, #ccc 100%);
background:linear-gradient(top, #ccc 0, #ccc 20%, #fff 20%, #fff 40%, #ccc 40%, #ccc 60%, #fff 60%, #fff 80%, #ccc 80%, #ccc 100%);
}
</code></pre></div></div>
<p><strong>我们完成了!</strong></p>
<p><img src="/blog/55.png" alt="" /></p>
<p>你喜欢吗?你有其他类似的把戏吗?如果有的话,在下面的评论留下它们的链接。</p>
<h2 id="译者注">译者注</h2>
<p>本文为翻译文章,原文为“<a href="http://net.tutsplus.com/tutorials/html-css-techniques/create-a-document-icon-with-css3/">How to Create a Beautiful Icon with CSS3</a>”</p>
由计算机谈最强大脑周玮
2014-01-25T00:00:00+00:00
http://yanhaijing.com/computer/2014/01/25/talk-zhouwei-with-computer
<p>最近看了,江苏卫视的最强大脑,特别当看到周玮的时候,让我想起了以前看到说印度有个人能算几百位数书的开方(前段时间这个人好像死了),看的时候很是振奋,可以说是不明觉历,以我的能力心算22*22都费劲的人,真的很佩服此人 ,开始也没多想,只是觉得很厉害。</p>
<p>下面是周玮现场计算的三道题</p>
<p><img src="/blog/56.png" alt="" /></p>
<p>随后看到网上有很多人质疑,关于质疑的种种声音我就不提了,有兴趣的可以自行搜索。</p>
<p>当看到有人给出了三道题的算法,可以看这里<a href="http://www.guokr.com/article/437913/">http://www.guokr.com/article/437913/</a>。</p>
<p>看完后更是觉得五体投地了,这么复杂的过程,难度很大啊,网上的质疑文章都给出了算法,也都说常人经过训练也可做到,我想说的是,再怎么训练你,你能跑过博尔特吗,这个是需要天赋的,除了天赋之外,我想说的就是周玮一定有什么地方异于常人,也许在大脑的构造上。</p>
<p>我想肯定是多方面的原因,成就了周玮,下面就几方面展开。</p>
<h2 id="方法论">方法论</h2>
<p>其实网友给出的解体方法,可以算作方法论,如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>613 = ( (63)2)2×6=...
</code></pre></div></div>
<p>具体步骤参见文章链接,其实这样分解开来,对常人而言,也是不可完成的任务。</p>
<p>这其实让我想到计算机的移位操作,比如仔汇编里面同样计算1024/2时,我们可以用除指令,也可以用移位指令,这其实就是方法的问题,其实在高级语言中,编辑器会帮我们优化的,不信看下的图,vc6将除以2优化为移位操作,当然优化的过程很复杂,这里只举了一个特例,有兴趣的同学可以自己研究。</p>
<p><img src="/blog/57.png" alt="" /></p>
<p>假如两台同样配置的计算机,肯定是移位的速度更快了,只是计算机太快了,1/10000秒和1/100秒之间的差别我们根本就感觉不到,这其实也是写程序的时候要在性能和可维护性之间折中的原因,如果不影响用户体验,1毫秒和10毫秒又有什么区别呢,这其实也是开发时会把优化放到最后来做,因为根据80/20原则,1个月才会被用回执行一次的操作,优化它的意义不大。</p>
<p>好了,打住吧,扯远了,继续本文的主题,方法其实很重要,比如在发明乘法之前,计算11个11只和,只能计算11遍了,但有了乘法后就不同了,同样对数的发明将乘法运算降级为加法运算,这同样是方法的重要性,我们不能否定,周玮发明了自己的方法,毕竟乘法发明前,计算100个同样数字的和可以算天文数字了。</p>
<p>这其实在我们小学的时候也很常见,比如那些出水管,进水管的题,开始的时候我们学的都是算数法,后来我们学习了方程,可是用方程的速度明显没有算数算的快,因为寻找算数的过程其实 刚好是解方程最后的结果,我想现在很多人,都已经不会用算术法来解决这些问题,。</p>
<p>著名的爱迪生测灯泡体积的故事,其实也是方法的问题;我们计算机学的算法,也是方法的问题,同样的问题,算法不同,时间可能差很多,因为随着问题难度的增加,不同算法之间所差的时间会成爆炸式增长,计算25*25,周玮的速度可能和常人一样,或者比常人慢,但计算123456789*123456789,周玮的方法可能是和时间成对数,而普通人可能就是指数了。</p>
<h2 id="天赋论">天赋论</h2>
<p>如果将人脑比喻成计算机的话,正常人的脑子如果是pc的话,那周玮的大脑可以说是超级计算机,就上前面说的,博尔特在百米上有天赋,这个可能是常人再怎么训练也达不到的,古人说世上无难事只怕有心人,只要功夫深铁杵磨成针,但前提也要选择自己有天赋的方面,不然再怎么训练可能也只是平平常常。</p>
<p>继续方法轮的例子,假如两个台电脑用同样的方法计算,但是超级计算机的速度肯定会比pc快得不是一点半点,道理是一样的,一个人的大脑肯定和别人不一样,智商也不相同,在我们从小到大的学习生涯中,总有一些人,不怎么学习,但人家学习成绩就是好,没办法,智商高,每个人的人脑发育也不同,像我这种从下营养不良发育不好的可能不光是身体,我们的大脑压根就和周玮不再同一个频率,他是酷睿i7我是酷睿i1,不具可比性,这个问题没法解决,如果是电脑的话我们可以换一台,但是人的话就没办法了。</p>
<p>有些人天生记忆力好,有些人天生对数字敏感,有些人天生能吃,这都是父母给的,后天无法改变。</p>
<h2 id="效率论">效率论</h2>
<p>众所周知,我们的大脑体积是固定的,脑细胞数目也是固定的,理论上在容量一定的情况下,能装下的东西就是有限的,如果你满脑子装着金钱,那你还能淡泊名利吗。</p>
<p>我们都有体会,我们没有小时候的记忆力好,而且都会说,现在不行了,上学的时候记忆力多么多么好,不知大家有没有想过我们现在要做的事情太多了,脑子里想的东西太多了,很难在塌下心来专心做一件事情了,从出生到现在,其实我们还是很厉害的,出生是一张白纸,到现在能在社会中生存,其实每个人都很厉害,小孩1岁会走路,3岁会说话,小学初中那么多知识,我们都能轻松应对,为什么,因为那时候我们专一,没有烦心事,小孩一天到晚都在学汉语,那能学的不快吗。</p>
<p>周玮很少与身边人交往,他脑子里面肯定没我们这么多乱七八糟的事情,心思都在算数上,效率100%,我们呢,可能连1%都不到,自然比不过了。</p>
<p>看见过一个从小失去双手的孩子,学会了用脚,穿衣服,用脚弹钢琴,我敢说,有手的人一定学不会,因为不能全心全意做这件事,自然会觉得别人做到了很厉害。</p>
<p>女孩子到了初中,成绩很容易下滑,不少有青春期女孩子比男孩子事情多的缘故,每天心理装着事情,自然干什么都干不好。</p>
<p>有人说我赋予感性,有人说我赋予理性,总之厚此薄彼,分配好优先级也是一种能力。</p>
<h2 id="结构论">结构论</h2>
<p>接下来进入本文的重点,看了网友的分析,其实我想起了大学时,计算机组成结构老师讲的串行加法器和并行加法器,学过计算机的人都是知道,其实计算机里面只有加法器,加减乘除最后都会用加法来模拟,说不定我们人脑也是如此,丰富的情感,缜密的逻辑,都是几千亿可脑细胞并行做加法模拟出来的,其实很奇怪,大脑就是肉,怎么会有思想,同样是肉的大腿,为啥没有思想,空气中的无数细菌为啥没有智慧(当然可能有)。</p>
<p>又扯远了,回到我们的主题,我们的加法器假如它是四位加法器,如果我们要算8位数的运算就需要两个加法器,第一个计算低四位,第二个计算高四位,这时就有问题了,计算高四位的计算器只有等待低四位计算完成,才能进行计算,因为不确定有没有进位,串行结构就是这样,但是有牛人设计成了并行加法器,高位计算不需要等待低位的进位,进位可以瞬间计算出来,这样其实计算的效率就大大提高了,如果我们要计算10000位数的运算,如果高位需要等待低位的话,即便每秒能算100个数的加减法,那也需要100s,而并行的话就大大提高了效率,只需要1s。</p>
<p>上面只是举个例子,我想周玮的大脑结构可能和常人不同,我们计算100+200*300/100的时候可能是顺序计算的,也许周玮是并行也说不定,也许他没发明算法,而它的脑子就是算法,能瞬间出结果,这也不是不可能的,大千世界无奇不有吗。</p>
<h2 id="结论">结论</h2>
<p>当上帝为你关闭一扇门时,也会为你打开一扇窗,塞翁失马焉知非福啊,我想可能不是上面的某一个原因造就了周玮,可能是综合的,也可能是其他原因,我们能做的就是祝福他,也祝福我们自己,上帝从不眷顾谁,愿世界更美好。</p>
JavaScript里的依赖注入
2014-01-24T00:00:00+00:00
http://yanhaijing.com/javascript/2014/01/24/dependency-injection-in-javascript
<p>我喜欢引用这句话,“程序是对复杂性的管理”。计算机世界是一个巨大的抽象建筑群。我们简单的包装一些东西然后发布新工具,周而复始。现在思考下,你所使用的语言包括的一些内建的抽象函数或是低级操作符。这在JavaScript里是一样的。</p>
<p>迟早你需要用到其他开发人员的抽象成果——即你依靠别人的代码。我喜欢依赖自由(无依赖)的模块,但那是难以实现的。甚至你创建的那些漂亮的黑盒子组件也或多或少会依赖一些东西。这正是依赖注入大显身手的之处。现在有效地管理依赖的能力是绝对必要的。本文总结了我对问题探索和一些的解决方案。</p>
<h2 id="目标">目标</h2>
<p>设想我们有两个模块。第一个是负责Ajax请求服务(<code class="language-plaintext highlighter-rouge">service</code>),第二个是路由(<code class="language-plaintext highlighter-rouge">router</code>)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var service = function() {
return { name: 'Service' };
}
var router = function() {
return { name: 'Router' };
}
我们有另一个函数需要用到这两个模块。
var doSomething = function(other) {
var s = service();
var r = router();
};
</code></pre></div></div>
<p>为使看起来更有趣,这函数接受一个参数。当然,我们完全可以使用上面的代码,但这显然不够灵活。如果我们想使用<code class="language-plaintext highlighter-rouge">ServiceXML</code>或<code class="language-plaintext highlighter-rouge">ServiceJSON</code>呢,或者如果我们需要一些测试模块呢。我们不能仅靠编辑函数体来解决问题。首先,我们可以通过函数的参数来解决依赖性。即:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var doSomething = function(service, router, other) {
var s = service();
var r = router();
};
</code></pre></div></div>
<p>我们通过传递额外的参数来实现我们想要的功能,然而,这会带来新的问题。想象如果我们的<code class="language-plaintext highlighter-rouge">doSomething</code> 方法散落在我们的代码中。如果我们需要更改依赖条件,我们不可能更改所有调用函数的文件。</p>
<p>我们需要一个能帮我们搞定这些的工具。这就是依赖注入尝试解决的问题。让我们写下一些我们的依赖注入解决办法应该达到的目标:</p>
<ul>
<li>我们应该能够注册依赖关系</li>
<li>注入应该接受一个函数,并返回一个我们需要的函数</li>
<li>我们不能写太多东西——我们需要精简漂亮的语法</li>
<li>注入应该保持被传递函数的作用域</li>
<li>被传递的函数应该能够接受自定义参数,而不仅仅是依赖描述</li>
<li>堪称完美的清单,下面 让我们实现它。</li>
</ul>
<h2 id="requirejs--amd的方法">RequireJS / AMD的方法</h2>
<p>你可能对<a href="http://requirejs.org/">RequireJS</a>早有耳闻,它是解决依赖注入不错的选择。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>define(['service', 'router'], function(service, router) {
// ...
});
</code></pre></div></div>
<p>这种想法是先描述需要的依赖,然后再写你的函数。这里参数的顺序很重要。如上所说,让我们写一个叫做<code class="language-plaintext highlighter-rouge">injector</code>的模块,能接受相同的语法。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var doSomething = injector.resolve(['service', 'router'], function(service, router, other) {
expect(service().name).to.be('Service');
expect(router().name).to.be('Router');
expect(other).to.be('Other');
});
doSomething("Other");
</code></pre></div></div>
<blockquote>
<p>再继续之前我应该解释清楚<code class="language-plaintext highlighter-rouge">doSomething</code>函数体内容,我使用<a href="https://github.com/LearnBoost/expect.js">expect.js</a> (断言方面的库)仅是为了保证我写的代码的行为和我期望的是一样的,体现一点点TDD(测试驱动开发)方法。</p>
</blockquote>
<p>下面开始我们的<code class="language-plaintext highlighter-rouge">injector</code>模块,这是非常棒的一个单例模式,所以它能在我们程序的不同部分工作的很好。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var injector = {
dependencies: {},
register: function(key, value) {
this.dependencies[key] = value;
},
resolve: function(deps, func, scope) {
}
}
</code></pre></div></div>
<p>这是一个非常简单的对象,有两个方法,一个用来存储的属性。我们要做的是检查<code class="language-plaintext highlighter-rouge">deps</code>数组并在<code class="language-plaintext highlighter-rouge">dependencies</code>变量中搜索答案。剩下的只是调用<code class="language-plaintext highlighter-rouge">.apply</code>方法并传递之前的<code class="language-plaintext highlighter-rouge">func</code>方法的参数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>resolve: function(deps, func, scope) {
var args = [];
for(var i=0; i<deps.length, d=deps[i]; i++) {
if(this.dependencies[d]) {
args.push(this.dependencies[d]);
} else {
throw new Error('Can\'t resolve ' + d);
}
}
return function() {
func.apply(scope || {}, args.concat(Array.prototype.slice.call(arguments, 0)));
}
}
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">scope</code>是可选的,<code class="language-plaintext highlighter-rouge">Array.prototype.slice.call(arguments, 0)</code>是必须的,用来将<code class="language-plaintext highlighter-rouge">arguments</code>变量转换为真正的数组。到目前为止还不错。我们的测试通过了。这种实现的问题是,我们需要写所需部件两次,并且我们不能混淆他们的顺序。附加的自定义参数总是位于依赖之后。</p>
<h2 id="反射方法">反射方法</h2>
<p>根据维基百科的定义反射是指一个程序在运行时检查和修改一个对象的结构和行为的能力。简单的说,在JavaScript的上下文里,这具体指读取和分析的对象或函数的源代码。让我们完成文章开头提到的<code class="language-plaintext highlighter-rouge">doSomething</code>函数。如果你在控制台输出<code class="language-plaintext highlighter-rouge">doSomething.tostring()</code>。你将得到如下的字符串:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"function (service, router, other) {
var s = service();
var r = router();
}"
</code></pre></div></div>
<p>通过此方法返回的字符串给我们遍历参数的能力,更重要的是,能够获取他们的名字。这其实是<a href="http://angularjs.org/">Angular</a> 实现它的依赖注入的方法。我偷了一点懒,直接截取Angular代码中获取参数的正则表达式。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/^function\s*[^\(]*\(\s*([^\)]*)\)/m
</code></pre></div></div>
<p>我们可以像下面这样修改<code class="language-plaintext highlighter-rouge">resolve</code> 的代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>resolve: function() {
var func, deps, scope, args = [], self = this;
func = arguments[0];
deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, '').split(',');
scope = arguments[1] || {};
return function() {
var a = Array.prototype.slice.call(arguments, 0);
for(var i=0; i<deps.length; i++) {
var d = deps[i];
args.push(self.dependencies[d] && d != '' ? self.dependencies[d] : a.shift());
}
func.apply(scope || {}, args);
}
}
</code></pre></div></div>
<p>我们执行正则表达式的结果如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>["function (service, router, other)", "service, router, other"] 看起来,我们只需要第二项。一旦我们清楚空格并分割字符串就得到`deps`数组。只有一个大的改变:
var a = Array.prototype.slice.call(arguments, 0);
...
args.push(self.dependencies[d] && d != '' ? self.dependencies[d] : a.shift());
</code></pre></div></div>
<p>我们循环遍历<code class="language-plaintext highlighter-rouge">dependencies</code>数组,如果发现缺失项则尝试从<code class="language-plaintext highlighter-rouge">arguments</code>对象中获取。谢天谢地,当数组为空时,<code class="language-plaintext highlighter-rouge">shift</code>方法只是返回<code class="language-plaintext highlighter-rouge">undefined</code>,而不是抛出一个错误(这得益于web的思想)。新版的<code class="language-plaintext highlighter-rouge">injector</code> 能像下面这样使用:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var doSomething = injector.resolve(function(service, other, router) {
expect(service().name).to.be('Service');
expect(router().name).to.be('Router');
expect(other).to.be('Other');
});
doSomething("Other");
</code></pre></div></div>
<p>不必重写依赖并且他们的顺序可以打乱。它仍然有效,我们成功复制了Angular的魔法。</p>
<p>然而,这种做法并不完美,这就是反射类型注射一个非常大的问题。压缩会破坏我们的逻辑,因为它改变参数的名字,我们将无法保持正确的映射关系。例如,<code class="language-plaintext highlighter-rouge">doSometing()</code>压缩后可能看起来像这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var doSomething=function(e,t,n){var r=e();var i=t()}
</code></pre></div></div>
<p>Angular团队提出的解决方案看起来像:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var doSomething = injector.resolve(['service', 'router', function(service, router) {
}]);
</code></pre></div></div>
<p>这看起来很像我们开始时的解决方案。我没能找到一个更好的解决方案,所以决定结合这两种方法。下面是<code class="language-plaintext highlighter-rouge">injector</code>的最终版本。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var injector = {
dependencies: {},
register: function(key, value) {
this.dependencies[key] = value;
},
resolve: function() {
var func, deps, scope, args = [], self = this;
if(typeof arguments[0] === 'string') {
func = arguments[1];
deps = arguments[0].replace(/ /g, '').split(',');
scope = arguments[2] || {};
} else {
func = arguments[0];
deps = func.toString().match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].replace(/ /g, '').split(',');
scope = arguments[1] || {};
}
return function() {
var a = Array.prototype.slice.call(arguments, 0);
for(var i=0; i<deps.length; i++) {
var d = deps[i];
args.push(self.dependencies[d] && d != '' ? self.dependencies[d] : a.shift());
}
func.apply(scope || {}, args);
}
}
}
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">resolve</code>访客接受两或三个参数,如果有两个参数它实际上和文章前面写的一样。然而,如果有三个参数,它会将第一个参数转换并填充<code class="language-plaintext highlighter-rouge">deps</code>数组,下面是一个测试例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var doSomething = injector.resolve('router,,service', function(a, b, c) {
expect(a().name).to.be('Router');
expect(b).to.be('Other');
expect(c().name).to.be('Service');
});
doSomething("Other");
</code></pre></div></div>
<p>你可能注意到在第一个参数后面有两个逗号——注意这不是笔误。空值实际上代表“<code class="language-plaintext highlighter-rouge">Other</code>”参数(占位符)。这显示了我们是如何控制参数顺序的。</p>
<h2 id="直接注入scope">直接注入Scope</h2>
<p>有时我会用到第三个注入变量,它涉及到操作函数的作用域(换句话说,就是<code class="language-plaintext highlighter-rouge">this</code>对象)。所以,很多时候不需要使用这个变量。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var injector = {
dependencies: {},
register: function(key, value) {
this.dependencies[key] = value;
},
resolve: function(deps, func, scope) {
var args = [];
scope = scope || {};
for(var i=0; i<deps.length, d=deps[i]; i++) {
if(this.dependencies[d]) {
scope[d] = this.dependencies[d];
} else {
throw new Error('Can\'t resolve ' + d);
}
}
return function() {
func.apply(scope || {}, Array.prototype.slice.call(arguments, 0));
}
}
}
</code></pre></div></div>
<p>我们所做的一切其实就是将依赖添加到作用域。这样做的好处是,开发人员不用再写依赖性参数;它们已经是函数作用域的一部分。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var doSomething = injector.resolve(['service', 'router'], function(other) {
expect(this.service().name).to.be('Service');
expect(this.router().name).to.be('Router');
expect(other).to.be('Other');
});
doSomething("Other");
</code></pre></div></div>
<h2 id="结束语">结束语</h2>
<p>其实我们大部分人都用过依赖注入,只是我们没有意识到。即使你不知道这个术语,你可能在你的代码里用到它百万次了。希望这篇文章能加深你对它的了解。</p>
<p>在这篇文章中提到的例子都可以在<a href="https://github.com/krasimir/blog-posts/tree/master/JavaScriptDependencyInjection">这里</a>找到。</p>
<h2 id="译者注">译者注</h2>
<p>本文为译文,原文为“<a href="http://flippinawesome.org/2014/01/20/dependency-injection-in-javascript/">Dependency Injection in JavaScript</a>”。</p>
一套名企WEB前端面试题,不提供答案
2014-01-20T00:00:00+00:00
http://yanhaijing.com/web/2014/01/20/web-front-end-interview-question
<ol>
<li>
<p>说说你对Doctype的理解</p>
</li>
<li>
<p>web产品开发的流程</p>
</li>
<li>
<p>说说你对盒子模型的理解</p>
</li>
<li>
<p>前端页面有哪三层构成,分别是什么?作用是什么?</p>
</li>
<li>
<p>行内元素有哪些?块级元素有哪些?他们如何相互转化?</p>
</li>
<li>
<p>根据表格写出结构代码?
<img src="/blog/49.png" alt="表格" /></p>
</li>
<li>
<p>CSS的引入方式有哪些?</p>
</li>
<li>
<p>请用CSS来定义<code class="language-plaintext highlighter-rouge"><p></code>标签,要求实现以下效果:字体颜色再IE6下为黑色,IE7下为红色,IE8下为绿色,其他浏览器下为黄色。</p>
</li>
<li>
<p>谈谈CSS Sprites技术</p>
</li>
<li>
<p>你做的页面在哪些浏览器下测试过?这些浏览器的内核分别是什么?这些浏览器的兼容性有哪些。</p>
</li>
<li>
<p>谈谈你对SEO的理解。</p>
</li>
<li>
<p>请简单说一些你对HTML5和CSS3的了解情况。</p>
</li>
<li>
<p>如何用JS获取ID为title的元素,并将其字体大小设为16px。</p>
</li>
<li>
<p>jQuery获取form表单中第二个input元素的方法。</p>
</li>
<li>
<p>如何选中一个table中索引为偶数的tr。</p>
</li>
<li>
<p>看图写代码题,需要写CSS和JS。重要的就是一排图片,鼠标放上去会根据鼠标位置显示一个div,里面是tips。</p>
</li>
</ol>
<p>这套题目是金山的面试题。(亲身经历)题量还是挺大的呵。</p>
有趣的JavaScript原生数组函数
2014-01-17T00:00:00+00:00
http://yanhaijing.com/javascript/2014/01/17/fun-with-javascript-native-array-functions
<p>在JavaScript中,可以通过两种方式创建数组,构造函数和数组直接量, 其中后者为首选方法。数组对象继承自<code class="language-plaintext highlighter-rouge">Object.prototype</code>,对数组执行<code class="language-plaintext highlighter-rouge">typeof</code>操作符返回<code class="language-plaintext highlighter-rouge">‘object’</code>而不是<code class="language-plaintext highlighter-rouge">‘array’</code>。然而执行<code class="language-plaintext highlighter-rouge">[] instanceof Array</code>返回<code class="language-plaintext highlighter-rouge">true</code>。此外,还有类数组对象是问题更复杂,如字符串对象,<code class="language-plaintext highlighter-rouge">arguments</code>对象。<code class="language-plaintext highlighter-rouge">arguments</code>对象不是<code class="language-plaintext highlighter-rouge">Array</code>的实例,但却有个<code class="language-plaintext highlighter-rouge">length</code>属性,并且值能通过索引获取,所以能像数组一样通过循环操作。</p>
<p>在本文中,我将复习一些数组原型的方法,并探索这些方法的用法。</p>
<ul>
<li>循环<code class="language-plaintext highlighter-rouge">.forEach</code></li>
<li>断言<code class="language-plaintext highlighter-rouge">.some</code>和<code class="language-plaintext highlighter-rouge">.every</code></li>
<li><code class="language-plaintext highlighter-rouge">.join</code>和<code class="language-plaintext highlighter-rouge">.concat</code>的区别</li>
<li>栈和队列<code class="language-plaintext highlighter-rouge">.pop</code>,<code class="language-plaintext highlighter-rouge">.push</code>,<code class="language-plaintext highlighter-rouge">.shift</code>和<code class="language-plaintext highlighter-rouge">.unshift</code></li>
<li>模型映射<code class="language-plaintext highlighter-rouge">.map</code></li>
<li>查询<code class="language-plaintext highlighter-rouge">.filter</code></li>
<li>排序<code class="language-plaintext highlighter-rouge">.sort</code></li>
<li>计算<code class="language-plaintext highlighter-rouge">.reduce</code>和<code class="language-plaintext highlighter-rouge">.reduceRight</code></li>
<li>复制<code class="language-plaintext highlighter-rouge">.slice</code></li>
<li>万能的<code class="language-plaintext highlighter-rouge">.splice</code></li>
<li>查找<code class="language-plaintext highlighter-rouge">.indexOf</code></li>
<li><code class="language-plaintext highlighter-rouge">in</code>操作符</li>
<li>走进<code class="language-plaintext highlighter-rouge">.reverse</code></li>
</ul>
<p><img src="/blog/47.png" alt="" /></p>
<p>如果你想测试上面的例子,您可以复制并粘贴到您的浏览器的控制台中。</p>
<h2 id="循环foreach">循环<code class="language-plaintext highlighter-rouge">.forEach</code></h2>
<p>这是JavaScript原生数组方法中最简单的方法。不用怀疑,IE7和IE8不支持此方法。</p>
<p><code class="language-plaintext highlighter-rouge">forEach</code>方法需要一个回调函数,数组内的每个元素都会调用一次此方法,此方法需要三个参数如下:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">value</code> 当前操作的数组元素</li>
<li>当前操作元素的数组索引</li>
<li><code class="language-plaintext highlighter-rouge">array</code> 当前数组的引用</li>
</ul>
<p>此外,可以传递可选的第二个参数,作为每个调用函数的上下文(<code class="language-plaintext highlighter-rouge">this</code>)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>['_', 't', 'a', 'n', 'i', 'f', ']'].forEach(function (value, index, array) {
this.push(String.fromCharCode(value.charCodeAt() + index + 2))
}, out = [])
out.join('')
// <- 'awesome'
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">.join</code>函数我将在下文提及,上面例子中,它将数组中的不同元素拼接在一起,类似于如下的效果:<code class="language-plaintext highlighter-rouge">out[0] + '' + out[1] + '' + out[2] + '' + out[n]</code>。</p>
<p>我们不能用<code class="language-plaintext highlighter-rouge">break</code>中断<code class="language-plaintext highlighter-rouge">forEach</code>循环,抛出异常是不明智的方法。幸运的是,我们有其他的方法中断操作。</p>
<h2 id="断言some和every">断言<code class="language-plaintext highlighter-rouge">.some</code>和<code class="language-plaintext highlighter-rouge">.every</code></h2>
<p>如果你曾经用过.NET的枚举,这些方法的名字和<code class="language-plaintext highlighter-rouge">.Any(x => x.IsAwesome)</code> 和 <code class="language-plaintext highlighter-rouge">.All(x => x.IsAwesome)</code>非常相似。</p>
<p>这些方法和<code class="language-plaintext highlighter-rouge">.forEach</code>类似,需要一个包含<code class="language-plaintext highlighter-rouge">value</code>,<code class="language-plaintext highlighter-rouge">index</code>,和<code class="language-plaintext highlighter-rouge">array</code>三个参数的回调函数,并且也有一个可选的第二个上下文参数。MDN对<code class="language-plaintext highlighter-rouge">.some</code>的描述如下:</p>
<blockquote>
<p><code class="language-plaintext highlighter-rouge">some</code>将会给数组里的每一个元素执行一遍回调函数,直到有一个回调函数返回true位置。如果找到目标元素,<code class="language-plaintext highlighter-rouge">some</code>立即返回<code class="language-plaintext highlighter-rouge">true</code>,否则<code class="language-plaintext highlighter-rouge">some</code>返回<code class="language-plaintext highlighter-rouge">false</code>。回调函数只对已经指定值的数组索引执行;它不会对已删除的或未指定值的元素执行。</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>max = -Infinity
satisfied = [10, 12, 10, 8, 5, 23].some(function (value, index, array) {
if (value > max) max = value
return value < 10
})
console.log(max)
// <- 12
satisfied
// <- true
</code></pre></div></div>
<p>注意,当回调函数的<code class="language-plaintext highlighter-rouge">value < 10</code> 条件满足时,中断函数循环。<code class="language-plaintext highlighter-rouge">.every</code>的工作行为类似,但回调函数要返回<code class="language-plaintext highlighter-rouge">false</code>而不是<code class="language-plaintext highlighter-rouge">true</code>。</p>
<h2 id="join和concat的区别"><code class="language-plaintext highlighter-rouge">.join</code>和<code class="language-plaintext highlighter-rouge">.concat</code>的区别</h2>
<p><code class="language-plaintext highlighter-rouge">.join</code>方法经常和<code class="language-plaintext highlighter-rouge">.concat</code>混淆。<code class="language-plaintext highlighter-rouge">.join</code>(分隔符)方法创建一个字符串,会将数组里面每个元素用分隔符连接。如果没有提供分隔符,默认的分隔符为“,”。<code class="language-plaintext highlighter-rouge">.concat</code>方法创建一个新数组,其是对原数组的浅拷贝(注意是浅拷贝哦)。</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">.concat</code> 的标志用法:<code class="language-plaintext highlighter-rouge">array.concat(val, val2, val3, valn)</code></li>
<li><code class="language-plaintext highlighter-rouge">.concat</code> 返回一个新书组</li>
<li><code class="language-plaintext highlighter-rouge">array.concat()</code>没有参数的情况下,会返回原数组的浅拷贝</li>
</ul>
<p>浅拷贝意味着新数组和原数组保持相同的对象引用,这通常是好事。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = { foo: 'bar' }
var b = [1, 2, 3, a]
var c = b.concat()
console.log(b === c)
// <- false
b[3] === a && c[3] === a
// <- true
</code></pre></div></div>
<h2 id="栈和队列poppushshift和unshift">栈和队列<code class="language-plaintext highlighter-rouge">.pop</code>,<code class="language-plaintext highlighter-rouge">.push</code>,<code class="language-plaintext highlighter-rouge">.shift</code>和<code class="language-plaintext highlighter-rouge">.unshift</code></h2>
<p>每个人都知道向数组添加元素用<code class="language-plaintext highlighter-rouge">.push</code>。但你知道一次可以添加多个元素吗?如下<code class="language-plaintext highlighter-rouge">[].push('a', 'b', 'c', 'd', 'z')</code>。</p>
<p><code class="language-plaintext highlighter-rouge">.pop</code>方法和<code class="language-plaintext highlighter-rouge">.push</code>成对使用,它返回数组的末尾元素并将元素从数组移除。如果数组为空,返回<code class="language-plaintext highlighter-rouge">void 0(undefined)</code>。使用<code class="language-plaintext highlighter-rouge">.push</code>和<code class="language-plaintext highlighter-rouge">.pop</code>我们能轻易模拟出LIFO(后进先出或先进后出)栈。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function Stack () {
this._stack = []
}
Stack.prototype.next = function () {
return this._stack.pop()
}
Stack.prototype.add = function () {
return this._stack.push.apply(this._stack, arguments)
}
stack = new Stack()
stack.add(1,2,3)
stack.next()
// <- 3
</code></pre></div></div>
<p>相反,我们可以用<code class="language-plaintext highlighter-rouge">.unshift</code> 和 <code class="language-plaintext highlighter-rouge">.shift</code>模拟FIFO(先进先出)队列。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function Queue () {
this._queue = []
}
Queue.prototype.next = function () {
return this._queue.shift()
}
Queue.prototype.add = function () {
return this._queue.unshift.apply(this._queue, arguments)
}
queue = new Queue()
queue.add(1,2,3)
queue.next()
// <- 1
</code></pre></div></div>
<p>用<code class="language-plaintext highlighter-rouge">.shift</code>或<code class="language-plaintext highlighter-rouge">.pop</code>能很容易遍历数组元素,并做一些操作。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>list = [1,2,3,4,5,6,7,8,9,10]
while (item = list.shift()) {
console.log(item)
}
list
// <- []
</code></pre></div></div>
<h2 id="模型映射map">模型映射<code class="language-plaintext highlighter-rouge">.map</code></h2>
<blockquote>
<p><code class="language-plaintext highlighter-rouge">map</code> 方法会给原数组中的每个元素(必须有值)都调用一次 <code class="language-plaintext highlighter-rouge">callback</code> 函数<code class="language-plaintext highlighter-rouge">.callback</code> 每次执行后的返回值组合起来形成一个新数组。<code class="language-plaintext highlighter-rouge">callback</code>函数只会在有值的索引上被调用; 那些从来没被赋过值或者使用<code class="language-plaintext highlighter-rouge">delete</code>删除的索引则不会被调用。——MDN</p>
</blockquote>
<p><code class="language-plaintext highlighter-rouge">Array.prototype.map</code>方法和上面我们提到的<code class="language-plaintext highlighter-rouge">.forEach</code>,<code class="language-plaintext highlighter-rouge">.some</code>和<code class="language-plaintext highlighter-rouge">.every</code>有相同的参数:<code class="language-plaintext highlighter-rouge">.map(fn(value, index, array), thisArgument)</code>。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>values = [void 0, null, false, '']
values[7] = void 0
result = values.map(function(value, index, array){
console.log(value)
return value
})
// <- [undefined, null, false, '', undefined × 3, undefined]
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">undefined × 3</code> 值解释<code class="language-plaintext highlighter-rouge">.map</code>不会在没被赋过值或者使用<code class="language-plaintext highlighter-rouge">delete</code>删除的索引上调用,但他们仍然被包含在结果数组中。<code class="language-plaintext highlighter-rouge">map</code>在遍历或改变数组方面非常有用,如下所示:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 遍历
[1, '2', '30', '9'].map(function (value) {
return parseInt(value, 10)
})
// 1, 2, 30, 9
[97, 119, 101, 115, 111, 109, 101].map(String.fromCharCode).join('')
// <- 'awesome'
// 一个映射新对象的通用模式
items.map(function (item) {
return {
id: item.id,
name: computeName(item)
}
})
</code></pre></div></div>
<h2 id="查询filter">查询<code class="language-plaintext highlighter-rouge">.filter</code></h2>
<blockquote>
<p><code class="language-plaintext highlighter-rouge">filter</code>对每个数组元素执行一次回调函数,并返回一个由回调函数返回<code class="language-plaintext highlighter-rouge">true</code>的元素 组成的新数组。回调函数只会对已经指定值的数组项调用。</p>
</blockquote>
<p>用法例子:<code class="language-plaintext highlighter-rouge">.filter(fn(value, index, array), thisArgument)</code>。把它想象成<code class="language-plaintext highlighter-rouge">.Where(x => x.IsAwesome)</code> LINQ expression(如果你熟悉C#),或者SQL语句里面的<code class="language-plaintext highlighter-rouge">WHERE</code>。考虑到<code class="language-plaintext highlighter-rouge">.filter</code>仅返回<code class="language-plaintext highlighter-rouge">callback</code>函数返回真值的值,下面是一些有趣的例子。没有传递给回调函数测试的元素被简单的跳过,不会包含进返回的新书组里。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[void 0, null, false, '', 1].filter(function (value) {
return value
})
// <- [1]
[void 0, null, false, '', 1].filter(function (value) {
return !value
})
// <- [void 0, null, false, '']
</code></pre></div></div>
<h2 id="排序sort比较函数">排序<code class="language-plaintext highlighter-rouge">.sort</code>(比较函数)</h2>
<blockquote>
<p>如果未提供比较函数,元素会转换为字符串,并按字典许排列。例如,在字典序里,“80”排在“9”之前,但实际上我们希望的是80在9之后(数字排序)。</p>
</blockquote>
<p>像大部分排序函数一样,<code class="language-plaintext highlighter-rouge">Array.prototype.sort(fn(a,b))</code>需要一个包含两个测试参数的回调函数,并且要产生一下三种返回值之一:</p>
<ul>
<li>如果<code class="language-plaintext highlighter-rouge">a</code>在<code class="language-plaintext highlighter-rouge">b</code>前,则返回值小于零</li>
<li>如果<code class="language-plaintext highlighter-rouge">a</code>和<code class="language-plaintext highlighter-rouge">b</code>是等价的,则返回值等于零</li>
<li>如果<code class="language-plaintext highlighter-rouge">a</code>在<code class="language-plaintext highlighter-rouge">b</code>后,则返回值大于零</li>
</ul>
<p>代码</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[9,80,3,10,5,6].sort()
// <- [10, 3, 5, 6, 80, 9]
[9,80,3,10,5,6].sort(function (a, b) {
return a - b
})
// <- [3, 5, 6, 9, 10, 80]
</code></pre></div></div>
<h2 id="计算reduce和reduceright">计算<code class="language-plaintext highlighter-rouge">.reduce</code>和<code class="language-plaintext highlighter-rouge">.reduceRight</code></h2>
<p>首先<code class="language-plaintext highlighter-rouge">reduce</code>函数不是很好理解,.reduce从左到右而<code class="language-plaintext highlighter-rouge">.reduceRight</code>从右到左循环遍历数组,每次调用接收目前为止的部分结果和当前遍历的值。</p>
<p>两种方法都有如下典型用法:<code class="language-plaintext highlighter-rouge">.reduce(callback(previousValue, currentValue, index, array), initialValue)</code>。</p>
<p><code class="language-plaintext highlighter-rouge">previousValue</code>是最后被调用的回调函数的返回值,<code class="language-plaintext highlighter-rouge">initialValue</code>是开始时<code class="language-plaintext highlighter-rouge">previousValue</code>被初始化的值。<code class="language-plaintext highlighter-rouge">currentValue</code> 是当前被遍历的元素值,<code class="language-plaintext highlighter-rouge">index</code>是当前元素在数组中的索引值。<code class="language-plaintext highlighter-rouge">array</code>是对调用<code class="language-plaintext highlighter-rouge">.reduce</code>数组的简单引用。</p>
<p>一个典型的用例,使用<code class="language-plaintext highlighter-rouge">.reduce</code>的求和函数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Array.prototype.sum = function () {
return this.reduce(function (partial, value) {
return partial + value
}, 0)
};
[3,4,5,6,10].sum()
// <- 28
</code></pre></div></div>
<p>上面提到如果想把数组连成一个字符串,可以使用<code class="language-plaintext highlighter-rouge">.join</code>。当数组的值是对象的情况下,除非对象有能返回其合理值的<code class="language-plaintext highlighter-rouge">valueof</code>或<code class="language-plaintext highlighter-rouge">toString</code>方法,否则<code class="language-plaintext highlighter-rouge">.join</code>的表现和你期望的不一样。然而,我们可以使用<code class="language-plaintext highlighter-rouge">.reduce</code>作为对象的字符串生成器。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function concat (input) {
return input.reduce(function (partial, value) {
if (partial) {
partial += ', '
}
return partial + value
}, '')
}
concat([
{ name: 'George' },
{ name: 'Sam' },
{ name: 'Pear' }
])
// <- 'George, Sam, Pear'
</code></pre></div></div>
<h2 id="复制slice">复制<code class="language-plaintext highlighter-rouge">.slice</code></h2>
<p>和<code class="language-plaintext highlighter-rouge">.concat</code>类似,调用<code class="language-plaintext highlighter-rouge">.slice</code>缺省参数时,返回原数组的浅拷贝。<code class="language-plaintext highlighter-rouge">slice</code>函数需要两个参数,一个是开始位置和一个结束位置。</p>
<p><code class="language-plaintext highlighter-rouge">Array.prototype.slice</code>能被用来将类数组对象转换为真正的数组。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
// <- ['a', 'b']
</code></pre></div></div>
<p>这对<code class="language-plaintext highlighter-rouge">.concat</code>不适用,因为它会用数组包裹类数组对象。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Array.prototype.concat.call({ 0: 'a', 1: 'b', length: 2 })
// <- [{ 0: 'a', 1: 'b', length: 2 }]
</code></pre></div></div>
<p>除此之外,另一个常见用途是从参数列表中移除最初的几个元素,并将类数组对象转换为真正的数组。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function format (text, bold) {
if (bold) {
text = '<b>' + text + '</b>'
}
var values = Array.prototype.slice.call(arguments, 2)
values.forEach(function (value) {
text = text.replace('%s', value)
})
return text
}
format('some%sthing%s %s', true, 'some', 'other', 'things')
// <- <b>somesomethingother things</b>
</code></pre></div></div>
<h2 id="万能的splice">万能的<code class="language-plaintext highlighter-rouge">.splice</code></h2>
<p><code class="language-plaintext highlighter-rouge">.splice</code>是我最喜欢的原生数组函数之一。它允许你删除元素,插入新元素,或在同一位置同时进行上述操作,而只使用一个函数调用。注意和<code class="language-plaintext highlighter-rouge">.concat</code>和<code class="language-plaintext highlighter-rouge">.slice</code>不同的是<code class="language-plaintext highlighter-rouge">.splice</code>函数修改原数组。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var source = [1,2,3,8,8,8,8,8,9,10,11,12,13]
var spliced = source.splice(3, 4, 4, 5, 6, 7)
console.log(source)
// <- [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ,13]
spliced
// <- [8, 8, 8, 8]
</code></pre></div></div>
<p>你可能已经注意到,它也返回被删除的元素。如果你想遍历已经删除的数组时这可能会派上用场。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var source = [1,2,3,8,8,8,8,8,9,10,11,12,13]
var spliced = source.splice(9)
spliced.forEach(function (value) {
console.log('removed', value)
})
// <- removed 10
// <- removed 11
// <- removed 12
// <- removed 13
console.log(source)
// <- [1, 2, 3, 8, 8, 8, 8, 8, 9]
</code></pre></div></div>
<h2 id="查找indexof">查找<code class="language-plaintext highlighter-rouge">.indexOf</code></h2>
<p>通过<code class="language-plaintext highlighter-rouge">.indexOf</code>,我们可以查找数组元素的位置。如果没有匹配元素则返回<code class="language-plaintext highlighter-rouge">-1</code>。我发现我用的很多的一个模式是连续比较,例如<code class="language-plaintext highlighter-rouge">a === 'a' || a === 'b' || a === 'c'</code>,或者即使只有两个结果的比较。在这种情况下,你也可以使用<code class="language-plaintext highlighter-rouge">.indexOf</code>,像这样:<code class="language-plaintext highlighter-rouge">['a', 'b', 'c'].indexOf(a) !== -1</code>。</p>
<p>注意这对指向同一个引用的对象同样适用。第二个参数是开始查询的起始位置。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = { foo: 'bar' }
var b = [a, 2]
console.log(b.indexOf(1))
// <- -1
console.log(b.indexOf({ foo: 'bar' }))
// <- -1
console.log(b.indexOf(a))
// <- 0
console.log(b.indexOf(a, 1))
// <- -1
b.indexOf(2, 1)
// <- 1
</code></pre></div></div>
<p>如果你想从后向前搜索,<code class="language-plaintext highlighter-rouge">.lastIndexOf</code>能派上用场。</p>
<h2 id="in操作符"><code class="language-plaintext highlighter-rouge">in</code>操作符</h2>
<p>在面试中新手容易犯的错误是混淆<code class="language-plaintext highlighter-rouge">.indexOf</code>和<code class="language-plaintext highlighter-rouge">in</code>操作符,如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = [1, 2, 5]
1 in a
// <- true, 但因为 2!
5 in a
// <- false
</code></pre></div></div>
<p>问题的关键是<code class="language-plaintext highlighter-rouge">in</code>操作符检索对象的键而非值。当然,这在性能上比<code class="language-plaintext highlighter-rouge">.indexOf</code>快得多。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = [3, 7, 6]
1 in a === !!a[1]
// <- true
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">in</code>操作符类似于将键值转换为布尔值。<code class="language-plaintext highlighter-rouge">!!</code>表达式通常被开发者用来双重取非一个值(转化为布尔值)。实际上相当于强制转换为布尔值,任何为真的值被转为<code class="language-plaintext highlighter-rouge">true</code>,任何为假的值被转换为<code class="language-plaintext highlighter-rouge">false</code>。</p>
<h2 id="走进reverse">走进<code class="language-plaintext highlighter-rouge">.reverse</code></h2>
<p>这方法将数组中的元素翻转并替换原来的元素。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = [1, 1, 7, 8]
a.reverse()
// [8, 7, 1, 1]
</code></pre></div></div>
<p>和复制不同的是,数组本身被更改。在以后的文章中我将展开对这些概念的理解,去看看如何创建一个库,如Underscore或Lo-Dash。</p>
<h2 id="相关文章">相关文章</h2>
<ul>
<li><a href="http://yanhaijing.com/javascript/2015/05/08/member-of-object/">细说JavaScript中对象的属性和方法</a></li>
</ul>
<h2 id="译者注">译者注</h2>
<p>本文为翻译文章,原文为“<a href="http://flippinawesome.org/2013/11/25/fun-with-javascript-native-array-functions/">Fun with JavaScript Native Array Functions</a>”。</p>
编写更好的CSS
2014-01-08T00:00:00+00:00
http://yanhaijing.com/css/2014/01/08/writing-better-css
<p>编写好的CSS代码能提升页面的渲染速度。本质上,一条规则都没有引擎解析的最快。MDN上将CSS选择符归拆分成四个主要类别,如下所示,性能依次降低。</p>
<ul>
<li>ID 规则</li>
<li>Class 规则</li>
<li>标签规则</li>
<li>通用规则</li>
</ul>
<p>对效率普遍认识是从Steve Souders在2009年出版的《高性能网站建设进阶指南》开始的,虽然Souders的书中罗列的非常详细,你可以在<a href="http://csswizardry.com/2011/09/writing-efficient-css-selectors/">这里</a>查看完整列表引用。你也可以在谷歌的<a href="https://developers.google.com/speed/docs/best-practices/rendering#UseEfficientCSSSelectors">高效的CSS选择器的最佳实践</a>中查看更多的细节。</p>
<p>本文我想分享一些我在编写高性能CSS中用到的简单的例子和指导方针。受MDN的编写高效的CSS指南的启发,并遵循类似的格式。</p>
<h2 id="避免过度约束">避免过度约束</h2>
<p>作为一般规则,不添加不必要的约束。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
ul#someid {..}
.menu#otherid{..}
// 好的
#someid {..}
#otherid {..}
</code></pre></div></div>
<h2 id="后代选择符最烂">后代选择符最烂</h2>
<p>不仅性能低下而且代码很脆弱,html代码和css代码严重耦合,html代码结构发生变化时,CSS也得修改,这是多么糟糕,特别是在大公司里,写html和css的往往不是同一个人。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 烂透了
html div tr td {..}
</code></pre></div></div>
<h2 id="避免链式交集选择符">避免链式(交集)选择符</h2>
<p>这和过度约束的情况类似,更明智的做法是简单的创建一个新的CSS类选择符。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
.menu.left.icon {..}
// 好的
.menu-left-icon {..}
</code></pre></div></div>
<h2 id="坚持kiss原则">坚持KISS原则</h2>
<p>想象我们有如下的DOM:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><ul id="navigator">
<li><a href="#" class="twitter">Twitter</a></li>
<li><a href="#" class="facebook">Facebook</a></li>
<li><a href="#" class="dribble">Dribbble</a></li>
</ul>
</code></pre></div></div>
<p>下面是对应的规则……</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
#navigator li a {..}
// 好的
#navigator {..}
</code></pre></div></div>
<h2 id="使用复合语法">使用复合语法</h2>
<p>尽可能使用复合语法。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
.someclass {
padding-top: 20px;
padding-bottom: 20px;
padding-left: 10px;
padding-right: 10px;
background: #000;
background-image: url(../imgs/carrot.png);
background-position: bottom;
background-repeat: repeat-x;
}
// 好的
.someclass {
padding: 20px 10px 20px 10px;
background: #000 url(../imgs/carrot.png) repeat-x bottom;
}
</code></pre></div></div>
<h2 id="避免不必要的命名空间">避免不必要的命名空间</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
.someclass table tr.otherclass td.somerule {..}
//好的
.someclass .otherclass td.somerule {..}
</code></pre></div></div>
<h2 id="避免不必要的重复">避免不必要的重复</h2>
<p>尽可能组合重复的规则。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
.someclass {
color: red;
background: blue;
font-size: 15px;
}
.otherclass {
color: red;
background: blue;
font-size: 15px;
}
// 好的
.someclass, .otherclass {
color: red;
background: blue;
font-size: 15px;
}
</code></pre></div></div>
<h2 id="尽可能精简规则">尽可能精简规则</h2>
<p>在上面规则的基础上,你可以进一步合并不同类里的重复的规则。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
.someclass {
color: red;
background: blue;
height: 150px;
width: 150px;
font-size: 16px;
}
.otherclass {
color: red;
background: blue;
height: 150px;
width: 150px;
font-size: 8px;
}
// 好的
.someclass, .otherclass {
color: red;
background: blue;
height: 150px;
width: 150px;
}
.someclass {
font-size: 16px;
}
.otherclass {
font-size: 8px;
}
</code></pre></div></div>
<h2 id="避免不明确的命名约定">避免不明确的命名约定</h2>
<p>最好使用表示语义的名字。一个好的CSS类名应描述它是什么而不是它像什么。</p>
<h2 id="避免-importants">避免 !importants</h2>
<p>其实你应该也可以使用其他优质的选择器。</p>
<h2 id="遵循一个标准的声明顺序">遵循一个标准的声明顺序</h2>
<p>虽然有一些排列CSS属性顺序<a href="http://css-tricks.com/new-poll-how-order-css-properties/">常见的方式</a>,下面是我遵循的一种流行方式。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.someclass {
/* Positioning */
/* Display & Box Model */
/* Background and typography styles */
/* Transitions */
/* Other */
}
</code></pre></div></div>
<h2 id="组织好的代码格式">组织好的代码格式</h2>
<p>代码的易读性和易维护性成正比。下面是我遵循的格式化方法。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
.someclass-a, .someclass-b, .someclass-c, .someclass-d {
...
}
// 好的
.someclass-a,
.someclass-b,
.someclass-c,
.someclass-d {
...
}
// 好的做法
.someclass {
background-image:
linear-gradient(#000, #ccc),
linear-gradient(#ccc, #ddd);
box-shadow:
2px 2px 2px #000,
1px 4px 1px 1px #ddd inset;
}
</code></pre></div></div>
<p>显然,这些只是极少数的规则,是我在我自己的CSS中,本着更高效和更易维护性而尝试遵循的规则。如果你想阅读更多的知识,我建议阅读MDN上的<a href="https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Writing_efficient_CSS">编写高效的CSS</a>和谷歌指南上的<a href="https://developers.google.com/speed/docs/best-practices/rendering#UseEfficientCSSSelectors">优化浏览器渲染</a>。</p>
<h2 id="译者注">译者注</h2>
<p>本文为译文,原文为“<a href="http://flippinawesome.org/2013/08/12/writing-better-css/">Writing Better CSS</a>”</p>
探索JavaScript中Null和Undefined的深渊
2014-01-05T00:00:00+00:00
http://yanhaijing.com/javascript/2014/01/05/exploring-the-abyss-of-null-and-undefined-in-javascript
<p>当讨论JavaScript中的原始数据类型时,大多数人都知道的基本知识,从String,Number到Boolean。这些原始类型相当简单,行为符合常识。但是,本文将更多聚焦独特的原始数据类型Null和Undefined,是什么让他们如此相似,却又似是而非。</p>
<h2 id="理解null和undefined">理解Null和Undefined</h2>
<p>在JavaScript中,<code class="language-plaintext highlighter-rouge">null</code>是字面量同时也是语言中的关键字,用来表示无法识别的对象值。换句话说,这用来表示“无值(no value)”。虽然相似,<code class="language-plaintext highlighter-rouge">undefined</code>实际上代表了不存在的值(non-existence of a value)。都是完全不可变的,没有属性和方法,也不能给其属性赋值。事实上,试图访问或定义一个属性将会引发一个类型错误(<code class="language-plaintext highlighter-rouge">TypeError</code>)。正如他们的名字暗示的那样,他们是完全无效的值。</p>
<p>没有值代表的布尔值是false,这意味着他们在条件上下文中会被被计算为false,如<code class="language-plaintext highlighter-rouge">if</code>语句。使用相等操作符( <code class="language-plaintext highlighter-rouge">==</code> )比较这两个值和其他false值,他们并不等于除了自己:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>null == 0; // false
undefined == ""; // false
null == false; // false
undefined == false; // false
null == undefined; // true
</code></pre></div></div>
<p>尽管如此,和其他相似之处,但<code class="language-plaintext highlighter-rouge">null</code>和<code class="language-plaintext highlighter-rouge">undefined</code>并不是等价的。每个作为其独特的类型的唯一成员,<code class="language-plaintext highlighter-rouge">undefined</code>是Undefined类型和<code class="language-plaintext highlighter-rouge">null</code>是Null类型。使用全等操作符(<code class="language-plaintext highlighter-rouge">===</code>)比较这两个值,这要求类型和值都相等,下面证明这一点:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>undefined === null; // false
</code></pre></div></div>
<p>这是一个重要的区别,服务于不同的目的和理由。区分这两个值,你可以认为<code class="language-plaintext highlighter-rouge">undefined</code>代表一个意想不到的没有值而<code class="language-plaintext highlighter-rouge">null</code>作为预期没有值的代表。</p>
<h2 id="产生undefined">产生Undefined</h2>
<p>有许多的方法产生一个<code class="language-plaintext highlighter-rouge">undefined</code>值的代码。它通常遇到当试图访问一个不存在的值时。在这种情况下,在JavaScript这种动态的弱类型语言中,只会默认返回一个<code class="language-plaintext highlighter-rouge">undefined</code>值,而不是上升为一个错误。
任何声明变量时没有提供一个初始值,都会有一个为<code class="language-plaintext highlighter-rouge">undefined</code>的默认值:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var foo; // 默认值为 undefined
</code></pre></div></div>
<p>当试图访问一个不存在的对象属性或数组项时,返回一个<code class="language-plaintext highlighter-rouge">undefined</code>值:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var array = [1, 2, 3];
var foo = array.foo; // foo 属性不存在, 返回 undefined
var item = array[5]; // 数组中没有索引为5的项,返回 undefined
</code></pre></div></div>
<p>如果省略了函数的返回语句,返回<code class="language-plaintext highlighter-rouge">undefined</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var value = (function(){})(); // 返回 undefined
</code></pre></div></div>
<p>函数调用时未提供的值结果将为<code class="language-plaintext highlighter-rouge">undefined</code>参数值:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function(undefined){
// 参数是 undefined
})();
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">void</code>操作符也可以返回一个<code class="language-plaintext highlighter-rouge">undefined</code>值。像Underscore的库使用它作为一个防御式的类型检查,因为它是不可变的,可以在任何上下文依赖返回<code class="language-plaintext highlighter-rouge">undefined</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function isUndefined(obj){
return obj === void 0;
}
</code></pre></div></div>
<p>最后,<code class="language-plaintext highlighter-rouge">undefined</code>是一个预定义的全局变量(不像<code class="language-plaintext highlighter-rouge">null</code>关键字)初始化为<code class="language-plaintext highlighter-rouge">undefined</code>值:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>'undefined' in window; // true
</code></pre></div></div>
<p>ECMAScript 5中,这个变量是只读的,以前并非如此。</p>
<h2 id="null的用例">Null的用例</h2>
<p>null的用例是使他与众不同的主要方面,因为不像<code class="language-plaintext highlighter-rouge">undefined</code>,<code class="language-plaintext highlighter-rouge">null</code>被认为是更有用。这正是为什么<code class="language-plaintext highlighter-rouge">typeof</code>操作符作用于<code class="language-plaintext highlighter-rouge">null</code>值时返回“object”。最初的理由是,现在仍然是,通常用作一个空引用一个空对象的预期,就像一个占位符。<code class="language-plaintext highlighter-rouge">typeof</code>的这种行为已经被确认为一个错误,虽然提出了修正,出于后兼容的目的,这一点已经保持不变。
这就是为什么JavaScript环境从来没有设置一个值为<code class="language-plaintext highlighter-rouge">null</code>;它必须以编程方式完成。正如文档MDN所说:</p>
<blockquote>
<p>在api中,<code class="language-plaintext highlighter-rouge">null</code>是经常检索对象的地方可以预期,但没有相关的对象。</p>
</blockquote>
<p>这适用于DOM,它是独立于语言的,不属于ECMAScript规范的范围。因为它是一个外部API,试图获取一个不存在的元素返回一个<code class="language-plaintext highlighter-rouge">null</code>值,而不是<code class="language-plaintext highlighter-rouge">undefined</code>。
一般来说,如果你需要给一个变量或属性指定一个不变值,将它传递给一个函数,或者从一个函数返回<code class="language-plaintext highlighter-rouge">null</code>,<code class="language-plaintext highlighter-rouge">null</code>几乎总是最好的选择。简而言之,JavaScript使用<code class="language-plaintext highlighter-rouge">undefined</code>并且程序员应该使用<code class="language-plaintext highlighter-rouge">null</code>。
<code class="language-plaintext highlighter-rouge">null</code>的另一个可行的用例,也被认为是良好的实践是一个显式指定变量为无效(<code class="language-plaintext highlighter-rouge">object= null</code>)当一个引用不再是必需的。通过分配<code class="language-plaintext highlighter-rouge">null</code>值,有效地清除引用,并假设对象没有引用其他代码,指定垃圾收集,确保回收内存。</p>
<h2 id="深入挖掘">深入挖掘</h2>
<p>使<code class="language-plaintext highlighter-rouge">null</code>和<code class="language-plaintext highlighter-rouge">undefined</code>像黑洞的不只是他们的行为,而是在他们在JavaScript环境的内部的处理方式。他们似乎通常并不具有同样的关联特征与其他原生或内置对象。
在ES5中,<code class="language-plaintext highlighter-rouge">Object.prototype.toString</code>方法,已经成为实际的类型检查标准,这在<code class="language-plaintext highlighter-rouge">null</code>和<code class="language-plaintext highlighter-rouge">undefined</code>中被证明是一致的:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Object.prototype.toString.call(null); // [object Null]
Object.prototype.toString.call(undefined); // [object Undefined]
</code></pre></div></div>
<p>然而,<code class="language-plaintext highlighter-rouge">Object.prototype.toString</code>方法实际上并不是检索<code class="language-plaintext highlighter-rouge">null</code>的内部<code class="language-plaintext highlighter-rouge">[[Class]]</code>属性或<code class="language-plaintext highlighter-rouge">undefined</code>的公开构造函数。根据文档,以下步骤发生在被调用过程中:</p>
<ol>
<li>如果值是<code class="language-plaintext highlighter-rouge">undefined</code>,返回“<code class="language-plaintext highlighter-rouge">[object Undefined]</code>”。</li>
<li>如果这个值为<code class="language-plaintext highlighter-rouge">null</code>,则返回“<code class="language-plaintext highlighter-rouge">[object Null]</code>”。</li>
<li>让O作为调用<code class="language-plaintext highlighter-rouge">ToObject</code>同时传递<code class="language-plaintext highlighter-rouge">this</code>值作为参数的结果值。</li>
<li>让class是O的内部属性<code class="language-plaintext highlighter-rouge">[[Class]]</code>的值。</li>
<li>返回的结果连接三个字符串“<code class="language-plaintext highlighter-rouge">[object </code>”,class,和“<code class="language-plaintext highlighter-rouge">]</code>”的结果的字符串值。</li>
</ol>
<p>该方法执行一个简单的字符串返回值,如果它检测到<code class="language-plaintext highlighter-rouge">null</code>或<code class="language-plaintext highlighter-rouge">undefined</code>和其他对象统一的功能。在整个规范中这是很常见的,因为当遇到<code class="language-plaintext highlighter-rouge">null</code>和<code class="language-plaintext highlighter-rouge">undefined</code>值时大多数方法包含一个简单的捕捉并返回。事实上,没有迹象表明他们包含与任何原生对象相关联的内部属性。就好像他们不是对象。我很想知道如果一个JavaScript的原生环境内部实际存在的显式方案会怎样?也许有人更熟悉一个可以参与的实现。</p>
<h2 id="结论">结论</h2>
<p>无论这些原生对象多么不寻常,理解<code class="language-plaintext highlighter-rouge">null</code>和<code class="language-plaintext highlighter-rouge">undefined</code>之间的差异,和他们在JavaScript的语言基础中截然不同的角色。它可能不能使你的应用程序有所突破,但是一般来说,它仅被证明在开发和调试中长期有益。</p>
<h2 id="译者注">译者注</h2>
<p>本文问翻译文章,原文为“<a href="http://flippinawesome.org/2013/12/09/exploring-the-abyss-of-null-and-undefined-in-javascript/">Exploring the Abyss of Null and Undefined in JavaScript</a>”</p>
<p>本文较难以理解,可参照原文便于理解。</p>
30个你必须记住的CSS选择符
2014-01-04T00:00:00+00:00
http://yanhaijing.com/css/2014/01/04/the-30-css-selectors-you-must-memorize
<p>所以你学会了基础的id,类和后代选择符,然后你就一直用它们了吗?如果是这样,你丢失了(css的)巨大的灵活性。在本文中提到的很多选择器属于CSS3规范的一部分,因此,只有在现代浏览器中才可使用。</p>
<h2 id="1">1.*</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>* {
margin: 0;
padding: 0;
}
</code></pre></div></div>
<p>对于初学者,在学习更多高级选择器之前,最先了解的选择器。</p>
<p>星号选择器将匹配页面里的每一个元素。很多开发者使用这个技巧将外边距和内边距重置为零。虽然在快速测试时这确实很好用,但我建议你永远不要再生产代码中使用它。它给浏览器带来大量不必要的负担。</p>
<p>* 也能作为子选择符使用。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#container * {
border: 1px solid black;
}
</code></pre></div></div>
<p>这将匹配<code class="language-plaintext highlighter-rouge">#container div</code>的每一个后代元素。再次强调,尽量不要使用这种技术。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/star.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE6+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="2x">2.#X</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#container {
width: 960px;
margin: auto;
}
</code></pre></div></div>
<p>井号前缀允许我们选择id。这是最常见的用法,不过应该慎重使用ID选择器。</p>
<blockquote>
<p>反复问自己:我一定需要id来匹配要选择的元素吗?</p>
</blockquote>
<p>id选择符是唯一的,不允许重复使用。如果可能的话,先尝试使用一个标签名称,一个新的HTML5元素,甚至是一个伪类。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/id.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE6+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="3-x">3. .X</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.error {
color: red;
}
</code></pre></div></div>
<p>现在介绍的是类选择符。id和类的不同之处在于后者可以多次使用。当你想给一组元素应用样式的时候可以使用类选择符。另外,当你紧想给特殊元素应用样式的时候才使用id。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/class.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE6+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="4-x-y">4. X Y</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>li a {
text-decoration: none;
}
</code></pre></div></div>
<p>下一个最常用的选择符是后代选择符。当你需要给你的选择符增加特殊性的时候你可以使用。例如,如果你只想匹配无序列表下的锚元素?此时后代选择符派上用场。</p>
<blockquote>
<p>小贴士——如果你的选择符看起来像这样 <code class="language-plaintext highlighter-rouge">X Y Z A B.error</code>,那你就错了。时刻问自己使用这高的权重是否有必要。</p>
</blockquote>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/descend.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE6+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="5-x">5. X</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a { color: red; }
ul { margin-left: 0; }
</code></pre></div></div>
<p>如果你想匹配页面上的所有的元素,根据他们的类型,而不是id或类名?显而易见,使用类型选择符。如果你需要选择所有的无序列表,请使用<code class="language-plaintext highlighter-rouge">ul {}</code>。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/tagName.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE6+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="6-xvisited-and-xlink">6. X:visited and X:link</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a:link { color: red; }
a:visted { color: purple; }
</code></pre></div></div>
<p>我们使用<code class="language-plaintext highlighter-rouge">:link</code> 伪类选择符选择所有已经被点击过的锚标签。</p>
<p>此外,我们也有<code class="language-plaintext highlighter-rouge">:visited</code>伪类选择符,正如你期望的,允许我们仅给页面上被点击过的或被访问过的锚标签应用样式。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/links.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE7+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="7-x--y">7. X + Y</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ul + p {
color: red;
}
</code></pre></div></div>
<p>这被称作相邻选择符。它将只选择紧贴在X元素之后Y元素。上面的例子,仅每一个<code class="language-plaintext highlighter-rouge">ul</code>之后的第一个段落元素的文本为红色。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/adjacent.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE7+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="8-x--y">8. X > Y</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>div#container > ul {
border: 1px solid black;
}
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">X Y</code>和<code class="language-plaintext highlighter-rouge">X > Y</code>之间的不同点是后者只选择直接子代。例如,考虑如下的标记。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div id="container">
<ul>
<li> List Item
<ul>
<li> Child </li>
</ul>
</li>
<li> List Item </li>
<li> List Item </li>
<li> List Item </li>
</ul>
</div>
</code></pre></div></div>
<p>选择符<code class="language-plaintext highlighter-rouge">#container > ul</code>将只选择id为container的div的直接子代ul。它不匹配更深层的li的子代的ul。</p>
<p>因此,使用子代选择符有性能上的优势。事实上,这同样适用于基于css选择器的javascript引擎。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/childcombinator.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE7+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="9-x--y">9. X ~ Y</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ul ~ p {
color: red;
}
</code></pre></div></div>
<p>这是兄弟选择符和<code class="language-plaintext highlighter-rouge">X + Y</code>一样,然而,它没有约束。与相邻选择符(<code class="language-plaintext highlighter-rouge">ul + li</code>)仅选择前一个选择符后面的第一个元素比起来,兄弟选择符更宽泛。它会选择,我们上面例子中跟在ul后面的任何p元素。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/generalcombinator.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE7+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="10-xtitle">10. X[title]</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a[title] {
color: green;
}
</code></pre></div></div>
<p>被称为属性选择器,在我们上面的例子里,这只会选择有title属性的锚标签。没有此属性的锚标签将不受影像。但如果你需要更多的特性怎么办呢?呵呵……</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/attributes.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE7+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="11-xhreffoo">11. X[href=”foo”]</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a[href="http://net.tutsplus.com"] {
color: #1f6053; /* nettuts green */
}
</code></pre></div></div>
<p>上面的代码段将给所有href属性为http://net.tutsplus.com的锚标签添加样式;他们将会显示为我们的品牌绿色。所有其他的锚标签将不受影响。</p>
<blockquote>
<p>注意我们将href值用引号包裹。记住,当使用javascript的css选择符引擎时也这么做。如果可能的话,尽可能使用css3选择符代替非官方的方法。</p>
</blockquote>
<p>这工作的很好,但有点不够灵活。如果链接确实直接连接到Nettus+还好,但是,也许路径是到nettust的相对路径呢?在这种情况下,我们可以使用一点正则表达式语法。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/attributes2.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE7+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="12-xhrefnettuts">12. X[href*=”nettuts”]</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a[href*="tuts"] {
color: #1f6053; /* nettuts green */
}
</code></pre></div></div>
<p>来了不是~这就是我们需要的代码。*号指定了包含该属性的值必须包含定义的值。就是说,这句代码包含了href值为 nettuts.com,net.tutsplus.com或者tutsplus.com。</p>
<p>记住这个描述过于宽泛,如果是某个锚点标签链接到某个连接中带有tuts非Envato的网站(tutsplus属于Envato旗下网站)呢?因此你需要更多特性来限制,分别使用^和&来限定字符串的开始和结束。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/attributes3.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE7+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="13-xhrefhttp">13. X[href^=”http”]</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a[href^="http"] {
background: url(path/to/external/icon.png) no-repeat;
padding-left: 10px;
}
</code></pre></div></div>
<p>有没有想过某些网站是如何定义一个图标的链接的?我确定你肯定看到过。这些链接很容易让你跳转到另一个网站。</p>
<p>使用^(carat)符灰常简单啦。这个符号常常在正则表达式中表示字符串的开始。如果我们想指向所有以”http”开头的”href”属性的锚点的话,我们就可以使用类似于上面的那段代码啦。</p>
<blockquote>
<p>注意啦,我们不需要搜索”http://”,完全没必要,因为我们还要包含以https://开头的链接呢。</p>
</blockquote>
<p>如果我们想为所有链接到图片的链接定义样式咋办?这种情况下,我们得搜索字符串的结束了不是。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/attributes4.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE7+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="14-xhrefjpg">14. X[href$=”.jpg”]</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a[href$=".jpg"] {
color: red;
}
</code></pre></div></div>
<p>又来了,我们还是使用正则表达式符号,<code class="language-plaintext highlighter-rouge">$(dolor)</code>,来作为字符串的结束标记。这种情况下,我们就会搜索所有url以.jpg为结尾的锚点啦。记住记住这种情况下gif和png格式的图片不会被选择哦。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/attributes5.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE7+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="15-xdata-foo">15. X[data-*=”foo”]</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a[data-filetype="image"] {
color: red;
}
</code></pre></div></div>
<p>回顾最近一条,我们如何能包含各种图片类型:png,jpeg,jpg,gif?很容易想到,我们能通过多个选择器来不是,像这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a[href$=".jpg"],
a[href$=".jpeg"],
a[href$=".png"],
a[href$=".gif"] {
color: red;
}
</code></pre></div></div>
<p>不过,这样很蛋疼,效率极低。另一个解决办法是使用自定义属性。如果我们加了一种自己的 <code class="language-plaintext highlighter-rouge">data-filetype</code> 属性给每一个链接到图片的锚点的话呢?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><a href="path/to/image.jpg" data-filetype="image"> Image Link </a>
</code></pre></div></div>
<p>这样关联后,我们就能使用标准的属性选择器来指定这些链接啦。看下面:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a[data-filetype="image"] {
color: red;
}
</code></pre></div></div>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/attributes6.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE7+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="16-xfoobar">16. X[foo~=”bar”]</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a[data-info~="external"] {
color: red;
}
a[data-info~="image"] {
border: 1px solid black;
}
</code></pre></div></div>
<p>这儿有个鲜为人知的特殊技巧,绝对让你印象时刻。<code class="language-plaintext highlighter-rouge">~(tilda)</code>符,它可以帮助我们指向那些以空格隔开多个值的属性的元素(真拗口,这翻译我自己都看不懂,水平问题)</p>
<p>以我们第15条的自定义属性为例,上面的代码中我们创建了 <code class="language-plaintext highlighter-rouge">data-info</code>属性,这个属性可以定义以空格分隔的多个值。这样,这个链接本身就是个icon,并且指向的也是一个图片链接,像下面这样。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><a href="path/to/image.jpg" data-info="external image"> Click Me, Fool </a>
</code></pre></div></div>
<p>有了这样适当的标记,通过使用 <code class="language-plaintext highlighter-rouge">~</code> 属性选择器的技巧,我们就可以指向任何具有着两种属性的任何一种啦。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/* Target data-info attr that contains the value "external" */
a[data-info~="external"] {
color: red;
}
/* And which contain the value "image" */
a[data-info~="image"] {
border: 1px solid black;
}
</code></pre></div></div>
<p>很棒,不是吗?</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/attributes7.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE7+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="17-xchecked">17. X:checked</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>input[type=radio]:checked {
border: 1px solid black;
}
</code></pre></div></div>
<p>这种伪类只会匹配已经被选中的单选元素。就是这么简单。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/checked.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>E9+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="18-xafter">18. X:after</h2>
<p><code class="language-plaintext highlighter-rouge">before</code> 和 <code class="language-plaintext highlighter-rouge">after</code> 伪元素也很蛋疼。貌似人们每天都能找到或者发明一些新办法来有效地使用它们。它们很容易控制选择器周围的内容。</p>
<p>很多第一次使用是因为他们需要对<code class="language-plaintext highlighter-rouge">clear-fix</code>进行改进。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.clearfix:after {
content: "";
display: block;
clear: both;
visibility: hidden;
font-size: 0;
height: 0;
}
.clearfix {
*display: inline-block;
_height: 1%;
}
</code></pre></div></div>
<p>这个改进使用了<code class="language-plaintext highlighter-rouge">:after</code>伪类元素来在元素后增加一个空间,然后清除它。这个牛X的技巧你应该收藏到工具包里,特别是当<code class="language-plaintext highlighter-rouge">overflow:hidden</code>方法无能为力的时候。</p>
<p>想看看其他创造性的用法:<a href="http://net.tutsplus.com/tutorials/html-css-techniques/quick-tip-getting-clever-with-css3-shadows/">访问我滴创建阴影的窍门</a></p>
<blockquote>
<p>通过Css3选择器的标准说明书,你应该有技巧地使用这些伪类语法——双冒号<code class="language-plaintext highlighter-rouge">::</code>。不过为了兼容,浏览器会接受一个双引号。</p>
</blockquote>
<p><strong>兼容性</strong></p>
<ul>
<li>IE8+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="19-xhover">19. X:hover</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>div:hover {
background: #e3e3e3;
}
</code></pre></div></div>
<p>我去,这个你必须懂。典型的官方形式的用户触发伪类。听起来会有点迷惑,不过实际上并非如此。想在用户在某个元素上面悬停时定义个特别的样式?这个属性就是做这个的。</p>
<blockquote>
<p>记住啦,较old版本的IE,只能在锚点标签后使用这个hover。</p>
</blockquote>
<p>这个选择器你用得最多的情况,估计可能就是在锚点的悬停时加个border-bottom啦。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a:hover {
border-bottom: 1px solid black;
}
</code></pre></div></div>
<blockquote>
<p>小贴士: <code class="language-plaintext highlighter-rouge">border-bottom:1px solid black;</code>比 <code class="language-plaintext highlighter-rouge">text-decoration:underline;</code>好看一点哦。(真的?我去)</p>
</blockquote>
<p><strong>兼容性</strong></p>
<ul>
<li>IE6+ (In IE6, :hover must be applied to an anchor element)</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="20-xnotselector">20. X:not(selector)</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>div:not(#container) {
color: blue;
}
</code></pre></div></div>
<p>not伪类灰常有用。例如我要选择所有的div,除了有id为container的。上面那个代码片段就能完美的实现。</p>
<p>如果我想选择除了p以外的所有元素,我可以这么做:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>*:not(p) {
color: green;
}
</code></pre></div></div>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/not.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE9+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="21-xpseudoelement">21. X::pseudoElement</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>p::first-line {
font-weight: bold;
font-size: 1.2em;
}
</code></pre></div></div>
<p>我们可以使用伪元素(以<code class="language-plaintext highlighter-rouge">::</code>为表示)来定义元素的样式。例如第一行,第一个字符,记住啦,这种方法只能应用于同级元素才有效。</p>
<blockquote>
<p>伪元素由两个冒号组成:<code class="language-plaintext highlighter-rouge">::</code></p>
</blockquote>
<p><strong>指定p的第一个字符的样式</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>p::first-letter {
float: left;
font-size: 2em;
font-weight: bold;
font-family: cursive;
padding-right: 2px;
}
</code></pre></div></div>
<p>这段代码会找到所有段落,然后再从中定义这些段落的第一个字符。</p>
<p>这常常使用在仿报纸的文章首字母样式。</p>
<p>**指定p的首行样式 **</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>p::first-line {
font-weight: bold;
font-size: 1.2em;
}
</code></pre></div></div>
<p>同样,这个<code class="language-plaintext highlighter-rouge">::first-line</code>伪元素会像我们期望的那样,只定义段落的第一行的样式。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/pseudoElements.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE6+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="22-xnth-childn">22. X:nth-child(n)</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>li:nth-child(3) {
color: red;
}
</code></pre></div></div>
<p>想想那些没法从元素堆中选择元素的日子。<code class="language-plaintext highlighter-rouge">nth-child</code>伪类解决了这个问题。</p>
<p>请注意 <code class="language-plaintext highlighter-rouge">nth-child</code>接收整数和变量,不过不是从0开始的,如果你想选定第二个li,使用 <code class="language-plaintext highlighter-rouge">li:nth-child(2)</code>.</p>
<p>我们甚至使用这个办法来选择任意的子元素。例如,我们可以用 <code class="language-plaintext highlighter-rouge">li:nth-child(4n)</code>来完成4为倍数的所有元素的选择。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/pseudoElements.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE9+</li>
<li>Firefox 3.5+</li>
<li>Chrome</li>
<li>Safari</li>
</ul>
<h2 id="23-xnth-last-childn">23. X:nth-last-child(n)</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>li:nth-last-child(2) {
color: red;
}
</code></pre></div></div>
<p>如果我有灰常多的li在ul里面,我只想要最后3个li怎么办?不必使用<code class="language-plaintext highlighter-rouge">li:nth-child(397)</code>,你可以使用<code class="language-plaintext highlighter-rouge">nth-last-child</code>伪类。</p>
<p>这种技巧和第六条几乎一样有效,不过两者的不同之处在于它从结束开始收集,用回溯的方式进行。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/nthLast.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE9+</li>
<li>Firefox 3.5+</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="24-xnth-of-typen">24. X:nth-of-type(n)</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ul:nth-of-type(3) {
border: 1px solid black;
}
</code></pre></div></div>
<p>你应该有很多时候想要元素类型来选择元素而不是通过孩子。</p>
<p>想象一下标记5个无序列表(UL)。如果你想定义第三个ul,并且没有一个唯一的id来找到它,你就可以使用 <code class="language-plaintext highlighter-rouge">nth-of-type(3)</code>伪类了。在上面这个代码段中,只有第三个ul才会有黑色的边框。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/nthOfType.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE9+</li>
<li>Firefox 3.5+</li>
<li>Chrome</li>
<li>Safari</li>
</ul>
<h2 id="25-xnth-last-of-typen">25. X:nth-last-of-type(n)</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ul:nth-last-of-type(3) {
border: 1px solid black;
}
</code></pre></div></div>
<p>没错,我们一样可以使用<code class="language-plaintext highlighter-rouge">nth-last-of-type</code>来从结束开始回溯这个选择器,来找到我们想要的元素</p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE9+</li>
<li>Firefox 3.5+</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="26-xfirst-child">26. X:first-child</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ul li:first-child {
border-top: none;
}
</code></pre></div></div>
<p>这个结构的伪类让我们可以选择某个元素的第一个子孩子。你通常可以使用这个办法来删除第一个或者最后一个元素的边框。</p>
<p>例如,你有一系列的rows,每一个都有<code class="language-plaintext highlighter-rouge">border-top</code> 和<code class="language-plaintext highlighter-rouge">border-bottom</code>。这种情况下,第一行和最后一行看起来会灰常怪。</p>
<p>很多设计师会使用first和last类来弥补这个缺陷。相反,你可以使用这些伪类来解决这些问题。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/firstChild.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE7+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="27-xlast-child">27. X:last-child</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ul > li:last-child {
color: green;
}
</code></pre></div></div>
<p>与<code class="language-plaintext highlighter-rouge">first-child</code>相反,<code class="language-plaintext highlighter-rouge">last-child</code>会选择父节点的最后一个子节点。</p>
<p><strong>例子:</strong></p>
<p>我们建立一些例子来示范这些类的可能的用法。我们会建立一种风格来展示。</p>
<p><strong>标记</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><ul>
<li> List Item </li>
<li> List Item </li>
<li> List Item </li>
</ul>
</code></pre></div></div>
<p>没啥特别的,就是一个简单的序列。</p>
<p><strong>Css</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ul {
width: 200px;
background: #292929;
color: white;
list-style: none;
padding-left: 0;
}
li {
padding: 10px;
border-bottom: 1px solid black;
border-top: 1px solid #3c3c3c;
}
</code></pre></div></div>
<p><img src="/blog/45.png" alt="" /></p>
<blockquote>
<p>这个样式会设置一个背景,删除浏览器默认的ul的padding值,并定义边框给每一个li来提供一点深度。</p>
</blockquote>
<p>如上图所示,唯一的问题是最上面的边框和最下面的边框看起来有点儿怪。让我们来使用<code class="language-plaintext highlighter-rouge">:first-child</code>和<code class="language-plaintext highlighter-rouge">:last-child</code>来解决这个问题。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>li:first-child {
border-top: none;
}
li:last-child {
border-bottom: none;
}
</code></pre></div></div>
<p><img src="/blog/46.png" alt="" /></p>
<p>看上图,解决了不是。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/firstChild.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE9+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<p><em>是滴,IE8支持 first-child 不过不支持last-child。</em></p>
<h2 id="28-xonly-child">28. X:only-child</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>div p:only-child {
color: red;
}
</code></pre></div></div>
<p>实话说,你很可能会发现你不会经常使用 <code class="language-plaintext highlighter-rouge">only-child</code>伪类。尽管如此,它确实有用,你应该需要它。</p>
<p>它可以帮助你选择是父节点的独生子(没别的孩子啦)的元素。例如,使用上面的代码,只有是div的唯一子孩子的p段落才会定义其颜色为red。</p>
<p>让我们来假定下面的标记。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div><p> My paragraph here. </p></div>
<div>
<p> Two paragraphs total. </p>
<p> Two paragraphs total. </p>
</div>
</code></pre></div></div>
<p>这样,第二个div的p标签的内容不会被选中。只有第一个div的p才会被选中。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/onlyChild.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE9+</li>
<li>Firefox</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="29-xonly-of-type">29. X:only-of-type</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>li:only-of-type {
font-weight: bold;
}
</code></pre></div></div>
<p>这种结构的伪类有几种灰常聪明的用法。它可以选定在其父节点内没有兄弟节点的元素。例如,我们可以选择只有一个li作为其子孩子的ul。</p>
<p>首先,取决于你想怎样完成这一目标。你可以使用 <code class="language-plaintext highlighter-rouge">ul li</code>,不过,这回选择所有li元素。唯一的办法是使用<code class="language-plaintext highlighter-rouge">only-of-type</code>。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ul > li:only-of-type {
font-weight: bold;
}
</code></pre></div></div>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/onlyOfType.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE9+</li>
<li>Firefox 3.5+</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="30-xfirst-of-type">30. X:first-of-type</h2>
<p><code class="language-plaintext highlighter-rouge">first-of-type</code> 伪类可以让你选择该类型的第一个兄弟节点。</p>
<p><strong>一个测试</strong></p>
<p>为了更好地理解它,让我们来测试一下啊。拷贝下面的标记到你的编辑器。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div>
<p> My paragraph here. </p>
<ul>
<li> List Item 1 </li>
<li> List Item 2 </li>
</ul>
<ul>
<li> List Item 3 </li>
<li> List Item 4 </li>
</ul>
</div>
</code></pre></div></div>
<p>现在,别急着往下读,试试看如何能只选择’LIST ITEM 2’?如果你搞定了(或者放弃了),继续读。</p>
<p><strong>解决办法1</strong></p>
<p>有很多办法能搞定这个测试。我们回顾其中一小部分。以使用<code class="language-plaintext highlighter-rouge">first-of-type</code>开始。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ul:first-of-type > li:nth-child(2) {
font-weight: bold;
}
</code></pre></div></div>
<p>这个代码段本质上表示,“找到第一个无序列表,然后找到这里面的li,然后,继续使用过滤器直到找到第二个li。</p>
<p><strong>解决办法2</strong></p>
<p>另一个可行的办法是毗邻选择器。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>p + ul li:last-child {
font-weight: bold;
}
</code></pre></div></div>
<p>在这个方案中,我们找到p的临近节点ul,然后找到ul的li的最后一个孩子。</p>
<p><strong>解决办法3</strong></p>
<p>我们可以随心所欲滴选择这些选择器。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ul:first-of-type li:nth-last-child(1) {
font-weight: bold;
}
</code></pre></div></div>
<p>这次,我们取到第一个ul,然后找到第一个元素,不过是从最后一个开始数。哈哈。</p>
<p><a href="http://cdn.tutsplus.com/net/uploads/legacy/840_cssSelectors/selectors/firstOfType.html">查看例子</a></p>
<p><strong>兼容性</strong></p>
<ul>
<li>IE9+</li>
<li>Firefox 3.5+</li>
<li>Chrome</li>
<li>Safari</li>
<li>Opera</li>
</ul>
<h2 id="结论">结论</h2>
<p>如果你仍在为解决old浏览器的缺陷而纠结,如IE6。在使用这些新的选择器方面,你仍然需要非常小心。不过,别因为这个阻碍了你对这些新玩意儿的学习。别虐待自己。确保关注这里的兼容性列表。应一方面,你可以使用 Dean Edward’s excellent IE9.js script 来为旧浏览器提供新的选择器支持。(我去。cool)</p>
<p>其次,当使用javascript库时,如流行的jQuery,最好尽可能使用这些css3本身的选择器而不是使用库的自定义方法/选择器。这能让你的代码更快哦,因为这些选择器引擎本身就能被浏览器解析,而不是用这些库选择器。</p>
<p>感谢阅读,希望你能学到一两个技巧。</p>
<h2 id="译者注">译者注</h2>
<p>本文问翻译文章,原文为“<a href="http://net.tutsplus.com/tutorials/html-css-techniques/the-30-css-selectors-you-must-memorize/">The 30 CSS Selectors you Must Memorize</a>”</p>
揭秘JavaScript中谜一样的this
2013-12-28T00:00:00+00:00
http://yanhaijing.com/javascript/2013/12/28/demystifying-this-in-javascript
<p>在这篇文章里我想阐明JavaScript中的this,希望对你理解this的工作机制有一些帮助。作为JavaScript程序员学习this对于你的发展有很大帮助,可以说利大于弊。这篇文章的灵感来自于我最近的工作——我即将完成的书的最后章节——JavaScript 应用程序设计(<a href="http://bevacqua.io/buildfirst">JavaScript Application Design</a>)(注意:现在你可以购买<a href="http://bevacqua.io/bf/book">早期版本</a>),我写的是关于scope工作原理的方面。</p>
<p>似是而非,这可能是你对this的感觉:</p>
<p><img src="/blog/48.gif" alt="" /></p>
<p>很疯狂,不是吗?在这篇短文,我旨在揭开它的神秘面纱。</p>
<h2 id="this的工作原理">this的工作原理</h2>
<p>如果一个函数被作为一个对象的方法调用,那么this将被指派为这个对象。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var parent = {
method: function () {
console.log(this);
}
};
parent.method();
// <- parent
</code></pre></div></div>
<p>注意这种行为非常“脆弱”,如果你获取一个方法的引用并且调用它,那么this的值不会是parent了,而是window全局对象。这让大多数开发者迷惑。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var parentless = parent.method;
parentless();
// <- Window
</code></pre></div></div>
<p>底线是你应该查看调用链去理解被调用函数是一个对象的属性还是它自己。如果它被作为属性调用,那么this的值将变成该属性的对象,否则this的值将被指派为全局对象或window。如果在严格模式下,this的值将是<code class="language-plaintext highlighter-rouge">undefined</code>。</p>
<p>在被当作构造函数的情况下,当使用new关键字时,this将被指派为被创建的实例对象。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function ThisClownCar () {
console.log(this);
}
new ThisClownCar();
// <- ThisClownCar {}
</code></pre></div></div>
<p>注意在这种情况下没有办法识别出一个函数是否应该被用作构造函数,从而省略new关键字this的结果将是全局对象,就像我们上面看到的没有用parent调用的例子。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ThisClownCar();
// <- Window
</code></pre></div></div>
<h2 id="篡改this">篡改this</h2>
<p><code class="language-plaintext highlighter-rouge">.call</code>,<code class="language-plaintext highlighter-rouge">.apply</code>和<code class="language-plaintext highlighter-rouge">.bind</code>方法被用来操作调用函数的方式,帮我们定义this的值和传递给函数的参数值。</p>
<p><code class="language-plaintext highlighter-rouge">Function.prototype.call</code> 可以有任意数量的参数,第一个参数被分配给this,剩下的被传递给调用函数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Array.prototype.slice.call([1, 2, 3], 1, 2)
// <- [2]
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Function.prototype.apply</code> 的行为和<code class="language-plaintext highlighter-rouge">.call</code>类似,但它传递给函数的参数是一个数组而不是任意参数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>String.prototype.split.apply('13.12.02', ['.'])
// <- ['13', '12', '02']
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Function.prototype.bind</code> 创建一个特殊的函数,该函数将永远使用传递给<code class="language-plaintext highlighter-rouge">.bind</code>的参数作为this的值,以及能够分配部分参数,创建原函数的珂里化(currided)版本。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var arr = [1, 2];
var add = Array.prototype.push.bind(arr, 3);
// effectively the same as arr.push(3)
add();
// effectively the same as arr.push(3, 4)
add(4);
console.log(arr);
// <- [1, 2, 3, 3, 4]
</code></pre></div></div>
<h2 id="作用域链中的this">作用域链中的this</h2>
<p>在下面的例子,this将无法在作用域链中保持不变。这是规则的缺陷,并且常常会给业余开发者带来困惑。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function scoping () {
console.log(this);
return function () {
console.log(this);
};
}
scoping()();
// <- Window
// <- Window
</code></pre></div></div>
<p>一个常见的方法是创建一个局部变量保持对this的引用,并且在子作用域中不能有同命变量。子作用域中的同名变量将覆盖父作用域中对this的引用。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function retaining () {
var self = this;
return function () {
console.log(self);
};
}
retaining()();
// <- Window
</code></pre></div></div>
<p>除非你真的想同时使用父作用域的this,以及当前this值,由于某些莫名其妙的原因,我更喜欢是使用的方法<code class="language-plaintext highlighter-rouge">.bind</code>函数。这可以用来将父作用域的this指定给子作用域。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function bound () {
return function () {
console.log(this);
}.bind(this);
}
bound()();
// <- Window
</code></pre></div></div>
<h2 id="其他问题">其他问题?</h2>
<p>你是否有任何关于this的问题?关于this怎样?请让我知道如果你认为我错过了任何其他边界情况或优雅的解决方案。</p>
<p>如果你喜欢这篇文章,一定要看看我即将到来的书JavaScript应用程序设计:构建第一种方法( <a href="http://bevacqua.io/buildfirst">JavaScript Application Design: A Build First Approach</a>),您可以访问购买<a href="http://bevacqua.io/bf/book">早期版本</a>的链接。</p>
<h2 id="译者注">译者注</h2>
<p>本文为翻译文章,原文“<a href="http://flippinawesome.org/2013/12/09/demystifying-this-in-javascript/">Demystifying this in JavaScript</a>”</p>
2013年JavaScript开发人员调查结果
2013-12-27T00:00:00+00:00
http://yanhaijing.com/javascript/2013/12/27/javascript-survey-results
<p>JavaScript开发人员调查现在已经结束,一如既往社区对结果进行了进一步分析:</p>
<ul>
<li><a href="http://dailyjs.com/files/2013-survey-summary.pdf">总结</a>(<a href="http://www.csdn.net/article/2013-12-16/2817820-javascript-survey-results">中文</a>)</li>
<li><a href="http://dailyjs.com/files/2013-survey-data.zip">原始数据(电子表格)</a></li>
<li><a href="http://dailyjs.com/2012/12/24/javascript-survey-results/">2012年结果</a></li>
</ul>
<p><img src="/blog/43.png" alt="" /></p>
<p>51%的被参与者写客户端代码,而28%的人说他们编写服务器端代码。去年客户端的占比是98%,所以我猜想,DailyJS起到了一定的积极作用,但有趣的是服务器端开发怎么发展的如此迅猛。</p>
<p>当被问到在哪里写javascript时,54%的人回答“工作”,45%的人回答“项目”。这可能一个人同时回答两种情况——我发现这对程序员是通用的,无论是专业人士还是业余爱好者。</p>
<p>大多数的参与者编写JavaScript已经有三到五年(34%)。我不禁觉得这得益于Node的增长——人们在经历使用其他的语言进行后台开发后,重新发现JavaScript的魅力,或者得益于前端框架的成长,如AngularJS和Backbone.js。我无法想象设计人员不具备JavaScript技巧。</p>
<p>78%的参与者说他们没有使用可以编译成JavaScript的语言(类似coffeescript)。我已经注意到Node社区的一些颇具影响力的成员对这些语言的声音,所以似乎看起来参与者同意。我在博客中尽量保持关于这些语言的一些报道,但总的来说重点是JavaScript。与其他语言不同,使用tab可以节省一点点尺寸,所以我不太介意使用哪种方法。</p>
<p>CoffeeScript 是最流行的“编译(conpile-to)”语言(64%),TypeScript从去年开始初见端倪(19%)。</p>
<p>代码的样式问题很混乱,结尾的分号,逗号和方法保留一个空格是最受欢迎的选择。有趣的是9%的人使用tab而11%的人使用空格。客户端开发者好像偏爱于四个空格,然而仅有8%的人选择此项。</p>
<p>测试的结果太好笑了:</p>
<ul>
<li>是:25%</li>
<li>否:26%</li>
<li>有时(Sometimes)/不总是(not enough)/不太多</li>
</ul>
<p>我喜欢你的诚实,“不总是”可能只是谦虚,所以我意识到“很多参与者仅写些例子,但他们觉得自己可以做的更好”。</p>
<p>Jasmine非常流行,大约占30%。我始终认为tap是最好的方法,但它只占2%。Mocha表现很好,占到27%,QUnit下降到16%。我认为这很能证明参加调查的很大一部分是Node开发者,但也可能是人们看到Mocha作为一个浏览器/Node模块的魅力,而且QUnit很多时候仅配合jQuery使用(这不一定是真的)。</p>
<p>CI服务?36%的人回答是。Node绝对是CI服务的好基友——我最近开始用TeamCity的服务器做objective - c项目并且这是令人吃惊的困难。和搭配Travis CI的开源Node项目比起来,这简直让人可笑。然而,Jenkins是最流行的CI服务(44%),TeamCity斩获(13%),所以也许人们发现跟踪客户端或Node检测很容易,在使用多种语言现有企业CI服务的帮助下。</p>
<p><img src="/blog/44.png" alt="" /></p>
<p>原来人们喜欢AMD!然而如果我们把CommonJS的结果分开来看,我们发现17%的人使用CommonJS而12%的人使用Browserify。很长一段时间我提倡CommonJS,但Browserify的理论很有说服力……</p>
<p>当看到AngularJS和Backbone.js在客户端框架中各占25%时,我很吃惊,他们有各自的内涵,但我不自觉的认为他们用来解决不同的问题。</p>
<p>一般常识认为支持IE似乎应该从IE 8开始(37%)。我猜那是公司的支持要求,这是已经存在十年之久的web开发人员心中的阵痛。</p>
<p>你使用ES6的特性吗?85%的人回答“不”,所以不过你没有使用,其实你一点也不糟糕。我可能仅仅写DailyJS文章的时候会用到,但我们将会在明年看到这种情况开始增长。</p>
<p>PHP是最受欢迎的主要开发语言(24%),c#有17%。你好.NET成员!</p>
<p>感谢大家参加调查!如果你可以用这些数据做一些很酷的事,我很想听听。</p>
<p><strong>译者注</strong></p>
<p>本文为翻译文章,原文为“<a href="http://dailyjs.com/2013/12/12/javascript-survey-results">JavaScript Developer Survey 2013: Results</a>”</p>
给HTML初学者的30条最佳实践
2013-12-23T00:00:00+00:00
http://yanhaijing.com/html/2013/12/23/30-html-best-practices-for-beginners
<p><a href="http://net.tutsplus.com/">Nettuts +</a>运营最困难的方面是为很多技能水平不同的用户提供服务。如果我们发布太多高级教程,我的新手用户将无法从中受益。相反也是如此。我们尽我们最大的努力,但如果你觉得你被忽略了请联系我们。这个网站是为你服务的,所以说出来!如此说来,今天的教程是专为那些刚刚进入web开发领域的人准备的。如果你的经验是一年或更少,希望在这里列出的一些技巧将帮助你成为更好、更高效的开发者!</p>
<p>闲话少说,让我们回顾三十个创建标记的最佳实践。</p>
<h2 id="1保持标签闭合">1.保持标签闭合</h2>
<p>以前,经常见到类似下面的代码(译注:这是多久以前啊……):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><li>Some text here.
<li>Some new text here.
<li>You get the idea.
</code></pre></div></div>
<p>注意外面包裹的UL/OL标签被遗漏了(谁知是故意还是无意的),而且还忘记了关闭LI标签。按今天的标准来看,这是很明显的糟糕做法,应该100%避免。总之,保持闭合标签。否则,你验证html标签的时候可能遇到问题。</p>
<p>更好的方式</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><ul>
<li>Some text here. </li>
<li>Some new text here. </li>
<li>You get the idea. </li>
</ul>
</code></pre></div></div>
<h2 id="2声明正确的文档类型">2.声明正确的文档类型</h2>
<p><img src="/blog/28.png" alt="声明正确的文档类型" /></p>
<p>笔者早先曾加入过许多CSS论坛,每当用户遇到问题,我们会建议他首先做两件事:</p>
<ol>
<li>
<p>验证CSS文件,保证没有错误。</p>
</li>
<li>
<p>确认添加了正确的doctype</p>
</li>
</ol>
<blockquote>
<p>DOCTYPE 出现在HTML标签之前,它告诉浏览器这个页面包含的是HTML,XHTML,还是两者混合,这样浏览器才能正确解析。</p>
</blockquote>
<p>通常有四种文档类型可供选择:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN” “http://www.w3.org/TR/html4/strict.dtd”>
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
</code></pre></div></div>
<p>关于该使用什么样的文档类型声明,一直有不同的说法。通常认为使用最严格的声明是最佳选择,但研究表明,大部分浏览器会使用普通的方式解析这种声明,所以很多人选择使用HTML4.01标准。选择声明的底线是,它是不是真的适合你,所以你要综合考虑来选择适合你得项目的声明。</p>
<h2 id="3永远不要使用内联样式">3.永远不要使用内联样式</h2>
<p>当你在埋头写代码时,可能会经常顺手或偷懒的加上一点行内css代码,就像这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><p style="color: red;">I'm going to make this text red so that it really stands out and makes people take notice! </p>
</code></pre></div></div>
<p>这样看起来即方便又没有问题。然而,这在你的编码实践中是个错误。</p>
<p>在你写代码时,在内容结构完成之前最好不要加入样式代码。</p>
<blockquote>
<p>这样的编码方式就像打游击,是一种很山寨的做法。——Chris Coyier</p>
</blockquote>
<p>更好的做法是,完成标签部分后,再把这个P的样式定义在外部样式表文件里。</p>
<p><strong>建议</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#someElement > p {
color: red;
}
</code></pre></div></div>
<h2 id="4将所有外部css文件放入head标签内">4.将所有外部css文件放入head标签内</h2>
<p>理论上讲,你可以在任何位置引入CSS样式表,但HTML规范建议在网页的head标记中引入,这样可以加快页面的渲染速度。</p>
<blockquote>
<p>雅虎的开发过程中,我们发现,在head标签中引入样式表,会加快网页加载速度,因为这样可以使页面逐步渲染。 —— ySlow团队</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><head>
<title>My Favorites Kinds of Corn</title>
<link rel="stylesheet" type="text/css" media="screen" href="path/to/file.css" />
<link rel="stylesheet" type="text/css" media="screen" href="path/to/anotherFile.css" />
</head>
</code></pre></div></div>
<h2 id="5javascript文件放在底部">5.javascript文件放在底部</h2>
<p><img src="/blog/29.png" alt="javascript文件放在底部" /></p>
<p>要记住一个原则,就是让页面以最快的速度呈现在用户面前。当加载一个脚本时,页面会暂停加载,直到脚本完全载入并执行完成。因此会浪费用户更多的时间。</p>
<p>如果你的JS文件只是要实现某些功能,(比如点击按钮事件),那就放心的在body底部引入它,这绝对是最佳的方法。</p>
<p><strong>建议</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><p>And now you know my favorite kinds of corn. </p>
<script type="text/javascript" src="path/to/file.js"></script>
<script type="text/javascript" src="path/to/anotherFile.js"></script>
</body>
</html>
</code></pre></div></div>
<h2 id="6永远不要使用内联javascript现在不是1996年了">6.永远不要使用内联javascript。现在不是1996年了!</h2>
<p>许多年以前,还存在一种这样的方式,就是直接将JS代码加入到HTML标签中。尤其是在简单的图片相册中非常常见。本质上讲,一个“onclick”事件是附加在 标签上的,其效果等同于一些JS代码。不需要讨论太多,非常不应该使用这样的方式,应该把代码转移到一个外部JS文件中,然后使用“ addEventListener / attachEvent ”加入事件监听器。或者使用<a href="http://jquery.com/">jquery</a>等框架,只需要使用“click”方法。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$('a#moreCornInfoLink').click(function() {
alert('Want to learn more about corn?');
});
</code></pre></div></div>
<h2 id="7边开发边验证">7.边开发,边验证</h2>
<p><img src="/blog/30.png" alt="" /></p>
<p>很多人并不真正理解标准验证的意义和价值,笔者在一篇博客中详细分析了这个问题。一句话,验证是为你服务的,不是给你找麻烦的。</p>
<p>如果你刚开始从事网页制作,那强烈建议你下载<a href="https://addons.mozilla.org/en-US/firefox/addon/60">Web Developer Toolbar</a>(chrome用户请自行在应用商店搜索,ie用户可能就杯具了) ,并在编码过程中随时使用”HTML标准验证”和“CSS标准验证”。如果你认为CSS是一种非常好学的语言,那么它会把你整的死去活来。你不严谨的代码会让你的页面漏洞百出,问题不断,一个好的方法就是—— 验证,验证,再验证。</p>
<h2 id="8下载firebug">8.下载firebug</h2>
<p><img src="/blog/31.png" alt="" /></p>
<p>Firebug是当之无愧的网页开发最佳插件,它不但可以调试JavaScript,还可以直观的让你了解页面标记的属性和位置。不用多说, <a href="https://addons.mozilla.org/en-US/firefox/addon/1843">下载</a>!</p>
<h2 id="9使用firebug">9.使用firebug</h2>
<p><img src="/blog/32.png" alt="" /></p>
<p>据笔者观察,大部分的使用者仅仅使用了Firebug 20%的功能,那真是太浪费了,你不妨花几个小时的时间来系统学习这个工具,相信会让你事半功倍。</p>
<p><strong>资源</strong></p>
<ul>
<li><a href="http://michaelsync.net/2007/09/08/firebug-tutorial-overview-of-firebug">Overview of Firebug</a></li>
<li><a href="http://www.digitalmediaminute.com/screencast/firebug-js/">Debug Javascript With Firebug – video tutorial</a></li>
</ul>
<h2 id="10保持标签名小写">10.保持标签名小写</h2>
<p>理论上讲,html不区分大小写,你可以随意书写,例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><DIV>
<P>Here's an interesting fact about corn. </P>
</DIV>
</code></pre></div></div>
<p>但最好不要这样写,费力气输入大些字母没有任何用处,并且会让代码很难看.</p>
<p><strong>建议</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div>
<p>Here's an interesting fact about corn. </p>
</div>
</code></pre></div></div>
<h2 id="11使用h1-h6标签">11.使用H1-H6标签</h2>
<p>笔者建议你在网页中使用其中全部六种标记,虽然大部分人只会用到前四个,但使用最多的H会有很多好处,比如设备友好、搜索引擎友好等,不妨把你的P标签都替换成H6。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><h1>This is a really important corn fact! </h1>
<h6>Small, but still significant corn fact goes here. </h6>
</code></pre></div></div>
<h2 id="12写博客时请将h1留给文章标题">12.写博客时,请将H1留给文章标题</h2>
<p><img src="/blog/33.jpg" alt="" /></p>
<p>今天笔者在<a href="http://www.twitter.com/nettuts">Twitter</a>上发起一次讨论:是该把H1定义到LOGO上还是定义到文章标题上,有80%的人选择了后者。</p>
<p>当然具体如何使用要看你的需求,但我建议你在建立博客的时候,将文章题目定为H1,这对搜索引擎优化(seo)是非常有好处的。</p>
<h2 id="13下载yslow">13.下载ySlow</h2>
<p><img src="/blog/34.png" alt="" /></p>
<p>在过去几年里,雅虎的团队在前端开发领域做了许多伟大的工作。前不久,它们发布了一个叫ySlow的Firebug扩展,它会分析你的网页,并返回 一个“成绩单”,上面细致分析了这个网页的方方面面,提出需要改进的地方,虽然它有点苛刻,但它绝对会对你有所帮助,强烈推荐—— <a href="http://developer.yahoo.com/yslow/">ySlow</a>!</p>
<h2 id="14使用无序列表ul包裹导航菜单">14.使用无序列表(UL)包裹导航菜单</h2>
<p><img src="/blog/35.png" alt="" /></p>
<p>通常网站都会有导航菜单,你可以用这样的方式定义:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div id="nav">
<a href="#">Home </a>
<a href="#">About </a>
<a href="#">Contact </a>
</div>
</code></pre></div></div>
<p>如果你想书写优美的代码,那最好不要用这种方式。</p>
<blockquote>
<p>为什么要用UL布局导航菜单? ——因为UL生来就是为定义列表准备的</p>
</blockquote>
<p>最好这样定义:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><ul id="nav">
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</code></pre></div></div>
<h2 id="15学习如何应对ie">15.学习如何应对IE</h2>
<p>IE一直以来都是前端开发人员的噩梦!</p>
<p>如果你的CSS样式表基本定型了,那么可以为IE单独建立一个样式表,然后这样仅对IE生效:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><!--[if lt IE 7]>
<link rel="stylesheet" type="text/css" media="screen" href="path/to/ie.css" />
<![endif]-->
</code></pre></div></div>
<p>这些代码的意思是:如果用户浏览器是IE6及以下,那这段代码才会生效。如果你想把IE7也包含进来,那么就把“[if lt IE 7]”改为“[if lte IE 7]”。</p>
<h2 id="16选择合适的ide">16.选择合适的IDE</h2>
<p><img src="/blog/36.png" alt="" /></p>
<p>不论你是Windows还是Mac用户,这里都有很多优秀的编辑器供你选择:</p>
<p>Mac 用户</p>
<ul>
<li><a href="http://www.panic.com/coda">Coda</a></li>
<li><a href="http://macrabbit.com/espresso/">Espresso</a></li>
<li><a href="http://macromates.com/">TextMate</a></li>
<li><a href="http://www.aptana.com/">Aptana</a></li>
<li>
<p><a href="http://www.adobe.com/products/dreamweaver/">DreamWeaver CS4</a>
PC 用户</p>
</li>
<li><a href="http://intype.info/home/index.php">InType</a></li>
<li><a href="http://www.e-texteditor.com/">E-Text Editor</a></li>
<li><a href="http://notepad-plus.sourceforge.net/uk/site.htm">Notepad++</a></li>
<li><a href="http://www.aptana.com/">Aptana</a></li>
<li><a href="http://www.adobe.com/products/dreamweaver/">Dreamweaver CS4</a></li>
</ul>
<h2 id="17上线前压缩代码">17.上线前,压缩代码</h2>
<p><img src="/blog/37.png" alt="" /></p>
<p>通过压缩您的CSS和Javascript文件,您可以减少总大小的25%左右,但在开发过程中不必压缩,然而,一旦网站完成后,利用一些网络压缩程序或多或少节省一些带宽。下面列出一些工具。</p>
<p><strong>Javascript 压缩服务</strong></p>
<ul>
<li><a href="http://javascriptcompressor.com/">Javascript Compressor</a></li>
<li><a href="http://www.xmlforasp.net/JSCompressor.aspx">JS Compressor</a></li>
</ul>
<p><strong>CSS Compression Services</strong></p>
<ul>
<li><a href="http://www.cssoptimiser.com/">CSS Optimiser</a></li>
<li><a href="http://www.cssdrive.com/index.php/main/csscompressor/">CSS Compressor</a></li>
<li><a href="http://www.cleancss.com/">Clean CSS</a></li>
</ul>
<h2 id="18精简精简在精简">18.精简,精简,在精简</h2>
<p><img src="/blog/38.jpg" alt="" /></p>
<p>回望我们大多数人写的第一个页面,一定会发现严重的 “DIV癖”( divitis ),通常初学者的本能就是把一个段落用DIV包起来,然后为了控制定位而套上更多的DIV。—— 其实这是一种低效而有害的做法。</p>
<blockquote>
<p>网页写完后,一定要多次回头检查,尽量的减少元素的数量。 能用UL布局的列表就不要用一个个的DIV去布局。</p>
</blockquote>
<p>正如写文章的关键是“缩减,缩减,缩减”一样,写页面也要遵循这个标准。</p>
<h2 id="19给所有图片加上alt属性">19.给所有图片加上“alt”属性</h2>
<p>为图片加上alt属性的好处是不言而喻的 —— 这样可以让禁用图片或者使用特殊设备的用户无障碍得了解你的王爷信息,并且对图像搜索引擎友好。</p>
<p><strong>糟糕的做法</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><IMG SRC="cornImage.jpg" />
</code></pre></div></div>
<p><strong>更好的做法</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><img src="cornImage.jpg" alt="A corn field I visited." />
</code></pre></div></div>
<h2 id="20通宵达旦">20.通宵达旦</h2>
<p>我经常不知不觉的学习工作到凌晨,我认为这是个很好的状况。</p>
<p>我的“啊~哈!”时间( “AH-HA” moments,指柳暗花明或豁然开朗的时刻)通常都发生在深夜,比如我彻底理解JavaScript的“闭包”概念,就是在这样一种情况下。如果你还没有感受过这种奇妙的时刻,那就马上试试吧!</p>
<h2 id="21查看源代码">21.查看源代码</h2>
<p><img src="/blog/39.png" alt="" /></p>
<p>没有什么比模仿你的偶像能让你更快的学习HTML。起初,我们都要甘做复印机,然后慢慢得发展自己的风格。研究你喜欢的网站页面代码,看看他们是怎么实现的。这是高手的必经之路,你一定要试一下。注意:只是学习和模仿他们的编码风格,而不是抄袭和照搬!</p>
<p>留意网络上各种炫酷的JavaScript效果,如果看上去是使用了插件,那根据它源码中head标签内的文件名,就可以找到这个插件名称,然后就可以学习它据为己用。</p>
<h2 id="22为所有的元素定义样式">22.为所有的元素定义样式</h2>
<p>这一条在你制作其他公司企业网站时尤为必要。你自己不使用blockquote标记?那用户可能会用,你自己不使用OL?用户也可能会。花时间做一个页面,显示出ul, ol, p, h1-h6, blockquotes, 等等元素的样式,检查一下是否有遗漏。</p>
<h2 id="23使用第三方服务">23.使用第三方服务</h2>
<p><img src="/blog/40.gif" alt="" /></p>
<p>现在互联网上流行着许多可以免费加在网页中的API,这些工具非常强大。它可以帮你实现许多巧妙的功能,更重要的是可以帮你宣传网站。</p>
<p>24.掌握Photoshop</p>
<p><img src="/blog/41.png" alt="" /></p>
<p>Photoshop是前端工程师的一个重要工具,如果你已经熟练掌握HTML和CSS,那不妨多学习一下Photshop。</p>
<ol>
<li>观看Psdtuts+上的<a href="http://psd.tutsplus.com/category/videos/">视频课程</a>。</li>
<li>花费每月25$注册成为<a href="http://www.lynda.com/">Lynda.com</a>的会员,海量精品课程。</li>
<li>推荐“<a href="http://www.mydamnchannel.com/You_Suck_at_Photoshop/Season_1/1DistortWarpandLayerEffects_1373.aspx">You Suck at Photoshop</a>”系列</li>
<li>花费几个小时记住尽可能多的PS快捷键。</li>
</ol>
<h2 id="25学习每一个html标签">25.学习每一个HTML标签</h2>
<p>虽然有些HTML标签很少用到,但你依然应该了解他们。比如“abbr”、“cite”等,你必须学习它们以备不时之需。</p>
<p>顺便说下,如果你不熟悉上面两个标签,可以看下下面的解释:</p>
<ul>
<li><strong>abbr</strong> 和你估计的差不多,它是abbreviation的缩写(英语差的估计不到),“Blvd”能用<code class="language-plaintext highlighter-rouge"><abbr></code>标签包裹,因为他是“boulevard”的缩写。(喜大普奔也可以喽)。</li>
<li><strong>cite</strong> 被用来作为引用内容的标题(blockquote)。例如,如果你在你的博客中引用本篇文章,你可以将“给HTML初学者的三十条最佳实践”用<code class="language-plaintext highlighter-rouge"><cite></code>包裹,注意它不应该被用来包裹引用的作者,这是常见的误区。</li>
</ul>
<h2 id="26参与社区讨论">26.参与社区讨论</h2>
<p>网络上有许许多多优秀的资源,而社区中也隐藏着许多高手,这里你既可以自学,也能请教经验丰富的开发者。</p>
<h2 id="27使用resetcss">27.使用reset.css</h2>
<p>Css Reset也就Reset Css ,就是重置一些HTML标签样式,或者说默认的样式。</p>
<p>关于是否应该使用CSS Reset,网上也有激烈的争论,笔者是建议使用的。你可以先选用一些成熟的CSS Reset,然后慢慢演变成适合自己的。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>html, body, div, span,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
img, ins, kbd, q, s, samp,
small, strike, strong,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-size: 100%;
vertical-align: baselinebaseline;
background: transparent;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
</code></pre></div></div>
<h2 id="28对齐元素">28.对齐元素</h2>
<p><img src="/blog/42.png" alt="" /></p>
<p>简单来说,你应该尽可能的对齐你的网页元素。可以观察一下你喜欢的网站,它们的LOGO、标题、图表、段落肯定是对得非常整齐的。否则就会显得混乱和不专业。</p>
<h2 id="29关于psd切片">29.关于PSD切片</h2>
<p><img src="/blog/43.jpg" alt="" /></p>
<p>现在你已经掌握了HTML、CSS、Photoshop知识,那么你还需要学习如何把PSD转换为网页上的图片和背景,下面有两个不错的教程:</p>
<ul>
<li><a href="http://net.tutsplus.com/videos/screencasts/slice-and-dice-that-psd/">Slice and Dice that PSD</a></li>
<li><a href="http://net.tutsplus.com/videos/screencasts/converting-a-design-from-psd-to-html/">From PSD to HTML/CSS</a></li>
</ul>
<h2 id="30不要随意使用框架">30.不要随意使用框架</h2>
<p>Javascript和CSS都有许多优秀的框架,但如果你是初学者,不要急于使用它们。如果你还没能熟练的驾驭CSS,使用框架会混淆你的知识体系。尽管你可能能会说javascript和jQuery是可以同事学习的,但这对css并不适合。我个人提倡960 CSS 网格框架,并且我经常使用它。还是那句话,如果你是css的初学者,学习框架只会让你更加困惑。</p>
<p>CSS框架是为熟练开发者设计的,这样会节省它们大量的时间。</p>
<h2 id="译者注">译者注</h2>
<p>这篇文章发表于2009年,弹指一挥间,4年时间已悄然溜走,文中有些知识已显得陈旧过时,但很多规则同样适用,下面是译者补充的关于已经过时的建议,如果你觉得哪里需要改进可以参与讨论。</p>
<p><strong>#1</strong></p>
<p>html5标准放宽了要求,允许标签不闭合,而且浏览器也能很好的修正这个问题,但这并不是你不闭合标签的理由,有时不闭合标签可能带来无法预知的错误。放宽标准其实是降低了开发的门槛,这本是web的真谛,人人可参与,其实xhtml规范要求页面有错误就停止渲染,这本身并不现实,而且浏览器也从来没有这么做过,毕竟用户宁愿看到有些错误的页面,也不愿看到白板一张。</p>
<p><strong>#8</strong></p>
<p>跨浏览器的firebug正在开发当中,然而发布之日却遥遥无期,chrome,safari,ie和opera都有自己的开发者工具,而且功能也不错,这里推荐chrome的开发者工具,大有后来者居上之风,其发展可谓一日千里,相信超越firebug指日可待。</p>
<p><strong>#9</strong></p>
<p><a href="http://www.ruanyifeng.com/blog/2008/06/firebug_tutorial.html">Firebug入门指南-阮一峰的博客</a></p>
<p><strong>#16</strong></p>
<p><a href="http://www.sublimetext.com/">sublime</a></p>
<p><strong>#20</strong></p>
<p>我以前也曾这样,确实学到不少知识,但自从看了曲黎敏副教授讲解的《黄帝内经》后,就不这样了,我也不建议你这样,11点是睡觉的最晚时间了</p>
<p><strong>#23</strong></p>
<p>英文原文标题为“使用twitter ”,这个在中国应该是微博么!!!</p>
<p><strong>#25</strong></p>
<p>address标签也容易被误用,你不见得知道哦!</p>
<p><strong>#27</strong></p>
<p>上面给出的代码可能已经更新,下面有地址,建议用normalize.css而非rerset.css</p>
<ul>
<li><a href="http://meyerweb.com/eric/tools/css/reset/">meyer 的 reset.css</a></li>
<li><a href="http://html5doctor.com/html-5-reset-stylesheet/">html5doctor的 reset.css</a></li>
<li><a href="http://necolas.github.io/normalize.css/">normalize.css</a></li>
</ul>
<p><strong>#30</strong></p>
<p>现在<a href="http://www.bootcss.com/">bootstrap</a>或来着居上,本人提倡这个框架,发展很猛,当然还有好多框架都很不错哦。</p>
<p><strong>#31</strong></p>
<p>本文为翻译文章原文 “<a href="http://net.tutsplus.com/tutorials/html-css-techniques/30-html-best-practices-for-beginners/">30 html best practices for beginner</a>“,本文为本人整理,<a href="http://www.bitscn.com/school/HTMLCSS/201002/181730.html">原译文</a>在此 译文,在原译文基础上有所改动。</p>
<p>本系列文章有三篇,令两篇如下:</p>
<ul>
<li><a href="http://yanhaijing.com/javascript/2013/12/11/24-JavaScript-best-practices-for-beginners">给javascript初学者的24条建议</a></li>
<li><a href="http://yanhaijing.com/jquery/2013/12/05/writing-better-jquery-code">高效jQuery的奥秘</a></li>
</ul>
给JavaScript初学者的24条最佳实践
2013-12-11T00:00:00+00:00
http://yanhaijing.com/javascript/2013/12/11/24-JavaScript-best-practices-for-beginners
<p>作为”<a href="http://yanhaijing.com/html/2013/12/23/30-html-best-practices-for-beginners/">30 HTML和CSS最佳实践</a>”的后续,本周,我们将回顾JavaScript的知识 !如果你看完了下面的内容,请务必让我们知道你掌握的小技巧!</p>
<h2 id="1使用--代替-">1.使用 === 代替 ==</h2>
<table>
<tbody>
<tr>
<td>JavaScript 使用2种不同的等值运算符:===</td>
<td>!== 和 ==</td>
<td>!=,在比较操作中使用前者是最佳实践。</td>
</tr>
</tbody>
</table>
<blockquote>
<p>“如果两边的操作数具有相同的类型和值,===返回true,!==返回false。”——JavaScript:语言精粹</p>
</blockquote>
<p>然而,当使用==和!=时,你可能会遇到类型不同的情况,这种情况下,操作数的类型会被强制转换成一样的再做比较,这可能不是你想要的结果。</p>
<h2 id="2eval邪恶">2.Eval=邪恶</h2>
<p>起初不太熟悉时,“eval”让我们能够访问JavaScript的编译器(译注:这看起来很强大)。从本质上讲,我们可以将字符串传递给eval作为参数,而执行它。</p>
<p>这不仅大幅降低脚本的性能(译注:JIT编译器无法预知字符串内容,而无法预编译和优化),而且这也会带来巨大的安全风险,因为这样付给要执行的文本太高的权限,避而远之。</p>
<h2 id="3省略未必省事">3.省略未必省事</h2>
<p>从技术上讲,你可以省略大多数花括号和分号。大多数浏览器都能正确理解下面的代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if(someVariableExists)
x = false
</code></pre></div></div>
<p>然后,如果像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if(someVariableExists)
x = false
anotherFunctionCall();
</code></pre></div></div>
<p>有人可能会认为上面的代码等价于下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if(someVariableExists) {
x = false;
anotherFunctionCall();
} 不幸的是,这种理解是错误的。实际上的意思如下:
if(someVariableExists) {
x = false;
}
anotherFunctionCall();
</code></pre></div></div>
<p>你可能注意到了,上面的缩进容易给人花括号的假象。无可非议,这是一种可怕的实践,应不惜一切代价避免。仅有一种情况下,即只有一行的时候,花括号是可以省略的,但这点是饱受争议的。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if(2 + 2 === 4) return 'nicely done';
</code></pre></div></div>
<p><strong>未雨绸缪</strong></p>
<p>很可能,有一天你需要在if语句块中添加更多的语句。这样的话,你必须重写这段代码。底线——省略是雷区。</p>
<h2 id="4使用jslint">4.使用JSLint</h2>
<p><a href="http://www.jslint.com/">JSLint</a>是由大名鼎鼎的<a href="http://www.crockford.com/">道格拉斯</a>(Douglas Crockford)编写的调试器。简单的将你的代码粘贴进JSLint中,它会迅速找出代码中明显的问题和错误。</p>
<blockquote>
<p>“JSLint扫面输入的源代码。如果发现一个问题,它返回一条描述问题和一个代码中的所在位置的消息。问题并不一定是语法错误,尽管通常是这样。JSLint还会查看一些编码风格和程序结构问题。这并不能保证你的程序是正确的。它只是提供了另一双帮助发现问题的眼睛。”——JSLing 文档</p>
</blockquote>
<p>部署脚本之前,运行JSLint,只是为了确保你没有做出任何愚蠢的错误。</p>
<h2 id="5将脚本放在页面的底部">5.将脚本放在页面的底部</h2>
<p>在本系列前面的文章里已经提到过这个技巧,我粘贴信息在这里。</p>
<p><img src="/blog/27.png" alt="html代码" /></p>
<p>记住——首要目标是让页面尽可能快的呈献给用户,脚本的夹在是阻塞的,脚本加载并执行完之前,浏览器不能继续渲染下面的内容。因此,用户将被迫等待更长时间。</p>
<p>如果你的js只是用来增强效果——例如,按钮的单击事件——马上将脚本放在body结束之前。这绝对是最佳实践。</p>
<p><strong>建议</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><p>And now you know my favorite kinds of corn. </p>
<script type="text/javascript" src="path/to/file.js"></script>
<script type="text/javascript" src="path/to/anotherFile.js"></script>
</body>
</html>
</code></pre></div></div>
<h2 id="6避免在for语句内声明变量">6.避免在For语句内声明变量</h2>
<p>当执行冗长的for语句时,要保持语句块的尽量简洁,例如:</p>
<p><strong>糟糕</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for(var i = 0; i < someArray.length; i++) {
var container = document.getElementById('container');
container.innerHtml += 'my number: ' + i;
console.log(i);
}
</code></pre></div></div>
<p>注意每次循环都要计算数组的长度,并且每次都要遍历dom查询“container”元素——效率严重地下!</p>
<p><strong>建议</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var container = document.getElementById('container');
for(var i = 0, len = someArray.length; i < len; i++) {
container.innerHtml += 'my number: ' + i;
console.log(i);
}
</code></pre></div></div>
<p>感兴趣可以思考如何继续优化上面的代码,欢迎留下评论大家分享。</p>
<h2 id="7构建字符串的最优方法">7.构建字符串的最优方法</h2>
<p>当你需要遍历数组或对象的时候,不要总想着“for”语句,要有创造性,总能找到更好的办法,例如,像下面这样。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var arr = ['item 1', 'item 2', 'item 3', ...];
var list = '<ul><li>' + arr.join('</li><li>') + '</li></ul>';
</code></pre></div></div>
<blockquote>
<p>我不是你心中神,但请你相信我(不信你自己测试)——这是迄今为止最快的方法!使用原生代码(如 join()),不管系统内部做了什么,通常比非原生快很多。——James Padolsey, james.padolsey.com</p>
</blockquote>
<h2 id="8减少全局变量">8.减少全局变量</h2>
<blockquote>
<p>只要把多个全局变量都整理在一个名称空间下,拟将显著降低与其他应用程序、组件或类库之间产生糟糕的相互影响的可能性。——Douglas Crockford</p>
</blockquote>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var name = 'Jeffrey';
var lastName = 'Way';
function doSomething() {...}
console.log(name); // Jeffrey -- 或 window.name
</code></pre></div></div>
<p><strong>更好的做法</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var DudeNameSpace = {
name : 'Jeffrey',
lastName : 'Way',
doSomething : function() {...}
}
console.log(DudeNameSpace.name); // Jeffrey
</code></pre></div></div>
<p>注:这里只是简单命名为 “DudeNameSpace”,实际当中要取更合理的名字。</p>
<h2 id="9给代码添加注释">9.给代码添加注释</h2>
<p>似乎没有必要,当请相信我,尽量给你的代码添加更合理的注释。当几个月后,重看你的项目,你可能记不清当初你的思路。或者,假如你的一位同事需要修改你的代码呢?总而言之,给代码添加注释是重要的部分。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 循环数组,输出每项名字(译者注:这样的注释似乎有点多余吧).
for(var i = 0, len = array.length; i < len; i++) {
console.log(array[i]);
}
</code></pre></div></div>
<h2 id="10拥抱渐进增强">10.拥抱渐进增强</h2>
<p>确保javascript被禁用的情况下能平稳退化。我们总是被这样的想法吸引,“大多数我的访客已经启用JavaScript,所以我不必担心。”然而,这是个很大的误区。</p>
<p>你可曾花费片刻查看下你漂亮的页面在javascript被关闭时是什么样的吗?(下载<a href="https://addons.mozilla.org/en-US/firefox/addon/60"> Web Developer</a> 工具就能很容易做到(译者注:chrome用户在应用商店里自行下载,ie用户在Internet选项中设置)),这有可能让你的网站支离破碎。作为一个经验法则,设计你的网站时假设JavaScript是被禁用的,然后,在此基础上,逐步增强你的网站。</p>
<h2 id="11不要给setinterval或settimeout传递字符串参数">11.不要给”setInterval”或”setTimeout”传递字符串参数</h2>
<p>考虑下面的代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setInterval(
"document.getElementById('container').innerHTML += 'My new number: ' + i", 3000
);
</code></pre></div></div>
<p>不仅效率低下,而且这种做法和”eval”如出一辙。从不给setInterval和setTimeout传递字符串作为参数,而是像下面这样传递函数名。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setInterval(someFunction, 3000);
</code></pre></div></div>
<h2 id="12不要使用with语句">12.不要使用”with”语句</h2>
<p>乍一看,”with”语句看起来像一个聪明的主意。基本理念是,它可以为访问深度嵌套对象提供缩写,例如……</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>with (being.person.man.bodyparts) {
arms = true;
legs = true;
}
</code></pre></div></div>
<p>而不是像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>being.person.man.bodyparts.arms = true;
being.person.man.bodyparts.legs= true;
</code></pre></div></div>
<p>不幸的是,经过测试后,发现这时“设置新成员时表现得非常糟糕。作为代替,您应该使用变量,像下面这样。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var o = being.person.man.bodyparts;
o.arms = true;
o.legs = true;
</code></pre></div></div>
<h2 id="13使用代替-new-ojbect">13.使用{}代替 new Ojbect()</h2>
<p>在JavaScript中创建对象的方法有多种。可能是传统的方法是使用”new”加构造函数,像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var o = new Object();
o.name = 'Jeffrey';
o.lastName = 'Way';
o.someFunction = function() {
console.log(this.name);
}
</code></pre></div></div>
<p>然而,这种方法的受到的诟病不及实际上多。作为代替,我建议你使用更健壮的对象字面量方法。</p>
<p><strong>更好的做法</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var o = {
name: 'Jeffrey',
lastName = 'Way',
someFunction : function() {
console.log(this.name);
}
};
</code></pre></div></div>
<p>注意,如果你只是想创建一个空对象,{}更好。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var o = {};
</code></pre></div></div>
<blockquote>
<p>“对象字面量使我们能够编写更具特色的代码,而且相对简单的多。不需要直接调用构造函数或维持传递给函数的参数的正确顺序,等”——dyn-web.com</p>
</blockquote>
<h2 id="14使用代替-new-array">14.使用[]代替 new Array()</h2>
<p>这同样适用于创建一个新的数组。</p>
<p>例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = new Array();
a[0] = "Joe";
a[1] = 'Plumber';
</code></pre></div></div>
<p><strong>更好的做法:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = ['Joe','Plumber'];
</code></pre></div></div>
<blockquote>
<p>“javascript程序中常见的错误是在需要对象的时候使用数组,而需要数组的时候却使用对象。规则很简单:当属性名是连续的整数时,你应该使用数组。否则,请使用对象”——Douglas Crockford</p>
</blockquote>
<h2 id="15定义多个变量时省略var关键字用逗号代替">15.定义多个变量时,省略var关键字,用逗号代替</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var someItem = 'some string';
var anotherItem = 'another string';
var oneMoreItem = 'one more string';
</code></pre></div></div>
<p><strong>更好的做法</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var someItem = 'some string',
anotherItem = 'another string',
oneMoreItem = 'one more string';
</code></pre></div></div>
<p>…应而不言自明。我怀疑这里真的有所提速,但它能是你的代码更清晰。</p>
<h2 id="17谨记不要省略分号">17.谨记,不要省略分号</h2>
<p>从技术上讲,大多数浏览器允许你省略分号。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var someItem = 'some string'
function doSomething() {
return 'something'
}
</code></pre></div></div>
<p>已经说过,这是一个非常糟糕的做法可能会导致更大的,难以发现的问题。</p>
<p><strong>更好的做法</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var someItem = 'some string';
function doSomething() {
return 'something';
}
</code></pre></div></div>
<h2 id="18for-in语句">18.”For in”语句</h2>
<p>当遍历对象的属性时,你可能会发现还会检索方法函数。为了解决这个问题,总在你的代码里包裹在一个if语句来过滤信息。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>for(key in object) {
if(object.hasOwnProperty(key) {
...then do something...
}
}
</code></pre></div></div>
<p>参考 JavaScript:语言精粹,道格拉斯(Douglas Crockford)。</p>
<h2 id="19使用firebug的timer功能优化你的代码">19.使用Firebug的”timer”功能优化你的代码</h2>
<p>在寻找一个快速、简单的方法来确定操作需要多长时间吗?使用Firebug的“timer”功能来记录结果。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function TimeTracker(){
console.time("MyTimer");
for(x=5000; x > 0; x--){}
console.timeEnd("MyTimer");
}
</code></pre></div></div>
<h2 id="20阅读阅读反复阅读">20.阅读,阅读,反复阅读</h2>
<p>虽然我是一个巨大的web开发博客的粉丝(像这样!),午餐之余或上床睡觉之前,实在没有什么比一本书更合适了,坚持放一本web开发方面书在你的床头柜。下面是一些我最喜爱的JavaScript书籍。</p>
<ul>
<li><a href="http://www.packtpub.com/object-oriented-javascript-applications-libraries/book">Object-Oriented JavaScript</a>(<a href="http://s.click.taobao.com/t?e=m%3D2%26s%3DRTedFwhY5ykcQipKwQzePOeEDrYVVa64K7Vc7tFgwiFRAdhuF14FMeupzlnrjcvD1aH1Hk3GeOiLbAnOMKhNmUKQIyb6a3ytbepvpTpVkkGwOnkIiEYnhxRRelJrXu3g">JavaScript面向对象编程指南</a> <a href="http://ishare.iask.sina.com.cn/f/18963277.html">pdf</a>)</li>
<li><a href="http://oreilly.com/catalog/9780596517748/">JavaScript:The Good Parts</a>(<a href="http://s.click.taobao.com/t?e=m%3D2%26s%3DR57sZUeY%2Bd8cQipKwQzePOeEDrYVVa64K7Vc7tFgwiFRAdhuF14FMTkkQUThmjGRt4hWD5k2kjOLbAnOMKhNmUKQIyb6a3ytQ3ve%2Fl8eVw5iZOsdwOXcw9FIO2YfYLJG">JavaScript语言精粹 修订版</a> <a href="http://pan.baidu.com/share/link?shareid=381250&uk=4043705155&fid=1352735570">pdf</a>)</li>
<li><a href="http://net.tutsplus.com/tutorials/JavaScript-ajax/24-JavaScript-best-practices-for-beginners/www.packtpub.com/learning-jquery-1.3/boo">Learning jQuery 1.3</a>(<a href="http://s.click.taobao.com/t?e=m%3D2%26s%3D4OykraCsZmQcQipKwQzePOeEDrYVVa64LKpWJ%2Bin0XJRAdhuF14FMbOLn3WkI14k1aH1Hk3GeOiLbAnOMKhNmUKQIyb6a3ytlvVWKrpGG8BlboWVDkT64ko1h03ocqnd">jQuery基础教程 第4版</a> <a href="http://download.csdn.net/detail/wang5yong1fei5/4631965">pdf</a>)</li>
<li><a href="http://oreilly.com/catalog/9780596527464/">Learning JavaScript</a>(<a href="http://s.click.taobao.com/t?e=m%3D2%26s%3DrfLwyl5bYj0cQipKwQzePOeEDrYVVa64K7Vc7tFgwiFRAdhuF14FMXFrLRwCQreut4hWD5k2kjOLbAnOMKhNmUKQIyb6a3ytK49eyT2J3GnLe30VI4uWF7UMVP8sn%2BEI">JavaScript学习指南</a> <a href="http://pan.baidu.com/share/link?shareid=180605&uk=839021066&fid=1732362737">pdf</a>)</li>
</ul>
<p>读了他们……多次。我仍将继续!</p>
<h2 id="21自执行函数">21.自执行函数</h2>
<p>和调用一个函数类似,它很简单的使一个函数在页面加载或父函数被调用时自动运行。简单的将你的函数用圆括号包裹起来,然后添加一个额外的设置,这本质上就是调用函数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(function doSomething() {
return {
name: 'jeff',
lastName: 'way'
};
})();
</code></pre></div></div>
<h2 id="22原生代码永远比库快">22.原生代码永远比库快</h2>
<p>JavaScript库,例如jQuery和Mootools等可以节省大量的编码时间,特别是AJAX操作。已经说过,总是记住,库永远不可能比原生JavaScript代码更快(假设你的代码正确)。</p>
<p>jQuery的“each”方法是伟大的循环,但使用原生”for”语句总是更快。</p>
<h2 id="23道格拉斯的-jsonparse">23.道格拉斯的 JSON.Parse</h2>
<p>尽管JavaScript 2(ES5)已经内置了JSON 解析器。但在撰写本文时,我们仍然需要自己实现(兼容性)。道格拉斯(Douglas Crockford),JSON之父,已经创建了一个你可以直接使用的解析器。这里可以下载(链接已坏,可以在这里查看相关信息<a href="http://www.json.org/">http://www.json.org/</a>)。</p>
<p>只需简单导入脚本,您将获得一个新的全局JSON对象,然后可以用来解析您的json文件。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var response = JSON.parse(xhr.responseText);
var container = document.getElementById('container');
for(var i = 0, len = response.length; i < len; i++) {
container.innerHTML += '<li>' + response[i].name + ' : ' + response[i].email + '</li>';
}
</code></pre></div></div>
<h2 id="24移除language属性">24.移除”language”属性</h2>
<p>曾经脚本标签中的“language”属性非常常见。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><script type="text/javascript" language="javascript">
...
</script>
</code></pre></div></div>
<p>然而,这个属性早已被弃用,所以请移除(译者注:html5 中已废弃,但如果你喜欢,你仍然可以添加)。</p>
<h2 id="就这样吧伙计">就这样吧,伙计</h2>
<p>现在你已经学到了,24条JavaScript初学者的必备技巧。让我知道你高效技巧吧!感谢你的阅读。本系列的第三部分主题会是什么呢(思索中)?</p>
<h2 id="译者补充">译者补充</h2>
<p>第三部分在这里:<a href="http://yanhaijing.com/jquery/2013/12/05/writing-better-jquery-code" title="高效jQuery的奥秘">高效jQuery的奥秘</a></p>
<p>本文为翻译文章,原文为“<a href="http://net.tutsplus.com/tutorials/JavaScript-ajax/24-JavaScript-best-practices-for-beginners/">24 JavaScript Best Practices for Beginners</a>”</p>
<p>关于#20 的补充,下面是译者认为的一些好书,有兴趣的读者可以留言讨论</p>
<ul>
<li>javascript模式(和上面JavaScript面向对象编程指南同一作者,这本书更好)</li>
<li><a href="http://s.click.taobao.com/t?e=m%3D2%26s%3D7JBKET3HVg8cQipKwQzePOeEDrYVVa64K7Vc7tFgwiFRAdhuF14FMbQfprq2TIqM79%2FTFaMDK6SLbAnOMKhNmUKQIyb6a3ytVWVB3vm4o2puVbPmyAmYxA781kxZh%2Btl">javascript设计模式</a></li>
<li><a href="http://s.click.taobao.com/t?e=m%3D2%26s%3D0%2Btb0f1ie9gcQipKwQzePOeEDrYVVa64K7Vc7tFgwiFRAdhuF14FMRoxJ4Y7%2BVMS1aH1Hk3GeOiLbAnOMKhNmUKQIyb6a3ytVWVB3vm4o2og0Jo0WqRSJ%2FJbfS%2Fcwass">编写可维护的javascript</a>(尼古拉斯新书)</li>
<li>高性能javascript(尼古拉斯 已绝版)</li>
<li>javascript语言精髓与编程实践</li>
<li><a href="http://s.click.taobao.com/t?e=m%3D2%26s%3D7WvGKcQEZmUcQipKwQzePOeEDrYVVa64LKpWJ%2Bin0XJRAdhuF14FMb3JbfLAgCyrJ1gyddu7kN%2BLbAnOMKhNmUKQIyb6a3ytlvVWKrpGG8Dh1g3lKEZJG1fcZDdkAJi1">javascript高级程序设计</a>(尼古拉斯)</li>
</ul>
css定位和大小代码段集锦
2013-12-06T00:00:00+00:00
http://yanhaijing.com/css/2013/12/06/css-positioning-and-size-of-the-code-segment
<p>css 有时候会很棘手,尤其是在定位和设置大小的时候。本文包含了我整理的一系列有用的片段,它们使我的生活变得更容易,希望也能帮到你们。</p>
<p>注:每个段落的下方有一个表,说明浏览器的支持情况。</p>
<h2 id="定位">定位</h2>
<h3 id="水平和垂直方向">水平和垂直方向</h3>
<p>动态调整大小的元素。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.parent { position: relative; }
.child {
position: absolute;
left: 50%;
top: 50%
-webkit-transform: translate(-50%, -50%);
-moz-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
-o-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
</code></pre></div></div>
<p><img src="/blog/17.png" alt="" /></p>
<p>固定大小的元素。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.parent { position: relative; }
.child {
position: absolute;
left: 50%;
top: 50%
height: 250px;
width: 500px;
/* Translate element based on it's size */
margin-left: -125px;
marign-top: -250px;
}
</code></pre></div></div>
<p><img src="/blog/18.png" alt="" /></p>
<p>随着百分比变化的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.parent { position: relative; }
.child {
position: absolute;
height: 50%;
width: 50%;
left: 25%; /* (100% - width) / 2 */
top: 25%; /* (100% - height) / 2 */
}
</code></pre></div></div>
<p><img src="/blog/19.png" alt="" /></p>
<h3 id="水平">水平</h3>
<p>块级元素的宽度值。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.block {
margin-left: auto;
margin-right: auto;
}
</code></pre></div></div>
<p><img src="/blog/20.png" alt="" /></p>
<p>内联和内联块元素</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.parent { text-align: center; }
.child { display: inline-block; }
</code></pre></div></div>
<p><img src="/blog/21.png" alt="" /></p>
<h3 id="垂直">垂直</h3>
<p>静态父元素中的内联和内联块元素</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.parent { line-height: 500px; }
.child {
display: inline-block;
vertical-align: middle;
}
</code></pre></div></div>
<p><img src="/blog/22.png" alt="" /></p>
<p>伪表格</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.parent { display: table; }
.child {
display: table-cell;
vertical-align: middle;
}
</code></pre></div></div>
<p><img src="/blog/23.png" alt="" /></p>
<h2 id="尺寸">尺寸</h2>
<p>下面创建一个全尺寸的块元素,但是因为有边框,内边距与外边距而没有成功。盒模型的属性使它没有成为预期的大小。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>html { min-height: 100%; }
body { height: 100%; }
.block {
height: 100%;
width: 100%;
-webkit-border-sizing: border-box;
-moz-border-sizing: border-box;
border-sizing: border-box;
}
</code></pre></div></div>
<p><img src="/blog/24.png" alt="" /></p>
<p>接下来的代码创建一个全尺寸的块元素为全屏幕,不依赖于边框和内边距。你可以为某个模块设定值来创建空间,比如标头。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>html { min-height: 100%; }
body { height: 100%; }
.center {
position: absolute; /* or fixed */
bottom: 0;
left: 0;
right: 0;
top: 0; /* top: 50px; would reserve 50px for an header */
}
</code></pre></div></div>
<p><img src="/blog/25.png" alt="" /></p>
<p>接下来我们创建一个绝对元素总是等于或大于视窗,基于文档的高度</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>html {
position: relative;
min-height: 100%;
}
body { height: 100%; }
.block {
min-height: 100%;
position: absolute;
}
</code></pre></div></div>
<p><img src="/blog/26.png" alt="" /></p>
<h2 id="结论">结论</h2>
<p>在这里讨论的所有方法几乎都可以通过嵌套来组合它们。你还知道其他很棒的技巧或有用的代码么?来这里分享吧!</p>
<p>注:本文为转载文章,感谢@<a href="http://weibo.com/wsluyu2011" title="散步的鱼的微博">鏾步的魚</a>的翻译,<a href="http://www.cnblogs.com/wsluyu/p/3461525.html">原文在这里</a></p>
高效jQuery的奥秘
2013-12-05T00:00:00+00:00
http://yanhaijing.com/jquery/2013/12/05/writing-better-jquery-code
<p>讨论jQuery和javascript性能的文章并不罕见。然而,本文我计划总结一些速度方面的技巧和我本人的一些建议,来提升你的jQuery和javascript代码。好的代码会带来速度的提升。快速渲染和响应意味着更好的用户体验。</p>
<p>首先,在脑子里牢牢记住jQuery就是javascript。这意味着我们应该采取相同的编码惯例,风格指南和最佳实践。</p>
<p>首先,如果你是一个javascript新手,我建议您阅读 《<a href="http://yanhaijing.com/javascript/2013/12/11/24-JavaScript-best-practices-for-beginners/">给JavaScript初学者的24条最佳实践</a>》 ,这是一篇高质量的javascript教程,接触jQuery之前最好先阅读。</p>
<p>当你准备使用jQuery,我强烈建议你遵循下面这些指南:</p>
<h2 id="缓存变量">缓存变量</h2>
<p>DOM遍历是昂贵的,所以尽量将会重用的元素缓存。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
h = $('#element').height();
$('#element').css('height',h-20);
// 建议
$element = $('#element');
h = $element.height();
$element.css('height',h-20);
</code></pre></div></div>
<h2 id="避免全局变量">避免全局变量</h2>
<p>jQuery与javascript一样,一般来说,最好确保你的变量在函数作用域内。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
$element = $('#element');
h = $element.height();
$element.css('height',h-20);
// 建议
var $element = $('#element');
var h = $element.height();
$element.css('height',h-20);
</code></pre></div></div>
<h2 id="使用匈牙利命名法">使用匈牙利命名法</h2>
<p>在变量前加$前缀,便于识别出jQuery对象。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
var first = $('#first');
var second = $('#second');
var value = $first.val();
// 建议 - 在jQuery对象前加$前缀
var $first = $('#first');
var $second = $('#second'),
var value = $first.val();
</code></pre></div></div>
<h2 id="使用-var-链单-var-模式">使用 Var 链(单 Var 模式)</h2>
<p>将多条var语句合并为一条语句,我建议将未赋值的变量放到后面。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var
$first = $('#first'),
$second = $('#second'),
value = $first.val(),
k = 3,
cookiestring = 'SOMECOOKIESPLEASE',
i,
j,
myArray = {};
</code></pre></div></div>
<h2 id="请使用on">请使用’On’</h2>
<p>在新版jQuery中,更短的 on(“click”) 用来取代类似 click() 这样的函数。在之前的版本中 on() 就是 bind()。自从jQuery 1.7版本后,on() 附加事件处理程序的首选方法。然而,出于一致性考虑,你可以简单的全部使用 on()方法。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
$first.click(function(){
$first.css('border','1px solid red');
$first.css('color','blue');
});
$first.hover(function(){
$first.css('border','1px solid red');
})
// 建议
$first.on('click',function(){
$first.css('border','1px solid red');
$first.css('color','blue');
})
$first.on('hover',function(){
$first.css('border','1px solid red');
})
</code></pre></div></div>
<h2 id="精简javascript">精简javascript</h2>
<p>一般来说,最好尽可能合并函数。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
$first.click(function(){
$first.css('border','1px solid red');
$first.css('color','blue');
});
// 建议
$first.on('click',function(){
$first.css({
'border':'1px solid red',
'color':'blue'
});
});
</code></pre></div></div>
<h2 id="链式操作">链式操作</h2>
<p>jQuery实现方法的链式操作是非常容易的。下面利用这一点。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
$second.html(value);
$second.on('click',function(){
alert('hello everybody');
});
$second.fadeIn('slow');
$second.animate({height:'120px'},500);
// 建议
$second.html(value);
$second.on('click',function(){
alert('hello everybody');
}).fadeIn('slow').animate({height:'120px'},500);
</code></pre></div></div>
<h2 id="维持代码的可读性">维持代码的可读性</h2>
<p>伴随着精简代码和使用链式的同时,可能带来代码的难以阅读。添加缩紧和换行能起到很好的效果。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
$second.html(value);
$second.on('click',function(){
alert('hello everybody');
}).fadeIn('slow').animate({height:'120px'},500);
// 建议
$second.html(value);
$second
.on('click',function(){ alert('hello everybody');})
.fadeIn('slow')
.animate({height:'120px'},500);
</code></pre></div></div>
<h2 id="选择短路求值">选择短路求值</h2>
<table>
<tbody>
<tr>
<td>短路求值是一个从左到右求值的表达式,用 &&(逻辑与)或</td>
<td> </td>
<td>(逻辑或)操作符。</td>
</tr>
</tbody>
</table>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
function initVar($myVar) {
if(!$myVar) {
$myVar = $('#selector');
}
}
// 建议
function initVar($myVar) {
$myVar = $myVar || $('#selector');
}
</code></pre></div></div>
<h2 id="选择捷径">选择捷径</h2>
<p>精简代码的其中一种方式是利用编码捷径。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
if(collection.length > 0){..}
// 建议
if(collection.length){..}
</code></pre></div></div>
<h2 id="繁重的操作中分离元素">繁重的操作中分离元素</h2>
<p>如果你打算对DOM元素做大量操作(连续设置多个属性或css样式),建议首先分离元素然后在添加。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
var
$container = $("#container"),
$containerLi = $("#container li"),
$element = null;
$element = $containerLi.first();
//... 许多复杂的操作
// better
var
$container = $("#container"),
$containerLi = $container.find("li"),
$element = null;
$element = $containerLi.first().detach();
//... 许多复杂的操作
$container.append($element);
</code></pre></div></div>
<h2 id="熟记技巧">熟记技巧</h2>
<p>你可能对使用jQuery中的方法缺少经验,一定要查看的文档,可能会有一个更好或更快的方法来使用它。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
$('#id').data(key,value);
// 建议 (高效)
$.data('#id',key,value);
</code></pre></div></div>
<h2 id="使用子查询缓存的父元素">使用子查询缓存的父元素</h2>
<p>正如前面所提到的,DOM遍历是一项昂贵的操作。典型做法是缓存父元素并在选择子元素时重用这些缓存元素。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
var
$container = $('#container'),
$containerLi = $('#container li'),
$containerLiSpan = $('#container li span');
// 建议 (高效)
var
$container = $('#container '),
$containerLi = $container.find('li'),
$containerLiSpan= $containerLi.find('span');
</code></pre></div></div>
<h2 id="避免通用选择符">避免通用选择符</h2>
<p>将通用选择符放到后代选择符中,性能非常糟糕。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
$('.container > *');
// 建议
$('.container').children();
</code></pre></div></div>
<h2 id="避免隐式通用选择符">避免隐式通用选择符</h2>
<p>通用选择符有时是隐式的,不容易发现。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
$('.someclass :radio');
// 建议
$('.someclass input:radio');
</code></pre></div></div>
<h2 id="优化选择符">优化选择符</h2>
<p>例如,Id选择符应该是唯一的,所以没有必要添加额外的选择符。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
$('div#myid');
$('div#footer a.myLink');
// 建议
$('#myid');
$('#footer .myLink');
</code></pre></div></div>
<h2 id="避免多个id选择符">避免多个ID选择符</h2>
<p>在此强调,ID 选择符应该是唯一的,不需要添加额外的选择符,更不需要多个后代ID选择符。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕
$('#outer #inner');
// 建议
$('#inner');
</code></pre></div></div>
<h2 id="坚持最新版本">坚持最新版本</h2>
<p>新版本通常更好:更轻量级,更高效。显然,你需要考虑你要支持的代码的兼容性。例如,2.0版本不支持ie 6/7/8。</p>
<h2 id="摒弃弃用方法">摒弃弃用方法</h2>
<p>关注每个新版本的废弃方法是非常重要的并尽量避免使用这些方法。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// 糟糕 - live 已经废弃
$('#stuff').live('click', function() {
console.log('hooray');
});
// 建议
$('#stuff').on('click', function() {
console.log('hooray');
});
// 注:此处可能不当,应为live能实现实时绑定,delegate或许更合适
</code></pre></div></div>
<h2 id="利用cdn">利用CDN</h2>
<p>谷歌的CND能保证选择离用户最近的缓存并迅速响应。(使用谷歌CND请自行搜索地址,此处地址以不能使用,推荐jquery官网提供的<a href="http://code.jquery.com/jquery-1.10.2.min.js">CDN</a>)。</p>
<h2 id="必要时组合jquery和javascript原生代码">必要时组合jQuery和javascript原生代码</h2>
<p>如上所述,jQuery就是javascript,这意味着用jQuery能做的事情,同样可以用原生代码来做。原生代码(或 <a href="http://vanilla-js.com/">vanilla</a>)的可读性和可维护性可能不如jQuery,而且代码更长。但也意味着更高效(通常更接近底层代码可读性越差,性能越高,例如:汇编,当然需要更强大的人才可以)。牢记没有任何框架能比原生代码更小,更轻,更高效(注:测试链接已失效,可上网搜索测试代码)。</p>
<p>鉴于vanilla 和 jQuery之间的性能差异,我强烈建议吸收两人的精华,使用(可能的话)和<a href="http://www.leebrimelow.com/native-methods-jQuery/">jQuery等价的原生代码</a>。</p>
<h2 id="最后忠告">最后忠告</h2>
<p>最后,我记录这篇文章的目的是提高jQuery的性能和其他一些好的建议。如果你想深入的研究对这个话题你会发现很多乐趣。记住,jQuery并非不可或缺,仅是一种选择。思考为什么要使用它。DOM操作?ajax?模版?css动画?还是选择符引擎?或许javascript微型框架或jQuery的定制版是更好的选择。</p>
<p>注:本文为翻译文章,原文为 “<a href="http://flippinawesome.org/2013/11/25/writing-better-jquery-code/">Writing Better jQuery Code</a>“。</p>
利用函数的惰性载入提高javascript代码性能
2013-10-16T00:00:00+00:00
http://yanhaijing.com/javascript/2013/10/16/function-of-the-inertia-load
<p>在 javascript 代码中,因为各浏览器之间的行为的差异,我们经常会在函数中包含了大量的 <code class="language-plaintext highlighter-rouge">if</code> 语句,以检查浏览器特性,解决不同浏览器的兼容问题。
例如,我们最常见的为 <code class="language-plaintext highlighter-rouge">dom</code> 节点添加事件的函数:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function addEvent (type, element, fun) {
if (element.addEventListener) {
element.addEventListener(type, fun, false);
}
else if(element.attachEvent){
element.attachEvent('on' + type, fun);
}
else{
element['on' + type] = fun;
}
}
</code></pre></div></div>
<p>每次调用 <code class="language-plaintext highlighter-rouge">addEvent</code> 函数的时候,它都要对浏览器所支持的能力进行检查,首先检查是否支持 <code class="language-plaintext highlighter-rouge">addEventListener</code> 方法,如果不支持,再检查是否支持 <code class="language-plaintext highlighter-rouge">attachEvent</code> 方法,如果还不支持,就用 dom 0 级的方法添加事件。
这个过程,在 <code class="language-plaintext highlighter-rouge">addEvent</code> 函数每次调用的时候都要走一遍,其实,如果浏览器支持其中的一种方法,那么他就会一直支持了,就没有必要再进行其他分支的检测了,
也就是说,<code class="language-plaintext highlighter-rouge">if</code> 语句不必每次都执行,代码可以运行的更快一些。</p>
<p>解决的方案就是称之为惰性载入的技巧。</p>
<p>所谓惰性载入,就是说函数的if分支只会执行一次,之后调用函数时,直接进入所支持的分支代码。
有两种实现惰性载入的方式,第一种事函数在第一次调用时,对函数本身进行二次处理,该函数会被覆盖为符合分支条件的函数,这样对原函数的调用就不用再经过执行的分支了,
我们可以用下面的方式使用惰性载入重写 <code class="language-plaintext highlighter-rouge">addEvent()</code>。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function addEvent (type, element, fun) {
if (element.addEventListener) {
addEvent = function (type, element, fun) {
element.addEventListener(type, fun, false);
}
}
else if(element.attachEvent){
addEvent = function (type, element, fun) {
element.attachEvent('on' + type, fun);
}
}
else{
addEvent = function (type, element, fun) {
element['on' + type] = fun;
}
}
return addEvent(type, element, fun);
}
</code></pre></div></div>
<p>在这个惰性载入的 <code class="language-plaintext highlighter-rouge">addEvent()</code> 中,<code class="language-plaintext highlighter-rouge">if</code> 语句的每个分支都会为 <code class="language-plaintext highlighter-rouge">addEvent</code> 变量赋值,有效覆盖了原函数。
最后一步便是调用了新赋函数。下一次调用 <code class="language-plaintext highlighter-rouge">addEvent()</code> 的时候,便会直接调用新赋值的函数,这样就不用再执行 <code class="language-plaintext highlighter-rouge">if</code> 语句了。</p>
<p>第二种实现惰性载入的方式是在声明函数时就指定适当的函数。
这样在第一次调用函数时就不会损失性能了,只在代码加载时会损失一点性能。
一下就是按照这一思路重写的 <code class="language-plaintext highlighter-rouge">addEvent()</code>。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var addEvent = (function () {
if (document.addEventListener) {
return function (type, element, fun) {
element.addEventListener(type, fun, false);
}
}
else if (document.attachEvent) {
return function (type, element, fun) {
element.attachEvent('on' + type, fun);
}
}
else {
return function (type, element, fun) {
element['on' + type] = fun;
}
}
})();
</code></pre></div></div>
<p>这个例子中使用的技巧是创建一个匿名的自执行函数,通过不同的分支以确定应该使用那个函数实现,实际的逻辑都一样,
不一样的地方就是使用了函数表达式(使用了 <code class="language-plaintext highlighter-rouge">var</code> 定义函数)和新增了一个匿名函数,另外每个分支都返回一个正确的函数,并立即将其赋值给变量 <code class="language-plaintext highlighter-rouge">addEvent</code>。</p>
<p>惰性载入函数的优点只执行一次 <code class="language-plaintext highlighter-rouge">if</code> 分支,避免了函数每次执行时候都要执行 <code class="language-plaintext highlighter-rouge">if</code> 分支和不必要的代码,因此提升了代码性能,至于那种方式更合适,就要看您的需求而定了。</p>
<h2 id="注">注</h2>
<p>本文为转载文章,原文:<a href="http://justjavac.com/javascript/2013/10/16/function-of-the-inertia-load.html">“利用函数的惰性载入提高javascript代码性能”</a></p>
Node.js 给前端带来了什么
2013-10-07T00:00:00+00:00
http://yanhaijing.com/nodejs/2013/10/07/node-js-and-the-new-web-front-end
<p>在软件开发领域,前端工程师曾经是一个比较纠结的职业。
在 Web 技术真正发展起来之前的相当长一段时间里,由于技术门槛很低,前端工程师行业一直是鱼龙混杂的状态。
其中很多号称是 Web 开发者的人实际上并没有什么专业的前端技能,有些工作就是被当做简单的力气活由美术设计师顺便做做而已。
当时很多人甚至并不认为有朝一日会有这么一群人用 HTML、CSS 和 JavaScript 这三门技术谋生——他们想,这怎么可能呢——这些技术看起来都是如此简单,
随随便便混在一起用就哦了,把做这些活看成一种正儿八经的职业简直是笑话。</p>
<p>随着技术发展,人们对前端工作的看法开始改变了,一些人从美工页面仔真正转变为专业的前端工程师。
JavaScript,这门很多工程师曾经把它当做玩具而不屑一顾的脚本语言悄然演变成推动互联网发展的核心驱动力。
伴随着越来越多的浏览器的出现使得用 HTML 和 CSS 兼容各种浏览器变得越来越难,于是能实现兼容各种浏览器的页面成为了前端工程师的金字招牌,前端职业开始变得炙手可热。</p>
<h2 id="两个独立的-ui-层">两个独立的 UI 层</h2>
<p>即使 Ajax 这种技术风靡全球之后,前端工程师的主要工作曾经也仅局限于浏览器窗口之内。
HTML、CSS 和 JavaScript 是前端工程师必须要掌握的三种核心技术,前端同后端的唯一交集仅仅是前端需要确保后端的数据能够以正确合适的格式输出到浏览器上。
在某种意义上来说,Web 开发有两个 UI 层,一个是在浏览器里面我们最终看到的,另一个在 server 端,负责生成和拼接页面。
因为传统前端基本上没有办法自主决定 server 端如何处理数据拼接生成页面,因为数据如何组织,往往是会受到后端工程师所选择的技术框架的影响的,而后端不理解前端的一些需求,
所以他们选择的时候也就很少会从前端方便的角度进行考虑。</p>
<p><img src="/blog/123.png" alt="" /></p>
<p>在上面这张图的结构里,浏览器里的UI层是完全归前端工程师管的。
服务器端的UI层则是前后端都关心的部分,剩下的部分是服务端的底层,诸如数据处理、缓存、权限控制和其他核心模块,这些是归后端管的。
我们还是回过头来看我们所关心的 server 端 UI 层吧,这一层所做的事情通常是拼页面模板以及处理具体的业务交互逻辑。</p>
<p>所以,传统的前后端分工是由前端负责浏览器,而其他浏览器之外的东东统统归由后端负责。
前后端的交集 server 的 UI 层也是由后端来主导的。
这是目前最主流的一种前后端分工方式。</p>
<h2 id="让-nodejs-来改变这一切">让 Node.js 来改变这一切</h2>
<p>Node.js 一发布,立刻在前端工程师中引起了轩然大波,前端工程师们几乎立刻对这一项技术表露出了相当大的热情和期待。
上一次一种技术能被整个前端界如此关注那还是在几年之前,那时候 Ajax 这个概念刚刚被提出来。
让 JavaScript 跑在 server 端,这个想法简直太棒了。
这下我们不用再去学那些什么 PHP 啦、Ruby 啦、Java 啦、Scala 啦或者其他什么对前端来说奇怪的语言,也可以轻松地将我们的领域扩展到 server 端,多么美好的前景!</p>
<p>我从来不是一个 PHP 的爱好者,但是我在 Yahoo 工作的时候,我不得不使用 PHP。
为了这份工作,我得忍受花费许多额外的时间去修复由于 PHP 的傻逼特性导致的坑。
对于一直使用 Java 作为服务端语言的我来说,对PHP实在是很难适应。
我相信,也一直坚持认为一种静态类型的语言更加适合用来构建你的业务逻辑的核心部分。
因此,虽然我很喜欢 JavaScript,但我也不会用它来做所有的事情,比如我绝对不会只用 JavaScript 来实现一个完整的购物车系统。</p>
<p>对于我来说,Node.js 不是一个解决一切问题的银弹,我不会用它来取代 server 端所有别的语言模块。
事实上,Node.js 可以做到其他后端语言所能做到的几乎所有的事情,但是我不会这么做。
我所认为的比较合适的做法是用 Node.js 来解决 server 端 UI 层的问题,这样我就可以将这一层从后端的其他部分剥离出来。</p>
<p><img src="/blog/124.png" alt="" /></p>
<p>现在越来越多的公司倾向于采用面向服务(service-oriented)的架构,由后端提供给前端 RESTful 的接口,这么做是为了更好的做前后端的依赖分离。
如果所有的关键业务逻辑都封装成 REST 调用,就意味着在上层只需要考虑如何用这些 REST 接口构建具体的应用。
那些后端程序员们根本不操心具体数据是如何从一个页面传递到另一个页面的,他们也不用管用户数据更新是通过 Ajax 异步获取的还是通过刷新页面,
当然他们更不关心的是你究竟在用 jQuery 还是 YUI——这与他们根本毫无关系嘛。
后端程序员真正应该关心的难道不应该是数据如何存储、如何容错以及如何保证安全性吗?</p>
<p>现在我们看看 Node.js 带来的好处吧,当后端程序员提供了 REST 服务之后,现在我们前端程序员可以使用 Node.js 来处理 server 端的UI层啦,
我们可以将通过 REST 调用拿到的数据随心所欲地进行处理,不管是渲染模板还是直接提供给 Ajax,现在我们仅仅用 JavaScript一种语言就可以轻松实现这些。
至于后端程序员,他们只需要保证数据的正确性,无论他们使用任何一种语言来封装 REST 调用,都不会对前端造成影响,这样前后端的职责不就被更好地划分了吗?
这样分工之后前端的领域就从浏览器小框框里面扩展到了 server 的 UI 层,而这一层本来对于后端来说是一件他们做起来不轻松的零碎活儿。</p>
<h2 id="不这太耸人听闻了">不!这太耸人听闻了!</h2>
<p>前端工程师想接手 server 的 UI 层是不那么容易被后端工程师们理解和接受的,因为这部分工作本来属于后端工程师的职责。
尤其是现在还有很多后端工程师认为 JavaScript 是一种极简单的“玩具语言”,他们将会想如此重要的服务端工作怎么能交给这群看起来不太靠谱的人用如此“不严肃”的语言来玩?
在我的经验里,这种观念上的冲突是前后端工程师们在是否引入 Node.js 这一问题上的最大分歧。
Server 端 UI 层是前后端的中间地带,而之前通常后端程序员们对这个地带比较有主导权,所以一旦你进入这个本来属于后端主导的领域,争议自然是不可避免的。</p>
<p>实际上放弃传统的角色立场,将 server 的 UI 层分给前端,在大型 Web 架构下是很有意义的。
不这么做的话,有时候前端想要从后端要到正确的数据,还不得不关心后端究竟是用什么语言实现的。
过去的分工中,那些原本属于核心业务底层考虑的东西会被暴露给 server 的 UI 层,而这些问题往往会不小心影响到前端。
前端本来不需要关注这些问题,因为前后端所关心的方面根本完全不一样嘛。
如果你理解单一职能、责任分离和模块化,你就会理解我所说的,甚至会觉得以前不把 server 的 UI 层分给前端实在是很笨。</p>
<p>只可惜,之前 Node.js 这样的东东不存在,所以当时没有前端合适的技术让前端工程师们自己搞定 server 的 UI 层。
于是后端的同学们用 PHP 的人就顺手把 UI 用 PHP 的模板实现了,同样的用 Java 的后端同学也自然而然地用 JSP 搞定这个问题。
这不是前端的同学不愿意去做 Server 的 UI,而是因为在之前,没有一种我们熟悉的技术让我们能够搞定这些事情,但是现在不一样了,我们有 Node.js 了。</p>
<h2 id="结论">结论</h2>
<p>我很喜欢 Node.js,我喜欢由这项技术给前端界带来的更大的发展潜力。
我并不认为整个后端完全用 Node.js 来实现会是一个很好的方案,尽管 Node.js 完全可以做到这一切。
我认为目前 Node.js 最大的价值是能让前端完全把控整个 UI 层,不论是浏览器的还是 Server 端的,做到这一点,我们工作的效率能得到很大的提升。
我们前端更擅长于决定数据以何种方式呈现能带给用户更好的体验,而后端则更加了解如何处理数据。
在这种新的分工方式下,后端只需要提供合适的数据操作接口,前端自己就能构建漂亮的、有效率的、可用性高的接口,从而实现用户所喜欢的各种交互。</p>
<p>使用 Node.js 来搞定 server 的 UI 层也将后端工程师从他们不擅长的领域解放了出来。
于是我们得到了一个 Web 开发的灵丹妙药:前后端之间只需要通过数据来交互,这种模型使得两方相互独立,各自都能够快速迭代开发,而只要保证数据接口不变,
前后端彼此之间就不会造成任何影响。</p>
<p>果断尝试一下吧,这个方案也许正适合你的团队。</p>
<h2 id="注">注</h2>
<p>原文:http://justjavac.com/nodejs/2013/10/07/node-js-and-the-new-web-front-end.html</p>
函数式 CSS
2013-09-02T00:00:00+00:00
http://yanhaijing.com/css/2013/09/02/functional-css-fcss
<p>在<a href="https://www.wealthfront.com/engineering" title="前端风暴">Wealthfront</a>我们是一个函数式编程的超级粉丝。强调不变性和函数式风格意味着更少的“意外”(surprises),因为副作用是有限的或不存在的。我们能将独立的组件迅速构建出大型系统,通过组合的方式组合组件。函数式编程原则直接应用在大多数语言中,即使他们不是被定义为函数式。同样适用非函数式的css。让我们看下我们最喜欢的(和最讨厌的)一些特性在样式语言中。</p>
<ul>
<li>一切都是全局作用域。</li>
<li>一切都是可变的。</li>
<li>优先级的计算,基于一些有趣的<a href="http://www.w3.org/TR/css3-selectors/#specificity" title="w3c css3 selectors">规则</a>。</li>
</ul>
<p>因此让我们讨论下我们能做什么。Wealthfront的 CSS(实际是 SCSS)风格指南概述一些经验法则,让我们在CSS中获得函数式编程范式的效益。确切的说,指南通过限制副作用减少意外,提倡组合使我们的样式表更具伸缩性。在本文中,我将介绍一些我们的样式指南中的主要的规则。</p>
<h2 id="引入作用域">引入作用域</h2>
<p>大部分语言中,变量定义被限制在它们的作用域内。在Javascript中,变量的作用域是他们所在的函数,而其他语言中,如Java变量具有块级作用域。我在我的作用域里定义的变量不能被其他人在我的作用域外重定义或修改。</p>
<p>作用域是防御式编程和降低副作用的重要部分。如果您的规则、函数或变量只存在于一个受限的作用域,那么你可以放心没人会更改它,无论是有意还是无意的。</p>
<p>CSS没有作用域。样式定义时可能意外重写其他规则,而且无法保证你挑选的样式规则名称没被其他人使用。它可能在完全不同的文件里,并且是深度嵌套的选择符。假如你问你的样式规则选择了一个相同的名字,则意外重写其他人规则的概率骤然升高。让我们思考下面的例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/* profile.css */
.error { color: orange; }
.success { color: blue; }
/* signup.css */
.error { color: red; }
.success { color: green; }
</code></pre></div></div>
<p>假如我们在我们的HTML中引入上面的CSS文件,我们无意中用其中一个样式重写(覆盖)了另一个。随着网站的增长,这种状况变得越来越复杂和普遍。</p>
<p>那么我们如何在CSS中引入作用域呢?</p>
<p>在CSS中模拟更细粒度的作用域的安全做法是用命名约定。在这种情况下,我们为我们所有的样式规则设置命名空间通过添加一个前缀。前缀命名空间规则不是一个新的理念,但重要的是我们知道我们为什么那样做。在前缀的样式中我们创造了我们的“作用域”,你可以说我们的css是在“prefix”作用域内——我们的样式只在设置了前缀的规则中存在。</p>
<p>让我们用前缀符尝试修改前面的例子:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/* profile.css */
.profile-error { color: orange; }
.profile-success { color: blue; }
/* signup.css */
.signup-error { color: red; }
.signup-success { color: green; }
</code></pre></div></div>
<p>加前缀允许我们封装我们的规则,保护它们免受修改。有了这些附加的前缀,我们的规则不再冲突。我们使他们免受副作用,通过声明我们的规则在命名空间作用域中。</p>
<h2 id="减小依赖增大可重用性">减小依赖,增大可重用性</h2>
<p>通过使用复杂的选择符来保持我们的标记整洁和无类是很容易的。我们都见过像下面这样的CSS:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.whitepaper-link {
font-weight: bold;
font-size:12px;
}
.main-nav .whitepaper-link {
font-size:16px;
}
.main-footer .whitepaper-link {
font-size:9px;
}
</code></pre></div></div>
<p>但如果我们想在其他地方有小号的.whitepaper-link呢?上面这样的嵌套选择符我们在我们的样式中强制了DOM结构(样式规则和DOM结构紧紧耦合在一起)。这就是说”你只能在main-footer中有一个小的whitepaper链接“。在CSS规则中强制结构阻止我们重用样式,并且将我们数据的表现和它在结构中的表现混合在一起(结构和表现紧耦合)。当我们通过嵌套的选择符强制结构时,我们在它们间创造了依赖。在软件工程的任何区域管理依赖都是令人头痛和容易出错的。我们应该避免。</p>
<p>代替强制结构,让我们像下面这样定义它:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.whitepaper-link {
font-weight: bold;
font-size:12px;
}
.whitepaper-link-large {
font-size:16px;
}
.whitepaper-link-small {
font-size:9px;
}
</code></pre></div></div>
<p>我们的标记能添加 .whitepaper-link 和 .whitepaper-link-small 类到页脚元素来实现和旧版中一样的效果。现在我们能复用 “small”样式到站点中的任何其他元素,无论它是否在footer元素内。我们在这真正见到的是组合的魔力,我们将在一分钟后讨论。</p>
<h2 id="避免突变性">避免突变性</h2>
<p>在CSS中重写样式规则并非是罕见的(恰恰相反)。例如,你可能想让一个错误消息有不同的效果如果它在侧边栏里。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/* errors.css.scss */
.error { color: red; }
.sidebar .error { border:1px solid red; }
</code></pre></div></div>
<p>这是意大利面条式代码的东西。这不是与一群工程师使用全局变量类似。一些工程师在他们的代码中将重定义变量(样式),然而其他人仍然期望它(变量)保持原来的定义。这 .error 样式变得不再安全,无法知道它在给定的上下文中的确切行为。样式规则变得充满意外,然而我们讨厌意外。</p>
<p>解决方案是从不重写一个已经定义的样式规则。如果你把规则当成不可更改的——那意味着,它们是一成不变的,定义之后也永远无法改变它们——你可以避免许多由全局变量和不稳定变量引起的问题。</p>
<p>我们可以通过组合实现这些,无论我们通过元素的类属性或通过Sass的@extend 指令。</p>
<h2 id="组合是你的福音">组合是你的福音</h2>
<p>让我们看下我们如何用我们上面描述的来完成例子。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/* errors.css.scss */
.error { color: red; }
.sidebar-error { border:1px solid red; }
\<!-- example.html -->
\<div class="error sidebar-error">Oh no!</div>
</code></pre></div></div>
<p>我们没有重定义 .error 规则,而是给我们的 error div增加一个新的规则来增强它。我们的 error div 的表现将是 .error 和 .sidebar-error的组合。</p>
<p>这仍然让人有点迷惑,我们没有重写 .error 规则自己,但我们重写他的一个属性。如果你正在使用 Sass,更富有表现力的在你的样式中用SCSS方式定义组合是通过@extend指令。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/*errors.css.scss*/
.error { color: red; }
.sidebar-error {
@extend .error;
border:1px solid red;
}
example.html
<!-- example.html -->
<div class="sidebar-error">Oh no!</div>
</code></pre></div></div>
<p>现在我们的标记保持苗条,并且没有给人错误的印象,它看起来应该像 .error。任何浏览错误样式表的开发者将看到 .slidebar-error是 .error 外加一个边扩展。他们能自信的使用 .error 因为它从没被重定义,并且我们仍将有我们自定义的 .sidebar-error 表现。</p>
<h2 id="fcss">FCSS</h2>
<p><a href="https://www.wealthfront.com/engineering" title="前端风暴">Wealthfront</a>有一些更多的规则,我们在本文中没有讨论。但他们都融入到整个我们讨论过的指导原则中。例如,我们避免元素和过多伪类及伪对象选择符,因为他们和DOM结构紧耦合,并且我们更喜欢类选择符替代id选择符来提高可重用性(ID 选择符被用来非常特殊的,单个元素重写或样式)。</p>
<p>在此强调:</p>
<ul>
<li>通过前缀为你的类增加命名空间,增加伪作用域和减少意外。</li>
<li>不要通过多次定义重写样式中的规则,而是用组合。</li>
<li>不要用嵌套,元素选择符或过度的伪类及伪对象选择符——他们将你的CSS和DOM结构仅仅绑定在一起。</li>
<li>每一个团队需要自己的样式指南在缺少约束的语言中强制约束。如果运气好,我们的“函数式”方法将带给你一些灵感。</li>
</ul>
<p>注:本文为翻译文章,原文为 <a href="http://flippinawesome.org/2013/08/26/functional-css-fcss/#comment-3181" title="Functional CSS (FCSS)">Functional CSS (FCSS)</a>。</p>
认识javascript中的作用域和上下文
2013-08-30T00:00:00+00:00
http://yanhaijing.com/javascript/2013/08/30/understanding-scope-and-context-in-javascript
<p>javascript中的作用域(scope)和上下文(context)是这门语言的独到之处,这部分归功于他们带来的灵活性。每个函数有不同的变量上下文和作用域。这些概念是javascript中一些强大的设计模式的后盾。然而这也给开发人员带来很大困惑。下面全面揭示了javascript中的上下文和作用域的不同,以及各种设计模式如何使用他们。</p>
<h2 id="上下文-vs-作用域">上下文 vs 作用域</h2>
<p>首先需要澄清的问题是上下文和作用域是不同的概念。多年来我注意到许多开发者经常将这两个术语混淆,错误的将一个描述为另一个。平心而论,这些术语变得非常混乱不堪。</p>
<p>每个函数调用都有与之相关的作用域和上下文。从根本上说,范围是基于函数(function-based)而上下文是基于对象(object-based)。换句话说,作用域是和每次函数调用时变量的访问有关,并且每次调用都是独立的。上下文总是关键字 this 的值,是调用当前可执行代码的对象的引用。</p>
<h2 id="变量作用域">变量作用域</h2>
<p>变量能够被定义在局部或者全局作用域,这导致运行时变量的访问来自不同的作用域。全局变量需被声明在函数体外,在整个运行过程中都存在,能在任何作用域中访问和修改。局部变量仅在函数体内定义,并且每次函数调用都有不同的作用域。这主题是仅在调用中的赋值,求值和对值的操作,不能访问作用域之外的值。</p>
<p>目前javascript不支持块级作用域,块级作用域指在if语句,switch语句,循环语句等语句块中定义变量,这意味着变量不能在语句块之外被访问。当前任何在语句块中定义的变量都能在语句块之外访问。然而,这种情况很快会得到改变,let 关键字已经正式添加到ES6规范。用它来代替var关键字可以将局部变量声明为块级作用域。</p>
<h2 id="this-上下文">“this” 上下文</h2>
<p>上下文通常是取决于一个函数如何被调用。当函数作为对象的方法被调用时,this 被设置为调用方法的对象:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var object = {
foo: function(){
alert(this === object);
}
};
object.foo(); // true
</code></pre></div></div>
<p>同样的原理适用于当调用一个函数时通过new的操作符创建一个对象的实例。当以这种方式调用时,this 的值将被设置为新创建的实例:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function foo(){
alert(this);
}
foo() // window
new foo() // foo
</code></pre></div></div>
<p>当调用一个未绑定函数,this 将被默认设置为 全局上下文(global context) 或window对象(如果在浏览器中)。然而如果函数在严格模式下被执行(“use strict”),this的值将被默认设置为undefined。</p>
<h2 id="执行上下文和作用域链">执行上下文和作用域链</h2>
<p>javascript是一个单线程语言,这意味着在浏览器中同时只能做一件事情。当javascript解释器初始执行代码,它首先默认竟如全局上下文。每次调用一个函数将会创建一个新的执行上下文。</p>
<p>这里经常发生混淆,这术语”执行上下文(execution context)“在这里的所要表达的意思是作用域,不是前面讨论的上下文。这是槽糕的命名,然而这术语ECMAScript规范所定义的,无奈的遵守吧。</p>
<p>每次新创建一个执行上下文,会被添加到作用域链的顶部,又是也成为执行或调用栈。浏览器总是运行在位于作用域链顶部当前执行上下文。一旦完成,它(当前执行上下文)将从栈顶被移除并且将控制权归还给之前的执行上下文。例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function first(){
second();
function second(){
third();
function third(){
fourth();
function fourth(){
// do something
}
}
}
}
first();
</code></pre></div></div>
<p>运行前面的代码将会导致嵌套的函数被从上倒下执行直到 fourth 函数,此时作用域链从上到下为: fourth, third, second, first, global。fourth 函数能够访问全局变量和任何在first,second和third函数中定义的变量,就如同访问自己的变量一样。一旦fourth函数执行完成,fourth晕高兴上下文将被从作用域链顶端移除并且执行将返回到thrid函数。这一过程持续进行直到所有代码已完成执行。</p>
<p>不同执行上下文之间的变量命名冲突通过攀爬作用域链解决,从局部直到全局。这意味着具有相同名称的局部变量在作用域链中有更高的优先级。</p>
<p>简单的说,每次你试图访问函数执行上下文中的变量时,查找进程总是从自己的变量对象开始。如果在自己的变量对象中没发现要查找的变量,继续搜索作用域链。它将攀爬作用域链检查每一个执行上下文的变量对象去寻找和变量名称匹配的值。</p>
<h2 id="闭包">闭包</h2>
<p>当一个嵌套的函数在定义(作用域)的外面被访问,以至它可以在外部函数返回后被执行,此时一个闭包形成。它(闭包)维护(在内部函数中)对外部函数中局部变量,arguments和函数声明的访问。封装允许我们从外部作用域中隐藏和保护执行上下文,而暴露公共接口,通过接口进一步操作。一个简单的例子看起来如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function foo(){
var local = 'private variable';
return function bar(){
return local;
}
}
var getLocalVariable = foo();
getLocalVariable() // private variable
</code></pre></div></div>
<p>其中最流行的闭包类型是广为人知的模块模式。它允许你模拟公共的,私有的和特权成员:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var Module = (function(){
var privateProperty = 'foo';
function privateMethod(args){
//do something
}
return {
publicProperty: "",
publicMethod: function(args){
//do something
},
privilegedMethod: function(args){
privateMethod(args);
}
}
})();
</code></pre></div></div>
<p>模块实际上有些类似于单例,在末尾添加一对括号,当解释器解释完后立即执行(立即执行函数)。闭包执行上下位的外部唯一可用的成员是返回对象中公用的方法和属性(例如<em>Module.publicMethod</em>)。然而,所有的私有属性和方法在整个程序的生命周期中都将存在,由于(闭包)使执行上下文收到保护,和变量的交互要通过公用的方法。</p>
<p>另一种类型的闭包叫做立即调用函数表达式(immediately-invoked function expression IIFE),无非是一个在window上下文中的自调用匿名函数(self-invoked anonymous function)。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function(window){
var a = 'foo', b = 'bar';
function private(){
// do something
}
window.Module = {
public: function(){
// do something
}
};
})(this);
</code></pre></div></div>
<p>对保护全局命名空间,这种表达式非常有用,所有在函数体内声明的变量都是局部变量,并通过闭包在整个运行环境保持存在。这种封装源代码的方式对程序和框架都是非常流行的,通常暴露单一全局接口与外界交互。</p>
<h2 id="call-和-apply">Call 和 Apply</h2>
<p>这两个简单的方法,内建在所有的函数中,允许在自定义上下文中执行函数。call 函数需要参数列表而 apply 函数允许你传递参数为数组:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function user(first, last, age){
// do something
}
user.call(window, 'John', 'Doe', 30);
user.apply(window, ['John', 'Doe', 30]);
</code></pre></div></div>
<p>执行的结果是相同的,user 函数在window上下文上被调用,并提供了相同的三个参数。</p>
<p>ECMAScript 5 (ES5)引入了<em>Function.prototype.bind</em>方法来控制上下文,它返回一个新函数,这函数(的上下文)被永久绑定到bind方法的第一个参数,无论函数被如何调用。它通过闭包修正函数的上下文,下面是为不支持的浏览器提供的方案:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if(!('bind' in Function.prototype)){
Function.prototype.bind = function(){
var fn = this, context = arguments[0], args = Array.prototype.slice.call(arguments, 1);
return function(){
return fn.apply(context, args);
}
}
}
</code></pre></div></div>
<p>它常用在上下文丢失:面向对象和事件处理。这点有必要的因为 节点的addEventListener 方法总保持函数执行的上下文为事件处理被绑定的节点,这点很重要。然而如果你使用高级面向对象技术并且需要维护回调函数的上下文是方法的实例,你必须手动调整上下文。这就是bind 带来的方便:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function MyClass(){
this.element = document.createElement('div');
this.element.addEventListener('click', this.onClick.bind(this), false);
}
MyClass.prototype.onClick = function(e){
// do something
};
</code></pre></div></div>
<p>当回顾bind函数的源代码,你可能注意到下面这一行相对简单的代码,调用Array的一个方法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Array.prototype.slice.call(arguments, 1);
</code></pre></div></div>
<p>有趣的是,这里需要注意的是<em>arguments</em>对象实际上并不是一个数组,然而它经常被描述为类数组(array-like)对象,很向 nodelist(<em>document.getElementsByTagName()</em>方法返回的结果)。他们包含lenght属性,值能够被索引,但他们仍然不是数组,由于他们不支持原生的数组方法,比如slice和push。然而,由于他们有和数组类似的行为,数组的方法能被调用和劫持。如果你想这样,在类数组的上下文中执行数组方法,可参照上面的例子。</p>
<p>这种调用其他对象方法的技术也被应用到面向对象中,当在javascript中模仿经典继承(类继承):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>MyClass.prototype.init = function(){
// call the superclass init method in the context of the "MyClass" instance
MySuperClass.prototype.init.apply(this, arguments);
}
</code></pre></div></div>
<p>通过在子类(MyClass)的实例中调用超类(MySuperClass)的方法,我们能重现这种强大的设计模式。</p>
<h2 id="结论">结论</h2>
<p>在你开始学习高级设计模式之前理解这些概念是非常重要的,由于作用域和上下文在现代javascript中扮演重要的和根本的角色。无论我们谈论闭包,面向对象,和继承或各种原生实现,上下文和作用域都扮演重要角色。如果你的目标是掌握javascript语言并深入了解它的组成,作用域和上下文应该是你的起点。</p>
<h2 id="译者补充">译者补充</h2>
<p>作者实现的bind函数是不完全的,调用bind返回的函数时不能传递参数,下面的代码修复了这个问题:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>if(!(‘bind’ in Function.prototype)){
Function.prototype.bind = function(){
var fn = this, context = arguments[0], args = Array.prototype.slice.call(arguments, 1);
return function(){
return fn.apply(context, args.concat(arguments));//fixed
}
}
}
</code></pre></div></div>
<p>注:本文为翻译文章,原文为 <a href="http://flippinawesome.org/2013/08/26/understanding-scope-and-context-in-javascript/">Understanding Scope and Context in JavaScript</a>。</p>
JavaScript 封装问题
2013-08-30T00:00:00+00:00
http://yanhaijing.com/javascript/2013/08/30/encapsulation-of-javascript
<p>为什么会用这样一个题目呢,这是要说封装的什么问题,本文并不讲高深的封装理论,只是解决一个小问题。</p>
<h2 id="问题来源">问题来源</h2>
<p>今天在百度知道上闲逛,遇到一个网友的问题,问题如下,问题的地址见<a href="http://zhidao.baidu.com/question/587036591.html?sort=6&old=1#here">这里</a>:</p>
<p><img src="/blog/10.png" alt="百度知道问题截图" /></p>
<p>下面先不看看其他网友给的答案:</p>
<p><img src="/blog/11.png" alt="百度知道网友回答截图" /></p>
<p>网友大部分回答不能一起定义,那么我们来分析下为什么这样做是错的,然后给出相应的解决办法。</p>
<h2 id="重现问题">重现问题</h2>
<p>先来说说为什么调用出错,我在自己的浏览器里重现了问题,处于实验并未全部复原代码,并且用到了全局变量哦:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function Dialog(){
Dialog.prototype = {
init:function(){
console.log("ok");
}
}
}
var a = new Dialog();
a.init();
</code></pre></div></div>
<p>下面是火狐提示的错误:</p>
<p><img src="/blog/12.png" alt="火狐下提示错误" /></p>
<h2 id="分析问题">分析问题</h2>
<p>init不是一个方法,这是为什么呢,我们将调用代码修改下,出于演示,并未遵循JsLint代码规范:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = new Dialog();
typeof a.init;
</code></pre></div></div>
<p><img src="/blog/13.png" alt="代码执行结果" /></p>
<p>结果显示为undefined,也就是init没有被定义,或者说在求值过程中未找到init标识符的值,这是因为在调用函数时函数里面的内容才会被解析执行,所以在调用new Dialog(),时其内部的代码尚未执行,所以设置Dialog的原型的语句尚未执行,通过这个例子可以看出绑定this的prototype的过程是在执行构造函数内部代码之前,可以用下面的代码来解释下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function Dialog(){
var that = Object.create(Object.getPrototypeOf(this));
Dialog.prototype = {}
}
</code></pre></div></div>
<h2 id="巧妙解决">“巧妙解决”</h2>
<p>很明显获取原型时尚未设置原型,但当我们再次调用new 时情况将发生改变:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = new Dialog();
var b = new Dialog();
typeof b.init;
</code></pre></div></div>
<p><img src="/blog/14.png" alt="代码执行结果" /></p>
<p>在此调用时奇迹发生了,我们看到第二次调用时成功获取了值,只有第一次时值是未获去,那我们是不是可以改造下我们的dialog函数呢:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function Dialog(){
Dialog.prototype = {
init:function(){
console.log("ok");
}
}
}
new Dialog();
var a = new Dialog();
typeof a.init;
</code></pre></div></div>
<p>我们只需先调用下构造函数便解决了问题,如果你觉得上面的代码还是有悖于封装我们可以再做改变:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var Dialog = (function(){
function Dialog(){
Dialog.prototype = {
init:function(){
console.log("ok");
}
}
}
new Dialog();
return Dialog;
}());
</code></pre></div></div>
<h2 id="问题中的问题">问题中的问题</h2>
<p>上面真的解决问题了吗,你难道没有疑问呢,如果你已经看出问题所在在了,那你也一定能想出解决办法,而且你应该是一个高手,那么让我们看看这样巧妙的解决办法有什么问题,为此我们来构造下面的代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = new Dialog();
typeof a instanceof Dialog;
</code></pre></div></div>
<p>也许你会问为什么会这样写,也许下面的结果更让你吃惊:</p>
<p><img src="/blog/15.png" alt="代码执行结果" /></p>
<p>结果为false,为什么我的a不是Dialog的实例呢,我的a明明是Dialog创建的,要想搞清这个问题我们先得说清楚 instanceof关键字的工作原理,当我们调用类似a instanceof Dialog 这样的语句时,解释器是怎么判断a是Dialog创建的对象的呢,原来解释器是判断a的原型是否为Dialog的prototype属性所指向的对象也就是说如果a的原型和Dialog的prorotype属性指向同一个对象就认为a是Dialog的对象,当然在判断时并不是至判断a的的原型,而是判断原型链中的每个对象,例如:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = [];
a instanceof Array;
a instanceof Object;
</code></pre></div></div>
<p>上面的两条语句都会返回true,因为a的原型链中包含这两个对象。</p>
<p>而上面我们的代码为什么结果为false呢,那是因为当我们每次调用Dialog构造函数时都会在内部重写Dialog的原型,而已经创建的对象的原型会指向原来的原型对象,解释器在判断两个对象是否相等时,要判断两个对象是否引用同一块地址,而不是两个对象是否有相同的属性和方法,所以上面出现false的原因就很清楚了,所以上面的解决办法就出现问题了,而且是很大的问题。显然这种方法行不通。</p>
<h2 id="看清本质">看清本质</h2>
<p>那我们有没有办法解决问题呢,让我们先来看看作者想要实现什么,作者想要实现的封装,也就是构造函数和构造函数的原型分开写的问题,作者想把他们写到一起,作者认为这才是封装,那么我们先来看下封装是什么,作者对封装的理解是否有误:</p>
<blockquote>
<p>封装,1、在程序上,隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别;将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。</p>
</blockquote>
<p>上面是对封装的解释,可以看出这跟作者描述的封装并不是一个意思,作者此处所想表达的实际上是更好的代码结构。</p>
<h2 id="建议">建议</h2>
<p>既然清楚了作者的意思,来看下解决办法,如何将构造函数的定义和原型的定义写到一起呢,看下我给出的解决办法:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var Dialog = (function(){
function Dialog(){
}
Dialog.prototype = {
init:function(){
console.log("ok");
}
}
return Dialog;
}());
var a = new Dialog();
a instanceof Dialog;
</code></pre></div></div>
<p><img src="/blog/16.png" alt="代码执行结果" /></p>
<p>好了问题解决了,结果正确了,而且我们也得到了比较清晰的代码结构。</p>
Javascript继承-原型的陷阱
2013-08-23T00:00:00+00:00
http://yanhaijing.com/javascript/2013/08/23/javascript-inheritance-how-to-shoot-yourself-in-the-foot-with-prototypes
<p>在学习javascript的过程中,许多新手发现很难弄明白javascript复杂的的原型继承工作机制。在这篇文章中我谈谈在通过父函数的原型继承模型中如何实现实例属性。</p>
<h2 id="一个简单的widget-对象">一个简单的Widget 对象</h2>
<p>在下面的代码中,我们有个一父类 Widget,父类有个属性 messages和父类为Widget的SubWidget类。在这种情况下我们想让SubWidget的每个实例在初始化的时候一个空的消息数组:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var Widget = function( name ){
this.messages = [];
};
Widget.prototype.type='Widget';
var SubWidget = function( name ){
this.name = name;
Widget.apply( this, Array.prototype.slice.call( arguments ) );
};
SubWidget.prototype = new Widget();
</code></pre></div></div>
<p>在我们设置SubWidget 的原型为Widget的一个实例之前,对象的关系图如下:</p>
<p><img src="/blog/1.png" alt="" /></p>
<p>代码最后一行将SubWidget的父类设置为Widget类的一个实例,”new”关键字背后,创建了继承树并且绑定了对象的原型链,现在我们的对象关系图看起来像下面这样:</p>
<p><img src="/blog/2.png" alt="" /></p>
<p>你看出问题所在了吗?让我们创建子类的实例凸显问题:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var sub1 = new SubWidget( 'foo' );
var sub2 = new SubWidget( 'bar' );
sub1.messages.push( 'foo' );
sub2.messages.push( 'bar' );
</code></pre></div></div>
<p>现在我们的对象关系图看起来像这样:</p>
<p><img src="/blog/3.png" alt="" /></p>
<p>在谈论真正的问题之前,我想想退一步,先谈谈widget构造函数中的属性(type),如果在实例初始化过程中没有初始化属性(type)那实际上这个属性存在widget构造函数中(实际上存在wedget的实例中,也就是subwidget实例的原型中)。然而,一旦在(子类实例)初始化过程中属性被赋予新值,如 sub1.type = ‘Fuzzy Bunny’,它将变成实例的属性,如图所示:</p>
<p><img src="/blog/4.png" alt="" /></p>
<h2 id="思考问题">思考问题</h2>
<p>我们的bug开始变得很清晰,让我们输出sub1和sub2的messages数组:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var Widget = function(){
this.messages = [];
};
Widget.prototype.type='Widget';
var SubWidget = function( name ){
this.name = name;
};
SubWidget.prototype = new Widget();
var sub1 = new SubWidget( 'foo' );
var sub2 = new SubWidget( 'bar' );
sub1.messages.push( 'foo' );
sub2.messages.push( 'bar' );
console.log( sub1.messages ); //[ 'foo', 'bar' ]
console.log( sub2.messages ); //[ 'foo', 'bar' ]
</code></pre></div></div>
<p>如果你运行这段代码,在你的控制台将出现2个重复 [“foo”, “bar”]。每个对象共享相同的messages数组。</p>
<h2 id="解决问题">解决问题</h2>
<p>最容易想到的办法,我们可以给SubWidget构造函数添加新属性,如下所示:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var SubWidget = function( name ){
this.name = name;
this.messages = [];
};
</code></pre></div></div>
<p>然而,如果我们想创建其他继承自Widget的对象呢?新对象也要添加消息数组。很快维护和扩展我们的代码将变成一场噩梦。另外,如果我们想给Widget构造函数添加其他属性,我们如何将这些属性编程子类的实例属性?这种方法是不可重用的和不够灵活。</p>
<p>为了妥善解决这个问题,需要给我们的SubWidget构造函数添加一行代码,调用Widget构造函数并且传入SubWidget构造函数的作用域。为此我们要用apply()方法,可以灵活的无副作用的将SubWidget构造函数的arguments传入Widget构造函数中。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var Widget = function(){
this.messages = [];
};
Widget.prototype.type='Widget';
var SubWidget = function( name ){
this.name = name;
Widget.apply( this, Array.prototype.slice.call(arguments) );
};
SubWidget.prototype = new Widget();
</code></pre></div></div>
<p>apply()方法可以让我们可以将messages数字的作用域更改为SubWidget的实例。现在我们创建的每一个实例对象都有一个实例messages 数组。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var Widget = function( ){
this.messages = [];
};
Widget.prototype.type='Widget';
var SubWidget = function( name ){
this.name = name;
Widget.apply( this, Array.prototype.slice.call( arguments ) );
};
SubWidget.prototype = new Widget();
var sub1 = new SubWidget( 'foo' );
var sub2 = new SubWidget( 'bar' );
sub1.messages.push( 'foo' );
sub2.messages.push( 'bar' );
console.log(sub1.messages); // ['foo']
console.log(sub2.messages); // ['bar']
</code></pre></div></div>
<p>运行上面的代码,你将看见 [“foo”] 和 [“bar”] ,因为我们的对象实例现在有自己的messages数组属性。</p>
<p>现在我们的对象关系图如下:</p>
<p><img src="/blog/5.png" alt="" /></p>
<h2 id="译者补充">译者补充</h2>
<p>上面的继承方式是借用构造函数模式,《javascript patterns》中有详细介绍,作者写的很详细了,但有2个小问题在此补充:</p>
<p>1</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var SubWidget = function( name ){
this.name = name;
Widget.apply( this, Array.prototype.slice.call( arguments ) );
};
</code></pre></div></div>
<p>作者的代码中父类会覆盖子类的属性,这有悖于重构的概念,稍加改变即可,在子类构造函数中先调用父类构造函数,这相当于java中的super:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var SubWidget = function( name ){
Widget.apply( this, Array.prototype.slice.call( arguments ) );
this.name = name;
};
</code></pre></div></div>
<p>2</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var SubWidget = function( name ){
this.name = name;
Widget.apply( this, Array.prototype.slice.call( arguments ) );
};
SubWidget.prototype = new Widget();
</code></pre></div></div>
<p>父类的属性被初始化了2次,一次是借用构造函数,一次是new Widget(),造成浪费,稍加改变即可:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var SubWidget = function( name ){
this.name = name;
Widget.apply( this, Array.prototype.slice.call( arguments ) );
};
SubWidget.prototype = Widget.prototype;
</code></pre></div></div>
<p>关于继承我还写了另一篇文章,可以完美解决上面的所有问题《<a href="http://yanhaijing.com/javascript/2014/11/09/object-inherit-of-js/">JavaScript对象继承一瞥</a>》。</p>
<h2 id="注">注</h2>
<p>英文原文:”<a href="http://flippinawesome.org/2013/06/03/javascript-inheritance-how-to-shoot-yourself-in-the-foot-with-prototypes/#comment-2875" title="javascript-inheritance-how-to-shoot-yourself-in-the-foot-with-prototypes">JavaScript Inheritance – How To Shoot Yourself In the Foot With Prototypes!</a>”</p>
解耦你的html,css和javascript
2013-08-22T00:00:00+00:00
http://yanhaijing.com/web/2013/08/22/decoupling-html-css-and-javascript
<p>今天在web上任何大一点的网站或应用程序都包含大量的html,css和javascript。随着互联网的发展和我们对互联网越来越依赖,计划组织和维护你的前端代码是绝对必要的。</p>
<p>今天大一点的web公司,越来越多的人接触越到比例越来越大的前端代码。因此,大多数公司努力保持他们的代码模块化。改变一个应用程序的一部分经常无意中影响到看似不相关的其他部分。</p>
<p>防止意外的影响不是一个容易解决的问题,特别是由于HTML、CSS和JavaScript本身是相互关联的。</p>
<p>更糟糕的是,传统的计算机科学原则如分离关注点,一直是服务器端开发的一部分,却很少被讨论在前端代码中。</p>
<p>在本文中,我将谈谈我是如何学会分离我的HTML、CSS和JavaScript。据我的经验和我所知道的其他人的经验,完成目标的最好的方式是不明显,反直觉的,有时不利于大量所谓的“最佳实践”的。</p>
<h2 id="目标">目标</h2>
<p>HTML、CSS和JavaScript之间总是会有一些耦合。不管怎样,这些技术被用来互相作用。举个例子,一个fly-in transition(飞入变换)可能在样式表中定义了一个类选择器,但它通常是由用户交互来触发,通过JavaScript来执行,并且通过在html中添加一个类来初始化。</p>
<p>由于在你的前端代码中存在一些耦合是不可避免的。你的目标不应该是简单地完全消除耦合,而应该是最小化耦合,使这部分代码不必依赖其他部分。</p>
<p>后端开发人员应该能够改变HTML模板中的标记而不用担心不小心破坏一个CSS规则或一些JavaScript功能。随着今天的Web团队规模的增大和专业化,这一目标是比以往更重要。</p>
<h2 id="反模式">反模式</h2>
<p>前端代码是不是紧密耦合并不是明显的。然而雪上加霜的是,从一个角度看似松散耦合从另一个角度看事实上却是紧密耦合的。</p>
<p>以下都是反模式,包括我多次见过或我亲自做过并从我的错误中学会的。对每一个,我试图解释为什么耦合是不好的,以及如何避免。</p>
<h2 id="过于复杂的选择器">过于复杂的选择器</h2>
<p>CSS禅意花园向世界证明了你可以完全改变整个网站的外观并丝毫没有改变标记。这是语义Web的运动的海报,和它的一个主要原则是避免使用表象的类。</p>
<p>乍一看,CSS禅意花园可能看起来像一个解耦的很好的例子。毕竟,它的全部意义是将样式从标记分离。然而,问题是,要做到这一点,你通常需要在你的样式表看起来像下面这样的选择器:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#sidebar section:first-child h3 + p { }
</code></pre></div></div>
<p>CSS禅意花园中,HTML几乎完全脱离了CSS,但CSS对html是超级耦合和需要结构的标记的知识非常了解。</p>
<p>这看上去还不错如果一个人即维护CSS还维护HTML,但是一旦你增加一些更多的人到你的组合,它很快就会失控。如果一个开发者来临并添加一个<div>在<section>之前,上述规则将不会工作,开发人员可能不知道自己做了什么。</p>
<p>CSS禅意花园是一个伟大的想法,只要你的网站的标记很少改变。但今天的Web应用程序通常不会有这样的情形。</p>
<p>代替冗长,复杂的CSS选择器,最好(只要有可能)风格是你所有的可视化组件有一个或多个类在根元素组件自身上。例如,如果你在你的边栏里有一个子菜单,仅仅添加submenu类到每一个子菜单元素,而不要像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ul.sidebar > li > ul {
/* submenu styles */
}
</code></pre></div></div>
<p>这种方法最终需要更多的类在HTML中,但它会降低(html,css)之间的耦合,从长远来看这使CSS代码更可重用和可维护。它也可以让你的标记标记标记自我(self-documenting)。如果在HTML中没有类,开发人员不清楚CSS无法预知她的标记变化将影响其他的代码。另一方面,如果在HTML有类那很明显元素被应用的样式和功能。</p>
<h2 id="一个类有超过一个职责">一个类有超过一个职责</h2>
<p>有时一个类即用于样式的目的,还作为一个JavaScript钩子(javascript选择符)。虽然这可能看起来像一个节约(因为它需要一个更少的类标记)实际上一个元素表示和行为的耦合。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><button class="add-item">Add to Cart</button>
</code></pre></div></div>
<p>上面的例子展示了一个“添加到购物车”按钮样式与添加物品类。</p>
<p>如果开发者想要给这个元素添加一个单击事件监听器,它可以很容易将已经存在的类当作挂钩。我的意思是,如果一个(类)已经存在,为什么要添加另一个呢?</p>
<p>但是想象一下,在整个网站里,有很多这些按钮,他们全都看起来一样,并且全部调用相同的JavaScript功能。然后想象一下营销团队想要其中一个按钮看起来完全与众不同。也许它需要很多更大的更吸引眼球的颜色。</p>
<p>这里有一个问题,因为JavaScript代码里单击事件监听器期待 add-item 类继续被使用,但是你的新按钮现在不能使用这个类(或它必须复原一切已经声明的(样式)并且重置新样式)。更大的问题是,如果你有一些测试代码,也期待着add-item类出现,所以你也会有另一个地方的代码(测试代码)需要更新。</p>
<p>更糟糕的是如果你的 “Add to Cart” 功能使用的不仅仅是这个应用程序。如果代码已经被抽象到一个独立的模块然后一个简单的风格改变现在可能在完全不同的应用程序间引入bug。</p>
<p>使用类作为JavaScript钩子是完全可以接受的(实际上鼓励),但是如果你要这样做,要按这种方法,避免样式类和行为类之间的耦合。</p>
<p>我个人的建议是为所有JavaScript钩子使用前缀。我使用 js - *。这样,当一个开发人员在HTML源码中看到这样一个类(带前缀),她就清楚知道到哪里去找使用这个类的目的。</p>
<p>上面的例子中 “Add to Cart” 将被重写为:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><button class="js-add-to-cart add-item">Add to Cart</button>
</code></pre></div></div>
<p>现在,如果一个特定的 “Add to Cart”按钮需要看起来不同,您可以简单地改变样式类和分离行为类本身。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><button class="js-add-to-cart add-item-special">Add to Cart</button>
</code></pre></div></div>
<h2 id="javascript对样式知道的太多">JavaScript对样式知道的太多</h2>
<p>同样,JavaScript可以使用类来查找DOM中的元素,它也可以添加或删除类来改变元素的样式。但这可能产生一个问题,如果这些类不是明显区别于出现在页面加载中的类。</p>
<p>如果JavaScript对关于组件样式代码知道的太多,下面的情况将变得非常平常,一个CSS开发人员修改样式表并没有意识到他正在破坏关键的功能。</p>
<p>这并不是说JavaScript不应该更改组件的外观在用户与他们交互后,但应该通过一种一致的接口。它应该使用与定义的默认样式类有明显区别的类。</p>
<p>类似于 js-* 前缀的类,我建议使用 is-* 前缀定义改变可视化组件状态的类选择器。一个CSS规则示例可能看起来像这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.pop-up.is-visible { }
</code></pre></div></div>
<p>注意,状态类(is-visible)和组件类(pop-up)是交集关系,这一点是重要的。因为状态规则描述组件的状态,他们不应该独立出现。这区别有助于进一步区分状态样式是自默认组件样式的特殊情况。</p>
<p>此外,为了使用一个类似 is-* 的前缀,我们可以编写测试程序来确保我们遵循了规则。一个测试这些类型的规则方法是使用 CSSLint 和 HTML Inspector。</p>
<p>更多关于特定状态类的信息中可以在优秀的书 <a href="http://snook.ca/">SMACSSJonathan Snook</a> 中找到。</p>
<h2 id="javascript-选择符">JavaScript “选择符”</h2>
<p>jQuery 和新的APIs 如document.querySelectorAll使用户非常容易在DOM中通过语言他们熟悉的CSS选择器查找元素。虽然这是非常强大的,但它有已经CSS选择器出现的同样的问题。</p>
<p>JavaScript“选择器”不应该过度依赖于DOM结构。这样的选择器是非常慢的,而且需要非常了解HTML知识。</p>
<p>参考第一个示例,开发人员在一个HTML模板上工作应该能够对标记做基本更改而不用担心会基本功能产生影像。如果功能是可以打破的,它应该在标记能明显看出。</p>
<p>我已经提到过 js-* 前缀的类应该作为JavaScript钩子。除了将样式类从功能类分离,他们还表达标记的意图。当有人在编辑HTML是看到一个 js-*前缀的类,他们就会知道这是用来做什么的。如果JavaScript代码查询元素不依赖特定的标记结构,在标记中不是明显的什么功能正在发生。</p>
<p>代替长DOM结构复杂的选择器,坚持单类选择器或id选择器。</p>
<p>考虑下面的代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var saveBtn = document.querySelector("#modal div:last-child > button:last-child")
</code></pre></div></div>
<p>这长的选择器使您不必给HTML添加一个类,也会让你的代码很容易受标记的变化的影像。如果设计师突然决定把保存按钮和取消按钮交换位置,上面的选择器将不再匹配。</p>
<p>一个更好的方法(使用上面提到的前缀的方法)仅仅是使用一个类。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var saveBtn = document.querySelector(".js-save-btn")
</code></pre></div></div>
<p>现在的标记可以随意改变,只要类一直在正确的元素上,一切都将很好地工作。</p>
<h2 id="类是你的钥匙">类是你的钥匙</h2>
<p>几乎所有类型的HTML、CSS和JavaScript之间的耦合可以被减轻通过适当的使用的类和一个可预测的类命名约定。</p>
<p>乍一看在标记中使用大量的类这可能看起来像一个高耦合的信号,因为HTML需要知道这些类的名字。但我认为使用类的做法非常类似传统的程序设计事件模型(隐式风格调用)或外观模式(观察者模式或发布订阅模式)。</p>
<p>在事件驱动编程中,取而代之对象 a 调用对象 b 一个方法,对象 a 只是简单的在一个给定的情况会下会触发特定的事件,而对象b可以订阅该事件。这种方式,对象a不需要知道任何关于对象b 的接口,只需要知道要监听的事件。</p>
<p>可以说事件系统存在一个形式的耦合由于对象B需要知道事件的名字从而订阅,但和对象a需要知道对象B的公共方法相比它是相当松散的耦合。</p>
<p>给HTML添加类是非常相似的。取而代之在CSS文件定义复杂的选择器(这相当于知道HTML的内部接口),它可以通过一个类简单地定义组件的视觉外观。HTML可能会选择使用或不使用这个类,CSS无需关心。</p>
<p>同样,JavaScript不需要复杂DOM序列功能,这也需要非常了解html结构知识,它可以简单的监听用户的交互元素,通过协商的类名。</p>
<p>类应该是将你的HTML、CSS和JavaScript连接在一起的纽带。在我的经验中这是是最简单和最棒的方法来连接这三个技术而没有将他们混合在一起太多。</p>
<h2 id="未来">未来</h2>
<p><a href="http://www.whatwg.org/">WHATWG</a>目前正在为<a href="http://www.w3.org/TR/2013/WD-components-intro-20130606/">Web组件</a>规范工作,这将允许开发人员将HTML、CSS和JavaScript压在一起作为单独的组件或模块从而从其余的页面封装。</p>
<p>当有一天规范在大多数浏览器中实现,我在这篇文章中给的很多建议将变得不那么关键(因为它会更加明显代码是为了什么工作);然而,它仍然是重要的,理解更广泛的原则和为什么需要他们。</p>
<p>即使在Web组件的时代这些做法变得不那么重要,这一理论仍然适用。用于大型的团队和大型应用程序的做法,仍然会工作对于个体开发人员写的小模块。相反是不必要的情况。</p>
<h2 id="结论">结论</h2>
<p>编写可维护的HTML、CSS和JavaScript的要点是个体开发人员可以很容易地和自信地编辑部分的代码库而这些变化不经意间影响其他不相关的部分。</p>
<p>防止意想不到的后果的一个最好的方法来是通过一组可预见的类来连接这三个技术,任何开发人员遇到这些类都能清楚它们的意图和选择的他们目的。</p>
<p>为了避免上面的反模式,牢记下面的原则:</p>
<ul>
<li>在CSS和JavaScript用明确组件和行为类代替复杂的CSS选择器。</li>
<li>风格组件依赖于它们是什么,而不是他们的位置。</li>
<li>样式和行为不要使用相同的类。</li>
<li>从默认的样式分离出状态样式。</li>
</ul>
<p>这种方式往往在HTML中呈现大量类,但在可预见性和可维护性上的收益是值得的。毕竟,在HTML中添加类是相当容易,并大多数开发人员的技术水平可以做到。引用 <a href="http://nicolasgallagher.com/about-html-semantics-front-end-architecture/">Nicolas Gallagher的一段话</a>:</p>
<blockquote>
<p>当你选择修改HTML和CSS,试图减少你花在写作和编辑CSS上的大量时间,这包括接受,你必须花更多的时间更改HTML元素的类,如果你想改变元素的风格。这被证明是相当实用的,包括前端和后端开发人员——任何人都可以重新排列预先构建的“乐高积木”;事实证明,没有人可以执行css炼金术。</p>
</blockquote>
<h2 id="注">注</h2>
<p>原文<<a href="http://philipwalton.com/articles/decoupling-html-css-and-javascript/">Decoupling Your HTML, CSS, and JavaScript</a>></p>
网格如此简单
2013-08-22T00:00:00+00:00
http://yanhaijing.com/css/2013/08/22/dont-overthink-it-grids
<blockquote>
<p>绝大多数的网站都在使用一个网格。 他们可能没有明确的使用网格系统,但如果他们有一个向左浮动“主要内容区”和一个向右浮动的“侧边栏”,这就是一个简单的网格。</p>
</blockquote>
<p>如果需要更复杂的布局,人们往往会借助框架。 他们猜想网格是超高难度的事情最好留给超级CSS书呆子。 这一理念导致这样的事实,很多网格系统是非常复杂的 。</p>
<p>下面是我如何构建网格。 它一点都不难也不复杂。 甚至使他们灵活也不是一个大的任务。</p>
<h2 id="容器">容器</h2>
<p>块级元素的宽度和它的父元素一样( width: auto; )。 我们可以把它的宽度看作是100%。 wrapper 对于网格可能语义不太好,但它只是一个普通的包装器,所以div是合适的。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div class="grid">
<!-- 100% wide -->
</div>
</code></pre></div></div>
<p><img src="/blog/6.png" alt="简单网格例子" /></p>
<h2 id="列">列</h2>
<p>让我们从一个实用的,通用的需要开始:主要内容区宽度为2/3和1/3宽的侧边栏。 我们只用2个div元素代表两列并取的适当的类名。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div class="grid">
<div class="col-2-3">
Main Content
</div>
<div class="col-1-3">
Sidebar
</div>
</div>
</code></pre></div></div>
<p>为了让他们水平排列,我们只需要浮动他们并设置合适的宽度。 我们选择他们像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[class*='col-'] {
float: left;
}
</code></pre></div></div>
<p>各个宽度的设置像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.col-2-3 {
width: 66.66%;
}
.col-1-3 {
width: 33.33%;
}
</code></pre></div></div>
<p>这就是全部的前提关于简单网格。</p>
<h2 id="给容器清除浮动">给容器清除浮动</h2>
<p>父元素会发生坍塌高度变为0,因为它的子元素全部浮动,浮动的元素将脱离标准流。 下面让我们修补这种情况通过clear属性。 你只需像下面这样:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.grid:after {
content: "";
display: table;
clear: both;
}
</code></pre></div></div>
<h2 id="间隙">间隙</h2>
<p>网格最难的部分是间隙(列之间的空隙)。 到目前为止,我们已经使用百分比制成了灵活的网格。 我们可以使所有复杂数学计算并且让我们的间隙也使用百分比,但无论如何我个人不喜欢百分比的间隙,我喜欢固定像素尺寸的间隙。 此外,我们正试图解决这个问题。</p>
<p><strong>第一步</strong>是使用 box-sizing: border-box;。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>*, *:after, *:before {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
</code></pre></div></div>
<p>现在,当我们设置元素的宽度,这个宽度=width+padding+border。</p>
<p><strong>第二步</strong>是给除了最后一个以外的所有列的右内边距设置一个固定值。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[class*='col-'] {
padding-right: 20px;
}
[class*='col-']:last-of-type {
padding-right: 0;
}
</code></pre></div></div>
<p><img src="/blog/7.png" alt="2列网格例子" /></p>
<p>这就是基本间隙的所有设置了。</p>
<h2 id="外部间隙">外部间隙</h2>
<p>需要设置外部间隙(父元素的内边距)?我喜欢使用一个可选类:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div class="grid grid-pad">
Grid with outside gutters also
</div>
</code></pre></div></div>
<p><strong>第一步</strong>是给网格父元素添加左内边距(以及可选的的顶部和底部内边距):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.grid-pad {
padding: 20px 0 20px 20px;
}
</code></pre></div></div>
<p><strong>第二步</strong>是重新设置最后一列的右内边距</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.grid-pad > [class*='col-']:last-of-type {
padding-right: 20px;
}
</code></pre></div></div>
<p><img src="/blog/7.png" alt="2列有外边据网格例子" /></p>
<h2 id="更多列选择">更多列选择</h2>
<p>超级简单:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.col-1-2 {
width: 50%;
}
.col-1-4 {
width: 25%;
}
.col-1-8 {
width: 12.5%;
}
</code></pre></div></div>
<p>做你想做的效果,只要确保列分数加起来为1。 耶,需要一点点思考,但比平常更容易。</p>
<p><img src="/blog/9.png" alt="多列网格例子" /></p>
<h2 id="sass">Sass</h2>
<p>在这里我不过分使用它,但使用它让所有事情变得更加简洁(还可用less):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>* {
@include box-sizing(border-box);
}
$pad: 20px;
.grid {
background: white;
margin: 0 0 $pad 0;
&:after {
/* Or @extend clearfix */
content: "";
display: table;
clear: both;
}
}
[class*='col-'] {
float: left;
padding-right: $pad;
.grid &:last-of-type {
padding-right: 0;
}
}
.col-2-3 {
width: 66.66%;
}
.col-1-3 {
width: 33.33%;
}
.col-1-2 {
width: 50%;
}
.col-1-4 {
width: 25%;
}
.col-1-8 {
width: 12.5%;
}
/* Opt-in outside padding */
.grid-pad {
padding: $pad 0 $pad $pad;
[class*='col-']:last-of-type {
padding-right: $pad;
}
}
</code></pre></div></div>
<h2 id="模块">模块</h2>
<p>我喜欢这些网格内工作的“模块”。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><div class="grid">
<div class="col-2-3">
<article class="module">
stuff
</article>
<article class="module">
stuff
</article>
</div>
<div class="col-1-3">
<aside class="module">
Sidebar stuff. Sub modules?
</aside>
</div>
</div>
</code></pre></div></div>
<p>将内容分解成块这种方法看起不错。 额外的副作用是每个模块可以自己的内边距,来是文本内容和网格的边缘保持距离。</p>
<h2 id="成果">成果</h2>
<p>这是在CodePen上的一个<a href="http://codepen.io/chriscoyier/pen/eGcLw">例子</a></p>
<h2 id="浏览器不支持">浏览器不支持</h2>
<p>工作仅仅在IE 8+,和所有其他标准的东西看起来是好的。 如果你需要支持IE 7,你将不得不做一些其他的工作)。</p>
<p>此外,Flexbox让这变得更好更容易(有多种方法,包括从新设置盒子模型),但我认为,我们大约需要一年事件,直到我们可以开始考虑使用它。</p>
<p>相关</p>
<p>查看<a href="http://oocss.org/grids_docs.html">OOCSS grids</a></p>
<p>注:本文为翻译文章,原文章名称《<a href="http://css-tricks.com/dont-overthink-it-grids/">Don’t Overthink It Grids</a>》</p>
Javascript:10天设计一门语言
2013-06-22T00:00:00+00:00
http://yanhaijing.com/javascript/2013/06/22/javascript-designing-a-language-in-10-days
<p>演进和使用的JavaScript是早在1995年开发的一种语言,真的是刚刚起步。</p>
<p>网景公司在1995年四月聘请Brendan Eich ,他被告知,他有10天时间创造并制作了一种将在Netscape的浏览器中运行,以原型为工作方式的编程语言。那时候,Web创新的步伐是激烈的,由于微软突然使互联网的焦点集中在它即将发布的Windows 95操作系统作为对新兴Netscape的浏览器和服务器产品的系统发布的回应。</p>
<p>当时网景得到如此多的来自微软的关注,因为网景考虑将Web浏览器和服务器作为一个分布式操作系统一种新形式,而不仅仅是一个单一的应用程序的。自从Mosaic在1993年首次亮相,网络便成为可移植的,跨越Windows,Macintosh和Unix,并给了软件开发者他们可以为所有这些环境中开发应用程序的希望。</p>
<p>但是,仅凭HTML本身去定义一个新的应用程序开发环境或操作系统,是远远不够。为了巩固便携式操作系统的概念,网络和网景公司需要便携式的编程语言。</p>
<p>Sun公司的Java语言似乎是便携式重量级应用的解决方案。一种产生的字节代码并运行在Java虚拟机中的编译语言,Java支持丰富的来自C++的面向对象的模式并且似乎能够能够实现和Ç++和C相似的性能.Java 是Web对Microsoft的Visual C + +的答案。</p>
<h2 id="走进javascript">走进javascript</h2>
<p>众所周知Java是一个丰富的,复杂的,编译的,专门针对专业程序员的语言。Netscape和其他人一样都想要一个轻量级的解释语言作为对java的补充。这种语言需要呼应和微软的Visual Basic和解释和相像,方便嵌入在网页中适合非科班出身的程序员。根据艾希的回忆:</p>
<p>回到1995年的5月,如果我在JavaScript中加入了类,我会被告知这是太像Java或者是JavaScript是在与Java竞争…</p>
<p>我根据市场营销原因,使它看起来很像Java,但不能使它太像java,javascript需要是一个愚蠢的小弟弟语言。</p>
<p>鉴于上面所有这些要求,约束,和局限性,布兰登 艾希所有的时间非常紧迫,他必须制作了一个基于原型工作的,既能够满足太阳公司的需求又能够赶上Netscape的2.0 B测试版的发布时间表。</p>
<h2 id="技术的启示">技术的启示</h2>
<p>虽然对大多数程序员来说日程和约束可能已经不可能反生的问题,布兰登 艾希建设新的编程语言已经有很长的历史了。从他作为一个学生开始在伊利诺伊大学里的经验,在那里他建立语言只是进行语法实验。在硅谷图形(美国计算机公司),布兰登 艾希创造出来的语言,可以用来构建网络监控工具的扩展。</p>
<p>很显然,对于布兰登 艾希来说建造一个 “再一次” 语言 并不是这困难的部分,对布兰登 艾希来说这最困难的部分是建造一个复杂的,强大的语言,但却被禁止使用保留用于Java的面向对象的语法。他想在JavaScript中嵌入先进的功能,并且不涉及java语言的语法。所以导致这门语言最初看起来似乎很简单,重量轻,尚未成熟的程序员将能够很轻易的利用其潜在的能力。</p>
<p>像许多其他语言一样,JavaScript的基本语法来自C语言,包括大括号,分号和保留字。JavaScript是轻便的,和具备简单的语义和更好的动态内存特性的友好的C语言版本。因为一个典型的网页的生命周期的持续时间从几秒钟到几分钟不等,JavaScript可以采取一种非常简单的方法,去实现并发性和内存管理。</p>
<p>布兰登 艾希建立了一个简化的对象模型,组合结构来自C语言,模式来自Smalltalk和LISP提供的数据和代码之间的对称性。超卡事件模型启发在HTML文档中添加事件的模式。面向对象的模式是可行的,但通过用原型在运行时的语义(作为Self的代替),而不是编译器支持的类语法(如在Java和C++中)。</p>
<h2 id="一鸣惊人">一鸣惊人?</h2>
<p>几乎所有成功的编程语言需要一个2.0版本,在他们正真大步向前发展之前,但但现在为止,我们还没有看到,也可能永远不会看到一个JavaScript2.0。没有什么能建造在10天时间就是完美的,可是,一旦某些东西被释放到野外,错误或不完善的地方迅速成为必不可少的特色,并且是几乎不可能改变。根据布兰登 艾希回忆:</p>
<p>在开始时为了生存,JavaScript拥有的足够的好东西。如果你回想在20世纪90年代,JavaScript是被人们所诅咒的,因为它主要用于在您的浏览器的底部状态栏的滚动消息或闪烁图像。随着JavaScript获得一些进化方面的改进[在20世纪90年代后期]通过[ECMA]标准的过程。在2004年和2005年,它变得速度足够够快,足够够好去催生Web 2.0革命。</p>
<p>尽管JavaScript的最初版本可能并不完美,但它首次采用了比较简单的应用程序,所以使它有时间在幕后来慢慢演变,并解决其早期的弱点。此外,由于JavaScript的丰富对其其运行时的支持,而不是在它的语言的语法,提高JavaScript实现,而无需改变现有的JavaScript程序的语法是比较简单的。</p>
<h2 id="当今时代">当今时代</h2>
<p>当Ajax革命开始时,JAVASCRIPT已经在浏览器存在将近10年,移动的JavaScript成为主流应用的重要组成部分的发展。微软触发Ajax在Web接口的统治地位,通过加入XMLHttpRequest的功能在它的Internet Explorer浏览器。其他浏览器迅速加入了类似的功能,允许JavaScript从服务器检索数据和更新HTML文档,而不需要一整页的请求 - 响应周期。有了这个创新,高度互动的用户界面功能被搬进创建类似桌面的经验日益丰富的应用程序,如浏览器谷歌邮件和谷歌地图。</p>
<p>由于每一页所需的代码和数据量的增加,它暴露了JavaScript基于浏览器实现运行时的弱点。不在是运行每分钟左右就重新启动JavaScript,同一个页面会在浏览器中停留数分钟伴随着巨大的,动态的在内存中的数据元素和几乎连续的后台与服务器通信。谷歌建立了自己的Chrome浏览器的V8 JavaScript解释器,把浏览器市场上的通知,绝不会容忍低性能的JavaScript实现。市场也迅速跟进和改进的JavaScript解释器全线表现。</p>
<p>例如Node.js这样的项目,使人们有可能使用JavaScript的语言构建Web应用程序的服务器元素。因为JavaScript已经从一开始急就是基于事件的,建设管理的复杂性,多线程不使用JavaScript的高度可扩展的Web应用程序变得很自然的。</p>
<p>随着HTML5的出现,这是完全有可能,JavaScript将很快成为主导移动和桌面应用程序的编程语言。演进和使用的JavaScript是真的才刚刚开始,这是令人印象深刻的10天,早在1995年开发的一种语言。</p>
<p>要查看我对艾希的采访,访问这里http://youtu.be/IPxQ9kEaF8c。</p>
函数要多小才够好——谈小函数之道
2012-05-29T00:00:00+00:00
http://yanhaijing.com/program/2012/05/29/to-use-little-function
<blockquote>
<p>“设计良好的函数往往比较小,而过大函数的设计往往一塌糊涂,或者存在很大的优化空间。”</p>
</blockquote>
<p>也许你认为讨论函数的大小没有必要,原因是<strong>函数设计的本质是内聚,它的大小只是它的表现形式</strong>。
而上面的原因有必要让我们讨论一下函数的大小问题。</p>
<p>我对函数的核心思路:我提出代码最小处理单元的概念:一个基本操作(赋值,比较等),一个函数调用(包括调用后判断返回值进行判断)都看成一个最小处理单元。
那么,一个函数,最小处理单元合理的个数范围在7以内。
如果超过了7,你就要考虑把他们拆分成多个函数了(为什么是7?人同时能够处理的信息不超过7个)。</p>
<p><strong>最小数目没有限制,即便是只有1个,也有存在的必要</strong>。</p>
<p>在下面的情况下我会将函数拆分为更小的函数:</p>
<h3 id="1一眼不能够看到函数所有的代码">1、一眼不能够看到函数所有的代码。</h3>
<p>如果函数过长,无法一眼看到一个函数所有的代码,我会毫不犹豫的拆分。
我不想让读者去翻屏,也不想让读者前顾后盼,顾此失彼。
<strong>漂亮的函数应该让读者一眼就知道他在做什么以及怎么做的</strong>。</p>
<h3 id="2局部变量过多">2、局部变量过多。</h3>
<p>如果局部变量超过七个,我会考虑拆分函数。
变量过多意味着我要记录太多的状态,这会加重我大脑的负担,同时要考虑太多的东西。
这也同时意味着我可能没有对函数功能进行深入的思考。</p>
<h3 id="3太多的缩进">3、太多的缩进。</h3>
<p>太多的缩进意味着太多的嵌套,要么是循环,要么是判断,都会导致复杂的逻辑。</p>
<h3 id="4不处于同一抽象层次">4、不处于同一抽象层次。</h3>
<p>举例,有一个初始化函数,需要初始化配置数据,套接字,数据库连接,通道状态。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Void init()
{
Config_init();
Socket_init();
Db_init();
Int I = 0;
For (I = 0;I < max_chn_num;i++)//初始化所有通道
{
G_user_chn[i].status = status_init;
……
}
}
</code></pre></div></div>
<p>上个函数中对所有通道的初始化一块代码就和其他的不处于一个抽象层次,我们应该将它封装起来:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>void chn_init()
{
Int I = 0;
For (I = 0;I < max_chn_num;i++)//初始化所有通道
{
G_user_chn[i].status =status_init;
……
}
}
</code></pre></div></div>
<p>函数最小可以有多小,它存在的意义</p>
<p>我见过的最优秀的函数:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>int max(int a, intb)
{
return a > b?a:b;
}
</code></pre></div></div>
<p>这个函数很小,只有一行,但是他存在的意义在于:在函数的调用点,我们一眼就知道是获取a和b中的最大值,而不是分析 <code class="language-plaintext highlighter-rouge">a > b?a:b</code> 的逻辑。
这样可以节省程序员的脑力成本,从而达到一个目的:<strong>漂亮的函数应该让读者一眼就知道他在做什么以及怎么做的</strong>。</p>
<h3 id="最后的建议">最后的建议:</h3>
<p>在对新员工培训的过程中,发现程序员新手一般对函数的大小不够敏感。
所以,我建议你可以多尝试编写10行左右(甚至更小)的函数,慢慢你会发现小函数原来具有大威力。</p>
<h2 id="注">注</h2>
<p>原文:<a href="http://justjavac.com/other/2012/05/29/to-use-little-function.html">http://justjavac.com/other/2012/05/29/to-use-little-function.html</a></p>
为什么++[[]][+[]]+[+[]]=10?
2012-05-24T00:00:00+00:00
http://yanhaijing.com/javascript/2012/05/24/can-you-explain-why-10
<p>首先,问“<a href="http://stackoverflow.com/q/7202157/343194" title="Can you explain why ++[[]][+[]]+[+[]] = “10”?">这个问题(英文)</a>”的人是个天才,他怎么会遇到这样的一个问题。
其次,回答这个问题的人更是一个天才,我难以想象他会回答这个问题,更难以想象的是,他的回答是如此的详细和丰富和完整,真正称得上诲人不倦。</p>
<p>既然遇到了这个问题,我们不妨也跟着提高一下。</p>
<p>这是一个<a href="http://justjavac.com/javascript/2012/04/05/you-dont-know-javascript.html">Javascript 语言题目</a>,一个完全有效的等式,不信自己可以试一下,下面看看高人的题解:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>++[[]][+[]]+[+[]]
</code></pre></div></div>
<p>如果把这段表达式拆分开来,它相等于:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>++[[]][+[]]
+
[+[]]
</code></pre></div></div>
<p>在 JavaScript 里,<code class="language-plaintext highlighter-rouge">+[] === 0</code> 是完全正确的。 <code class="language-plaintext highlighter-rouge">+</code> 会把一些字符转化成数字,在这里,这个式子会变成 <code class="language-plaintext highlighter-rouge">+""</code> 或 <code class="language-plaintext highlighter-rouge">0</code>。</p>
<p>因此,我们可以简化一下(<code class="language-plaintext highlighter-rouge">++</code> 比 <code class="language-plaintext highlighter-rouge">+</code> 有更高的优先级):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>++[[]][0]
+
[0]
</code></pre></div></div>
<p>因为 <code class="language-plaintext highlighter-rouge">[[]][0]</code> 的意思是:获取 <code class="language-plaintext highlighter-rouge">[[]]</code> 的第一个元素,这就得出了下面的结果:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">[[]][0]</code> 返回内部数组 (<code class="language-plaintext highlighter-rouge">[]</code>)。根据语言规范,我们说 <code class="language-plaintext highlighter-rouge">[[]][0] === []</code> 是不正确的,但让我们把这个内部数组称作 A,以避免错误的写法。</li>
<li><code class="language-plaintext highlighter-rouge">++[[]][0] == A + 1</code>, 因为 <code class="language-plaintext highlighter-rouge">++</code> 的意思是”加一”。</li>
<li><code class="language-plaintext highlighter-rouge">++[[]][0] === +(A + 1)</code>;换句话说,你得到的永远是个数值( <code class="language-plaintext highlighter-rouge">+1</code> 并不一定得到的是个数值,但 <code class="language-plaintext highlighter-rouge">++</code> 一定是)。</li>
</ul>
<p>同样,我们可以把这一堆代码简化的更清晰。让我们把 A 换回成 <code class="language-plaintext highlighter-rouge">[]</code> :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+([] + 1)
+
[0]
</code></pre></div></div>
<p>在 JavaScript 里,这也是正确的:<code class="language-plaintext highlighter-rouge">[] + 1 === "1"</code>,因为 <code class="language-plaintext highlighter-rouge">[] == ""</code> (这相当于一个空的数组的内部元素连接),于是:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+([] + 1) === +("” + 1),并且
+("” + 1) === +("1"),并且
+("1") === 1
</code></pre></div></div>
<p>让我们再次简化一下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1
+
[0]
</code></pre></div></div>
<p>同样,在 Javascript 里,这是正确的:<code class="language-plaintext highlighter-rouge">[0] == "0"</code>,因为这是相当于一个有一个元素的数组的内部元素的连接。
各元素会使用,分隔。
当只有一个元素时,你可以推论出这个过程的结果就是它自身的第一个元素。</p>
<p>所以,最终我们得到 (数字 + 字符串 = 字符串):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1
+
"0"
=== "10" // 耶!
</code></pre></div></div>
<p>如果你想知道更详细的信息,请访问<a href="http://stackoverflow.com/q/7202157/343194" title="Can you explain why ++[[]][+[]]+[+[]] = “10”?">这里(英文)</a>。</p>
<h2 id="注">注</h2>
<p>本文问转载文章,原文:<a href="http://justjavac.com/javascript/2012/05/24/can-you-explain-why-10.html">http://justjavac.com/javascript/2012/05/24/can-you-explain-why-10.html</a></p>
如何构建优质代码
2012-04-13T00:00:00+00:00
http://yanhaijing.com/program/2012/04/13/how-to-build-quality-code
<h2 id="1dry">1.DRY</h2>
<p>DRY 是一个最简单的法则,也是最容易被理解的。
但它也可能是最难被应用的(因为要做到这样,我们需要在泛型设计上做相当的努力,
这并不是一件容易的事)。</p>
<p>它意味着,当我们在两个或多个地方发现一些相似的代码的时候,
我们需要把他们的共性抽象出来,形成一个唯一的新方法,并且改变现有的代码,
让他们以一些合适的参数调用这个新的方法。</p>
<p>DRY 这一法则可能是编程界中最通用的法则了。
目前为止,应该没有哪个程序员对这一法则存有异议。
但是,我们却能发现,一些程序在编写单元测试时忘记了这一法则:
让我们想象一下,当你改变一个类的若干接口,如果你没有使用DRY,
那么,那些通过调用一系例类的接口的unit test的程序,都需要被手动的更改。</p>
<p>比如:如果你的unit test的诸多test cases中没有使用一个标准共有的构造类的方法,
而是每个test case自己去构造类的实例,那么,
当类的构造函数被改变时,你需要修改多少个test cases啊。
这就是不使用DRY法则所带来的恶果。</p>
<h2 id="2短小的方法">2.短小的方法</h2>
<p>至少,我们有下面三个不错的理由要求程序员们写下短小的方法。</p>
<ul>
<li><p>代码会变得更容易阅读。</p></li>
<li><p>代码会变得更容易重用(短方法可以减少代码间的耦合程度)</p></li>
<li><p>代码会变得更容易测试。</p></li>
</ul>
<h2 id="3良好的命名规范">3.良好的命名规范</h2>
<p>使用不错的统一的命名规范可以让你的程序变得更容易阅读和维护,
当一个类,一个函数,一个变量的名字达到了那种可以“望文生义”的境界时,
我们就可以少一些文档,少一些沟通。</p>
<h2 id="4赋予每个类正确的职责">4.赋予每个类正确的职责</h2>
<p>一个类,一个职责,这类规则可以参考一下类的SOLID 法则。
但我们这里强调的不是一种单一的职责,而是一个正确的职责。
如果你有一个类叫Customer,我们就不应该让这个类有sales的方法,
我们只能让这个类有和Customer有最直接关系的方法。</p>
<h2 id="5把代码组织起来">5.把代码组织起来</h2>
<p>把代码组织起来有两组层次。</p>
<p>物理层组织:无论你使用什么样的目录,包(package)或命名空间(namespace)等的结构,
你需要把你的类用一种标准的方法组织起来,这样可以方便查找。
这是一种物理性质的代码组织。</p>
<p>逻辑层组织: 所谓逻辑层,主要是说,我们如果把两个不同功能的类或方法通过某种规范联系和组织起来。
这里主要关注的是程序模块间的接口。
这就是我们经常见到的程序模块的架构。</p>
<h2 id="6创建大量的单元测试">6.创建大量的单元测试</h2>
<p>单元测试是最接近BUG的地方,也是修改BUG成本最低的地方,同样也是决定整个软件质量好坏的地方。所以,只要有可能,你就应该写更多的,更好的单元测试案例,这样当你未来有相应代码改变的时候,你可以很简单的知道代码的改变是否影响了其它单元。</p>
<h2 id="7经常重构你的代码">7.经常重构你的代码</h2>
<p>软件开发是一种持续的发现的过程,从而让你的代码可以跟上最新的实际需求的变化。
所以,我们要经常重构自己的代码来跟上这样的变化。
当然,重构是有风险的,并不是所有的重构都是成功的,也不是我们随时都可以重构代码。</p>
<p>下面是两个重构代码的先要条件,以避免让你引入更多的BUG,或是把本来就烂的代码变得更烂。</p>
<p>有大量的单元测试来测试。正如前面所说,重构需要用大量的单元测试来做保障和测试。</p>
<p>每次重构都不要大,用点点滴滴的小的重构来代替那种大型的重构。
有太多的时候,当我们一开始计划重构2000行代码,而在3个小时后,
我们就放弃这个计划并把代码恢复到原始的版本。
所以,我们推荐的是,重构最好是从点点滴滴积累起来的。</p>
<h2 id="8程序注释是邪恶的">8.程序注释是邪恶的</h2>
<p>这一条一定是充满争议的,大多数程序员都认为程序注释是非常好的,
是的,没错,程序注释在理论上是非常不错的。</p>
<p>但是,在实际过程中,程序员们写出来的注释却是很糟糕的,
从而导致了程序注释成为了一切邪恶的化身,也导致了我们在阅读程序时,
大多数时候,我们都不读注释而直接读代码。</p>
<p>所以,在这里,我们并不是鼓励不写注释,而是——如果你的注释写得不够好的话,
那么,你还不如把更重要的时间花在重构一下你的代码,让你的代码更加易读,
更加清楚,这会比注释更好。</p>
<h2 id="9注重接口而不是实现">9.注重接口,而不是实现</h2>
<p>这是一个最经典的规则了。</p>
<p>接口注重的是——“What”,是抽象,实现注重的是——“How”,是细节。
接口相当于一种合同契约,而实际的细节相当于对这种合同契约的一种运作和实现。
运作是可以很灵活的,而合同契约则需要是相对需要稳定和不变的。</p>
<p>如果,一个接口没有设计好,而需要经常性的变化的话,那我们可以试想一下,这带来的后果,
这绝对会是一件成本很大的事情。</p>
<p>所以,在软件开发和调试中,接口是重中之重,而不是实现。
然而我们的程序员总是注重于实现细节,所以他们局部的代码写的非常不错,
但软件整体却设计得相对较差。
这点需要我们多多注意。</p>
<h2 id="10代码审查机制">10.代码审查机制</h2>
<p>所有人都会出错,一个人出错的概率是很大的,两个人出错的概率就会小一些,
人多一些,出错的概率就会越来越小。</p>
<p>因为,人多了,就能够从不同的角度看待一个事情,虽然这样可能导致无效率的争论,
但比起软件产品release后出现问题的维护成本,这点成本算是相当值得的。</p>
<p>所以,这就是我们需要让不同的人来review代码,代码审查机制不但是一种发现问题的最有效的机制,
同时也是一种可以知识共享的机制。</p>
<p>当然,对于Code Review来说,下面有几个基本原则:</p>
<p>审查者的能力一定要大于或等于代码作者的能力,
不然,代码审查就成了一种对新手的training。</p>
<p>而且,为了让审查者真正负责起来,而不是在敷衍审查工作,
我们需要让审查者对审查过的代码负主要责任,而不是代码的作者。</p>
<p>另外,好的代码审查应该不是当代码完成的时候,
而是在代码编写的过程中,不断地迭代代码审查。
好的实践,无论代码是否完成,代码审核需要几天一次地不断地进行。</p>
<h2 id="注">注</h2>
<p>原文:<a href="http://justjavac.com/other/2012/04/13/how-to-build-quality-code.html">http://justjavac.com/other/2012/04/13/how-to-build-quality-code.html</a></p>
写了10年Javascript未必全了解的连续赋值运算
2012-04-05T00:00:00+00:00
http://yanhaijing.com/javascript/2012/04/05/javascript-continuous-assignment-operator
<h2 id="一引子">一、引子</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = {n:1};
a.x = a = {n:2};
alert(a.x); // --> undefined
</code></pre></div></div>
<p>看 jQuery 源码 时发现的这种写法。
以上第二句 <code class="language-plaintext highlighter-rouge">a.x = a = {n:2}</code> 是一个连续赋值表达式。
这个连续赋值表达式在引擎内部究竟发生了什么?是如何解释的?</p>
<h2 id="二猜想">二、猜想</h2>
<p><strong>猜想1</strong>:从左到右赋值,<code class="language-plaintext highlighter-rouge">a.x</code> 先赋值为 <code class="language-plaintext highlighter-rouge">{n:2}</code>,但随后 a 赋值为 <code class="language-plaintext highlighter-rouge">{n:2}</code>,
即 a 被重写了,值为 <code class="language-plaintext highlighter-rouge">{n:2}</code>,新的 a 没有 x 属性,因此为 <code class="language-plaintext highlighter-rouge">undefined</code>。</p>
<p>步骤如下</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a.x = {n:2};
a = {n:2};
</code></pre></div></div>
<p>这种解释得出的结果与实际运行结果一致,貌似是对的。</p>
<blockquote>
<p>注意「猜想1」中 a.x 被赋值过。</p>
</blockquote>
<p><strong>猜想2</strong>:从右到左赋值,a 先赋值为 <code class="language-plaintext highlighter-rouge">{n:2}</code>,<code class="language-plaintext highlighter-rouge">a.x</code> 发现 a 被重写后(之前 a 是 <code class="language-plaintext highlighter-rouge">{a:1}</code>),
<code class="language-plaintext highlighter-rouge">a.x = {n:2}</code> 引擎限制 <code class="language-plaintext highlighter-rouge">a.x</code> 赋值,忽略了。</p>
<p>步骤如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a = {n:2};
a.x 未被赋值{n:2}
</code></pre></div></div>
<p>等价于 <code class="language-plaintext highlighter-rouge">a.x = (a = {n:2})</code>,即执行了第一步,这样也能解释 <code class="language-plaintext highlighter-rouge">a.x</code> 为 <code class="language-plaintext highlighter-rouge">undefined</code> 了。</p>
<blockquote>
<p>注意「猜想2」中 a.x 压根没被赋值过。</p>
</blockquote>
<h2 id="三证明">三、证明</h2>
<p>上面两种猜想相信多数人都有,群里讨论呆呆认为是「猜想1」, 我认为是「猜想2」。其实都错了。
我忽略了引用的关系。</p>
<p>如下,加一个变量 b,指向 a。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = {n:1};
var b = a; // 持有a,以回查
a.x = a = {n:2};
alert(a.x);// --> undefined
alert(b.x);// --> [object Object]
</code></pre></div></div>
<p>发现 <code class="language-plaintext highlighter-rouge">a.x</code> 仍然是 <code class="language-plaintext highlighter-rouge">undefined</code>,神奇的是 <code class="language-plaintext highlighter-rouge">b.x</code> 并未被赋值过(比如:<code class="language-plaintext highlighter-rouge">b.x={n:2})</code>,却变成了 <code class="language-plaintext highlighter-rouge">[object Object]</code>。
b 是指向 <code class="language-plaintext highlighter-rouge">a({n:1})</code> 的,只有 <code class="language-plaintext highlighter-rouge">a.x = {n:2}</code> 执行了才说明b是有x属性的。
实际执行过程:从右到左,a 先被赋值为 <code class="language-plaintext highlighter-rouge">{n:2}</code>,随后 <code class="language-plaintext highlighter-rouge">a.x</code> 被赋值 <code class="language-plaintext highlighter-rouge">{n:2}</code>。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a = {n:2};
a.x = {n:2};
</code></pre></div></div>
<p>等价于</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a.x = (a = {n:2});
</code></pre></div></div>
<p>与猜想2的区别在于 <code class="language-plaintext highlighter-rouge">a.x</code> 被赋值了,猜想2中并未赋值。
最重要的区别,第一步 <code class="language-plaintext highlighter-rouge">a = {n:2}</code> 的 a 指向的是新的对象 <code class="language-plaintext highlighter-rouge">{n:2}</code>, 第二步 <code class="language-plaintext highlighter-rouge">a.x = {n:2}</code> 中的 a 是 <code class="language-plaintext highlighter-rouge">{a:1}</code>。</p>
<p>即在这个连等语句</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>a.x = a = {n:2};
</code></pre></div></div>
<p>a.x 中的a指向的是 <code class="language-plaintext highlighter-rouge">{n:1}</code>,a 指向的是 <code class="language-plaintext highlighter-rouge">{n:2}</code>。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> a.x = a = {n:2}
│ │
{n:1}<──┘ └─>{n:2}
</code></pre></div></div>
<h2 id="四解惑">四:解惑</h2>
<p>这篇写完,或许部分人看完还是晕晕的。
因为里面的文字描述实在是绕口。</p>
<p>最初我在理解这个连等赋值语句时</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>var a = {n:1};
a.x = a = {n:2};
</code></pre></div></div>
<p>认为引擎会限制 <code class="language-plaintext highlighter-rouge">a.x</code> 的重写(a 被重写后),实际却不是这样的。
指向的对象已经不同了。引擎也没有限制 <code class="language-plaintext highlighter-rouge">a.x={n:2}</code> 的重写。</p>
<h2 id="五结束">五:结束</h2>
<p>呵,以另一个连续赋值题结束。
fun 执行后,这里的 变量 b 溢出到 fun 外成为了全局变量。</p>
<p>想到了吗?</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>function fun(){
var a = b = 5;
}
fun();
alert(typeof a); // --> undefined
alert(typeof b); // --> number
</code></pre></div></div>
<h2 id="注">注</h2>
<p>原文:<a href="http://justjavac.com/javascript/2012/04/05/javascript-continuous-assignment-operator.html">http://justjavac.com/javascript/2012/04/05/javascript-continuous-assignment-operator.html</a></p>
在Windows系统配置Jekyll
2011-12-30T00:00:00+00:00
http://yanhaijing.com/jekyll/2011/12/30/run-jekyll-on-window
<p><a href="http://jekyllrb.com/">Jekyll</a> 是一个简单的网站静态页面生成工具。由于是用Ruby语音编写的,所以在Windows系统上配置起来还是稍微有点繁琐的。具体过程如下:</p>
<ol>
<li>安装Ruby:在Windows系统上当然使用rubyinstaller了, <a href="http://rubyinstaller.org/downloads/">猛击我下载</a> (笔者使用的版本是:Ruby 1.9.3-p545)</li>
<li>安装Ruby DevKit: <a href="https://github.com/downloads/oneclick/rubyinstaller/DevKit-tdm-32-4.5.2-20111229-1559-sfx.exe">猛击我下载</a></li>
<li>安装Jekyll</li>
<li>安装Python: <a href="http://portablepython.com/wiki/PortablePython3.2.1.1">猛击我下载</a></li>
<li>安装Pygments</li>
</ol>
<p>以下是详细步骤:</p>
<p>1.从rubyinstaller下载安装包并安装到某个磁盘中,比如:E:\Ruby192,在安装界面把所有的选项都勾选上;</p>
<p><img src="/blog/140.png" alt="" /></p>
<p>2.把下载的DevKit解压到某个目录,比如 E:\devkit , 在该目录中运行如下命令:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> ruby dk.rb init
</code></pre></div></div>
<p>来生成一个config.xml配置文件,该配置文件中包含了前面的Ruby安装目录 (E:\Ruby192)
然后运行如下命令</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> ruby dk.rb install
</code></pre></div></div>
<p>3.然后运行如下命令安装Jekyll:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> gem install jekyll --version=2.5.3
</code></pre></div></div>
<p>现在可以开始使用jekyll了。如果您还需要使用代码高亮工具,则需要继续安装Pygments ,过程如下:</p>
<p>4.安装下载的Portable Python(笔者使用的是PortablePython_3.2.1.1.exe),安装目录为E:\Portable_Python_3.2.1.1</p>
<p>然后把E:\Portable_Python_3.2.1.1\App\Scripts和E:\Portable_Python_3.2.1.1\App目录分别添加到系统Path环境变量中</p>
<p>5.把下载的distribute-0.6.49.tar.gz解压的某个目录(比如:E:\distribute-0.6.28), <a href="http://pypi.python.org/pypi/distribute#downloads">猛击我下载</a></p>
<p>在该目录中运行如下命令:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> python distribute_setup.py
</code></pre></div></div>
<p>6.然后通过如下命令来安装pygments:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> easy_install Pygments
</code></pre></div></div>
<p>最后需要修改2处Bug:</p>
<ol>
<li>
<p>Pygmentize中的Bug:修改如下文件
E:\Ruby192\lib\ruby\gems\1.9.1\gems\albino-1.3.3\lib\albino.rb
修改的内容参考 <a href="https://gist.github.com/1185645">这里</a></p>
</li>
<li>
<p>由于中文XP系统使用的GBK编码,GBK编码导致jekyll处理的bug,修改E:\Ruby192\lib\ruby\gems\1.9.1\gems\jekyll-0.11.2\lib\jekyll\convertible.rb这个文件,修改方式 <a href="https://github.com/imathis/octopress/issues/232#issuecomment-2480736">参考这里</a></p>
</li>
</ol>
<p>然后就可以使用Jekyll了,在生成静态页面的时候 可能还会出现 GBK字符不能编码的问题,但是不影响生成网页了。</p>
<h2 id="更新">更新</h2>
<p>笔者最近安装了最新版的jekyll,已经不存在需要修复两处bug的问题了,安装到第六步就ok了,上面提到的gbk编码问题,可用如下方法解决,在cmd窗口输入如下命令,临时改变编码。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> chcp 65001
</code></pre></div></div>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="http://www.madhur.co.in/blog/2011/09/01/runningjekyllwindows.html">Running Jekyll on Windows</a></li>
<li><a href="http://chxt6896.github.io/blog/2012/02/13/blog-jekyll-native.html">Jekyll 本地调试之若干问题</a></li>
<li><a href="http://www.ruanyifeng.com/blog/2012/08/blogging_with_jekyll.html">搭建一个免费的,无限流量的Blog</a></li>
<li><a href="http://wowubuntu.com/markdown/">Markdown 语法说明 (简体中文版)</a></li>
<li><a href="http://jekyllcn.com/">jekyll中文站</a></li>
<li><a href="http://markable.in/editor/">markdown在线编辑器</a></li>
<li><a href="http://markdownpad.com/">markdownpad 2</a></li>
</ul>
Jekyll Introduction
2011-12-29T00:00:00+00:00
http://yanhaijing.com/jekyll/2011/12/29/jekyll-introduction
<p>This Jekyll introduction will outline specifically what Jekyll is and why you would want to use it.
Directly following the intro we’ll learn exactly <em>how</em> Jekyll does what it does.</p>
<h2 id="overview">Overview</h2>
<h3 id="what-is-jekyll">What is Jekyll?</h3>
<p>Jekyll is a parsing engine bundled as a ruby gem used to build static websites from
dynamic components such as templates, partials, liquid code, markdown, etc. Jekyll is known as “a simple, blog aware, static site generator”.</p>
<h3 id="examples">Examples</h3>
<p>This website is created with Jekyll. <a href="https://github.com/mojombo/jekyll/wiki/Sites">Other Jekyll websites</a>.</p>
<h3 id="what-does-jekyll-do">What does Jekyll Do?</h3>
<p>Jekyll is a ruby gem you install on your local system.
Once there you can call <code class="language-plaintext highlighter-rouge">jekyll --server</code> on a directory and provided that directory
is setup in a way jekyll expects, it will do magic stuff like parse markdown/textile files,
compute categories, tags, permalinks, and construct your pages from layout templates and partials.</p>
<p>Once parsed, Jekyll stores the result in a self-contained static <code class="language-plaintext highlighter-rouge">_site</code> folder.
The intention here is that you can serve all contents in this folder statically from a plain static web-server.</p>
<p>You can think of Jekyll as a normalish dynamic blog but rather than parsing content, templates, and tags
on each request, Jekyll does this once <em>beforehand</em> and caches the <em>entire website</em> in a folder for serving statically.</p>
<h3 id="jekyll-is-not-blogging-software">Jekyll is Not Blogging Software</h3>
<p><strong>Jekyll is a parsing engine.</strong></p>
<p>Jekyll does not come with any content nor does it have any templates or design elements.
This is a common source of confusion when getting started.
Jekyll does not come with anything you actually use or see on your website - you have to make it.</p>
<h3 id="why-should-i-care">Why Should I Care?</h3>
<p>Jekyll is very minimalistic and very efficient.
The most important thing to realize about Jekyll is that it creates a static representation of your website requiring only a static web-server.
Traditional dynamic blogs like Wordpress require a database and server-side code.
Heavily trafficked dynamic blogs must employ a caching layer that ultimately performs the same job Jekyll sets out to do; serve static content.</p>
<p>Therefore if you like to keep things simple and you prefer the command-line over an admin panel UI then give Jekyll a try.</p>
<p><strong>Developers like Jekyll because we can write content like we write code:</strong></p>
<ul>
<li>Ability to write content in markdown or textile in your favorite text-editor.</li>
<li>Ability to write and preview your content via localhost.</li>
<li>No internet connection required.</li>
<li>Ability to publish via git.</li>
<li>Ability to host your blog on a static web-server.</li>
<li>Ability to host freely on GitHub Pages.</li>
<li>No database required.</li>
</ul>
<h1 id="how-jekyll-works">How Jekyll Works</h1>
<p>The following is a complete but concise outline of exactly how Jekyll works.</p>
<p>Be aware that core concepts are introduced in rapid succession without code examples.
This information is not intended to specifically teach you how to do anything, rather it
is intended to give you the <em>full picture</em> relative to what is going on in Jekyll-world.</p>
<p>Learning these core concepts should help you avoid common frustrations and ultimately
help you better understand the code examples contained throughout Jekyll-Bootstrap.</p>
<h2 id="initial-setup">Initial Setup</h2>
<p>After <a href="/index.html#start-now">installing jekyll</a> you’ll need to format your website directory in a way jekyll expects.
Jekyll-bootstrap conveniently provides the base directory format.</p>
<h3 id="the-jekyll-application-base-format">The Jekyll Application Base Format</h3>
<p>Jekyll expects your website directory to be laid out like so:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.
|-- _config.yml
|-- _includes
|-- _layouts
| |-- default.html
| |-- post.html
|-- _posts
| |-- 2011-10-25-open-source-is-good.markdown
| |-- 2011-04-26-hello-world.markdown
|-- _site
|-- index.html
|-- assets
|-- css
|-- style.css
|-- javascripts
</code></pre></div></div>
<ul>
<li>
<p><strong>_config.yml</strong>
Stores configuration data.</p>
</li>
<li>
<p><strong>_includes</strong>
This folder is for partial views.</p>
</li>
<li>
<p><strong>_layouts</strong>
This folder is for the main templates your content will be inserted into.
You can have different layouts for different pages or page sections.</p>
</li>
<li>
<p><strong>_posts</strong>
This folder contains your dynamic content/posts.
the naming format is required to be <code class="language-plaintext highlighter-rouge">@YEAR-MONTH-DATE-title.MARKUP@</code>.</p>
</li>
<li>
<p><strong>_site</strong>
This is where the generated site will be placed once Jekyll is done transforming it.</p>
</li>
<li>
<p><strong>assets</strong>
This folder is not part of the standard jekyll structure.
The assets folder represents <em>any generic</em> folder you happen to create in your root directory.
Directories and files not properly formatted for jekyll will be left untouched for you to serve normally.</p>
</li>
</ul>
<p>(read more: <a href="https://github.com/mojombo/jekyll/wiki/Usage">https://github.com/mojombo/jekyll/wiki/Usage</a>)</p>
<h3 id="jekyll-configuration">Jekyll Configuration</h3>
<p>Jekyll supports various configuration options that are fully outlined here:
(<a href="https://github.com/mojombo/jekyll/wiki/Configuration">https://github.com/mojombo/jekyll/wiki/Configuration</a>)</p>
<h2 id="content-in-jekyll">Content in Jekyll</h2>
<p>Content in Jekyll is either a post or a page.
These content “objects” get inserted into one or more templates to build the final output for its respective static-page.</p>
<h3 id="posts-and-pages">Posts and Pages</h3>
<p>Both posts and pages should be written in markdown, textile, or HTML and may also contain Liquid templating syntax.
Both posts and pages can have meta-data assigned on a per-page basis such as title, url path, as well as arbitrary custom meta-data.</p>
<h3 id="working-with-posts">Working With Posts</h3>
<p><strong>Creating a Post</strong>
Posts are created by properly formatting a file and placing it the <code class="language-plaintext highlighter-rouge">_posts</code> folder.</p>
<p><strong>Formatting</strong>
A post must have a valid filename in the form <code class="language-plaintext highlighter-rouge">YEAR-MONTH-DATE-title.MARKUP</code> and be placed in the <code class="language-plaintext highlighter-rouge">_posts</code> directory.
If the data format is invalid Jekyll will not recognize the file as a post. The date and title are automatically parsed from the filename of the post file.
Additionally, each file must have <a href="https://github.com/mojombo/jekyll/wiki/YAML-Front-Matter">YAML Front-Matter</a> prepended to its content.
YAML Front-Matter is a valid YAML syntax specifying meta-data for the given file.</p>
<p><strong>Order</strong>
Ordering is an important part of Jekyll but it is hard to specify a custom ordering strategy.
Only reverse chronological and chronological ordering is supported in Jekyll.</p>
<p>Since the date is hard-coded into the filename format, to change the order, you must change the dates in the filenames.</p>
<p><strong>Tags</strong>
Posts can have tags associated with them as part of their meta-data.
Tags may be placed on posts by providing them in the post’s YAML front matter.
You have access to the post-specific tags in the templates. These tags also get added to the sitewide collection.</p>
<p><strong>Categories</strong>
Posts may be categorized by providing one or more categories in the YAML front matter.
Categories offer more significance over tags in that they can be reflected in the URL path to the given post.
Note categories in Jekyll work in a specific way.
If you define more than one category you are defining a category hierarchy “set”.
Example:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---
title : Hello World
categories : [lessons, beginner]
---
</code></pre></div></div>
<p>This defines the category hierarchy “lessons/beginner”. Note this is <em>one category</em> node in Jekyll.
You won’t find “lessons” and “beginner” as two separate categories unless you define them elsewhere as singular categories.</p>
<h3 id="working-with-pages">Working With Pages</h3>
<p><strong>Creating a Page</strong>
Pages are created by properly formatting a file and placing it anywhere in the root directory or subdirectories that do <em>not</em> start with an underscore.</p>
<p><strong>Formatting</strong>
In order to register as a Jekyll page the file must contain <a href="https://github.com/mojombo/jekyll/wiki/YAML-Front-Matter">YAML Front-Matter</a>.
Registering a page means 1) that Jekyll will process the page and 2) that the page object will be available in the <code class="language-plaintext highlighter-rouge">site.pages</code> array for inclusion into your templates.</p>
<p><strong>Categories and Tags</strong>
Pages do not compute categories nor tags so defining them will have no effect.</p>
<p><strong>Sub-Directories</strong>
If pages are defined in sub-directories, the path to the page will be reflected in the url.
Example:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.
|-- people
|-- bob
|-- essay.html
</code></pre></div></div>
<p>This page will be available at <code class="language-plaintext highlighter-rouge">http://yourdomain.com/people/bob/essay.html</code></p>
<p><strong>Recommended Pages</strong></p>
<ul>
<li><strong>index.html</strong>
You will always want to define the root index.html page as this will display on your root URL.</li>
<li><strong>404.html</strong>
Create a root 404.html page and GitHub Pages will serve it as your 404 response.</li>
<li><strong>sitemap.html</strong>
Generating a sitemap is good practice for SEO.</li>
<li><strong>about.html</strong>
A nice about page is easy to do and gives the human perspective to your website.</li>
</ul>
<h2 id="templates-in-jekyll">Templates in Jekyll</h2>
<p>Templates are used to contain a page’s or post’s content.
All templates have access to a global site object variable: <code class="language-plaintext highlighter-rouge">site</code> as well as a page object variable: <code class="language-plaintext highlighter-rouge">page</code>.
The site variable holds all accessible content and metadata relative to the site.
The page variable holds accessible data for the given page or post being rendered at that point.</p>
<p><strong>Create a Template</strong>
Templates are created by properly formatting a file and placing it in the <code class="language-plaintext highlighter-rouge">_layouts</code> directory.</p>
<p><strong>Formatting</strong>
Templates should be coded in HTML and contain YAML Front Matter.
All templates can contain Liquid code to work with your site’s data.</p>
<p><strong>Rending Page/Post Content in a Template</strong>
There is a special variable in all templates named : <code class="language-plaintext highlighter-rouge">content</code>.
The <code class="language-plaintext highlighter-rouge">content</code> variable holds the page/post content including any sub-template content previously defined.
Render the content variable wherever you want your main content to be injected into your template:</p>
<pre><code>...
<body>
<div id="sidebar"> ... </div>
<div id="main">
{{content}}
</div>
</body>
...</code></pre>
<h3 id="sub-templates">Sub-Templates</h3>
<p>Sub-templates are exactly templates with the only difference being they
define another “root” layout/template within their YAML Front Matter.
This essentially means a template will render inside of another template.</p>
<h3 id="includes">Includes</h3>
<p>In Jekyll you can define include files by placing them in the <code class="language-plaintext highlighter-rouge">_includes</code> folder.
Includes are NOT templates, rather they are just code snippets that get included into templates.
In this way, you can treat the code inside includes as if it was native to the parent template.</p>
<p>Any valid template code may be used in includes.</p>
<h2 id="using-liquid-for-templating">Using Liquid for Templating</h2>
<p>Templating is perhaps the most confusing and frustrating part of Jekyll.
This is mainly due to the fact that Jekyll templates must use the Liquid Templating Language.</p>
<h3 id="what-is-liquid">What is Liquid?</h3>
<p><a href="https://github.com/Shopify/liquid">Liquid</a> is a secure templating language developed by <a href="http://shopify.com">Shopify</a>.
Liquid is designed for end-users to be able to execute logic within template files
without imposing any security risk on the hosting server.</p>
<p>Jekyll uses Liquid to generate the post content within the final page layout structure and as the primary interface for working with
your site and post/page data.</p>
<h3 id="why-do-we-have-to-use-liquid">Why Do We Have to Use Liquid?</h3>
<p>GitHub uses Jekyll to power <a href="http://pages.github.com/">GitHub Pages</a>.
GitHub cannot afford to run arbitrary code on their servers so they lock developers down via Liquid.</p>
<h3 id="liquid-is-not-programmer-friendly">Liquid is Not Programmer-Friendly.</h3>
<p>The short story is liquid is not real code and its not intended to execute real code.
The point being you can’t do jackshit in liquid that hasn’t been allowed explicitly by the implementation.
What’s more you can only access data-structures that have been explicitly passed to the template.</p>
<p>In Jekyll’s case it is not possible to alter what is passed to Liquid without hacking the gem or running custom plugins.
Both of which cannot be supported by GitHub Pages.</p>
<p>As a programmer - this is very frustrating.</p>
<p>But rather than look a gift horse in the mouth we are going to
suck it up and view it as an opportunity to work around limitations and adopt client-side solutions when possible.</p>
<p><strong>Aside</strong>
My personal stance is to not invest time trying to hack liquid. It’s really unnecessary
<em>from a programmer’s</em> perspective. That is to say if you have the ability to run custom plugins (i.e. run arbitrary ruby code)
you are better off sticking with ruby. Toward that end I’ve built <a href="http://github.com/plusjade/mustache-with-jekyll">Mustache-with-Jekyll</a></p>
<h2 id="static-assets">Static Assets</h2>
<p>Static assets are any file in the root or non-underscored subfolders that are not pages.
That is they have no valid YAML Front Matter and are thus not treated as Jekyll Pages.</p>
<p>Static assets should be used for images, css, and javascript files.</p>
<h2 id="how-jekyll-parses-files">How Jekyll Parses Files</h2>
<p>Remember Jekyll is a processing engine. There are two main types of parsing in Jekyll.</p>
<ul>
<li><strong>Content parsing.</strong>
This is done with textile or markdown.</li>
<li><strong>Template parsing.</strong>
This is done with the liquid templating language.</li>
</ul>
<p>And thus there are two main types of file formats needed for this parsing.</p>
<ul>
<li><strong>Post and Page files.</strong>
All content in Jekyll is either a post or a page so valid posts and pages are parsed with markdown or textile.</li>
<li><strong>Template files.</strong>
These files go in <code class="language-plaintext highlighter-rouge">_layouts</code> folder and contain your blogs <strong>templates</strong>. They should be made in HTML with the help of Liquid syntax.
Since include files are simply injected into templates they are essentially parsed as if they were native to the template.</li>
</ul>
<p><strong>Arbitrary files and folders.</strong>
Files that <em>are not</em> valid pages are treated as static content and pass through
Jekyll untouched and reside on your blog in the exact structure and format they originally existed in.</p>
<h3 id="formatting-files-for-parsing">Formatting Files for Parsing.</h3>
<p>We’ve outlined the need for valid formatting using <strong>YAML Front Matter</strong>.
Templates, posts, and pages all need to provide valid YAML Front Matter even if the Matter is empty.
This is the only way Jekyll knows you want the file processed.</p>
<p>YAML Front Matter must be prepended to the top of template/post/page files:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>---
layout: post
category : pages
tags : [how-to, jekyll]
---
... contents ...
</code></pre></div></div>
<p>Three hyphens on a new line start the Front-Matter block and three hyphens on a new line end the block.
The data inside the block must be valid YAML.</p>
<p>Configuration parameters for YAML Front-Matter is outlined here:
<a href="https://github.com/mojombo/jekyll/wiki/YAML-Front-Matter">A comprehensive explanation of YAML Front Matter</a></p>
<h4 id="defining-layouts-for-posts-and-templates-parsing">Defining Layouts for Posts and Templates Parsing.</h4>
<p>The <code class="language-plaintext highlighter-rouge">layout</code> parameter in the YAML Front Matter defines the template file for which the given post or template should be injected into.
If a template file specifies its own layout, it is effectively being used as a <code class="language-plaintext highlighter-rouge">sub-template.</code>
That is to say loading a post file into a template file that refers to another template file with work in the way you’d expect; as a nested sub-template.</p>
<h2 id="how-jekyll-generates-the-final-static-files">How Jekyll Generates the Final Static Files.</h2>
<p>Ultimately, Jekyll’s job is to generate a static representation of your website.
The following is an outline of how that’s done:</p>
<ol>
<li>
<p><strong>Jekyll collects data.</strong>
Jekyll scans the posts directory and collects all posts files as post objects. It then scans the layout assets and collects those and finally scans other directories in search of pages.</p>
</li>
<li>
<p><strong>Jekyll computes data.</strong>
Jekyll takes these objects, computes metadata (permalinks, tags, categories, titles, dates) from them and constructs one
big <code class="language-plaintext highlighter-rouge">site</code> object that holds all the posts, pages, layouts, and respective metadata.
At this stage your site is one big computed ruby object.</p>
</li>
<li>
<p><strong>Jekyll liquifies posts and templates.</strong>
Next jekyll loops through each post file and converts (through markdown or textile) and <strong>liquifies</strong> the post inside of its respective layout(s).
Once the post is parsed and liquified inside the the proper layout structure, the layout itself is “liquified”.
<strong>Liquification</strong> is defined as follows: Jekyll initiates a Liquid template, and passes a simpler hash representation of the ruby site object as well as a simpler
hash representation of the ruby post object. These simplified data structures are what you have access to in the templates.</p>
</li>
<li>
<p><strong>Jekyll generates output.</strong>
Finally the liquid templates are “rendered”, thereby processing any liquid syntax provided in the templates
and saving the final, static representation of the file.</p>
</li>
</ol>
<p><strong>Notes.</strong>
Because Jekyll computes the entire site in one fell swoop, each template is given access to
a global <code class="language-plaintext highlighter-rouge">site</code> hash that contains useful data. It is this data that you’ll iterate through and format
using the Liquid tags and filters in order to render it onto a given page.</p>
<p>Remember, in Jekyll you are an end-user. Your API has only two components:</p>
<ol>
<li>The manner in which you setup your directory.</li>
<li>The liquid syntax and variables passed into the liquid templates.</li>
</ol>
<p>All the data objects available to you in the templates via Liquid are outlined in the <strong>API Section</strong> of Jekyll-Bootstrap.
You can also read the original documentation here: <a href="https://github.com/mojombo/jekyll/wiki/Template-Data">https://github.com/mojombo/jekyll/wiki/Template-Data</a></p>
<h2 id="conclusion">Conclusion</h2>
<p>I hope this paints a clearer picture of what Jekyll is doing and why it works the way it does.
As noted, our main programming constraint is the fact that our API is limited to what is accessible via Liquid and Liquid only.</p>
<p>Jekyll-bootstrap is intended to provide helper methods and strategies aimed at making it more intuitive and easier to work with Jekyll =)</p>
<p><strong>Thank you</strong> for reading this far.</p>
<h2 id="next-steps">Next Steps</h2>
<p>Please take a look at <a href=""></a>
or jump right into <a href="">Usage</a> if you’d like.</p>