1

Update 1: I didn't mention this earlier, but I was trying to avoid using the new keyword. The Polyfill should work just fine, as well. I should have been clear that I was looking to see if there was an alternative way of writing similar code.


Chris Elliott's article 'Common Misconceptions about Inheritance in JS' illustrates a great way to use closures for data privacy, but does so with the ES6 method Object.assign.

He describes it as:

Object.assign() is a new ES6 feature championed by Rick Waldron that was previously implemented in a few dozen libraries. You might know it as $.extend() from jQuery or _.extend() from Underscore. Lodash has a version of it called assign(). You pass in a destination object, and as many source objects as you like, separated by commas.

It will copy all of the enumerable own properties by assignment from the source objects to the destination objects with last in priority. If there are any property name conflicts, the version from the last object passed in wins.

How can I write this sample without using the ES6 feature Object.assign?

let animal = {
  animalType: 'animal',

  describe () {
    return `An ${this.animalType} with ${this.furColor} fur, 
      ${this.legs} legs, and a ${this.tail} tail.`;
  }
};

let mouseFactory = function mouseFactory () {
  let secret = 'secret agent';

  return Object.assign(Object.create(animal), {
    animalType: 'mouse',
    furColor: 'brown',
    legs: 4,
    tail: 'long, skinny',
    profession () {
      return secret;
    }
  });
};

let james = mouseFactory();
Dave Voyles
  • 4,495
  • 7
  • 33
  • 44
  • Use lodash or underscore, or loop over the elements and copy the properties. – baao Oct 11 '15 at 22:28
  • Or use the polyfill on the MDN `Object.assign` page. –  Oct 11 '15 at 22:30
  • That's likely what I'll end up doing. – Dave Voyles Oct 11 '15 at 22:38
  • Just a note, but the "update" style tends to make your question harder to read for people not following it from the beginning. Or at least put them at the end. –  Oct 11 '15 at 23:07
  • All objects are garbage collected, whether they are created using `new` or in any other way. –  Oct 11 '15 at 23:08
  • I thought that using Object.create would not return a new function each time it was called though, and that was the benefit of using it. Or am I misunderstanding? – Dave Voyles Oct 11 '15 at 23:15

2 Answers2

2

By writing your own version of object.assign!

function assign(target, sources) {
    // for each object in source, iterate through properties
    // check has own property and add the property to the target 
    // object

    // or just modify in place, too many different ways to do this
    let newTarget = new target();


    for (let i = 0; i < sources.length; i++) {
      for (let key in sources[i]) {
        if (sources[i].hasOwnProperty(key)) {
          newTarget[key] = sources[i][key];
        }
      }
    }
    return newTarget;
}

EDIT: You absolutely don't have to use new - you could also just add them to an empty object.

function assign(sources) {
    // for each object in source, iterate through properties
    // check has own property and add the property to the target 
    // object

    // this is not the important part
    let newTarget = {};


    for (let i = 0; i < sources.length; i++) {
      for (let key in sources[i]) {
        if (sources[i].hasOwnProperty(key)) {
          newTarget[key] = sources[i][key];
        }
      }
    }
    return newTarget;
}
Jonah Williams
  • 20,499
  • 6
  • 65
  • 53
1

You can actually just use Object.create which is ES5 as described here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

I have rewritten your code in this format, there you go:

var animal = {
  animalType: 'animal',

  describe: function() {
    return 'An ' + this.animalType + ' with ' + this.furColor + ' fur, ' 
      + this.legs + ' legs, and a ' + this.tail + ' tail.';
  }
};

var mouseFactory = function mouseFactory () {
  var secret = 'secret agent';

  return Object.create(animal, {
    animalType: {
        value: 'mouse',
        writable: true
    },
    furColor: {
        value: 'brown',
        writable: true
    },
    legs: {
        value: 4,
        writable: true        
    },
    tail: {
        value: 'long, skinny',
        writable: true        
    },
    profession: {
        value: function () {
          return secret;
        }
    }
  });
};

var james = mouseFactory();

var dave = mouseFactory();
dave.furColor = 'white';

console.log(james.describe());
// An mouse with brown fur, 4 legs, and a long, skinny tail.

console.log(dave.describe());
// An mouse with white fur, 4 legs, and a long, skinny tail.
Tiago Romero Garcia
  • 1,068
  • 9
  • 11
  • This is how I considered using it as well. Will that point to the same instance each time I create a new variable behind it? For example, these two variables refer to two different instances of the object? var james = mouseFactory(); // returns first instance var dave = mouseFactory(); // returns a second instance – Dave Voyles Oct 11 '15 at 22:44
  • Correct. I have just edited my example adding 'writable: true' to the arguments. Now I have created a 2nd variable, ```dave```, changed its fur color to white and when I call describe on both I still have different results. Which proves they are 2 different instances – Tiago Romero Garcia Oct 11 '15 at 22:52
  • Perfect, exactly what I was hoping for. Thank you! – Dave Voyles Oct 11 '15 at 22:54