javascriptoopobject-composition

Keeping self properties private with object composition


i was looking at a blog on object composition and it showed this code here:

const canSayHi = self => ({
  sayHi: () => console.log(`Hi! I'm ${self.name}`)
});
const canEat = () => ({
  eat: food => console.log(`Eating ${food}...`)
});
const canPoop = () => ({
  poop: () => console.log('Going to 💩...')
});

// Combined previous behaviours
const socialBehaviors = self => Object.assign({}, canSayHi(self), canEat(), canPoop());

const alligator = name => {
  const self = {
    name
  };

  const alligatorBehaviors = self => ({
    bite: () => console.log("Yum yum!")
  });

  return Object.assign(self, socialBehaviors(self), alligatorBehaviors(self));
};


const jack = alligator("jack");

When you go to access the jack alligator, you can of course access the name property on jack. What if i wanted this property to be private, so that jack.name returned undefined. I am assuming this would require a closure but am unsure of the best/cleanest way to do it, or simply the conventional way it is done.

I understand that you could just not pass self to object.assign, but what if you had in self a mixture of variables that you wanted some to be private and some to be public? I would have thought the solution here would be to have a 'state' literal with all the private variables and a 'base' literal that had all the public variables, if you wanted to access anything in the base literal you would then need to use this in the other objects in objects.assign.


Solution

  • Every internal function that uses the .name is passed self explicitly while the object is being constructed, so you can just return

    Object.assign({}, ...
    

    instead of

    Object.assign(self, ...
    

    and the returned object will not have a name property.

    const canSayHi = self => ({
      sayHi: () => console.log(`Hi! I'm ${self.name}`)
    });
    const canEat = () => ({
      eat: food => console.log(`Eating ${food}...`)
    });
    const canPoop = () => ({
      poop: () => console.log('Going to 💩...')
    });
    
    // Combined previous behaviours
    const socialBehaviors = self => Object.assign({}, canSayHi(self), canEat(), canPoop());
    
    const alligator = name => {
      const self = {
        name
      };
    
      const alligatorBehaviors = self => ({
        bite: () => console.log("Yum yum!")
      });
    
      return Object.assign({}, socialBehaviors(self), alligatorBehaviors(self));
    };
    
    
    const jack = alligator("jack");
    console.log('jack.name:', jack.name);
    jack.bite();
    jack.eat('somefood');
    jack.sayHi();