Matthew Harwood Matthew Harwood - 12 days ago 5
Javascript Question

When using Object.create() how do you reference Object keys withing other object keys

I'm trying to learn Object.create by making a contrived calculator module. I've tried

bind
I've tried removing
this
, but no results.

QUESTION:

How do you reference a property of an object within another property of an element like you would with a class. Or is my example not a very good pattern? If so, how should I structure my Calculator Object to afford event listeners on
creation
?

Calculator.js

const Calculator = {
inputArr: [],
init: (selector)=> {
const el = document.querySelector(selector);
el.addEventListener('click', this.pushValue); // this wont work.
return this;
},
pushValue: (e) => {
let val = e.target.value;
if(val){
this.inputArr.push(val);
console.log(e.target, this.inputArr); // this wouldn't work.
}
}
};

const adder = Object.create(Calculator).init('#calc');


HTML:

<div id="calc">
<button class="btns" value="1">1</button>
<button class="btns" value="2">2</button>
</div>

Answer

The problem in that code is you've used arrow functions, but closing over the wrong this. Arrow functions close over the this where they're defined, rather than having it set when they're called. In your case, it's closing over the this at global scope.

If you make init and pushValue normal functions and call them via references to the object created via Object.create, they'll get called with the correct this:

const Calculator = {
  inputArr: [],
  init: function(selector) {                                 // ****
    const el = document.querySelector(selector);
    el.addEventListener('click', this.pushValue.bind(this)); // ****
    return this;
  },
  pushValue: function(e) {                                   // ****
    let val = e.target.value;
    if(val){
      this.inputArr.push(val);
      console.log(e.target, this.inputArr);
    }
  }
};

const adder = Object.create(Calculator).init('#calc');

You do need to bind the call to pushValue from the event listener (otherwise, this will refer to the element). Or alternately, wrap it in an arrow:

el.addEventListener('click', e => this.pushValue(e));

Working example using the arrow wrapper on this.pushValue:

const Calculator = {
  inputArr: [],
  init: function(selector) { // ****
    const el = document.querySelector(selector);
    el.addEventListener('click', e => this.pushValue(e)); // ****
    return this;
  },
  pushValue: function(e) { // ****
    let val = e.target.value;
    if (val) {
      this.inputArr.push(val);
      console.log(e.target, this.inputArr);
    }
  }
};

const adder = Object.create(Calculator).init('#calc');
<div id="calc">
  <button class="btns" value="1">1</button>
  <button class="btns" value="2">2</button>
</div>