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

feat: Numeric Separatorsの対応、ビット演算子の書き直し #1312

Merged
merged 19 commits into from
May 9, 2021
5 changes: 1 addition & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ on:
branches-ignore:
- 'publish1' # srcはテストしても意味がないため

env:
CI: true

jobs:
build:
runs-on: ${{ matrix.os }}
Expand All @@ -30,7 +27,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 12.x, 10.x]
node-version: [14.x, 12.x]
name: "Test on Node.js ${{ matrix.node-version }}"
steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion config/base.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = {
"mocha": true
},
"parserOptions": {
"ecmaVersion": 2020,
"ecmaVersion": 2021,
"sourceType": "module"
},
"rules": {
Expand Down
2,446 changes: 1,127 additions & 1,319 deletions package-lock.json

Large diffs are not rendered by default.

30 changes: 15 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,26 +48,26 @@
"textlint"
],
"devDependencies": {
"@babel/core": "^7.13.13",
"@babel/preset-env": "^7.13.12",
"@babel/register": "^7.13.8",
"@babel/core": "^7.14.0",
"@babel/preset-env": "^7.14.1",
"@babel/register": "^7.13.16",
"@jxa/global-type": "^1.3.4",
"@jxa/run": "^1.3.4",
"@power-doctest/javascript": "^5.2.0",
"@power-doctest/markdown": "^5.2.0",
"@power-doctest/tester": "^5.2.2",
"@power-doctest/javascript": "^5.3.0",
"@power-doctest/markdown": "^5.3.0",
"@power-doctest/tester": "^5.3.0",
"@textlint-ja/textlint-rule-no-synonyms": "^1.1.0",
"@textlint-rule/textlint-rule-no-invalid-control-character": "^1.2.0",
"@textlint-rule/textlint-rule-require-header-id": "^1.0.1",
"@textlint/regexp-string-matcher": "^1.1.0",
"@types/meow": "^5.0.0",
"@types/node": "^14.14.37",
"acorn": "^7.4.1",
"@types/node": "^14.14.44",
"acorn": "^8.2.4",
"add-text-to-markdown": "^2.0.0",
"consolemock": "^1.1.0",
"eslint": "^7.23.0",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint": "^7.25.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"esquery": "^1.4.0",
"front-matter": "^4.0.2",
"gitbook-plugin-anchors": "^0.7.1",
Expand All @@ -80,14 +80,14 @@
"gitbook-plugin-page-toc-button": "^0.1.1",
"gitbook-summary-to-path": "^1.1.0",
"globby": "^11.0.3",
"honkit": "^3.6.19",
"honkit": "^3.6.20",
"js-levenshtein": "^1.1.6",
"jsdom": "16.2.2",
"jsdom-global": "3.0.2",
"lodash": "^4.17.21",
"meow": "^7.1.1",
"micromatch": "^4.0.2",
"mocha": "^8.3.2",
"micromatch": "^4.0.4",
"mocha": "^8.4.0",
"npm-run-all": "^4.1.5",
"power-assert": "^1.6.1",
"prettier": "^2.2.1",
Expand Down Expand Up @@ -116,7 +116,7 @@
"unist-util-find-before": "^2.0.5",
"unist-util-parents": "^1.0.3",
"unist-util-select": "^3.0.4",
"vm2": "^3.9.2",
"vm2": "^3.9.3",
"wait-on": "^5.3.0",
"workbox-cli": "^3.6.3"
},
Expand Down
11 changes: 10 additions & 1 deletion prh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,16 @@ rules:
- expected: 構文エラー
patterns:
- シンタックスエラー

- expected: $1ビット
patterns:
- /(\d+)?bit\b/i
specs:
- from: 2bit表記
to : 2ビット表記
- from: 32bit整数
to : 32ビット整数
- from: BitBucket
to : BitBucket
# 一致とマッチ
## 一致は ===
- expected: $1一致
Expand Down
42 changes: 37 additions & 5 deletions source/basic/data-type/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ false; // => false
数値には`42`のような整数リテラルと`3.14159`のような浮動小数点数リテラルがあります。

