GibboK GibboK - 4 months ago 13
Javascript Question

JavaScript getter and setter on dojo defined class do not work as expected

I have defined a "class" in dojo, with a property

foo
with defined
JavaScript
getter
and
setter
.

In the code below, setter is never being called and getter is called twice.

I would like to know:


  • Why this happen ONLY on when defining a dojo "class"?

  • How to fix it?



I am aware of dojo custom getter and setter, but I'am interested in understanding why does not work in this "class".

Live demo:
https://jsfiddle.net/yz8ft4eh/

require(["dojo/_base/declare"], function(declare) {
var Person = declare(null, {
_foo: null,
get foo() {
console.log('getter foo ' + this._foo);
return this._foo;
},
set foo(value) {
console.log('setter foo ' + value);
this._foo = value;
},
constructor: function(name) {
this.name = name;
this.foo = 1000;
}
});
var folk = new Person("phiggins");
folk.foo = 2000;
});

Answer

The problem is that Dojo doesn't understand that you have a getter/setter property there. It's important to remember that a lot of Dojo was written a long time ago, before even the ES5 spec in 2009 was released. While the project is active, there's only so much time in the day and they may not have time to go back and retrofit support for newer things everywhere. (That said, I'm surprised this remains an issue in 2016, seven years after getter/setter properties were added to JavaScript.)

I can't find uncompressed source online for v1.10.4 (the one in your fiddle), but I can find 1.10.6, and it uses a simple for-in loop to copy your properties. We can see that declare calls safeMixin, which does this:

for (name in source) {
    t = source[name];
    if ((t !== op[name] || !(name in op)) && name != cname) {
        if (opts.call(t) == "[object Function]") {
            // non-trivial function method => attach its name
            t.nom = name;
        }
        target[name] = t;
    }
}

To support using getter/setter properties, it would have to be using Object.getOwnPropertyDescriptor or similar there. So instead of copying your property properly, it's just reading the value of it.

You can solve that by initializing the property in your constructor instead:

require(["dojo/_base/declare"], function(declare) {
  function getFoo() {
    console.log('getter foo ' + this._foo);
    return this._foo;
  }
  function setFoo(value) {
    console.log('setter foo ' + value);
    this._foo = value;
  }
  var Person = declare(null, {
    _foo: null,
    constructor: function(name) {
      Object.defineProperty(this, "foo", {
        get: getFoo,
        set: setFoo
      });
      this.name = name;
      this.foo = 1000;
    }
  });
  var folk = new Person("phiggins");
  folk.foo = 2000;
});
<link href="https://ajax.googleapis.com/ajax/libs/dojo/1.10.0/dijit/themes/claro/claro.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/dojo/1.10.4/dojo/dojo.js"></script>

Comments