Search code examples
javascriptinheritancegetterprototypal-inheritance

JavaScript getter running during inheritance


I am playing around with using JavaScript getters and inheritance, and I ran into this interesting behavior. The comments at the end of the lines are the order in which the statements are printing.

function createFruit(size) {
   return {
      get size() {
         console.log('getting size'); // 2, 4
         return size;
      },
   };
}

function createApple(color) {
   let apple = {
      get color() {
         return color;
      },
   };

   return Object.assign(Object.create(apple), createFruit(2));
}


let small = createFruit(1);
console.log('calling getSize fruit'); // 1
console.log(small.size); // 3

let green = createApple('green');
console.log(green.color); // 5
console.log('calling getSize apple'); // 6
console.log(green.size); // 7
   

Output:

calling getSize fruit
getting size
1
getting size
green
calling getSize apple
2

The parts are confused about are the order that four, six, and seven print. I was under the understanding that getters in JavaScript are lazy, meaning they are only called when needed. I don't need to get the size of green until the final line, but the getter is being called when the apple is being constructed. Why is this happening? And how can I rewrite my code to avoid this behavior?

As a side note, please let me know if the general pattern I'm using is a bad way to do this.


Solution

  • Object.assign does not copy property attributes like getter functions, it uses them to evaluate the property value and copy that onto the target. So during the createApple call, the getter is invoked and a non-getter size property is created on the apple object.

    How can I rewrite my code to avoid this behavior?

    Don't use Object.assign with objects that contain accessory properties as source arguments. You can write

    function createApple(color) {
      return Object.defineProperties(createFruit(2), {
        color: {
          get() {
            return color;
          }
        }
      });
    }