これらのリテラルで表現できる数値は[IEEE 754][]の倍精度浮動小数として扱われます。
倍精度浮動小数では64bitで数値を表現します
64bitのうち52bitを数字の格納のために使い、11bitを小数点の位置に使い、残りの1bitはプラスとマイナスの符号です
倍精度浮動小数では64ビットで数値を表現します
64ビットのうち52ビットを数字の格納のために使い、11ビットを小数点の位置に使い、残りの1ビットはプラスとマイナスの符号です
そのため、正確に扱える数値の最大値は`2^53-1`(2の53乗から1引いた値)となります。

#### 整数リテラル {#integer-literal}
Expand Down Expand Up @@ -221,7 +221,7 @@ console.log(0x30A2); // => 12450
2e8; // => 200000000
```

### [ES2020] BigInt {#Bigint-literal}
### [ES2020] BigInt {#bigint-literal}

`1`や`3.14159`などの数値リテラルは[IEEE 754][]の倍精度浮動小数であるため、正確に扱える数値の最大値は`2^53-1`(2×10の53乗から1引いた値)です。
この数値リテラルで安全に表せる最大の数値は`Number.MAX_SAFE_INTEGER`として定義されています。
Expand All @@ -233,13 +233,13 @@ console.log(Number.MAX_SAFE_INTEGER); // => 9007199254740991
数値リテラルで`2^53-1`(`9007199254740991`)よりも大きな値を表現したり計算すると間違った結果となる場合があります。

この問題を解決するために、ES2020では`BigInt`という新しい整数型のデータ型とリテラルが追加されました。
数値リテラルは倍精度浮動小数(64bit)で数値を扱うのに対して、BigIntでは任意の精度の整数を扱えます。
数値リテラルは倍精度浮動小数(64ビット)で数値を扱うのに対して、BigIntでは任意の精度の整数を扱えます。
そのため、BigIntでは`2^53-1`(`9007199254740991`)よりも大きな整数を正しく表現できます。

BigIntリテラルは、数値の後ろに`n`をつけます。

{{book.console}}
```
```js
console.log(1n); // => 1n
// 2^53-1より大きな値も扱える
console.log(9007199254740992n); // => 9007199254740992n
Expand All @@ -255,6 +255,38 @@ BigIntは整数を扱うデータ型であるため、次のように小数点

<!-- textlint-enable -->

### [ES2021] Numeric Separators {#numeric-separators}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Numeric Separatorsをデータ型に追加。
それぞれのリテラルに書くかまよったけど、Numeric Separatorsとして数値のリテラルで使えるよという形にした。


数値が大きくなるほど、桁数の見間違いなどが発生しやすくなります。
次のコードは、1兆を数値リテラルで書いていますが、桁数を読み取りにくいです。

```js
1000000000000;
```

ES2021から、数値リテラル内の区切り文字として`_`を追加できるNumeric Separatorsがサポートされています。
Numeric Separatorsは、数値リテラル内では区切り文字として`_`が追加できます。
次のコードも、1兆を数値リテラルで書いています。数値リテラルを評価する際に`_`は単純に無視されるため同じ意味となります。

{{book.console}}
```js
1_000_000_000_000;
```

Numeric Separatorsは数値リテラルである整数、浮動小数点、BigIntのリテラル内でのみ利用できます。
また、`_`はリテラルの先頭や数値の最後に追加することはできません。

<!-- textlint-disable -->
<!-- doctest:SyntaxError -->
```js
_123; // 変数として評価される
3._14; // => SyntaxError
0x52_; // => SyntaxError
1234n_; // => SyntaxError
```

<!-- textlint-enable -->

### 文字列(String){#string}

文字列リテラル共通のルールとして、同じ記号で囲んだ内容を文字列として扱います。
Expand Down
92 changes: 65 additions & 27 deletions source/basic/operator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -487,69 +487,104 @@ console.log(43 <= 42); // => false

## ビット演算子 {#bit-operator}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ビット演算は書き直し。
図も入れた。
ビット演算自体がユースケースにないので、これ自体をまるっとスキップするのも検討する


ビット演算子はオペランドを符号付き32bit整数に変換してから演算します。
ビット演算子による演算結果は10進数の数値を返します。
ビット演算子では、オペランドである数値を符号付き32ビット整数(`0`と`1`からなる32個のビットの集合)として扱います。

たとえば、`9`という数値は符号付き32bit整数では次のように表現されます。
たとえば、`1`という数値は符号付き32ビット整数のビットでは、`00000000000000000000000000000001` として表現されます。
わかりやすく4ビットごとに区切ると `0000_0000_0000_0000_0000_0000_0000_0001` のような32ビットの集合となります。
符号付き32ビット整数では、先頭の最上位ビット(一番左のビット)は符号を表し、`0`の場合は正の値、`1`の場合は負の値であることを示しています。

{{book.console}}
```js
console.log(0b0000000000000000000000000001001); // => 9
// Number#toStringメソッドを使うことで2進数表記の文字列を取得できる
console.log((9).toString(2)); // => "1001"
```
![1の符号付き32bit整数での表現](./img/0000_0000_0000_0000_0000_0000_0000_0001.png)

また、`-9`という数値は、ビッグエンディアンの2の補数形式で表現されるため、次のようになります。
符号付き32ビット整数では負の数値は、2の補数形式という形式で表現されます。
2の補数とは、それぞれのビットを反転して1ビットを足した値となります。

{{book.console}}
```js
console.log(0b11111111111111111111111111110111); // => 4294967287
// ゼロ桁埋め右シフトをしてからNumber#toStringで2進数表記を取得できる
console.log((-9 >>> 0).toString(2)); // => "11111111111111111111111111110111"
```
たとえば、`-1` という数値の符号付き32ビット整数は、次のように2の補数で求められます。

- 10進数の`1`は、符号付き32ビット整数では`0000_0000_0000_0000_0000_0000_0000_0001`となる
- `0000_0000_0000_0000_0000_0000_0000_0001` の各ビットを反転すると `1111_1111_1111_1111_1111_1111_1111_1110` となる
- これに1ビットを足すと `1111_1111_1111_1111_1111_1111_1111_1111` となる

これによって、`-1`の符号付き32ビット整数は `1111_1111_1111_1111_1111_1111_1111_1111` となります。

![-1の符号付き32ビット整数での表現](./img/1111_1111_1111_1111_1111_1111_1111_1111.png)

<!-- textlint-disable ja-technical-writing/sentence-length -->

そのため符号付き32ビット整数で表現できる数値の範囲は-2の31乗(`1111_1111_1111_1111_1111_1111_1111_1111`)から2の31乗(`0111_1111_1111_1111_1111_1111_1111_1111` )までとなります。
32ビットを超える数値については、32ビットをはみ出るビットが最上位(一番左)から順番に捨てられます。

<!-- textlint-enable ja-technical-writing/sentence-length -->
これから見ていくビット演算子はオペランドを符号付き32ビット整数として扱い、その演算結果を10進数の数値として返します。

### ビット論理積(`&`) {#bit-and}

論理積演算子(`&`)はビットごとの**AND**演算した結果を返します。
ビット論理積演算子(`&`)はビットごとの**AND**演算した結果を返します。
AND演算では、オペランドの各ビットがどちらも`1`の場合は`1`となり、それ以外の場合は`0`となります。

次のコードでは、10進数の`15`と`9`をAND演算しています。
`15`は、符号付き32ビット整数では`0000_0000_0000_0000_0000_0000_0000_1111`となります。
`9`は、符号付き32ビット整数では`0000_0000_0000_0000_0000_0000_0000_1001`となります。
これらをAND演算した結果は`0000_0000_0000_0000_0000_0000_0000_1001`となり、10進数の値である`9`を返します。

{{book.console}}
```js
console.log(15 & 9); // => 9
// 同じ位の各ビット同士をAND演算する(上位の`0`は省略)
// 1111
// 1001
// ----
// 1001
console.log(0b1111 & 0b1001); // => 0b1001
```

### ビット論理和(`|`) {#bit-or}

論理和演算子(`|`)はビットごとの**OR**演算した結果を返します。
ビット論理和演算子(`|`)はビットごとの**OR**演算した結果を返します。
OR演算では、オペランドの各ビットがどちらか片方でも`1`の場合は`1`となり、両方とも`0`の場合は`0`となります。

{{book.console}}
```js
console.log(15 | 9); // => 15
// 同じ位の各ビット同士をOR演算する(上位の`0`は省略)
// 1111
// 1001
// ----
// 1111
console.log(0b1111 | 0b1001); // => 0b1111
```

### ビット排他的論理和(`^`) {#bit-xor}

排他的論理和演算子(`^`)はビットごとの**XOR**演算した結果を返します。
ビット排他的論理和演算子(`^`)はビットごとの**XOR**演算した結果を返します。
XOR演算では、オペランドのビットが異なるなら`1`、両方とも同じなら`0`となります。

{{book.console}}
```js
console.log(15 ^ 9); // => 6
// 同じ位の各ビット同士をXOR演算する(上位の`0`は省略)
// 1111
// 1001
// ----
// 0110
console.log(0b1111 ^ 0b1001); // => 0b0110
```

### ビット否定(`~`) {#bit-not}

単項演算子の否定演算子(`~`)はオペランドを反転した値を返します
単項演算子の否定演算子(`~`)はオペランドの各ビットを反転した値を返します
これは1の補数として知られている値と同じものです。

次のコードでは、10進数で`15`を否定演算子(`~`)で各ビットを反転させた値を得ています。
`15` は `0000_0000_0000_0000_0000_0000_0000_1111`です。
各ビットを反転させると`1111_1111_1111_1111_1111_1111_1111_0000` となり、10進数では`-16` となります。

{{book.console}}
```js
console.log(~15); // => -16
console.log(~0b1111); // => -0b10000
```

否定演算子(`~`)はビット演算以外でも使われていることがあります。
`~x`のように`x`をビット否定演算子で演算した結果は、`-(x + 1)`となります。
この性質を利用する形で、ビット否定演算子(`~`)はビット演算以外でも使われていることがあります。

文字列(Stringオブジェクト)が持つ`indexOf`メソッドは、マッチする文字列を見つけて、そのインデックス(位置)を返すメソッドです。
この`indexOf`メソッドは、検索対象が見つからない場合には`-1`を返します。
Expand All @@ -574,7 +609,7 @@ console.log(~(-1)); // => 0

JavaScriptでは`0`も、if文では`false`として扱われます。
そのため、`~indexOfの結果`が`0`となるのは、その文字列が見つからなかった場合だけとなります。
次のコードのような否定演算子(`~`)と`indexOf`メソッドを使ったイディオムが一部では使われていました。
次のコードのように否定演算子(`~`)と`indexOf`メソッドを使ったイディオムが一部では使われていました。

{{book.console}}
```js
Expand Down Expand Up @@ -620,7 +655,7 @@ num << bit;
{{book.console}}
```js
console.log( 9 << 2); // => 36
console.log(0b1111 << 2); // => 0b111100
console.log(0b1111 << 2); // => 0b11_1100
```

