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
даже нет смысла рассматривать, потому что решение с ним будет куда длиннее.
Однако, в результатах есть те, у кого решение ещё короче:
Рассматривать вариант с решением в три символа не будем, потому что он не верифицирован, а потому скорее всего пользователь, оставивший его, считерил. А вот варианты в 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.
Однако, решение Object,Proxy
— это ровно 12 символов, как и у ребят в топе. Ещё, если приглядеться, то увидим, что их решения снова были в каких-то старых браузерах, как и в случае с четвёртой задачей.
Что ж, сперва убедимся, что в современном Хроме ничего не работает:
> Object instanceof Proxy
Uncaught TypeError: Function has non-object prototype 'undefined' in instanceof check
Теперь откроем один из тех старых Хромиумов, что использовался в четвёртой задаче, а именно — 65-й. В нём вот так:
Если же откроем 52-й, который у некоторых ребят из топа, то в нём будет так:
Вероятно, когда выкатили Proxy
, налажали с прототипами, и получилась обратная ситуация: любой объект — инстанс Proxy
. Чтобы узнать точнее, нужно так же пройтись бисектом по репозиторию V8, как в четвёртой задаче. Попробуйте, если интересно.
Решение в 12 символов:
instance(Object,Proxy)