From 9602cb14713ec8fc5ce499b9f37c81dc8802cb53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=89=E5=84=BF?= Date: Sat, 5 May 2018 11:49:39 +0800 Subject: [PATCH 001/446] =?UTF-8?q?JavaScript=20=E6=98=AF=E5=A6=82?= =?UTF-8?q?=E4=BD=95=E8=BF=90=E4=BD=9C=E7=9A=84=EF=BC=9A=E7=94=A8=20Mutati?= =?UTF-8?q?onObserver=20=E8=BF=BD=E8=B8=AA=20DOM=20=E7=9A=84=E5=8F=98?= =?UTF-8?q?=E5=8C=96=20(#3727)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md * Update how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md Proofreading-1 --- ...anges-in-the-dom-using-mutationobserver.md | 98 +++++++++---------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md b/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md index 6e08099a75d..6a57166a24c 100644 --- a/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md +++ b/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md @@ -2,40 +2,40 @@ > * 原文作者:[Alexander Zlatkov](https://blog.sessionstack.com/@zlatkov?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md) -> * 译者: -> * 校对者: +> * 译者:[EmilyQiRabbit](https://github.com/EmilyQiRabbit) +> * 校对者:[jasonxia23](https://github.com/jasonxia23) -# How JavaScript works: tracking changes in the DOM using MutationObserver +# JavaScript 是如何运作的:用 MutationObserver 追踪 DOM 的变化 -This is post # 10 of the series dedicated to exploring JavaScript and its building components. In the process of identifying and describing the core elements, we also share some rules of thumb we use when building [SessionStack](https://www.sessionstack.com/?utm_source=medium&utm_medium=blog&utm_content=javascript-series-push-notifications-intro), a JavaScript application that needs to be robust and highly-performant to help users see and reproduce their web app defects real-time. +本系列专门研究 JavaScript 及其构建组件,这是第 10 期。在识别和描述核心元素的过程中,我们也分享了一些构建 [SessionStack](https://www.sessionstack.com/?utm_source=medium&utm_medium=blog&utm_content=javascript-series-push-notifications-intro) 的重要法则,SessionStack 是一个 JavaScript 应用,为了帮助用户实时查看和再现他们的 web 应用程序缺陷,它需要健壮并且高性能。 ![](https://cdn-images-1.medium.com/max/800/0*mPXf5zRCdEQ42Hn0.) -Web apps are getting increasingly heavy on the client-side, due to many reasons such as the need of a richer UI to accommodate what more complex apps have to offer, real-time calculations, and so on. +web 应用正在持续的越来越侧重客户端,这是由很多原因造成的,例如需要更丰富的 UI 来承载复杂应用的需求,实时运算,等等。 -The increased complexity makes it harder to know the exact state of the UI at every given moment during the lifecycle of your web app. +持续增加的复杂度使得在 web 应用的生命周期的任意时刻中获取 UI 的确切状态越来越困难。 -This gets even harder if you’re building some kind of a framework or just a library, for example, that has to react and perform certain actions that are dependent on the DOM. +而当你在搭建某些框架或者库的时候,甚至会更加困难,例如,前者需要根据 DOM 来作出反应并执行特定的动作。 -### Overview +### 概览 -[MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) is a Web API provided by modern browsers for detecting changes in the DOM. With this API one can listen to newly added or removed nodes, attribute changes or changes in the text content of text nodes. +[MutationObserver](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) 是一个现代浏览器提供的 Web API,用于检测 DOM 的变化。借助这个 API,可以监听到节点的新增或移除,节点的属性变化或者字符节点的字符内容变化。 -Why would you want to do that? +为什么你会想要监听 DOM? -There are quite a few cases in which the MutationObserver API can come really handy. For instance: +这里有很多 MutationObserver API 带来极大便捷的例子,比如: -* You want to notify your web app visitor that some change has occurred on the page he’s currently on. -* You’re working on a new fancy JavaScript framework that loads dynamically JavaScript modules based on how the DOM changes. -* You might be working on a WYSIWYG editor, trying to implement undo/redo functionality. By leveraging the MutationObserver API, you know at any given point what changes have been made, so you can easily undo them. +* 你想要提醒 web 应用的用户,他现在所浏览的页面有内容发生了变化。 +* 你正在使用一个新的花哨的 JavaScript 框架,它根据 DOM 的变化动态加载 JavaScript 模块。 +* 也许你正在开发一个 WYSIWYG 编辑器,并试着实现撤销/重做功能。借助 MutationObserver API,你在任何时间都能知道发生了什么变化,所以撤销也就非常容易。 ![](https://cdn-images-1.medium.com/max/800/1*48tGIboHxgLeKEjMTGkUGg.png) -These are just a few examples of how the MutationObserver can be of help. +这里有几个关于 MutationObserver 是如何带来便捷的例子。 -#### How to use MutationObserver +#### 如何使用 MutationObserver -Implementing `MutationObserver` into your app is rather easy. You need to create a `MutationObserver` instance by passing it a function that would be called every time a mutation has occurred. The first argument of the function is a collection of all mutations which have occurred in a single batch. Each mutation provides information about its type and the changes which have occurred. +将 `MutationObserver` 应用于你的应用相当简单。你需要通过传入一个函数来创建一个 `MutationObserver` 实例,每当有变化发生,这个函数将会被调用。函数的第一个参数是一个批次内所有的变化(mutation)的集合。每个变化都会提供它的类型和已经发生的变化的信息。 ``` var mutationObserver = new MutationObserver(function(mutations) { @@ -45,16 +45,16 @@ var mutationObserver = new MutationObserver(function(mutations) { }); ``` -The created object has three methods: +这个被创建的对象有三个方法: -* `observe` — starts listening for changes. Takes two arguments — the DOM node you want to observe and a settings object -* `disconnect` — stops listening for changes -* `takeRecords` — returns the last batch of changes before the callback has been fired. +* `observe` — 开始监听变化。需要两个参数 - 你需要观察的 DOM 和一个设置对象 +* `disconnect` — 停止监听变化 +* `takeRecords` — 在回调函数调用之前,返回最后一个批次的变化。 -The following snippet shows how to start observing: +下面这个代码片段展示了如何开始观察: ``` -// Starts listening for changes in the root HTML element of the page. +// 开始监听页面中根 HTML 元素中的变化。 mutationObserver.observe(document.documentElement, { attributes: true, characterData: true, @@ -65,40 +65,40 @@ mutationObserver.observe(document.documentElement, { }); ``` -Now, let’s say that you have some very simple `div` in the DOM: +现在,假设在 DOM 中你有一些非常简单的 `div` : ```
Simple div
``` -Using jQuery, you canremove the `class` attribute from that div: +使用 jQuery,你可以移除这个 div 的 `class` 属性: ``` $("#sample-div").removeAttr("class"); ``` -As we have started observing, after calling `mutationObserver.observe(...)` we’re going to see a log in the console of the respective [MutationRecord](https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord): +当我们开始观察,在调用 `mutationObserver.observe(...)` 之后我们将会在控制台看到每个 [MutationRecord](https://developer.mozilla.org/en-US/docs/Web/API/MutationRecord) 的日志: ![](https://cdn-images-1.medium.com/max/800/1*UxkSstuyCvmKkBTnjbezNw.png) -This is the mutation caused by removing the `class` attribute. +这个是由移除 `class` 属性导致的变化。 -And finally, in order to stop observing the DOM after the job is done, you can do the following: +最后,为了在任务结束后停止对 DOM 的观察,你可以这样做: ``` -// Stops the MutationObserver from listening for changes. +// 停止 MutationObserver 对变化的监听。 mutationObserver.disconnect(); ``` -Nowadays, the `MutationObserver` is widely supported: +现在,`MutationObserver` 已经被广泛支持: ![](https://cdn-images-1.medium.com/max/800/0*nlOmrsfy-Y1XoR8B.) -#### Alternatives +#### 备择方案 -The `MutationObserver`, however, has not always been around. So what did developers resort to before the `MutationObserver` came along? +不管怎么说,`MutationObserver` 并不是一直就有的。那么当 `MutationObserver` 出现之前,开发者用的是什么? -There are a few other options available: +这是几个可用的其他选项: * **Polling** * **MutationEvents** @@ -106,27 +106,27 @@ There are a few other options available: #### Polling -The simplest and most unsophisticated way was by polling. Using the browser setInterval WebAPI you can set up a task that would periodically check if any changes have occurred. Naturally, this method significantly degrades web app/website performance. +最简单的最接近原生的方法是 polling。使用浏览器的 setInterval web 接口你可以设置一个在一段时间后检查是否有变化发生的的任务。自然,这个方法将会严重的降低应用或者网站的性能。 #### MutationEvents -In the year 2000, the [MutationEvents API](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events) was introduced. Albeit useful, mutation events are fired on every single change in the DOM which again causes performance issues. Nowadays the `MutationEvents` API has been deprecated, and soon modern browsers will stop supporting it altogether. +在 2000 年,[MutationEvents API](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events) 被引入。尽管很有用,但是每次 DOM 发生变化 mutation events 都会被触发,这将再次导致性能问题。现在 `MutationEvents` 接口已经被废弃,很快,现代浏览器将会完全停止对它的支持。 -This is the browser support for `MutationEvents`: +这是浏览器对 `MutationEvents` 的支持: ![](https://cdn-images-1.medium.com/max/800/0*l-QdpBfjwNfPDTyh.) #### CSS animations -A somewhat strange alternative is one that relies on [CSS animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations). It might sound a bit confusing. Basically, the idea is to create an animation which would be triggered once an element has been added to the DOM. The moment the animation starts, the `animationstart` event will be fired: if you have attached an event handler to that event, you’d know exactly when the element has been added to the DOM. The animation’s execution time period should be so small that it’s practically invisible to the user. +一个有点奇怪的备选方案依赖于 [CSS animations](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations)。这可能听起来有些让人困惑。基本上,这个方案是创建一个动画,一旦一个元素被添加到了 DOM,这个动画就将会被触发。动画开始的时候,`animationstart` 事件会被触发:如果你对这个事件绑定了一个处理器,你将会确切的知道元素是什么时候被添加到 DOM 的。动画执行的时间段很短,所以实际应用的时候它对用户是不可见的。 -First, we need a parent element, inside which, we’d like to listen to node insertions: +首先,我们需要一个父级元素,我们在它的内部监听节点的插入: ```
``` -In order to get a handle on node insertion, we need to set up a series of [keyframe](https://www.w3schools.com/cssref/css3_pr_animation-keyframes.asp) animations which will start when the node is inserted: +为了得到节点插入的处理器,我们需要设置一系列的 [keyframe](https://www.w3schools.com/cssref/css3_pr_animation-keyframes.asp) 动画,当节点插入的时候,动画将会开始。 ``` @keyframes nodeInserted { @@ -135,7 +135,7 @@ In order to get a handle on node insertion, we need to set up a series of [keyfr } ``` -With the keyframes created, the animation needs to be applied on the elements you’d like to listen for. Note the small durations — they are relaxing the animation footprint in the browser: +keyframes 已经创建,动画还需要被应用于你想要监听的元素。注意应设置很小的 duration 值 —— 它们将会减弱动画在浏览器上留下的痕迹: ``` #container-element * { @@ -144,20 +144,20 @@ With the keyframes created, the animation needs to be applied on the elements yo } ``` -This adds the animation to all child nodes of the `container-element`. When the animation ends, the insertion event will fire. +这为 `container-element` 的所有子节点都添加了动画。当动画结束后,插入的事件将会被触发。 -We need a JavaScript function which will act as the event listener. Within the function, the initial `event.animationName` check must be made to ensure it’s the animation we want. +我们需要一个作为事件监听者的 JavaScript 方法。在方法内部,必须确保初始的 `event.animationName` 检测是我们想要的那个动画。 ``` var insertionListener = function(event) { - // Making sure that this is the animation we want. + // 确保这是我们想要的那个动画。 if (event.animationName === "nodeInserted") { console.log("Node has been inserted: " + event.target); } } ``` -Now it’s time to add the event listener to the parent: +现在是时候为父级元素添加事件监听了: ``` document.addEventListener(“animationstart”, insertionListener, false); // standard + firefox @@ -166,17 +166,17 @@ document.addEventListener(“webkitAnimationStart”, insertionListener, false); ``` -This is the browser support for CSS animations: +这是浏览器对于 CSS 动画的支持: ![](https://cdn-images-1.medium.com/max/800/0*W4wHvVAeUmc45vA2.) -`MutationObserver` offers a number of advantages over the above-mentioned solutions. In essence, it covers every single change that can possibly occur in the DOM and it’s way more optimized as it fires the changes in batches. On top of it, `MutationObserver` is supported by all major modern browsers, along with a couple of polyfills which use `MutationEvents` under the hood. +`MutationObserver` 能提供上述提到的解决方案没有的很多优点。本质上,它能覆盖到每一个可能发生的 DOM 的变化,并且它会在一个批次的变化发生后被触发,这种方法使得它得到大大的优化。最重要的,`MutationObserver` 被所有的主流现代浏览器所支持,还有一些使用引擎下 MutationEvents 的 polyfill。 -`MutationObserver` occupies a central position in [SessionStack](https://www.sessionstack.com/?utm_source=medium&utm_medium=blog&utm_content=mutation-observer-post)’s library. +`MutationObserver` 在 [SessionStack](https://www.sessionstack.com/?utm_source=medium&utm_medium=blog&utm_content=mutation-observer-post) 库中占据了核心位置。 -Once you integrate the SessionStack’s library in your web app, it starts collecting data such as DOM changes, network requests, exceptions, debug messages, etc. and sends it to our servers., SessionStack uses this data to recreate everything that happened to your users and show your product issues the same way they happened to your users. Quite a few users think that SessionStack records an actual video — it doesn’t. Recording an actual video is very heavy, while the small amount of data we gather is very lightweight and doesn’t impact the UX and performance of your web app. +你一旦将 SessionStack 库整合进 web 应用,它就开始收集 DOM 变化、网络请求、错误信息、debug 信息等等,并发送到我们的服务器。SessionStack 使用这些信息重新创建了你的用户端发生的一切,并以发生在用户端的同样的方式将产品的问题展现给你。很多用户认为 SessionStack 记录的实际是视频 -- 然而它并没有。记录真实情况的视频是很耗费资源的,然而我们收集的少量数据却很轻量,并不会影响 UX 和你的 web 应用的性能。 -There is a free plan if you’d like to [give SessionStack a try](https://www.sessionstack.com/?utm_source=medium&utm_medium=source&utm_content=javascript-series-web-workers-try-now). +如果你想要[尝试一下 SessionStack](https://www.sessionstack.com/?utm_source=medium&utm_medium=source&utm_content=javascript-series-web-workers-try-now),这是一个免费的设计案例。 ![](https://cdn-images-1.medium.com/max/800/0*h2Z_BnDiWfVhgcEZ.) From 5738193e2f0bcf2f69da7da5bedb816652e6a16b Mon Sep 17 00:00:00 2001 From: Shery <382254994@qq.com> Date: Sat, 5 May 2018 22:45:06 +0800 Subject: [PATCH 002/446] =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E5=A4=A7=E5=9E=8B=20?= =?UTF-8?q?JavaScript=20=E5=BA=94=E7=94=A8=E7=A8=8B=E5=BA=8F=20(#3721)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 设计大型 JavaScript 应用程序 设计大型 JavaScript 应用程序 * Update designing-very-large-javascript-applications.md * 校对修改 校对修改 --- ...ning-very-large-javascript-applications.md | 241 +++++++++--------- 1 file changed, 121 insertions(+), 120 deletions(-) diff --git a/TODO1/designing-very-large-javascript-applications.md b/TODO1/designing-very-large-javascript-applications.md index 64106b6ba30..33c13155057 100644 --- a/TODO1/designing-very-large-javascript-applications.md +++ b/TODO1/designing-very-large-javascript-applications.md @@ -2,356 +2,357 @@ > * 原文作者:[Malte Ubl](https://medium.com/@cramforce?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/designing-very-large-javascript-applications.md](https://github.com/xitu/gold-miner/blob/master/TODO1/designing-very-large-javascript-applications.md) -> * 译者: -> * 校对者: +> * 译者:[Shery](https://github.com/shery15) +> * 校对者:[Starrier](https://github.com/Starriers) -# Designing very large (JavaScript) applications +# 设计大型 JavaScript 应用程序 -This is a mildly edited transcript of my JSConf Australia talk. [Watch the whole talk on YouTube](https://www.youtube.com/watch?v=ZZmUwXEiPm4). +这是我在 JavaScript 澳大利亚开发者大会(JSConf AU)上演讲内容的文字编辑记录。[在 YouTube 上观看整个演讲视频](https://www.youtube.com/watch?v=ZZmUwXEiPm4)。 ![](https://cdn-images-1.medium.com/max/800/1*DqvlkOgHSKmp5Tu1eX5mdw.png) -Slide text: Hello, I used to build very large JavaScript applications. +幻灯片文本:你好,我曾经构建过非常大型的 JavaScript 应用。 -Hello, I used to build very large JavaScript applications. I don’t really do that anymore, so I thought it was a good time to give a bit of a retrospective and share what I learned. Yesterday I was having a beer at the conference party and I was asked: “Hey Malte, what actually gives you the right, the authority, to talk about the topic?” and I suppose answering this is actually on topic for this talk, although I usually find it a bit weird to talk about myself. So, I build this JavaScript framework at Google. It is used by Photos, Sites, Plus, Drive, Play, the search engine, all these sites. Some of them are pretty large, you might have used a few of them. +你好,我曾经构建过非常大型的 JavaScript 应用。我不再那么做了,所以我认为现在是个好时机来回顾并分享我学到的东西。昨天我在会议聚会上喝啤酒时,有人问我:“嘿,马尔特,究竟是什么赋予了你权利和权威,来讲这个话题?”我想这个问题的答案实际上就是这个演讲的主题,尽管我通常觉得谈论自己有点奇怪。大概是因为,我在谷歌构建了这样一个 JavaScript 框架。它被 Google 照片,Google 协作平台,Google+,Google 云端硬盘,Google Play,搜索引擎,所有这些网站使用。其中一些项目非常大,你可能已经使用了其中的一些。 ![](https://cdn-images-1.medium.com/max/800/1*v0r4OVf-RXr9ePakdmv5LQ.png) -Slide text: I thought React was good. +幻灯片文本:我认为 React 很好。 -This Javascript framework is not open source. The reason it is not open source is that it kind of came out at the same time as React and I was like “Does the world really need another JS framework to choose from?”. Google already has a few of those–Angular and Polymer–and felt like another one would confuse people, so I just thought we’d just keep it to ourselves. But besides not being open source, I think there is a lot to learn from it and it is worth sharing the things we learned along the way. +这个 Javascript 框架不是开源的。它不是开源的原因是它与 React 同时出现,我想“世界是否真的需要另一个 JS 框架来做选择?”。谷歌已经拥有了一些 JS 框架,Angular 和 Polymer,并且我觉得再有一个会让人们感到困惑,所以我只是认为我们应该把它留给我们自己。但除了不是开源的,我认为还是有很多东西可以从中学习,值得分享我们一路上学到的东西。 ![](https://cdn-images-1.medium.com/max/800/1*LL3uYYDMT5uIFRxR_7JxPQ.png) -Picture of lots of people. +一张人山人海的图片. -So, let’s talk about very large applications and the things they have in common. Certainly that there might be a lot of developers. It might be a few dozens or even more–and these are humans with feelings and interpersonal problems and you may have to factor that in. +所以,我们来谈谈非常大型的应用,以及他们之间的共同点。当然可能会有很多开发者参与其中。可能有几十人甚至更多,他们都有自己的情感和人际问题,你必须要考虑到这一点。 -![](https://cdn-images-1.medium.com/max/800/1*WEH24kaBbar8-1gzN_AO3w.png)Picture of very old building. +![](https://cdn-images-1.medium.com/max/800/1*WEH24kaBbar8-1gzN_AO3w.png)一张非常古老建筑的图片. -And even if your team is not as big, maybe you’ve been working on the thing for a while, and maybe you’re not even the first person maintaining it, you might not have all the context, there might be stuff that you don’t really understand, there might be other people in your team that don’t understand everything about the application. These are the things we have to think about when we build very large applications. +即使你的规模不大,也许你已经在这个领域工作了一段时间,也许你甚至不是第一个维护它的人,你可能不了解项目的所有结构或者内容,可能有些东西是你不太明白的,你的团队中可能还有其他人不了解应用程序的所有信息。这些都是我们在构建非常大型的应用程序时,必须考虑的事情。 ![](https://cdn-images-1.medium.com/max/800/1*fzb42X35lNGmkQHhJLhEBQ.png) -Tweet saying: A team of senior engineers without junior engineers is a team of engineers. +推特: 一个没有初级工程师的高级工程师团队是一个工程师团队。 -Another thing I wanted to do here is to give this a bit of context in terms of our careers. I think many of us would consider themselves senior engineers. Or we are not quite there yet, but we want to become one. What I think being senior means is that I’d be able to solve almost every problem that somebody might throw at me. I know my tools, I know my domain. And the other important part of that job is that I make the junior engineers eventually be senior engineers. +我想在这里做的另一件事是以我们的职业生涯说明下背景。我想我们很多人会认为自己是高级工程师。或者是还差一点点,但我们想成为一个高级工程师。我认为高级的意思是我几乎可以解决其他人可能抛出的任何问题。我熟悉我的工具,我熟悉我的领域。而这项工作的另一个重要部分是我让初级工程师最终成为高级工程师。 ![](https://cdn-images-1.medium.com/max/800/1*xpRJ1dXHMlFq1V4oDKU__w.png) -Slide text: Junior -> Senior -> ? +幻灯片文本:初级 -> 高级 -> ? -But what happens is that at some point we may wonder “what might be the next step?”. When we reached that seniority stage, what is the next thing we are going to do? For some of us the answer may be management, but I don’t think that should be the answer for everyone, because not everyone should be a manager, right? Some of us are really great engineers and why shouldn’t we get to do that for the rest of our lives? +但是会发生什么呢?在某种程度上,我们可能会怀疑“下一步可能是什么?”。当我们达到这个高级阶段时,我们接下来要做什么?对于我们中的一些人来说,答案可能是做管理,但我认为这不应该成为每个人的答案,因为不是每个人都应该成为管理者,对吗?我们中有些人是非常优秀的工程师,为什么我们不应该在我们的余生中也这样做? ![](https://cdn-images-1.medium.com/max/800/1*wL5wiTWICj1keue9YZOAhQ.png) -Slide text: “I know how I would solve the problem” +幻灯片文本:“我知道我会如何解决问题” -I want to propose a way to level up above that senior level. The way I would talk about myself as a senior engineer is that I’d say “I know how I would solve the problem” and because I know how I would solve it I could also teach someone else to do it. +我想提出一种方法来升级到高级水平。我把自己当作高级工程师的方式是,我会说:“我知道如何解决这个问题”,并且因为我知道如何解决这个问题,所以我也可以教别人去解决它。 ![](https://cdn-images-1.medium.com/max/800/1*UyLoKH7y54JAYigVlwCJpQ.png) -Slide text: “I know how others would solve the problem” +幻灯片文本:“我知道别人怎么解决这个问题” -And my theory is that the next level is that I can say about myself “I know how _others_ would solve the problem”. +我的理论是,下一个层次是我可以对自己说:“我知道**别人**会如何解决这个问题”。 ![](https://cdn-images-1.medium.com/max/800/1*zBBGLRIZw94gp54pspvx-g.png) -Slide text: “I can anticipate how API choices and abstractions impact the way other people would solve the problem.” +幻灯片文本:“我可以预测 API 选择和抽象如何影响其他人解决问题的方式。” -Let’s make that a bit more concrete. You make that sentence: “I can anticipate how the API choices that I’m making, or the abstractions that I’m introducing into a project, how they impact how other people would solve a problem.” I think this is a powerful concept that allows me to reason about how the choices I’m making impact an application. +让我们更具体一点。你说了这样一句话:“我可以预见我做出 API 选择时,或者我往项目中引入的抽象时,他们如何影响其他人如何解决问题。”我认为这是一个强大的概念,可以让我思考我所做的选择对应用程序的影响。 ![](https://cdn-images-1.medium.com/max/800/1*LnDv6Ry0Hq2MaQEARaD8rg.png) -Slide text: An application of empathy. +幻灯片文本:同理心的应用。 -I would call this an application of empathy. You’re thinking with other software engineers and you’re thinking about how what you do and the APIs that you are giving them, how they impact how they write software. +我会称之为同理心的应用。你在和其他软件工程师一起思考,你在思考你所做的事情以及你给他们的 API 是怎么样的,以及它们如何影响其他工程师编写软件。 ![](https://cdn-images-1.medium.com/max/800/1*pnYiZTAfQqsbeS7kVkLe_g.png) -Slide text: Empathy on easy mode. +幻灯片文本:对简易模式感同身受。 -Luckily this is empathy on easy mode. Empathy is generally hard, and this is still very hard. But at least the people that you are having empathy with, they are also other software engineers. And so while they might be very different from you, they at least have in common that they are building software. This type of empathy is really something you can get quite good at as you gain more experience. +幸运的是,这是对简易模式感同身受。同理心通常很难,而且这仍然非常困难。但至少与你有同感的人,他们也是软件工程师。尽管他们可能与你截然不同,但他们至少同你一样也在开发软件。当你获得更多的经验时,你可以很擅长的运用这种类型的同理心。 ![](https://cdn-images-1.medium.com/max/800/1*Op0wLWIqwZ-A5iSuWrqtKA.png) -Slide text: Programming model. +幻灯片文本:编程模型。 -Thinking about these topics there is one really important term that I want to talk about, which is the programming model–a word that I’m going to use a lot. It stands for “given a set of APIs, or of libraries, or of frameworks, or of tools–how do people write software in that context.” And my talk is really about, how subtle changes in APIs and so forth, how they impact the programming model. +考虑到这些话题,我想谈谈一个非常重要的术语,那就是编程模型,这个词我会用很多次。它代表“给定一套 API,库,框架或工具,人们如何在这种背景下编写软件”。我的演讲内容真的是关于 API 等等的细微变化,如何影响编程模型。 ![](https://cdn-images-1.medium.com/max/800/1*zuLA-tH9b8k4i1yfKMScmA.png) -Slide text: Programming model impact examples: React, Preact, Redux, Date picker from npm, npm. +幻灯片文本:影响编程模型的示例:React,Preact,Redux,Date picker,npm。 + +我想举几个影响编程模型的例子:假设你有一个 Angular 项目,并且你说:“我将把它移植到 React 中”,这显然会改变人们编写软件的方式,对吧?但是接下来你想:“啊哈,60 KB 就为了使用一点虚拟 DOM 操作,让我们切换到 Preact”,这是一个 与 React API 兼容的库,即使你做出了这个选择,它也不会改变人们编写软件的方式。或许随着项目的进展,你会觉得“单单 React 自有的状态管理还不够,应用会变得很复杂,我应该有一些东西来管理应用状态,我会引入 Redux”,这将改变人们编写软件的方式。然后又来了个新需求“我们需要一个日期选择器”,你到 npm 上进行搜索,有 500 个结果,你选了一个日期组件。你挑选哪一个真的很重要吗?它绝对不会改变你编写软件的方式。但是,npm 以及它的庞大生态集合,绝对会改变你编写软件的方式。当然,这几个例子只是一些可能影响人们编写软件的事。 -I want to give a few examples of things that impact the programming model: Let’s say you have an Angular project and you say “I’m going to port this to React” that is obviously going to change how people write software, right? But then you’re like “Ah, 60KB for a bit of virtual DOM munging, let’s switch to Preact”–that is an API compatible library, it is not going to change how people write software, just because you make that choice. Maybe then you’re like “this is all really complex, I should have something orchestrating how my application works, I’m going to introduce Redux.”–that is going to change how people write software. You then get this requirement “we need a date picker” and you go to npm, there are 500 results, you pick one. Does it really matter which one you pick? It definitely won’t change how you write software. But having npm at your fingertips, this vast collection of modules, having that around absolutely changes how you write software. Of course, these are just a few examples of things that might or might impact how people write software. ![](https://cdn-images-1.medium.com/max/800/1*KfcGnWC3WcwBqGYLPiybgw.png) -Slide text: Code splitting. +幻灯片文本:代码分割. -Now I want to talk about one aspect that all large JavaScript applications have in common, when you deliver them to users: Which is that they eventually get so big that you don’t want to deliver them all at once. And for this we’ve all introduced this technique called code splitting. What code splitting means is that you define a set of bundles for your application. So, you’re saying “Some users only use this part of my app, some users use another part”, and so you put together bundles that only get downloaded when the part of an application that a user is actually dealing with is executed. This is something all of us can do. Like many things it was invented by the closure compiler–at least in the JavaScript world. But I think the most popular way of doing code splitting is with webpack. And if you are using RollupJS, which is super awesome, they just recently added support for it as well. Definitely something y’all should do, but there are some things to think about when you introduce this to an application, because it does have impact on the programming model. +现在我想谈谈所有大型 JavaScript 应用在将它们交付给用户时的一个共同点:它们最终变得非常大,以至于你不希望一开始就把整个应用一次性传输给用户。为此,我们引入了这种称为代码分割的技术。代码分割意味着你为应用程序定义了一组打包。所以,你会说“有些用户只使用我的应用程序的这一部分,有些用户使用另一部分”,因此,当用户实际使用应用程序时,只有使用到的部分才被下载执行。这是我们所有人都可以做到的。像许多事情一样,它是由闭包编译器实现的 —— 至少在 JavaScript 世界中。但我认为使用 webpack 进行代码分割是最流行的方式。如果你使用的是 RollupJS,这是超棒的,他们最近也增加了对代码分割的支持。代码分割绝对是你们应该做的事情,但是当你将它引入到应用程序中时有一些事情需要考虑,因为它确实对编程模型有影响。 ![](https://cdn-images-1.medium.com/max/800/1*vAR8HCbwiwX8bVa0xIsk6g.png) -Slide text: Sync -> Async. +幻灯片文本:同步 -> 异步。 -You have things that used to be sync that now become async. Without code splitting your application is nice and simple. There is this one big thing. It starts up, and then it is stable, you can reason about it, you don’t have to wait for stuff. With code splitting, you might sometimes say “Oh, I need that bundle”, so you now need to go to the network, and you have to factor in that this can happen, and so the applications becomes more complex. +你有过去是同步现在成为异步的东西。你的应用程序在没有代码分割时,简单美好。有一件大事。它启动,然后它很稳定,你了解它的前世今生,你不必等待资源加载。有了代码分割后,有时候你可能会说“哦,我需要那个打包文件”,所以你现在需要利用网络来获取所需的文件,这也使得你必须考虑网络可能出现异常情况,所以应用程序也变得更加复杂。 ![](https://cdn-images-1.medium.com/max/800/1*DqT7As1rm_M9cxyW1RIW6w.png) -Slide text: Human. +幻灯片文本:人性化。 -Also, we have humans entering the field, because code splitting requires you to define bundles, and it requires you to think about when to load them, so these humans, engineers on your team, they now have to make decisions what is going into which bundle and when to load that bundle. Every time you have a human involved, that clearly impacts the programming model, because they have to think about such things. +此外,我们需要有人介入,因为代码拆分需要你定义如何打包,需要你考虑何时加载它们,所以,那些在你们团队的工程师们现在必须决定哪些文件打包到一起,什么时候加载那些打包文件。每次有人介入时,都会明显影响编程模型,因为他们必须考虑这些问题。 ![](https://cdn-images-1.medium.com/max/800/1*0jNa8A5ciY6pCJCN65vLiA.png) -Slide text: Route based code splitting. +幻灯片文本:基于路由的代码分割。 -There is one very established way that solves this problem, that gets the human out of the mess when doing code splitting, which is called route based code splitting. If you’re not using code splitting yet, that is probably how you should do it as a first cut. Routes are the baseline URL structure of your application. You might, for example, have your product pages on `/product/` and you might have your category pages somewhere else. You just make each route one bundle, and your router in your application now understands there is code splitting. And whenever the user goes to a route, the router loads the associated bundle, and then within that route you can forget about code splitting existing. Now you are back to the programming model that is almost the same as having a big bundle for everything. It is a really nice way to do this, and definitely a good first step. +有一种非常成熟的方法可以解决这个问题,它可以将我们从进行代码分割的混乱中解脱出来,它被称作基于路由的代码分割。如果你还没有使用代码分割,那它可能是你初次进行代码分割的方式。路由将应用程序以 URL 粒度进行分割。例如,你的产品页面可能在 `/product/` 上,并且你的分类页面可能在其他地方。你只需将每个路由用的文件打包到一起,然后你的应用程序将根据路由自动进行代码分割。无论何时用户访问路由,路由都会加载相关的打包文件,有了路由之后,你可以忘记代码分割的存在。再从编程模型上来看,这几乎与将所有东西都有打包到一起一样。这是一种非常好的代码分割方法,绝对是个好的开始。 -But the title of this talk is designing **VERY** large JavaScript applications, and they quickly become so big that a single bundle per route might not be feasible anymore, because the routes themselves become very big. I actually have a good example for an application that is big enough. +但是这个演讲的主题是设计**非常**大型的 JavaScript 应用程序,并且它们会很快变得巨大无比,以至于基于路由的代码分割不再适用,因为路由本身也会变得非常大。实际上我有一个关于应用程序的好例子。 ![](https://cdn-images-1.medium.com/max/800/1*ox94bGuhxWXE-OubL7St6w.png) -Google Search query screenshot for “public speaking 101”. +“public speaking 101”的谷歌搜索查询截图。 -I was figuring out how to become a public speaker coming up to this talk, and I get this nice list of blue links. You could totally envision that this page fits well into a single route bundle. +我正在弄清楚如何成为这场演讲的公众演讲者,并且我得到了一个很好的蓝色链接列表。你完全可以设想这个页面非常适合将所有文件打包到一个路由里。 ![](https://cdn-images-1.medium.com/max/800/1*P-XiIPnuzq9_KLA1nG-uRA.png) -Google Search query screenshot for “weath”. +“weath”的谷歌搜索查询截图。 -But then I was wondering about the weather because California had a rough winter, and suddenly there was this completely different module. So, this seemingly simple route is more complicated than we thought. +但后来我对天气感到疑惑,因为加州有一个严峻的冬天,突然间有了这个完全不同的模块。所以,这个看似简单的路由比我们想象的更为复杂。 ![](https://cdn-images-1.medium.com/max/800/1*Y7e5LoeBggY01aRkJAiwWA.png) -Google Search query screenshot for “20 usd to aud”. +“20 usd to aud”的谷歌搜索查询截图。 -And then I was invited to this conference, and was checking out how much 1 US dollar is in Australian dollars, and there is this complex currency converter. Obviously there is about 1000s more of these specialized modules, and it infeasible to put them all in one bundle, because that bundle would be a few megabytes in size, and users would become really unhappy. +后来我被邀请参加这次会议,我查看了 1 美元是多少澳元,那时出现了这个复杂的货币转换器。很显然,这些专用模块大约有 1000 多个,将它们放在同一个打包文件中是不可行的,因为打包文件的大小会有几兆字节,用户将会真的变得不高兴。 ![](https://cdn-images-1.medium.com/max/800/1*qZhd4a0S-CCB5mUiN3fo5Q.png) -Slide text: Lazy load at component level? +幻灯片文本:组件级别的懒加载? -So, we can’t just use route based code splitting, we have to come up with a different way of doing it. Route based code splitting was nice, because you split your app at the coarsest level, and everything further down could ignore it. Since I like simple things, how about doing super fine-grained instead of super coarse-grained splitting. Let’s think about what would happen if we lazy loaded every single component of our website. That seems really nice from an efficiency point of view when you only think about bandwidth. It might be super bad from other point of views like latency, but it is certainly worth a consideration. +所以,我们不能只使用基于路由的代码分割,我们必须想出一个不同的方式来做代码分割。基于路由的代码拆分很不错,因为你将应用程序进行了最粗略级别的拆分,而当应用程序进一步增长时,它能起到的作用就微乎其微了。因为我喜欢直截了当,那么做超级细粒度而不是超级粗粒度拆分怎么样。让我们想象如果我们网站的每一个组件都懒加载,会发生什么。当你只考虑带宽时,从效率的角度来看,这似乎非常好。从延迟等其他观点来看,这可能是非常糟糕的,但它肯定是值得考虑。 ![](https://cdn-images-1.medium.com/max/800/1*Lr2hIk4eH9uU33e77zeSmA.png) -Slide text: React component statically depend on their children. +幻灯片文本:React 组件同他们的子组件是静态依赖关系。 -But let’s imagine, for example, your application uses React. And in React components statically depend on their children. That means if you stop doing that because you are lazy loading your children, then it changes your programming model, and things stop being so nice. +但让我们想象一下,例如,你的应用程序使用 React。并且在 React 中,组件们同他们的子组件是静态依赖关系。这意味着如果你懒加载你的子组件,就会改变你的编程模型,并且事情会变得不那么美好,这让你只好叫停这种策略。 ![](https://cdn-images-1.medium.com/max/800/1*SWkk2vyn344qCNCPSIkXPA.png) -ES6 import example. +ES6 导入示例。 -Let’s say you have a currency converter component that you want to put on your search page, you import it, right? That is the normal way of doing it in ES6 modules. +假设你有一个货币转换器组件,你想把它放在你的搜索页面上,你可以导入它,是这样的吧?这是在 ES6 模块中使用的普通方式。 ![](https://cdn-images-1.medium.com/max/800/1*RxlHaYEav0OaODKYKiUubw.png) -Loadable component example. +Loadable 组件示例。 -But if you want to lazy load it, you get code like this where you use dynamic import, which is a new fancy thing to lazy load ES6 modules and you wrap it in a loadable component. There are certainly 500 million ways to do this, and I am not a React expert, but all of these will change how you write the application. +但是如果你想延迟加载它,你会得到这样的代码,你把它包装在 Loadable 组件中,你还使用一种懒加载 ES6 模块的新方式动态导入。当然有成千上万种方法可以做到这一点,我不是 React 专家,但所有这些方式都会改变你编写应用程序的方式。 ![](https://cdn-images-1.medium.com/max/800/1*N5AMAbobPjsO_lXCPt9-ZA.png) -Slide text: Static -> Dynanic. +幻灯片文本:静态 -> 动态。 -And things aren’t as nice anymore–something that was static, now becomes dynamic, which is another red flag for the programming model changing. +事情不再那么美好了 —— 一些静态的东西现在变成了动态的,这是编程模型改变的另一个警示。 ![](https://cdn-images-1.medium.com/max/800/1*j9OB_yjli59MZMyIs9V0_A.png) -Slide text: Who decides what to lazy load when? +幻灯片文本:谁来决定何时对什么东西进行懒加载? -You have to suddenly wonder: “Who decides what to lazy load when” because that is going to impact the latency of your application. +你不得已突然想知道:“谁来决定何时对什么东西进行懒加载”,因为这会影响到应用程序的等待时间。 ![](https://cdn-images-1.medium.com/max/800/1*rsJ-C7ph0BrJiwTjHKv6_w.png) -Slide text: Static or dynamic? +幻灯片文本:静态还是动态? -The human is there again and they have to think about “there is static import, there is dynamic import, when do I use which?”. Getting this wrong is really bad because one static import, when it should have been dynamic suddenly may put stuff into the same bundle that shouldn’t be. These are the things that are going to go wrong when you have a lot of engineers over long periods of time. +人类再次出现,他们必须思考“有静态导入,有动态导入,什么时候该用哪一个?”。弄错就非常糟糕了,当一个静态导入的文件突然变成动态导入的时候,可能会把某些东西错误的打包进文件。随着时间的推移,同时你又有很多工程师在这个项目上开发,恐怕就会出错。 ![](https://cdn-images-1.medium.com/max/800/1*QGoX4bYhEAuNjuKwQhQ0hg.png) -Slide text: Split logic and rendering +幻灯片文本:分割逻辑和渲染。 -Now I’m going to talk about how Google actually does this and what is one way to get a good programming model, while also achieving good performance. What we do is we take our components and we split them by rendering logic, and by application logic, like what happens when you press a button on that currency converter. +接下来我会分享 Google 如何做到保证良好编程模型的前提下,又有不错的性能的。我们通过渲染逻辑和应用逻辑来分割组件,比如当你按下货币转换器上的按钮时发生的情况。 ![](https://cdn-images-1.medium.com/max/800/1*vMskVnAwJgkZmvl4E-8E4Q.png) -Slide text: Only load logic if it was rendered. +幻灯片文本:仅在渲染时加载是唯一的加载逻辑。 -So, now we have two separate things, and we only ever load the application logic for a component when we previously rendered it. This turns out to be a very simple model, because you can simply server side render a page, and then whatever was actually rendered, triggers downloading the associated application bundles. This puts the human out of the system, as loading is triggered automatically by rendering. +所以,现在我们有两件独立的事情,并且我们只在渲染时才加载组件的应用程序逻辑。事实证明,这是一个非常简单的模型,因为你可以简单地在服务端渲染页面,然后由实际呈现的内容,触发下载关联的应用程序打包文件。因为加载是通过渲染自动触发的,这使得人得以脱离系统。 ![](https://cdn-images-1.medium.com/max/800/1*Doqt-GOkUp13Qgk5r7WR1g.png) -Slide text: Currency converter on search result page. +幻灯片文本:搜索结果页面上的货币转换器。 -This model may seem nice, but it does have some tradeoffs. If you know how server side rendering typically works in frameworks like React or Vue.js, what they do is a process called hydration. The way hydration works, is you server side render something, and then on the client you render it again, which means you have to load the code to render something that is already on the page, which is incredibly wasteful both in terms of loading the code and in terms of executing it. It is a bunch of wasted bandwidth, it is a bunch of wasted CPU–but it is really nice, because you get to ignore on the client side that you server side rendered something. The method we use at Google is not like that. So, if you design this very large application, you have think about: Do I take that super fast method that is more complicated, or do I go with hydration which is less efficient, but such a nice programming model? You will have to make this decision. +这个模型看起来不错,但它确实有一些折衷。如果你知道通常服务端渲染在 React 或 Vue.js 等框架中如何工作,这个过程被称为 hydration。hydration 是这样的,你服务端渲染的一些东西,然后在客户端再次渲染它,这意味着你必须加载代码来渲染一些已经在页面上的东西,这在加载代码和执行代码方面都是巨大的浪费。这么做既浪费带宽,又浪费 CPU —— 但它确实很好,因为你在客户端忽略了服务端渲染的东西。我们在 Google 使用的方法不是那样的。所以,如果你设计这个非常大型的应用程序,你就会想:我是采用那种更复杂的超快速方法,还是采用效率较低的 hydration 方式,但这样能有个良好的编程模型?你将不得不做出这个决定。 ![](https://cdn-images-1.medium.com/max/800/1*uteTbmuKZF1wGvoysgsBYw.png) -Slide text: 2017 Happy New Year. +幻灯片文本:2017 新年快乐。 -My next topic is my favorite problem in computer science–which is not naming things, although I probably gave this a bad name. It is the _“2017 holiday special problem”_. Who here has ever written some code, and now it is no longer needed but it is still in your codebase? … This happens, and I think CSS is particularly famous for it. You have this one big CSS file. There is this selector in there. Who really knows whether that still matches anything in your app? So, you end up just keeping it there. I think the CSS community is at the forefront of a revolution, because they realized this is a problem, and they created solutions like CSS-in-JS. With that you have a single file component, the 2017HolidaySpecialComponent, and you can say “it is not 2017 anymore” and you can delete the whole component and everything is gone in one swoop. That makes it very easy to delete code. I think this is a very big idea, and it should be applied to more than just CSS. +我的下一个话题是我最喜欢的计算机科学问题 —— 它不是命名事物,尽管我很可能给它起了个糟糕的名字。这是“**2017 年假期特别问题**”。过去有人写过一些代码,现在不再需要它们了,但它仍然在你的代码库中?...这种情况时常发生,我认为 CSS 的问题尤为突出。你有一个大型 CSS 文件。里面有很多样式选择器。谁真的知道哪些样式选择器是否仍然对应着你应用中的内容?所以,你最终只能把那些代码留在那里。我认为 CSS 社区处于变革的最前沿,因为他们意识到这个问题,并且他们创建了诸如 CSS-in-JS 之类的解决方案。因为你的组件可以放到一个单独的文件里,2017 年假期特别问题组件,你可以说“它不再是 2017 问题”,你可以删除整个组件,并且所有相关文件一并消失。这使得删除代码非常容易。我认为这是一个非常好的想法,它不仅仅适用于 CSS。 ![](https://cdn-images-1.medium.com/max/800/1*rkAN_sLohIO63JCOTZ1JgA.png) -Slide text: Avoid central configuration at all cost. +幻灯片文本:不惜一切代价避免中央配置。 -I want to give a few examples of this general idea that you want to avoid central configuration of your application at all cost, because central configuration, like having a central CSS file, makes it very hard to delete code. +我想举几个例子,说明为什么你想不惜一切代价避免在你的应用程序中采用中央配置,因为中央配置(比如大型 CSS 文件)使得代码难以删除。 ![](https://cdn-images-1.medium.com/max/800/1*-OoPTo-xaxFr2YOGGFnapw.png) -Slide text: routes.js. +幻灯片文本:routes.js。 -I was talking before about routes in your application. Many applications would have a file like “routes.js” that has all your routes, and then those routes map themselves to some root component. That is an example of central configuration, something you do not want in a large application. Because with this some engineer says “Do I still need that root component? I need to update that other file, that is owned by some other team. Not sure I’m allowed to change it. Maybe I’ll do it tomorrow”. With that these files becomes addition-only. +我之前在你的应用程序中谈论过路由。许多应用程序都会有一个类似“routes.js”的文件,其中包含所有路由信息,这些路由将自己映射到某个根组件。这是一个中央配置的例子,你不会希望在大型应用程序中这么做。因为有了这种中央配置,工程师会说:“我还需要那个根组件吗?我需要更新其他文件,那是其他团队负责的文件。我不确定是否被允许修改它。也许我该明天再做“。之后,这些文件只会越来越大。 ![](https://cdn-images-1.medium.com/max/800/1*NsqgsGwmgEcy_PedNzmnbQ.png) -Slide text: webpack.config.js. +幻灯片文本:webpack.config.js。 -Another example of this anti-pattern is the webpack.config.js file, where you have this one thing that is assumed to build your entire application. That might go fine for a while, but eventually needing to know about every aspect of what some other team did somewhere in the app just doesn’t scale. Once again, we need a pattern to emerge how to decentralize the configuration of our build process. +这种反模式的另一个例子是 webpack.config.js 文件,在这里你可以假设你通过它构建了整个应用程序。刚开始可能没什么问题,但随着时间的推移,这份配置不再适用,你需要知道其他团队在应用程序中做了什么,这样才能对配置文件做出兼容性的调整。再一次,我们需要一个模式来展现如何分散我们构建过程的配置。 ![](https://cdn-images-1.medium.com/max/800/1*L7ZmdS2JvqwWJySz-X50xw.png) -Slide text: package.json. +幻灯片文本:package.json。 -Here is a good example: package.json, which is used by npm. Every package says “I have these dependencies, this is how you run me, this is how you build me”. Obviously there can’t be one giant configuration file for all of npm. That just wouldn’t work with hundreds of thousands of files. It would definitely get you a lot of merge conflicts in git. Sure, npm is very big, but I’d argue that many of our applications get big enough that we have to worry about the same kind of problems and have to adopt the same kind of patterns. I don’t have all the solutions, but I think that the idea that CSS-in-JS brought to the table is going to come to other aspects of our applications. +这有一个很好的例子:npm 使用的 package.json。每个软件包都会说“我有这些依赖关系,这就是你如何运行我,如何构建我的方式”。显然,对于所有的 npm,都不能有一个巨大的配置文件。这对于成千上万的文件来说不起作用。这肯定会让你在 git 操作中遇到很多合并冲突。当然,npm 非常大,但我认为我们的许多应用程序已经变得足够大,让我们不得不担心同样的问题,并且必须采用相同的模式。我没有所有的解决方案,但我认为 CSS-in-JS 的想法将会涉及我们应用程序的其他方面。 ![](https://cdn-images-1.medium.com/max/800/1*E_g_WgMXGuJtyG-F4AGTNg.png) -Slide text: Dependency trees. +幻灯片文本:依赖关系树。 -More abstractly I would describe this idea that we take responsibility for how our application is designed in the abstract, how it is organized, as _taking responsibility of shaping the dependency tree of our application_. When I say “dependency” I mean that very abstractly. It could be module dependencies, it could be data dependencies, service dependencies, there are many different kinds. +更抽象地说,我会描述这个想法,即我们负责如何抽象地设计我们的应用程序,如何组织它,作为**承担塑造我们的应用程序的依赖树的责任**。当我说“依赖”时,我的意思是非常抽象的。它可能是模块依赖关系,可能是数据依赖关系,服务依赖关系,还有很多不同的类型。 ![](https://cdn-images-1.medium.com/max/800/1*DfOMmyxC4guVZkyQ4IlF7g.png) -Slide text: Example dependency tree with router and 3 root components. +幻灯片文本:由路由和3个根组件构成的依赖关系树示例。 -Obviously, we all have super complicated applications, but I’m going to use a very simple example. It has only 4 components. It has a router that knows how to go from one route of your application to the next, and it has a few root components, A, B, and C. +显然,我们都有超复杂的应用程序,但我会用一个非常简单的例子。它只有4个组成部分。它有一个路由,知道如何从应用程序的一个路由到下一个路由,它有几个根组件,A,B和C。 ![](https://cdn-images-1.medium.com/max/800/1*CivPR-20NP0dXlIkWfBk6w.png) -Slide text: The central import problem. +幻灯片文本:中心导入问题。 -As I mentioned before this has the central import problem. +正如我之前提到的那样,这具有中心导入问题。 ![](https://cdn-images-1.medium.com/max/800/1*Y9AgFj90bpFsKq6e7o7Jbw.png) -Slide text: Example dependency tree with router and 3 root components. Router imports root components. +幻灯片文本:由路由和3个根组件构成的依赖关系树示例。路由导入根组件。 -Because the router now has to import all the root components, and if you want to delete one of them you have to go to the router, you have to delete the import, you have to delete the route, and eventually you have the holiday special 2017 problem. +因为路由现在必须导入所有的根组件,如果你想删除其中的一个,你不得不进入路由文件,删除引用,删除路由,并最终你有了 2017 假期特别问题。 ![](https://cdn-images-1.medium.com/max/800/1*isSwE9e1XLiEw9sbZHwmQQ.png) -Slide text: Import -> Enhance. +幻灯片文本:导入 -> 增强。 -We at Google have come up with a solution for this, that I want to introduce to you, which I don’t think we have ever talked about. We invented a new concept. It is called enhance. It is something you use instead of import. +我们在谷歌已经为此提出了一个解决方案,我想向你们介绍一下,我想我们从来没有谈过这件事。我们提出了一个新概念。它被称为增强。这是你用来代替导入的东西。 ![](https://cdn-images-1.medium.com/max/800/1*7yPG-uXeixsnQk3k-X9UXw.png) -Slide text: Import -> Enhance. +幻灯片文本:导入 -> 增强。 -In fact, it is the opposite of import. It is a reverse dependency. If you enhance a module, you make that module have a dependency on you. +实际上,这与导入是相反的。这是一个逆向依赖。如果你增强一个模块,你会让这个模块对你有依赖性。 ![](https://cdn-images-1.medium.com/max/800/1*bDH4yzG0mrrYlrs2C9twsA.png) -Slide text: Example dependency tree with router and 3 root components. Root components enhance router. +幻灯片文本:由路由和3个根组件构成的依赖关系树示例。根组件增强了路由。 -Looking at the dependency graph, what happens it that there are still the same components, but the arrows point in the opposite direction. So, instead of the router importing the root component, the root components announce themselves using enhance to the router. This means I can get rid of a root component by just deleting the file. Because it is no longer enhancing the router, that is the only operation you have to do to delete the component. +看看依赖关系图,它发生了什么,仍然是相同的组件,但箭头指向相反的方向。因此,不是路由导入根组件,根组件宣布自己增强了路由的功能。这意味着我可以通过删除文件来删除根组件。因为它不再增强路由,所以这是删除组件的唯一操作。 ![](https://cdn-images-1.medium.com/max/800/1*HDW95QuGKQCsXqwXiUtB5g.png) -Slide text: Who decides when to use enhance? +幻灯片文本:谁来决定何时使用增强? -That is really nice, if it wasn’t for the humans again. They now have to think about “Do I import something, or do I use enhance? Which one do I use under which circumstances?”. +这真的很棒,如果它不是再次涉及人性化。他们现在必须考虑“我是该导入它,还是使用增强?我在哪种情况下使用哪一种方式?”。 ![](https://cdn-images-1.medium.com/max/800/1*Hr47VQZYSKiBuDap2XgbbQ.png) -Image: Danger. Hazardous chemicals. +图片:危险。危险化学品。 -This is particular bad case of this problem, because the power of enhancing a module, of being able to make everything else in the system have a dependency on you is very powerful and very dangerous if gotten wrong. It is easy to imagine that this might lead to really bad situations. So, at Google we decided it is a nice idea, but we make it illegal, nobody gets to use it–with one exception: generated code. It is a really good fit for generated code actually, and it solves some of the inherent problems of generated code. With generated code you sometimes have to import files you can’t even see, have to guess their names. If, however, the generated file is just there in the shadows and enhances whatever it needs, then you don’t have these problems. You never have to know about these files at all. They just magically enhance the central registry. +这是这个问题的特别糟糕的情况,因为增强模块的能力,能够使系统中的所有其他东西都依赖于你是非常强大的,如果出错的话,就会非常危险。很容易想象这可能会导致非常糟糕的情况。所以,在谷歌我们认为这是一个好主意,但我们也认为它是非法的,没有人可以使用它 —— 有一个例外:生成的代码。它实际上非常适合于生成的代码,它解决了生成代码的一些固有问题。有了生成的代码,你有时必须导入你甚至看不到的文件,必须猜测他们的名字。但是,如果生成的文件恰好不可见,并增强了它所需的任何内容,那么你就没有这些问题。你根本不需要知道这些文件。他们只是神奇地增强了中央注册表。 ![](https://cdn-images-1.medium.com/max/800/1*od_6cmgitlBJk1g9QxU7Ng.png) -Slide text: Single file component pointing to its parts that enhance a router. +幻灯片文本:单文件组件指向其增强路由的组件。 -Let’s take a look at a concrete example. We have our single file component here. We run a code generator on it and we extract this little route definition file from it. And that route file just says “Hey Router, here I am, please import me”. And obviously you can use this pattern for all kinds of other things. Maybe you are using GraphQL and your router should know about your data dependency, then you can just use the same pattern. +我们来看一个具体的例子。我们这里有个单文件组件。我们在其上运行代码生成器,并从中提取这个小的路由定义文件。那个路由文件只是说“嘿,路由,我在这里,请导入我”。显然,你可以将这种模式用于各种其他事情。也许你正在使用 GraphQL,你的路由应该知道你的数据依赖关系,那么你可以使用相同的模式。 ![](https://cdn-images-1.medium.com/max/800/1*Tg_CvUNzT9K0tbzIVC79kw.png) -Slide text: The base bundle. +幻灯片文本:基本打包文件。 -Unfortunately this is not all we need to know. There is my second favorite problem in computer science which I call the “_Base bundle pile of trash”_. The base bundle in your graph of bundles in your application is the one bundle that will always get loaded–independent of how the user interacts with the application. So, it is particularly important, because if it is big, then everything further down will also be big. If it small, then dependent bundles at least have a chance of being small as well. A little anecdote: At some point I joined the Google Plus JavaScript infrastructure team, and I found out that their base bundle had 800KB of JavaScript. So, my warning to you is: If you want to be more successful than Google Plus, don’t have 800KB of JS in your base bundle. Unfortunately it is very easy to get to such a bad state. +不幸的是,这不仅仅是我们所需要知道的。第二个我最喜欢的计算机科学问题,我称之为“**基础垃圾打包文件**”。在应用程序的打包逻辑中的基本打包文件总是会被加载,而与用户与应用程序的交互方式无关。所以,这一点尤其重要,因为如果它很大,那么所有进一步深入的东西都会很大。如果它很小,那么依赖文件也有可能变小。一个小故事:在某个时候,我加入了 Google Plus JavaScript 基础架构团队,并且我发现他们的基础打包文件包含 800 KB 的 JavaScript。所以,我对你的警告是:如果你想比 Google Plus 更成功,那么在你的基础打包文件中不要有 800 KB 的 JS。不幸的是,很容易就达到这样一种糟糕的状态。 ![](https://cdn-images-1.medium.com/max/800/1*wW_u72nFdPiKjEINH4ubDg.png) -Slide text: Base bundle pointing to 3 different dependencies. +幻灯片文本:指向 3 个不同依赖关系的基础打包文件。 -Here is an example. Your base bundle needs to depend on the routes, because when you go from A to B, you need to already know the route for B, so it has to always be around. But what you really don’t want in the base bundle is any form of UI code, because depending on how a user enters your app, there might be different UI. So, for example the date picker should absolutely not be in your base bundle, and neither should the checkout flow. But how do we prevent that? Unfortunately imports are very fragile. You might innocently import that cool _util_ package, because it has a function to make random numbers. And now somebody says “I need a utility for self driving cars” and suddenly you import the machine learning algorithms for self driving cars into your base bundle. Things like that can happen very easily since imports are transitive, and so things tend to pile up over time. +这有一个例子。你的基础打包文件需要依赖于路由,因为当你从 A 到 B 时,你需要知道 B 的路由,所以它总是在周围。但是你真正不想要的是将任何形式的 UI 代码打包进基础打包文件,是因为取决于用户如何进入你的应用程序,可能会有不同的用户界面。所以,例如日期选择器绝对不应该放在你的基础打包文件中,结账流程也不应该。但我们如何防止这种情况?不幸的是导入非常脆弱。你可能在无意中导入那个很酷的**工具**包,因为它有一个函数来生成随机数。现在有人说“我需要一种自动驾驶汽车的实用工具”,并且突然将自动驾驶汽车的机器学习算法导入到你的基础打包文件中。类似这样的事情很容易发生,因为导入是传递性的,所以问题往往会随着时间的推移而累积起来。 ![](https://cdn-images-1.medium.com/max/800/1*myk-tffGyQx74OIZT4n0mw.png) -Slide text: Forbidden dependency tests. +幻灯片文本:禁止依赖测试。 -The solution we found for this are _forbidden dependency tests_. Forbidden dependency tests are a way to assert that for example your base bundle does not depend on any UI. +我们找到的解决方案是**禁止依赖测试**。禁止依赖测试是一种断言,例如你的基础打包文件不依赖于任何 UI。 ![](https://cdn-images-1.medium.com/max/800/1*vDtioYTfzhCB9e7jc9A4pg.png) -Slide text: Assert that base bundle does not depend on React.Component. +幻灯片文本:断言基本打包文件不依赖于 React.Component。 -Let’s take a look at a concrete example. In React every component needs to inherit from React.Component. So , if your goal is that no UI could ever be in the base bundle just add this one test that asserts that React.Component is not a transitive dependency of your base bundle. +我们来看一个具体的例子。在 React 中,每个组件都需要继承自 React.Component。因此,如果你的目标是基本打包文件中没有 UI,只需添加一个测试来确定 React.Component 不是你基本打包文件的传递依赖。 ![](https://cdn-images-1.medium.com/max/800/1*s5rDafWJi90dcrlEQSAepg.png) -Forbidden dependencies crossed out. +禁止的依赖关系被删除。 -Looking at the previous example again, you just get a test failure when someone wants to add the date picker. And these test failures are typically very easy to fix right then, because usually that person didn’t really mean to add the dependency–it just crept in through some transitive path. Compare this to when this dependency would have been around for 2 years because you didn’t have a test. In those cases it is typically extremely hard to refactor your code to get rid of the dependency. +再看一下前面的例子,当有人想添加日期选择器时,只会出现测试失败。而这些测试失败通常很容易就能很好地解决,因为通常这个人并不是真的想要添加依赖关系 —— 它只是通过一些传递路径进入。比较这一点,当这种依赖关系已经存在了 2 年,因为你没有测试。在这些情况下,通常很难通过重构代码来摆脱依赖关系。 ![](https://cdn-images-1.medium.com/max/800/1*ONmcxDRRdY9DpR8QfwMj4g.png) -Slide text: The most natural path. +幻灯片文本:最自然的路径。 -Ideally though, you find that most natural path. +理想情况下,你会发现最自然的路径。 ![](https://cdn-images-1.medium.com/max/800/1*7XRIRO-_Y165Gn7Zff_fKQ.png) -Slide text: Most straightforward way must be the right way. +幻灯片文本:最直接的方式必须是正确的。 -You want to get to a state where whatever the engineers on your team do, the most straightforward way is also the right way–so that they don’t get off the path, so that they naturally do the right thing. +你想要达到这样一个状态,无论你的团队中的工程师做什么,最直接的方式也是正确的方式 —— 这样他们就不会离开这条道路,所以他们自然而然地做了正确的事情。 ![](https://cdn-images-1.medium.com/max/800/1*T6E-ExC2HWa0X--OiJ_vAA.png) -Slide text: Otherwise add a test that ensure the right way. +幻灯片文本:否则添加一个确保正确的测试。 -This might not always be possible. In that case just add a test. But this is not something that many people feel empowered to do. But **please feel empowered to add tests to your application that ensure the major invariants of your infrastructure**. Tests are not only for testing that your math functions do the right thing. They are also for infrastructure and for the major design features of your application. +这可能不总是可行的。在那种情况下,只需添加一个测试。但这不是很多人认为有权做的事情。但是,**为确保你的基础架构保持不变,请为你的测试程序添加测试的授权**。测试不仅仅是为了测试你的数学函数是否正确。它们也用于基础架构和应用程序的主要设计特性。 ![](https://cdn-images-1.medium.com/max/800/1*y3COuLXS8b1vAQQESjp30Q.png) -Slide text: Avoid human judgement outside of application domain. +幻灯片文本:避免在应用领域之外进行人为判断。 -Try to avoid human judgement whenever possible outside of the application domain. When working on an application we have to understand the business, but not every engineer in your organization can and will understand how code splitting works. And they don’t need to do that. Try to introduce these thing into your application in a way that is fine when not everybody understands them and keeps the complexity in their heads. +尽可能避免在应用领域之外进行人为判断。在开发应用程序时,我们必须了解业务,但是并非团队中的每位工程师都能理解代码拆分的原理。而且他们不需要那样做。在不是每个人都能理解它们的时候,试着将这些东西以一种友好的方式引入到你的应用程序中,并保持其复杂性。 ![](https://cdn-images-1.medium.com/max/800/1*CqeGbdnSFMRPtZWPIRZCvw.png) -Slide text: Make it easy to delete code. +幻灯片文本:可以轻松删除代码。 -And really just make it easy to delete code. My talk is called “building very large JavaScript applications”. The best advice I can give: Don’t let your applications get very large. The best way to not get there is to delete stuff before it is too late. +真的,让删除代码简单点。我的演讲题为“构建非常大型的 JavaScript 应用程序”。我可以给出的最佳建议:不要让你的应用程序变得非常大。最好的办法是在还来得及的时候开始删除东西。 ![](https://cdn-images-1.medium.com/max/800/1*Mt_beSIamHND0E6NjBBetA.png) -Slide text: No abstraction is better than the wrong abstraction. +幻灯片文本:没有抽象比错误的抽象更好。 -I want to address just one more point, which is that people sometimes say that having no abstractions at all is better than having the wrong abstractions. What this really means is that the cost of the wrong abstraction is very high, so be careful. I think this is sometimes misinterpreted. It does not mean that you should have no abstractions. It just means you have to be very careful. +我想再谈一点,那就是人们有时会说,没有抽象比错误的抽象要好。这实际上意味着错误的抽象代价非常高,所以要小心。我认为这有时会被误解。这并不意味着你不应该有抽象。这只是意味着你必须非常小心。 -> W_e have to become good at finding the right abstractions_. +> **我们必须善于找到正确的抽象**。 ![](https://cdn-images-1.medium.com/max/800/1*oNXlH0ththqRlPeRm2z0Sw.png) -Slide text: Empathy and experience -> Right abstractions. +幻灯片文本:同理心和经验 -> 正确的抽象。 -As I was saying at the start of the presentation: The way to get there is to use empathy and think with your engineers on your team about how they will use your APIs and how they will use your abstractions. Experience is how you flesh out that empathy over time. Put together, empathy and experience is what enables you to choose the right abstractions for your application +正如我在演讲开始时所说的:实现目标的方式是使用同理心,并与团队中的工程师一起思考他们将如何使用你的 API​​ 以及他们将如何使用抽象。你如何随着时间的推移充实这种同理心会成为经验。综上所述,同理心和经验使你能够为你的应用程序选择正确的抽象 --- From 3099813172480291d3a482d5e317f85c443beb7f Mon Sep 17 00:00:00 2001 From: YiLin Wang Date: Sat, 5 May 2018 23:00:16 +0800 Subject: [PATCH 003/446] =?UTF-8?q?=E5=A6=82=E4=BD=95=E9=80=83=E7=A6=BB=20?= =?UTF-8?q?async/await=20=E5=9C=B0=E7=8B=B1=20(#3738)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 1/1 * 按第一次校对建议修改 * 按第二次校对建议修改 --- TODO1/avoiding-the-async-await-hell.md | 158 ++++++++++++------------- 1 file changed, 79 insertions(+), 79 deletions(-) diff --git a/TODO1/avoiding-the-async-await-hell.md b/TODO1/avoiding-the-async-await-hell.md index 96a99fff307..566539d072d 100644 --- a/TODO1/avoiding-the-async-await-hell.md +++ b/TODO1/avoiding-the-async-await-hell.md @@ -2,135 +2,135 @@ > * 原文作者:[Aditya Agarwal](https://medium.freecodecamp.org/@adityaa803?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/avoiding-the-async-await-hell.md](https://github.com/xitu/gold-miner/blob/master/TODO1/avoiding-the-async-await-hell.md) -> * 译者: -> * 校对者: +> * 译者:[Colafornia](https://github.com/Colafornia) +> * 校对者:[Starriers](https://github.com/Starriers) [whuzxq](https://github.com/whuzxq) -# How to escape async/await hell +# 如何逃离 async/await 地狱 -![](https://cdn-images-1.medium.com/max/1000/1*_3nDjjPTWn4ohLt96IcwCA.png) +![](http://o7ts2uaks.bkt.clouddn.com/1__3nDjjPTWn4ohLt96IcwCA.png) -async/await freed us from callback hell, but people have started abusing it — leading to the birth of async/await hell. +async/await 将我们从回调地狱中解脱,但人们的滥用,导致了 async/await 地狱的诞生。 -In this article, I will try to explain what async/await hell is, and I’ll also share some tips to escape it. +本文将阐述什么是 async/await 地狱,以及逃离 async/await 地狱的几个方法。 -### What is async/await hell +### 什么是 async/await 地狱 -While working with Asynchronous JavaScript, people often write multiple statements one after the other and slap an **await** before a function call. This causes performance issues, as many times one statement doesn’t depend on the previous one — but you still have to wait for the previous one to complete. +进行 JavaScript 异步编程时,大家经常需要逐一编写多个复杂语句的代码,并都在调用语句前标注了 **await**。由于大多数情况下,一个语句并不依赖于前一个语句,但是你仍不得不等前一个语句完成,这会导致性能问题。 -### An example of async/await hell +### 一个 async/await 地狱示例 -Consider if you wrote a script to order a pizza and a drink. The script might look like this: +思考一下,如果你需要写一段脚本预订一个披萨和一杯饮料。脚本可能会是这样的: -``` +```javascript (async () => { - const pizzaData = await getPizzaData() // async call - const drinkData = await getDrinkData() // async call - const chosenPizza = choosePizza() // sync call - const chosenDrink = chooseDrink() // sync call - await addPizzaToCart(chosenPizza) // async call - await addDrinkToCart(chosenDrink) // async call - orderItems() // async call + const pizzaData = await getPizzaData() // 异步调用 + const drinkData = await getDrinkData() // 异步调用 + const chosenPizza = choosePizza() // 同步调用 + const chosenDrink = chooseDrink() // 同步调用 + await addPizzaToCart(chosenPizza) // 异步调用 + await addDrinkToCart(chosenDrink) // 异步调用 + orderItems() // 异步调用 })() ``` -On the surface it looks correct, and it does work. But this is not a good implementation, because it leaves concurrency out of the picture. Let’s understand what its doing so that we can nail down the issue. +表面上看起来没什么问题,这段代码也可以执行。但是它并不是一个好的实现,因为它没有考虑并发性。让我们了解一下这段代码是怎么运行的,这样才可以确定问题所在。 -#### Explanation +#### 解释 -We have wrapped our code in an async [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE). The following occurs in this exact order: +我们将这段代码包裹在一个异步的 [IIFE 立即执行函数](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) 中。准确的执行顺序如下: -1. Get the list of pizzas. -2. Get the list of drinks. -3. Choose one pizza from the list. -4. Choose one drink from the list. -5. Add the chosen pizza to the cart. -6. Add the chosen drink to the cart. -7. Order the items in the cart. +1. 获取披萨列表。 +2. 获取饮料列表。 +3. 从列表中选择一份披萨。 +4. 从列表中选择一杯饮料。 +5. 将选中披萨加入购物车。 +6. 将选中饮料加入购物车。 +7. 将购物车内物品下单。 -#### So what’s wrong ? +#### 哪里出问题了? -As I stressed earlier, all these statements execute one by one. There is no concurrency here. Think carefully: why are we waiting to get the list of pizzas before trying to get the list of drinks? We should just try to get both the lists together. However when we need to choose a pizza, we do need to have the list of pizzas beforehand. The same goes for the drinks. +如我之前所强调过的,所有语句都会逐一执行。此处并无并发操作。仔细想一下:为什么我们要在获取披萨列表完成后才去获取饮料列表呢?两个列表应该一起获取。但是我们在选择披萨时,确实需要在这之前已获取饮料列表。饮料同理。 -So we can conclude that the pizza related work and drink related work can happen in parallel, but the individual steps involved in pizza related work need to happen sequentially (one by one). +因此,我们可以总结出,披萨相关的事务与饮料相关事务可以并发发生,但是披萨相关事务内部的独立步骤需要继发进行(逐一进行)。 -#### Another example of bad implementation +#### 另一个糟糕实现的例子 -This JavaScript snippet will get the items in the cart and place a request to order them. +这段代码将获取购物车内的东西,并发起一个请求下单。 -``` +```javascript async function orderItems() { - const items = await getCartItems() // async call + const items = await getCartItems() // 异步调用 const noOfItems = items.length for(var i = 0; i < noOfItems; i++) { - await sendRequest(items[i]) // async call + await sendRequest(items[i]) // 异步调用 } } ``` -In this case, the for loop has to wait for the `sendRequest()` function to complete before continuing the next iteration. However, we don’t actually need to wait. We want to send all the requests as quickly as possible and then we can wait for all of them to complete. +在这种情况下,for 循环需要等待 `sendRequest()` 函数完成后才能进行下一个迭代。事实上,我们不需要等待。我们想要尽快发送所有请求,然后等待所有请求执行完毕。 -I hope that now you are getting closer to understanding what is async/await hell and how severely it affects the performance of your program. Now I want to ask you a question. +希望现在你可以更理解 async/await 地狱是什么,以及它对你的程序性能影响有多么严重。现在,我想问你一个问题。 -### What if we forget the await keyword ? +### 如果我们忘了 await 关键字会怎样? -If you forget to use **await** while calling an async function, the function starts executing. This means that await is not required for executing the function. The async function will return a promise, which you can use later. +如果你在调用一个异步函数时忘了使用 **await** 关键字,该函数就会立即开始执行。这意味着 await 对于函数的执行来说不是必需的。异步函数会返回一个 promise 对象,你可以稍后使用这个 promise。 -``` +```javascript (async () => { const value = doSomeAsyncTask() - console.log(value) // an unresolved promise + console.log(value) // 一个未完成的 promise })() ``` -Another consequence is that the compiler won’t know that you want to wait for the function to execute completely. Thus the compiler will exit the program without finishing the async task. So we do need the **await** keyword. +不使用 await 调用异步函数的另一个后果是,编译器不知道你想等待这个函数执行完成。因此编译器将在异步任务完成之前就退出程序。因此我们确实需要 await 关键字。 -One interesting property of promises is that you can get a promise in one line and wait for it to resolve in another. This is the key to escaping async/await hell. +promise 有一个好玩的特性,你可以在一行代码中得到一个 promise 对象,在另一行代码中得到这个 promise 的执行结果。这是逃离 async/await 地狱的关键。 -``` +```javascript (async () => { const promise = doSomeAsyncTask() const value = await promise - console.log(value) // the actual value + console.log(value) // 实际的返回值 })() ``` -As you can see, `doSomeAsyncTask()` is returning a promise. At this point `doSomeAsyncTask()` has started its execution. To get the resolved value of the promise, we use the await keyword and that will tell JavaScript to not execute the next line immediately, but instead wait for the promise to resolve and then execute the next line. +如你所见,`doSomeAsyncTask()` 返回了一个 promise 对象。此时 `doSomeAsyncTask()` 开始执行。我们使用 await 关键字来获取 promise 对象的执行结果,并告诉 JavaScript 不要立即执行下一行代码,而是等待 promise 执行完成再执行下一行代码。 -### How to get out of async/await hell ? +### 如何逃离 async/await 地狱? -You should follow these steps to escape async/await hell. +你需要遵循以下步骤: -#### Find statements which depend on the execution of other statements +#### 找到依赖其它语句执行结果的语句 -In our first example, we were selecting a pizza and a drink. We concluded that, before choosing a pizza, we need to have the list of pizzas. And before adding the pizza to the cart, we’d need to choose a pizza. So we can say that these three steps depend on each other. We cannot do one thing until we have finished the previous thing. +在第一个示例中,我们选择了一份披萨和一杯饮料。可以推断出在选择一份披萨前,我们需要先获得所有披萨的列表。在将选择的披萨加入购物车之前,我们需要先选择一份披萨。因此我们可以说这三个步骤是互相依赖的。我们不能在前一件事完成之前做下一件事。 -But if we look at it more broadly, we find that selecting a pizza doesn’t depend on selecting a drink, so we can select them in parallel. That is one thing that machines can do better than we can. +但是如果把问题看得更广泛一些,我们可以发现选披萨并不依赖选饮料,因此我们可以并行选择。这方面,机器可以比我们做的更好。 -Thus we have discovered some statements which depend on the execution of other statements and some which do not. +因此我们已经发现有一些语句依赖于其它语句的执行,有些则不依赖。 -#### Group-dependent statements in async functions +#### 将互相依赖的语句包裹在 async 函数中 -As we saw, selecting pizza involves dependent statements like getting the list of pizzas, choosing one, and then adding the chosen pizza to the cart. We should group these statements in an async function. This way we get two async functions, `selectPizza()` and `selectDrink()` . +如我们所见,选择披萨包括了如获取披萨列表,选择披萨,将所选披萨加入购物车等依赖语句。我们应该将这些语句包裹在一个 async 函数中。这样我们得到了两个 async 函数,`selectPizza()` 和 `selectDrink()`。 -#### Execute these async functions concurrently +#### 并发执行 async 函数 -We then take advantage of the event loop to run these async non blocking functions concurrently. Two common patterns of doing this is **returning promises early** and the **Promise.all method**. +然后我们可以利用事件循环并发执行这些非阻塞 async 函数。有两种常用模式,分别是**优先返回 promises** 和使用**Promise.all 方法**。 -### Let’s fix the examples +### 让我们来修改一下示例 -Following the three steps, let’s apply them on our examples. +遵循以下三个步骤,将它们应用到我们的示例中。 -``` +```javascript async function selectPizza() { - const pizzaData = await getPizzaData() // async call - const chosenPizza = choosePizza() // sync call - await addPizzaToCart(chosenPizza) // async call + const pizzaData = await getPizzaData() // 异步调用 + const chosenPizza = choosePizza() // 同步调用 + await addPizzaToCart(chosenPizza) // 异步调用 } async function selectDrink() { - const drinkData = await getDrinkData() // async call - const chosenDrink = chooseDrink() // sync call - await addDrinkToCart(chosenDrink) // async call + const drinkData = await getDrinkData() // 异步调用 + const chosenDrink = chooseDrink() // 同步调用 + await addDrinkToCart(chosenDrink) // 异步调用 } (async () => { @@ -138,38 +138,38 @@ async function selectDrink() { const drinkPromise = selectDrink() await pizzaPromise await drinkPromise - orderItems() // async call + orderItems() // 异步调用 })() -// Although I prefer it this way +// 我更喜欢这种方法 (async () => { - Promise.all([selectPizza(), selectDrink()]).then(orderItems) // async call + Promise.all([selectPizza(), selectDrink()]).then(orderItems) // 异步调用 })() ``` -Now we have grouped the statements into two functions. Inside the function, each statement depends on the execution of the previous one. Then we concurrently execute both the functions `selectPizza()` and `selectDrink()` . +现在我们将语句分组到两个函数中。在函数内部,每个语句依赖于前一个语句的执行。然后我们并发执行两个函数 `selectPizza()` 和 `selectDrink()`。 -In the second example, we need to deal with an unknown number of promises. Dealing with this situation is super easy: we just create an array and push the promises in it. Then using `Promise.all()` we concurrently wait for all the promises to resolve. +在第二个例子中,我们需要处理未知数量的 promise。解决这种情况很容易:创建一个数组,将 promise push 进去。然后使用 `Promise.all()` 我们就可以并行等待所有的 promise 处理完毕。 -``` +```javascript async function orderItems() { - const items = await getCartItems() // async call + const items = await getCartItems() // 异步调用 const noOfItems = items.length const promises = [] for(var i = 0; i < noOfItems; i++) { - const orderPromise = sendRequest(items[i]) // async call - promises.push(orderPromise) // sync call + const orderPromise = sendRequest(items[i]) // 异步调用 + promises.push(orderPromise) // 同步调用 } - await Promise.all(promises) // async call + await Promise.all(promises) // 异步调用 } ``` -I hope this article helped you see beyond the basics of async/await, and also helped you improve the performance of your application. +希望本文可以帮你提高 async/await 的基础水平并提升应用的性能。 -If you liked the article, please clap your heart out. Tip — You can clap 50 times! +如果喜欢本文,请点个喜欢。 -Please also share on Fb and Twitter. If you’d like to get updates, follow me on [Twitter](https://twitter.com/dev__adi) and [Medium](https://medium.com/@adityaa803/). If anything is not clear or you want to point out something, please comment down below. +也请分享到 Fb 和 Twitter。如果想获取文章更新,可以在 [Twitter](https://twitter.com/dev__adi) 和 [Medium](https://medium.com/@adityaa803/) 上关注我。有任何问题可以在评论中指出。 --- From 00ae9ba7aabf84f51e6eb50ea2220298881f6c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=86=E5=B0=98?= <296176438@qq.com> Date: Sun, 6 May 2018 11:17:07 +0800 Subject: [PATCH 004/446] =?UTF-8?q?=E6=9B=B4=E4=B8=BA=E8=AF=A6=E7=BB=86?= =?UTF-8?q?=E7=9A=84=E5=9C=B0=E5=9B=BE=E3=80=81=E5=AF=BC=E8=88=AA=E5=92=8C?= =?UTF-8?q?=E5=8A=A9=E6=89=8B=E5=8A=9F=E8=83=BD=20=E2=80=94=E2=80=94=20Goo?= =?UTF-8?q?gle=20I/O=202018=20=E7=9A=84=20Android=20=E5=BA=94=E7=94=A8?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=20(#3766)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Transaction completed * Revised --- ...etailed-map-navigation-assistant-action.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/TODO1/google-i-o-2018-for-android-updated-w-more-detailed-map-navigation-assistant-action.md b/TODO1/google-i-o-2018-for-android-updated-w-more-detailed-map-navigation-assistant-action.md index cf36af6f410..a5c61f21dfc 100644 --- a/TODO1/google-i-o-2018-for-android-updated-w-more-detailed-map-navigation-assistant-action.md +++ b/TODO1/google-i-o-2018-for-android-updated-w-more-detailed-map-navigation-assistant-action.md @@ -2,32 +2,32 @@ > * 原文作者:[technacity](https://twitter.com/technacity) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/google-i-o-2018-for-android-updated-w-more-detailed-map-navigation-assistant-action.md](https://github.com/xitu/gold-miner/blob/master/TODO1/google-i-o-2018-for-android-updated-w-more-detailed-map-navigation-assistant-action.md) -> * 译者: -> * 校对者: +> * 译者:[sisibeloved](https://github.com/sisibeloved) +> * 校对者:[leviding](https://github.com/leviding) -# Google I/O 2018 for Android updated w/ more detailed map, navigation, & Assistant Action +# 更为详细的地图、导航和助手功能 —— Google I/O 2018 的 Android 应用更新 ![](https://9to5google.files.wordpress.com/2018/04/google_io_18_app.jpg?quality=82&w=1024#038;strip=all&w=1600) -With I/O 2018 less than a week away, Google has updated the [official Android companion app](https://play.google.com/store/apps/details?id=com.google.samples.apps.iosched) with a more detailed map and better navigation tools for conference attendees. Meanwhile, earlier in the week, Google also launched an Assistant Action. +离 I/O 2018 仅剩一个星期,Google 更新了 [Android 官方指南应用](https://play.google.com/store/apps/details?id=com.google.samples.apps.iosched),为大会参与者带来了更加详尽的地图和更好的导航工具。同时,这周早些时候,Google 也上线了一个新的谷歌助手(Google Assistant)功能。 -The [Android app was updated](https://9to5google.com/2018/04/26/google-io-2018-android-material-design/) for the 2018 developer conference last week and features the latest [Material Design](https://9to5google.com/2018/04/26/what-is-material-design-2-examples-launch-io/) flourishes, like a BottomAppBar, white theme, and rounded interface elements. +为了迎接 2018 开发者大会,上周这个 [Android 应用已经被更新](https://9to5google.com/2018/04/26/google-io-2018-android-material-design/),并展示了一些最新的 [Material Design](https://9to5google.com/2018/04/26/what-is-material-design-2-examples-launch-io/) 进展,像 BottomAppBar、白色主题和圆形界面元素。 -Version 6.1 this evening updates the Maps tab with a more detailed layout of the Shoreline Amphitheater. First debuting on the [iOS client](https://go.redirectingat.com/?id=3947X1518523&xs=1&isjs=1&url=https%3A%2F%2Fitunes.apple.com%2Fus%2Fapp%2Fgoogle-i-o-2017%2Fid1109898820%3Fmt%3D8%26ign-mpt%3Duo%253D4&xguid=d44cc47b8aff3d8b9ff34bd030eaddac&xuuid=ed349d34e7eb230b1c8b9d9f2397146e&xsessid=d3d0fe4235c34199f73e1f3178be0274&xcreo=0&xed=0&sref=https%3A%2F%2F9to5google.com%2F2018%2F05%2F02%2Fgoogle-i-o-2018-for-android-updated-w-more-detailed-map-navigation-assistant-action%2F&xtz=-480&jv=13.3.0&bv=2.5.1), which [teased upcoming Android Auto announcements](https://9to5google.com/2018/04/30/google-io-18-android-auto-new/), this new map features realistic 3D drawings of tents, sandboxes, and other concession stands. +5 月 2 日晚的 6.1 版本更新了地图标签页,带有更为详尽的海岸线圆形剧场的布局。这个新的地图在[梳理了即将到来的 Android Auto 公告](https://9to5google.com/2018/04/30/google-io-18-android-auto-new/)的 [iOS 客户端](https://go.redirectingat.com/?id=3947X1518523&xs=1&isjs=1&url=https%3A%2F%2Fitunes.apple.com%2Fus%2Fapp%2Fgoogle-i-o-2017%2Fid1109898820%3Fmt%3D8%26ign-mpt%3Duo%253D4&xguid=d44cc47b8aff3d8b9ff34bd030eaddac&xuuid=ed349d34e7eb230b1c8b9d9f2397146e&xsessid=d3d0fe4235c34199f73e1f3178be0274&xcreo=0&xed=0&sref=https%3A%2F%2F9to5google.com%2F2018%2F05%2F02%2Fgoogle-i-o-2018-for-android-updated-w-more-detailed-map-navigation-assistant-action%2F&xtz=-480&jv=13.3.0&bv=2.5.1)上首先亮相,展示了真实的 3D 的帐篷、沙箱和其它的展台的图像。 -The previous version used general outlines and only provided generic labels. In the updated app, the map features long descriptions of what attendees can expect from each location. +之前的版本只提供了一个总的大纲和通用的标签。在更新后的应用中,地图展示了各个角度的特征,还带有与会者想要的详尽的描述。 ![google-io-18-app-update-1](https://9to5google.files.wordpress.com/2018/05/google-io-18-app-update-1.png?w=246&h=437&quality=82&strip=all) ![google-io-18-app-update-2](https://9to5google.files.wordpress.com/2018/05/google-io-18-app-update-2.png?w=246&h=437&quality=82&strip=all) ![google-io-18-app-update-3](https://9to5google.files.wordpress.com/2018/05/google-io-18-app-update-3.png?w=246&h=437&quality=82&strip=all) ![google-io-18-app-update-old](https://9to5google.files.wordpress.com/2018/05/google-io-18-app-update-old.png?w=246&h=437&quality=82&strip=all) -When viewing a session listing, the bottom bar features a new place icon that takes users to the exact location on the map for easier navigation. It resides right next to the share button on the left. +当查看会议列表时,底部栏更新了一个新的定位图标,点击即可在地图上精确定位,这样可以更方便地导航. 这个图标紧邻位于左边的分享图标的右侧。 -Lastly, the Info tab adds a “Related apps” section that links to the I/O 18 Google Assistant Action [launched earlier this week](https://twitter.com/ActionsOnGoogle/status/991346508204314624). Tapping directly opens the Assistant listing page. Users learn more about sessions, browse topics, and more by asking “Talk to Google I/O 18” on most Assistant platforms. +最后,『信息』标签添加了一个『相关应用』板块,链接到[这周上线](https://twitter.com/ActionsOnGoogle/status/991346508204314624)的 I/O 18 Google Assistant 功能。点击会直接打开助手的列表页。在多数带有助手的平台上,用户可以说出「Talk to Google I/O 18」来了解更多会议信息,浏览话题,以及获取更多资讯。 ![google-io-18-action-4](https://9to5google.files.wordpress.com/2018/05/google-io-18-action-4.png?w=246&h=437&quality=82&strip=all) ![google-io-18-action-1](https://9to5google.files.wordpress.com/2018/05/google-io-18-action-1.png?w=246&h=437&quality=82&strip=all) ![google-io-18-action-2](https://9to5google.files.wordpress.com/2018/05/google-io-18-action-2.png?w=246&h=437&quality=82&strip=all) ![google-io-18-action-3](https://9to5google.files.wordpress.com/2018/05/google-io-18-action-3.png?w=246&h=437&quality=82&strip=all) --- -[Check out 9to5Google on YouTube for more news:](https://www.youtube.com/c/9to5google?sub_confirmation=1) +[在 YouTube 上查看 9to5Google 以获取更多信息:](https://www.youtube.com/c/9to5google?sub_confirmation=1) * YouTube 视频链接:https://youtu.be/EgXUcyPWcRA From 15b1cc7b9cc354316a7c4e7b05b7904389c9dce6 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sun, 6 May 2018 11:18:28 +0800 Subject: [PATCH 005/446] Update google-i-o-2018-for-android-updated-w-more-detailed-map-navigation-assistant-action.md --- ...d-updated-w-more-detailed-map-navigation-assistant-action.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO1/google-i-o-2018-for-android-updated-w-more-detailed-map-navigation-assistant-action.md b/TODO1/google-i-o-2018-for-android-updated-w-more-detailed-map-navigation-assistant-action.md index a5c61f21dfc..30a314067c2 100644 --- a/TODO1/google-i-o-2018-for-android-updated-w-more-detailed-map-navigation-assistant-action.md +++ b/TODO1/google-i-o-2018-for-android-updated-w-more-detailed-map-navigation-assistant-action.md @@ -21,7 +21,7 @@ 当查看会议列表时,底部栏更新了一个新的定位图标,点击即可在地图上精确定位,这样可以更方便地导航. 这个图标紧邻位于左边的分享图标的右侧。 -最后,『信息』标签添加了一个『相关应用』板块,链接到[这周上线](https://twitter.com/ActionsOnGoogle/status/991346508204314624)的 I/O 18 Google Assistant 功能。点击会直接打开助手的列表页。在多数带有助手的平台上,用户可以说出「Talk to Google I/O 18」来了解更多会议信息,浏览话题,以及获取更多资讯。 +最后,「信息」标签添加了一个「相关应用」板块,链接到[这周上线](https://twitter.com/ActionsOnGoogle/status/991346508204314624)的 I/O 18 Google Assistant 功能。点击会直接打开助手的列表页。在多数带有助手的平台上,用户可以说出「Talk to Google I/O 18」来了解更多会议信息,浏览话题,以及获取更多资讯。 ![google-io-18-action-4](https://9to5google.files.wordpress.com/2018/05/google-io-18-action-4.png?w=246&h=437&quality=82&strip=all) ![google-io-18-action-1](https://9to5google.files.wordpress.com/2018/05/google-io-18-action-1.png?w=246&h=437&quality=82&strip=all) ![google-io-18-action-2](https://9to5google.files.wordpress.com/2018/05/google-io-18-action-2.png?w=246&h=437&quality=82&strip=all) ![google-io-18-action-3](https://9to5google.files.wordpress.com/2018/05/google-io-18-action-3.png?w=246&h=437&quality=82&strip=all) From 1edbb8e43d1d7b219752f1e7a43498f82fd62cad Mon Sep 17 00:00:00 2001 From: Shery <382254994@qq.com> Date: Sun, 6 May 2018 21:43:09 +0800 Subject: [PATCH 006/446] =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E5=A4=A7=E5=9E=8B=20?= =?UTF-8?q?JavaScript=20=E5=BA=94=E7=94=A8=E7=A8=8B=E5=BA=8F(=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E4=BF=AE=E6=94=B9)=20(#3769)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 设计大型 JavaScript 应用程序(校对修改) --- ...ning-very-large-javascript-applications.md | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/TODO1/designing-very-large-javascript-applications.md b/TODO1/designing-very-large-javascript-applications.md index 33c13155057..7be691b1ce0 100644 --- a/TODO1/designing-very-large-javascript-applications.md +++ b/TODO1/designing-very-large-javascript-applications.md @@ -3,7 +3,7 @@ > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/designing-very-large-javascript-applications.md](https://github.com/xitu/gold-miner/blob/master/TODO1/designing-very-large-javascript-applications.md) > * 译者:[Shery](https://github.com/shery15) -> * 校对者:[Starrier](https://github.com/Starriers) +> * 校对者:[Starrier](https://github.com/Starriers) [Allen](https://github.com/allenlongbaobao) # 设计大型 JavaScript 应用程序 @@ -59,7 +59,7 @@ 幻灯片文本:“我可以预测 API 选择和抽象如何影响其他人解决问题的方式。” -让我们更具体一点。你说了这样一句话:“我可以预见我做出 API 选择时,或者我往项目中引入的抽象时,他们如何影响其他人如何解决问题。”我认为这是一个强大的概念,可以让我思考我所做的选择对应用程序的影响。 +让我们更具体一点。你说了这样一句话:“我可以预见我做出 API 选择时,或者我往项目中引入抽象时,它们如何影响其他人解决问题。”我认为这是一个强大的概念,可以让我思考我所做的选择对应用程序的影响。 ![](https://cdn-images-1.medium.com/max/800/1*LnDv6Ry0Hq2MaQEARaD8rg.png) @@ -77,13 +77,13 @@ 幻灯片文本:编程模型。 -考虑到这些话题,我想谈谈一个非常重要的术语,那就是编程模型,这个词我会用很多次。它代表“给定一套 API,库,框架或工具,人们如何在这种背景下编写软件”。我的演讲内容真的是关于 API 等等的细微变化,如何影响编程模型。 +考虑到这些话题,我想谈谈一个非常重要的术语,那就是编程模型,这个词我会用很多次。它代表“给定一套 API,库,框架或工具,人们如何在这种背景下编写软件”。我演讲的真正内容是关于 API 等细微变化对编程模型的影响。 ![](https://cdn-images-1.medium.com/max/800/1*zuLA-tH9b8k4i1yfKMScmA.png) 幻灯片文本:影响编程模型的示例:React,Preact,Redux,Date picker,npm。 -我想举几个影响编程模型的例子:假设你有一个 Angular 项目,并且你说:“我将把它移植到 React 中”,这显然会改变人们编写软件的方式,对吧?但是接下来你想:“啊哈,60 KB 就为了使用一点虚拟 DOM 操作,让我们切换到 Preact”,这是一个 与 React API 兼容的库,即使你做出了这个选择,它也不会改变人们编写软件的方式。或许随着项目的进展,你会觉得“单单 React 自有的状态管理还不够,应用会变得很复杂,我应该有一些东西来管理应用状态,我会引入 Redux”,这将改变人们编写软件的方式。然后又来了个新需求“我们需要一个日期选择器”,你到 npm 上进行搜索,有 500 个结果,你选了一个日期组件。你挑选哪一个真的很重要吗?它绝对不会改变你编写软件的方式。但是,npm 以及它的庞大生态集合,绝对会改变你编写软件的方式。当然,这几个例子只是一些可能影响人们编写软件的事。 +我想举几个影响编程模型的例子:假设你有一个 Angular 项目,并且你说:“我将把它移植到 React 中”,这显然会改变人们编写软件的方式,对吧?但是接下来你想:“啊哈,60 KB 就为了使用一点虚拟 DOM 操作,让我们切换到 Preact”,这是一个 与 React API 兼容的库,即使你做出了这个选择,它也不会改变人们编写软件的方式。或许随着项目的进展,你会觉得“单单 React 自有的状态管理还不够,应用会变得很复杂,我应该有一些东西来管理应用状态,我会引入 Redux”,这将改变人们编写软件的方式。然后又来了个新需求“我们需要一个日期选择器”,你到 npm 上进行搜索,有 500 个结果,你选了一个日期组件。你挑选哪一个真的很重要吗?它绝对不会改变你编写软件的方式。但是,npm 以及它的庞大生态集合,绝对会改变你编写软件的方式。当然,这些只是可能影响人们如何编写软件的几个例子。 ![](https://cdn-images-1.medium.com/max/800/1*KfcGnWC3WcwBqGYLPiybgw.png) @@ -96,7 +96,7 @@ 幻灯片文本:同步 -> 异步。 -你有过去是同步现在成为异步的东西。你的应用程序在没有代码分割时,简单美好。有一件大事。它启动,然后它很稳定,你了解它的前世今生,你不必等待资源加载。有了代码分割后,有时候你可能会说“哦,我需要那个打包文件”,所以你现在需要利用网络来获取所需的文件,这也使得你必须考虑网络可能出现异常情况,所以应用程序也变得更加复杂。 +你有过去是同步现在成为异步的东西。你的应用程序在没有代码分割时,简单美好。整个项目只有一件大事。它启动,然后它很稳定,你了解它的前世今生,你不必等待资源加载。有了代码分割后,有时候你可能会说“哦,我需要那个打包文件”,所以你现在需要利用网络来获取所需的文件,这也使得你必须考虑网络可能出现异常情况,所以应用程序也变得更加复杂。 ![](https://cdn-images-1.medium.com/max/800/1*DqT7As1rm_M9cxyW1RIW6w.png) @@ -108,9 +108,9 @@ 幻灯片文本:基于路由的代码分割。 -有一种非常成熟的方法可以解决这个问题,它可以将我们从进行代码分割的混乱中解脱出来,它被称作基于路由的代码分割。如果你还没有使用代码分割,那它可能是你初次进行代码分割的方式。路由将应用程序以 URL 粒度进行分割。例如,你的产品页面可能在 `/product/` 上,并且你的分类页面可能在其他地方。你只需将每个路由用的文件打包到一起,然后你的应用程序将根据路由自动进行代码分割。无论何时用户访问路由,路由都会加载相关的打包文件,有了路由之后,你可以忘记代码分割的存在。再从编程模型上来看,这几乎与将所有东西都有打包到一起一样。这是一种非常好的代码分割方法,绝对是个好的开始。 +有一种非常成熟的方法可以解决这个问题,它可以将我们从进行代码分割的混乱中解脱出来,它被称作基于路由的代码分割。如果你还没有使用代码分割,那它可能是你初次进行代码分割的方式。路由将应用程序以 URL 粒度进行分割。例如,你的产品页面可能在 `/product/` 上,并且你的分类页面可能在其他地方。你只需将每个路由用的文件打包到一起,然后你的应用程序将根据路由自动进行代码分割。无论何时用户访问路由,路由都会加载相关的打包文件,有了路由之后,你可以忘记代码分割的存在。再从编程模型上来看,这几乎与将所有东西都打包到一起一样。这是一种非常好的代码分割方法,绝对是个好的开始。 -但是这个演讲的主题是设计**非常**大型的 JavaScript 应用程序,并且它们会很快变得巨大无比,以至于基于路由的代码分割不再适用,因为路由本身也会变得非常大。实际上我有一个关于应用程序的好例子。 +但是这个演讲的主题是设计**非常**大型的 JavaScript 应用程序,并且这类应用程序很快会变得巨大无比,路由本身也会随之变大,以至于基于路由的代码分割不再适用。实际上我有一个关于这类应用程序的好例子。 ![](https://cdn-images-1.medium.com/max/800/1*ox94bGuhxWXE-OubL7St6w.png) @@ -188,13 +188,13 @@ Loadable 组件示例。 幻灯片文本:搜索结果页面上的货币转换器。 -这个模型看起来不错,但它确实有一些折衷。如果你知道通常服务端渲染在 React 或 Vue.js 等框架中如何工作,这个过程被称为 hydration。hydration 是这样的,你服务端渲染的一些东西,然后在客户端再次渲染它,这意味着你必须加载代码来渲染一些已经在页面上的东西,这在加载代码和执行代码方面都是巨大的浪费。这么做既浪费带宽,又浪费 CPU —— 但它确实很好,因为你在客户端忽略了服务端渲染的东西。我们在 Google 使用的方法不是那样的。所以,如果你设计这个非常大型的应用程序,你就会想:我是采用那种更复杂的超快速方法,还是采用效率较低的 hydration 方式,但这样能有个良好的编程模型?你将不得不做出这个决定。 +这个模型看起来不错,但它确实有一些折中。如果你知道通常服务端渲染在 React 或 Vue.js 等框架中如何工作,这个过程被称为 hydration。hydration 是这样的,你服务端渲染的一些东西,然后在客户端再次渲染它,这意味着你必须加载代码来渲染一些已经在页面上的东西,这在加载代码和执行代码方面都是巨大的浪费。这么做既浪费带宽,又浪费 CPU —— 但它确实很好,因为你在客户端忽略了服务端渲染的东西。我们在 Google 使用的方法不是那样的。所以,如果你设计这个非常大型的应用程序,你就会想:我是采用那种更复杂的超快速方法,还是采用效率较低的 hydration 方式,但这样能有个良好的编程模型?你将不得不做出这个决定。 ![](https://cdn-images-1.medium.com/max/800/1*uteTbmuKZF1wGvoysgsBYw.png) 幻灯片文本:2017 新年快乐。 -我的下一个话题是我最喜欢的计算机科学问题 —— 它不是命名事物,尽管我很可能给它起了个糟糕的名字。这是“**2017 年假期特别问题**”。过去有人写过一些代码,现在不再需要它们了,但它仍然在你的代码库中?...这种情况时常发生,我认为 CSS 的问题尤为突出。你有一个大型 CSS 文件。里面有很多样式选择器。谁真的知道哪些样式选择器是否仍然对应着你应用中的内容?所以,你最终只能把那些代码留在那里。我认为 CSS 社区处于变革的最前沿,因为他们意识到这个问题,并且他们创建了诸如 CSS-in-JS 之类的解决方案。因为你的组件可以放到一个单独的文件里,2017 年假期特别问题组件,你可以说“它不再是 2017 问题”,你可以删除整个组件,并且所有相关文件一并消失。这使得删除代码非常容易。我认为这是一个非常好的想法,它不仅仅适用于 CSS。 +我的下一个话题是我最喜欢的计算机科学问题 —— 它不是命名问题,尽管我很可能给它起了个糟糕的名字。这是“**2017 年假期特别问题**”。过去有人写过一些代码,现在不再需要它们了,但它仍然在你的代码库中?...这种情况时常发生,我认为 CSS 的问题尤为突出。你有一个大型 CSS 文件。里面有很多样式选择器。谁真的知道哪些样式选择器是否仍然对应着你应用中的内容?所以,你最终只能把那些代码留在那里。我认为 CSS 社区处于变革的最前沿,因为他们意识到这个问题,并且他们创建了诸如 CSS-in-JS 之类的解决方案。因为你的组件可以放到一个单独的文件里,2017 年假期特别问题组件,你可以说“它不再是 2017 问题”,你可以删除整个组件,并且所有相关文件一并消失。这使得删除代码非常容易。我认为这是一个非常好的想法,它不仅仅适用于 CSS。 ![](https://cdn-images-1.medium.com/max/800/1*rkAN_sLohIO63JCOTZ1JgA.png) @@ -228,9 +228,9 @@ Loadable 组件示例。 ![](https://cdn-images-1.medium.com/max/800/1*DfOMmyxC4guVZkyQ4IlF7g.png) -幻灯片文本:由路由和3个根组件构成的依赖关系树示例。 +幻灯片文本:由路由和 3 个根组件构成的依赖关系树示例。 -显然,我们都有超复杂的应用程序,但我会用一个非常简单的例子。它只有4个组成部分。它有一个路由,知道如何从应用程序的一个路由到下一个路由,它有几个根组件,A,B和C。 +显然,我们都有超复杂的应用程序,但我会用一个非常简单的例子。它只有 4 个组成部分。它有一个路由,知道如何从应用程序的一个路由到下一个路由,它有几个根组件:A、B 和 C。 ![](https://cdn-images-1.medium.com/max/800/1*CivPR-20NP0dXlIkWfBk6w.png) @@ -284,7 +284,7 @@ Loadable 组件示例。 幻灯片文本:基本打包文件。 -不幸的是,这不仅仅是我们所需要知道的。第二个我最喜欢的计算机科学问题,我称之为“**基础垃圾打包文件**”。在应用程序的打包逻辑中的基本打包文件总是会被加载,而与用户与应用程序的交互方式无关。所以,这一点尤其重要,因为如果它很大,那么所有进一步深入的东西都会很大。如果它很小,那么依赖文件也有可能变小。一个小故事:在某个时候,我加入了 Google Plus JavaScript 基础架构团队,并且我发现他们的基础打包文件包含 800 KB 的 JavaScript。所以,我对你的警告是:如果你想比 Google Plus 更成功,那么在你的基础打包文件中不要有 800 KB 的 JS。不幸的是,很容易就达到这样一种糟糕的状态。 +不幸的是,这不仅仅是我们所需要知道的。第二个我最喜欢的计算机科学问题,我称之为“**基础垃圾打包文件**”。在应用程序的打包逻辑中的基本打包文件总是会被加载,而与用户与应用程序的交互方式无关。所以,这一点尤其重要,因为如果它很大,那么所有进一步深入的东西都会很大。如果它很小,那么依赖文件也有可能变小。一个小故事:在某个时候,我加入了 Google Plus JavaScript 基础架构团队,并且我发现他们的基础打包文件包含 800 KB 的 JavaScript。所以,我对你的警告是:如果你想比 Google Plus 更成功,就不要让你的 JS 基础打包文件超过 800 KB,但不幸的是你的文件体积很难维持在理想状态。 ![](https://cdn-images-1.medium.com/max/800/1*wW_u72nFdPiKjEINH4ubDg.png) From 72a695913ce7e4f8014c3bfe23d4d0e74803d5be Mon Sep 17 00:00:00 2001 From: John_Jiang Date: Sun, 6 May 2018 21:46:22 +0800 Subject: [PATCH 007/446] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20=20python=20?= =?UTF-8?q?=E5=92=8C=20keras=20=20=E5=AE=9E=E7=8E=B0=E5=8D=B7=E7=A7=AF?= =?UTF-8?q?=E7=A5=9E=E7=BB=8F=E7=BD=91=E7=BB=9C=20(#3744)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update making-sense-of-ethereums-layer-2-scaling-solutions-state-channels-plasma-and-truebit.md * 2018-03-10 * 第一次翻译 * 校对一次 * update * 第一次校对,添加校对者信息 * 校对完成 --- ...l-neural-network-using-python-and-keras.md | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/TODO/implementation-of-convolutional-neural-network-using-python-and-keras.md b/TODO/implementation-of-convolutional-neural-network-using-python-and-keras.md index 8f06302a191..1473b736946 100644 --- a/TODO/implementation-of-convolutional-neural-network-using-python-and-keras.md +++ b/TODO/implementation-of-convolutional-neural-network-using-python-and-keras.md @@ -2,82 +2,82 @@ > * 原文作者:[rubikscode](https://rubikscode.net) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO/implementation-of-convolutional-neural-network-using-python-and-keras.md](https://github.com/xitu/gold-miner/blob/master/TODO/implementation-of-convolutional-neural-network-using-python-and-keras.md) -> * 译者: -> * 校对者: +> * 译者:[JohnJiangLA](https://github.com/JohnJiangLA) +> * 校对者:[Gladysgong](https://github.com/Gladysgong) [Starrier](https://github.com/Starriers) -# Implementation of Convolutional Neural Network using Python and Keras +# 使用 Python 和 Keras 实现卷积神经网络 -Have you ever wondered, how does Snapchat detect faces? How do self-driving cars know where a road is? You are right, they are using special kind of neural networks used for computer vision – Convolutional Neural Networks. In the **[previous article](https://rubikscode.net/2018/02/26/introduction-to-convolutional-neural-networks/),** we had a chance to examine how they work. We covered layers of these networks and their functionalities. Basically, additional layers of Convolutional Neural Networks preprocess image in the format that standard neural network can work with. The first step in doing so is detecting certain features or attributes on the input image. This is done by convolutional layer. +你有没有想过?Snapchat 是如何检测人脸的?自动驾驶汽车是怎么知道路在哪里?你猜的没错,他们是使用了卷积神经网络这种专门用于处理计算机视觉的神经网络。在[**前一篇文章**](https://rubikscode.net/2018/02/26/introduction-to-convolutional-neural-networks/)中,我们研究了它们是怎么工作的。我们讨论了这些神经网络的层及其功能。基本上,卷积神经网络的附加层会将图像处理成神经网络能够支持的标准格式。这样做的第一步是检测某些特征或属性,这些工作是由卷积层完成的。 -This layer use filters to detect low-level features, like edges and curves, as well as higher levels features, like a face or a hand. Than Convolutional Neural Network use additional layers to remove linearity from the image, something that could cause overfitting. When linearity is removed, additional layers for compressing the image and flattening the data are used. Finally, this information is passed into a neural network, called Fully-Connected Layer in the world of Convolutional Neural Networks. Again, the goal of this article is to show you how to implement all these concepts, so more details about these layers, how they work and what is the purpose of each of them can be found in the **[previous article](https://rubikscode.net/2018/02/26/introduction-to-convolutional-neural-networks/)**. +这一层使用过滤器来检测低层次的特征(比如边缘和曲线)以及更高层次的特征(比如脸和手)。然后卷积神经网络使用附加层消除图像中的线性干扰,这些干扰会导致过分拟合。当线性干扰移除后,附加层会将图像下采样并将数据进行降维。最后,这些信息会被传递到一个神经网络中,在卷积神经网络中它叫全连接层。同样,本文的目标是如何实现这些层,因此关于这些附加层的更多细节以及如何工作和具体用途都可以在[**前一篇文章**](https://rubikscode.net/2018/02/26/introduction-to-convolutional-neural-networks/)中找到。 ![](https://i.imgur.com/Tnkq3Tf.png) -Before we wander off into the problem we are solving and the code itself make sure to setup your environment. As in all previous articles from this **[series](https://rubikscode.net/2018/02/19/artificial-neural-networks-series/)**, I will be using Python 3.6. Also, I am using Anaconda and Spyder, but you can use any IDE that you preffer. However, the important thing to do is to install Tensorflow and Keras. Instructions for installing and using TensorFlow can be found [**here**](https://rubikscode.net/2018/02/05/introduction-to-tensorflow-with-python-example/), while instructions for installing and using Keras are [**here**](https://rubikscode.net/2018/02/12/implementing-simple-neural-network-using-keras-with-python-example/). +在我们开始解决问题和开始码代码之前,请正确配置好你的环境。与[**本系列**](https://rubikscode.net/2018/02/19/artificial-neural-networks-series/)之前的所有文章一样,我会使用 Python 3.6。另外,我使用的是 Anaconda 和 Spyder,但是你也可以使用其他的 IDE。然后,最重要的是安装 Tensorflow 和 Keras。安装和使用 Tensorflow 的说明请查看[**此处**](https://rubikscode.net/2018/02/05/introduction-to-tensorflow-with-python-example/),而安装和使用 Keras 的说明请查看[**此处**](https://rubikscode.net/2018/02/12/implementing-simple-neural-network-using-keras-with-python-example/)。 -## MNIST Dataset +## MNIST 数据集 -So, in this article, we will teach our network how to recognize digits in the image. For this, we will use another famous dataset – MNIST Dataset. Extending its predecessor **[NIST](https://www.nist.gov/sites/default/files/documents/srd/nistsd19.pdf)**, this dataset has a training set of 60,000 samples and testing set of 10,000 images of handwritten digits. All digits have been size-normalized and centered. Size of the images is also fixed, so preprocessing image data is minimized. This is why this dataset is so popular. It is considered to be a “Hello World” example in the world of Convolutional Neural Networks. +因此,在本文中,我们将训练我们的网络来识别图像中的数字。为此,我们将使用另一个著名的数据集 —— MNIST 数据集。在前身 [**NIST**](https://www.nist.gov/sites/default/files/documents/srd/nistsd19.pdf) 的基础上,这个数据集由一个包含 60,000 个样本的训练集和一个包含 10,000 个手写数字图像的测试集组成。所有数字都已大小归一化并居中了。图像的大小也是固定的,因此预处理图像数据已被最简化了。这也是这个数据集为何如此流行的原因。它被认为是卷积神经网络世界中的 “Hello World” 样例。 ![](https://i.imgur.com/dMRUT6k.png) -###### MNIST Dataset samples +###### MNIST 数据集样例 -Also, using Convolutional Neural Networks we can get almost human results. Currently, the record is held by the Parallel Computing Center (Khmelnitskiy, Ukraine). They used an ensemble of only 5 convolutional neural networks and got the error rate of 0.21 percent. Pretty cool, isn’t it? +此外,使用卷积神经网络,我们可以得到与人类判断相差无几的结果。目前,这一纪录由 Parallel Computing Center(赫梅尔尼茨基,乌克兰)保持。他们只使用了由 5 个卷积神经网络组成的集合,并将错误率控制在 0.21%。很酷吧? -## Importing Libraries and Data +## 导入库和数据 -Like in previous articles in this **[series](https://rubikscode.net/2018/02/19/artificial-neural-networks-series/)**, we will first import all necessary libraries. Some of these will be familiar, but some of them we will explain a bit further. +与[**本系列**](https://rubikscode.net/2018/02/19/artificial-neural-networks-series/)前面的文章一样,我们首先导入所有必要的库。其中一些是我们熟悉的,但是其中一些需要进一步讲解。 -As you can see we will be using _numpy_, the library that we already used in previous examples for operations on multi-dimensional arrays and matrices. Also, you can see that we are using some features from _Keras_ Libraries that we already used in **[this article](https://rubikscode.net/2018/02/12/implementing-simple-neural-network-using-keras-with-python-example/)**, but also a couple of new ones. _Sequential_ and _Dense_ are used for creating the model and standard layers, ie. fully-connected layer. +正如你所见,我们将使用 numpy,这是我们在前面的示例中用于操作多维数组和矩阵的库。另外,也可以看到,我们会使用一些[**本系列**](https://rubikscode.net/2018/02/12/implementing-simple-neural-network-using-keras-with-python-example/)之前 Keras 库中用过的特性,也会使用一些新特性。比如创建模型和标准层(比如全连接层)会使用到 **Sequential** 和 **Dense**。 -Also, we can see some new classes we use from _Keras_. _Conv2D_ is class that we will use to create a convolutional layer. _MaxPooling2D_ is class used for pooling layer, and _Flatten_ class is used for flattening level. We use _to_categorical_ from _Keras utils_ as well. This class is used to convert a vector (integers) to a binary class matrix, ie. it is used for [**one-hot encoding**](https://en.wikipedia.org/wiki/One-hot). Finally, notice that we will use _matplotlib_ for displaying the results. +此外,我们还会使用一些 **Keras** 中的新类。**Conv2D** 是用于创建卷积层的类。**MaxPooling2D** 则是用于创建池化层的类,而 **Flatten** 是用于降维的类。我们也使用 **Keras util** 中的 **to_categorical**。该类用于将向量(整形量)转化为二值类别矩阵,即它用于 [**one-hot 编码**](https://en.wikipedia.org/wiki/One-hot)。最后,注意我们将使用 **matplotlib** 来显示结果。 -After we imported all necessary libraries and classes, we need to take care of the data. Lucky for us, Keras provided MNIST dataset so we don’t need to download it. As mentioned, all these images are already partially preprocessed. This means that they are having same size and digits displayed on them are properly positioned. So, let’s import this dataset and prepare data for our model: +导入必要的库和类之后,我们需要处理数据。幸运的是,Keras 提供了 MNIST 数据集, 所以我们不需要下载它。如前所述,所有这些图像都已经进行了部分预处理。这意味着他们有相同的大小和数字位于合适的位置。因此,让我们导入这个数据集并为我们的模型准备数据: -As you can see we imported MNIST dataset from the Keras _datasets_. Then we loaded data in train and test matrices. After that, we got the dimensions of images using _shape_ property and reshaped input data so it represents one channel input images. Basically, we are using just one channel of this image, not the regular three (RGB). This is done to simplify this implementation. Then we normalized the data in the input matrixes. In the end, we encoded the output matrixes using _to_categorical_. +如你所见,我们从 Keras 数据集中导入了 MNIST 数据集。然后,我们将数据加载到训练和测试矩阵中。在此基础上,利用形状属性得到图像的维数,并对输入数据进行重构,从而得到输入图像的一个通道。基本上,我们只使用这个图像的一个通道,而不是常规的三个通道(RGB)。这样做是为了简化实现的难度。然后对输入矩阵中的数据进行归一化处理。最后,我们使用 **to_categorical** 对输出矩阵进行编码。 -## Model Creation +## 模型创建 -Now, when data is prepared, we can proceed to the fun stuff – the creation of the model: +现在,数据已经准备好了,我们可以开始最有趣的环节了 —— 创建模型: -We used _Sequential_ for this, of course, and started off by adding Convolutional Layers using _Conv2D_ class. As you can see there are few parameters this class is using, so let’s explore them. The first parameter is defining a number of filters that will be used, ie. number of features that will be detected. It is a common procedure to start from 32 and then go to bigger number of features from that moment on. That is exactly what we are doing, in first convolutional layer we are detecting 32 features, in second 64 and in third and final 128 features. Size of the filters that will be used is defined using next parameter – _kernel_size,_ and we have chosen 3 by 3 filters. +理所当然的,我们为此需要使用 **Sequential**,并首先使用 Conv2D 类添加卷积层。正如你所见的,这个类使用的参数很少,所以让我们一起来研究下。第一个参数是定要使用的过滤器个数,即要检测的特征个数。通常来说我们从 32 开始随后逐步增大这个数字。这正是我们在做的,在第一个卷积层中我们检测 32 个特征,第二层中 64 个,最后的第三层中 128 个。使用的过滤器大小则由下一个参数 —— **kernel_size** 来定义,我们已经选择了 3*3 的过滤器。 -For the activation function, we are using rectifier function. This way we are adding non-linearity level automatically with every Convolutional layer. Another way to achieve this, and a bit more advanced, is by using _LeakyReLU_ form _keras.layers.advanced_activations_. This is not like standard rectifier function, but instead of squashing all values that are below a certain value to 0 it has a slight negative slope. If you decide to use this, beware that you have to use linear activation in _Conv2D._ Here is an example how that would look like: +在激活函数中,我们使用整流器函数。这样,在每个卷积层中非线性程度都会自然增加。实现这一点的另一种方法是使用 **keras.layers.advanced_activations** 中的 **LeakyReLU**。它不像标准的整理器函数,不是将所有低于某一固定值的值压缩为零,而是有一个轻微的负斜率。如果你决定使用它,请注意必须使用 **Conv2D** 中的线性激活。下面就是这种方式的样例: -We digressed for a bit. Let’s get back to _Conv2D_ and its parameters. Another very important parameter is _input_shape._ Using this parameter we are defining dimensions of our input image. As mentioned, we are only using one channel, that is why the final dimension our _input_shape_ is 1. Other dimensions we picked up from an input image. +我们有点跑题了。讲回到 Conv2D 及其参数。另一个非常重要的参数是 **input_shape**。使用这个参数,定义输入图像的维数。如前所述,我们只使用一个通道,这是为什么我们的 **input_shape** 最终维度是 1。这是我们从输入图像中提取的维度。 -Also, we added other layers to our model too. _Dropout_ layer is helping us with overfitting, and after that, we added pooling layer by using _MaxPooling2D_ class. This layer is apparently using the max-pool algorithm, and size of the pooling filter is 2 by 2. Pooling layer is followed by Flattening layer, which is followed by Fully-connected layer. For the final Fully-Connected layer we added the neural network with two layers, for which we used _Dense_ class. In the end, we compiled our model, and we used Adam optimizer. +此外,我们还在模型中添加了其它层。Dropout 层能帮助我们防止过分拟合,此后,我们使用 **MasPooling2D** 类添加池化层。显然,这一层使用的是 max-pool 算法,池化过滤器的大小则是 2*2。池化层之后是降维层,最后是全连接层。对于最后的全连接层,我们添加了两层的神经网络,对于这两层,我们使用了 **Dense** 类。最后,我们编译模型,并使用了 Adam 优化器。 -If you are struggling with some of these concepts, you can check **[previous blog post](https://rubikscode.net/2018/02/26/introduction-to-convolutional-neural-networks/)** where mechanisms of Convolutional Layers are explained. Also, if you have a problem with following some _Keras_ concepts, this [**blog post**](https://rubikscode.net/2018/02/12/implementing-simple-neural-network-using-keras-with-python-example/) can help you. +如果你不明白其中的一些概念,你可以查看[**之前的文章**](https://rubikscode.net/2018/02/26/introduction-to-convolutional-neural-networks/),其中解释了卷积层的原理机制。另外,如果你对于一些 Keras 的内容有疑惑,那么[**这篇文章**](https://rubikscode.net/2018/02/12/implementing-simple-neural-network-using-keras-with-python-example/)会帮助到你。 -## Training +## 训练 -Very well, our data is pre-processed and our model created. Let’s merge them together, and train our model. For this we are using, already familiar, function _fit_. We pass the input matrices and define _batch_size_ and number of _epochs._ Another thing we are doing is defining _validation_split._ This parameter is used to define which fraction of testing data is going to be used as validation data. +很好,我们的数据预处理了,我们的模型也建好了。下面我们将他们合并到一起,并训练我们的模型。为了使我们正在使用的能够运转正常。我们传入输入矩阵并定义 **batch_size** 和 **epoch** 数。我们要做的另外一件事是定义 **validation_split**。这个参数用于定义将测试数据的哪个部分用作验证数据。 -Basically, the model will set aside part of training data, but it will use it to evaluate the loss and other model metrics at the end of each epoch. This is not the same as testing data because we are using it after every epoch. +基本上,该模型将保留部分训练数据,但它使用这部分数据在每个循环结束时计算损失和其他模型矩阵。这与测试数据不同,因为我们在每个循环结束后都会使用它。 -After that our model is trained and ready. We are using _evaluate_ method and pass testing set to it. Here we will get the accuracy of our Convolutional Neural Network. +在我们的模型已经训练完成并准备好之后,我们使用 **evaluate** 方法并将测试集传入。这里我们能够得出这个卷积神经网络的准确率。 -## Predictions +## 预测 -One more thing we could do is to gather predictions of our network on the test dataset. This way we can compare predicted results with actual ones. For this, we will use _predict_ method. Using this method we can also make predictions on a single input. +我们可以做的另一件事是在测试数据集中收集对对神经网络的预测。这样,我们就可以将预测结果和实际结果进行比较。为此,我们将使用 **predict** 方法。使用这个方法我们还可以对单个输入进行预测。 -## Results +## 结果 -Let’s use these predictions we just gathered for the final touch of our implementation. We are going to display predicted digit, with the actual one. And we will display the image for which we made the prediction. Basically, we will make nice visual representation for our implementation, after all, we are processing images here. +让我们使用这些我们刚刚收集到的预测来完成我们实现的最后一步。我们将显示预测的数字与实际的数字。我们还会显示我们预测的图像。基本上,我们将为我们的实现做很好的可视化展示。毕竟,我们在处理图像。 -Here we used _pyplot_ to display ten images with actual result and our predictions. And this is how it looks like when we run our implementation: +在这里,我们使用了 **pyplot** 来显示十幅图像,并给出了实际结果和我们的预测。当我们运行我们的实现时,如下图所示: ![](https://i.imgur.com/q70wn55.png) -We have run twenty epochs and got the accuracy – 99.39%. Not bad at all. There is always room for improvement, of course. +我们运行了 20 轮并得到了 99.39% 的准确率。并不差,当然这还有提升空间。 -## Conclusion +## 结论 -Convolutional Neural Networks are one very interesting sub-field and one of the most influential innovations in the field of computer vision. Here you could see how to implement one simple version of these networks and how to use it for detecting digits on MNIST dataset. +卷积神经网络是计算机视觉领域中一个非常有趣的分支,也是最有影响力的创新之一。本文中我们实现了这些神经网络中的一个简易版本并用它来检测 MNIST 数据集上的数字。 -Thanks for reading! +感谢阅读! --- From b510f80b751ea4b4d43a3c8cf661f6c85163c361 Mon Sep 17 00:00:00 2001 From: Starrier <1342878298@qq.com> Date: Sun, 6 May 2018 21:52:06 +0800 Subject: [PATCH 008/446] =?UTF-8?q?=20=E7=94=A8=E6=88=B7=E4=BD=93=E9=AA=8C?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E7=A8=80=E7=BC=BA=E6=80=A7=EF=BC=9A=E6=88=90?= =?UTF-8?q?=E4=B8=BA=E5=B8=B8=E6=80=81=E7=9A=84=E5=BF=83=E7=90=86=E5=81=8F?= =?UTF-8?q?=E5=B7=AE=20(#3729)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SVG 深入教程 SVG 深入教程 * Update an-in-depth-svg-tutorial * 添加译者 校对人员信息 * Delete an-in-depth-svg-tutorial * UX 中的 SCARCITY:成为常态的心理偏见 UX 中的 SCARCITY:成为常态的心理偏见 * Update scarcity-in-ux-the-psychological-bias-that-became-the-norm.md * 校对完成 第一次 * Update scarcity-in-ux-the-psychological-bias-that-became-the-norm.md * 第二次修改 --- ...psychological-bias-that-became-the-norm.md | 154 +++++++++--------- 1 file changed, 77 insertions(+), 77 deletions(-) diff --git a/TODO1/scarcity-in-ux-the-psychological-bias-that-became-the-norm.md b/TODO1/scarcity-in-ux-the-psychological-bias-that-became-the-norm.md index 786a8a1d142..c0814331789 100644 --- a/TODO1/scarcity-in-ux-the-psychological-bias-that-became-the-norm.md +++ b/TODO1/scarcity-in-ux-the-psychological-bias-that-became-the-norm.md @@ -2,195 +2,195 @@ > * 原文作者:[David Teodorescu](https://uxdesign.cc/@davidteodorescu?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/scarcity-in-ux-the-psychological-bias-that-became-the-norm.md](https://github.com/xitu/gold-miner/blob/master/TODO1/scarcity-in-ux-the-psychological-bias-that-became-the-norm.md) -> * 译者: -> * 校对者: +> * 译者:[Starrier](https://github.com/Starriers) +> * 校对者:[wavezhang](https://github.com/wavezhang)、[rydensun](https://github.com/rydensun) -# Scarcity in UX: The psychological bias that became the norm +# 用户体验中的稀缺性:成为常态的心理偏差 -## Short analysis on the current state of affairs and a few tips to keep in mind. +## 简要分析了现状,并给出了几个注意事项。 ![](https://cdn-images-1.medium.com/max/1000/1*-XgzQw76IgZE2gwbOrNSCA.png) -You know how it works. +你知道它的工作原理。 -Casually watching a review on Unbox Therapy about this mug that apparently is unspillable. I’m having a laugh but by the end of the video I’m also intrigued what people ask for it. +随便看了一篇 Unbox Therapy 关于这个杯子的的测评,显然这个杯子的特点是不会倒。我在笑,但在视频的结尾,我很好奇人们对它的需求是什么。 -There it is on Amazon. On sale at $14.99 from $24.99\. For a limited time only. Only 3 left in stock for the stainless steel version. I love stainless steel. It’s a bargain and it will soon be gone. I’ll be left to drink coffee from my spillable mug. It would be a shame to pass this. F**k it. 💸 +在 Amazon 上可以找到,原价 $24.99 限时特价 $14.99,不锈钢版本的杯子只剩下三件。。我喜欢不锈钢,这很便宜,它们很快就会被卖光。也许我只能继续在自已之前的杯子中喝咖啡了,如果真是这样就太可惜了。💸 ![](https://cdn-images-1.medium.com/max/800/1*r1wMSvUUzF8eccxymhsRbg.jpeg) -Yes, it’s unspillable. Most of the times. +是的,大多数时间不会倒下。 * * * -### It makes things desirable +### 这就让事情令人满意了 -Scarcity is the psychological bias that makes us place a higher value on things that are scarce than those in abundance. Basically, we tend to like things that are harder to obtain. +稀缺性是一种心理上的偏见,它使我们对稀缺的东西赋予了更高的价值。基本上,我们倾向于喜欢较难获得的东西。 * * * -### It has become the norm +### 它已经成为了常态 -As most things, scarcity started offline. Expensive restaurants serve small portions on large plates to suggest that ingredients are rare and prestigious colleges have limited places to maintain the sense of exclusivity. +就像大多数事情一样,稀缺性是从线下开始的。昂贵的餐厅在大盘子上只供应小部分,这表明食材是稀缺的,名牌大学保持排他性的有限名额。 ![](https://cdn-images-1.medium.com/max/800/1*Vt56n6UejBd6GOLn9NyW4A.jpeg) -This looks expensive. +这看上去非常昂贵。 -But as tech businesses became more mature and digital products more refined, scarcity was quickly adopted online and it is now one of the most popular methods to increase desirability. +但随着科技企业越来越成熟,数字产品也越来越精致,稀缺性很快在网上被采用,它现在是提高需求最受欢迎的方法之一。 -We have come to a point in which people are so used to seeing and expecting some form of scarcity when browsing online, that implementing one inside your product is not a competitive advantage anymore but a starting point for any goal that aims to satisfy users’ needs. +我们已经到了这样一个地步:人们已经习惯于在网上浏览时看到并期待某种形式的稀缺,在你的产品中实现一种稀缺性已经不再是一种竞争优势,而是任何旨在满足用户需求目标的起点。 * * * -### It combines multiple biases +### 它结合了多种认知偏差 -Scarcity became popular because it’s extremely powerful and fairly easy to implement. And the reason it’s so effective is because it combines multiple biases into one: +稀缺性之所以流行,是因为它非常强大,而且相当容易实现。它之所以如此有效,是因为它将多种认知偏差结合在一起: -#### 1. Loss aversion +#### 1. 损失规避 -If we don’t act upon a scarce product, it basically means we’ll lose both the product itself in the short run but also our freedom to choose it in the long run. Double the loss = double the pain. +如果我们不对稀缺性产品采取行动,这基本上意味着我们将在短期内失去产品本身,但从长远来看,我们将失去选择它的自由。双倍的损失 = 加倍的痛苦。 ![](https://cdn-images-1.medium.com/max/800/1*kMA-FslhlGIDlrYzOqVXsA.png) -#### 2. Social proof +#### 2. 社会证明 -Usually, products become scarce when the demand is high. Once that happens, it implies that other people bought it in the past so it must be valuable and we should seize the opportunity. +通常情况下,当需求很大时,产品就会变得稀缺。一旦发生这种情况,它意味着其他人在过去购买了它,所以它必须是有价值的,我们应该抓住这个机会。 ![](https://cdn-images-1.medium.com/max/800/1*vYQJeMv3Ig2rVsngSQiIvQ.png) -#### 3. Anticipated regret +#### 3. 预期遗憾 -When facing a decision, we anticipate not only the events but also the associated regret we might experience. Deciding to act now is our attempt to try and eliminate that possibility. +当我们面对一个决定时,我们不仅预料到事件的发生,而且预料到我们可能会经历与之相关的遗憾。决定现在采取行动是我们试图消除这种可能性的努力。 ![](https://cdn-images-1.medium.com/max/800/1*3lDEBvknnYRvpuU-AmeM7w.png) * * * -### It comes in different forms +### 它有不同形式 -Even though scarcity can be applied to unquantifiable features like quality or experiences, its effect is much more powerful when assessing measurable resources like objects or places. It’s the reason the likes of Amazon and Booking.com embrace it and use it extensively. +尽管稀缺性可应用于质量或体验等无法量化的特性,但在评估可测量的资源(如对象或地方)时,它的效果要强大得多。这也是 Amazon 和 Booking.com 等公司接受并广泛使用它的原因。 -Based on these measurable resources, there are 3 main forms of scarcity: +基于这些可衡量的资源,主要有三种稀缺性形式: -### 1. Time-limited scarcity +### 1. 限时稀缺性 -When time has a limit, it creates a deadline that makes people act before the time is up. When the deadline is unknown, people are not certain that they can get the object anymore unless they act now, which increases the pressure but shows lack of empathy from a UX standpoint. +当时间有限制时,它会创造一个期限,让人们在时间结束之前行动起来。当截止日期未知时,人们不确定他们是否还能得到目标,除非他们现在就采取行动,这增加了压力,但从用户体验的角度来看,显示出缺乏同理心。 -_Examples:_ +**例如:** -#### Lightning Deals on Amazon: Good +#### 在 Amazon 上的秒杀产品:好 -They last a few hours and show the deadline. They are accompanied by the percentage claimed by other people to highlight the urgency. +它们持续会几个小时并显示最后期限。与之相伴的还有其他人为强调紧迫性而提出的百分比。 ![](https://cdn-images-1.medium.com/max/800/1*-o8SOwM2cuwNsXvWk9Ykkw.png) -#### **Courses on Interaction Design Foundation: Smart** +#### **交互设计基础课程:明智** -Present the time until enrolment ends. Fully booked courses are still displayed to show people what it’s like to miss the opportunity. +在注册结束前显示时间。全部预定的课程仍然展示给人们看,来让他们体验错过这个机会是什么感觉。 ![](https://cdn-images-1.medium.com/max/800/1*gI1o7e2uhFVbPlDurYQhsg.png) -#### Buying things on eBay: Bad +#### 在 eBay 上买东西:槽糕 -Time limited products are marked with a red icon and a vague “Almost gone” tag. Not showing when the offer ends is unthoughtful and manipulative. +限时产品有红色图标和模糊到“几乎消失”的标签。没有显示优惠结束时间是不合时宜和糟糕的操作。 ![](https://cdn-images-1.medium.com/max/800/1*tbadRIMbFesPQPtSAZfrEQ.png) -#### Searching places on Airbnb: Fair +#### 在 Airbnb 搜索地点:公平 -It shows people how limited the offer is by displaying the low percentage of listings left and a “Rare find” tag to make them feel lucky about their search. +它通过显示剩余列表的低百分比和一个“稀有”标签来显示人们对报价的限制,使他们对搜索感到幸运。 ![](https://cdn-images-1.medium.com/max/800/1*Wx7V4DkqclV1Y-Gd3lJHKw.png) -### 2. Quantity-limited scarcity +### 2. 数量有限的稀缺性 -Limited or rare supplies are perceived by people as a threat to their freedom of choice, triggering a reaction to fight the threat and maintain their access to the resource. +人们认为有限或稀缺的物资会对他们的选择自由构成威胁,从而引起对抗威胁并保持对资源访问的反应。 -> **Quantity-limited scarcity is considered more effective than time-limited scarcity because the end of the supply is unpredictable, depending exclusively on demand rather than time.** +> **数量有限的稀缺被认为比时间有限的稀缺更有效,因为供应的终点是不可预测的,完全取决于需求而非时间。** -_Examples:_ +**比如:** -#### Looking for hotels on Booking.com: Impressive +#### 在 Booking.com 上查找酒店:令人印象深刻 -Booking is the Usain Bolt of scarcity and probably owe much of their success to it. They show the number of rooms left but also a ton of tags and labels that make you feel you’re about to make the deal of your life. +预定是 Usain Bolt 的稀缺性,而且大部分成功都归功于它。它们显示房间的数量,但同时也显示了大量的标签,而且这些标签让你觉得你的生活非常舒适。 -It’s smart how they use the massive data they have and, even though everything is pretty overwhelming, the information is useful. +他们使用所拥有的大量数据的方式很明智,尽管这些数据非常庞大,但它们都是有用的。 ![](https://cdn-images-1.medium.com/max/800/1*9F03hEXKTnjETwY0TR3zBQ.png) -#### Booking flights on Ryan Air: Good +#### 通过 Ryan Air 预定航班:非常好 -They take advantage of the fact that cheaper seats sell first and use this to highlight the limited number of seats left for the lowest price. +他们利用便宜的座位先出售的事实,来突显以最低价格出售的数量有限的席位。 ![](https://cdn-images-1.medium.com/max/800/1*ZFpVSmeUx9_ZSx6pcAZvBw.png) -#### Buying clothes on Selfridges: Subtle +#### 在 Selfridges 买衣服:Subtle -Product details display both the available and unavailable sizes. This way, it makes the available ones feel more scarce. Subtle and useful as some people are between sizes. +产品的详细信息显示可用和不可用尺寸。这样,它使得可用的感觉更稀缺。这微妙而有用,因为有些人的尺寸需求介于给定尺寸之间。 ![](https://cdn-images-1.medium.com/max/800/1*4jmFwuyTRB9MwwIjFWkP5Q.png) -### 3. Access-limited scarcity +### 3. 访问受限的稀缺性 -It refers to limited access to features like information, groups or spaces. Research showed that censorship made people place a higher value on the restricted features than those that were not because exclusivity made them feel special. +它指的是信息,组或空间等功能的有限访问。研究表明,审查制度使得人们更重视受限功能,而不是因为排他性使他们感到特别。 -_Examples:_ +**例如:** -#### Becoming a subscriber on Medium +#### 订阅 Medium -Medium charges you if you want to be able to read all the stories on their platform. But once you do, you’ll be one of their privileged users. +如果你希望可以阅读其平台的所有内容,Medium 会收取费用。一旦你决定那么做,也意味着你成为了 Medium 特权用户。 ![](https://cdn-images-1.medium.com/max/800/1*YnpAAetpwd1celecktHz-g.png) -#### Joining Tinder Select +#### 加入 Tinder Select -Tinder uses the “Elo” ranking system to rate members based on desirability and invites the top ones to join the closed version called Tinder Select. Even though a bit cynical for the rest of the pack, it does what it’s suppose to do: reward popular users by making them feel unique. +Tinder 使用 “Elo” 排名系统,根据需要来对成员进行评级,并邀请顶级用户加入名为 Tinder Select 的封闭版本。尽管对其他用户不公平,但它也只是做了它应该做的事情:通过给予活跃用户重视度来奖励他们。 ![](https://cdn-images-1.medium.com/max/800/1*OCrjwQajhoWi5_CArlBf_g.png) * * * -### It is controversial but it shouldn’t be +### 它拥有不应该存在的争议 -If suitable for the product we design for, scarcity can optimise user flows and impact business goals. It reframes information and alerts users when there is a need for urgency. +如果适合我们设计的产品,稀缺性可以优化用户流程并影响商业目标。在紧急情况下,它会重构信息并通知用户。 -> Some might argue that this forces them to make a decision, but as long as the numbers are real, what’s the alternative? Isn’t the sense of regret or frustration caused by us failing to tell them about the scarce product in time just as bad? Aren’t we offering an awful user experience if that happens? Rushing people into making a decision seems rather fair as long as we’re presenting them the facts. +> 可能有人会说,这迫使他们做出决定,但只要数字是真实,我们别无它选!是因为我们没有及时告知他们产品稀缺而造成的遗憾和沮丧?如果发生这种情况,我们不是在提供糟糕的用户体验么?只要我们向他们介绍事实,促使人们匆忙做出决定似乎是相当公平的。 -Having said that, I agree that some businesses take advantage of this and use it unethically by inventing fake stocks and artificial memberships. But this is true for any other method used with questionable intentions and it always leads to loss of credibility in the long run. +话虽如此,我同意一些企业利用这一优势,不道德地利用它,制造假库存和会员身份。但对于任何其他使用可疑意图的方法而言,久而久之,导致信誉受损是毫无疑问的。 ![](https://cdn-images-1.medium.com/max/800/1*0yTyOzOVgnL-XbRRj3escA.png) * * * -### It should follow a few rules +### 它应该遵循一些规则 -To avoid this, below are some suggestions for making the best out of scarcity and actually improve the UX: +为了避免此类情况发生,以下建议可以充分利用稀缺性,而且可以切实改善用户体验: -#### Do +#### 可以做的事情 -* use **scarcity** to increase perceived value and expedite conversions -* use **time scarcity** to promote products that are time sensitive -* use **quantity scarcity** to make people aware of stock shortages -* use **access scarcity** to highlight the advantages of the restricted features -* use **A/B testing** to test what scarcity message works best for your audience -* use **usability testing** to test the impact of messages on credibility and trust -* use **animated elements** to emphasise urgency (e.g. showing a glowing red icon to highlight the real-time status) +*   使用**稀缺性**来提高感知价值并加速转换 +*   使用**时间稀缺性**来推广对时间敏感的产品 +*   使用**数量稀缺性**让人们意识到库存短缺 +*   使用**访问稀缺性** 来突出受限功能的特性 +*   使用 **A/B 测试**来测试那些稀缺性消息对受众最有效 +* 使用**可用性测试**来测试消息对可信度和信任度的影响 +* 使用**动画元素**来强调紧迫性(例如,显示一个发光的红色图标以突出实时状态) -#### Don’t +#### 不可以做的事情 -* do not use scarcity **without testing** it first with users -* do not use scarcity if stocks are **not reliable** -* do not use scarcity if the messages are not **bug free** -* do not use **fake numbers** to create artificially scarce products +*   不要在**没有测试**的情况下使用稀缺性,因为它首先应与用户结合使用 +*   如果库存**不可信**,不要使用稀缺性 +*   如果信息**没有缺陷**,不要使用稀缺性 +*   不要使用**虚假数字**人为制造稀缺产品 * * * -### Conclusion +### 结论 -Scarcity makes us place a higher value on things that are scarce and, over time, has become the go to method for increasing desirability. It is powerful because it combines multiple biases (Loss aversion, Social proof and Anticipated regret) and it comes in different forms (Time, Quantity and Access). +稀缺性让我们对稀缺事物有了更深层次的认识,随着时间的推移,它已经成为提高效益的方法。它很强大,因为它结合了很多种认知偏差(损失厌恶,社会证明和预期遗憾),而且存在多种实现形式(时间,数量和访问)。 -It is controversial but it shouldn’t be because hiding the information from people is not really an option. It can also improve UX if you follow a few simple rules. +但这不应该存在争议,因为对人们隐藏信息不是一个真正的选择。如果你遵循一些简单的规则,还可以提升用户体验。 --- From 1379ed5dd8b6542e38a52640f5781fb3e4d53433 Mon Sep 17 00:00:00 2001 From: weberpan Date: Mon, 7 May 2018 10:00:58 +0800 Subject: [PATCH 009/446] =?UTF-8?q?=E6=B7=B1=E5=85=A5=E6=B5=85=E5=87=BA=20?= =?UTF-8?q?JavaScript=20=E5=85=B3=E9=94=AE=E8=AF=8D=20=E2=80=94=20this=20(?= =?UTF-8?q?#3715)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update mastering-javascript-this-keyword-detailed-guide.md 测试更改 * update translate mastering-this * 继续翻译 * 新修改 * 继续翻译 * update * 完成翻译 * 小优化 * 小优化 * 修复图片链接问题 * 根据校对意见修改 --- ...-javascript-this-keyword-detailed-guide.md | 646 +++++++++--------- 1 file changed, 323 insertions(+), 323 deletions(-) diff --git a/TODO1/mastering-javascript-this-keyword-detailed-guide.md b/TODO1/mastering-javascript-this-keyword-detailed-guide.md index c0fb2355ec6..d80c878c2b6 100644 --- a/TODO1/mastering-javascript-this-keyword-detailed-guide.md +++ b/TODO1/mastering-javascript-this-keyword-detailed-guide.md @@ -2,117 +2,117 @@ > * 原文作者:[Jay](https://www.thecodingdelight.com/author/ljay189/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/mastering-javascript-this-keyword-detailed-guide.md](https://github.com/xitu/gold-miner/blob/master/TODO1/mastering-javascript-this-keyword-detailed-guide.md) -> * 译者: -> * 校对者: +> * 译者:[老教授](https://juejin.im/user/58ff449a61ff4b00667a745c) +> * 校对者:[allen](https://github.com/allenlongbaobao)、[dz](https://github.com/dazhi1011) -# Mastering JavaScript this Keyword – Detailed Guide +# 深入浅出 JavaScript 关键词 -- this -The JavaScript `this` keyword is often considered one of the most confusing aspects of the language. JavaScript has come a long way, now with node.js being used to run JavaScript in the server, along with the continual evolution of the language. Needless to say that this language is not disappearing anytime soon. +要说 JavaScript 这门语言最容易让人困惑的知识点,`this` 关键词肯定算一个。JavaScript 语言面世多年,一直在进化完善,现在在服务器上还可以通过 node.js 来跑 JavaScript。显然,这门语言还会活很久。 -Therefore, I believe that if you are a JavaScript developer or somebody who works with web technologies, learning how JavaScript works and also its idiosyncrasies will pay dividends down the road. +所以说,我一直相信,如果你是一个 JavaScript 开发者或者说 Web 开发者,学好 JavaScript 的运作原理以及语言特点肯定对你以后大有好处。 -## Prerequisites +## 开始之前 -Before reading ahead, it is strongly recommended that you have a solid understanding of the following. +在开始正文之前,我强烈推荐你先掌握好下面的知识: -* [Variable scope and hoisting](https://www.thecodingdelight.com/variable-scope-hoisting-javascript/) -* [Functions in JavaScript](https://www.codecademy.com/courses/functions-in-javascript-2-0/0/1) -* [Closures](https://medium.com/dailyjs/i-never-understood-javascript-closures-9663703368e8) +* [变量作用域和作用域提升](https://www.thecodingdelight.com/variable-scope-hoisting-javascript/) +* [JavaScript 的函数](https://www.codecademy.com/courses/functions-in-javascript-2-0/0/1) +* [闭包](https://medium.com/dailyjs/i-never-understood-javascript-closures-9663703368e8) -Without a solid understanding of the fundamentals, discussions regarding the JavaScript `this` keyword will only add a layer of confusion and frustration. +如果没有对这些基础知识掌握踏实,直接讨论 JavaScript 的 `this` 关键词只会让你感到更加地困惑和挫败。 -## Why should I Learn `this`? +## 我为什么要学 `this`? -If the basic introduction did not convince you to explore the `this` keyword in detail, I will cover the why in this section. +如果上面的简单介绍没有说服你来深入探索 `this` 关键词,那我用这节来讲讲为什么要学。 -A very valid question, considering that people like Douglas Crockford have stopped using `new` and `this`, and instead, opted for an entirely functional approach for code reuse. +考虑这样一个重要问题,假设开发者,比如 Douglas Crockford (译者注:JavaScript 领域必知牛人),不再使用 `new` 和 `this`,转而使用完完全全的函数式写法来做代码复用,会怎样? -Traditionally, `new` and `this` has been and continues to be used extensively to achieve code reuse via the built-in [prototypal inheritance](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance) that JavaScript provides out of the box. +事实上,基于 JavaScript 内置的现成的[原型继承](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Inheritance)功能,我们已经使用并且将继续广泛使用 `new` 和 `this` 关键词来实现代码复用。 -The first reason is that you aren’t going to be working just with code that you have written. Existing code and code that is being written even as you are reading this sentence likely contain the ‘`this`‘ keyword. Would definitely help if you understand how this works right (pun intended)? +理由一,如果只能使用自己写过的代码,你是没法工作的。现有的代码以及你读到这句话时别人正在写的代码都很有可能包含 `this` 关键词。那么学习怎么用好它是不是很有用呢? -Therefore, even if you don’t want to use this in your code base, in order to interpret the behavior of legacy code, having a strong understanding of how `this` works will help. +因此,即使你不打算在你的代码库中使用它,深入掌握 `this` 的原理也能让你在接手别人的代码理解其逻辑时事半功倍。 -The second reason is **expanding your coding vision and skill**. Working with a variety of patterns will deepen your understanding of how you see, read, write and interpret code. We write code not for the machine to interpret, but for ourselves. This does not simply apply to your JavaScript skills. +理由二,**拓展你的编码视野和技能**。使用不同的设计模式会加深你对代码的理解,怎么去看、怎么去读、怎么去写、怎么去理解。我们写代码不仅是给机器去解析,还是写给我们自己看的。这不仅适用于 JavaScript,对其他编程语言亦是如此。 -> Deepening your understanding will impact and influence how you write code, regardless of the language/framework you are working with. +> 随着对编程理念的逐步深入理解,它会逐渐塑造你的编码风格,不管你用的是什么语言什么框架。 -Just as Picasso dabbled in areas that he did not particularly enjoy or agree with for inspiration, having this knowledge will expand your knowledge and understanding of code. +就像毕加索会为了获得灵感而涉足那些他并不是很赞同很感兴趣的领域,学习 this 会拓展你的知识,加深对代码的理解。 -## What is `this`? +## 什么是 `this` ? -[![JavaScript this call context](//personalzone-hulgokm2zfcmm9u.netdna-ssl.com/wp-content/uploads/2018/03/JavaScript-this-call-context.jpg)](//personalzone-hulgokm2zfcmm9u.netdna-ssl.com/wp-content/uploads/2018/03/JavaScript-this-call-context.jpg) +[![JavaScript this 指向](https://personalzone-hulgokm2zfcmm9u.netdna-ssl.com/wp-content/uploads/2018/03/JavaScript-this-call-context.jpg)](https://personalzone-hulgokm2zfcmm9u.netdna-ssl.com/wp-content/uploads/2018/03/JavaScript-this-call-context.jpg) -Before I start explaining, if you have a programming background in the traditional class-based OOP languages (E.g. C#, Java, C++), please throw away all your preconceived notions of what the ‘`this`‘ keyword is supposed to be. The JavaScript `this` keyword behaves quite differently, because JavaScript is not a [class-based Object-oriented programming language](https://en.wikipedia.org/wiki/Class-based_programming). +在我开始讲解前,如果你学过一门基于类的面向对象编程语言(比如 C#,Java,C++),那请将你对 `this` 这个关键词应该是做什么用的先入为主的概念扔到垃圾桶里。JavaScript 的 `this` 关键词是很不一样,因为 JavaScript 本来就不是一门基于类的[面向对象编程语言](https://en.wikipedia.org/wiki/Class-based_programming)。 -Although in ES6, JavaScript provides users with an option to code using classes, it is simply [syntactic sugar](https://www.quora.com/What-is-syntactic-sugar-in-programming-languages) for the underlying prototypal inheritance structure. +虽说 ES6 里面 JavaScript 提供了类这个特性给我们用,但它只是一个[语法糖](https://www.quora.com/What-is-syntactic-sugar-in-programming-languages),一个基于原型继承的语法糖。 -**The ‘`this`‘ keyword is a pointer that points to the object that called the function.** +**`this` 就是一个指针,指向我们调用函数的对象。** -I cannot stress just how important the previous sentence is. Remember, there were no classes in JavaScript until it was added in ES6. [Classes](http://2ality.com/2015/02/es6-classes-final.html) are merely syntactic sugar for linking objects together to formulate a behavior that is similar to the class based inheritance that most of us all are so used to. All the behind the scenes magics is woven together via linking of the prototype chain. +我难以强调上一句话有多重要。请记住,在 Class 添加到 ES6 之前,JavaScript 中没有 Class 这种东西。[Class](http://2ality.com/2015/02/es6-classes-final.html) 只不过是一个将对象串在一起表现得像类继承一样的语法糖,以一种我们已经习惯的写法。所有的魔法背后都是用原型链编织起来的。 -If the previous sentence is difficult to understand, the context of this is very similar to an English sentence. For example, the following line +如果上面的话不好理解,那你可以这样想,this 的上下文跟英语句子的表达很相似。比如下面的例子 `Bob.callPerson(John);` -Can be written in English as “Bob called a person named John”. Since `callPerson()` was called from Bob, `this` points at Bob. We will get into the nitty-gritty in the upcoming sections. By the end of this post, you will leave with a much better understanding (and confidence) of the `this` keyword in JavaScript. +就可以用英语写成 “Bob called a person named John”。由于 `callPerson()` 是 Bob 发起的,那 `this` 就指向 Bob。我们将在下面的章节深入更多的细节。到了这篇文章结束时,你会对 `this` 关键词有更好的理解(和信心)。 -## Execution Context +## 执行上下文 -> _Execution context_ is a concept in the language spec that—in layman’s terms—roughly equates to the ‘environment’ a function executes in; that is, variable scope (and the _scope chain_, variables in closures from outer scopes), function arguments, and the value of the `this` object. +> **执行上下文** 是语言规范中的一个概念,用通俗的话讲,大致等同于函数的执行“环境”。具体的有:变量作用域(和 _作用域链条_,闭包里面来自外部作用域的变量),函数参数,以及 `this` 对象的值。 > -> Source: [Stackoverflow.com](https://stackoverflow.com/questions/9384758/what-is-the-execution-context-in-javascript-exactly) +> 引自: [Stackoverflow.com](https://stackoverflow.com/questions/9384758/what-is-the-execution-context-in-javascript-exactly) -Remember, right now, we are focused on ascertaining what `this` is pointing at. Therefore, the only thing we need to ask ourselves is: +记住,现在起,我们专注于查明 `this` 关键词到底指向哪。因此,我们现在要思考的就一个问题: -* what is calling the function? What object is calling the function? +* 是什么调用函数?是哪个对象调用了函数? -To understand this key concept, let us examine some examples. +为了理解这个关键概念,我们来测一下下面的代码。 ``` var person = { -name: "Jay", -greet: function() { -console.log("hello, " + this.name); -} + name: "Jay", + greet: function() { + console.log("hello, " + this.name); + } }; person.greet(); ``` -Who/what is calling the g_reet function_? It is the object `person` right? On the left hand side of the `greet()` call, there is a person object. Therefore, the this keyword will point at `person`. Therefore, `this.name` will be equal to `"Jay"`. Now, using the example above, what if I were to add the following: +谁调用了 _greet 函数_?是 `person` 这个对象对吧?在 `greet()` 调用的左边是一个 person 对象,那么 this 关键词就指向 `person`,`this.name` 就等于 `"Jay"`。现在,还是用上面的例子,我加点料: ``` -var greet = person.greet; // store reference to function; -greet(); // call function +var greet = person.greet; // 将函数引用存起来; +greet(); // 调用函数 ``` -What do you think the console will output in this case? “Jay”? `undefined`? Or something else? +你觉得在这种情况下控制台会输出什么?“Jay”?`undefined`?还是别的? -The answer is `undefined`. If you are surprised by the output, no need to feel ashamed. You are about to learn something that will help you unlock a crucial gate in your JavaScript journey. +正确答案是 `undefined`。如果你对这个结果感到惊讶,不必惭愧。你即将学习的东西将帮助你在 JavaScript 旅程中打开关键的大门。 -> The value of `this` is not defined by the object it is placed in, but by **how it is called**. +> `this` 的值并不是由函数定义放在哪个对象里面决定,而是函数执行时由谁来唤起决定。 -Let this newfound revelation sink in before proceeding. +对于这个意外的结果我们暂且压下,继续看下去。(感觉前后衔接得不够流畅) -With this, we are going to examine the **three ways** that the `this` keyword is defined. +带着这个困惑,我们接着测试下 `this` **三种**不同的定义方式。 -## Identifying where `this` points at +## 找出 `this` 的指向 -We examined this in the previous section. But because this (pun unintended) is so important, we will review it again. First of all, I have a challenge for you: Examine the following code. +上一节我们已经对 `this` 做了测试。但是这块知识实在重要,我们需要再好好琢磨一下。在此之前,我想用下面的代码给你出个题: ``` var name = "Jay Global"; var person = { -name: 'Jay Person', -details: { -name: 'Jay Details', -print: function() { -return this.name; -} -}, -print: function() { -return this.name; -} + name: 'Jay Person', + details: { + name: 'Jay Details', + print: function() { + return this.name; + } + }, + print: function() { + return this.name; + } }; console.log(person.details.print()); // ? console.log(person.print()); // ? @@ -122,80 +122,80 @@ console.log(name1()); // ? console.log(name2.print()) // ? ``` -Write down your expected answers for the `console.log()` outputs. If you are not sure, review the previous section. +`console.log()` 将会输出什么,把你的答案写下来。如果你还想不清楚,复习下上一节。 -Once you are ready, feel free to check out the answers below. +准备好了吗?放松心情,我们来看下面的答案。 -### Answers and Explanations +### 答案和解析 ##### person.details.print() -Before we proceed, who/what is calling print? In JavaScript, we read from left to right. Therefore, this points at `details` not `person`. This is an important distinction to make, so bear it in mind if this is something new to you. +首先,谁调用了 print 函数?在 JavaScript 中我们都是从左读到右。于是 this 指向 `details` 而不是 `person`。这是一个很重要的区别,如果你对这个感到陌生,那赶紧把它记下。 -The `print` key in `details` holds a function that returns `this.name`. Since we have identified that this refers to details, the function should return the value `'Jay Details'`. +`print` 作为 `details` 对象的一个 key,指向一个返回 `this.name` 的函数。既然我们已经找出 this 指向 details ,那函数的输出就应该是 `'Jay Details'`。 ##### person.print() -Once again, identify what `this` is pointing at. `print()` is being called on the `person` object right? +再来一次,找出 `this` 的指向。`print()` 是被 `person` 对象调用的,没错吧? -In this case, the `print` function on `person` returns `this.name`. `this` is currently pointing at `person`, so “`Jay Person`” will be returned. +在这种情况,`person` 里的 `print` 函数返回 `this.name`。`this` 现在指向 `person` 了,那 `'Jay Person'` 就是返回值。 ##### console.log(name1) -This one can be a little tricky. In the previous line, we have the following pieces of code. +这一题就有点狡猾了。在上一行有这样一句代码: ``` var name1 = person.print; ``` -I won’t blame you if that is what you thought. Unfortunately, it is wrong. Remember, the `this` keyword is bound when the function is called. What is in front of `name1()`? Nothing. Therefore, the `this` keyword is going to point at the global `window` object. +如果你是通过这句来思考的,我不会怪你。很遗憾,这样去想是错的。要记住,`this` 关键词是在函数调用时才做绑定的。`name1()` 前面是什么?什么都没有。因此 `this` 关键词就将指向全局的 `window` 对象去。 -Therefore, the answer is “`Jay Global`”. +因此,答案是 `'Jay Global'`。 -##### person.print() +##### name2.print() -Take a look at the object that `name2` is pointing at. It is the `details` object right? +看一下 `name2` 指向哪个对象,是 `details` 对象没错吧? -So what will the following print? If you understood all the material up until now, this should come quite naturally if you put your mind to it. +所以下面这句会打印出什么呢?如果到目前为止的所有小点你都理解了,那这里稍微思考下你就自然有答案了。 ``` console.log(name2.print()) // ?? ``` -The answer is “`Jay Details`” because print is called on `name2` which points at `details`. +答案是 `'Jay Details'`,因为 `print` 是 `name2` 调起的,而 `name2` 指向 `details`。 -### Lexical Scope +### 词法作用域 -You might be asking: **What is lexical scope**? +你可能会问:“**什么是词法作用域?**” -Heck, why are we even covering it when we are focusing on understanding the JavaScript `this` keyword? Well, it comes into play when we start working with ES6’s arrow functions. If you have written JavaScript for a year or more, chances are, you have come across arrow functions. And they will be used more and more as ES6 becomes more standardized. +逗我呢,我们不是在探讨 `this` 关键词吗,这个又是哪里冒出来的?好吧,当我们用起 ES6 的箭头函数,这个就要考虑了。如果你已经写了不止一年的 JavaScript,那你很可能已经碰到箭头函数。随着 ES6 逐渐成为现实标准,箭头函数也变得越来越常用。 -[Lexical scope in JavaScript](https://toddmotto.com/everything-you-wanted-to-know-about-javascript-scope/#lexical-scope) can be confusing to grasp. If you [understand closures](https://www.thecodingdelight.com/javascript-closure/), it will be much easier to conceptualize. Let’s take a look at a brief code snippet. +[JavaScript 的词法作用域](https://toddmotto.com/everything-you-wanted-to-know-about-javascript-scope/#lexical-scope) 并不好懂。如果你 [理解闭包](https://www.thecodingdelight.com/javascript-closure/),那要理解这个概念就容易多了。来看下下面的小段代码。 ``` -// lexical scope of outerFn +// outerFn 的词法作用域 var outerFn = function() { -var n = 5; -console.log(innerItem); -// lexical scope of innerFn -var innerFn = function() { -var innerItem = "inner"; // Error. Can only go upwards with the elevator. Not downwards. -console.log(n); -}; -return innerFn; + var n = 5; + console.log(innerItem); + // innerFn 的词法作用域 + var innerFn = function() { + var innerItem = "inner"; // 错了。只能坐着电梯向上,不能向下。 + console.log(n); + }; + return innerFn; }; outerFn()(); ``` -Think of a building with a crappy elevator that can only travel upwards. +想象一下一栋楼里面有一架只能向上走的诡异电梯。 -[![JavaScript lexical scope is a lot like a building with an elevator that only goes up](//personalzone-hulgokm2zfcmm9u.netdna-ssl.com/wp-content/uploads/2018/03/JavaScript-lexical-scope-building.jpg)](//personalzone-hulgokm2zfcmm9u.netdna-ssl.com/wp-content/uploads/2018/03/JavaScript-lexical-scope-building.jpg) +[![JavaScript 的词法作用域就像楼里的一架只能向上走的诡异电梯](https://personalzone-hulgokm2zfcmm9u.netdna-ssl.com/wp-content/uploads/2018/03/JavaScript-lexical-scope-building.jpg)](https://personalzone-hulgokm2zfcmm9u.netdna-ssl.com/wp-content/uploads/2018/03/JavaScript-lexical-scope-building.jpg) -The top floor of a building is the global windows object. If you are on the first floor, you are able to see and access the items in the upper floors such as those stored the second and third floor (`outerFn` and the global `window` object). +建筑的顶层就是全局 windows 对象。如果你现在在一楼,你就可以看到并访问那些放在楼上的东西,比如放在二楼的 `outerFn` 和放在三楼的 `window` 对象。 -That is why when we run the following code: `outerFn()()`; it logs 5 onto the console and not `undefined`. +这就是为什么我们执行代码 `outerFn()()`,它在控制台打出了 5 而不是 `undefined`。 -However, when we try to log `innerItem` from the `outerFn` lexical scope, we get the following error. Remember, the lexical scope in JavaScript is like a crappy elevator in a building that can only move upwards. Since outerFn is one lexical scope above the innerFn, it cannot go down into the innerFn lexical scope and retrieve the value inside of it. Which is why we get the following error: +然而,当我们试着在 `outerFn` 词法作用域下打出日志 `innerItem`,我们遇到了下面的报错。请记住,JavaScript 的词法作用域就好像建筑里面那个只能向上走的诡异电梯。由于 outerFn 的词法作用域在 innerFn 上面,所以它不能向下走到 innerFn 的词法作用域里面并拿到里面的值。这就是触发下面报错的原因: ``` test.html:304 Uncaught ReferenceError: innerItem is not defined @@ -203,480 +203,480 @@ at outerFn (test.html:304) at test.html:313 ``` -### `this` and Arrow Functions +### `this` 和箭头函数 -In [ES6](http://es6-features.org/#ExpressionBodies), whether you like it or not, [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) were introduced. For those that are not yet accustomed to arrow functions or are new to JavaScript, how it behaves in conjunction with the `this` keyword may cause you some confusion and potentially grief, this section is dedicated to you! +在 [ES6](http://es6-features.org/#ExpressionBodies) 里面,不管你喜欢与否,[箭头函数](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)被引入了进来。对于那些还没用惯箭头函数或者新学 JavaScript 的人来说,当箭头函数和 `this` 关键词混合使用时会发生什么,这个点可能会给你带来小小的困惑和淡淡的忧伤。那这个小节就是为你们准备的! -> What is the main difference between _arrow functions_ and _regular functions_ when it comes to the `this` keyword? +> 当涉及到 `this` 关键词,**箭头函数** 和 **普通函数** 主要的不同是什么? -**Answer**: +**答案:** -> Arrow Functions **lexically** bind their context so `this` actually refers to the originating context. +> 箭头函数按**词法作用域**来绑定它的上下文,所以 `this` 实际上会引用到原来的上下文。 > -> source: [hackernoon.com](https://hackernoon.com/javascript-es6-arrow-functions-and-lexical-this-f2a3e2a5e8c4) +> 引自:[hackernoon.com](https://hackernoon.com/javascript-es6-arrow-functions-and-lexical-this-f2a3e2a5e8c4) -I couldn’t have said it any better. +我实在没法给出比这个更好的总结。 -Arrow functions preserve the [lexical scope](https://stackoverflow.com/questions/1047454/what-is-lexical-scope) of its current execution context, while regular functions don’t. In other words, arrow functions derive the value of `this` from the lexical scope that contains the arrow function. +箭头函数保持它当前执行上下文的[词法作用域](https://stackoverflow.com/questions/1047454/what-is-lexical-scope)不变,而普通函数则不会。换句话说,箭头函数从包含它的词法作用域中继承到了 `this` 的值。 -Let’s examine a few code snippets to ensure that you properly understand this. Understanding how this works (no pun intended) will save you a lot of headaches in the future, as the `this` keyword and arrow functions are used in conjunction quite regularly. +我们不妨来测试一些代码片段,确保你真的理解了。想清楚这块知识点未来会让你少点头痛,因为你会发现 `this` 关键词和箭头函数太经常一起用了。 -### Example +### 示例 -Take a look at the following code snippet carefully. +仔细阅读下面的代码片段。 ``` var object = { -data: [1,2,3], -dataDouble: [1,2,3], -double: function() { -console.log("this inside of outerFn double()"); -console.log(this); -return this.data.map(function(item) { -console.log(this); // What is this ??? -return item * 2; -}); -}, -doubleArrow: function() { -console.log("this inside of outerFn doubleArrow()"); -console.log(this); -return this.dataDouble.map(item => { -console.log(this); // What is this ??? -return item * 2; -}); -} + data: [1,2,3], + dataDouble: [1,2,3], + double: function() { + console.log("this inside of outerFn double()"); + console.log(this); + return this.data.map(function(item) { + console.log(this); // 这里的 this 是什么?? + return item * 2; + }); + }, + doubleArrow: function() { + console.log("this inside of outerFn doubleArrow()"); + console.log(this); + return this.dataDouble.map(item => { + console.log(this); // 这里的 this 是什么?? + return item * 2; + }); + } }; object.double(); object.doubleArrow(); ``` -If we look at the execution context, both of these are called on `object`. So, it is safe to assume that this inside of the two function refers to `object` right? Yes, but I recommend you to copy and paste this code and test it yourself. +如果我们看执行上下文,那这两个函数都是被 `object` 调用的。所以,就此断定这两个函数里面的 this 都指向 `object` 不为过吧?是的,但我建议你拷贝这段代码然后自己测一下。 -Here is the big question +这里有个大问题: -> What does `this` point at inside of the inner `map` function inside `arrow()` and `doubleArrow()`? +> `arrow()` 和 `doubleArrow()` 里面的 `map` 函数里面的 `this` 又指向哪里呢? -[![this and arrow function](//personalzone-hulgokm2zfcmm9u.netdna-ssl.com/wp-content/uploads/2018/03/this-and-arrow-function.jpg)](//personalzone-hulgokm2zfcmm9u.netdna-ssl.com/wp-content/uploads/2018/03/this-and-arrow-function.jpg) +[![this 和箭头函数](https://personalzone-hulgokm2zfcmm9u.netdna-ssl.com/wp-content/uploads/2018/03/this-and-arrow-function.jpg)](https://personalzone-hulgokm2zfcmm9u.netdna-ssl.com/wp-content/uploads/2018/03/this-and-arrow-function.jpg) -The following image should provide a huge hint. If you are not sure, please take around 5 minutes to think about what we have discussed in previous section. Afterwards, write down your answer to what you think this points to before proceeding. In the following section, we will answer the question. +上一张图已经给了一个大大的提示。如果你还不确定,那请花5分钟将我们上一节讨论的内容再好好想想。然后,根据你的理解,在实际执行代码前把你认为的 this 应该指向哪里写下来。在下一节我们将会回答这个问题。 -### Revision of Execution Context +### 回顾执行上下文 -The title should already have given it away. In case you don’t know, the map function iterates through the array that it is called upon and applies the return value of the passed in callback to each of its items. [Read more about JavaScript’s map function](https://www.thecodingdelight.com/functional-programming-javascript-map/) if you are unsure or are just curious. +这个标题已经把答案泄露出来了。在你看不到的地方,map 函数对调用它的数组进行遍历,将数组的每一项传到回调函数里面并把执行结果返回。如果你对 JavaScript 的 map 函数不太了解或有所好奇,可以读读[这个](https://www.thecodingdelight.com/functional-programming-javascript-map/)了解更多。 -Anyhow, since `map()` is called on `this.data`, this will point at the array stored inside of the `data` key, which is `[1,2,3]`. Using the same logic, `this.dataDouble` should point at another array with the data `[1,2,3]`. +总之,由于 `map()` 是被 `this.data` 调起的,于是 this 将指向那个存储在 `data` 这个 key 里面的数组,即 `[1,2,3]`。同样的逻辑,`this.dataDouble` 应该指向另一个数组,值为 `[1,2,3]`。 -Now, if the function call is made on `object`, we have established that this refers to `object` right? Okay, let’s move onto the following code snippet. +现在,如果函数是 `object` 调用的,我们已经确定 this 指向 `object` 对吧?好,那来看看下面的代码片段。 ``` double: function() { -return this.data.map(function(item) { -console.log(this); // What is this ??? -return item * 2; -}); + return this.data.map(function(item) { + console.log(this); // 这里的 this 是什么?? + return item * 2; + }); } ``` -Here is a trick question: who is calling the [anonymous function](https://en.wikibooks.org/wiki/JavaScript/Anonymous_functions) passed to `map()`? The answer is: no object is. To make things clear, here is a basic implementation of the `map` function. +这里有个很有迷惑性的问题:传给 `map()` 的那个[匿名函数](https://en.wikibooks.org/wiki/JavaScript/Anonymous_functions)是谁调用的?答案是:这里没有一个对象是。为了看得更明白,这里给出一个 `map` 函数的基本实现。 ``` // Array.map polyfill if (Array.prototype.map === undefined) { -Array.prototype.map = function(fn) { -var rv = []; -for(var i=0, l=this.length; i Arrow Functions Lexically Bind their Context to the **Originating Context** +> 箭头函数按词法作用域将它的上下文绑定到 **原来的上下文** -Now, you might be asking: what is the originating context? Good question! +现在,你可能会问:原来的上下文是什么?问得好! -Who is the original caller of `doubleArrow()`? `object` right? That is the originating context 🙂 +谁是 `doubleArrow()` 的初始调用者?就是 `object` 对吧?那它就是原来的上下文 🙂 -## this and `use strict` +## this 和 `use strict` -In ES5, the [strict mode](https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/dev-guides/hh673540(v=vs.85)) feature was added in order to make the language more robust and minimize human errors. One prime example is with the relationship between how this behaves in strict mode. In order to write your code in strict mode, all you have to do is write the string `"use strict";` at the top of the scope that you are working with. +为了让 JavaScript 更加健壮及尽量减少人为出错,ES5 引进了[严格模式](https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/dev-guides/hh673540(v=vs.85))。一个典型的例子就是 this 在严格模式下的表现。你如果想按照严格模式来写代码,你只需要在你正在写的代码的作用域最顶端加上这么一行 `"use strict;"`。 -Remember that JavaScript is traditionally function scoped, not block scoped. For example, +记住,传统的 JavaScript 只有函数作用域,没有块作用域。举个例子: ``` function strict() { -// Function-level strict mode syntax -'use strict'; -function nested() { return 'And so am I!'; } -return "Hi! I'm a strict mode function! " + nested(); + // 函数级严格模式写法 + 'use strict'; + function nested() { return 'And so am I!'; } + return "Hi! I'm a strict mode function! " + nested(); } function notStrict() { return "I'm not strict."; } ``` -Code snippet provided by [Mozilla Developer Network.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) +代码片段来自 [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode)。 -Although block scoping can be achieved with the features that ES6 provides such as the [let keyword](https://www.thecodingdelight.com/javascript-es6-best-parts/#ftoc-heading-7). +不过呢,ES6 里面通过 [let 关键词](https://www.thecodingdelight.com/javascript-es6-best-parts/#ftoc-heading-7)提供了块作用域的特性。 -Now, let’s look at a simple code snippet to see how this behaves in strict mode and out of strict mode. Please run the code snippet below before proceeding. +现在,来看一段简单代码,看下 this 在严格模式和非严格模式下会怎么表现。在继续之前,请将下面的代码运行一下。 ``` (function() { -"use strict"; -console.log(this); + "use strict"; + console.log(this); })(); (function() { -// Without strict mode -console.log(this); + // 不使用严格模式 + console.log(this); })(); ``` -As you saw, `this` points at `undefined` in strict mode. In contrast, `this` points at the global `window` object without strict mode. In most cases, when users use this, they don’t want it to point at the global window object. Strict mode greatly reduces the possibility of developers shooting themselves in the foot when using the `this` keyword. +正如你看到的,`this` 在严格模式下指向 `undefined`。相对的,非严格模式下 `this` 指向全局变量 `window`。大部分情况下,开发者使用 this ,并不希望它指向全局 window 对象。严格模式帮我们在使用 `this` 关键词时,尽量少做搬起石头砸自己脚的蠢事。 -For example, what if the global window object has a property with the same key value as the one that you are using in your object? For example +举个例子,如果全局的 window 对象刚好有一个 key 的名字和你希望访问到的对象的 key 相同,会怎样?上代码吧: ``` (function() { -// "use strict"; -var item = { -document: "My document", -getDoc: function() { -return this.document; -} -} -var getDoc = item.getDoc; -console.log(getDoc()); + // "use strict"; + var item = { + document: "My document", + getDoc: function() { + return this.document; + } + } + var getDoc = item.getDoc; + console.log(getDoc()); })(); ``` -There are two problems in this code. +这段代码有两个问题。 -1. `this` will not point at `item` on line 10. -2. If the program is run without strict mode, no error will be thrown, since the `document` object is a property of the global `window` object. +1. `this` 将不会指向 `item`。 +2. 如果程序在非严格模式下运行,将不会有错误抛出,因为全局的 `window` 对象也有一个名为 `document` 的属性。 -In this simple example, it won’t be much of a problem, since the code snippet is very small. +在这个简单示例中,因为代码较短也就不会形成大问题。 -If you run code like this in production, when working with the item stored in `getDoc`, you will see a firework of errors that will be fairly difficult to trace, especially if the codebase is large has a lot of interactions between objects. +如果你是在生产环境像上面那样写,当用到 `getDoc` 返回的数据时,你将收获一堆难以定位的报错。如果你代码库比较大,对象间互动比较多,那问题就更严重了。 -Fortunately, if we run the code snippet in strict mode, since this is `undefined`, it will immediately throw an error back at us. +值得庆幸的是,如果我们是在严格模式下跑这段代码,由于 this 是 `undefined`,于是立刻就有一个报错抛给我们: > `test.html:312 Uncaught TypeError: Cannot read property 'document' of undefined` > `at getDoc (test.html:312)` > `at test.html:316` > `at test.html:317` -## Explicitly Setting the Execution Context +## 明确设置执行上下文 -We have talked a lot about execution context and the this keyword assuming that nobody was explicitly manipulating the execution context. +先前假定大家都对执行上下文不熟,于是我们聊了很多关于执行上下文和 this 的知识。 -What can be both a blessing and a curse in JavaScript is that this “**call/execution context**” can be manipulated directly by a developer using JavaScript’s built-in features. They are +让人欢喜让人忧的是,在 JavaScript 中通过使用内置的特性开发者就可以直接操作**执行上下文**了。这些特性包括: -* [bind()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind): Explicitly set the `this` value to an object of your choice without calling the function. Pass in n number of arguments delimited by the comma character (,). E.g. `func.bind(this, param1, param2, ...)`. -* [apply()](https://www.w3schools.com/js/js_function_apply.asp): Explicitly set the `this` value to an object of your choice. The second parameter is an array containing all the arguments that you would like to pass to the function. Lastly, **call the function**. -* [call()](https://docs.microsoft.com/en-us/scripting/javascript/reference/call-method-function-javascript): Explicitly set the `this` value to an object of your choice and like `bind`, pass in a number of arguments using the comma delimiter. E.g. `print.call(this, param1, param2, ...)`;. Lastly, **call the function**. +* [bind()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind):不需要执行函数就可以将 `this` 的值准确设置到你选择的一个对象上。还可以通过逗号隔开传递多个参数,如 `func.bind(this, param1, param2, ...)` 。 +* [apply()](https://www.w3schools.com/js/js_function_apply.asp):将 `this` 的值准确设置到你选择的一个对象上。第二个参数是一个数组,数组的每一项是你希望传递给函数的参数。最后,**执行函数**。 +* [call()](https://docs.microsoft.com/en-us/scripting/javascript/reference/call-method-function-javascript):将 `this` 的值准确设置到你选择的一个对象上,然后想 `bind` 一样通过逗号分隔传递多个参数给函数。如:`print.call(this, param1, param2, ...)`。最后,**执行函数**。 -All the built-in functions mentioned above have a commonality in that they are used to make the `this` keyword point to something else. These features enable us to do some really cool things. Unfortunately, the topic is so broad that it will require many posts to cover all of them, so for the sake of brevity, I will not blab on about its application points in this post. +上面提到的所有内置函数都有一个共同点,就是它们都是用来将 `this` 关键词指向到其他地方。这些特性可以让我们玩一些骚操作。只是呢,这个话题太广了都够写好几篇文章了,所以简洁起见,这篇文章我不打算展开它的实际应用。 -**Note**: Out of the three functions mentioned above, only `bind()` does not directly call the function after setting the `this` keyword. +**重点**:上面那三个函数,只有 `bind()` 在设置好 `this` 关键词后不立刻执行函数。 -## When to Use Bind, Call and Apply +## 什么时候用 bind、call 和 apply -You are probably thinking: This is confusing as it is already. What is the purpose of learning all of this? +你可能在想:现在已经很乱了,学习所有这些的目的是什么? -First of all, you will see bind, call and apply used everywhere, especially in large libraries and frameworks. If you don’t understand what it does, you are woefully utilizing only a small part of the power JavaScript offers. +首先,你会看到 bind、call 和 apply 这几个函数到处都会用到,特别是在一些大型的库和框架。如果你没理解它做了些什么,那可怜的你就只用上了 JavaScript 提供的强大能力的一小部分而已。 -If you don’t want to read about possible usages and want to get into learning right away, feel free to skip this section. +如果你不想了解一些可能的用法而想立刻读下去,当然了,你可以直接跳过这节,没关系。 -A lot of the possible usages listed below are very broad and deep topics (most likely unable to be covered in a single post), so if you want to learn more about them, I will attach links. In the future, I might add sections to this ultimate guide, so that people can get the most out of it. +下面列出来的应用场景都是一些具有深度和广度的话题(一篇文章基本上是讲不完的),所以我放了一些链接供你深度阅读用。未来我可能会在这篇终极指南里面继续添加新的小节,这样大家就可以一次看过瘾。 -1. [Borrowing methods](https://medium.com/@thejasonfile/borrowing-methods-from-a-function-in-javascript-713a0beed40d) -2. [Currying](https://www.sitepoint.com/currying-in-functional-javascript/) -3. [Partial application](http://benalman.com/news/2012/09/partial-application-in-javascript/#partial-application) -4. [Dependency injection](http://krasimirtsonev.com/blog/article/Dependency-injection-in-JavaScript) +1. [方法借用](https://medium.com/@thejasonfile/borrowing-methods-from-a-function-in-javascript-713a0beed40d) +2. [柯里化](https://www.sitepoint.com/currying-in-functional-javascript/) +3. [偏函数应用](http://benalman.com/news/2012/09/partial-application-in-javascript/#partial-application) +4. [依赖注入](http://krasimirtsonev.com/blog/article/Dependency-injection-in-JavaScript) -If I am missing any other practical use cases, please leave a message and let me know. I am always out to improve the guides so that you as a reader can get the most out of reading. +如果我漏掉了其他实践案例,请留言告知。我会经常来优化这篇指南,这样你作为读者就可以读到最丰富的内容。 -> Read high quality open source code to take your knowledge and skills to the next level. +> 阅读高质量的开源代码可以升级你的知识和技能。 -Seriously, you will see some real practical application of the this keyword, call, apply, bind in some open source code. I talk about this along with other methods of [becoming a better programmer](https://www.thecodingdelight.com/become-better-programmer/). +讲真,你会在一些开源代码上看到 this 关键词、call、apply 和 bind 的实际应用。我会将这块结合着其他能[帮你成为更好的程序员](https://www.thecodingdelight.com/become-better-programmer/)的方法一起讲。 -In my opinion, the best open source to start reading is [underscore](http://underscorejs.org/). It isn’t monolithic compared to other open source projects like [d3](https://github.com/d3/d3), so it is perfect for educational purposes. Furthermore, it is compact, well-documented and the coding style is relatively easy to follow. +在我看来,开始阅读最好的开源代码是 [underscore](http://underscorejs.org/)。它并不像其他开源项目,如 [d3](https://github.com/d3/d3),那样铁板一块,而是内部代码相互比较独立,因而它是教学用的最佳选择。另外,它代码简洁,文档详细,编码风格也是相当容易学习。 -## JavaScript `this` and bind +## JavaScript 的 `this` 和 bind -As mentioned, `bind` allows you to explicitly set what this points at without actually calling the function. Here is a simple example +前面提到了,`bind` 允许你明确设定 this 的指向而不用实际去执行函数。这里是一个简单示例: ``` var bobObj = { -name: "Bob" + name: "Bob" }; function print() { -return this.name; + return this.name; } -// explicitly set this to point at "bobObj" +// 将 this 明确指向 "bobObj" var printNameBob = print.bind(bobObj); -console.log(printNameBob()); // this points at bob, so logs "Bob" +console.log(printNameBob()); // this 会指向 bob,于是输出结果是 "Bob" ``` -In the example above, if we were to remove the bind method, then this would point at the global `window` object. +在上面的示例中,如果你把 bind 那行去掉,那 this 将会指向全局 `window` 对象。 -This might sound very stupid, but you should use `bind` when you want to bind the `this` object to a specific object. In some cases, we may want to borrow methods from another objects. For example, +这好像很蠢,但在你想将 `this` 绑定到具体对象前你就必须用 `bind` 来绑定。在某些场景下,我们可能想从另一个对象中借用一些方法。举个例子, ``` var obj1 = { -data: [1,2,3], -printFirstData: function() { -if (this.data.length) -return this.data[0]; -} + data: [1,2,3], + printFirstData: function() { + if (this.data.length) + return this.data[0]; + } }; var obj2 = { -data: [4,5,6], -printSecondData: function() { -if (this.data.length > 1) -return this.data[1]; -} + data: [4,5,6], + printSecondData: function() { + if (this.data.length > 1) + return this.data[1]; + } }; -// get access to obj'2 method to use for obj1; +// 在 obj1 中借用 obj2 的方法 var getSecondData = obj2.printSecondData.bind(obj1); -console.log(getSecondData()); // prints 2 +console.log(getSecondData()); // 输出 2 ``` -In the sample code snippet, the `obj2` has a method called `printSecondData` which we want to lend to `obj1`. In the following line +在这个代码片段里,`obj2` 有一个名为 `printSecondData` 的方法,而我们想将这个方法借给 `obj1`。在下一行 ``` var getSecondData = obj2.printSecondData.bind(obj1); ``` -we are using the power of bind to give `obj1` access to the `printSecondData` method on `obj2`. +通过使用 bind ,我们让 `obj1` 可以访问 `obj2` 的 `printSecondData` 方法。 -### Exercise +### 练习 -In the code below +在下面的代码中 ``` var object = { -data: [1,2,3], -double: function() { -this.data.forEach(function() { -// Get this to point to object. -console.log(this); -}); -} + data: [1,2,3], + double: function() { + this.data.forEach(function() { + // Get this to point to object. + console.log(this); + }); + } }; object.double(); ``` -Have the this keyword point at `object`. Hint: You do not have to rewrite `this.data.forEach`. +怎么让 this 关键词指向 `object`。提示:你并不需要重写 `this.data.forEach`。 -##### Answer +##### 答案 -In one of the previous section, we need to be aware of execution context. If you look closely at how the anonymous function is called, it is not called as a method of a object. Therefore, the `this` keyword will point at the global `window` object. +在上一节中,我们了解了执行上下文。如果你对匿名函数调用那部分看得够细心,你就知道它并不会作为某个对象的方法被调用。因此,`this` 关键词指向了全局 `window` 对象。 -Therefore, we need to bind object as the context to the anonymous function so that this points to `object`. now, when `double` runs, `object` is calling it, so the `this` inside of `double` when `object.double` is called is `object`. +于是我们需要将 object 作为上下文绑定到匿名函数上,使得里面的 this 指向 `object`。现在,`double` 函数跑起来时,是 `object` 调用了它,那么 `double` 里面的 `this` 指向 `object`。 ``` var object = { -data: [1,2,3], -double: function() { -return this.data.forEach(function() { -// Get this to point to object. -console.log(this); -}.bind(this)); -} + data: [1,2,3], + double: function() { + return this.data.forEach(function() { + // Get this to point to object. + console.log(this); + }.bind(this)); + } }; object.double(); ``` -However, what happens if we do the following? +那,如果我们像下面这样做呢? ``` var double = object.double; -double(); // ?? +double(); // ?? ``` -What is the call context of `double()`? It is the global context. Therefore, we will get the following error. +`double()` 的调用上下文是什么?是全局上下文。于是,我们就会看到下面的报错。 > `Uncaught TypeError: Cannot read property 'forEach' of undefined` > `at double (test.html:282)` > `at test.html:289` -So, we need to be mindful of how we call functions when using the `this` keyword. We can reduce the possibility of this kind of error by providing an API to users that fixes the this keyword. Remember that this comes at the expense of flexibility, so count the costs before making a decision. +所以,当我们用到 `this` 关键词时,就要小心在意我们调用函数的方式。我们可以在提供 API 给用户时固定 this 关键词,以此减少这种类型的错误。但请记住,这么做的代价是牺牲了灵活性,所以做决定前要考虑清楚。 ``` var double = object.double.bind(object); -double(); // no more error +double(); // 不再报错 ``` -## JavaScript `this` and call +## JavaScript `this` 和 call -The call method is very similar to bind, but the big difference is that `call`, as the name implies, immediately calls/executes the function. +call 方法和 bind 很相似,但就如它名字所暗示的,`call` 会立刻呼起(执行)函数,这是两个函数的最大区别。 ``` var item = { -name: "I am" + name: "I am" }; function print() { -return this.name; + return this.name; } -// executed right away +// 立刻执行 var printNameBob = console.log(print.call(item)); ``` -Most of the use cases with `call`, `apply` and `bind` will overlap. The most important thing as a programmer is to first understand the differences between the three methods and use them according to their design and purpose. Once you understand, you will be able to use them creatively in your code to create powerful constructs. +`call`、`apply`、`bind` 大部分使用场景是重叠的。作为一个程序员最重要的还是先了解清楚这三个方法之间的差异,从而能根据它们的设计和目的的不同来选用。只要你了解清楚了,你就可以用一种更有创意的方式来使用它们,写出更独到精彩的代码。 -When working with fixed amount of arguments, it is good to use `call` or `bind`. For example, a `doLogin` function will accept two arguments always: `username` and `password`. In this case, if you need to bind this to a specific object, `call` or `bind` will serve you well. +在参数数量固定的场景,`call` 或 `bind` 是不错的选择。比如说,一个叫 `doLogin` 的函数经常是接受两个参数:`username` 和 `password`。在这个场景下,如果你需要将 this 绑定到一个特定的对象上,`call` 或 `bind` 会挺好用的。 -### How to use call +### 如何使用 call -One of the most common uses in the past was to convert array-like objects such as the `arguments` object into arrays. For example, +以前一个最常用的场景是把一个类数组对象,比如 `arguments` 对象,转化成数组。举个例子: ``` function convertArgs() { -var convertedArgs = Array.prototype.slice.call(arguments); -console.log(arguments); -console.log(Array.isArray(arguments)); // false -console.log(convertedArgs); -console.log(Array.isArray(convertedArgs)); // true + var convertedArgs = Array.prototype.slice.call(arguments); + console.log(arguments); + console.log(Array.isArray(arguments)); // false + console.log(convertedArgs); + console.log(Array.isArray(convertedArgs)); // true } convertArgs(1,2,3,4); ``` -In the example above, we used call to convert the `argument` object into an array. In the next example, we will call a method available on the `Array` object, setting the argument object as the this in its method to add up the arguments passed. +在上面的例子中,我们使用 call 将 `argument` 对象转化成一个数组。在下一个例子中,我们将会调用一个 `Array` 对象的方法,并将 argument 对象设置为方法的 this,以此来将传进来参数加在一起。 ``` function add (a, b) { -return a + b; + return a + b; } function sum() { -return Array.prototype.reduce.call(arguments, add); + return Array.prototype.reduce.call(arguments, add); } console.log(sum(1,2,3,4)); // 10 ``` -We are calling reduce on an array like object. Note that arguments is not an array, but we are giving it access to the reduce method. If you are curious about how reduce works, you can [read about reduce here](https://www.thecodingdelight.com/map-filter-reduce/). +我们在一个类数组对象上调用了 reduce 函数。要知道 arguments 不是一个数组,但我们给了它调用 reduce 方法的能力。如果你对 reduce 感兴趣,可以在[这里了解更多](https://www.thecodingdelight.com/map-filter-reduce/)。 -### Exercises +### 练习 -Now, it is time to solidify your newfound knowledge. +现在是时候巩固下你新学到的知识。 -1. [document.querySelectorAll()](https://www.w3schools.com/jsref/met_document_queryselectorall.asp) returns a `NodeList`, which is an array like object. Write a function that takes a CSS selector and returns an array of Nodes selected. -2. Create a function that accepts an array of key value pairs and sets the value to the item that this keyword is pointing at and return that object. If this is `null` or `undefined`, create a new `object`. E.g. `set.call( {name: "jay"}, {age: 10, email: '[[email protected]](/cdn-cgi/l/email-protection)'}); // return {name: "jay", age: 10, email: '[[email protected]](/cdn-cgi/l/email-protection)'}` +1. [document.querySelectorAll()](https://www.w3schools.com/jsref/met_document_queryselectorall.asp) 返回一个类数组对象 `NodeList`。请写一个函数,它接收一个 CSS 选择器,然后返回一个选择到的 DOM 节点数组。 +2. 请写一个函数,它接收一个由键值对组成的数组,然后将这些键值对设置到 this 关键词指向的对象上,最后将该对象返回。如果 this 是 `null` 或 `undefined`,那就新建一个 `object`。示例:`set.call( {name: "jay"}, {age: 10, email: '[[email protected]](/cdn-cgi/l/email-protection)'}); // return {name: "jay", age: 10, email: '[[email protected]](/cdn-cgi/l/email-protection)'}`。 -## JavaScript this and apply +## JavaScript this 和 apply -The apply is the array accepting version of call. Therefore, when using `apply`, think of arrays. +apply 就是接受数组版本的 call。于是当使用 `apply` 时,多联想下数组。 -> Apply a method to a list. +> 将一个方法应用(apply)到一个数组上。 -That is how I remember it and it has helped. Apply adds another plethora of possibilities to your already stacked arsenal of tools as you will soon come to see. +我用这句话来记住它,而且还挺管用。apply 为你的现有堆积的军火库又添加了一样利器,增加了很多新的可能,你很快就能体会到这一点。 -When working with a dynamic list of arguments, use apply. Converting a set of data into an array and using apply can allow you to create some powerful and flexible functions that will make your life a lot easier. +当你要处理参数数量动态变化的场景,用 apply 吧。将一系列数据转化为数组并用上 apply 能让你写出更好用和更具弹性的代码,会让你的工作更轻松。 -### How to use apply +### 如何使用 apply -[Math.min](https://www.w3schools.com/jsref/jsref_min.asp) and `max` are functions that accept n number of arguments and returns the max and min respectively. Instead of passing in n arguments, you can put n arguments into an array and pass it into min using `apply`. +[Math.min](https://www.w3schools.com/jsref/jsref_min.asp) 和 `max` 都是可以接受多个参数并返回最小值和最大值的函数。除了直接传 n 个参数,你也可以将这 n 个参数放到一个数组里然后借助 `apply` 将它传到 min 函数里。 ``` -Math.min(1,2,3,4); // returns 1 -Math.min([1,2,3,4]); // returns NaN. Only accepts numbers. -Math.min.apply(null, [1,2,3,4]); // returns 1 +Math.min(1,2,3,4); // 返回 1 +Math.min([1,2,3,4]); // 返回 NaN。只接受数字 +Math.min.apply(null, [1,2,3,4]); // 返回 1 ``` -Did that bend your mind? If so, allow me to explain. By using apply, we are passing in an array, since it accepts an array as the second arguments. What +看晕了吗?如果真晕了,那我来解释下。使用 apply 时我们要传一个数组因为它需要数组作为第二个参数。而下面 ``` -Math.min.apply(null, [1,2,3,4]); // returns 1 +Math.min.apply(null, [1,2,3,4]); // 返回 1 ``` -is doing is essentially the following +做的事情基本等同于 -`Math.min(1,2,3,4); // returns 1 +`Math.min(1,2,3,4); // 返回 1 ` -That is the magic of apply and what i wanted to point out. It works the same way as `call`, but instead of n arguments, we are just passing in an array. Fantastic right? Wait, does that mean `Math.min.call(null, 1,2,3,4);` works the same way as `Math.min.apply(null, [1,2,3,4]);`? +这就是我想指出来的 apply 的神奇之处。它和 `call` 工作原理,不过我们只要传给它一个数组而不是 n 个参数。很好玩对吧?桥豆麻袋,这是否意味着 `Math.min.call(null, 1,2,3,4);` 执行起来和 `Math.min.apply(null, [1,2,3,4]);` 一样? -Yep, you bet! You are now starting to get the hang of it 🙂 +啊,你说对了!看来你已经开始掌握它了 🙂 -Let’s look at another application. +让我们来看下另一种用法。 ``` function logArgs() { -console.log.apply(console, arguments); + console.log.apply(console, arguments); } logArgs(1,3,'I am a string', {name: "jay", age: "1337"}, [4,5,6,7]); ``` -Yep, you can even pass in array like objects as the second argument to `apply`. Cool right? +没错,你甚至可以传一个类数组对象作为 `apply` 的第二个参数。很酷对吧? -### Exercises +### 练习 -1. Create a function that accepts an array of key value pairs and sets the value to the item that this keyword is pointing at and return that object. If this is `null` or `undefined`, create a new `object`. E.g. `set.apply( {name: "jay"}, [{age: 10}]); // return {name: "jay", age: 10}` -2. Create a function similar to `Math.max` and `min`, but one one that applies calculations. The first two arguments should be `numbers`. Make sure to convert the arguments after the second into an **array of functions.** A sample template to get started with is provided below +1. 写一个函数,它接受一个由键值对组成的数组,然后将这些键值对设置到 this 关键词指向的对象上,最后将该对象返回。如果 this 是 `null` 或 `undefined`,那就新建一个 `object`。示例:`set.apply( {name: "jay"}, [{age: 10}]); // 返回 {name: "jay", age: 10}` +2. 写一个类似 `Math.max` 和 `min` 的函数,不过接收的不是数字而是运算。前两个参数必须是`数字`,而后面的参数你要将其转化为一个**函数数组**。下面提供一个方便你上手理解的示例: ``` function operate() { -if (arguments.length < 3) { -throw new Error("Must have at least three arguments"); -} -if (typeof arguments[0] !== 'number' || typeof arguments[1] !== 'number') { -throw new Error("first two arguments supplied must be a number"); -} -// Write code ... -// An array of functions. Hint use either call, apply or bind. Don't iterate over arguments and place functions in new array. -var args; -var result = 0; -// Good luck + if (arguments.length < 3) { + throw new Error("至少要三个参数"); + } + if (typeof arguments[0] !== 'number' || typeof arguments[1] !== 'number') { + throw new Error("前两个参数必须是数字"); + } + // 写代码 + // 这是一个由函数组成的数组。你可以用 call、apply 或者 bind。但不要直接遍历参数然后直接塞到一个数组里 + var args; + var result = 0; + // 好了,开始吧,祝好运 } function sum(a, b) { -return a + b; + return a + b; } function multiply(a,b) { -return a * b; + return a * b; } -console.log(operate(10, 2, sum, multiply)); // must return 32 -> (10 + 2) + (10 * 2) = 32 +console.log(operate(10, 2, sum, multiply)); // 必须返回 32 -> (10 + 2) + (10 * 2) = 32 ``` -## Additional Resource and Readings +## 其他文章和资料 -In case my explanations did not make sense to you, below are some additional resources that will help you understand how bind works in JavaScript. +假如我上面的解释没能让你释疑,那下面这些额外的资料可以帮你更好地理解 bind 在 JavaScript 里面是怎么运作的。 -* [Understanding JavaScript function bind prototype](https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/) -* [Stackoverflow – Use of the JavaScript bind method](https://stackoverflow.com/questions/2236747/use-of-the-javascript-bind-method) -* [How-to: call() , apply() and bind() in JavaScript](https://www.codementor.io/niladrisekhardutta/how-to-call-apply-and-bind-in-javascript-8i1jca6jp) -* [JavaScript .call() .apply() and .bind() — explained to a total noob](https://medium.com/@owenyangg/javascript-call-apply-and-bind-explained-to-a-total-noob-63f146684564) +* [理解 JavaScript 函数 bind 的原型方法](https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/) +* [Stackoverflow – 使用 JavaScript 的 bind 函数](https://stackoverflow.com/questions/2236747/use-of-the-javascript-bind-method) +* [JavaScript 中 call(), apply() 和 bind() 如何使用](https://www.codementor.io/niladrisekhardutta/how-to-call-apply-and-bind-in-javascript-8i1jca6jp) +* [一看就懂 —— JavaScript 的 .call() .apply() 和 .bind()](https://medium.com/@owenyangg/javascript-call-apply-and-bind-explained-to-a-total-noob-63f146684564) -I also strongly recommend studying up on [JavaScript’s prototype chain](https://www.digitalocean.com/community/tutorials/understanding-prototypes-and-inheritance-in-javascript), because not only is the `this` key word used heavily, it is the standard way of implementing inheritance in JavaScript. +我还强烈推荐你去学习 [JavaScript 原型链](https://www.digitalocean.com/community/tutorials/understanding-prototypes-and-inheritance-in-javascript),不单是因为里面用到大量的 `this` 关键词,而且它还是 JavaScript 实现继承的标准方式。 -Below are a list of books that will take your knowledge and understanding of how `this` can be used. +下面列出一些帮你了解 `this` 如何使用的书籍: -* [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript (Effective Software Development Series)](http://amzn.to/2HGhsDP): Although an oldie, the book is well written and provides clear examples of how this, apply, call, bind can be used to improve the way that you write code. The book is written by Dave Hermann a member of [TC39](https://www.ecma-international.org/memento/TC39-M.htm), so you can bet that he knows his JavaScript! -* [You dont’ know JS – this and Object Prototoypes](https://github.com/getify/You-Dont-Know-JS/tree/master/this%20%26%20object%20prototypes): Kyle Simpson does a great job in explaining how objects and prototypes work with each other in a clear, relatively beginner-friendly manner. +* [编写高质量 JavaScript代码的68个有效方法](http://amzn.to/2HGhsDP):虽然是本古董,但此书确实写得挺好而且还提供了简单易懂的示例,教你怎么用好 this、apply、call 和 bind 来写出好代码。书的作者是 [TC39](https://www.ecma-international.org/memento/TC39-M.htm) 的一个成员 Dave Hermann,所以你大可放心,他对 JavaScript 肯定理解深刻。 +* [你不知道的 JS —— this 和对象原型](https://github.com/getify/You-Dont-Know-JS/tree/master/this%20%26%20object%20prototypes):Kyle Simpson 以一种清晰明了、对初学者很友好的方式,解释了对象和原型是怎么相互影响运作起来的,写得很棒! -## Conclusion +## 总结 -The JavaScript `this` keyword is here to stay, considering that an unimaginably large amount of code has already been written using it. +考虑到 `this` 关键词已经用到了难以计量的代码中,它是 JavaScript 中我们不得不聊的话题。 -A good artisan knows how to use his/her tools. As a JavaScript developer, it is of utmost importance that you know how to utilize its features. +一个优秀的艺术家肯定精于工具的使用。作为一个 JavaScript 开发者,怎么用好它的特性是最最重要的。 -If you would like to see more in-depth explanation regarding a specific aspect of the `this` keyword, or more code, please let me know. Some possible options include posts on the following (but not limited to) +如果你想看到一些从特定角度对 `this` 关键词深入剖析的文章或者更多的代码,请别忘了告诉我。这些可能的角度可以是(但不限于)下面这些: -* `this` and the `new` keyword. -* The prototype chain in JavaScript. -* `this` and JavaScript classes. +* `this` 和 `new` 关键词。 +* JavaScript 的原型链。 +* `this` 和 JavaScript 的类。 -Additionally, if there are any specific issues or additions that you would like to see in this post, please email me or send me a message. I just updated [my GitHub profile](https://github.com/JWLee89) to display my email address. I am looking forward to building up this guide so that readers will continue to benefit from it, regardless of their level of experience. Let’s partake in this journey together! +另外,关于这篇文章你如果有什么具体的问题或补充,请给我发邮件或信息。我刚在[我的 Github 个人主页](https://github.com/JWLee89)更新了我的邮箱地址。我希望将这个教程完善起来,这样不管哪个级别的开发者看到它都能从中受益。让我们一起把它做好! -Thank you for reading and looking forward to hearing ideas or suggestions on what to add to this guide so that readers get the most out of it. +多谢捧场了老铁,然后,这篇文章还能再补充点什么对读者有用的东西,我真的很期待听到你的观点和建议。 -Take care and until next time! +保重,下次见! -### About the Author [Jay](https://www.thecodingdelight.com/author/ljay189/) +### 关于作者 [Jay](https://www.thecodingdelight.com/author/ljay189/) -I am a programmer currently living in Seoul, South Korea. I created this blog as an outlet to express what I know / have been learning in text form for retaining knowledge and also to hopefully help the wider community. I am passionate about data structures and algorithms. The back-end and databases is where my heart is at. +我是一个现居韩国首尔的程序员。我创立这个博客的目的,就是想用文字形式将所学所想沉淀下来,也希望为社区做些贡献。我热衷于数据结构和算法,而后台和数据库则是我心中最爱。 --- From 3d76eb74fb7c4e6aaafcbdb9cf66478b7de5d11e Mon Sep 17 00:00:00 2001 From: LeviDing Date: Mon, 7 May 2018 10:42:51 +0800 Subject: [PATCH 010/446] Create sharing-databases-between-laravel-applications.md --- ...-databases-between-laravel-applications.md | 190 ++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 TODO1/sharing-databases-between-laravel-applications.md diff --git a/TODO1/sharing-databases-between-laravel-applications.md b/TODO1/sharing-databases-between-laravel-applications.md new file mode 100644 index 00000000000..f92d09a3b8b --- /dev/null +++ b/TODO1/sharing-databases-between-laravel-applications.md @@ -0,0 +1,190 @@ +> * 原文地址:[Sharing databases between Laravel applications](https://dyrynda.com.au/blog/sharing-databases-between-laravel-applications) +> * 原文作者:[Michael Dyrynda](https://dyrynda.com.au/) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/sharing-databases-between-laravel-applications.md](https://github.com/xitu/gold-miner/blob/master/TODO1/sharing-databases-between-laravel-applications.md) +> * 译者: +> * 校对者: + +# Sharing databases between Laravel applications + +### Introduction + +If you happen to follow me on [Twitter](https://twitter.com/michaeldyrynda), you may have seen me tweeting about some of the work I'm doing in my day job. We have a customer-facing members area and an internal CRM that both work with the same main database. + +The CRM was built before I started working at my now-employer, whilst the members area was built by me as a contractor in the early part of 2017\. The members area itself was a fresh Laravel application, whilst the CRM is a totally custom-written piece of software. + +As a contractor, I had a sanitised copy of the database, and I managed to reverse engineer the Eloquent models from the database schema, creating [factories](https://laravel.com/docs/5.6/database-testing#writing-factories) along the way, in order to be able to write tests for the members application. + +In late 2017, we started migrating our CRM to Laravel as well, in order to modernise the code base a bit, give it a standard structure, and make it easy to make changes to it moving forward. Now that we had two Laravel applications, we started looking at how best to share data between them. + +### Eloquent models + +Database models are the easiest part to deal with. For this, we simply created models for each of the shared database tables, created a package, and pulled them into our apps as a [vcs repository](https://getcomposer.org/doc/05-repositories.md#loading-a-package-from-a-vcs-repository) with Composer. This allowed us to share the models, without exposing them publicly via Packagist. + +The models in this package each extend from their own base model, which sets the connection per database, and contain the minimal amount of logic to tie them all together. + +We try and keep the package models to just the relationships between each other, and any generic methods or behaviours; the idea being that each application using them would then extend from them and implement their own specific logic as needed. + +### Migrations + +Migrations are where things start getting a little bit tricky. Whilst we have a database that is technically owned by the CRM application, the migrations should be available to any application that will access the data in it. The question then became, "which application is responsible for managing the database schema?" + +Laravel ships with multiple database connections in the `config/database.php` file, showing you the availability of various drivers. We simply define multiple connections that all use the `mysql` driver. + +We had a few requirements for managing the database schema: + +1. No single application using the databases should be responsible for managing migrations +2. The migrations should be able to be used for testing purposes +3. We would like to use Laravel's migration functionality, if possible + +The first two requirements are fairly straightforward, assuming we could solve the third in some way. + +### A standalone utility + +I spent quite some time putting together an Artisan-like utility that focuses solely on the migration and database seeding functionality - [Nomad](https://github.com/michaeldyrynda/nomad). Nomad can be pulled into a standalone Composer project - such as [Vagabond](https://github.com/michaeldyrynda/vagabond), in order to manage database migrations for many applications. + +The Vagabond project is subsequently used as a package that you can pull into your applications - as a VCS repository - and using a service provider, instruct Laravel to load its migrations, in addition to any migrations that might exist in the application that uses it. + +``` +// In your Vagabond project's service provider +public function boot() +{ + $this->loadMigrationsFrom(dirname(__FILE__).'/../database/migrations'); +} +``` + +### Nomad in practice + +The first problem we encountered with the Nomad approach, was that if you don't specify the connection migrations ought to run against in your migration files, they will all run against your default connection. + +``` +// In your migration file +public function up() +{ + Schema::connection('the_connection')->create('table', function (Blueprint $table) + { + // + } +} +``` + +The second problem, was that although the Laravel application would run the migrations against the correct connection, it would track _all_ of the migrations against the default connection i.e. if you ran migrations for three different connections, they resulting migration history would all be tracked in the `migrations` table of your application's default connection. + +Why is this a problem? If your database user had sufficient privileges, it would try and run the same migrations over and over on a database where those tables already existed. + +This issue is compounded if you have many different applications all using the centralised migration files, trying to run the same migrations each time. + +In order to solve this problem, we created folders for each connection's migrations within the migration project's `database/migrations` folder. + +``` +database/migrations/ + /crm + /gis + /coverage +``` + +In doing so, we could now use the `path` and `database` arguments for various migration commands, allowing us to explicitly run migrations per-connection: `php nomad migrate --database=gis --path=database/migrations/gis`. This ensured that only the `gis` migrations were run, and that the history for the run migrations was tracked in the `gis` database's `migrations` table. + +This now solved requirements 1 and 3; we were now using Laravel-style migrations from a standalone repository _and_ we had a standalone application capable of running the migrations. This means we can run migrations for a specific database connection anywhere the code a) had access to the database server and b) had a user with sufficient privileges. + +### Using the shared migrations and models in testing + +Another problem we encountered along the way was in running tests. + +In our test environment, we use Laravel's [`RefreshDatabase`](https://laravel.com/docs/5.6/database-testing#resetting-the-database-after-each-test) trait, which intelligently handles building up and tearing down your entire database for each test. At the time of writing, however, whilst it runs all of the migrations correctly, _it only drops tables on the default database connection_. + +This means if we have tests for an application using its own database as well as one of the shared ones, each test will fail as Laravel tries to run the migrations for a connection that was not dropped. There is a [solution](https://github.com/laravel/framework/issues/21063#issuecomment-360616841) to this problem by [Sepehr Lajevardi](https://twitter.com/sepehrlajevardi), which was pointed out to me by [Keith Damiani](https://twitter.com/keithdamiani). + +The trait in Sepehr's suggestion overwrites Laravel's default `refreshTestDatabase` method with one that looks for a property containing an array of connections to drop tables from. + +### Database configuration + +Now that you have your models and migrations all packaged up in their own repository, the last thing you don't want to have to. manually duplicate from project to project is the configuration itself. + +Laravel actually makes it pretty easy to merge configurations from third-party packages into the main configuration. In our production application, our database configuration has absolutely _no_ connections configured in it. + +Instead, this functionality lives inside each database connection's service provider. We have a top-level provider that each extends from, and by default each provider need only define a single protected property; `$connectionName`. + +You can see a standalone sample of this functionality [here](https://gist.github.com/michaeldyrynda/381024012249661a52fed7351c3e39a5). + +All you need to do in your application is add the service provider to your `config/app.php` file's providers array and define the necessary environment variables for each connection. + +### Continuous Integration + +The very last piece of this puzzle for us is to have the tests running in a CI-pipeline. For us, this is [BitBucket](https://bitbucket.org). + +As our existing database contains a lot of `ENUM` fields (I don't recommend using them, not least of all because they are not supported by the library - `doctrine/dbal` - that Laravel uses for its migration functionality), we had to use MySQL in our test environment. + +Using containers in our CI pipeline makes it easy spin up a MySQL service, however, it is not immediately obvious how you would configure multiple databases. As the MariaDB image we're using doesn't let you specify the port it binds to, multiple database services all try to listen on the same port (3306) and subsequently fail to start, causing the test suite to fail. + +The solution is so simple that it was overlooked initially; use the MySQL client to create the databases before the test suite runs. + +Your `bitbucket-pipelines.yml` file ought to look like the following: + +``` +image: php:7.1.15 + +pipelines: + default: + - step: + deployment: test + caches: + - composer + script: + - apt-get update && apt-get install -y unzip git mysql-client + - docker-php-ext-install pdo_mysql + - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer + - cp -n .env.example .env + - export DB_USERNAME=root + - export DB_DATABASE=first_db + - export DB_PASSWORD=supersecret + - export DB_SECOND_USERNAME=root + - export DB_SECOND_DATABASE=second_db + - export DB_SECOND_PASSWORD=supersecret + - export DB_THIRD_USERNAME=root + - export DB_THIRD_DATABASE=third_db + - export DB_THIRD_PASSWORD=supersecret + - composer install + - php artisan key:generate + - mysql -uroot -psupersecret -h127.0.0.1 -e 'create database second_db; create database third_db;' + - vendor/bin/phpunit --colors=always -c phpunit.xml + services: + - mariadb +definitions: + services: + mariadb: + image: mariadb:5.5 + environment: + MYSQL_DATABASE: 'first_db' + MYSQL_ROOT_PASSWORD: 'supersecret' +``` + +The `export` lines set the database configuration for each of the three databases that our app interacts with. We let the MariaDB service configure the first database using the `MYSQL_DATABASE` environment variable, then use the MySQL client to create `second_db` and `third_db`. + +> The `MYSQL_ROOT_PASSWORD` variable is defined as a static string as I couldn't figure out how to get a random password injected into the deploy step, but if you know how to do so let me know! + +### Conclusion + +If you ever find yourself needing to work with applications that share two or more databases, I hope you've learnt something about managing and working with them in this post. + +A lot has been covered: + +* packaging models and migrations in a standalone [Vagabond](https://github.com/michaeldyrynda/vagabond) project +* running migrations as a standalone application with [Nomad](https://github.com/michaeldyrynda/nomad) +* dealing with multiple database connections in tests +* successfully running your tests using [BitBucket Pipelines](https://bitbucket.org/product/features/pipelines) with multiple databases + +One consideration we'll have to make as a result of this decoupling of application from database, is how and when migrations should be run, given we'll need to now do this as a separate operation. It will, of course vary on a case by case basis, and we'll need to be sure to test against each application to ensure no breaking changes were introduced to the database. + +It took me a couple of months to get this to a working state, so I hope that I'm able to save you some time should you find yourself in my shoes at some point in the future! + +Thanks to [Keith Damiani](https://twitter.com/keithdamiani) and [Sepehr Lajevardi](https://twitter.com/sepehrlajevardi) for pointing out one of my last missing puzzle pieces, + +[Jake Bennett](https://twitter.com/jacobbennett) and I discussed this migration behaviour in [episode 43](http://www.northmeetssouth.audio/43) of the North Meets South web podcast. + +If you have any questions about anything covered in this post, or suggestions for improvement, feel free to [reach out](https://twitter.com/michaeldyrynda) on Twitter. + + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 0a412bf8911ecc4225b4e784ed45ce557a6574d3 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Mon, 7 May 2018 10:44:08 +0800 Subject: [PATCH 011/446] Create how-to-easily-detect-objects-with-deep-learning-on-raspberrypi.md --- ...jects-with-deep-learning-on-raspberrypi.md | 474 ++++++++++++++++++ 1 file changed, 474 insertions(+) create mode 100644 TODO1/how-to-easily-detect-objects-with-deep-learning-on-raspberrypi.md diff --git a/TODO1/how-to-easily-detect-objects-with-deep-learning-on-raspberrypi.md b/TODO1/how-to-easily-detect-objects-with-deep-learning-on-raspberrypi.md new file mode 100644 index 00000000000..f948ba305c3 --- /dev/null +++ b/TODO1/how-to-easily-detect-objects-with-deep-learning-on-raspberrypi.md @@ -0,0 +1,474 @@ +> * 原文地址:[How to easily Detect Objects with Deep Learning on Raspberry Pi](https://medium.com/nanonets/how-to-easily-detect-objects-with-deep-learning-on-raspberrypi-225f29635c74) +> * 原文作者:[Sarthak Jain](https://medium.com/@sarthakjain?source=post_header_lockup) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/how-to-easily-detect-objects-with-deep-learning-on-raspberrypi.md](https://github.com/xitu/gold-miner/blob/master/TODO1/how-to-easily-detect-objects-with-deep-learning-on-raspberrypi.md) +> * 译者: +> * 校对者: + +# How to easily Detect Objects with Deep Learning on Raspberry Pi + +## The real world poses challenges like having limited data and having tiny hardware like Mobile Phones and Raspberry Pis which can’t run complex Deep Learning models. This post demonstrates how you can do object detection using a Raspberry Pi. Like cars on a road, oranges in a fridge, signatures in a document and teslas in space. + +Disclaimer: I’m building [nanonets.com](https://nanonets.com/objectdetection/?utm_source=medium.com&utm_medium=content&utm_campaign=How%20to%20easily%20Detect%20Objects%20with%20Deep%20Learning%20on%20RaspberryPi&utm_content=top) to help build ML with less data and no hardware + +> _If you’re impatient scroll to the bottom of the post for the Github Repos_ + +![](https://cdn-images-1.medium.com/max/800/1*YJbdykJRHFlzlIXWwn0nIA.gif) + +Detecting Vehicles on the Road of Mumbai. + +### Why Object Detection?, Why Raspberry Pi? + +The raspberry pi is a neat piece of hardware that has captured the hearts of a generation with ~15M devices sold, with hackers building even [cooler projects](http://www.trustedreviews.com/opinion/best-raspberry-pi-projects-pi-3-pi-zero-2949390) on it. Given the popularity of Deep Learning and the [Raspberry Pi Camera](https://www.raspberrypi.org/products/camera-module-v2/) we thought it would be nice if we could detect any object using Deep Learning on the Pi. + +Now you will be able to detect a photobomber in your selfie, someone entering Harambe’s cage, where someone kept the Sriracha or an Amazon delivery guy entering your house. + +![](https://cdn-images-1.medium.com/max/1000/1*rqB2c-c3yz09CtQLWb2dFg.png) + +### What is Object Detection? + +20Myears of evolution have made human vision fairly evolved. The human brain has [30**_%_** of it’s Neurons work on processing vision (as compared with 8 percent for touch and just 3 percent for hearing)](http://discovermagazine.com/1993/jun/thevisionthingma227). Humans have two major advantages when compared with machines. One is stereoscopic vision, the second is an almost infinite supply of training data (an infant of 5 years has had approximately 2.7B Images sampled at 30fps). + +![](https://cdn-images-1.medium.com/max/800/1*4tPwx3wG720gOmIOaONOEQ.jpeg) + +To mimic human level performance scientists broke down the visual perception task into four different categories. + +1. **Classification**, assigns a label to an entire image +2. **Localization**, assigns a bounding box to a particular label +3. **Object Detection**, draws multiple bounding boxes in an image +4. **Image segmentation**, creates precise segments of where objects lie in an image + +Object detection has been good enough for a variety of applications (even though image segmentation is a much more precise result, it suffers from the complexity of creating training data. It typically takes a human annotator 12x more time to segment an image than draw bounding boxes; this is more anecdotal and lacks a source). Also, after detecting objects, it is separately possible to segment the object from the bounding box. + +#### Using Object Detection: + +Object detection is of significant practical importance and has been used across a variety of industries. Some of the examples are mentioned below: + +![](https://cdn-images-1.medium.com/max/800/1*ZUGVScHbBgmmzO82bALIZQ.jpeg) + +### How do I use Object Detection to solve my own problem? + +Object Detection can be used to answer a variety of questions. These are the broad categories: + +1. **Is an object present** in my Image or not? eg is there an intruder in my house +2. **Where is an object** in the image? eg when a car is trying to navigate it’s way through the world, its important to know where an object is. +3. **How many objects** are there in an image? Object detection is one of the most efficient ways of counting objects. eg How many boxes in a rack inside a warehouse +4. **What are the different types of objects** in the Image? eg Which animal is there in which part of the Zoo? +5. **What is the size of an object?** Especially with a static camera, it is easy to figure out the size of an object. eg What is the size of the Mango +6. **How are different objects interacting with each other?** egHow does the formation on a football field effect the result? +7. **Where is an object with respect to time (Tracking an Object). eg** Tracking a moving object like a train and calculating it’s speed etc. + +### Object Detection in under 20 Lines of Code + +![](https://cdn-images-1.medium.com/max/800/1*I4vKwR9X33DoNz36I1IooQ.jpeg) + +YOLO Algorithm Visualized. + +There are a variety of models/architectures that are used for object detection. Each with trade-offs between speed, size, and accuracy. We picked one of the most popular ones: [YOLO](https://pjreddie.com/darknet/yolo/) (You only look once). and have shown how it works below in under 20 lines of code (if you ignore the comments). + +**Note: This is pseudo code, not intended to be a working example. It has a black box which is the CNN part of it which is fairly standard and shown in the image below.** + +You can read the full paper here: [https://pjreddie.com/media/files/papers/yolo_1.pdf](https://pjreddie.com/media/files/papers/yolo_1.pdf) + +![](https://cdn-images-1.medium.com/max/800/1*hV1SLRRZ-5ySyARb2P0uXA.png) + +Architecture of the Convolutional Neural Network used in YOLO. + +``` +#this is an Image of size 140x140. We will assume it to be black and white (ie only one channel, it would have been 140x140x3 for rgb) +image = readImage() + +#We will break the Image into 7 coloumns and 7 rows and process each of the 49 different parts independently +NoOfCells = 7 + +#we will try and predict if an image is a dog, cat, cow or wolf. Therfore the number of classes is 4 +NoOfClasses = 4 +threshold = 0.7 + +#step will be the size of step to take when moving across the image. Since the image has 7 cells step will be 140/7 = 20 +step = height(image)/NoOfCells + +#stores the class for each of the 49 cells, each cell will have 4 values which correspond to the probability of a cell being 1 of the 4 classes +#prediction_class_array[i,j] is a vector of size 4 which would look like [0.5 #cat, 0.3 #dog, 0.1 #wolf, 0.2 #cow] +prediction_class_array = new_array(size(NoOfCells,NoOfCells,NoOfClasses)) + +#stores 2 bounding box suggestions for each of the 49 cells, each cell will have 2 bounding boxes, with each bounding box having x, y, w ,h and c predictions. (x,y) are the coordinates of the center of the box, (w,h) are it's height and width and c is it's confidence +predictions_bounding_box_array = new_array(size(NoOfCells,NoOfCells,NoOfCells,NoOfCells)) + +#it's a blank array in which we will add the final list of predictions +final_predictions = [] + +#minimum confidence level we require to make a prediction +threshold = 0.7 + +for (i<0; i predictions_bounding_box_array[i,j,1, 4] else 1] + + # we will get the class which has the highest probability, for [0.5 #cat, 0.3 #dog, 0.1 #wolf, 0.2 #cow], 0.5 is the highest probability corresponding to cat which is at position 0. So index_of_max_value will return 0 + predicted_class = index_of_max_value(prediction_class_array[i,j]) + + #we will check if the prediction is above a certain threshold (could be something like 0.7) + if predictions_bounding_box_array[i,j,best_bounding_box, 4] * max_value(prediction_class_array[i,j]) > threshold: + + #the prediction is an array which has the x,y coordinate of the box, the height and the width + prediction = [predictions_bounding_box_array[i,j,best_bounding_box, 0:4], predicted_class] + + final_predictions.append(prediction) + + +print final_predictions +``` + +YOLO in <20 lines of code, explained. + +### How do we build a Deep Learning model for Object Detection? + +#### The workflow for Deep Learning has 6 Primary Steps Broken into 3 Parts + +1. Gathering Training Data +2. Training the model +3. Predictions on New Images + +![](https://cdn-images-1.medium.com/max/800/1*hUOIe8skkgMQx68-279z_A.jpeg) + +* * * + +### Phase 1 — Gather Training Data + +#### **Step 1. Collect Images (at least 100 per Object):** + +For this task, you probably need a few 100 Images per Object. Try to capture data as close to the data you’re going to finally make predictions on. + +![](https://cdn-images-1.medium.com/max/800/1*ZqUXpif7jgmAsIwX7ZFdrQ.png) + +#### **Step 2. Annotate (draw boxes on those Images manually):** + +Draw bounding boxes on the images. You can use a tool like [labelImg](https://github.com/tzutalin/labelImg). You will typically need a few people who will be working on annotating your images. This is a fairly intensive and time consuming task. + +![](https://cdn-images-1.medium.com/max/800/1*osRdxUvKXSaOHX-9VyGbCQ.png) + +* * * + +### Phase 2 — Training a Model on a GPU Machine + +#### **Step 3. Finding a Pretrained Model for Transfer Learning:** + +You can read more about this at [medium.com/nanonets/nanonets-how-to-use-deep-learning-when-you-have-limited-data-f68c0b512cab](http://medium.com/nanonets/nanonets-how-to-use-deep-learning-when-you-have-limited-data-f68c0b512cab). You need a pretrained model so you can reduce the amount of data required to train. Without it, you might need a few 100k images to train the model. + +[You can find a bunch of pretrained models here](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md) + +#### **Step 4. Training on a GPU (cloud service like AWS/GCP etc or your own GPU Machine):** + +![](https://cdn-images-1.medium.com/max/800/1*b1-9TBSK6GUMGWd27wcLvQ.png) + +#### Docker Image + +The process of training a model is unnecessarily difficult to simplify the process we created a docker image would make it easy to train. + +To start training the model you can run: + +``` +sudo nvidia-docker run -p 8000:8000 -v `pwd`:data docker.nanonets.com/pi_training -m train -a ssd_mobilenet_v1_coco -e ssd_mobilenet_v1_coco_0 -p '{"batch_size":8,"learning_rate":0.003}' +``` + +#### [Please refer to this link for details on how to use](https://github.com/NanoNets/RaspberryPi-ObjectDetection-TensorFlow) + +The docker image has a run.sh script that can be called with the following parameters + +``` +run.sh [-m mode] [-a architecture] [-h help] [-e experiment_id] [-c checkpoint] [-p hyperparameters] + +-h display this help and exit +-m mode: should be either `train` or `export` +-p key value pairs of hyperparameters as json string +-e experiment id. Used as path inside data folder to run current experiment +-c applicable when mode is export, used to specify checkpoint to use for export +``` + +You can find more details at: + +* [**NanoNets/RaspberryPi-ObjectDetection-TensorFlow**: RaspberryPi-ObjectDetection-TensorFlow - Object Detection using TensorFlow on a Raspberry Pi](https://github.com/NanoNets/RaspberryPi-ObjectDetection-TensorFlow) + +**To train a model you need to select the right hyper parameters.** + +**Finding the right parameters** + +The art of “Deep Learning” involves a little bit of hit and try to figure out which are the best parameters to get the highest accuracy for your model. There is some level of black magic associated with this, along with a little bit of theory. [This is a great resource for finding the right parameters](https://blog.slavv.com/37-reasons-why-your-neural-network-is-not-working-4020854bd607). + +**Quantize Model (make it smaller to fit on a small device like the Raspberry Pi or Mobile)** + +Small devices like Mobile Phones and Rasberry PI have very little memory and computation power. + +Training neural networks is done by applying many tiny nudges to the weights, and these small increments typically need floating point precision to work (though there are research efforts to use quantized representations here too). + +Taking a pre-trained model and running inference is very different. One of the magical qualities of Deep Neural Networks is that they tend to cope very well with high levels of noise in their inputs. + +**Why Quantize?** + +Neural network models can take up a lot of space on disk, with the original AlexNet being over 200 MB in float format for example. Almost all of that size is taken up with the weights for the neural connections, since there are often many millions of these in a single model. + +The Nodes and Weights of a neural network are originally stored as 32-bit floating point numbers. The simplest motivation for quantization is to shrink file sizes by storing the min and max for each layer, and then compressing each float value to an eight-bit integer.The size of the files is reduced by 75%. + +![](https://cdn-images-1.medium.com/max/800/0*Ey92vYBh1Wq2uHfH.png) + +**Code for Quantization:** + +``` +curl -L "https://storage.googleapis.com/download.tensorflow.org/models/inception_v3_2016_08_28_frozen.pb.tar.gz" | + tar -C tensorflow/examples/label_image/data -xz +bazel build tensorflow/tools/graph_transforms:transform_graph +bazel-bin/tensorflow/tools/graph_transforms/transform_graph \ +--in_graph=tensorflow/examples/label_image/data/inception_v3_2016_08_28_frozen.pb \ + --out_graph=/tmp/quantized_graph.pb \ + --inputs=input \ + --outputs=InceptionV3/Predictions/Reshape_1 \ + --transforms='add_default_attributes strip_unused_nodes(type=float, shape="1,299,299,3") + remove_nodes(op=Identity, op=CheckNumerics) fold_constants(ignore_errors=true) + fold_batch_norms fold_old_batch_norms quantize_weights quantize_nodes + strip_unused_nodes sort_by_execution_order +``` +* * * + +### **Phase 3: Predictions on New Images using the Raspberry Pi** + +#### **Step 5. Capture a new Image via the camera** + +You need the Raspberry Pi camera live and working. Then capture a new Image + +![](https://cdn-images-1.medium.com/max/800/1*tMcyYPmB8aCJYXSS8Y2I8A.jpeg) + +For instructions on how to install checkout this [link](https://thepihut.com/blogs/raspberry-pi-tutorials/16021420-how-to-install-use-the-raspberry-pi-camera) + +``` +import picamera, os +from PIL import Image, ImageDraw +camera = picamera.PiCamera() +camera.capture('image1.jpg') +os.system("xdg-open image1.jpg") +``` + +Code to Capture a new Image. + +#### Step 6. Predicting a new Image + +**Download Model** + +Once your done training the model you can download it on to your pi. To export the model run: + +``` +sudo nvidia-docker run -v `pwd`:data docker.nanonets.com/pi_training -m export -a ssd_mobilenet_v1_coco -e ssd_mobilenet_v1_coco_0 -c /data/0/model.ckpt-8998 +``` + +Then download the model onto the Raspberry Pi. + +**Install TensorFlow on the Raspberry Pi** + +Depending on your device you might need to change the installation a little + +``` +sudo apt-get install libblas-dev liblapack-dev python-dev libatlas-base-dev gfortran python-setuptools libjpeg-dev + +sudo pip install Pillow + +sudo pip install http://ci.tensorflow.org/view/Nightly/job/nightly-pi-zero/lastSuccessfulBuild/artifact/output-artifacts/tensorflow-1.4.0-cp27-none-any.whl + +git clone [https://github.com/tensorflow/models.git](https://github.com/tensorflow/models.git) + +sudo apt-get install -y protobuf-compiler + +cd models/research/protoc object_detection/protos/*.proto --python_out=. + +export PYTHONPATH=$PYTHONPATH:/home/pi/models/research:/home/pi/models/research/slim +``` + +**Run model for predicting on the new Image** + +``` +python ObjectDetectionPredict.py --model data/0/quantized_graph.pb --labels data/label_map.pbtxt --images /data/image1.jpg /data/image2.jpg +``` + +* * * + +### Performance Benchmarks on Raspberry Pi + +The Raspberry Pi has constraints on both Memory and Compute (a version of Tensorflow Compatible with the Raspberry Pi GPU is still not available). Therefore, it is important to benchmark how much time do each of the models take to make a prediction on a new image. + +![](https://cdn-images-1.medium.com/max/1000/1*Z1z6TWrmvpW5DQ0WPKkTFw.png) + +Benchmarks for different Object Detection Models running on Raspberry Pi. + +* * * + +![](https://cdn-images-1.medium.com/max/800/1*m5grJCpQ6Dk6Ee-JEBIPPg.jpeg) + +#### We at NanoNets have a goal of making working with Deep Learning super easy. Object Detection is a major focus area for us and we have made a workflow that solves a lot of the challenges of implementing Deep Learning models. + +### How NanoNets make the Process Easier: + +#### 1. No Annotation Required + +We have removed the need to annotate Images, we have expert annotators who will **annotate your images for you**. + +#### 2. Automatic Best Model and Hyper Parameter Selection + +We **automatically train the best mode**l for you, to achieve this we run a battery of model with different parameters to select the best for your data + +#### 3. No Need for expensive Hardware and GPUs + +NanoNets is **entirely in the cloud** and runs without using any of your hardware. Which makes it much easier to use. + +#### 4. Great for Mobile devices like the Raspberry Pi + +Since devices like the Raspberry Pi and mobile phones were not built to run complex compute heavy tasks, you can outsource the workload to our cloud which does all of the compute for you + +### Here is a simple snippet to make prediction on an image using the NanoNets API + +``` +import picamera, json, requests, os, random +from time import sleep +from PIL import Image, ImageDraw + +#capture an image +camera = picamera.PiCamera() +camera.capture('image1.jpg') +print('caputred image') + +#make a prediction on the image +url = 'https://app.nanonets.com/api/v2/ObjectDetection/LabelFile/' +data = {'file': open('image1.jpg', 'rb'), \ + 'modelId': ('', 'YOUR_MODEL_ID')} +response = requests.post(url, auth=requests.auth.HTTPBasicAuth('YOUR_API_KEY', ''), files=data) +print(response.text) + +#draw boxes on the image +response = json.loads(response.text) +im = Image.open("image1.jpg") +draw = ImageDraw.Draw(im, mode="RGBA") +prediction = response["result"][0]["prediction"] +for i in prediction: + draw.rectangle((i["xmin"],i["ymin"], i["xmax"],i["ymax"]), fill=(random.randint(1, 255),random.randint(1, 255),random.randint(1, 255),127)) +im.save("image2.jpg") +os.system("xdg-open image2.jpg") +``` + +Code to make a prediction on a new Image using NanoNets. + +### Build your Own NanoNet + +![](https://cdn-images-1.medium.com/max/800/1*D0woyU-XyyqlUsNP1ToOBA.png) + +### You can try building your own model from: + +### 1. Using a GUI (also auto annotate Images): [https://nanonets.com/objectdetection/](https://nanonets.com/objectdetection/?utm_source=medium.com&utm_medium=content&utm_campaign=How%20to%20easily%20Detect%20Objects%20with%20Deep%20Learning%20on%20RaspberryPi&utm_content=bottom) + +### 2. Using our API: [https://github.com/NanoNets/object-detection-sample-python](https://github.com/NanoNets/object-detection-sample-python) + +#### Step 1: Clone the Repo + +``` +git clone [https://github.com/NanoNets/object-detection-sample-python.git](https://github.com/NanoNets/object-detection-sample-python.git) +cd object-detection-sample-python +sudo pip install requests +``` + +#### Step 2: Get your free API Key + +Get your free API Key from [http://app.nanonets.com/user/api_key](http://app.nanonets.com/user/api_key) + +#### Step 3: Set the API key as an Environment Variable + +``` +export NANONETS_API_KEY=YOUR_API_KEY_GOES_HERE +``` + +#### Step 4: Create a New Model + +``` +python ./code/create-model.py +``` + +> Note: This generates a MODEL_ID that you need for the next step + +#### Step 5: Add Model Id as Environment Variable + +``` +export NANONETS_MODEL_ID=YOUR_MODEL_ID +``` + +#### Step 6: Upload the Training Data + +Collect the images of object you want to detect. You can annotate them either using our web UI (https://app.nanonets.com/ObjectAnnotation/?appId=YOUR_MODEL_ID) or use open source tool like [labelImg](https://github.com/tzutalin/labelImg). Once you have dataset ready in folders, `images` (image files) and `annotations` (annotations for the image files), start uploading the dataset. + +``` +python ./code/upload-training.py +``` + +#### Step 7: Train Model + +Once the Images have been uploaded, begin training the Model + +``` +python ./code/train-model.py +``` + +#### Step 8: Get Model State + +The model takes ~2 hours to train. You will get an email once the model is trained. In the meanwhile you check the state of the model + +``` +watch -n 100 python ./code/model-state.py +``` + +#### Step 9: Make Prediction + +Once the model is trained. You can make predictions using the model + +``` +python ./code/prediction.py PATH_TO_YOUR_IMAGE.jpg +``` + +* * * + +### Code (Github Repos) + +#### Github Repos to Train a model: + +1. [Tensorflow Code for model Training and Quantization](https://github.com/NanoNets/RaspberryPi-ObjectDetection-TensorFlow) +2. [NanoNets Code for model Training](https://github.com/NanoNets/IndianRoadsObjectDetectionDataset) + +#### Github Repos for Raspberry Pi to make Predictions (ie Detecting New Objects): + +1. [Tensorflow Code for making Predictions on the Raspberry Pi](https://github.com/NanoNets/TF-OD-Pi-Test) +2. [NanoNets Code for making Predictions on the Raspberry Pi](https://gist.github.com/sjain07/a30388035c0b39b53841c501f8262ee2) + +#### Datasets with Annotations: + +1. [Cars on Indian Roads sees, dataset for extracting vehicles from Images of Indian Roads](https://github.com/NanoNets/IndianRoadsObjectDetectionDataset) +2. [Coco Dataset](http://cocodataset.org/#download) + + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From d7e514be43c344f4ae8e24701273455ead57a582 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 8 May 2018 11:19:30 +0800 Subject: [PATCH 012/446] Update how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md --- ...acking-changes-in-the-dom-using-mutationobserver.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md b/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md index 6a57166a24c..0bea0b27237 100644 --- a/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md +++ b/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md @@ -11,6 +11,16 @@ ![](https://cdn-images-1.medium.com/max/800/0*mPXf5zRCdEQ42Hn0.) +1. [[译] JavaScript 是如何工作的:对引擎、运行时、调用堆栈的概述](https://juejin.im/post/5a05b4576fb9a04519690d42) +2. [[译] JavaScript 是如何工作的:在 V8 引擎里 5 个优化代码的技巧](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code.md) +3. [[译] JavaScript 是如何工作的:内存管理 + 处理常见的4种内存泄漏](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-memory-management-how-to-handle-4-common-memory-leaks.md) +4. [[译] JavaScript 是如何工作的: 事件循环和异步编程的崛起 + 5个如何更好的使用 async/await 编码的技巧](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-event-loop-and-the-rise-of-async-programming-5-ways-to-better-coding-with.md) +5. [[译] JavaScript 是如何工作的:深入剖析 WebSockets 和拥有 SSE 技术 的 HTTP/2,以及如何在二者中做出正确的选择](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-deep-dive-into-websockets-and-http-2-with-sse-how-to-pick-the-right-path.md) +6. [[译] JavaScript 是如何工作的:与 WebAssembly 一较高下 + 为何 WebAssembly 在某些情况下比 JavaScript 更为适用](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-a-comparison-with-webassembly-why-in-certain-cases-its-better-to-use-it.md) +7. [[译] JavaScript 是如何工作的:Web Worker 的内部构造以及 5 种你应当使用它的场景](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-the-building-blocks-of-web-workers-5-cases-when-you-should-use-them.md) +8. [[译] JavaScript 是如何工作的:Web Worker 生命周期及用例](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-service-workers-their-life-cycle-and-use-cases.md) +9. [[译] JavaScript 是如何工作的:Web 推送通知的机制](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-the-mechanics-of-web-push-notifications.md) + web 应用正在持续的越来越侧重客户端,这是由很多原因造成的,例如需要更丰富的 UI 来承载复杂应用的需求,实时运算,等等。 持续增加的复杂度使得在 web 应用的生命周期的任意时刻中获取 UI 的确切状态越来越困难。 From 44d3208f7423dbcfd89dfea54a53da53ae3c0bf7 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 8 May 2018 11:21:00 +0800 Subject: [PATCH 013/446] Update how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md --- ...-works-tracking-changes-in-the-dom-using-mutationobserver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md b/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md index 0bea0b27237..35c47050603 100644 --- a/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md +++ b/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md @@ -5,7 +5,7 @@ > * 译者:[EmilyQiRabbit](https://github.com/EmilyQiRabbit) > * 校对者:[jasonxia23](https://github.com/jasonxia23) -# JavaScript 是如何运作的:用 MutationObserver 追踪 DOM 的变化 +# JavaScript 是如何工作的:用 MutationObserver 追踪 DOM 的变化 本系列专门研究 JavaScript 及其构建组件,这是第 10 期。在识别和描述核心元素的过程中,我们也分享了一些构建 [SessionStack](https://www.sessionstack.com/?utm_source=medium&utm_medium=blog&utm_content=javascript-series-push-notifications-intro) 的重要法则,SessionStack 是一个 JavaScript 应用,为了帮助用户实时查看和再现他们的 web 应用程序缺陷,它需要健壮并且高性能。 From 28be1975eea5cec741190e943130bc4b363f8b0b Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 8 May 2018 11:21:11 +0800 Subject: [PATCH 014/446] Update how-javascript-works-the-rendering-engine-and-tips-to-optimize-its-performance.md --- ...g-engine-and-tips-to-optimize-its-performance.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/TODO1/how-javascript-works-the-rendering-engine-and-tips-to-optimize-its-performance.md b/TODO1/how-javascript-works-the-rendering-engine-and-tips-to-optimize-its-performance.md index 0373691f077..aa69f28aa6e 100644 --- a/TODO1/how-javascript-works-the-rendering-engine-and-tips-to-optimize-its-performance.md +++ b/TODO1/how-javascript-works-the-rendering-engine-and-tips-to-optimize-its-performance.md @@ -5,10 +5,21 @@ > * 译者:[stormluke](https://github.com/stormluke) > * 校对者:[allenlongbaobao](https://github.com/allenlongbaobao)、[Usey95](https://github.com/Usey95) -# JavaScript 如何工作:渲染引擎和性能优化技巧 +# JavaScript 是如何工作的:渲染引擎和性能优化技巧 这是探索 JavaScript 及其构建组件专题系列的第 11 篇。在识别和描述核心元素的过程中,我们分享了在构建 [SessionStack](https://www.sessionstack.com/?utm_source=medium&utm_medium=blog&utm_content=js-series-rendering-engine-intro) 时使用的一些经验法则。SessionStack 是一个需要鲁棒且高性能的 JavaScript 应用程序,它帮助用户实时查看和重现它们 Web 应用程序的缺陷。 +1. [[译] JavaScript 是如何工作的:对引擎、运行时、调用堆栈的概述](https://juejin.im/post/5a05b4576fb9a04519690d42) +2. [[译] JavaScript 是如何工作的:在 V8 引擎里 5 个优化代码的技巧](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code.md) +3. [[译] JavaScript 是如何工作的:内存管理 + 处理常见的4种内存泄漏](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-memory-management-how-to-handle-4-common-memory-leaks.md) +4. [[译] JavaScript 是如何工作的: 事件循环和异步编程的崛起 + 5个如何更好的使用 async/await 编码的技巧](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-event-loop-and-the-rise-of-async-programming-5-ways-to-better-coding-with.md) +5. [[译] JavaScript 是如何工作的:深入剖析 WebSockets 和拥有 SSE 技术 的 HTTP/2,以及如何在二者中做出正确的选择](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-deep-dive-into-websockets-and-http-2-with-sse-how-to-pick-the-right-path.md) +6. [[译] JavaScript 是如何工作的:与 WebAssembly 一较高下 + 为何 WebAssembly 在某些情况下比 JavaScript 更为适用](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-a-comparison-with-webassembly-why-in-certain-cases-its-better-to-use-it.md) +7. [[译] JavaScript 是如何工作的:Web Worker 的内部构造以及 5 种你应当使用它的场景](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-the-building-blocks-of-web-workers-5-cases-when-you-should-use-them.md) +8. [[译] JavaScript 是如何工作的:Web Worker 生命周期及用例](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-service-workers-their-life-cycle-and-use-cases.md) +9. [[译] JavaScript 是如何工作的:Web 推送通知的机制](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-the-mechanics-of-web-push-notifications.md) +10. [[译] JavaScript 是如何工作的:用 MutationObserver 追踪 DOM 的变化](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md) + 当构建 Web 应用程序时,你不只是编写独立运行的 JavaScript 代码片段。你编写的 JavaScript 需要与环境进行交互。理解环境是如何工作的以及它是由什么组成的,你就能够构建更好的应用程序,并且能更好地处理应用程序发布后才会显现的潜在问题。 ![](https://cdn-images-1.medium.com/max/800/1*lMBu87MtEsVFqqbfMum-kA.png) From 655fa31b00ee4d5cb2a6005ba5db5fd4eb95ccea Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 8 May 2018 11:24:20 +0800 Subject: [PATCH 015/446] Create how-javascript-works-inside-the-networking-layer-how-to-optimize-its-performance-and-security.md --- ...o-optimize-its-performance-and-security.md | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 TODO1/how-javascript-works-inside-the-networking-layer-how-to-optimize-its-performance-and-security.md diff --git a/TODO1/how-javascript-works-inside-the-networking-layer-how-to-optimize-its-performance-and-security.md b/TODO1/how-javascript-works-inside-the-networking-layer-how-to-optimize-its-performance-and-security.md new file mode 100644 index 00000000000..91ab1fac06f --- /dev/null +++ b/TODO1/how-javascript-works-inside-the-networking-layer-how-to-optimize-its-performance-and-security.md @@ -0,0 +1,181 @@ +> * 原文地址:[How JavaScript Works: Inside the Networking Layer + How to Optimize Its Performance and Security](https://blog.sessionstack.com/how-javascript-works-inside-the-networking-layer-how-to-optimize-its-performance-and-security-f71b7414d34c) +> * 原文作者:[Alexander Zlatkov](https://blog.sessionstack.com/@zlatkov?source=post_header_lockup) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-inside-the-networking-layer-how-to-optimize-its-performance-and-security.md](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-inside-the-networking-layer-how-to-optimize-its-performance-and-security.md) +> * 译者: +> * 校对者: + +# How JavaScript Works: Inside the Networking Layer + How to Optimize Its Performance and Security + +This is post # 12 of the series dedicated to exploring JavaScript and its building components. In the process of identifying and describing the core elements, we also share some rules of thumb we use when building [SessionStack](https://www.sessionstack.com/?utm_source=medium&utm_medium=blog&utm_content=js-series-networking-layer-intro), a JavaScript application that needs to be robust and highly-performant to help users see and reproduce their web app defects real-time. + +If you missed the previous chapters, you can find them here: + +1. [[译] JavaScript 是如何工作的:对引擎、运行时、调用堆栈的概述](https://juejin.im/post/5a05b4576fb9a04519690d42) +2. [[译] JavaScript 是如何工作的:在 V8 引擎里 5 个优化代码的技巧](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code.md) +3. [[译] JavaScript 是如何工作的:内存管理 + 处理常见的4种内存泄漏](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-memory-management-how-to-handle-4-common-memory-leaks.md) +4. [[译] JavaScript 是如何工作的: 事件循环和异步编程的崛起 + 5个如何更好的使用 async/await 编码的技巧](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-event-loop-and-the-rise-of-async-programming-5-ways-to-better-coding-with.md) +5. [[译] JavaScript 是如何工作的:深入剖析 WebSockets 和拥有 SSE 技术 的 HTTP/2,以及如何在二者中做出正确的选择](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-deep-dive-into-websockets-and-http-2-with-sse-how-to-pick-the-right-path.md) +6. [[译] JavaScript 是如何工作的:与 WebAssembly 一较高下 + 为何 WebAssembly 在某些情况下比 JavaScript 更为适用](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-a-comparison-with-webassembly-why-in-certain-cases-its-better-to-use-it.md) +7. [[译] JavaScript 是如何工作的:Web Worker 的内部构造以及 5 种你应当使用它的场景](https://github.com/xitu/gold-miner/blob/master/TODO/how-javascript-works-the-building-blocks-of-web-workers-5-cases-when-you-should-use-them.md) +8. [[译] JavaScript 是如何工作的:Web Worker 生命周期及用例](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-service-workers-their-life-cycle-and-use-cases.md) +9. [[译] JavaScript 是如何工作的:Web 推送通知的机制](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-the-mechanics-of-web-push-notifications.md) +10. [[译] JavaScript 是如何工作的:用 MutationObserver 追踪 DOM 的变化](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-tracking-changes-in-the-dom-using-mutationobserver.md) +11. [[译] JavaScript 是如何工作的:渲染引擎和性能优化技巧](https://github.com/xitu/gold-miner/blob/master/TODO1/how-javascript-works-the-rendering-engine-and-tips-to-optimize-its-performance.md) + +Just like we mentioned in our previous blog post about the [rendering engine](https://blog.sessionstack.com/how-javascript-works-the-rendering-engine-and-tips-to-optimize-its-performance-7b95553baeda), we believe that the difference between good and great JavaScript developers is that the latter not only understand the nuts and bolts of the language but also its internals and the surrounding environment. + +#### A little bit of history + +Forty-nine years ago, a thing called ARPAnet was created. It was [an early packet switching network](https://en.wikipedia.org/wiki/Packet_switching) and the first network [to implement the TCP/IP suite](https://en.wikipedia.org/wiki/Internet_protocol_suite). That network set up a link between University of California and Stanford Research Institute. 20 years later Tim Berners-Lee circulated a proposal for a “Mesh” which later became better known as the World Wide Web. In those 49, years the internet has come a long way, starting from just two computers exchanging packets of data, to reaching more than 75 million servers, 3.8B people using the internet and 1.3B websites. + +![](https://cdn-images-1.medium.com/max/800/1*x8P3OcgcgKrEEDpgT2IKkQ.jpeg) + +In this post, we’ll try to analyze what techniques modern browsers employ to automatically boost performance (without you even knowing it), and we’ll specifically zoom in on the browser networking layer. At the end, we’ll provide some ideas on how to help browsers boost even more the performance of your web apps. + +### Overview + +The modern web browser has been specifically designed for the fast, efficient and secure delivery of web apps/websites. With hundreds of components running on different layers, from process management and security sandboxing to GPU pipelines, audio and video, and many more, the web browser looks more like an operating system rather than just a software application. + +The overall performance of the browser is determined by a number of large components: parsing, layout, style calculation, JavaScript and WebAssembly execution, rendering, and of course, **the networking stack**. + +Engineers often think that the networking stack is a bottleneck. This is frequently the case since all resources need to be fetched from the internet before the rest of the steps are unblocked. For the networking layer to be efficient it needs to play the role of more than just a simple socket manager. It is presented to us as a very simple mechanism for resource fetching but it’s actually an entire platform with its own optimization criteria, APIs, and services. + +![](https://cdn-images-1.medium.com/max/800/1*WqInzMPQGGcMX9AOONN76g.jpeg) + +As web developers, we don’t have to worry about the individual TCP or UDP packets, request formatting, caching and everything else that’s going on. The entire complexity is taken care of by the browser so we can focus on the application we’re developing. Knowing what’s happening under the hood, however, can help us create faster and more secure applications. + +In essence, here’s what’s happening when the user starts interacting with the browser: + +* The user enters a URL in the browser address bar +* Given the URL of a resource on the web, the browser starts by checking its local and application caches and tries to use a local copy to fulfill the request. +* If the cache cannot be used, the browser takes the domain name from the URL and requests the IP address of the server from a [DNS](https://en.wikipedia.org/wiki/Domain_Name_System). If the domain is cached, no DNS query is needed. +* The browser creates an HTTP packet saying that it requests a web page located on the remote server. +* The packet is sent to the TCP layer which adds its own information on top of the HTTP packet. This information is required to maintain the started session. +* The packet is then handed to the IP layer which main job is to figure out a way to send the packet from the user to the remote server. This information is also stored on top of the packet. +* The packet is sent to the remote server. +* Once the packet is received, the response gets sent back in a similar manner. + +The W3C [Navigation Timing specification](http://www.w3.org/TR/navigation-timing/) provides a browser API as well as visibility into the timing and performance data behind the life of every request in the browser. Let’s inspect the components, as each plays a critical role in delivering the optimal user experience: + +![](https://cdn-images-1.medium.com/max/800/1*rjBdCBwOx5Gp_A6b6FQgfw.png) + +The whole networking process is very complex and there are many different layers which can become a bottleneck. This is why browsers strive to improve performance on their side by using various techniques so that the impact of the entire network communication is minimal. + +### Socket management + +Let’s start with some terminology: + +* **Origin** — A triple of application protocol, domain name and port number (e.g. https, [www.example.com](http://www.example.com), 443) +* **Socket pool** — a group of sockets belonging to the same origin (all major browsers limit the maximum pool size to 6 sockets) + +JavaScript and WebAssembly **do not** allow us to manage the lifecycle of individual network sockets, and that’s a good thing! This not only keeps our hands clean but it also allows the browser to automate a lot of performance optimizations some of which include socket reuse, request prioritization and late binding, protocol negotiation, enforcing connection limits, and many other. + +Actually, modern browsers go the extra mile to separate the request management cycle from socket management. Sockets are organized in pools, which are grouped by origin, and each pool enforces its own connection limits and security constraints. Pending requests are queued, prioritized, and then bound to individual sockets in the pool. Unless the server intentionally closes the connection, the same socket can be automatically reused across multiple requests! + +![](https://cdn-images-1.medium.com/max/800/1*0e8X3UTBpsiBSZKa3l1hXA.png) + +Since the opening of a new TCP connection comes at an additional cost, the reuse of connections introduces great performance benefits on its own. By default, browsers use the so-called “keepalive” mechanism which saves time from opening a new connection to the server when a request is made. The average time for opening a new TCP connection is: + +* Local requests — `23ms` +* Transcontinental requests — `120ms` +* Intercontinental requests — `225ms` + +This architecture opens the door to a number of other optimization opportunities. The requests can be executed in a different order depending on their priority. The browser can optimize the bandwidth allocation across all sockets or it can open sockets in anticipation of a request. + +As I mentioned before, this is all managed by the browser and does not require any work on our side. But this doesn’t necessarily mean that we can’t do anything to help. Choosing the right network communication patterns, type, and frequency of transfers, choice of protocols and tuning/optimization of our server stack can play a great role in improving the overall performance of an application. + +Some browsers even go one step further. For example, Chrome can self-teach itself to get faster as you use it. It learns based on the sites visited and the typical browsing patterns so it can anticipate likely user behavior and take action before the user does anything. The simplest example is pre-rendering a page when the user hovers on a link. If you’re interested in learning more about Chrome’s optimizations, you can check out this chapter [https://www.igvita.com/posa/high-performance-networking-in-google-chrome/](https://www.igvita.com/posa/high-performance-networking-in-google-chrome/) of the [High-Performance Browser Networking](https://hpbn.co) book. + +### Network Security and Sandboxing + +Allowing the browser to manage the individual sockets has another very important purpose: this way the browser enables the enforcement of a consistent set of security and policy constraints on untrusted application resources. For example, the browser does not allow direct API access to raw network sockets as this would enable any malicious application to make arbitrary connections to any host. The browser also enforces connection limits which protect the server as well as the client from resource exhaustion. + +The browser formats all outgoing requests to enforce consistent and well-formed protocol semantics to protect the server. Similarly, response decoding is done automatically to protect the user from malicious servers. + +#### TLS negotiation + +[Transport Layer Security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security) is a cryptographic protocol that provides communication security over a computer network. It finds widespread use in many applications, one of which is web browsing. Websites can use TLS to secure all communications between their servers and web browsers. + +The entire TLS handshake consists of the following steps: + +1. The client sends a “Client hello” message to the server, along with the client’s random value and supported cipher suites. +2. The server responds by sending a “Server hello” message to the client, along with the server’s random value. +3. The server sends its certificate for authentication to the client and may request a similar certificate from the client. The server sends the “Server hello done” message. +4. If the server has requested a certificate from the client, the client sends it. +5. The client creates a random Pre-Master Secret and encrypts it with the public key from the server’s certificate, sending the encrypted Pre-Master Secret to the server. +6. The server receives the Pre-Master Secret. The server and the client each generate the Master Secret and session keys based on the Pre-Master Secret. +7. The client sends a “Change cipher spec” notification to the server to indicate that the client will start using the new session keys for hashing and encrypting messages. The client also sends a “Client finished” message. +8. The server receives the “Change cipher spec” and switches its record layer security state to symmetric encryption using the session keys. The server sends a “Server finished” message to the client. +9. Client and server can now exchange application data over the secured channel they have established. All messages sent from the client to the server and back are encrypted using the session key. + +The user is warned in case any of the verifications fail — e.g., the server is using a self-signed certificate. + +#### Same-origin policy + +Two pages have the same origin if the protocol, port (if one is specified), and host are the same for both pages. + +Here are some examples of resources which may be embedded cross-origin: + +* JavaScript with ``. Error messages for syntax errors are only available for same-origin scripts +* CSS with ``. Due to the relaxed syntax rules of CSS, cross-origin CSS requires a correct Content-Type header. Restrictions vary by browser +* Images with `` +* Media files with `