Subclassing native objects: instanceof not working properly

假装没事ソ 提交于 2019-12-18 14:38:43

问题


I'm trying to subclass the native JS Error object in CoffeeScript to get specialized error types, but i found that the instanceof does not work correctly if i don't define a constructor in the subclasses:

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

The problem seems to be caused by how the generated JS constructor functions are defined. When i don't define a constructor in CoffeeScript:

SimpleError = (function(_super) {

  __extends(SimpleError, _super);

  function SimpleError() {
    return SimpleError.__super__.constructor.apply(this, arguments);
  }

  return SimpleError;

})(Error);

And when i do define a constructor in CoffeeScript:

SuperConstructorError = (function(_super) {

  __extends(SuperConstructorError, _super);

  function SuperConstructorError() {
    SuperConstructorError.__super__.constructor.apply(this, arguments);
  }

  return SuperConstructorError;

})(Error);

As you can see, the difference is a simple return in the first case. I don't understand why this makes any difference in the instanceof behavior though, as the super constructor is just being applied to the this object (i.e. the super constructor is not being called with new), but then again i don't understand a whole lot of how JS constructors work =P

And the weird thing is that this behavior seems to only happen when subclassing native JS objects. If i subclass CoffeeScript classes everything works as expected.

Any idea of why this might be happening and how could i avoid writing dummy constructors just for the instanceof operator to work correctly?

Thanks!

Update

So the user matyr answered with a link to the commit where this behavior was introduced, but it doesn't quite explain what is happening here, so i'll try to explain that a little bit in case anyone else wonders why this works this way.

The main problem is this inherited nasty "feature" from JavaScript which let us define a constructor function that returns an object other than the one being constructed:

function Foo() {
    return {'LOL': 'You fool!'};
}
new Foo() instanceof Foo // -> false

And there is also the fact that some native constructors, like Error, Array, String and whatnot don't need to be called with new: they will just return a new object of the corresponding type if you happen to forget it.

In the end, add these two ugly things together and the result is that you should remember to write class MyError extends Error then constructor: -> super instead of the more intuitive class MyError extends Error if you want the instanceof operator to work properly with MyError. That's because CoffeeScript's implicit constructor will just return whatever the parent constructor returns, and in this case will do return Error.apply(this, arguments) which will just return a shinny new error object instead of the object you passed as the this argument. Yay!

Update 2 (Feb 25 2013)

This problem was fixed in CoffeeScript 1.5.0! =D

Now extending native objects works as expected:

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

Update 3 (Mar 04 2013)

Aaand it's gone on 1.6.0 =P


回答1:


For better or worse, the return was added on 1.3.1 to fix #1966 (and #2111).



来源:https://stackoverflow.com/questions/10805084/subclassing-native-objects-instanceof-not-working-properly

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!