Search code examples
javascriptclassthisprototypenew-operator

How to create classes using prototypes without using "new" and "this"


I am trying to understand what is going on under all the syntactic sugar when we use "classes" in ES6, as well as what is happening when we are using the "new" and the "this" keyword. I know JavaScript is a prototype based programming language, and I have come up with the following code below to do this without using "new" or "this".

//Constructor function passes 3 custom properties to a predetermined object and then returns that object.
function ConstructorTestFunction(value1, value2, value3, object){
  object.property1 = value1;
  object.property2 = value2;
  object.property3 = value3;
  return object;
}

/*Since I am not using "this" I have wrapped the "ConstructorTestFunction.prototype" object into a function
called "ConstructorTestFunctionPrototype". This will allow me to pass the parent object's properties into 
methods within the objects ".prototype" property */
function ConstructorTestFunctionPrototype(object){ 
  ConstructorTestFunction.prototype = {
    addValues(){
      return object.property1 + object.property2 + object.property3;
    },
    minusValues(){
      return object.property1 - object.property2 - object.property3;
    },
    multiplyValues(){
      return object.property1 * object.property2 * object.property3;
    },
    divideValues(){
      return object.property1 / object.property2 / object.property3;
    }
  };
  return ConstructorTestFunction.prototype
};

// Since I am not using "new", I set _object1 to a blank javascript object with [[prototype]] = Object.prototype
const _object1 = {};

/*Change _object1's [[prototype]] to "ConstructorTestFunction.prototype", with the object's parent object
passed into it's hidden [[prototype]] property*/
_object1.__proto__ = ConstructorTestFunctionPrototype(_object1);

/*Creates object1, which is an instance of ConstructorTestFunction. It passes in "_object1" which is an object
with "[[prototype]] = ConstructorTestFunctionPrototype(_object1)", so it inherits all the methods from 
"ConstructorTestFunction.prototype" whilst referencing the properties of the parent object. It also passes in
values for all assigned properties included in the class.
*/
const object1 = ConstructorTestFunction(40, 50, 245, _object1);

// Since I am not using "new", I set _object2 to a blank javascript object with [[prototype]] = Object.prototype
const _object2 = {};

/*Change _object2's [[prototype]] to "ConstructorTestFunction.prototype", with the object's parent object
passed into it's hidden [[prototype]] property*/
_object2.__proto__ = ConstructorTestFunctionPrototype(_object2);

/*Creates object2, which is an instance of ConstructorTestFunction. It passes in "_object2" which is an object
with "[[prototype]] = ConstructorTestFunctionPrototype(_object2)", so it inherits all the methods from 
"ConstructorTestFunction.prototype" whilst referencing the properties of the parent object. It also passes in
values for all assigned properties included in the class.
*/
const object2 = ConstructorTestFunction(1, 2, 3, _object2);

Other than this not being the standard way of doing things in Javascript. Is this method of me reformulating the code to not use "this" or "new" going to have major performance issues? If so, then how would I go about replacing "this" and "new" without having major performance issues?

Basically, if I truly understand [[prototype]] and what "new" and "this" is doing, then I should be able to create some code that uses [[prototype]] to create "classes" within JavaScript that does not use "new" and "this". Otherwise, I do not truly know what these keywords are doing if I can't replace their functionality within custom code.

Thanks for your help in advance.


Solution

  • Each call to ConstructorTestFunctionPrototype generates a new set of methods (addValues, etc.). This removes the performance/memory advantages of using prototypes in the first place.

    You can test this by noticing that if you run your code, object1.addValues !== object2.addValues. Whereas traditional code would have them be ===, using polymorphism over this to operate on the correct object.

    The benefit of using the usual class/prototype system in JavaScript is precisely to share the memory and optimization advantages of methods among all instances of the class. In exchange you need to find the method's target using this, instead of binding it to a specific single object as you have done here.


    As @Pointy notes in his comment on the OP, there are things the language can do that you cannot do at runtime. In this case the magic built in to the language is the translation of x.y(z) causing y to be called with x as the this value.

    You can "emulate" this using a non-special argument, but then you lose out on the nice syntax. For example, you can write y(x, z) and then instead of using this instead the definition of y, use the first argument. But if you want access to the thing before the ., you need to use this.