LoginSignup
12
3

More than 3 years have passed since last update.

toNumber 時に副作用を起こさせる。

Posted at

ある日のニジボックスメンバーの times にて。

image.png

で、これを見た時に「おや?」となったんですよね。

image.png

まぁ単純に上のif文を消し忘れただけだと思うんですが、このままだとどうあがいても4行目以降の三項演算子まで到達しません。

ただ、ちょろっと変えると4行目以降の三項演算子部分に到達させることができます。

そこで、以下のチャレンジを思いつきました。

問題

以下の関数に何かを渡して、 "申込書到達" って出せる applicationProgressValue の引数の値を示しなさい。

const applicationProgressValue = (num) => {
  if (num != 0 || num != 1 || num != 2) {
    return "";
  }
  return num == 0
    ? "申込書到達"
    : num == 1
    ? "申込手続中"
    : num == 2
    ? "申込完了"
    : "";
};

もしも自分で解いてみたいという方は下の解答を見ないで、ちょっと時間を使って解いてみてください。

ヒント: 厳密等価演算子 (===) ではなく、等価演算子 (==) にしてます。

解答

var n = { 
  v: 0, 
  valueOf: function() {
    if (this.v > 2) {
      this.v = 0
    }
    return this.v++
  }
}

applicationProgressValue(n); // 申込書到達

valueOf を使います。等価演算子の中で Primitive での比較を行うため、暗黙の primitive 変換を行います。その時に valueOf 関数の中で v を increment させて、副作用を起こさせます。

const applicationProgressValue = (num) => {
  if (
    num != 0 || // num: 0
    num != 1 || // num: 1
    num != 2) { // num: 2
    return "";
  }

  return num == 0 // num: 0 if (num > 2) v = 0 になるので。
    ? "申込書到達" // return される。
    : num == 1
    ? "申込手続中"
    : num == 2
    ? "申込完了"
    : "";
};

valueOf で副作用を及ぼせる、というのはこういう時によく使いますね。まぁ知らなくてもいいっちゃいいんですけど、知っておくと何かといいかと。

問題2

もう一つ問題を作りました。

以下の関数に何かを渡して、 "申込書到達" って出せる applicationProgressValue の引数の値を示しなさい。ただし、 if 文の中身の順序が変わることがあります。順序に依存してはいけません。

const applicationProgressValue = (num) => {
  if (+num !== 0 || `${num}` !== "1" || (num + "") !== "false") {
    return "";
  }
  return +num === 0
    ? "申込書到達"
    : +num === 1
    ? "申込手続中"
    : +num === 2
    ? "申込完了"
    : "";
};

こっちのが若干ハッキーですね。解いてみたい方は解いてみてください。

ヒント: 厳密等価演算子になっていますが、明示的にこちらから型を変換しています。

解答2

var n = {
  [Symbol.toPrimitive]: function(hint) {
    if (hint === "string") {
      return "1";
    }
    if (hint === "number") {
      return 0;
    }
    return false;
  }
};

applicationProgressValue(n); // 申込書到達

今回は primitive に変換するのですが、 valueOf で副作用を及ぼす技は封じられています。その代わり、 primitive への変換をこちらで明示的に行ってから比較しています。

また、 if 文の中身が +num !== 0 ||${num}!== "" || (num+"") !== "false" だったり、 (num+"") !== "false" ||${num}!== "" || +num !== 0 だったりと中身の順序が書き換わったとしても動く必要があります。

Symbol.toPrimitive は ES2015 から新しく追加された Primitive 関数に変更する時の関数です。これの第一引数(上の例で言う hint )に string, number, default といった文字列が型の変換時にhintとして入ります。

+num とやった場合は数値への変換なので number になり、 ${num} とやった場合は文字列への変換なので string になります。それ以外の場合、特に足し算は number でも string でも使うので default になります。

これを使って Symbol.toPrimitive 関数を作れば良いです。

const applicationProgressValue = (num) => {
  if (+num !== 0 ||      // hintは number なので 0
      `${num}` !== "1" || // hintは string なので ""
      (num+"") !== "false") {    // hintは default なので false
    return "";
  }
  return +num === 0 // +num は 0 なので
    ? "申込書到達"  // 申込書到達
    : +num === 1
    ? "申込手続中"
    : +num === 2
    ? "申込完了"
    : "";
};

元々は雑談で書いてあったコードをちらっと見たら面白かったので、メンバーに問題として提示してしまったというプログラミングクイズでした。

参考:
https://qiita.com/yosuke_furukawa/items/e59a1766b1aec686b4df

12
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
3