Я пытаюсь создать подкласс собственного объекта JS Error
в CoffeeScript, чтобы получить специализированные типы ошибок, но обнаружил, что экземпляр instanceof
работает неправильно если я не определяю конструктор в подклассах:
class SimpleError extends Error
class EmptyConstructorError extends Error
constructor: ->
class SuperConstructorError extends Error
constructor: ->
super
new SimpleError instanceof SimpleError # -> false
new EmptyConstructorError instanceof EmptyConstructorError # -> true
new SuperConstructorError instanceof SuperConstructorError # -> true
Проблема, по-видимому, вызвана тем, как определены сгенерированные функции конструктора JS. Когда я неопределяю конструктор в CoffeeScript:
SimpleError = (function(_super) {
__extends(SimpleError, _super);
function SimpleError() {
return SimpleError.__super__.constructor.apply(this, arguments);
}
return SimpleError;
})(Error);
И когда я определяю конструктор в CoffeeScript:
SuperConstructorError = (function(_super) {
__extends(SuperConstructorError, _super);
function SuperConstructorError() {
SuperConstructorError.__super__.constructor.apply(this, arguments);
}
return SuperConstructorError;
})(Error);
Как видите, разница проста return
в первом случае. Я не понимаю, почему это имеет какое-либо значение в поведении instanceof
, поскольку суперконструктор просто применяется к объекту this
(т. е. суперконструктор не вызывается с new
), но опять же, я не совсем понимаю, как работают JS-конструкторы =P
И самое странное, что такое поведение происходит только при создании подклассов нативных JS-объектов. Если я подклассифицирую классы CoffeeScript, все работает так, как ожидалось.
Есть идеи, почему это может происходить и как избежать написания фиктивных конструкторов только для правильной работы оператора instanceof
?
Спасибо!
Итак, пользователь matyr ответил со ссылкой на коммит, где было введено это поведение, но это не совсем объясняет, что здесь происходит, поэтому я попытаюсь объяснить, что немного на случай, если кто-то еще задается вопросом, почему это работает таким образом.
Основная проблема заключается в этой неприятной «фиче», унаследованной от JavaScript, которая позволяет нам определить функцию-конструктор, которая возвращает объект, отличный от создаваемого:
function Foo() {
return {'LOL': 'You fool!'};
}
new Foo() instanceof Foo // -> false
Также существует тот факт, что некоторые нативные конструкторы, такие как Error
, Array
, String
и еще много чего не нужно вызывать с new
: они просто вернут новый объект соответствующего типа если вы случайно забудете об этом.
В конце концов, сложите эти две уродливые вещи вместе, и в результате вы должны не забыть написать класс MyError extends Error, а затем конструктор: -> super
вместо более интуитивного class MyError extends Error
, если вы хотите, чтобы оператор instanceof
правильно работал с MyError
. Это связано с тем, что неявный конструктор CoffeeScript просто вернет все, что вернет родительский конструктор, и в этом случае он вернет Error.apply(this, arguments)
, который просто вернет блестящий новый объект ошибки вместо объекта, который вы передали. как этот
аргумент.Ура!
Эта проблема была исправлена в CoffeeScript 1.5.0! =D
Теперь расширение нативных объектов работает должным образом:
class MyError extends Error
new MyError instanceof MyError # -> true :)
Aaи его больше нет в версии 1.6.0 =P