Why isn't my object private when my variable is? - javascript

I'm trying to make a object private, but not sure how to do that. As you can see the name variable is private, I can't edit it but when it comes to the object that I return, I can edit. I dont want that to be possible, though.
I quite new to object-oriented and private methods in javascript, so could someone tell me what's right and wrong here. :) How can I solve this?
Thanks!
var User = function() {
var name = 'bob';
this.getName = function() {
return name;
}
var otherInfo = {
age: 20,
human: true,
}
this.getOther = function() {
return otherInfo;
}
}
var person = new User();
var name = person.getName();
name = 'jenny';
console.log(person.getName()); // bob
var other = person.getOther();
other.age = 'wtf?';
console.log(person.getOther()); // { age: 'wtf?', human: true }

Primitive values such as Strings are passed by value. This means when you assign a String to your variable, you are setting the actual value of the String to the variable.
Objects are passed by reference. This means when you assign an Object to your variable, you are simply making a reference to the Object, not it's actual value. If you had one Object and assigned it to 6 different variables, each variable would have a reference to the same underlying Object.
In your example, your getOther method is returning a reference to the otherInfo object. So when you set the age property to "wtf", you are setting it on the Object that your variable refers to.

You also declare var name twice.
When you var person = new User(); a var name is declared within the scope of the User function.
When you var name = person.getName(); you are declaring a variable with the same name outside the scope of the User function.
So, when you name = 'Jenny'; the interpreter associates this string the name variable outside the scope of User.
In general, it is a bad idea to use such variables with common names (name, title, id, ...) as global variables. I would refer to the object attributes with this. and define setters as well as getters. You can also ignore the setters and refer to the User attributes with person., like this:
function User() {
this.name = 'bob';
this.getName = function() {
return this.name;
}
this.otherInfo = {
age: 20,
human: true,
}
this.getOther = function() {
return this.otherInfo;
}
}
var person = new User();
console.log(person.getName()); // bob
person.name = 'jenny';
console.log(person.getName()); // jenny
console.log(person.getOther(); // { age: 20, human: true }
person.otherInfo.age = 'wtf?';
console.log(person.getOther()); // { age: 'wtf?', human: true }

You don't have “private” things in object scope in Javascript. The only way to hide things from the outside is with a function scope, like you did with name (a local variable in the function User).
To return an object with the same content every time, you could make the object inside the function. Like this, for instance:
this.getOther = function() {
var otherInfo = {
age: 20,
human: true,
}
return otherInfo;
}
Or just:
this.getOther = function() {
return {
age: 20,
human: true,
};
}
In both cases, you will be creating a new object with every call.

It is happen, because in JS object passes by link - there are not coped from source object.
Just try copy object:
var User = function() {
var name = 'bob';
this.getName = function() {
return name;
}
var otherInfo = {
age: 20,
human: true,
}
this.getOther = function() {
return Object.assign({}, otherInfo);
}
}
var person = new User();
var name = person.getName();
name = 'jenny';
console.log(person.getName()); // bob
var other = person.getOther();
other.age = 'wtf?';
console.log(person.getOther()); // { age: 20, human: true }

Related

Constructor function

What is the difference between these two codes?
let user = new function() {
this.name = "John";
this.isAdmin = false;
}
vs
let user = {
name: "John",
isAdmin: false
}
The only difference is that the first user, created via new, has an internal prototype of a (nearly) empty object, and that empty object inherits from Object.prototype:
instance <- (empty object) <- Object.prototype
let user = new function() {
this.name = "John";
this.isAdmin = false;
};
console.log(user);
Technically, the object isn't entirely empty, it has a non-enumerable constructor property pointing to the function
This nearly empty object is the .prototype property of the function - but since that prototype object doesn't have any properties on it, it looks empty. If you had put properties on the function's .prototype object, these would be visible on the intermediate object:
function Foo() {
this.name = "John";
this.isAdmin = false;
}
Foo.prototype.prop = 'val';
let user = new Foo();
console.log(user);
console.log(Object.getPrototypeOf(user).hasOwnProperty('prop'));
console.log(Object.getPrototypeOf(user) === Foo.prototype);
The user from an object literal, on the other hand, inherits directly from Object.prototype:
instance <- Object.prototype
let user = {
name: "John",
isAdmin: false
}
console.log(Object.getPrototypeOf(user) === Object.prototype);
Firstly both of them create a new object and the following code inside them is assigned to the object.
They then execute the code in the function body and can return the value.

How to access attribute of an inner object in javascript object

Here is my code:
function TaskRepository () {
var self = this;
var entity = {
'startTime': 900,
'endTime': 1830
};
this.setStartTime = function(sTime){
self.entity.startTime = sTime;
};
this.getStartTime = function(){
return self.entity.startTime;
};
}
But the following code is not working
var x= new TaskRepository();
x.setStartTime(6);
What's wrong here? What I'm missing? I've also tried to access the attribute via self.entity['startTime'] but this also is not working.
Since you're using the function as a constructor, you have to set the entity as a property, not as a pseudo-private variable. If you plan to make alot of these taskRepos, you can move the two methods to the prototype.
function TaskRepository () {
this.entity = {
'startTime': 900,
'endTime': 1830
};
this.setStartTime = function(sTime){
this.entity.startTime = sTime;
};
this.getStartTime = function(){
return this.entity.startTime;
};
}
OR
function TaskRepository () {
this.entity = {
'startTime': 900,
'endTime': 1830
};
}
TaskRepository.prototype = {
'setStartTime' : function(sTime) {
this.entity.startTime = sTime;
},
'getStartTime' : function(){
return this.entity.startTime;
}
};
Description of the problem:
var x= new TaskRepository();
X is an instance of TaskRepository.
var self = this;
Self is a local variable within X (the instance of TaskRepository) referencing the instance.
var entity = {
'startTime': 900,
'endTime': 1830
};
entity is a local variable within the instance that has nothing to do with the instance and thus you can not access it using the instance.. as seen here.
self.entity.startTime
Solution:
Either remove self ( => entity.startTime )or do this:
this.entity = {
'startTime': 900,
'endTime': 1830
};
Edit for better comprehension:
function TaskRepository () {
//THIS refers to the instance of TaskRepository that we named X
//We don't need SELF since we are already within the instance and it's easy to access THIS
//Entity is now part of any TaskRepository instances
this.entity = {
'startTime': 900,
'endTime': 1830
};
this.setStartTime = function(sTime){
//THIS refers to the instance of TaskRepository because we are in a method that is part of the instance (Hint it has THIS.setStartTime)
//To access entity you use the context this
this.entity.startTime = sTime;
};
this.getStartTime = function(){
return this.entity.startTime;
};
}
entity is not a property of the object you're creating with new TaskRepository, it's a local variable held by the context of the call to new TaskRepository. Since the functions you're creating during that call continue to exist after the call completes, that context is kept alive and you can continue to access it from those functions (they're closures over the context, and so they keep the context alive).
Since entity isn't a property, you don't access it via the instance. It's a variable your functions close over, you just use it directly:
function TaskRepository () {
var self = this; // <=== Unless you use this for something you haven't shown,
// you can remove it
var entity = {
'startTime': 900,
'endTime': 1830
};
this.setStartTime = function(sTime){
entity.startTime = sTime;
// ^---------- no 'self'
};
this.getStartTime = function(){
return entity.startTime;
// ^---------- no 'self'
};
}
Each call to new TaskRepository creates a new context, and so each has its own copy of entity, and so it ends up being specific to the object even though it's not a property on the object.
This is the classic way to create "private" properties for instances.
Side note: The single quotes on your property names in the object initializer where you create entity are unnecessary. Harmless, but unnecessary.
#Shilly's answer works fine, but it would make the entity variable public. In case you wanted to keep it private, just use your code but removing the references to entity via self:
function TaskRepository () {
var entity = {
'startTime': 900,
'endTime': 1830
};
this.setStartTime = function(sTime){
entity.startTime = sTime;
};
this.getStartTime = function(){
return entity.startTime;
};
}

