Return True

13. Truth

function truth(x) {
  return x.valueOf() && !x;
}

Мы уже ни раз встречались с valueOf в предыдущих задачах, потому знаем, что можем без проблем его подменить на нужный нам метод. Однако, если сделаем это в лоб, то ничего не получим:

> truth({ valueOf: () => true })
false

А всё потому, что вторая часть выражения не использует valueOf. Согласно спеке, при логическом отрицании используется операция приведения к булеву типу, которая в данном случае работает привычным нам образом — получила на вход объект, выдала true.

Получается, что нужно передать какое-то такое значение в качестве параметра, которое при приведении к булеву типу превратится в false, но у которого при этом будет метод valueOf. Например, пустую строку!

> Boolean('')
false

Пробуем:

> truth(x='',x.valueOf=_=>true)
''

Ничего не работает. Но тут дело не в том, что мы неправильно выбрали значение, а в том, что нельзя просто взять и записать в примитив какой-то метод.

В Джаваскрипте мы можем получать какие-то свойства примитивов или вызывать их методы, как делаем это с объектами. Но лишь потому, что в этот момент они на лету превращаются в объекты. Работает это так:

  1. Мы решили вызывать метод примитива или добавить его.

  2. Джаваскрипт создаёт новый объект на основе этого примитива (для строки используется конструктор String, для булева типа Boolean и так далее).

  3. Вызывает его метод (или добавляет, если мы добавляли).

  4. Возвращает нам результат (при использовании use strict может ещё и ошибку выкинуть).

Примитив при этом никак не меняется, и новый созданный объект тоже никуда не возвращается, а просто удаляется сборщиком мусора, когда приходит время.

Потому, когда мы пытаемся записать valueOf в примитив — строку — у нас ничего не получается. Метод просто записывается в промежуточный объект и теряется, не доходя до нас.

Но поскольку при вызове метода примитив на лету конвертируется в объект, то это значит, что мы можем добавить метод в прототип этого объекта. Например, так:

> truth('',String.prototype.valueOf=_=>true)
true

35 символов и решение готово!

Можем подсократить и получить доступ к прототипу через примитив:

truth('',''.__proto__.valueOf=_=>true)

Уже 31. Очевидно, что можно сократить ещё и true:

truth('',''.__proto__.valueOf=_=>1)

28. Чтобы получить 27, как у ребят в топе, можем вместо строки использовать число. Нам ведь всё равно, какой будет примитив. Лишь бы он был отрицателен при булевой проверке, а потому:

truth(0,0..__proto__.valueOf=_=>1)

27 символов.