38343


# 検証環境

  * Google Chrome 61.0.3163.100
  * Firefox 56.0

# コード

下記コードを実行すると、期待通りの結果を返します。

[code]

    console.log('abcd'.indexOf('abcd', 1));   // -1
    console.log('abcd'.includes('abcd', 1));  // false
    
[/code]

次に下記コードを実行すると、期待に反した結果が返ってきます。

[code]

    console.log('abcd'.indexOf('abcd', -1));  // 0
    console.log('abcd'.includes('abcd', -1)); // true
    
[/code]

# ECMAScript 2017

  * [21.1.3.7 String.prototype.includes - ECMAScript® 2017 Language Specification](https://tc39.github.io/ecma262/#sec-string.prototype.includes)
  * [21.1.3.8 String.prototype.indexOf - ECMAScript® 2017 Language Specification](http://www.ecma-international.org/ecma-262/8.0/#sec-string.prototype.indexof)

`String.prototype.includes`, `String.prototype.indexOf` の仕様を読むと、第二引数
`position` は `ToInteger()` を通すことで整数化しています。

  * [7.1.4 ToInteger - ECMAScript® 2017 Language Specification](http://www.ecma-international.org/ecma-262/8.0/#sec-tointeger)

>   4. Return the number value that is the same sign as number and whose
> magnitude is floor(abs(number)).
>

`number` が `-1` であった場合、`abs(number)` によって `-1` は `1`
となりますので、「'abcd'.indexOf('abcd', -1) と 'abcd'.indexOf('abcd', 1)
の返り値は等価となるはず」と考えていますが、期待に反して、Google Chrome/Firefox は等価ではない挙動を示しています。

# MDN

  * [String.prototype.indexOf() - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf)

> `fromIndex` Optional  
>  呼び出す文字列内の検索を始めるための位置。整数を指定できる。デフォルトの値は 0。 **fromIndex <= 0
> の場合、文字列全体が検索される**。fromIndex >= str.length の場合、 文字列は検索されず -1 を返す。searchValue
> が空文字でない限り、str.length が返される。

MDNによれば、第二引数に負の数が指定された場合は `0`
が指定されたのと同じ挙動となるようですが、仕様と異なる説明がされているように感じ、もやもやしています。

仕様をどのように解釈すれば良いのでしょうか。


CC BY-SA 3.0
2017-10-01T15:18:32.657
38342
2017-10-01T16:13:36.260
2017-10-01T15:26:37.250
20262
20262
question
javascript ecmascript-6
String.prototype.includes, String.prototype.indexOf の第二引数で負の数を指定した挙動について


抽象演算`ToInteger`は、仕様に基づき疑似jsコードで書くと次のようになります:

[code]

    const ToInteger = argument => {
        // 1. Let number be ? ToNumber(argument).
        const number = Number(argument);
        // 2. If number is NaN, return +0.
        if (isNaN(number)) {
            return +0;
        }
        // 3. If number is +0, -0, +∞, or -∞, return number.
        if (number === 0 || !isFinite(number)) {
            return number;
        }
        // 4. Return the number value that is the same sign as number and whose magnitude is floor(abs(number)). 
        return (number < 0 ? -1 : 1) * Math.floor(Math.abs(number));
    };
    
[/code]

`ToInteger`は文字通り数値を整数に丸めた値を返します。ただし、 ** _符号は同一 the same sign_** であり、 **_数値の大きさ
magnitude は絶対値を切り捨てたもの floor(abs(number))_** としています。

（補足:
符号を無視する場合の抽象演算として`ToUint*`が定義されています。これとは別に符号を維持する整数化メソッドとして`ToInteger`があるのです）。

他方、`String.prototype.indexOf`の仕様によれば、第6ステップ:

>   6. Let start be min(max(pos, 0), len).
>

より、`ToInteger`されたposition引数が負数であった場合、`max`関数（これは`Math.max`と考えていいです）により`0`に引き上げられ、省略したときと同じ結果になるでしょう。これを指して「fromIndex
<= 0 の場合、文字列全体が検索される」と書かれていると考えられます。

結論として、indexOf/includesの第二引数についてはsliceなどのように負のインデックスを処理することが仕様としては要求されておらず、むしろ負である場合はこの引数を省略した場合と同様にみなして処理すると定められている、と解釈されます。


CC BY-SA 3.0
2017-10-01T15:58:32.473
38343
2017-10-01T16:13:36.260
2017-10-01T16:13:36.260
38342
answer