Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object.defineProperty get/set closure

Ok, I try to create new object this way:

var src = {a:'a', b:'b', c:'c'};
var out = {};
for(var prop in src){   
    Object.defineProperty(out, prop,{
        get: function(){
            return src[prop];
        },
        set: function(val){
            src[prop]=val;
        }
    })
}

And get a bad result:

out = {a:'c', b:'c', c:'c'}

I know other ways to create this object, so as:

for (var prop in src) {
    (function(prop) {
        Object.defineProperty(out, prop, {
            get: function() {
                return src[prop];
            },
            set: function(val) {
                src[prop] = val;
            }
        })
    })(prop)
}

or:

Object.keys(src).map(function(prop){
    Object.defineProperty(out, prop,{
        get: function(){
            return src[prop];
        },
        set: function(val){
            src[prop]=val;
        }
    })
})

But I can't understand why, in the first method, a string parameter "prop" will be sent to the function 'defineProperty' by link. Help me to understand this please. Sorry for bad english.

like image 964
3y3 Avatar asked Oct 05 '22 17:10

3y3


1 Answers

When you create a function inside a loop you create a closure around the variables used in that loop. In this case there is a closure around prop. Each function (the getters) has a reference to prop so when they are called later on (when the getter is used) they use the value in prop which happens to be the last value that was assigned in the loop.

In other words, since the getter is called later, the value in prop is whatever value it was last set to. defineProperty, on the other hand, gets the correct value since there is no closure. It is called with the value at the time of the call rather than after the loop is complete.

like image 122
Hemlock Avatar answered Oct 10 '22 02:10

Hemlock