How does function scope work in JavaScript?

I am having some trouble understanding function scope in JavaScript:
function Person() {
var _firstName;
var _lastName;
}
personOne = new Person();
personOne._firstName = "Fred";
alert(personOne._firstName);
This outputs "Fred", but I thought the variables of the Person function would only be accessible inside the function. Why does it work?
In JavaScript, objects are dynamically-expandable.
For example:
var obj = {};
obj.firstName = "Matías"; // <-- This adds the property even if it doesn't exist
In the other hand, if you want to declare properties that should be part of the object in the constructor function you need to qualify them with this:
function Person() {
this._firstName = null;
this._lastName = null;
}
Extra info
If you want to avoid objects from being dynamically-expandable, you can use the ECMA-Script 5 Object.preventExtensions function:
var obj = {};
Object.preventExtensions(obj);
// This property won't be added!
obj.text = "hello world";
Because in the line:
personOne._firstName = "Fred";
You assigned a new property to the object with a value of "Fred". It has nothing to do with the (internally-scoped) variable you declared inside the function.
And in the following line, you're actually alerting the value of the newly-created property and not the variable.
See MDN
It works because you create the property _firstName for the object Person.
personOne._firstName = "Fred"; // here you create the property
In the example below I highlighted the fact that _firstName is innacesible.
function Person() {
var _firstName= "Fred";;
var _lastName;
}
personOne = new Person();
alert(personOne._firstName); // output: "undefined"
If you want to make them accesible you can make use of this to add a new property to the object.
function Person() {
var self = this;
self._firstName= "Fred";
var _lastName;
return self;
}
var personOne = new Person();
alert(personOne._firstName); // output: "Fred"
In this line:
personOne._firstName = "Fred";
you are actually creating the property _firstName of the object. it is not the same as the var you created in the constructor function. You can always add new properties to a JavaScript object. Each property of an object is accessible. There is no such thing as a private property.
Due to function scope you will not be able to change the variables inside Person() from outside if you do not make methods available in Person to set the values of those variables.
function Person(firstName, lastName) {
var _firstName = firstName, _lastName = lastname;
this.getFullName = function () {
return _firstName + " " + _lastName;
};
}
var someone = new Person("Some", "one");
someone._firstName = "something else"; // creates new property in the object someone
var fullName = someone.getFullName(); // still returns "some one"
You cannot reference _firstName inside the object declaration. If you want to access the object property you need to declare with this, otherwise the _firstName or _lastName are considered local variables.
So to access the _firstName or _lastName as object properties, you have to declare as in the following way:
function Person() {
this._firstName = "John";
this._lastName = "Doe";
}
You may access _firstName and _lastName after you instantiate the Person.
personOne = new Person();
personOne._firstName = "Fred";
This way you will override the properties already defined in the object constructor. In your case because _firstName is declared inside the Object definition the scope of that variable is bind locally to that object. But when you instantiate the object you assign a new property which extends the originally declared object.
The most widely accepted pattern is to bind the object properties on the object declaration, but declare the object methods on their prototype level.
Like so:
function Person() {
this._firstName;
//...
}
Person.prototype = function() {
getName : function() {
//...
},
setName : function(name) {
this._firstName = name;
}
}
JS objects objects are "expandable", you can add properties dinamically.
function Person() {
var _firstName;
var _lastName;
}
var personOne = new Person();
console.log(personOne); // Person {}
personOne._firstName = "Fred";
personOne._age = 20;
console.log(personOne); // Person {_firstName: "Fred", _age: 20}
You don't actually have access to the _firstName created inside the function scope, but rather you create a property with that name on your newly created object.
In order to expose a property, you attach it using this keyword (which is a reference to the newly created object).
function Person() {
console.log(this); // Person {}
this._firstName = "Sam";
console.log(this); // Person {_firstName: "Sam"}
}

