diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..6313b56c57 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index ec822e83a9..38ee795352 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -1,9 +1,14 @@ # JavaScript 简介 +<<<<<<< HEAD 我们一起来聊一下 JavaScript,用它能做什么,它有哪些特性,以及一些跟它配合使用的技术。 +======= +Let's see what's so special about JavaScript, what we can achieve with it, and which other technologies play well with it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 什么是 JavaScript? +<<<<<<< HEAD **JavaScript** 最初的目的是为了“**赋予网页生命**”。 这种编程语言我们称之为**脚本**。它们可以写在 HTML 中,在页面加载的时候会自动执行。 @@ -31,6 +36,35 @@ JavaScript 在刚诞生的时候,它的名字叫 “LiveScript”。但是因 上面这些很容易记忆,因为经常出现在网上关于开发的文章中。我们也会这样用。例如:某个新的功能,如果 JavaScript 引擎 V8 是支持的,那么我们可以认为这个功能在 Chrome 和 Opera 中可以正常运行。 ```smart header="引擎是如何工作的?" +======= +*JavaScript* was initially created to *"make web pages alive"*. + +The programs in this language are called *scripts*. They can be written right in a web page's HTML and run automatically as the page loads. + +Scripts are provided and executed as plain text. They don't need special preparation or compilation to run. + +In this aspect, JavaScript is very different from another language called [Java](https://en.wikipedia.org/wiki/Java_(programming_language)). + +```smart header="Why JavaScript?" +When JavaScript was created, it initially had another name: "LiveScript". But Java was very popular at that time, so it was decided that positioning a new language as a "younger brother" of Java would help. + +But as it evolved, JavaScript became a fully independent language with its own specification called [ECMAScript](http://en.wikipedia.org/wiki/ECMAScript), and now it has no relation to Java at all. +``` + +Today, JavaScript can execute not only in the browser, but also on the server, or actually on any device that has a special program called [the JavaScript engine](https://en.wikipedia.org/wiki/JavaScript_engine). + +The browser has an embedded engine sometimes called a "JavaScript virtual machine". + +Different engines have different "codenames". For example: + +- [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- in Chrome and Opera. +- [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- in Firefox. +- ...There are other codenames like "Trident" and "Chakra" for different versions of IE, "ChakraCore" for Microsoft Edge, "Nitro" and "SquirrelFish" for Safari, etc. + +The terms above are good to remember because they are used in developer articles on the internet. We'll use them too. For instance, if "a feature X is supported by V8", then it probably works in Chrome and Opera. + +```smart header="How do engines work?" +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 引擎很复杂,但是基本原理很简单。 @@ -38,16 +72,28 @@ JavaScript 在刚诞生的时候,它的名字叫 “LiveScript”。但是因 2. 然后将脚本转化(“编译”)为机器语言。 3. 然后就可以在机器上飞速的运行。 +<<<<<<< HEAD 引擎会对流程中的每个阶段都进行优化。它甚至可以在运行时监视编译的脚本,分析数据流并根据这些对机器代码应用优化。最后,脚本会执行地非常快。 +======= +The engine applies optimizations at each step of the process. It even watches the compiled script as it runs, analyzes the data that flows through it, and applies optimizations to the machine code based on that knowledge. When it's done, scripts run quite fast. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` ## 浏览器中的 JavaScript 能做什么 ? +<<<<<<< HEAD 现代的 JavaScript 是一种“安全”语言。它不提供对内存或 CPU 的底层访问,因为它最初是为浏览器创建的,不需要这些功能。 JavaScript 的能力依赖于它执行的环境。例如:[Node.js](https://wikipedia.org/wiki/Node.js) 允许 JavaScript 读写任意文件、执行网络请求等。 浏览器中的 JavaScript 可以完成所有和网页相关的操作、处理用户和 Web 服务器之间的交互。 +======= +Modern JavaScript is a "safe" programming language. It does not provide low-level access to memory or CPU, because it was initially created for browsers which do not require it. + +JavaScript's capabilities greatly depend on the environment it's running in. For instance, [Node.js](https://wikipedia.org/wiki/Node.js) supports functions that allow JavaScript to read/write arbitrary files, perform network requests, etc. + +In-browser JavaScript can do everything related to webpage manipulation, interaction with the user, and the webserver. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如,浏览器中的 JavaScript 可以完成下面这些事: @@ -61,7 +107,11 @@ JavaScript 的能力依赖于它执行的环境。例如:[Node.js](https://wik 为了用户的(信息)安全,在浏览器中的 JavaScript 的能力是有限的。这样主要是为了阻止邪恶的网站获得或修改用户的私人数据。 +<<<<<<< HEAD 这些限制的例子有: +======= +Examples of such restrictions include: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb * 网页中的 JavaScript 不能读、写、复制及执行用户磁盘上的文件或程序。它没有直接访问操作系统的功能。 @@ -70,6 +120,7 @@ JavaScript 的能力依赖于它执行的环境。例如:[Node.js](https://wik JavaScript 有很多方式和设备的照相机/麦克风交互,但是这些都需要提前获得用户的授权许可。所以,JavaScript 并不会偷偷地通过你的摄像头观察你,更不会把你的信息发送到[美国国家安全局](https://en.wikipedia.org/wiki/National_Security_Agency)。 - 不同的浏览器标签页之间基本彼此不相关。有时候,也会有一些关系。例如,通过 JavaScript 打开另外一个新的标签页。但即使在这种情况下,如果两个标签页打开的不是同一个网站(域名、协议或者端口任一不相同的网站),他们都不能够相互通信。 +<<<<<<< HEAD 这就是“同源策略”。为了解决不同标签页交互的问题,两个同源的网站必须**都**包含一些特殊的 JavaScript 代码,才能够实现数据交换。 这个限制也是为了用户的信息安全。例如,用户打开的 `http://anysite.com` 网页的 JavaScript 不能访问 `http://gmail.com`(另外一个标签页打开的网页)也不能从那里窃取信息。 @@ -78,22 +129,45 @@ JavaScript 的能力依赖于它执行的环境。例如:[Node.js](https://wik ![](limitations.png) 浏览器环境外的 JavaScript 一般没有这些限制。例如服务端的 JavaScript 就没有这些限制。现代浏览器还允许通过 JavaScript 来安装浏览器插件或扩展,当然这也是在用户授权的前提下。 +======= + This is called the "Same Origin Policy". To work around that, *both pages* must agree for data exchange and contain a special JavaScript code that handles it. We'll cover that in the tutorial. + + This limitation is, again, for the user's safety. A page from `http://anysite.com` which a user has opened must not be able to access another browser tab with the URL `http://gmail.com` and steal information from there. +- JavaScript can easily communicate over the net to the server where the current page came from. But its ability to receive data from other sites/domains is crippled. Though possible, it requires explicit agreement (expressed in HTTP headers) from the remote side. Once again, that's a safety limitation. + +![](limitations.png) + +Such limits do not exist if JavaScript is used outside of the browser, for example on a server. Modern browsers also allow plugin/extensions which may ask for extended permissions. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## JavaScript 为什么与众不同? 至少有 **3** 件事值得一提: ```compare +<<<<<<< HEAD + 和 HTML/CSS 完全的集成。 + 使用简单的工具完成简单的任务。 + 被所有的主流浏览器支持,并且默认开启。 +======= ++ Full integration with HTML/CSS. ++ Simple things are done simply. ++ Support by all major browsers and enabled by default. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` +JavaScript is the only browser technology that combines these three things. +<<<<<<< HEAD 满足这三条的浏览器技术也只有 JavaScript 了。 这就是为什么 JavaScript 与众不同!这也是为什么它是创建浏览器界面的最普遍的工具。 当然,学习一项新技术的时候,最好先看一下它的前景。所以,接下来我们继续讨论包括新语言和浏览器功能在内的现代趋势。 +======= +That's what makes JavaScript unique. That's why it's the most widespread tool for creating browser interfaces. + +While planning to learn a new technology, it's beneficial to check its perspectives. So let's move on to the modern trends affecting it, including new languages and browser abilities. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 比 JavaScript “好” 的语言 @@ -104,18 +178,36 @@ JavaScript 的能力依赖于它执行的环境。例如:[Node.js](https://wik 所以,最近出现了很多不同的语言,这些语言在浏览器中执行之前,都会被**编译**(转化)成 JavaScript。 +<<<<<<< HEAD 现代化的工具使得编译速度非常快速和透明,实际上允许开发人员使用另一种语言编写代码并将其自动转换为 JavaScript。 +======= +Modern tools make the transpilation very fast and transparent, actually allowing developers to code in another language and auto-converting it "under the hood". +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 这些语言的例子有: +<<<<<<< HEAD - [CoffeeScript](http://coffeescript.org/) 是 JavaScript 的语法糖,它语法简短,明确简洁。通常使用 Ruby 的人喜欢用。 - [TypeScript](http://www.typescriptlang.org/) 将注意力集中在增加严格的数据类型。这样就能简化开发,也能用于开发复杂的系统。TypeScript 是微软开发的。 - [Dart](https://www.dartlang.org/) 是一门独立的语言。它拥有自己的引擎用于在非浏览器环境中运行(如:在手机应用中运行)。最开始是 Google 提供的用于替代 JavaScript 的,但是现在,它和其他上述的语言一样,浏览器也要求它被编译成 JavaScript 。 当然,还有更多其他的语言。即使我们在使用这些语言,我们也需要知道 JavaScript。因为学习 JavaScript 可以让我们真正明白我们自己在做什么。 +======= +- [CoffeeScript](http://coffeescript.org/) is a "syntactic sugar" for JavaScript. It introduces shorter syntax, allowing us to write clearer and more precise code. Usually, Ruby devs like it. +- [TypeScript](http://www.typescriptlang.org/) is concentrated on adding "strict data typing" to simplify the development and support of complex systems. It is developed by Microsoft. +- [Dart](https://www.dartlang.org/) is a standalone language that has its own engine that runs in non-browser environments (like mobile apps). It was initially offered by Google as a replacement for JavaScript, but as of now, browsers require it to be transpiled to JavaScript just like the ones above. + +There are more. Of course, even if we use one of these languages, we should also know JavaScript to really understand what we're doing. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 总结 +<<<<<<< HEAD - JavaScript 最开始是为浏览器设计的一门语言,但是现在也可以在其它的环境中运行。 - 现在,JavaScript 是一门在浏览器中使用最广、并且能够很好集成 HTML/CSS 的语言。 - 有很多其他的语言可以编译成 JavaScript,这些语言还提供更多的功能。最好要了解一下这些语言,至少需要在掌握 JavaScript 之后简单地看一下。 +======= +- JavaScript was initially created as a browser-only language, but is now used in many other environments as well. +- Today, JavaScript has a unique position as the most widely-adopted browser language with full integration with HTML/CSS. +- There are many languages that get "transpiled" to JavaScript and provide certain features. It is recommended to take a look at them, at least briefly, after mastering JavaScript. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/01-getting-started/1-intro/limitations.png b/1-js/01-getting-started/1-intro/limitations.png index d5d315c5c1..3ef6b1e087 100644 Binary files a/1-js/01-getting-started/1-intro/limitations.png and b/1-js/01-getting-started/1-intro/limitations.png differ diff --git a/1-js/01-getting-started/1-intro/limitations@2x.png b/1-js/01-getting-started/1-intro/limitations@2x.png index 01483997da..7aa1c41ae4 100644 Binary files a/1-js/01-getting-started/1-intro/limitations@2x.png and b/1-js/01-getting-started/1-intro/limitations@2x.png differ diff --git a/1-js/01-getting-started/2-code-editors/article.md b/1-js/01-getting-started/2-code-editors/article.md index 64cea6a40b..4027264641 100644 --- a/1-js/01-getting-started/2-code-editors/article.md +++ b/1-js/01-getting-started/2-code-editors/article.md @@ -2,6 +2,7 @@ 程序员接触时间最长的就是代码编辑器(后面简称编辑器)。 +<<<<<<< HEAD 编辑器主要分两种:IDE(集成开发环境)和轻量编辑器。很多人喜欢这两种各选一个。 ## IDE @@ -19,12 +20,36 @@ IDE 加载项目(通常包含多个文件),并且允许在不同文件之 除了 Visual Studio,以上这些 IDE 都是跨平台的。Visual Studio 无法在 Linux 上使用。 大多数 IDE 是收费的,但是他们都可以试用。这些费用对于一个合格的程序员的薪水来说,肯定算不了什么,所以去选一个对你来说最好的吧。 +======= +There are two main types of code editors: IDEs and lightweight editors. Many people use one tool of each type. + +## IDE + +The term [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) (Integrated Development Environment) refers to a powerful editor with many features that usually operates on a "whole project." As the name suggests, it's not just an editor, but a full-scale "development environment." + +An IDE loads the project (which can be many files), allows navigation between files, provides autocompletion based on the whole project (not just the open file), and integrates with a version management system (like [git](https://git-scm.com/)), a testing environment, and other "project-level" stuff. + +If you haven't selected an IDE yet, consider the following options: + +- [WebStorm](http://www.jetbrains.com/webstorm/) for frontend development. The same company offers other editors for other languages (paid). +- [Netbeans](http://netbeans.org/) (free). + +All of these IDEs are cross-platform. + +For Windows, there's also "Visual Studio", not to be confused with "Visual Studio Code." "Visual Studio" is a paid and mighty Windows-only editor, well-suited for the .NET platform. A free version of it is called [Visual Studio Community](https://www.visualstudio.com/vs/community/). + +Many IDEs are paid but have a trial period. Their cost is usually negligible compared to a qualified developer's salary, so just choose the best one for you. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 轻量编辑器 “轻量编辑器”没有 IDE 那么功能强大,但是他们一般很快、优雅而且简单。 +<<<<<<< HEAD “轻量编辑器”主要用于立即打开编辑一个文件。 +======= +They are mainly used to open and edit a file instantly. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb “轻量编辑器”和 IDE 有很大的区别。IDE 一般是项目中使用,就意味着一开始要加载很多数据,并且在使用的过程中会分析项目的结构和内容。如果我们只需要编辑一个文件,那么 “轻量编辑器” 会更快的帮我们打开文件。 @@ -32,11 +57,19 @@ IDE 加载项目(通常包含多个文件),并且允许在不同文件之 下面是一些流行的“轻量编辑器”: +<<<<<<< HEAD - [Visual Studio Code](https://code.visualstudio.com/)(跨平台、免费)。 - [Atom](https://atom.io/)(跨平台、免费)。 - [Sublime Text](http://www.sublimetext.com)(跨平台、共享软件)。 - [Notepad++](https://notepad-plus-plus.org/)(Windows、免费)。 - [Vim](http://www.vim.org/) 和 [Emacs](https://www.gnu.org/software/emacs/) 很棒,前提是如果你知道怎么用。 +======= +- [Visual Studio Code](https://code.visualstudio.com/) (cross-platform, free) also has many IDE-like features. +- [Atom](https://atom.io/) (cross-platform, free). +- [Sublime Text](http://www.sublimetext.com) (cross-platform, shareware). +- [Notepad++](https://notepad-plus-plus.org/) (Windows, free). +- [Vim](http://www.vim.org/) and [Emacs](https://www.gnu.org/software/emacs/) are also cool if you know how to use them. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 我的最爱 @@ -44,13 +77,26 @@ IDE 加载项目(通常包含多个文件),并且允许在不同文件之 我使用的编辑器: +<<<<<<< HEAD - 使用 [WebStorm](http://www.jetbrains.com/webstorm/) 写 JavaScript,如果项目涉及其他语言我切换到上面列出的其他 Jetbrains 产品之一。 - 轻量编辑器 —— [Sublime Text](http://www.sublimetext.com) 或 [Atom](https://atom.io/)。 +======= +- As an IDE for JS -- [WebStorm](http://www.jetbrains.com/webstorm/) (I switch to one of the other JetBrains offerings when using other languages) +- As a lightweight editor -- [Sublime Text](http://www.sublimetext.com) or [Atom](https://atom.io/). +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 不要争吵 +<<<<<<< HEAD 上面列表中的编辑器都是我或我的朋友(他们都是我认为很优秀的开发人员)已经使用了很长时间并且很满意的。 +======= +The editors in the lists above are those that either I or my friends whom I consider good developers have been using for a long time and are happy with. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 世上还有很多其他的很好的编辑器,你们可以选择一个自己最喜欢的。 +<<<<<<< HEAD 选择编辑器就像选择其他工具一样。要看你的项目,以及个人的习惯、喜好。 +======= +The choice of an editor, like any other tool, is individual and depends on your projects, habits, and personal preferences. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/01-getting-started/3-devtools/article.md b/1-js/01-getting-started/3-devtools/article.md index be0ea58fbf..b35beb8b09 100644 --- a/1-js/01-getting-started/3-devtools/article.md +++ b/1-js/01-getting-started/3-devtools/article.md @@ -1,5 +1,6 @@ # 开发者控制台 +<<<<<<< HEAD 代码是很容易出现错误的。你也很可能犯错误...... 哦,我在说什么?只要你是人,你一定会犯错误(在写代码的时候),除非你是[机器人](https://en.wikipedia.org/wiki/Bender_(Futurama))。 但在浏览器中,默认情况下用户是看不到错误的。所以,如果脚本中有错误,我们看不到是什么错误,更不能够修复。 @@ -9,6 +10,17 @@ 通常,开发者倾向于使用 Chrome 或 Firefox 来开发,因为它们有最好的开发者工具。一些其他的浏览器也提供开发者工具,有时还具有一些特殊的功能,通常它们都是在追赶 Chrome 或 Firefox。所以大多数人都有 “最喜欢” 的浏览器,当遇到某个浏览器独有的问题的时候,人们就会切换到其他的浏览器。 开发者工具很强大,功能丰富。首先,我们要学习如何打开它们,查找错误和运行 JavaScript 命令。 +======= +Code is prone to errors. You will quite likely make errors... Oh, what am I talking about? You are *absolutely* going to make errors, at least if you're a human, not a [robot](https://en.wikipedia.org/wiki/Bender_(Futurama)). + +But in the browser, users don't see errors by default. So, if something goes wrong in the script, we won't see what's broken and can't fix it. + +To see errors and get a lot of other useful information about scripts, "developer tools" have been embedded in browsers. + +Most developers lean towards Chrome or Firefox for development because those browsers have the best developer tools. Other browsers also provide developer tools, sometimes with special features, but are usually playing "catch-up" to Chrome or Firefox. So most developers have a "favorite" browser and switch to others if a problem is browser-specific. + +Developer tools are potent; they have many features. To start, we'll learn how to open them, look at errors, and run JavaScript commands. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## Google Chrome @@ -24,6 +36,7 @@ ![chrome](chrome.png) +<<<<<<< HEAD 具体什么样,要看你的 Chrome 版本。它随着时间一直在变,但是都很类似。 - 上面这个,我们能看到红色的错误信息。这个场景中,脚本里有一个未知的 “lalala” 命令。 @@ -32,26 +45,65 @@ 在错误信息的下方,有个 `>` 标志。它代表 “命令行”,在 “命令行” 中,我们可以输入 JavaScript 命令,按下 `key:Enter` 来执行(`key:Shift+Enter` 用来输入多行命令)。 现在,我们能看到错误就够了。稍后,在 章节中,我们会重新更加深入地讨论开发者工具。 +======= +The exact look of developer tools depends on your version of Chrome. It changes from time to time but should be similar. + +- Here we can see the red-colored error message. In this case, the script contains an unknown "lalala" command. +- On the right, there is a clickable link to the source `bug.html:12` with the line number where the error has occurred. + +Below the error message, there is a blue `>` symbol. It marks a "command line" where we can type JavaScript commands. Press `key:Enter` to run them (`key:Shift+Enter` to input multi-line commands). + +Now we can see errors, and that's enough for a start. We'll come back to developer tools later and cover debugging more in-depth in the chapter . +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## Firefox、Edge 和其他 +<<<<<<< HEAD 大多数其他的浏览器都是通过 `key:F12` 来打开开发者工具。 他们的外观和感觉都非常相似,一旦你学会了他们中的一个(可以先尝试 Chrome),其他的也就很快了。 +======= +## Firefox, Edge, and others + +Most other browsers use `key:F12` to open developer tools. + +The look & feel of them is quite similar. Once you know how to use one of these tools (you can start with Chrome), you can easily switch to another. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## Safari Safari(Mac 系统中浏览器,不支持 Windows 和 Linux 系统)有一点点不同。我们需要先开启 “开发菜单”。 +<<<<<<< HEAD 打开 “偏好设置”,选择“高级”选项。选中最下方的那个选择框。 ![safari](safari.png) 现在,我们通过 `key:Cmd+Opt+C` 就能打开或关闭控制台了。另外注意,有一个名字为 “开发” 的顶部菜单出现了。它有很多命令和选项。 +======= +Open Preferences and go to the "Advanced" pane. There's a checkbox at the bottom: + +![safari](safari.png) + +Now `key:Cmd+Opt+C` can toggle the console. Also, note that the new top menu item named "Develop" has appeared. It has many commands and options. + +## Multi-line input + +Usually, when we put a line of code into the console, and then press `key:Enter`, it executes. + +To insert multiple lines, press `key:Shift+Enter`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 总结 +<<<<<<< HEAD * 开发者工具允许我们查看错误、执行命令、检查变量等等。 * 在 Windows 系统中,可以通过 `key:F12` 开启开发者工具。Mac 系统下,Chrome 需要使用 `key:Cmd+Opt+J`,Safari 使用 `key:Cmd+Opt+C`(需要提前开启)。 现在我们的环境准备好了,下一章,我们将开始讨论 JavaScript。 +======= +- Developer tools allow us to see errors, run commands, examine variables, and much more. +- They can be opened with `key:F12` for most browsers on Windows. Chrome for Mac needs `key:Cmd+Opt+J`, Safari: `key:Cmd+Opt+C` (need to enable first). + +Now we have the environment ready. In the next section, we'll get down to JavaScript. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/01-hello-world/article.md b/1-js/02-first-steps/01-hello-world/article.md index 0727719bcc..b1192532dd 100644 --- a/1-js/02-first-steps/01-hello-world/article.md +++ b/1-js/02-first-steps/01-hello-world/article.md @@ -1,16 +1,30 @@ # Hello, world! +<<<<<<< HEAD 你现在所阅读的这个教程是 JavaScript 的核心内容,这部分内容是平台无关的。接下来,你将会学习 Node.js 以及使用 Node.js 的其他平台。 但是,我们需要一个工作环境来运行代码,由于本书是在线的,所以浏览器是一个不错的选择。我们会尽可能少地使用浏览器特定的命令(比如 `alert`),所以如果你打算使用如 Node.js 的其他环境,你不必多花时间来关心这些特定指令。另一方面,浏览器的具体细节我们会在教程的[下一部分](/ui)介绍。 首先,让我们看看如何将脚本添加到网页上。对于服务器端环境,你只需要使用诸如 `"node my.js"` 的 Node.js 的命令行来执行它。 +======= +This part of the tutorial is about core JavaScript, the language itself. Later on, you'll learn about Node.js and other platforms that use it. + +But we need a working environment to run our scripts and, since this book is online, the browser is a good choice. We'll keep the amount of browser-specific commands (like `alert`) to a minimum so that you don't spend time on them if you plan to concentrate on another environment (like Node.js). We'll focus on JavaScript in the browser in the [next part](/ui) of the tutorial. + +So first, let's see how we attach a script to a webpage. For server-side environments (like Node.js), you can execute the script with a command like `"node my.js"`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## "script" 标签 JavaScript 程序可以使用 ` ``` +<<<<<<< HEAD 这些注释是给不支持 ` ``` +<<<<<<< HEAD 这里,`/path/to/script.js` 是脚本文件的绝对路径(从站点根目录开始)。 也可以提供相对于当前页面的相对路径。比如,`src="script.js"` 意思是来自当前文件夹的 `"script.js"` 文件。 我们还可以提供一个完整的 URL 地址,例如: +======= +Here, `/path/to/script.js` is an absolute path to the script file (from the site root). + +You can also provide a relative path from the current page. For instance, `src="script.js"` would mean a file `"script.js"` in the current folder. + +We can give a full URL as well. For instance: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```html @@ -94,6 +145,7 @@ JavaScript 程序可以使用 ` ``` +<<<<<<< HEAD 我们必须选择,要么外部的`` 这种方式插入。 有关浏览器脚本以及它们和网页的关系,还有很多可学的。但是请记住,这部分教程主要是针对 JavaScript 语言的,所以我们不该分散自己的注意力。我们使用浏览器作为运行 JavaScript 的一种方式,非常便于我们在线阅读。 +======= +- We can use a ``. + + +There is much more to learn about browser scripts and their interaction with the webpage. But let's keep in mind that this part of the tutorial is devoted to the JavaScript language, so we shouldn't distract ourselves with browser-specific implementations of it. We'll be using the browser as a way to run JavaScript, which is very convenient for online reading, but only one of many. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/01-hello-world/hello-world-render.png b/1-js/02-first-steps/01-hello-world/hello-world-render.png index ffe810697c..ba94e017d6 100644 Binary files a/1-js/02-first-steps/01-hello-world/hello-world-render.png and b/1-js/02-first-steps/01-hello-world/hello-world-render.png differ diff --git a/1-js/02-first-steps/01-hello-world/hello-world-render@2x.png b/1-js/02-first-steps/01-hello-world/hello-world-render@2x.png index c4411027ca..9b2d4479f6 100644 Binary files a/1-js/02-first-steps/01-hello-world/hello-world-render@2x.png and b/1-js/02-first-steps/01-hello-world/hello-world-render@2x.png differ diff --git a/1-js/02-first-steps/02-structure/article.md b/1-js/02-first-steps/02-structure/article.md index 7b5c702fd7..47387ef906 100644 --- a/1-js/02-first-steps/02-structure/article.md +++ b/1-js/02-first-steps/02-structure/article.md @@ -1,22 +1,38 @@ # 代码结构 +<<<<<<< HEAD 第一件需要学习的事情就是构建代码块。 +======= +The first thing we'll study is the building blocks of code. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 语句 语句是执行操作的语法结构和命令。 +<<<<<<< HEAD 我们已经见过语句 `alert('Hello, world!')`,可以用来显示消息。 我们可以在代码中编写任意数量的语句。语句之间可以使用分号分割。 例如,我们将 Hello World 这条信息一分为二: +======= +We've already seen a statement, `alert('Hello, world!')`, which shows the message "Hello, world!". + +We can have as many statements in our code as we want. Statements can be separated with a semicolon. + +For example, here we split "Hello World" into two alerts: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run no-beautify alert('Hello'); alert('World'); ``` +<<<<<<< HEAD 通常,每条语句在单独的一行书写 —— 这会提高代码的可读性。 +======= +Usually, statements are written on separate lines to make the code more readable: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run no-beautify alert('Hello'); @@ -34,11 +50,19 @@ alert('Hello') alert('World') ``` +<<<<<<< HEAD 此处,JavaScript 将分行符解释成“隐式”的分号。这也被称为[自动分号插入](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion)。 **多数情况下,换行意味着一个分号。但是“多数情况”并不意味着“总是”!** 有很多换行并不是分号的例子,比如: +======= +Here, JavaScript interprets the line break as an "implicit" semicolon. This is called an [automatic semicolon insertion](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion). + +**In most cases, a newline implies a semicolon. But "in most cases" does not mean "always"!** + +There are cases when a newline does not mean a semicolon. For example: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run no-beautify alert(3 + @@ -46,7 +70,11 @@ alert(3 + + 2); ``` +<<<<<<< HEAD 代码输出 `6`,因为 JavaScript 并没有在这里插入分号。 显而易见的是,如果一行以加号 `"+"` 结尾,那么这是一个“不完整的表达式”,不需要分号。所以,这个例子得到了预期的结果。 +======= +The code outputs `6` because JavaScript does not insert semicolons here. It is intuitively obvious that if the line ends with a plus `"+"`, then it is an "incomplete expression", so the semicolon is not required. And in this case that works as intended. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb **但存在 JavaScript 无法假设分号是否真正被需要的情况。** @@ -59,9 +87,15 @@ alert(3 + [1, 2].forEach(alert) ``` +<<<<<<< HEAD 不需要考虑方括号 `[]` 和 `forEach` 的含义,我们接下来会学习它们,现在它们并不重要。让我们记住最终结果:先显示 `1`,然后显示 `2`。 现在我们在代码前面加入一个 `alert`,并且不用分号结束它。 +======= +No need to think about the meaning of the brackets `[]` and `forEach` yet. We'll study them later. For now, just remember the result of the code: it shows `1` then `2`. + +Now, let's add an `alert` before the code and *not* finish it with a semicolon: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run no-beautify alert("There will be an error") @@ -69,7 +103,11 @@ alert("There will be an error") [1, 2].forEach(alert) ``` +<<<<<<< HEAD 现在,如果我们运行代码,仅仅第一个 `alert` 显示了文本,接着我们收到了一个错误! +======= +Now if we run the code, only the first `alert` is shown and then we have an error! +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 但是,如果我们在第一个 `alert` 后加入一个分号,就工作正常了: ```js run @@ -78,27 +116,49 @@ alert("All fine now"); [1, 2].forEach(alert) ``` +<<<<<<< HEAD 现在,我们能得到 "All fine now" ,然后是 `1` 和 `2`。 出现无分号变量的错误,是因为 JavaScript 并不在方括号 `[...]` 前添加一个隐式的分号。 所以,因为分号并不会自动插入,第一个例子被视为一条简单的语句,我们从引擎看到的是这样子的: +======= +Now we have the "All fine now" message followed by `1` and `2`. + + +The error in the no-semicolon variant occurs because JavaScript does not assume a semicolon before square brackets `[...]`. + +So, because the semicolon is not auto-inserted, the code in the first example is treated as a single statement. Here's how the engine sees it: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run no-beautify alert("There will be an error")[1, 2].forEach(alert) ``` +<<<<<<< HEAD 但它应该是两条语句,而不是一条。这种情况下的合并是不对的,所以才会造成错误。诸如此类,还有很多。 ```` 如果语句被换行分割,非常建议在语句之间添加分号。这个规则被社区广泛采纳。让我们再次强调 —— 大部分时候可以省略分号,但是最好是,尤其对于新手,加上它。 +======= +But it should be two separate statements, not one. Such a merging in this case is just wrong, hence the error. This can happen in other situations. +```` + +We recommend putting semicolons between statements even if they are separated by newlines. This rule is widely adopted by the community. Let's note once again -- *it is possible* to leave out semicolons most of the time. But it's safer -- especially for a beginner -- to use them. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 注释 +<<<<<<< HEAD 随着时间推移,程序变得越来越复杂。为代码添加**注释**来描述发生了什么(What happens)和为什么(Why),变得非常有必要了。 注释可以在脚本的任何地方添加,它们并不会影响代码的执行,因为引擎会简单地忽略它们。 +======= +As time goes on, programs become more and more complex. It becomes necessary to add *comments* which describe what the code does and why. + +Comments can be put into any place of a script. They don't affect its execution because the engine simply ignores them. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb **单行注释以两个正斜杠字符 `//` 开始。** @@ -124,9 +184,15 @@ alert('Hello'); alert('World'); ``` +<<<<<<< HEAD 注释的内容被忽略了,所以如果我们在 /* ... */ 中放入代码,并不会执行。 有时候,可以很方便地临时禁用代码: +======= +The content of comments is ignored, so if we put code inside /* ... */, it won't execute. + +Sometimes it can be handy to temporarily disable a part of code: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run /* 注释代码 @@ -135,8 +201,13 @@ alert('Hello'); alert('World'); ``` +<<<<<<< HEAD ```smart header="使用热键!" 在大多数的编辑器中,一行代码可以使用 `key:Ctrl+/` 热键进行单行注释,诸如 `key:Ctrl+Shift+/` 的热键可以进行多行注释(选择代码,然后按下热键)。对于 Mac 电脑,应使用 `key:Cmd` 而不是 `key:Ctrl`。 +======= +```smart header="Use hotkeys!" +In most editors, a line of code can be commented out by pressing the `key:Ctrl+/` hotkey for a single-line comment and something like `key:Ctrl+Shift+/` -- for multiline comments (select a piece of code and press the hotkey). For Mac, try `key:Cmd` instead of `key:Ctrl`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` ````warn header="不支持注释嵌套!" @@ -154,6 +225,12 @@ alert( 'World' ); 注释你的代码,请不要有任何迟疑。 +<<<<<<< HEAD 注释会增加代码总量,但这一点也不是问题。有很多工具可以在你部署到服务器之前缩减代码。这些工具会移除注释,所以注释不会出现在发布的脚本中。所以,注释对我们的生产没有任何负面影响。 在进一步的教程中,会有一章 的内容解释如何书写更好的注释。 +======= +Comments increase the overall code footprint, but that's not a problem at all. There are many tools which minify code before publishing to a production server. They remove comments, so they don't appear in the working scripts. Therefore, comments do not have negative effects on production at all. + +Later in the tutorial there will be a chapter that also explains how to write better comments. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/03-strict-mode/article.md b/1-js/02-first-steps/03-strict-mode/article.md index bb283c5cbd..cb82bf8ab7 100644 --- a/1-js/02-first-steps/03-strict-mode/article.md +++ b/1-js/02-first-steps/03-strict-mode/article.md @@ -1,5 +1,6 @@ # 新模式,"use strict" +<<<<<<< HEAD 长久以来,JavaScript 不断发展且并未带来任何兼容性问题。新特性被加入该语言,旧的功能也没有改变。 这么做有利于兼容旧代码,但缺点是 JavaScript 创造者的任何错误和不完美的考虑也永远地保留在了语言中。 @@ -11,6 +12,19 @@ 这个指令看上去是一个字符串 `"use strict"` 或者 `'use strict'`。当它处于脚本文件的顶部时,则整个脚本文件都将以“现代”模式进行工作。 比如: +======= +For a long time, JavaScript evolved without compatibility issues. New features were added to the language while old functionality didn't change. + +That had the benefit of never breaking existing code. But the downside was that any mistake or an imperfect decision made by JavaScript's creators got stuck in the language forever. + +This was the case until 2009 when ECMAScript 5 (ES5) appeared. It added new features to the language and modified some of the existing ones. To keep the old code working, most modifications are off by default. You need to explicitly enable them with a special directive: `"use strict"`. + +## "use strict" + +The directive looks like a string: `"use strict"` or `'use strict'`. When it is located at the top of a script, the whole script works the "modern" way. + +For example: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js "use strict"; @@ -21,6 +35,7 @@ 稍后我们才会学习函数(一种组合命令的方式)。 +<<<<<<< HEAD 但我们可以提前了解一下,`"use strict"` 可以放在函数(大多数函数)而不是整个脚本的开头。那么严格模式仅在该函数中启用。但通常人们会将它用于整个脚本。 @@ -32,6 +47,19 @@ ```js no-strict alert("some code"); // 下面的 "use strict" 会被忽略,必须在最顶部。 +======= +Looking ahead, let's just note that `"use strict"` can be put at the start of most kinds of functions instead of the whole script. Doing that enables strict mode in that function only. But usually, people use it for the whole script. + + +````warn header="Ensure that \"use strict\" is at the top" +Please make sure that `"use strict"` is at the top of your scripts, otherwise strict mode may not be enabled. + +Strict mode isn't enabled here: + +```js no-strict +alert("some code"); +// "use strict" below is ignored--it must be at the top +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb "use strict"; @@ -41,14 +69,40 @@ alert("some code"); 只有注释可以出现在 `"use strict"` 的上面。 ```` +<<<<<<< HEAD ```warn header="没有办法取消 `use strict`" 没有类似于 `"no use strict"` 这样的指令可以使程序返回默认模式。 一旦进入了严格模式,就没有退路了。 +======= +```warn header="There's no way to cancel `use strict`" +There is no directive like `"no use strict"` that reverts the engine to old behavior. + +Once we enter strict mode, there's no return. +``` + +## Browser console + +For the future, when you use a browser console to test features, please note that it doesn't `use strict` by default. + +Sometimes, when `use strict` makes a difference, you'll get incorrect results. + +Even if we press `key:Shift+Enter` to input multiple lines, and put `use strict` on top, it doesn't work. That's because of how the console executes the code internally. + +The reliable way to ensure `use strict` would be to input the code into console like this: + +```js +(function() { + 'use strict'; + + // ...your code... +})() +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` ## 总是使用 "use strict" +<<<<<<< HEAD 使用 `"use strict"` 与“默认”模式的区别仍然有待完善。 在接下来的章节中,当我们学习语言功能时,我们会记录严格模式的差异。幸运的是,没有那么多。实际上,它们为我们编写代码提供了极大的便利。 @@ -59,3 +113,15 @@ alert("some code"); 2. 严格模式通过顶部的 `"use strict"` 启用。一些新语言特性诸如 "classes" 和 "modules" 也会自动开启严格模式。 3. 所有的现代浏览器都支持严格模式。 4. 建议始终使用 `"use strict"` 启动脚本。本教程的所有例子都默认采用严格模式,除非特别指定(非常少)。 +======= +We have yet to cover the differences between strict mode and the "default" mode. + +In the next chapters, as we learn language features, we'll note the differences between the strict and default modes. Luckily, there aren't many and they actually make our lives better. + +For now, it's enough to know about it in general: + +1. The `"use strict"` directive switches the engine to the "modern" mode, changing the behavior of some built-in features. We'll see the details later in the tutorial. +2. Strict mode is enabled by placing `"use strict"` at the top of a script or function. Several language features, like "classes" and "modules", enable strict mode automatically. +3. Strict mode is supported by all modern browsers. +4. We recommended always starting scripts with `"use strict"`. All examples in this tutorial assume strict mode unless (very rarely) specified otherwise. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/04-variables/2-declare-variables/solution.md b/1-js/02-first-steps/04-variables/2-declare-variables/solution.md index 65194ae7d9..2fecf89655 100644 --- a/1-js/02-first-steps/04-variables/2-declare-variables/solution.md +++ b/1-js/02-first-steps/04-variables/2-declare-variables/solution.md @@ -18,4 +18,8 @@ let currentUserName = "John"; 现代编辑器的自动补全可以让长变量名变得容易书写。不要浪费这个特性。一个名字中包含三个词就挺好的。 +<<<<<<< HEAD 如果你的编辑器没有合适的自动补全,换[一个新的吧](/code-editors)。 +======= +And if your editor does not have proper autocompletion, get [a new one](/code-editors). +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/04-variables/2-declare-variables/task.md b/1-js/02-first-steps/04-variables/2-declare-variables/task.md index a048299cf6..0e411f1780 100644 --- a/1-js/02-first-steps/04-variables/2-declare-variables/task.md +++ b/1-js/02-first-steps/04-variables/2-declare-variables/task.md @@ -4,5 +4,10 @@ importance: 3 # 给出正确的名字 +<<<<<<< HEAD 1. 使用我们的星球的名字创建一个变量。你会怎么命名这个变量? 2. 创建一个变量来存储当前浏览者的名字。你会怎么命名这个变量? +======= +1. Create a variable with the name of our planet. How would you name such a variable? +2. Create a variable to store the name of a current visitor to a website. How would you name that variable? +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/04-variables/article.md b/1-js/02-first-steps/04-variables/article.md index e5b2a3ae17..2bedd03b5a 100644 --- a/1-js/02-first-steps/04-variables/article.md +++ b/1-js/02-first-steps/04-variables/article.md @@ -1,16 +1,28 @@ # 变量 +<<<<<<< HEAD 大多数情况下,脚本需要处理信息。这有两个例子: 1. 一个网上商店 —— 这里的信息可能包含正在售卖的商品和购物车 2. 一个聊天应用 —— 这里的信息可能包括用户、消息等等。 +======= +Most of the time, a JavaScript application needs to work with information. Here are two examples: +1. An online shop -- the information might include goods being sold and a shopping cart. +2. A chat application -- the information might include users, messages, and much more. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 变量就是用来储存这些信息的。 ## 一个变量 +<<<<<<< HEAD 一个 [变量](https://en.wikipedia.org/wiki/Variable_(computer_science)) 是数据的“命名存储”。我们可以使用变量来保存商品、访客和其他信息。 在 JavaScript 中创建一个变量,我们需要用到 `let` 关键字。 +======= +A [variable](https://en.wikipedia.org/wiki/Variable_(computer_science)) is a "named storage" for data. We can use variables to store goodies, visitors, and other data. + +To create a variable in JavaScript, use the `let` keyword. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 下面的语句创建(换句话说,**声明**或者**定义**)了一个名称为 "message" 的变量: @@ -18,7 +30,11 @@ let message; ``` +<<<<<<< HEAD 现在,通过赋值操作符 `=` 为变量添加一些数据: +======= +Now, we can put some data into it by using the assignment operator `=`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js let message; @@ -39,7 +55,11 @@ alert(message); // 显示变量内容 */!* ``` +<<<<<<< HEAD 简洁一点,我们将变量定义和赋值合并成一行: +======= +To be concise, we can combine the variable declaration and assignment into a single line: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let message = 'Hello!'; // 定义变量,并且赋值 @@ -53,7 +73,11 @@ alert(message); // Hello! let user = 'John', age = 25, message = 'Hello'; ``` +<<<<<<< HEAD 看上去代码长度更短,但并不推荐。为了更好的可读性,请一行声明一个变量。 +======= +That might seem shorter, but we don't recommend it. For the sake of better readability, please use a single line per variable. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 多行变量声明有点长,但更容易阅读: @@ -63,7 +87,11 @@ let age = 25; let message = 'Hello'; ``` +<<<<<<< HEAD 一些程序员采用下面的形式书写多个变量: +======= +Some people also define multiple variables in this multiline style: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js no-beautify let user = 'John', age = 25, @@ -78,19 +106,33 @@ let user = 'John' , message = 'Hello'; ``` +<<<<<<< HEAD 技术上讲,这些变体都有一样的效果。所以,这是个个人品味和审美方面的问题。 ````smart header="`var` 而不是 `let`" 在较旧的脚本中,你也可能发现另一个关键字 `var`,而不是 `let`: +======= +Technically, all these variants do the same thing. So, it's a matter of personal taste and aesthetics. + + +````smart header="`var` instead of `let`" +In older scripts, you may also find another keyword: `var` instead of `let`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js *!*var*/!* message = 'Hello'; ``` +<<<<<<< HEAD `var` 关键字与 `let` **大体**相同,也用来声明变量,但稍微有些不同,也有点“老派”。 `let` 和 `var` 之间有些微妙的差别,但目前对于我们来说并不重要。我们将会在 章节中介绍它们。 +======= +The `var` keyword is *almost* the same as `let`. It also declares a variable, but in a slightly different, "old-school" way. + +There are subtle differences between `let` and `var`, but they do not matter for us yet. We'll cover them in detail in the chapter . +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```` ## 一个现实生活的类比 @@ -101,10 +143,15 @@ let user = 'John' ![](variable.png) +<<<<<<< HEAD 我们可以在盒子内放入任何值。 同样,我们也可以改变它。值可以根据需要多次改变。 +======= +We can put any value in the box. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb +We can also change it as many times as we want: ```js run let message; @@ -136,29 +183,51 @@ alert(hello); // Hello world! alert(message); // Hello world! ``` +<<<<<<< HEAD ```smart header="函数式语言" 有趣的是,也存在禁止更改变量值的[函数式](https://en.wikipedia.org/wiki/Functional_programming)编程语言。比如,[Scala](http://www.scala-lang.org/) 或者 [Erlang](http://www.erlang.org/)。 +======= +```smart header="Functional languages" +It's interesting to note that there exist [functional](https://en.wikipedia.org/wiki/Functional_programming) programming languages, like [Scala](http://www.scala-lang.org/) or [Erlang](http://www.erlang.org/) that forbid changing variable values. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 在这种类型的语言中,一旦值保存在盒子中,就永远存在。如果你试图保存其他值,它会强制创建一个新盒子(声明一个新变量),无法重用先前的变量。 +<<<<<<< HEAD 虽然看上去有点奇怪,但是这些语言有很大的发展潜力。不仅如此,在某些领域,比如并行计算,这个限制有一定的好处。研究这样的一门语言(即使不打算很快就用上它)有助于开阔视野。 +======= +Though it may seem a little odd at first sight, these languages are quite capable of serious development. More than that, there are areas like parallel computations where this limitation confers certain benefits. Studying such a language (even if you're not planning to use it soon) is recommended to broaden the mind. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` ## 变量命名 [#variable-naming] +<<<<<<< HEAD JavaScript 的变量命名有两个限制: 1. 变量名称必须仅包含字母,数字,符号 `$` and `_`。 2. 首字符必须非数字。 有效的命名,例如: +======= +There are two limitations on variable names in JavaScript: + +1. The name must contain only letters, digits, or the symbols `$` and `_`. +2. The first character must not be a digit. + +Examples of valid names: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js let userName; let test123; ``` +<<<<<<< HEAD 如果命名包括多个单词,通常采用驼峰式命名法([camelCase](https://en.wikipedia.org/wiki/CamelCase))。也就是,单词一个接一个,每个单词以大写字母开头:`myVeryLongName`。 +======= +When the name contains multiple words, [camelCase](https://en.wikipedia.org/wiki/CamelCase) is commonly used. That is: words go one after another, each word except first starting with a capital letter: `myVeryLongName`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 有趣的是,美元符号 `'$'` 和下划线 `'_'` 也可以在命名中使用。它们是正常的符号,就跟字母一样,没有任何特殊的含义。 @@ -176,6 +245,7 @@ alert($ + _); // 3 ```js no-beautify let 1a; // 不能以数字开始 +<<<<<<< HEAD let my-name; // 连字符 '-' 不允许在命名中出现 ``` @@ -185,6 +255,17 @@ let my-name; // 连字符 '-' 不允许在命名中出现 ````smart header="允许非英文字母,但不推荐" 可以使用任何语言,包括西里尔字母(cyrillic letters)甚至是象形文字,就像这样: +======= +let my-name; // hyphens '-' aren't allowed in the name +``` + +```smart header="Case matters" +Variables named `apple` and `AppLE` are two different variables. +``` + +````smart header="Non-Latin letters are allowed, but not recommended" +It is possible to use any language, including cyrillic letters or even hieroglyphs, like this: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js let имя = '...'; @@ -194,10 +275,17 @@ let 我 = '...'; 技术上讲,完全没有错误,这样的命名是完全允许的,但是有一个国际传统:在变量命名中使用英文。哪怕我们正在写一个很小的脚本,它也有可能有很长的生命周期。某个时候,来自其他国家的人可能会阅读它。 ```` +<<<<<<< HEAD ````warn header="保留字" 有一长串的保留字无法用作变量命名,因为它们被语言本身采用了。 比如,单词 `let`、`class`、`return`、`function` 被保留。 +======= +````warn header="Reserved names" +There is a [list of reserved words](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords), which cannot be used as variable names because they are used by the language itself. + +For example: `let`, `class`, `return`, and `function` are reserved. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 下面的代码将会抛出一个语法错误: @@ -209,37 +297,56 @@ let return = 5; // 同样,不能使用 "return",错误! ````warn header="未采用 `use strict` 下的赋值" +<<<<<<< HEAD 一般,需要在使用一个变量前定义它。但是在旧时代,可以简单地赋值来创建一个变量,而不需要 `let`。如果不使用 `use strict`,这仍然正常工作,这种行为是为了保持与旧脚本的兼容。 +======= +Normally, we need to define a variable before using it. But in the old times, it was technically possible to create a variable by a mere assignment of the value without using `let`. This still works now if we don't put `use strict` in our scripts to maintain compatibility with old scripts. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run no-strict // 注意:这个例子中没有 "use strict" +<<<<<<< HEAD num = 5; // 如果变量 "num" 不存在,就会被创建 +======= +num = 5; // the variable "num" is created if it didn't exist +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb alert(num); // 5 ``` +<<<<<<< HEAD 这是个糟糕的做法,严格模式下会抛出错误。 +======= +This is a bad practice and would cause an error in strict mode: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb -```js run untrusted +```js "use strict"; *!* num = 5; // error: num is not defined */!* ``` - ```` ## 常量 +<<<<<<< HEAD 声明一个常数(不变)变量,可以使用 `const` 而非 `let`: +======= +To declare a constant (unchanging) variable, use `const` instead of `let`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js const myBirthday = '18.04.1982'; ``` +<<<<<<< HEAD 使用 `const` 声明的变量称为“常量”。它们不能被修改,尝试这样做就会造成错误: +======= +Variables declared using `const` are called "constants". They cannot be changed. An attempt to do so would cause an error: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run const myBirthday = '18.04.1982'; @@ -247,7 +354,11 @@ const myBirthday = '18.04.1982'; myBirthday = '01.01.2001'; // 错误,不能对常量重新赋值 ``` +<<<<<<< HEAD 当程序员能确定这个变量永远不会改变的时候,就可以使用 `const` 来确保这种行为,并且清楚地向别人传递这一事实。 +======= +When a programmer is sure that a variable will never change, they can declare it with `const` to guarantee and clearly communicate that fact to everyone. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ### 大写形式的常数 @@ -256,7 +367,11 @@ myBirthday = '01.01.2001'; // 错误,不能对常量重新赋值 这些常量使用大写和下划线命名。 +<<<<<<< HEAD 就像这样: +======= +For instance, let's make constants for colors in so-called "web" (hexadecimal) format: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run const COLOR_RED = "#F00"; @@ -271,6 +386,7 @@ alert(color); // #FF7F00 好处: +<<<<<<< HEAD - `COLOR_ORANGE` 比 `"#FF7F00"` 更容易记忆。 - 比起 `COLOR_ORANGE` 而言,`"#FF7F00"` 更容易输错。 - 阅读代码时,`COLOR_ORANGE` 比 `#FF7F00` 更有含义。 @@ -278,13 +394,26 @@ alert(color); // #FF7F00 什么时候该为常量使用大写命名,什么时候常规命名。让我们弄清楚一点: 作为一个常数,意味着值永远不变。但是有些常量在执行之前就已知了(比如红色的十六进制值),还有些在执行期间实时“计算”,赋值之后才不会改变。 +======= +- `COLOR_ORANGE` is much easier to remember than `"#FF7F00"`. +- It is much easier to mistype `"#FF7F00"` than `COLOR_ORANGE`. +- When reading the code, `COLOR_ORANGE` is much more meaningful than `#FF7F00`. + +When should we use capitals for a constant and when should we name it normally? Let's make that clear. + +Being a "constant" just means that a variable's value never changes. But there are constants that are known prior to execution (like a hexadecimal value for red) and there are constants that are *calculated* in run-time, during the execution, but do not change after their initial assignment. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: ```js const pageLoadTime = /* time taken by a webpage to load */; ``` +<<<<<<< HEAD `pageLoadTime` 的值在页面加载之前是未知的,所以采用常规命名。但是它仍然是个常量,因为赋值之后不会改变。 +======= +The value of `pageLoadTime` is not known prior to the page load, so it's named normally. But it's still a constant because it doesn't change after assignment. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 换句话说,大写命名的常量仅用作“硬编码”值的别名。 @@ -292,6 +421,7 @@ const pageLoadTime = /* time taken by a webpage to load */; 谈到变量,还有一件非常重要的事。 +<<<<<<< HEAD 请合理地命名变量。如果有需要,花时间思考一下。 变量命名是编程过程中最重要和最复杂的技能之一。快速地浏览变量的命名就知道代码是一个初学者还是有经验的开发者书写的。 @@ -299,9 +429,19 @@ const pageLoadTime = /* time taken by a webpage to load */; 在一个现实的项目中,大多数的时间用来修改和扩展现有的代码库,而不是从头开始写一些完全独立的代码。当一段时间后,我们做完其他事情,重返我们的代码,找到命名良好的信息要容易得多。换句话说,变量要有个好名字。 声明变量之前,多花点时间思考它的良好命名。你会受益良多。 +======= +A variable name should have a clean, obvious meaning, describe the data that it stores. + +Variable naming is one of the most important and complex skills in programming. A quick glance at variable names can reveal which code was written by a beginner versus an experienced developer. + +In a real project, most of the time is spent modifying and extending an existing code base rather than writing something completely separate from scratch. When we return to some code after doing something else for a while, it's much easier to find information that is well-labeled. Or, in other words, when the variables have good names. + +Please spend time thinking about the right name for a variable before declaring it. Doing so will repay you handsomely. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 一些很好遵循的规则是: +<<<<<<< HEAD - 使用人类可读的命名,比如 `userName` 或者 `shoppingCart`。 - 诸如 `a`、`b`、`c` 的缩写和短名称,离它们远点,除非你真的知道你在干什么。 - 变量名要能够准确描述变量并且足够简洁。不好的例子就是 `data` 和 `value`,这样的名称等于什么都没说。如果能够非常明显地从上下文知道数据和值所表达的含义,这样使用它们也是可以的。 @@ -315,18 +455,45 @@ const pageLoadTime = /* time taken by a webpage to load */; 结果是,这个变量就像是被扔进不同东西盒子,而没有改变它的贴纸。现在里面是什么?谁知道呢。我们需要靠近一点,然后检查它。 这样的程序员节省了一点变量声明的时间,但却在调试代码的时候损失数十倍。 +======= +- Use human-readable names like `userName` or `shoppingCart`. +- Stay away from abbreviations or short names like `a`, `b`, `c`, unless you really know what you're doing. +- Make names maximally descriptive and concise. Examples of bad names are `data` and `value`. Such names say nothing. It's only okay to use them if the context of the code makes it exceptionally obvious which data or value the variable is referencing. +- Agree on terms within your team and in your own mind. If a site visitor is called a "user" then we should name related variables `currentUser` or `newUser` instead of `currentVisitor` or `newManInTown`. + +Sounds simple? Indeed it is, but creating descriptive and concise variable names in practice is not. Go for it. + +```smart header="Reuse or create?" +And the last note. There are some lazy programmers who, instead of declaring new variables, tend to reuse existing ones. + +As a result, their variables are like boxes into which people throw different things without changing their stickers. What's inside the box now? Who knows? We need to come closer and check. + +Such programmers save a little bit on variable declaration but lose ten times more on debugging. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 额外声明一个变量绝对是利大于弊的。 +<<<<<<< HEAD 目前的 JavaScript 压缩器和浏览器都很够很好地优化代码,所以不会产生性能问题。为不同的值使用不同的变量可以帮助引擎进行优化。 +======= +Modern JavaScript minifiers and browsers optimize code well enough, so it won't create performance issues. Using different variables for different values can even help the engine optimize your code. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` ## 总结 +<<<<<<< HEAD 我们可以声明变量来存储数据。可以通过使用 `var`、`let` 或者 `const` 来完成。 +======= +We can declare variables to store data by using the `var`, `let`, or `const` keywords. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - `let` -- 新时代的变量声明方式。Chrome(V8)中代码必须开启严格模式以使用 `let`。 - `var` -- 旧时代的变量声明方式。一般情况下,我们不会使用它。但是,我们会在 章节介绍 `var` 和 `let` 的微妙差别,以防你需要它们。 - `const` -- 类似于`let`,但是变量的值无法被修改。 +<<<<<<< HEAD 变量应当以一种容易理解变量内部是什么的方式进行命名。 +======= +Variables should be named in a way that allows us to easily understand what's inside them. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/04-variables/variable-change.png b/1-js/02-first-steps/04-variables/variable-change.png index 6dd3803d97..9135e930a0 100644 Binary files a/1-js/02-first-steps/04-variables/variable-change.png and b/1-js/02-first-steps/04-variables/variable-change.png differ diff --git a/1-js/02-first-steps/04-variables/variable-change@2x.png b/1-js/02-first-steps/04-variables/variable-change@2x.png index f57b04ab16..c9569e6386 100644 Binary files a/1-js/02-first-steps/04-variables/variable-change@2x.png and b/1-js/02-first-steps/04-variables/variable-change@2x.png differ diff --git a/1-js/02-first-steps/04-variables/variable.png b/1-js/02-first-steps/04-variables/variable.png index ab532d91d3..6d24825561 100644 Binary files a/1-js/02-first-steps/04-variables/variable.png and b/1-js/02-first-steps/04-variables/variable.png differ diff --git a/1-js/02-first-steps/04-variables/variable@2x.png b/1-js/02-first-steps/04-variables/variable@2x.png index c9c37f034e..845f344084 100644 Binary files a/1-js/02-first-steps/04-variables/variable@2x.png and b/1-js/02-first-steps/04-variables/variable@2x.png differ diff --git a/1-js/02-first-steps/05-types/article.md b/1-js/02-first-steps/05-types/article.md index e4041b1b8a..f96d1b541d 100644 --- a/1-js/02-first-steps/05-types/article.md +++ b/1-js/02-first-steps/05-types/article.md @@ -1,6 +1,10 @@ # 数据类型 +<<<<<<< HEAD JavaScript 中的变量可以保存任何数据。变量在前一刻可以是个字符串,然后又收到一个数值: +======= +A variable in JavaScript can contain any data. A variable can at one moment be a string and at another be a number: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // 没有错误 @@ -10,7 +14,11 @@ message = 123456; 允许这种操作的编程语言称为“动态类型”(dynamically typed)的编程语言,意思是,拥有数据类型,但是变量并不限于数据类型中的任何一个。 +<<<<<<< HEAD 在 JavaScript 中有七种基本数据类型。这一章我们会学习基本知识,下一章我们会详细介绍它们。 +======= +There are seven basic data types in JavaScript. Here, we'll cover them in general and in the next chapters we'll talk about each of them in detail. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## number 类型 @@ -19,11 +27,19 @@ let n = 123; n = 12.345; ``` +<<<<<<< HEAD *number* 类型用于整数和浮点数。 数字有很多操作,比如,乘法 `*`、除法 `/`、加法 `+`、减法 `-` 等等。 除了常规的数字,还包括所谓的“特殊数值”也属于这种类型:`Infinity`、`-Infinity` 和 `NaN`。 +======= +The *number* type represents both integer and floating point numbers. + +There are many operations for numbers, e.g. multiplication `*`, division `/`, addition `+`, subtraction `-`, and so on. + +Besides regular numbers, there are so-called "special numeric values" which also belong to this data type: `Infinity`, `-Infinity` and `NaN`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - `Infinity` 代表数学概念中的[无穷大](https://en.wikipedia.org/wiki/Infinity) ∞。是一个比任何数字都大的特殊值。 @@ -33,7 +49,11 @@ n = 12.345; alert( 1 / 0 ); // Infinity ``` +<<<<<<< HEAD 或者在代码中直接提及它。 +======= + Or just reference it directly: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run alert( Infinity ); // Infinity @@ -44,12 +64,17 @@ n = 12.345; alert( "not a number" / 2 ); // NaN, 这样的除法是错误的 ``` +<<<<<<< HEAD `NaN` 是粘性的。任何对 `NaN` 的进一步操作都会给出 `NaN`: +======= + `NaN` is sticky. Any further operation on `NaN` returns `NaN`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run alert( "not a number" / 2 + 5 ); // NaN ``` +<<<<<<< HEAD 所以,如果在数学表达式中有一个 `NaN`,会被传播到最终结果。 ```smart header="数学运算是安全的" @@ -59,12 +84,27 @@ n = 12.345; ``` 特殊的数值属于 **number** 类型。当然,对这个词的一般认识是,它们并不是数字。 +======= + So, if there's a `NaN` somewhere in a mathematical expression, it propagates to the whole result. + +```smart header="Mathematical operations are safe" +Doing maths is "safe" in JavaScript. We can do anything: divide by zero, treat non-numeric strings as numbers, etc. + +The script will never stop with a fatal error ("die"). At worst, we'll get `NaN` as the result. +``` + +Special numeric values formally belong to the "number" type. Of course they are not numbers in the common sense of this word. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 我们将在章节 了解更多有关使用数字的内容。 ## string 类型 +<<<<<<< HEAD JavaScript 中的字符串必须被包含在引号里面。 +======= +A string in JavaScript must be surrounded by quotes. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js let str = "Hello"; @@ -92,9 +132,15 @@ alert( `Hello, *!*${name}*/!*!` ); // Hello, John! alert( `the result is *!*${1 + 2}*/!*` ); // 结果是 3 ``` +<<<<<<< HEAD `${…}` 内的表达式会被计算,结果成为字符串的一部分。可以在 `${…}` 内放置任何东西:诸如 `name` 的变量,或者诸如 `1 + 2` 的算数表达式,或者其他一些更复杂的。 需要注意的是,这仅仅在反引号内有效,其他引号不允许这种嵌入。 +======= +The expression inside `${…}` is evaluated and the result becomes a part of the string. We can put anything in there: a variable like `name` or an arithmetical expression like `1 + 2` or something more complex. + +Please note that this can only be done in backticks. Other quotes don't have this embedding functionality! +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run alert( "the result is ${1 + 2}" ); // 结果是 ${1 + 2} (双引号什么也不做) ``` @@ -128,31 +174,57 @@ let isGreater = 4 > 1; alert( isGreater ); // true (比较的结果是 "yes") ``` +<<<<<<< HEAD 更详细的内容将会在章节 进行介绍。 +======= +We'll cover booleans more deeply in the chapter . +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## "null" 值 +<<<<<<< HEAD 特殊的 `null` 值不属于上述任何一种类型。 它构成一个独立的类型,只包含 `null` 值: +======= +The special `null` value does not belong to any of the types described above. + +It forms a separate type of its own which contains only the `null` value: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js let age = null; ``` +<<<<<<< HEAD 相比较于其他语言,JavaScript 中的 `null` 不是一个“对不存在对象的引用”或者 “null 指针”。 仅仅是一个含义为“无”、“空”或“值未知”的特殊值。 上面的代码表示,由于某些原因,`age` 是未知的。 +======= +In JavaScript, `null` is not a "reference to a non-existing object" or a "null pointer" like in some other languages. + +It's just a special value which represents "nothing", "empty" or "value unknown". + +The code above states that `age` is unknown or empty for some reason. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## "undefined" 值 +<<<<<<< HEAD 特殊值和 `null` 一样,自成类型。 +======= +The special value `undefined` also stands apart. It makes a type of its own, just like `null`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb `undefined` 的含义是 `未被赋值`。 +<<<<<<< HEAD 如果变量被声明,而未被赋值,那么它的值就是 `undefined`: +======= +If a variable is declared, but not assigned, then its value is `undefined`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let x; @@ -170,26 +242,47 @@ x = undefined; alert(x); // "undefined" ``` +<<<<<<< HEAD ...但是不建议这样做。通常,使用使用 `null` 将一个“空”或者“未知”的值写入变量中,`undefined` 仅仅用于检验,以查看变量是否被赋值或者其他类似的操作。 +======= +...But we don't recommend doing that. Normally, we use `null` to assign an "empty" or "unknown" value to a variable, and we use `undefined` for checks like seeing if a variable has been assigned. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## object 类型和 symbol 类型 `object` 类型是特殊的类型。 +<<<<<<< HEAD 其他所有的类型都称为“原生类型”,因为它们的值只包含一个单独的东西(字符串、数字或者其他)。相反,对象用于储存数据集合和更复杂的实体。在充分了解原生类型之后,我们会在章节 介绍对象。 `symbol` 类型用于创建对象的唯一标识符。为了学习的完整性,我们在这里提到 `symbol` 类型,但最好在学完对象之后再学习它。 +======= +All other types are called "primitive" because their values can contain only a single thing (be it a string or a number or whatever). In contrast, objects are used to store collections of data and more complex entities. We'll deal with them later in the chapter after we learn more about primitives. + +The `symbol` type is used to create unique identifiers for objects. We have to mention it here for completeness, but it's better to study this type after objects. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## typeof 运算符 [#type-typeof] +<<<<<<< HEAD `typeof` 运算符返回参数的类型。当我们想要分别处理不同类型值的时候,或者简单地进行检验,就很有用。 +======= +The `typeof` operator returns the type of the argument. It's useful when we want to process values of different types differently or just want to do a quick check. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 它支持两种语法形式: +<<<<<<< HEAD 1. 作为运算符:`typeof x`。 2. 函数形式:`typeof(x)`。 换言之,有括号和没有括号,结果是一样的。 +======= +1. As an operator: `typeof x`. +2. As a function: `typeof(x)`. + +In other words, it works with parentheses or without them. The result is the same. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 对 `typeof x` 的调用返回数据类型的字符串。 @@ -217,16 +310,25 @@ typeof alert // "function" (3) */!* ``` +<<<<<<< HEAD 最后三行可能需要额外的说明: 1. `Math` 是一个提供数学运算的内建对象。我们会在章节 学习它。此处作为一个对象的例子。 2. `typeof null` 的结果是 `"object"`。这是不对的。这是官方在 `typeof` 方面承认的错误,只是为了兼容性而保留。当然,`null` 不是一个对象。它有自己的类型,是一个特殊值。再次强调,这是语言中的一个错误。 3. `typeof alert` 的结果是 `"function"`,因为 `alert` 在语言中是一个函数。我们会在下一章学习函数,那时我们会了解到,在语言中没有一个特别的 "function" 类型。函数隶属于 object 类型。但是 `typeof` 会对函数区分对待。这不正确,但在实践中非常方便。 +======= +The last three lines may need additional explanation: + +1. `Math` is a built-in object that provides mathematical operations. We will learn it in the chapter . Here, it serves just as an example of an object. +2. The result of `typeof null` is `"object"`. That's wrong. It is an officially recognized error in `typeof`, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own. So, again, this is an error in the language. +3. The result of `typeof alert` is `"function"`, because `alert` is a function of the language. We'll study functions in the next chapters where we'll see that there's no special "function" type in JavaScript. Functions belong to the object type. But `typeof` treats them differently. Formally, it's incorrect, but very convenient in practice. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 总结 JavaScript 中有七种基本的类型。 +<<<<<<< HEAD - `number` 用于任何类型的数字:整数或者浮点数。 - `string` 用于字符串。一个字符串可以包含一个或多个字符,所以没有单独的单字符类型。 - `boolean` 用于 `true` 和 `false`。 @@ -234,11 +336,24 @@ JavaScript 中有七种基本的类型。 - `undefined` 用于未定义的值 —— 只有一个 `undefined` 值的独立类型。 - `object` 用于更复杂的数据结构。 - `symbol` 用于唯一的标识符。 +======= +There are 7 basic data types in JavaScript. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb `typeof` 运算符可以查看变量的类型。 +<<<<<<< HEAD - 两种形式:`typeof x` 或者 `typeof(x)`。 - 返回的类型的字符串,比如 `"string"`。 - `null` 返回 `"object"` —— 这是语言中的一个错误,实际上它并不是一个对象。 在接下来的章节中,我们将重点介绍原生类型值,一旦掌握了,我们将继续讨论对象。 +======= +The `typeof` operator allows us to see which type is stored in a variable. + +- Two forms: `typeof x` or `typeof(x)`. +- Returns a string with the name of the type, like `"string"`. +- For `null` returns `"object"` -- this is an error in the language, it's not actually an object. + +In the next chapters, we'll concentrate on primitive values and once we're familiar with them, we'll move on to objects. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md b/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md index bbb50e78bb..f73833c371 100644 --- a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md +++ b/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md @@ -10,13 +10,22 @@ true + false = 1 "4" - 2 = 2 "4px" - 2 = NaN 7 / 0 = Infinity -" -9\n" + 5 = " -9\n5" -" -9\n" - 5 = -14 -null + 1 = 1 // (3) -undefined + 1 = NaN // (4) +" -9 " + 5 = " -9 5" // (3) +" -9 " - 5 = -14 // (4) +null + 1 = 1 // (5) +undefined + 1 = NaN // (6) ``` +<<<<<<< HEAD 1. 字符串的加法 `"" + 1` 会将 `1` 转换为一个字符串:`"" + 1 = "1"`,然后我们得到了 `"1" + 0`,再次应用同样的规则。 2. 减法 `-` (就像大多数数学操作那样)只能用于数字,它会将空字符串 `""` 转换为 `0`。 3. `null` 经过数字化转换之后会变为 `0`。 4. `undefined` 经过数字化转换之后会变为 `NaN`。 +======= +1. The addition with a string `"" + 1` converts `1` to a string: `"" + 1 = "1"`, and then we have `"1" + 0`, the same rule is applied. +2. The subtraction `-` (like most math operations) only works with numbers, it converts an empty string `""` to `0`. +3. The addition with a string appends the number `5` to the string. +4. The subtraction always converts to numbers, so it makes `" -9 "` a number `-9` (ignoring spaces around it). +5. `null` becomes `0` after the numeric conversion. +6. `undefined` becomes `NaN` after the numeric conversion. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/task.md b/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/task.md index a44227ce4f..a14e966630 100644 --- a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/task.md +++ b/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/task.md @@ -17,8 +17,8 @@ true + false "4" - 2 "4px" - 2 7 / 0 -" -9\n" + 5 -" -9\n" - 5 +" -9 " + 5 +" -9 " - 5 null + 1 undefined + 1 ``` diff --git a/1-js/02-first-steps/06-type-conversions/article.md b/1-js/02-first-steps/06-type-conversions/article.md index cf4b446a3d..5c5a7c1f24 100644 --- a/1-js/02-first-steps/06-type-conversions/article.md +++ b/1-js/02-first-steps/06-type-conversions/article.md @@ -1,13 +1,24 @@ # 类型转换 +<<<<<<< HEAD 大多数情况下,运算符和函数会自动转换将值转换为正确的类型。称之为“类型转换”。 +======= +Most of the time, operators and functions automatically convert the values given to them to the right type. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 比如,`alert` 会自动将任何值转换为字符串。算术运算符会将值转换为数字。 +<<<<<<< HEAD 还有些例子,需要显式进行类型转换,以得到正确的结果。 ```smart header="对象还未纳入讨论中" 本章并不会讨论 object 类型。先学习原始类型,之后我们会学习 object 类型。对象的类型转换在章节 介绍。 +======= +There are also cases when we need to explicitly convert a value to the expected type. + +```smart header="Not talking about objects yet" +In this chapter, we won't cover objects. Instead, we'll study primitives first. Later, after we learn about objects, we'll see how object conversion works in the chapter . +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` ## ToString @@ -16,7 +27,11 @@ 比如,`alert(value)` 将 `value` 转换为 string 类型,然后显示这个值。 +<<<<<<< HEAD 也可以显式地调用 `String(value)` 来达到这一目的: +======= +We can also call the `String(value)` function to convert a value to a string: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let value = true; @@ -28,7 +43,11 @@ alert(typeof value); // string */!* ``` +<<<<<<< HEAD 转换为 string 类型的值往往是可预见的。`false` 变成 `"false"`,`null` 变成 `"null"` 等。 +======= +String conversion is mostly obvious. A `false` becomes `"false"`, `null` becomes `"null"`, etc. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## ToNumber @@ -40,7 +59,11 @@ alert(typeof value); // string alert( "6" / "2" ); // 3, strings are converted to numbers ``` +<<<<<<< HEAD 也可以使用 `Number(value)` 显式地将这个值转换为 number 类型。 +======= +We can use the `Number(value)` function to explicitly convert a `value` to a number: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let str = "123"; @@ -51,9 +74,15 @@ let num = Number(str); // 变成 number 类型 123 alert(typeof num); // number ``` +<<<<<<< HEAD 当从 string 类型源读取值时,比如一个文本表单,但是我们期待数字输入,就经常进行显式转换。 如果字符串不是一个有效的数字,转换的结果会是 `NaN`,例如: +======= +Explicit conversion is usually required when we read a value from a string-based source like a text form but expect a number to be entered. + +If the string is not a valid number, the result of such a conversion is `NaN`. For instance: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let age = Number("an arbitrary string instead of a number"); @@ -67,8 +96,13 @@ number 类型转换规则: |-------|-------------| |`undefined`|`NaN`| |`null`|`0`| +<<<<<<< HEAD |true 和 false | `1` and `0` | | `string` | 字符串开始和末尾的空白会被移除,剩下的如果是空字符串,结果为 `0`,否则 —— 从字符串中读出数字。错误返回 `NaN`。 | +======= +|true and false | `1` and `0` | +| `string` | Whitespaces from the start and end are removed. If the remaining string is empty, the result is `0`. Otherwise, the number is "read" from the string. An error gives `NaN`. | +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: @@ -79,31 +113,53 @@ alert( Number(true) ); // 1 alert( Number(false) ); // 0 ``` +<<<<<<< HEAD 请注意 `null` 和 `undefined` 有点不同。`null` 变成数字 `0`,`undefined` 变成 `NaN`。 ````smart header="加号'+' 连接字符串" 几乎所有的算术运算符都将值转换为数字,加号 `+` 是个例外。如果其中一个运算元是字符串,则另一个也会转换为字符串。 然后,连接两者: +======= +Please note that `null` and `undefined` behave differently here: `null` becomes zero while `undefined` becomes `NaN`. + +````smart header="Addition '+' concatenates strings" +Almost all mathematical operations convert values to numbers. A notable exception is addition `+`. If one of the added values is a string, the other one is also converted to a string. + +Then, it concatenates (joins) them: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run alert( 1 + '2' ); // '12' (字符串在加号右边) alert( '1' + 2 ); // '12' (字符串在加号左边) ``` +<<<<<<< HEAD 这仅仅发生在其中一方为字符串(译者注:或者双方都为字符串)的情况下。其他情况下会被转换为数字。 +======= +This only happens when at least one of the arguments is a string. Otherwise, values are converted to numbers. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```` ## ToBoolean 转换为 boolean 类型是最为简单的一个。 +<<<<<<< HEAD 逻辑操作或显式调用 `Boolean(value)` 会触发 boolean 类型转换。 +======= +It happens in logical operations (later we'll meet condition tests and other similar things) but can also be performed explicitly with a call to `Boolean(value)`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 转换规则如下: +<<<<<<< HEAD - 假值,比如 `0`、空的字符串、`null`、`undefined` 和 `NaN` 变成 `false`。 - 其他值变成 `true`。 +======= +- Values that are intuitively "empty", like `0`, an empty string, `null`, `undefined`, and `NaN`, become `false`. +- Other values become `true`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 比如: @@ -115,8 +171,13 @@ alert( Boolean("hello") ); // true alert( Boolean("") ); // false ``` +<<<<<<< HEAD ````warn header="请注意: 包含 0 的字符串 `\"0\"` 是 `true`" 一些编程语言(比如 PHP)视 `"0"` 为 `false`。但在 JavaScript 中,非空的字符串总是 `true`。 +======= +````warn header="Please note: the string with zero `\"0\"` is `true`" +Some languages (namely PHP) treat `"0"` as `false`. But in JavaScript, a non-empty string is always `true`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run alert( Boolean("0") ); // true @@ -127,11 +188,19 @@ alert( Boolean(" ") ); // 空白, 也是 true (任何非空字符串是 true) ## 总结 +<<<<<<< HEAD 有三种常用的类型转换:转换为 string 类型、转换为 number 类型和转换为 boolean 类型。 **`ToString`** —— 输出内容时 `ToString` 发生转换,或通过 `String(value)` 进行显式转换。原始类型值的 string 类型转换通常是可预见的。 **`ToNumber`** -- 进行算术操作时发生 `ToNumber` 转换,或通过 `Number(value)` 进行显式转换。 +======= +The three most widely used type conversions are to string, to number, and to boolean. + +**`ToString`** -- Occurs when we output something. Can be performed with `String(value)`. The conversion to string is usually obvious for primitive values. + +**`ToNumber`** -- Occurs in math operations. Can be performed with `Number(value)`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb `ToNumber` 转换遵循以下规则: @@ -142,7 +211,11 @@ alert( Boolean(" ") ); // 空白, 也是 true (任何非空字符串是 true) |true / false | `1 / 0` | | `string` | 字符串“按原样读取”,两端的空白被忽略。空字符串变成 `0`。出错变成 `NaN`。 | +<<<<<<< HEAD **`ToBoolean`** -- 进行逻辑操作时发生 `ToBoolean` 转换。或通过 `Boolean(value)` 进行显式转换。 +======= +**`ToBoolean`** -- Occurs in logical operations. Can be performed with `Boolean(value)`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb `ToBoolean` 遵循以下规则: @@ -157,4 +230,8 @@ alert( Boolean(" ") ); // 空白, 也是 true (任何非空字符串是 true) - `undefined` 进行 `ToNumber` 时变成 `NaN`,而非 `0`。 - `"0"` 和只有空格的字符串(比如:`" "` )在进行 `ToBoolean` 变成 `true`。 +<<<<<<< HEAD 对象的转换并未在此提及,我们会在章节 介绍,随后我们会学习 JavaScript 更多基础的细节。 +======= +Objects aren't covered here. We'll return to them later in the chapter that is devoted exclusively to objects after we learn more basic things about JavaScript. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/07-operators/article.md b/1-js/02-first-steps/07-operators/article.md index 9520a857f3..753f385aea 100644 --- a/1-js/02-first-steps/07-operators/article.md +++ b/1-js/02-first-steps/07-operators/article.md @@ -1,15 +1,28 @@ # 运算符 +<<<<<<< HEAD 从入学开始,我们就熟悉许多运算符,比如说加号 `+`、乘号 `*`、减号 `-` 等。 这个章节,我们将关注一些在学校数学课程中没有涵盖的运算符。 +======= +We know many operators from school. They are things like addition `+`, multiplication `*`, subtraction `-`, and so on. + +In this chapter, we'll concentrate on aspects of operators that are not covered by school arithmetic. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 术语:「一元运算符」、「二元运算符」、「运算元」 +<<<<<<< HEAD 首先,我们简单浏览一下常用术语。 - **运算元** —— 运算符应用的对象。比如说乘法运算 `5 * 2`,有两个运算元:左运算元 `5` 和右运算元 `2`。有时候人们也称其为「参数」。 - 如果一个运算符对应的只有一个运算元,那么它是 **一元运算符**。比如说一元运算符 `-`,它的作用是对数字取反: +======= +Before we move on, let's grasp some common terminology. + +- *An operand* -- is what operators are applied to. For instance, in the multiplication of `5 * 2` there are two operands: the left operand is `5` and the right operand is `2`. Sometimes, people call these "arguments" instead of "operands". +- An operator is *unary* if it has a single operand. For example, the unary negation `-` reverses the sign of a number: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let x = 1; @@ -19,13 +32,18 @@ */!* alert( x ); // -1,一元负号运算符生效 ``` +<<<<<<< HEAD - 如果一个运算符拥有两个运算元,那么它是 **二元运算符**。减号还存在二元运算符形式: +======= +- An operator is *binary* if it has two operands. The same minus exists in binary form as well: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run no-beautify let x = 1, y = 3; alert( y - x ); // 2,二元运算符减号做减运算 ``` +<<<<<<< HEAD 正式说明:我们正在讨论两种不同的运算符:一元负号运算符(单一运算元,改变正负号)和二元运算符减号(两个运算元,做减法)。 ## 字符串连接功能,二元运算符 + @@ -35,13 +53,28 @@ 通常,加号 `+` 用来求和。 但是如果加号 `+` 应用于字符串,它将合并(连接)各个字符串: +======= + Formally, we're talking about two different operators here: the unary negation (single operand: reverses the sign) and the binary subtraction (two operands: subtracts). + +## String concatenation, binary + + +Now, let's see special features of JavaScript operators that are beyond school arithmetics. + +Usually, the plus operator `+` sums numbers. + +But, if the binary `+` is applied to strings, it merges (concatenates) them: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js let s = "my" + "string"; alert(s); // mystring ``` +<<<<<<< HEAD 注意:只要任一运算元是字符串,那么其它运算元也将转化为字符串。 +======= +Note that if one of the operands is a string, the other one is converted to a string too. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 举个例子: @@ -50,7 +83,11 @@ alert( '1' + 2 ); // "12" alert( 2 + '1' ); // "21" ``` +<<<<<<< HEAD 可以看出,字符串在前和在后并不影响这个规则。简单来说:如果任一运算元是字符串,那么其它运算元将被转化为字符串。 +======= +See, it doesn't matter whether the first operand is a string or the second one. The rule is simple: if either operand is a string, the other one is converted into a string as well. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 但是,请注意:运算符的运算方向是由左至右。如果是两个数字,后面再跟一个字符串,那么两个数字会先相加,再转化为字符串: @@ -59,7 +96,11 @@ alert( 2 + '1' ); // "21" alert(2 + 2 + '1' ); // "41" 而不是 "221" ``` +<<<<<<< HEAD 字符串连接和转化是加号 `+` 的一个特性。其它的数学运算符都只对数字有效。通常,他们会转化运算元为数字。 +======= +String concatenation and conversion is a special feature of the binary plus `+`. Other arithmetic operators work only with numbers and always convert their operands to numbers. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 举个例子:减号和除号: @@ -70,9 +111,15 @@ alert( '6' / '2' ); // 3 ## 数字转化功能,一元运算符 + +<<<<<<< HEAD 加号 `+` 有两种形式。一种是以上讨论的二元运算符,还有一种是一元运算符。 一元运算符加号,或者说,加号 `+` 应用于单个值,对数字没有作用。但是如果运算元是非数字,它会将其转化为数字。 +======= +The plus `+` exists in two forms: the binary form that we used above and the unary form. + +The unary plus or, in other words, the plus operator `+` applied to a single value, doesn't do anything to numbers. But if the operand is not a number, the unary plus converts it into a number. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 比如: @@ -91,9 +138,15 @@ alert( +"" ); // 0 */!* ``` +<<<<<<< HEAD 它的效果和 `Number(...)` 相同,但是更加简短。 将字符串转化为数字的需求经常出现。比如,如果我们正在从 HTML 表单中取值,通常得到的都是字符串。 +======= +It actually does the same thing as `Number(...)`, but is shorter. + +The need to convert strings to numbers arises very often. For example, if we are getting values from HTML form fields, they are usually strings. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 如果我们想对他们求和,该怎么办? @@ -106,7 +159,11 @@ let oranges = "3"; alert( apples + oranges ); // "23",二元运算符连接成字符串 ``` +<<<<<<< HEAD 如果我们想把它们当做数字对待,我们需要转化它们,然后再求和: +======= +If we want to treat them as numbers, we need to convert and then sum them: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let apples = "2"; @@ -121,6 +178,7 @@ alert( +apples + +oranges ); // 5 // alert( Number(apples) + Number(oranges) ); // 5 ``` +<<<<<<< HEAD 从一个数学家的视角来看,大量的加号可能很奇怪。但是从一个程序员的视角,没什么好奇怪的:一元运算符首先起作用,他们将字符串转为数字,然后二元运算符加号对它们求和。 为什么一元运算符先于二元运算符作用于运算元?接下去我们将讨论到,这是由于它们拥有更高的 **优先级**。 @@ -136,6 +194,23 @@ alert( +apples + +oranges ); // 5 在 JavaScript 中有众多运算符。每个运算符都有对应的优先级数字。数字越大,越先执行。如果优先级相同,那么执行顺序由左至右。 摘抄自 [优先级表](https://developer.mozilla.org/en/JavaScript/Reference/operators/operator_precedence) (你不必记住它,只要知道一元运算符优先级高于二元运算符): +======= +From a mathematician's standpoint, the abundance of pluses may seem strange. But from a programmer's standpoint, there's nothing special: unary pluses are applied first, they convert strings to numbers, and then the binary plus sums them up. + +Why are unary pluses applied to values before the binary ones? As we're going to see, that's because of their *higher precedence*. + +## Operator precedence + +If an expression has more than one operator, the execution order is defined by their *precedence*, or, in other words, the implicit priority order of operators. + +From school, we all know that the multiplication in the expression `1 + 2 * 2` should be calculated before the addition. That's exactly the precedence thing. The multiplication is said to have *a higher precedence* than the addition. + +Parentheses override any precedence, so if we're not satisfied with the implicit order, we can use them to change it. For example: `(1 + 2) * 2`. + +There are many operators in JavaScript. Every operator has a corresponding precedence number. The one with the larger number executes first. If the precedence is the same, the execution order is from left to right. + +Here's an extract from the [precedence table](https://developer.mozilla.org/en/JavaScript/Reference/operators/operator_precedence) (you don't need to remember this, but note that unary operators are higher than corresponding binary ones): +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb | Precedence | Name | Sign | |------------|------|------| @@ -150,13 +225,21 @@ alert( +apples + +oranges ); // 5 | 3 | assignment | `=` | | ... | ... | ... | +<<<<<<< HEAD 我们可以看到,「一元运算符 加号」 拥有优先级 `16`,高于「二元运算符 加法」的优先级 `13`。这也是为什么表达式 `"+apples + +oranges"` 一元加号先生效,然后才是二元加法。 +======= +As we can see, the "unary plus" has a priority of `16` which is higher than the `13` of "addition" (binary plus). That's why, in the expression `"+apples + +oranges"`, unary pluses work before the addition. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 赋值运算符 我们知道赋值符号 `=` 也是一个运算符。在优先级表中显示它的优先级非常低,只有 `3`。 +<<<<<<< HEAD 这也是为什么,当我们赋值时,比如 `x = 2 * 2 + 1`,所有的计算先执行,然后 `=` 执行,将计算结果存储到 `x`。 +======= +That's why, when we assign a variable, like `x = 2 * 2 + 1`, the calculations are done first and then the `=` is evaluated, storing the result in `x`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js let x = 2 * 2 + 1; @@ -178,14 +261,25 @@ alert( b ); // 4 alert( c ); // 4 ``` +<<<<<<< HEAD 链式赋值由右到左执行。首先最右侧表达式 `2 + 2` 执行,然后将结果赋值给左侧:`c`、`b`、`a`。最后,所有的变量都共享一个值。 ````smart header="赋值运算符 `\"=\"` 返回一个值" 每个运算符都有一个返回值。对于以加号 `+` 或者乘号 `*` 为例的大部分运算符而言,这一点很显然。对于赋值运算符而言,这一点同样适用。 +======= +Chained assignments evaluate from right to left. First, the rightmost expression `2 + 2` is evaluated and then assigned to the variables on the left: `c`, `b` and `a`. At the end, all the variables share a single value. + +````smart header="The assignment operator `\"=\"` returns a value" +An operator always returns a value. That's obvious for most of them like addition `+` or multiplication `*`. But the assignment operator follows this rule too. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 语句 `x = value` 把 `value` 的值写入 `x` *然后返回 x*。 +<<<<<<< HEAD 下面是一个在复杂语句中使用赋值的例子: +======= +Here's a demo that uses an assignment as part of a more complex expression: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let a = 1; @@ -201,12 +295,20 @@ alert( c ); // 0 以上这个例子,`(a = b + 1)` 的结果是赋值给 `a` 的值(即是 3)。然后该值用于与 `3` 相减。 +<<<<<<< HEAD 有趣的代码,不是吗?我们应该理解它的原理,因为我们有时会在第三方库中见到这样的写法,我们自己不应该这样写。这样的小技巧让代码变得不整洁,阅读性也变差。 +======= +Funny code, isn't it? We should understand how it works, because sometimes we see it in 3rd-party libraries, but shouldn't write anything like that ourselves. Such tricks definitely don't make code clearer or readable. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```` ## 求余运算符 % +<<<<<<< HEAD 运算符 `%` 尽管看上去是个百分号,但和百分数没有什么关系。 +======= +The remainder operator `%`, despite its appearance, is not related to percents. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb `a % b` 的结果是 `a` 除以 `b` 的余数。 @@ -232,7 +334,13 @@ alert( 2 ** 3 ); // 8 (2 * 2 * 2) alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2) ``` +<<<<<<< HEAD 这个运算符对于 `a` 和 `b` 是非整数的情况依然适用,举个例子: +======= +The operator works for non-integer numbers as well. + +For instance: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run alert( 4 ** (1/2) ); // 2 (1 / 2 幂相当于开方,这是数学常识) @@ -245,7 +353,11 @@ alert( 8 ** (1/3) ); // 2 (1 / 3 幂相当于开三次方) 对一个数进行加操作或者减操作是最常见的数值运算符。 +<<<<<<< HEAD 因此,围绕着它们,有一些特殊的运算符: +======= +So, there are special operators for it: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - **自相加** `++` 将变量与1相加: @@ -263,6 +375,7 @@ alert( 8 ** (1/3) ); // 2 (1 / 3 幂相当于开三次方) ``` ```warn +<<<<<<< HEAD 自相加/自相减只能应用于变量。尝试将其应用于数值(比如 `5++`)会报错。 ``` @@ -272,12 +385,29 @@ alert( 8 ** (1/3) ); // 2 (1 / 3 幂相当于开三次方) - 当运算符在变量前,被称为「前置形式」:`++counter`。 两者都做同一件事:将变量 `counter` 与 `1` 相加。 +======= +Increment/decrement can only be applied to variables. Trying to use it on a value like `5++` will give an error. +``` + +The operators `++` and `--` can be placed either before or after a variable. + +- When the operator goes after the variable, it is in "postfix form": `counter++`. +- The "prefix form" is when the operator goes before the variable: `++counter`. + +Both of these statements do the same thing: increase `counter` by `1`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 那么,有什么区别呢?有,但只有当我们需要 `++/--` 的返回值时才能看到区别。 +<<<<<<< HEAD 让我们来明确这一点。我们知道,所有的运算符都有返回值。自相加/自相减也不例外。前置形式返回一个新的值,但后置返回原来的值(做加法/减法之前的值)。 为了直观看到区别,看下面的例子: +======= +Let's clarify. As we know, all operators return a value. Increment/decrement is no exception. The prefix form returns the new value while the postfix form returns the old value (prior to increment/decrement). + +To see the difference, here's an example: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let counter = 1; @@ -286,9 +416,15 @@ let a = ++counter; // (*) alert(a); // *!*2*/!* ``` +<<<<<<< HEAD `(*)` 所在的行是前置形式 `++counter`,对 `counter` 做自相加,返回的是新的值 `2`。因此 `alert` 显示的是 `2`。 下面让我们看看后置形式: +======= +In the line `(*)`, the *prefix* form `++counter` increments `counter` and returns the new value, `2`. So, the `alert` shows `2`. + +Now, let's use the postfix form: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let counter = 1; @@ -297,11 +433,19 @@ let a = counter++; // (*) 将 ++counter 改为 counter++ alert(a); // *!*1*/!* ``` +<<<<<<< HEAD `(*)` 所在的行是后置形式 `counter++`,它同样对 `counter` 做加法,但是返回的是**旧值**(做加法之前)。因此 `alert` 显示的是 `1`。 +======= +In the line `(*)`, the *postfix* form `counter++` also increments `counter` but returns the *old* value (prior to increment). So, the `alert` shows `1`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 总结: +<<<<<<< HEAD - 如果自相加/自相减的值不会被使用,那么两者形式没有区别: +======= +- If the result of increment/decrement is not used, there is no difference in which form to use: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let counter = 0; @@ -309,21 +453,34 @@ alert(a); // *!*1*/!* ++counter; alert( counter ); // 2,以上两行作用相同 ``` +<<<<<<< HEAD - 如果我们想要对变量自相加 **并且** 立刻使用值,那么我们需要使用前置形式: +======= +- If we'd like to increase a value *and* immediately use the result of the operator, we need the prefix form: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let counter = 0; alert( ++counter ); // 1 ``` +<<<<<<< HEAD - 如果我们想要使用之前的值,那么我们需要使用后置形式: +======= +- If we'd like to increment a value but use its previous value, we need the postfix form: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let counter = 0; alert( counter++ ); // 0 ``` +<<<<<<< HEAD ````smart header="自相加/自相减和其它运算符的对比" `++/--` 运算符同样可以在表达式内部使用。它们的优先级比绝大部分的运算符要高。 +======= +````smart header="Increment/decrement among other operators" +The operators `++/--` can be used inside expressions as well. Their precedence is higher than most other arithmetical operations. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 举个例子: @@ -339,11 +496,19 @@ let counter = 1; alert( 2 * counter++ ); // 2,因为 counter++ 返回的是「旧值」 ``` +<<<<<<< HEAD 尽管从技术层面上来说可行,但是这样的写法会减少代码的可阅读性。在一行上做多个操作 —— 这样并不好。 当阅读代码时,快速的视觉「纵向」扫描会很容易漏掉 `counter++`,知道那个变量自相加并不简单。 「一行一个操作」模式是更好的选择: +======= +Though technically okay, such notation usually makes code less readable. One line does multiple things -- not good. + +While reading code, a fast "vertical" eye-scan can easily miss something like `counter++` and it won't be obvious that the variable increased. + +We advise a style of "one line -- one action": +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let counter = 1; @@ -368,11 +533,19 @@ counter++; - 右移 ( `>>` ) - 无符号右移 ( `>>>` ) +<<<<<<< HEAD 这些操作使用得非常少。为了理解它们,我们需要探讨底层的数字表达形式,现在不是做这个的最好时机。尤其是我们现在不会立刻使用它。如果你感兴趣,可以阅读 MDN 中的[位运算符](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators)相关文章。当有实际需求出现的时候再去阅读是更明智的选择。 +======= +These operators are used very rarely. To understand them, we need to delve into low-level number representation and it would not be optimal to do that right now, especially since we won't need them any time soon. If you're curious, you can read the [Bitwise Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) article on MDN. It would be more practical to do that when a real need arises. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 修改并替换 +<<<<<<< HEAD 我们经常需要对一个变量进行操作,然后把新的结果存储给变量。 +======= +We often need to apply an operator to a variable and store the new result in that same variable. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 举个例子: @@ -382,7 +555,11 @@ n = n + 5; n = n * 2; ``` +<<<<<<< HEAD 这个操作可以使用运算符 `+=` 和 `*=` 来简化: +======= +This notation can be shortened using the operators `+=` and `*=`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let n = 2; @@ -392,7 +569,11 @@ n *= 2; // now n = 14 (同n = n * 2) alert( n ); // 14 ``` +<<<<<<< HEAD 简短的「修改并替换」 运算符对所有的运算符都有效,以及位运算符:`/=`、`-=`等等。 +======= +Short "modify-and-assign" operators exist for all arithmetical and bitwise operators: `/=`, `-=`, etc. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 这些运算符和正常的赋值运算符拥有相同的优先级,因此它们会在其它运算之后运行: @@ -406,9 +587,15 @@ alert( n ); // 16 (右侧计算首先进行,和 n *= 8 相同) ## 逗号运算符 +<<<<<<< HEAD 逗号运算符 `,` 是最少见最不常使用的运算符之一。有时候它会被用来写更简短的代码,因此为了能够理解代码,我们需要了解它。 逗号运算符能让我们处理多个语句,使用 `,` 将它们分开。每个语句都运行了,但是只有最后的语句结果会被返回。 +======= +The comma operator `,` is one of the rarest and most unusual operators. Sometimes, it's used to write shorter code, so we need to know it in order to understand what's going on. + +The comma operator allows us to evaluate several expressions, dividing them with a comma `,`. Each of them is evaluated but only the result of the last one is returned. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 举个例子: @@ -420,17 +607,30 @@ let a = (1 + 2, 3 + 4); alert( a ); // 7 (3 + 4 的结果) ``` +<<<<<<< HEAD 这里,第一个语句 `1 + 2` 运行了,但是它的结果被丢弃了,然后 `3 + 4` 运行,计算结果被返回。 +======= +Here, the first expression `1 + 2` is evaluated and its result is thrown away. Then, `3 + 4` is evaluated and returned as the result. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```smart header="逗号运算符的优先级非常低" 请注意逗号运算符的优先级非常低,比 `=` 还要低,因此上面你的例子中圆括号非常重要。 +<<<<<<< HEAD 如果没有圆括号:`a = 1 + 2, 3 + 4` 会先执行 `+`,将数值相加得到 `a = 3, 7`,然后赋值运算符 `=` 执行, 'a = 3',然后逗号之后的数值 `7` 不会再执行,它被忽略掉了。 ``` 为什么我们需要这样一个运算符,它只返回最后一个值呢? 有时候,人们会使用它把几个操作放在一行上来进行复杂的运算。 +======= +Without them: `a = 1 + 2, 3 + 4` evaluates `+` first, summing the numbers into `a = 3, 7`, then the assignment operator `=` assigns `a = 3`, and finally the number after the comma, `7`, is not processed so it's ignored. +``` + +Why do we need an operator that throws away everything except the last part? + +Sometimes, people use it in more complex constructs to put several actions in one line. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 举个例子: @@ -441,4 +641,8 @@ for (*!*a = 1, b = 3, c = a * b*/!*; a < 10; a++) { } ``` +<<<<<<< HEAD 这样的技巧在许多 JavaScript 框架中都有使用,这也是为什么我们提到它。但是通常它并不能提升代码的可阅读性,使用它之前,我们要想清楚。 +======= +Such tricks are used in many JavaScript frameworks. That's why we're mentioning them. But, usually, they don't improve code readability so we should think well before using them. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/08-comparison/1-comparison-questions/task.md b/1-js/02-first-steps/08-comparison/1-comparison-questions/task.md index 2c4446fefe..1f95ca14fa 100644 --- a/1-js/02-first-steps/08-comparison/1-comparison-questions/task.md +++ b/1-js/02-first-steps/08-comparison/1-comparison-questions/task.md @@ -4,7 +4,11 @@ importance: 5 # 值的比较 +<<<<<<< HEAD 以下表达式的执行结果是? +======= +What will be the result for these expressions? +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js no-beautify 5 > 4 diff --git a/1-js/02-first-steps/08-comparison/article.md b/1-js/02-first-steps/08-comparison/article.md index 9389c1e450..2c29682560 100644 --- a/1-js/02-first-steps/08-comparison/article.md +++ b/1-js/02-first-steps/08-comparison/article.md @@ -1,18 +1,34 @@ # 值的比较 +<<<<<<< HEAD 在 JavaScript 中,我们可以使用一些熟知的数学符号进行值的比较: - 大于 / 小于:a > ba < b。 - 大于等于 / 小于等于:a >= ba <= b。 - 检测两个值的相等写为 `a == b`(注意表达式中是两个等号 `=`,若写为单个等号 `a = b` 则表示赋值)。 - 检测两个值的不等,在数学中使用 符号,而在 JavaScript 中则通过在赋值符号前增加叹号表示:a != b。 +======= +We know many comparison operators from maths: + +- Greater/less than: a > b, a < b. +- Greater/less than or equals: a >= b, a <= b. +- Equals: `a == b` (please note the double equals sign `=`. A single symbol `a = b` would mean an assignment). +- Not equals. In maths the notation is , but in JavaScript it's written as an assignment with an exclamation sign before it: a != b. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 比较结果为 Boolean 类型 +<<<<<<< HEAD 和其他操作符一样,比较操作符也会有返回值,其类型为布尔值(Boolean)。 - `true` —— 表示“yes(是)”,“correct(正确)”或“the truth(真理)”。 - `false` —— 表示“no(否)”,“wrong(错误)”或“a lie(谎言)”。 +======= +Like all other operators, a comparison returns a value. In this case, the value is a boolean. + +- `true` -- means "yes", "correct" or "the truth". +- `false` -- means "no", "wrong" or "not the truth". +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 示例: @@ -31,7 +47,11 @@ alert( result ); // true ## 字符串间的比较 +<<<<<<< HEAD 在比较字符串的大小时,会使用“字典”或“词典”顺序进行判定。 +======= +To see whether a string is greater than another, JavaScript uses the so-called "dictionary" or "lexicographical" order. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 换言之,字符串是按字符(母)逐个进行比较的。 @@ -45,6 +65,7 @@ alert( 'Bee' > 'Be' ); // true 字符串间的比较算法非常简单: +<<<<<<< HEAD 1. 首先比较两个字符串的首位字符大小。 2. 如果一方字符较大(或较小),则该字符串大于(或小于)另一个字符串。算法结束。 3. 否则,两个字符串中的字符相等,继续取出各自的后一位字符进行比较。 @@ -54,20 +75,40 @@ alert( 'Bee' > 'Be' ); // true 在上面的例子中,`'Z' > 'A'` 在算法的第 1 步就得到了返回结果。 字符串 `"Glow"` 和 `"Glee"` 会按字符逐个进行比较: +======= +1. Compare the first character of both strings. +2. If the first character from the first string is greater (or less) than the other string's, then the first string is greater (or less) than the second. We're done. +3. Otherwise, if both strings' first characters are the same, compare the second characters the same way. +4. Repeat until the end of either string. +5. If both strings end at the same length, then they are equal. Otherwise, the longer string is greater. + +In the examples above, the comparison `'Z' > 'A'` gets to a result at the first step while the strings `"Glow"` and `"Glee"` are compared character-by-character: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 1. `G` 和 `G` 相等。 2. `l` 和 `l` 相等。 3. `o` 比 `e` 大,算法停止,第一个字符串大于第二个。 +<<<<<<< HEAD ```smart header="非真正的字典顺序,而是 Unicode 编码顺序" 在上面的算法中,比较大小的逻辑与字典或电话簿中的排序很像,但也不完全相同。 比如说,算法中的比较对大小写是敏感的。大写的 `"A"` 并不等于小写的 `"a"`。哪一个更大呢?实际上小写的 `"a"` 更大。至于原因嘛,这是因为在内部的编码表中(Unicode),小写字母的字符索引更大。我们会在 这章讨论更多关于字符串的细节。 +======= +```smart header="Not a real dictionary, but Unicode order" +The comparison algorithm given above is roughly equivalent to the one used in dictionaries or phone books, but it's not exactly the same. + +For instance, case matters. A capital letter `"A"` is not equal to the lowercase `"a"`. Which one is greater? The lowercase `"a"`. Why? Because the lowercase character has a greater index in the internal encoding table JavaScript uses (Unicode). We'll get back to specific details and consequences of this in the chapter . +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` ## 不同类型间的比较 +<<<<<<< HEAD 当不同类型的值进行比较时,它们会首先被转为数字(number)再判定大小。 +======= +When comparing values of different types, JavaScript converts the values to numbers. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 示例: @@ -76,7 +117,13 @@ alert( '2' > 1 ); // true,字符串 '2' 会被转为数字 2 alert( '01' == 1 ); // true,字符串 '01' 会被转为数字 1 ``` +<<<<<<< HEAD 对于布尔类型,`true` 会被转为 `1`、`false` 转为 `0`,即有: +======= +For boolean values, `true` becomes `1` and `false` becomes `0`. + +For example: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run alert( true == 1 ); // true @@ -101,24 +148,40 @@ alert( Boolean(b) ); // true alert(a == b); // true! ``` +<<<<<<< HEAD 对于 JavaScript 而言这种现象蛮正常的,因为它会把待比较的值转为数字后再做比较(因此 `"0"` 变成了 `0` )。若只是将一个变量转为 `Boolean`,则会使用其他的类型转换规则。 +======= +From JavaScript's standpoint, this result is quite normal. An equality check converts values using the numeric conversion (hence `"0"` becomes `0`), while the explicit `Boolean` conversion uses another set of rules. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```` ## 严格相等 +<<<<<<< HEAD 普通的相等性检查 `==` 存在一个问题,它不能区分出 `0` 和 `false`: +======= +A regular equality check `==` has a problem. It cannot differentiate `0` from `false`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run alert( 0 == false ); // true ``` +<<<<<<< HEAD 也同样无法区分空字符串和 `false`: +======= +The same thing happens with an empty string: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run alert( '' == false ); // true ``` +<<<<<<< HEAD 这是因为在比较不同类型的值时,处于相等判断符号 `==` 两侧的值会被转换为数字的原因。空字符串和 `false` 也是如此,转换后它们都等于 0。 +======= +This happens because operands of different types are converted to numbers by the equality operator `==`. An empty string, just like `false`, becomes a zero. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 如果我们需要区分 `0` 和 `false`,该怎么做呢? @@ -132,19 +195,33 @@ alert( '' == false ); // true alert( 0 === false ); // false,比较不同类型的值 ``` +<<<<<<< HEAD 同样的,与“不相等”符号 `!=` 类似,“严格不相等”表示为 `!==`。 严格相等的操作符虽然略为冗长,但是它很清楚地显示了比较的意图,降低你犯错的可能性。 +======= +There is also a "strict non-equality" operator `!==` analogous to `!=`. + +The strict equality operator is a bit longer to write, but makes it obvious what's going on and leaves less room for errors. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 涉及 null 和 undefined 的比较 让我们看看更多的边缘案例。 +<<<<<<< HEAD 当使用 `null` 或 `undefined` 与其他值进行比较时,其返回结果常常出乎你的意料。 当使用严格相等 `===` 比较二者时: 它们是不相等的,因为它们属于不同的类型。 +======= +There's a non-intuitive behavior when `null` or `undefined` are compared to other values. + + +For a strict equality check `===` +: These values are different, because each of them is a different type. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run alert( null === undefined ); // false @@ -157,10 +234,17 @@ JavaScript 存在一个专属的规则,会判定它们互等。而它们就像 alert( null == undefined ); // true ``` +<<<<<<< HEAD 当使用数学式或其他比较方法 `< > <= >=` 时: `null/undefined` 的值会被转换为数字:`null` 转为 `0`,`undefined` 转为 `NaN`。 下面让我们看看,这些规则会带来什么有趣的现象。同时更重要的是,我们需要从中学会如何远离这些特性带来的“陷阱”。 +======= +For maths and other comparisons `< > <= >=` +: `null/undefined` are converted to numbers: `null` becomes `0`, while `undefined` becomes `NaN`. + +Now let's see some funny things that happen when we apply these rules. And, what's more important, how to not fall into a trap with them. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ### 奇怪的结果:null vs 0 @@ -172,15 +256,27 @@ alert( null == 0 ); // (2) false alert( null >= 0 ); // (3) *!*true*/!* ``` +<<<<<<< HEAD 是的,上面的结果完全打破了你对数学的认识。在最后一行代码显示“`null` 大于等于 0”的情况下,前两行代码中一定会有一个是正确的,然而事实表明它们的结果都是 false。 为什么会出现这种反常结果,这是因为相等性检测 `==` 和普通比较符 `> < >= <=` 的代码逻辑是相互独立的。进行值的比较会把 `null` 转为数字,因此它被转为了 `0`。这就是为什么(3)中 `null >= 0` 返回 true,(1)中 `null > 0` 返回 false。 另一方面,`undefined` 和 `null` 在相等性检测 `==` 中不会进行任何的类型转换,它们有自己独立的比较规则,所以除了它们之间互等外不会等于任何其他的值。这就解释了为什么(2)中 `null == 0` 会返回 false。 +======= +Mathematically, that's strange. The last result states that "`null` is greater than or equal to zero", so in one of the comparisons above it must be `true`, but they are both false. + +The reason is that an equality check `==` and comparisons `> < >= <=` work differently. Comparisons convert `null` to a number, treating it as `0`. That's why (3) `null >= 0` is true and (1) `null > 0` is false. + +On the other hand, the equality check `==` for `undefined` and `null` is defined such that, without any conversions, they equal each other and don't equal anything else. That's why (2) `null == 0` is false. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ### “拒不合作”的 undefined +<<<<<<< HEAD `undefined` 不应该参与任何值的比较: +======= +The value `undefined` shouldn't be compared to other values: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run alert( undefined > 0 ); // false (1) @@ -188,25 +284,50 @@ alert( undefined < 0 ); // false (2) alert( undefined == 0 ); // false (3) ``` +<<<<<<< HEAD 为何它看起来如此厌恶 0?无论怎么比较双方其结果总是返回 false! 原因如下: - `(1)` 和 `(2)` 中返回 `false` 是因为 `undefined` 在比较中被转换为了 `NaN`,而 `NaN` 是一个特殊的数值型取值,它与任何值进行比较都会返回 `false`。 - `(3)` 中返回 `false` 是因为这是一个相等性检测,而 `undefined` 只与 `null` 相等。 +======= +Why does it dislike zero so much? Always false! + +We get these results because: + +- Comparisons `(1)` and `(2)` return `false` because `undefined` gets converted to `NaN` and `NaN` is a special numeric value which returns `false` for all comparisons. +- The equality check `(3)` returns `false` because `undefined` only equals `null`, `undefined`, and no other value. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ### 规避错误 +<<<<<<< HEAD 我们为何要研究以上示例?我们需要时刻记得这些古怪的规则吗?不,并不需要。虽然这些规则随着使用都会烂熟于胸,但是我们需要遵循更为可靠的方法来避免潜在的问题。 +======= +Why did we go over these examples? Should we remember these peculiarities all the time? Well, not really. Actually, these tricky things will gradually become familiar over time, but there's a solid way to evade problems with them: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 除了严格相等 `===` 外,其他凡是有 `undefined/null` 参与的比较,我们都需要额外 “关照”。 +<<<<<<< HEAD 除非你非常清楚自己在做什么,否则永远不要使用 `>= > < <=` 去比较一个可能为 `null/undefined` 的变量。对于取值可能是 `null/undefined` 的变量,请按需要分别检查它的取值情况。 +======= +Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you're really sure of what you're doing. If a variable can have these values, check for them separately. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 小结 +<<<<<<< HEAD - 比较操作符始终返回逻辑值。 - 字符串间按“词典”顺序逐字符比较大小。 - 当待比较的值类型不同时,它们会被转为数字(不包括严格相等检测)进行比较。 - 在非严格相等 `==` 下,`null` 和 `undefined` 相等且各自不等于任何其他的值。 - 在使用 `>` 或 `<` 进行比较时,需要注意变量可能为 `null/undefined` 的情况。比较好的方法是单独检查变量是否等于 `null/undefined`。 +======= +- Comparison operators return a boolean value. +- Strings are compared letter-by-letter in the "dictionary" order. +- When values of different types are compared, they get converted to numbers (with the exclusion of a strict equality check). +- The values `null` and `undefined` equal `==` each other and do not equal any other value. +- Be careful when using comparisons like `>` or `<` with variables that can occasionally be `null/undefined`. Checking for `null/undefined` separately is a good idea. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/09-alert-prompt-confirm/article.md b/1-js/02-first-steps/09-alert-prompt-confirm/article.md index f2b4e3ff71..46a4bc3a86 100644 --- a/1-js/02-first-steps/09-alert-prompt-confirm/article.md +++ b/1-js/02-first-steps/09-alert-prompt-confirm/article.md @@ -1,8 +1,14 @@ # 交互:alert、prompt、confirm +<<<<<<< HEAD 本部分旨在覆盖 JavaScript “原生”,而无需针对特定环境进行调整。 但我们仍然使用浏览器作为演示环境。所以我们至少应该知道一些用户界面函数。在本章节我们将会熟悉函数 `alert`、`prompt` 和 `confirm` 的用法。 +======= +In this part of the tutorial we cover JavaScript language "as is", without environment-specific tweaks. + +But we'll still be using the browser as our demo environment, so we should know at least a few of its user-interface functions. In this chapter, we'll get familiar with the browser functions `alert`, `prompt` and `confirm`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## alert @@ -12,7 +18,11 @@ alert(message); ``` +<<<<<<< HEAD 浏览器会弹出一段信息并暂停脚本,直到用户点击了“确定”。 +======= +This shows a message and pauses script execution until the user presses "OK". +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 举个例子: @@ -20,27 +30,48 @@ alert(message); alert("Hello"); ``` +<<<<<<< HEAD 这个小窗口被称为 **模态窗**。"modal" 意味着用户不能与页面的其他部分进行交互,点击其他按钮等,直到他们处理完窗口。在这种情况下 - 直到他们按下“确定”。 ## prompt `prompt` 函数接收两个参数: +======= +The mini-window with the message is called a *modal window*. The word "modal" means that the visitor can't interact with the rest of the page, press other buttons, etc. until they have dealt with the window. In this case -- until they press "OK". + +## prompt + +The function `prompt` accepts two arguments: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js no-beautify -result = prompt(title[, default]); +result = prompt(title, [default]); ``` +<<<<<<< HEAD 浏览器会显示一个带有文本消息的模态窗口,还有 input 框和确定/取消按钮。 `title` : 显示给用户的文本 +======= +It shows a modal window with a text message, an input field for the visitor, and the buttons OK/CANCEL. + +`title` +: The text to show the visitor. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb `default` : 可选的第二个参数,指定 input 框的初始值。 +<<<<<<< HEAD 用户在 prompt 对话框的 input 框输入文本并点击确定。不然就点击取消按钮或敲击 `key:Esc` 键来取消。 `prompt` 返回输入的文本;如果取消输入就返回 `null`。 +======= +The visitor may type something in the prompt input field and press OK. Or they can cancel the input by pressing CANCEL or hitting the `key:Esc` key. + +The call to `prompt` returns the text from the input field or `null` if the input was canceled. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 举个例子: @@ -50,16 +81,27 @@ let age = prompt('How old are you?', 100); alert(`You are ${age} years old!`); // You are 100 years old! ``` +<<<<<<< HEAD ````warn header="IE: always supply a `default`" 第二个参数可选。但是如果我们不提供的话,Internet Explorer 会把 `"undefined"` 插入到 prompt。 在 Internet Explorer 中运行将会看到: +======= +````warn header="In IE: always supply a `default`" +The second parameter is optional, but if we don't supply it, Internet Explorer will insert the text `"undefined"` into the prompt. + +Run this code in Internet Explorer to see: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let test = prompt("Test"); ``` +<<<<<<< HEAD 所以,在 IE 中看起来很好,建议始终提供第二个参数: +======= +So, for prompts to look good in IE, we recommend always providing the second argument: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let test = prompt("Test", ''); // <-- for IE @@ -74,7 +116,11 @@ let test = prompt("Test", ''); // <-- for IE result = confirm(question); ``` +<<<<<<< HEAD `confirm` 函数显示一个带有 `question` 和两个按钮的模态窗口:确定和取消。 +======= +The function `confirm` shows a modal window with a `question` and two buttons: OK and CANCEL. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 点击确定返回 `true`,点击取消返回 `false`。 @@ -88,22 +134,39 @@ alert( isBoss ); // true if OK is pressed ## 总结 +<<<<<<< HEAD 我们探讨了与用户交互的 3 个浏览器指定的函数: +======= +We covered 3 browser-specific functions to interact with visitors: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb `alert` : 显示信息。 `prompt` +<<<<<<< HEAD : 显示信息要求用户输入文本。点击确定返回文本,点击取消或按下 `key:Esc` 键返回 `null`。 +======= +: shows a message asking the user to input text. It returns the text or, if CANCEL or `key:Esc` is clicked, `null`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb `confirm` : 显示信息等待用户点击确定或取消。点击确定返回 `true`,点击取消或 `key:Esc` 键返回 `false`。 +<<<<<<< HEAD 这些方法都是模态的:它们暂停脚本执行,并且不允许用户与该页面的其余部分交互,直到消息被解除。 +======= +All these methods are modal: they pause script execution and don't allow the visitor to interact with the rest of the page until the window has been dismissed. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 上述所有方法共有两个限制: +<<<<<<< HEAD 1. 模态窗口的确切位置由浏览器决定。通常在页面中心。 2. 窗口的确切外观还取决于浏览器。我们不能修改它。 +======= +1. The exact location of the modal window is determined by the browser. Usually, it's in the center. +2. The exact look of the window also depends on the browser. We can't modify it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 这是简单的代价。还有其他一些方法可以显示更漂亮的窗口,并与用户进行更丰富的交互,但如果“花里胡哨”不是非常重要,这些方法也可以工作的很好。 diff --git a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.png b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.png index 8c57b18850..04fb1fb0b7 100644 Binary files a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.png and b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.png differ diff --git a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2@2x.png b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2@2x.png index cbb2c611a0..08226b7903 100644 Binary files a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2@2x.png and b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2@2x.png differ diff --git a/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task.png b/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task.png deleted file mode 100644 index 8b54dc83d5..0000000000 Binary files a/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task.png and /dev/null differ diff --git a/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task@2x.png b/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task@2x.png deleted file mode 100644 index 92001dfe86..0000000000 Binary files a/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task@2x.png and /dev/null differ diff --git a/1-js/02-first-steps/10-ifelse/article.md b/1-js/02-first-steps/10-ifelse/article.md index 56f50e69d1..57fe3c3e85 100644 --- a/1-js/02-first-steps/10-ifelse/article.md +++ b/1-js/02-first-steps/10-ifelse/article.md @@ -1,12 +1,22 @@ # 条件运算符:if、'?' +<<<<<<< HEAD 有时我们需要根据不同条件执行不同的操作。 `if` 语句可以解决这个问题,条件运算符(三元),简称为“问号”运算符也可以解决。 +======= +Sometimes, we need to perform different actions based on different conditions. + +To do that, we can use the `if` statement and the conditional operator `?`, that's also called a "question mark" operator. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## "if" 语句 +<<<<<<< HEAD `if` 语句获得一个条件,计算这个条件表达式,如果计算结果是 `true`,就会执行对应的代码。 +======= +The `if` statement evaluates a condition and, if the condition's result is `true`, executes a block of code. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 如下: @@ -18,9 +28,15 @@ if (year == 2015) alert( 'You are right!' ); */!* ``` +<<<<<<< HEAD 在上面的例子中,条件是一个简单的相等性检查:`year == 2015`,但它可能更复杂。 如果有多个语句要执行,我们必须将要执行的代码块封装在大括号内: +======= +In the example above, the condition is a simple equality check (`year == 2015`), but it can be much more complex. + +If we want to execute more than one statement, we have to wrap our code block inside curly braces: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js if (year == 2015) { @@ -29,16 +45,29 @@ if (year == 2015) { } ``` +<<<<<<< HEAD 建议每次使用 if 语句都用大括号 `{}` 来包装代码块,即使只有一条语句也是。这样能提高代码可读性。 +======= +We recommend wrapping your code block with curly braces `{}` every time you use an `if` statement, even if there is only one statement to execute. Doing so improves readability. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 布尔转换 +<<<<<<< HEAD `if(……)` 语句会计算圆括号包围的表达式的结果并将其转换为布尔类型。 +======= +The `if (…)` statement evaluates the expression in its parentheses and converts the result to a boolean. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 让我们回顾一下 一章中的转换规则: +<<<<<<< HEAD - 数字 `0`、一个空字符串 `""`、`null`、`undefined` 和 `NaN` 都会转换成 `false`。因为他们被称为 “falsy” 值。 - 其他值变成 `true`,所以它们被称为 “truthy”。 +======= +- A number `0`, an empty string `""`, `null`, `undefined`, and `NaN` all become `false`. Because of that they are called "falsy" values. +- Other values become `true`, so they are called "truthy". +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 所以,下面条件下的代码永远不会执行: @@ -48,7 +77,11 @@ if (0) { // 0 是 falsy } ``` +<<<<<<< HEAD ……但下面的条件 — 始终有效: +======= +...and inside this condition -- it always will: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js if (1) { // 1 是 truthy @@ -56,7 +89,11 @@ if (1) { // 1 是 truthy } ``` +<<<<<<< HEAD 我们也可以将未计算的布尔值传入 `if` 语句,如下所示: +======= +We can also pass a pre-evaluated boolean value to `if`, like this: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js let cond = (year == 2015); // 相等运算符的结果是 true 或 false @@ -68,11 +105,15 @@ if (cond) { ## "else" 语句 +<<<<<<< HEAD `if` 语句包含一个可选的 “else” 块。如果条件不成立,就会执行它内部的代码。 +======= +The `if` statement may contain an optional "else" block. It executes when the condition is false. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 如下所示: ```js run -let year = prompt('In which year was ECMAScript-2015 specification published?', ''); +let year = prompt('In which year was the ECMAScript-2015 specification published?', ''); if (year == 2015) { alert( 'You guessed it right!' ); @@ -83,12 +124,16 @@ if (year == 2015) { ## 多个条件:"else if" +<<<<<<< HEAD 有时我们需要测试一个条件的几个变体。使用 `else if` 子句可以实现。 +======= +Sometimes, we'd like to test several variants of a condition. The `else if` clause lets us do that. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 如下所示: ```js run -let year = prompt('In which year was ECMAScript-2015 specification published?', ''); +let year = prompt('In which year was the ECMAScript-2015 specification published?', ''); if (year < 2015) { alert( 'Too early...' ); @@ -99,6 +144,7 @@ if (year < 2015) { } ``` +<<<<<<< HEAD 在上面的代码中,首先检查 `year < 2015`。如果它是假的,就会转到下一个条件 `year > 2015`,否则显示最后一个 `alert`。 可以有更多的 `else if` 块。结尾的 `else` 是可选的。 @@ -106,6 +152,15 @@ if (year < 2015) { ## 三元运算符 '?' 有时我们需要根据一个条件去声明变量。 +======= +In the code above, JavaScript first checks `year < 2015`. If that is falsy, it goes to the next condition `year > 2015`. If that is also falsy, it shows the last `alert`. + +There can be more `else if` blocks. The final `else` is optional. + +## Conditional operator '?' + +Sometimes, we need to assign a variable depending on a condition. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 如下所示: @@ -124,16 +179,26 @@ if (age > 18) { alert(accessAllowed); ``` +<<<<<<< HEAD 所谓的“三元”或“问号”操作符让我们可以更简便地达到目的。 它用问号 `?` 表示。“三元”意味着操作符有三个操作数。它实际上是JavaScript 中唯一一个有这么多操作数的操作符。 +======= +The so-called "conditional" or "question mark" operator lets us do that in a shorter and simpler way. + +The operator is represented by a question mark `?`. Sometimes it's called "ternary", because the operator has three operands. It is actually the one and only operator in JavaScript which has that many. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 语法如下: ```js -let result = condition ? value1 : value2 +let result = condition ? value1 : value2; ``` +<<<<<<< HEAD 计算条件结果,如果结果为真,则返回 `value1`,否则返回 `value2`。 +======= +The `condition` is evaluated: if it's truthy then `value1` is returned, otherwise -- `value2`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 如下所示: @@ -141,7 +206,13 @@ let result = condition ? value1 : value2 let accessAllowed = (age > 18) ? true : false; ``` +<<<<<<< HEAD 技术上,我们可以省略 `age > 18` 外面的括号。问号运算符的优先级较低。它在比较 `>` 后执行,所以也会执行相同的操作: +======= +Technically, we can omit the parentheses around `age > 18`. The question mark operator has a low precedence, so it executes after the comparison `>`. + +This example will do the same thing as the previous one: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // 比较运算符 “age > 18” 首先执行 @@ -149,10 +220,17 @@ let accessAllowed = (age > 18) ? true : false; let accessAllowed = age > 18 ? true : false; ``` +<<<<<<< HEAD 但括号使代码更具可读性,所以建议使用它们。 ````smart 在上面的例子中,可以去掉问号运算符,因为比较本身就返回 `true/false`: +======= +But parentheses make the code more readable, so we recommend using them. + +````smart +In the example above, you can avoid using the question mark operator because the comparison itself returns `true/false`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // 下面代码同样可以实现 @@ -162,7 +240,11 @@ let accessAllowed = age > 18; ## 多个 '?' +<<<<<<< HEAD 使用一系列问号 `?` 运算符可以返回一个取决于多个条件的值。 +======= +A sequence of question mark operators `?` can return a value that depends on more than one condition. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 如下所示: ```js run @@ -176,6 +258,7 @@ let message = (age < 3) ? 'Hi, baby!' : alert( message ); ``` +<<<<<<< HEAD 很难一下子看出发生了什么。但经过仔细观察,我们可以看到它只是一个普通的检查序列。 1. 第一个问号检查 `age < 3`。 @@ -184,6 +267,16 @@ alert( message ); 4. 如果为真 — 返回 `'Greetings!'`,否则 — 在最后一个冒号 `":"` 后面返回 `'What an unusual age!'`。 使用 `if..else` 实现上面的逻辑: +======= +It may be difficult at first to grasp what's going on. But after a closer look, we can see that it's just an ordinary sequence of tests: + +1. The first question mark checks whether `age < 3`. +2. If true -- it returns `'Hi, baby!'`. Otherwise, it continues to the expression after the colon '":"', checking `age < 18`. +3. If that's true -- it returns `'Hello!'`. Otherwise, it continues to the expression after the next colon '":"', checking `age < 100`. +4. If that's true -- it returns `'Greetings!'`. Otherwise, it continues to the expression after the last colon '":"', returning `'What an unusual age!'`. + +Here's how this looks using `if..else`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js if (age < 3) { @@ -210,6 +303,7 @@ let company = prompt('Which company created JavaScript?', ''); */!* ``` +<<<<<<< HEAD 根据 `company =='Netscape'` 条件,要么执行 `?` 后面的第一部分方法并显示对应内容,要么执行第二部分的方法并显示对应内容。 在这里我们不是把结果赋值给变量。而是根据条件执行不同的代码。 @@ -219,6 +313,17 @@ let company = prompt('Which company created JavaScript?', ''); 这种写法比 `if` 语句更短,对一些程序员很有吸引力。但它的可读性较差。 下面是使用 `if` 语句实现的相同功能的代码,进行下比较: +======= +Depending on the condition `company == 'Netscape'`, either the first or the second expression after the `?` gets executed and shows an alert. + +We don't assign a result to a variable here. Instead, we execute different code depending on the condition. + +**We don't recommend using the question mark operator in this way.** + +The notation is shorter than the equivalent `if` statement, which appeals to some programmers. But it is less readable. + +Here is the same code using `if` for comparison: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run no-beautify let company = prompt('Which company created JavaScript?', ''); @@ -232,6 +337,12 @@ if (company == 'Netscape') { */!* ``` +<<<<<<< HEAD 因为我们的眼睛垂直扫描代码。跨越多行的结构比长的水平代码更容易理解。 问号 `?` 的作用是根据条件返回一个或另一个值。请正确使用它。`if` 还可以用来执行代码的不同分支。 +======= +Our eyes scan the code vertically. Code blocks which span several lines are easier to understand than a long, horizontal instruction set. + +The purpose of the question mark operator `?` is to return one value or another depending on its condition. Please use it for exactly that. Use `if` when you need to execute different branches of code. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/11-logical-operators/1-alert-null-2-undefined/task.md b/1-js/02-first-steps/11-logical-operators/1-alert-null-2-undefined/task.md index 512fd6f33a..a2719ce0d9 100644 --- a/1-js/02-first-steps/11-logical-operators/1-alert-null-2-undefined/task.md +++ b/1-js/02-first-steps/11-logical-operators/1-alert-null-2-undefined/task.md @@ -4,7 +4,11 @@ importance: 5 # 或运算的结果是什么? +<<<<<<< HEAD 如下代码将会输出什么? +======= +What is the code below going to output? +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js alert( null || 2 || undefined ); diff --git a/1-js/02-first-steps/11-logical-operators/2-alert-or/task.md b/1-js/02-first-steps/11-logical-operators/2-alert-or/task.md index fac364cc24..13a8df6b0f 100644 --- a/1-js/02-first-steps/11-logical-operators/2-alert-or/task.md +++ b/1-js/02-first-steps/11-logical-operators/2-alert-or/task.md @@ -4,7 +4,11 @@ importance: 3 # 或运算和 alerts 的结果是什么? +<<<<<<< HEAD 下面的代码将会输出什么? +======= +What will the code below output? +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js alert( alert(1) || 2 || alert(3) ); diff --git a/1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/task.md b/1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/task.md index 2331e3e223..432bda60eb 100644 --- a/1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/task.md +++ b/1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/task.md @@ -4,7 +4,11 @@ importance: 5 # 与操作的结果是什么? +<<<<<<< HEAD 下面这段代码将会显示什么? +======= +What is this code going to show? +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js alert( 1 && null && 2 ); diff --git a/1-js/02-first-steps/11-logical-operators/5-alert-and-or/solution.md b/1-js/02-first-steps/11-logical-operators/5-alert-and-or/solution.md index c98fae47c1..1fa3f30d73 100644 --- a/1-js/02-first-steps/11-logical-operators/5-alert-and-or/solution.md +++ b/1-js/02-first-steps/11-logical-operators/5-alert-and-or/solution.md @@ -12,5 +12,9 @@ alert( null || 2 && 3 || 4 ); null || 3 || 4 ``` +<<<<<<< HEAD 现在的结果就是第一个真值:`3`。 +======= +Now the result is the first truthy value: `3`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/11-logical-operators/5-alert-and-or/task.md b/1-js/02-first-steps/11-logical-operators/5-alert-and-or/task.md index 05b7f18aa7..226f7dd65e 100644 --- a/1-js/02-first-steps/11-logical-operators/5-alert-and-or/task.md +++ b/1-js/02-first-steps/11-logical-operators/5-alert-and-or/task.md @@ -4,7 +4,11 @@ importance: 5 # 或运算、与运算、或运算串联的结果 +<<<<<<< HEAD 结果将会是什么? +======= +What will the result be? +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js alert( null || 2 && 3 || 4 ); diff --git a/1-js/02-first-steps/11-logical-operators/8-if-question/task.md b/1-js/02-first-steps/11-logical-operators/8-if-question/task.md index 466673e2a9..aa1164a345 100644 --- a/1-js/02-first-steps/11-logical-operators/8-if-question/task.md +++ b/1-js/02-first-steps/11-logical-operators/8-if-question/task.md @@ -6,7 +6,11 @@ importance: 5 下面哪一个 `alert` 将会被执行? +<<<<<<< HEAD `if(...)` 语句内表达式的结果是什么? +======= +What will the results of the expressions be inside `if(...)`? +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js if (-1 || 0) alert( 'first' ); diff --git a/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.png b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.png new file mode 100644 index 0000000000..32f0d4b94c Binary files /dev/null and b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.png differ diff --git a/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task@2x.png b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task@2x.png new file mode 100644 index 0000000000..c3867e62c0 Binary files /dev/null and b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task@2x.png differ diff --git a/1-js/02-first-steps/10-ifelse/4-check-login/solution.md b/1-js/02-first-steps/11-logical-operators/9-check-login/solution.md similarity index 100% rename from 1-js/02-first-steps/10-ifelse/4-check-login/solution.md rename to 1-js/02-first-steps/11-logical-operators/9-check-login/solution.md diff --git a/1-js/02-first-steps/10-ifelse/4-check-login/task.md b/1-js/02-first-steps/11-logical-operators/9-check-login/task.md similarity index 71% rename from 1-js/02-first-steps/10-ifelse/4-check-login/task.md rename to 1-js/02-first-steps/11-logical-operators/9-check-login/task.md index 9925377b9c..b3d0ab3de1 100644 --- a/1-js/02-first-steps/10-ifelse/4-check-login/task.md +++ b/1-js/02-first-steps/11-logical-operators/9-check-login/task.md @@ -20,6 +20,10 @@ importance: 3 请使用嵌套的 `if` 块。注意代码整体的可读性。 +<<<<<<< HEAD:1-js/02-first-steps/10-ifelse/4-check-login/task.md 提示:将空字符串输入,prompt 会获取到一个空字符串 `''`。Prompt 运行过程中,按下 `key:ESC` 键会得到 `null`。 +======= +Hint: passing an empty input to a prompt returns an empty string `''`. Pressing `key:ESC` during a prompt returns `null`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/02-first-steps/11-logical-operators/9-check-login/task.md [示例] diff --git a/1-js/02-first-steps/11-logical-operators/article.md b/1-js/02-first-steps/11-logical-operators/article.md index 7a65d5bbbc..cc0feca378 100644 --- a/1-js/02-first-steps/11-logical-operators/article.md +++ b/1-js/02-first-steps/11-logical-operators/article.md @@ -2,7 +2,11 @@ JavaScript 里有三个逻辑运算符:`||` (或), `&&` (与), `!` (非)。 +<<<<<<< HEAD 虽然被称为“逻辑”,这些运算符却可以被应用于任意类型的值,而不仅仅是布尔值。结果也同样可能是任意类型。 +======= +Although they are called "logical", they can be applied to values of any type, not only boolean. Their result can also be of any type. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 让我们来详细看一下。 @@ -14,9 +18,15 @@ JavaScript 里有三个逻辑运算符:`||` (或), `&&` (与), `!` (非)。 result = a || b; ``` +<<<<<<< HEAD 在传统的编程中,逻辑或仅能够操作布尔值。如果参与运算的一个参数为 `true`,结果将返回 `true`,否则返回 `false`。 在 JavaScript 中,逻辑运算符更加灵活强大。但是首先我们看一下操作数是布尔值的时候发生了什么。 +======= +In classical programming, the logical OR is meant to manipulate boolean values only. If any of its arguments are `true`, it returns `true`, otherwise it returns `false`. + +In JavaScript, the operator is a little bit trickier and more powerful. But first, let's see what happens with boolean values. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 下面是四种可能的逻辑组合: @@ -29,9 +39,15 @@ alert( false || false ); // false 正如我们所见,除了两个操作数都是 `false` 的情况,结果总是 `true`。 +<<<<<<< HEAD 如果操作数不是布尔值,那么它将会被转化为布尔值来参与运算。 例如,数字 `1` 将会被作为 `true`,数字 `0` 则作为 `false`: +======= +If an operand is not a boolean, it's converted to a boolean for the evaluation. + +For instance, the number `1` is treated as `true`, the number `0` as `false`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run if (1 || 0) { // 工作原理相当于 if( true || false ) @@ -39,7 +55,11 @@ if (1 || 0) { // 工作原理相当于 if( true || false ) } ``` +<<<<<<< HEAD 大多数时间,或 `||` 会被用在 `if` 语句中,用来测试是否有**任何**给定的条件是正确的。 +======= +Most of the time, OR `||` is used in an `if` statement to test if *any* of the given conditions is `true`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: @@ -64,9 +84,15 @@ if (hour < 10 || hour > 18 || isWeekend) { } ``` +<<<<<<< HEAD ## 或运算寻找第一个真值 上文提到的逻辑处理多少有些传统了。下面让我们看看 JavaScript 的“附加”特性。 +======= +## OR finds the first truthy value + +The logic described above is somewhat classical. Now, let's bring in the "extra" features of JavaScript. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 拓展的算法如下所示。 @@ -78,13 +104,23 @@ result = value1 || value2 || value3; 或运算符 `||` 做了如下的事情: +<<<<<<< HEAD - 从左到右依次计算操作数。 - 将每一个操作数转化为布尔值。如果结果是 `true`,就停止计算,返回这个操作数的初始值。 - 如果所有的操作数都被计算过(也就是,转换结果都是 `false`),返回最后一个操作数。 +======= +- Evaluates operands from left to right. +- For each operand, converts it to boolean. If the result is `true`, stops and returns the original value of that operand. +- If all operands have been evaluated (i.e. all were `false`), returns the last operand. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 返回的值是操作数的初始形式,不会做布尔转换。 +<<<<<<< HEAD 也就是,一个或 `"||"` 运算的链将返回第一个真值,如果这样的值不存在就返回该链的最后一个值。 +======= +In other words, a chain of OR `"||"` returns the first truthy value or the last one if no truthy value is found. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: @@ -97,6 +133,7 @@ alert( null || 0 || 1 ); // 1(第一个真值) alert( undefined || null || 0 ); // 0(所有的转化结果都是 false,返回最后一个值) ``` +<<<<<<< HEAD 对比与“纯粹的、传统的、仅仅处理布尔值的或运算”,这个就有很有趣的用法了。 1. **获取变量列表或者表达式的第一个真值** @@ -104,6 +141,15 @@ alert( undefined || null || 0 ); // 0(所有的转化结果都是 false,返 假设我们有几个变量,它们可能包含某些数据或者是 `null/undefined`。我们需要选出第一个包含数据的变量。 我们可以这样应用或运算 `||`: +======= +This leads to some interesting usage compared to a "pure, classical, boolean-only OR". + +1. **Getting the first truthy value from a list of variables or expressions.** + + Imagine we have a list of variables which can either contain data or be `null/undefined`. How can we find the first one with data? + + We can use OR `||`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let currentUser = null; @@ -116,6 +162,7 @@ alert( undefined || null || 0 ); // 0(所有的转化结果都是 false,返 alert( name ); // 选出了 “John” - 第一个真值 ``` +<<<<<<< HEAD 如果 `currentUser` 和 `defaultUser` 都是假值,那么结果就是 `"unnamed"`。 2. **短路取值。** @@ -124,6 +171,16 @@ alert( undefined || null || 0 ); // 0(所有的转化结果都是 false,返 当表达式作为第二个参数并且有一定副作用,比如变量赋值的时候,这就清楚可见了。 如果我们运行下面的例子,`x` 将不会被赋值: +======= + If both `currentUser` and `defaultUser` were falsy, `"unnamed"` would be the result. +2. **Short-circuit evaluation.** + + Operands can be not only values, but arbitrary expressions. OR evaluates and tests them from left to right. The evaluation stops when a truthy value is reached, and the value is returned. This process is called "a short-circuit evaluation" because it goes as short as possible from left to right. + + This is clearly seen when the expression given as the second argument has a side effect like a variable assignment. + + In the example below, `x` does not get assigned: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run no-beautify let x; @@ -133,7 +190,11 @@ alert( undefined || null || 0 ); // 0(所有的转化结果都是 false,返 alert(x); // undefined,因为 (x = 1) 没有被执行 ``` +<<<<<<< HEAD ...如果第一个参数是 `false`,或运算将会继续并计算第二个参数,也就会运行赋值操作。 +======= + If, instead, the first argument is `false`, `||` evaluates the second one, thus running the assignment: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run no-beautify let x; @@ -143,11 +204,19 @@ alert( undefined || null || 0 ); // 0(所有的转化结果都是 false,返 alert(x); // 1 ``` +<<<<<<< HEAD 赋值操作只是一个很简单的情况,其他副作用同样可以被包含进来。 正如我们所见,这种用法是“`if` 语句的简便方式”。第一个操作数被转化为布尔值,如果是假,那么第二个参数就会被执行。 大多数情况下,使用正常的 `if` 语句,这样代码可读性更高,但是有时候这种方式会很简洁。 +======= + An assignment is a simple case. There may be side effects, that won't show up if the evaluation doesn't reach them. + + As we can see, such a use case is a "shorter way of doing `if`". The first operand is converted to boolean. If it's false, the second one is evaluated. + + Most of time, it's better to use a "regular" `if` to keep the code easy to understand, but sometimes this can be handy. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## &&(与) @@ -157,7 +226,11 @@ alert( undefined || null || 0 ); // 0(所有的转化结果都是 false,返 result = a && b; ``` +<<<<<<< HEAD 传统的编程中,当两个操作数都是真值,与操作返回 `true`,否则返回 `false`: +======= +In classical programming, AND returns `true` if both operands are truthy and `false` otherwise: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run alert( true && true ); // true @@ -173,11 +246,15 @@ let hour = 12; let minute = 30; if (hour == 12 && minute == 30) { - alert( 'Time is 12:30' ); + alert( 'The time is 12:30' ); } ``` +<<<<<<< HEAD 就像或运算一样,与运算的操作数可以是任意类型的值: +======= +Just as with OR, any value is allowed as an operand of AND: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run if (1 && 0) { // 作为 true && false 来执行 @@ -186,7 +263,11 @@ if (1 && 0) { // 作为 true && false 来执行 ``` +<<<<<<< HEAD ## 与操作寻找第一个假值 +======= +## AND finds the first falsy value +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 给出多个参加与运算的值: @@ -196,9 +277,15 @@ result = value1 && value2 && value3; 与运算 `&&` 做了如下的事情: +<<<<<<< HEAD - 从左到右依次计算操作数。 - 将每一个操作数转化为布尔值。如果结果是 `false`,就停止计算,返回这个操作数的初始值。 - 如果所有的操作数都被计算过(也就是,转换结果都是 `true`),返回最后一个操作数。 +======= +- Evaluates operands from left to right. +- For each operand, converts it to a boolean. If the result is `false`, stops and returns the original value of that operand. +- If all operands have been evaluated (i.e. all were truthy), returns the last operand. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 换句话说,与操作符返回第一个假值,如果没有假值就返回最后一个值。 @@ -230,6 +317,7 @@ alert( 1 && 2 && null && 3 ); // null alert( 1 && 2 && 3 ); // 3,最后一个值 ``` +<<<<<<< HEAD ````smart header="与运算 `&&` 在或操作符 `||` 之前执行" 与运算 `&&` 的优先级比或运算 `||` 要高,所以它将会比或运算先执行。 @@ -238,6 +326,12 @@ alert( 1 && 2 && 3 ); // 3,最后一个值 ```js run alert( 5 || 1 && 0 ); // 5 ``` +======= +````smart header="Precedence of AND `&&` is higher than OR `||`" +The precedence of AND `&&` operator is higher than OR `||`. + +So the code `a && b || c && d` is essentially the same as if the `&&` expressions were in parentheses: `(a && b) || (c && d)`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```` 就像或运算一样,与运算 `&&` 有时候能够代替 `if`。 @@ -250,7 +344,11 @@ let x = 1; (x > 0) && alert( 'Greater than zero!' ); ``` +<<<<<<< HEAD `&&` 右边的代码只有运算抵达到那里才能被执行。也就是,当且仅当 `(x > 0)` 返回了真值。 +======= +The action in the right part of `&&` would execute only if the evaluation reaches it. That is, only if `(x > 0)` is true. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 所以我们基本可以类似的得到: @@ -262,9 +360,15 @@ if (x > 0) { } ``` +<<<<<<< HEAD 带 `&&` 的代码变体看上去更短。但是 `if` 的含义更明显,可读性也更高。 所以建议是根据目的选择代码的结构。需要条件判断就用 `if`,需要与运算就用 `&&`。 +======= +The variant with `&&` appears shorter. But `if` is more obvious and tends to be a little bit more readable. + +So we recommend using every construct for its purpose: use `if` if we want if and use `&&` if we want AND. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## !(非) @@ -278,8 +382,13 @@ result = !value; 操作符接受一个参数,并按如下运作: +<<<<<<< HEAD 1. 将操作数转化为布尔类型:`true/false`。 2. 返回相反的值。 +======= +1. Converts the operand to boolean type: `true/false`. +2. Returns the inverse value. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: @@ -295,7 +404,11 @@ alert( !!"non-empty string" ); // true alert( !!null ); // false ``` +<<<<<<< HEAD 也就是,第一个非运算将该值转化为布尔类型并取反,第二个非运算再次取反。最后我们就得到了一个任意值到布尔值的转化。 +======= +That is, the first NOT converts the value to boolean and returns the inverse, and the second NOT inverses it again. In the end, we have a plain value-to-boolean conversion. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 这是一个更详细的方法,完成的同样的事情 -- 一个内置的 `Boolean` 函数: @@ -303,3 +416,5 @@ alert( !!null ); // false alert( Boolean("non-empty string") ); // true alert( Boolean(null) ); // false ``` + +The precedence of NOT `!` is the highest of all logical operators, so it always executes first, before `&&` or `||`. diff --git a/1-js/02-first-steps/12-while-for/2-which-value-while/solution.md b/1-js/02-first-steps/12-while-for/2-which-value-while/solution.md index abee806bb9..6f1d6f9836 100644 --- a/1-js/02-first-steps/12-while-for/2-which-value-while/solution.md +++ b/1-js/02-first-steps/12-while-for/2-which-value-while/solution.md @@ -7,18 +7,28 @@ while (++i < 5) alert( i ); ``` +<<<<<<< HEAD    第一个值是 `i=1`,因为 `++i` 首先递增 `i` 然后返回新值。因此先比较 `1 < 5` 然后 `alert` 显示 `1`。    然后按照 `2,3,4…` —— 数值一个接着一个出现。比较总是使用递增值,因为 `++` 在变量前。    最终,`i=4` 递增到 `5`,当比较 `while(5 < 5)` 失败时,循环停止。所以没有显示 `5`。   2. **从 1 到 5** +======= + The first value is `i = 1`, because `++i` first increments `i` and then returns the new value. So the first comparison is `1 < 5` and the `alert` shows `1`. + + Then follow `2, 3, 4…` -- the values show up one after another. The comparison always uses the incremented value, because `++` is before the variable. + + Finally, `i = 4` is incremented to `5`, the comparison `while(5 < 5)` fails, and the loop stops. So `5` is not shown. +2. **From 1 to 5** +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let i = 0; while (i++ < 5) alert( i ); ``` +<<<<<<< HEAD 第一个值也是 `i=1`。后缀形式 `i++` 递增 `i` 然后返回**旧**值,因此比较 `i++ < 5` 将使用 `i=0` (与 `++i < 5` 相反)。    但 `alert` 调用是独立的。这是在递增和比较之后执行的另一条语句。因此它得到了当前的 `i=1`。 @@ -28,3 +38,14 @@    我们在 `i=4` 时暂停,前缀形式 `++i` 会递增并在比较中使用 `5`。但我们这里还有后缀形式 `i++`。因此,它将 `i` 递增到 `5`,但返回旧值。因此实际比较的是 `while(4 < 5)` —— true,控制继续执行 `alert`。    `i=5` 是最后一个值,因为下一步 `while(5 < 5)` 为 false。 +======= + The first value is again `i = 1`. The postfix form of `i++` increments `i` and then returns the *old* value, so the comparison `i++ < 5` will use `i = 0` (contrary to `++i < 5`). + + But the `alert` call is separate. It's another statement which executes after the increment and the comparison. So it gets the current `i = 1`. + + Then follow `2, 3, 4…` + + Let's stop on `i = 4`. The prefix form `++i` would increment it and use `5` in the comparison. But here we have the postfix form `i++`. So it increments `i` to `5`, but returns the old value. Hence the comparison is actually `while(4 < 5)` -- true, and the control goes on to `alert`. + + The value `i = 5` is the last one, because on the next step `while(5 < 5)` is false. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/12-while-for/2-which-value-while/task.md b/1-js/02-first-steps/12-while-for/2-which-value-while/task.md index ce6ba71723..3fc3c878f2 100644 --- a/1-js/02-first-steps/12-while-for/2-which-value-while/task.md +++ b/1-js/02-first-steps/12-while-for/2-which-value-while/task.md @@ -2,11 +2,19 @@ importance: 4 --- +<<<<<<< HEAD # 哪个值才是 while? 对于每次循环,写出你认为会显示的值,然后与答案进行比较。 两次循环的 `alert` 值是否相同? +======= +# Which values does the while loop show? + +For every loop iteration, write down which value it outputs and then compare it with the solution. + +Both loops `alert` the same values, or not? +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 1. 前缀形式 `++i`: diff --git a/1-js/02-first-steps/12-while-for/6-repeat-until-correct/task.md b/1-js/02-first-steps/12-while-for/6-repeat-until-correct/task.md index 43922b1fbd..8bdb3e8d3c 100644 --- a/1-js/02-first-steps/12-while-for/6-repeat-until-correct/task.md +++ b/1-js/02-first-steps/12-while-for/6-repeat-until-correct/task.md @@ -3,7 +3,11 @@ importance: 5 # 重复输入,直到正确为止 +<<<<<<< HEAD 编写一个提示输入大于 `100` 的循环。如果使用者输入其他数值 —— 请他重新输入。 +======= +Write a loop which prompts for a number greater than `100`. If the visitor enters another number -- ask them to input again. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 循环必须请求一个数值,直到使用者输入一个大于 `100` 的数值或取消输入/输入空行为止。 diff --git a/1-js/02-first-steps/12-while-for/7-list-primes/solution.md b/1-js/02-first-steps/12-while-for/7-list-primes/solution.md index 741b287522..18a4b779c2 100644 --- a/1-js/02-first-steps/12-while-for/7-list-primes/solution.md +++ b/1-js/02-first-steps/12-while-for/7-list-primes/solution.md @@ -26,4 +26,8 @@ for (let i = 2; i <= n; i++) { // for each i... } ``` +<<<<<<< HEAD 这段代码有很大空间可以优化。例如,我们可以从 `2` 到 `i` 的平方根中寻找除数。但无论如何,如果我们想要在很大的时间间隔内实现高效率,我们需要改变方法,依赖高等数学和复杂算法,如[二次筛选] [Quadratic sieve](https://en.wikipedia.org/wiki/Quadratic_sieve), [General number field sieve](https://en.wikipedia.org/wiki/General_number_field_sieve) 等等。 +======= +There's a lot of space to opimize it. For instance, we could look for the divisors from `2` to square root of `i`. But anyway, if we want to be really efficient for large intervals, we need to change the approach and rely on advanced maths and complex algorithms like [Quadratic sieve](https://en.wikipedia.org/wiki/Quadratic_sieve), [General number field sieve](https://en.wikipedia.org/wiki/General_number_field_sieve) etc. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/12-while-for/article.md b/1-js/02-first-steps/12-while-for/article.md index 0a01a165a9..1f14606b66 100644 --- a/1-js/02-first-steps/12-while-for/article.md +++ b/1-js/02-first-steps/12-while-for/article.md @@ -1,10 +1,18 @@ # 循环:while 和 for +<<<<<<< HEAD 我们经常需要连续多次执行类似的操作。 例如,我们需要从列表中逐个输出商品时,或者对从 1 到 10 的每个数字运行相同的代码时。 **循环**是一种多次重复运行同一部分代码的方法。 +======= +We often need to repeat actions. + +For example, outputting goods from a list one after another or just running the same code for each number from 1 to 10. + +*Loops* are a way to repeat the same code multiple times. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## "while" 循环 @@ -31,11 +39,19 @@ while (i < 3) { // 结果分别是 0、1、2 循环体的单次执行叫作**一次迭代**。上面示例中的循环进行三次迭代。 +<<<<<<< HEAD 如果上述示例中没有 `i++`,那么循环(理论上)会永远重复。实际上,浏览器提供了阻止这种循环的方法,对于服务器端 JavaScript,我们可以终止该过程。 任何表达式或变量都可以是循环条件,而不仅仅是比较。对它们进行计算,并通过 `while` 将其结果转化为布尔值。 例如,`while (i != 0)` 可简写为 `while (i)`: +======= +If `i++` was missing from the example above, the loop would repeat (in theory) forever. In practice, the browser provides ways to stop such loops, and in server-side JavaScript, we can kill the process. + +Any expression or variable can be a loop condition, not just comparisons: the condition is evaluated and converted to a boolean by `while`. + +For instance, a shorter way to write `while (i != 0)` is `while (i)`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let i = 3; @@ -47,8 +63,13 @@ while (i) { // 当 i 变成 0 时,条件为 false,循环终止 } ``` +<<<<<<< HEAD ````smart header="Brackets are not required for a single-line body" 如果循环体只有一条语句,则可以省略括号 `{…}`: +======= +````smart header="Curly braces are not required for a single-line body" +If the loop body has a single statement, we can omit the curly braces `{…}`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let i = 3; @@ -68,7 +89,11 @@ do { } while (condition); ``` +<<<<<<< HEAD 循环首先执行循环体,然后检查条件,当条件为真时,重复执行循环体。 +======= +The loop will first execute the body, then check the condition, and, while it's truthy, execute it again and again. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: @@ -80,11 +105,19 @@ do { } while (i < 3); ``` +<<<<<<< HEAD 这种形式的语法很少使用,除非您希望不管条件是否为真,循环体**至少执行一次**。通常其他形式是首选:`while(…) {…}`。 +======= +This form of syntax should only be used when you want the body of the loop to execute **at least once** regardless of the condition being truthy. Usually, the other form is preferred: `while(…) {…}`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## "for" 循环 +<<<<<<< HEAD `for` 循环是最常使用的。 +======= +The `for` loop is the most commonly used loop. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 看起来就像这样: @@ -102,6 +135,7 @@ for (let i = 0; i < 3; i++) { // 结果为 0、1、2 } ``` +<<<<<<< HEAD 我们逐部分地检查 `for` 语句: | 部分 | | | @@ -110,6 +144,16 @@ for (let i = 0; i < 3; i++) { // 结果为 0、1、2 | 条件 | `i < 3`| 在每次循环迭代之前检查,如果失败,循环停止。 | | 步骤 | `i++` | 在每次迭代后执行主体,但在条件检查之前执行。 | | 主体 | `alert(i)`| 条件为真时,重复运行。 | +======= +Let's examine the `for` statement part-by-part: + +| part | | | +|-------|----------|----------------------------------------------------------------------------| +| begin | `i = 0` | Executes once upon entering the loop. | +| condition | `i < 3`| Checked before every loop iteration. If false, the loop stops. | +| step| `i++` | Executes after the body on each iteration but before the condition check. | +| body | `alert(i)`| Runs again and again while the condition is truthy. | +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 一般循环算法的工作原理如下: @@ -121,9 +165,15 @@ Run begin → ... ``` +<<<<<<< HEAD 如果您是循环方面的小白,那么回到这个例子,在一张纸上重现它逐步运行的过程,可能会对你有所帮助。 以下是我们示例中发生的情况: +======= +If you are new to loops, it could help to go back to the example and reproduce how it runs step-by-step on a piece of paper. + +Here's exactly what happens in our case: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // for (let i = 0; i < 3; i++) alert(i) @@ -140,7 +190,11 @@ if (i < 3) { alert(i); i++ } ``` ````smart header="Inline variable declaration" +<<<<<<< HEAD 这里“计数”变量 `i` 在循环中声明。这叫做“内联”变量声明。这样的变量只在循环中可见。 +======= +Here, the "counter" variable `i` is declared right in the loop. This is called an "inline" variable declaration. Such variables are visible only inside the loop. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run for (*!*let*/!* i = 0; i < 3; i++) { @@ -149,7 +203,11 @@ for (*!*let*/!* i = 0; i < 3; i++) { alert(i); // 错误,没有这个变量。 ``` +<<<<<<< HEAD 我们可以使用现有的变量而不是定义变量: +======= +Instead of defining a variable, we could use an existing one: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let i = 0; @@ -190,9 +248,15 @@ for (; i < 3;) { } ``` +<<<<<<< HEAD 该循环与 `while (i < 3)` 等价。 实际上我们可以删除所有内容,从而创建一个无限循环: +======= +This makes the loop identical to `while (i < 3)`. + +We can actually remove everything, creating an infinite loop: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js for (;;) { @@ -200,15 +264,27 @@ for (;;) { } ``` +<<<<<<< HEAD 请注意 `for` 的两个 `;` 必须存在,否则会出现语法错误。 +======= +Please note that the two `for` semicolons `;` must be present. Otherwise, there would be a syntax error. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 跳出循环 +<<<<<<< HEAD 通常条件为假时,循环会终止。 但我们随时都可以强制退出,因为有一个特殊的 `break` 指令可以做到这一点。 例如,下述循环要求用户输入一系列数字,但会在没有数字输入时候“终止”。 +======= +Normally, a loop exits when its condition becomes falsy. + +But we can force the exit at any time using the special `break` directive. + +For example, the loop below asks the user for a series of numbers, "breaking" when no number is entered: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js let sum = 0; @@ -227,15 +303,27 @@ while (true) { alert( 'Sum: ' + sum ); ``` +<<<<<<< HEAD 如果用户输入空行或取消输入,在 `(*)` 行 `break` 指令会被激活。它立刻终止循环,将控制权传递给循环后的第一行,即,`alert`。 根据需要,"无限循环 + `break`" 的组合非常适用于不必在循环开始/结束时检查条件,但在中间甚至是主体的多个位置进行检查的情况。 +======= +The `break` directive is activated at the line `(*)` if the user enters an empty line or cancels the input. It stops the loop immediately, passing control to the first line after the loop. Namely, `alert`. + +The combination "infinite loop + `break` as needed" is great for situations when a loop's condition must be checked not in the beginning or end of the loop, but in the middle or even in several places of its body. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 继续下一次迭代 [#continue] +<<<<<<< HEAD `continue` 指令是 `break` 的“轻量版”。这并不能阻止整个循环。相反,它将停止当前的迭代,并强制启动新一轮循环(如果条件允许的话)。 如果我们完成了当前的迭代,并且希望继续执行下一次迭代,我们就可以使用它。 +======= +The `continue` directive is a "lighter version" of `break`. It doesn't stop the whole loop. Instead, it stops the current iteration and forces the loop to start a new one (if the condition allows). + +We can use it if we're done with the current iteration and would like to move on to the next one. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 下述循环使用 `continue` 只输出奇数: @@ -249,10 +337,17 @@ for (let i = 0; i < 10; i++) { } ``` +<<<<<<< HEAD 对于偶数的 `i`,`continue` 指令停止执行,将控制权传递给下一次 `for`(使用下一个数字)的迭代。因此 `alert` 仅被奇数值调用。 ````smart header="The directive `continue` helps to decrease nesting level" 显示奇数的循环如下所示: +======= +For even values of `i`, the `continue` directive stops executing the body and passes control to the next iteration of `for` (with the next number). So the `alert` is only called for odd values. + +````smart header="The `continue` directive helps decrease nesting" +A loop that shows odd values could look like this: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js for (let i = 0; i < 10; i++) { @@ -264,6 +359,7 @@ for (let i = 0; i < 10; i++) { } ``` +<<<<<<< HEAD 从技术角度看,它与上述示例完全相同。当然,我们可以将代码包装在 `if` 块而不是 `continue` 块。 但作为副作用,我们还有一个嵌套级别(花括号内的 `alert` 调用)。如果 `if` 中代码超过几行,则可能会降低总体可读性。 @@ -271,6 +367,15 @@ for (let i = 0; i < 10; i++) { ````warn header="No `break/continue` to the right side of '?'" 请注意非表达式的语法结构不能与三元运算符 `?` 一起使用。特别是 `break/continue` 这样的指令是不被允许使用的。 +======= +From a technical point of view, this is identical to the example above. Surely, we can just wrap the code in an `if` block instead of using `continue`. + +But as a side-effect, this created one more level of nesting (the `alert` call inside the curly braces). If the code inside of`if` is longer than a few lines, that may decrease the overall readability. +```` + +````warn header="No `break/continue` to the right side of '?'" +Please note that syntax constructs that are not expressions cannot be used with the ternary operator `?`. In particular, directives such as `break/continue` aren't allowed there. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如,我们使用如下代码: @@ -282,24 +387,39 @@ if (i > 5) { } ``` +<<<<<<< HEAD ...然后用问号重写: +======= +...and rewrite it using a question mark: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js no-beautify -(i > 5) ? alert(i) : *!*continue*/!*; // continue not allowed here +(i > 5) ? alert(i) : *!*continue*/!*; // continue isn't allowed here ``` +<<<<<<< HEAD ...然后会停止运行。这样的代码将给出语法错误: 这只是不适用 `?` 而不是 `if` 的另一个原因。 +======= +...it stops working. Code like this will give a syntax error: + + +This is just another reason not to use the question mark operator `?` instead of `if`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```` ## break/continue 标签 有时候我们需要从多个嵌套循环中跳出来。 +<<<<<<< HEAD 例如,下述代码中我们的循环使用 `i` 和 `j`,提示坐标 `(i, j)` 从 `(0,0)` 到 `(3,3)`: +======= +For example, in the code below we loop over `i` and `j`, prompting for the coordinates `(i, j)` from `(0,0)` to `(3,3)`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run no-beautify for (let i = 0; i < 3; i++) { @@ -318,7 +438,11 @@ alert('Done!'); 如果用户取消输入,我们需要另一种方法来停止这个过程。 +<<<<<<< HEAD 在 `input` 之后的普通 `break` 只会打破内部循环。这还不够。标签可以拯救。 +======= +The ordinary `break` after `input` would only break the inner loop. That's not sufficient--labels, come to the rescue! +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb **标签**是在循环之前带有冒号的标识符: ```js @@ -327,9 +451,13 @@ labelName: for (...) { } ``` +<<<<<<< HEAD `break ` 语句跳出循环至标签处。 就像这样: +======= +The `break ` statement in the loop below breaks out to the label: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run no-beautify *!*outer:*/!* for (let i = 0; i < 3; i++) { @@ -347,7 +475,11 @@ labelName: for (...) { alert('Done!'); ``` +<<<<<<< HEAD 上述代码中,`break outer` 向上寻找名为 `outer` 的标签并跳出当前循环。 +======= +In the code above, `break outer` looks upwards for the label named `outer` and breaks out of that loop. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 因此,控制权直接从 `(*)` 转至 `alert('Done!')`。 @@ -358,10 +490,17 @@ outer: for (let i = 0; i < 3; i++) { ... } ``` +<<<<<<< HEAD `continue` 指令也可以与标签一起使用。在这种情况下,执行跳转到标记循环的下一次迭代。 ````warn header="Labels are not a \"goto\"" 标签不允许我们跳到任意代码位置。 +======= +The `continue` directive can also be used with a label. In this case, code execution jumps to the next iteration of the labeled loop. + +````warn header="Labels are not a \"goto\"" +Labels do not allow us to jump into an arbitrary place in the code. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如,这样做是不可能的: ```js @@ -370,7 +509,11 @@ break label; // 跳转到标签?不。 label: for (...) ``` +<<<<<<< HEAD 只有在循环内部才能调用 `break/continue`,并且标签必须位于指令上方的某个位置。 +======= +A call to `break/continue` is only possible from inside a loop and the label must be somewhere above the directive. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```` ## 总结 @@ -383,6 +526,12 @@ label: for (...) 通常使用 `while(true)` 来构造“无限”循环。这样的循环就像任何其他循环一样,可以通过 `break` 指令来终止。 +<<<<<<< HEAD 如果我们不想在当前迭代中做任何事,并且想要转移至下一次迭代,那么 `continue` 指令就会执行它。 `break/continue` 支持循环前的标签。标签是 `break/continue` 避免嵌套并转到外部循环的唯一方法。 +======= +If we don't want to do anything in the current iteration and would like to forward to the next one, we can use the `continue` directive. + +`break/continue` support labels before the loop. A label is the only way for `break/continue` to escape a nested loop to go to an outer one. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/02-first-steps/13-switch/article.md b/1-js/02-first-steps/13-switch/article.md index 0f53e310f2..5a546344ad 100644 --- a/1-js/02-first-steps/13-switch/article.md +++ b/1-js/02-first-steps/13-switch/article.md @@ -148,7 +148,7 @@ switch (a) { 比如,我们来看下面的代码: ```js run -let arg = prompt("Enter a value?") +let arg = prompt("Enter a value?"); switch (arg) { case '0': case '1': @@ -163,7 +163,7 @@ switch (arg) { alert( 'Never executes!' ); break; default: - alert( 'An unknown value' ) + alert( 'An unknown value' ); } ``` diff --git a/1-js/02-first-steps/14-function-basics/4-pow/task.md b/1-js/02-first-steps/14-function-basics/4-pow/task.md index 7acf9d7102..e66ddbd966 100644 --- a/1-js/02-first-steps/14-function-basics/4-pow/task.md +++ b/1-js/02-first-steps/14-function-basics/4-pow/task.md @@ -9,7 +9,7 @@ importance: 4 ```js pow(3, 2) = 3 * 3 = 9 pow(3, 3) = 3 * 3 * 3 = 27 -pow(1, 100) = 1 * 1 * ...*1 = 1 +pow(1, 100) = 1 * 1 * ...* 1 = 1 ``` 创建一个 web  页面,提示输入 `x` 和 `n` 然后返回 `pow(x,n)` 的函数值。 diff --git a/1-js/02-first-steps/14-function-basics/article.md b/1-js/02-first-steps/14-function-basics/article.md index 47f42f324b..4ed37cb742 100644 --- a/1-js/02-first-steps/14-function-basics/article.md +++ b/1-js/02-first-steps/14-function-basics/article.md @@ -101,7 +101,11 @@ showMessage(); alert( userName ); // *!*Bob*/!*, 值被函数修改 ``` +<<<<<<< HEAD 只有在没有本地变量的情况下才会使用外部变量。因此如果我们忘记了 `let`,可能会发生意外修改的情况。 +======= +The outer variable is only used if there's no local one. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 如果在函数中声明了同名变量,那么它**遮蔽**外部变量。例如,在如下代码中,函数使用本地的 `userName`,外部部分被忽略: @@ -128,7 +132,11 @@ alert( userName ); // *!*John*/!*,未更改,函数没有访问外部变量 全局变量在任意函数中都是可见的(除非被局部变量遮蔽)。 +<<<<<<< HEAD 通常,函数声明与任务相关的所有变量。全局变量只存储项目级的数据,所以这些变量从任何地方都可以访问是很重要的事情。现代的代码有很少或没有全局变量。大多数变量存在于它们的函数中。 +======= +It's a good practice to minimize the use of global variables. Modern code has few or no globals. Most variables reside in their functions. Sometimes though, they can be useful to store project-level data. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` ## 参数 @@ -204,6 +212,12 @@ function showMessage(from, text = anotherFunction()) { } ``` +```smart header="Evaluation of default parameters" + +In JavaScript, a default parameter is evaluated every time the function is called without the respective parameter. In the example above, `anotherFunction()` is called every time `showMessage()` is called without the `text` parameter. This is in contrast to some other languages like Python, where any default parameters are evaluated only once during the initial interpretation. + +``` + ````smart header="Default parameters old-style" 旧版本的 JavaScript 不支持默认参数。所以有其他的方法来支持它们,您可以在旧的脚本中找到。 @@ -263,7 +277,7 @@ function checkAge(age) { */!* } else { *!* - return confirm('Got a permission from the parents?'); + return confirm('Do you have permission from your parents?'); */!* } } @@ -334,7 +348,11 @@ return*!*;*/!* ## 函数命名 [#function-naming] +<<<<<<< HEAD 函数是行为。所以它们的名字通常是动词。它应该简短且尽可能准确地描述函数的作用。这样读代码的人就能得到正确的线索。 +======= +Functions are actions. So their name is usually a verb. It should be brief, as accurate as possible and describe what the function does, so that someone reading the code gets an indication of what the function does. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 一种普遍的做法是用动词前缀来开始一个函数,这个前缀模糊地描述了这个动作。团队内部必须就前缀的含义达成一致。 @@ -366,19 +384,33 @@ checkPermission(..) // 检查权限并返回 true/false 有几个违反这一规则的例子: +<<<<<<< HEAD - `getAge` —— 如果它显示一个 `alert` 和这个 age(只应该得到),那就是有问题的。 - `createForm` —— 如果它修改文档,向它添加一个表单(只应该创建它并返回),那就是有问题的。 - `checkPermission` —— 如果显示 `access granted/denied` 消息(只应执行检查并返回结果),那就是错误的。 这些例子具有前缀的共同含义。它们对您的意义取决于您和您的团队。也许您的代码行为不同是很正常的。但是您应该对前缀意味着什么,前缀函数能做什么和不能做什么有一个明确的理解。所有相同的前缀函数都应遵守规则。团队应该分享知识。 +======= +- `getAge` -- would be bad if it shows an `alert` with the age (should only get). +- `createForm` -- would be bad if it modifies the document, adding a form to it (should only create it and return). +- `checkPermission` -- would be bad if it displays the `access granted/denied` message (should only perform the check and return the result). + +These examples assume common meanings of prefixes. You and your team are free to agree on other meanings, but usually they're not much different. In any case, you should have a firm understanding of what a prefix means, what a prefixed function can and cannot do. All same-prefixed functions should obey the rules. And the team should share the knowledge. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` ```smart header="Ultrashort function names" 常用的函数有时会有**非常短**的名字。 +<<<<<<< HEAD 例如,[jQuery](http://jquery.com) 框架定义函数用 `$`。[LoDash](http://lodash.com/) 库的核心函数命名用 `_`。 这些都是例外,一般而言,函数名应简明扼要且具有描述性。 +======= +For example, the [jQuery](http://jquery.com) framework defines a function with `$`. The [Lodash](http://lodash.com/) library has its core function named `_`. + +These are exceptions. Generally functions names should be concise and descriptive. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` ## 函数 == 注释 diff --git a/1-js/02-first-steps/14-function-basics/function_basics.png b/1-js/02-first-steps/14-function-basics/function_basics.png index 7eb6e11e54..f5e6f94183 100644 Binary files a/1-js/02-first-steps/14-function-basics/function_basics.png and b/1-js/02-first-steps/14-function-basics/function_basics.png differ diff --git a/1-js/02-first-steps/14-function-basics/function_basics@2x.png b/1-js/02-first-steps/14-function-basics/function_basics@2x.png index e629bd712c..c31b2636a6 100644 Binary files a/1-js/02-first-steps/14-function-basics/function_basics@2x.png and b/1-js/02-first-steps/14-function-basics/function_basics@2x.png differ diff --git a/1-js/02-first-steps/15-function-expressions-arrows/article.md b/1-js/02-first-steps/15-function-expressions-arrows/article.md index e4f5da2a18..90991c6ba8 100644 --- a/1-js/02-first-steps/15-function-expressions-arrows/article.md +++ b/1-js/02-first-steps/15-function-expressions-arrows/article.md @@ -78,8 +78,13 @@ let func = sayHi; 这两种声明的函数是一样的,那么他们有什么不同的地方呢? +<<<<<<< HEAD ````smart header="Why there's a semicolon at the end?" 这里可能有个疑问,为什么函数表达式结尾有一个 `;`,而函数声明没有: +======= +````smart header="Why is there a semicolon at the end?" +You might wonder, why does Function Expression have a semicolon `;` at the end, but Function Declaration does not: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js function sayHi() { @@ -175,7 +180,11 @@ ask( 让我们来阐述函数声明和表达式之间的关键区别。 +<<<<<<< HEAD 首先是语法:看下面代码 +======= +First, the syntax: how to differentiate between them in the code. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - **函数声明:** 函数在主代码流中单独声明。 @@ -185,8 +194,13 @@ ask( return a + b; } ``` +<<<<<<< HEAD - **函数表达式:** 一个函数,在一个表达式中或另一个语法结构中创建。这里,该函数由赋值表达式 `=` 右侧创建:     +======= +- *Function Expression:* a function, created inside an expression or inside another syntax construct. Here, the function is created at the right side of the "assignment expression" `=`: + +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // Function Expression let sum = function(a, b) { @@ -198,11 +212,19 @@ ask( **函数表达式在执行到达时创建并可用。** +<<<<<<< HEAD 一旦执行到右侧分配 `let sum = function…`,就会创建并可以使用(复制,调用等)。 +======= +Once the execution flow passes to the right side of the assignment `let sum = function…` -- here we go, the function is created and can be used (assigned, called, etc. ) from now on. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 函数声明则不同。 +<<<<<<< HEAD **函数声明可用于整个脚本/代码块。** +======= +**A Function Declaration is usable in the whole script (or a code block, if it's inside a block).** +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 换句话说,当 JavaScript **准备**运行脚本或代码块时,它首先在其中查找函数声明并创建函数。我们可以将其视为“初始化阶段”。 @@ -350,8 +372,13 @@ welcome(); // ok now ``` +<<<<<<< HEAD ```smart header="When to choose Function Declaration versus Function Expression?" 作为一个经验,当我们需要声明一个函数时,首先要考虑的是函数声明语法,这是我们之前使用的语法。它给如何组织我们的代码提供了更多的自由,因为我们可以在声明它们之前调用这些函数。 +======= +```smart header="When should you choose Function Declaration versus Function Expression?" +As a rule of thumb, when we need to declare a function, the first to consider is Function Declaration syntax, the one we used before. It gives more freedom in how to organize our code, because we can call such functions before they are declared. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 在代码中查找 `function f(…) {…}` 比 `let f = function(…) {…}` 更容易。 @@ -375,7 +402,7 @@ let func = (arg1, arg2, ...argN) => expression ```js let func = function(arg1, arg2, ...argN) { return expression; -} +}; ``` ...精简下。 diff --git a/1-js/02-first-steps/16-javascript-specials/article.md b/1-js/02-first-steps/16-javascript-specials/article.md index f8c6018e85..efd848be1d 100644 --- a/1-js/02-first-steps/16-javascript-specials/article.md +++ b/1-js/02-first-steps/16-javascript-specials/article.md @@ -102,9 +102,14 @@ typeof function(){} == "function" // 函数特殊 ## 交互 +<<<<<<< HEAD 我们使用浏览器作为工作环境,所以基本的 UI 功能将是: [`prompt(question[, default])`](mdn:api/Window/prompt) : 询问一个问题,并返回访问者输入的内容,如果他按下「取消」则返回 `null`。 +======= +[`prompt(question, [default])`](mdn:api/Window/prompt) +: Ask a `question`, and return either what the visitor entered or `null` if they clicked "cancel". +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb [`confirm(question)`](mdn:api/Window/confirm) : 提出一个问题,并建议在确定和取消之间进行选择。该选项以 `true/false` 形式返回。 @@ -112,7 +117,11 @@ typeof function(){} == "function" // 函数特殊 [`alert(message)`](mdn:api/Window/alert) : 输出一个 `消息`。 +<<<<<<< HEAD 所有这些函数都会产生**模态框**,它们会暂停代码执行并阻止访问者与页面交互,直到用户输入内容。 +======= +All these functions are *modal*, they pause the code execution and prevent the visitor from interacting with the page until they answer. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 举个例子: @@ -148,8 +157,13 @@ JavaScript 支持以下运算符: 三元运算 : 唯一具有三个参数的操作:`cond? resultA: resultB`。如果 `cond` 是真的,则返回 `resultA`,否则返回 `resultB`。 +<<<<<<< HEAD 逻辑运算符 : 逻辑与 `&&` 和或 `||` 执行短路评估,然后返回停止时的值。 +======= +Logical operators +: Logical AND `&&` and OR `||` perform short-circuit evaluation and then return the value where it stopped. Logical NOT `!` converts the operand to boolean type and returns the inverse value. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 比较运算符 : 运算符 `==` 将不同类型的值转换为一个数字(除了 `null` 和 `undefined`,它们彼此相等而没有别的情况),所以下面的例子是相等的: @@ -160,12 +174,25 @@ JavaScript 支持以下运算符: ``` 其他比较也转换为数字。 +<<<<<<< HEAD 严格相等运算符 `===` 不会进行转换:不同的类型总是为其指定不同的值,因此: 值 `null` 和 `undefined` 是特殊的:它们只在 `==` 下相等。 字符串按照字符顺序逐一比较,其他类型转换为数字。 逻辑运算符 : 其他合规的运算符比较少,其中有逗号运算符。 +======= + Other comparisons convert to a number as well. + + The strict equality operator `===` doesn't do the conversion: different types always mean different values for it. + + Values `null` and `undefined` are special: they equal `==` each other and don't equal anything else. + + Greater/less comparisons compare strings character-by-character, other types are converted to a number. + +Other operators +: There are few others, like a comma operator. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb More in: , , . 更多信息:, , 。 diff --git a/1-js/03-code-quality/01-debugging-chrome/article.md b/1-js/03-code-quality/01-debugging-chrome/article.md index 2b69951f14..26aa6d3c39 100644 --- a/1-js/03-code-quality/01-debugging-chrome/article.md +++ b/1-js/03-code-quality/01-debugging-chrome/article.md @@ -20,7 +20,11 @@ 切换按钮 会打开文件列表的选项卡。 +<<<<<<< HEAD 让我们在预览树中点击和选择 `index.html` 和 `hello.js`。应该会出现这个视图: +======= +Let's click it and select `hello.js` in the tree view. Here's what should show up: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ![](chrome-tabs.png) @@ -34,7 +38,11 @@ ## Console(控制台) +<<<<<<< HEAD 如果我们按下 `Esc`,下面会出现一个控制台,我们可以输入一些命令然后按下 `key:Enter` 来执行。 +======= +If we press `key:Esc`, then a console opens below. We can type commands there and press `key:Enter` to execute. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 语句执行之后,会将其结果显示在下面。 @@ -56,11 +64,19 @@ 当代码被暂停时,我们可以检查当前的变量、在控制台执行命令等等。换句话说,我们可以调试它。 +<<<<<<< HEAD 我们总是可以在右侧的面板中找到断点的列表。当我们在数个文件中有许多断点时,这是非常有用的。 - 快速跳转至代码中的断点(通过点击右侧面板中的对应的断点)。 - 通过取消选中来临时禁用。 - 通过右键单击和选择移除来删除一个断点。 - 等等。 +======= +We can always find a list of breakpoints in the right pane. That's useful when we have many breakpoints in various files. It allows us to: +- Quickly jump to the breakpoint in the code (by clicking on it in the right pane). +- Temporarily disable the breakpoint by unchecking it. +- Remove the breakpoint by right-clicking and selecting Remove. +- ...And so on. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```smart header="条件断点" 在行号上**右键单击**允许你创建一个**条件**断点。只有当给定的条件为真(即满足条件)时才会被触发。 @@ -169,10 +185,17 @@ for (let i = 0; i < 5; i++) { ## 总结 +<<<<<<< HEAD 我们可以看到,有 3 种方式来暂停一个脚本: 1. 一个断点。 2. `debugger` 声明。 3. 一个错误(如果开发者工具是打开状态并且按钮 是开启状态)。 +======= +As we can see, there are three main ways to pause a script: +1. A breakpoint. +2. The `debugger` statements. +3. An error (if dev tools are open and the button is "on"). +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 然后我们就能检查变量,并逐步查看执行器在哪里走错路了。 diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.png b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.png index efa3c19df1..abc59905a2 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources@2x.png index e184bdd01f..546615e30e 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources@2x.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources@2x.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.png index 2fe449c9b6..caf60ebb61 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint@2x.png index e4abc89d1f..3f628b6ffc 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint@2x.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint@2x.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.png index 98b22e777c..0fc22b2ea6 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console@2x.png index 3269a80f06..f01a7d1017 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console@2x.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console@2x.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.png index 719293d2e5..424ca26b0a 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause@2x.png index 5c22ab361b..04cc849d10 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause@2x.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause@2x.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.png index 1848ccfacf..00507833a3 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1@2x.png index fcabf722eb..d2a38bf0c9 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1@2x.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1@2x.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.png b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.png index ff91c531f8..df9e13f3fb 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.png differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs@2x.png index 09b10bf48d..5793bd0596 100644 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs@2x.png and b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs@2x.png differ diff --git a/1-js/03-code-quality/02-coding-style/1-style-errors/solution.md b/1-js/03-code-quality/02-coding-style/1-style-errors/solution.md index 3ce1ed0d20..8aa0800943 100644 --- a/1-js/03-code-quality/02-coding-style/1-style-errors/solution.md +++ b/1-js/03-code-quality/02-coding-style/1-style-errors/solution.md @@ -2,6 +2,7 @@ 你可以注意到以下几点: ```js no-beautify +<<<<<<< HEAD function pow(x,n) // <- 参数之间没有空格 { // <- 大括号单独一行 let result=1; // <- = 号两边没有空格 @@ -15,11 +16,30 @@ let x=prompt("x?",''), n=prompt("n?",'') // <-- 从技术角度来看是可以 if (n<0) // <- (n < 0) 里面没有空格,并且应该在前面加一个空行 { // <- 大括号单独一行 // 下面的一行太长了,或许拆分成 2 行更好 +======= +function pow(x,n) // <- no space between arguments +{ // <- figure bracket on a separate line + let result=1; // <- no spaces before or after = + for(let i=0;i>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb alert(`Power ${n} is not supported, please enter an integer number greater than zero`); } else // <- 可以像 "} else {" 这样写在一行上 { +<<<<<<< HEAD alert(pow(x,n)) // 没有空格和 ; +======= + alert(pow(x,n)) // no spaces and missing ; +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb } ``` diff --git a/1-js/03-code-quality/02-coding-style/article.md b/1-js/03-code-quality/02-coding-style/article.md index 47cd5b680b..ec8553087b 100644 --- a/1-js/03-code-quality/02-coding-style/article.md +++ b/1-js/03-code-quality/02-coding-style/article.md @@ -1,14 +1,26 @@ +<<<<<<< HEAD # 代码风格 +======= +# Coding Style +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 我们的代码必须尽可能的清晰和易读。 +<<<<<<< HEAD 这实际是一种编程艺术 —— 以一种正确并且人类易读的方式编码来完成一个复杂的任务。 有一个帮助你(实现上面的目标)的事情就是良好的代码风格。 +======= +That is actually the art of programming -- to take a complex task and code it in a way that is both correct and human-readable. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 语法 +<<<<<<< HEAD 一个含有规则的备忘录(更多细节如下): +======= +Here is a cheatsheet with some suggested rules (see below for more details): +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ![](code-style.png) ![](figure-bracket-style.png) +<<<<<<< HEAD 总结: - 对于很短的代码,一行是可以接受的:例如 `if (cond) return null`. - 但是括号中的每个语句单独一行通常更好些。 @@ -83,12 +110,27 @@ if (n < 0) { 一行的最大长度应该有所限制。没有人喜欢盯着一条长长的水平线。最好把它分割一下。 一行的最大长度在团队层面上达成一致即可。通常是 80 或 120 个字符。 +======= +In summary: +- For very short code, one line is acceptable. For example: `if (cond) return null`. +- But a separate line for each statement in brackets is usually easier to read. + +### Line Length + +No one likes to read a long horizontal line of code. It's best practice to split them up and limit the length of your lines. + +The maximum line length should be agreed upon at the team-level. It's usually 80 or 120 characters. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ### 缩进 有两种类型的缩进: +<<<<<<< HEAD - **水平方向上的缩进: 2(4) 个空格。** +======= +- **Horizontal indents: 2 or 4 spaces.** +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 一个水平缩进通常由 2 或 4 个空格或者 "Tab" 制表符构成。选择哪一个方式是一场古老的圣战。如今空格更普遍一点。 @@ -107,9 +149,15 @@ if (n < 0) { } ``` +<<<<<<< HEAD - **垂直方向上的缩进:用于将逻辑块中的代码进行分割的空行。** 即使是单个函数通常也被分割为数个逻辑块。在下面的例子中,初始化的变量、主要的循环结构和返回值都被垂直分割了。 +======= +- **Vertical indents: empty lines for splitting code into logical blocks.** + + Even a single function can often be divided into logical blocks. In the example below, the initialization of variables, the main loop and returning the result are split vertically: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js function pow(x, n) { @@ -125,6 +173,7 @@ if (n < 0) { 插入一个额外的空行有助于让代码更加地易读。连续超过 9 行都没有被垂直分割的代码是不应该出现的。 +<<<<<<< HEAD ### 分号 每一个语句后面都应该有一个分号。即使它可能会被跳过。 @@ -140,6 +189,23 @@ if (n < 0) { 有时候在循环中使用 ["continue"](info:while-for#continue) 指令避免额外的 `if(..) { ... }` 嵌套是一个好主意: 例如: +======= +### Semicolons + +A semicolon should be present after each statement, even if it could possibly be skipped. + +There are languages where a semicolon is truly optional and it is rarely used. In JavaScript, though, there are cases where a line break is not interpreted as a semicolon, leaving the code vulnerable to errors. + +As you become more mature as a programmer, you may choose a no-semicolon style like [StandardJS](https://standardjs.com/). Until then, it's best to use semicolons to avoid possible pitfalls. + +### Nesting Levels + +Try to avoid nesting code too many levels deep. + +Sometimes it's a good idea to use the ["continue"](info:while-for#continue) directive in a loop to avoid extra nesting. + +For example, instead of adding a nested `if` conditional like this: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js for (let i = 0; i < 10; i++) { @@ -162,7 +228,11 @@ for (let i = 0; i < 10; i++) { 例如,下面的两个结构是相同的。 +<<<<<<< HEAD 第一个: +======= +Option 1: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js function pow(x, n) { @@ -180,7 +250,11 @@ function pow(x, n) { } ``` +<<<<<<< HEAD 还有这个: +======= +Option 2: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js function pow(x, n) { @@ -199,6 +273,7 @@ function pow(x, n) { } ``` +<<<<<<< HEAD 但是第二个更加的可读,因为 `n < 0` 这个”边缘情况“已经提前被处理过,并且我们有一个 ”主“ 代码流,而不需要额外的嵌套。 ## 函数在代码下面 @@ -206,6 +281,15 @@ function pow(x, n) { 如果你正在写几个”辅助类“的函数和一些使用它们的代码,有三种方式来放置它们。 1. 函数在调用它们的那些代码之上: +======= +The second one is more readable because the "edge case" of `n < 0` is handled early on. Once the check is done we can move on to the "main" code flow without the need for additional nesting. + +## Function Placement + +If you are writing several "helper" functions and the code that uses them, there are three ways to organize the functions. + +1. Functions declared above the code that uses them: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // *!*函数声明*/!* @@ -235,7 +319,6 @@ function pow(x, n) { walkAround(); // --- *!*helper functions*/!* --- - function createElement() { ... } @@ -248,10 +331,15 @@ function pow(x, n) { ... } ``` +<<<<<<< HEAD 3. 混合,函数定义在它第一次被使用的地方。 +======= +3. Mixed: a function is declared where it's first used. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 大多数时候,第二种方式更好。 +<<<<<<< HEAD 这是因为当在阅读代码时,我们首先想要知道的是“它做了什么”。如果代码先行,它就会提供这些信息。或许我们一点也不需要阅读这些函数,尤其是他们的名字足够表示出他们做了什么的时候。 ## 风格指南 @@ -263,11 +351,25 @@ function pow(x, n) { 当然,一个团队可能会考虑一个他们自己的风格指南。但是现在,他们没必要这样做。现在有很多已经尝试过并制作好的风格指南,可以很容易采用。 例如: +======= +That's because when reading code, we first want to know *what it does*. If the code goes first, then it provides that information. Then, maybe we won't need to read the functions at all, especially if their names are descriptive of what they actually do. + +## Style Guides + +A style guide contains general rules about "how to write" code, e.g. which quotes to use, how many spaces to indent, where to put line breaks, etc. A lot of minor things. + +When all members of a team use the same style guide, the code looks uniform, regardless of which team member wrote it. + +Of course, a team can always write their own style guide. Most of the time though, there's no need to. There are many existing tried and true options to choose from, so adopting one of these is usually your best bet. + +Some popular choices: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - [Google JavaScript 风格指南](https://google.github.io/styleguide/javascriptguide.xml) - [Airbnb JavaScript 风格指南](https://github.com/airbnb/javascript) - [Idiomatic.JS](https://github.com/rwaldron/idiomatic.js) - [StandardJS](https://standardjs.com/) +<<<<<<< HEAD - (还有很多) 如果你是一个初学者,你可以从本章中上面的内容开始,然后浏览风格指南并提取出常见规则或者选择一个。 @@ -281,6 +383,19 @@ function pow(x, n) { 因此推荐你安装一个,即使你不想坚持某个 "code style"。它们会帮你找出书写错误 —— 这就已经足够好了。 最出名的工具有: +======= +- (plus many more) + +If you're a novice developer, start with the cheatsheet at the beginning of this chapter. Once you've mastered that you can browse other style guides to pick up common principles and decide which one you like best. + +## Automated Linters + +Linters are tools that can automatically check the style of your code and make suggestions for refactoring. + +The great thing about them is that style-checking can also find some bugs, like typos in variable or function names. Because of this feature, installing a linter is recommended even if you don't want to stick to one particular "code style". + +Here are the most well-known linting tools: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - [JSLint](http://www.jslint.com/) -- 第一批 linters 之一。 - [JSHint](http://www.jshint.com/) -- 比 JSLint 多了更多设置。 @@ -288,15 +403,28 @@ function pow(x, n) { 它们都能够做这些工作。笔者使用 [ESLint](http://eslint.org/). +<<<<<<< HEAD 大多数 linters 都可以与编辑器集成在一起:只需在编辑器中启用插件并配置风格即可。 +======= +Most linters are integrated with many popular editors: just enable the plugin in the editor and configure the style. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如,要使用 ESLint 你应该这样做: +<<<<<<< HEAD 1. 安装 [Node.JS](https://nodejs.org/). 2. 使用 `npm install -g eslint` 命令(npm 是 Node.JS 的包安装工具)安装 ESLint。 3. 在你项目的根目录(包含你所有文件的那个目录)创建一个名叫 `.eslintrc` 的配置文件。 这有一个 `.eslintrc` 的例子: +======= +1. Install [Node.js](https://nodejs.org/). +2. Install ESLint with the command `npm install -g eslint` (npm is a JavaScript package installer). +3. Create a config file named `.eslintrc` in the root of your JavaScript project (in the folder that contains all your files). +4. Install/enable the plugin for your editor that integrates with ESLint. The majority of editors have one. + +Here's an example of an `.eslintrc` file: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js { @@ -313,6 +441,7 @@ function pow(x, n) { } ``` +<<<<<<< HEAD 这里的 `"extends"` 指令表示我们是基于 "eslint:recommended" 的设置项而进行设置的,并且我们还制定了我们自己的规则。 在你的编辑器中安装 / 启用插件以和 ESLint 集成。大多数编辑都有的。 @@ -324,11 +453,26 @@ function pow(x, n) { 因此即使你不关心风格,也推荐你使用一个 linter。 某些 IDE 还支持内置的 linting,但不像 ESLint 那么灵活可配置。 +======= +Here the directive `"extends"` denotes that the configuration is based on the "eslint:recommended" set of settings. After that, we specify our own. + +It is also possible to download style rule sets from the web and extend them instead. See for more details about installation. + +Also certain IDEs have built-in linting, which is convenient but not as customizable as ESLint. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 总结 +<<<<<<< HEAD 本章的所有语法规则和样式指南旨在提高可读性,因此所有的内容都是值得商榷的。 当我们思考“如何写地更好”的时候,唯一的标准是“什么会让代码更加可读和容易理解,什么会帮助我们避免错误”。这是当选择一种风格或讨论哪一种更好的时候要牢记的主要原则。 阅读风格指南,以查看相关的最新想法,并遵循那些你发现的最好的。 +======= +All syntax rules described in this chapter (and in the style guides referenced) aim to increase the readability of your code, but all of them are debatable. + +When we think about writing "better" code, the questions we should ask are, "What makes the code more readable and easier to understand?" and "What can help us avoid errors?" These are the main things to keep in mind when choosing and debating code styles. + +Reading popular style guides will allow you to keep up to date with the latest ideas about code style trends and best practices. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/03-code-quality/02-coding-style/code-style.png b/1-js/03-code-quality/02-coding-style/code-style.png index 278fd294cf..1c6d355dd5 100644 Binary files a/1-js/03-code-quality/02-coding-style/code-style.png and b/1-js/03-code-quality/02-coding-style/code-style.png differ diff --git a/1-js/03-code-quality/02-coding-style/code-style@2x.png b/1-js/03-code-quality/02-coding-style/code-style@2x.png index b576143455..832623d9c1 100644 Binary files a/1-js/03-code-quality/02-coding-style/code-style@2x.png and b/1-js/03-code-quality/02-coding-style/code-style@2x.png differ diff --git a/1-js/03-code-quality/02-coding-style/figure-bracket-style.png b/1-js/03-code-quality/02-coding-style/figure-bracket-style.png index 112c2803ed..b04db65c67 100644 Binary files a/1-js/03-code-quality/02-coding-style/figure-bracket-style.png and b/1-js/03-code-quality/02-coding-style/figure-bracket-style.png differ diff --git a/1-js/03-code-quality/02-coding-style/figure-bracket-style@2x.png b/1-js/03-code-quality/02-coding-style/figure-bracket-style@2x.png index ce6e75c4dd..0e994ca4b3 100644 Binary files a/1-js/03-code-quality/02-coding-style/figure-bracket-style@2x.png and b/1-js/03-code-quality/02-coding-style/figure-bracket-style@2x.png differ diff --git a/1-js/03-code-quality/03-comments/article.md b/1-js/03-code-quality/03-comments/article.md index c83bac1011..7f5609a3ae 100644 --- a/1-js/03-code-quality/03-comments/article.md +++ b/1-js/03-code-quality/03-comments/article.md @@ -162,7 +162,11 @@ function addJuice(container) { ## 总结 +<<<<<<< HEAD 一个好的开发者的标志之一就是他的注释:它们的存在甚至它们的缺席。 +======= +An important sign of a good developer is comments: their presence and even their absence. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 好的注释可以使我们更好的维护代码,并且在很长时间之后依然可以更高效地回到代码中和使用其功能。 diff --git a/1-js/03-code-quality/04-ninja-code/article.md b/1-js/03-code-quality/04-ninja-code/article.md index 2f9b55d219..58f6c6be16 100644 --- a/1-js/03-code-quality/04-ninja-code/article.md +++ b/1-js/03-code-quality/04-ninja-code/article.md @@ -5,7 +5,11 @@ 学而不思则罔,思而不学则殆。 ``` +<<<<<<< HEAD 过去的程序员忍者使用这些技巧来使代码维护者的头脑更加敏锐。 +======= +Programmer ninjas of the past used these tricks to sharpen the mind of code maintainers. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 代码审查大师在测试任务中寻找它们。 @@ -32,9 +36,15 @@ i = i ? i < 0 ? Math.max(0, len + i) : i : 0; ``` +<<<<<<< HEAD 很酷,对吗?如果你这样写了,那些看到这一行代码并尝试去理解 `i` 的值是什么的开发者们就会有一个“快活的”的时光了。然后会来找你寻求答案。 告诉他短一点总是更好的。引导他进入忍者之路。 +======= +Cool, right? If you write like that, a developer who comes across this line and tries to understand what is the value of `i` is going to have a merry time. Then come to you, seeking for an answer. + +Tell them that shorter is always better. Initiate them into the paths of ninja. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 一个字母的变量 @@ -44,11 +54,19 @@ i = i ? i < 0 ? Math.max(0, len + i) : i : 0; 编码更快(也更糟糕)的另一种方式是到处使用单字母的变量名。像是 `a`、`b` 或 `c`。 +<<<<<<< HEAD 短变量会像森林中真正的忍者一样在代码中消失不见。没有人能够通过编辑器的“搜索”找到它。即使有人做到了,他也不能“破译”出变量名 `a` 或 `b` 是什么意思。 +======= +A short variable disappears in the code like a real ninja in the forest. No one will be able to find it using "search" of the editor. And even if someone does, they won't be able to "decipher" what the name `a` or `b` means. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ...但是有一个例外情况。一个真正的忍者绝不会在 `"for"` 循环中使用 `i` 作为计数器。在任何地方都可以,但是这里不会用。看一下四周吧,还有很多不常用的字母呢。例如 `x` 或 `y`。 +<<<<<<< HEAD 如果循环体能够达到 1-2 页(如果可以的话可以让它更长)那么长的话,使用一个不常用的变量作为循环的计数器就更酷了。如果某人看到循环内部的深处后,他就不能很快地找出变量 `x` 是循环计数器啦。 +======= +An exotic variable as a loop counter is especially cool if the loop body takes 1-2 pages (make it longer if you can). Then if someone looks deep inside the loop, they won't be able to quickly figure out that the variable named `x` is the loop counter. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 使用缩写 @@ -82,7 +100,11 @@ i = i ? i < 0 ? Math.max(0, len + i) : i : 0; 尝试一下吧。新手可能会诧异 —— 这些名字对于忍者来说真的有用吗?事实上,是的! +<<<<<<< HEAD 一方面,变量名仍然有着一些含义。它说明了变量内是什么:一个字符串、一个数字或是其他的东西。但是当一个局外人试图理解代码时,他会惊讶地发现实际上没有任何有效信息!最终无法改变你精心思考过的代码。 +======= + Sure, the variable name still means something. It says what's inside the variable: a string, a number or something else. But when an outsider tries to understand the code, they'll be surprised to see that there's actually no information at all! And will ultimately fail to alter your well-thought code. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 事实上,值的类型很容易就能通过调试看出来。但是变量名的含义呢?它存了哪一个字符串/数字? @@ -152,9 +174,15 @@ function ninjaFunction(elem) { } ``` +<<<<<<< HEAD 想要在第二部分中使用 `elem` 的程序员会非常的诧异滴...只有在调试期间,在检查代码之后,他会发现他正在使用克隆过的变量! 经常看到这样的代码,即使对经验丰富的忍者来说也是致命的。 +======= +A fellow programmer who wants to work with `elem` in the second half of the function will be surprised... Only during the debugging, after examining the code they will find out that they're working with a clone! + +Seen in code regularly. Deadly effective even against an experienced ninja. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 下划线的乐趣 @@ -170,7 +198,13 @@ function ninjaFunction(elem) { 事实上,从一方面来说,看似写了一些东西:`super..`、`mega..`、`nice..`,但是从另一方面来说 —— 并没有提供任何细节。读者可能要寻找一个隐藏的含义或深思一两个小时。 +<<<<<<< HEAD ## 重叠外部变量 +======= + + +## Overlap outer variables +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```quote author="关尹子" 处明者不见暗中一物
, @@ -194,7 +228,11 @@ function render() { 跳过 `render` 的程序员可能不会注意到有一个本地 `user` 遮挡外部的 `user` 了。 +<<<<<<< HEAD 然后他会假设 `user` 仍然是外部的变量然后使用它,`authenticateUser()` 的结果... 陷阱出来啦!你好呀,调试器... +======= +Then they'll try to work with `user` assuming that it's the external variable, the result of `authenticateUser()`... The trap is sprung! Hello, debugger... +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 无处不在的副作用! @@ -203,7 +241,11 @@ function render() { **一个非常好的技巧 - 除了主要任务之外,还要向它们添加一个“有用的”动作** +<<<<<<< HEAD 当你的同事看到被命名为 `is..`、`check..` 或 `find...` 的函数改变了某些东西的时候,他的脸上肯定是一脸懵逼的状态。 +======= +An expression of dazed surprise on the face of your colleague when they see a function named `is..`, `check..` or `find...` changing something -- will definitely broaden your boundaries of reason. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb **另一种惊喜的方式是返回非标准的结果。** @@ -226,7 +268,11 @@ function render() { **将多个动作加入到一起可以保护您的代码避免重用。** +<<<<<<< HEAD 想象一下,另一个开发者只想检查邮箱而不想输出任何信息。你的函数 `validateEmail(email)` 对他而言就不合适啦。所以他不会找你问一些关于这些函数的事情从而打断你的思考。 +======= +Imagine, another developer wants only to check the email, and not output any message. Your function `validateEmail(email)` that does both will not suit them. So they won't break your meditation by asking anything about it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 总结 diff --git a/1-js/03-code-quality/05-testing-mocha/article.md b/1-js/03-code-quality/05-testing-mocha/article.md index 988534045f..5601291113 100644 --- a/1-js/03-code-quality/05-testing-mocha/article.md +++ b/1-js/03-code-quality/05-testing-mocha/article.md @@ -96,7 +96,11 @@ describe("pow", function() { ```html src="index.html" ``` +<<<<<<< HEAD 该页面可分为四部分: +======= +The page can be divided into five parts: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 1. `` -- 为测试添加第三方库和样式文件。 2. ` diff --git a/1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js index 10a032d03a..e5ce2ce433 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js @@ -1,6 +1,6 @@ describe("pow", function() { - describe("raises x to power n", function() { + describe("raises x to power 3", function() { function makeTest(x) { let expected = x * x * x; diff --git a/1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js index a5a3459795..75ff5e99fd 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js @@ -1,6 +1,6 @@ describe("pow", function() { - describe("raises x to power n", function() { + describe("raises x to power 3", function() { function makeTest(x) { let expected = x * x * x; diff --git a/1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js index a5a3459795..75ff5e99fd 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js @@ -1,6 +1,6 @@ describe("pow", function() { - describe("raises x to power n", function() { + describe("raises x to power 3", function() { function makeTest(x) { let expected = x * x * x; diff --git a/1-js/03-code-quality/06-polyfills/article.md b/1-js/03-code-quality/06-polyfills/article.md index 4dfc4de49b..6bb97796da 100644 --- a/1-js/03-code-quality/06-polyfills/article.md +++ b/1-js/03-code-quality/06-polyfills/article.md @@ -19,21 +19,38 @@ JavaScript 引擎背后的团队关于首先要实现什么有着他们自己想 实际上,Babel 包含了两部分: +<<<<<<< HEAD 1. 第一,transpiler 程序,就是重写代码的那个。开发者在他自己的电脑上运行它。它将代码重写到旧的标准中。然后将代码交付给用户的网站。诸如 [webpack](http://webpack.github.io/) 或 [brunch](http://brunch.io/) 这样的现代项目构建系统提供了每当代码改变都会自动运行 transpiler 的方法,因此我们这边没有任何的时间损失。 +======= +1. First, the transpiler program, which rewrites the code. The developer runs it on their own computer. It rewrites the code into the older standard. And then the code is delivered to the website for users. Modern project build system like [webpack](http://webpack.github.io/) provide means to run transpiler automatically on every code change, so that very easy to integrate into development process. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 2. 第二,polyfill。 +<<<<<<< HEAD transpiler 会重写代码,因此现有的语法特性都被覆盖了。但是对于新特性我们需要写一个特殊的脚本来实现它们。JavaScript 是一个高度动态化的语言。脚本可能不仅是添加一些新特性,还会修改一些内置特性,以便于它们表现得符合现代标准。 脚本有一个术语 "polyfill" 表示用来“填补”缺口并添加缺少的实现。 +======= + New language features may include new built-in functions and syntax constructs. + The transpiler rewrites the code, transforming syntax constructs into older ones. But as for new built-in functions, we need to implement them. JavaScript is a highly dynamic language, scripts may add/modify any functions, so that they behave according to the modern standard. + + A script that updates/adds new functions is called "polyfill". It "fills in" the gap and adds missing implementations. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 两个有意思的 polyfills 是: - [babel polyfill](https://babeljs.io/docs/usage/polyfill/) 支持很多,但是很大。 - [polyfill.io](http://polyfill.io) 服务允许我们能根据自己所需的特性来按需加载、构建 polyfill。 +<<<<<<< HEAD 因此,我们需要为那些旧引擎设置 transpiler 并添加 polyfill 来支持现代特性。 如果我们的目标是现代引擎,并且整个项目不使用其他地方不支持的特性,那么我们就不需要使用 Babel。 +======= +So, if we're going to use modern language features, a transpiler and a polyfill are necessary. + +## Examples in the tutorial +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 教程中的案例 @@ -48,9 +65,16 @@ alert('Press the "Play" button in the upper-right corner to run'); ```` ```offline +<<<<<<< HEAD 当你正在阅读离线版本时,例子是不可运行的。但是它们通常是可以工作的。:) ``` [Chrome Canary](https://www.google.com/chrome/browser/canary.html) 适用于所有示例,但其他现代浏览器大多数也都很好。 需要注意的是,在生产环境中我们可以用 Babel 来将代码转换为适合少数近代浏览器的代码,所以它们没有这样的限制,代码会在任何地方运行。 +======= +As you're reading the offline version, in PDF examples are not runnable. In EPUB some of them can run. +``` + +Google Chrome is usually the most up-to-date with language features, good to run bleeding-edge demos without any transpilers, but other modern browsers also work fine. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js b/1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js index 4bec82dd37..08089f6dba 100644 --- a/1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js +++ b/1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js @@ -1,7 +1,11 @@ function isEmpty(obj) { for (let key in obj) { +<<<<<<< HEAD // 如果进到循环里面,说明有属性。 +======= + // if the loop has started, there is a property +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb return false; } return true; -} \ No newline at end of file +} diff --git a/1-js/04-object-basics/01-object/3-is-empty/solution.md b/1-js/04-object-basics/01-object/3-is-empty/solution.md index 663c81b3fd..7d53fd7375 100644 --- a/1-js/04-object-basics/01-object/3-is-empty/solution.md +++ b/1-js/04-object-basics/01-object/3-is-empty/solution.md @@ -1,3 +1,4 @@ +<<<<<<< HEAD 遍历一个对象,如果对象存在任何属性则 `return false`。 ```js @@ -8,3 +9,6 @@ function isEmpty(obj) { return true; } ``` +======= +Just loop over the object and `return false` immediately if there's at least one property. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/04-object-basics/01-object/4-const-object/task.md b/1-js/04-object-basics/01-object/4-const-object/task.md index a2997a1304..46e62472d1 100644 --- a/1-js/04-object-basics/01-object/4-const-object/task.md +++ b/1-js/04-object-basics/01-object/4-const-object/task.md @@ -4,7 +4,11 @@ importance: 5 # 不可变对象 +<<<<<<< HEAD 有可能改变 `const` 修饰的对象吗, 你怎么看呢? +======= +Is it possible to change an object declared with `const`? What do you think? +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js const user = { diff --git a/1-js/04-object-basics/01-object/article.md b/1-js/04-object-basics/01-object/article.md index 7153f2b71c..ca96f297fd 100644 --- a/1-js/04-object-basics/01-object/article.md +++ b/1-js/04-object-basics/01-object/article.md @@ -1,8 +1,12 @@ # 对象 +<<<<<<< HEAD 正如我们从《引言:类型》那章中知道的那样,JavaScript 中有七种数据类型。有六种原始类型,因为他们的值只包含一种东西(字符串,数值或者其他)。 相反,对象用来存储键值对和更复杂的实体。在 JavaScript 中,对象深入到这门语言的方方面面。所以在我们深入理解这门语言之前,必须先理解对象。 +======= +As we know from the chapter , there are seven data types in JavaScript. Six of them are called "primitive", because their values contain only a single thing (be it a string or a number or whatever). +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 对象可以通过花括号 `{…}` 和其中包含一些可选的**属性**来创建。属性是一个键值对,键是一个字符串(也叫做属性名),值可以是任何类型。 @@ -201,7 +205,7 @@ let obj = { for: 1, let: 2, return: 3 -} +}; alert( obj.for + obj.let + obj.return ); // 6 ``` @@ -216,12 +220,22 @@ alert(obj.__proto__); // [object Object],这样不行 我们从代码中可以看出来,把它赋值成 `5` 被忽略了。 +<<<<<<< HEAD 如果我们蓄意去存储随机的键值对或者允许一个访问者去指定键,那可能就会产生很多 bug 并且使对象变得危险。 +======= +That can become a source of bugs and even vulnerabilities if we intend to store arbitrary key-value pairs in an object, and allow a visitor to specify the keys. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 比如,访问者可能选择 "__proto__" 作为键,这个赋值的逻辑就失败了(像上面那样)。 +<<<<<<< HEAD 有一种让对象把 `__proto__` 作为属性的方法,在后面章节会讲到,现在我们先来学习对象的更多知识。 还有另外一种数据结构 [Map](info:map-set-weakmap-weakset),我们会在后面章节学到,它支持任意的键值。 +======= +There is a way to make objects treat `__proto__` as a regular property, which we'll cover later, but first we need to know more about objects. +There's also another data structure [Map](info:map-set-weakmap-weakset), that we'll learn in the chapter , which supports arbitrary keys. +```` +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```` @@ -295,7 +309,13 @@ alert( "age" in user ); // true,user.age 存在 alert( "blabla" in user ); // false,user.blabla 不存在。 ``` +<<<<<<< HEAD 注意 `in` 的左边必须是**属性名**。通常是一个字符串,如果不用字符串,那就是一个字符串变量。 +======= +Please note that on the left side of `in` there must be a *property name*. That's usually a quoted string. + +If we omit quotes, that would mean a variable containing the actual name will be tested. For instance: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let user = { age: 30 }; @@ -333,8 +353,13 @@ alert( "test" in obj ); // true,属性存在! 语法: ```js +<<<<<<< HEAD for(key in object) { // 各个属性键值的执行区 +======= +for (key in object) { + // executes the body for each key among object properties +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb } ``` @@ -347,7 +372,7 @@ let user = { isAdmin: true }; -for(let key in user) { +for (let key in user) { // keys alert( key ); // name, age, isAdmin // 属性键的值 @@ -355,7 +380,13 @@ for(let key in user) { } ``` +<<<<<<< HEAD 注意,所有的 "for" 都允许我们在循环中定义变量,像 `let key` 这样。 +======= +Note that all "for" constructs allow us to declare the looping variable inside the loop, like `let key` here. + +Also, we could use another variable name here instead of `key`. For instance, `"for (let prop in obj)"` is also widely used. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 同样,我们可以用其他属性名来代替 `key`。例如 `"for(let prop in obj)"` 也很常用。 @@ -377,7 +408,7 @@ let codes = { }; *!* -for(let code in codes) { +for (let code in codes) { alert(code); // 1, 41, 44, 49 } */!* @@ -435,7 +466,7 @@ let codes = { "+1": "USA" }; -for(let code in codes) { +for (let code in codes) { alert( +code ); // 49, 41, 44, 1 } ``` @@ -598,8 +629,13 @@ for (let key in user) { } */!* +<<<<<<< HEAD // 现在复制是独立的复制了 clone.name = "Pete"; // 改变它的值 +======= +// now clone is a fully independent clone +clone.name = "Pete"; // changed the data in it +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb alert( user.name ); // 原对象属性值不变 ``` @@ -609,7 +645,7 @@ alert( user.name ); // 原对象属性值不变 语法是: ```js -Object.assign(dest[, src1, src2, src3...]) +Object.assign(dest, [src1, src2, src3...]) ``` - 参数 `dest` 和 `src1, ..., srcN`(可以有很多个)是对象。 @@ -692,7 +728,14 @@ user.sizes.width++; // 在这里改变一个属性的值 alert(clone.sizes.width); // 51,在这里查看属性的值 ``` +<<<<<<< HEAD 为了解决上面的的问题,我们在复制的时候应该检查 `user[key]` 的每一个值,如果是一个对象,我们再复制一遍这个对象,这叫做深拷贝。 +======= +To fix that, we should use the cloning loop that examines each value of `user[key]` and, if it's an object, then replicate its structure as well. That is called a "deep cloning". + +There's a standard algorithm for deep cloning that handles the case above and more complex cases, called the [Structured cloning algorithm](http://w3c.github.io/html/infrastructure.html#safe-passing-of-structured-data). In order not to reinvent the wheel, we can use a working implementation of it from the JavaScript library [lodash](https://lodash.com), the method is called [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep). + +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 有一个标准的深拷贝算法,解决上面和一些更复杂的情况,叫做 [Structured cloning algorithm](https://w3c.github.io/html/infrastructure.html#internal-structured-cloning-algorithm)。为了不重复造轮子,我们使用它的一个 JS 实现的库 [lodash](https://lodash.com), 方法名叫做 [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep)。 @@ -708,10 +751,17 @@ alert(clone.sizes.width); // 51,在这里查看属性的值 - 点符号: `obj.property`。 - 方括号 `obj["property"]`,方括号中可以使用变量 `obj[varWithKey]`。 +<<<<<<< HEAD 其他操作: - 删除属性:`delete obj.prop`。 - 检查属性是否存在:`"key" in obj`。 - 遍历对象:`for(let key in obj)` 循环。 +======= +Additional operators: +- To delete a property: `delete obj.prop`. +- To check if a property with the given key exists: `"key" in obj`. +- To iterate over an object: `for (let key in obj)` loop. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 对象根据引用来赋值或者复制。换句话说,变量存的不是对象的"值",而是值的 "引用"(内存地址)。 所以复制变量或者传递变量到方法中只是复制了对象的引用。 diff --git a/1-js/04-object-basics/01-object/object-user-delete.png b/1-js/04-object-basics/01-object/object-user-delete.png index 688158f9b0..8702675c81 100644 Binary files a/1-js/04-object-basics/01-object/object-user-delete.png and b/1-js/04-object-basics/01-object/object-user-delete.png differ diff --git a/1-js/04-object-basics/01-object/object-user-delete@2x.png b/1-js/04-object-basics/01-object/object-user-delete@2x.png index e1ef655414..698766bb0a 100644 Binary files a/1-js/04-object-basics/01-object/object-user-delete@2x.png and b/1-js/04-object-basics/01-object/object-user-delete@2x.png differ diff --git a/1-js/04-object-basics/01-object/object-user-empty.png b/1-js/04-object-basics/01-object/object-user-empty.png index 483d072c6d..6b1f27a690 100644 Binary files a/1-js/04-object-basics/01-object/object-user-empty.png and b/1-js/04-object-basics/01-object/object-user-empty.png differ diff --git a/1-js/04-object-basics/01-object/object-user-empty@2x.png b/1-js/04-object-basics/01-object/object-user-empty@2x.png index 8db894cb3b..5f261eca41 100644 Binary files a/1-js/04-object-basics/01-object/object-user-empty@2x.png and b/1-js/04-object-basics/01-object/object-user-empty@2x.png differ diff --git a/1-js/04-object-basics/01-object/object-user-isadmin.png b/1-js/04-object-basics/01-object/object-user-isadmin.png index 4e76eeb76a..2ce66a49d3 100644 Binary files a/1-js/04-object-basics/01-object/object-user-isadmin.png and b/1-js/04-object-basics/01-object/object-user-isadmin.png differ diff --git a/1-js/04-object-basics/01-object/object-user-isadmin@2x.png b/1-js/04-object-basics/01-object/object-user-isadmin@2x.png index b409776903..4a15dac64b 100644 Binary files a/1-js/04-object-basics/01-object/object-user-isadmin@2x.png and b/1-js/04-object-basics/01-object/object-user-isadmin@2x.png differ diff --git a/1-js/04-object-basics/01-object/object-user-props.png b/1-js/04-object-basics/01-object/object-user-props.png index 2bfdfabdb3..b0486e900e 100644 Binary files a/1-js/04-object-basics/01-object/object-user-props.png and b/1-js/04-object-basics/01-object/object-user-props.png differ diff --git a/1-js/04-object-basics/01-object/object-user-props@2x.png b/1-js/04-object-basics/01-object/object-user-props@2x.png index 4935b59ce8..20859fe911 100644 Binary files a/1-js/04-object-basics/01-object/object-user-props@2x.png and b/1-js/04-object-basics/01-object/object-user-props@2x.png differ diff --git a/1-js/04-object-basics/01-object/object-user.png b/1-js/04-object-basics/01-object/object-user.png index 16179209f3..6215b82079 100644 Binary files a/1-js/04-object-basics/01-object/object-user.png and b/1-js/04-object-basics/01-object/object-user.png differ diff --git a/1-js/04-object-basics/01-object/object-user@2x.png b/1-js/04-object-basics/01-object/object-user@2x.png index 7203895324..c66fa5159b 100644 Binary files a/1-js/04-object-basics/01-object/object-user@2x.png and b/1-js/04-object-basics/01-object/object-user@2x.png differ diff --git a/1-js/04-object-basics/01-object/object.png b/1-js/04-object-basics/01-object/object.png index f94d094a96..a853c9c393 100644 Binary files a/1-js/04-object-basics/01-object/object.png and b/1-js/04-object-basics/01-object/object.png differ diff --git a/1-js/04-object-basics/01-object/object@2x.png b/1-js/04-object-basics/01-object/object@2x.png index 003c2f6eab..12011ff5c7 100644 Binary files a/1-js/04-object-basics/01-object/object@2x.png and b/1-js/04-object-basics/01-object/object@2x.png differ diff --git a/1-js/04-object-basics/01-object/variable-contains-reference.png b/1-js/04-object-basics/01-object/variable-contains-reference.png index d6e7fddff6..cdd53d0b2d 100644 Binary files a/1-js/04-object-basics/01-object/variable-contains-reference.png and b/1-js/04-object-basics/01-object/variable-contains-reference.png differ diff --git a/1-js/04-object-basics/01-object/variable-contains-reference@2x.png b/1-js/04-object-basics/01-object/variable-contains-reference@2x.png index 145bad29af..0701261980 100644 Binary files a/1-js/04-object-basics/01-object/variable-contains-reference@2x.png and b/1-js/04-object-basics/01-object/variable-contains-reference@2x.png differ diff --git a/1-js/04-object-basics/01-object/variable-copy-reference.png b/1-js/04-object-basics/01-object/variable-copy-reference.png index 97510c4b21..2870858420 100644 Binary files a/1-js/04-object-basics/01-object/variable-copy-reference.png and b/1-js/04-object-basics/01-object/variable-copy-reference.png differ diff --git a/1-js/04-object-basics/01-object/variable-copy-reference@2x.png b/1-js/04-object-basics/01-object/variable-copy-reference@2x.png index a64238a52b..e7b994c534 100644 Binary files a/1-js/04-object-basics/01-object/variable-copy-reference@2x.png and b/1-js/04-object-basics/01-object/variable-copy-reference@2x.png differ diff --git a/1-js/04-object-basics/01-object/variable-copy-value.png b/1-js/04-object-basics/01-object/variable-copy-value.png index e21af09907..c360a0e132 100644 Binary files a/1-js/04-object-basics/01-object/variable-copy-value.png and b/1-js/04-object-basics/01-object/variable-copy-value.png differ diff --git a/1-js/04-object-basics/01-object/variable-copy-value@2x.png b/1-js/04-object-basics/01-object/variable-copy-value@2x.png index 2f0b2f47d4..323bc46224 100644 Binary files a/1-js/04-object-basics/01-object/variable-copy-value@2x.png and b/1-js/04-object-basics/01-object/variable-copy-value@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/article.md b/1-js/04-object-basics/02-garbage-collection/article.md index 79a63115d2..6c8295205b 100644 --- a/1-js/04-object-basics/02-garbage-collection/article.md +++ b/1-js/04-object-basics/02-garbage-collection/article.md @@ -207,6 +207,10 @@ JavaScript 引擎做了许多优化,使其运行速度更快,并且不会影 如果你熟悉低级编程,关于 V8 引擎垃圾回收器的更详细信息请参阅文章 [V8 的垃圾回收:垃圾回收](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection)。 +<<<<<<< HEAD [V8 博客](http://v8project.blogspot.com/)还不时发布关于内存管理变化的文章。当然,为了学习垃圾收集,你最好通过学习 V8 引擎内部知识来进行准备,并阅读一个叫 [Vyacheslav Egorov](http://mrale.ph) 的 V8 引擎工程师的博客。我之所以说『V8』,因为它最容易在互联网上找到文章。对于其他引擎,许多方法是相似的,但在垃圾收集上许多方面有所不同。 +======= +[V8 blog](https://v8.dev/) also publishes articles about changes in memory management from time to time. Naturally, to learn the garbage collection, you'd better prepare by learning about V8 internals in general and read the blog of [Vyacheslav Egorov](http://mrale.ph) who worked as one of V8 engineers. I'm saying: "V8", because it is best covered with articles in the internet. For other engines, many approaches are similar, but garbage collection differs in many aspects. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 当你需要低级别的优化时,对引擎的深入了解是很好的。在熟悉了该语言之后,把熟悉引擎作为下一步是明智的。 diff --git a/1-js/04-object-basics/02-garbage-collection/family-delete-refs.png b/1-js/04-object-basics/02-garbage-collection/family-delete-refs.png index 5c10f0e47e..05160890e2 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-delete-refs.png and b/1-js/04-object-basics/02-garbage-collection/family-delete-refs.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-delete-refs@2x.png b/1-js/04-object-basics/02-garbage-collection/family-delete-refs@2x.png index 24d29630e5..a92fc573b6 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-delete-refs@2x.png and b/1-js/04-object-basics/02-garbage-collection/family-delete-refs@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-family.png b/1-js/04-object-basics/02-garbage-collection/family-no-family.png index a4ce30a354..e60f9b88d8 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-no-family.png and b/1-js/04-object-basics/02-garbage-collection/family-no-family.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-family@2x.png b/1-js/04-object-basics/02-garbage-collection/family-no-family@2x.png index 0d9949839e..7549037060 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-no-family@2x.png and b/1-js/04-object-basics/02-garbage-collection/family-no-family@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-father-2.png b/1-js/04-object-basics/02-garbage-collection/family-no-father-2.png index e24dba5b57..823d0a618c 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-no-father-2.png and b/1-js/04-object-basics/02-garbage-collection/family-no-father-2.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-father-2@2x.png b/1-js/04-object-basics/02-garbage-collection/family-no-father-2@2x.png index a6c4ee36a3..9e100d522a 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-no-father-2@2x.png and b/1-js/04-object-basics/02-garbage-collection/family-no-father-2@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-father.png b/1-js/04-object-basics/02-garbage-collection/family-no-father.png index df14624bc0..5ddb6f68ff 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-no-father.png and b/1-js/04-object-basics/02-garbage-collection/family-no-father.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-father@2x.png b/1-js/04-object-basics/02-garbage-collection/family-no-father@2x.png index 5ab4b37920..868c748a06 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family-no-father@2x.png and b/1-js/04-object-basics/02-garbage-collection/family-no-father@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family.png b/1-js/04-object-basics/02-garbage-collection/family.png index dbbc01d2f5..f37464e6c5 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family.png and b/1-js/04-object-basics/02-garbage-collection/family.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/family@2x.png b/1-js/04-object-basics/02-garbage-collection/family@2x.png index 64b4619ba1..4f5d47210d 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/family@2x.png and b/1-js/04-object-basics/02-garbage-collection/family@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-1.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-1.png index 423191778e..5cfe664c35 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-1.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-1.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-1@2x.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-1@2x.png index 223ea32a12..cf93a1605e 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-1@2x.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-1@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-2.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-2.png index da63d39693..2bbe4241d4 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-2.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-2.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-2@2x.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-2@2x.png index 1f614e3e6d..f4a7abb526 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-2@2x.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-2@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-3.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-3.png index e77144c1d7..665a22784f 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-3.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-3.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-3@2x.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-3@2x.png index 37e349b627..60d4059cca 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-3@2x.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-3@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-4.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-4.png index 110e0d9c4b..4ba6e17ecc 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-4.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-4.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-4@2x.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-4@2x.png index c09d75f9d1..8ac09c80d6 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-4@2x.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-4@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-5.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-5.png index bc4ea9670e..35c8816dcb 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-5.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-5.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-5@2x.png b/1-js/04-object-basics/02-garbage-collection/garbage-collection-5@2x.png index 0ab697e683..4db0c1732e 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/garbage-collection-5@2x.png and b/1-js/04-object-basics/02-garbage-collection/garbage-collection-5@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin.png b/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin.png index 29c4fcbea8..9ddac3e299 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin.png and b/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin@2x.png b/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin@2x.png index 2f80f19a21..9069781f24 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin@2x.png and b/1-js/04-object-basics/02-garbage-collection/memory-user-john-admin@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost.png b/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost.png index cdc1d49049..ae1684b2d2 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost.png and b/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost@2x.png b/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost@2x.png index d58afdb582..c510380f9e 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost@2x.png and b/1-js/04-object-basics/02-garbage-collection/memory-user-john-lost@2x.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/memory-user-john.png b/1-js/04-object-basics/02-garbage-collection/memory-user-john.png index 3ba5730de9..2ad00b6cb1 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/memory-user-john.png and b/1-js/04-object-basics/02-garbage-collection/memory-user-john.png differ diff --git a/1-js/04-object-basics/02-garbage-collection/memory-user-john@2x.png b/1-js/04-object-basics/02-garbage-collection/memory-user-john@2x.png index 5aa81bb0c0..f365ac0365 100644 Binary files a/1-js/04-object-basics/02-garbage-collection/memory-user-john@2x.png and b/1-js/04-object-basics/02-garbage-collection/memory-user-john@2x.png differ diff --git a/1-js/04-object-basics/03-symbol/article.md b/1-js/04-object-basics/03-symbol/article.md index 59a2de6116..bac44f39a1 100644 --- a/1-js/04-object-basics/03-symbol/article.md +++ b/1-js/04-object-basics/03-symbol/article.md @@ -18,8 +18,13 @@ let id = Symbol(); 我们可以给 Symbol 一个描述(也称为 Symbol 名),这对于调试非常有用: +<<<<<<< HEAD ```js // id 是描述为 "id" 的 Symbol +======= +```js run +// id is a symbol with the description "id" +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb let id = Symbol("id"); ``` @@ -50,7 +55,13 @@ alert(id); // 类型错误:无法将 Symbol 值转换为 String。 */!* ``` +<<<<<<< HEAD 如果我们真的想显示一个 Symbol,我们需要在它上面调用 `.toString()`,如下所示: +======= +That's a "language guard" against messing up, because strings and symbols are fundamentally different and should not occasionally convert one into another. + +If we really want to show a symbol, we need to call `.toString()` on it, like here: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let id = Symbol("id"); *!* @@ -58,7 +69,18 @@ alert(id.toString()); // Symbol(id),现在它起作用了 */!* ``` +<<<<<<< HEAD 这是一种防止混乱的“语言保护”,因为 String 和 Symbol 有本质上的不同,而且不应该偶尔将它们相互转化。 +======= +Or get `symbol.description` property to get the description only: +```js run +let id = Symbol("id"); +*!* +alert(id.description); // id +*/!* +``` + +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```` ## “隐藏”属性 @@ -75,7 +97,11 @@ user[id] = "ID Value"; alert( user[id] ); // 我们可以使用 Symbol 作为键来访问数据。 ``` +<<<<<<< HEAD 在 string `"id"` 上使用 `Symbol("id")` 有什么好处? +======= +What's the benefit of using `Symbol("id")` over a string `"id"`? +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 我们用更深入一点的示例来说明这一点。 diff --git a/1-js/04-object-basics/04-object-methods/7-calculator/solution.md b/1-js/04-object-basics/04-object-methods/7-calculator/solution.md index 22c4bf187c..459997624e 100644 --- a/1-js/04-object-basics/04-object-methods/7-calculator/solution.md +++ b/1-js/04-object-basics/04-object-methods/7-calculator/solution.md @@ -1,6 +1,5 @@ - -```js run demo +```js run demo solution let calculator = { sum() { return this.a + this.b; @@ -20,4 +19,3 @@ calculator.read(); alert( calculator.sum() ); alert( calculator.mul() ); ``` - diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md b/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md index a19f7a77bd..db268a8fa0 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md @@ -1,6 +1,6 @@ 解决方案就是在每次调用后返回这个对象本身。 -```js run +```js run demo let ladder = { step: 0, up() { @@ -28,7 +28,7 @@ ladder.up().up().down().up().down().showStep(); // 1 我们也可以每行一个调用。对于长链接它更具可读性: -```js +```js ladder .up() .up() @@ -37,4 +37,3 @@ ladder .down() .showStep(); // 1 ``` - diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md index 2d37bf4eb5..788e5c590d 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md @@ -30,7 +30,11 @@ ladder.down(); ladder.showStep(); // 1 ``` +<<<<<<< HEAD 修改 `up` 和 `down` 的代码让调用可以链接,就像这样: +======= +Modify the code of `up`, `down` and `showStep` to make the calls chainable, like this: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js ladder.up().up().down().showStep(); // 1 diff --git a/1-js/04-object-basics/04-object-methods/article.md b/1-js/04-object-basics/04-object-methods/article.md index 8beb5b17b8..fc0a520106 100644 --- a/1-js/04-object-basics/04-object-methods/article.md +++ b/1-js/04-object-basics/04-object-methods/article.md @@ -63,7 +63,11 @@ user.sayHi(); // Hello! ```smart header="Object-oriented programming" 当我们在代码中用对象表示实体时,这就是所谓的[面向对象编程](https://en.wikipedia.org/wiki/Object-oriented_programming),简称为 "OOP"。 +<<<<<<< HEAD OOP 是一门很大的学问,也是一门有其本身乐趣的学问。怎样选择合适的实体?如何组织它们之间的交互?这就是架构,有很多关于此方面的书,例如 E.Gamma、R.Helm、R.Johnson 和 J.Vissides 所著的《设计模式:可复用面向对象软件的基础》、G.Booch 所著的《面向对象分析与设计》等等。在后面的 一章中,我们将会触及这个主题的浅层内容。 +======= +OOP is a big thing, an interesting science of its own. How to choose the right entities? How to organize the interaction between them? That's architecture, and there are great books on that topic, like "Design Patterns: Elements of Reusable Object-Oriented Software" by E.Gamma, R.Helm, R.Johnson, J.Vissides or "Object-Oriented Analysis and Design with Applications" by G.Booch, and more. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` ### 方法简写 @@ -72,14 +76,19 @@ OOP 是一门很大的学问,也是一门有其本身乐趣的学问。怎样 ```js // 这些对象作用一样 -let user = { +user = { sayHi: function() { alert("Hello"); } }; +<<<<<<< HEAD // 方法简写看起来更好,对吧? let user = { +======= +// method shorthand looks better, right? +user = { +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb *!*  sayHi() { // 与 "sayHi: function()" 一样 */!* @@ -166,7 +175,11 @@ admin.sayHi(); // 噢哟!在 sayHi() 使用了旧的变量名。错误! ## “this” 不受限制 +<<<<<<< HEAD 在 JavaScript 中,"this" 关键字与大多数其他编程语言中的不同。首先,它可以用于任何函数。 +======= +In JavaScript, "this" keyword behaves unlike most other programming languages. It can be used in any function. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 这样的代码没有语法错误: @@ -176,9 +189,15 @@ function sayHi() { } ``` +<<<<<<< HEAD `this` 是在运行时求值的。它可以是任何值。 例如,从不同的对象中调用同一个函数可能会有不同的 "this" 值: +======= +The value of `this` is evaluated during the run-time, depending on the context. And it can be anything. + +For instance, here the same function is assigned to two different objects and has different "this" in the calls: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let user = { name: "John" }; @@ -189,7 +208,11 @@ function sayHi() { } *!* +<<<<<<< HEAD // 在两个对象中使用的是相同的函数 +======= +// use the same function in two objects +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb user.f = sayHi; admin.f = sayHi; */!* @@ -202,7 +225,14 @@ admin.f(); // Admin (this == admin) admin['f'](); // Admin(使用点或方括号语法来访问这个方法,都没有关系。) ``` +<<<<<<< HEAD 实际上,我们可以在没有任何对象的情况下调用函数: +======= +The rule is simple: if `obj.f()` is called, then `this` is `obj` during the call of `f`. So it's either `user` or `admin` in the example above. + +````smart header="Calling without an object: `this == undefined`" +We can even call the function without an object at all: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run function sayHi() { @@ -214,9 +244,16 @@ sayHi(); // undefined 在这种情况下,严格模式下的 `this` 值为 `undefined`。如果我们尝试访问 `this.name`,将会出现错误。 +<<<<<<< HEAD 在非严格模式(没有使用 `use strict`)的情况下,`this` 将会是**全局对象**(浏览器中的 `window`,我们稍后会进行讨论)。`"use strict"` 可以修复这个历史行为。 请注意,通常在没有对象的情况下使用 `this` 的函数调用是不常见的,会(导致)编程错误。如果函数中有 `this`,那么通常意味着它是在对象上下文环境中被调用的。 +======= +In non-strict mode the value of `this` in such case will be the *global object* (`window` in a browser, we'll get to it later in the chapter [](info:global-object)). This is a historical behavior that `"use strict"` fixes. + +Usually such call is an programming error. If there's `this` inside a function, it expects to be called in an object context. +```` +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```smart header="The consequences of unbound `this`" 如果你来自其他的编程语言,那么你可能熟悉『绑定 `this`』的概念。在对象定义的方法中,`this` 总是指向该对象。 @@ -257,7 +294,11 @@ user.hi(); // John (the simple call works) 该方法立即被括号 `()` 调用。但它无效。 +<<<<<<< HEAD 你可以看到该调用导致了错误,因为调用中的 `"this"` 为 `undefined`。 +======= +You can see that the call results in an error, because the value of `"this"` inside the call becomes `undefined`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 这样是正确的(对象点方法): ```js @@ -276,7 +317,11 @@ user.hi(); 1. 首先,点 `'.'` 取得这个 `obj.method` 属性。 2. 其后的括号 `()` 调用它。 +<<<<<<< HEAD 那么,`this` 是如何从第一部分传递到第二部分的呢? +======= +So, how does the information about `this` get passed from the first part to the second one? +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 如果把这些操作分离开,那么 `this` 肯定会丢失: @@ -312,11 +357,19 @@ hi(); // 错误,因为 this 未定义 (user, "hi", true) ``` +<<<<<<< HEAD 当在引用类型上用 `()` 调用时,它们接收到这个对象和它的方法的所有信息,并且设定正确的 `this` 值(这里等于 `user`)。 +======= +When parentheses `()` are called on the Reference Type, they receive the full information about the object and its method, and can set the right `this` (`=user` in this case). +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb `hi = user.hi` 赋值等其他的操作,将引用类型作为一个整体丢弃,只获取 `user.hi`(一个函数)的值进行传递。因此,进一步操作『失去』了 `this`(值)。 +<<<<<<< HEAD 所以如果直接使用点 `obj.method()` 或方括号语法 `obj[method]()`(它们在这里并无差别)调用函数,那么作为结果,`this` 值会以正确的方式进行传递。 +======= +So, as the result, the value of `this` is only passed the right way if the function is called directly using a dot `obj.method()` or square brackets `obj['method']()` syntax (they do the same here). Later in this tutorial, we will learn various ways to solve this problem such as [func.bind()](/bind#solution-2-bind). +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 箭头函数没有自己的 "this" diff --git a/1-js/04-object-basics/05-object-toprimitive/article.md b/1-js/04-object-basics/05-object-toprimitive/article.md index 81b2d76bc0..3e13922822 100644 --- a/1-js/04-object-basics/05-object-toprimitive/article.md +++ b/1-js/04-object-basics/05-object-toprimitive/article.md @@ -3,6 +3,7 @@ 当对象相加 `obj1 + obj2`,相减 `obj1 - obj2`,或者使用 `alert(obj)` 打印时会发生什么? +<<<<<<< HEAD 在对象中有特殊的方法用来做转换。 在 一章中,我们已经看到了数值,字符串和布尔转换的规则。但是我们给对象的原始值转换留下了一点疑问。正如我们所知道的方法和符号一样,现在我们可以解决这个问题了。 @@ -20,11 +21,30 @@ 该算法允许我们使用特殊的对象方法自定义转换。 取决于上下文,转换具有所谓的“暗示”。 +======= +In that case objects are auto-converted to primitives, and then the operation is carried out. + +In the chapter we've seen the rules for numeric, string and boolean conversions of primitives. But we left a gap for objects. Now, as we know about methods and symbols it becomes possible to fill it. + +1. All objects are `true` in a boolean context. There are only numeric and string conversions. +2. The numeric conversion happens when we subtract objects or apply mathematical functions. For instance, `Date` objects (to be covered in the chapter ) can be subtracted, and the result of `date1 - date2` is the time difference between two dates. +3. As for the string conversion -- it usually happens when we output an object like `alert(obj)` and in similar contexts. + +## ToPrimitive + +We can fine-tune string and numeric conversion, using special object methods. + +The conversion algorithm is called `ToPrimitive` in the [specification](https://tc39.github.io/ecma262/#sec-toprimitive). It's called with a "hint" that specifies the conversion type. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 这里有三种变体: `"string"` +<<<<<<< HEAD : 当一个操作期望一个字符串时,对于对象到字符串的转换,比如 `alert`: +======= +: For an object-to-string conversion, when we're doing an operation on an object that expects a string, like `alert`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // output @@ -35,7 +55,11 @@ ``` `"number"` +<<<<<<< HEAD : 当一个操作需要一个数字时,用于对象到数字的转换,如 `maths`: +======= +: For an object-to-number conversion, like when we're doing maths: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // 显式转换 @@ -52,7 +76,11 @@ `"default"` : 在少数情况下发生,当操作者“不确定”期望的类型时。 +<<<<<<< HEAD 例如,二进制加 `+` 可以和字符串(连接)和数字(相加)发生作用,所以类型是字符串和数字都可以。或者当一个对象用 `==` 与一个字符串、数字或符号进行比较时。 +======= + For instance, binary plus `+` can work both with strings (concatenates them) and numbers (adds them), so both strings and numbers would do. Or when an object is compared using `==` with a string, number or a symbol, it's also unclear which conversion should be done. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // 二进制加 @@ -159,14 +187,30 @@ alert(user + 500); // toString -> John500 如果没有 `Symbol.toPrimitive` 和 `valueOf`,`toString` 将处理所有原始转换。 +<<<<<<< HEAD ## ToPrimitive 和 ToString/ToNumber +======= +## Return types +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 关于所有原始转换方法,有一个重要的点需要知道,就是它们不一定会返回“暗示的”原始值。 没有限制 `toString()` 是否返回字符串,或 `Symbol.toPrimitive` 方法是否为 "number" 暗示返回数字。 +<<<<<<< HEAD **唯一强制性的事情是:这些方法必须返回一个原始值。** +======= +The only mandatory thing: these methods must return a primitive, not an object. + +```smart header="Historical notes" +For historical reasons, if `toString` or `valueOf` returns an object, there's no error, but such value is ignored (like if the method didn't exist). That's because in ancient times there was no good "error" concept in JavaScript. + +In contrast, `Symbol.toPrimitive` *must* return a primitive, otherwise there will be an error. +``` + +## Further operations +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 发起转换的操作获取该原始值,然后继续使用该原始值,并在必要时应用进一步的转换。 @@ -208,11 +252,14 @@ alert(user + 500); // toString -> John500 alert(obj + 2); // 3 (ToPrimitive 返回布尔值,非字符串 => ToNumber) ``` +<<<<<<< HEAD ```smart header="历史笔记" 由于历史原因,`toString` 或 `valueOf` 方法**应该**返回一个原始值:如果它们中的任何一个返回了一个对象,虽然不会报错,但是该对象被忽略(就像该方法不存在一样)。 相反,`Symbol.toPrimitive` **必须**返回一个原始值,否则会出现错误。 ``` +======= +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 概要 diff --git a/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/solution.md b/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/solution.md index 27978ac1f0..4da30203ab 100644 --- a/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/solution.md +++ b/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/solution.md @@ -2,7 +2,11 @@ 如果一个函数返回一个对象,那么 `new` 返回那个对象而不是 `this`。 +<<<<<<< HEAD 所以他们可以,例如,返回相同的外部定义的对象 `obj`: +======= +So they can, for instance, return the same externally defined object `obj`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run no-beautify let obj = {}; diff --git a/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/solution.md b/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/solution.md index e5583c5d01..86bb65416d 100644 --- a/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/solution.md +++ b/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/solution.md @@ -1,5 +1,3 @@ - - ```js run demo function Calculator() { diff --git a/1-js/04-object-basics/06-constructor-new/article.md b/1-js/04-object-basics/06-constructor-new/article.md index 5d4b4e0208..64fff0f1a6 100644 --- a/1-js/04-object-basics/06-constructor-new/article.md +++ b/1-js/04-object-basics/06-constructor-new/article.md @@ -83,7 +83,15 @@ let user = new function() { 构造函数不能被再次调用,因为它不保存在任何地方,只是被创建和调用。所以这个技巧的目的是封装构建单个对象的代码,而不是将来重用。 ```` +<<<<<<< HEAD ## 双语法构造函数:new.target +======= +## Constructor mode test: new.target + +```smart header="Advanced stuff" +The syntax from this section is rarely used, skip it unless you want to know everything. +``` +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 在一个函数内部,我们可以使用 `new.target` 属性来检查它被调用时,是否使用了 `new`。 @@ -94,14 +102,32 @@ function User() { alert(new.target); } +<<<<<<< HEAD // 不带 new: +======= +// without "new": +*!* +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb User(); // undefined +*/!* +<<<<<<< HEAD // 带 new: +======= +// with "new": +*!* +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb new User(); // function User { ... } +*/!* ``` +<<<<<<< HEAD 这可以使 `new` 和常规语法的工作原理相同: +======= +That can be used inside the function to know whether it was called with `new`, "in constructor mode", or without it, "in regular mode". + +We can also make both `new` and regular calls to do the same, like this: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run function User(name) { @@ -116,7 +142,13 @@ let john = User("John"); // 重新调用 new User alert(john.name); // John ``` +<<<<<<< HEAD 这种方法有时用在库中以使语法更加灵活。但因为省略 `new` 使得它不易阅读,这可不是一件好事。 而通过 `new` 我们可以都知道这个新对象正在创建。 +======= +This approach is sometimes used in libraries to make the syntax more flexible. So that people may call the function with or without `new`, and it still works. + +Probably not a good thing to use everywhere though, because omitting `new` makes it a bit less obvious what's going on. With `new` we all know that the new object is being created. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 构造函数 Return @@ -215,5 +247,9 @@ JavaScript 为许多内置的对象提供了构造函数:比如日期 Date, ```smart header="Objects, we'll be back!" 在本章中,我们只介绍关于对象和构造函数的基础知识。它们对于在下一章中更多地了解数据类型和函数非常重要。 +<<<<<<< HEAD 在我们了解了这一章之后 我们返回到对象并深入其中,包括继承和类。 +======= +After we learn that, we return to objects and cover them in-depth in the chapters and . +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` diff --git a/1-js/05-data-types/01-primitives-methods/1-string-new-property/solution.md b/1-js/05-data-types/01-primitives-methods/1-string-new-property/solution.md index c2be790406..7b2122dbcb 100644 --- a/1-js/05-data-types/01-primitives-methods/1-string-new-property/solution.md +++ b/1-js/05-data-types/01-primitives-methods/1-string-new-property/solution.md @@ -6,16 +6,23 @@ let str = "Hello"; str.test = 5; // (*) -alert(str.test); +alert(str.test); ``` +<<<<<<< HEAD 这里有两种结果: 1. `undefined` 2. 报错。 +======= +Depending on whether you have `use strict` or not, the result may be: +1. `undefined` (no strict mode) +2. An error (strict mode). +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 在 `(*)` 的那一行到底发生了什么呢: +<<<<<<< HEAD 1. 当访问 `str` 的属性时,创建一个“包装对象”。 2. 当对属性进行操作的时候。这个对象获得了 `test` 属性。 3. 操作结束,“包装对象”消失。 @@ -23,10 +30,21 @@ alert(str.test); 在最后一行,对字符串上的新的包装对象的每个对象操作,`str` 不再追踪这个属性。 一些浏览器进一步限制程序员,并且不允许将属性分配给基本类型。这就是为什么它有点远离规范,但在实践中我们却可以在 `(*)` 行看到错误。 +======= +1. When a property of `str` is accessed, a "wrapper object" is created. +2. In strict mode, writing into it is an error. +3. Otherwise, the operation with the property is carried on, the object gets the `test` property, but after that the "wrapper object" disappears. + +So, without strict mode, in the last line `str` has no trace of the property. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb **这个例子清楚地表明,基本类型不是对象。** +<<<<<<< HEAD 基本类型不能存储数据。 所有的属性/方法操作都是在临时对象的帮助下执行的。 +======= +They can't store additional data. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/05-data-types/01-primitives-methods/article.md b/1-js/05-data-types/01-primitives-methods/article.md index 9912266a46..57e4e6e065 100644 --- a/1-js/05-data-types/01-primitives-methods/article.md +++ b/1-js/05-data-types/01-primitives-methods/article.md @@ -1,13 +1,22 @@ # 基本类型的方法 +<<<<<<< HEAD JavaScript 允许我们像对象一样使用基本类型(字符串,数字等)。 基本类型还提供调用方法等。我们会尽快研究这些,但首先我们会看看它是如何工作的,毕竟基本类型不是对象(在这里我们会分析的更加清楚)。 我们来看看基本类型和对象之间的关键区别。 +======= +JavaScript allows us to work with primitives (strings, numbers, etc.) as if they were objects. + +They also provide methods to call as such. We will study those soon, but first we'll see how it works because, of course, primitives are not objects (and here we will make it even clearer). + +Let's look at the key distinctions between primitives and objects. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 基本类型 +<<<<<<< HEAD - 是原始类型中的一种值。 - 在 JavaScript 中有 6 种基本类型:`string`、`number`、`boolean`、`symbol`、`null` 和 `undefined`。 @@ -17,6 +26,17 @@ JavaScript 允许我们像对象一样使用基本类型(字符串,数字等 - 可以使用大括号 `{}` 创建对象,例如:`{name: "John", age: 30}`。JavaScript 中还有其他种类的对象,例如函数就是对象。 关于对象的最好的事情之一是我们可以存储一个函数作为它的一个属性: +======= +- Is a value of a primitive type. +- There are 6 primitive types: `string`, `number`, `boolean`, `symbol`, `null` and `undefined`. + +An object + +- Is capable of storing multiple values as properties. +- Can be created with `{}`, for instance: `{name: "John", age: 30}`. There are other kinds of objects in JavaScript: functions, for example, are objects. + +One of the best things about objects is that we can store a function as one of its properties. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let john = { @@ -31,7 +51,11 @@ john.sayHi(); // Hi buddy! 所以我们在这里创建了一个包含 `sayHi` 方法的对象 `john`。 +<<<<<<< HEAD 许多内置对象已经存在,例如那些处理日期,错误,HTML 元素等的内置对象。它们具有不同的属性和方法。 +======= +Many built-in objects already exist, such as those that work with dates, errors, HTML elements, etc. They have different properties and methods. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 但是,这些特性都是有成本的! @@ -46,9 +70,15 @@ john.sayHi(); // Hi buddy! 而解决方案看起来多少有点尴尬,如下: +<<<<<<< HEAD 1. 基本类型仍然是原始数据。如预期相同,提供单个值 2. JavaScript 允许访问字符串,数字,布尔值和符号的方法和属性。 3. 当进行访问时,创建一个特殊的“包装对象”,它提供额外的功能,运行后即被销毁。 +======= +1. Primitives are still primitive. A single value, as desired. +2. The language allows access to methods and properties of strings, numbers, booleans and symbols. +3. In order for that to work, a special "object wrapper" that provides the extra functionality is created, and then is destroyed. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb “包装对象”对于每种基本类型调用都是不同的,如`String`, `Number`, `Boolean` 和 `Symbol`。因此,他们提供了不同的方法。 @@ -92,18 +122,22 @@ alert( n.toFixed(2) ); // 1.23 例如: ```js run -alert( typeof 1 ); // "number" +alert( typeof 0 ); // "number" -alert( typeof new Number(1) ); // "object"! +alert( typeof new Number(0) ); // "object"! ``` +<<<<<<< HEAD 同样的,`zero`,是一个对象,alert 将显示出来: +======= +Objects are always truthy in `if`, so here the alert will show up: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let zero = new Number(0); if (zero) { // zero is true, because it's an object - alert( "zero is truthy?!?" ); + alert( "zero is truthy!?!" ); } ``` diff --git a/1-js/05-data-types/02-number/3-repeat-until-number/_js.view/test.js b/1-js/05-data-types/02-number/3-repeat-until-number/_js.view/test.js index 219fa8068e..6bd0123dba 100644 --- a/1-js/05-data-types/02-number/3-repeat-until-number/_js.view/test.js +++ b/1-js/05-data-types/02-number/3-repeat-until-number/_js.view/test.js @@ -18,7 +18,7 @@ describe("readNumber", function() { assert.strictEqual(readNumber(), 0); }); - it("continues the loop unti meets a number", function() { + it("continues the loop until meets a number", function() { prompt.onCall(0).returns("not a number"); prompt.onCall(1).returns("not a number again"); prompt.onCall(2).returns("1"); @@ -35,4 +35,4 @@ describe("readNumber", function() { assert.isNull(readNumber()); }); -}); \ No newline at end of file +}); diff --git a/1-js/05-data-types/02-number/8-random-min-max/solution.md b/1-js/05-data-types/02-number/8-random-min-max/solution.md index 562be1f62f..432e81e1c8 100644 --- a/1-js/05-data-types/02-number/8-random-min-max/solution.md +++ b/1-js/05-data-types/02-number/8-random-min-max/solution.md @@ -2,8 +2,13 @@ 这可以分两个阶段完成: +<<<<<<< HEAD 1. 如果我们将 0..1 的随机数乘以 `max-min`,则可能值的间隔从 0..1 增加到 `0..max-min`。 2. 现在,如果我们添加 `min`,则可能的间隔将从 `min` 变为 `max`。 +======= +1. If we multiply a random number from 0..1 by `max-min`, then the interval of possible values increases `0..1` to `0..max-min`. +2. Now if we add `min`, the possible interval becomes from `min` to `max`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 函数实现: diff --git a/1-js/05-data-types/02-number/9-random-int-min-max/task.md b/1-js/05-data-types/02-number/9-random-int-min-max/task.md index 8ae2b30676..cc697e70f9 100644 --- a/1-js/05-data-types/02-number/9-random-int-min-max/task.md +++ b/1-js/05-data-types/02-number/9-random-int-min-max/task.md @@ -12,9 +12,9 @@ importance: 2 功能示例: ```js -alert( random(1, 5) ); // 1 -alert( random(1, 5) ); // 3 -alert( random(1, 5) ); // 5 +alert( randomInteger(1, 5) ); // 1 +alert( randomInteger(1, 5) ); // 3 +alert( randomInteger(1, 5) ); // 5 ``` 您可以使用[上一个任务](info:task/random-min-max)的解决方案作为基础。 diff --git a/1-js/05-data-types/02-number/article.md b/1-js/05-data-types/02-number/article.md index 9c7d1a830b..4301e67432 100644 --- a/1-js/05-data-types/02-number/article.md +++ b/1-js/05-data-types/02-number/article.md @@ -1,6 +1,10 @@ # 数字类型 +<<<<<<< HEAD JavaScript 中的所有数字都以 64 位格式 [IEEE-754](http://en.wikipedia.org/wiki/IEEE_754-1985) 存储,也称为“双精度”。 +======= +All numbers in JavaScript are stored in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754-2008_revision), also known as "double precision floating point numbers". +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 让我们回顾一下并展开我们目前了解的内容。 @@ -26,11 +30,16 @@ alert( 7.3e9 ); // 7.3 billions (7,300,000,000) ```js 1e3 = 1 * 1000 -1.23e6 = 1.23 * 1000000 +1.23e6 = 1.23 * 1000000 ``` +<<<<<<< HEAD 现在让我们写一些非常小的东西。例如:1 微秒(百万分之一秒): +======= +Now let's write something very small. Say, 1 microsecond (one millionth of a second): + +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js let ms = 0.000001; ``` @@ -38,7 +47,7 @@ let ms = 0.000001; 就像以前一样,使用 `"e"` 可以提供帮助。如果我们想避免明确地写零,我们可以说: ```js -let ms = 1e-6; // six zeroes to the left from 1 +let ms = 1e-6; // six zeroes to the left from 1 ``` @@ -152,8 +161,13 @@ alert( num.toString(2) ); // 11111111 alert( Math.floor(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23 ``` +<<<<<<< HEAD 2. 函数 [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) 将点数后的数字四舍五入到 `n` 个数字并返回结果的字符串表示。 +======= +2. The method [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) rounds the number to `n` digits after the point and returns a string representation of the result. + +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let num = 12.34; alert( num.toFixed(1) ); // "12.3" @@ -170,19 +184,23 @@ alert( num.toString(2) ); // 11111111 ```js run let num = 12.34; - alert( num.toFixed(5) ); // "12.34000", added zeroes to make exactly 5 digits + alert( num.toFixed(5) ); // "12.34000", added zeroes to make exactly 5 digits ``` 我们可以使用一元加号或 `Number()` 调用将其转换为数字:`+ num.toFixed(5)`。 ## 不精确计算 +<<<<<<< HEAD 在 js 内部,一个数字以 64 位格式 [IEEE-754](http://en.wikipedia.org/wiki/IEEE_754-1985) 表示,所以正好有 64 位可以存储一个数字:其中 52 个被使用存储这些数字,其中 11 个存储小数点的位置(它们对于整数为零),1 位用于符号。 +======= +Internally, a number is represented in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754-2008_revision), so there are exactly 64 bits to store a number: 52 of them are used to store the digits, 11 of them store the position of the decimal point (they are zero for integer numbers), and 1 bit is for the sign. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 如果一个数字太大,它会溢出 64 位存储,可能会输出无穷大: ```js run -alert( 1e500 ); // Infinity +alert( 1e500 ); // Infinity ``` 可能不那么明显,但经常会发生精度的损失。 @@ -193,7 +211,11 @@ alert( 1e500 ); // Infinity alert( 0.1 + 0.2 == 0.3 ); // *!*false*/!* ``` +<<<<<<< HEAD 没错,如果我们检查 `0.1` 和 `0.2` 的总和是否为 `0.3`,们会得到 `false`。 +======= +That's right, if we check whether the sum of `0.1` and `0.2` is `0.3`, we get `false`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 奇怪!那么如果不是 `0.3`,那么又是什么呢? @@ -201,13 +223,23 @@ alert( 0.1 + 0.2 == 0.3 ); // *!*false*/!* alert( 0.1 + 0.2 ); // 0.30000000000000004 ``` +<<<<<<< HEAD 哎哟!这里的错误有更大的误差。想象一下,你正在制作一个电子购物网站,访问者将 `0.10 美元` 和 `0.20 美元` 商品放入他的图表中。订单总额将是 `$ 0.30000000000000004`。这会让任何人感到惊讶。 +======= +Ouch! There are more consequences than an incorrect comparison here. Imagine you're making an e-shopping site and the visitor puts `$0.10` and `$0.20` goods into their chart. The order total will be `$0.30000000000000004`. That would surprise anyone. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 但为什么会发生这样的事呢? +<<<<<<< HEAD 一个数字以二进制形式存储在内存中,一个 1 和 0 的序列。但是像十进制数字系统看起来很简单的 `0.1`,`0.2` 这样的分数实际上是二进制形式的无限分数。 换句话说,什么是 `0.1`?`0.1` 就是把 1 除以 10 `1/10`,即十分之一。在十进制数字系统中,这些数字很容易表示。将它比作三分之一:`1/3`。它变成了无尽的分数 `0.33333(3)`。 +======= +A number is stored in memory in its binary form, a sequence of bits - ones and zeroes. But fractions like `0.1`, `0.2` that look simple in the decimal numeric system are actually unending fractions in their binary form. + +In other words, what is `0.1`? It is one divided by ten `1/10`, one-tenth. In decimal numeral system such numbers are easily representable. Compare it to one-third: `1/3`. It becomes an endless fraction `0.33333(3)`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 所以,按这种用 `10` 划分可以保证在十进制系统中运行良好,但用 `3` 划分不是。出于同样的原因,在二进制数字系统中,`2` 的幂的分割保证工作,但 `1/10` 变成一个无限的二进制分数。 @@ -227,40 +259,62 @@ alert( 0.1.toFixed(20) ); // 0.10000000000000000555 ```smart header="不仅仅是 JavaScript" 许多其他编程语言也存在同样的问题。 +<<<<<<< HEAD PHP, Java, C, Perl, Ruby 给出完全相同的结果,因为它们基于相同的数字格式。 ``` 我们能解决这个问题吗?当然,有很多方法: 1. 我们可以在特定函数的帮助下对结果进行四舍五入 [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed): +======= +PHP, Java, C, Perl, Ruby give exactly the same result, because they are based on the same numeric format. +``` - ```js run - let sum = 0.1 + 0.2; - alert( sum.toFixed(2) ); // 0.30 - ``` +Can we work around the problem? Sure, the most reliable method is to round the result with the help of a method [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed): +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb +```js run +let sum = 0.1 + 0.2; +alert( sum.toFixed(2) ); // 0.30 +``` + +<<<<<<< HEAD 请注意 `toFixed` 总是返回一个字符串。它确保它在小数点后有 2 位数字。如果我们有电子购物并需要显示 `0.30 美元`,这实际上很方便。对于其他情况,我们可以使用一元加号将它强制为一个数字: +======= +Please note that `toFixed` always returns a string. It ensures that it has 2 digits after the decimal point. That's actually convenient if we have an e-shopping and need to show `$0.30`. For other cases, we can use the unary plus to coerce it into a number: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - ```js run - let sum = 0.1 + 0.2; - alert( +sum.toFixed(2) ); // 0.3 - ``` +```js run +let sum = 0.1 + 0.2; +alert( +sum.toFixed(2) ); // 0.3 +``` +<<<<<<< HEAD 2. 我们可以暂时将数字转换为数学整数,然后将其恢复。它是这样工作的: +======= +We also can temporarily multiply the numbers by 100 (or a bigger number) to turn them into integers, do the maths, and then divide back. Then, as we're doing maths with integers, the error somewhat decreases, but we still get it on division: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - ```js run - alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3 - ``` +```js run +alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3 +alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001 +``` +<<<<<<< HEAD 这是有效的,因为当我们做 `0.1 * 10 = 1` 和 `0.2 * 10 = 2` 时,那么这两个数字就变成了整数,并且没有精度损失。 3. 如果我们在与一家商店打交道,那么最激进的解决方案就是将所有价格存储在美分中,并且根本不使用分数。但是,如果我们应用 30% 的折扣呢?在实践中,完全回避分数是很难实现的,所以上述解决方案有助于避免这种缺陷。 +======= +So, multiply/divide approach reduces the error, but doesn't remove it totally. + +Sometimes we could try to evade fractions at all. Like if we're dealing with a shop, then we can store prices in cents instead of dollars. But what if we apply a discount of 30%? In practice, totally evading fractions is rarely possible. Just round them to cut "tails" when needed. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ````smart header="有趣的事情" 尝试运行这个: ```js run -// Hello! I'm a self-increasing number! +// Hello! I'm a self-increasing number! alert( 9999999999999999 ); // shows 10000000000000000 ``` @@ -272,7 +326,11 @@ JavaScript 在这种事件中不会触发错误。它尽最大努力使数字符 ```samrt header="两个零" 数字内部表示的另一个有趣结果是存在两个零:`0` 和 `-0`。 +<<<<<<< HEAD 这是因为一个符号由一个位表示,所以每个数字可以是正数或负数,包括零。 +======= +That's because a sign is represented by a single bit, so every number can be positive or negative, including a zero. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 在大多数情况下,这种区别并不明显,因为操作员可以将它们视为相同。 ``` @@ -283,8 +341,13 @@ JavaScript 在这种事件中不会触发错误。它尽最大努力使数字符 还记得这两个特殊的数值吗? +<<<<<<< HEAD - `Infinite`(和 `-Infinite`)是一个特殊的数值,比任何数值都大(小)。 - `NaN` 代表一个错误。 +======= +- `Infinity` (and `-Infinity`) is a special numeric value that is greater (less) than anything. +- `NaN` represents an error. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 它们属于 `数字` 类型,但不是 `普通` 数字,因此有特殊函数可以检查它们: @@ -326,10 +389,17 @@ alert( isFinite(num) ); 有一种特殊的内置方法 [Object.is](mdn:js/Object/is),它可以比较 `===` 等值,但对于两种边缘情况更可靠: +<<<<<<< HEAD 1. 它适用于 `NaN`: `Object.is(NaN,NaN)=== true`,这是件好事。 2. 值 `0` 和 `-0` 是不同的:`Object.is(0,-0)=== false`,它不是很重要,但这些值在技术上是不同的。 在所有其他情况下,`Object.is(a,b)` 与 `a === b` 相同。 +======= +1. It works with `NaN`: `Object.is(NaN, NaN) === true`, that's a good thing. +2. Values `0` and `-0` are different: `Object.is(0, -0) === false`, technically that's true, because internally the number has a sign bit that may be different even if all other bits are zeroes. + +In all other cases, `Object.is(a, b)` is the same as `a === b`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 这种比较方式经常用于 JavaScript 规范。当内部算法需要比较两个值完全相同时,它使用 Object.is(内部称为 [SameValue](https://tc39.github.io/ecma262/#sec-samevalue))。 ``` @@ -349,7 +419,11 @@ alert( +"100px" ); // NaN 这就是 `parseInt` 和 `parseFloat` 的作用。 +<<<<<<< HEAD 他们从字符串中“读出”一个数字,直到他们可以。如果发生错误,则返回收集的数字。函数 `parseInt` 返回一个整数,而 `parseFloat` 将返回一个浮点数: +======= +They "read" a number from a string until they can't. In case of an error, the gathered number is returned. The function `parseInt` returns an integer, whilst `parseFloat` will return a floating-point number: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run alert( parseInt('100px') ); // 100 @@ -417,9 +491,13 @@ JavaScript 有一个内置的 [Math](https://developer.mozilla.org/en/docs/Web/J 对于不同的进制: +<<<<<<< HEAD - 可以在十六进制(`0x`),八进制(`0o`)和二进制(`0b`)系统中直接写入数字。 - `parseInt(str,base)` 解析来自任何数字系统的整数,其基数为:`2≤base≤36`。 - `num.toString(base)` 将数字转换为数字系统中具有给定 `base` 的字符串。 +======= +- Use `parseInt/parseFloat` for the "soft" conversion, which reads a number from a string and then returns the value they could read before the error. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 将 `12pt` 和 `100px` 等值转换为数字: @@ -427,9 +505,13 @@ JavaScript 有一个内置的 [Math](https://developer.mozilla.org/en/docs/Web/J 分数: +<<<<<<< HEAD - 使用 `Math.floor`,`Math.ceil`,`Math.trunc`,`Math.round` 或 `num.toFixed(precision)` 循环。 - 请记住,使用分数时会损失精度。 更多的数学函数: - 需要时请参阅 [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) 对象,虽然这个文档非常小,但是它可以满足基础的要求。 +======= +- See the [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) object when you need them. The library is very small, but can cover basic needs. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/05-data-types/03-string/1-ucfirst/solution.md b/1-js/05-data-types/03-string/1-ucfirst/solution.md index 3beccff9ff..e709a7d0de 100644 --- a/1-js/05-data-types/03-string/1-ucfirst/solution.md +++ b/1-js/05-data-types/03-string/1-ucfirst/solution.md @@ -15,7 +15,7 @@ let newStr = str[0].toUpperCase() + str.slice(1); 这是第二种变体: -```js run +```js run demo function ucFirst(str) { if (!str) return str; diff --git a/1-js/05-data-types/03-string/2-check-spam/solution.md b/1-js/05-data-types/03-string/2-check-spam/solution.md index a65b37dad6..8522336862 100644 --- a/1-js/05-data-types/03-string/2-check-spam/solution.md +++ b/1-js/05-data-types/03-string/2-check-spam/solution.md @@ -1,6 +1,10 @@ +<<<<<<< HEAD 为了使搜索不区分大小写,我们将字符串改为小写,然后搜索: +======= +To make the search case-insensitive, let's bring the string to lower case and then search: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb -```js run +```js run demo function checkSpam(str) { let lowerStr = str.toLowerCase(); diff --git a/1-js/05-data-types/03-string/2-check-spam/task.md b/1-js/05-data-types/03-string/2-check-spam/task.md index c330b26879..bfaae8fab9 100644 --- a/1-js/05-data-types/03-string/2-check-spam/task.md +++ b/1-js/05-data-types/03-string/2-check-spam/task.md @@ -4,7 +4,11 @@ importance: 5 # 检查 spam +<<<<<<< HEAD 写一个函数 `checkSpam(str)`,如果 `str` 包含 `viagra` 或 `XXX` 就返回 `true`,否则返回 `false`。 +======= +Write a function `checkSpam(str)` that returns `true` if `str` contains 'viagra' or 'XXX', otherwise false. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 函数必须不区分大小写: diff --git a/1-js/05-data-types/03-string/3-truncate/_js.view/test.js b/1-js/05-data-types/03-string/3-truncate/_js.view/test.js index c252f16bee..9914923315 100644 --- a/1-js/05-data-types/03-string/3-truncate/_js.view/test.js +++ b/1-js/05-data-types/03-string/3-truncate/_js.view/test.js @@ -1,5 +1,5 @@ describe("truncate", function() { - it("truncate the long string to the given lenth (including the ellipsis)", function() { + it("truncate the long string to the given length (including the ellipsis)", function() { assert.equal( truncate("What I'd like to tell on this topic is:", 20), "What I'd like to te…" @@ -13,4 +13,4 @@ describe("truncate", function() { ); }); -}); \ No newline at end of file +}); diff --git a/1-js/05-data-types/03-string/3-truncate/solution.md b/1-js/05-data-types/03-string/3-truncate/solution.md index 9e4860bc51..503e704342 100644 --- a/1-js/05-data-types/03-string/3-truncate/solution.md +++ b/1-js/05-data-types/03-string/3-truncate/solution.md @@ -2,10 +2,9 @@ 注意省略号实际上有一个 unicode 字符,而不仅仅是三个点。 -```js run +```js run demo function truncate(str, maxlength) { - return (str.length > maxlength) ? + return (str.length > maxlength) ? str.slice(0, maxlength - 1) + '…' : str; } ``` - diff --git a/1-js/05-data-types/03-string/4-extract-currency/solution.md b/1-js/05-data-types/03-string/4-extract-currency/solution.md index 8b13789179..e69de29bb2 100644 --- a/1-js/05-data-types/03-string/4-extract-currency/solution.md +++ b/1-js/05-data-types/03-string/4-extract-currency/solution.md @@ -1 +0,0 @@ - diff --git a/1-js/05-data-types/03-string/article.md b/1-js/05-data-types/03-string/article.md index 1f104edb6b..fef23b07fc 100644 --- a/1-js/05-data-types/03-string/article.md +++ b/1-js/05-data-types/03-string/article.md @@ -132,7 +132,11 @@ alert( `My\n`.length ); // 3 ```warn header="`length` is a property" 掌握其他语言的人,有时会错误地调用 `str.length()` 而不是 `str.length`。这是行不通的。 +<<<<<<< HEAD 请注意 `str.length` 是一个数字属性,而不是函数。之后不需要添加括号。 +======= +Please note that `str.length` is a numeric property, not a function. There is no need to add parenthesis after it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` ## 访问字符。 @@ -275,8 +279,13 @@ while ((pos = str.indexOf(target, pos + 1)) != -1) { */!* ``` +<<<<<<< HEAD ```smart header="`str.lastIndexOf(subStr, pos)`" 还有一个类似的方法 [str.lastIndexOf(subStr, pos)](mdn:js/String/lastIndexOf),他从字符串的末尾开始搜索。 +======= +```smart header="`str.lastIndexOf(substr, position)`" +There is also a similar method [str.lastIndexOf(substr, position)](mdn:js/String/lastIndexOf) that searches from the end of a string to its beginning. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 它会以相反的顺序列出事件。 ``` @@ -451,7 +460,11 @@ JavaScript 中有三种获取字符串的方法:`substring`、`substr` 和 `sl ```smart header="Which one to choose?" 他们可以完成这项工作,形式上,`substr` 有一个小缺点:它不是在 JavaScript 核心规范中描述的,而是在附录 B 中,它涵盖了主要由于历史原因而存在的浏览器特性。因此,非浏览器环境可能无法支持它。但实际上它在任何地方都有效。 +<<<<<<< HEAD 作者发现自己几乎一直在使用 `slice`。 +======= +The author finds themself using `slice` almost all the time. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` ## 比较字符串 @@ -551,7 +564,11 @@ alert( 'Österreich'.localeCompare('Zealand') ); // -1 ## 内部,Unicode ```warn header="Advanced knowledge" +<<<<<<< HEAD 这部分会深入字符串内部。如果你计划处理表情符号、罕见的象形文字字符或其他罕见符号,这些知识会对你有用。 +======= +The section goes deeper into string internals. This knowledge will be useful for you if you plan to deal with emoji, rare mathematical or hieroglyphic characters or other rare symbols. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 如果你不打算支持它们,你可以跳过这一部分。 ``` diff --git a/1-js/05-data-types/04-array/10-maximal-subarray/_js.view/test.js b/1-js/05-data-types/04-array/10-maximal-subarray/_js.view/test.js index 143ad53451..b44e76fe7e 100644 --- a/1-js/05-data-types/04-array/10-maximal-subarray/_js.view/test.js +++ b/1-js/05-data-types/04-array/10-maximal-subarray/_js.view/test.js @@ -30,4 +30,8 @@ describe("getMaxSubSum", function() { it("maximal subsum of [-1, -2] equals 0", function() { assert.equal(getMaxSubSum([-1, -2]), 0); }); -}); \ No newline at end of file + + it("maximal subsum of [2, -8, 5, -1, 2, -3, 2] equals 6", function() { + assert.equal(getMaxSubSum([2, -8, 5, -1, 2, -3, 2]), 6); + }); +}); diff --git a/1-js/05-data-types/04-array/10-maximal-subarray/solution.md b/1-js/05-data-types/04-array/10-maximal-subarray/solution.md index 60cc94afd0..c04fa31a45 100644 --- a/1-js/05-data-types/04-array/10-maximal-subarray/solution.md +++ b/1-js/05-data-types/04-array/10-maximal-subarray/solution.md @@ -1,4 +1,8 @@ +<<<<<<< HEAD # 慢的解决方案 +======= +# Slow solution +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 我们可以计算所有可能的子集的和。 @@ -29,8 +33,13 @@ -9 -9 + 11 +<<<<<<< HEAD // 从 -11 开始: -11 +======= +// Starting from 11 +11 +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` 这样写出来的代码实际上是一个嵌套循环:外部循环遍历数组所有元素,然后内部循环计算从当前元素之后的所有子数组集的和。 @@ -59,7 +68,11 @@ alert( getMaxSubSum([100, -9, 2, -3, 5]) ); // 100 该方案的时间复杂度是 [O(n2)](https://en.wikipedia.org/wiki/Big_O_notation)。也就是说,如果我们把数组大小增加 2 倍,那么算法的运行时间将会延长4倍。 +<<<<<<< HEAD 对于大型数组(1000,10000 或者更多项)这种算法会导致严重的时间消耗。 +======= +For big arrays (1000, 10000 or more items) such algorithms can lead to a serious sluggishness. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb # 快的解决方案 @@ -67,7 +80,7 @@ alert( getMaxSubSum([100, -9, 2, -3, 5]) ); // 100 如果文字描述不太好理解,就直接看下面的代码吧,真的很短: -```js run +```js run demo function getMaxSubSum(arr) { let maxSum = 0; let partialSum = 0; diff --git a/1-js/05-data-types/04-array/10-maximal-subarray/task.md b/1-js/05-data-types/04-array/10-maximal-subarray/task.md index 0f05a0843e..d718723519 100644 --- a/1-js/05-data-types/04-array/10-maximal-subarray/task.md +++ b/1-js/05-data-types/04-array/10-maximal-subarray/task.md @@ -8,7 +8,11 @@ importance: 2 任务是:找出连续的 `arr` 的子数组,其里面所有项的和最大。 +<<<<<<< HEAD 写出函数 `getMaxSubSum(arr)`,用其找出并返回最大和。 +======= +Write the function `getMaxSubSum(arr)` that will return that sum. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: @@ -27,4 +31,8 @@ getMaxSubSum([*!*1, 2, 3*/!*]) = 6 (所有项的和) getMaxSubSum([-1, -2, -3]) = 0 ``` +<<<<<<< HEAD 请尝试想出一个快速的解决方案:复杂度可以是 [O(n2)](https://en.wikipedia.org/wiki/Big_O_notation),有能力达到 O(n) 则更好。 +======= +Please try to think of a fast solution: [O(n2)](https://en.wikipedia.org/wiki/Big_O_notation) or even O(n) if you can. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/05-data-types/04-array/2-create-array/solution.md b/1-js/05-data-types/04-array/2-create-array/solution.md index eec9055e7c..f032b55f0c 100644 --- a/1-js/05-data-types/04-array/2-create-array/solution.md +++ b/1-js/05-data-types/04-array/2-create-array/solution.md @@ -5,6 +5,6 @@ let styles = ["Jazz", "Blues"]; styles.push("Rock-n-Roll"); styles[Math.floor((styles.length - 1) / 2)] = "Classics"; alert( styles.shift() ); -styles.unshift("Rap", "Reggie"); +styles.unshift("Rap", "Reggae"); ``` diff --git a/1-js/05-data-types/04-array/array-pop.png b/1-js/05-data-types/04-array/array-pop.png index 023642fa7c..4d6867b14b 100644 Binary files a/1-js/05-data-types/04-array/array-pop.png and b/1-js/05-data-types/04-array/array-pop.png differ diff --git a/1-js/05-data-types/04-array/array-pop@2x.png b/1-js/05-data-types/04-array/array-pop@2x.png index 301fd8f66b..c65ef94467 100644 Binary files a/1-js/05-data-types/04-array/array-pop@2x.png and b/1-js/05-data-types/04-array/array-pop@2x.png differ diff --git a/1-js/05-data-types/04-array/array-shift.png b/1-js/05-data-types/04-array/array-shift.png index 5f2cef5dd0..5798a64760 100644 Binary files a/1-js/05-data-types/04-array/array-shift.png and b/1-js/05-data-types/04-array/array-shift.png differ diff --git a/1-js/05-data-types/04-array/array-shift@2x.png b/1-js/05-data-types/04-array/array-shift@2x.png index 1bd68b3154..ba95f2651e 100644 Binary files a/1-js/05-data-types/04-array/array-shift@2x.png and b/1-js/05-data-types/04-array/array-shift@2x.png differ diff --git a/1-js/05-data-types/04-array/array-speed.png b/1-js/05-data-types/04-array/array-speed.png index 3737e8248f..a400d0c1d8 100644 Binary files a/1-js/05-data-types/04-array/array-speed.png and b/1-js/05-data-types/04-array/array-speed.png differ diff --git a/1-js/05-data-types/04-array/array-speed@2x.png b/1-js/05-data-types/04-array/array-speed@2x.png index e45624b502..11a3e67c91 100644 Binary files a/1-js/05-data-types/04-array/array-speed@2x.png and b/1-js/05-data-types/04-array/array-speed@2x.png differ diff --git a/1-js/05-data-types/04-array/article.md b/1-js/05-data-types/04-array/article.md index 3336a3b3bb..0932ac5b42 100644 --- a/1-js/05-data-types/04-array/article.md +++ b/1-js/05-data-types/04-array/article.md @@ -1,12 +1,24 @@ +<<<<<<< HEAD # 数组 对象允许存储键值化的集合,这很好。 但很多时候我们需要的是**有序集合**,里面的元素都是按顺序排列的。例如,我们可能需要存储一些列表,比如用户、商品以及 HTML 元素等。 +======= +# Arrays + +Objects allow you to store keyed collections of values. That's fine. + +But quite often we find that we need an *ordered collection*, where we have a 1st, a 2nd, a 3rd element and so on. For example, we need that to store a list of something: users, goods, HTML elements etc. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 这里使用对象就不是很方便了,因为对象不提供能够管理元素顺序的方法。我们不能在已有的元素“之间”插入一个新的属性。这种场景下对象就不太适用了。 +<<<<<<< HEAD 这时一个特殊的数据结构数组(`Array`)就派上用场了,它能存储有序的集合。 +======= +There exists a special data structure named `Array`, to store ordered collections. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 声明 @@ -79,12 +91,18 @@ arr[3](); // hello ``` +<<<<<<< HEAD ````smart header="以逗号结尾" 数组和对象一样,都可以在末尾冗余一个逗号: ```js +======= +````smart header="Trailing comma" +An array, just like an object, may end with a comma: +```js +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb let fruits = [ - "Apple", - "Orange", + "Apple", + "Orange", "Plum"*!*,*/!* ]; ``` @@ -95,7 +113,11 @@ let fruits = [ ## pop/push, shift/unshift 方法 +<<<<<<< HEAD [队列](https://en.wikipedia.org/wiki/Queue_(abstract_data_type))是最常见的使用数组的方法之一. 在计算机科学中,这意味着一个有序的元素的集合支持两个操作: +======= +A [queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) is one of the most common uses of an array. In computer science, this means an ordered collection of elements which supports two operations: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - `push` 在末端添加一个元素. - `shift` 取出队列最前端的一个元素,整个队列往前移,这样原先排第二的元素现在排在了第一。 @@ -104,9 +126,15 @@ let fruits = [ 这两种操作数组都支持. +<<<<<<< HEAD 队列的应用在实践中经常会碰到,例如需要在屏幕上显示消息队列。 数组还有另一个用例,就是数据结构[栈](https://en.wikipedia.org/wiki/Stack_(abstract_data_type))。 +======= +In practice we need it very often. For example, a queue of messages that need to be shown on-screen. + +There's another use case for arrays -- the data structure named [stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)). +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 它支持两种操作: @@ -121,7 +149,11 @@ let fruits = [ 对于栈来说,最后放进去的是最先接收的,也叫做 LIFO(后进先出)法则。而与队列相对应的叫做 FIFO(先进先出)。 +<<<<<<< HEAD JavaScript 中的数组既可以用作队列,也可以用作栈。它们允许从前端/末端来添加/删除元素。 +======= +Arrays in JavaScript can work both as a queue and as a stack. They allow you to add/remove elements both to/from the beginning or the end. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 这在计算机科学中叫做[双端队列](https://en.wikipedia.org/wiki/Double-ended_queue)。 @@ -189,11 +221,19 @@ alert( fruits ); ## 内部 +<<<<<<< HEAD 数组是一种特殊的对象。使用方括号来访问属性 `arr[0]` 实际上是来自于对象的语法。这个数字被用作键值。 +======= +An array is a special kind of object. The square brackets used to access a property `arr[0]` actually come from the object syntax. That's essentially the same as `obj[key]`, where `arr` is the object, while numbers are used as keys. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 他们扩展了对象,提供了特殊的方法来处理有序的数据集合,还添加了 `length` 属性。但是核心还是一个对象。 +<<<<<<< HEAD 记住,在 JavaScript 中只有 7 种基本类型。数组是一个对象因此其行为也像一个对象。 +======= +Remember, there are only 7 basic types in JavaScript. Array is an object and thus behaves like an object. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如,它是通过引用来复制的: @@ -203,6 +243,11 @@ let fruits = ["Banana"] let arr = fruits; // 通过引用复制 (两个变量引用的是相同的数组) alert( arr === fruits ); // true +<<<<<<< HEAD +======= + +arr.push("Pear"); // modify the array by reference +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb arr.push("Pear"); // 通过引用修改数组 @@ -229,9 +274,15 @@ fruits.age = 25; // 用任意的名字创建属性 数组误用的几种方式: +<<<<<<< HEAD - 添加一个非数字的属性比如 `arr.test = 5`。 - 制造空洞,比如:添加 `arr[0]` 后添加 `arr[1000]` (它们中间什么都没有)。 - 以倒序填充数组, 比如 `arr[1000]`,`arr[999]` 等等。 +======= +- Add a non-numeric property like `arr.test = 5`. +- Make holes, like: add `arr[0]` and then `arr[1000]` (and nothing between them). +- Fill the array in the reverse order, like `arr[1000]`, `arr[999]` and so on. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 请将数组视为作用于**有序数据**的特殊结构,它们为此提供了特殊的方法。数组在 JavaScript 引擎内部是经过特殊调整的,使得更好的作用于连续的有序数据,所以请以这种方式使用数组。如果你需要任意键值,那很有可能实际上你需要的是常规对象 `{}`。 @@ -296,7 +347,7 @@ let fruits = ["Apple", "Orange", "Plum"]; // 迭代数组元素 for (let fruit of fruits) { - alert( fruit ); + alert( fruit ); } ``` @@ -320,7 +371,11 @@ for (let key in arr) { 在浏览器和其它环境中有一种“类数组”的对象,它们**看似是数组**,也就是说,它们有 `length` 和索引属性,但是也可能有其它的非数字的属性和方法,这通常是我们不需要的。`for..in` 循环会把它们都列出来。所以如果我们需要处理类数组对象,这些“额外”的属性就会存在问题。 +<<<<<<< HEAD 2. `for..in` 循环适用于普通对象,不适用于数组,而且会慢 10-100 倍。当然即使是这样也依然非常快。只有在遇到瓶颈或者一些不相关的场景增速可能会有问题。但是我们仍然应该了解这其中的不同。 +======= +2. The `for..in` loop is optimized for generic objects, not arrays, and thus is 10-100 times slower. Of course, it's still very fast. The speedup may only matter in bottlenecks. But still we should be aware of the difference. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 通常来说,我们不应该用 `for..in` 来处理数组。 @@ -338,7 +393,11 @@ fruits[123] = "Apple"; alert( fruits.length ); // 124 ``` +<<<<<<< HEAD 要知道的是我们通常不会这样使用数组。 +======= +Note that we usually don't use arrays like that. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb `length` 属性的另一个有意思的点是它是可写的。 @@ -369,7 +428,11 @@ let arr = *!*new Array*/!*("Apple", "Pear", "etc"); 如果调用 `new Array` 使用的是一个单独的数字作为参数,那么就会创建一个**指定了长度,却没有任何项**的数组。 +<<<<<<< HEAD 让我们看看如何搬起石头砸自己的脚: +======= +Let's see how one can shoot themself in the foot: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let arr = new Array(2); // 会创建一个数组 [2] 吗? @@ -385,7 +448,11 @@ alert( arr.length ); // length 2 ## 多维数组 +<<<<<<< HEAD 数组里的项也可以是数组。我们可以以多维数组的方式存储矩阵: +======= +Arrays can have items that are also arrays. We can use it for multidimensional arrays, for example to store matrices: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let matrix = [ @@ -431,7 +498,11 @@ alert( "1,2" + 1 ); // "1,21" ## 总结 +<<<<<<< HEAD 数组是一种特殊的对象,适用于存储和管理有序的数据项。 +======= +Array is a special kind of object, suited to storing and managing ordered data items. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - 声明: @@ -443,7 +514,14 @@ alert( "1,2" + 1 ); // "1,21" let arr = new Array(item1, item2...); ``` +<<<<<<< HEAD 调用 `new Array(number)` 会创建一个指定长度的数组,且不含有任何项。 +======= + The call to `new Array(number)` creates an array with the given length, but without elements. + +- The `length` property is the array length or, to be precise, its last numeric index plus one. It is auto-adjusted by array methods. +- If we shorten `length` manually, the array is truncated. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - `length` 属性是数组的长度,准确地说,是它的最后一个数字索引值加一。它由数组方法自动调整。 - 如果我们手动缩短 `length`,那么数组就会被截断。 @@ -455,9 +533,13 @@ alert( "1,2" + 1 ); // "1,21" - `shift()` 从前端移除并返回该元素。 - `unshift(...items)` 从前端添加项 `items`。 +<<<<<<< HEAD 遍历数组的元素: - `for (let i=0; i。 +======= +We will return to arrays and study more methods to add, remove, extract elements and sort arrays in the chapter . +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/05-data-types/04-array/queue.png b/1-js/05-data-types/04-array/queue.png index 5e1fb640ce..39af3beda4 100644 Binary files a/1-js/05-data-types/04-array/queue.png and b/1-js/05-data-types/04-array/queue.png differ diff --git a/1-js/05-data-types/04-array/queue@2x.png b/1-js/05-data-types/04-array/queue@2x.png index 6acfc83d12..75045a51dc 100644 Binary files a/1-js/05-data-types/04-array/queue@2x.png and b/1-js/05-data-types/04-array/queue@2x.png differ diff --git a/1-js/05-data-types/04-array/stack.png b/1-js/05-data-types/04-array/stack.png index d1c9cb9afd..7d2599355b 100644 Binary files a/1-js/05-data-types/04-array/stack.png and b/1-js/05-data-types/04-array/stack.png differ diff --git a/1-js/05-data-types/04-array/stack@2x.png b/1-js/05-data-types/04-array/stack@2x.png index b3835fa442..16c6225186 100644 Binary files a/1-js/05-data-types/04-array/stack@2x.png and b/1-js/05-data-types/04-array/stack@2x.png differ diff --git a/1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js b/1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js index 024d6d6c2b..490f570ada 100644 --- a/1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js +++ b/1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js @@ -1,8 +1,10 @@ function camelize(str) { return str - .split('-') // my-long-word -> ['my', 'long', 'word'] - .map( + .split('-') // splits 'my-long-word' into array ['my', 'long', 'word'] + .map( + // capitalizes first letters of all array items except the first one + // converts ['my', 'long', 'word'] into ['my', 'Long', 'Word'] (word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1) - ) // ['my', 'long', 'word'] -> ['my', 'Long', 'Word'] - .join(''); // ['my', 'Long', 'Word'] -> myLongWord + ) + .join(''); // joins ['my', 'Long', 'Word'] into 'myLongWord' } diff --git a/1-js/05-data-types/05-array-methods/10-average-age/task.md b/1-js/05-data-types/05-array-methods/10-average-age/task.md index 5ef6491bb1..089db5828a 100644 --- a/1-js/05-data-types/05-array-methods/10-average-age/task.md +++ b/1-js/05-data-types/05-array-methods/10-average-age/task.md @@ -4,7 +4,11 @@ importance: 4 # 获取平均 +<<<<<<< HEAD 编写 `getAverageAge(users)` 函数,该函数获取一个具有 age 属性的对象数组,并获取平均值。 +======= +Write the function `getAverageAge(users)` that gets an array of objects with property `age` and returns the average age. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 平均的公式是 `(age1 + age2 + ... + ageN) / N`。 @@ -19,4 +23,3 @@ let arr = [ john, pete, mary ]; alert( getAverageAge(arr) ); // (25 + 30 + 29) / 3 = 28 ``` - diff --git a/1-js/05-data-types/05-array-methods/11-array-unique/solution.md b/1-js/05-data-types/05-array-methods/11-array-unique/solution.md index 65995830dc..9f94804997 100644 --- a/1-js/05-data-types/05-array-methods/11-array-unique/solution.md +++ b/1-js/05-data-types/05-array-methods/11-array-unique/solution.md @@ -2,7 +2,7 @@ - 对于每个元素,我们将检查结果数组是否已经有该元素。 - 如果有,则忽略,否则添加结果。 -```js run +```js run demo function unique(arr) { let result = []; diff --git a/1-js/05-data-types/05-array-methods/2-filter-range/solution.md b/1-js/05-data-types/05-array-methods/2-filter-range/solution.md index e69de29bb2..73993a07a0 100644 --- a/1-js/05-data-types/05-array-methods/2-filter-range/solution.md +++ b/1-js/05-data-types/05-array-methods/2-filter-range/solution.md @@ -0,0 +1,14 @@ +```js run demo +function filterRange(arr, a, b) { + // added brackets around the expression for better readability + return arr.filter(item => (a <= item && item <= b)); +} + +let arr = [5, 3, 8, 1]; + +let filtered = filterRange(arr, 1, 4); + +alert( filtered ); // 3,1 (matching values) + +alert( arr ); // 5,3,8,1 (not modified) +``` diff --git a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js index 61cda126b6..488db3755b 100644 --- a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js +++ b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js @@ -1,5 +1,4 @@ - function filterRangeInPlace(arr, a, b) { for (let i = 0; i < arr.length; i++) { @@ -12,4 +11,4 @@ function filterRangeInPlace(arr, a, b) { } } -} \ No newline at end of file +} diff --git a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md index e69de29bb2..36e3130ff0 100644 --- a/1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md +++ b/1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md @@ -0,0 +1,21 @@ +```js run demo +function filterRangeInPlace(arr, a, b) { + + for (let i = 0; i < arr.length; i++) { + let val = arr[i]; + + // remove if outside of the interval + if (val < a || val > b) { + arr.splice(i, 1); + i--; + } + } + +} + +let arr = [5, 3, 8, 1]; + +filterRangeInPlace(arr, 1, 4); // removed the numbers except from 1 to 4 + +alert( arr ); // [3, 1] +``` diff --git a/1-js/04-object-basics/06-constructor-new/4-calculator-extendable/_js.view/solution.js b/1-js/05-data-types/05-array-methods/6-calculator-extendable/_js.view/solution.js similarity index 100% rename from 1-js/04-object-basics/06-constructor-new/4-calculator-extendable/_js.view/solution.js rename to 1-js/05-data-types/05-array-methods/6-calculator-extendable/_js.view/solution.js diff --git a/1-js/04-object-basics/06-constructor-new/4-calculator-extendable/_js.view/test.js b/1-js/05-data-types/05-array-methods/6-calculator-extendable/_js.view/test.js similarity index 100% rename from 1-js/04-object-basics/06-constructor-new/4-calculator-extendable/_js.view/test.js rename to 1-js/05-data-types/05-array-methods/6-calculator-extendable/_js.view/test.js diff --git a/1-js/04-object-basics/06-constructor-new/4-calculator-extendable/solution.md b/1-js/05-data-types/05-array-methods/6-calculator-extendable/solution.md similarity index 100% rename from 1-js/04-object-basics/06-constructor-new/4-calculator-extendable/solution.md rename to 1-js/05-data-types/05-array-methods/6-calculator-extendable/solution.md diff --git a/1-js/04-object-basics/06-constructor-new/4-calculator-extendable/task.md b/1-js/05-data-types/05-array-methods/6-calculator-extendable/task.md similarity index 72% rename from 1-js/04-object-basics/06-constructor-new/4-calculator-extendable/task.md rename to 1-js/05-data-types/05-array-methods/6-calculator-extendable/task.md index 31d72ecb11..99371ddaa2 100644 --- a/1-js/04-object-basics/06-constructor-new/4-calculator-extendable/task.md +++ b/1-js/05-data-types/05-array-methods/6-calculator-extendable/task.md @@ -17,7 +17,11 @@ importance: 5 alert( calc.calculate("3 + 7") ); // 10 ``` +<<<<<<< HEAD:1-js/04-object-basics/06-constructor-new/4-calculator-extendable/task.md 2. 然后添加 calculate 新操作的方法 `addOperator(name, func)`。它需要运算符 `name` 和实现它的双参数函数 `func(a,b)`。 +======= +2. Then add the method `addMethod(name, func)` that teaches the calculator a new operation. It takes the operator `name` and the two-argument function `func(a,b)` that implements it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/05-data-types/05-array-methods/6-calculator-extendable/task.md 例如,我们添加乘法`*`,除法`/`和求幂`**`: diff --git a/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md b/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md index f19ac14aa8..74d8302738 100644 --- a/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md +++ b/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md @@ -1,17 +1,22 @@ ```js run no-beautify -function sortByName(arr) { - arr.sort((a, b) => a.name > b.name); +function sortByAge(arr) { + arr.sort((a, b) => a.age > b.age ? 1 : -1); } let john = { name: "John", age: 25 }; let pete = { name: "Pete", age: 30 }; let mary = { name: "Mary", age: 28 }; -let arr = [ john, pete, mary ]; +let arr = [ pete, john, mary ]; -sortByName(arr); +sortByAge(arr); +<<<<<<< HEAD // 现在排序是:[john, mary, pete] +======= +// now sorted is: [john, mary, pete] +alert(arr[0].name); // John +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb alert(arr[1].name); // Mary +alert(arr[2].name); // Pete ``` - diff --git a/1-js/05-data-types/05-array-methods/8-sort-objects/task.md b/1-js/05-data-types/05-array-methods/8-sort-objects/task.md index 7f4b584b47..4012615d4c 100644 --- a/1-js/05-data-types/05-array-methods/8-sort-objects/task.md +++ b/1-js/05-data-types/05-array-methods/8-sort-objects/task.md @@ -2,9 +2,15 @@ importance: 5 --- +<<<<<<< HEAD # 排序对象 编写函数 `sortByName(users)` 获得对象数组 property的属性 `name` 并对它进行排序。 +======= +# Sort users by age + +Write the function `sortByAge(users)` that gets an array of objects with the `age` property and sorts them by `age`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: @@ -13,11 +19,12 @@ let john = { name: "John", age: 25 }; let pete = { name: "Pete", age: 30 }; let mary = { name: "Mary", age: 28 }; -let arr = [ john, pete, mary ]; +let arr = [ pete, john, mary ]; -sortByName(arr); +sortByAge(arr); // now: [john, mary, pete] +alert(arr[0].name); // John alert(arr[1].name); // Mary +alert(arr[2].name); // Pete ``` - diff --git a/1-js/05-data-types/05-array-methods/article.md b/1-js/05-data-types/05-array-methods/article.md index d9f3963eef..ee5db9fcb3 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -36,7 +36,11 @@ alert( arr.length ); // 3 所以应该使用特殊的方法。 +<<<<<<< HEAD [arr.splice(str)](mdn:js/Array/splice) 方法可以说是数组界的瑞士军刀。它可以做所有事情:添加,删除和插入元素。 +======= +The [arr.splice(str)](mdn:js/Array/splice) method is a swiss army knife for arrays. It can do everything: insert, remove and replace elements. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 语法是: @@ -122,7 +126,11 @@ alert( arr ); // 1,2,3,4,5 arr.slice(start, end) ``` +<<<<<<< HEAD 它从所有元素的开始索引 `"start"` 复制到 `"end"` (不包括 `"end"`) 返回一个新的数组。`start` 和 `end` 都可以是负数,在这种情况下,从末尾计算索引。 +======= +It returns a new array containing all items from index `"start"` to `"end"` (not including `"end"`). Both `start` and `end` can be negative, in that case position from array end is assumed. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 它和字符串的 `str.slice` 有点像,就是把子字符串替换成子数组。 @@ -201,7 +209,40 @@ let arrayLike = { alert( arr.concat(arrayLike) ); // 1,2,something,else ``` +<<<<<<< HEAD ## 查询数组 +======= +## Iterate: forEach + +The [arr.forEach](mdn:js/Array/forEach) method allows to run a function for every element of the array. + +The syntax: +```js +arr.forEach(function(item, index, array) { + // ... do something with item +}); +``` + +For instance, this shows each element of the array: + +```js run +// for each element call alert +["Bilbo", "Gandalf", "Nazgul"].forEach(alert); +``` + +And this code is more elaborate about their positions in the target array: + +```js run +["Bilbo", "Gandalf", "Nazgul"].forEach((item, index, array) => { + alert(`${item} is at index ${index} in ${array}`); +}); +``` + +The result of the function (if it returns any) is thrown away and ignored. + + +## Searching in array +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 这些是在数组中查询某些内容的方法。 @@ -209,9 +250,15 @@ alert( arr.concat(arrayLike) ); // 1,2,something,else [arr.indexOf](mdn:js/Array/indexOf)、[arr.lastIndexOf](mdn:js/Array/lastIndexOf) 和 [arr.includes](mdn:js/Array/includes) 方法与字符串操作具有相同的语法,只不过这里是对数组元素而不是字符进行操作: +<<<<<<< HEAD - `arr.indexOf(item, from)` 从索引 `from` 查询 `item`,如果找到返回索引,否则返回 `-1`。 - `arr.lastIndexOf(item, from)` — 和上面相同,只是从尾部开始查询。 - `arr.includes(item, from)` — 从索引 `from` 查询 `item`,如果找到则返回 `true`。 +======= +- `arr.indexOf(item, from)` looks for `item` starting from index `from`, and returns the index where it was found, otherwise `-1`. +- `arr.lastIndexOf(item, from)` -- same, but looks for from right to left. +- `arr.includes(item, from)` -- looks for `item` starting from index `from`, returns `true` if found. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: @@ -246,7 +293,12 @@ alert( arr.includes(NaN) );// true (correct) 语法: ```js let result = arr.find(function(item, index, array) { +<<<<<<< HEAD  // 如果查询到返回 true +======= + // if true is returned, item is returned and iteration is stopped + // for falsy scenario returns undefined +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb }); ``` @@ -274,9 +326,15 @@ alert(user.name); // John 在现实生活中,对象数组是很常见,所以`find` 方法非常有用。 +<<<<<<< HEAD 注意在这个例子中我们传给了 `find` 一个单参数函数 `item => item.id == 1`。其他参数 `find` 很少使用。 与 [arr.findIndex](mdn:js/Array/findIndex) 方法本质上是相同的,但它返回找到元素的索引而不是元素本身。 +======= +Note that in the example we provide to `find` the function `item => item.id == 1` with one argument. Other arguments of this function are rarely used. + +The [arr.findIndex](mdn:js/Array/findIndex) method is essentially the same, but it returns the index where the element was found instead of the element itself and `-1` is returned when nothing is found. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ### filter @@ -284,11 +342,20 @@ alert(user.name); // John 如果需要匹配的有很多,我们可以使用 [arr.filter(fn)](mdn:js/Array/filter)。 +<<<<<<< HEAD 语法与 `find` 大致相同,但是它返回的是所有匹配元素组成的数组: ```js let results = arr.filter(function(item, index, array) {  // 在元素通过过滤器时返回 true +======= +The syntax is similar to `find`, but filter continues to iterate for all array elements even if `true` is already returned: + +```js +let results = arr.filter(function(item, index, array) { + // if true item is pushed to results and iteration continues + // returns empty array for complete falsy scenario +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb }); ``` @@ -329,7 +396,7 @@ let result = arr.map(function(item, index, array) { 例如,在这里我们将每个元素转换为它的字符串长度: ```js run -let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length) +let lengths = ["Bilbo", "Gandalf", "Nazgul"].map(item => item.length); alert(lengths); // 5,7,6 ``` @@ -416,8 +483,13 @@ alert(arr); // *!*1, 2, 15*/!* ``` ```` +<<<<<<< HEAD ````smart header="箭头函数最好" [箭头函数](info:function-expression#arrow-functions)还记得吗?这里使用箭头函数会更加简洁: +======= +````smart header="Arrow functions for the best" +Remember [arrow functions](info:function-expressions-arrows#arrow-functions)? We can use them here for neater sorting: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js arr.sort( (a, b) => a - b ); @@ -477,7 +549,11 @@ alert( str.split('') ); // t,e,s,t ``` ```` +<<<<<<< HEAD [arr.join(str)](mdn:js/Array/join) 与 `split` 相反。它会在它们之间创建一串由 `str` 粘合的 `arr` 项。 +======= +The call [arr.join(separator)](mdn:js/Array/join) does the reverse to `split`. It creates a string of `arr` items glued by `separator` between them. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: @@ -491,7 +567,11 @@ alert( str ); // Bilbo;Gandalf;Nazgul ### reduce/reduceRight +<<<<<<< HEAD 当我们需要遍历一个数组时 — 我们可以使用 `forEach`。 +======= +When we need to iterate over an array -- we can use `forEach`, `for` or `for..of`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 当我们需要迭代并返回每个元素的数据时 — 我们可以使用 `map`。 @@ -500,16 +580,22 @@ alert( str ); // Bilbo;Gandalf;Nazgul 语法是: ```js -let value = arr.reduce(function(previousValue, item, index, arr) { +let value = arr.reduce(function(previousValue, item, index, array) { // ... }, initial); ``` 该函数应用于元素。从第二个参数开始你可能就会觉得很眼熟了: +<<<<<<< HEAD - `item` — 当前的数组元素。 - `index` — 当前索引。 - `arr` — 数组本身。 +======= +- `item` -- is the current array item. +- `index` -- is its position. +- `array` -- is the array. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 目前为止,这很像 `forEach/map`。但还有一个参数不同就是: @@ -539,7 +625,11 @@ alert(result); // 15 ![](reduce.png) +<<<<<<< HEAD 或者以表格的形式出现,每行代表的是下一个数组元素的函数调用: +======= +Or in the form of a table, where each row represents a function call on the next array element: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb | |`sum`|`current`|`result`| |---|-----|---------|---------| @@ -585,6 +675,7 @@ arr.reduce((sum, current) => sum + current); [arr.reduceRight](mdn:js/Array/reduceRight) 也一样,但是遍历是从右到左。 +<<<<<<< HEAD ## 迭代:forEach [arr.forEach](mdn:js/Array/forEach) 方法允许为数组的每个元素运行一个函数。 @@ -613,6 +704,8 @@ arr.forEach(function(item, index, array) { 该函数的结果(如果它返回的话)被抛弃并被忽略。 +======= +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## Array.isArray 数组基于对象。不构成单独的语言类型。 @@ -689,11 +782,22 @@ alert(youngerUsers.length); // 2 - `slice(start, end)` — 它从所有元素的开始索引 `"start"` 复制到 `"end"` (不包括 `"end"`) 返回一个新的数组。 - `concat(...items)` — 返回一个新数组:复制当前数组的所有成员并向其中添加 `items`。如果有任何` items` 是一个数组,那么就取其元素。 +<<<<<<< HEAD - 查询元素: - `indexOf/lastIndexOf(item, pos)` — 从 `pos` 找到 `item`,则返回索引否则返回 `-1`。 - `includes(value)` — 如果数组有 `value`,则返回 `true`,否则返回 `false`。 - `find/filter(func)` — 通过函数过滤元素,返回 `true` 条件的符合 find 函数的第一个值或符合 filter 函数的全部值。 - `findIndex` 和 `find` 类似,但返回索引而不是值。 +======= +- To search among elements: + - `indexOf/lastIndexOf(item, pos)` -- look for `item` starting from position `pos`, return the index or `-1` if not found. + - `includes(value)` -- returns `true` if the array has `value`, otherwise `false`. + - `find/filter(func)` -- filter elements through the function, return first/all values that make it return `true`. + - `findIndex` is like `find`, but returns the index instead of a value. + +- To iterate over elements: + - `forEach(func)` -- calls `func` for every element, does not return anything. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - 转换数组: - `map(func)` — 从每个元素调用 `func` 的结果创建一个新数组。 @@ -702,11 +806,16 @@ alert(youngerUsers.length); // 2 - `split/join` — 将字符串转换为数组并返回。 - `reduce(func, initial)` — 通过为每个元素调用 `func` 计算数组上的单个值并在调用之间传递中间结果。 +<<<<<<< HEAD - 迭代元素: - `forEach(func)` — 为每个元素调用 `func`,不返回任何东西。 - 其他:  - `Array.isArray(arr)` 检查 `arr` 是否是一个数组。 +======= +- Additionally: + - `Array.isArray(arr)` checks `arr` for being an array. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 请注意,`sort`,`reverse` 和 `splice` 方法修改数组本身。 diff --git a/1-js/05-data-types/05-array-methods/reduce.png b/1-js/05-data-types/05-array-methods/reduce.png index 41476d2aea..7566c4d84c 100644 Binary files a/1-js/05-data-types/05-array-methods/reduce.png and b/1-js/05-data-types/05-array-methods/reduce.png differ diff --git a/1-js/05-data-types/05-array-methods/reduce@2x.png b/1-js/05-data-types/05-array-methods/reduce@2x.png index f31647d17b..7c9fd6692b 100644 Binary files a/1-js/05-data-types/05-array-methods/reduce@2x.png and b/1-js/05-data-types/05-array-methods/reduce@2x.png differ diff --git a/1-js/05-data-types/06-iterable/article.md b/1-js/05-data-types/06-iterable/article.md index ada5f4a6e9..eac7af1310 100644 --- a/1-js/05-data-types/06-iterable/article.md +++ b/1-js/05-data-types/06-iterable/article.md @@ -3,9 +3,16 @@ **Iterable** (可迭代对象)是数组的泛化。这个概念是说任何对象都可在 `for..of` 循环中使用。 +<<<<<<< HEAD 数组本身就是可迭代的。但不仅仅是数组。字符串也可以迭代,很多其他内建对象也都可以迭代。 在核心 JavaScript 中,可迭代对象用途广泛。我们将会看到,很多内建的操作和方法都依赖于它。 +======= +Of course, Arrays are iterable. But there are many other built-in objects, that are iterable as well. For instance, Strings are iterable also. As we'll see, many built-in operators and methods rely on them. + +If an object represents a collection (list, set) of something, then `for..of` is a great syntax to loop over it, so let's see how to make it work. + +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## Symbol.iterator @@ -27,10 +34,17 @@ let range = { 为了让 `range` 对象可迭代(也就让 `for..of` 可以运行)我们需要为对象添加一个名为 `Symbol.iterator` 的方法(一个特殊的内置标记)。 +<<<<<<< HEAD - 当 `for..of` 循环开始,它将会调用这个方法(如果没找到,就会报错)。 - 这个方法必须返回一个迭代器 —— 一个有 `next` 方法的对象。 - 当 `for..of` 循环希望取得下一个数值,它就调用这个对象的 `next()` 方法。 - `next()` 返回结果的格式必须是 `{done: Boolean, value: any}`,当 `done=true` 时,表示迭代结束,否则 `value` 必须是一个未被迭代的新值。 +======= +1. When `for..of` starts, it calls that method once (or errors if not found). The method must return an *iterator* -- an object with the method `next`. +2. Onward, `for..of` works *only with that returned object*. +3. When `for..of` wants the next value, it calls `next()` on that object. +4. The result of `next()` must have the form `{done: Boolean, value: any}`, where `done=true` means that the iteration is finished, otherwise `value` must be the new value. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 这是 `range` 的全部实现: @@ -43,7 +57,12 @@ let range = { // 1. 使用 for..of 将会首先调用它: range[Symbol.iterator] = function() { +<<<<<<< HEAD // 2. ...它返回一个迭代器: +======= + // ...it returns the iterator object: + // 2. Onward, for..of works only with this iterator, asking it for next values +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb return { current: this.from, last: this.to, @@ -66,10 +85,17 @@ for (let num of range) { } ``` +<<<<<<< HEAD 这段代码中有几点需要着重关注: - `range` 自身没有 `next()` 方法。 - 相反,是调用 `range[Symbol.iterator]()` 时将会被创建的另一个所谓的“迭代器”对象,将会处理迭代操作。 +======= +Please note the core feature of iterables: an important separation of concerns: + +- The `range` itself does not have the `next()` method. +- Instead, another object, a so-called "iterator" is created by the call to `range[Symbol.iterator]()`, and it handles the whole iteration. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 所以,迭代器对象和迭代的对象其实是分离的。 @@ -101,10 +127,19 @@ for (let num of range) { } ``` +<<<<<<< HEAD 现在 `range[Symbol.iterator]()` 返回了 `range` 对象自身:它包括了必需的 `next()` 方法并通过 `this.current` 记忆了当前迭代进程。有时候,这样也可以。但缺点是,现在不可能同时在 `range` 上运行两个 `for..of` 循环了:这两个循环将会共享迭代状态,因为仅有一个迭代器 —— 也就是对象自身。 ```smart header="Infinite iterators" 无穷迭代也是可行的。例如,`range` 设置为 `range.to = Infinity` 则成为无穷迭代。或者我们可以创建一个可迭代对象,它生成一个伪随机数无穷序列。也是可用的。 +======= +Now `range[Symbol.iterator]()` returns the `range` object itself: it has the necessary `next()` method and remembers the current iteration progress in `this.current`. Shorter? Yes. And sometimes that's fine too. + +The downside is that now it's impossible to have two `for..of` loops running over the object simultaneously: they'll share the iteration state, because there's only one iterator -- the object itself. But two parallel for-ofs is a rare thing, even in async scenarios. + +```smart header="Infinite iterators" +Infinite iterators are also possible. For instance, the `range` becomes infinite for `range.to = Infinity`. Or we can make an iterable object that generates an infinite sequence of pseudorandom numbers. Also can be useful. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb `next` 没有什么限制,它可以返回越来越多的值,这也很常见。 @@ -120,11 +155,20 @@ for (let num of range) { ```js run for (let char of "test") { +<<<<<<< HEAD alert( char ); // t,然后 e,然后 s,然后 t } ``` 对于 UTF-16 的扩展字符,它也能正常工作! +======= + // triggers 4 times: once for each character + alert( char ); // t, then e, then s, then t +} +``` + +And it works correctly with surrogate pairs! +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let str = '𝒳😂'; @@ -139,7 +183,11 @@ for (let char of str) { 但是为了更深层的了解知识概念,我们来看看如何显式的创建迭代器。 +<<<<<<< HEAD 我们将会采用与 `for..of` 一样的方法迭代字符串,但是是直接的调用。这段代码将会获取字符串的迭代器,然后“手动”调用它。 +======= +We'll iterate over a string in exactlly the same way as `for..of`, but with direct calls. This code creates a string iterator and gets values from it "manually": +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let str = "Hello"; @@ -165,7 +213,13 @@ while (true) { - **Iterables** 是应用于 `Symbol.iterator` 方法的对象,像上文所述。 - **Array-likes** 是有索引和 `length` 属性的对象,所以它们很像数组。 +<<<<<<< HEAD 很自然的,这些属性都可以结合起来。例如,字符串既是可迭代对象(`for..of` 可以迭代字符串)也是类数组对象(它们有数字索引也有 `length` 属性)。 +======= +When we use JavaScript for practical tasks in browser or other environments, we may meet objects that are iterables or array-likes, or both. + +For instance, strings are both iterable (`for..of` works on them) and array-like (they have numeric indexes and `length`). +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 但是一个可迭代对象也许不是类数组对象。反之亦然,一个类数组对象可能也不可迭代。 @@ -186,11 +240,19 @@ for (let item of arrayLike) {} */!* ``` +<<<<<<< HEAD 它们有什么共同点?可迭代对象和类数组对象通常都不是数组,他们没有 `push`,`pop` 等等方法。如果我们有一个这样的对象并且想像数组那样操作它,这就有些不方便了。 ## Array.from 有一个全局方法 [Array.from](mdn:js/Array/from) 可以把它们全都结合起来。它以一个可迭代对象或者类数组对象作为参数并返回一个真正的 `Array` 数组。然后我们就可以用该对象调用数组的方法了。 +======= +Both iterables and array-likes are usually *not arrays*, they don't have `push`, `pop` etc. That's rather inconvenient if we have such an object and want to work with it as with an array. E.g. we would like to work with `range` using array methods. How to achieve that? + +## Array.from + +There's a universal method [Array.from](mdn:js/Array/from) that takes an iterable or array-like value and makes a "real" `Array` from it. Then we can call array methods on it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: @@ -222,7 +284,11 @@ alert(arr); // 1,2,3,4,5 (数组的 toString 转化函数生效) Array.from(obj[, mapFn, thisArg]) ``` +<<<<<<< HEAD 第二个参数 `mapFn` 应是一个在元素被添加到数组前,施加于每个元素的方法,`thisArg` 允许设置方法的 `this` 对象。 +======= +The optional second argument `mapFn` can be a function that will be applied to each element before adding to the array, and `thisArg` allows to set `this` for it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: diff --git a/1-js/05-data-types/07-map-set-weakmap-weakset/02-filter-anagrams/solution.md b/1-js/05-data-types/07-map-set-weakmap-weakset/02-filter-anagrams/solution.md index 101ad46b9f..28dc38acef 100644 --- a/1-js/05-data-types/07-map-set-weakmap-weakset/02-filter-anagrams/solution.md +++ b/1-js/05-data-types/07-map-set-weakmap-weakset/02-filter-anagrams/solution.md @@ -59,7 +59,7 @@ map.set(sorted, word); 这就是答案: -```js run +```js run demo function aclean(arr) { let obj = {}; @@ -68,7 +68,7 @@ function aclean(arr) { obj[sorted] = arr[i]; } - return Array.from(Object.values(obj)); + return Object.values(obj); } let arr = ["nap", "teachers", "cheaters", "PAN", "ear", "era", "hectares"]; diff --git a/1-js/05-data-types/07-map-set-weakmap-weakset/03-iterable-keys/task.md b/1-js/05-data-types/07-map-set-weakmap-weakset/03-iterable-keys/task.md index c86540e3d8..a8f8203955 100644 --- a/1-js/05-data-types/07-map-set-weakmap-weakset/03-iterable-keys/task.md +++ b/1-js/05-data-types/07-map-set-weakmap-weakset/03-iterable-keys/task.md @@ -16,7 +16,11 @@ map.set("name", "John"); let keys = map.keys(); *!* +<<<<<<< HEAD // 错误:numbers.push 不是一个函数 +======= +// Error: keys.push is not a function +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb keys.push("more"); */!* ``` diff --git a/1-js/05-data-types/07-map-set-weakmap-weakset/article.md b/1-js/05-data-types/07-map-set-weakmap-weakset/article.md index 00c3a3722a..dc5f9db1d3 100644 --- a/1-js/05-data-types/07-map-set-weakmap-weakset/article.md +++ b/1-js/05-data-types/07-map-set-weakmap-weakset/article.md @@ -47,7 +47,11 @@ alert( map.size ); // 3 ```js run let john = { name: "John" }; +<<<<<<< HEAD // 存下每个用户的来访次数 +======= +// for every user, let's store their visits count +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb let visitsCountMap = new Map(); // john 是 map 的键 @@ -159,6 +163,7 @@ for (let entry of recipeMap) { // 和 recipeMap.entries() 一样 另外,`Map` 有一个内建的 `forEach` 方法,和 `Array` 很像: ```js +// runs the function for each (key, value) pair recipeMap.forEach( (value, key, map) => { alert(`${key}: ${value}`); // cucumber: 500 等等 }); @@ -223,7 +228,11 @@ set.forEach((value, valueAgain, set) => { 注意到这里有个有趣得事情。`forEach` 函数用于 `Set` 时有三个参数:value,然后又一个 value,之后是目标对象。确实,相同值的 value 在参数中出现了两次。 +<<<<<<< HEAD 这是为了兼容 `Map`,它在使用 `forEach` 方法时也包括三个参数。 +======= +That's for compatibility with `Map` where `forEach` has three arguments. Looks a bit strange, for sure. But may help to replace `Map` with `Set` in certain cases with ease, and vice versa. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 适用于 `Map` 的迭代方法 set 也同样支持: @@ -253,9 +262,33 @@ john = null; 通常情况下,当某数据存在于内存中时,对象的属性或者数组的元素或其他的数据结构将被认为是可以获取的并留存于内存。 +<<<<<<< HEAD 在一个正常 `Map` 中,我们将某对象存储为键还是值并不重要。它将会被一直保留在内存中,就算已经没有指向它的引用。 例如: +======= +For instance, if we put an object into an array, then while the array is alive, the object will be alive as well, even if there are no other references to it. + +Like this: + +```js +let john = { name: "John" }; + +let array = [ john ]; + +john = null; // overwrite the reference + +*!* +// john is stored inside the array, so it won't be garbage-collected +// we can get it as array[0] +*/!* +``` + +Or, if we use an object as the key in a regular `Map`, then while the `Map` exists, that object exists as well. It occupies memory and may not be garbage collected. + +For instance: + +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js let john = { name: "John" }; @@ -265,12 +298,19 @@ map.set(john, "..."); john = null; // 覆盖引用 *!* +<<<<<<< HEAD // john 被保存在 map 中 // 我们可以通过 map.keys() 得到它 +======= +// john is stored inside the map, +// we can get it by using map.keys() +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb */!* ``` +`WeakMap/WeakSet` are fundamentally different in this aspect. They do not prevent garbage-collection of key objects. +<<<<<<< HEAD 除了 `WeakMap/WeakSet`。 **`WeakMap/WeakSet` 不会阻止内存移除对象。** @@ -278,6 +318,11 @@ john = null; // 覆盖引用 我们从 `WeakMap` 开始。 它和 `Map` 的第一个区别是它的键必须是对象,不能是基础类型的值: +======= +Let's explain it starting with `WeakMap`. + +The first difference from `Map` is that `WeakMap` keys must be objects, not primitive values: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let weakMap = new WeakMap(); @@ -287,7 +332,12 @@ let obj = {}; weakMap.set(obj, "ok"); // 运行正常(对象作为键) *!* +<<<<<<< HEAD weakMap.set("test", "Whoops"); // 错误,因为“test”是原始类型 +======= +// can't use a string as the key +weakMap.set("test", "Whoops"); // Error, because "test" is not an object +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb */!* ``` @@ -306,21 +356,32 @@ john = null; // 覆盖引用 把它和上面普通 `Map` 的例子对比一下。现在,如果 `john` 仅作为 `WeakMap` 的键 —— 它将会被自动删除。 +<<<<<<< HEAD ...并且 `WeakMap` 并不支持方法 `keys()`,`values()`,`entries()`,我们不能对它进行迭代。所以没有办法获取它的所有键值。 +======= +`WeakMap` does not support iteration and methods `keys()`, `values()`, `entries()`, so there's no way to get all keys or values from it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb `WeakMap` 仅有如下方法: - `weakMap.get(key)` - `weakMap.set(key, value)` -- `weakMap.delete(key, value)` +- `weakMap.delete(key)` - `weakMap.has(key)` +<<<<<<< HEAD 为什么会有这些限制?这是处于一些技术原因。如果一个对象没有任何引用(就像上面代码中的 `john`),那么它将会被自动删除。但是从技术上讲,它没有完全指定**什么时候清理会发生**。 JavaScript 引擎将会决定何时清理。它可能会选择马上清理内存或者等待:当更多需要删除操作发生的时候再删除。所以,技术上说,目前 `WeakMap` 中元素个数并不可知。引擎可能已经清理,也可能没有,也可能只进行了部分的清理。处于这个原因,允许访问 `WeakMap` 整体的方法并不支持。 +======= +Why such a limitation? That's for technical reasons. If an object has lost all other references (like `john` in the code above), then it is to be garbage-collected automatically. But technically it's not exactly specified *when the cleanup happens*. + +The JavaScript engine decides that. It may choose to perform the memory cleanup immediately or to wait and do the cleaning later when more deletions happen. So, technically the current element count of a `WeakMap` is not known. The engine may have cleaned it up or not, or did it partially. For that reason, methods that access `WeakMap` as a whole are not supported. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 现在,我们在哪里需要这样的结构? +<<<<<<< HEAD `WeakMap` 的目的是,我们可以当且仅当该对象存在时,为对象存储一些内容。但我们并不会因为存储了对象的一些内容,就强制对象一直保留在内存中。 ```js @@ -329,12 +390,28 @@ weakMap.put(john, "secret documents"); ``` 当我们对对象有个主存储区,并且需要保存仅当对象活跃时候才相关的附加信息时,这一点就很有用了。 +======= +The idea of `WeakMap` is that we can store something for an object that should exist only while the object exists. But we do not force the object to live by the mere fact that we store something for it. + +```js +weakMap.set(john, "secret documents"); +// if john dies, secret documents will be destroyed automatically +``` + +That's useful for situations when we have a main storage for the objects somewhere and need to keep additional information, that is only relevant while the object lives. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 让我们看一个例子。 +<<<<<<< HEAD 例如,我们写了一段代码来保存每个用户的访问次数。信息保存在一个 map 中:用户是键,访问次数是值。当用户离开了,我们也就不再需要保存他的访问次数了。 有一个方法可以追踪离开的游客并手动清理记录: +======= +For instance, we have code that keeps a visit count for each user. The information is stored in a map: a user is the key and the visit count is the value. When a user leaves, we don't want to store their visit count anymore. + +One way would be to keep track of users, and when they leave -- clean up the map manually: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let john = { name: "John" }; @@ -352,7 +429,11 @@ john = null; // 但是记录依旧在 map 中,我们需要清理它! */!* alert( visitsCountMap.size ); // 1 +<<<<<<< HEAD // 它还在内存中,因为 Map 将它作为键 +======= +// and john is also in the memory, because Map uses it as the key +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` 另一个方法是使用 `WeakMap`: @@ -371,17 +452,33 @@ john = null; // 所以这个对象会自动的从内存和 visitsCountMap 中删除 ``` +<<<<<<< HEAD 使用普通的 `Map`,用户离开后的数据清理是一很乏味的任务:我们不仅要从主存储区移除用户(可能是变量或者数组),还需要将附加的数据存储例如 `visitsCountMap` 也清除。当用户在代码的一个位置进行管理,而附加结构位于另一个位置,并且没有获取有关清除的信息这样的复杂的情况下,这种操作就很笨重。 `WeakMap` 能让事情简单很多,因为它能够自动清理。它里面诸如上面的例子中来访次数这样的信息,当且仅当对象键存在的时候才存在。 +======= +With a regular `Map`, cleaning up after a user has left becomes a tedious task: we not only need to remove the user from its main storage (be it a variable or an array), but also need to clean up the additional stores like `visitsCountMap`. And it can become cumbersome in more complex cases when users are managed in one place of the code and the additional structure is in another place and is getting no information about removals. + +```summary +`WeakMap` can make things simpler, because it is cleaned up automatically. The information in it like visits count in the example above lives only while the key object exists. +``` +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb `WeakSet` 的行为类似: +<<<<<<< HEAD - 它和 `Set` 类似,但是我们仅能将对象添加进 `WeakSet`(不可以是基础类型) - 仅当对象存在其他位置的引用时它才存在于 set 中。 - 就像 `Set` 一样,它支持 `add`,`has` 和 `delete`,不支持 `size`,`keys()` 也不支持迭代器。 例如,我们可以用它来追踪一个项目是否被检查过: +======= +- It is analogous to `Set`, but we may only add objects to `WeakSet` (not primitives). +- An object exists in the set while it is reachable from somewhere else. +- Like `Set`, it supports `add`, `has` and `delete`, but not `size`, `keys()` and no iterations. + +For instance, we can use it to keep track of whether a message is read: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js let messages = [ @@ -393,22 +490,45 @@ let messages = [ // 用数组的元素来填充(3 个元素) let unreadSet = new WeakSet(messages); +<<<<<<< HEAD // 我们可以使用 unreadSet 来看一个 message 是否未读 alert(unreadSet.has(messages[1])); // true // 读过之后就将它从 set 中移除 +======= +// use unreadSet to see whether a message is unread +alert(unreadSet.has(messages[1])); // true + +// remove it from the set after reading +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb unreadSet.delete(messages[1]); // true // 当我们对消息列表做 shift 操作,set 就会自动清理 messages.shift(); +<<<<<<< HEAD // 不需要清理 unreadSet,它现在还有两个元素 // 可惜并没有方法可以获取元素数目,所以无法显示出来 ``` `WeakMap` 和 `WeakSet` 最显著的限制就是没有迭代器,也不能获取当前所有内容。这可能会有点不方便,但是实际上并不妨碍 `WeakMap/WeakSet` 的主要任务 —— 作为对象的附加存储,该对象在其他位置被保存或管理。 +======= + +*!* +// no need to clean unreadSet, it now has 2 items +*/!* +// (though technically we don't know for sure when the JS engine clears it) +``` + +The most notable limitation of `WeakMap` and `WeakSet` is the absence of iterations, and inability to get all current content. That may appear inconvenient, but does not prevent `WeakMap/WeakSet` from doing their main job -- be an "additional" storage of data for objects which are stored/managed at another place. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 总结 +<<<<<<< HEAD - `Map` —— 是一个键值对集合 +======= +Regular collections: +- `Map` -- is a collection of keyed values. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 和普通 `Object` 的区别: @@ -421,7 +541,13 @@ messages.shift(); - 和 array 不同,set 不允许元素重新排序。 - 保持插入的顺序。 +<<<<<<< HEAD - `WeakMap` —— `Map` 的一个变体,仅允许对象作为键,并且当对象由于其他原因不可引用的时候将其删除。 +======= +Collections that allow garbage-collection: + +- `WeakMap` -- a variant of `Map` that allows only objects as keys and removes them once they become inaccessible by other means. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - 它不支持整体的操作:没有 `size` 属性,没有 `clear()` 方法,没有迭代器。 @@ -429,4 +555,8 @@ messages.shift(); - 同样不支持 `size/clear()` 和迭代器。 +<<<<<<< HEAD `WeakMap` 和 `WeakSet` 被用作主要对象存储的次要数据结构补充。一旦对象从存储移除,那么存在于 `WeakMap/WeakSet` 的数据将会被自动清除。 +======= +`WeakMap` and `WeakSet` are used as "secondary" data structures in addition to the "main" object storage. Once the object is removed from the main storage, if it is only found in the `WeakMap/WeakSet`, it will be cleaned up automatically. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/05-data-types/08-keys-values-entries/01-sum-salaries/solution.md b/1-js/05-data-types/08-keys-values-entries/01-sum-salaries/solution.md index e69de29bb2..27a7b418ab 100644 --- a/1-js/05-data-types/08-keys-values-entries/01-sum-salaries/solution.md +++ b/1-js/05-data-types/08-keys-values-entries/01-sum-salaries/solution.md @@ -0,0 +1,29 @@ +```js run demo +function sumSalaries(salaries) { + + let sum = 0; + for (let salary of Object.values(salaries)) { + sum += salary; + } + + return sum; // 650 +} + +let salaries = { + "John": 100, + "Pete": 300, + "Mary": 250 +}; + +alert( sumSalaries(salaries) ); // 650 +``` +Or, optionally, we could also get the sum using `Object.values` and `reduce`: + +```js +// reduce loops over array of salaries, +// adding them up +// and returns the result +function sumSalaries(salaries) { + return Object.values(salaries).reduce((a, b) => a + b, 0) // 650 +} +``` diff --git a/1-js/05-data-types/08-keys-values-entries/article.md b/1-js/05-data-types/08-keys-values-entries/article.md index 3ba7685ac0..ece5862c49 100644 --- a/1-js/05-data-types/08-keys-values-entries/article.md +++ b/1-js/05-data-types/08-keys-values-entries/article.md @@ -45,7 +45,7 @@ let user = { }; ``` -- `Object.keys(user) = [name, age]` +- `Object.keys(user) = ["name", "age"]` - `Object.values(user) = ["John", 30]` - `Object.entries(user) = [ ["name","John"], ["age",30] ]` diff --git a/1-js/05-data-types/09-destructuring-assignment/1-destruct-user/task.md b/1-js/05-data-types/09-destructuring-assignment/1-destruct-user/task.md index f3e3704324..b2213323ac 100644 --- a/1-js/05-data-types/09-destructuring-assignment/1-destruct-user/task.md +++ b/1-js/05-data-types/09-destructuring-assignment/1-destruct-user/task.md @@ -2,9 +2,9 @@ importance: 5 --- -# 解构赋值 +# Destructuring assignment -有以下对象: +We have an object: ```js let user = { @@ -13,18 +13,18 @@ let user = { }; ``` -写一个解构赋值语句使得: +Write the destructuring assignment that reads: -- `name` 属性赋值给变量 `name`。 -- `years` 属性赋值给 `age`。 -- `isAdmin` 属性赋值给变量 `isAdmin`(如果属性缺失则赋值为 false)。 +- `name` property into the variable `name`. +- `years` property into the variable `age`. +- `isAdmin` property into the variable `isAdmin` (false if absent) -赋值语句后的值必须是: +The values after the assignment should be: ```js let user = { name: "John", years: 30 }; -// 等号左侧是你的代码 +// your code to the left side: // ... = user alert( name ); // John diff --git a/1-js/05-data-types/09-destructuring-assignment/6-max-salary/_js.view/solution.js b/1-js/05-data-types/09-destructuring-assignment/6-max-salary/_js.view/solution.js index d95cf1b1e5..f4bd5c761c 100644 --- a/1-js/05-data-types/09-destructuring-assignment/6-max-salary/_js.view/solution.js +++ b/1-js/05-data-types/09-destructuring-assignment/6-max-salary/_js.view/solution.js @@ -3,7 +3,7 @@ function topSalary(salaries) { let max = 0; let maxName = null; - for(let [name, salary] of Object.entries(salaries)) { + for(const [name, salary] of Object.entries(salaries)) { if (max < salary) { max = salary; maxName = name; diff --git a/1-js/05-data-types/09-destructuring-assignment/article.md b/1-js/05-data-types/09-destructuring-assignment/article.md index 5e7e7b42b6..15eea1cb57 100644 --- a/1-js/05-data-types/09-destructuring-assignment/article.md +++ b/1-js/05-data-types/09-destructuring-assignment/article.md @@ -31,8 +31,13 @@ alert(surname); // Kantor let [firstName, surname] = "Ilya Kantor".split(' '); ``` +<<<<<<< HEAD ````smart header="\"解构\"并不意味着\"破坏\"" 这种语法叫做“解构赋值”,因为它通过将结构中的各元素复制到变量中来达到“解构”的目的。但数组本身是没有被修改的。 +======= +````smart header="\"Destructuring\" does not mean \"destructive\"." +It's called "destructuring assignment," because it "destructurizes" by copying items into variables. But the array itself is not modified. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 也就是以下代码的更精简写法而已: ```js @@ -42,6 +47,7 @@ let surname = arr[1]; ``` ```` +<<<<<<< HEAD ````smart header="忽略第一个元素" 数组中不想要的元素也可以通过添加额外的逗号来把它丢弃: @@ -49,12 +55,25 @@ let surname = arr[1]; *!* // 不需要第一个和第二个元素 let [, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; +======= +````smart header="Ignore elements using commas" +Unwanted elements of the array can also be thrown away via an extra comma: + +```js run +*!* +// second element is not needed +let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb */!* alert( title ); // Consul ``` +<<<<<<< HEAD 在以上的代码中,数组的第一个和第二个元素被跳过,第三个元素被赋值给了 `title` 变量,剩下的元素也被跳过了。 +======= +In the code above, the second element of the array is skipped, the third one is assigned to `title`, and the rest of the array is also skipped. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```` ````smart header="用于等号右侧的任何可迭代对象" @@ -128,6 +147,7 @@ alert(name1); // Julius alert(name2); // Caesar *!* +// Note that type of `rest` is Array. alert(rest[0]); // Consul alert(rest[1]); // of the Roman Republic alert(rest.length); // 2 @@ -146,6 +166,7 @@ let [firstName, surname] = []; */!* alert(firstName); // undefined +alert(surname); // undefined ``` 如果我们想要提供一个“默认值”给未赋值的变量,我们可以使用 `=` 来提供: @@ -335,7 +356,11 @@ let title, width, height; } ``` +<<<<<<< HEAD 为了告诉 JavaScript 这不是一个代码块,我们可以把整个赋值表达式用括号 `(...)` 包起来: +======= +To show JavaScript that it's not a code block, we can wrap the whole assignment in parentheses `(...)`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let title, width, height; @@ -383,6 +408,8 @@ alert(item2); // Donut 除了 `extra` 属性的整个 `options` 对象都能被赋值给对应的变量。 +Note that `size` and `items` itself is not destructured. + ![](destructuring-complex.png) 最终,我们得到了 `width`、`height`、`item1`、`item2` 和具有默认值的 `title` 变量。 @@ -407,7 +434,11 @@ function showMenu(title = "Untitled", width = 200, height = 100, items = []) { } ``` +<<<<<<< HEAD 现实情况下的问题就是你怎么记得住这么多参数的顺序,通常集成开发环境工具(IDE)会尽力帮助我们,特别是当代码有良好的文档注释的时候,但… 另一个问题就是当大部分的参数采用默认值就好的情况下,怎么调用这个函数。 +======= +In real-life, the problem is how to remember the order of arguments. Usually IDEs try to help us, especially if the code is well-documented, but still... Another problem is how to call a function when most parameters are ok by default. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 难道像这样? @@ -503,7 +534,11 @@ showMenu(); // Menu 100 200 let {prop : varName = default, ...} = object ``` +<<<<<<< HEAD 这表示属性 `prop` 会被赋值给变量 `varName`,如果没有这个属性的话,就会使用 `default` 的值。 +======= + This means that property `prop` should go into the variable `varName` and, if no such property exists, then the `default` value should be used. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - 解构数组的语法: @@ -511,6 +546,10 @@ showMenu(); // Menu 100 200 let [item1 = default, item2, ...rest] = array ``` +<<<<<<< HEAD 数组的第一个元素赋值给 `item1`,第二个元素赋值给 `item2`,剩下的所有组成另一个数组 `rest`。 +======= + The first item goes to `item1`; the second goes into `item2`, all the rest makes the array `rest`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - 更多复杂的案例情况下,等号左侧必须和等号右侧有相同的结构。 diff --git a/1-js/05-data-types/09-destructuring-assignment/destructuring-complex.png b/1-js/05-data-types/09-destructuring-assignment/destructuring-complex.png index 50c4ffc93a..c46bf6e45c 100644 Binary files a/1-js/05-data-types/09-destructuring-assignment/destructuring-complex.png and b/1-js/05-data-types/09-destructuring-assignment/destructuring-complex.png differ diff --git a/1-js/05-data-types/09-destructuring-assignment/destructuring-complex@2x.png b/1-js/05-data-types/09-destructuring-assignment/destructuring-complex@2x.png index bb908281df..26032a3d62 100644 Binary files a/1-js/05-data-types/09-destructuring-assignment/destructuring-complex@2x.png and b/1-js/05-data-types/09-destructuring-assignment/destructuring-complex@2x.png differ diff --git a/1-js/05-data-types/10-date/2-get-week-day/solution.md b/1-js/05-data-types/10-date/2-get-week-day/solution.md index 3ffff7c6e7..8c76ef6384 100644 --- a/1-js/05-data-types/10-date/2-get-week-day/solution.md +++ b/1-js/05-data-types/10-date/2-get-week-day/solution.md @@ -2,7 +2,7 @@ 我们创建一个星期数组,这样可以通过它的序号得到名称: -```js run +```js run demo function getWeekDay(date) { let days = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']; diff --git a/1-js/05-data-types/10-date/3-weekday/solution.md b/1-js/05-data-types/10-date/3-weekday/solution.md index 58f81bb35d..6b1e67d005 100644 --- a/1-js/05-data-types/10-date/3-weekday/solution.md +++ b/1-js/05-data-types/10-date/3-weekday/solution.md @@ -1,3 +1,4 @@ +<<<<<<< HEAD ```js run function getLocalDay(date) { @@ -12,3 +13,5 @@ function getLocalDay(date) { alert( getLocalDay(new Date(2012, 0, 3)) ); // 2 ``` +======= +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/05-data-types/10-date/3-weekday/task.md b/1-js/05-data-types/10-date/3-weekday/task.md index 353a34ba1e..1c043ceb4e 100644 --- a/1-js/05-data-types/10-date/3-weekday/task.md +++ b/1-js/05-data-types/10-date/3-weekday/task.md @@ -4,7 +4,11 @@ importance: 5 # 欧洲的星期表示方法 +<<<<<<< HEAD 欧洲国家的星期计算是从星期一(数字 1)开始,然后星期二(数字 2),直到星期日(数字 7)。写一个函数 `getLocalDay(date)`,返回日期的欧洲式星期数。 +======= +European countries have days of week starting with Monday (number 1), then Tuesday (number 2) and till Sunday (number 7). Write a function `getLocalDay(date)` that returns the "European" day of week for `date`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js no-beautify let date = new Date(2012, 0, 3); // 3 Jan 2012 diff --git a/1-js/05-data-types/10-date/4-get-date-ago/solution.md b/1-js/05-data-types/10-date/4-get-date-ago/solution.md index 08f5d35784..b5afa2980a 100644 --- a/1-js/05-data-types/10-date/4-get-date-ago/solution.md +++ b/1-js/05-data-types/10-date/4-get-date-ago/solution.md @@ -11,7 +11,7 @@ function getDateAgo(date, days) { 要实现这一点,我们可以复制这个日期,就像这样: -```js run +```js run demo function getDateAgo(date, days) { let dateCopy = new Date(date); diff --git a/1-js/05-data-types/10-date/5-last-day-of-month/solution.md b/1-js/05-data-types/10-date/5-last-day-of-month/solution.md index 65f61c5bae..4f642536e1 100644 --- a/1-js/05-data-types/10-date/5-last-day-of-month/solution.md +++ b/1-js/05-data-types/10-date/5-last-day-of-month/solution.md @@ -1,5 +1,5 @@ Let's create a date using the next month, but pass zero as the day: -```js run +```js run demo function getLastDayOfMonth(year, month) { let date = new Date(year, month + 1, 0); return date.getDate(); diff --git a/1-js/05-data-types/10-date/6-get-seconds-today/solution.md b/1-js/05-data-types/10-date/6-get-seconds-today/solution.md index 2af9ae5802..583a1f4976 100644 --- a/1-js/05-data-types/10-date/6-get-seconds-today/solution.md +++ b/1-js/05-data-types/10-date/6-get-seconds-today/solution.md @@ -22,5 +22,5 @@ alert( getSecondsToday() ); function getSecondsToday() { let d = new Date(); return d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds(); -}; +} ``` diff --git a/1-js/05-data-types/10-date/8-format-date-relative/solution.md b/1-js/05-data-types/10-date/8-format-date-relative/solution.md index 124c143190..34dc3f4092 100644 --- a/1-js/05-data-types/10-date/8-format-date-relative/solution.md +++ b/1-js/05-data-types/10-date/8-format-date-relative/solution.md @@ -1,6 +1,6 @@ 为获取 `date` 距离当前时间的间隔 —— 我们将两个日期相减。 -```js run +```js run demo function formatDate(date) {  let diff = new Date() - date; // 差值用毫秒表示 @@ -57,12 +57,18 @@ function formatDate(date) { let diffSec = Math.round(diffMs / 1000); let diffMin = diffSec / 60; let diffHour = diffMin / 60; +<<<<<<< HEAD  // 格式化  year = year.toString().slice(-2); +======= + + // formatting + year = year.toString().slice(-2); +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb month = month < 10 ? '0' + month : month; dayOfMonth = dayOfMonth < 10 ? '0' + dayOfMonth : dayOfMonth; - + if (diffSec < 1) { return 'right now'; } else if (diffMin < 1) { diff --git a/1-js/05-data-types/10-date/article.md b/1-js/05-data-types/10-date/article.md index 8fed5dfb9d..3dbf9b98a2 100644 --- a/1-js/05-data-types/10-date/article.md +++ b/1-js/05-data-types/10-date/article.md @@ -2,7 +2,11 @@ 让我一起探讨一个新的内置对象:[日期](mdn:js/Date)。该对象存储日期、时间以及提供管理它们的方法。 +<<<<<<< HEAD 举个例子,我们可以使用它来存储创建、修改事件的时间,或者用来度量时间开销,再或者用来打印当前时间。 +======= +For instance, we can use it to store creation/modification times, to measure time, or just to print out the current date. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## 创建 @@ -39,7 +43,13 @@ ```js run let date = new Date("2017-01-26"); - alert(date); // Thu Jan 26 2017 ... + alert(date); + // The time portion of the date is assumed to be midnight GMT and + // is adjusted according to the timezone the code is run in + // So the result could be + // Thu Jan 26 2017 11:00:00 GMT+1100 (Australian Eastern Daylight Time) + // or + // Wed Jan 25 2017 16:00:00 GMT-0800 (Pacific Standard Time) ``` `new Date(year, month, date, hours, minutes, seconds, ms)` @@ -108,7 +118,11 @@ alert( date.getHours() ); alert( date.getUTCHours() ); ``` +<<<<<<< HEAD 在以上给出的方法中,有两个与众不同的,它们没有 UTC 版本: +======= +Besides the given methods, there are two special ones that do not have a UTC-variant: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb [getTime()](mdn:js/Date/getTime) : 返回日期的时间戳 —— 从 1970-1-1 00:00:00 UTC+0 开始的毫秒数。 @@ -418,4 +432,8 @@ alert(`Loading started ${performance.now()}ms ago`); // 小数点后超过 3 位是错误,只有前三位是正确的 ``` +<<<<<<< HEAD Node.JS 拥有 `microtime` 模块以及其他方法。从技术上来说,任何设备和环境都允许获取更精确的数值,不只是 `Date` 对象。 +======= +Node.js has `microtime` module and other ways. Technically, any device and environment allows to get more precision, it's just not in `Date`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/05-data-types/11-json/article.md b/1-js/05-data-types/11-json/article.md index e7127b9c0e..adfa1f7fe0 100644 --- a/1-js/05-data-types/11-json/article.md +++ b/1-js/05-data-types/11-json/article.md @@ -66,10 +66,17 @@ alert(json); 方法 `JSON.stringify(student)` 接受对象并将其转换为一个字符串。 +<<<<<<< HEAD 得到的 `json` 字符串是一个被称为 **JSON 编码**或者**序列化**或者**字符串化**或者**编组**的对象。我们准备好通过网线传输或存储。 请注意,JSON 编码的对象与对象字面量有几个重要的区别: +======= +The resulting `json` string is called a *JSON-encoded* or *serialized* or *stringified* or *marshalled* object. We are ready to send it over the wire or put into a plain data store. + + +Please note that a JSON-encoded object has several important differences from the object literal: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - 字符串使用双引号。JSON 中没有单引号或反引号。所以 `'John'` 转成 `"John"`。 - 对象属性名称也是双引号的。这是强制性的。所以 `age:30` 转成 `"age":30`。 @@ -131,7 +138,7 @@ let meetup = { title: "Conference", *!* room: { - number: 123, + number: 23, participants: ["john", "ann"] } */!* @@ -190,7 +197,11 @@ replacer space : 文本添加缩进、空格和换行符 +<<<<<<< HEAD 大部分情况,`JSON.stringify` 仅与第一个参数一起使用。但是,如果我们需要微调替换过程,比如过滤掉循环引用,我们可以使用 `JSON.stringify` 的第二个参数。 +======= +Most of the time, `JSON.stringify` is used with the first argument only. But if we need to fine-tune the replacement process, like to filter out circular references, we can use the second argument of `JSON.stringify`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 如果我们传递一组属性给它,只有这一组属性会被编码 @@ -244,7 +255,11 @@ alert( JSON.stringify(meetup, *!*['title', 'participants', 'place', 'name', 'num 幸运的是,我们也可以使用一个函数作为 `replacer`。 +<<<<<<< HEAD 该函数将调用每个 `(key,value)` 对,并且返回 “replacement” 值,它将被用来代替原来的值。 +======= +The function will be called for every `(key, value)` pair and should return the "replaced" value, which will be used instead of the original one. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 在我们的例子中,除 `occupiedBy` 外我们都可以按照原样返回 `value`。要忽略 `occupiedBy`,下面的代码返回 `undefined`: @@ -281,7 +296,11 @@ number: 23 请注意 `replacer` 函数获取包括嵌套对象和数组项的每个键/值对。它被递归地应用。`replacer` 里面 `this` 的值是包含当前属性的对象。 +<<<<<<< HEAD 第一个调用很特别。它是使用特殊的“包装对象”制作的: `{"": meetup}`。换句话说,第一个 `(key,value)` 对是空键,并且该值是整个目标对象。这就是为什么上面的例子中第一行是 `":[object Object]"` 的原因。 +======= +The first call is special. It is made using a special "wrapper object": `{"": meetup}`. In other words, the first `(key, value)` pair has an empty key, and the value is the target object as a whole. That's why the first line is `":[object Object]"` in the example above. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 基于这个理念为 `replacer` 提供了更强大的功能:如有必要,它有机会分析和替换/跳过整个对象。 @@ -332,7 +351,11 @@ alert(JSON.stringify(user, null, 2)); ## 定制 "toJSON" +<<<<<<< HEAD 像 `toString` 进行字符串转换,对象可以提供 `toJSON` 方法来进行 JSON 转换。如果可用,`JSON.stringify` 会自动调用它。 +======= +Like `toString` for string conversion, an object may provide method `toJSON` for to-JSON conversion. `JSON.stringify` automatically calls it if available. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: @@ -361,7 +384,11 @@ alert( JSON.stringify(meetup) ); 在这里我们可以看到 `date` `(1)` 变成了一个字符串。这是因为所有日期都有一个内置的 `toJSON` 方法来返回这种类型的字符串。 +<<<<<<< HEAD 现在让我们为对象 `room` 添加一个自定义的 `toJSON`: +======= +Now let's add a custom `toJSON` for our object `room` `(2)`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let room = { @@ -409,7 +436,11 @@ str : JSON 字符串解析。 reviver +<<<<<<< HEAD : 将为每个 `(key,value)` 对调用的可选函数(键,值)进行转换。 +======= +: Optional function(key,value) that will be called for each `(key, value)` pair and can transform the value. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 例如: @@ -520,8 +551,16 @@ alert( schedule.meetups[1].date.getDate() ); // works! ## 总结 +<<<<<<< HEAD - JSON 是一种数据格式,对于大多数编程语言都有自己的独立标准和库。 - JSON 支持 objects,arrays,strings,numbers,booleans 和 `null`。 - JavaScript 提供序列化成 JSON 的方法 [JSON.stringify](mdn:js/JSON/stringify)和解析 JSON 方法 [JSON.parse](mdn:js/JSON/parse)。 - 这两种方法都支持用于智能读/写的转换函数。 - 如果一个对象具有 `toJSON`,那么它可被 `JSON.stringify` 调用。 +======= +- JSON is a data format that has its own independent standard and libraries for most programming languages. +- JSON supports plain objects, arrays, strings, numbers, booleans, and `null`. +- JavaScript provides methods [JSON.stringify](mdn:js/JSON/stringify) to serialize into JSON and [JSON.parse](mdn:js/JSON/parse) to read from JSON. +- Both methods support transformer functions for smart reading/writing. +- If an object has `toJSON`, then it is called by `JSON.stringify`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/1-js/05-data-types/11-json/json-meetup.png b/1-js/05-data-types/11-json/json-meetup.png index 0a26e0a679..2686664249 100644 Binary files a/1-js/05-data-types/11-json/json-meetup.png and b/1-js/05-data-types/11-json/json-meetup.png differ diff --git a/1-js/05-data-types/11-json/json-meetup@2x.png b/1-js/05-data-types/11-json/json-meetup@2x.png index b5f6a40129..a8f2cd6094 100644 Binary files a/1-js/05-data-types/11-json/json-meetup@2x.png and b/1-js/05-data-types/11-json/json-meetup@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.png b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.png index c45418ff4d..d0d37e35e7 100644 Binary files a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.png and b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.png differ diff --git a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree@2x.png b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree@2x.png index 6fc39ae130..3a937c64cd 100644 Binary files a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree@2x.png and b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/article.md b/1-js/06-advanced-functions/01-recursion/article.md index bcc4af5f7e..62ce4dfa52 100644 --- a/1-js/06-advanced-functions/01-recursion/article.md +++ b/1-js/06-advanced-functions/01-recursion/article.md @@ -318,9 +318,15 @@ let company = { 或者说,一家公司有很多部门。 +<<<<<<< HEAD - 一个部门有员工列表,比如,`销售`部有 2 名员工:John 和 Alice。 - 或者一个部门可能划分为几个子部门,比如`开发`有两个分支:`网站`和`内部`,它们都有自己的员工。 - 当一个子部门增长时,它可能会划分为子部门(或者团队)。 +======= +- A department may have an array of staff. For instance, `sales` department has 2 employees: John and Alice. +- Or a department may split into subdepartments, like `development` has two branches: `sites` and `internals`. Each of them has their own staff. +- It is also possible that when a subdepartment grows, it divides into subsubdepartments (or teams). +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 比如,`网站`部门在未来可能会分为`网站 A` 和 `网站 B`。它们可能还会再分,没有图示,脑补一下吧。 diff --git a/1-js/06-advanced-functions/01-recursion/linked-list-0.png b/1-js/06-advanced-functions/01-recursion/linked-list-0.png index 000a80da8c..c694e7021f 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list-0.png and b/1-js/06-advanced-functions/01-recursion/linked-list-0.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list-0@2x.png b/1-js/06-advanced-functions/01-recursion/linked-list-0@2x.png index 5a2368694f..1fe50ace43 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list-0@2x.png and b/1-js/06-advanced-functions/01-recursion/linked-list-0@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list-remove-1.png b/1-js/06-advanced-functions/01-recursion/linked-list-remove-1.png index 477989ad8a..b4c89ecdf0 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list-remove-1.png and b/1-js/06-advanced-functions/01-recursion/linked-list-remove-1.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list-remove-1@2x.png b/1-js/06-advanced-functions/01-recursion/linked-list-remove-1@2x.png index 41de7661c4..6b5b95a0fa 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list-remove-1@2x.png and b/1-js/06-advanced-functions/01-recursion/linked-list-remove-1@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list-split.png b/1-js/06-advanced-functions/01-recursion/linked-list-split.png index ac22034905..310a360665 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list-split.png and b/1-js/06-advanced-functions/01-recursion/linked-list-split.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list-split@2x.png b/1-js/06-advanced-functions/01-recursion/linked-list-split@2x.png index 201c66f150..2de39ca4f5 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list-split@2x.png and b/1-js/06-advanced-functions/01-recursion/linked-list-split@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list.png b/1-js/06-advanced-functions/01-recursion/linked-list.png index 64b6fb2b72..80fabffc6a 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list.png and b/1-js/06-advanced-functions/01-recursion/linked-list.png differ diff --git a/1-js/06-advanced-functions/01-recursion/linked-list@2x.png b/1-js/06-advanced-functions/01-recursion/linked-list@2x.png index c28fa82594..1e6dc14839 100644 Binary files a/1-js/06-advanced-functions/01-recursion/linked-list@2x.png and b/1-js/06-advanced-functions/01-recursion/linked-list@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/recursion-pow.png b/1-js/06-advanced-functions/01-recursion/recursion-pow.png index 30577f89c2..354f012714 100644 Binary files a/1-js/06-advanced-functions/01-recursion/recursion-pow.png and b/1-js/06-advanced-functions/01-recursion/recursion-pow.png differ diff --git a/1-js/06-advanced-functions/01-recursion/recursion-pow@2x.png b/1-js/06-advanced-functions/01-recursion/recursion-pow@2x.png index c19973420d..22ae944108 100644 Binary files a/1-js/06-advanced-functions/01-recursion/recursion-pow@2x.png and b/1-js/06-advanced-functions/01-recursion/recursion-pow@2x.png differ diff --git a/1-js/06-advanced-functions/01-recursion/recursive-salaries.png b/1-js/06-advanced-functions/01-recursion/recursive-salaries.png index 2b90154090..b40783a9b4 100644 Binary files a/1-js/06-advanced-functions/01-recursion/recursive-salaries.png and b/1-js/06-advanced-functions/01-recursion/recursive-salaries.png differ diff --git a/1-js/06-advanced-functions/01-recursion/recursive-salaries@2x.png b/1-js/06-advanced-functions/01-recursion/recursive-salaries@2x.png index 261ab144ed..e37597128f 100644 Binary files a/1-js/06-advanced-functions/01-recursion/recursive-salaries@2x.png and b/1-js/06-advanced-functions/01-recursion/recursive-salaries@2x.png differ diff --git a/1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md b/1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md index a5c7457f0d..37b93d09d8 100644 --- a/1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md +++ b/1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md @@ -61,8 +61,13 @@ function showName(firstName, lastName, ...titles) { showName("Julius", "Caesar", "Consul", "Imperator"); ``` +<<<<<<< HEAD ````warn header="Rest 参数必须放到参数列表的末尾" Rest 参数会收集参数列表中剩余的所有参数,所以下面这种用法是行不通的: +======= +````warn header="The rest parameters must be at the end" +The rest parameters gather all remaining arguments, so the following does not make sense and causes an error: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js function f(arg1, ...rest, arg2) { // ...rest 后面还有个 arg2?! @@ -119,7 +124,13 @@ function f() { f(1); // 1 ``` +<<<<<<< HEAD 我们已经知道箭头函数自身是没有 `this` 的,现在我们更进一步还知道它缺少 `arguments` 这个特殊的对象。 +======= +```` + +As we remember, arrow functions don't have their own `this`. Now we know they don't have the special `arguments` object either. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ## Spread 操作符(展开操作符) [#spread-operator] @@ -135,7 +146,11 @@ alert( Math.max(3, 5, 1) ); // 5 假如我们已有数组 `[3, 5, 1]`,我们该如何用它调用 `Math.max` 呢? +<<<<<<< HEAD 直接把数组“原样”传入是不会奏效的,因为 `Math.max` 期待你传入一系列的数值型参数,而不是单一的数组: +======= +Passing it "as is" won't work, because `Math.max` expects a list of numeric arguments, not a single array: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js run let arr = [3, 5, 1]; @@ -145,7 +160,11 @@ alert( Math.max(arr) ); // NaN */!* ``` +<<<<<<< HEAD 毫无疑问我们不可能手动地去一一设置参数 `Math.max(arg[0], arg[1], arg[2])`,因为我们不确定需要设置多少个参数。待代码最终执行时,这个参数数组可能很大,也可能啥也没用。这样手动设置实为下策。 +======= +And surely we can't manually list items in the code `Math.max(arr[0], arr[1], arr[2])`, because we may be unsure how many there are. As our script executes, there could be a lot, or there could be none. And that would get ugly. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb **Spread 操作符** 来拯救你了!它看起来和 Rest 参数操作符很像,都表示为 `...`,但是二者完全做了相反的事。 diff --git a/1-js/06-advanced-functions/03-closure/4-closure-sum/solution.md b/1-js/06-advanced-functions/03-closure/4-closure-sum/solution.md index 87ac80b37d..cd18e62bdc 100644 --- a/1-js/06-advanced-functions/03-closure/4-closure-sum/solution.md +++ b/1-js/06-advanced-functions/03-closure/4-closure-sum/solution.md @@ -1,4 +1,8 @@ +<<<<<<< HEAD 为了使第二个括号有效,第一个(括号)必须返回一个函数。 +======= +For the second parentheses to work, the first ones must return a function. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 就像这样: diff --git a/1-js/06-advanced-functions/03-closure/4-closure-sum/task.md b/1-js/06-advanced-functions/03-closure/4-closure-sum/task.md index 0f9c9a3c8a..bd8525ede7 100644 --- a/1-js/06-advanced-functions/03-closure/4-closure-sum/task.md +++ b/1-js/06-advanced-functions/03-closure/4-closure-sum/task.md @@ -6,7 +6,11 @@ importance: 4 编写一个像 `sum(a)(b) = a+b` 这样工作的 `sum` 函数。 +<<<<<<< HEAD 是的,就是这种通过双括号的方式(并不是错误)。 +======= +Yes, exactly this way, using double parentheses (not a mistype). +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 举个例子: diff --git a/1-js/06-advanced-functions/03-closure/6-filter-through-function/solution.md b/1-js/06-advanced-functions/03-closure/6-filter-through-function/solution.md index 020d44ac62..c191c1ad96 100644 --- a/1-js/06-advanced-functions/03-closure/6-filter-through-function/solution.md +++ b/1-js/06-advanced-functions/03-closure/6-filter-through-function/solution.md @@ -14,7 +14,7 @@ alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6 # inArray 筛选器 -```js run +```js run demo function inArray(arr) { return function(x) { return arr.includes(x); diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.png b/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.png index d51e8167f8..8e39564ffe 100644 Binary files a/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.png and b/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.png differ diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy@2x.png b/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy@2x.png index e70edbd6d4..39cc13a422 100644 Binary files a/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy@2x.png and b/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy@2x.png differ diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/solution.md b/1-js/06-advanced-functions/03-closure/8-make-army/solution.md index ff1370eff6..065450c953 100644 --- a/1-js/06-advanced-functions/03-closure/8-make-army/solution.md +++ b/1-js/06-advanced-functions/03-closure/8-make-army/solution.md @@ -55,9 +55,13 @@ function makeArmy() { 作为结果,所有的 `shooter` 都是从外部词法环境获得同样一个值最后的 `i=10`。 +<<<<<<< HEAD 修改起来是很简单的: +======= +We can fix it by moving the variable definition into the loop: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb -```js run +```js run demo function makeArmy() { let shooters = []; @@ -80,9 +84,15 @@ army[0](); // 0 army[5](); // 5 ``` +<<<<<<< HEAD 现在正常工作了,因为`for (..) {...}` 内的代码块每次执行都会创建一个新的词法环境,其中具有对应的 `i` 的值。 所以,现在 `i` 值的距离(显示数字的地方更近了。现在它不是在 `makeArmy()` 词法环境中,而是在对应的当前循环迭代的词法环境中。`shooter` 从它创建的位置获得值。 +======= +Now it works correctly, because every time the code block in `for (let i=0...) {...}` is executed, a new Lexical Environment is created for it, with the corresponding variable `i`. + +So, the value of `i` now lives a little bit closer. Not in `makeArmy()` Lexical Environment, but in the Lexical Environment that corresponds the current loop iteration. That's why now it works. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ![](lexenv-makearmy.png) @@ -90,7 +100,6 @@ army[5](); // 5 其他技巧也是可以的,让我们了解一下,以便让我们更好地理解这个问题: - ```js run function makeArmy() { let shooters = []; diff --git a/1-js/06-advanced-functions/03-closure/article.md b/1-js/06-advanced-functions/03-closure/article.md index 3dc10afabf..49e8fee7d5 100644 --- a/1-js/06-advanced-functions/03-closure/article.md +++ b/1-js/06-advanced-functions/03-closure/article.md @@ -1,9 +1,15 @@ # 闭包 +<<<<<<< HEAD JavaScript 是一种非常面向函数的语言。它给我们很大的发挥空间。函数创建后,可以赋值给其他变量或作为参数传递给另一个函数,并在完全不同的位置进行调用。 我们知道函数可以访问其外部变量;这是一个常用的特性。 +======= +JavaScript is a very function-oriented language. It gives us a lot of freedom. A function can be created dynamically, copied to another variable or passed as an argument to another function and called from a totally different place later. + +We know that a function can access variables outside of it, this feature is used quite often. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 但是当外部变量变化时会发生什么呢?函数获得的是最新的值还是创建时的值呢? @@ -63,33 +69,59 @@ JavaScript 是一种非常面向函数的语言。它给我们很大的发挥空 要了解究竟发生了什么,首先我们要来讨论一下『变量』究竟是什么? +<<<<<<< HEAD 在 JavaScript 中,每个运行的函数、代码块或整个程序,都有一个称为**词法环境(Lexical Environment)**的关联对象。 +======= +In JavaScript, every running function, code block `{...}`, and the script as a whole have an internal (hidden) associated object known as the *Lexical Environment*. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 词法环境对象由两部分组成: +<<<<<<< HEAD 1. **环境记录(Environment Record)**—— 一个把所有局部变量作为其属性(包括一些额外信息,比如 `this` 值)的对象。 2. **外部词法环境(outer lexical environment)**的引用 —— 通常是嵌套当前代码(当前花括号之外)之外代码的词法环境。 所以,『变量』只是环境记录这个特殊内部对象的属性。『访问或修改变量』意味着『访问或改变词法环境的一个属性』。 +======= +1. *Environment Record* -- an object that stores all local variables as its properties (and some other information like the value of `this`). +2. A reference to the *outer lexical environment*, the one associated with the outer code. + +**So, a "variable" is just a property of the special internal object, `Environment Record`. "To get or change a variable" means "to get or change a property of that object".** +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 举个例子,这段简单的代码中只有一个词法环境: ![lexical environment](lexical-environment-global.png) +<<<<<<< HEAD 这是一个所谓的与整个程序关联的全局词法环境。在浏览器中,所有的 ` + ``` + +- And, the last minor thing, the top-level value of `this` in a module is `undefined` (why should it be `window` anyway?): +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```html run ``` +<<<<<<< HEAD **使用 ` - - - - diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/index.html b/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/index.html deleted file mode 100644 index fdee13d01b..0000000000 --- a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Console clock - - - - - - - - - - diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2.png b/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2.png deleted file mode 100644 index ad4a409322..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2@2x.png b/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2@2x.png deleted file mode 100644 index 199ed3ee60..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal.png b/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal.png deleted file mode 100644 index 70708c2848..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal@2x.png b/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal@2x.png deleted file mode 100644 index 0db1301815..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png deleted file mode 100644 index e63d7d7844..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png deleted file mode 100644 index 3d1be9ccef..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2.png deleted file mode 100644 index 435ec5f89f..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2@2x.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2@2x.png deleted file mode 100644 index 5731da73a8..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/index.html b/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/index.html deleted file mode 100644 index fdee13d01b..0000000000 --- a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Console clock - - - - - - - - - - diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/clock.js b/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/clock.js deleted file mode 100644 index b1a2625079..0000000000 --- a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/clock.js +++ /dev/null @@ -1,34 +0,0 @@ - - -function Clock({ template }) { - this._template = template; -} - -Clock.prototype._render = function() { - let date = new Date(); - - let hours = date.getHours(); - if (hours < 10) hours = '0' + hours; - - let mins = date.getMinutes(); - if (mins < 10) min = '0' + mins; - - let secs = date.getSeconds(); - if (secs < 10) secs = '0' + secs; - - let output = this._template - .replace('h', hours) - .replace('m', mins) - .replace('s', secs); - - console.log(output); -}; - -Clock.prototype.stop = function() { - clearInterval(this._timer); -}; - -Clock.prototype.start = function() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); -}; diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/index.html b/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/index.html deleted file mode 100644 index fdee13d01b..0000000000 --- a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Console clock - - - - - - - - - - diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/task.md b/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/task.md deleted file mode 100644 index 9e4b0b651a..0000000000 --- a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/task.md +++ /dev/null @@ -1,9 +0,0 @@ -importance: 5 - ---- - -# 重写 class 声明 - -按要求重写所有的 `Clock` 类声明方式,把所有类定义方式从 prototype 变为“现代”的 "class" 语法。 - -P.S. clock 仅在 console 中打印,请打开 console 查看。 diff --git a/1-js/07-object-oriented-programming/09-class/animal-rabbit-extends.png b/1-js/07-object-oriented-programming/09-class/animal-rabbit-extends.png deleted file mode 100644 index 2db88f3663..0000000000 Binary files a/1-js/07-object-oriented-programming/09-class/animal-rabbit-extends.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/09-class/animal-rabbit-extends@2x.png b/1-js/07-object-oriented-programming/09-class/animal-rabbit-extends@2x.png deleted file mode 100644 index 9539fe9ec6..0000000000 Binary files a/1-js/07-object-oriented-programming/09-class/animal-rabbit-extends@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/09-class/class-user.png b/1-js/07-object-oriented-programming/09-class/class-user.png deleted file mode 100644 index 5579e6bbdf..0000000000 Binary files a/1-js/07-object-oriented-programming/09-class/class-user.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/09-class/class-user@2x.png b/1-js/07-object-oriented-programming/09-class/class-user@2x.png deleted file mode 100644 index 5a85e6589d..0000000000 Binary files a/1-js/07-object-oriented-programming/09-class/class-user@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/index.html b/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/index.html deleted file mode 100644 index 7ac1db7143..0000000000 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - Console clock - - - - - - - - - - - diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/index.html b/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/index.html deleted file mode 100644 index b48a2a0074..0000000000 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/index.html +++ /dev/null @@ -1,34 +0,0 @@ - - - - - Console clock - - - - - - - - - - - diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/task.md b/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/task.md deleted file mode 100644 index 933637247a..0000000000 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/task.md +++ /dev/null @@ -1,13 +0,0 @@ -importance: 5 - ---- - -# 继承 clock - -我们有一个 `Clock` 类。到目前为止,它每秒都会打印时间。 - -从 `Clock` 继承并创建一个新的类 `ExtendedClock`,同时添加一个参数 `precision` —— 每次 "ticks" 之间间隔的毫秒数,默认是 `1000`(1秒)。 - -- 你的代码在 `extended-clock.js` 文件里 -- 不要修改原来的 `clock.js`。只继承它。 - diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object.png b/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object.png deleted file mode 100644 index d4ff37e561..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png deleted file mode 100644 index a54a9d2f86..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static.png b/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static.png deleted file mode 100644 index 998c823300..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static@2x.png deleted file mode 100644 index 98a80d38fa..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object.png deleted file mode 100644 index c5d712632f..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object@2x.png deleted file mode 100644 index edc4e841ec..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal.png deleted file mode 100644 index 70708c2848..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal@2x.png deleted file mode 100644 index 0db1301815..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance.png b/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance.png deleted file mode 100644 index 542a0c9fa9..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance@2x.png deleted file mode 100644 index 21485062a8..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop.png b/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop.png deleted file mode 100644 index 637d179398..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop@2x.png deleted file mode 100644 index af7b443bcb..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/11-instanceof/instanceof.png b/1-js/07-object-oriented-programming/11-instanceof/instanceof.png deleted file mode 100644 index 85aa9a55f3..0000000000 Binary files a/1-js/07-object-oriented-programming/11-instanceof/instanceof.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/11-instanceof/instanceof@2x.png b/1-js/07-object-oriented-programming/11-instanceof/instanceof@2x.png deleted file mode 100644 index fba7712207..0000000000 Binary files a/1-js/07-object-oriented-programming/11-instanceof/instanceof@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance.png b/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance.png deleted file mode 100644 index 7cc655036f..0000000000 Binary files a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance@2x.png b/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance@2x.png deleted file mode 100644 index f53ecf68fd..0000000000 Binary files a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/01-property-descriptors/article.md b/1-js/07-object-properties/01-property-descriptors/article.md similarity index 83% rename from 1-js/07-object-oriented-programming/01-property-descriptors/article.md rename to 1-js/07-object-properties/01-property-descriptors/article.md index dbb11c7e9a..d3604c8227 100644 --- a/1-js/07-object-oriented-programming/01-property-descriptors/article.md +++ b/1-js/07-object-properties/01-property-descriptors/article.md @@ -3,7 +3,13 @@ 我们知道,对象可以存储属性。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/01-property-descriptors/article.md 到目前为止,属性对我们来说是一个简单的“键-值”对。但对象属性实际上是更复杂可变的东西。 +======= +Till now, a property was a simple "key-value" pair to us. But an object property is actually a more flexible and powerful thing. + +In this chapter we'll study additional configuration options, and in the next we'll see how to invisibly turn them into getter/setter functions. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/07-object-properties/01-property-descriptors/article.md ## 属性的标志 @@ -118,7 +124,11 @@ user.name = "Pete"; // 错误:不能设置只读属性'name'... */!* ``` +<<<<<<< HEAD:1-js/07-object-oriented-programming/01-property-descriptors/article.md 现在没有人可以改变我们的用户名称,除非他用自己的 `defineProperty` 来覆盖我们的用户。 +======= +Now no one can change the name of our user, unless they apply their own `defineProperty` to override ours. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/07-object-properties/01-property-descriptors/article.md 以下是相同的操作,但针对的是属性不存在的情况: @@ -191,7 +201,11 @@ alert(Object.keys(user)); // name 一个不可配置的属性不能被 `defineProperty` 删除或修改。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/01-property-descriptors/article.md 例如,`Math.PI` 是只读的、不可枚举和不可配置的: +======= +For instance, `Math.PI` is read-only, non-enumerable and non-configurable: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/07-object-properties/01-property-descriptors/article.md ```js run let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI'); @@ -296,6 +310,7 @@ for (let key in user) { 还有一些限制访问**整个**对象的方法: [Object.preventExtensions(obj)](mdn:js/Object/preventExtensions) +<<<<<<< HEAD:1-js/07-object-oriented-programming/01-property-descriptors/article.md : 禁止向对象添加属性。 [Object.seal(obj)](mdn:js/Object/seal) @@ -305,6 +320,16 @@ for (let key in user) { : 禁止添加/删除/更改属性,为所有现有属性设置 `configurable: false, writable: false`。 还有对他们的测试: +======= +: Forbids the addition of new properties to the object. + +[Object.seal(obj)](mdn:js/Object/seal) +: Forbids adding/removing of properties. Sets `configurable: false` for all existing properties. + +[Object.freeze(obj)](mdn:js/Object/freeze) +: Forbids adding/removing/changing of properties. Sets `configurable: false, writable: false` for all existing properties. +And also there are tests for them: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/07-object-properties/01-property-descriptors/article.md [Object.isExtensible(obj)](mdn:js/Object/isExtensible) : 如果添加属性被禁止,则返回 `false`,否则返回 `true`。 diff --git a/1-js/07-object-oriented-programming/02-property-accessors/article.md b/1-js/07-object-properties/02-property-accessors/article.md similarity index 91% rename from 1-js/07-object-oriented-programming/02-property-accessors/article.md rename to 1-js/07-object-properties/02-property-accessors/article.md index f43cc586fa..0cb9808bd1 100644 --- a/1-js/07-object-oriented-programming/02-property-accessors/article.md +++ b/1-js/07-object-properties/02-property-accessors/article.md @@ -84,12 +84,22 @@ alert(user.surname); // Cooper 现在我们有一个“虚拟”属性。它是可读写的,但实际上并不存在。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/02-property-accessors/article.md ```smart header="访问器属性只能访问 get/set" 属性可以是“数据属性”或“访问器属性”,但不能同时属于两者。 一旦使用 `get prop()` 或 `set prop()` 定义了一个属性,它就是一个访问器属性。所以必须有一个getter来读取它,如果我们对它赋值,它必须是一个 setter。 有时候只有一个 setter 或者只有一个 getter 是正常的。但在这种情况下,该属性将不可读或可写。 +======= +```smart header="Accessor properties are only accessible with get/set" +Once a property is defined with `get prop()` or `set prop()`, it's an accessor property, not a data property any more. + +- If there's a getter -- we can read `object.prop`, otherwise we can't. +- If there's a setter -- we can set `object.prop=...`, otherwise we can't. + +And in either case we can't `delete` an accessor property. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/07-object-properties/02-property-accessors/article.md ``` diff --git a/1-js/07-object-properties/index.md b/1-js/07-object-properties/index.md new file mode 100644 index 0000000000..67fcccaffc --- /dev/null +++ b/1-js/07-object-properties/index.md @@ -0,0 +1,3 @@ +# Object properties configuration + +In this section we return to objects and study their properties even more in-depth. diff --git a/1-js/08-error-handling/1-try-catch/try-catch-flow.png b/1-js/08-error-handling/1-try-catch/try-catch-flow.png deleted file mode 100644 index 6a91b63295..0000000000 Binary files a/1-js/08-error-handling/1-try-catch/try-catch-flow.png and /dev/null differ diff --git a/1-js/08-error-handling/1-try-catch/try-catch-flow@2x.png b/1-js/08-error-handling/1-try-catch/try-catch-flow@2x.png deleted file mode 100644 index 8bf9680fd6..0000000000 Binary files a/1-js/08-error-handling/1-try-catch/try-catch-flow@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/solution.md b/1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/task.md b/1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/solution.md b/1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/task.md b/1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/solution.md b/1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/task.md b/1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/solution.md b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md similarity index 85% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md index a46bda6e14..78350f31db 100644 --- a/1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/solution.md +++ b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md @@ -44,7 +44,11 @@ alert( lazy.stomach ); // 现在,所有的都在正常运行,因为 `this.stomach=` 不会在 `stomach` 中执行查找。该值会被直接写入 `this` 对象。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/solution.md 此外,我们还可以通过确保每只仓鼠都有自己的胃来完全回避这个问题: +======= +Also we can totally evade the problem by making sure that each hamster has their own stomach: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md ```js run let hamster = { diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/task.md b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/article.md b/1-js/08-prototypes/01-prototype-inheritance/article.md similarity index 50% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/article.md rename to 1-js/08-prototypes/01-prototype-inheritance/article.md index 4f70358535..b9d54ce587 100644 --- a/1-js/07-object-oriented-programming/03-prototype-inheritance/article.md +++ b/1-js/08-prototypes/01-prototype-inheritance/article.md @@ -12,7 +12,11 @@ ![prototype](object-prototype-empty.png) +<<<<<<< HEAD:1-js/07-object-oriented-programming/03-prototype-inheritance/article.md `[[Prototype]]` 有一个“神奇”的意义。当我们想要从 `object` 中读取一个属性时,它就丢失了。JavaScript 会自动从原型中获取它。在编程中,这样的事情称为“原型继承”。许多很酷的语言特性和编程技巧都是基于它的。 +======= +The prototype is a little bit "magical". When we want to read a property from `object`, and it's missing, JavaScript automatically takes it from the prototype. In programming, such thing is called "prototypal inheritance". Many cool language features and programming techniques are based on it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/01-prototype-inheritance/article.md 属性 `[[Prototype]]` 是内部的而且隐藏的,但是设置它的方法却有很多种。 @@ -31,7 +35,17 @@ rabbit.__proto__ = animal; */!* ``` +<<<<<<< HEAD:1-js/07-object-oriented-programming/03-prototype-inheritance/article.md 请注意 `__proto__` 与 `[[Prototype]]` **不一样**。这是一个 getter/setter。我们之后会讨论如何设置它,但是现在 `__proto__` 工作的很好。 +======= +```smart header="`__proto__` is a historical getter/setter for `[[Prototype]]`" +Please note that `__proto__` is *not the same* as `[[Prototype]]`. That's a getter/setter for it. + +It exists for historical reasons, in modern language it is replaced with functions `Object.getPrototypeOf/Object.setPrototypeOf` that also get/set the prototype. We'll study the reasons for that and these functions later. + +By the specification, `__proto__` must only be supported by browsers, but in fact all environments including server-side support it. For now, as `__proto__` notation is a little bit more intuitively obvious, we'll use it in the examples. +``` +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/01-prototype-inheritance/article.md 如果我们在 `rabbit` 中查找一个属性,而且它丢失了,JavaScript 会自动从 `animal` 中获取它。 @@ -62,7 +76,11 @@ alert( rabbit.jumps ); // true ![](proto-animal-rabbit.png) +<<<<<<< HEAD:1-js/07-object-oriented-programming/03-prototype-inheritance/article.md 我们可以说 "`animal` 是 `rabbit`" 的原型或者说 "`rabbit` 的原型继承自 `animal`"。 +======= +Here we can say that "`animal` is the prototype of `rabbit`" or "`rabbit` prototypically inherits from `animal`". +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/01-prototype-inheritance/article.md 因此如果 `animal` 有许多有用的属性和方法,那么它们在 `rabbit` 中会自动变为可用的。这种属性行为称为“继承”。 @@ -106,13 +124,17 @@ let animal = { let rabbit = { jumps: true, +*!* __proto__: animal +*/!* }; let longEar = { earLength: 10, +*!* __proto__: rabbit -} +*/!* +}; // walk is taken from the prototype chain longEar.walk(); // Animal walk @@ -123,16 +145,29 @@ alert(longEar.jumps); // true (from rabbit) 实际上只有两个限制: +<<<<<<< HEAD:1-js/07-object-oriented-programming/03-prototype-inheritance/article.md 1. 引用不能形成闭环。如果我们试图在一个闭环中分配 `__proto__`,JavaScript 会抛出异常。 2. `__proto__` 的值可以是对象,也可以是 `null`。所有其他的值(例如原语)都会被忽略。 +======= +1. The references can't go in circles. JavaScript will throw an error if we try to assign `__proto__` in a circle. +2. The value of `__proto__` can be either an object or `null`, other types (like primitives) are ignored. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/01-prototype-inheritance/article.md 这也可能是显而易见的,即:只能有一个 `[[Prototype]]`。对象不能继承自其他两个对象。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/03-prototype-inheritance/article.md ## 读写规则 +======= +## Writing doesn't use prototype +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/01-prototype-inheritance/article.md 原型仅用于读取属性。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/03-prototype-inheritance/article.md 对于数据属性(不是 getters/setters)写/删除操作直接在对象上进行操作。 +======= +Write/delete operations work directly with the object. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/01-prototype-inheritance/article.md 在以下示例中,我们将 `walk` 方法分配给 `rabbit`: @@ -146,7 +181,7 @@ let animal = { let rabbit = { __proto__: animal -} +}; *!* rabbit.walk = function() { @@ -161,9 +196,15 @@ rabbit.walk(); // Rabbit! Bounce-bounce! ![](proto-animal-rabbit-walk-2.png) +<<<<<<< HEAD:1-js/07-object-oriented-programming/03-prototype-inheritance/article.md 对于 getters/setters —— 如果我们读写一个属性,就会在原型中查找并调用它们。 例如,在以下代码中检查出 `admin.fullName` 属性: +======= +That's for data properties only, not for accessors. If a property is a getter/setter, then it behaves like a function: getters/setters are looked up in the prototype. + +For that reason `admin.fullName` works correctly in the code below: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/01-prototype-inheritance/article.md ```js run let user = { @@ -194,15 +235,25 @@ admin.fullName = "Alice Cooper"; // (**) ## "this" 的值 +<<<<<<< HEAD:1-js/07-object-oriented-programming/03-prototype-inheritance/article.md 在上面的例子中可能会出现一个有趣的现象:在 `set fullName(value)` 中 `this` 的值是什么?属性 `this.name` 和 `this.surname` 写在哪里: `user` 还是 `admin`? +======= +An interesting question may arise in the example above: what's the value of `this` inside `set fullName(value)`? Where the properties `this.name` and `this.surname` are written: into `user` or `admin`? +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/01-prototype-inheritance/article.md 答案很简单:`this` 根本不受原型的影响。 **无论在哪里找到方法:在对象或者原型中。调用方法时,`this` 始终是点之前的对象。** +<<<<<<< HEAD:1-js/07-object-oriented-programming/03-prototype-inheritance/article.md 因此,实际上 setter 使用 `admin` 作为 `this`,而不是 `user`。 这是一件非常重要的事情,因为我们可能有一个有很多方法而且继承它的大对象。然后我们可以在继承的对象上运行它的方法,它们将修改这些对象的状态,而不是大对象的。 +======= +So, the setter call `admin.fullName=` uses `admin` as `this`, not `user`. + +That is actually a super-important thing, because we may have a big object with many methods and inherit from it. Then inherited objects can run its methods, and they will modify the state of these objects, not the big one. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/01-prototype-inheritance/article.md 例如,这里的 `animal` 代表“方法存储”,而且 `rabbit` 在使用它。 @@ -241,6 +292,7 @@ alert(animal.isSleeping); // undefined (no such property in the prototype) 因此,方法是共享的,但对象状态不是。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/03-prototype-inheritance/article.md ## 总结 - JavaScript 中,所有的对象都有一个隐藏的 `[[Prototype]]` 属性,它可以是另一个对象或者 `null`。 @@ -248,3 +300,82 @@ alert(animal.isSleeping); // undefined (no such property in the prototype) - `[[Prototype]]` 引用的对象称为“原型”。 - 如果我们想要读取 `obj` 属性或者调用一个方法,而且它不存在,那么 JavaScript 就会尝试在原型中查找它。写/删除直接在对象上进行操作,它们不使用原型(除非属性实际上是一个 setter)。 - 如果我们调用 `obj.method()`,而且 `method` 是从原型中获取的,`this` 仍然会引用 `obj`。因此方法重视与当前对象一起工作,即使它们是继承的。 +======= +## for..in loop + +The `for..in` loops over inherited properties too. + +For instance: + +```js run +let animal = { + eats: true +}; + +let rabbit = { + jumps: true, + __proto__: animal +}; + +*!* +// Object.keys only return own keys +alert(Object.keys(rabbit)); // jumps +*/!* + +*!* +// for..in loops over both own and inherited keys +for(let prop in rabbit) alert(prop); // jumps, then eats +*/!* +``` + +If that's not what we want, and we'd like to exclude inherited properties, there's a built-in method [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): it returns `true` if `obj` has its own (not inherited) property named `key`. + +So we can filter out inherited properties (or do something else with them): + +```js run +let animal = { + eats: true +}; + +let rabbit = { + jumps: true, + __proto__: animal +}; + +for(let prop in rabbit) { + let isOwn = rabbit.hasOwnProperty(prop); + + if (isOwn) { + alert(`Our: ${prop}`); // Our: jumps + } else { + alert(`Inherited: ${prop}`); // Inherited: eats + } +} +``` + +Here we have the following inheritance chain: `rabbit` inherits from `animal`, that inherits from `Object.prototype` (because `animal` is a literal object `{...}`, so it's by default), and then `null` above it: + +![](rabbit-animal-object.png) + +Note, there's one funny thing. Where is the method `rabbit.hasOwnProperty` coming from? We did not define it. Looking at the chain we can see that the method is provided by `Object.prototype.hasOwnProperty`. In other words, it's inherited. + +...But why `hasOwnProperty` does not appear in `for..in` loop, like `eats` and `jumps`, if it lists all inherited properties. + +The answer is simple: it's not enumerable. Just like all other properties of `Object.prototype`, it has `enumerable:false` flag. That's why they are not listed. + +```smart header="All other iteration methods ignore inherited properties" +All other key/value-getting methods, such as `Object.keys`, `Object.values` and so on ignore inherited properties. + +They only operate on the object itself. Properties from the prototype are taken into account. +``` + +## Summary + +- In JavaScript, all objects have a hidden `[[Prototype]]` property that's either another object or `null`. +- We can use `obj.__proto__` to access it (a historical getter/setter, there are other ways, to be covered soon). +- The object referenced by `[[Prototype]]` is called a "prototype". +- If we want to read a property of `obj` or call a method, and it doesn't exist, then JavaScript tries to find it in the prototype. +- Write/delete operations for act directly on the object, they don't use the prototype (assuming it's a data property, not is a setter). +- If we call `obj.method()`, and the `method` is taken from the prototype, `this` still references `obj`. So methods always work with the current object even if they are inherited. +- The `for..in` loop iterates over both own and inherited properties. All other key/value-getting methods only operate on the object itself. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/01-prototype-inheritance/article.md diff --git a/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.png b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.png new file mode 100644 index 0000000000..3ecff3fddf Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty@2x.png b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty@2x.png new file mode 100644 index 0000000000..f80e61e680 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty@2x.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.png new file mode 100644 index 0000000000..48e02ad18b Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain@2x.png new file mode 100644 index 0000000000..1a04db885e Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain@2x.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.png new file mode 100644 index 0000000000..22b867fc88 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2@2x.png new file mode 100644 index 0000000000..8d9f9f1ae9 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2@2x.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.png new file mode 100644 index 0000000000..bc76d64baa Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3@2x.png new file mode 100644 index 0000000000..725a4c150b Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3@2x.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.png new file mode 100644 index 0000000000..29fc6d5039 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk@2x.png new file mode 100644 index 0000000000..724d6111b6 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk@2x.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.png new file mode 100644 index 0000000000..d50327f0e7 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit@2x.png new file mode 100644 index 0000000000..0506b92b69 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit@2x.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.png b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.png new file mode 100644 index 0000000000..c37d007804 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin@2x.png b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin@2x.png new file mode 100644 index 0000000000..8c3f546ebc Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin@2x.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/rabbit-animal-object.png b/1-js/08-prototypes/01-prototype-inheritance/rabbit-animal-object.png new file mode 100644 index 0000000000..3924233ddd Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/rabbit-animal-object.png differ diff --git a/1-js/08-prototypes/01-prototype-inheritance/rabbit-animal-object@2x.png b/1-js/08-prototypes/01-prototype-inheritance/rabbit-animal-object@2x.png new file mode 100644 index 0000000000..5350a19356 Binary files /dev/null and b/1-js/08-prototypes/01-prototype-inheritance/rabbit-animal-object@2x.png differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/solution.md b/1-js/08-prototypes/02-function-prototype/1-changing-prototype/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/solution.md rename to 1-js/08-prototypes/02-function-prototype/1-changing-prototype/solution.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/task.md b/1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/task.md rename to 1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/solution.md b/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/solution.md rename to 1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/task.md b/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/task.md rename to 1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/task.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/article.md b/1-js/08-prototypes/02-function-prototype/article.md similarity index 67% rename from 1-js/07-object-oriented-programming/04-function-prototype/article.md rename to 1-js/08-prototypes/02-function-prototype/article.md index 27a87a7239..f29fc7adf0 100644 --- a/1-js/07-object-oriented-programming/04-function-prototype/article.md +++ b/1-js/08-prototypes/02-function-prototype/article.md @@ -1,5 +1,6 @@ # 函数原型 +<<<<<<< HEAD:1-js/07-object-oriented-programming/04-function-prototype/article.md 在现代 JavaScript 中,我们可以使用 `__proto__` 设置一个原型,如前一篇文章中所述。但事情并非如此。 JavaScript 从一开始就有了原型继承。这是该语言的核心特征之一。 @@ -13,6 +14,17 @@ JavaScript 从一开始就有了原型继承。这是该语言的核心特征之 当用 `new F()` 创建一个新对象时,该对象的 `[[Prototype]]` 被设置为 `F.prototype`。 换句话说,如果 `F` 有一个 `prototype` 属性,该属性具有一个对象类型的值,那么 `new` 运算符就会使用它为新对象设置 `[[Prototype]]`。 +======= +Remember, new objects can be created with a constructor function, like `new F()`. + +If `F.prototype` is an object, then `new` operator uses it to set `[[Prototype]]` for the new object. + +```smart +JavaScript had prototypal inheritance from the beginning. It was one of the core features of the language. + +But in the old times, there was no direct access to it. The only thing that worked reliably was a `"prototype"` property of the constructor function, described in this chapter. So there are many scripts that still use it. +``` +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/02-function-prototype/article.md 请注意,`F.prototype` 意味着在 `F` 上有一个名为 `"prototype"` 的常规属性。这听起来与“原型”这个术语很相似,但在这里我们的意思是值有这个名字的常规属性。 @@ -42,8 +54,17 @@ alert( rabbit.eats ); // true ![](proto-constructor-animal-rabbit.png) +<<<<<<< HEAD:1-js/07-object-oriented-programming/04-function-prototype/article.md 在图片上,`"prototype"` 是一个水平箭头,它是一个常规属性,`[[Prototype]]` 是垂直的,意味着是继承自 `animal` 的 `rabbit`。 +======= +On the picture, `"prototype"` is a horizontal arrow, meaning a regular property, and `[[Prototype]]` is vertical, meaning the inheritance of `rabbit` from `animal`. + +```smart header="`F.prototype` only used at `new F` time" +`F.prototype` property is only used when `new F` is called, it assigns `[[Prototype]]` of the new object. After that, there's no connection between `F.prototype` and the new object. Think of it as a "one-time gift". +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/02-function-prototype/article.md +If, after the creation, `F.prototype` property changes (`F.prototype = `), then new objects created by `new F` will have another object as `[[Prototype]]`, but already existing objects keep the old one. +``` ## 默认的函数原型,构造函数属性 @@ -139,7 +160,11 @@ Rabbit.prototype.jumps = true // the default Rabbit.prototype.constructor is preserved ``` +<<<<<<< HEAD:1-js/07-object-oriented-programming/04-function-prototype/article.md 或者,也可以手动重新创建 `constructor` 属性: +======= +Or, alternatively, recreate the `constructor` property manually: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/02-function-prototype/article.md ```js Rabbit.prototype = { @@ -159,9 +184,15 @@ Rabbit.prototype = { 一切都很简单,只需要几条笔记就能说清楚: +<<<<<<< HEAD:1-js/07-object-oriented-programming/04-function-prototype/article.md - `F.prototype` 属性与 `[[Prototype]]` 不同。`F.prototype` 唯一的作用是:当 `new F()` 被调用时,它设置新对象的 `[[Prototype]]`。 - `F.prototype` 的值应该是一个对象或 null:其他值将不起作用。 - `"prototype"` 属性在设置为构造函数时仅具有这种特殊效果,并且用 `new` 调用。 +======= +- The `F.prototype` property is not the same as `[[Prototype]]`. The only thing `F.prototype` does: it sets `[[Prototype]]` of new objects when `new F()` is called. +- The value of `F.prototype` should be either an object or null: other values won't work. +- The `"prototype"` property only has such a special effect when set on a constructor function, and invoked with `new`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/02-function-prototype/article.md 在常规对象上,`prototype` 没什么特别的: ```js diff --git a/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.png b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.png new file mode 100644 index 0000000000..6ab9c6934a Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.png differ diff --git a/1-js/08-prototypes/02-function-prototype/function-prototype-constructor@2x.png b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor@2x.png new file mode 100644 index 0000000000..3beb0a5733 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring.png b/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring.png new file mode 100644 index 0000000000..c47938674b Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring.png differ diff --git a/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring@2x.png b/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring@2x.png new file mode 100644 index 0000000000..293c88bf93 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/native-prototypes-classes.png b/1-js/08-prototypes/02-function-prototype/native-prototypes-classes.png new file mode 100644 index 0000000000..c4658a9291 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/native-prototypes-classes.png differ diff --git a/1-js/08-prototypes/02-function-prototype/native-prototypes-classes@2x.png b/1-js/08-prototypes/02-function-prototype/native-prototypes-classes@2x.png new file mode 100644 index 0000000000..79445fac90 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/native-prototypes-classes@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/object-prototype-1.png b/1-js/08-prototypes/02-function-prototype/object-prototype-1.png new file mode 100644 index 0000000000..a69cad4a1b Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/object-prototype-1.png differ diff --git a/1-js/08-prototypes/02-function-prototype/object-prototype-1@2x.png b/1-js/08-prototypes/02-function-prototype/object-prototype-1@2x.png new file mode 100644 index 0000000000..9d661ac17e Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/object-prototype-1@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/object-prototype.png b/1-js/08-prototypes/02-function-prototype/object-prototype.png new file mode 100644 index 0000000000..820ffcc736 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/object-prototype.png differ diff --git a/1-js/08-prototypes/02-function-prototype/object-prototype@2x.png b/1-js/08-prototypes/02-function-prototype/object-prototype@2x.png new file mode 100644 index 0000000000..1eb7e8749d Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/object-prototype@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.png b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.png new file mode 100644 index 0000000000..4b381460a5 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.png differ diff --git a/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit@2x.png b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit@2x.png new file mode 100644 index 0000000000..c6e6ab5f60 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/rabbit-animal-object.png b/1-js/08-prototypes/02-function-prototype/rabbit-animal-object.png new file mode 100644 index 0000000000..3924233ddd Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/rabbit-animal-object.png differ diff --git a/1-js/08-prototypes/02-function-prototype/rabbit-animal-object@2x.png b/1-js/08-prototypes/02-function-prototype/rabbit-animal-object@2x.png new file mode 100644 index 0000000000..5350a19356 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/rabbit-animal-object@2x.png differ diff --git a/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.png b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.png new file mode 100644 index 0000000000..8f3519a747 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.png differ diff --git a/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor@2x.png b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor@2x.png new file mode 100644 index 0000000000..07acb8f4a9 Binary files /dev/null and b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor@2x.png differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/solution.md b/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/solution.md rename to 1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/task.md b/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/task.md rename to 1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/solution.md b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/solution.md rename to 1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/task.md b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/task.md rename to 1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/article.md b/1-js/08-prototypes/03-native-prototypes/article.md similarity index 55% rename from 1-js/07-object-oriented-programming/05-native-prototypes/article.md rename to 1-js/08-prototypes/03-native-prototypes/article.md index 246f2453b4..2b5a677cfd 100644 --- a/1-js/07-object-oriented-programming/05-native-prototypes/article.md +++ b/1-js/08-prototypes/03-native-prototypes/article.md @@ -15,6 +15,7 @@ alert( obj ); // "[object Object]" ? 生成字符串 `"[object Object]"` 的代码在哪里?那就是内置的一个 `toString` 方法,但是它在哪里呢?`obj` 是空的! +<<<<<<< HEAD:1-js/07-object-oriented-programming/05-native-prototypes/article.md ...然而简短的表达式 `obj = {}` 和 `obj = new Object()` 是一个意思,其中 `Object` 是一个内置的对象构造函数。并且这个方法有一个 `Object.prototype` 属性,这个属性引用了一个庞大的对象,这个庞大的对象有 `toString` 方法和其他的一些方法。 就像这样(所有的这些都是内置的): @@ -26,6 +27,19 @@ alert( obj ); // "[object Object]" ? ![](object-prototype-1.png) 之后当 `obj.toString()` 被调用时,这个方法是在 `Object.prototype` 中被取到的。 +======= +...But the short notation `obj = {}` is the same as `obj = new Object()`, where `Object` is a built-in object constructor function, with its own `prototype` referencing a huge object with `toString` and other methods. + +Here's what's going on: + +![](object-prototype.png) + +When `new Object()` is called (or a literal object `{...}` is created), the `[[Prototype]]` of it is set to `Object.prototype` according to the rule that we discussed in the previous chapter: + +![](object-prototype-1.png) + +So then when `obj.toString()` is called the method is taken from `Object.prototype`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/03-native-prototypes/article.md 我们可以这样验证它: @@ -48,7 +62,11 @@ alert(Object.prototype.__proto__); // null 例如,当我们创建一个数组 `[1, 2, 3]`,内部使用默认的 `new Array()` 构造函数。因此这个数组数据被写进了这个新的数组对象,并且 `Array.prototype` 成为这个数组对象的原型且为数组对象提供数组的操作方法。这样内存的存储效率是很高的。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/05-native-prototypes/article.md 按照规范,所有的内置原型顶端都是 `Object.prototype`。有些时候人们说“一切都从对象上继承而来”。 +======= +By specification, all of the built-in prototypes have `Object.prototype` on the top. Sometimes people say that "everything inherits from objects". +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/03-native-prototypes/article.md 下面是完整的示意图(3 个内置对象): @@ -82,11 +100,19 @@ alert(arr); // 1,2,3 <-- the result of Array.prototype.toString ![](native-prototypes-array-tostring.png) +<<<<<<< HEAD:1-js/07-object-oriented-programming/05-native-prototypes/article.md 像 Chrome 开发者控制台这样的浏览器内置工具也显示继承关系的(可能需要对内置对象使用 `console.dir`): ![](console_dir_array.png) 其他内置对象以同样的方式运行。即使是函数。它们是内置构造函数 `Function` 创建出来的对象,并且他们的方法:`call/apply` 和其他方法都来自 `Function.prototype`。函数也有自己的 `toString` 方法。 +======= +In-browser tools like Chrome developer console also show inheritance (`console.dir` may need to be used for built-in objects): + +![](console_dir_array.png) + +Other built-in objects also work the same way. Even functions -- they are objects of a built-in `Function` constructor, and their methods (`call`/`apply` and others) are taken from `Function.prototype`. Functions have their own `toString` too. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/03-native-prototypes/article.md ```js run function f() {} @@ -119,11 +145,27 @@ String.prototype.show = function() { "BOOM!".show(); // BOOM! ``` +<<<<<<< HEAD:1-js/07-object-oriented-programming/05-native-prototypes/article.md 在开发的过程中我们可能会想在内置对象上创建一个我们想要的方法。把他们添加到原生对象的原型中可能看起来不错,但那样通常来说是个坏主意。 原型是全局的,所以很容易造成冲突。如果有两个代码段都添加了 `String.prototype.show` 方法,那么其中一个将覆盖另外一个。 在现代编程中,只有一种情况下修改原生原型是被允许的。那就是在做 polyfills (译者注:兼容)的时候。换句话说,如果有一个 JavaScript 规范还没有被我们的 JavaScript 引擎支持(或者我们希望被支持的那些规范),那么需要手动实现这些规范并且把这些手动实现填充到内置对象的原型上。 +======= +During the process of development, we may have ideas for new built-in methods we'd like to have, and we may be tempted to add them to native prototypes. But that is generally a bad idea. + +```warn +Prototypes are global, so it's easy to get a conflict. If two libraries add a method `String.prototype.show`, then one of them will be overwriting the other. + +So, generally, modifying a native prototype is considered a bad idea. +``` + +**In modern programming, there is only one case where modifying native prototypes is approved. That's polyfilling.** + +Polyfilling is a term for making a substitute for a method that exists in JavaScript specification, but not yet supported by current JavaScript engine. + +Then we may implement it manually and populate the built-in prototype with it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/03-native-prototypes/article.md 例如: @@ -134,9 +176,15 @@ if (!String.prototype.repeat) { //假设没有这个方法 String.prototype.repeat = function(n) { //重复字符串 n 次 +<<<<<<< HEAD:1-js/07-object-oriented-programming/05-native-prototypes/article.md //实际上代码是比这个更复杂的, //当 "n" 的值为负数的时候抛出异常 //完整的算法在规范中 +======= + // actually, the code should be a little bit more complex than that + // (the full algorithm is in the specification) + // but even an imperfect polyfill is often considered good enough +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/03-native-prototypes/article.md return new Array(n + 1).join(this); }; } @@ -144,37 +192,72 @@ if (!String.prototype.repeat) { //假设没有这个方法 alert( "La".repeat(3) ); // LaLaLa ``` +<<<<<<< HEAD:1-js/07-object-oriented-programming/05-native-prototypes/article.md ## 从原型中借用 在 章节中我们讨论的方法借用: +======= + +## Borrowing from prototypes + +In the chapter we talked about method borrowing. + +That's when we take a method from one object and copy it into another. + +Some methods of native prototypes are often borrowed. + +For instance, if we're making an array-like object, we may want to copy some array methods to it. + +E.g. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/03-native-prototypes/article.md ```js run -function showArgs() { +let obj = { + 0: "Hello", + 1: "world!", + length: 2, +}; + *!* +<<<<<<< HEAD:1-js/07-object-oriented-programming/05-native-prototypes/article.md // 从数组借用 join 方法并在 arguments 的上下文中调用 alert( [].join.call(arguments, " - ") ); +======= +obj.join = Array.prototype.join; +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/03-native-prototypes/article.md */!* -} -showArgs("John", "Pete", "Alice"); // John - Pete - Alice +alert( obj.join(',') ); // Hello,world! ``` +<<<<<<< HEAD:1-js/07-object-oriented-programming/05-native-prototypes/article.md 因为 `join` 方法在 `Array.prototype` 对象上,我们可以直接调用它并且重写上面的代码: +======= +It works, because the internal algorithm of the built-in `join` method only cares about the correct indexes and the `length` property, it doesn't check that the object is indeed the array. And many built-in methods are like that. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/03-native-prototypes/article.md -```js -function showArgs() { -*!* - alert( Array.prototype.join.call(arguments, " - ") ); -*/!* -} -``` +Another possibility is to inherit by setting `obj.__proto__` to `Array.prototype`, so all `Array` methods are automatically available in `obj`. + +But that's impossible if `obj` already inherits from another object. Remember, we only can inherit from one object at a time. +<<<<<<< HEAD:1-js/07-object-oriented-programming/05-native-prototypes/article.md 这样是更有效率的,因为它避免了一个额外数组对象 `[]` 的创建。另一方面,这样做,需要更长的时间来编写。 +======= +Borrowing methods is flexible, it allows to mix functionality from different objects if needed. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/03-native-prototypes/article.md ## 总结 +<<<<<<< HEAD:1-js/07-object-oriented-programming/05-native-prototypes/article.md - 所有的内置对象都遵循一样的模式: - 方法都存储在原型对象上(`Array.prototype`、`Object.prototype`、`Date.prototype` 等)。 - 对象本身只存储数据(数组元素、对象属性、日期)。 - 基本数据类型同样在包装对象的原型上存储方法:`Number.prototype`、`String.prototype` 和 `Boolean.prototype`。只有 `undefined` 和 `null` 没有包装对象。 - 内置对象的原型可以被修改或者被新的方法填充。但是这样做是不被推荐的。只有当添加一个还没有被 JavaScript 引擎支持的新方法的时候才可能允许这样做。 +======= +- All built-in objects follow the same pattern: + - The methods are stored in the prototype (`Array.prototype`, `Object.prototype`, `Date.prototype` etc). + - The object itself stores only the data (array items, object properties, the date). +- Primitives also store methods in prototypes of wrapper objects: `Number.prototype`, `String.prototype`, `Boolean.prototype`. Only `undefined` and `null` do not have wrapper objects. +- Built-in prototypes can be modified or populated with new methods. But it's not recommended to change them. Probably the only allowable cause is when we add-in a new standard, but not yet supported by the engine JavaScript method. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/03-native-prototypes/article.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/console_dir_array.png b/1-js/08-prototypes/03-native-prototypes/console_dir_array.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/console_dir_array.png rename to 1-js/08-prototypes/03-native-prototypes/console_dir_array.png diff --git a/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.png b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.png new file mode 100644 index 0000000000..6ab9c6934a Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor@2x.png b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor@2x.png new file mode 100644 index 0000000000..3beb0a5733 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.png b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.png new file mode 100644 index 0000000000..c47938674b Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring@2x.png b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring@2x.png new file mode 100644 index 0000000000..293c88bf93 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.png b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.png new file mode 100644 index 0000000000..c4658a9291 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes@2x.png b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes@2x.png new file mode 100644 index 0000000000..79445fac90 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype-1.png b/1-js/08-prototypes/03-native-prototypes/object-prototype-1.png new file mode 100644 index 0000000000..a69cad4a1b Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/object-prototype-1.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype-1@2x.png b/1-js/08-prototypes/03-native-prototypes/object-prototype-1@2x.png new file mode 100644 index 0000000000..9d661ac17e Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/object-prototype-1@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype-null.png b/1-js/08-prototypes/03-native-prototypes/object-prototype-null.png new file mode 100644 index 0000000000..792c4f4237 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/object-prototype-null.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype-null@2x.png b/1-js/08-prototypes/03-native-prototypes/object-prototype-null@2x.png new file mode 100644 index 0000000000..d97efa8849 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/object-prototype-null@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype.png b/1-js/08-prototypes/03-native-prototypes/object-prototype.png new file mode 100644 index 0000000000..820ffcc736 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/object-prototype.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype@2x.png b/1-js/08-prototypes/03-native-prototypes/object-prototype@2x.png new file mode 100644 index 0000000000..1eb7e8749d Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/object-prototype@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.png b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.png new file mode 100644 index 0000000000..4b381460a5 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit@2x.png b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit@2x.png new file mode 100644 index 0000000000..c6e6ab5f60 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit@2x.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.png b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.png new file mode 100644 index 0000000000..8f3519a747 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.png differ diff --git a/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor@2x.png b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor@2x.png new file mode 100644 index 0000000000..07acb8f4a9 Binary files /dev/null and b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor@2x.png differ diff --git a/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md new file mode 100644 index 0000000000..8278f8f4d2 --- /dev/null +++ b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md @@ -0,0 +1,39 @@ + +可以使用 `Object.keys` 列出所有可枚举键值,然后输出。 + +<<<<<<< HEAD:1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/solution.md +为了使 `toString` 不可枚举,我们使用属性描述器来定义它。`Object.create` 语法允许我们为一个对象提供属性描述器作为第二参数。 +======= +To make `toString` non-enumerable, let's define it using a property descriptor. The syntax of `Object.create` allows us to provide an object with property descriptors as the second argument. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md + +```js run +*!* +let dictionary = Object.create(null, { +  toString: { // 定义 toString 方法 +    value() { // value 是一个函数 + return Object.keys(this).join(); + } + } +}); +*/!* + +dictionary.apple = "Apple"; +dictionary.__proto__ = "test"; + +// apple 和 __proto__ 在循环内 +for(let key in dictionary) { +  alert(key); // "apple",然后 "__proto__" +} + +// 通过 toString 得到逗号分隔的属性值 +alert(dictionary); // "apple,__proto__" +``` + +<<<<<<< HEAD:1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/solution.md +当我们使用描述器创建一个属性,它的标识默认是 `false`。因此在以上代码中,`dictonary.toString` 是不可枚举的。 +======= +When we create a property using a descriptor, its flags are `false` by default. So in the code above, `dictionary.toString` is non-enumerable. + +See the the chapter [](info:property-descriptors) for review. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/task.md b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/task.md rename to 1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/solution.md b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/solution.md rename to 1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/task.md b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md similarity index 62% rename from 1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/task.md rename to 1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md index 796952dfa9..1150b2e665 100644 --- a/1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/task.md +++ b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md @@ -2,7 +2,11 @@ importance: 5 --- +<<<<<<< HEAD:1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/task.md # 调用方式的差异 +======= +# The difference between calls +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md 让我们创建一个新的 `rabbit` 对象: diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/article.md b/1-js/08-prototypes/04-prototype-methods/article.md similarity index 62% rename from 1-js/07-object-oriented-programming/06-prototype-methods/article.md rename to 1-js/08-prototypes/04-prototype-methods/article.md index e424e5f1d8..a6a2ca285e 100644 --- a/1-js/07-object-oriented-programming/06-prototype-methods/article.md +++ b/1-js/08-prototypes/04-prototype-methods/article.md @@ -1,15 +1,31 @@ +<<<<<<< HEAD:1-js/07-object-oriented-programming/06-prototype-methods/article.md # 原型方法 本章节我们会讨论原型(prototype)的附加方法。 获取/设置原型的方式有很多,我们已知的有: +======= +# Prototype methods, objects without __proto__ + +In the first chapter of this section, we mentioned that there are modern methods to setup a prototype. + +The `__proto__` is considered outdated and somewhat deprecated (in browser-only part of the JavaScript standard). + +The modern methods are: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/04-prototype-methods/article.md - [Object.create(proto[, descriptors])](mdn:js/Object/create) —— 利用 `proto` 作为 `[[Prototype]]` 和可选的属性描述来创建一个空对象。 - [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) —— 返回 `obj` 对象的 `[[Prototype]]`。 - [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) —— 将 `obj` 对象的 `[[Prototype]]` 设置为 `proto`。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/06-prototype-methods/article.md 举个例子: +======= +These should be used instead of `__proto__`. + +For instance: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/04-prototype-methods/article.md ```js run let animal = { @@ -66,13 +82,29 @@ let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescr 这里有历史遗留问题。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/06-prototype-methods/article.md - 在上古时代 `prototype` 作为一个构造函数的属性来运行。 - 之后在 2012 年: `Object.create` 出现在标准中。它允许利用给定的原型来创建对象,但是不能 get/set 原型。因此许多浏览器厂商实现了非标准属性 `__proto__`,允许任何时候 get/set 原型。 - 之后在 2015 年: `Object.setPrototypeOf` 和 `Object.getPrototypeOf` 被加入到标准中。 `__proto__` 在几乎所有地方都得到实现,因此它成了标准以外的替代方案 B,在非浏览器环境下,它的支持性是不确定的,可选的。 +======= +- The `"prototype"` property of a constructor function works since very ancient times. +- Later in the year 2012: `Object.create` appeared in the standard. It allowed to create objects with the given prototype, but did not allow to get/set it. So browsers implemented non-standard `__proto__` accessor that allowed to get/set a prototype at any time. +- Later in the year 2015: `Object.setPrototypeOf` and `Object.getPrototypeOf` were added to the standard, to perform the same functionality as `__proto__`. As `__proto__` was de-facto implemented everywhere, it was kind-of deprecated and made its way to the Annex B of the standard, that is optional for non-browser environments. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/04-prototype-methods/article.md 目前为止,我们拥有了所有这些方式。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/06-prototype-methods/article.md 从技术上来讲,我们可以在任何时候 get/set `[[Prototype]]`。但是通常我们只在创建对象的时候设置它一次,自那之后不再修改:`rabbit` 继承自 `animal`,之后不再改变。对此 JavaScript 引擎做了高度的优化。运行中利用 `Object.setPrototypeOf` 或者 `obj.__proto__=` 来更改 prototype 是一个非常缓慢的操作。但是,这是可行的。 +======= +Why was `__proto__` replaced by the functions `getPrototypeOf/setPrototypeOf`? That's an interesting question, requiring us to understand why `__proto__` is bad. Read on to get the answer. + +```warn header="Don't reset `[[Prototype]]` unless the speed doesn't matter" +Technically, we can get/set `[[Prototype]]` at any time. But usually we only set it once at the object creation time, and then do not modify: `rabbit` inherits from `animal`, and that is not going to change. + +And JavaScript engines are highly optimized to that. Changing a prototype "on-the-fly" with `Object.setPrototypeOf` or `obj.__proto__=` is a very slow operation, it breaks internal optimizations for object property access operations. So evade it unless you know what you're doing, or JavaScript speed totally doesn't matter for you. +``` +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/04-prototype-methods/article.md ## 「极简」对象 @@ -95,11 +127,21 @@ alert(obj[key]); // [object Object],而不是 "some value"! 我们不应该感到惊讶。`__proto__` 属性很特别:它必须是对象或者 `null` 值,字符串不能成为 prototype。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/06-prototype-methods/article.md 但是我们并不想实现这样的行为,对吗?我们想要存储键值对,然而键名为 `"__proto__"` 没有被正确存储。所以这是一个错误。在这里,结果并没有很严重。但是在其他用例中,prototype 可能被改变,因此可能导致完全意想不到的结果。 +======= +But we didn't *intend* to implement such behavior, right? We want to store key/value pairs, and the key named `"__proto__"` was not properly saved. So that's a bug! + +Here the consequences are not terrible. But in other cases, we may be assigning object values, then the prototype may indeed be changed. As the result, the execution will go wrong in totally unexpected ways. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/04-prototype-methods/article.md 最可怕的是 —— 通常开发者完全不会考虑到这一点。这让类似的 bug 很难被发现,甚至使得它们容易遭到攻击,特别是当 JavaScript 被用在服务端的时候。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/06-prototype-methods/article.md 这样的情况只出现在 `__proto__` 上,所有其他的属性都是正常被「赋值」。 +======= +Unexpected things also may happen when accessing `toString` property -- that's a function by default, and other built-in properties. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/04-prototype-methods/article.md 怎么样避免这个错误呢? @@ -111,9 +153,15 @@ alert(obj[key]); // [object Object],而不是 "some value"! ![](object-prototype-2.png) +<<<<<<< HEAD:1-js/07-object-oriented-programming/06-prototype-methods/article.md 因此,如果 `obj.__proto__` 被读取或者赋值,那么对应的 getter/setter 从它的原型被调用,它会获取/设置 `[[Prototype]]`。 就像开头所说:`__proto__` 是访问 `[[Prototype]]` 的方式,而不是 `[[prototype]]` 本身。 +======= +So, if `obj.__proto__` is read or set, the corresponding getter/setter is called from its prototype, and it gets/sets `[[Prototype]]`. + +As it was said in the beginning of this tutorial section: `__proto__` is a way to access `[[Prototype]]`, it is not `[[Prototype]]` itself. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/04-prototype-methods/article.md 现在,我们想要使用一个对象作为关联数组,我们可以用一个小技巧: @@ -153,12 +201,13 @@ alert(obj); // Error (没有 toString 方法) ```js run let chineseDictionary = Object.create(null); -chineseDictionary.hello = "ni hao"; -chineseDictionary.bye = "zai jian"; +chineseDictionary.hello = "你好"; +chineseDictionary.bye = "再见"; alert(Object.keys(chineseDictionary)); // hello,bye ``` +<<<<<<< HEAD:1-js/07-object-oriented-programming/06-prototype-methods/article.md ## 获取所有属性 获取一个对象的键/值有很多种方法。 @@ -209,22 +258,26 @@ for(let prop in rabbit) alert(prop); // jumps,然后 eats 如果我们想要区分继承属性,有一个内置方法 [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty):如果 `obj` 有名为 `key` 的自身属性(而非继承),返回值为 `true`。 因此我们可以找出继承属性(或者对它们进行一些操作): +======= +## Summary -```js run -let animal = { - eats: true -}; +Modern methods to setup and directly access the prototype are: -let rabbit = { - jumps: true, - __proto__: animal -}; +- [Object.create(proto[, descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` (can be `null`) and optional property descriptors. +- [Object.getPrototypeOf(obj)](mdn:js/Object.getPrototypeOf) -- returns the `[[Prototype]]` of `obj` (same as `__proto__` getter). +- [Object.setPrototypeOf(obj, proto)](mdn:js/Object.setPrototypeOf) -- sets the `[[Prototype]]` of `obj` to `proto` (same as `__proto__` setter). + +The built-in `__proto__` getter/setter is unsafe if we'd want to put user-generated keys in to an object. Just because a user may enter "__proto__" as the key, and there'll be an error with hopefully easy, but generally unpredictable consequences. -for(let prop in rabbit) { - let isOwn = rabbit.hasOwnProperty(prop); - alert(`${prop}: ${isOwn}`); // jumps:true, then eats:false -} +So we can either use `Object.create(null)` to create a "very plain" object without `__proto__`, or stick to `Map` objects for that. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/04-prototype-methods/article.md + +Also, `Object.create` provides an easy way to shallow-copy an object with all descriptors: + +```js +let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); ``` +<<<<<<< HEAD:1-js/07-object-oriented-programming/06-prototype-methods/article.md 这个例子中我们有以下继承链:`rabbit`,然后 `animal`,然后 `Object.prototype` (因为 `animal` 是个字面量对象 `{...}`,因此默认是这样),然后最终到达 `null`: ![](rabbit-animal-object.png) @@ -245,6 +298,15 @@ for(let prop in rabbit) { - [Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) —— 返回包含所有自身字符串属性名称的数组。 - [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) —— 返回包含所有自身属性名称的数组。 - [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty):如果 `obj` 拥有名为 `key` 的自身属性(非继承得来),返回 `true`。 +======= + + +- [Object.keys(obj)](mdn:js/Object/keys) / [Object.values(obj)](mdn:js/Object/values) / [Object.entries(obj)](mdn:js/Object/entries) -- returns an array of enumerable own string property names/values/key-value pairs. +- [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) -- returns an array of all own symbolic keys. +- [Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) -- returns an array of all own string keys. +- [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) -- returns an array of all own keys. +- [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): it returns `true` if `obj` has its own (not inherited) keytt named `key`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/08-prototypes/04-prototype-methods/article.md 同时我们还明确了 `__proto__` 是 `[[Prototype]]` 的 getter/setter,位置在 `Object.prototype`,和其他方法相同。 diff --git a/1-js/08-prototypes/04-prototype-methods/object-prototype-2.png b/1-js/08-prototypes/04-prototype-methods/object-prototype-2.png new file mode 100644 index 0000000000..343435af6c Binary files /dev/null and b/1-js/08-prototypes/04-prototype-methods/object-prototype-2.png differ diff --git a/1-js/08-prototypes/04-prototype-methods/object-prototype-2@2x.png b/1-js/08-prototypes/04-prototype-methods/object-prototype-2@2x.png new file mode 100644 index 0000000000..86b8a678e1 Binary files /dev/null and b/1-js/08-prototypes/04-prototype-methods/object-prototype-2@2x.png differ diff --git a/1-js/08-prototypes/04-prototype-methods/object-prototype-null.png b/1-js/08-prototypes/04-prototype-methods/object-prototype-null.png new file mode 100644 index 0000000000..792c4f4237 Binary files /dev/null and b/1-js/08-prototypes/04-prototype-methods/object-prototype-null.png differ diff --git a/1-js/08-prototypes/04-prototype-methods/object-prototype-null@2x.png b/1-js/08-prototypes/04-prototype-methods/object-prototype-null@2x.png new file mode 100644 index 0000000000..d97efa8849 Binary files /dev/null and b/1-js/08-prototypes/04-prototype-methods/object-prototype-null@2x.png differ diff --git a/1-js/08-prototypes/index.md b/1-js/08-prototypes/index.md new file mode 100644 index 0000000000..8554a0e30b --- /dev/null +++ b/1-js/08-prototypes/index.md @@ -0,0 +1 @@ +# Prototypes, inheritance diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/clock.js b/1-js/09-classes/01-class/1-rewrite-to-class/_js.view/solution.js similarity index 59% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/clock.js rename to 1-js/09-classes/01-class/1-rewrite-to-class/_js.view/solution.js index c710b9da9b..0b31cf334e 100644 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/clock.js +++ b/1-js/09-classes/01-class/1-rewrite-to-class/_js.view/solution.js @@ -1,21 +1,21 @@ class Clock { constructor({ template }) { - this._template = template; + this.template = template; } - _render() { + render() { let date = new Date(); let hours = date.getHours(); if (hours < 10) hours = '0' + hours; let mins = date.getMinutes(); - if (mins < 10) min = '0' + mins; + if (mins < 10) mins = '0' + mins; let secs = date.getSeconds(); if (secs < 10) secs = '0' + secs; - let output = this._template + let output = this.template .replace('h', hours) .replace('m', mins) .replace('s', secs); @@ -24,11 +24,15 @@ class Clock { } stop() { - clearInterval(this._timer); + clearInterval(this.timer); } start() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); + this.render(); + this.timer = setInterval(() => this.render(), 1000); } } + + +let clock = new Clock({template: 'h:m:s'}); +clock.start(); diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/clock.js b/1-js/09-classes/01-class/1-rewrite-to-class/_js.view/source.js similarity index 85% rename from 1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/clock.js rename to 1-js/09-classes/01-class/1-rewrite-to-class/_js.view/source.js index 26081a35a1..f1749c8ba5 100644 --- a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/clock.js +++ b/1-js/09-classes/01-class/1-rewrite-to-class/_js.view/source.js @@ -9,7 +9,7 @@ function Clock({ template }) { if (hours < 10) hours = '0' + hours; let mins = date.getMinutes(); - if (mins < 10) min = '0' + mins; + if (mins < 10) mins = '0' + mins; let secs = date.getSeconds(); if (secs < 10) secs = '0' + secs; @@ -32,3 +32,6 @@ function Clock({ template }) { }; } + +let clock = new Clock({template: 'h:m:s'}); +clock.start(); diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.md b/1-js/09-classes/01-class/1-rewrite-to-class/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.md rename to 1-js/09-classes/01-class/1-rewrite-to-class/solution.md diff --git a/1-js/09-classes/01-class/1-rewrite-to-class/task.md b/1-js/09-classes/01-class/1-rewrite-to-class/task.md new file mode 100644 index 0000000000..3399b2c343 --- /dev/null +++ b/1-js/09-classes/01-class/1-rewrite-to-class/task.md @@ -0,0 +1,13 @@ +importance: 5 + +--- + +# 重写 class 声明 + +<<<<<<< HEAD:1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/task.md +按要求重写所有的 `Clock` 类声明方式,把所有类定义方式从 prototype 变为“现代”的 "class" 语法。 +======= +The `Clock` class is written in functional style. Rewrite it the "class" syntax. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/01-class/1-rewrite-to-class/task.md + +P.S. clock 仅在 console 中打印,请打开 console 查看。 diff --git a/1-js/09-classes/01-class/article.md b/1-js/09-classes/01-class/article.md new file mode 100644 index 0000000000..30a958df71 --- /dev/null +++ b/1-js/09-classes/01-class/article.md @@ -0,0 +1,351 @@ + +# Class basic syntax + +```quote author="Wikipedia" +In object-oriented programming, a *class* is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions or methods). +``` + +In practice, we often need to create many objects of the same kind, like users, or goods or whatever. + +As we already know from the chapter , `new function` can help with that. + +But in the modern JavaScript, there's a more advanced "class" construct, that introduces great new features which are useful for object-oriented programming. + +## The "class" syntax + +The basic syntax is: +```js +class MyClass { + // class methods + constructor() { ... } + method1() { ... } + method2() { ... } + method3() { ... } + ... +} +``` + +Then `new MyClass()` creates a new object with all the listed methods. + +The `constructor()` method is called automatically by `new`, so we can initialize the object there. + +For example: + +```js run +class User { + + constructor(name) { + this.name = name; + } + + sayHi() { + alert(this.name); + } + +} + +// Usage: +let user = new User("John"); +user.sayHi(); +``` + +When `new User("John")` is called: +1. A new object is created. +2. The `constructor` runs with the given argument and assigns `this.name` to it. + +...Then we can call methods, such as `user.sayHi`. + + +```warn header="No comma between class methods" +A common pitfall for novice developers is to put a comma between class methods, which would result in a syntax error. + +The notation here is not to be confused with object literals. Within the class, no commas are required. +``` + +## What is a class? + +So, what exactly is a `class`? That's not an entirely new language-level entity, as one might think. + +Let's unveil any magic and see what a class really is. That'll help in understanding many complex aspects. + +In JavaScript, a class is a kind of a function. + +Here, take a look: + +```js run +class User { + constructor(name) { this.name = name; } + sayHi() { alert(this.name); } +} + +// proof: User is a function +*!* +alert(typeof User); // function +*/!* +``` + +What `class User {...}` construct really does is: +1. Creates a function named `User`, that becomes the result of the class declaration. + - The function code is taken from the `constructor` method (assumed empty if we don't write such method). +3. Stores all methods, such as `sayHi`, in `User.prototype`. + +Afterwards, for new objects, when we call a method, it's taken from the prototype, just as described in the chapter . So `new User` object has access to class methods. + +We can illustrate the result of `class User` declaration as: + +![](class-user.png) + +Here's the code to introspect it: + + +```js run +class User { + constructor(name) { this.name = name; } + sayHi() { alert(this.name); } +} + +// class is a function +alert(typeof User); // function + +// ...or, more precisely, the constructor method +alert(User === User.prototype.constructor); // true + +// The methods are in User.prototype, e.g: +alert(User.prototype.sayHi); // alert(this.name); + +// there are exactly two methods in the prototype +alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi +``` + +## Not just a syntax sugar + +Sometimes people say that `class` is a "syntax sugar" in JavaScript, because we could actually declare the same without `class` keyword at all: + +```js run +// rewriting class User in pure functions + +// 1. Create constructor function +function User(name) { + this.name = name; +} +// any function prototype has constructor property by default, +// so we don't need to create it + +// 2. Add the method to prototype +User.prototype.sayHi = function() { + alert(this.name); +}; + +// Usage: +let user = new User("John"); +user.sayHi(); +``` + +The result of this definition is about the same. So, there are indeed reasons why `class` can be considered a syntax sugar to define a constructor together with its prototype methods. + +Although, there are important differences. + +1. First, a function created by `class` is labelled by a special internal property `[[FunctionKind]]:"classConstructor"`. So it's not entirely the same as creating it manually. + + Unlike a regular function, a class constructor can't be called without `new`: + + ```js run + class User { + constructor() {} + } + + alert(typeof User); // function + User(); // Error: Class constructor User cannot be invoked without 'new' + ``` + + Also, a string representation of a class constructor in most JavaScript engines starts with the "class..." + + ```js run + class User { + constructor() {} + } + + alert(User); // class User { ... } + ``` + +2. Class methods are non-enumerable. + A class definition sets `enumerable` flag to `false` for all methods in the `"prototype"`. + + That's good, because if we `for..in` over an object, we usually don't want its class methods. + +3. Classes always `use strict`. + All code inside the class construct is automatically in strict mode. + + +Also, in addition to its basic operation, the `class` syntax brings many other features with it which we'll explore later. + +## Class Expression + +Just like functions, classes can be defined inside another expression, passed around, returned, assigned etc. + +Here's an example of a class expression: + +```js +let User = class { + sayHi() { + alert("Hello"); + } +}; +``` + +Similar to Named Function Expressions, class expressions may or may not have a name. + +If a class expression has a name, it's visible inside the class only: + +```js run +// "Named Class Expression" +// (no such term in the spec, but that's similar to Named Function Expression) +let User = class *!*MyClass*/!* { + sayHi() { + alert(MyClass); // MyClass is visible only inside the class + } +}; + +new User().sayHi(); // works, shows MyClass definition + +alert(MyClass); // error, MyClass not visible outside of the class +``` + + +We can even make classes dynamically "on-demand", like this: + +```js run +function makeClass(phrase) { + // declare a class and return it + return class { + sayHi() { + alert(phrase); + }; + }; +} + +// Create a new class +let User = makeClass("Hello"); + +new User().sayHi(); // Hello +``` + + +## Getters/setters, other shorthands + +Just like literal objects, classes may include getters/setters, generators, computed properties etc. + +Here's an example for `user.name` implemented using `get/set`: + +```js run +class User { + + constructor(name) { + // invokes the setter + this.name = name; + } + +*!* + get name() { +*/!* + return this._name; + } + +*!* + set name(value) { +*/!* + if (value.length < 4) { + alert("Name is too short."); + return; + } + this._name = value; + } + +} + +let user = new User("John"); +alert(user.name); // John + +user = new User(""); // Name too short. +``` + +The class declaration creates getters and setters in `User.prototype`, like this: + +```js +Object.defineProperties(User.prototype, { + name: { + get() { + return this._name + }, + set(name) { + // ... + } + } +}); +``` + +Here's an example with computed properties: + +```js run +function f() { return "sayHi"; } + +class User { + [f()]() { + alert("Hello"); + } + +} + +new User().sayHi(); +``` + +For a generator method, similarly, prepend it with `*`. + +## Class properties + +```warn header="Old browsers may need a polyfill" +Class-level properties are a recent addition to the language. +``` + +In the example above, `User` only had methods. Let's add a property: + +```js run +class User { + name = "Anonymous"; + + sayHi() { + alert(`Hello, ${this.name}!`); + } +} + +new User().sayHi(); +``` + +The property is not placed into `User.prototype`. Instead, it is created by `new`, separately for every object. So, the property will never be shared between different objects of the same class. + + +## Summary + +The basic class syntax looks like this: + +```js +class MyClass { + prop = value; // field + + constructor(...) { // constructor + // ... + } + + method(...) {} // method + + get something(...) {} // getter method + set something(...) {} // setter method + + [Symbol.iterator]() {} // method with computed name/symbol name + // ... +} +``` + +`MyClass` is technically a function (the one that we provide as `constructor`), while methods, getters and settors are written to `MyClass.prototype`. + +In the next chapters we'll learn more about classes, including inheritance and other features. diff --git a/1-js/09-classes/01-class/class-user.png b/1-js/09-classes/01-class/class-user.png new file mode 100644 index 0000000000..dc8b756794 Binary files /dev/null and b/1-js/09-classes/01-class/class-user.png differ diff --git a/1-js/09-classes/01-class/class-user@2x.png b/1-js/09-classes/01-class/class-user@2x.png new file mode 100644 index 0000000000..aaa53708fa Binary files /dev/null and b/1-js/09-classes/01-class/class-user@2x.png differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/solution.md b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/solution.md rename to 1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/task.md b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/task.md rename to 1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.md b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.md new file mode 100644 index 0000000000..dcb4ffe59a --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.md @@ -0,0 +1 @@ +[js src="solution.view/extended-clock.js"] diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/clock.js b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/clock.js similarity index 64% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/clock.js rename to 1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/clock.js index c710b9da9b..d701c0caeb 100644 --- a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/clock.js +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/clock.js @@ -1,21 +1,21 @@ class Clock { constructor({ template }) { - this._template = template; + this.template = template; } - _render() { + render() { let date = new Date(); let hours = date.getHours(); if (hours < 10) hours = '0' + hours; let mins = date.getMinutes(); - if (mins < 10) min = '0' + mins; + if (mins < 10) mins = '0' + mins; let secs = date.getSeconds(); if (secs < 10) secs = '0' + secs; - let output = this._template + let output = this.template .replace('h', hours) .replace('m', mins) .replace('s', secs); @@ -24,11 +24,11 @@ class Clock { } stop() { - clearInterval(this._timer); + clearInterval(this.timer); } start() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); + this.render(); + this.timer = setInterval(() => this.render(), 1000); } } diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js similarity index 53% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js rename to 1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js index 4eb12381f9..ca613ca5e5 100644 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js @@ -2,11 +2,11 @@ class ExtendedClock extends Clock { constructor(options) { super(options); let { precision=1000 } = options; - this._precision = precision; + this.precision = precision; } start() { - this._render(); - this._timer = setInterval(() => this._render(), this._precision); + this.render(); + this.timer = setInterval(() => this.render(), this.precision); } }; diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/index.html b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/index.html new file mode 100644 index 0000000000..f76a436238 --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/index.html @@ -0,0 +1,12 @@ + + + + + diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/clock.js b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/clock.js similarity index 64% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/clock.js rename to 1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/clock.js index c710b9da9b..d701c0caeb 100644 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/clock.js +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/clock.js @@ -1,21 +1,21 @@ class Clock { constructor({ template }) { - this._template = template; + this.template = template; } - _render() { + render() { let date = new Date(); let hours = date.getHours(); if (hours < 10) hours = '0' + hours; let mins = date.getMinutes(); - if (mins < 10) min = '0' + mins; + if (mins < 10) mins = '0' + mins; let secs = date.getSeconds(); if (secs < 10) secs = '0' + secs; - let output = this._template + let output = this.template .replace('h', hours) .replace('m', mins) .replace('s', secs); @@ -24,11 +24,11 @@ class Clock { } stop() { - clearInterval(this._timer); + clearInterval(this.timer); } start() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); + this.render(); + this.timer = setInterval(() => this.render(), 1000); } } diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html new file mode 100644 index 0000000000..c0609858b8 --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html @@ -0,0 +1,21 @@ + + + diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md new file mode 100644 index 0000000000..91bb3b0f89 --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md @@ -0,0 +1,20 @@ +importance: 5 + +--- + +# 继承 clock + +我们有一个 `Clock` 类。到目前为止,它每秒都会打印时间。 + +<<<<<<< HEAD:1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/task.md +从 `Clock` 继承并创建一个新的类 `ExtendedClock`,同时添加一个参数 `precision` —— 每次 "ticks" 之间间隔的毫秒数,默认是 `1000`(1秒)。 + +- 你的代码在 `extended-clock.js` 文件里 +- 不要修改原来的 `clock.js`。只继承它。 +======= + +[js src="source.view/clock.js"] + +Create a new class `ExtendedClock` that inherits from `Clock` and adds the parameter `precision` -- the number of `ms` between "ticks". Should be `1000` (1 second) by default. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md + diff --git a/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.png b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.png new file mode 100644 index 0000000000..c610e28c3e Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.png differ diff --git a/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png new file mode 100644 index 0000000000..4819c7f798 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/solution.md b/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/solution.md rename to 1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/task.md b/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/task.md rename to 1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md diff --git a/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.png b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.png new file mode 100644 index 0000000000..0d887dbc0e Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.png differ diff --git a/1-js/09-classes/02-class-inheritance/animal-rabbit-extends@2x.png b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends@2x.png new file mode 100644 index 0000000000..af09271a91 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends@2x.png differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/article.md b/1-js/09-classes/02-class-inheritance/article.md similarity index 66% rename from 1-js/07-object-oriented-programming/10-class-inheritance/article.md rename to 1-js/09-classes/02-class-inheritance/article.md index 8989dfc51e..579e60e334 100644 --- a/1-js/07-object-oriented-programming/10-class-inheritance/article.md +++ b/1-js/09-classes/02-class-inheritance/article.md @@ -1,40 +1,94 @@ +<<<<<<< HEAD:1-js/07-object-oriented-programming/10-class-inheritance/article.md # 类继承和 super 类可以继承另外一个类。这是一个非常棒的语法,在技术上是它基于原型继承实现的。 为了继承另外一个类,我们需要在括号 `{..}` 前指定 `"extends"` 和父类 +======= +# Class inheritance + +Let's say we have two classes. + +`Animal`: + +```js +class Animal { + constructor(name) { + this.speed = 0; + this.name = name; + } + run(speed) { + this.speed += speed; + alert(`${this.name} runs with speed ${this.speed}.`); + } + stop() { + this.speed = 0; + alert(`${this.name} stopped.`); + } +} + +let animal = new Animal("My animal"); +``` + +![](rabbit-animal-independent-animal.png) + + +...And `Rabbit`: + +```js +class Rabbit { + constructor(name) { + this.name = name; + } + hide() { + alert(`${this.name} hides!`); + } +} + +let rabbit = new Rabbit("My rabbit"); +``` + +![](rabbit-animal-independent-rabbit.png) + + +Right now they are fully independent. + +But we'd want `Rabbit` to extend `Animal`. In other words, rabbits should be based on animals, have access to methods of `Animal` and extend them with its own methods. + +To inherit from another class, we should specify `"extends"` and the parent class before the braces `{..}`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/02-class-inheritance/article.md 这里我们写一个继承自 `Animal` 的 `Rabbit`: ```js run class Animal { - constructor(name) { this.speed = 0; this.name = name; } - run(speed) { this.speed += speed; alert(`${this.name} runs with speed ${this.speed}.`); } - stop() { this.speed = 0; alert(`${this.name} stopped.`); } - } +// Inherit from Animal by specifying "extends Animal" *!* +<<<<<<< HEAD:1-js/07-object-oriented-programming/10-class-inheritance/article.md // 从 Animal 继承 +======= +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/02-class-inheritance/article.md class Rabbit extends Animal { +*/!* hide() { alert(`${this.name} hides!`); } } -*/!* let rabbit = new Rabbit("White Rabbit"); @@ -42,11 +96,23 @@ rabbit.run(5); // 白色兔子会以速度 5 奔跑。 rabbit.hide(); // 白色兔子藏了起来! ``` +<<<<<<< HEAD:1-js/07-object-oriented-programming/10-class-inheritance/article.md 就如你期望的那样,也正如我们之前所见,`extends` 关键字实际上是给 `Rabbit.prototype` 添加了一个属性 `[[Prototype]]`,并且它会指向 `Animal.prototype`。 ![](animal-rabbit-extends.png) 所以,现在 `rabbit` 即可以访问它自己的方法,也可以访问 `Animal` 的方法。 +======= +Now the `Rabbit` code became a bit shorter, as it uses `Animal` constructor by default, and it also can `run`, as animals do. + +Internally, `extends` keyword adds `[[Prototype]]` reference from `Rabbit.prototype` to `Animal.prototype`: + +![](animal-rabbit-extends.png) + +So, if a method is not found in `Rabbit.prototype`, JavaScript takes it from `Animal.prototype`. + +As we can recall from the chapter , JavaScript uses the same prototypal inheritance for build-in objects. E.g. `Date.prototype.[[Prototype]]` is `Object.prototype`, so dates have generic object methods. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/02-class-inheritance/article.md ````smart header="Any expression is allowed after `extends`" 类语法不仅允许在 `extends` 后指定一个类,也允许指定任何表达式。 @@ -163,7 +229,11 @@ setTimeout(function() { super.stop() }, 1000); 到目前为止,`Rabbit` 还没有自己的 `constructor`。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/10-class-inheritance/article.md 根据[规范](https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation),如果一个类继承了另一个类并且没有构造函数,那么将生成以下构造函数: +======= +According to the [specification](https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation), if a class extends another class and has no `constructor`, then the following "empty" `constructor` is generated: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/02-class-inheritance/article.md ```js class Rabbit extends Animal { @@ -263,6 +333,7 @@ alert(rabbit.earLength); // 10 让我们再深入的去研究下 `super`。顺便说一句,我们会发现一些有趣的事情。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/10-class-inheritance/article.md 首先要说的是,从我们迄今为止学到的知识来看,`super` 是不可能运行的。 的确是这样,让我们问问自己,在技术上它是如何实现的?当一个对象方法运行时,它会将当前对象作为 `this`。如果之后我们调用 `super.method()`,那么如何检索 `method`?我们想当然地认为需要从当前对象的原型中获取 `method`。但是从技术上讲,我们(或者 JavaScript 的引擎)可以做到这一点吗? @@ -272,6 +343,17 @@ alert(rabbit.earLength); // 10 让我们尝试去这么做看看。简单起见,我们不使用类,只使用普通对象。 在这里,`rabbit.eat()` 会调用父对象的 `animal.eat()` 方法: +======= +First to say, from all that we've learned till now, it's impossible for `super` to work at all! + +Yeah, indeed, let's ask ourselves, how it could technically work? When an object method runs, it gets the current object as `this`. If we call `super.method()` then, it needs to retrieve the `method` from the prototype of the current object. + +The task may seem simple, but it isn't. The engine knows the current object `this`, so it could get the parent `method` as `this.__proto__.method`. Unfortunately, such a "naive" solution won't work. + +Let's demonstrate the problem. Without classes, using plain objects for the sake of simplicity. + +In the example below, `rabbit.__proto__ = animal`. Now let's try: in `rabbit.eat()` we'll call `animal.eat()`, using `this.__proto__`: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/02-class-inheritance/article.md ```js run let animal = { @@ -369,6 +451,7 @@ longEar.eat(); // Error: Maximum call stack size exceeded 为了提供解决方法,JavaScript 为函数额外添加了一个特殊的内部属性:`[[HomeObject]]`。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/10-class-inheritance/article.md **当一个函数被定义为类或者对象方法时,它的 `[[HomeObject]]` 属性就成为那个对象** 这实际上违反了 “解除绑定” 功能的想法,因为函数会记录他们的对象,并且 `[[HomeObject]]` 不能被改变,所以这个绑定是永久的。因此这是一个非常重要的语言变化。 @@ -376,11 +459,18 @@ longEar.eat(); // Error: Maximum call stack size exceeded 但是这种改变是安全的。`[[HomeObject]]` 只有使用 `super` 调用父类的方法是才会被使用。所以它不会破坏兼容性。 让我们看看它是如何帮助 `super` 运行的 —— 我们再次使用普通对象: +======= +When a function is specified as a class or object method, its `[[HomeObject]]` property becomes that object. + +Then `super` uses it to resolve the parent prototype and its methods. + +Let's see how it works, first with plain objects: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/02-class-inheritance/article.md ```js run let animal = { name: "Animal", - eat() { // [[HomeObject]] == animal + eat() { // animal.eat.[[HomeObject]] == animal alert(`${this.name} eats.`); } }; @@ -388,7 +478,7 @@ let animal = { let rabbit = { __proto__: animal, name: "Rabbit", - eat() { // [[HomeObject]] == rabbit + eat() { // rabbit.eat.[[HomeObject]] == rabbit super.eat(); } }; @@ -396,16 +486,18 @@ let rabbit = { let longEar = { __proto__: rabbit, name: "Long Ear", - eat() { // [[HomeObject]] == longEar + eat() { // longEar.eat.[[HomeObject]] == longEar super.eat(); } }; *!* +// works correctly longEar.eat(); // Long Ear eats. */!* ``` +<<<<<<< HEAD:1-js/07-object-oriented-programming/10-class-inheritance/article.md 每个方法都会在内部的 `[[HomeObject]]` 属性上标记它的对象。然后 `super` 利用它来解析父级原型。 `[[HomeObject]]` 是为类和简单对象中定义的方法定义的。但是对于对象,方法必须按照给定的方式定义:使用 `method()`,而不是 `"method: function()"`。 @@ -416,16 +508,35 @@ longEar.eat(); // Long Ear eats. let animal = { eat: function() { // 应该使用简短语法:eat() {...} // ... +======= +It works as intended, due to `[[HomeObject]]` mechanics. A method, such as `longEar.eat`, knows its `[[HomeObject]]` and takes the parent method from its prototype. Without any use of `this`. + +### Methods are not "free" + +As we've known before, generally functions are "free", not bound to objects in JavaScript. So they can be copied between objects and called with another `this`. + +The very existance of `[[HomeObject]]` violates that principle, because methods remember their objects. `[[HomeObject]]` can't be changed, so this bond is forever. + +The only place in the language where `[[HomeObject]]` is used -- is `super`. So, if a method does not use `super`, then we can still consider it free and copy between objects. But with `super` things may go wrong. + +Here's the demo of a wrong `super` call: + +```js run +let animal = { + sayHi() { + console.log(`I'm an animal`); +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/02-class-inheritance/article.md } }; let rabbit = { __proto__: animal, - eat: function() { - super.eat(); + sayHi() { + super.sayHi(); } }; +<<<<<<< HEAD:1-js/07-object-oriented-programming/10-class-inheritance/article.md *!* rabbit.eat(); // 调用 super 报错(因为没有 [[HomeObject]]) */!* @@ -448,8 +559,15 @@ class Animal { run(speed = 0) { this.speed += speed; alert(`${this.name} runs with speed ${this.speed}.`); +======= +let plant = { + sayHi() { + console.log("I'm a plant"); +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/02-class-inheritance/article.md } +}; +<<<<<<< HEAD:1-js/07-object-oriented-programming/10-class-inheritance/article.md static compare(animalA, animalB) { return animalA.speed - animalB.speed; } @@ -507,9 +625,34 @@ alert(Rabbit.prototype.__proto__ === Animal.prototype); `Object` 有 `Object.defineProperty`,`Object.keys` 等等的方法,但是 `Array`,`Date` 等等并不会继承他们。 这里有一张图来描述 `Date` 和 `Object` 的结构: +======= +let tree = { + __proto__: plant, +*!* + sayHi: rabbit.sayHi // (*) +*/!* +}; + +*!* +tree.sayHi(); // I'm an animal (?!?) +*/!* +``` + +A call to `tree.sayHi()` shows "I'm an animal". Definitevely wrong. + +The reason is simple: +- In the line `(*)`, the method `tree.sayHi` was copied from `rabbit`. Maybe we just wanted to avoid code duplication? +- So its `[[HomeObject]]` is `rabbit`, as it was created in `rabbit`. There's no way to change `[[HomeObject]]`. +- The code of `tree.sayHi()` has `super.sayHi()` inside. It goes up from `rabbit` and takes the method from `animal`. -![](object-date-inheritance.png) +![](super-homeobject-wrong.png) +### Methods, not function properties +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/02-class-inheritance/article.md + +`[[HomeObject]]` is defined for methods both in classes and in plain objects. But for objects, methods must be specified exactly as `method()`, not as `"method: function()"`. + +<<<<<<< HEAD:1-js/07-object-oriented-programming/10-class-inheritance/article.md 请注意,`Date` 和 `Object` 之间没有关联。`Object` 和 `Date` 都是独立存在的。`Date.prototype` 继承自 `Object.prototype`,但也仅此而已。 这种差异是由于历史原因而存在的:在 JavaScript 语言被创建时,并没有考虑过类语法和静态方法的继承 @@ -549,24 +692,39 @@ arr.constructor === PowerArray 更重要的是,我们可以定制这种行为。在这种情况下,如果存在静态的 getter `Symbol.species`,那么就会使用它的返回值作为构造函数。 举个例子,这里因为有 `Symbol.species`,像 `map`,`filter` 这样的内置方法将返回“普通”数组: +======= +The difference may be non-essential for us, but it's important for JavaScript. + +In the example below a non-method syntax is used for comparison. `[[HomeObject]]` property is not set and the inheritance doesn't work: ```js run -class PowerArray extends Array { - isEmpty() { - return this.length === 0; +let animal = { + eat: function() { // should be the short syntax: eat() {...} + // ... + } +}; +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/02-class-inheritance/article.md + +let rabbit = { + __proto__: animal, + eat: function() { + super.eat(); } +}; *!* +<<<<<<< HEAD:1-js/07-object-oriented-programming/10-class-inheritance/article.md // 内置函数会使用它作为构造函数 static get [Symbol.species]() { return Array; } +======= +rabbit.eat(); // Error calling super (because there's no [[HomeObject]]) +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/02-class-inheritance/article.md */!* -} - -let arr = new PowerArray(1, 2, 5, 10, 50); -alert(arr.isEmpty()); // false +``` +<<<<<<< HEAD:1-js/07-object-oriented-programming/10-class-inheritance/article.md // filter 使用 arr.constructor[Symbol.species] 作为构造函数创建了新数组 let filteredArr = arr.filter(item => item >= 10); @@ -577,3 +735,19 @@ alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function ``` 在一些高级的场景中,我们可以使用它从结果值中剔除一些不需要的扩展功能。又或者可以进一步扩展它。 +======= +## Summary + +1. To extend a class: `class Child extends Parent`: + - That means `Child.prototype.__proto__` will be `Parent.prototype`, so methods are inherited. +2. When overriding a constructor: + - We must call parent constructor as `super()` in `Child` constructor before using `this`. +3. When overriding another method: + - We can use `super.method()` in a `Child` method to call `Parent` method. +4. Internals: + - Methods remember their class/object in the internal `[[HomeObject]]` property. That's how `super` resolves parent methods. + - So it's not safe to copy a method with `super` from one object to another. + +Also: +- Arrow functions don't have own `this` or `super`, so they transparently fit into the surrounding context. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/02-class-inheritance/article.md diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.png b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.png new file mode 100644 index 0000000000..8d30622ceb Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.png differ diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-array-object@2x.png b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object@2x.png new file mode 100644 index 0000000000..00f6bd80d0 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object@2x.png differ diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.png b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.png new file mode 100644 index 0000000000..f8afbbcd64 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.png differ diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2@2x.png b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2@2x.png new file mode 100644 index 0000000000..cf5aa6554f Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2@2x.png differ diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.png b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.png new file mode 100644 index 0000000000..a6f8964e6c Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.png differ diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal@2x.png b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal@2x.png new file mode 100644 index 0000000000..2e3f4d7ffa Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal@2x.png differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal.png b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-run-animal.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal.png rename to 1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-run-animal.png diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal@2x.png b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-run-animal@2x.png similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal@2x.png rename to 1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-run-animal@2x.png diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.png b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.png new file mode 100644 index 0000000000..79351a7540 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.png differ diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal@2x.png b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal@2x.png new file mode 100644 index 0000000000..346574e0c6 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal@2x.png differ diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.png b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.png new file mode 100644 index 0000000000..3d3b78cca5 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.png differ diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit@2x.png b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit@2x.png new file mode 100644 index 0000000000..a923d10e55 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit@2x.png differ diff --git a/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.png b/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.png new file mode 100644 index 0000000000..b1871dc722 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.png differ diff --git a/1-js/09-classes/02-class-inheritance/this-super-loop.png b/1-js/09-classes/02-class-inheritance/this-super-loop.png new file mode 100644 index 0000000000..e68ed70e7e Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/this-super-loop.png differ diff --git a/1-js/09-classes/02-class-inheritance/this-super-loop@2x.png b/1-js/09-classes/02-class-inheritance/this-super-loop@2x.png new file mode 100644 index 0000000000..037d075874 Binary files /dev/null and b/1-js/09-classes/02-class-inheritance/this-super-loop@2x.png differ diff --git a/1-js/09-classes/03-static-properties-methods/animal-rabbit-static.png b/1-js/09-classes/03-static-properties-methods/animal-rabbit-static.png new file mode 100644 index 0000000000..c5e7e3e490 Binary files /dev/null and b/1-js/09-classes/03-static-properties-methods/animal-rabbit-static.png differ diff --git a/1-js/09-classes/03-static-properties-methods/animal-rabbit-static@2x.png b/1-js/09-classes/03-static-properties-methods/animal-rabbit-static@2x.png new file mode 100644 index 0000000000..de434af2cf Binary files /dev/null and b/1-js/09-classes/03-static-properties-methods/animal-rabbit-static@2x.png differ diff --git a/1-js/09-classes/03-static-properties-methods/article.md b/1-js/09-classes/03-static-properties-methods/article.md new file mode 100644 index 0000000000..b940e04c86 --- /dev/null +++ b/1-js/09-classes/03-static-properties-methods/article.md @@ -0,0 +1,226 @@ + +# Static properties and methods + +We can also assign a method to the class function, not to its `"prototype"`. Such methods are called *static*. + +An example: + +```js run +class User { +*!* + static staticMethod() { +*/!* + alert(this === User); + } +} + +User.staticMethod(); // true +``` + +That actually does the same as assigning it as a function property: + +```js +function User() { } + +User.staticMethod = function() { + alert(this === User); +}; +``` + +The value of `this` inside `User.staticMethod()` is the class constructor `User` itself (the "object before dot" rule). + +Usually, static methods are used to implement functions that belong to the class, but not to any particular object of it. + +For instance, we have `Article` objects and need a function to compare them. The natural choice would be `Article.compare`, like this: + +```js run +class Article { + constructor(title, date) { + this.title = title; + this.date = date; + } + +*!* + static compare(articleA, articleB) { + return articleA.date - articleB.date; + } +*/!* +} + +// usage +let articles = [ + new Article("HTML", new Date(2019, 1, 1)), + new Article("CSS", new Date(2019, 0, 1)), + new Article("JavaScript", new Date(2019, 11, 1)) +]; + +*!* +articles.sort(Article.compare); +*/!* + +alert( articles[0].title ); // CSS +``` + +Here `Article.compare` stands "over" the articles, as a means to compare them. It's not a method of an article, but rather of the whole class. + +Another example would be a so-called "factory" method. Imagine, we need few ways to create an article: + +1. Create by given parameters (`title`, `date` etc). +2. Create an empty article with today's date. +3. ... + +The first way can be implemented by the constructor. And for the second one we can make a static method of the class. + +Like `Article.createTodays()` here: + +```js run +class Article { + constructor(title, date) { + this.title = title; + this.date = date; + } + +*!* + static createTodays() { + // remember, this = Article + return new this("Today's digest", new Date()); + } +*/!* +} + +let article = Article.createTodays(); + +alert( article.title ); // Todays digest +``` + +Now every time we need to create a today's digest, we can call `Article.createTodays()`. Once again, that's not a method of an article, but a method of the whole class. + +Static methods are also used in database-related classes to search/save/remove entries from the database, like this: + +```js +// assuming Article is a special class for managing articles +// static method to remove the article: +Article.remove({id: 12345}); +``` + +## Static properties + +[recent browser=Chrome] + +Static properties are also possible, just like regular class properties: + +```js run +class Article { + static publisher = "Ilya Kantor"; +} + +alert( Article.publisher ); // Ilya Kantor +``` + +That is the same as a direct assignment to `Article`: + +```js +Article.publisher = "Ilya Kantor"; +``` + +## Statics and inheritance + +Statics are inherited, we can access `Parent.method` as `Child.method`. + +For instance, `Animal.compare` in the code below is inherited and accessible as `Rabbit.compare`: + +```js run +class Animal { + + constructor(name, speed) { + this.speed = speed; + this.name = name; + } + + run(speed = 0) { + this.speed += speed; + alert(`${this.name} runs with speed ${this.speed}.`); + } + +*!* + static compare(animalA, animalB) { + return animalA.speed - animalB.speed; + } +*/!* + +} + +// Inherit from Animal +class Rabbit extends Animal { + hide() { + alert(`${this.name} hides!`); + } +} + +let rabbits = [ + new Rabbit("White Rabbit", 10), + new Rabbit("Black Rabbit", 5) +]; + +*!* +rabbits.sort(Rabbit.compare); +*/!* + +rabbits[0].run(); // Black Rabbit runs with speed 5. +``` + +Now we can call `Rabbit.compare` assuming that the inherited `Animal.compare` will be called. + +How does it work? Again, using prototypes. As you might have already guessed, `extends` gives `Rabbit` the `[[Prototype]]` reference to `Animal`. + + +![](animal-rabbit-static.png) + +So, `Rabbit` function now inherits from `Animal` function. And `Animal` function normally has `[[Prototype]]` referencing `Function.prototype`, because it doesn't `extend` anything. + +Here, let's check that: + +```js run +class Animal {} +class Rabbit extends Animal {} + +// for static properties and methods +alert(Rabbit.__proto__ === Animal); // true + +// the next step up leads to Function.prototype +alert(Animal.__proto__ === Function.prototype); // true + +// the "normal" prototype chain for object methods +alert(Rabbit.prototype.__proto__ === Animal.prototype); +``` + +This way `Rabbit` has access to all static methods of `Animal`. + +## Summary + +Static methods are used for the functionality that doesn't relate to a concrete class instance, doesn't require an instance to exist, but rather belongs to the class as a whole, like `Article.compare` -- a generic method to compare two articles. + +Static properties are used when we'd like to store class-level data, also not bound to an instance. + +The syntax is: + +```js +class MyClass { + static property = ...; + + static method() { + ... + } +} +``` + +That's technically the same as assigning to the class itself: + +```js +MyClass.property = ... +MyClass.method = ... +``` + +Static properties are inherited. + +Technically, for `class B extends A` the prototype of the class `B` itself points to `A`: `B.[[Prototype]] = A`. So if a field is not found in `B`, the search continues in `A`. diff --git a/1-js/09-classes/04-private-protected-properties-methods/article.md b/1-js/09-classes/04-private-protected-properties-methods/article.md new file mode 100644 index 0000000000..78d6c78f59 --- /dev/null +++ b/1-js/09-classes/04-private-protected-properties-methods/article.md @@ -0,0 +1,330 @@ + +# Private and protected properties and methods + +One of the most important principles of object oriented programming -- delimiting internal interface from the external one. + +That is "a must" practice in developing anything more complex than a "hello world" app. + +To understand this, let's break away from development and turn our eyes into the real world. + +Usually, devices that we're using are quite complex. But delimiting the internal interface from the external one allows to use them without problems. + +## A real-life example + +For instance, a coffee machine. Simple from outside: a button, a display, a few holes...And, surely, the result -- great coffee! :) + +![](coffee.jpg) + +But inside... (a picture from the repair manual) + +![](coffee-inside.jpg) + +A lot of details. But we can use it without knowing anything. + +Coffee machines are quite reliable, aren't they? We can use one for years, and only if something goes wrong -- bring it for repairs. + +The secret of reliability and simplicity of a coffee machine -- all details are well-tuned and *hidden* inside. + +If we remove the protective cover from the coffee machine, then using it will be much more complex (where to press?), and dangerous (it can electrocute). + +As we'll see, in programming objects are like coffee machines. + +But in order to hide inner details, we'll use not a protective cover, but rather special syntax of the language and conventions. + +## Internal and external interface + +In object-oriented programming, properties and methods are split into two groups: + +- *Internal interface* -- methods and properties, accessible from other methods of the class, but not from the outside. +- *External interface* -- methods and properties, accessible also from outside the class. + +If we continue the analogy with the coffee machine -- what's hidden inside: a boiler tube, heating element, and so on -- is its internal interface. + +An internal interface is used for the object to work, its details use each other. For instance, a boiler tube is attached to the heating element. + +But from the outside a coffee machine is closed by the protective cover, so that no one can reach those. Details are hidden and inaccessible. We can use its features via the external interface. + +So, all we need to use an object is to know its external interface. We may be completely unaware how it works inside, and that's great. + +That was a general introduction. + +In JavaScript, there are three types of properties and members: + +- Public: accessible from anywhere. They comprise the external interface. Till now we were only using public properties and methods. +- Private: accessible only from inside the class. These are for the internal interface. + +In many other languages there also exist "protected" fields: accessible only from inside the class and those extending it. They are also useful for the internal interface. They are in a sense more widespread than private ones, because we usually want inheriting classes to gain access to properly do the extension. + +Protected fields are not implemented in JavaScript on the language level, but in practice they are very convenient, so they are emulated. + +In the next step we'll make a coffee machine in JavaScript with all these types of properties. A coffee machine has a lot of details, we won't model them to stay simple (though we could). + +## Protecting "waterAmount" + +Let's make a simple coffee machine class first: + +```js run +class CoffeeMachine { + waterAmount = 0; // the amount of water inside + + constructor(power) { + this.power = power; + alert( `Created a coffee-machine, power: ${power}` ); + } + +} + +// create the coffee machine +let coffeeMachine = new CoffeeMachine(100); + +// add water +coffeeMachine.waterAmount = 200; +``` + +Right now the properties `waterAmount` and `power` are public. We can easily get/set them from the outside to any value. + +Let's change `waterAmount` property to protected to have more control over it. For instance, we don't want anyone to set it below zero. + +**Protected properties are usually prefixed with an underscore `_`.** + +That is not enforced on the language level, but there's a convention that such properties and methods should not be accessed from the outside. Most programmers follow it. + +So our property will be called `_waterAmount`: + +```js run +class CoffeeMachine { + _waterAmount = 0; + + set waterAmount(value) { + if (value < 0) throw new Error("Negative water"); + this._waterAmount = value; + } + + get waterAmount() { + return this._waterAmount; + } + + constructor(power) { + this._power = power; + } + +} + +// create the coffee machine +let coffeeMachine = new CoffeeMachine(100); + +// add water +coffeeMachine.waterAmount = -10; // Error: Negative water +``` + +Now the access is under control, so setting the water below zero fails. + +## Read-only "power" + +For `power` property, let's make it read-only. It sometimes happens that a property must be set at creation time only, and then never modified. + +That's exactly the case for a coffee machine: power never changes. + +To do so, we only need to make getter, but not the setter: + +```js run +class CoffeeMachine { + // ... + + constructor(power) { + this._power = power; + } + + get power() { + return this._power; + } + +} + +// create the coffee machine +let coffeeMachine = new CoffeeMachine(100); + +alert(`Power is: ${coffeeMachine.power}W`); // Power is: 100W + +coffeeMachine.power = 25; // Error (no setter) +``` + +````smart header="Getter/setter functions" +Here we used getter/setter syntax. + +But most of the time `get.../set...` functions are preferred, like this: + +```js +class CoffeeMachine { + _waterAmount = 0; + + *!*setWaterAmount(value)*/!* { + if (value < 0) throw new Error("Negative water"); + this._waterAmount = value; + } + + *!*getWaterAmount()*/!* { + return this._waterAmount; + } +} + +new CoffeeMachine().setWaterAmount(100); +``` + +That looks a bit longer, but functions are more flexible. They can accept multiple arguments (even if we don't need them right now). So, for the future, just in case we need to refactor something, functions are a safer choice. + +Surely, there's a tradeoff. On the other hand, get/set syntax is shorter, so ultimately there's no strict rule, it's up to you to decide. +```` + +```smart header="Protected fields are inherited" +If we inherit `class MegaMachine extends CoffeeMachine`, then nothing prevents us from accessing `this._waterAmount` or `this._power` from the methods of the new class. + +So protected fields are naturally inheritable. Unlike private ones that we'll see below. +``` + +## Private "#waterLimit" + +[recent browser=none] + +There's a finished JavaScript proposal, almost in the standard, that provides language-level support for private properties and methods. + +Privates should start with `#`. They are only accessible from inside the class. + +For instance, here we add a private `#waterLimit` property and extract the water-checking logic into a separate method: + +```js +class CoffeeMachine { +*!* + #waterLimit = 200; +*/!* + +*!* + #checkWater(value) { + if (value < 0) throw new Error("Negative water"); + if (value > this.#waterLimit) throw new Error("Too much water"); + } +*/!* + + _waterAmount = 0; + + set waterAmount(value) { +*!* + this.#checkWater(value); +*/!* + this._waterAmount = value; + } + + get waterAmount() { + return this._waterAmount; + } + +} + +let coffeeMachine = new CoffeeMachine(); + +*!* +coffeeMachine.#checkWater(); // Error +coffeeMachine.#waterLimit = 1000; // Error +*/!* + +coffeeMachine.waterAmount = 100; // Works +``` + +On the language level, `#` is a special sign that the field is private. We can't access it from outside or from inheriting classes. + +Private fields do not conflict with public ones. We can have both private `#waterAmount` and public `waterAmount` fields at the same time. + +For instance, let's make `waterAmount` an accessor for `#waterAmount`: + +```js run +class CoffeeMachine { + + #waterAmount = 0; + + get waterAmount() { + return this.#waterAmount; + } + + set waterAmount(value) { + if (value < 0) throw new Error("Negative water"); + this.#waterAmount = value; + } +} + +let machine = new CoffeeMachine(); + +machine.waterAmount = 100; +alert(machine.#waterAmount); // Error +``` + +Unlike protected ones, private fields are enforced by the language itself. That's a good thing. + +But if we inherit from `CoffeeMachine`, then we'll have no direct access to `#waterAmount`. We'll need to rely on `waterAmount` getter/setter: + +```js +class MegaCoffeeMachine extends CoffeeMachine() { + method() { +*!* + alert( this.#waterAmount ); // Error: can only access from CoffeeMachine +*/!* + } +} +``` + +In many scenarios such limitation is too severe. If we extend a `CoffeeMachine`, we may have legitimate reason to access its internals. That's why protected fields are used most of the time, even though they are not supported by the language syntax. + +````warn +Private fields are special. + +Remember, usually we can access fields by this[name]: + +```js +class User { + ... + sayHi() { + let fieldName = "name"; + alert(`Hello, ${this[fieldName]}`); + } +} +``` + +With private fields that's impossible: `this['#name']` doesn't work. That's a syntax limitation to ensure privacy. +```` + +## Summary + +In terms of OOP, delimiting of the internal interface from the external one is called [encapsulation]("https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)"). + +It gives the following benefits: + +Protection for users, so that they don't shoot themselves in the feet +: Imagine, there's a team of developers using a coffee machine. It was made by the "Best CoffeeMachine" company, and works fine, but a protective cover was removed. So the internal interface is exposed. + + All developers are civilized -- they use the coffee machine as intended. But one of them, John, decided that he's the smartest one, and made some tweaks in the coffee machine internals. So the coffee machine failed two days later. + + That's surely not John's fault, but rather the person who removed the protective cover and let John do his manipulations. + + The same in programming. If a user of a class will change things not intended to be changed from the outside -- the consequences are unpredictable. + +Supportable +: The situation in programming is more complex than with a real-life coffee machine, because we don't just buy it once. The code constantly undergoes development and improvement. + + **If we strictly delimit the internal interface, then the developer of the class can freely change its internal properties and methods, even without informing the users..** + + It's much easier to develop, if you know that certain methods can be renamed, their parameters can be changed, and even removed, because no external code depends on them. + + For users, when a new version comes out, it may be a total overhaul, but still simple to upgrade if the external interface is the same. + +Hiding complexity +: People adore to use things that are simple. At least from outside. What's inside is a different thing. + + Programmers are not an exception. + + **It's always convenient when implementation details are hidden, and a simple, well-documented external interface is available.** + +To hide internal interface we use either protected or public properties: + +- Protected fields start with `_`. That's a well-known convention, not enforced at the language level. Programmers should only access a field starting with `_` from its class and classes inheriting from it. +- Private fields start with `#`. JavaScript makes sure we only can access those from inside the class. + +Right now, private fields are not well-supported among browsers, but can be polyfilled. diff --git a/1-js/09-classes/04-private-protected-properties-methods/coffee-inside.jpg b/1-js/09-classes/04-private-protected-properties-methods/coffee-inside.jpg new file mode 100644 index 0000000000..60f84664d1 Binary files /dev/null and b/1-js/09-classes/04-private-protected-properties-methods/coffee-inside.jpg differ diff --git a/1-js/09-classes/04-private-protected-properties-methods/coffee.jpg b/1-js/09-classes/04-private-protected-properties-methods/coffee.jpg new file mode 100644 index 0000000000..ee26e1c064 Binary files /dev/null and b/1-js/09-classes/04-private-protected-properties-methods/coffee.jpg differ diff --git a/1-js/09-classes/05-extend-natives/article.md b/1-js/09-classes/05-extend-natives/article.md new file mode 100644 index 0000000000..f85aa02425 --- /dev/null +++ b/1-js/09-classes/05-extend-natives/article.md @@ -0,0 +1,90 @@ + +# Extending built-in classes + +Built-in classes like Array, Map and others are extendable also. + +For instance, here `PowerArray` inherits from the native `Array`: + +```js run +// add one more method to it (can do more) +class PowerArray extends Array { + isEmpty() { + return this.length === 0; + } +} + +let arr = new PowerArray(1, 2, 5, 10, 50); +alert(arr.isEmpty()); // false + +let filteredArr = arr.filter(item => item >= 10); +alert(filteredArr); // 10, 50 +alert(filteredArr.isEmpty()); // false +``` + +Please note a very interesting thing. Built-in methods like `filter`, `map` and others -- return new objects of exactly the inherited type. They rely on the `constructor` property to do so. + +In the example above, +```js +arr.constructor === PowerArray +``` + +So when `arr.filter()` is called, it internally creates the new array of results using exactly `new PowerArray`, not basic `Array`. That's actually very cool, because we can keep using `PowerArray` methods further on the result. + +Even more, we can customize that behavior. + +We can add a special static getter `Symbol.species` to the class. If exists, it should return the constructor that JavaScript will use internally to create new entities in `map`, `filter` and so on. + +If we'd like built-in methods like `map`, `filter` will return regular arrays, we can return `Array` in `Symbol.species`, like here: + +```js run +class PowerArray extends Array { + isEmpty() { + return this.length === 0; + } + +*!* + // built-in methods will use this as the constructor + static get [Symbol.species]() { + return Array; + } +*/!* +} + +let arr = new PowerArray(1, 2, 5, 10, 50); +alert(arr.isEmpty()); // false + +// filter creates new array using arr.constructor[Symbol.species] as constructor +let filteredArr = arr.filter(item => item >= 10); + +*!* +// filteredArr is not PowerArray, but Array +*/!* +alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function +``` + +As you can see, now `.filter` returns `Array`. So the extended functionality is not passed any further. + +## No static inheritance in built-ins + +Built-in objects have their own static methods, for instance `Object.keys`, `Array.isArray` etc. + +As we already know, native classes extend each other. For instance, `Array` extends `Object`. + +Normally, when one class extends another, both static and non-static methods are inherited. + +So, if `Rabbit extends Animal`, then: + +1. `Rabbit.methods` are callable for `Animal.methods`, because `Rabbit.[[Prototype]] = Animal`. +2. `new Rabbit().methods` are also available, because `Rabbit.prototype.[[Prototype]] = Animal.prototype`. + +That's thoroughly explained in the chapter [](info:static-properties-methods#statics-and-inheritance). + +But built-in classes are an exception. They don't inherit statics `(1)` from each other. + +For example, both `Array` and `Date` inherit from `Object`, so their instances have methods from `Object.prototype`. But `Array.[[Prototype]]` does not point to `Object`. So there's `Object.keys()`, but not `Array.keys()` and `Date.keys()`. + +Here's the picture structure for `Date` and `Object`: + +![](object-date-inheritance.png) + +Note, there's no link between `Date` and `Object`. Both `Object` and `Date` exist independently. `Date.prototype` inherits from `Object.prototype`, but that's all. diff --git a/1-js/09-classes/05-extend-natives/object-date-inheritance.png b/1-js/09-classes/05-extend-natives/object-date-inheritance.png new file mode 100644 index 0000000000..73020a49e8 Binary files /dev/null and b/1-js/09-classes/05-extend-natives/object-date-inheritance.png differ diff --git a/1-js/09-classes/05-extend-natives/object-date-inheritance@2x.png b/1-js/09-classes/05-extend-natives/object-date-inheritance@2x.png new file mode 100644 index 0000000000..6520f9e265 Binary files /dev/null and b/1-js/09-classes/05-extend-natives/object-date-inheritance@2x.png differ diff --git a/1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/solution.md b/1-js/09-classes/06-instanceof/1-strange-instanceof/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/solution.md rename to 1-js/09-classes/06-instanceof/1-strange-instanceof/solution.md diff --git a/1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/task.md b/1-js/09-classes/06-instanceof/1-strange-instanceof/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/task.md rename to 1-js/09-classes/06-instanceof/1-strange-instanceof/task.md diff --git a/1-js/07-object-oriented-programming/11-instanceof/article.md b/1-js/09-classes/06-instanceof/article.md similarity index 80% rename from 1-js/07-object-oriented-programming/11-instanceof/article.md rename to 1-js/09-classes/06-instanceof/article.md index f2e74d31f6..0d96696e98 100644 --- a/1-js/07-object-oriented-programming/11-instanceof/article.md +++ b/1-js/09-classes/06-instanceof/article.md @@ -46,14 +46,26 @@ alert( arr instanceof Object ); // true 有一点需要留意,`arr` 同时还隶属于 `Object` 类。因为从原型上来讲,`Array` 是继承自 `Object` 类的。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/11-instanceof/article.md `instanceof` 在检测中会将原型链考虑在内,此外,还能借助静态方法 `Symbol.hasInstance` 来改善检测效果。 +======= +The `instanceof` operator examines the prototype chain for the check, but we can set a custom logic the static method `Symbol.hasInstance`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/06-instanceof/article.md `obj instanceof Class` 语句的大致执行过程如下: +<<<<<<< HEAD:1-js/07-object-oriented-programming/11-instanceof/article.md 1. 如果提供了静态方法 `Symbol.hasInstance`,那就直接用这个方法进行检测: ```js run // 假设具有 canEat 属性的对象为动物类 +======= +1. If there's a static method `Symbol.hasInstance`, then just call it: `Class[Symbol.hasInstance](obj)`. It should return either `true` or `false`. We're done. + For example: + + ```js run + // setup instanceOf check that assumes that anything that canEat is an animal +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/06-instanceof/article.md class Animal { static [Symbol.hasInstance](obj) { if (obj.canEat) return true; @@ -61,10 +73,18 @@ alert( arr instanceof Object ); // true } let obj = { canEat: true }; +<<<<<<< HEAD:1-js/07-object-oriented-programming/11-instanceof/article.md alert(obj instanceof Animal); // 返回 true:调用 Animal[Symbol.hasInstance](obj) ``` 2. 大部分的类是没有 `Symbol.hasInstance` 方法的,这时会检查 `Class.prototype` 是否与 `obj` 的原型链中的任何一个原型相等。 +======= + + alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) is called + ``` + +2. Most classes do not have `Symbol.hasInstance`. In that case, the standard logic is used: `obj instanceOf Classs` checks whether `Class.prototype` equals to one of prototypes in the `obj` prototype chain. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/06-instanceof/article.md 简而言之,是这么比较的: ```js @@ -117,7 +137,11 @@ alert( rabbit instanceof Rabbit ); // false 所以,为了谨慎起见,最好避免修改 `prototype`。 +<<<<<<< HEAD:1-js/07-object-oriented-programming/11-instanceof/article.md ## 福利:使用 Object 的 toString 方法来揭示类型 +======= +## Bonus: Object.prototype.toString for the type +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/06-instanceof/article.md 大家都知道,一个普通对象被转化为字符串时为 `[object Object]`: @@ -182,7 +206,11 @@ alert( {}.toString.call(user) ); // [object User] 大部分和环境相关的对象也有这个属性。以下输出可能因浏览器不同而异: ```js run +<<<<<<< HEAD:1-js/07-object-oriented-programming/11-instanceof/article.md // 环境相关对象和类的 toStringTag: +======= +// toStringTag for the environment-specific object and class: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/09-classes/06-instanceof/article.md alert( window[Symbol.toStringTag]); // window alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest diff --git a/1-js/09-classes/06-instanceof/instanceof.png b/1-js/09-classes/06-instanceof/instanceof.png new file mode 100644 index 0000000000..ad0fc77ab3 Binary files /dev/null and b/1-js/09-classes/06-instanceof/instanceof.png differ diff --git a/1-js/09-classes/06-instanceof/instanceof@2x.png b/1-js/09-classes/06-instanceof/instanceof@2x.png new file mode 100644 index 0000000000..c25b166b3f Binary files /dev/null and b/1-js/09-classes/06-instanceof/instanceof@2x.png differ diff --git a/1-js/07-object-oriented-programming/13-mixins/article.md b/1-js/09-classes/07-mixins/article.md similarity index 100% rename from 1-js/07-object-oriented-programming/13-mixins/article.md rename to 1-js/09-classes/07-mixins/article.md diff --git a/1-js/07-object-oriented-programming/13-mixins/head.html b/1-js/09-classes/07-mixins/head.html similarity index 100% rename from 1-js/07-object-oriented-programming/13-mixins/head.html rename to 1-js/09-classes/07-mixins/head.html diff --git a/1-js/09-classes/07-mixins/mixin-inheritance.png b/1-js/09-classes/07-mixins/mixin-inheritance.png new file mode 100644 index 0000000000..6142ce7fcc Binary files /dev/null and b/1-js/09-classes/07-mixins/mixin-inheritance.png differ diff --git a/1-js/09-classes/07-mixins/mixin-inheritance@2x.png b/1-js/09-classes/07-mixins/mixin-inheritance@2x.png new file mode 100644 index 0000000000..ccbd743002 Binary files /dev/null and b/1-js/09-classes/07-mixins/mixin-inheritance@2x.png differ diff --git a/1-js/09-classes/index.md b/1-js/09-classes/index.md new file mode 100644 index 0000000000..87846ef6ba --- /dev/null +++ b/1-js/09-classes/index.md @@ -0,0 +1 @@ +# Classes diff --git a/1-js/08-error-handling/1-try-catch/1-finally-or-code-after/solution.md b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md similarity index 100% rename from 1-js/08-error-handling/1-try-catch/1-finally-or-code-after/solution.md rename to 1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md diff --git a/1-js/08-error-handling/1-try-catch/1-finally-or-code-after/task.md b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md similarity index 74% rename from 1-js/08-error-handling/1-try-catch/1-finally-or-code-after/task.md rename to 1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md index 03750a1763..1e3182cf76 100644 --- a/1-js/08-error-handling/1-try-catch/1-finally-or-code-after/task.md +++ b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md @@ -33,6 +33,10 @@ importance: 5 */!* ``` +<<<<<<< HEAD:1-js/08-error-handling/1-try-catch/1-finally-or-code-after/task.md 我们只是需要在代码执行完之后,清除工作空间,而不管是不是在执行的过程中遇到异常。 +======= +We definitely need the cleanup after the work, doesn't matter if there was an error or not. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md 那么,是用 `finally` 好呢还是两种方式都一样?如果哪种更好,请举例说明在什么情况下它会更好? diff --git a/1-js/08-error-handling/1-try-catch/article.md b/1-js/10-error-handling/1-try-catch/article.md similarity index 81% rename from 1-js/08-error-handling/1-try-catch/article.md rename to 1-js/10-error-handling/1-try-catch/article.md index 696ec18247..8b5ce9feac 100644 --- a/1-js/08-error-handling/1-try-catch/article.md +++ b/1-js/10-error-handling/1-try-catch/article.md @@ -68,7 +68,7 @@ try { } catch(err) { - alert(`Error has occured!`); // *!*(3) <--*/!* + alert(`Error has occurred!`); // *!*(3) <--*/!* } @@ -108,14 +108,24 @@ try { } ``` +<<<<<<< HEAD:1-js/08-error-handling/1-try-catch/article.md 因为 `try..catch` 包裹了计划要执行的 `setTimeout` 函数。但是函数本身要稍后才能执行,这时引擎已经离开了 `try..catch` 结构。 +======= +That's because the function itself is executed later, when the engine has already left the `try..catch` construct. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/1-try-catch/article.md 要捕捉到计划中将要执行的函数中的异常,那么 `try..catch` 必须在这个函数之中: ```js run setTimeout(function() { +<<<<<<< HEAD:1-js/08-error-handling/1-try-catch/article.md try { noSuchVariable; // try..catch 处理异常! } catch (e) { +======= + try { + noSuchVariable; // try..catch handles the error! + } catch { +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/1-try-catch/article.md alert( "error is caught here!" ); } }, 1000); @@ -165,6 +175,19 @@ try { } ``` +## Optional "catch" binding + +[recent browser=new] + +If we don't need error details, `catch` may omit it: + +```js +try { + // ... +} catch { + // error object omitted +} +``` ## 使用 "try..catch" @@ -194,7 +217,11 @@ alert( user.age ); // 30 得到报错之后我们就应该满意了吗?当然不! +<<<<<<< HEAD:1-js/08-error-handling/1-try-catch/article.md 如果这样做,当拿到的数据出错,用户就不会知道(除非他们打开开发者控制台)。代码执行失败却没有提示信息会导致糟糕的用户体验。 +======= +This way, if something's wrong with the data, the visitor will never know that (unless they open the developer console). And people really don't like when something "just dies" without any error message. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/1-try-catch/article.md 让我们来用 `try..catch` 来处理这个错误: @@ -255,7 +282,11 @@ try { throw ``` +<<<<<<< HEAD:1-js/08-error-handling/1-try-catch/article.md 技术上讲,我们可以使用任何东西来作为一个异常对象。甚至可以是基础类型,比如数字或者字符串。但是更好的方式是用对象,尤其是有 `name` 和 `message` 属性的对象(某种程度上和内置的异常有可比性)。 +======= +Technically, we can use anything as an error object. That may be even a primitive, like a number or a string, but it's better to use objects, preferably with `name` and `message` properties (to stay somewhat compatible with built-in errors). +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/1-try-catch/article.md JavaScript 有很多标准异常的内置的构造器:`Error`、 `SyntaxError`、`ReferenceError`、`TypeError` 和其他的。我们也可以用他们来创建异常对象。 @@ -325,7 +356,11 @@ try { ## 再次抛出异常 +<<<<<<< HEAD:1-js/08-error-handling/1-try-catch/article.md 上面的例子中,我们用 `try..catch` 处理没有被正确返回的数据,但是也有可能在 `try {...}` 代码块内发生**另一个预料之外的**异常,例如变量未定义或者其他不是返回的数据不正确的异常。 +======= +In the example above we use `try..catch` to handle incorrect data. But is it possible that *another unexpected error* occurs within the `try {...}` block? Like a programming error (variable is not defined) or something else, not just that "incorrect data" thing. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/1-try-catch/article.md 例如: @@ -342,7 +377,11 @@ try { } ``` +<<<<<<< HEAD:1-js/08-error-handling/1-try-catch/article.md 当然,一切皆有可能。程序员也是会犯错的。即使是一些开源的被数百万人用了几十年的项目 —— 一个严重的 bug 因为他引发的严重的黑客事件被发现(比如发生在 `ssh` 工具上的黑客事件)。 +======= +Of course, everything's possible! Programmers do make mistakes. Even in open-source utilities used by millions for decades -- suddenly a bug may be discovered that leads to terrible hacks. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/1-try-catch/article.md 对我们来说,`try..catch` 是用来捕捉“数据错误”的异常,但是 catch 本身会捕捉到**所有**来自于 `try` 的异常。这里,我们遇到了预料之外的错误,但是仍然抛出了 `"JSON Error"` 的信息,这是不正确的,同时也会让我们的代码变得更难调试。 @@ -476,7 +515,11 @@ try { 1. 如果对于 "Make an error?" 你的回答是 "Yes",那么执行 `try -> catch -> finally`。 2. 如果你的回答是 "No",那么执行 `try -> finally`。 +<<<<<<< HEAD:1-js/08-error-handling/1-try-catch/article.md `finally` 的语法通常用在:我们在 `try..catch` 之前开始一个操作,不管在该代码块中执行的结果怎样,我们都想结束的时候执行某个操作。 +======= +The `finally` clause is often used when we start doing something and want to finalize it in any case of outcome. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/1-try-catch/article.md 比如,生成斐波那契数的函数 `fib(n)` 的执行时间,通常,我们在开始和结束的时候测量。但是,如果该函数在被调用的过程中发生异常,就如执行下面的代码就会返回负数或者非整数的异常。 @@ -508,14 +551,18 @@ try { } */!* -alert(result || "error occured"); +alert(result || "error occurred"); alert( `execution took ${diff}ms` ); ``` 你可以通过后面的不同的输入来检验上面代码的执行:先在 `prompt` 弹框中先输入 `35` —— 它会正常执行,`try` 代码执行后执行 `finally` 里面的代码。然后再输入 `-1`,会立即捕获一个异常,执行时间将会是 `0ms`。两次的测量结果都是正确的。 +<<<<<<< HEAD:1-js/08-error-handling/1-try-catch/article.md 换句话说,有两种方式退出这个函数的执行:`return` 或是 `throw`,`finally` 语法都能处理。 +======= +In other words, the function may finish with `return` or `throw`, that doesn't matter. The `finally` clause executes in both cases. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/1-try-catch/article.md ```smart header="Variables are local inside `try..catch..finally`" @@ -575,9 +622,15 @@ function func() { 设想一下,`try..catch` 之外出现了一个严重的异常,代码停止执行,可能是因为编程异常或者其他更严重的异常。 +<<<<<<< HEAD:1-js/08-error-handling/1-try-catch/article.md 那么,有没办法来应对这种情况呢?我们希望记录这个异常,给用户一些提示信息(通常,用户是看不到提示信息的),或者做一些其他操作。 虽然没有这方面的规范,但是代码的执行环境一般会提供这种机制,因为这真的很有用。例如,Node.JS 有 [process.on('uncaughtException')](https://nodejs.org/api/process.html#process_event_uncaughtexception) 。对于浏览器环境,我们可以绑定一个函数到 [window.onerror](mdn:api/GlobalEventHandlers/onerror),当遇到未知异常的时候,它就会执行。 +======= +Is there a way to react on such occurrences? We may want to log the error, show something to the user (normally they don't see error messages) etc. + +There is none in the specification, but environments usually provide it, because it's really useful. For instance, Node.js has [process.on('uncaughtException')](https://nodejs.org/api/process.html#process_event_uncaughtexception) for that. And in the browser we can assign a function to special [window.onerror](mdn:api/GlobalEventHandlers/onerror) property. It will run in case of an uncaught error. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/1-try-catch/article.md 语法如下: @@ -630,7 +683,11 @@ window.onerror = function(message, url, line, col, error) { ## 总结 +<<<<<<< HEAD:1-js/08-error-handling/1-try-catch/article.md `try..catch` 结构允许我们处理执行时的异常,它允许我们尝试执行代码,并且捕获执行过程中可能发生的异常。 +======= +The `try..catch` construct allows to handle runtime errors. It literally allows to "try" running the code and "catch" errors that may occur in it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/1-try-catch/article.md 语法如下: @@ -653,7 +710,13 @@ try { - `name` —— 异常名称(异常对象的构造函数的名称)。 - `stack`(没有标准) —— 异常发生时的调用栈。 +<<<<<<< HEAD:1-js/08-error-handling/1-try-catch/article.md 我们也可以通过使用 `throw` 运算符来生成自定义的异常。技术上来讲,`throw` 的参数没有限制,但是通常它是一个继承自内置的 `Error` 类的异常对象。更对关于异常的扩展,请看下个章节。 +======= +If an error object is not needed, we can omit it by using `catch {` instead of `catch(err) {`. + +We can also generate our own errors using the `throw` operator. Technically, the argument of `throw` can be anything, but usually it's an error object inheriting from the built-in `Error` class. More on extending errors in the next chapter. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/1-try-catch/article.md 重新抛出异常,是一种异常处理的基本模式:`catch` 代码块通常处理某种已知的特定类型的异常,所以它应该抛出其他未知类型的异常。 diff --git a/1-js/10-error-handling/1-try-catch/try-catch-flow.png b/1-js/10-error-handling/1-try-catch/try-catch-flow.png new file mode 100644 index 0000000000..8b37753814 Binary files /dev/null and b/1-js/10-error-handling/1-try-catch/try-catch-flow.png differ diff --git a/1-js/10-error-handling/1-try-catch/try-catch-flow@2x.png b/1-js/10-error-handling/1-try-catch/try-catch-flow@2x.png new file mode 100644 index 0000000000..f85eb63218 Binary files /dev/null and b/1-js/10-error-handling/1-try-catch/try-catch-flow@2x.png differ diff --git a/1-js/08-error-handling/2-custom-errors/1-format-error/solution.md b/1-js/10-error-handling/2-custom-errors/1-format-error/solution.md similarity index 100% rename from 1-js/08-error-handling/2-custom-errors/1-format-error/solution.md rename to 1-js/10-error-handling/2-custom-errors/1-format-error/solution.md diff --git a/1-js/08-error-handling/2-custom-errors/1-format-error/task.md b/1-js/10-error-handling/2-custom-errors/1-format-error/task.md similarity index 100% rename from 1-js/08-error-handling/2-custom-errors/1-format-error/task.md rename to 1-js/10-error-handling/2-custom-errors/1-format-error/task.md diff --git a/1-js/08-error-handling/2-custom-errors/article.md b/1-js/10-error-handling/2-custom-errors/article.md similarity index 78% rename from 1-js/08-error-handling/2-custom-errors/article.md rename to 1-js/10-error-handling/2-custom-errors/article.md index e4e458f753..3e3d118557 100644 --- a/1-js/08-error-handling/2-custom-errors/article.md +++ b/1-js/10-error-handling/2-custom-errors/article.md @@ -6,7 +6,11 @@ JavaScript 允许我们在使用 `throw` 时带任何参数,所以从技术层面上说,我们自定义的错误不需要继承 `Error` 类,但如果我们继承了这个类,就能使用 `obj instanceof Error` 来鉴别错误对象,所以我们最好继承它。 +<<<<<<< HEAD:1-js/08-error-handling/2-custom-errors/article.md 在我们进行开发时,我们自己的异常类通常是有层次结构的,例如 `HttpTimeoutError` 可能继承自 `HttpError` 等。 +======= +As the application grows, our own errors naturally form a hierarchy, for instance `HttpTimeoutError` may inherit from `HttpError`, and so on. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/2-custom-errors/article.md ## 扩展错误 @@ -19,7 +23,11 @@ let json = `{ "name": "John", "age": 30 }`; 在这里面,我们使用 `JSON.parse`。如果它接收到错误的 `json`,就会抛出 `SyntaxError`。 +<<<<<<< HEAD:1-js/08-error-handling/2-custom-errors/article.md 但即使是格式正确的 `json`,也并不表示它就是可用的,对吧?它有可能会遗漏一些必要的数据。例如,缺失了对用户所必需的 `name` 和 `age` 属性。 +======= +But even if `json` is syntactically correct, that doesn't mean that it's a valid user, right? It may miss the necessary data. For instance, it may not have `name` and `age` properties that are essential for our users. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/2-custom-errors/article.md 函数 `readUser(json)` 不仅会读取 JSON,也会检查(验证)数据。如果没有所需要的字段,或者格式不正确,那也是错误。而这不是 `SyntaxError`,因为数据在语法上是正确的,但是有其他的错误。我们称之为 `ValidationError` 并且为之创建一个类。这种类型的错误也应该承载缺少的字段的信息。 @@ -126,7 +134,11 @@ try { 使用 `instanceof` 的做法会好很多,因为我们在以后会扩展 `ValidationError`,创造一个它的子类型,例如 `PropertyRequiredError`。而 `instanceof` 对于新的继承类也适用。所以这是个长远的保证。 +<<<<<<< HEAD:1-js/08-error-handling/2-custom-errors/article.md 还有一点很重要,在 `catch` 语句捕捉到未知的错误时,它会在抛出行 `(**)` 处重新抛出,`catch` 语句仅仅知道如何处理验证和语法错误,而其他错误(代码中的打印错误等)不应该被捕获。 +======= +Also it's important that if `catch` meets an unknown error, then it rethrows it in the line `(**)`. The `catch` block only knows how to handle validation and syntax errors, other kinds (due to a typo in the code or other unknown ones) should fall through. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/2-custom-errors/article.md ## 更进一步的继承 @@ -185,7 +197,11 @@ try { 这个 `PropertyRequiredError` 十分容易上手:我们只需要传递属性名:`new PropertyRequiredError(property)`。易懂的 `message` 属性将会由构造器提供。 +<<<<<<< HEAD:1-js/08-error-handling/2-custom-errors/article.md 需要注意的是,在 `PropertyRequiredError` 构造器中的 `this.name` 是再次进行手动赋值的。这可能会造成冗余 —— 在创建每个自定义错误的时候都要进行赋值 `this.name = `。但这并不是唯一的办法。我们可以创建自己的“基础异常”类,通过将 `this.constructor.name` 赋值给 `this.name` 来卸下我们肩上的负担,然后再进行继承。 +======= +Please note that `this.name` in `PropertyRequiredError` constructor is again assigned manually. That may become a bit tedious -- to assign `this.name = ` in every custom error class. But there's a way out. We can make our own "basic error" class that removes this burden from our shoulders by using `this.constructor.name` for `this.name` in its constructor. And then inherit all ours custom errors from it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/2-custom-errors/article.md 我们称其为 `MyError`。 @@ -218,7 +234,11 @@ alert( new PropertyRequiredError("field").name ); // PropertyRequiredError ## 包装异常 +<<<<<<< HEAD:1-js/08-error-handling/2-custom-errors/article.md 上述代码中的函数 `readUser` 的目的就是“读取用户数据”,对吧?在此过程中可能会出现多个不同类型的异常,目前我们有 `SyntaxError` 和 `ValidationError`,但在将来,函数 `readUser` 将会不断壮大,新添加的代码或许会导致其他类型的异常。 +======= +The purpose of the function `readUser` in the code above is "to read the user data", right? There may occur different kinds of errors in the process. Right now we have `SyntaxError` and `ValidationError`, but in the future `readUser` function may grow and probably generate other kinds of errors. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/2-custom-errors/article.md 调用函数 `readUser` 的代码要能够处理这些异常。现在它在 `catch` 语句块中使用多个 `if` 语句来检测不同类型的异常以及抛出未知异常。但如果函数 `readUser` 抛出了多种异常 —— 我们扪心自问:我们真的需要一个接一个地处理它抛出的异常吗? @@ -302,6 +322,12 @@ try { ## 总结 +<<<<<<< HEAD:1-js/08-error-handling/2-custom-errors/article.md - 我们能够正常地继承 `Error` 以及其他内置的错误类,只需要注意 `name` 属性以及不要忘了调用 `super`。 - 大多数时候,我们应该使用 `instanceof` 来检测一些特定的异常。它也能够在继承中使用。但有时我们会发现来自第三方库的异常,并且不容易得到它的类。那么 `name` 属性就可用于这一类的检测。 - 包装异常是一种广泛应用的技术,当一个函数处理低级别的异常时,用一个高级别的对象来报告错误。低级别的异常有时会变成这个对象的属性,就像上面例子中的 `err.cause`,但这并不严格要求。 +======= +- We can inherit from `Error` and other built-in error classes normally, just need to take care of `name` property and don't forget to call `super`. +- We can use `instanceof` to check for particular errors. It also works with inheritance. But sometimes we have an error object coming from the 3rd-party library and there's no easy way to get the class. Then `name` property can be used for such checks. +- Wrapping exceptions is a widespread technique: a function handles low-level exceptions and creates higher-level errors instead of various low-level ones. Low-level exceptions sometimes become properties of that object like `err.cause` in the examples above, but that's not strictly required. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/10-error-handling/2-custom-errors/article.md diff --git a/1-js/08-error-handling/index.md b/1-js/10-error-handling/index.md similarity index 100% rename from 1-js/08-error-handling/index.md rename to 1-js/10-error-handling/index.md diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.md b/1-js/11-async/01-callbacks/01-animate-circle-callback/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.md rename to 1-js/11-async/01-callbacks/01-animate-circle-callback/solution.md diff --git a/6-async/01-callbacks/01-animate-circle-callback/solution.view/index.html b/1-js/11-async/01-callbacks/01-animate-circle-callback/solution.view/index.html similarity index 100% rename from 6-async/01-callbacks/01-animate-circle-callback/solution.view/index.html rename to 1-js/11-async/01-callbacks/01-animate-circle-callback/solution.view/index.html diff --git a/6-async/01-callbacks/01-animate-circle-callback/task.md b/1-js/11-async/01-callbacks/01-animate-circle-callback/task.md similarity index 100% rename from 6-async/01-callbacks/01-animate-circle-callback/task.md rename to 1-js/11-async/01-callbacks/01-animate-circle-callback/task.md diff --git a/6-async/01-callbacks/article.md b/1-js/11-async/01-callbacks/article.md similarity index 70% rename from 6-async/01-callbacks/article.md rename to 1-js/11-async/01-callbacks/article.md index 817ec74a28..5074c9c375 100644 --- a/6-async/01-callbacks/article.md +++ b/1-js/11-async/01-callbacks/article.md @@ -23,7 +23,11 @@ function loadScript(src) { loadScript('/my/script.js'); ``` +<<<<<<< HEAD:6-async/01-callbacks/article.md 函数是**异步**调用的,因为动作不是此刻(加载脚本)完成的,而是之后。 +======= +The function is called "asynchronously," because the action (script loading) finishes not now, but later. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/01-callbacks/article.md 调用初始化脚本加载,然后继续执行。当脚本正在被加载时,下面的代码可能已经完成了执行,如果加载需要时间,那么同一时间,其他脚本可能也会被运行。 @@ -45,7 +49,11 @@ newFunction(); // 没有这个函数! */!* ``` +<<<<<<< HEAD:6-async/01-callbacks/article.md 很明显,浏览器没有时间去加载脚本。因此,对新函数的立即调用失败了。`loadScript` 函数并没有提供追踪加载完成时方法。脚本加载然后最终的运行,仅此而已。但我们希望了解脚本何时加载完成,以使用其中的新函数和新变量。 +======= +Naturally, the browser probably didn't have time to load the script. So the immediate call to the new function fails. As of now, the `loadScript` function doesn't provide a way to track the load completion. The script loads and eventually runs, that's all. But we'd like to know when it happens, to use new functions and variables from that script. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/01-callbacks/article.md 我们将 `callback` 函数作为第二个参数添加至 `loadScript` 中,函数在脚本加载时被执行: @@ -94,11 +102,19 @@ loadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', s 这被称为“基于回调”的异步编程风格。异步执行某些动作的函数,应该提供一个在函数完成时可以运行的 `callback` 参数。 +<<<<<<< HEAD:6-async/01-callbacks/article.md 我们 `loadScript` 中就是那么做的,但很明显这是一般性的方法。 +======= +Here we did it in `loadScript`, but of course, it's a general approach. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/01-callbacks/article.md ## 在回调中回调 +<<<<<<< HEAD:6-async/01-callbacks/article.md 如何顺序加载两个脚本:先是第一个,然后是第二个? +======= +How can we load two scripts sequentially: the first one, and then the second one after it? +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/01-callbacks/article.md 最明显的方法是将第二个 `loadScript` 调用放在回调中,就像这样: @@ -140,7 +156,11 @@ loadScript('/my/script.js', function(script) { ## 处理错误 +<<<<<<< HEAD:6-async/01-callbacks/article.md 上述示例中,我们并没有考虑错误因素。假如加载失败会如何?我们的回调应该可以立即对其做出响应。 +======= +In the above examples we didn't consider errors. What if the script loading fails? Our callback should be able to react on that. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/01-callbacks/article.md 这是可以跟踪错误的 `loadScript` 改进版: @@ -173,15 +193,27 @@ loadScript('/my/script.js', function(error, script) { 再一次强调,我们使用的 `loadScript` 方法是非常常规的。它被称为 "error-first callback" 风格。 +<<<<<<< HEAD:6-async/01-callbacks/article.md 惯例是: 1. `callback` 的第一个参数是为了错误发生而保留的。一旦发生错误,`callback(err)` 就会被调用。 2. 第二个参数(如果有需要)用于成功的结果。此时 `callback(null, result1, result2…)` 将被调用。 +======= +The convention is: +1. The first argument of the `callback` is reserved for an error if it occurs. Then `callback(err)` is called. +2. The second argument (and the next ones if needed) are for the successful result. Then `callback(null, result1, result2…)` is called. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/01-callbacks/article.md 因此单个 `callback` 函数可以同时具有报告错误以及传递返回结果的作用。 +<<<<<<< HEAD:6-async/01-callbacks/article.md ## 回调金字塔 从第一步可以看出,这是异步编码的一种可行性方案。的确如此,对于一个或两个的简单嵌套,这样的调用看起来非常好。 +======= +## Pyramid of Doom + +From the first look, it's a viable way of asynchronous coding. And indeed it is. For one or maybe two nested calls it looks fine. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/01-callbacks/article.md 但对于一个接一个的多个异步动作,代码就会变成这样: @@ -220,7 +252,11 @@ loadScript('1.js', function(error, script) { 如果嵌套变多,代码层次就会变深,维护难度也随之增加,尤其是如果我们有一个不是 `...` 的真实代码,就会包含更多的循环,条件语句等。 +<<<<<<< HEAD:6-async/01-callbacks/article.md 这有时称为“回调地狱”或者“回调金字塔”。 +======= +That's sometimes called "callback hell" or "pyramid of doom." +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/01-callbacks/article.md ![](callback-hell.png) @@ -260,6 +296,7 @@ function step3(error, script) { }; ``` +<<<<<<< HEAD:6-async/01-callbacks/article.md 看到了么?效果一样,但是没有深层的嵌套了,因为我们使每个动作都有一个独立的顶层函数。 这很有效,但代码看起来就像是一个被分裂的表格。你可能注意到了,它的可读性非常差。在阅读时,需要在块之间切换。这非常不方便,尤其是不熟悉代码的读者,他们甚至不知道该跳转到何处。 @@ -269,3 +306,14 @@ function step3(error, script) { 或许还有更好的方法。 幸运地是,有其他方法可以避免回调金字塔。其中一个最好的方法是使用 "promises",我们将在下一章中详细描述。 +======= +See? It does the same, and there's no deep nesting now because we made every action a separate top-level function. + +It works, but the code looks like a torn apart spreadsheet. It's difficult to read, and you probably noticed that one needs to eye-jump between pieces while reading it. That's inconvenient, especially if the reader is not familiar with the code and doesn't know where to eye-jump. + +Also, the functions named `step*` are all of single use, they are created only to avoid the "pyramid of doom." No one is going to reuse them outside of the action chain. So there's a bit of a namespace cluttering here. + +We'd like to have something better. + +Luckily, there are other ways to avoid such pyramids. One of the best ways is to use "promises," described in the next chapter. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/01-callbacks/article.md diff --git a/1-js/11-async/01-callbacks/callback-hell.png b/1-js/11-async/01-callbacks/callback-hell.png new file mode 100644 index 0000000000..567ec284e6 Binary files /dev/null and b/1-js/11-async/01-callbacks/callback-hell.png differ diff --git a/1-js/11-async/01-callbacks/callback-hell@2x.png b/1-js/11-async/01-callbacks/callback-hell@2x.png new file mode 100644 index 0000000000..4d6b0ad703 Binary files /dev/null and b/1-js/11-async/01-callbacks/callback-hell@2x.png differ diff --git a/6-async/01-callbacks/one.js b/1-js/11-async/01-callbacks/one.js similarity index 100% rename from 6-async/01-callbacks/one.js rename to 1-js/11-async/01-callbacks/one.js diff --git a/6-async/02-promise-basics/01-re-resolve/solution.md b/1-js/11-async/02-promise-basics/01-re-resolve/solution.md similarity index 100% rename from 6-async/02-promise-basics/01-re-resolve/solution.md rename to 1-js/11-async/02-promise-basics/01-re-resolve/solution.md diff --git a/6-async/02-promise-basics/01-re-resolve/task.md b/1-js/11-async/02-promise-basics/01-re-resolve/task.md similarity index 100% rename from 6-async/02-promise-basics/01-re-resolve/task.md rename to 1-js/11-async/02-promise-basics/01-re-resolve/task.md diff --git a/6-async/02-promise-basics/02-delay-promise/solution.md b/1-js/11-async/02-promise-basics/02-delay-promise/solution.md similarity index 100% rename from 6-async/02-promise-basics/02-delay-promise/solution.md rename to 1-js/11-async/02-promise-basics/02-delay-promise/solution.md diff --git a/6-async/02-promise-basics/02-delay-promise/task.md b/1-js/11-async/02-promise-basics/02-delay-promise/task.md similarity index 100% rename from 6-async/02-promise-basics/02-delay-promise/task.md rename to 1-js/11-async/02-promise-basics/02-delay-promise/task.md diff --git a/3-animation/2-css-animations/3-animate-circle/solution.md b/1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.md similarity index 100% rename from 3-animation/2-css-animations/3-animate-circle/solution.md rename to 1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.md diff --git a/6-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html b/1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html similarity index 100% rename from 6-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html rename to 1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html diff --git a/6-async/02-promise-basics/03-animate-circle-promise/task.md b/1-js/11-async/02-promise-basics/03-animate-circle-promise/task.md similarity index 100% rename from 6-async/02-promise-basics/03-animate-circle-promise/task.md rename to 1-js/11-async/02-promise-basics/03-animate-circle-promise/task.md diff --git a/1-js/11-async/02-promise-basics/article.md b/1-js/11-async/02-promise-basics/article.md new file mode 100644 index 0000000000..bec6851afb --- /dev/null +++ b/1-js/11-async/02-promise-basics/article.md @@ -0,0 +1,338 @@ +# Promise + +Imagine that you're a top singer, and fans ask day and night for your upcoming single. + +To get some relief, you promise to send it to them when it's published. You give your fans a list to which they can subscribe for updates. They can fill in their email addresses, so that when the song becomes available, all subscribed parties instantly receive it. And even if something goes very wrong, say, if plans to publish the song are cancelled, they will still be notified. + +Everyone is happy, because the people don't crowd you any more, and fans, because they won't miss the single. + +This is a real-life analogy for things we often have in programming: + +1. A "producing code" that does something and takes time. For instance, the code loads data over a network. That's a "singer". +2. A "consuming code" that wants the result of the "producing code" once it's ready. Many functions may need that result. These are the "fans". +3. A *promise* is a special JavaScript object that links the "producing code" and the "consuming code" together. In terms of our analogy: this is the "subscription list". The "producing code" takes whatever time it needs to produce the promised result, and the "promise" makes that result available to all of the subscribed code when it's ready. + +The analogy isn't terribly accurate, because JavaScript promises are more complex than a simple subscription list: they have additional features and limitations. But it's fine to begin with. + +The constructor syntax for a promise object is: + +```js +let promise = new Promise(function(resolve, reject) { + // executor (the producing code, "singer") +}); +``` + +The function passed to `new Promise` is called the *executor*. When the promise is created, this executor function runs automatically. It contains the producing code, that should eventually produce a result. In terms of the analogy above: the executor is the "singer". + +The resulting `promise` object has internal properties: + +- `state` — initially "pending", then changes to either "fulfilled" or "rejected", +- `result` — an arbitrary value, initially `undefined`. + +When the executor finishes the job, it should call one of the functions that it gets as arguments: + +- `resolve(value)` — to indicate that the job finished successfully: + - sets `state` to `"fulfilled"`, + - sets `result` to `value`. +- `reject(error)` — to indicate that an error occurred: + - sets `state` to `"rejected"`, + - sets `result` to `error`. + +![](promise-resolve-reject.png) + +Later we'll see how these changes become known to "fans". + +Here's an example of a Promise constructor and a simple executor function with its "producing code" (the `setTimeout`): + +```js run +let promise = new Promise(function(resolve, reject) { + // the function is executed automatically when the promise is constructed + + // after 1 second signal that the job is done with the result "done" + setTimeout(() => *!*resolve("done")*/!*, 1000); +}); +``` + +We can see two things by running the code above: + +1. The executor is called automatically and immediately (by the `new Promise`). +2. The executor receives two arguments: `resolve` and `reject` — these functions are pre-defined by the JavaScript engine. So we don't need to create them. We only should call one of them when ready. + +After one second of "processing" the executor calls `resolve("done")` to produce the result: + +![](promise-resolve-1.png) + +That was an example of a successful job completion, a "fulfilled promise". + +And now an example of the executor rejecting the promise with an error: + +```js +let promise = new Promise(function(resolve, reject) { + // after 1 second signal that the job is finished with an error + setTimeout(() => *!*reject(new Error("Whoops!"))*/!*, 1000); +}); +``` + +![](promise-reject-1.png) + +To summarize, the executor should do a job (something that takes time usually) and then call `resolve` or `reject` to change the state of the corresponding Promise object. + +The Promise that is either resolved or rejected is called "settled", as opposed to a initially "pending" Promise. + +````smart header="There can be only a single result or an error" +The executor should call only one `resolve` or one `reject`. The promise's state change is final. + +All further calls of `resolve` and `reject` are ignored: + +```js +let promise = new Promise(function(resolve, reject) { + resolve("done"); + + reject(new Error("…")); // ignored + setTimeout(() => resolve("…")); // ignored +}); +``` + +The idea is that a job done by the executor may have only one result or an error. + +Also, `resolve`/`reject` expect only one argument (or none) and will ignore additional arguments. +```` + +```smart header="Reject with `Error` objects" +In case something goes wrong, we can call `reject` with any type of argument (just like `resolve`). But it is recommended to use `Error` objects (or objects that inherit from `Error`). The reasoning for that will soon become apparent. +``` + +````smart header="Immediately calling `resolve`/`reject`" +In practice, an executor usually does something asynchronously and calls `resolve`/`reject` after some time, but it doesn't have to. We also can call `resolve` or `reject` immediately, like this: + +```js +let promise = new Promise(function(resolve, reject) { + // not taking our time to do the job + resolve(123); // immediately give the result: 123 +}); +``` + +For instance, this might happen when we start to do a job but then see that everything has already been completed and cached. + +That's fine. We immediately have a resolved promise. +```` + +```smart header="The `state` and `result` are internal" +The properties `state` and `result` of the Promise object are internal. We can't directly access them from our "consuming code". We can use the methods `.then`/`.catch`/`.finally` for that. They are described below. +``` + +## Consumers: then, catch, finally + +A Promise object serves as a link between the executor (the "producing code" or "singer") and the consuming functions (the "fans"), which will receive the result or error. Consuming functions can be registered (subscribed) using methods `.then`, `.catch` and `.finally`. + +### then + +The most important, fundamental one is `.then`. + +The syntax is: + +```js +promise.then( + function(result) { *!*/* handle a successful result */*/!* }, + function(error) { *!*/* handle an error */*/!* } +); +``` + +The first argument of `.then` is a function that: + +1. runs when the promise is resolved, and +2. receives the result. + +The second argument of `.then` is a function that: + +1. runs when the promise is rejected, and +2. receives the error. + +For instance, here's a reaction to a successfully resolved promise: + +```js run +let promise = new Promise(function(resolve, reject) { + setTimeout(() => resolve("done!"), 1000); +}); + +// resolve runs the first function in .then +promise.then( +*!* + result => alert(result), // shows "done!" after 1 second +*/!* + error => alert(error) // doesn't run +); +``` + +The first function was executed. + +And in the case of a rejection -- the second one: + +```js run +let promise = new Promise(function(resolve, reject) { + setTimeout(() => reject(new Error("Whoops!")), 1000); +}); + +// reject runs the second function in .then +promise.then( + result => alert(result), // doesn't run +*!* + error => alert(error) // shows "Error: Whoops!" after 1 second +*/!* +); +``` + +If we're interested only in successful completions, then we can provide only one function argument to `.then`: + +```js run +let promise = new Promise(resolve => { + setTimeout(() => resolve("done!"), 1000); +}); + +*!* +promise.then(alert); // shows "done!" after 1 second +*/!* +``` + +### catch + +If we're interested only in errors, then we can use `null` as the first argument: `.then(null, errorHandlingFunction)`. Or we can use `.catch(errorHandlingFunction)`, which is exactly the same: + + +```js run +let promise = new Promise((resolve, reject) => { + setTimeout(() => reject(new Error("Whoops!")), 1000); +}); + +*!* +// .catch(f) is the same as promise.then(null, f) +promise.catch(alert); // shows "Error: Whoops!" after 1 second +*/!* +``` + +The call `.catch(f)` is a complete analog of `.then(null, f)`, it's just a shorthand. + +### finally + +Just like there's a `finally` clause in a regular `try {...} catch {...}`, there's `finally` in promises. + +The call `.finally(f)` is similar to `.then(f, f)` in the sense that it always runs when the promise is settled: be it resolve or reject. + +`finally` is a good handler for performing cleanup, e.g. stopping our loading indicators, as they are not needed any more, no matter what the outcome is. + +Like this: + +```js +new Promise((resolve, reject) => { + /* do something that takes time, and then call resolve/reject */ +}) +*!* + // runs when the promise is settled, doesn't matter successfully or not + .finally(() => stop loading indicator) +*/!* + .then(result => show result, err => show error) +``` + +It's not exactly an alias of `then(f,f)` though. There are several important differences: + +1. A `finally` handler has no arguments. In `finally` we don't know whether the promise is successful or not. That's all right, as our task is usually to perform "general" finalizing procedures. +2. A `finally` handler passes through results and errors to the next handler. + + For instance, here the result is passed through `finally` to `then`: + ```js run + new Promise((resolve, reject) => { + setTimeout(() => resolve("result"), 2000) + }) + .finally(() => alert("Promise ready")) + .then(result => alert(result)); // <-- .then handles the result + ``` + + And here there's an error in the promise, passed through `finally` to `catch`: + + ```js run + new Promise((resolve, reject) => { + throw new Error("error"); + }) + .finally(() => alert("Promise ready")) + .catch(err => alert(err)); // <-- .catch handles the error object + ``` + + That's very convenient, because `finally` is not meant to process a promise result. So it passes it through. + + We'll talk more about promise chaining and result-passing between handlers in the next chapter. + +3. Last, but not least, `.finally(f)` is a more convenient syntax than `.then(f, f)`: no need to duplicate the function `f`. + +````smart header="On settled promises handlers runs immediately" +If a promise is pending, `.then/catch/finally` handlers wait for the result. Otherwise, if a promise has already settled, they execute immediately: + +```js run +// an immediately resolved promise +let promise = new Promise(resolve => resolve("done!")); + +promise.then(alert); // done! (shows up right now) +``` + +The good thing is: a `.then` handler is guaranteed to run whether the promise takes time or settles it immediately. +```` + +Next, let's see more practical examples of how promises can help us to write asynchronous code. + +## Example: loadScript + +We've got the `loadScript` function for loading a script from the previous chapter. + +Here's the callback-based variant, just to remind us of it: + +```js +function loadScript(src, callback) { + let script = document.createElement('script'); + script.src = src; + + script.onload = () => callback(null, script); + script.onerror = () => callback(new Error(`Script load error for ${src}`)); + + document.head.append(script); +} +``` + +Let's rewrite it using Promises. + +The new function `loadScript` will not require a callback. Instead, it will create and return a Promise object that resolves when the loading is complete. The outer code can add handlers (subscribing functions) to it using `.then`: + +```js run +function loadScript(src) { + return new Promise(function(resolve, reject) { + let script = document.createElement('script'); + script.src = src; + + script.onload = () => resolve(script); + script.onerror = () => reject(new Error(`Script load error for ${src}`)); + + document.head.append(script); + }); +} +``` + +Usage: + +```js run +let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"); + +promise.then( + script => alert(`${script.src} is loaded!`), + error => alert(`Error: ${error.message}`) +); + +promise.then(script => alert('One more handler to do something else!')); +``` + +We can immediately see a few benefits over the callback-based pattern: + + +| Promises | Callbacks | +|----------|-----------| +| Promises allow us to do things in the natural order. First, we run `loadScript(script)`, and `.then` we write what to do with the result. | We must have a `callback` function at our disposal when calling `loadScript(script, callback)`. In other words, we must know what to do with the result *before* `loadScript` is called. | +| We can call `.then` on a Promise as many times as we want. Each time, we're adding a new "fan", a new subscribing function, to the "subscription list". More about this in the next chapter: [](info:promise-chaining). | There can be only one callback. | + +So Promises give us better code flow and flexibility. But there's more. We'll see that in the next chapters. diff --git a/6-async/02-promise-basics/head.html b/1-js/11-async/02-promise-basics/head.html similarity index 100% rename from 6-async/02-promise-basics/head.html rename to 1-js/11-async/02-promise-basics/head.html diff --git a/6-async/02-promise-basics/promise-init.png b/1-js/11-async/02-promise-basics/promise-init.png similarity index 100% rename from 6-async/02-promise-basics/promise-init.png rename to 1-js/11-async/02-promise-basics/promise-init.png diff --git a/6-async/02-promise-basics/promise-init@2x.png b/1-js/11-async/02-promise-basics/promise-init@2x.png similarity index 100% rename from 6-async/02-promise-basics/promise-init@2x.png rename to 1-js/11-async/02-promise-basics/promise-init@2x.png diff --git a/1-js/11-async/02-promise-basics/promise-reject-1.png b/1-js/11-async/02-promise-basics/promise-reject-1.png new file mode 100644 index 0000000000..3ae74879cf Binary files /dev/null and b/1-js/11-async/02-promise-basics/promise-reject-1.png differ diff --git a/1-js/11-async/02-promise-basics/promise-reject-1@2x.png b/1-js/11-async/02-promise-basics/promise-reject-1@2x.png new file mode 100644 index 0000000000..9eff3793c7 Binary files /dev/null and b/1-js/11-async/02-promise-basics/promise-reject-1@2x.png differ diff --git a/1-js/11-async/02-promise-basics/promise-resolve-1.png b/1-js/11-async/02-promise-basics/promise-resolve-1.png new file mode 100644 index 0000000000..b4bb51826b Binary files /dev/null and b/1-js/11-async/02-promise-basics/promise-resolve-1.png differ diff --git a/1-js/11-async/02-promise-basics/promise-resolve-1@2x.png b/1-js/11-async/02-promise-basics/promise-resolve-1@2x.png new file mode 100644 index 0000000000..ecb4af35df Binary files /dev/null and b/1-js/11-async/02-promise-basics/promise-resolve-1@2x.png differ diff --git a/1-js/11-async/02-promise-basics/promise-resolve-reject.png b/1-js/11-async/02-promise-basics/promise-resolve-reject.png new file mode 100644 index 0000000000..6f0294f013 Binary files /dev/null and b/1-js/11-async/02-promise-basics/promise-resolve-reject.png differ diff --git a/1-js/11-async/02-promise-basics/promise-resolve-reject@2x.png b/1-js/11-async/02-promise-basics/promise-resolve-reject@2x.png new file mode 100644 index 0000000000..b59301ff77 Binary files /dev/null and b/1-js/11-async/02-promise-basics/promise-resolve-reject@2x.png differ diff --git a/6-async/03-promise-chaining/01-then-vs-catch/solution.md b/1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md similarity index 100% rename from 6-async/03-promise-chaining/01-then-vs-catch/solution.md rename to 1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md diff --git a/6-async/03-promise-chaining/01-then-vs-catch/task.md b/1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md similarity index 56% rename from 6-async/03-promise-chaining/01-then-vs-catch/task.md rename to 1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md index 9c74794c76..ecebd38186 100644 --- a/6-async/03-promise-chaining/01-then-vs-catch/task.md +++ b/1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md @@ -3,10 +3,15 @@ 这两段代码片段是否相等?换句话说,对于任何处理函数在任何情况下,它们的行为方式是否相同? ```js -promise.then(f1, f2); +promise.then(f1).catch(f2); ``` +<<<<<<< HEAD:6-async/03-promise-chaining/01-then-vs-catch/task.md 对比: +======= +Versus: + +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md ```js -promise.then(f1).catch(f2); +promise.then(f1, f2); ``` diff --git a/1-js/11-async/03-promise-chaining/article.md b/1-js/11-async/03-promise-chaining/article.md new file mode 100644 index 0000000000..b2ba231ec5 --- /dev/null +++ b/1-js/11-async/03-promise-chaining/article.md @@ -0,0 +1,387 @@ + +# Promises chaining + +Let's return to the problem mentioned in the chapter : we have a sequence of asynchronous tasks to be done one after another. For instance, loading scripts. How can we code it well? + +Promises provide a couple of recipes to do that. + +In this chapter we cover promise chaining. + +It looks like this: + +```js run +new Promise(function(resolve, reject) { + + setTimeout(() => resolve(1), 1000); // (*) + +}).then(function(result) { // (**) + + alert(result); // 1 + return result * 2; + +}).then(function(result) { // (***) + + alert(result); // 2 + return result * 2; + +}).then(function(result) { + + alert(result); // 4 + return result * 2; + +}); +``` + +The idea is that the result is passed through the chain of `.then` handlers. + +Here the flow is: +1. The initial promise resolves in 1 second `(*)`, +2. Then the `.then` handler is called `(**)`. +3. The value that it returns is passed to the next `.then` handler `(***)` +4. ...and so on. + +As the result is passed along the chain of handlers, we can see a sequence of `alert` calls: `1` -> `2` -> `4`. + +![](promise-then-chain.png) + +The whole thing works, because a call to `promise.then` returns a promise, so that we can call the next `.then` on it. + +When a handler returns a value, it becomes the result of that promise, so the next `.then` is called with it. + +To make these words more clear, here's the start of the chain: + +```js run +new Promise(function(resolve, reject) { + + setTimeout(() => resolve(1), 1000); + +}).then(function(result) { + + alert(result); + return result * 2; // <-- (1) + +}) // <-- (2) +// .then… +``` + +The value returned by `.then` is a promise, that's why we are able to add another `.then` at `(2)`. When the value is returned in `(1)`, that promise becomes resolved, so the next handler runs with the value. + +**A classic newbie error: technically we can also add many `.then` to a single promise. This is not chaining.** + +For example: +```js run +let promise = new Promise(function(resolve, reject) { + setTimeout(() => resolve(1), 1000); +}); + +promise.then(function(result) { + alert(result); // 1 + return result * 2; +}); + +promise.then(function(result) { + alert(result); // 1 + return result * 2; +}); + +promise.then(function(result) { + alert(result); // 1 + return result * 2; +}); +``` + +What we did here is just several handlers to one promise. They don't pass the result to each other, instead they process it independently. + +Here's the picture (compare it with the chaining above): + +![](promise-then-many.png) + +All `.then` on the same promise get the same result -- the result of that promise. So in the code above all `alert` show the same: `1`. + +In practice we rarely need multiple handlers for one promise. Chaining is used much more often. + +## Returning promises + +Normally, a value returned by a `.then` handler is immediately passed to the next handler. But there's an exception. + +If the returned value is a promise, then the further execution is suspended until it settles. After that, the result of that promise is given to the next `.then` handler. + +For instance: + +```js run +new Promise(function(resolve, reject) { + + setTimeout(() => resolve(1), 1000); + +}).then(function(result) { + + alert(result); // 1 + +*!* + return new Promise((resolve, reject) => { // (*) + setTimeout(() => resolve(result * 2), 1000); + }); +*/!* + +}).then(function(result) { // (**) + + alert(result); // 2 + + return new Promise((resolve, reject) => { + setTimeout(() => resolve(result * 2), 1000); + }); + +}).then(function(result) { + + alert(result); // 4 + +}); +``` + +Here the first `.then` shows `1` returns `new Promise(…)` in the line `(*)`. After one second it resolves, and the result (the argument of `resolve`, here it's `result*2`) is passed on to handler of the second `.then` in the line `(**)`. It shows `2` and does the same thing. + +So the output is again 1 -> 2 -> 4, but now with 1 second delay between `alert` calls. + +Returning promises allows us to build chains of asynchronous actions. + +## Example: loadScript + +Let's use this feature with `loadScript` to load scripts one by one, in sequence: + +```js run +loadScript("/article/promise-chaining/one.js") + .then(function(script) { + return loadScript("/article/promise-chaining/two.js"); + }) + .then(function(script) { + return loadScript("/article/promise-chaining/three.js"); + }) + .then(function(script) { + // use functions declared in scripts + // to show that they indeed loaded + one(); + two(); + three(); + }); +``` + +This code can be made bit shorter with arrow functions: + +```js run +loadScript("/article/promise-chaining/one.js") + .then(script => loadScript("/article/promise-chaining/two.js")) + .then(script => loadScript("/article/promise-chaining/three.js")) + .then(script => { + // scripts are loaded, we can use functions declared there + one(); + two(); + three(); + }); +``` + + +Here each `loadScript` call returns a promise, and the next `.then` runs when it resolves. Then it initiates the loading of the next script. So scripts are loaded one after another. + +We can add more asynchronous actions to the chain. Please note that code is still "flat", it grows down, not to the right. There are no signs of "pyramid of doom". + +Please note that technically we can add `.then` directly to each `loadScript`, like this: + +```js run +loadScript("/article/promise-chaining/one.js").then(script1 => { + loadScript("/article/promise-chaining/two.js").then(script2 => { + loadScript("/article/promise-chaining/three.js").then(script3 => { + // this function has access to variables script1, script2 and script3 + one(); + two(); + three(); + }); + }); +}); +``` + +This code does the same: loads 3 scripts in sequence. But it "grows to the right". So we have the same problem as with callbacks. + +People who start to use promises sometimes don't know about chaining, so they write it this way. Generally, chaining is preferred. + +Sometimes it's ok to write `.then` directly, because the nested function has access to the outer scope. In the example above the most nested callback has access to all variables `script1`, `script2`, `script3`. But that's an exception rather than a rule. + + +````smart header="Thenables" +To be precise, `.then` may return an arbitrary "thenable" object, and it will be treated the same way as a promise. + +A "thenable" object is any object with a method `.then`. + +The idea is that 3rd-party libraries may implement "promise-compatible" objects of their own. They can have extended set of methods, but also be compatible with native promises, because they implement `.then`. + +Here's an example of a thenable object: + +```js run +class Thenable { + constructor(num) { + this.num = num; + } + then(resolve, reject) { + alert(resolve); // function() { native code } + // resolve with this.num*2 after the 1 second + setTimeout(() => resolve(this.num * 2), 1000); // (**) + } +} + +new Promise(resolve => resolve(1)) + .then(result => { + return new Thenable(result); // (*) + }) + .then(alert); // shows 2 after 1000ms +``` + +JavaScript checks the object returned by `.then` handler in the line `(*)`: if it has a callable method named `then`, then it calls that method providing native functions `resolve`, `reject` as arguments (similar to executor) and waits until one of them is called. In the example above `resolve(2)` is called after 1 second `(**)`. Then the result is passed further down the chain. + +This feature allows to integrate custom objects with promise chains without having to inherit from `Promise`. +```` + + +## Bigger example: fetch + +In frontend programming promises are often used for network requests. So let's see an extended example of that. + +We'll use the [fetch](mdn:api/WindowOrWorkerGlobalScope/fetch) method to load the information about the user from the remote server. The method is quite complex, it has many optional parameters, but the basic usage is quite simple: + +```js +let promise = fetch(url); +``` + +This makes a network request to the `url` and returns a promise. The promise resolves with a `response` object when the remote server responds with headers, but *before the full response is downloaded*. + +To read the full response, we should call a method `response.text()`: it returns a promise that resolves when the full text downloaded from the remote server, with that text as a result. + +The code below makes a request to `user.json` and loads its text from the server: + +```js run +fetch('/article/promise-chaining/user.json') + // .then below runs when the remote server responds + .then(function(response) { + // response.text() returns a new promise that resolves with the full response text + // when we finish downloading it + return response.text(); + }) + .then(function(text) { + // ...and here's the content of the remote file + alert(text); // {"name": "iliakan", isAdmin: true} + }); +``` + +There is also a method `response.json()` that reads the remote data and parses it as JSON. In our case that's even more convenient, so let's switch to it. + +We'll also use arrow functions for brevity: + +```js run +// same as above, but response.json() parses the remote content as JSON +fetch('/article/promise-chaining/user.json') + .then(response => response.json()) + .then(user => alert(user.name)); // iliakan +``` + +Now let's do something with the loaded user. + +For instance, we can make one more request to GitHub, load the user profile and show the avatar: + +```js run +// Make a request for user.json +fetch('/article/promise-chaining/user.json') + // Load it as json + .then(response => response.json()) + // Make a request to GitHub + .then(user => fetch(`https://api.github.com/users/${user.name}`)) + // Load the response as json + .then(response => response.json()) + // Show the avatar image (githubUser.avatar_url) for 3 seconds (maybe animate it) + .then(githubUser => { + let img = document.createElement('img'); + img.src = githubUser.avatar_url; + img.className = "promise-avatar-example"; + document.body.append(img); + + setTimeout(() => img.remove(), 3000); // (*) + }); +``` + +The code works, see comments about the details, but it should be quite self-descriptive. Although, there's a potential problem in it, a typical error of those who begin to use promises. + +Look at the line `(*)`: how can we do something *after* the avatar has finished showing and gets removed? For instance, we'd like to show a form for editing that user or something else. As of now, there's no way. + +To make the chain extendable, we need to return a promise that resolves when the avatar finishes showing. + +Like this: + +```js run +fetch('/article/promise-chaining/user.json') + .then(response => response.json()) + .then(user => fetch(`https://api.github.com/users/${user.name}`)) + .then(response => response.json()) +*!* + .then(githubUser => new Promise(function(resolve, reject) { +*/!* + let img = document.createElement('img'); + img.src = githubUser.avatar_url; + img.className = "promise-avatar-example"; + document.body.append(img); + + setTimeout(() => { + img.remove(); +*!* + resolve(githubUser); +*/!* + }, 3000); + })) + // triggers after 3 seconds + .then(githubUser => alert(`Finished showing ${githubUser.name}`)); +``` + +Now right after `setTimeout` runs `img.remove()`, it calls `resolve(githubUser)`, thus passing the control to the next `.then` in the chain and passing forward the user data. + +As a rule, an asynchronous action should always return a promise. + +That makes it possible to plan actions after it. Even if we don't plan to extend the chain now, we may need it later. + +Finally, we can split the code into reusable functions: + +```js run +function loadJson(url) { + return fetch(url) + .then(response => response.json()); +} + +function loadGithubUser(name) { + return fetch(`https://api.github.com/users/${name}`) + .then(response => response.json()); +} + +function showAvatar(githubUser) { + return new Promise(function(resolve, reject) { + let img = document.createElement('img'); + img.src = githubUser.avatar_url; + img.className = "promise-avatar-example"; + document.body.append(img); + + setTimeout(() => { + img.remove(); + resolve(githubUser); + }, 3000); + }); +} + +// Use them: +loadJson('/article/promise-chaining/user.json') + .then(user => loadGithubUser(user.name)) + .then(showAvatar) + .then(githubUser => alert(`Finished showing ${githubUser.name}`)); + // ... +``` + +## Summary + +If a `.then` (or `catch/finally`, doesn't matter) handler returns a promise, the rest of the chain waits until it settles. When it does, its result (or error) is passed further. + +Here's a full picture: + +![](promise-handler-variants.png) diff --git a/6-async/03-promise-chaining/getMessage.js b/1-js/11-async/03-promise-chaining/getMessage.js similarity index 100% rename from 6-async/03-promise-chaining/getMessage.js rename to 1-js/11-async/03-promise-chaining/getMessage.js diff --git a/archive/promise/thenable.js b/1-js/11-async/03-promise-chaining/head.html similarity index 52% rename from archive/promise/thenable.js rename to 1-js/11-async/03-promise-chaining/head.html index b72c0871e7..0a0075fb92 100644 --- a/archive/promise/thenable.js +++ b/1-js/11-async/03-promise-chaining/head.html @@ -1,4 +1,5 @@ -function loadScript(src) { + + diff --git a/6-async/03-promise-chaining/one.js b/1-js/11-async/03-promise-chaining/one.js similarity index 100% rename from 6-async/03-promise-chaining/one.js rename to 1-js/11-async/03-promise-chaining/one.js diff --git a/1-js/11-async/03-promise-chaining/promise-handler-variants-2.png b/1-js/11-async/03-promise-chaining/promise-handler-variants-2.png new file mode 100644 index 0000000000..712f57b828 Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-handler-variants-2.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-handler-variants-2@2x.png b/1-js/11-async/03-promise-chaining/promise-handler-variants-2@2x.png new file mode 100644 index 0000000000..5b57be8094 Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-handler-variants-2@2x.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-handler-variants.png b/1-js/11-async/03-promise-chaining/promise-handler-variants.png new file mode 100644 index 0000000000..2643fde89d Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-handler-variants.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-handler-variants@2x.png b/1-js/11-async/03-promise-chaining/promise-handler-variants@2x.png new file mode 100644 index 0000000000..313e241cc9 Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-handler-variants@2x.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-then-chain.png b/1-js/11-async/03-promise-chaining/promise-then-chain.png new file mode 100644 index 0000000000..fab8a92ad4 Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-then-chain.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-then-chain@2x.png b/1-js/11-async/03-promise-chaining/promise-then-chain@2x.png new file mode 100644 index 0000000000..421b593599 Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-then-chain@2x.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-then-many.png b/1-js/11-async/03-promise-chaining/promise-then-many.png new file mode 100644 index 0000000000..b13d20a9ef Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-then-many.png differ diff --git a/1-js/11-async/03-promise-chaining/promise-then-many@2x.png b/1-js/11-async/03-promise-chaining/promise-then-many@2x.png new file mode 100644 index 0000000000..f0609c9467 Binary files /dev/null and b/1-js/11-async/03-promise-chaining/promise-then-many@2x.png differ diff --git a/6-async/03-promise-chaining/three.js b/1-js/11-async/03-promise-chaining/three.js similarity index 100% rename from 6-async/03-promise-chaining/three.js rename to 1-js/11-async/03-promise-chaining/three.js diff --git a/6-async/03-promise-chaining/two.js b/1-js/11-async/03-promise-chaining/two.js similarity index 100% rename from 6-async/03-promise-chaining/two.js rename to 1-js/11-async/03-promise-chaining/two.js diff --git a/6-async/03-promise-chaining/user.json b/1-js/11-async/03-promise-chaining/user.json similarity index 100% rename from 6-async/03-promise-chaining/user.json rename to 1-js/11-async/03-promise-chaining/user.json diff --git a/6-async/03-promise-chaining/02-error-async/solution.md b/1-js/11-async/04-promise-error-handling/01-error-async/solution.md similarity index 100% rename from 6-async/03-promise-chaining/02-error-async/solution.md rename to 1-js/11-async/04-promise-error-handling/01-error-async/solution.md diff --git a/1-js/11-async/04-promise-error-handling/01-error-async/task.md b/1-js/11-async/04-promise-error-handling/01-error-async/task.md new file mode 100644 index 0000000000..78ea7d7b55 --- /dev/null +++ b/1-js/11-async/04-promise-error-handling/01-error-async/task.md @@ -0,0 +1,15 @@ +# setTimeout 中的错误 + +<<<<<<< HEAD:6-async/03-promise-chaining/02-error-async/task.md +你认为 `.catch` 会不会触发?请解释一下? +======= +What do you think? Will the `.catch` trigger? Explain your answer. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/04-promise-error-handling/01-error-async/task.md + +```js +new Promise(function(resolve, reject) { + setTimeout(() => { + throw new Error("Whoops!"); + }, 1000); +}).catch(alert); +``` diff --git a/1-js/11-async/04-promise-error-handling/article.md b/1-js/11-async/04-promise-error-handling/article.md new file mode 100644 index 0000000000..3424d099e1 --- /dev/null +++ b/1-js/11-async/04-promise-error-handling/article.md @@ -0,0 +1,345 @@ + +# Error handling with promises + +Asynchronous actions may sometimes fail: in case of an error the corresponding promise becomes rejected. For instance, `fetch` fails if the remote server is not available. We can use `.catch` to handle errors (rejections). + +Promise chaining is great at that aspect. When a promise rejects, the control jumps to the closest rejection handler down the chain. That's very convenient in practice. + +For instance, in the code below the URL is wrong (no such site) and `.catch` handles the error: + +```js run +*!* +fetch('https://no-such-server.blabla') // rejects +*/!* + .then(response => response.json()) + .catch(err => alert(err)) // TypeError: failed to fetch (the text may vary) +``` + +Or, maybe, everything is all right with the site, but the response is not valid JSON: + +```js run +fetch('/') // fetch works fine now, the server responds with the HTML page +*!* + .then(response => response.json()) // rejects: the page is HTML, not a valid json +*/!* + .catch(err => alert(err)) // SyntaxError: Unexpected token < in JSON at position 0 +``` + +The easiest way to catch all errors is to append `.catch` to the end of chain: + +```js run +fetch('/article/promise-chaining/user.json') + .then(response => response.json()) + .then(user => fetch(`https://api.github.com/users/${user.name}`)) + .then(response => response.json()) + .then(githubUser => new Promise((resolve, reject) => { + let img = document.createElement('img'); + img.src = githubUser.avatar_url; + img.className = "promise-avatar-example"; + document.body.append(img); + + setTimeout(() => { + img.remove(); + resolve(githubUser); + }, 3000); + })) +*!* + .catch(error => alert(error.message)); +*/!* +``` + +Normally, `.catch` doesn't trigger at all, because there are no errors. But if any of the promises above rejects (a network problem or invalid json or whatever), then it would catch it. + +## Implicit try..catch + +The code of a promise executor and promise handlers has an "invisible `try..catch`" around it. If an exception happens, it gets caught and treated as a rejection. + +For instance, this code: + +```js run +new Promise((resolve, reject) => { +*!* + throw new Error("Whoops!"); +*/!* +}).catch(alert); // Error: Whoops! +``` + +...Works exactly the same as this: + +```js run +new Promise((resolve, reject) => { +*!* + reject(new Error("Whoops!")); +*/!* +}).catch(alert); // Error: Whoops! +``` + +The "invisible `try..catch`" around the executor automatically catches the error and treats it as a rejection. + +This happens not only in the executor, but in its handlers as well. If we `throw` inside a `.then` handler, that means a rejected promise, so the control jumps to the nearest error handler. + +Here's an example: + +```js run +new Promise((resolve, reject) => { + resolve("ok"); +}).then((result) => { +*!* + throw new Error("Whoops!"); // rejects the promise +*/!* +}).catch(alert); // Error: Whoops! +``` + +This happens for all errors, not just those caused by the `throw` statement. For example, a programming error: + +```js run +new Promise((resolve, reject) => { + resolve("ok"); +}).then((result) => { +*!* + blabla(); // no such function +*/!* +}).catch(alert); // ReferenceError: blabla is not defined +``` + +The final `.catch` not only catches explicit rejections, but also occasional errors in the handlers above. + +## Rethrowing + +As we already noticed, `.catch` behaves like `try..catch`. We may have as many `.then` handlers as we want, and then use a single `.catch` at the end to handle errors in all of them. + +In a regular `try..catch` we can analyze the error and maybe rethrow it if can't handle. The same thing is possible for promises. + +If we `throw` inside `.catch`, then the control goes to the next closest error handler. And if we handle the error and finish normally, then it continues to the closest successful `.then` handler. + +In the example below the `.catch` successfully handles the error: + +```js run +// the execution: catch -> then +new Promise((resolve, reject) => { + + throw new Error("Whoops!"); + +}).catch(function(error) { + + alert("The error is handled, continue normally"); + +}).then(() => alert("Next successful handler runs")); +``` + +Here the `.catch` block finishes normally. So the next successful `.then` handler is called. + +In the example below we see the other situation with `.catch`. The handler `(*)` catches the error and just can't handle it (e.g. it only knows how to handle `URIError`), so it throws it again: + +```js run +// the execution: catch -> catch -> then +new Promise((resolve, reject) => { + + throw new Error("Whoops!"); + +}).catch(function(error) { // (*) + + if (error instanceof URIError) { + // handle it + } else { + alert("Can't handle such error"); + +*!* + throw error; // throwing this or another error jumps to the next catch +*/!* + } + +}).then(function() { + /* never runs here */ +}).catch(error => { // (**) + + alert(`The unknown error has occurred: ${error}`); + // don't return anything => execution goes the normal way + +}); +``` + +Then the execution jumps from the first `.catch` `(*)` to the next one `(**)` down the chain. + +In the section below we'll see a practical example of rethrowing. + +## Fetch error handling example + +Let's improve error handling for the user-loading example. + +The promise returned by [fetch](mdn:api/WindowOrWorkerGlobalScope/fetch) rejects when it's impossible to make a request. For instance, a remote server is not available, or the URL is malformed. But if the remote server responds with error 404, or even error 500, then it's considered a valid response. + +What if the server returns a non-JSON page with error 500 in the line `(*)`? What if there's no such user, and GitHub returns a page with error 404 at `(**)`? + +```js run +fetch('no-such-user.json') // (*) + .then(response => response.json()) + .then(user => fetch(`https://api.github.com/users/${user.name}`)) // (**) + .then(response => response.json()) + .catch(alert); // SyntaxError: Unexpected token < in JSON at position 0 + // ... +``` + + +As of now, the code tries to load the response as JSON no matter what and dies with a syntax error. You can see that by running the example above, as the file `no-such-user.json` doesn't exist. + +That's not good, because the error just falls through the chain, without details: what failed and where. + +So let's add one more step: we should check the `response.status` property that has HTTP status, and if it's not 200, then throw an error. + +```js run +class HttpError extends Error { // (1) + constructor(response) { + super(`${response.status} for ${response.url}`); + this.name = 'HttpError'; + this.response = response; + } +} + +function loadJson(url) { // (2) + return fetch(url) + .then(response => { + if (response.status == 200) { + return response.json(); + } else { + throw new HttpError(response); + } + }) +} + +loadJson('no-such-user.json') // (3) + .catch(alert); // HttpError: 404 for .../no-such-user.json +``` + +1. We make a custom class for HTTP Errors to distinguish them from other types of errors. Besides, the new class has a constructor that accepts `response` object and saves it in the error. So error-handling code will be able to access the response. +2. Then we put together the requesting and error-handling code into a function that fetches the `url` *and* treats any non-200 status as an error. That's convenient, because we often need such logic. +3. Now `alert` shows a more helpful descriptive message. + +The great thing about having our own class for errors is that we can easily check for it in error-handling code using `instanceof`. + +For instance, we can make a request, and then if we get 404 -- ask the user to modify the information. + +The code below loads a user with the given name from GitHub. If there's no such user, then it asks for the correct name: + +```js run +function demoGithubUser() { + let name = prompt("Enter a name?", "iliakan"); + + return loadJson(`https://api.github.com/users/${name}`) + .then(user => { + alert(`Full name: ${user.name}.`); + return user; + }) + .catch(err => { +*!* + if (err instanceof HttpError && err.response.status == 404) { +*/!* + alert("No such user, please reenter."); + return demoGithubUser(); + } else { + throw err; // (*) + } + }); +} + +demoGithubUser(); +``` + +Please note: `.catch` here catches all errors, but it "knows how to handle" only `HttpError 404`. In that particular case it means that there's no such user, and `.catch` just retries in that case. + +For other errors, it has no idea what could go wrong. Maybe a programming error or something. So it just rethrows it in the line `(*)`. + +## Unhandled rejections + +What happens when an error is not handled? For instance, after the rethrow `(*)` in the example above. + +Or we could just forget to append an error handler to the end of the chain, like here: + +```js untrusted run refresh +new Promise(function() { + noSuchFunction(); // Error here (no such function) +}) + .then(() => { + // successful promise handlers, one or more + }); // without .catch at the end! +``` + +In case of an error, the promise state becomes "rejected", and the execution should jump to the closest rejection handler. But there is no such handler in the examples above. So the error gets "stuck". There's no code to handle it. + +In practice, just like with a regular unhandled errors, it means that something has terribly gone wrong. + +What happens when a regular error occurs and is not caught by `try..catch`? The script dies. Similar thing happens with unhandled promise rejections. + +The JavaScript engine tracks such rejections and generates a global error in that case. You can see it in the console if you run the example above. + +In the browser we can catch such errors using the event `unhandledrejection`: + +```js run +*!* +window.addEventListener('unhandledrejection', function(event) { + // the event object has two special properties: + alert(event.promise); // [object Promise] - the promise that generated the error + alert(event.reason); // Error: Whoops! - the unhandled error object +}); +*/!* + +new Promise(function() { + throw new Error("Whoops!"); +}); // no catch to handle the error +``` + +The event is the part of the [HTML standard](https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections). + +If an error occurs, and there's no `.catch`, the `unhandledrejection` handler triggers, and gets the `event` object with the information about the error, so we can do something. + +Usually such errors are unrecoverable, so our best way out is to inform the user about the problem and probably report the incident to the server. + +In non-browser environments like Node.js there are other similar ways to track unhandled errors. + + +## Summary + +- `.catch` handles promise rejections of all kinds: be it a `reject()` call, or an error thrown in a handler. +- We should place `.catch` exactly in places where we want to handle errors and know how to handle them. The handler should analyze errors (custom error classes help) and rethrow unknown ones. +- It's ok not to use `.catch` at all, if there's no way to recover from an error. +- In any case we should have the `unhandledrejection` event handler (for browsers, and analogs for other environments), to track unhandled errors and inform the user (and probably our server) about the them, so that our app never "just dies". + +And finally, if we have load-indication, then `.finally` is a great handler to stop it when the fetch is complete: + +```js run +function demoGithubUser() { + let name = prompt("Enter a name?", "iliakan"); + +*!* + document.body.style.opacity = 0.3; // (1) start the indication +*/!* + + return loadJson(`https://api.github.com/users/${name}`) +*!* + .finally(() => { // (2) stop the indication + document.body.style.opacity = ''; + return new Promise(resolve => setTimeout(resolve)); // (*) + }) +*/!* + .then(user => { + alert(`Full name: ${user.name}.`); + return user; + }) + .catch(err => { + if (err instanceof HttpError && err.response.status == 404) { + alert("No such user, please reenter."); + return demoGithubUser(); + } else { + throw err; + } + }); +} + +demoGithubUser(); +``` + +Here on the line `(1)` we indicate loading by dimming the document. The method doesn't matter, could use any type of indication instead. + +When the promise is settled, be it a successful fetch or an error, `finally` triggers at the line `(2)` and stops the indication. + +There's a little browser trick `(*)` with returning a zero-timeout promise from `finally`. That's because some browsers (like Chrome) need "a bit time" outside promise handlers to paint document changes. So it ensures that the indication is visually stopped before going further on the chain. diff --git a/1-js/11-async/04-promise-error-handling/getMessage.js b/1-js/11-async/04-promise-error-handling/getMessage.js new file mode 100644 index 0000000000..6c5893433a --- /dev/null +++ b/1-js/11-async/04-promise-error-handling/getMessage.js @@ -0,0 +1,3 @@ +function getMessage() { + return "Hello, world!"; +} diff --git a/6-async/03-promise-chaining/head.html b/1-js/11-async/04-promise-error-handling/head.html similarity index 62% rename from 6-async/03-promise-chaining/head.html rename to 1-js/11-async/04-promise-error-handling/head.html index 31c6b42713..a0b7419623 100644 --- a/6-async/03-promise-chaining/head.html +++ b/1-js/11-async/04-promise-error-handling/head.html @@ -1,16 +1,4 @@ diff --git a/6-async/04-promise-api/01-promise-errors-as-results/task.md b/1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md similarity index 70% rename from 6-async/04-promise-api/01-promise-errors-as-results/task.md rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md index 086154c14f..f71d0f7de5 100644 --- a/6-async/04-promise-api/01-promise-errors-as-results/task.md +++ b/1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md @@ -17,16 +17,24 @@ Promise.all(urls.map(url => fetch(url))) for(let response of responses) { alert(`${response.url}: ${response.status}`); } - )); + }); ``` +<<<<<<< HEAD:6-async/04-promise-api/01-promise-errors-as-results/task.md 问题是如果任何请求都失败了,那么 `Promise.all` 就会 reject error,而且所有的其他请求结果都会丢失。 +======= +The problem is that if any of requests fails, then `Promise.all` rejects with the error, and we lose the results of all the other requests. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md 这并不好。 修改代码会导致 `(*)` 行的 `responses` 数组包含成功响应的对象和失败时获取的 error 对象。 +<<<<<<< HEAD:6-async/04-promise-api/01-promise-errors-as-results/task.md 例如,如果其中一个 URL 失效,那么就会变成这样: +======= +For instance, if one of the URLs is bad, then it should be like: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md ```js let urls = [ diff --git a/6-async/01-callbacks/01-animate-circle-callback/solution.md b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.md similarity index 100% rename from 6-async/01-callbacks/01-animate-circle-callback/solution.md rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.md diff --git a/6-async/04-promise-api/02-promise-errors-as-results-2/solution.view/index.html b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html similarity index 100% rename from 6-async/04-promise-api/02-promise-errors-as-results-2/solution.view/index.html rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html diff --git a/6-async/04-promise-api/02-promise-errors-as-results-2/source.view/index.html b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html similarity index 100% rename from 6-async/04-promise-api/02-promise-errors-as-results-2/source.view/index.html rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html diff --git a/6-async/04-promise-api/02-promise-errors-as-results-2/task.md b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md similarity index 60% rename from 6-async/04-promise-api/02-promise-errors-as-results-2/task.md rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md index 0382e5dda7..856c9f1aad 100644 --- a/6-async/04-promise-api/02-promise-errors-as-results-2/task.md +++ b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md @@ -1,6 +1,10 @@ # 用 JSON fetch 的容错处理 +<<<<<<< HEAD:6-async/04-promise-api/02-promise-errors-as-results-2/task.md 改进之前 任务的解决方案。我们现在只需要调用 `fetch`,但要从给定的 URL 中加载 JSON。 +======= +Improve the solution of the previous task . Now we need not just to call `fetch`, but to load the JSON objects from the given URLs. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md 这是改进后的代码: @@ -25,7 +29,11 @@ Promise.all(urls.map(url => fetch(url))) }); ``` +<<<<<<< HEAD:6-async/04-promise-api/02-promise-errors-as-results-2/task.md 问题是如果任意请求都失败了,那么 `Promise.all` 就会 reject error,而且会丢失其他所有请求的结果。因此上述代码不易于容错,与上一个任务相同。 +======= +The problem is that if any of requests fails, then `Promise.all` rejects with the error, and we lose results of all the other requests. So the code above is not fault-tolerant, just like the one in the previous task. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md 请修改代码后,保证 `(*)` 的数组中包含请求成功解析后的 JSON 和错误的 JSON。 diff --git a/6-async/04-promise-api/article.md b/1-js/11-async/05-promise-api/article.md similarity index 68% rename from 6-async/04-promise-api/article.md rename to 1-js/11-async/05-promise-api/article.md index 448d78cc97..972953ee3c 100644 --- a/6-async/04-promise-api/article.md +++ b/1-js/11-async/05-promise-api/article.md @@ -35,13 +35,17 @@ function loadCached(url) { return fetch(url) .then(response => response.text()) .then(text => { - cache[url] = text; + cache.set(url,text); return text; }); } ``` +<<<<<<< HEAD:6-async/04-promise-api/article.md 我们可以使用 `loadCached(url).then(…)`,因为函数保证会返回一个 promise。这是 `Promise.resolve` 在 `(*)` 行的目的:它确保了接口的统一性。我们可以在 `loadCached` 之后使用 `.then`。 +======= +We can use `loadCached(url).then(…)`, because the function is guaranteed to return a promise. That's the purpose `Promise.resolve` serves in the line `(*)`: it makes sure the interface is unified. We can always use `.then` after `loadCached`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/05-promise-api/article.md ## Promise.reject @@ -63,23 +67,37 @@ let promise = new Promise((resolve, reject) => reject(error)); ## Promise.all +<<<<<<< HEAD:6-async/04-promise-api/article.md 该方法并行运行多个 promise,并等待所有 promise 准备就绪。 +======= +Let's say we want to run many promises to execute in parallel, and wait till all of them are ready. + +For instance, download several URLs in parallel and process the content when all are done. + +That's what `Promise.all` is for. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/05-promise-api/article.md 语法: ```js -let promise = Promise.all(iterable); +let promise = Promise.all([...promises...]); ``` +<<<<<<< HEAD:6-async/04-promise-api/article.md 它需要一个带有 promise 的 `iterable` 对象,技术上来说,它是可以迭代的,但通常情况下,它只是一个数组,而且会返回一个新的 promise。新的 promise 是在所有 promise 都被解决并拥有一个存放结果的数组之后才出现的。 +======= +It takes an array of promises (technically can be any iterable, but usually an array) and returns a new promise. + +The new promise resolves when all listed promises are settled and has an array of their results. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/05-promise-api/article.md 例如,下面的 `Promise.all` 在 3 秒之后被处理,然后它的结果就是一个 `[1, 2, 3]` 数组: ```js run Promise.all([ - new Promise((resolve, reject) => setTimeout(() => resolve(1), 3000)), // 1 - new Promise((resolve, reject) => setTimeout(() => resolve(2), 2000)), // 2 - new Promise((resolve, reject) => setTimeout(() => resolve(3), 1000)) // 3 + new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1 + new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2 + new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3 ]).then(alert); // 1,2,3 when promises are ready: each promise contributes an array member ``` @@ -132,7 +150,6 @@ Promise.all(requests) 例如: - ```js run Promise.all([ new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)), @@ -145,12 +162,21 @@ Promise.all([ 这里的第二个 promise 在两秒内为 reject。这立即导致了对 `Promise.all` 的 reject。因此 `.catch` 被执行:避免 error 成为整个 `Promise.all` 的结果。 +<<<<<<< HEAD:6-async/04-promise-api/article.md 重要的细节是 promise 没有提供 "cancel" 或 "abort" 执行方法。因此,其他 promise 会继续执行,并最终为 settle,但它们的结果会被忽略。 +======= +The important detail is that promises provide no way to "cancel" or "abort" their execution. So other promises continue to execute, and then eventually settle, but all their results are ignored. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/05-promise-api/article.md 有避免这种情况的方法:我们可以编写额外的代码到 `clearTimeout`(或在出现 error 时取消)promise,或者我们可以将 error 作为结果数组中的成员显示出来(参阅本章下的 task)。 +<<<<<<< HEAD:6-async/04-promise-api/article.md ````smart header="`Promise.all(iterable)` 允许在 `iterable` 中无 promise" 通常 `Promise.all(iterable)` 接受可迭代的 promise(大多数情况是数组)。但如果这些对象中的任何一个不是 promise,它就会被封装进 `Promise.resolve`。 +======= +````smart header="`Promise.all(...)` allows non-promise items in `iterable`" +Normally, `Promise.all(...)` accepts an iterable (in most cases an array) of promises. But if any of those objects is not a promise, it's wrapped in `Promise.resolve`. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/05-promise-api/article.md 例如。这里的结果是 `[1, 2, 3]`: @@ -170,7 +196,11 @@ Promise.all([ ## Promise.race +<<<<<<< HEAD:6-async/04-promise-api/article.md 与 `Promise.all` 类似,所有的 promise 都是可迭代的,但不会等待所有都完成 —— 只等待第一个完成(或者有 error),然后继续执行。 +======= +Similar to `Promise.all`, it takes an iterable of promises, but instead of waiting for all of them to finish, it waits for the first result (or error), and goes on with it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/05-promise-api/article.md 语法是: @@ -194,9 +224,16 @@ Promise.race([ `Promise` 类有 4 中静态方法: +<<<<<<< HEAD:6-async/04-promise-api/article.md 1. `Promise.resolve(value)` —— 根据给定值返回 resolved promise, 2. `Promise.reject(error)` —— 根据给定错误返回 rejected promise, 3. `Promise.all(promises)` —— 等待所有的 promise 为 resolve 时返回存放它们结果的数组。如果任意给定的 promise 为 reject,那么它就会变成 `Promise.all` 的错误结果,所以所有的其他结果都会被忽略。 4. `Promise.race(promises)` —— 等待第一个 promise 被解决,其结果/错误即为结果。 +======= +1. `Promise.resolve(value)` -- makes a resolved promise with the given value. +2. `Promise.reject(error)` -- makes a rejected promise with the given error. +3. `Promise.all(promises)` -- waits for all promises to resolve and returns an array of their results. If any of the given promises rejects, then it becomes the error of `Promise.all`, and all other results are ignored. +4. `Promise.race(promises)` -- waits for the first promise to settle, and its result/error becomes the outcome. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb:1-js/11-async/05-promise-api/article.md 这四个方法中,`Promise.all` 在实战中使用的最多。 diff --git a/6-async/04-promise-api/head.html b/1-js/11-async/05-promise-api/head.html similarity index 100% rename from 6-async/04-promise-api/head.html rename to 1-js/11-async/05-promise-api/head.html diff --git a/1-js/11-async/05-promise-api/iliakan.json b/1-js/11-async/05-promise-api/iliakan.json new file mode 100644 index 0000000000..32f89971a8 --- /dev/null +++ b/1-js/11-async/05-promise-api/iliakan.json @@ -0,0 +1,4 @@ +{ + "name": "iliakan", + "isAdmin": true +} diff --git a/1-js/11-async/05-promise-api/one.js b/1-js/11-async/05-promise-api/one.js new file mode 100644 index 0000000000..948a60e075 --- /dev/null +++ b/1-js/11-async/05-promise-api/one.js @@ -0,0 +1,3 @@ +function one() { + alert(1); +} diff --git a/1-js/11-async/05-promise-api/two.js b/1-js/11-async/05-promise-api/two.js new file mode 100644 index 0000000000..b04795b86c --- /dev/null +++ b/1-js/11-async/05-promise-api/two.js @@ -0,0 +1,3 @@ +function two() { + alert(2); +} diff --git a/1-js/11-async/06-promisify/article.md b/1-js/11-async/06-promisify/article.md new file mode 100644 index 0000000000..7c84912b57 --- /dev/null +++ b/1-js/11-async/06-promisify/article.md @@ -0,0 +1,118 @@ +# Promisification + +Promisification -- is a long word for a simple transform. It's conversion of a function that accepts a callback into a function returning a promise. + +To be more precise, we create a wrapper-function that does the same, internally calling the original one, but returns a promise. + +Such transforms are often needed in real-life, as many functions and libraries are callback-based. But promises are more convenient. So it makes sense to promisify those. + +For instance, we have `loadScript(src, callback)` from the chapter . + +```js run +function loadScript(src, callback) { + let script = document.createElement('script'); + script.src = src; + + script.onload = () => callback(null, script); + script.onerror = () => callback(new Error(`Script load error for ${src}`)); + + document.head.append(script); +} + +// usage: +// loadScript('path/script.js', (err, script) => {...}) +``` + +Let's promisify it. The new `loadScriptPromise(src)` function will do the same, but accept only `src` (no callback) and return a promise. + +```js +let loadScriptPromise = function(src) { + return new Promise((resolve, reject) => { + loadScript(src, (err, script) => { + if (err) reject(err) + else resolve(script); + }); + }) +} + +// usage: +// loadScriptPromise('path/script.js').then(...) +``` + +Now `loadScriptPromise` fits well in our promise-based code. + +As we can see, it delegates all the work to the original `loadScript`, providing its own callback that translates to promise `resolve/reject`. + +As we may need to promisify many functions, it makes sense to use a helper. + +That's actually very simple -- `promisify(f)` below takes a to-promisify function `f` and returns a wrapper function. + +That wrapper does the same as in the code above: returns a promise and passes the call to the original `f`, tracking the result in a custom callback: + +```js +function promisify(f) { + return function (...args) { // return a wrapper-function + return new Promise((resolve, reject) => { + function callback(err, result) { // our custom callback for f + if (err) { + return reject(err); + } else { + resolve(result); + } + } + + args.push(callback); // append our custom callback to the end of arguments + + f.call(this, ...args); // call the original function + }); + }; +}; + +// usage: +let loadScriptPromise = promisify(loadScript); +loadScriptPromise(...).then(...); +``` + +Here we assume that the original function expects a callback with two arguments `(err, result)`. That's what we encounter most often. Then our custom callback is in exactly the right format, and `promisify` works great for such a case. + +But what if the original `f` expects a callback with more arguments `callback(err, res1, res2)`? + +Here's a modification of `promisify` that returns an array of multiple callback results: + +```js +// promisify(f, true) to get array of results +function promisify(f, manyArgs = false) { + return function (...args) { + return new Promise((resolve, reject) => { + function *!*callback(err, ...results*/!*) { // our custom callback for f + if (err) { + return reject(err); + } else { + // resolve with all callback results if manyArgs is specified + *!*resolve(manyArgs ? results : results[0]);*/!* + } + } + + args.push(callback); + + f.call(this, ...args); + }); + }; +}; + +// usage: +f = promisify(f, true); +f(...).then(arrayOfResults => ..., err => ...) +``` + +In some cases, `err` may be absent at all: `callback(result)`, or there's something exotic in the callback format, then we can promisify such functions without using the helper, manually. + +There are also modules with a bit more flexible promisification functions, e.g. [es6-promisify](https://github.com/digitaldesignlabs/es6-promisify). In Node.js, there's a built-in `util.promisify` function for that. + +```smart +Promisification is a great approach, especially when you use `async/await` (see the next chapter), but not a total replacement for callbacks. + +Remember, a promise may have only one result, but a callback may technically be called many times. + +So promisification is only meant for functions that call the callback once. Further calls will be ignored. +``` diff --git a/1-js/11-async/07-microtask-queue/article.md b/1-js/11-async/07-microtask-queue/article.md new file mode 100644 index 0000000000..1918ea04e9 --- /dev/null +++ b/1-js/11-async/07-microtask-queue/article.md @@ -0,0 +1,192 @@ + +# Microtasks and event loop + +Promise handlers `.then`/`.catch`/`.finally` are always asynchronous. + +Even when a Promise is immediately resolved, the code on the lines *below* `.then`/`.catch`/`.finally` will still execute before these handlers . + +Here's the demo: + +```js run +let promise = Promise.resolve(); + +promise.then(() => alert("promise done")); + +alert("code finished"); // this alert shows first +``` + +If you run it, you see `code finished` first, and then `promise done`. + +That's strange, because the promise is definitely done from the beginning. + +Why did the `.then` trigger afterwards? What's going on? + +## Microtasks + +Asynchronous tasks need proper management. For that, the standard specifies an internal queue `PromiseJobs`, more often referred to as "microtask queue" (v8 term). + +As said in the [specification](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues): + +- The queue is first-in-first-out: tasks enqueued first are run first. +- Execution of a task is initiated only when nothing else is running. + +Or, to say that simply, when a promise is ready, its `.then/catch/finally` handlers are put into the queue. They are not executed yet. JavaScript engine takes a task from the queue and executes it, when it becomes free from the current code. + +That's why "code finished" in the example above shows first. + +![](promiseQueue.png) + +Promise handlers always go through that internal queue. + +If there's a chain with multiple `.then/catch/finally`, then every one of them is executed asynchronously. That is, it first gets queued, and executed when the current code is complete and previously queued handlers are finished. + +**What if the order matters for us? How can we make `code finished` work after `promise done`?** + +Easy, just put it into the queue with `.then`: + +```js run +Promise.resolve() + .then(() => alert("promise done!")) + .then(() => alert("code finished")); +``` + +Now the order is as intended. + +## Event loop + +In-browser JavaScript execution flow, as well as Node.js, is based on an *event loop*. + +"Event loop" is a process when the engine sleeps and waits for events. When they occur - handles them and sleeps again. + +Events may come either comes from external sources, like user actions, or just as the end signal of an internal task. + +Examples of events: +- `mousemove`, a user moved their mouse. +- `setTimeout` handler is to be called. +- an external ` diff --git a/1-js/12-generators-iterators/index.md b/1-js/12-generators-iterators/index.md new file mode 100644 index 0000000000..ccc909d1aa --- /dev/null +++ b/1-js/12-generators-iterators/index.md @@ -0,0 +1,2 @@ + +# Generators, advanced iteration diff --git a/1-js/13-modules/01-modules-intro/article.md b/1-js/13-modules/01-modules-intro/article.md index 8535adb50b..edd0c5608a 100755 --- a/1-js/13-modules/01-modules-intro/article.md +++ b/1-js/13-modules/01-modules-intro/article.md @@ -1,4 +1,5 @@ +<<<<<<< HEAD # 模块 (Modules) 简介 当我们的应用日益增大时,我们想要将应用分割成多个文件,即我们所说的“模块”。 @@ -26,6 +27,35 @@ - `import` 关键字允许从其他模块中导入一些诸如函数之类的功能等等。 例如,我们有一个名为 `sayHi.js` 的文件导出一个函数: +======= +# Modules, introduction + +As our application grows bigger, we want to split it into multiple files, so called 'modules'. +A module usually contains a class or a library of useful functions. + +For a long time, JavaScript existed without a language-level module syntax. That wasn't a problem, because initially scripts were small and simple. So there was no need. + +But eventually scripts became more and more complex, so the community invented a variety of ways to organize code into modules, special libraries to load modules on demand. + +For instance: + +- [AMD](https://en.wikipedia.org/wiki/Asynchronous_module_definition) -- one of the most ancient module systems, initially implemented by the library [require.js](http://requirejs.org/). +- [CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1) -- the module system created for Node.js server. +- [UMD](https://github.com/umdjs/umd) -- one more module system, suggested as a universal one, compatible with AMD and CommonJS. + +Now all these slowly become a part of history, but we still can find them in old scripts. The language-level module system appeared in the standard in 2015, gradually evolved since then, and is now supported by all major browsers and in Node.js. + +## What is a module? + +A module is just a file, a single script, as simple as that. + +There are directives `export` and `import` to interchange functionality between modules, call functions of one module from another one: + +- `export` keyword labels variables and functions that should be accessible from outside the current module. +- `import` allows to import functionality from other modules. + +For instance, if we have a file `sayHi.js` exporting a function: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // 📁 sayHi.js @@ -34,7 +64,11 @@ export function sayHi(user) { } ``` +<<<<<<< HEAD 然后在其他的文件里导入并使用它: +======= +...Then another file may import and use it: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // 📁 main.js @@ -44,6 +78,7 @@ alert(sayHi); // function... sayHi('John'); // Hello, John! ``` +<<<<<<< HEAD 在这个章节里,我们专注于语言本身,但是我们使用浏览器作为演示环境,那么就让我们开始来看看怎么在浏览器中使用模块的。 由于模块使用特殊的关键词和功能,所以我们必须通过使用属性 ` ``` +<<<<<<< HEAD 在这里我们可以在浏览器里看到它,但是对于任何模块来说都是一样的。 ### 模块级作用域(Module-level scope) @@ -93,6 +150,31 @@ sayHi('John'); // Hello, John! ```html run @@ -103,6 +185,7 @@ sayHi('John'); // Hello, John! ``` +<<<<<<< HEAD 如果我们真的需要创建一个窗口级别(window-level)的全局变量,我们可以显式地将它分配给 `window` 并以 `window.user` 来访问它。但是这样做需要你有足够充分的理由,否则就不要这样。 ### 模块代码仅在第一次导入时解析 @@ -112,6 +195,17 @@ sayHi('John'); // Hello, John! 这具有很重要的后果。我们来看一下下面的例子: 首先,如果执行一个模块中的代码带来一些副作用,比如显示一个消息,然后多次导入它但是只会显示一次,即第一次: +======= +If we really need to make a window-level global variable, we can explicitly assign it to `window` and access as `window.user`. But that's an exception requiring a good reason. + +### A module code is evaluated only the first time when imported + +If the same module is imported into multiple other places, its code is executed only the first time, then exports are given to all importers. + +That has important consequences. Let's see that on examples. + +First, if executing a module code brings side-effects, like showing a message, then importing it multiple times will trigger it only once -- the first time: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // 📁 alert.js @@ -119,7 +213,11 @@ alert("Module is evaluated!"); ``` ```js +<<<<<<< HEAD // 从不同的文件导入相同模块 +======= +// Import the same module from different files +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb // 📁 1.js import `./alert.js`; // Module is evaluated! @@ -128,11 +226,19 @@ import `./alert.js`; // Module is evaluated! import `./alert.js`; // (nothing) ``` +<<<<<<< HEAD 在日常开发中,顶级模块主要是用于初始化使用的。我们创建数据结构,预填充它们,如果我们想要可重用某些东西,只要导出即可。 下面是一个高级点的例子: 我们假设一个模块导出了一个对象: +======= +In practice, top-level module code is mostly used for initialization. We create data structures, pre-fill them, and if we want something to be reusable -- export it. + +Now, a more advanced example. + +Let's say, a module exports an object: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // 📁 admin.js @@ -141,9 +247,15 @@ export let admin = { }; ``` +<<<<<<< HEAD 如果这个模块被导入到多个文件中,模块仅仅在第一次导入的时候解析创建 `admin` 对象。然后将其传入所有导入的位置。 所有导入位置都得到了唯一的 `admin` 对象。 +======= +If this module is imported from multiple files, the module is only evaluated the first time, `admin` object is created, and then passed to all further importers. + +All importers get exactly the one and only `admin` object: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // 📁 1.js @@ -155,6 +267,7 @@ import {admin} from './admin.js'; alert(admin.name); // Pete *!* +<<<<<<< HEAD // 1.js 和 2.js 导入相同的对象 // 1.js 中对对象的修改,在 2.js 中是可访问的 */!* @@ -165,6 +278,18 @@ alert(admin.name); // Pete 这种行为对于需要配置的模块来说是非常棒的。我们可以在第一次导入时设置所需要的属性,然后在后面的导入中就可以直接使用了。 例如,下面的 `admin.js` 模块可能提供特定的功能,但是希望在外部可访问 `admin` 对象: +======= +// Both 1.js and 2.js imported the same object +// Changes made in 1.js are visible in 2.js +*/!* +``` + +So, let's reiterate -- the module is executed only once. Exports are generated, and then they are shared between importers, so if something changes the `admin` object, other modules will see that . + +Such behavior is great for modules that require configuration. We can set required properties on the first import, and then in further imports it's ready. + +For instance, `admin.js` module may provide certain functionality, but expect the credentials to come into the `admin` object from outside: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // 📁 admin.js @@ -175,7 +300,11 @@ export function sayHi() { } ``` +<<<<<<< HEAD 现在,在 `init.js`——我们 app 的第一个脚本中,设置了 `admin.name`。现在每个位置都能看到它了,包括来自 `admin.js` 本身的调用。 +======= +Now, in `init.js`, the first script of our app, we set `admin.name`. Then everyone will see it, including calls made from inside `admin.js` itself: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```js // 📁 init.js @@ -194,6 +323,7 @@ sayHi(); // Ready to serve, *!*Pete*/!*! ### import.meta +<<<<<<< HEAD `import.meta` 对象包含当前模块的一些信息。 它的内容取决于其所在环境,比如说在浏览器环境中,它包含脚本的链接,如果是在 HTML 中的话就是当前页面的链接。 @@ -209,6 +339,23 @@ sayHi(); // Ready to serve, *!*Pete*/!*! 这是一个小功能,但为了完整性,我们应该提到它。 在一个模块中,顶级 `this` 是未定义的,而不是像非模块脚本中的全局变量。 +======= +The object `import.meta` contains the information about the current module. + +Its content depends on the environment. In the browser, it contains the url of the script, or a current webpage url if inside HTML: + +```html run height=0 + +``` + +### Top-level "this" is undefined + +That's kind of a minor feature, but for completeness we should mention it. + +In a module, top-level `this` is undefined, as opposed to a global object in non-module scripts: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```html run height=0 ``` +<<<<<<< HEAD ## 特定于浏览器的功能 与常规脚本相比,拥有 `type="module"` 标识的脚本有几个特定于浏览器的差异。 @@ -238,10 +386,31 @@ sayHi(); // Ready to serve, *!*Pete*/!*! 它的一个副作用是,模块脚本总是“看见”完全加载的 HTML 页面,包括在它们后面的 HTML 元素。 例如: +======= +## Browser-specific features + +There are also several browser-specific differences of scripts with `type="module"` compared to regular ones. + +You may want skip those for now if you're reading for the first time, or if you don't use JavaScript in a browser. + +### Module scripts are deferred + +Module scripts are *always* deferred, same effect as `defer` attribute (described in the chapter [](info:script-async-defer)), for both external and inline scripts. + +In other words: +- external module scripts ` + +Compare to regular script below: + + ``` +<<<<<<< HEAD 注意:上面的第二个脚本要先于前一个脚本执行,所以我们先会看到 `undefined`,然后才是 `object`。 这是因为模块脚本被延迟执行了,所以要等到页面加载结束才执行。而普通脚本就没有这个限制了,它会马上执行,所以我们先看到它的输出。 @@ -278,6 +462,27 @@ sayHi(); // Ready to serve, *!*Pete*/!*! ```html +======= +Please note: the second script actually works before the first! So we'll see `undefined` first, and then `object`. + +That's because modules are deferred, so way wait for the document to be processed. The regular scripts runs immediately, so we saw its output first. + +When using modules, we should be aware that HTML-page shows up as it loads, and JavaScript modules run after that, so the user may see the page before the JavaScript application is ready. Some functionality may not work yet. We should put transparent overlays or "loading indicators", or otherwise ensure that the visitor won't be confused by that. + +### Async works on inline scripts + +Async attribute ` ``` +<<<<<<< HEAD ### 外部脚本 外部脚本相较于其他脚本有两个显著的差异: @@ -292,10 +498,20 @@ sayHi(); // Ready to serve, *!*Pete*/!*! 1. 具有相同 `src` 属性值的外部脚本仅运行一次: ```html +======= +### External scripts + +There are two notable differences of external module scripts: + +1. External scripts with same `src` run only once: + ```html + +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ``` +<<<<<<< HEAD 2. 从其他域名获取的外部脚本需要加上 [CORS](mdn:Web/HTTP/CORS) 头。换句话说,如果一个模块脚本是从其他域名获取的,那么它所在的远端服务器必须提供 `Access-Control-Allow-Origin: *`(可能使用加载的域名代替 `*`)响应头以指明当前请求是被允许的。 ```html @@ -320,6 +536,32 @@ import {sayHi} from 'sayHi'; // Error,“裸”模块 ### 兼容性,"nomodule" 旧时的浏览器不理解 `type="module"` 值。对于位置类型的脚本会被忽略掉。对于它们来说可以使用 `nomodule` 属性来提供后备: +======= +2. External scripts that are fetched from another domain require [CORS](mdn:Web/HTTP/CORS) headers. In other words, if a module script is fetched from another domain, the remote server must supply a header `Access-Control-Allow-Origin: *` (may use fetching domain instead of `*`) to indicate that the fetch is allowed. + ```html + + + + ``` + + That ensures better security by default. + +### No "bare" modules allowed + +In the browser, `import` must get either a relative or absolute URL. Modules without any path are called "bare" modules. Such modules are not allowed in `import`. + +For instance, this `import` is invalid: +```js +import {sayHi} from 'sayHi'; // Error, "bare" module +// the module must have a path, e.g. './sayHi.js' or wherever the module is +``` + +Certain environments, like Node.js or bundle tools allow bare modules, without any path, as they have own ways for finding modules and hooks to fine-tune them. But browsers do not support bare modules yet. + +### Compatibility, "nomodule" + +Old browsers do not understand `type="module"`. Scripts of the unknown type are just ignored. For them, it's possible to provide a fallback using `nomodule` attribute: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```html run ``` +<<<<<<< HEAD 如果我们使用打包工具,当脚本被打包进一个单一文件(或者几个文件),在这些脚本中,`import/export` 语句被特殊的打包函数处理后替代。因此最终打包好的脚本不包含任何 `import/export` 语句,它也不需要 `type="module"` 属性,我们仅像普通脚本一样使用就好了: ```html @@ -377,3 +620,50 @@ import {sayHi} from 'sayHi'; // Error,“裸”模块 在生产环境中,开发者经常基于性能或者其他原因而使用诸如 [Webpack](https://webpack.js.org) 这类的打包工具。 在下一章里,我们将会看到更多关于模块以及如何导入/导出的例子。 +======= +If we use bundle tools, then as scripts are bundled together into a single file (or few files), `import/export` statements inside those scripts are replaced by special bundler functions. So the resulting "bundled" script does not contain any `import/export`, it doesn't require `type="module"`, and we can put it into a regular script: + +```html + + +``` + +## Build tools + +In real-life, browser modules are rarely used in their "raw" form. Usually, we bundle them together with a special tool such as [Webpack](https://webpack.js.org/) and deploy to the production server. + +One of the benefits of using bundlers -- they give more control over how modules are resolved, allowing bare modules and much more, like CSS/HTML modules. + +Build tools do the following: + +1. Take a "main" module, the one intended to be put in ` + diff --git a/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js b/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js new file mode 100644 index 0000000000..cff234b7c5 --- /dev/null +++ b/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js @@ -0,0 +1,11 @@ +export function hi() { + alert(`Hello`); +} + +export function bye() { + alert(`Bye`); +} + +export default function() { + alert("Module loaded (export default)!"); +} diff --git a/1-js/13-modules/index.md b/1-js/13-modules/index.md new file mode 100644 index 0000000000..78fb060e89 --- /dev/null +++ b/1-js/13-modules/index.md @@ -0,0 +1,2 @@ + +# Modules diff --git a/1-js/plan3.txt b/1-js/plan3.txt deleted file mode 100644 index 4d553174f5..0000000000 --- a/1-js/plan3.txt +++ /dev/null @@ -1,5 +0,0 @@ -todo: - -intl? -proxy? -eval? diff --git a/10-misc/12-mutation-observer/article.md b/10-misc/12-mutation-observer/article.md new file mode 100644 index 0000000000..552c2a8907 --- /dev/null +++ b/10-misc/12-mutation-observer/article.md @@ -0,0 +1,250 @@ + +# Mutation observer + +`MutationObserver` is a built-in object that observes a DOM element and fires a callback in case of changes. + +We'll first see syntax, and then explore a real-world use case. + +## Syntax + +`MutationObserver` is easy to use. + +First, we create an observer with a callback-function: + +```js +let observer = new MutationObserver(callback); +``` + +And then attach it to a DOM node: + +```js +observer.observe(node, config); +``` + +`config` is an object with boolean options "what kind of changes to react on": +- `childList` -- changes in the direct children of `node`, +- `subtree` -- in all descendants of `node`, +- `attributes` -- attributes of `node`, +- `attributeOldValue` -- record the old value of attribute (infers `attributes`), +- `characterData` -- whether to observe `node.data` (text content), +- `characterDataOldValue` -- record the old value of `node.data` (infers `characterData`), +- `attributeFilter` -- an array of attribute names, to observe only selected ones. + +Then after any changes, the `callback` is executed, with a list of [MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) objects as the first argument, and the observer itself as the second argument. + +[MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) objects have properties: + +- `type` -- mutation type, one of + - `"attributes"`: attribute modified + - `"characterData"`: data modified, used for text nodes, + - `"childList"`: child elements added/removed, +- `target` -- where the change occurred: an element for "attributes", or text node for "characterData", or an element for a "childList" mutation, +- `addedNodes/removedNodes` -- nodes that were added/removed, +- `previousSibling/nextSibling` -- the previous and next sibling to added/removed nodes, +- `attributeName/attributeNamespace` -- the name/namespace (for XML) of the changed attribute, +- `oldValue` -- the previous value, only for attribute or text changes. + + +For example, here's a `
` with `contentEditable` attribute. That attribute allows us to focus on it and edit. + +```html run +
Click and edit, please
+ + +``` + +If we change the text inside `me`, we'll get a single mutation: + +```js +mutationRecords = [{ + type: "characterData", + oldValue: "me", + target: , + // other properties empty +}]; +``` + +If we select and remove the `me` altogether, we'll get multiple mutations: + +```js +mutationRecords = [{ + type: "childList", + target: , + removedNodes: [], + nextSibling: , + previousSibling: + // other properties empty +}, { + type: "characterData" + target: + // ...details depend on how the browser handles the change + // it may coalesce two adjacent text nodes "Edit " and ", please" into one node + // or it can just delete the extra space after "Edit". + // may be one mutation or a few +}]; +``` + +## Observer use case + +When `MutationObserver` is needed? Is there a scenario when such thing can be useful? + +We can track something like `contentEditable` and implement "undo/redo" functionality (record mutations and rollback/redo them on demand). There are also cases when `MutationObserver` is good from architectural standpoint. + +Let's say we're making a website about programming. Naturally, articles and other materials may contain source code snippets. + +An HTML markup of a code snippet looks like this: +```html +... +

+  // here's the code
+  let hello = "world";
+
+... +``` + +Also we'll use a JavaScript highlighting library on our site, e.g. [Prism.js](https://prismjs.com/). A call to `Prism.highlightElem(pre)` examines the contents of such `pre` elements and adds into them special tags and styles for colored syntax highlighting, similar to what you see in examples here, at this page. + +When to run that method? We can do it on `DOMContentLoaded` event, or at the bottom of the page. At that moment we have DOM ready, can search for elements `pre[class*="language"]` and call `Prism.highlightElem` on them: + +```js +// highlight all code snippets on the page +document.querySelectorAll('pre[class*="language"]').forEach(Prism.highlightElem); +``` + +Now the `
` snippet looks like this (without line numbers by default):
+
+```js
+// here's the code
+let hello = "world";
+```
+
+Everything's simple so far, right? There are `
` code snippets in HTML, we highlight them.
+
+Now let's go on. Let's say we're going to dynamically fetch materials from a server. We'll study methods for that [later in the tutorial](info:fetch-basics). For now it only matters that we fetch an HTML article from a webserver and display it on demand:
+
+```js
+let article = /* fetch new content from server */
+articleElem.innerHTML = article;
+```
+
+The new `article` HTML may contain code snippets. We need to call `Prism.highlightElem` on them, otherwise they won't get highlighted.
+
+**Where and when to call `Prism.highlightElem` for a dynamically loaded article?**
+
+We could append that call to the code that loads an article, like this:
+
+```js
+let article = /* fetch new content from server */
+articleElem.innerHTML = article;
+
+*!*
+let snippets = articleElem.querySelectorAll('pre[class*="language-"]');
+snippets.forEach(Prism.highlightElem);
+*/!*
+```
+
+...But imagine, we have many places in the code where we load contents: articles, quizzes, forum posts. Do we need to put the highlighting call everywhere? That's not very convenient, and also easy to forget.
+
+And what if the content is loaded by a third-party module? E.g. we have a forum written by someone else, that loads contents dynamically, and we'd like to add syntax highlighting to it. No one likes to patch third-party scripts.
+
+Luckily, there's another option.
+
+We can use `MutationObserver` to automatically detect code snippets inserted in the page and highlight them.
+
+So we'll handle the highlighting functionality in one place, relieving us from the need to integrate it.
+
+## Dynamic highlight demo
+
+Here's the working example.
+
+If you run this code, it starts observing the element below and highlighting any code snippets that appear there:
+
+```js run
+let observer = new MutationObserver(mutations => {
+
+  for(let mutation of mutations) {
+    // examine new nodes
+
+    for(let node of mutation.addedNodes) {
+      // we track only elements, skip other nodes (e.g. text nodes)
+      if (!(node instanceof HTMLElement)) continue;
+
+      // check the inserted element for being a code snippet
+      if (node.matches('pre[class*="language-"]')) {
+        Prism.highlightElement(node);
+      }
+
+      // maybe there's a code snippet somewhere in its subtree?
+      for(let elem of node.querySelectorAll('pre[class*="language-"]')) {
+        Prism.highlightElement(elem);
+      }
+    }
+  }
+
+});
+
+let demoElem = document.getElementById('highlight-demo');
+
+observer.observe(demoElem, {childList: true, subtree: true});
+```
+
+

Demo element with id="highlight-demo", obverved by the example above.

+ +The code below populates `innerHTML`. Please run the code above first, it will watch and highlight the new content: + +```js run +let demoElem = document.getElementById('highlight-demo'); + +// dynamically insert content with code snippets +demoElem.innerHTML = `A code snippet is below: +
 let hello = "world!"; 
+
Another one:
+
+
.class { margin: 5px; } 
+
+`; +``` + +Now we have `MutationObserver` that can track all highlighting in observed elements or the whole `document`. We can add/remove code snippets in HTML without thinking about it. + +## Additional methods + +There's a method to stop observing the node: + +- `observer.disconnect()` -- stops the observation. + +Additionally: + +- `mutationRecords = observer.takeRecords()` -- gets a list of unprocessed mutation records, those that happened, but the callback did not handle them. + +```js +// we're going to disconnect the observer +// it might have not yet handled some mutations +let mutationRecords = observer.takeRecords(); +// process mutationRecords + +// now all handled, disconnect +observer.disconnect(); +``` + +## Garbage collection + +Observers use weak references to nodes internally. That is: if a node is removed from DOM, and becomes unreachable, then it becomes garbage collected, an observer doesn't prevent that. + +## Summary + +`MutationObserver` can react on changes in DOM: attributes, added/removed elements, text content. + +We can use it to track changes introduced by other parts of our own or 3rd-party code. + +For example, to post-process dynamically inserted content, as demo `innerHTML`, like highlighting in the example above. diff --git a/10-misc/index.md b/10-misc/index.md new file mode 100644 index 0000000000..65ab3188a0 --- /dev/null +++ b/10-misc/index.md @@ -0,0 +1,4 @@ + +# Miscellaneous + +Not yet categorized articles. diff --git a/2-ui/1-document/01-browser-environment/article.md b/2-ui/1-document/01-browser-environment/article.md index a84c89c1fd..9b0a18919b 100644 --- a/2-ui/1-document/01-browser-environment/article.md +++ b/2-ui/1-document/01-browser-environment/article.md @@ -4,7 +4,11 @@ JavaScript 语言最初是为 Web 浏览器创建的。此后,它发展成为 平台可以是一个浏览器,一台网络服务器,一台洗衣机或其他**主机**。它们每个都提供特定于平台的功能。JavaScript 规范调用了**主机环境**。 +<<<<<<< HEAD 主机环境提供语言核心以外的平台特定对象和功能。Web 浏览器提供了一种控制网页的手段。Node.JS 提供了服务器端功能,等等。 +======= +A host environment provides platform-specific objects and functions additional to the language core. Web browsers give a means to control web pages. Node.js provides server-side features, and so on. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 以下是浏览器在 JavaScript 中运行时的一个鸟瞰图: diff --git a/2-ui/1-document/01-browser-environment/windowObjects.png b/2-ui/1-document/01-browser-environment/windowObjects.png index 81803bd09d..5408ecb158 100644 Binary files a/2-ui/1-document/01-browser-environment/windowObjects.png and b/2-ui/1-document/01-browser-environment/windowObjects.png differ diff --git a/2-ui/1-document/01-browser-environment/windowObjects@2x.png b/2-ui/1-document/01-browser-environment/windowObjects@2x.png index e6dae7c3d4..d84b380d57 100644 Binary files a/2-ui/1-document/01-browser-environment/windowObjects@2x.png and b/2-ui/1-document/01-browser-environment/windowObjects@2x.png differ diff --git a/2-ui/1-document/02-dom-nodes/article.md b/2-ui/1-document/02-dom-nodes/article.md index 733391d507..4c857028aa 100644 --- a/2-ui/1-document/02-dom-nodes/article.md +++ b/2-ui/1-document/02-dom-nodes/article.md @@ -106,7 +106,11 @@ drawHtmlTree(node3, 'div.domtree', 690, 150); 在生成 DOM 时,浏览器会自动处理文档中的错误,关闭标签等等。 +<<<<<<< HEAD 这样的“无效”文档: +======= +Such an document with unclosed tags: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ```html no-beautify

Hello @@ -176,7 +180,11 @@ drawHtmlTree(node6, 'div.domtree', 690, 500); 在这里我们看到一个新的树节点类型 —— *comment node*,标记为 `#comment`。 +<<<<<<< HEAD 我们可能会想 —— 为什么要将注释添加到 DOM 中?它不会以任何方式影响视觉表示。但是有一条规则 —— 如果 HTML 中有东西,那么它也必须在 DOM 树中。 +======= +We may think -- why is a comment added to the DOM? It doesn't affect the visual representation in any way. But there's a rule -- if something's in HTML, then it also must be in the DOM tree. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb **HTML 中的所有内容甚至注释都成为 DOM 的一部分。** @@ -209,7 +217,11 @@ drawHtmlTree(node6, 'div.domtree', 690, 500); 请注意,开发者工具中的 DOM 结构已经过简化。文本节点仅以文本形式显。根本没有“空白”(只有空格)的文本节点。这其实很好,因为大部分时间我们都对元素节点感兴趣。 +<<<<<<< HEAD 点击左上角的 按钮可以使用鼠标(或其他指针设备)从网页中选择一个节点并“检查”它(在“元素”选项卡中滚动到该节点)。当我们有一个巨大的 HTML 页面(和相应的巨大 DOM),并希望看到其中的一个特定元素的位置时,这很有用。 +======= +Clicking the button in the left-upper corner allows to choose a node from the webpage using a mouse (or other pointer devices) and "inspect" it (scroll to it in the Elements tab). This works great when we have a huge HTML page (and corresponding huge DOM) and would like to see the place of a particular element in it. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb 另一种方法是在网页上右键单击并在上下文菜单中选择“检查”。 @@ -225,7 +237,11 @@ drawHtmlTree(node6, 'div.domtree', 690, 500); ## 与控制台交互 +<<<<<<< HEAD 在我们研究 DOM 时,我们也可能想要使用 JavaScript。就比如:获取一个节点并运行一些代码来修改它,看看它长什么样。这里有一些在元素选项卡和控制台之间传输数据的提示。 +======= +As we explore the DOM, we also may want to apply JavaScript to it. Like: get a node and run some code to modify it, to see the result. Here are few tips to travel between the Elements tab and the console. +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb - 在元素标签中选择第一个 `

  • `。 - 按下 `key:Esc` —— 它将在元素标签下方打开控制台。 diff --git a/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md b/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md index d85ef0d8af..e0e40227e1 100644 --- a/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md +++ b/2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md @@ -1,4 +1,13 @@ +<<<<<<< HEAD 1. 是的,这是真的。`elem.lastChild` 一直是最后一个元素,它就没有 `nextSibling` ,所有如果存在子节点,这个问题的答案就是正确。 2. 不,这是错的,因为 `elem.children[0]` 是元素中的第一个子元素。但是或许有非元素的节点在它之前。所以 `previousSibling` 或许是一个文本节点。 请注意,对于这两种情况,如果没有子节点就会出现错误。比如说如果 `elem.lastChild` 是 `null`,我们就访问不到 `elem.lastChild.nextSibling`。 +======= +1. Yes, true. The element `elem.lastChild` is always the last one, it has no `nextSibling`. +2. No, wrong, because `elem.children[0]` is the first child *among elements*. But there may exist non-element nodes before it. So `previousSibling` may be a text node. Also, if there are no children, then trying to access `elem.children[0]` + +Please note: for both cases if there are no children, then there will be an error. + +If there are no children, `elem.lastChild` is `null`, so we can't access `elem.lastChild.nextSibling`. And the collection `elem.children` is empty (like an empty array `[]`). +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb diff --git a/2-ui/1-document/03-dom-navigation/article.md b/2-ui/1-document/03-dom-navigation/article.md index 8953f9494c..5a077cd6b0 100644 --- a/2-ui/1-document/03-dom-navigation/article.md +++ b/2-ui/1-document/03-dom-navigation/article.md @@ -7,11 +7,19 @@ libs: # 遍历 DOM +<<<<<<< HEAD DOM 让我们可以对元素和它们其中的内容做任何事,但是首先我们需要获取到对应的 DOM 对象,把这个对象赋予一个变量,然后我们才能修改这个对象。 对 DOM 的所有操作都是从 `document` 对象开始的。从这个对象我们可以到达任何节点。 这里是一张描述对象间链接的图片,通过这些链接我们可以在 DOM 节点之间遍历。 +======= +The DOM allows us to do anything with elements and their contents, but first we need to reach the corresponding DOM object. + +All operations on the DOM start with the `document` object. From it we can access any node. + +Here's a picture of links that allow for travel between DOM nodes: +>>>>>>> a0266c574c0ab8a0834dd38ed65e7e4ee27f9cdb ![](dom-links.png) @@ -86,7 +94,11 @@ DOM 让我们可以对元素和它们其中的内容做任何事,但是首先 ``` +<<<<<<< HEAD ...如果我们要找的是 `` 的子系元素,那我们可以先得到它直系子元素 `
    `,`