Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1-js/04-object-basics/05-object-toprimitive #111

Merged
merged 7 commits into from Jun 18, 2018
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
148 changes: 74 additions & 74 deletions 1-js/04-object-basics/05-object-toprimitive/article.md
@@ -1,93 +1,93 @@

# Object to primitive conversion
# 对象原始值转换

What happens when objects are added `obj1 + obj2`, subtracted `obj1 - obj2` or printed using `alert(obj)`?
当对象相加 `obj1 + obj2`,相减 `obj1 - obj2`,或者使用 `alert(obj)` 打印时会发生什么?

There are special methods in objects that do the conversion.
在对象中有特殊的方法用来做转换。

In the chapter <info:type-conversions> 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 close it.
<info:type-conversions> 一章中,我们已经看到了数值,字符串和布尔转换的规则。但是我没给对象留了一个空隙。正如我们所知道的方法和符号一样,现在我们可以关闭它。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

【我没】=> 【我们】


For objects, there's no to-boolean conversion, because all objects are `true` in a boolean context. So there are only string and numeric conversions.
对于对象,不存在 to-boolean 转换,因为所有对象在布尔上下文中都是 `true`。所以只有字符串和数值转换。

The numeric conversion happens when we subtract objects or apply mathematical functions. For instance, `Date` objects (to be covered in the chapter <info:date>) can be subtracted, and the result of `date1 - date2` is the time difference between two dates.
数值转换发生在对象相减或应用数学函数时。例如,Date 对象(将在 <info:date> 章节中介绍)可以相减,而 `date1 - date2` 的结果是两个日期之间的时间差。

As for the string conversion -- it usually happens when we output an object like `alert(obj)` and in similar contexts.
至于字符串转换 —— 它通常发生在我们输出像 `alert(obj)` 这样的对象和类似的上下文中。

## ToPrimitive