Private members in Javascript. Can they be non-static?

I have this code here:
var Person = (function() {
var name;
var PersonConstructor = function(n) {
name = n;
};
PersonConstructor.prototype.getName = function() {
return name;
};
return PersonConstructor;
})();
var people = [];
var person1 = new Person("Foo");
var person2 = new Person("Bar");
alert(person1.getName()); // I want it to be Foo
people.push(person1);
people.push(person2);
I got the idea of emulating classes from here.. But of course, I neglected the fact that the private variable var name; is also a static variable. Since this is tripping my current efforts I would like to know if there is a way to keep the private behaviour in this example but avoid the static one?
Use this.
var Person = (function() {
var PersonConstructor = function(n) {
this.name = n;
};
PersonConstructor.prototype.getName = function() {
return this.name;
};
return PersonConstructor;
})();
Unfortunately, this won't preserve the private state.
It's just a scope issue.
var Person = (function(){
var PersonConstructor = function(n){
// ***************************************************************
// PRIVATE VARIABLES AND FUNCTIONS
// ONLY PRIVELEGED METHODS MAY VIEW/EDIT/INVOKE
// ***************************************************************
var myName=n?n:"John Doe";
// ***************************************************************
// PRIVILEGED METHODS
// MAY BE INVOKED PUBLICLY AND MAY ACCESS PRIVATE ITEMS
// MAY NOT BE CHANGED; MAY BE REPLACED WITH PUBLIC FLAVORS
// ***************************************************************
this.toString=this.getName=function(){ return myName }
}
return PersonConstructor;
})();
var person1 = new Person("foo");
var person2 = new Person("bar");
alert(person1.getName());
alert(person1.toString());
alert(person1.myName);
// alerts "foo", "foo", undefined
EDIT - Here is my original solution.
var Person = function(n){
// ***************************************************************
// PRIVATE VARIABLES AND FUNCTIONS
// ONLY PRIVELEGED METHODS MAY VIEW/EDIT/INVOKE
// ***************************************************************
var myName=n?n:"John Doe";
// ***************************************************************
// PRIVILEGED METHODS
// MAY BE INVOKED PUBLICLY AND MAY ACCESS PRIVATE ITEMS
// MAY NOT BE CHANGED; MAY BE REPLACED WITH PUBLIC FLAVORS
// ***************************************************************
this.toString=this.getName=function(){ return myName }
}
var person1 = new Person("foo");
var person2 = new Person("bar");
alert(person1.getName());
alert(person1.toString());
alert(person1.myName);
// alerts "foo", "foo", undefined
There is no "private" when working with prototypes.
It should be noted there is no value in private state, avoid it like the plague. Closures are ugly and expensive.
var o = {
name: value
}
However if you insist on being delusional and want private state badly then
You can store state in a closure
function nameHolder(name) {
return {
get name() {
return name
},
set name(n) {
name = n
}
}
}
Note this is highly inefficient and has little benefit.
Alternatively you can store state in a weakmap
function privates() {
var map = new WeakMap()
return function (key) {
var v = map.get(key)
if (!v) {
v = {}
map.set(key, v)
}
return v
}
}
var NameHolder = (function () {
var state = privates()
return {
constructor: function (name) {
state(this).name = name
},
getName: function () {
return state(this).name
}
}
}())
WeakMap browser support is non-existant so emulate it using pd.Name

