Return True

07. Instance

function instance(x, y) {
  return x instanceof y && y instanceof x && x !== y;
}

В предыдущей задаче мы уже вспоминали, как работает instanceof, потому знаем, что надо или искать такие объекты, которые есть в цепочках прототипов друг у друга, или как-то подменять @@hasInstance.

Как ни странно, начнём с первого, а не со второго. Потому что заранее определённых глобальных объектов, которые чаще всего используются для проверки на instanceof не то чтобы много.

Как мы знаем, «в Джаваскрипте всё — объект». А раз так, то посмотрим на цепочку прототипов Object с помощью вот такой функции:

function printPrototypeChain(object) {
  let proto = Object.getPrototypeOf(object);
  let result = `[${object.name}]`;

  while (proto) {
    result += ` -> ${proto.constructor.name}`;
    proto = Object.getPrototypeOf(proto)
  }

  return result;
}

Функция очень простая. Мы поочерёдно вытаскиваем из цепочки прототипов следующего «родителя» и добавляем его название в строку.

Вот, что она выводит для Object:

> printPrototypeChain(Object)
"[Object] -> Function -> Object"

И ведь действительно, Object — это не только объект, но и функция! Работает она, кстати, довольно интересно:

> Object(1)
Number{1}

> Object('str')
String{"str"}

> Object({a: 1})
{a: 1}

То есть, она превращает примитивы в их объектные версии. А при передаче обычного объекта возвращает его. Нам это сейчас не принципиально, но грех не проверить:

> let obj = {a: 1}
> obj === Object(obj)
true

Да, это тот же объект.

Энивэй, для задачи нам важнее другое — в цепочке примитивов есть Function, а раз всё в Джаваскрипте объект, то и Function, конечно, тоже:

> printPrototypeChain(Function)
"[Function] -> Function -> Object"

Потому ответ для задачи напрашивается сам собой:

instance(Object,Function)

15 символов. Неплохо! Варианты с @@hasInstance даже нет смысла рассматривать, потому что решение с ним будет куда длиннее.

Однако, в результатах есть те, у кого решение ещё короче:

Топ по задаче instance

Рассматривать вариант с решением в три символа не будем, потому что он не верифицирован, а потому скорее всего пользователь, оставивший его, считерил. А вот варианты в 12 символов похожи на правду. Маловероятно, что они смогли как-то избавиться от Object, но вот от Function — вполне. Надо только понять, как.

Из объектов, которые бы были «с изюминкой», в голову приходит только Proxy, однако:

> printPrototypeChain(Proxy)
"[Proxy] -> Function -> Object"

Более того:

> instance(Object,Proxy)
TypeError: Function has non-object prototype 'undefined' in instanceof check

А всё потому, что Proxy, согласно документации, не имеет поля prototype, которое используется при проверке на instanceof:

The Proxy constructor [...] does not have a «prototype» property because Proxy exotic objects do not have a [[Prototype]] internal slot that requires initialization.

— 28.2.2 Properties of the Proxy Constructor

Однако, решение Object,Proxy — это ровно 12 символов, как и у ребят в топе. Ещё, если приглядеться, то увидим, что их решения снова были в каких-то старых браузерах, как и в случае с четвёртой задачей.

Что ж, сперва убедимся, что в современном Хроме ничего не работает:

> Object instanceof Proxy
Uncaught TypeError: Function has non-object prototype 'undefined' in instanceof check

Теперь откроем один из тех старых Хромиумов, что использовался в четвёртой задаче, а именно — 65-й. В нём вот так:

Результат работы instanceof в 65-м Хромиуме (false)

Если же откроем 52-й, который у некоторых ребят из топа, то в нём будет так:

Результат работы instanceof в 52-м Хромиуме (true)

Вероятно, когда выкатили Proxy, налажали с прототипами, и получилась обратная ситуация: любой объект — инстанс Proxy. Чтобы узнать точнее, нужно так же пройтись бисектом по репозиторию V8, как в четвёртой задаче. Попробуйте, если интересно.

Решение в 12 символов:

instance(Object,Proxy)