### 右シフト演算子(`>>`) {#right-shift}
Expand All @@ -639,20 +674,23 @@ num >> bit;
{{book.console}}
```js
console.log((-9) >> 2); // => -3
// 0b11111111111111111111111111110111 >> 2; // => 0b11111111111111111111111111111101
// 1111_1111_1111_1111_1111_1111_1111_0111 >> 2
// => 1111_1111_1111_1111_1111_1111_1111_1101
```

### ゼロ埋め右シフト演算子(`>>>`) {#fill-zero-right-shift}

ゼロ埋め右シフト演算子は、数値である`num`を`bit`の数だけ右へシフトするのは右シフト演算子(`>>`)と同じです。異なる点としては右にあふれたビットは破棄され、`0`のビットを左から詰めます。
ゼロ埋め右シフト演算子は、数値である`num`を`bit`の数だけ右へシフトするのは右シフト演算子(`>>`)と同じです。
異なる点としては右にあふれたビットは破棄され、`0`のビットを左から詰めます。

次のコードでは、`-9`を2ビット分だけゼロ埋め右シフトしています。
左端のビットは`0`となるため、常に正の値となります。

{{book.console}}
```js
console.log((-9) >>> 2); // => 1073741821
// 0b11111111111111111111111111110111 >>> 2; // => 0b00111111111111111111111111111101
// 1111_1111_1111_1111_1111_1111_1111_0111 >>> 2
// => 0011_1111_1111_1111_1111_1111_1111_1101
```

