skip to Main Content

Is it possible to "import" or "pass" a pre-defined object into a class that has access to methods on that class? Importantly, for performance reasons, I need to ensure that I am not re-creating or re-defining the "massive_entity_obj" in the example

class Distinct_Class {
   entities = null;

   constructor(massive_entity_obj) {
      this.entities = massive_entity_obj;
   }

   unique_func_1() {
      const some_value = "calcs based on unique data of Distinct_Class";
      return some_value;
   }
}

// defined in a separate module
massive_entity_obj = {
   property_1: {
      sub_property_a: "something",
      get sub_property_b() {
         // call a function in Distinct_Class in the context of Distinct_Class
         return this.unique_func_1(); // <-- this does not work
      },
   },
};

const distinct_1 = new Distinct_Class(massive_entity_obj)
const distinct_2 = new Distinct_Class(massive_entity_obj)
let sub_prop = distinct_1.entities.property_1.sub_property_b
let other_sub_prop = distinct_2.entities.property_1.sub_property_b

2

Answers


  1. Not in that way. I know of no good way to give an object and all of its properties access to the functions in a class-defined object like that. Even if the large object was initially made from the class, the object’s properties wouldn’t have access to the class functions unless the properties’ functions were only made using Arrow functions in the constructor of the class.

    Your best bet is probably one of two things: you could either make a class with static functions and variables, or create an object from the class in the global scope.

    Using static

    Making a class with static functions is pretty simple. Using static to define variables and functions basically makes your class into its own object as well as a class. If you want to know more about the static operator, click here. Here is an example:

    class myClass {
     static myFunction(value) {
      console.log(value);
     }
    }
    
    var myobject = {
     property: {
      a: 'Hello!',
      b: function() {
       myClass.myFunction(this.a);
      }
     }
    }
    
    myobject.property.b();
    

    A global object

    Creating an object from your existing class which your large object has access to by using a variable would allow for more flexibility, like holding changing variables and acting more like a custom object. Doing so would look like this:

    class myClass {
     constructor() {}
        
     myFunction(value) {
      console.log(value);
     }
    }
    
    var classobject = new myClass();
    
    var myobject = {
     property: {
      a: 'Hello!',
      b: function() {
       classobject.myFunction(this.a);
      }
     }
    }
    
    myobject.property.b();
    

    I would recommend one of these two methods listed above. If, however, you’re set on trying to make the existing object and the class mingle without re-creating the object, you could try the way listed below. It just doesn’t seem to be made to do it:

    Direct access

    Albeit, inconveniently given.

    The only way that I know that might resemble your code the closest is to assign the new class-made object as a property of each object you want to use the functions in — which isn’t particularly convenient. Only by giving your object’s properties an object made by myClass, or calling something like Object.assign(property, new myClass()) on each one would it give them the ability to use the this keyword to access the functions. If you do so, it also seems like you’re taking a class and an object and inverting their roles:

    class myClass {
     value = 'World';
    
     constructor(existingobject) {
      existingobject.property.access = this;
     }
    
     myFunction(value) {
      console.log(`${value}, ${this.value}!`);
     }
    }
    
    var myobject = {
     property: {
      a: 'Hello',
      b: function() {
       this.access.myFunction(this.a);
      }
     }
    }
    
    new myClass(myobject);
    myobject.property.b();
    

    Then, if you want myClass to access myObject, you add something like this.object = existingobject to the Constructor.

    I would recommend keeping them separate if possible by using one of the first two suggestions — especially if a lot of properties and sub-properties are objects in need of using the class’ functions.

    Login or Signup to reply.
  2. What the OP is looking for can be solved entirely generically.

    The only assumption one has to make about the to be injected "massive data blob" is that one is going to deal with it as a data structure, where each of this structure’s (own) entry (key-value pair), regardless of nested or not, gets treated through its property descriptor.

    This setting allows a combined cloning and delegation/binding strategy for getters and setters which are functions, hence their context (the thisArg) can be changed via bind.

    Implementing the approach as recursive function, one easily achieves a deep and real clone of any provided data-blob (due to the cloning of property descriptors), whilst also being able of rebinding any getter or setter to the context of the involved class-instance at construction/instantiation time.

    // e.g. defined in separate modules
    
    // ----- ----- ----- ----- -----
    class DistinctClass {
    
      #state;
      entities = null;
    
      constructor(dataBlob, protectedState) {
        this.#state = structuredClone(protectedState);
    
        this.entities =
          cloneStructureAndRebindAnyGetAndSet(this, dataBlob);
    
        console.log({
          'injected blob': dataBlob,
          'this.entities': this.entities,
        });
      }
      uniqueDataProcessingMethod() {
        console.log('calcs based on unique instance data/state.');
    
        return structuredClone(this.#state);
      }
    }
    // ----- ----- ----- ----- -----
    
    // ----- ----- ----- ----- -----
    const massiveDataBlob_1 = {
       property_1: {
          subProperty_A: "sub property A",
          get subProperty_B() {
             // - invocation of a `DistinctClass` instance specific
             //   method within the context of this very instance.
             return this?.uniqueDataProcessingMethod?.();
          },
       },
    };
    // ----- ----- ----- ----- -----
    
    // ----- ----- ----- ----- -----
    const massiveDataBlob_2 = {
       property_2: {
          subProperty_C: "sub property C",
          get subProperty_D() {
             // - invocation of a `DistinctClass` instance specific
             //   method within the context of this very instance.
             return this?.uniqueDataProcessingMethod?.();
          },
       },
    };
    // ----- ----- ----- ----- -----
    
    const distinct_A =
      new DistinctClass(massiveDataBlob_1, { foo: 'FOO', bar: 'BAR' });
    
    const distinct_B =
      new DistinctClass(massiveDataBlob_2, { baz: 'BAZ', bizz: 'BIZZ' });
    
    const distinctResult_A =
      distinct_A.entities.property_1.subProperty_B;
    
    const distinctResult_B =
      distinct_B.entities.property_2.subProperty_D;
    
    console.log({
      distinctResult_A,
      distinctResult_B,
    });
    .as-console-wrapper { min-height: 100%!important; top: 0; }
    <script>
    function isFunction(value) {
      return (
        typeof value === 'function' &&
        typeof value.call === 'function' &&
        typeof value.apply === 'function'
      );
    }
    
    function cloneStructureAndRebindAnyGetAndSet(context, data, result = {}) {
      if (Array.isArray(data)) {
    
        result = Array.map(item =>
          cloneStructureAndRebindAnyGetAndSet(context, item)
        );
    
      } else if (!!data && typeof data === 'object') {
    
        result = Object
    
          .keys(data)
          .reduce((target, key) => {
    
            const descriptor = Reflect.getOwnPropertyDescriptor(data, key);
            let { set, get, value } = descriptor;
      
            if (isFunction(set)) {
              set = set.bind(context);
            }
            if (isFunction(get)) {
              get = get.bind(context);
            }
            
            if (Object.hasOwn(descriptor, 'value')) {
    
              if (!!value && typeof value === 'object') {
    
                value = cloneStructureAndRebindAnyGetAndSet(context, value);
              }
              Reflect.defineProperty(target, key, { ...descriptor, value });
    
            } else {
    
              Reflect.defineProperty(target, key, {
                ...descriptor,
                ...(set ? { set } : {}),
                ...(get ? { get } : {}),
              });
            }
            return target;
    
          }, result);
    
      } else {
    
        result = data;
      }
      return result;
    }
    </script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search