0

I have the following code:

function Complex(real, imaginary) {
    let c = Object.create(Complex.methods);
    [c.real, c.imaginary] = [real, imaginary];
    return c;
}

Complex.methods = {
    toString() {return `${this.real}+${this.imaginary}i`},
    add(other) {
        if (!(other instanceof Complex)) throw new Error("Only supports complex-only addition");
        return Complex(this.real+other.real, this.imaginary+other.imaginary);
    }
}
let c1 = Complex(2,0);
let c2 = Complex(3,4);
console.log(c1.add(c2) + "");
// "Uncaught Error: Only supports complex-only addition",

This is occurring because c1 instanceof Complex is returning false. Why does it return false?

Comparing it with using the class keyword:

class CX {
    toString() {return "xxx"}
}
let c1 = new CX();
console.log(c1 + "", c1 instanceof CX);
// xxx true

But still curious why the first one doesn't recognize the instanceof operator.

Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
David542
  • 104,438
  • 178
  • 489
  • 842
  • 1
    `Complex` is a factory function which creates an object via `Object.create`. This newly created object's prototype is an object which got nailed directly to `Complex` as this function's `methods` property. Thus since `Complex` is not a constructor function (invoked via `new`) there are also no instances of `Complex`. – Peter Seliger Apr 09 '22 at 00:01
  • @PeterSeliger ok, if I were to invoke it as `let c1 = new Complex(2,0)`; let c2 = Complex(3,4);` would that allow me to use `instanceof` ? Or how would I basically see that `c2` was created with the `Complex()` factory function? – David542 Apr 09 '22 at 00:03
  • `other.constructor.name` returns _Object_, while `c1.constructor.name` returns _CX_. Try to debug it that way. – Reza Saadati Apr 09 '22 at 00:08
  • @RezaSaadati I see, though I don't really follow, could you post an answer with some details? – David542 Apr 09 '22 at 00:24
  • 1
    Do you understand [how `instanceof` works](https://stackoverflow.com/q/2449254/1048572)? It's not clear why you expect an object that inherits from `Complex.methods`, not `Complex.prototype`, to be an `instanceof Complex`. – Bergi Apr 09 '22 at 03:49

1 Answers1

1

The OP either chooses the classic (good ol' days) constructor function approach ...

function Complex(real, imaginary) {
  Object.assign(this, { real, imaginary });
}
Complex.prototype.toString = function toString () {
  return `${this.real} + ${this.imaginary}i`
};
Complex.prototype.add = function add (other) {
  if (!(other instanceof Complex)) {
    throw new Error("Only supports complex-only addition");
  }
  return new Complex(
    this.real + other.real,
    this.imaginary + other.imaginary
  );
};
let c1 = new Complex(2,0);
let c2 = new Complex(3,4);

console.log(c1.add(c2) + "");
.as-console-wrapper { min-height: 100%!important; top: 0; }

... or a class based approach ...

class Complex {
  constructor(real, imaginary) {
    Object.assign(this, { real, imaginary });
  }
  toString () {
    return `${this.real} + ${this.imaginary}i`
  }
  add (other) {
    if (!(other instanceof Complex)) {
      throw new Error("Only supports complex-only addition");
    }
    return new Complex(
      this.real + other.real,
      this.imaginary + other.imaginary
    );
  }
}
let c1 = new Complex(2,0);
let c2 = new Complex(3,4);

console.log(c1.add(c2) + "");
.as-console-wrapper { min-height: 100%!important; top: 0; }
Peter Seliger
  • 11,747
  • 3
  • 28
  • 37
  • thanks for this. Just one question: why do you use `Object.assign(...)` rather than `Object.create(...)` in the above? – David542 Apr 09 '22 at 00:28
  • 1
    Is `Object.assign(this, { real, imaginary });` the same as `this.real=real; this.imaginary=imaginary` and it's just a shortcut? – David542 Apr 09 '22 at 00:43
  • 1
    [`Object.create`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create) solely as is, is of no good help if it comes to dealing with instances and the `instanceof` operator. And choosing `Object.assign` is just shorter if it comes to the assignment of a lot of constructor parameters as instance properties ... `constructor(foo, bar, baz, biz, buz) { Object.assign(this, { foo, bar, baz, biz, buz }); }` ... is easier to write than ... `constructor(foo, bar, baz, biz, buz) { this.foo = foo; this.bar = bar; this.baz = baz; this.biz = biz; this.buz = buz; }` – Peter Seliger Apr 09 '22 at 00:45