<!-- textlint-enable eslint -->
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion source/basic/string-unicode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ console.log(codePoints); // => ["30ea", "30f3", "30b4", "1f34e"]
具体的には、Code Pointの要素数が4つなのに対して、Code Unitの要素数が5つになっています。
また、Code Pointでは1つのCode Pointが`🍎`に対応していますが、Code Unitでは2つのCode Unitで`🍎`に対応しています。JavaScriptでは「文字列はCode Unitが順番に並んだもの」として扱われるためこの文字列の要素数(長さ)はCode Unitの個数である5つとなっています。

ある1つの文字に対応するIDであるCode Pointを、16bit(2バイト)のCode Unitで表現するのがUTF-16というエンコード方式です。しかし、16bit(2バイト)で表現できる範囲は、65536種類(2の16乗)です。
ある1つの文字に対応するIDであるCode Pointを、16ビット(2バイト)のCode Unitで表現するのがUTF-16というエンコード方式です。しかし、16ビット(2バイト)で表現できる範囲は、65536種類(2の16乗)です。
現在、Unicodeに登録されているCode Pointは10万種類を超えているため、すべての文字とCode Unitを1対1の関係で表すことができません。

このような場合に、UTF-16では2つCode Unitの組み合わせ(合計4バイト)で1つの文字(1つのCode Point)を表現します。この仕組みを**サロゲートペア**と呼びます。
Expand Down
6 changes: 3 additions & 3 deletions source/basic/string/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,8 @@ Unicodeはすべての文字に対してID(Code Point)を振ることを目


