maxbit89
maxbit89

Reputation: 770

JavaScript Setter bug found?

I wrote a quick and dirty list ui in js and html that can be filtered: https://jsfiddle.net/maxbit89/2jab4fa4/

So the usage of this looks like this (Fiddle line: 96):

var list = new ui_list(document.body, 200, 300, "Test");
var encoder = function(dom, value) {
  console.log("begin encoding");
  console.log(value)
  dom.innerHTML = value.n;
}

list.add({'n': 1}, function() {
    this.value.n++;
    console.log(this.value.n);
//    this.value = this.value;
}, encoder);

So what this basically does is create a List and adds a Element to it that has an Object: {'n': 1} as a value and a onClickHandler(second parameter on list.add) which should increase the value by 1 (fiddle line: 104)

But it won't do this until you uncomment the line 106 in the fiddle. (Tested with Firefox 50.1.0, and Edge Browser)

Has any body an idea why js behaves like this? In a much simpler example this works just fine:

var myObj= {
    'onvalueChange' : function() {
        console.log('value changed');
    },
    'print' : function() {
          console.log('value:');
        console.log(this.value);
        console.log(this.value.n);
    }
};

Object.defineProperty(myObj, "value", {
    get: function() { return this._value; },
   set: function(value) {
        this.onvalueChange();
        this._value = value;
    }
});

myObj.value = {'n' : 1};

myObj.value.n++;

myObj.print();

Upvotes: 2

Views: 80

Answers (1)

ibrahim mahrir
ibrahim mahrir

Reputation: 31692

First you have the setter defined like this:

set: function (value) {
    this.encoder(this, value);
    this._value = value;
}

that means that every time the value is set, the encoder will be called with the new value to update the equivalent DOM element.

But then inside the event listener function you have:

function() {
    this.value.n++;
    console.log(this.value.n);
    //this.value = this.value;
}

where you think that this.value.n++ is setting the value (means it calls the setter which means the encoder will be called to update the DOM element). But it's not true. this.value.n++ is actually calling the getter. To explain more this:

this.value.n++;

is the same as:

var t = this.value; // call the getter
t.n++; // neither call the getter nor the setter. It just uses the reference (to the object) returned by the getter to set n

So, when you uncomment the line this.value = this.value;, the setter gets called, and the encoder gets called to update the DOM element.

So to fix the issue you have to either:

  • Make a call inside the getter to the encoder as you did for the setter (but this solution is very hacky as it will update the DOM element on every getter call even if nothing is being set).
  • Change this this.value.n++; to actually call the setter like: this.value = {n: this.value.n + 1}; (but this is hacky too as if value has a lot of key-value pairs then you have to enlist them all here just to set n).
  • Call the encoder inside the event listener which will be the best way to do it (or if you don't want to pass the parameters to it make another function (for example this.callEncoder()) that will call it and [you] call the new function instead inside the event listener).

Upvotes: 3

Related Questions