How to access attribute of an inner object in javascript object - javascript

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;
};
}

Related

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

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 }

How to inherit private variables in javascript with Object.create

I have this:
function Book (){
this.width = 7;
this.height = 10;
var pages = 100;
this.tear_page = function(){
return --pages;
}
}
function TechBook () {
var pages = 50;
this.open_book = function(){ return "Opened in page "+(pages/2); };
return this;
}
var bBook = Object.create(new Book(), new TechBook());
console.log(bBook);
console.log(bBook.tear_page());
console.log(bBook.open_book());
I can't get this to work. I got as far as getting TechBook to inherit the access to local/private variables from Book but only from the Book functions. If I add new methods or overwrite them, they can't get those variables any more. I wonder if there is a way to still have access to those variables from methods of the subclass and to create new private variables inside the subclass.
If this is not posible in any way, that would mean that you can't have private variables if you want inheritage, or viceversa. Oh, and btw, I know that chrome can now (thanks to ES6) implement classes naturally with: class TechBook extends Book (){} as many other languages, but as support is limited to last versions of chrome at this time... I wonder if there is any other way to solve this problem.
You can't inherit private in any language, only protected or public can be inherited.
That concept does not exist in javascript but u can emulate when creating an object (properties = public, scope things = private);
A work around could be add a property that execute a function that return the private variable/function of the object scope.
If expose a method that return a private object it can be modified because u have the returned reference.
I like to do it like this:
var SomeObject = function() {
//private var one, can't access it outside of this scope
var one = 1;
/*private object anotherObject, can't access it directly
but if you return it with a public function you can modify it.*/
var anotherObject = {
a : 'somevalue'
};
//public prop two, can access it outside of this scope.
this.two = 2;
//public method getOne, you can access it.
this.getOne = function() {
return one;
};
/* return private var anotherObject */
this.getAnotherObject = function() {
return anotherObject;
};
};
var someObject = new SomeObject();
console.log(someObject.two); // print in console 2
console.log(someObject.getOne()); // print in console 1
var referencedObject = someObject.getAnotherObject();
console.log(referencedObject);
referencedObject.a = 'anotherValue';
console.log(someObject.getAnotherObject());
Fiddle
Here is an example of how you might pass data by knowing a secret
function Book(secret) {
secret = secret || {};
var env = {}; // `env` to hold data requiring `secret`
this.width = 7;
this.height = 10;
env.pages = 100;
this.getEnv = function (s) { // give access to `env` if you know the secret
if (s === secret) return env;
};
this.tear_page = function () {
return --env.pages;
};
}
function TechBook(secret) {
secret = secret || {};
Book.call(this, secret); // construct from Book
var env = this.getEnv(secret); // get references via secret
this.open_book = function () {
return "Opened in page " + (env.pages/2);
};
}
TechBook.prototype = Object.create(Book.prototype); // set up inheritance
Using an Object reference as the secret will be more secure than using a primitive as you'll need the original reference for access.
Now you have
var bBook = new TechBook();
console.log(bBook); // instance of TechBook
console.log(bBook.tear_page()); // 99
console.log(bBook.open_book()); // Opened in page 49.5
Reference to fundamentals of prototype inheritance and Object.create property arguments.
Implemented based on your example
function Book (){
this.width = 7;
this.height = 10;
this.pages = 100;
this.tear_page = function(){
return --this.pages;
}
this.init = function() {
return this
}
}
Book.prototype = {
open_book: function(){ return "Opened in page "+(this.pages/2) }
}
var bBook = Object.create(new Book(), {pages: { value: 50 } }).init();
console.log( new Book()) // { width: 7, height: 10, pages: 100, tear_page: [Function], init: [Function] }
console.log( bBook ) //{}
console.log( bBook.width ) //->7
console.log( bBook.height ) //-> 10
console.log( bBook.pages ) // -> 50
console.log( bBook.tear_page()) //-> 49
console.log(bBook.open_book()) //-> Opened in page 25