JavaScript(ECMAScript)は文字コードとしてUnicodeを採用し、文字をエンコードする方式としてUTF-16を採用しています。
UTF-16とは、それぞれの文字を16bitのビット列に変換するエンコード方式です
Unicodeでは1文字を表すのに使う最小限のビットの組み合わせを**Code Unit**(符号単位)と呼び、UTF-16では各Code Unitのサイズが16bit(2バイト)です。
UTF-16とは、それぞれの文字を16ビットのビット列に変換するエンコード方式です
Unicodeでは1文字を表すのに使う最小限のビットの組み合わせを**Code Unit**(符号単位)と呼び、UTF-16では各Code Unitのサイズが16ビット(2バイト)です。

<!--
- 用語集: http://unicode.org/glossary/
Expand Down Expand Up @@ -248,7 +248,7 @@ console.log(str); // => "アオイ"
| 文字列 | ア | オ | イ |
| UTF-16のCode Unit(16進数) | 0x30A2 | 0x30AA | 0x30A4 |

このように、JavaScriptにおける文字列は16bitのCode Unitが順番に並んだものとして内部的に管理されています。
このように、JavaScriptにおける文字列は16ビットのCode Unitが順番に並んだものとして内部的に管理されています。
これは、ECMAScriptの内部表現としてUTF-16を採用しているだけで、JavaScriptファイル(ソースコードを書いたファイル)のエンコーディングとは関係ありません。そのため、JavaScriptファイル自体のエンコードは、UTF-16以外の文字コードであっても問題ありません。

UTF-16を利用していることはJavaScriptの内部的な表現であるため、気にする必要がないようにも思えます。
Expand Down
Loading