diff --git a/1-js/08-error-handling/2-custom-errors/1-format-error/task.md b/1-js/08-error-handling/2-custom-errors/1-format-error/task.md index 2c8e910fc0..36fd27a41b 100644 --- a/1-js/08-error-handling/2-custom-errors/1-format-error/task.md +++ b/1-js/08-error-handling/2-custom-errors/1-format-error/task.md @@ -2,13 +2,13 @@ importance: 5 --- -# Inherit from SyntaxError +# 继承 SyntaxError -Create a class `FormatError` that inherits from the built-in `SyntaxError` class. +创造一个继承自内置类 `SyntaxError` 的 `FormatError` 类。 -It should support `message`, `name` and `stack` properties. +它应该支持 `message`,`name` 和 `stack` 属性。 -Usage example: +用例: ```js let err = new FormatError("formatting error"); @@ -18,5 +18,5 @@ alert( err.name ); // FormatError alert( err.stack ); // stack alert( err instanceof FormatError ); // true -alert( err instanceof SyntaxError ); // true (because inherits from SyntaxError) +alert( err instanceof SyntaxError ); // true(因为它继承自 SyntaxError) ``` diff --git a/1-js/08-error-handling/2-custom-errors/article.md b/1-js/08-error-handling/2-custom-errors/article.md index d7d61166dd..e4e458f753 100644 --- a/1-js/08-error-handling/2-custom-errors/article.md +++ b/1-js/08-error-handling/2-custom-errors/article.md @@ -1,46 +1,46 @@ -# Custom errors, extending Error +# 自定义错误及扩展错误 -When we develop something, we often need our own error classes to reflect specific things that may go wrong in our tasks. For errors in network operations we may need `HttpError`, for database operations `DbError`, for searching operations `NotFoundError` and so on. +当我们在进行开发的时候,通常需要属于我们自己的错误类来反映任务中可能出现的特殊情况。对于网络操作错误,我们需要 `HttpError`,对于数据库操作错误,我们需要 `DbError`,对于搜索操作错误,我们需要 `NotFoundError`,等等。 -Our errors should support basic error properties like `message`, `name` and, preferably, `stack`. But they also may have other properties of their own, e.g. `HttpError` objects may have `statusCode` property with a value like `404` or `403` or `500`. +我们自定义的错误应该具有基本的错误属性,例如 `message`,`name` 以及更加详细的 `stack`。但是它们也会有属于自己的属性。举个例子,`HttpError` 对象会有一个 `statusCode` 属性,取值可能为 `404`、`403` 或 `500` 等。 -JavaScript allows to use `throw` with any argument, so technically our custom error classes don't need to inherit from `Error`. But if we inherit, then it becomes possible to use `obj instanceof Error` to identify error objects. So it's better to inherit from it. +JavaScript 允许我们在使用 `throw` 时带任何参数,所以从技术层面上说,我们自定义的错误不需要继承 `Error` 类,但如果我们继承了这个类,就能使用 `obj instanceof Error` 来鉴别错误对象,所以我们最好继承它。 -As we build our application, our own errors naturally form a hierarchy, for instance `HttpTimeoutError` may inherit from `HttpError`, and so on. +在我们进行开发时,我们自己的异常类通常是有层次结构的,例如 `HttpTimeoutError` 可能继承自 `HttpError` 等。 -## Extending Error +## 扩展错误 -As an example, let's consider a function `readUser(json)` that should read JSON with user data. +让我们用一个能够读取用户数据的函数 `readUser(json)` 来作为例子。 -Here's an example of how a valid `json` may look: +这里是一个可用的 `json` 的例子: ```js let json = `{ "name": "John", "age": 30 }`; ``` -Internally, we'll use `JSON.parse`. If it receives malformed `json`, then it throws `SyntaxError`. +在这里面,我们使用 `JSON.parse`。如果它接收到错误的 `json`,就会抛出 `SyntaxError`。 -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, if may not have `name` and `age` properties that are essential for our users. +但即使是格式正确的 `json`,也并不表示它就是可用的,对吧?它有可能会遗漏一些必要的数据。例如,缺失了对用户所必需的 `name` 和 `age` 属性。 -Our function `readUser(json)` will not only read JSON, but check ("validate") the data. If there are no required fields, or the format is wrong, then that's an error. And that's not a `SyntaxError`, because the data is syntactically correct, but another kind of error. We'll call it `ValidationError` and create a class for it. An error of that kind should also carry the information about the offending field. +函数 `readUser(json)` 不仅会读取 JSON,也会检查(验证)数据。如果没有所需要的字段,或者格式不正确,那也是错误。而这不是 `SyntaxError`,因为数据在语法上是正确的,但是有其他的错误。我们称之为 `ValidationError` 并且为之创建一个类。这种类型的错误也应该承载缺少的字段的信息。 -Our `ValidationError` class should inherit from the built-in `Error` class. +我们的 `ValidationError` 类应该继承自内置的 `Error` 类。 -That class is built-in, but we should have its approximate code before our eyes, to understand what we're extending. +`Error` 类是内置的,但是我们需要看一下大致的代码,来理解我们需要扩展什么。 -So here you are: +代码如下: ```js -// The "pseudocode" for the built-in Error class defined by JavaScript itself +// 由JavaScript本身定义的内置错误类“伪代码” class Error { constructor(message) { this.message = message; - this.name = "Error"; // (different names for different built-in error classes) - this.stack = ; // non-standard, but most environments support it + this.name = "Error"; //(不同内置错误类别的名称) + this.stack = ; // 非标准,但大多数环境支持它 } } ``` -Now let's go on and inherit `ValidationError` from it: +现在让我们开始用 `ValidationError` 来进行继承: ```js run untrusted *!* @@ -60,17 +60,17 @@ try { test(); } catch(err) { alert(err.message); // Whoops! - alert(err.name); // ValidationError - alert(err.stack); // a list of nested calls with line numbers for each + alert(err.name); // 验证错误 + alert(err.stack); // 每个行编号的嵌套调用列表 } ``` -Please take a look at the constructor: +来看看构造器: -1. In the line `(1)` we call the parent constructor. JavaScript requires us to call `super` in the child constructor, so that's obligatory. The parent constructor sets the `message` property. -2. The parent constructor also sets the `name` property to `"Error"`, so in the line `(2)` we reset it to the right value. +1. 行 `(1)` 被称为父类构造器。JavaScript 需要我们在子类构造器中调用 `super`,这是强制性的。父类构造器设定 `message` 属性。 +2. 父类构造器也设定 `name` 的值为 `"Error"`,所以在行 `(2)` 我们将其重置为正确的值 -Let's try to use it in `readUser(json)`: +让我们用 `readUser(json)` 来试试: ```js run class ValidationError extends Error { @@ -94,28 +94,28 @@ function readUser(json) { return user; } -// Working example with try..catch +// try..catch 实例 try { let user = readUser('{ "age": 25 }'); } catch (err) { if (err instanceof ValidationError) { *!* - alert("Invalid data: " + err.message); // Invalid data: No field: name + alert("Invalid data: " + err.message); // 无效的数据:缺失字段:name */!* } else if (err instanceof SyntaxError) { // (*) alert("JSON Syntax Error: " + err.message); } else { - throw err; // unknown error, rethrow it (**) + throw err; // 未知错误,再次抛出(**) } } ``` -The `try..catch` block in the code above handles both our `ValidationError` and the built-in `SyntaxError` from `JSON.parse`. +上面的 `try..catch` 代码块同时处理我们的 `ValidationError` 和来自 `JSON.parse` 的内置 `SyntaxError`。 -Please take a look at how we use `instanceof` to check for the specific error type in the line `(*)`. +接下来看看我们是如何使用 `instanceof` 来检测行 `(*)` 中的特定错误类型。 -We could also look at `err.name`, like this: +也看看 `err.name`,就像这样: ```js // ... @@ -124,13 +124,13 @@ We could also look at `err.name`, like this: // ... ``` -The `instanceof` version is much better, because in the future we are going to extend `ValidationError`, make subtypes of it, like `PropertyRequiredError`. And `instanceof` check will continue to work for new inheriting classes. So that's future-proof. +使用 `instanceof` 的做法会好很多,因为我们在以后会扩展 `ValidationError`,创造一个它的子类型,例如 `PropertyRequiredError`。而 `instanceof` 对于新的继承类也适用。所以这是个长远的保证。 -Also it's important that if `catch` meets an unknown error, then it rethrows it in the line `(**)`. The `catch` only knows how to handle validation and syntax errors, other kinds (due to a typo in the code or such) should fall through. +还有一点很重要,在 `catch` 语句捕捉到未知的错误时,它会在抛出行 `(**)` 处重新抛出,`catch` 语句仅仅知道如何处理验证和语法错误,而其他错误(代码中的打印错误等)不应该被捕获。 -## Further inheritance +## 更进一步的继承 -The `ValidationError` class is very generic. Many things may go wrong. The property may be absent or it may be in a wrong format (like a string value for `age`). Let's make a more concrete class `PropertyRequiredError`, exactly for absent properties. It will carry additional information about the property that's missing. +`ValidationError` 类是十分通用的。因此可能会在某些方面出错。属性可能缺失,格式可能发生错误(例如 `age` 属性的值为一个字符串)。让我们来创造一个更加具体的类 `PropertyRequiredError`,为属性缺失的错误而量身定做的。它将会承载属性缺失的相关信息。 ```js run class ValidationError extends Error { @@ -164,32 +164,32 @@ function readUser(json) { return user; } -// Working example with try..catch +// try..catch 实例 try { let user = readUser('{ "age": 25 }'); } catch (err) { if (err instanceof ValidationError) { *!* - alert("Invalid data: " + err.message); // Invalid data: No property: name + alert("Invalid data: " + err.message); // 无效的数据:缺失属性:name alert(err.name); // PropertyRequiredError alert(err.property); // name */!* } else if (err instanceof SyntaxError) { alert("JSON Syntax Error: " + err.message); } else { - throw err; // unknown error, rethrow it + throw err; // 未知错误,再次抛出 } } ``` -The new class `PropertyRequiredError` is easy to use: we only need to pass the property name: `new PropertyRequiredError(property)`. The human-readable `message` is generated by the constructor. +这个 `PropertyRequiredError` 十分容易上手:我们只需要传递属性名:`new PropertyRequiredError(property)`。易懂的 `message` 属性将会由构造器提供。 -Please note that `this.name` in `PropertyRequiredError` constructor is again assigned manually. That may become a bit tedius -- to assign `this.name = ` when creating each custom error. 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 the constructor. And then inherit from it. +需要注意的是,在 `PropertyRequiredError` 构造器中的 `this.name` 是再次进行手动赋值的。这可能会造成冗余 —— 在创建每个自定义错误的时候都要进行赋值 `this.name = `。但这并不是唯一的办法。我们可以创建自己的“基础异常”类,通过将 `this.constructor.name` 赋值给 `this.name` 来卸下我们肩上的负担,然后再进行继承。 -Let's call it `MyError`. +我们称其为 `MyError`。 -Here's the code with `MyError` and other custom error classes, simplified: +这是 `MyError` 以及其他自定义错误类的代码: ```js run class MyError extends Error { @@ -214,19 +214,19 @@ class PropertyRequiredError extends ValidationError { alert( new PropertyRequiredError("field").name ); // PropertyRequiredError ``` -Now custom errors are much shorter, especially `ValidationError`, as we got rid of the `"this.name = ..."` line in the constructor. +现在的自定义错误更加的简洁,特别是 `ValidationError`,我们在其构造器中删除了 `"this.name = ..."` 这一行。 -## Wrapping exceptions +## 包装异常 -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: the new code will probably generate other kinds of errors. +上述代码中的函数 `readUser` 的目的就是“读取用户数据”,对吧?在此过程中可能会出现多个不同类型的异常,目前我们有 `SyntaxError` 和 `ValidationError`,但在将来,函数 `readUser` 将会不断壮大,新添加的代码或许会导致其他类型的异常。 -The code which calls `readUser` should handle these errors. Right now it uses multiple `if` in the `catch` block to check for different error types and rethrow the unknown ones. But if `readUser` function generates several kinds of errors -- then we should ask ourselves: do we really want to check for all error types one-by-one in every code that calls `readUser`? +调用函数 `readUser` 的代码要能够处理这些异常。现在它在 `catch` 语句块中使用多个 `if` 语句来检测不同类型的异常以及抛出未知异常。但如果函数 `readUser` 抛出了多种异常 —— 我们扪心自问:我们真的需要一个接一个地处理它抛出的异常吗? -Often the answer is "No": the outer code wants to be "one level above all that". It wants to have some kind of "data reading error". Why exactly it happened -- is often irrelevant (the error message describes it). Or, even better if there is a way to get error details, but only if we need to. +通常答案是 “No”:外部代码想要比其他代码更高一级。它想要一些类似于“数据读取异常“的东西。它为什么发生 —— (其错误描述信息)通常是不相关的。或者,如果能有一种获取异常细节的办法就更好了,但这仅限于我们需要的时候。 -So let's make a new class `ReadError` to represent such errors. If an error occurs inside `readUser`, we'll catch it there and generate `ReadError`. We'll also keep the reference to the original error in its `cause` property. Then the outer code will only have to check for `ReadError`. +所以,我们创建一个 `ReadError` 类来表现上述的异常。如果在函数 `readUser` 中发生了异常,我们会将其捕获,并生成 `ReadError`。我们同时也会在其 `cause` 属性中保留对原始异常的引用。那么外部的代码就只需要检测 `ReadError`。 -Here's the code that defines `ReadError` and demonstrates its use in `readUser` and `try..catch`: +下面的代码定义了 `ReadError` ,并演示了如何在 `readUser` 和 `try..catch` 中使用它: ```js run class ReadError extends Error { @@ -285,7 +285,7 @@ try { if (e instanceof ReadError) { *!* alert(e); - // Original error: SyntaxError: Unexpected token b in JSON at position 1 + // 原错误:语法错误:在位置 1 处不应有 b alert("Original error: " + e.cause); */!* } else { @@ -294,14 +294,14 @@ try { } ``` -In the code above, `readUser` works exactly as described -- catches syntax and validation errors and throws `ReadError` errors instead (unknown errors are rethrown as usual). +上述代码中,`readUser` 正如描述的一样正常工作 —— 捕获语法以及验证的异常并且抛出 `ReadError` 异常用来代替之前的行为(未知的异常依旧重新抛出)。 -So the outer code checks `instanceof ReadError` and that's it. No need to list possible all error types. +所以外部代码负责检测 `instanceof ReadError`,不必列出所有可能的异常类型。 -The approach is called "wrapping exceptions", because we take "low level exceptions" and "wrap" them into `ReadError` that is more abstract and more convenient to use for the calling code. It is widely used in object-oriented programming. +这种途径称为“包装异常”,因为我们将“低级别的异常”包装为 `ReadError`,使得调用代码更加抽象和方便。它在面向对象编程中被广泛使用。 -## Summary +## 总结 -- 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`. -- Most of the time, we should 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 when a function handles low-level exceptions and makes a higher-level object to report about the errors. Low-level exceptions sometimes become properties of that object like `err.cause` in the examples above, but that's not strictly required. +- 我们能够正常地继承 `Error` 以及其他内置的错误类,只需要注意 `name` 属性以及不要忘了调用 `super`。 +- 大多数时候,我们应该使用 `instanceof` 来检测一些特定的异常。它也能够在继承中使用。但有时我们会发现来自第三方库的异常,并且不容易得到它的类。那么 `name` 属性就可用于这一类的检测。 +- 包装异常是一种广泛应用的技术,当一个函数处理低级别的异常时,用一个高级别的对象来报告错误。低级别的异常有时会变成这个对象的属性,就像上面例子中的 `err.cause`,但这并不严格要求。 diff --git a/LICENSE.md b/LICENSE.md index 60f06924e2..a2600d3d7d 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,14 +1,14 @@ -# Attribution-NonCommercial-ShareAlike 4.0 +# 署名 - 非商业性使用 - 相同方式共享 4.0 -The full license text is at . +完整的许可证文本位于 。 -You are free to: -- **Share** – copy and redistribute the tutorial in any medium or material. -- **Adapt** – remix, transform, and build upon the material. +你可以: +- **分享** —— 在任何媒体或材料中复制并扩散这些教程。 +- **合作** —— 在材料上进行重新编写或进行修改。 +- +根据以下条款: -Under the following terms: - -- **Attribution** — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. -- **NonCommercial** — You may not use the material for commercial purposes. -- **ShareAlike** — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original. +- **署名** —— 你必须提供适当的信用担保,提供许可证的链接,并表明是否进行了更改。你可以以任何合理的方式这么做,但这不代表许可证所有者认可你或你的行为。 +- **非商业化** —— 你不能将材料用于商业用途。 +- **相同方式共享** —— 如果你在材料上进行重新编写或进行修改,你必须对你的贡献使用与原来相同的许可证。 diff --git a/README.md b/README.md index 6494e18eda..9ed7c78605 100755 --- a/README.md +++ b/README.md @@ -1,36 +1,36 @@ -# The JavaScript Tutorial +# JavaScript教程 -This repository hosts the content of the Modern JavaScript Tutorial, published at [https://javascript.info](https://javascript.info). +这个仓库托管现代 JavaScript 教程的内容,发布于 [https://javascript.info](https://javascript.info)。 -## Translations +## 翻译 -Published: -- Russian: [https://github.com/iliakan/javascript-tutorial-ru](https://github.com/iliakan/javascript-tutorial-ru). +已发布: +- 俄语版: [https://github.com/iliakan/javascript-tutorial-ru](https://github.com/iliakan/javascript-tutorial-ru). -In progress: -- Chinese: the ongoing translation at [https://github.com/iliakan/javascript-tutorial-cn](https://github.com/iliakan/javascript-tutorial-cn), go ahead and join if you know Chinese. -- Spanish: https://github.com/lmauromb/javascript-tutorial-es -- German: https://github.com/MartinEls/javascript-tutorial-de +进行中: +- 中文版: 在 [https://github.com/iliakan/javascript-tutorial-cn](https://github.com/iliakan/javascript-tutorial-cn) 进行翻译,如果你懂中文,欢迎加入。 +- 西班牙语版: https://github.com/lmauromb/javascript-tutorial-es +- 德语版: https://github.com/MartinEls/javascript-tutorial-de -If you'd like to translate it into your language then fork the English tutorial and go ahead. I can publish the translation with your credits on a domain like fr.javascript.info or you can do it on your domain. +如果你想将它翻译至你使用的语言,就 fork 英文版教程并开始你的翻译。我能将它发布在域名 fr.javascript.info 下并署上你的名字,或由你发布在自己的域名下。 -You can also send PR for this file to indicate that there's work in progress. +你也可以对此文件发 PR 来表示工作正在进行中。 -Please note that the tutorial can run locally using . +请注意,此教程可以用 来达到本地运行的效果。 -## Structure +## 结构 -Every chapter, article or a task has its folder. +每个章节、每篇文章或每个任务都有其文件夹。 -The folder is named like `N-url`, where `N` is a number for the sorting purposes and `url` is the URL part with title of the material. +文件夹都是以 `N-url` 的方式命名,`N` 是用于分类的数字,`url` 是包含材料标题的 URL。 -The type of the material is defined by the file inside the folder: +材料的类型由文件夹内的文件来定义: - - `index.md` stands for a chapter - - `article.md` stands for an article - - `task.md` stands for a task (solution must be provided in `solution.md` file aswell) + - `index.md` 代表一个章节 + - `article.md` 代表一篇文章 + - `task.md` 代表一个任务(解答也必须出现在 `solution.md` 文件中) -Each of these files starts from the `# Main header`. +每个文件都从 `# Main header` 开始。 -Assets required for the material reside in the same folder. +材料所需要的资源需要处于同一文件夹内。 diff --git a/contributors.md b/contributors.md index c6b26c997f..f20a37185b 100644 --- a/contributors.md +++ b/contributors.md @@ -1,13 +1,13 @@ -The file lists people who made significant contributions to the project: +该文件列出了对本项目做出重大贡献的人员:
    -
  • Alexey Maximov @amaxcz (admin)
  • -
  • Alexey Shisterov (tutorial)
  • -
  • Anton Vernogor @smmurf (markup)
  • -
  • Artem Beztsenny @bezart (design)
  • -
  • Ilya Kantor @iliakan (tutorial, code)
  • -
  • Yuri Tkachenko @tyv (markup)
  • +
  • Alexey Maximov @amaxcz (管理员)
  • +
  • Alexey Shisterov (教程编写)
  • +
  • Anton Vernogor @smmurf (标记)
  • +
  • Artem Beztsenny @bezart (设计)
  • +
  • Ilya Kantor @iliakan (教程、代码编写)
  • +
  • Yuri Tkachenko @tyv (标记)
-The project exists for a long time, I might have missed someone. If you expect to find yourself in the list, but you're not -- please mail me at mk@javascript.ru. +此项目存在了很长时间,我可能遗漏了一些人员。如果你本来希望在列表中发现你的名字却没有 —— 请电邮至 mk@javascript.ru