0

I have a JavaScript class like this:

Dog = (function() {
    var name;

    function setName(_name) {
        name = _name;
    }

    return {
        setName: setName,
        name: name
    };
})();

When I run:

Dog.setName('Hero');

Dog.name is always undefined.

I am certainly missing something about JS scoping, but what?

3
  • 2
    How is Dog.setName defined, let alone a function? How does that execute without throwing a TypeError? Commented Aug 27, 2015 at 19:50
  • Right, i changed some function names, etc and i forgot to add it to return block. Updated the answer. Thanks. Commented Aug 27, 2015 at 19:52
  • To downvoter: Could you please explain what's wrong with the question so i could learn from mistakes? Commented Aug 27, 2015 at 19:57

6 Answers 6

3

You are returning an object where name property has a value of name at that point in time (which is undefined). The name property of the returned object is not somehow dynamically updated when the name variable inside the IIFE is updated.

There are many ways to handle what you appear to be wanting to do. Here's one:

Dog = (function() {
    var name;

    function setName(_name) {
        name = _name;
    }

    return Object.defineProperties({}, {
      setName: { value: setName },
      name:    { get: function() { return name; } }
    });

})();

This keeps name as a private variable, which can only be set via setName, but provides a getter property for obtaining its value.

The alternative proposed in another answer is equivalent, just a different way of writing it:

return {
  setName:  function(n) { name = n; },
  get name: function() { return name; }
};

Minor point, but in this particular context you don't need parentheses around your IIFE:

Dog = function() { }();

will work fine.

Sign up to request clarification or add additional context in comments.

2 Comments

Exactly. I too, thought this was the problem. But my knowledge of JS is not enough to solve this.
Thanks, this worked perfectly. Could you please tell me a little more about my class? Is it the right way to go? Are there better ways of implementing classes with closures and private vars?
2

This happens because you assume that setting name in the object retains a reference to the original name variable. Instead, you want to assign it to the current object (which, you might as well ignore the private variable altogether).

Dog = {
  name: '',
  setName: function(n) {
    this.name = n;
  }
};

However, if you want to keep name private then you create a getter for it instead.

var Dog = (function() {
  var name;

  return {
    setName: function(n) {
      name = n;
    },
    get name: function() {
      return name;
    }
  };
})();

Comments

2

The easy way to fix this is:

Dog = (function() {

var dog = {
    setName: setName,
    name: name
};

function setName(_name) {
    dog.name = _name;
}

return dog;
}

In your code, you were setting the wrong name variable.

var name;

function setName(_name) {
    name = _name;
}

In this function, setName is setting the internal variable name and not the property name. In JavaScript, strings are immutable, so when you change it, it creates a new string, and doesn't update the existing one.

3 Comments

Yeah, hoisting. However when i debug the script, i can clearly see that setName is setting the right "name" variable after it runs. I can see that the Dog's "name" is correctly set. But i can not get is outside of its scope.
That's not hoisting, that's closure. Hoisting refers to variables being declared inside a function and they all have a scope at the function level. Your's is a function inside a function and the inner function can reference the outer function's variables.
Yes, you say inner function can reference the outer function's variables, yet in your answer you state that inner function is setting the internal variable name. So how can the inner function reference the outer function's variable? (in regards to my class). I also think the problem you described is hoisting. Thanks.
0

This might be a better pattern for you. You're using the very old ES3 style constructor.

(function(exports) {

  function Dog(name) {
    this.name = name;
  }

  Dog.prototype.speak = function() {
    return "woof";
  };

  // other functions ...

  exports.Dog = Dog;
})(window);

var d = new Dog('Hero');
console.log(d.name); // "Hero"

You might want to look into ES6 classes too

class Dog {
  constructor(name) {
    this.name = name;
  }
}

let d = new Dog('Hero'); 
console.log(d.name); // "Hero"

3 Comments

Is there a way to use Singleton? So i have a Dog object throughout the whole application without creating "new".
This should've been your original question then. You can find countless examples of how to implement a singleton using JavaScript.
Sure, but judging from the setName function he wrote, it seems he was somehow interested in keeping name private.
0

Sounds like you want to make a constructor... Check this sample:

function Dog(prop) {
        this.name = prop.name;
        this.color = prop.color;
    }
    var myDog = new Dog({
        name:'Sam',
        color: 'brown'
    });
    alert()
    console.log('my dog\'s name is: '+myDog.name);
    console.log('my dog\'s color is: '+myDog.color);

you can try it here: http://jsfiddle.net/leojavier/ahs16jos/

I hope this helps man...

Comments

0

Use the 'this' keyword.

Dog = (function() {
    var name;

    function setName(_name) {
        this.name = _name;
    }

    return {
        setName: setName,
        name: name
    };
})();
Dog.setName('Hero');
alert(Dog.name);

2 Comments

Sorry, doesn't work. It can not set this.name, throws an error.
Worked for me in every browser tested. Updated answer to full example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.