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)
''
Ничего не работает. Но тут дело не в том, что мы неправильно выбрали значение, а в том, что нельзя просто взять и записать в примитив какой-то метод.
В Джаваскрипте мы можем получать какие-то свойства примитивов или вызывать их методы, как делаем это с объектами. Но лишь потому, что в этот момент они на лету превращаются в объекты. Работает это так:
Мы решили вызывать метод примитива или добавить его.
Джаваскрипт создаёт новый объект на основе этого примитива (для строки используется конструктор
String
, для булева типаBoolean
и так далее).Вызывает его метод (или добавляет, если мы добавляли).
Возвращает нам результат (при использовании
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 символов.