Is this a safe way to reference objects in JavaScript? - javascript

If I were to define two objects myDataStore and myDrawer something like this:
var myDataStore = function(myObjectRef) {
this.myInternalObject = myObjectRef;
};
var myDrawer = function(myObjRef) {
this.myInternalObject = myObjectRef;
};
And if I were to create an object like so:
[[EDIT - Adjusted Object Creation to Ensure 'this' is being mapped to myObject, not the global window object]]
(function(){
var myObject = window.myObject = function(){
this.dataStore = new myDataStore(this);
this.drawer = new myDrawer(this);
}
})();
Then myObject.dataStore.myInternalObject, and myObject.drawer.myInternalObject, would simply be pointers back to the original 'myObject' - not taking up any additional memory in the browser. Yes?
I am interested in implementing techniques like this - as it makes it easy for objects to communicate with each other.

Nope. this refers to whatever is on the left hand side of the . or if there is no left hand side of the . then it's the global object.
So if you did this:
var MyObj = {
"create": function() {
var myObject = {
dataStore = new myDataStore(this);
drawer = new myDrawer(this);
};
}
};
MyObj.create();
this would be MyObj. If you did this:
var myObject = {
dataStore = new myDataStore(this);
drawer = new myDrawer(this);
};
(not in a function) this would be window (assuming this is in a browser).

Yes, your assumption is correct. myInternalObject will be a reference and not a new object. You can test it like this:
var MyDataStore = function(myObjectRef) {
this.myInternalObject = myObjectRef;
};
var data = {
value: "value"
};
var dataStore = new MyDataStore(data);
data.value = "test";
console.log(dataStore.myInternalObject); // logs { value : "test" } instead of { value: "value" }

No. myObject.dataStore.myInternalObject and myObject.drawer.myInternalObject will both point to the global object (mapped to window in browsers), unless you're inside a function already when you declare myObject. In other words, it will be set to whatever this is in the context in which you declare myObject. It won't be myObject itself.

Related

Javascript Arrays as an object field