Classes inside object literals

I was just not sure how to search this out despite many tries, so forgive me if this has been answered before.
The question is simple: can I create an instance of class window.A.class() as window.B?
To clarify, I have an object literal holding all my data for a browser game:
var gameName = {
environment: function() {
this.place = "...";
// ...
// ...
},
game: function() {
this.player = function() {
// ...
}
}
// and so on...
}
Could I create a window-level gameName.environment() instance with var A = new gameName.environment()? Are there any restrictions to creating an object-bound class's instance outside the class' parent object?
It doesn't really matter in this case how/where a function is defined. Consider these two examples:
function Foo() {}
var obj = {
bar: Foo
};
and
var obj = {
bar: function () { }
};
As far as the function and the object are concerned, those two examples are equivalent. So no, there is no problem calling a function assigned to an object property with new. All you need is a reference to the function, it doesn't matter how you get that reference.
You could do
var Environment = gameName.environment;
var A = new Environment();
if you like that better, but that's totally unnecessary.

Always return same instance of class

I have this function:
WSConnection: function() {
var instance = new Connection();
return instance;
},
How to I tweak this code (avoid making multiple instances) so that I always get same instance of my Connection class when I call WSConnection();?
May be:
WSConnection: function() {
this.instance = this.instance || new Connection();
return this.instance;
},
Seems to me it is the most compact of all possible solutions..
Create a local variable next to the object that WSConnection is defined on:
var connectionInstance;
// ... {
WSConnection: function() {
return connectionInstance || connectionInstance = new Connection();
},
// ... }
If the object is itself a class then use a "private class" variable instead (this.connectionInstance).
If this use case is specific to the WSConnection function, use a lazily (or non-lazily—suit yourself) initialized global:
var _instance = null; // you can also just create the singleton instance here (eager/non-lazy initialization)
WSConnection: function() {
if (!_instance)
_instance = new Connection();
return _instance;
}
However, if this is an intrinsic quality of the Connection class altogether, turn it into a singleton; there are several ways to do that (e.g. how Java would do it), but since Javascript is a flexible language, I'd do it this way:
Connection = new Connection() // do this at a global scope
Now you will just use Connection to get the single global instance and not new Connection; this also means that you probably won't need the WSConnection function any more. Also, this ensures nothing will be able to create another instance of Connection (if that's what you need, that is)
Create the instance beforehand. You can wrap your containing object in a function, so that the instance variable is not global:
var someobj = (function() {
var instance = new Connection();
return {
WSConnection: function() {
return instance;
}
};
})();
Alternatively use lazy creation, i.e. create it on the first use:
var someobj = (function() {
var instance = null;
return {
WSConnection: function() {
if (instance == null) {
instance = new Connection();
}
return instance;
}
};
})();
WSConnection: (function(){
var instance = null;
return function() {
if( !instance ){
instance = new Connection();
}
return instance;
};
})()

How can I invoke a prototype function inside of a function object it b

var func_obj = function() {
console.log('wat');
this.my_proto_method();
};
func_obj.prototype.my_proto_method = function() {
console.log('how do I programming');
};
​func_obj();​
I'm trying to get the above code to work. From what I've read this should work, not sure what I'm doing wrong here. Also setup a fiddle here
To access prototype object/method with this you have to create new instance of func_obj. If you want to access prototype methods withod instance then you have to use prototype property as func_obj.prototype.my_proto_method().
var func_obj = function() {
console.log('wat');
this.my_proto_method();
// if called without new then access prototype as : func_obj.prototype.my_proto_method()
};
func_obj.prototype.my_proto_method = function() {
console.log('how do I programming');
};
​new func_obj();​
You need to prefix your call to func_obj with the new prefix:
var func_obj = function() {
console.log('wat');
this.my_proto_method();
};
func_obj.prototype.my_proto_method = function() {
console.log('how do I programming');
};
​var foo = new func_obj();​

Categories