0

(example is from the book but I don't seem to get it)

    function User (properties){
       for( var i in properties){
           (function(){
               this["get"+i] = function () { return properties[i];};
               this["set"+i] = function (valueOne) { properties[i] = valueOne; }; 
           }) ();
        }// END for
    }// END User

var userOne = new User ({ name: "Billy", age: 35 });
userOne.getname(); 

When I run this, User does not have getname method. How can I make the privileged method works?

Teemu
  • 21,017
  • 6
  • 49
  • 91
Bew Promtong
  • 61
  • 1
  • 2
  • 7

4 Answers4

3

You need both this and i captured in the closure:

function User (properties){
   for( var i in properties){
       (function(t, i){
           t["get"+i] = function () { return properties[i];};
           t["set"+i] = function (valueOne) { properties[i] = valueOne; }; 
       }) (this, i);
    }// END for
}// END User
rid
  • 54,159
  • 26
  • 138
  • 178
2

this is not who you think it is. Since you invoked the IIFE on nothing, this will be the global scope, so the window will get the methods getname, etc, which is not what you expected.

To fix it, if you want to keep the IIFE, you need to call it on the right context:

function User (properties){
       for( var i in properties){
           (function(key){
               this["get"+key] = function () { return properties[key];};
               this["set"+key] = function (valueOne) { properties[key] = valueOne; }; 
           }).call(this, i);
        }// END for
    }// END User

var userOne = new User ({ name: "Billy", age: 35 });
userOne.getname(); 

Note that you also forgot to pass the i argument to the function and to interpret it as parameter. Otherwise all the functions would get bound to the same key, thus userOne.getname would return 35.

Tibos
  • 26,262
  • 4
  • 43
  • 59
1

This is because you used an immediately invoked function

    for( var i in properties){
       (function(){  //<--- This
           this["get"+i] = function () { return properties[i];};
           this["set"+i] = function (valueOne) { properties[i] = valueOne; }; 
       }) ();
    }

Remove it and it will still not work, but your methods will be there. To get it fully working you should preserve i

    for( var i in properties){
       (function(i){  //<--- This
           this["get"+i] = function () { return properties[i];};
           this["set"+i] = function (valueOne) { properties[i] = valueOne; }; 
       }) (i); //<--- and this
    }

The latter issue is not as interesting (though related to) the first one.

Javascript has only what is known as "function scope" meaning that the only thing that limits function scope is...well...a function. Therefore, a common pattern is to use IIFEs like this inside of for loops or in many places where you don't want variables to leak.

However, the this parameter in javascript is weird. Understand the following and it will save you a ton of hassle: this in javascript is no different from any other parameter.

Let me explain.

There are four ways to invoke a function in javascript.

myFn.call("this param", "param 1", "param 2"); //this is "this param"
myFn.apply("this param", ["param 1", "param 2"]); //this is "this param"
myFn("param 1", "param 2"); 
//javascript takes a guess at what `this` should be - 
//usually it is set to the global `window`.
new myFn("param 1", "param 2"); 
//`this` is a new function with its' prototype set to myFn.prototype

If you were always to use the .call form all ambiguity would be gone and you can see that this is exactly like every other parameter. However that's extra syntax and people prefer using the simpler form which means you have to consider the rules for what is "this".

Therefore what you are doing in your example is placing getters and setters on the global window object.

I'm going to make a statement here that your book probably doesn't agree with but that I've picked up from years of learning, working with, and teaching javascript:

Don't use the new and this keywords.

These two keywords introduce a ton of concepts into JS that are confusing and really - unless you're making something very performance sensitive (you're not, I know you think you are, but you're not) - not necessary. Instead create new objects simply like this:

var user = { name: "Billy", age: 35 };

if you absolutely must have getters and setters this will do it:

function createObjectWithProps (properties){
   var obj = {};
   var state = {}[
   for( var k in properties){
       (function(key) {
           obj["get"+key] = function () { return state[key];};
           obj["set"+key] = function (valueOne) { state[key] = valueOne; }; 
        })(k)
    }
    return obj;
}

var userOne = createObjectWithProps ({ name: "Billy", age: 35 });
userOne.getname(); 

Although I'll go even further to state that getters and setters are not terribly useful in js, and when you DO use them it is standard to follow a pattern similar to what knockout does.

George Mauer
  • 103,465
  • 117
  • 349
  • 581
  • 2
    Wouldn't this bind `i` inside the functions to the last `i`? – rid Jan 22 '14 at 16:28
  • @rid it would not because at the time you are invoking it `i` is the "current" value then it iterates. – George Mauer Jan 22 '14 at 16:34
  • 1
    You can test to see the result. `setname` will exist, but it will return `35`. – rid Jan 22 '14 at 16:35
  • Thanks, George! I am trying to understand privileged methods in JavaScript and the book suggests the (function(){},)(); and says it will be much more powerful. I guess I will have to come here more often to learn javaScript. Can you suggest any good source for javaScript as a web developer? – Bew Promtong Jan 22 '14 at 16:37
  • Good point @rid, you're right, the issue is that `i` is being used inside those functions which is why it shows the last value at that point. – George Mauer Jan 22 '14 at 16:41
  • @BewPromtong [Javascript the Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) is *the* book to read. It's fairly short and will educate you about all the parts of javascript you need to be careful of. I don't agree with everything Crockford says in it, but all of it is important for beginners to hear. – George Mauer Jan 22 '14 at 16:42
  • @BewPromtong, I recommend [JavaScript: The Good Parts](http://shop.oreilly.com/product/9780596517748.do) by Douglas Crockford. Crockford is very opinionated though, so don't take everything he says as law. – rid Jan 22 '14 at 16:43
  • @rid high five on identical simultaneous advice – George Mauer Jan 22 '14 at 16:46
  • @GeorgeMauer, doesn't `createObjectWithProps()` need to return `obj` and not be called with `new`? – rid Jan 22 '14 at 16:47
  • Thanks all. Where do I close the question and how to mark for credit? I will try to absorb 'this' in JavaScript. – Bew Promtong Jan 22 '14 at 16:48
  • @rid yup, good catch, I accidentally ctrl-z'ed the return out of there. – George Mauer Jan 22 '14 at 16:48
  • @BewPromtong, click the checkmark under your accepted answer. Also take a look at http://stackoverflow.com/questions/3127429/javascript-this-keyword to learn more about `this`. – rid Jan 22 '14 at 16:50
  • @BewPromtong no need to close the question. It will stay open and people will continue reading it and learning from it. You can upvote any answers you like and select the "best" answer by clicking the checkmark near the vote number – George Mauer Jan 22 '14 at 16:50
  • @Rid and GeorgeMauer Thanks both of you for a quick reply. I can only click one. So I give credit to GeorgeMauer. I also just got a javaScript the Good Parts e-book. Thanks both! – Bew Promtong Jan 22 '14 at 16:56
-1

The problem is the "this" keyword.

Because you are using it inside an immediately invoked function it is pointing to the global scope.

try this:

function User (properties){

   for( var i in properties){
       (function(self,i){
           self["get"+i] = function () { return properties[i];};
           self["set"+i] = function (valueOne) { properties[i] = valueOne; }; 
       }) (this,i);
    }// END for
}// END User
Chris Charles
  • 4,266
  • 15
  • 29
  • Should be noted that you can also do `.call(this, i)` rather than `(this, i)` and keep the `this["get"+....` stuff. I prefer it the way you wrote it too though. – George Mauer Jan 22 '14 at 16:54