I am running into a problem with using an array as a Javascript field.
var Object = function () {
var admins = [];
this.addAdmin = function(admin){
this.admins.push(admin)
}
}
Normally I would expect admin to be pushed into the array admins but instead I get a 'cannot read property 'push' of undefined'.
If I'm not mistaken when I initialized the Object with new Object(), admins = []; should initialize the array. Is this a limitation of Javascript?
Thank you in advance.
var array creates a local variable. It does not create a property on the object.
You need:
this.admins = [];
or
admins.push(admin) /* without this */
In your function admins is a local variable to the function. You need to declare admins as a property on the instance.
function Obj(){
this.admins = [];
}
Obj.prototype.addAdmin = function(admin){
this.admins.push(admin);
}
obj = new Obj();
obj.addAdmin('tester');
Also, because Object is the global base object, don't create functions or objects named Object.
I suspect you've gotten confused (which is easy :-) ) because you've seen code like this:
class Obj {
admins = [];
addAdmin(admin) {
this.admins.push(admin);
}
}
That uses the modern class and class fields syntax to puts an admins property on the object constructed via new Obj. (Note there's no var before admins = [];.) But in your code, you've used the older function-based syntax. Within your function, var admins = []; just creates a local variable, not a property.
I'd suggest that if you want to create constructor functions, using the new class syntax above is the simpler, more powerful way to do that. If you want to use the older syntax, though, other answers have shown how, but for completeness either make admins a property of the object:
let Obj = function() {
this.admins = []; // ***
this.addAdmin = function(admin){
this.admins.push(admin)
};
};
or perhaps with addAdmin on the prototype:
let Obj = function() {
this.admins = []; // ***
};
Obj.prototype.addAdmin = function(admin){
this.admins.push(admin)
};
or use the fact addAdmins closes over the call to Obj, and thus the local admins:
let Obj = function() {
const admins = [];
this.addAdmin = function(admin){
admins.push(admin) // <=== No `this.` here, you want to close over the
// `admins` local
};
};
I am assumming Object is a placeholder, because it is a reserved keyword.
What is happening is, your variable var admins = []; is created locally and can noot be accesed with the this. as a result when you set the value in this.admins.push(admin) the admins there is undefined. you should modify your function to read this way
var Obj = function () {
this.admins = [];
this.addAdmin = function (admin) {
this.admins.push(admin);
};
};
const object = new Obj();
object.addAdmin(1);
you should not omit the this keyword like this(no pun intended) if you plan to new the function. Stick to the code above.
var Obj = function () {
var admins = [];
this.addAdmin = function (admin) {
admins.push(admin);
};
};
const object = new Obj();
console.log(object)

What is the best way to define dependent variables in an object?

In the Google developers recommendation for optimizing JavaScript code, they mention that the best way to declare/initialize new variables for object is to use the prototype. For instance, instead of:
foo.Bar = function() {
this.prop1_ = 4;
this.prop2_ = true;
this.prop3_ = [];
this.prop4_ = 'blah';
};
Use:
foo.Bar = function() {
this.prop3_ = [];
};
foo.Bar.prototype.prop1_ = 4;
foo.Bar.prototype.prop2_ = true;
foo.Bar.prototype.prop4_ = 'blah';
However, in my case I have a dependency between variables, for instance:
var appv2 = function(){
this.start(this.person, this.car);
};
appv2.prototype.toWhom = 'Mohamed';
appv2.prototype.person = new person(this.toWhom);
appv2.prototype.car = new car();
appv2.prototype.start = function(person, car){
console.log('start for appv2 is called');
person.sayHello('me app v2');
car.brand();
};
new appv2();
Using this.toWhom outside of the main constructor body or a method function of the object will yield undefined. To solve this I could use appv2.prototype.toWhom instead of this.toWhom or I could declare my dependent variables inside of the main constructor body.
But I would like to know what is the best way, in terms of performance, to accomplish this?
Thanks
To reference toWhom while creating person, you can either store the value in a separate variable:
var toWhom = appv2.prototype.toWhom = 'Mohamed';
appv2.prototype.person = new person(toWhom);
Or, reference it from the prototype, as you suspected:
appv2.prototype.person = new person(appv2.prototype.toWhom);
The reason this.toWhom is undefined is because this doesn't refer to an instance of appv2 there.

Can storing large variables in a closure cause problems?

I have a function in which I'm using closure as follows:
function myobject() {
var width=300,
height=400,
bigjsondata = { } // assume this is a big variable ~ 300k
function obj(htmlelement) {
// plot a graph in this htmlelement based on bigjsondata
}
return obj;
}
var plot1 = myobject();
plot1('#holder1');
var plot2 = myobject();
plot1('#holder2');
the variable bigjsondata contains a large dataset. The question is: does it allocate memory for bigjsondata whenever I create a variable var a = myobject() ?
Can it lead to memory problems if a lot of instances are created?
If so what is the best way to load it only once? (bigjsondata does not change)
Edit: At the end I would like myobject to be globally accessible.
not sure what you are trying to achieve, this should provide you with some private storage on different levels:
var privateStorage = function () {
// only 1 copy total
var bigJsonData = {...}
return function() {
// 1 copy for each instance
var instanceData = {...}
return function() {
// something to do many times per instance
return something_useful
}
}
}(); // returns function that privatelly knows about bigJsonData
var a = privateStorage(); // a is now 1st instance of the inner-most function
var b = privateStorage(); // a and b share the SAME bigJsonData object, but use different instanceData objects
a1 = a();
a2 = a();
Generally - yes, you code looks like creating a new instance for the bigjsondata each time you make a new myObject(); To get arround the issue, you can use anonymous initialization function like this:
myObject = null;
(function() {
var bigjsondata = { ... } // construct you large object here;
function myObjectInternal() {
// you can access `bigjsondata` from here.
// do not change `bigjsondata`, since it will now
// use the changed value in all new instances of `myObjectInternal`
}
myObjectInternal.prototype = {
data: function(_) {
// you can access `bigjsondata` from here too
}
};
myObject = myObjectInternal;
})();
This will create an anonymous function that is called immediately and only once (like a singleton). Inside the function, bigjsondata is a a closure to the myObjectInternal function, which is visible only in the anonymous one. That is why you define the outer global variable myObject, to latter make it point to the myObjectInternal function/object.
Define myObjectInternal as you would have myObject and you're good to go. So, in the following code:
var instance1 = new myObject();
var instance2 = new myObject();
it will use the same bigjsondata for instance1 and instance2
I would suggest going for an object oriented approach for this.
function obj (htmlelement)
{
this.htmlelement = $(htmlelement);
}
obj.prototype.htmlelement = null;
obj.prototype.bigjsondata = {};
obj.prototype.width = 300;
obj.prototype.height=400;
obj.prototype.plot = function ()
{
var htmlelement = this.htmlelement;
var bigjsondata = this.bigjsondata;
var width = this.width;
var height = this.height;
//plot graph here;
}
var plot1 = new obj('#holder1');
var plot2 = new obj('#holder2');
plot1.plot();
plot2.plot();
Here, the same bigjsondata will be shared among all objects of obj.

instantiating more than one object

in Javascript, I am creating an object like this:
var testObject = {
value: "this is my initial value",
setup: function() {
value: "foo"
}
};
Now, I would like to be able to instantiate this object, so I am trying this:
var myFirstObject = new testObject();
var mySecondObject = new testObject();
so that when I call .setup() it will change the value only for that referenced newly created object. How can I achieve this? This code does not seem to work.
You don't instantiate objects, you instantiate functions.
var testObject = function() {
this.value = "this is my initial value";
this.setup = function() {
this.value = "foo";
}
}
var myFirstObject = new testObject();
var mySecondObject = new testObject();
EDIT:
As per your comment, here's how you would bind to the DOM using functions on this object:
document.getElementById('idOfElem').addEventListener(
'click', myFirstObject.clickHandler);
Bear in mind that you won't have any guarantee that the click handler will be executed in the context of your object (i.e. in your click handler, this might not be your testObject instance). If your clickHandler intends to modify the object's instance variable in any way, it's better to ensure the context like so:
document.getElementById('el').addEventListener('click',
function() {
myObj.handleClick.apply(myObj, arguments);
});
You have numerous problems with your code. Firstly, you are trying to instantiate something, by calling a constructor function. Your testObject is not a function, so you'll cause a type error. You need to change testObject to be something along these lines:
var TestObject = function () {
this.value = "this is my initial value";
};
TestObject.prototype.setup = function () {
this.value = "foo";
};
Notice that I've used an uppercase T in that identifier... that's just best practice for a constructor function. Also notice how I've defined the setup method on the prototype. This is much more efficient than defining it as a property of the instance (with this.setup) since only one copy of the function needs to exist in memory.
Now that TestObject is a function it can be instantiated by calling it with the new operator:
var myFirstObject = new TestObject();
var mySecondObject = new TestObject();
When you call the setup method on an instance of TestObject, it will apply to that instance. In other words, the value of this inside the setup method will refer to the instance on which the method has been called:
myFirstObject.setup();
console.log(myFirstObject.value); // 'foo'
console.log(mySecondObject.value); // 'this is my initial value'
You have incorrectly defined your constructor. Try this:
function testObject() {
this.value = "this is my initial value";
this.setup = function() {
this.value = "foo"
}
};
You can then call new testObject().
The object notation your using is something you can compare with a static class.
Here is the code for what you're trying to achieve:
var testObject = function(val) {
this.value = "This is my initial value",
if (arguments[0]) {
this.value = val;
}
};
var first = new testObject(); //uses initial value
var second = new testObject("hi"); //value = hi
If you'd like to write classes using this notation take a look at this: http://ejohn.org/blog/simple-javascript-inheritance/
function yourObject(value, setup) {
return {
value: value,
setup: setup
};
}
var myFirstObject = new yourObject('a', function(){});
var mySecond = new yourObject('b', function(){});

How do i make a copy of an object? Javascript

I have a class in json format. I would like to make two instance. Right now (its pretty obvious why) when i 'make' two objects i really have 2 vars pointing to one. (b.blah = 'z' will make a.blah=='z')
How do i make a copy of an object?
var template = {
blah: 0,
init: function (storageObj) {
blah = storageObj;
return this; //problem here
},
func2: function (tagElement) {
},
}
a = template.init($('form [name=data]').eq(0));
b = template.init($('form [name=data2]').eq(0));
If you want multiple instances, sounds like a constructor might do you some good.
function Template(element) {
this.blah = element;
}
Template.prototype.func2 = function(tagElement) {
//...
};
var a = new Template($('form [name=data]').eq(0));
var b = new Template($('form [name=data2]').eq(0));
b.func2('form');
All methods on the function prototype (Template.prototype) will be accessible from each instance, and with each instance scoped accordingly. The new keyword will run the function and then return to you a brand new object, inheritting from the prototype.
You'll no longer have the exact same object point to a and b.
From the comments What is the most efficient way to deep clone an object in JavaScript?
var b = {}, key;
for (key in a){
if(a.hasOwnProperty(key)){
b[key] = a[key];
}
}

Categories

Resources