Подклассы нативных объектов: instanceof работает неправильно

Я пытаюсь создать подкласс собственного объекта 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), который просто вернет блестящий новый объект ошибки вместо объекта, который вы передали. как этотаргумент.Ура!

Обновление 2 (25 февраля 2013 г.)

Эта проблема была исправлена ​​в CoffeeScript 1.5.0! =D

Теперь расширение нативных объектов работает должным образом:

class MyError extends Error
new MyError instanceof MyError # -> true :)

Обновление 3 (04 марта 2013 г.)

Aaи его больше нет в версии 1.6.0 =P

16
задан Community 23 May 2017 в 11:45
поделиться