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?
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.
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;
}
};
})();
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.
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"
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...
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);
Related
When using get in an object like this, get works:
var people = {
name: "Alex",
get sayHi() {
return `Hi, ${this.name}!`
}
};
var person = people;
document.write(person.sayHi);
But with a function I get an error. How to use Getters and Setters in a function like this?
function People2() {
this.name = "Mike";
get sayHi() {
return `Hi, ${this.name}!`;
}
};
var user = new People2();
document.write(user.sayHi);
You can use the actual get and set keywords only in classes (ES2015) and object literals.
ECMAScript 5
In ES5, your would typically use Object.defineProperty to implement what you're trying to achieve:
function People2() {
this.name = "Mike";
}
Object.defineProperty(People2.prototype, "sayHi", {
get: function() {
return "Hi, " + this.name + "!";
}
});
ECMAScript 2015
In ES2015, you could also use classes to achieve the desired behavior:
class People2 {
constructor() {
this.name = "Mike";
}
get sayHi() {
return `Hi, ${this.name}!`;
}
}
You can try this
<script>
function People2(name) {
this.name = name;
};
People2.prototype = {
get sayHi() {
return `Hi, ${this.name}!`;}
};
var user = new People2('Alex');
document.write(user.sayHi);
</script>
or this one...
<script>
function people(name) {
this.name = name;
};
Object.defineProperty(people.prototype, 'sayHi', {
get: function() { return `Hi, ${this.name}!`; }
});
var person = new people('Alex');
document.write(person.sayHi);
</script>
For the case you want to define a property like as name for a function with more control, we can use Object.defineProperty on function itself as following:
function people(name) {
//this.name = name; //this can be modified freely by caller code! we don't have any control
var _name = name; //use a private var to store input `name`
Object.defineProperty(this, 'name', {
get: function() { return _name; }, //we can also use `return name;` if we don't use `name` input param for other purposes in our code
writable: false, //if we need it to be read-only
//... other configs
});
};
var person = new people('Alex');
console.log(person.name); //writes Alex
For example, use this:
function People2() {
this.name = "Mike";
this.__defineGetter__("sayHi", function() {
return `Hi, ${this.name}!`;
});
};
i am just starting learn a prototypes in javascript an i can't understand what a problem is in my code. I'm sorry, my question may seem silly
i got an error like this :
Uncaught TypeError: undefined is not a function
why is undefined? I inherited a function of the user.
can't understand it :(
var user = {
sayName: function() {
console.log(this.name)
}
};
user.name = "viktor";
user.sayName();
var user2 = {};
user2.prototype = user;
user2.name = "segey";
user2.sayName();
All you need to set up the prototype chain with plain objects is:
var user2 = Object.create(user); // set `user` as prototype of `user2`
user2.name = "segey";
user2.sayName();
For you question correct solution will:
function User() {
this.name = 'Viktor';
return this;
}
User.prototype = Object.create({
sayName: function() {
return this.name;
}
});
function User2() {}
User2.prototype = Object.create(User.prototype);
var user = new User();
user.sayName(); // 'Viktor'
user2 = new User2();
user2.name = 'Bogdan';
user2.sayName(); // 'Bogdan'
And detailed explanation with example.
Let say we have some basic class Animal. Our Animal have age and name.
function Animal() {
this.age = 5;
this.name = "Stuffy";
return this;
}
Animal.prototype = Object.create({
getAge: function() {
return this.age;
},
getName: function() {
return this.name;
}
});
And when I spent some time with architecture I understand that I also need SubClass of Animal. For example, let it will be Dog class with new property and functions. And also Dog must extend functions and properties from Animal class.
function Dog() {
Animal.apply(this, arguments); // Call parent constructor
this.wantsEat = true; // Add new properties
return this;
}
Dog.prototype = Object.create(Animal.prototype); // Create object with Animal prototype
Object.extend(Dog.prototype, { // Any extend() function, wish you want
constructor: Dog, // Restore constructor for Dog class
eat: function() {
this.wantsEat = false;
return this;
}
});
Or you can use Object.defineProperties() and extend in this way:
Dog.prototype = Object.create(Animal.prototype, {
constructor: {
value: Dog
},
eat: {
value: function() {
this.wantsEat = false;
return this;
}
}
});
I have an Object with a getter and setter where I'm intending to protect a property so that it can only be updated through a method. Let's call this object Person which has the following structure:
function Person(data) {
var _name = data.name;
this.name = function () {
return _name;
};
this.setName = data.setName || function (newValue) {
_name = newValue;
};
}
I want to be able to override setName and pass different implementations to each instance of Person but I can't quite seem to get it to work. I'm certain this is a closure issue, but I just can't get my head around it.
Given the following usage scenario, what am I doing wrong?
var p1 = new Person({
name: "Paul",
setName: function (newValue) {
this._name = newValue;
}
});
var p2 = new Person({ name: "Bob" });
p1.setName("Paul (updated)");
p2.setName("Bob (updated)");
p1 never updates its value and so is always "Paul" whereas p2 does and becomes "Bob (updated)". I want to be able to create as many unique instances of Person as I want, where some of them have their own implementation of setName and others will just use the default instance.
I've tried wrapping data.setName up in a closure like this and setting my custom setName to return the value instead:
this.setName = data.setName ? function () {
return (function (value) {
value = data.setName();
}(_name));
} : function (newValue) { _name = newValue; }
But I'm having no luck - I obviously just don't get closures as well as I thought! Any and all help always appreciated.
codepen.io example here
this._name = newValue;
_name is a private variable, not a property. It cannot be accessed on this. Have a look at Javascript: Do I need to put this.var for every variable in an object? if you're not sure about the difference.
function (value) {
value = data.setName();
}
value here is a local variable to that function's scope, not a "reference" to the _name variable you passed in. Setting it will only put a different value in that local variable, but not change anything else.
Possible solutions:
Pass a setter function that has access to the _name variable to the
validator:
function Person(data) {
var _name = data.name;
this.name = function () {
return _name;
};
if (data.nameValidator)
this.setName = function(newValue) {
_name = data.nameValidator(newValue, _name);
};
else
this.setName = function (newValue) {
_name = newValue;
};
}
var p1 = new Person({
name: "Paul",
nameValidator: function (newValue, oldValue) {
return (newValue /* ... */) ? newValue : oldValue;
}
});
Or let the validator return the value:
function Person(data) {
var _name = data.name;
this.name = function () {
return _name;
};
this.setName = function (newValue) {
_name = newValue;
};
if (data.makeNameValidator)
this.setName = data.makeNameValidator(this.setName);
}
var p1 = new Person({
name: "Paul",
makeNameValidator: function (nameSetter) {
return function(newValue) {
if (newValue) // ...
nameSetter(newValue);
};
}
});
Or make _name a property to which the validator can write. You could also put this on the original data object, for not exposing it on the Person interface:
function Person(data) {
this.name = function () {
return data.name;
};
if (data.setName)
this.setName = data.setName.bind(data);
else
this.setName = function(newValue) {
_name = newValue;
};
}
var p1 = new Person({
name: "Paul",
setName: function (newValue) {
if (newValue) // ...
this.name = newValue;
}
});
I feel this is the simplest solution so far, using bind:
function Person(data) {
var _data = {name:data.name};
this.name = function () {
return _data.name;
};
this.setName = data.setName ? data.setName.bind(_data) : function (newValue) {
_data.name = newValue;
};
}
var p1 = new Person({
name: "Paul",
setName: function (newName) {
this.name = newName;
}
});
var p2 = new Person({ name: "Bob" });
p1.setName("Paul (updated)");
p2.setName("Bob (updated)");
console.log(p1.name());
console.log(p2.name());
Instead of using a primitive to store the name we use an object, whose reference we can pass as this using bind. Originally, when you wrote this._name = newValue;, this meant something quite different from what you thought, namely the object you pass the intantiate the new Person.
demo
You are setting this._name in your p1 Person, whereas you are accessing the 'internal' variable _name in your Person() function, instead of this._name. This can easily be fixed by adding this. to parts in your Person() class.
function Person(data) {
this._name = data.name;
this.name = function () {
return this._name;
};
this.setName = data.setName || function (newValue) {
this._name = newValue;
};
}
Your second example, with the wrapping, is not working due to some errors you appear to have made in the setName() function. Firstly, you are not passing anything to the data.setName() function, meaning that it is receiving undefined for the first parameter. Next up, you are setting value to the returned value, whereas you should instead be setting _name to the returned value. Try this instead:
this.setName = data.setName ? function (newValue) {
return (function (value) {
_name = data.setName();
}(newValue));
} : function (newValue) { _name = newValue; }
U think it's a problem with this
this.setName = function (newValue) {
if (data.setName) {
data.setName.call(this, newValue);
} else {
_name = newValue;
}
}
$(document).ready(function () {
var patient = (function (options) {
var age = options.age;
var name = options.name;
function getName() {
return this.name;
}
function setName(val) {
name = val;
}
function getAge() {
return this.age;
}
function setAge(val) {
age = val;
}
return {
getAge: getAge,
setAge: setAge,
getName: getName,
setName: setName
}
})();
});
I realize that I'm never passing any options in my example here.
If I try to do something like patient.setAge('100') and then console.log(patient.getAge()) I get an error saying cannot read property Age of undefined. The overarching theme that I'm trying to get at is within a module, how can I emulate consturctors to instantiate a new patient object while keeping all the OOP goodness of private variables and all that jazz.
I've seen some examples of constructors in a module pattern on here and I haven't understood them very well. Is it a good idea in general to have a constructor in a module? Is its main purpose similarity with class-based languages?
This is a constructor:
function Patient(options) {
options = options || {};
this.age = options.age;
this.name = options.name;
}
$(document).ready(function () {
var patient = new Patient();
});
You can put it inside a module if you want. What you shouldn’t do is provide getters and setters, especially ones that don’t do anything. If you’re exposing a variable through two properties to get and set it, it should just be one property.
Try this
function Patient (options) {
options = options || {};
var age = options.age;
var name = options.name;
function getName() {
return name;
}
function setName(val) {
name = val;
}
function getAge() {
return age;
}
function setAge(val) {
age = val;
}
return {
getAge: getAge,
setAge: setAge,
getName: getName,
setName: setName
}
}); // pass empty object
$(document).ready(function () {
var p1 = new Patient({});
var p2 = new Patient();
var p3 = new Patient({age:20});
var p4 = new Patient({name:"abcd"});
var p5 = new Patient({age:21, name:"abcd"});
});
Consider this simple JavaScript module pattern:
var human = (function () {
var _name = '';
return {
name: _name,
setName: function (name) {
_name = name;
}
}
})();
human.setName('somebody');
alert(human.name); // shows an empty string
human = (function () {
var _name = '';
return {
name: function() {
return _name;
},
setName: function (name) {
_name = name;
}
}
})();
human.setName('somebody');
alert(human.name()); // shows 'somebody'
Why the second closure works fine, while the first closure is not working? See example here.
Please also see this fiddle, which proves that simple properties can be used instead of getter functions.
In Javascript
Strings and primitive types (boolean and numeric) are passed by value
Objects, arrays, and functions are passed by reference
As name is a string name: _name will store the current value of _name and not the reference to _name.
setName in your example will modify only _name.
getName will access _name which holds the current value.
.name will access the copied value which was set during the initialisation (name: _name).
See also SO: Javascript by reference vs. by value
Try with this:
var human = (function () {
var _name = '';
var returnObj = {};
returnObj.name = _name;
returnObj.setName = function (name) {
_name = name;
returnObj.name = name;
};
return returnObj;
})();
human.setName('somebody');
alert(human.name);
The problem with your code was that setName was assigning a value to the _name variable and you ware accessing the name property of the returned object.