开发者

How to procedurally set Javascript getter/setters? (or actually closure scopes)

开发者 https://www.devze.com 2023-04-01 04:43 出处:网络
I\'m trying to procedurally add getters/setters to objects in Javascript and although I think the code below should simply work, it doesn\'t act as I expected.

I'm trying to procedurally add getters/setters to objects in Javascript and although I think the code below should simply work, it doesn't act as I expected.

here is my code:

var data = {a:1, b:2, c:3};
function Abc(data) {
    var data = data || {};
    for ( var key in data ) {
        console.log(key, data[key]);
        this.__defineGetter__(key, function() {
                console.log('using getter');
                return data[key];
            })
    }
    return this;
}

abc = Abc(data);
console.log('this should be 1', abc.a);
console.log('this should be 2', abc.b);
console.log('this should be 3', abc.c);

and this is my unexpected output

a 1
b 2
c 3
using getter
this should be 1 3
using getter
this should be 2 3
using getter
this should be 3 3

the output makes absolutely no sense to me, but I get the same output on Chrome and Webkit, so I'm guessing I'm just stupid and this is not a bug of the Javascript engines :)


as the comments mention my triple开发者_开发技巧 use of "data" isn't really good!


By the time the closure that was passed to __defineGetter__ is executed, the loop has finished and key remains at the last value. Try this:

function Abc(data) {
    if(!(this instanceof Abc)) {
        return new Abc(data);
    }
    data = data || {};
    for(var key in data) {
        (function(key) {
            console.log(key, data[key]);
            this.__defineGetter__(key, function() {
                console.log('using getter');
                return data[key];
            });
        }).call(this, key);
    }
    return this;
}

Some other things:

  1. You weren't using var on key, so key was global. That wasn't causing your issue, but it's a good idea to add it anyway.
  2. You were declaring a new variable named data when there was already a data in the scope of the function. It's not needed to use var there since there's already a data in the function; I removed it.
  3. You weren't using new, which was also causing odd behavior; the new three lines at the top of the function cause it to act as if it was called with new.


@Robert Gould. main problem in your code sample is not in "defining getters and setters", but in "understanding closures".

  • See more about closures in MDN

Additionaly your code is invalid on using this keyword, because your Abc() function this object points to global window object. You must use new Abc() to properly use this keyword or must create new empty object inside Abc() and return it.

1)

function Abc(data) {
  data=data||{};
  for(key in data) {
    console.log(key, data[key]);
    (function(data,key) { // defining closure for our getter function 
      this.__defineGetter__(key, function() {
              console.log('using getter');
              return data[key];
            });
    }).call(this,data,key);
  }
  // dont need to return: when used *new* operator *this* is returned by default
}
var abc = new Abc(data);

or 2)

function Abc(data) {
  data=data||{};
  var obj={};
  for(key in data) {
    console.log(key, data[key]);
    (function(data,key) { // defining closure for our getter function 
      obj.__defineGetter__(key, function() {
              console.log('using getter');
              return data[key];
            });
    })(data,key);
  }
  return obj; // return object
}
var abc = Abc(data);

If you realy need to known about "defining getters|setterns" read about:

  • Object.defineProperty() MDN and get operator MDN
  • and for back compatibility __defineGetter__ MDN
  • you must also understand what not all top used browsers currently supports getters and setters for properties


key is a local variable in the scope of Abc, the anonymous function you wrote has no variable key, so it uses the one from the outer scope. That variable has changed to the last value in the loop by the time the anonymous function is used, so you see the last value. You can fix this with a closure:

this.__defineGetter__(key, (function(l_key){
    return function() {
        console.log('using getter');
        return data[l_key];
    }
})(key));

In that code l_key is a local copy of key to the anonymous function returned be another anonymous function.

0

精彩评论

暂无评论...
验证码 换一张
取 消