2

I am refactoring some code from an older ES5 codebase, where I was doing the following:

function ObjectCreatorFunction() {
  this.someProperty= {};
}

/*
 * Static method, to extend the object passed as a parameter with the
 * ObjectCreatorFunction instance properties and methods
 */
ObjectCreatorFunction.extend = function extend(object) {
  var key;

  ObjectCreatorFunction.call(object);
  for (key in ObjectCreatorFunction.prototype) {
    if (ObjectCreatorFunction.prototype.hasOwnProperty(key)) {
      if (!object.hasOwnProperty(key)) {
        object[key] = ObjectCreatorFunction.prototype[key];
      }
    }
  }

  return object;
};

ObjectCreatorFunction.prototype.someMethod = function someMethod() {...}
//etc

I am trying to do the same with an ES6 rewrite, so I a have this

class ClassName{
  constructor() {
    this.someProperty= {};
  }

  static extend(object) {
    let key;

    ClassName.constructor.call(object);
    for (key in ClassName.prototype) {
      if (ClassName.prototype.hasOwnProperty(key)) {
        if (!object.hasOwnProperty(key)) {
          object[key] = ClassName.prototype[key];
        }
      }
    }

    return object;
  }

  someMethod() {...}
  //etc
}

My problem is that the line ClassName.constructor.call(object); does not work as intended, i.e. The passed object does not get the instance properties of the class.

I have tried s few ways to rewrite this (even some unorthodox ones) to no avail.

How do I extend an object with a class' instance properties, using ES6?

DISCLAIMER:

My code is passed through a transpiling process, with babel and webpack. In case it has any effect to how classes work internally.

8
  • 1
    "The passed object does not get the instance properties of the class" - really, is that your only observation? You don't get any exception? Commented Sep 18, 2017 at 14:37
  • 2
    If you want to write mixins, I recommend not to use class syntax for them. function is still just fine. Commented Sep 18, 2017 at 14:38
  • @Bergi The way I have this written, no I don't get any exceptions. The passed object simply returns the same as it was when it got in. If I write it in any other way, e.g. ClassName.call(object) or new className.call(object) I am getting Cannot call a class as a function and className.call is not a constructor respectively Commented Sep 18, 2017 at 14:41
  • Yes, those are definitely what I had expected. Commented Sep 18, 2017 at 14:43
  • 1
    Because constructors of classes are a bit more than just syntactic sugar. Also the distinction between constructible-only (requiring new) functions and callable-only (forbidding new) is new in ES6 - with class constructors and methods as examples. The prototypical inheritance stayed the same, but the initialisation of instances works differently now especially for inherited classes. Commented Sep 18, 2017 at 15:08

1 Answer 1

3

No, this does not work with class syntax. It's a bit more than just syntactic sugar. The prototypical inheritance stayed the same, but the initialisation of instances works differently now especially for inherited classes, and you cannot invoke a constructor without new to not create a new instance.

I would recommend to be explicit about your mixin, and give it an init method:

class Mixin {
    constructor(methods) {
        this.descriptors = Object.getOwnPropertyDescriptors(methods);
    }
    extend(object) {
        for (const p in this.descriptors)) {
            if (Object.prototype.hasOwnProperty.call(object, p)) {
                if (process.env.NODE_ENV !== 'production') {
                    console.warn(`Object already has property "${p}"`);
                }
            } else {
                Object.defineProperty(object, p, this.descriptors[p]);
            }
        }
    }
}
// define a mixin:
const xy = new Mixin({
    initXY() {
        this.someProperty= {};
    },
    someMethod() { … }
});
// and use it:
class ClassName {
    constructor() {
        this.initXY();
    }
}
xy.extend(ClassName.prototype);
Sign up to request clarification or add additional context in comments.

2 Comments

I appreciate your time writing this, but this introduces a whole new level of "abstraction", where I just wanted to know if there was a straightforward way to achieve the effect of the ES5 code with classes. If it is not possible, that's a valid answer but I'd like to understand why. P.s. my code is passed through a transpiling process, with babel and webpack, not sure if it affects it
You can also drop the abstraction of the Mixin class and dump it all into one object literal. But yes, you cannot use a class to create mixins.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.