I came up with the situation below:
function Dog () {
"use strict";
this.age = 1;
var name = "Fido";
this.getName = function () { return name; }
}
And now I'm creating a new instance of the "Dog" class and printing the variable's values.
var d = new Dog;
document.write('<strong>Dog age:</strong> ' +d.age); \\Outputs "1" as expected
document.write('<br/>');
document.write('<strong>Dog name:</strong> ' +d.name); \\Outputs "undefined" as expected, 'cause it's a private variable.
document.write('<br/>');
document.write('<strong>Get Dog name:</strong> ' +d.getName()); \\Outputs "Fido", as expected.
But let's say I want to change the Dog's name, like this:
d.name = "Stinky";
document.write('<br/>');
document.write('<strong>Dog name Again:</strong> ' +d.name);
document.write('<br/>');
document.write('<strong>Get Dog name Again:</strong> ' +d.getName());
Based on this, I got a couple of questions:
Why on earth "d.name" didn't showed me "undefined"? Isn't "name" a private variable? I suppose you can't change private variables values, am I right? I wonder if this process have created a new variable, but this time, a public one, and with the same name. If so, is there a way to prevent the creation of new variables every time I try to assign a new property with the same name? Is there a way to throw a "type error" or something (well, this was what I expected).
And at last: Why "getName" printed the original value, "Fido", even after I assigned a new value to it ?
Any ideas?
Here's a fid to make things easier.
http://fiddle.jshell.net/yZpfg/2/
d.name = "Stinky"; is adding a new (public) property to to the d object, which is an instanceOf Dog.
the getter still references the (private) variable with the value Fido.
If you want to allow a consumer to change a private variable, you need a setter as well:
function Dog () {
"use strict";
this.age = 1;
var name = "Fido";
this.getName = function () { return name; }
this.setName = function (value) { name = value; }
}
var d = new Dog();
d.name; // undefined because there is no name public property
d.getName() // returns the internal private, "Fido"
d.setName('Stinky'); // the internal private is now "Stinky"
You're confusing local variables with instance properties. These are totally separate.
1) You set d.name, an instance property, and it's this you're calling, not the private variable.
2) Fido is the valeu of the private var, which is what your method returns, not the instance property, so the method will always say Fido.
Your original code should probably look like this:
function Dog () {
"use strict";
this.age = 1;
this.name = "Fido";
}
Dog.prototype.getName = function() { return this.name; }
Note I add the method to the prototype rather than explicitly adding it to each instance. This way, the instance inherits it. This is better practice; reusable code should be on the prototype, and it's better performing than adding it each time to every instance.
var name inside your constructor and someObj.name are never ever going to be the same thing. 2 different values that can be set to 2 different things. Instead, you need a setter function that works like your getter:
this.setName = function(newName) { name = newName; };
There is no way to raise an error when setting a property that has the same name as a private variable. They are 2 completely different things, and there is no callback when a property is set that you could even intercept.
Related
I'm trying to learn JavaScript, but got stuck with a problem (more with misunderstanding "this" keyword) that doesn't give me move on.
I've watched a lot of content about it and barely understood it, but still have some troubles.
I have some code:
function Person (name, age) {
this.name = name;
this.age = age;
this.changeName = function (name) {
this.name = name;
}
}
What do we use "this" here for?
As I understood we use "this" to create variable inside function constructor and give it value of our "name" parameter that we could refer to it. Am I right?
Then I have this code:
var p1 = new Person ("John", 30);
p1.changeName ("Jane");
console.log(p1.name);
As I sorted out here, we call method that overwrites our variable that we created to refer to. But it doesn't change actual parameter. So if it's right why do we use it? Doesn't it matter to have actual "name" parameter changed?
The whole code is from teaching app!
So if it's right why do we use it? Doesn't it matter to have actual "name" parameter changed?
No, there's no need to do that in this example. changeName changes the property on the object that was created by new Person.
It's true that that example code is a bit odd, because it creates the changeName function in the constructor but doesn't do the kinds of things you'd normally do when you create the function in the constructor. I'd expect that code to either be this, which puts the changeName on the prototype:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.changeName = function(name) {
this.name = name;
};
(or the class equivalent) or this:
function Person(name, age) {
this.getName = function() {
return name;
};
this.changeName = function(newName) {
name = newName;
};
this.getAge = function() {
return age;
};
}
That code does update the parameter (which has no effect at all on the code calling Person). It doesn't create name and age properties at all; instead, it just makes their values accessible via getName and getAge. There's also a function to change name (changeName); but there's no function to change age. People write code like that so that age cannot be changed from outside code created within the Person constructor.
I guess you may misunderstand which parameter you actually change, so I rewrite it like so, holp this helps.
function Person (argument_name, argument_age) {
this.name = argument_name;
this.age = argument_age;
this.changeName = function (argument_change_name) {
this.name = argument_change_name;
}
}
let p1 = new Person ("John", 30);
p1.changeName ("Jane");
console.log(p1);
The this keyword is used to create and assign values to variables in the class. It is again used to create functions, either getters or setters. So in your code this.changeName is a function that when passed a value will change the name of the person.
eg.
var a = 5; // a is an integer
var b = "Han"; //b is a string
var c = function(name){
//code here
} // then c is also a function;
How do I add properties to a constructor function in JavaScript? For example. If I have the following function.
function Hotel(name)
{
this.name = name;
};
var hotel1 = new Hotel('Park');
can I add a "local" variable that can be used locally within the class as if it were private with the same notation using the keyword "this". Of course it would not be private since objects created will be able to use it correct?
Can I do something like this. Do I use the this keyword or do I use the var keyword
which one is it? I have example 2 on the function constructor on the bottom
1. var numRooms = 40;
2. this.numRooms = 40;
3. numRooms : 40,
function Hotel(name)
{
this.name = name;
this.numRooms = 40;
};
I know that if I want a function within the object constructor I need to use the this word. Will that work as well for normal variables as I have asked above.
function Hotel(name)
{
this.name = name;
this.numRooms = 40;
this.addNumRoomsPlusFive = function()
{
return this.numRooms + 5;
}
};
You can simple add a private variable to your constructor:
function Hotel(name) {
var private = 'private';
this.name = name;
};
But if you will use your Hotel function without a new operator, all properties and functions which was attached to this will become global.
function Hotel(name) {
var private = 'private';
this.name = name;
};
var hotel = Hotel('test');
console.log(name); // test
It is good idea to return an object in constructor function:
function Hotel(name) {
var
private_var = 'private',
private_func = function() {
// your code
};
retur {
name: 'name',
public_func: private_func
}
};
var hotel = Hotel('test');
console.log(name); // undefined
So if you will use Hotel constructor without new operator no global variable will be created. This is possible only if the return value is an object. Otherwise, if you try to return anything that is not an object, the constructor will proceed with its usual behaviour and return this.
can I add a "local" variable that can be used locally within the class as if it were private with the same notation using the keyword "this".
Yes we can:
// API implementation in the library
function Hotel(name) {
// only our library code knows about the actual value
const numRooms = 'privateNoRoomsVar';
this.name = name;
this[numRooms] = 40;
this.addNumRoomsPlusFive = function() {
return this[numRooms] + 5;
}
};
// from the library user's perspective
const hotel = new Hotel('Transylvania');
console.log('rooms+5 =', hotel.addNumRoomsPlusFive());
console.log('hotel.numRooms =', hotel.numRooms); // undefined
// also, users don't have access to 'numRooms' variable so they can't use hotel[numRooms].
If a user looks at the source code and finds out the value privateNoRoomsVar, then they can misuse the API.
For that we need to use symobls:
// API implementation in the library
function Hotel(name) {
// no one can duplicate a symbol so the variable is really private
const numRooms = Symbol();
this.name = name;
this[numRooms] = 40;
this.addNumRoomsPlusFive = function() {
return this[numRooms] + 5;
}
};
// from the library user's perspective
const hotel = new Hotel('Transylvania');
console.log('rooms+5 =', hotel.addNumRoomsPlusFive());
console.log('hotel.numRooms =', hotel.numRooms); // undefined
// there is no way users will get access to the symbol object so the variable remains private.
Private class features, #privateField, are supported by all the browsers so we don’t have to worry about this anymore.
// API implementation in the library
class Hotel {
// private field
#numRooms = 40;
constructor(name) {
this.name = name;
}
addNumRoomsPlusFive() {
return this.#numRooms + 5;
}
};
// from the library user's perspective
const hotel = new Hotel('Transylvania');
console.log('rooms+5 =', hotel.addNumRoomsPlusFive());
console.log('hotel.numRooms =', hotel.numRooms); // undefined
//console.log('hotel.numRooms =', hotel.#numRooms); // throws error
Javascript historically creates objects from prototypes of other objects. It was a result of EMCA2015, that you have a distinct syntax of a class that specifies an object. As an aside, if you mouse over the table in that link it gives dates of when the feature was implemented.
A javascript object created by the new operator is more or less a combination of an associative array ( what you make with let avar={}; ) that can access the function level scopes it is defined in. The keys of the array are its properties. According to its creator, Javascript was created to be an easy to use program language without a hierarchy of types. One of the ways it accomplished this is by more or less considering its mapping type to be equivalent to the prototypical Object which object oriented programming languages describe.
Adding properties in 2022
function AProtoype(arg1, arg2, arg3){
//this defines a property
this.pa=arg1;
/* unicorns in this section */
let x = 1;
/*
a getter which has the same syntax as a property
but returns x from the scope which it references and
not the object.
*/
get getx() => x;
}
let object = new AProtoype(2,3,4);
Is equivalent to the following code for the purposes of data access but not inheritance and typing. The new operator also sets variables on an object that are used for these purposes.
function NewObject(arg1, arg2, arg3){
let prototype = {};
/*dragons in this section, as you are not using the this keyword to accomplish things*/
prototype.pa = arg1;
Object.defineProperty(prototype, "getx", {get:()=>x});
return prototype;
}
//If you do this instead of using the new operator it is an anti-pattern.
//And like all anti-patterns: "But it works!"
let object = NewObject(2,3,4);
The relevant property defining methods where in some sense supported as early as 2010, 2011. I do not have a contemporary source to that time to confirm if you could pull off what I'm doing though, and you'd only want to if all else failed and it needed to run on Internet Explorer 9. In the event all else is failing, you may want to read the documentation for Object.create, which is also of interest because more or less provides an api to make new objects.
Now, for a fun time and horror, you can also define a function that returns this, and get an object back with an equivalent binding of that function. The horror comes when it is an object in global scope, and you rename a property of that object; as Javascript will resolve the name collision by happily writing on whatever it finds if it can. You can then use this to re-implement the prototype pattern that javascripts new operator is built off of conceptually, for the sake of science.
When you use a "constructor function" in Javascript, any properties defined on the instance using the this keyword become public. This is unavoidable, because Javascript objects have no concept of private properties - if it exists, it can be accessed directly as object.property.
For example, if you tried to do as in the following snippet, mimicking a typical getter/setter pattern with a private variable in Java or C# (note that even if this worked, this is not idiomatic Javascript):
function MyObject(privateVar) {
this.privateVar = privateVar;
}
MyObject.prototype.getVar = function() {
return this.privateVar;
};
MyObject.prototype.setVar = function(newVal) {
this.privateVar = newVal;
};
then while you can indeed use the getter and setter to do as you expect, you can also just access and set the private variable directly! Demonstration:
function MyObject(privateVar) {
this.privateVar = privateVar;
}
MyObject.prototype.getVar = function() {
return this.privateVar;
};
MyObject.prototype.setVar = function(newVal) {
this.privateVar = newVal;
};
var obj = new MyObject(1);
// using public getter/setter
console.log(obj.getVar()); // 1
obj.setVar(2);
console.log(obj.getVar()); // 2
// using private variable directly - not intended to work
console.log(obj.privateVar); // 2
obj.privateVar = 3;
console.log(obj.getVar()); // 3 (using public API to get it to show that the direct update to the private variable also affects the intended public methods)
There is though a way to mimic the effect of private variables. They're not actually object properties - because, as I have just demonstrated, such are intrinsically public - but the same can be mimicked by:
not using a "constructor function" at all, but a regular function that happens to return an object. This is all a constructor function really does, anyway - the difference in JS is only syntactic, that you do not need to use the new keyword when you call the function. (Although you still can, if you really prefer - any function that returns an object can be called with new and behave in the same way as without it, although performance will likely suffer a little as the function would then construct a brand new object and throw it away. See MDN for a justification of these statements, particularly step 4.)
inside this function, using a regular variable as the private variable. This variable will be completely inaccessible from outside by the simple rules of scope, but you can still have the returned object retain access to it by the "magic" of closures.
Here is the above getter/setter example translated to this procedure, as well as demonstrations of it working. (I hasten to add again though, that this wouldn't be considered idiomatic code in Javascript.)
function makeObjectWithPrivateVar(privateVar) {
function getPrivateVar() {
return privateVar;
}
function setPrivateVar(newVal) {
privateVar = newVal;
}
return { getPrivateVar, setPrivateVar };
}
var obj = makeObjectWithPrivateVar(1);
// getter
console.log(obj.getPrivateVar()); // 1
// setter
obj.setPrivateVar(2);
// getter again to observe the change
console.log(obj.getPrivateVar()); // 2
// but how could we access the private var directly??
// answer, we can't
console.log(obj.privateVar); // undefined
console.log(privateVar); // ReferenceError, privateVar is not in scope!
Note finally though that it's rare in modern Javascript to use the function-based constructors in this style, since the class keyword makes it easier to mimic traditional class-based languages like Java if you really want to. And in particular, more recent browsers support private properties directly (you just have to prefix the property name with a #), so the initial code snippet translated into a class and using this feature, will work fine:
class MyObject {
#privateVar
constructor(privateVar) {
this.#privateVar = privateVar;
}
getVar() {
return this.#privateVar;
}
setVar(newVal) {
this.#privateVar = newVal;
}
}
var obj = new MyObject(1);
// using public getter/setter
console.log(obj.getVar()); // 1
obj.setVar(2);
console.log(obj.getVar()); // 2
// using private variable directly - now doesn't work
console.log(obj.privateVar); // undefined, it doesn't exist
// console.log(obj.#privateVar); // error as it's explicitly private, uncomment to see error message
Usually it's performed using closures:
var Hotel = (function() {
var numrooms=40; // some kind of private static variable
return function(name) { // constructor
this.numrooms = numrooms;
this.name = name;
};
}());
var instance = new Hotel("myname");
I am reading a tutorial on public/private methods and can't make sense of the difference.
For a private method it says, "Private members are made by the constructor. Ordinary vars and parameters of the constructor becomes the private members."
function Container(param) {
this.member = param;
var secret = 3;
var that = this;
}
And for public methods, "this technique is usually used to initialize public instance variables. The constructor's this variable is used to add members to the object."
function Container(param) {
this.member = param;
}
As you can see both functions have a params paramater and a this.member = param;. Yet one is a private instance variable and the other is a public instance variable?
Understanding closures
Opening a new function body creates a new scope. This kind of scope is called a closure in JS. Variables created within that scope are accessible in all its sub-scopes. This means any var-created variable will be made visible to sub-functions. In this example, myTemporaryVar is accessible within subScope.
function myParentScope() {
var myTemporaryVar = "sample";
function subScope() {
console.log(myTemporaryVar);
}
return subScope();
}
When you use a function with the new keyword, a new closure is created for the current instance. Any function created within that constructor will keep access to the scope variables. In the next example, the function sayHi can access the temporary variable myName.
function Person(name) {
var myName = name;
this.sayHi = function() {
console.log("Hi, my name is " + myName + ".");
};
}
p = new Person("Bob");
p.sayHi(); // Hi, my name is Bob.
Actually, passed parameters are the same as var-created variables. The constructor's parameters are accessible within any sub-function. So the previous example can be reduced to:
function Person(name) {
this.sayHi = function() {
console.log("Hi, my name is " + name + ".");
};
}
p = new Person("Bob");
p.sayHi(); // Hi, my name is Bob.
This is a very unique feature of JavaScript because it means var-created variables still exist after the end of the function as long as there still is a way to access them.
Simulating Class-based OOP privacy
Closures can be "abused" to create private members with getter and setter functions.
function Person(name) {
this.getName = function() {
return name;
};
this.setName = function(newname) {
name = newname;
};
}
p = new Person("Bob");
console.log(p.getName()); // "Bob"
p.setName("Alice");
console.log(p.getName()); // "Alice"
p.name; // undefined
Why this is not true privacy
The getter and setter have to be created within the constructor in order to access the var-variable. Methods added in the common prototype-extension way can't access them. Prototype-methods have to use the setters and getters too, which makes the privacy of such variables quite useless.
Person.prototype.sayGoodMorning = function() {
console.log("Good morning, my name is " + this.getName() + ".");
}
The only way to directly access the variable within a method is to actually create it in the constructor. But putting all methods inside the constructor is extremely inefficient as a new copy of the methods will be created for each instance. This is why many people prefer simply using custom notation to identify would-be private members. The Google JavaScript Style Guide recommends placing an underscore at the end of the variable name.
function Person(name) {
this.name_ = name;
}
Person.prototype.getName = function() {
return this.name_;
}
Person.prototype.setName = function(name) {
this.name_ = name;
}
Person.prototype.sayGoodMorning = function() {
console.log("Good morning, my name is " + this.name_ + ".");
}
It is the responsibility of the programmer to not be stupid and access the would-be private members. Note that this goes in total contradiction with Crockford's opinion, but to each is own. I learned JS after Python, so the underscore privacy is like a second nature to me.
No one of this variable is trully private because if you instanciate Container you can access to secret variable :
function Container(param) {
this.member = param;
var secret = 3;
var that = this;
}
var container = new Container();
console.log(container.secret);
container.secret = "toto";
console.log(container.secret);
console.log(container);
Here the result :
As you can see you can access to secret without any "Getter/Setter".
If you want to do object javascript with truly private variable, look at this tutorial :
http://blog.stchur.com/2011/09/26/true-private-variables-in-javascript-with-prototype/
For example, I have a person constructor as follows:
function Person(name) {
var username = name;
return {
getName: function() {return username;},
setName: function(name) {username=name;}
}
}
Then I want to create another Employee constructor which would inherit from the Person constructor and has a getId method that returns an auto-incremented id value for every object created out of the Employee constructor. For example,
var employee1 = new Employee('foo');
employee1.getName() // will output foo
employee1.getId() // will output 1
var employee2 = new Employee('qoo');
employee2.getName() // will output qoo
employee2.getId() // will output 2
The problem is I'm a little bit confused about how to create an id variable on Employee constructor that it only gets called once and then each object will get a fixed auto-incremented value. How should I set up the Employee constructor and the inheritance?
Given your Person constructor, you do not want to use prototypal inheritance but parasitic one.
The Employee constructor will be quite similar in the regard of creating local variables and defining local functions, only that the id will be initialized by incrementing a static counter instead of a paramter:
Employee.counter = 0;
function Employee(name) {
var that = Person(name);
var id = Employee.counter++;
that.getId = function() { return id; };
return that;
}
So this is an example of the famous JavaScript Module Pattern:
var Person = (function() {
var _name; // so called 'private variable'
function Person(name) {
_name = name;
}
Person.prototype.kill = function() {
console.log(_name + ' has been shot');
};
return Person;
})();
var paul = new Person('Paul');
paul.kill();
So far so good right? This logs 'Paul has been shot' to the console, which is what we want.
But.
Is _name really a private variable? I would define a private variable as a variable that belongs to an instance of an object, which is not accessible for the outside world. The last part works, I can not access _name from outside the closure.
But if I do this:
var paul = new Person('Paul');
var bran = new Person('Bran');
paul.kill();
bran.kill();
This will then log 'Bran has been shot', twice. No Paul there. So _name is actually shared with all instances of my Person object. That's what I would define as a 'static variable', although it's also not accessible from outside.
So is there any way to create a real private member variable with the module pattern? One that is not static.
Something that also happens a lot is defining this._name inside the constructor function, but that kills the private part, it's now accessible from outside:
function Person(name) {
this._name = name;
}
var bran = new Person();
console.log(bran._name); // yep, accessible
Question:
So. Private is not really private, just static. How do we create a real private member variable with the module pattern? A variable which belongs to an instance, which is not static, and a variable which is not accessible from the outside.
You're right; _name is more of a static variable. It is kept in the closure that contains the constructor, so every use of the constructor will use that same variable. And remember, this has nothing to do with classes, and everything to do with closures and functions. It can be pretty handy, but it isn't how you do private members.
Unsurprisingly, Douglas Crockford has a page devoted to private members in Javascript.
In order to make private members, you have to go 'one level deeper'. Don't use a closure outside of the constructor; use the constructor as the closure. Any variables or methods defined inside the constructor are obviously not usable by the outside world. In fact, they aren't accessible by the object, either, so they are rather extremely 'private'.
We want to use our private members though. :) So what to do?
Well, in the constructor, do this:
var Klass = function () {
var private = 3;
this.privileged = function () { return private; };
};
and then:
var k = Klass();
console.log(k.privileged()); // 3
See how that's using the constructor as a closure? this.privileged lives on, attached to the object, and thus private lives on, inside this.privileged's closure.
Unfortunately, there's one problem with private and privileged methods in Javascript. They must be instantiated from scratch each time. There is no code sharing. That's obviously what we want with private members, but it isn't ideal for methods. Using them slows down object instantiation and uses more memory. It's something to keep in mind when/if you run into efficiency problems.
"Real private member variables" and prototype based methods do not play nice together. The only way achieve what you want is to create all methods in the constructor.
var Person = (function() {
function Person(name) {
this.kill = function() {
console.log(name + ' has been shot');
};
}
return Person;
})();
var paul = new Person('Paul');
var bran = new Person('Bran');
paul.kill(); // Paul has been shot
bran.kill(); // Bran has been shot
But this will use more memory and be slower since each instance has a unique version of the kill function.
Conventionally, the underscore prefix is used for semi-private instance properties, as long as the data exposed is not a security risk. Most consumers of your javascript code know not to mess with underscore prefixed properties.
Some more reading you may find useful is here.
The problem is that your _name variable is outside the Person scope and shared between all Person instances. Do something like the following instead :
(function() {
var Person = function(name) {
var _name = name; // so called 'private variable'
this.getName = function()
{
return _name;
}
this.kill = function()
{
console.log(this.getName() + ' has been shot');
}
}
/*Alternative
Person.prototype.kill = function() {
console.log(this.getName() + ' has been shot');
};*/
window.Person = Person;
})();
var paul = new Person('Paul');
var bran = new Person('Bran');
paul.kill();
bran.kill();
A variable that needs to be private in instance scope has to be in such scope, for example:
function Person(name) {
var _name = name;
this.kill = function() {
console.log( _name + ' has been shot' );
}
}
A drawback of this is that kill has to be defined in a scope that also has access to the private variable.
In order to create a private var, you should put it inside the constructor, so your code would be like this:
var Person = (function() {
function Person(name) {
// since name is a param, you don't even have to create a _name var
// and assign name to it
this.getName = function () {
return name;
};
}
Person.prototype.kill = function() {
console.log(this.getName() + ' has been shot');
};
return Person;
})();
var paul = new Person('Paul');
paul.kill();
You can also declare .kill inside the constructor:
var Person = (function() {
function Person(name) {
this.kill = function() {
console.log(name + ' has been shot');
};
}
return Person;
})();
var paul = new Person('Paul');
paul.kill();