tag:crieit.net,2005:https://crieit.net/users/1kohei1/feed 1kohei1の投稿 - Crieit Crieitでユーザー1kohei1による最近の投稿 2019-01-11T17:03:12+09:00 https://crieit.net/users/1kohei1/feed tag:crieit.net,2005:PublicArticle/14717 2019-01-11T17:02:40+09:00 2019-01-11T17:03:12+09:00 https://crieit.net/posts/JS-42-true-false [JS] 42==trueがfalseになるわけ <p>JSを少しでも書いてる人は値を比較するときに必ず<code>===</code>を使えと教わることだと思います。<code>==</code>がいかにエンジニアの直感に背き、値を比較するかを書いてみようと思います。</p> <p>まずは、以下のコードが何をプリントするか予想してみてください。</p> <pre><code class="js">console.log(42 == true); // ? console.log('42' == true); // ? console.log(1 == true); // ? console.log(0 == false); // ? </code></pre> <p>.<br /> .<br /> .</p> <p>予想できましたか?結果はこうなります。</p> <pre><code class="js">console.log(42 == true); // false console.log('42' == true); // false console.log(1 == true); // true console.log(0 == false); // true </code></pre> <p><code>42 == true</code>や<code>'42' == true</code>は<code>true</code>になると予想したのではないでしょうか。なぜなら、<code>42</code>も<code>'42'</code>も単体では<a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Glossary/Truthy">truthy</a>だからです。</p> <p>その証拠に<code>if</code>で単体で比較すると<code>true</code>になります。</p> <pre><code class="js">if (42) { console.log('42 is true!'); } else { console.log('42 is false!'); } if ('42') { console.log('true'); } else { console.log('false'); } </code></pre> <p>これらはを実行するとどちらも<code>42 is true!</code>と<code>true</code>が出力されます。なので、型強制(coercion)が行われ<code>true == true</code>が実行されたと考えるのが自然です。</p> <p>ところがそうなりません。これはスペックを読むと理解することができます。(<a target="_blank" rel="nofollow noopener" href="https://www.ecma-international.org/ecma-262/9.0/index.html#sec-abstract-equality-comparison">Abstract Equality Comparison</a>の6と7より)</p> <blockquote> <p>x == y<br /> If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.<br /> If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).<br /> (日本語)<br /> もし、xがBooleanなら、ToNumber(x) == yを返す<br /> もし、yがBooleanなら、y == ToNumber(y)を返す</p> </blockquote> <p>つまり、実際に起きていたのは「Boolean型じゃない値をBoolean型に変換し比較」ではなく、「Boolean型の値をNumber型に変換し比較」だったのです。<br /> Boolean型をNumber型に変換すると<code>true</code>が1になり、<code>false</code>が0になります。そうすると、<code>42 == 1</code>と<code>'42' == 1</code>の比較になり当然結果は<code>false</code>になります。</p> <p>同じ理由で<code>1 == true</code>は<code>true</code>になり、<code>0 == false</code>も<code>true</code>になります。</p> <p>なので、タイトルの「JSで42==trueがfalseになるわけ」は「==で比較される値のうち片方がBooleanの場合、<strong>Booleanの値</strong>をNumberに型強制して比較するようにSpecに書かれているから」が答えになります。</p> <p>.<br /> .<br /> .</p> <p>せっかくなので、もう少し<code>==</code>を見てみましょう。片方がオブジェクトでもう片方が<code>String</code>, <code>Number</code>, <code>Symbol</code>の場合、オブジェクトを<a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Glossary/Primitive">プリミティブ型</a>に変換して比較することが指定されています。(<a target="_blank" rel="nofollow noopener" href="https://www.ecma-international.org/ecma-262/9.0/index.html#sec-abstract-equality-comparison">7.2.14 Abstract Equality Comparison</a>の8と9より)</p> <blockquote> <p>x == y<br /> If Type(x) is either String, Number, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).<br /> If Type(x) is Object and Type(y) is either String, Number, or Symbol, return the result of the comparison ToPrimitive(x) == y.<br /> (日本語訳)<br /> もし、xがString, Number, Symbolならx == ToPrimitive(y)を返す<br /> もし、yがString, Number, SymbolならToPrimitive(x) == yを返す</p> </blockquote> <p>ここで使われる<code>ToPrimitive</code>はJSエンジン内部で使われる処理のことで、オブジェクトのプロトタイプチェーンには定義されていません。ここでは<code>valueOf()</code>, <code>toString()</code>を順番に呼び、返ってきた値がプリミティブならその値を返す、プリミティブにならなかったら<code>TypeErroe</code>を返すという処理になっています。(<a target="_blank" rel="nofollow noopener" href="https://www.ecma-international.org/ecma-262/9.0/index.html#sec-toprimitive">7.1.1 ToPrimitive</a>)</p> <p>これを使ってみるとこのような面白い結果になります。</p> <pre><code class="js">console.log(1 == [1]); // true console.log(123 == [123]); // true console.log(true == [1]); // true console.log(false == []); // true </code></pre> <p><code>1 == [1]</code>は<code>[1].valueOf()</code>が<code>[1]</code>を返すので、<code>[1].toString()</code>を呼びます。そこでプリミティブの<code>"1"</code>が返ってくるため、<code>1 == "1"</code>の比較結果になります。型強制(coercion)が起こり<code>true</code>になります。</p> <p><code>123 == [123]</code>も前のサンプルと同様<code>123 == "123"</code>の比較になります。</p> <p><code>true == [1]</code>はトリッキーです。<code>true</code>があるため、まずここを処理しないといけません。<code>true</code>はNumber型に変換され、<code>1 == [1]</code>になります。これは最初のサンプルと同じなため、同じステップを踏み<code>true</code>になります。</p> <p><code>false == []</code>もいくつかのステップがあります。まず、<code>false</code>が<code>0</code>になり<code>0 == []</code>になります。<code>[].valueOf()</code>は<code>[]</code>のため、<code>[].valueOf()</code>の戻り値が使われます。<code>0 == ""</code>の比較です。<code>""</code>がNumber型に変換され<code>0</code>になり<code>0 == 0</code>は<code>true</code>です。空文字が0になるのは直感的ではないですが、そうなっています。</p> <p>このように予期しない結果になるため、比較するときは必ず<code>===</code>を使いましょう。</p> <p>(参考)<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/getify/You-Dont-Know-JS/blob/master/types%20%26%20grammar/ch4.md#abstract-equality">Abstract Equality - You Dont Know JS</a></p> 1kohei1 tag:crieit.net,2005:PublicArticle/14704 2018-12-31T17:36:58+09:00 2018-12-31T17:36:58+09:00 https://crieit.net/posts/JavaScript-Prototype JavaScriptのPrototype周りの勉強 <p>以下のようなコードを考えてみます。</p> <pre><code class="js">function Person(f, l) { this.firstName = f; this.lastName = l; this.fullName = function() { return `${this.firstName} ${this.lastName}`; } } var a = new Person('a', 'x'); var b = new Person('b', 'y'); a.fullName(); // "a x" b.fullName(); // "b y" </code></pre> <p>ここの<code>function Person</code>は<code>new</code>キーワードを使うことで新しいオブジェクトが返されるため、オブジェクトコンストラクタと<a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Working_with_Objects#Using_a_constructor_function">呼ばれます</a>。<br /> ここで、インスタンスはもう作られていますが、新しい関数を<code>a</code>と<code>b</code>、さらにこれから作られるすべての<code>Person</code>インスタンスに適用したいケースを考えます。ここでは、あいさつを表示する<code>greet</code>関数を加えます。</p> <pre><code class="js">Person.prototype.greet = function() { console.log(`Hello ${this.fullName()}!`); } a.greet(); // "Hello a x!" b.greet(); // "Hello b y!" // 新しいインスタンスにも適用されるかをテストする var c = new Person('c', 'z'); c.greet(); // "Hello c z! // greet関数の書き換えも可能。 Person.prototype.greet = function() { console.log(`Hello ${this.fullName()}! Have a nice day!`); } a.greet(); // Hello a x! Have a nice day! b.greet(); // Hello b y! Have a nice day! c.greet(); // Hello c z! Have a nice day! </code></pre> <p>こういうことが可能なのは、インスタンスとインスタンスの元になった<code>Person</code>は<code>Person.prototype</code>が指してるオブジェクトを共有しているからです。図にするとこうなります。<br /> <a href="https://crieit.now.sh/upload_images/a9a0272c234a2d5d0e01708773e05d415c29d1fad99dd.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a9a0272c234a2d5d0e01708773e05d415c29d1fad99dd.png?mw=700" alt="JS Object prototype.png" /></a></p> <p>すべてのインスタンスが<code>Person.prototype</code>を<code>.__proto__</code>で参照しています。</p> <p>JSはクラス構造がないかわりにオブジェクト同士を参照していく<a target="_blank" rel="nofollow noopener" href="https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain">オブジェクトチェーン</a>が存在します。これは、指定されたプロパティがない場合、<code>.__proto__</code>でつながってるオブジェクトを再帰的にみていく仕組みのことです。これのおかげでインスタンスに<code>greet</code>関数がなくてもgreet関数を呼び出すことができました。</p> <p>以下参考になったリンクを張っておきます。<br /> <a target="_blank" rel="nofollow noopener" href="https://qiita.com/howdy39/items/35729490b024ca295d6c">図で理解するJavaScriptのプロトタイプチェーン</a><br /> <a target="_blank" rel="nofollow noopener" href="http://maeharin.hatenablog.com/entry/20130215/javascript_prototype_chain">や...やっと理解できた!JavaScriptのプロトタイプチェー</a></p> 1kohei1