When an object is used in the context where a primitive is required, for instance, in an `alert` or mathematical operations, it's converted to a primitive value using the `ToPrimitive` algorithm ([specification](https://tc39.github.io/ecma262/#sec-toprimitive)).
当一个对象被用在需要原始值的上下文中时,例如,在 `alert` 或数学运算中,它会使用 `ToPrimitive` 算法转换为原始值 ([标准](https://tc39.github.io/ecma262/#sec-toprimitive)).

That algorithm allows us to customize the conversion using a special object method.
该算法允许我们使用特殊的对象方法自定义转换。

Depending on the context, the conversion has a so-called "hint".
取决于上下文,转换具有所谓的“暗示”。

There are three variants:
这里有三种变体:

`"string"`
: When an operation expects a string, for object-to-string conversions, like `alert`:
: 当一个操作期望一个字符串时,对于对象到字符串的转换,比如 `alert`

```js
// output
alert(obj);

// using object as a property key
// 使用对象作为属性键
anotherObj[obj] = 123;
```

`"number"`
: When an operation expects a number, for object-to-number conversions, like maths:
: 当一个操作需要一个数字时,用于对象到数字的转换,如 `maths`:

```js
// explicit conversion
// 显式转换
let num = Number(obj);

// maths (except binary plus)
let n = +obj; // unary plus
// maths (除了二进制加法)
let n = +obj; // 一元加法
let delta = date1 - date2;

// less/greater comparison
// 小于/大于的比较
let greater = user1 > user2;
```

`"default"`
: Occurs in rare cases when the operator is "not sure" what type to expect.
: 在少数情况下发生,当操作者“不确定”期望的类型时。

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.
例如,二进制加 `+` 可以和字符串(连接)和数字(相加)发生作用,所以类型是字符串和数字都可以。或者当一个对象用'=='与一个字符串、数字或符号进行比较时。

```js
// binary plus
// 二进制加
let total = car1 + car2;

// obj == string/number/symbol
if (user == 1) { ... };
```

The greater/less operator `<>` can work with both strings and numbers too. Still, it uses "number" hint, not "default". That's for historical reasons.
大于/小于运算符`<>`可以同时用于字符串和数字。不过,它使用“number”暗示,而不是“default”。这是历史原因。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

可以同时用于 => 也可以同时用于


In practice, all built-in objects except for one case (`Date` object, we'll learn it later) implement `"default"` conversion the same way as `"number"`. And probably we should do the same.
实际上,除了一种情况(`Date` 对象,我们稍后会学到它)之外,所有内置对象都实现了一个和`"number"`一样的`"default"`转换。可能我们也应该这样做。

Please note -- there are only three hints. It's that simple. There is no "boolean" hint (all objects are `true` in boolean context) or anything else. And if we treat `"default"` and `"number"` the same, like most built-ins do, then there are only two conversions.
请注意 —— 只有三种暗示。就这么简单。没有"boolean"暗示(所有对象在布尔上下文中都是 `true`)或其他任何东西。如果我们将`"default"``"number"`视为相同,就像大多数内置函数一样,那么只有两种转换了。

**To do the conversion, JavaScript tries to find and call three object methods:**
**为了进行转换,JavaScript尝试查找并调用三个对象方法:**

1. Call `obj[Symbol.toPrimitive](hint)` if the method exists,
2. Otherwise if hint is `"string"`
- try `obj.toString()` and `obj.valueOf()`, whatever exists.
3. Otherwise if hint is `"number"` or `"default"`
- try `obj.valueOf()` and `obj.toString()`, whatever exists.
1. 调用 `obj[Symbol.toPrimitive](hint)`如果这个方法存在的话,
2. 否则如果暗示是`"string"`
- 尝试 `obj.toString()` `obj.valueOf()`, 无论哪个存在。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

, 无论哪个存在 => ,无论哪个存在(去掉空格)

3. 否则,如果暗示`"number"` 或者 `"default"`
- 尝试 `obj.valueOf()` `obj.toString()`, 无论哪个存在。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

, 无论哪个存在 => ,无论哪个存在(去掉空格)


## Symbol.toPrimitive

Let's start from the first method. There's a built-in symbol named `Symbol.toPrimitive` that should be used to name the conversion method, like this:
我们从第一个方法开始。有一个名为 `Symbol.toPrimitive` 的内置符号应该用来命名转换方法,像这样:

```js
obj[Symbol.toPrimitive] = function(hint) {
// return a primitive value
// 返回一个原始值
// hint = one of "string", "number", "default"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

注释未翻译 hint = "string", "number", "default" 中的一个

}
```

For instance, here `user` object implements it:
例如,这里 `user` 对象实现它:

```js run
let user = {
Expand All @@ -100,37 +100,37 @@ let user = {
}
};

// conversions demo:
// 转换演示:
alert(user); // hint: string -> {name: "John"}
alert(+user); // hint: number -> 1000
alert(user + 500); // hint: default -> 1500
```

As we can see from the code, `user` becomes a self-descriptive string or a money amount depending on the conversion. The single method `user[Symbol.toPrimitive]` handles all conversion cases.
从代码中我们可以看到,根据转换的不同,`user` 变成一个自描述字符串或者一个金额。单个方法 `user[Symbol.toPrimitive]` 处理所有的转换情况。


## toString/valueOf

Methods `toString` and `valueOf` come from ancient times. They are not symbols (symbols did not exist that long ago), but rather "regular" string-named methods. They provide an alternative "old-style" way to implement the conversion.
方法`toString``valueOf`来自上古时代。它们不是符号(那么久之前还没有符号这个概念),而是“常规的”字符串命名的方法。它们提供了一种可替换的“老派”的方式来实现转换。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

那么久之前还没有符号这个概念 => 那时候还没有符号这个概念


If there's no `Symbol.toPrimitive` then JavaScript tries to find them and try in the order:
如果没有 `Symbol.toPrimitive` 那么 JavaScript尝试找到它们并且按照下面的顺序尝试:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

那么 JavaScript尝试找到它们并且按照下面的顺序尝试 => 那么 JavaScript 尝试找到它们并且按照下面的顺序进行尝试(1. 空格 2. 进行尝试)


- `toString -> valueOf` for "string" hint.
- `valueOf -> toString` otherwise.
- 对于"string"暗示,`toString -> valueOf`
- 其他情况,`valueOf -> toString`

For instance, here `user` does the same as above using a combination of `toString` and `valueOf`:
例如,在这里 `user` 使用 `toString` `valueOf` 的组合,上面的效果相同:

```js run
let user = {
name: "John",
money: 1000,

// for hint="string"
// 对于 hint="string"
toString() {
return `{name: "${this.name}"}`;
},

// for hint="number" or "default"
// 对于 hint="number" "default"
valueOf() {
return this.money;
}
Expand All @@ -142,7 +142,7 @@ alert(+user); // valueOf -> 1000
alert(user + 500); // valueOf -> 1500
```

Often we want a single "catch-all" place to handle all primitive conversions. In this case we can implement `toString` only, like this:
通常我们希望有一个“全能”的地方来处理所有原始转换。在这种情况下,我们可以只实现 `toString`,就像这样:

```js run
let user = {
Expand All @@ -157,80 +157,80 @@ alert(user); // toString -> John
alert(user + 500); // toString -> John500
```

In the absence of `Symbol.toPrimitive` and `valueOf`, `toString` will handle all primitive conversions.
如果没有 `Symbol.toPrimitive` `valueOf``toString` 将处理所有原始转换。


## ToPrimitive and ToString/ToNumber
## ToPrimitive ToString/ToNumber

The important thing to know about all primitive-conversion methods is that they do not necessarily return the "hinted" primitive.
了解所有原始转换方法的重要之处在于它们不一定会返回“暗示的”原始值。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

了解所有原始转换方法的重要之处在于它们不一定会返回“暗示的”原始值。 => 关于所有原始转换方法,有一个重要的点需要知道,就是它们不一定会返回“暗示的”原始值。


There is no control whether `toString()` returns exactly a string, or whether `Symbol.toPrimitive` method returns a number for a hint "number".
没有限制 `toString()` 是否返回字符串,或 `Symbol.toPrimitive` 方法是否为"number"暗示返回数字。

**The only mandatory thing: these methods must return a primitive.**
**唯一强制性的事情是:这些方法必须返回一个原始值。**

An operation that initiated the conversion gets that primitive, and then continues to work with it, applying further conversions if necessary.
启动转换的操作获取该原始值,然后继续使用该原始值,并在必要时应用进一步的转换。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

启动转换 => 发起转换


For instance:
例如:

- Mathematical operations (except binary plus) perform `ToNumber` conversion:
- 数学运算(二进制加法除外)执行 `ToNumber` 转换:

```js run
let obj = {
toString() { // toString handles all conversions in the absence of other methods
toString() { // toString 在没有其他方法的情况下处理所有转换
return "2";
}
};

alert(obj * 2); // 4, ToPrimitive gives "2", then it becomes 2
alert(obj * 2); // 4, ToPrimitive输出"2",然后就变成了 2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ToPrimitive输出"2",然后就变成了 2 => ToPrimitive 输出 "2",然后就变成了 2

```

- Binary plus checks the primitive -- if it's a string, then it does concatenation, otherwise it performs `ToNumber` and works with numbers.
- 二进制加法会检查原始值 —— 如果它是一个字符串,那么它会进行连接,否则它会执行 `ToNumber` 并使用数字。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

连接 => 级联(跟下面的级联操作保持一致)


String example:
字符串例子:
```js run
let obj = {
toString() {
return "2";
}
};

alert(obj + 2); // 22 (ToPrimitive returned string => concatenation)
alert(obj + 2); // 22 (ToPrimitive 返回字符串=> 级联操作)
```

Number example:
数值例子:
```js run
let obj = {
toString() {
return true;
}
};

alert(obj + 2); // 3 (ToPrimitive returned boolean, not string => ToNumber)
alert(obj + 2); // 3 (ToPrimitive 返回布尔值,非字符串=>ToNumber)
```

```smart header="Historical notes"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

标题未翻译

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

此处标题不知道有什么作用,为了不破坏其他地方可能有的引用,所以没有翻译。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

就翻译引号里面的Historical notes

For historical reasons, methods `toString` or `valueOf` *should* return a primitive: if any of them returns an object, then there's no error, but that object is ignored (like if the method didn't exist).
由于历史原因,`toString` `valueOf` 方法*应该*返回一个原始值:如果它们中的任何一个返回了一个对象,虽然不会报错,但是该对象被忽略(就像该方法不存在一样)。

In contrast, `Symbol.toPrimitive` *must* return a primitive, otherwise, there will be an error.
相反,Symbol.toPrimitive必须返回一个原始值,否则会出现错误。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

相反,Symbol.toPrimitive必须返回一个原始值,否则会出现错误。 => 相反,Symbol.toPrimitive 必须返回一个原始值,否则会出现错误。

```

## Summary
## 概要

The object-to-primitive conversion is called automatically by many built-in functions and operators that expect a primitive as a value.
对象到原始值的转换是由把一个原始值作为返回值的许多内置函数和操作符自动调用的。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

对象到原始值的转换是由把一个原始值作为返回值的许多内置函数和操作符自动调用的 => 对象到原始值的转换会被很多内置函数自动调用,还有将原始值作为返回值的操作器也会调用这种转换。
(这句话太长了,最好分解一下,不然读起来比较困难)


There are 3 types (hints) of it:
- `"string"` (for `alert` and other string conversions)
- `"number"` (for maths)
- `"default"` (few operators)
它有三种类型(暗示):
- `"string"` (对于 `alert` 和其他字符串转换)
- `"number"` (对于 `maths`)
- `"default"` (少数操作)

The specification describes explicitly which operator uses which hint. There are very few operators that "don't know what to expect" and use the `"default"` hint. Usually for built-in objects `"default"` hint is handled the same way as `"number"`, so in practice the last two are often merged together.
规范明确描述了哪个操作符使用哪个暗示。极少数操作者“不知道期望什么”并使用`"default"`暗示。通常对于内置对象,`"default"`暗示的处理方式与`"number"`相同,因此在实践中最后两个通常合并在一起。

The conversion algorithm is:
转换算法是:

1. Call `obj[Symbol.toPrimitive](hint)` if the method exists,
2. Otherwise if hint is `"string"`
- try `obj.toString()` and `obj.valueOf()`, whatever exists.
3. Otherwise if hint is `"number"` or `"default"`
- try `obj.valueOf()` and `obj.toString()`, whatever exists.
1. 调用 `obj[Symbol.toPrimitive](hint)`如果这个方法存在的话,
2. 否则如果暗示是`"string"`
- 尝试 `obj.toString()` `obj.valueOf()`, 无论哪个存在。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同样存在空格问题,【无论】前面的空格应该去掉

3. 否则,如果暗示`"number"` 或者 `"default"`
- 尝试 `obj.valueOf()` `obj.toString()`, 无论哪个存在。

In practice, it's often enough to implement only `obj.toString()` as a "catch-all" method for all conversions that return a "human-readable" representation of an object, for logging or debugging purposes.
在实践中,为了记录或调试目的,仅实现 `obj.toString()` 作为“全捕获"方法,对于一个对象的所有转换返回一个“人类可读”的形式,就足够了。
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

在实践中,为了记录或调试目的,仅实现 obj.toString() 作为“全捕获"方法,对于一个对象的所有转换返回一个“人类可读”的形式,就足够了。=> 在实践中,为了记录或调试目的,仅实现 obj.toString() 作为“全捕获"方法通常就够了,这样所有转换都能返回一种“人类可读”的对象表达形式。