111111111乘111111111JS侮辱女Sony去我

js点击值自动加1 的问题 怎么解决呢【php吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:148,976贴子:
js点击值自动加1 的问题 怎么解决呢收藏
我写的那个var i=0;function jia(){document.getElementById('price').value=++i;这个玩意,居然是追加1,结果就会变成,而不是我想要的1,2,3,4,5
51CTO学院12年行业品牌,1600万用户选择,中国专业IT技能学习平台,php.通过在线学习的方式,帮助广大技术人员实现技能提升,高薪就业的职业梦想,php.
把++i括号扩起来
parseInt先转换成整型再加
i++document.write(i)
登录百度帐号推荐应用中国电信集团企业标准_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
中国电信集团企业标准
&&中国电信网络资源
阅读已结束,下载本文需要
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,方便使用
还剩3页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢&figure&&img src=&https://pic3.zhimg.com/v2-0d01fdb07fe3ef4fe1aa6ee_b.jpg& data-rawwidth=&1380& data-rawheight=&535& class=&origin_image zh-lightbox-thumb& width=&1380& data-original=&https://pic3.zhimg.com/v2-0d01fdb07fe3ef4fe1aa6ee_r.jpg&&&/figure&&p&&i&译者注:&/i&&/p&&p&&i&原文作者研究了近2.4万篇 JavaScript 文章得出这篇总结,全文包含学习指南、新人上手、Webpack、性能、基础概念、函数式编程、面试、教程案例、Async Await、并发、V8、机器学习、数据可视化、调试、单元测试等章节,非常适合用于对自己 JavaScript 技术栈的比对,用于查漏补缺,适合收藏阅读。有删减。文中如有错误,欢迎评论指出。&/i&&/p&&p&&i&如果你对整体前端技术、React 生态技术栈或者 Web 开发关注较多,可以移步这里查看更多&/i&&/p&&ul&&li&&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&2017前端技术发展回顾&/a&&/li&&li&&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&从1.8万篇文章中脱颖而出45个最棒的 React.js 学习指南(2018版)&/a&&/li&&li&&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&从1.6万篇文章中挑出的最棒的 Web 开发学习指南(2018版)&/a& &/li&&li&&a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&从1万篇文章中挑出的40篇最棒的 Vue 学习指南(2018版)&/a&&/li&&/ul&&p&在过去的一年间(2017年),我们对比了近24000篇 JavaScript 文章,并从中挑选出了最好的55篇。&/p&&p&我们做了这个目录,认为阅读有经验的程序员写的文章是一个很好的学习方式。在学习了一两门课程之后,您可能在构建和发布实际应用时面临许多挑战。&/p&&p&通过这个目录,您可以更轻松地找到去年的最佳 JavaScript 教程,在这里有经验的开发人员会分享他们学习 JavaScript 的课程、见解和遇到的错误。&/p&&p&这个目录有&b&15个关键的主题&/b&,如下所示。他们分别是&b&学习指南、新人上手、Webpack、性能、基础概念、函数式编程、面试、教程案例、Async Await、并发、V8、机器学习、数据可视化、调试、单元测试。&/b&&/p&&figure&&img src=&https://pic4.zhimg.com/v2-fa02e677e47fd693e4d1d9_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&2624& data-rawheight=&1066& class=&origin_image zh-lightbox-thumb& width=&2624& data-original=&https://pic4.zhimg.com/v2-fa02e677e47fd693e4d1d9_r.jpg&&&/figure&&h2&指南&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//github.com/mbeaudru/modern-js-cheatsheet%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Modern-js-cheatsheet: 一份针对现代项目中经常遇到的 JavaScript 知识的备忘清单。&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//mgechev.github.io/javascript-algorithms/%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&不同著名的计算机科学算法的 JavaScript 实现。&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//github.com/Chalarangelo/30-seconds-of-code%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&30-seconds-of-code: 有用的 JavaScript 代码片段,你可以在30秒或更少的时间内理解。&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//codetower.github.io/es6-features%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&一个简单的交互式 ES6 功能列表&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//github.com/denysdovhan/wtfjs%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&去他*的 JavaScript:有趣和棘手的 JavaScript 示例列表&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//github.com/airbnb/javascript%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Airbnb 的 Javascript 风格指南&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=http%3A//dmitrysoshnikov.com/ecmascript/javascript-the-core-2nd-edition%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript. 核心内容:第二版 - Dmitry Soshnikov&/a&&/li&&/ul&&h2&上手&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.com/%40peterxjang/modern-javascript-explained-for-dinosaurs-f695eFutm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&萌新也能懂的现代 JavaScript 开发&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//trackchanges.postlight.com/modern-javascript-for-ancient-web-developers-58e7cae050f9%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&为远古 Web 开发者准备的的现代 JavaScript&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//www.youtube.com/watch%3Fv%3DhO7mzO83N1Q%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&2017年的 JavaScript 模式 - Scott Allen&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//dev.to/srebalaji/es6-for-beginners-with-example-c7%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&为新手准备的带示例的 ES6&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//eev.ee/blog//javascript-got-better-while-i-wasnt-looking%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&当我没有关注时,JavaScript 变得更好了&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.freecodecamp.org/i-just-asked-23-000-developers-what-they-think-of-javascript-heres-what-i-learned-9a06b61998fa%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&我刚问过 23,000 个开发人员对 JavaScript 的看法。这是我学到的。&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//hackernoon.com/how-i-rediscovered-my-love-for-javascript-after-throwing-90-of-it-in-the-trash-f1baed075d1b%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&我是如何在抛弃 JavaScript 90%的内容后,又重新找回对他的热爱的。&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//www.sitepoint.com/dom-manipulation-vanilla-javascript-no-jquery%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Vanilla JavaScript 中的 DOM 操作基础(无 jQuery) - SitePoint&/a&&/li&&/ul&&h2&Webpack&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//www.smashingmagazine.com/2017/02/a-detailed-introduction-to-webpack%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&一篇 Webpack 的详细介绍 – Smashing Magazine&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//dev.to/thelarkinn/i-maintain-webpack-ask-me-anything-an8%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&我维护 webpack,尽情向我提问!&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.com/webpack/webpack-bits-getting-the-most-out-of-the-commonschunkplugin-ab389e5f318%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&webpack bits: 充分利用 CommonsChunkPlugin()&/a&&/li&&/ul&&h2&性能&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.com/dev-channel/the-cost-of-javascript-e%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript 的代价 - Addy Osmani&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.com/%40addyosmani/javascript-start-up-performance-%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript 气动性能 - Addy Osmani&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//blog.sessionstack.com/how-javascript-works-memory-management-how-to-handle-4-common-memory-leaks-3f28b94cfbec%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript 是如何工作的:内存管理 + 如何处理四种常见的内存泄漏&/a&&/li&&/ul&&h2&概念&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//scotch.io/tutorials/understanding-scope-in-javascript%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&理解 JavaScript 中的作用域&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//auth0.com/blog/glossary-of-modern-javascript-concepts%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&现代 JavaScript 概念词汇表:第一部分&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//rainsoft.io/7-tips-to-handle-undefined-in-javascript%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&在 JavaScript 中处理 undefined 的7个技巧 - Dmitri Pavlutin?&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.freecodecamp.org/hard-coding-concepts-explained-with-simple-real-life-analogies-e37%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&深入浅出编程概念&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=http%3A//kosamari.com/notes/the-promise-of-a-burger-party%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&在汉堡排队中解释 JavaScript 中的 Promise&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.com/%40samerbuna/coding-tip-try-to-code-without-if-statements-d06799eed231%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&编程技巧:尝试不用 If 语句编程 – Samer Buna&/a&&/li&&/ul&&h2&函数式编程&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.com/javascript-scene/master-the-javascript-interview-what-is-functional-programming-7f218c68b3a0%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&精通 JavaScript 面试:什么是函数式编程?&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&函数式编程(编写软件)的兴起与衰落&/a&&/li&&/ul&&h2&面试&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.freecodecamp.org/the-definitive-javascript-handbook-for-a-developer-interview-44ffc6aeb54e%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&为开发者面试准备的最终版 JavaScript Handbook&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//www.youtube.com/watch%3Fv%3DoxoFVqetl1E%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript 面试最常见的十个问题&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//dev.to/arnavaggarwal/10-javascript-concepts-you-need-to-know-for-interviews%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&面试中你必须知道的10个 JavaScript 概念&/a&&/li&&/ul&&h2&教程案例&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//www.youtube.com/watch%3Fv%3DxGmXxpIj6vs%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&在4分30秒内编写“贪吃蛇”(使用纯浏览器端 JavaScript)&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.com/ladies-storm-hackathons/how-we-built-our-first-full-stack-javascript-web-app-in-three-weeks-8a4668dbd67c%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&我们是如何在三周内开发出我们的第一个全栈 JavaScript web app 的&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//blog.patricktriest.com/game-of-thrones-leaflet-webpack%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&开发一个 Thrones Map 互动游戏(第二部分) - Leaflet.js 和 Webpack&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//www.youtube.com/watch%3Fv%3DzVqczFZr124%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&使用 Javascript 创建一个区块链(区块链,第一部分)&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//moin.world//how-we-hacked-our-coffee-machine-with-javascript%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&我们是如何使用 JavaScript 黑掉咖啡机的&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//www.youtube.com/watch%3Fv%3DZxf1mnP5zcw%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Google 地图 JavaScript API 教程&/a&&/li&&/ul&&h2&Async Await&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.com/%40mgaafar/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec1Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&六个为什么 JavaScript 中的 Async/Await 完虐 Promise 的原因(教程)&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//www.youtube.com/watch%3Fv%3DNsQ2QIrQShU%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Async/Await: JavaScript 中的现代并发&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//blog.sessionstack.com/how-javascript-works-event-loop-and-the-rise-of-async-programming-5-ways-to-better-coding-with-2f077cFutm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript 是如何工作的:事件循环机制和异步编程的兴起 + 更好使用 async/await 编程的五种方式&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//www.youtube.com/watch%3Fv%3D568g8hxJJp4%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript 中的 async / await - What, Why and How - Fun Fun Function&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=http%3A//nikgrozev.com//async-await%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&图文并茂的解释 Await 和 Async&/a&&/li&&/ul&&h2&并发&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//webkit.org/blog/7846/concurrent-javascript-it-can-work%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&并发JavaScript:可以工作!&/a&&/li&&/ul&&h2&V8&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript 是如何工作的:V8 引擎内部机制及如何编写优化代码的 5 个诀窍&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.com/dailyjs/understanding-v8s-bytecode-317d46c94775%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&理解 V8 的字节码&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript 是如何工作的:引擎,运行时以及调用堆栈概览&/a&&/li&&/ul&&h2&机器学习&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//www.robinwieruch.de/neural-networks-deeplearnjs-javascript%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&使用 deeplearn.js 尝试 JavaScript 中的神经网络&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.freecodecamp.org/how-to-create-a-neural-network-in-javascript-in-only-30-lines-of-code-343dafc50d49%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&如何只用30行代码在 JavaScript 中构建一个神经网络&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//hackernoon.com/machine-learning-with-javascript-part-1-9b97f3ed4fe5%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&在 JavaScript 使用机器学习:第一部分&/a&&/li&&/ul&&h2&数据可视化&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.com/%40mbostock/a-better-way-to-code-2b1d%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&更好的编程方式:d3.express 介绍:集成的探索环境。&/a&&/li&&/ul&&h2&调试&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//raygun.com/javascript-debugging-tips%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&你可能不知道的14个 JavaScript 调试技巧&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.freecodecamp.org/how-to-get-the-most-out-of-the-javascript-console-b57ca9db3e6d%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&如何充分利用 JavaScript 控制台&/a&&/li&&/ul&&h2&单元测试&/h2&&ul&&li&&a href=&https://link.zhihu.com/?target=https%3A//www.youtube.com/watch%3Fv%3DEu35xM76kKY%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript中的单元测试[第1部分]:为什么要进行单元测试? - Fun Fun Function&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=https%3A//medium.com/powtoon-engineering/a-complete-guide-to-testing-javascript-in-cd5a2a%3Futm_source%3Dmybridge%26utm_medium%3Demail%26utm_campaign%3Dread_more& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&2017年 JavaScript 测试技术回顾&/a& &/li&&/ul&&blockquote&&i&原文 - Learn Plain JavaScript from Top Articles for the Past Year &/i& &br&&i&原文作者 - Mybridge &/i& &br&&i&原文地址 - &a href=&https://link.zhihu.com/?target=https%3A//github.com/Mybridge/learn-javascript& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&learn-javascript&/a&&/i& &br&&i&译者 - &a href=&https://link.zhihu.com/?target=https%3A//github.com/hijiangtao& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&hijiangtao&/a&&/i& &br&&i&译文地址 - &a href=&https://link.zhihu.com/?target=https%3A//hijiangtao.github.io//learn-plain-javascript-from-top-tutorials-for-the-past-year-v-2018/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&[译]从2.4万篇文章中挑出的最棒的 JavaScript 学习指南(2018版)&/a&&/i&&/blockquote&&h2&最后&/h2&&p&如果喜欢使用微信的同学可以关注个人公众号,微信搜索「&b&黯晓&/b&」或者扫描 &b&&a href=&https://link.zhihu.com/?target=https%3A//hijiangtao.github.io/assets/pic/qrcode_for_gh_4e090cdcbcc5_258.jpg& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&二维码&/a&&/b& 关注,会同步我在&b&&a href=&https://zhuanlan.zhihu.com/makewebgreatagain& class=&internal&&知乎&/a&&/b&以及&b&&a href=&https://link.zhihu.com/?target=https%3A//hijiangtao.github.io/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&个人博客&/a&&/b&上发表的文章,谈谈前端技术与日常有趣事。喜欢使用知乎的同学可以关注我的专栏&b&&a href=&https://zhuanlan.zhihu.com/makewebgreatagain& class=&internal&&初级前端工程师&/a&&/b&。&/p&&p&生活中难免犯错,请多多指教!&/p&
译者注:原文作者研究了近2.4万篇 JavaScript 文章得出这篇总结,全文包含学习指南、新人上手、Webpack、性能、基础概念、函数式编程、面试、教程案例、Async Await、并发、V8、机器学习、数据可视化、调试、单元测试等章节,非常适合用于对自己 JavaScript…
&figure&&img src=&https://pic1.zhimg.com/v2-997e34f7fab5b4d9828e_b.jpg& data-rawwidth=&4000& data-rawheight=&2250& class=&origin_image zh-lightbox-thumb& width=&4000& data-original=&https://pic1.zhimg.com/v2-997e34f7fab5b4d9828e_r.jpg&&&/figure&&h2&&b&原生 JavaScript 模块的现在与未来&/b&&/h2&&p&ECMAScript 2015 为原生 JavaScript 新增了模块体系,自其发布以来便引起了开发者们广泛的讨论和积极的实践。经过一年多的发展,原生 JavaScript 模块目前处于什么状态?它的未来又将如何?本文试图围绕这两个问题,对原生 JavaScript 模块做一个全面的介绍。&/p&&h2&&b&JavaScript 中的模块&/b&&/h2&&p&诞生之初的 JavaScript 没有内建模块化支持。当然,在那个时代,对于一个用来编写表单校验和页面上浮动公告栏的语言来说,「模块化」确实显得有些大材小用。&/p&&p&但是随着互联网的发展,尤其是 2006 年 ajax 技术的出现和之后 Web 2.0 的兴起,越来越多的业务逻辑向前端转移,前端开发的复杂程度和代码量逐渐提升。这时,由于缺乏模块化概念,JavaScript 的一些问题便凸显出来:代码难以复用、容易出现全局变量污染和命名冲突、依赖管理难以维护。开发者们使用诸如暴露全局对象、自执行函数等方法来规避这些问题,但仍无法从根本上解决问题。&/p&&h2&&b&CommonJS&/b&&/h2&&p&2009 年,基于将 JavaScript 应用于服务端的尝试,ServerJS 诞生了。之后 ServerJS 更名为 CommonJS,并逐步发展为一个完整的模块规范。&/p&&p&CommonJS 为模块的使用定义了一套 API。比如,它定义了全局函数 require,通过传入模块标识来引入其他模块,如果被引入的模块又依赖了其他模块,那么会依次加载这些模块;通过 module.exports 向外部暴露 API,以便其他的模块引入。&/p&&p&由于 CommonJS 加载模块是同步的,即只有加载完成才能进行接下来的操作,因此当应用于浏览器端时会受到网速的限制。&/p&&h2&&b&AMD&/b&&/h2&&p&之后,在 CommonJS 组织的讨论中,AMD(Asynchronous Module Definition)应运而生。和前者不同的是,它使用异步方式加载模块,因此更适合被浏览器端采用。AMD 用全局函数 define 来定义模块,它需要三个参数:模块名称、模块的依赖数组、所有依赖都可用之后执行的回调函数(该函数按照依赖声明的顺序,接收依赖作为参数)。&/p&&h2&&b&UMD&/b&&/h2&&p&如果需要同时支持 CommonJS 和 AMD 两种格式,那么可以使用 UMD(Universal Module Definition)。事实上,UMD 通过一系列 if/else 判断来确定当前环境支持的模块体系,因此多数情况下 UMD 格式的模块会占用更大的体积。&/p&&h2&&b&ES6 Modules&/b&&/h2&&p&无论是 CommonJS,AMD 还是 UMD,它们都不是标准的 JavaScript 模块解决方案。换句话说,它们都没有被写进 ECMA 的规范中。直到 2015 年 6 月,TC39 委员会终于将 Modules 写进 ECMAScript 2015 中,标志着原生模块新时代的到来。至此,JavaScript 文件有了两种形式:脚本(自 JavaScript 诞生起我们就在使用的)和模块(即 ECMAScript 2015 Modules)。下面就让我们来一起探索 ECMAScript 2015 Modules(以下简称 ES6 Modules)。&/p&&p&&br&&/p&&h2&&b&ES6 Modules 现状&/b&&/h2&&p&规范方面,在 2015 年的早些时候,ES6 Modules 的语法就已经设计完毕并且蓄势待发,但是模块在语义方面的实现,比如具体怎样加载和执行等,却仍然悬而未决,因为这牵扯到大量与现有 JavaScript 引擎和宿主环境(浏览器和 Node.js 等)的整合工作。随着最后期限的临近,委员会不得不进行妥协,即标准只定义 Modules 的语法,而具体的实现则交由宿主环境负责。&/p&&h2&&b&使用 Babel 和 webpack&/b&&/h2&&p&由于绝大多数浏览器都不支持 ES6 Modules,所以目前如果想使用它的语法,需要借助 Babel 和 webpack,即通过 Babel 将代码编译为 ES5 的语法,然后使用 webpack 打包成目标格式。一个精简后的 webpack 配置为:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&module.exports = {
entry: './main.js',
filename: 'bundle.js',
rules: [{test: /\.js$/, use: 'babel-loader'}]
&/code&&/pre&&/div&&p&以上配置告诉 webpack,项目入口为 &code&./main.js&/code&,用 &code&babel-loader&/code& 处理所有 js 文件,然后将结果打包至 &code&bundle.js&/code&。&/p&&h2&&b&如何开启 ES6 Modules&/b&&/h2&&p&时至今日,几大主流浏览器都在积极推进支持原生 ES6 Modules 的工作,部分浏览器的技术预览版也已经初步完成了这一使命。可以通过 &a href=&http://link.zhihu.com/?target=http%3A//caniuse.com& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&caniuse.com&/span&&span class=&invisible&&&/span&&/a& 查看目前浏览器的支持情况。&/p&&figure&&img src=&http://pic2.zhimg.com/v2-161d96dd28d8ff25b81202d_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&958& data-rawheight=&920& class=&origin_image zh-lightbox-thumb& width=&958& data-original=&http://pic2.zhimg.com/v2-161d96dd28d8ff25b81202d_r.jpg&&&/figure&&p&FireFox 的 Nightly 版本已经实现了对 ES6 Modules 的支持。想在浏览器中体验的话,需要执行以下步骤:&/p&&ol&&li&首先在 FireFox 网站下载新版 Nightly&/li&&/ol&&figure&&img src=&http://pic2.zhimg.com/v2-177f38ff2e8020ee7edf6c0f2cde77ed_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&2004& data-rawheight=&814& class=&origin_image zh-lightbox-thumb& width=&2004& data-original=&http://pic2.zhimg.com/v2-177f38ff2e8020ee7edf6c0f2cde77ed_r.jpg&&&/figure&&ol&&li&执行安装后,在标签页打开 &code&about:config&/code&,并点击 &code&I accept the risk!&/code& 按钮&/li&&/ol&&figure&&img src=&http://pic4.zhimg.com/v2-a20a90fda77a75107d33af_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1844& data-rawheight=&628& class=&origin_image zh-lightbox-thumb& width=&1844& data-original=&http://pic4.zhimg.com/v2-a20a90fda77a75107d33af_r.jpg&&&/figure&&ol&&li&找到 &code&dom.moduleScripts.enabled&/code& 选项,并双击将其开启&/li&&/ol&&figure&&img src=&http://pic3.zhimg.com/v2-ecf7bdf254ba02f636c91364c0bfc6ae_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1738& data-rawheight=&232& class=&origin_image zh-lightbox-thumb& width=&1738& data-original=&http://pic3.zhimg.com/v2-ecf7bdf254ba02f636c91364c0bfc6ae_r.jpg&&&/figure&&h2&&b&小试 ES6 Modules&/b&&/h2&&p&既然已经在 FireFox Nightly 中开启了支持,那么下面就让我们从一个例子开始,详细介绍 ES6 Modules 的特点。&/p&&h2&&b&一个例子&/b&&/h2&&p&首先,新建一个 HTML 文件 index.html:&/p&&div class=&highlight&&&pre&&code class=&language-html&&&span&&/span&&span class=&cp&&&!DOCTYPE html&&/span&
&span class=&p&&&&/span&&span class=&nt&&html&/span& &span class=&na&&lang&/span&&span class=&o&&=&/span&&span class=&s&&&en&&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&head&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&meta&/span& &span class=&na&&charset&/span&&span class=&o&&=&/span&&span class=&s&&&UTF-8&&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&title&/span&&span class=&p&&&&/span&Title&span class=&p&&&/&/span&&span class=&nt&&title&/span&&span class=&p&&&&/span&
&span class=&p&&&/&/span&&span class=&nt&&head&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&body&/span&&span class=&p&&&&/span&
&span class=&p&&&&/span&&span class=&nt&&script&/span& &span class=&na&&type&/span&&span class=&o&&=&/span&&span class=&s&&&module&&/span& &span class=&na&&src&/span&&span class=&o&&=&/span&&span class=&s&&&main.js&&/span&&span class=&p&&&&/&/span&&span class=&nt&&script&/span&&span class=&p&&&&/span&
&span class=&p&&&/&/span&&span class=&nt&&body&/span&&span class=&p&&&&/span&
&span class=&p&&&/&/span&&span class=&nt&&html&/span&&span class=&p&&&&/span&
&/code&&/pre&&/div&&p&值得注意的是,在 script 标签上我们增加了 &code&type=&module&&/code& 这条属性,目的是告诉浏览器我们引入的 js 文件会包含其他的模块。为何浏览器无法自行判断一个 js 文件是一般的脚本还是 ES6 模块?我们会在下一节具体说明这个问题,现在暂时放在一边。&/p&&p&接下来,编写以下两个 js 文件:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&// main.js
import utils from &./utils.js&
console.log(utils.add(3, 4))
// utils.js
export default {
add(a, b) {
return a + b
&/code&&/pre&&/div&&p&在 FireFox Nightly 中打开 index.html,就能在控制台看到 &code&utils.add(3, 4)&/code& 的结果被打印出来。&/p&&h2&&b&发生了什么&/b&&/h2&&p&在 utils.js 中,我们使用 &code&export&/code& 关键字导出了模块,它具有一个名为 &code&add&/code& 的方法,返回两个参数的和;在 main.js 中,我们使用 &code&import&/code& 关键字导入了 utils 模块,并调用其中的 &code&add&/code& 方法,将结果打印出来。&/p&&p&相信大家对 &code&import&/code& 和 &code&export&/code& 都不陌生,因为 webpack、Rollup 等打包工具早已支持了这种写法。但是和打包工具的处理不同的是,原生 ES6 Modules 要求在引入时提供完整路径,包括文件的扩展名。因此,在 main.js 中,如果将第一行代码改为 &code&import utils from &./utils&&/code&,那么是无法在浏览器中正常运行的。基于同样的原因,如果我们需要引入 node_modules 目录下的第三方包,现有打包工具支持的 &code&import Packages from 'package'&/code& 也是不能被 ES6 Modules 识别的,必须要写为:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import Package from './node_modules/package/dist/lib.js'
&/code&&/pre&&/div&&h2&&b&命名空间&/b&&/h2&&p&ES6 Modules 是如何解决命名冲突的问题的?试试把上述 main.js 的内容修改为:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var x = 1
console.log(x === window.x)
console.log(this === undefined)
&/code&&/pre&&/div&&p&如果将这段代码直接复制进浏览器的控制台并运行,那么会依次打印出 &code&true&/code& 和 &code&false&/code&。但是再次打开我们的 index.html,会发现控制台依次打印出了 &code&false&/code& 和 &code&true&/code&,和前者完全相反。&/p&&p&这是因为 ES6 Modules 执行在一个独立于全局的、只属于自己的作用域中(module-local scope)。由于这种机制,模块之间的命名冲突不复存在,并且同时也避免了变量污染全局作用域的问题。&/p&&h2&&b&严格模式强制开启&/b&&/h2&&p&在 ES6 Modules 中,严格模式是默认开启并且无法关闭的。现在将 main.js 的内容修改为:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var x = 1
&/code&&/pre&&/div&&p&再次运行时,浏览器就会抛出一个 SyntaxError 错误,这正是严格模式下试图删除一个变量时的浏览器行为。&/p&&h2&&b&异步加载&/b&&/h2&&p&ES6 Modules 默认是异步加载的,并且在页面渲染完毕后才会执行,这等同于打开了 script 标签的 defer 属性。为了验证这一点,可以将 index.html 改写为:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&&!DOCTYPE html&
&html lang=&en&&
&meta charset=&UTF-8&&
&title&Title&/title&
&script src=&./script1.js& type=&module&&&/script&
&script src=&./script2.js&&&/script&
&/code&&/pre&&/div&&p&然后新建两个 js 文件:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&// script1.js
console.log(1)
// script2.js
console.log(2)
&/code&&/pre&&/div&&p&在浏览器中打开 index.html,能够看到控制台分别打印出了 2 和 1。&/p&&figure&&img src=&http://pic1.zhimg.com/v2-fe11d3f07b21dc30829d30_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1984& data-rawheight=&494& class=&origin_image zh-lightbox-thumb& width=&1984& data-original=&http://pic1.zhimg.com/v2-fe11d3f07b21dc30829d30_r.jpg&&&/figure&&p&对照 WHATWG HTML 规范里的这幅图我们可以看出,对于 defer 的 script 标签,它的加载与后续文档元素的加载会并行执行,并且它的执行要等到所有元素解析完成之后。因此在上面的例子中,script2.js 会在 script1.js 之前执行。&/p&&h2&&b&总结&/b&&/h2&&p&综上所述,ES6 Modules 具有以下特点:&/p&&ul&&li&可使用 &code&import&/code& 引入其他模块(模块路径需书写完整,并且不能省略扩展名);使用 &code&export&/code& 导出对外接口&/li&&li&拥有独立于全局作用域的命名空间&/li&&li&严格模式强制开启&/li&&li&标签 &code&&script type=&module&&&/code& 默认是 defer 的&/li&&/ul&&h2&&b&ES6 Modules 的解析问题&/b&&/h2&&p&上一节中我们提到,浏览器中运行的 ES6 Modules 对应的 script 标签上需要增加 &code&type=&module&&/code& 属性。为何浏览器不自行判断某个 script 是脚本还是模块?第一眼看上去,似乎只要在代码中找到 &code&import&/code& 或 &code&export&/code& 就能够说明是 ES6 Modules 了。但是事情并没有这么简单。&/p&&h2&&b&挑战&/b&&/h2&&p&我们首先假设浏览器能够在解析时通过检测代码中是否包含&code&import&/code& 或 &code&export&/code& 来判断 js 文件是脚本还是模块。对于一个模块来说,一种可能的情况是,整个文件只有最后一行出现了一个 &code&export&/code&。由于这种情况的存在,浏览器必须解析整个文件才有可能得出最终的结论。&/p&&p&但是从上一节我们了解到,模块是强制运行在严格模式下的。如果浏览器在解析到最后一行时才发现 &code&import&/code& 或 &code&export&/code&,那么就需要以严格模式将整个文件重新进行解析。这样一来,第一次非严格模式下的解析就浪费了。&/p&&p&除此之外,真正的问题是,一个不含有 &code&import&/code& 和 &code&export&/code& 的 js 文件也有可能是一个模块。比如下面的两段代码:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&// main.js
import './onload.js'
console.log('onload.js is loaded')
// onload.js
window.addEventListener('load', _ =& {
console.log('Window is loaded')
&/code&&/pre&&/div&&p&虽然 onload.js 中没有出现 &code&import&/code& 和 &code&export&/code&,但是 main.js 以 &code&import&/code& 关键字引入了它,所以它也是一个模块。只解析它本身是没有办法知道这一点的。&/p&&p&总而言之,虽然文件中包含 &code&import&/code& 和 &code&export&/code& 预示了该文件是模块,但是不包含这两个关键字却不能说明它不是模块。&/p&&h2&&b&浏览器端的解决方案&/b&&/h2&&p&浏览器端的解决方案很简单,就是在 script 标签上显式地注明一个文件是模块:&code&type=&module&&/code&。这样浏览器就会以模块的方式解析这个文件,并且加载它所依赖的模块。&/p&&h2&&b&围绕 Node.js 的讨论&/b&&/h2&&p&对于 Node.js 而言,浏览器的解决方法显然是行不通的。Node.js 社区对此进行了激烈的讨论,目前主要有四个方案:&/p&&ul&&li&在文件头部增加 &code&use module&/code& 来标识该文件是模块&/li&&li&在 &code&package.json&/code& 中添加相应的元数据字段&/li&&li&每个模块至少包含一个 &code&import&/code& 或 &code&export&/code&(Unambiguous JavaScript Grammar 提案)&/li&&li&为模块文件定义一个新的后缀:&code&jsm&/code&&/li&&/ul&&p&讨论还没有最终的结论,目前看来后两者更有希望。&/p&&p&&br&&/p&&h2&&b&最新进展与展望&/b&&/h2&&p&ES6 Modules 进入标准以来,开发者们对它进行了充分的研究和积极的探索,以下就是两个例子。&/p&&h2&&b&动态加载方案 &code&import()&/code&&/b&&/h2&&p&目前 ES6 Modules 采用的是静态声明和静态解析,即在编译时就能确定模块的依赖关系,完成模块的加载。这不仅提高了加载效率,也使得 tree shaking 成为可能(Rollup 和 webpack 2 都基于此实现了 tree shaking)。&/p&&p&但是另一方面,某些时候仍然有动态加载的需求。举例来说,在一些场景下,直到运行时才能确定是否需要引入一个模块(比如根据用户的语言引入不同的模块)。为应对动态加载的需求,TC39 整理出了一套所谓「类函数」的模块加载语法提案:&code&import()&/code&,目前已经处于规范发布流程的 stage 3 阶段。一个典型的用例如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const main = document.querySelector('main')
main.addEventListener('click', event =& {
event.preventDefault()
import(`./section-modules/${ main.dataset.entryModule }.js`)
.then(module =& {
module.loadPageInto(main)
.catch(err =& {
main.textContent = err.message
&/code&&/pre&&/div&&p&从这个例子可以看出,&code&import()&/code& 允许我们动态地引入模块。此外,和 ES6 Modules 相比,它还有以下特点:&/p&&ul&&li&它可以出现在代码的任意层级,并且不会被提升&/li&&li&它可以接收任意字符串作为参数(本例中是一个运行时确定的模板字符串),而 ES6 Modules 只能接收字符串字面量&/li&&li&它返回一个 Promise,并且会将加载的模块作为参数传递给 resolve 回调&/li&&/ul&&p&我们有理由相信,如果这个提案最终被写进标准,对 ES6 Modules 来说将是一个很好的补充。&/p&&h2&&b&基于 ES6 Modules 的 module-pusher 尝试&/b&&/h2&&p&来自挪威奥斯陆的工程师 Marius Gundersen 在一篇博客里结合 ES6 Modules、HTTP/2、Service Worker 和 Bloom Filter,进行了从服务器将未经打包的模块推送至客户端的尝试。&/p&&p&他首先列举了现有打包策略的弊病:要么会造成浏览器下载一些用不到的代码,要么会造成同一个模块被多次重复下载。为了达到模块加载的最优解,他进行了以下尝试:&/p&&ol&&li&ES6 Modules 是可以被静态解析的,这使得服务端能够找到给定模块的所有依赖模块。重复这个过程就可以构建出整个应用的依赖关系树&/li&&li&利用 HTTP/2 的 server push,服务端可以在客户端发出请求之前主动向其推送文件。一旦客户端请求了一个模块,服务端就可以将这个模块的所有依赖连同这个模块本身一起推送给客户端。当客户端需要加载某个依赖时,就会发现这个依赖已经存在于它的缓存中&/li&&li&一个潜在的问题是,如果模块 A 和模块 B 都依赖了模块 C,那么当客户端请求模块 A 时,服务端会同时将模块 C 推送;之后若客户端请求模块 B,服务端由于并不知道客户端的缓存中已经存在模块 C,因此会再次推送模块 C,这样就造成了网络资源的浪费&/li&&li&解决方案是,客户端发送请求时在请求头带上一个 Bloom Filter,它携带了客户端缓存的信息,服务端接收请求后对照依赖关系树和
Bloom Filter,确定需要推送哪些模块。不在请求头写入完整的已缓存模块的列表的原因是,这样做会导致请求头变得很大,而一个 Bloom Filter 通常只占用 100 字节&/li&&li&客户端如何知道自己缓存了哪些模块?这里需要用到 Service Worker:当客户端发送一个模块请求时,Service Worker 首先拦截这个请求,查看缓存中是否有这个模块,如果有就直接返回;如果没有,那么就根据缓存中的已有模块建立一个 Bloom Filter,并且写入请求头,将请求发送出去;当服务端返回一个模块时,Service Worker 将其写入缓存并响应给客户端&/li&&/ol&&p&整个过程的流程图如下:&/p&&figure&&img src=&http://pic3.zhimg.com/v2-12a5c2f61f792ecbed9fb1ec09bd14e2_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1024& data-rawheight=&1374& class=&origin_image zh-lightbox-thumb& width=&1024& data-original=&http://pic3.zhimg.com/v2-12a5c2f61f792ecbed9fb1ec09bd14e2_r.jpg&&&/figure&&p&从这个例子可以看出,ES6 Modules 的新特性为前端工程化打开了更多的可能性。&/p&&h2&&b&ES6 Modules 未来展望&/b&&/h2&&p&截至目前,在 JavaScript 的各种宿主环境中,只有少数浏览器的技术预览版实现了对 ES6 Modules 的支持;即使主流浏览器都支持了,由于要考虑旧浏览器的兼容性问题,在今后的很长一段时间里,开发者们在编写代码时仍然需要像现在一样使用打包工具将模块打包成需要的格式,而不是使用真正的 ES6 Modules。&/p&&p&事实上,浏览器和 Node.js 支持只是 ES6 Modules 迈向实用的第一步。除此之外,JavaScript 生态链上的许多环节都需要进行相应的改变。比如,目前 npm 上的大量模块都是 CommonJS 格式的,它们是不能直接被 ES6 Modules 引用的。&/p&&p&由此可见,ES6 Modules 离我们还有一段距离。不过,我们相信它终究会到来。因为「一个烂的标准比没有标准好上千万倍」,更何况 ES6 Modules 并不是一个烂的标准。&/p&&h2&&b&参考文献&/b&&/h2&&ol&&li&&a href=&http://link.zhihu.com/?target=https%3A//www.sitepoint.com/understanding-es6-modules-via-their-history/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Understanding ES6 Modules via Their History&/a&&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//www.nczonline.net/blog/2016/04/es6-module-loading-more-complicated-than-you-think/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ES6 module loading: More complicated than you think&/a& &/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/tc39/proposal-dynamic-import& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&proposal-dynamic-import&/a& &/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//mariusgundersen.net/module-pusher/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Efficient module loading without bundling&/a& &/li&&/ol&
原生 JavaScript 模块的现在与未来ECMAScript 2015 为原生 JavaScript 新增了模块体系,自其发布以来便引起了开发者们广泛的讨论和积极的实践。经过一年多的发展,原生 JavaScript 模块目前处于什么状态?它的未来又将如何?本文试图围绕这两个问题,对原生…
&figure&&img src=&https://pic1.zhimg.com/v2-df4f48dcff73f733514ae_b.jpg& data-rawwidth=&1200& data-rawheight=&498& class=&origin_image zh-lightbox-thumb& width=&1200& data-original=&https://pic1.zhimg.com/v2-df4f48dcff73f733514ae_r.jpg&&&/figure&&p&不少人都曾经在 &a href=&https://link.zhihu.com/?target=https%3A//www.npmjs.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&npm&/a& 上发布过自己开发的 JavaScript 模块,而在使用一些模块的过程中,我经常产生“这个模块很有用,但如果能 xxx 就更好了”的想法。所以,本文将站在模块使用者的角度总结一下,如何能让模块变得更好用。&/p&&h2&提供 ES6 模块的入口&/h2&&p&webpack 和 rollup 都支持对 ES6 模块做一些静态优化(例如 &a href=&https://link.zhihu.com/?target=https%3A//webpack.js.org/guides/tree-shaking/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Tree Shaking&/a& 和 &a href=&https://zhuanlan.zhihu.com/p/& class=&internal&&Scope Hoisting&/a&),它们都会优先读取 package.json 中的 &i&module&/i& 字段作为 ES6 模块的入口,若没有 module 才会读取 main 字段作为 CommonJS 模块的入口。通常的做法是:使用 ES6 语法编写源码,然后用模块打包工具结合语法转换工具生成 CommonJS 模块和 ES6 模块,这样就可以同时提供 main 和 module 字段了。&/p&&h2&提供 TypeScript 的类型声明文件&/h2&&p&如果你的用户使用了 TypeScript 但你的模块没有提供声明文件,他们就不得不在项目中添加一段代码避免 TypeScript 的编译错误;另外,这样做并不只是对使用 TypeScript 的用户友好,因为大部分代码编辑器(Webstorm、VS Code 等)都能识别 TypeScript 的类型声明,它们可以据此提供更精准的代码提示并在用户传入错误的参数个数或类型时给出提示。&/p&&p&最好的做法是使用 TypeScript 编写你的模块,编译时会自动生成类型声明。除此之外,你也可以参照&a href=&https://link.zhihu.com/?target=https%3A//www.typescriptlang.org/docs/handbook/declaration-files/introduction.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&文档&/a&手动维护一份声明文件。你可以在你的模块根目录下添加 index.d.ts 文件,或者在 package.json 中声明 typings 字段提供声明文件的位置。&/p&&h2&让模块同时在 Node.js 与浏览器中运行&/h2&&p&你可以通过检测是否有名为 window 的全局变量(例如 &i&typeof window !== 'undefined'&/i&)来判断模块当前是运行在 Node.js 还是浏览器中,然后使用不同的方式实现你的功能。&/p&&p&这种方法比较常见,但如果用户使用了模块打包工具,这样做会导致 Node.js 与浏览器的实现方式都会被包含在最终的输出文件中。针对这个问题,开源社区提出了在 package.json 中添加 browser 字段的&a href=&https://link.zhihu.com/?target=https%3A//github.com/defunctzombie/package-browser-field-spec& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&提议&/a&,目前 webpack 和 rollup 都已经支持这个字段了。&/p&&p&browser 字段有两种使用方式:&/p&&ol&&li&给 browser 字段提供一个文件路径作为在浏览器端使用时的模块入口,但需要注意的是,打包工具会优先使用 browser 字段指定的文件路径作为模块入口,所以你的 module 字段会被忽略,这会导致打包工具不会优化你的代码。详细信息请参考&a href=&https://link.zhihu.com/?target=https%3A//github.com/webpack/webpack/issues/4674& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这个问题&/a&。&/li&&li&如果你只想替换其中一些文件,你可以声明一个对象。&/li&&/ol&&p&举个例子,假设你的模块里有两个文件:&i&http.js&/i& 和 &i&xhr.js&/i&,第一个文件使用 Node.js 中的
http 模块发起请求,另一个使用浏览器中的 XMLHTTPRequest 实现了同样的功能。为了使用适当的文件,你的模块代码中应该始终 &i&require('./path/to/http.js')&/i&,并在 package.json 中声明:&/p&&div class=&highlight&&&pre&&code class=&language-json&&&span&&/span&&span class=&p&&{&/span&
&span class=&nt&&&browser&&/span&&span class=&p&&:&/span& &span class=&p&&{&/span&
&span class=&nt&&&./path/to/http.js&&/span&&span class=&p&&:&/span& &span class=&s2&&&./path/to/xhr.js&&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&这样一来,当你的模块在打包工具中使用时,打包工具只会将 xhr.js 的代码包含在最终的输出文件中。&/p&&h2&使用各种服务武装你的项目&/h2&&p&大部分 JavaScript 项目都是开源的,而开源社区也提供了很多针对开源项目的免费服务,它们可以给你的项目提供更有力的帮助,这里列举几个比较常用的。&/p&&p&一个项目最常使用的服务就是持续集成了。持续集成服务能将测试、代码风格检测、打包等任务放在服务器上,并在你提交代码时自动运行,常用的有 &a href=&https://link.zhihu.com/?target=https%3A//travis-ci.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Travis CI&/a&、&a href=&https://link.zhihu.com/?target=https%3A//circleci.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&CircleCI&/a& 和
&a href=&https://link.zhihu.com/?target=https%3A//www.appveyor.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&AppVeyor&/a&。Travis CI 对开源项目免费,提供 Linux 与 OS X 运行环境;CircleCI 对开源与私有项目都免费,但每个月有 1500 分钟的运行时间限制;AppVeyor 提供 Windows 运行环境,同样对开源项目免费。&/p&&p&运行完测试之后,你还可以将测试覆盖率上传到 &a href=&https://link.zhihu.com/?target=https%3A//coveralls.io/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Coveralls&/a&。这个服务能让你在线浏览代码的测试覆盖情况。&/p&&p&如果你想让你的模块在各个版本的各种浏览器、平台下得到充分的测试,你还可以使用 &a href=&https://link.zhihu.com/?target=https%3A//saucelabs.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Sauce Labs&/a& 和 &a href=&https://link.zhihu.com/?target=https%3A//www.browserstack.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&BrowserStack&/a&,它们都是对开源项目免费的,但需要发邮件申请。&/p&&p&最后,&a href=&https://link.zhihu.com/?target=https%3A//shields.io/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Shields IO&/a& 提供了各种图标,这些图标能为你的项目提供很多额外信息,包括但不限于 npm 版本号、下载量、测试通过状态、测试覆盖率、文件大小、依赖是否过期等。&/p&&hr&&p&虽然以上的建议大多属于锦上添花,但这会让你的模块对用户更加友好,希望以上的建议能在你开发自己的模块时给你一点帮助。&/p&
不少人都曾经在
上发布过自己开发的 JavaScript 模块,而在使用一些模块的过程中,我经常产生“这个模块很有用,但如果能 xxx 就更好了”的想法。所以,本文将站在模块使用者的角度总结一下,如何能让模块变得更好用。提供 ES6 模块的入口webpack 和 ro…
&figure&&img src=&https://pic4.zhimg.com/v2-ffe1a7d56e2adffe7ceaea9_b.jpg& data-rawwidth=&458& data-rawheight=&244& class=&origin_image zh-lightbox-thumb& width=&458& data-original=&https://pic4.zhimg.com/v2-ffe1a7d56e2adffe7ceaea9_r.jpg&&&/figure&&h2&什么是柯里化?&/h2&&h2&官方的说法&/h2&&p&在计算机科学中,&a href=&http://link.zhihu.com/?target=https%3A//zh.wikipedia.org/wiki/%25E6%259F%25AF%25E9%E5%258C%2596& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&柯里化&/a&(英语:&code&Currying&/code&),又译为&code&卡瑞化&/code&或&code&加里化&/code&,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。这个技术由&code&克里斯托弗·斯特雷奇&/code&以逻辑学家&code&哈斯凯尔·加里&/code&命名的,尽管它是&code&Moses Sch?nfinkel&/code&和&code&戈特洛布·弗雷格发明的&/code&。&/p&&p&在直觉上,柯里化声称&b&如果你固定某些参数,你将得到接受余下参数的一个函数&/b&。&br&在理论计算机科学中,柯里化提供了在简单的理论模型中,比如:只接受一个单一参数的&code&lambda&/code&演算中,研究带有多个参数的函数的方式。&br&函数柯里化的对偶是&code&Uncurrying&/code&,一种使用匿名单参数函数来实现多参数函数的方法。&/p&&h2&方便的理解&/h2&&blockquote&Currying概念其实很简单,只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。&/blockquote&&p&如果我们需要实现一个求三个数之和的函数:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&function add(x, y, z) {
return x + y +
console.log(add(1, 2, 3)); // 6
var add = function(x) {
return function(y) {
return function(z) {
return x + y +
var addOne = add(1);
var addOneAndTwo = addOne(2);
var addOneAndTwoAndThree = addOneAndTwo(3);
console.log(addOneAndTwoAndThree);
&/code&&/pre&&/div&&p&这里我们定义了一个&code&add&/code&函数,它接受一个参数并返回一个新的函数。调用&code&add&/code&之后,返回的函数就通过闭包的方式记住了&code&add&/code&的第一个参数。一次性地调用它实在是有点繁琐,好在我们可以使用一个特殊的&code&curry&/code&帮助函数(&code&helper function&/code&)使这类函数的定义和调用更加容易。&/p&&p&用&code&ES6&/code&的箭头函数,我们可以将上面的&code&add&/code&实现成这样:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&const add = x =& y =& z =& x + y +
&/code&&/pre&&/div&&p&好像使用箭头函数更清晰了许多。&/p&&h2&偏函数?&/h2&&p&来看这个函数:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&function ajax(url, data, callback) {
&/code&&/pre&&/div&&p&有这样的一个场景:我们需要对多个不同的接口发起&code&HTTP&/code&请求,有下列两种做法:&/p&&ul&&li&在调用&code&ajax()&/code&函数时,传入全局&code&URL&/code&常量。&/li&&li&创建一个已经预设&code&URL&/code&实参的函数引用。&/li&&/ul&&p&下面我们创建一个新函数,其内部仍然发起&code&ajax()&/code&请求,此外在等待接收另外两个实参的同时,我们手动将&code&ajax()&/code&第一个实参设置成你关心的&code&API&/code&地址。&/p&&p&对于第一种做法,我们可能产生如下调用方式:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&function ajaxTest1(data, callback) {
ajax('http://www.test.com/test1', data, callback);
function ajaxTest2(data, callback) {
ajax('http://www.test.com/test2', data, callback);
&/code&&/pre&&/div&&p&对于这两个类似的函数,我们还可以提取出如下的模式:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&function beginTest(callback) {
ajaxTest1({
data: GLOBAL_TEST_1,
}, callback);
&/code&&/pre&&/div&&p&相信您已经看到了这样的模式:我们在函数调用现场(&code&function call-site&/code&),将实参应用(&code&apply&/code&) 于形参。如你所见,我们一开始仅应用了部分实参 —— 具体是将实参应用到&code&URL&/code&形参 —— 剩下的实参稍后再应用。&/p&&p&上述概念即为&b&偏函数&/b&的定义,偏函数一个减少函数参数个数的过程;这里的参数个数指的是希望传入的形参的数量。我们通过&code&ajaxTest1()&/code&把原函数&code&ajax()&/code&的参数个数从&code&3&/code&个减少到了&code&2&/code&个。&/p&&p&我们这样定义一个&code&partial()&/code&函数:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&function partial(fn, ...presetArgs) {
return function partiallyApplied(...laterArgs) {
return fn(...presetArgs, ...laterArgs);
&/code&&/pre&&/div&&p&&code&partial()&/code&函数接收&code&fn&/code&参数,来表示被我们偏应用实参(&code&partially apply&/code&)的函数。接着,&code&fn&/code&形参之后,&code&presetArgs&/code&数组收集了后面传入的实参,保存起来稍后使用。&/p&&p&我们创建并&code&return&/code&了一个新的内部函数(为了清晰明了,我们把它命名为&code&partiallyApplied(..)&/code&),该函数中,&code&laterArgs&/code&数组收集了全部实参。&/p&&p&使用箭头函数,则更为简洁:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var partial =
(fn, ...presetArgs) =&
(...laterArgs) =&
fn(...presetArgs, ...laterArgs);
&/code&&/pre&&/div&&p&使用偏函数的这种模式,我们重构之前的代码:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&function ajax(url, data, callback) {
var ajaxTest1 = partial(ajax, 'http://www.test.com/test1');
var ajaxTest2 = partial(ajax, 'http://www.test.com/test1');
&/code&&/pre&&/div&&p&再次思考&code&beginTest()&/code&函数,我们使用&code&partial()&/code&来重构它应该怎么做呢?&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&function ajax(url, data, callback) {
var beginTest = partial(ajax, 'http://www.test.com/test1', {
data: GLOBAL_TEST_1,
var ajaxTest1 = partial(ajax, 'http://www.test.com/test1');
var beginTest = partial(ajaxTest1, {
data: GLOBAL_TEST_1,
&/code&&/pre&&/div&&h2&一次传一个&/h2&&p&相信你已经在上述例子中看到了版本2比起版本1的优势所在了,没错,柯里化就是:将一个带有多个参数的函数转换为一次一个的函数的过程。每次调用函数时,它只接受一个参数,并返回一个函数,直到传递所有参数为止。&/p&&blockquote&The process of converting a function that takes multiple arguments into a function that takes them one at a time.&br&Each time the function is called it only accepts one argument and returns a function that takes one argument until all arguments are passed.&/blockquote&&p&假设我们已经创建了一个柯里化版本的&code&ajax()&/code&函数&code&curriedAjax()&/code&:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&curriedAjax('http://www.test.com/test1')
data: GLOBAL_TEST_1,
(function callback(data) {
// dosomething
&/code&&/pre&&/div&&p&我们将三次调用分别拆解开来,这也许有助于我们理解整个过程:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var ajaxTest1 = curriedAjax('http://www.test.com/test1');
var beginTest = ajaxTest1({
data: GLOBAL_TEST_1,
var ajaxCallback = beginTest(function callback(data) {
// dosomething
&/code&&/pre&&/div&&h2&实现柯里化&/h2&&p&那么,我们如何来实现一个自动的柯里化的函数呢?&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var currying = function(fn) {
var args = [];
return function() {
if (arguments.length === 0) {
return fn.apply(this, args); // 没传参数时,调用这个函数
[].push.apply(args, arguments); // 传入了参数,把参数保存下来
return arguments. // 返回这个函数的引用
&/code&&/pre&&/div&&p&调用上述&code&currying()&/code&函数:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var cost = (function() {
var money = 0;
return function() {
for (var i = 0; i & arguments. i++) {
money += arguments[i];
var cost = currying(cost);
cost(100); // 传入了参数,不真正求值
cost(200); // 传入了参数,不真正求值
cost(300); // 传入了参数,不真正求值
console.log(cost()); // 求值并且输出600
&/code&&/pre&&/div&&p&上述函数是我之前的&a href=&http://link.zhihu.com/?target=https%3A//github.com/xingbofeng/JavaScript-design-patterns/blob/master/ch3-%25E9%2597%25AD%25E5%258C%%E9%25AB%%%25E5%2587%25BD%25E6%/ch3-%25E9%2597%25AD%25E5%258C%%E9%25AB%%%25E5%2587%25BD%25E6%.md%23currying& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JavaScript设计模式与开发实践读书笔记之闭包与高阶函数&/a&所写的&code&currying&/code&版本,现在仔细思考后发现仍旧有一些问题。&/p&&blockquote&我们在使用柯里化时,要注意同时为函数预传的参数的情况。&/blockquote&&p&因此把上述柯里化函数更改如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var currying = function(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
if (arguments.length === 0) {
return fn.apply(this, args); // 没传参数时,调用这个函数
[].push.apply(args, arguments); // 传入了参数,把参数保存下来
return arguments. // 返回这个函数的引用
&/code&&/pre&&/div&&p&使用实例:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var cost = (function() {
var money = 0;
return function() {
for (var i = 0; i & arguments. i++) {
money += arguments[i];
var cost = currying(cost, 100);
cost(200); // 传入了参数,不真正求值
cost(300); // 传入了参数,不真正求值
console.log(cost()); // 求值并且输出600
&/code&&/pre&&/div&&p&你可能会觉得每次都要在最后调用一下不带参数的&code&cost()&/code&函数比较麻烦,并且在&code&cost()&/code&函数都要使用&code&arguments&/code&参数不符合你的预期。我们知道函数都有一个&code&length&/code&属性,表明函数期望接受的参数个数。因此我们可以充分利用预传参数的这个特点。&/p&&p&借鉴自&a href=&http://link.zhihu.com/?target=https%3A//github.com/mqyqingfeng/Blog& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&mqyqingfeng&/a&:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&function sub_curry(fn) {
var args = [].slice.call(arguments, 1);
return function() {
return fn.apply(this, args.concat([].slice.call(arguments)));
function curry(fn, length) {
length = length || fn.
var slice = Array.prototype.
return function() {
if (arguments.length & length) {
var combined = [fn].concat(slice.call(arguments));
return curry(sub_curry.apply(this, combined), length - arguments.length);
return fn.apply(this, arguments);
&/code&&/pre&&/div&&p&在上述函数中,我们在currying的返回函数中,每次把&code&arguments.length&/code&和&code&fn.length&/code&作比较,一旦&code&arguments.length&/code&达到了&code&fn.length&/code&的数量,我们就去调用&code&fn&/code&(&code&return fn.apply(this, arguments);&/code&)&/p&&p&验证:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var fn = curry(function(a, b, c) {
return [a, b, c];
fn(&a&, &b&, &c&) // [&a&, &b&, &c&]
fn(&a&, &b&)(&c&) // [&a&, &b&, &c&]
fn(&a&)(&b&)(&c&) // [&a&, &b&, &c&]
fn(&a&)(&b&, &c&) // [&a&, &b&, &c&]
&/code&&/pre&&/div&&h2&bind方法的实现&/h2&&p&使用柯里化,能够很方便地借用&code&call()&/code&或者&code&apply()&/code&实现&code&bind()&/code&方法的&code&polyfill&/code&。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Function.prototype.bind = Function.prototype.bind || function(context) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return me.apply(contenxt, finalArgs);
&/code&&/pre&&/div&&p&上述函数有的问题在于不能兼容构造函数。&/p&&p&&a href=&http://link.zhihu.com/?target=https%3A//developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Function.prototype.bind() by MDN&/a&如下说到:&/p&&blockquote&绑定函数适用于用new操作符 new 去构造一个由目标函数创建的新的实例。当一个绑定函数是用来构建一个值的,原来提供的 this 就会被忽略。然而, 原先提供的那些参数仍然会被前置到构造函数调用的前面。&/blockquote&&p&这是&a href=&http://link.zhihu.com/?target=https%3A//book.douban.com/subject//& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&基于MVC的JavaScript Web富应用开发&/a&的&code&bind()&/code&方法实现:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Function.prototype.bind = function(oThis) {
if (typeof this !== &function&) {
throw new TypeError(&Function.prototype.bind - what is trying to be bound is not callable&);
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(
this instanceof fNOP && oThis ? this : oThis || window,
aArgs.concat(Array.prototype.slice.call(arguments))
fNOP.prototype = this.
fBound.prototype = new fNOP();
&/code&&/pre&&/div&&h2&反柯里化(uncurrying)&/h2&&p&可能遇到这种情况:拿到一个柯里化后的函数,却想要它柯里化之前的版本,这本质上就是想将类似&code&f(1)(2)(3)&/code&的函数变回类似&code&g(1,2,3)&/code&的函数。&/p&&p&下面是简单的&code&uncurrying&/code&的实现方式:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&function uncurrying(fn) {
return function(...args) {
for (let i = 0; i & args. i++) {
ret = ret(args[i]); // 反复调用currying版本的函数
// 返回结果
&/code&&/pre&&/div&&blockquote&注意,不要以为uncurrying后的函数和currying之前的函数一模一样,它们只是行为类似!&/blockquote&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&var currying = function(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function() {
if (arguments.length === 0) {
return fn.apply(this, args); // 没传参数时,调用这个函数
[].push.apply(args, arguments); // 传入了参数,把参数保存下来
return arguments. // 返回这个函数的引用
function uncurrying(fn) {
return function(...args) {
for (let i = 0; i & args. i++) {
ret = ret(args[i]); // 反复调用currying版本的函数
// 返回结果
var cost = (function() {
var money = 0;
return function() {
for (var i = 0; i & arguments. i++) {
money += arguments[i];
var curryingCost = currying(cost);
var uncurryingCost = uncurrying(curryingCost);
console.log(uncurryingCost(100, 200, 300)()); // 600
&/code&&/pre&&/div&&h2&柯里化或偏函数有什么用?&/h2&&p&无论是柯里化还是偏应用,我们都能进行部分传值,而传统函数调用则需要预先确定所有实参。如果你在代码某一处只获取了部分实参,然后在另一处确定另一部分实参,这个时候柯里化和偏应用就能派上用场。&/p&&p&另一个最能体现柯里化应用的的是,当函数只有一个形参时,我们能够比较容易地组合它们(&code&单一职责原则(Single responsibility principle)&/code&)。因此,如果一个函数最终需要三个实参,那么它被柯里化以后会变成需要三次调用,每次调用需要一个实参的函数。当我们组合函数时,这种单元函数的形式会让我们处理起来更简单。&/p&&p&归纳下来,主要为以下常见的三个用途:&/p&&ul&&li&延迟计算&/li&&li&参数复用&/li&&li&动态生成函数&/li&&/ul&
什么是柯里化?官方的说法在计算机科学中,(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。这个技术由克里斯托弗·斯…
&figure&&img src=&https://pic3.zhimg.com/v2-70fd62dd0a2ff22ad2ceae6_b.jpg& data-rawwidth=&1287& data-rawheight=&708& class=&origin_image zh-lightbox-thumb& width=&1287& data-original=&https://pic3.zhimg.com/v2-70fd62dd0a2ff22ad2ceae6_r.jpg&&&/figure&&p&&/p&&p&
文/baixinchen&/p&&p&
腾讯SNG事业群——web前端开发工程师&/p&&p&&br&&/p&&p&&br&&/p&&h2&&b&0 写在前面&/b&&/h2&&blockquote&&b& 前端的技术革新从来没有停止过,但从最近的趋势来看,貌似有一个“新”名词出现,那就是函数式编程(FP,functional programming)。 &/b&&/blockquote&&p&vue、react这些热门的框架都多多少少有点涉及到函数式编程的领域,甚至已经开始有一些以函数式编程作为主范式的框架出现,比如说cyclejs。&/p&&p&那么,为什么函数式编程会如此重要呢?&/p&&p&&b&或许我们可以先从函数式编程的认识聊起。&/b&&/p&&p&&br&&/p&&h2&&b&1 何为函数式编程?&/b&&/h2&&p&&br&&/p&&p&&b&1.1 什么是纯函数?&/b&&/p&&p&&b&让我们回想一下初中数学&/b&&/p&&figure&&img src=&https://pic3.zhimg.com/v2-a9ebcf5d8d900c02cc3ec2b2cc6f445f_b.jpg& data-rawwidth=&197& data-rawheight=&152& class=&content_image& width=&197&&&/figure&&p&我们知道一个函数有定义域和值域,对于定义域里面的每一个值,都会对应值域中唯一确定的一个值。&/p&&p&这种函数的基本性质在编程里面却不一定成立,因为代码中的函数可能依赖于外部环境:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/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=&kd&&function&/span& &span class=&nx&&fnNotPure&/span&&span class=&p&&()&/span& &span class=&p&&{&/span&
&span class=&k&&if&/span& &span class=&p&&(&/span&&span class=&nx&&count&/span&&span class=&o&&++&/span& &span class=&o&&%&/span& &span class=&mi&&2&/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&&else&/span& &span class=&p&&{&/span&
&span class=&k&&return&/span& &span class=&mi&&2&/span&&span class=&p&&;&/span&
&span class=&p&&}&/span&
&span class=&p&&}&/span&
&span class=&nx&&fnNotPure&/span&&span class=&p&&();&/span& &span class=&c1&&// 0&/span&
&span class=&nx&&fnNotPure&/span&&span class=&p&&();&/span& &span class=&c1&&// 1&/span&
&/code&&/pre&&/div&&p&正如上面的代码,函数依赖了外部变量count,导致相同的输入却得到不同的输出。&/p&&p&在函数式编程有个概念称之为副作用(side effect),指的是函数的执行依赖于外部环境,这里的依赖可能是读取了外部变量,也可能是修改了外部变量。&/p&&p&一个函数如果依赖外部环境,那么它的行为就会变得不可预测。因为你不知道外部环境什么时候会被改变,有句话貌似流传已久,很直接地指出了副作用给代码组织可能带来的问题:&/p&&blockquote&&Shared mutable state/variable is the root of evil&(可共享可修改的变量是所有罪恶的根源)&/blockquote&&p&纯函数(pure function)的概念就是指没有副作用的函数,在理论上它等价于我们数学世界里面的函数概念。&/p&&p&纯函数的优势在于,它向我们保证了其&纯粹性&,同样的输入无论调用多少次,都会是一样的输出,并且不用担心调用过程会修改外部环境。每个函数都是足够独立足够抽象的个体,我们可以放心地将函数进行组合(compose),这让我们在做代码复用或者重构时,不用去担心函数是否会影响到其他地方。&/p&&p&&br&&/p&&p&&b&1.2 是什么而非怎么做&/b&&/p&&p&&b&函数式编程是声明式编程(declarative programming)的一种形式&/b&,你可能会联想到命令式编程(imperative programming)。&/p&&p&&b&命令式编程&/b&通过编程语句声明了每一步的具体操作,如何修改变量以及按照什么样的顺序,这里强调的是&b&how to&/b&。&/p&&p&而函数式编程关注的点在于是需要哪些变量,需要什么样的操作,这里强调的是 &b&what is&/b&。&/p&&p&直接看例子更容易理解:&/p&&div class=&highlight&&&pre&&code class=&language-js&&&span&&/span&&span class=&kd&&let&/span& &span class=&nx&&arr&/span& &span class=&o&&=&/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=&mi&&4&/span&&span class=&p&&,&/span& &span class=&mi&&5&/span&&span class=&p&&,&/span& &span class=&mi&&6&/span&&span class=&p&&,&/span& &span class=&mi&&7&/span&&span class=&p&&,&/span& &span class=&mi&&8&/span&&span cl

我要回帖

更多关于 xg111111111 的文章

 

随机推荐