Javascript Variable Scope. How Do I Access an Object Var From an Internal Object Function?

I'm wondering how to access the "nam"e variable with a function in a javascript object like so:
function myObj() {
this.vars = {
name: 'bob',
age: '42'
}
this.functions = {
sayName: function() {
alert(this.vars.name);
}
}
}
var obj = new myObj();
obj.functions.sayName();
What am I missing here?
I can get the name var if I pass a reference to itself like:
function myObj() {
this.vars = {
name: 'bob',
age: '42'
}
this.functions = {
sayName: function(name) {
alert(name);
}
}
}
var obj = new myObj();
obj.functions.sayName(obj.vars.name);
but that seems a little redundant... thoughts?
The issue is that in your function this.functions.sayName, the this keyword refers to this.functions instead of the current object1.
Here is a fix for that issue:
function myObj() {
var vars = this.vars = {
name: 'bob',
age: '42'
}
this.functions = {
sayName: function() {
alert(vars.name);
}
}
}
var obj = new myObj();
obj.functions.sayName();
This creates a local variable inside of myObj that you can access from any inner scope. It also exposes that variable to the outside world as a property of any instantiated myObj class, which means you can still do something like
obj.vars.name = 'test';
and it will still change the internal state of the object. That's probably the best solution for your current setup.
1 The behavior of the this keyword in JavaScript is interesting. I recommend the excellent writeup at the Mozilla Developer Network.
You could do it like this:
var thisObj = this;
this.functions = {
sayName: function() {
alert(thisObj.vars.name);
}
}
Not sure if that's the best solution, but it works. The Javascript this always refers to the internal scope if you're inside a function.
Just remove this:
function myObj() {
var vars = {
name: 'bob',
age: '42'
};
this.functions = {
sayName: function() {
alert(vars.name);
}
};
};
var obj = new myObj();
obj.functions.sayName();​
LIVE DEMO

Categories