There's a handful of questions extremely similar to this, I know, but I can't seem to get anything to work.
For sake of brevity, I'll summarize this. I have this constructor function with strings assigned as values of the keys (assignment).
var Quote = function() {
this.quote1 = 'You can discover more about a person in an hour of play than in a year of conversation.';
this.quote2 = 'Nothing is at last sacred but the integrity of your own mind.';
this.quote3 = 'We have to fight them daily, like fleas, those many small worries about the morrow, for they sap our energies.';
this.quote4 = 'Ethics are so annoying. I avoid them on principle.';
this.quote5 = "Never trust anything that can think for itself if you can't see where it keeps its brain.";
};
module.exports = Quote;
Using AJAX I am printing the value of the keys inside the constructor to a static page. However... I am only successful in printing "quote1", "quote2", etc... (Just the name of the keys).
This is what my function for accessing the constructor looks like. My question is: How can I access the string values assigned to the keys inside the constructor? Is that possible?
Thank you in advance.
module.exports = function(object) {
var propArray = Object.keys(object);
var randomProp = propArray[Math.floor(Math.random() * propArray.length)];
return {quote: randomProp};
};
Inside your function, you should be able to access the quote by key using this:
object[randomProp]
In JavaScript, object properties can be accessed using square bracket notation. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects.
As you might have expected, there are several things wrong with your code :)
I've rewritten it with pretty thorough comments.
<script>
// create your 'Quote' class, which can be instantiated as many times as necessary
var Quote = function() {
this.quote1 = 'You can discover more about a person in an hour of play than in a year of conversation.';
this.quote2 = 'Nothing is at last sacred but the integrity of your own mind.';
this.quote3 = 'We have to fight them daily, like fleas, those many small worries about the morrow, for they sap our energies.';
this.quote4 = 'Ethics are so annoying. I avoid them on principle.';
this.quote5 = "Never trust anything that can think for itself if you can't see where it keeps its brain.";
};
// now let's set up the 'exports' method on the function's prototype
// this will allow any new instance of the Quote function to call or override this method
// without affecting the master 'Quote' class
Quote.prototype.exports = function() {
// you don't need to explicitly pass an object to this function
// just have it reference itself using the 'this' keyword
var propArray = Object.keys(this);
var randomProp = propArray[Math.floor(Math.random() * propArray.length)];
// objects in JavaScript can be handled like associative arrays
// simply access the key of 'this' with the value provided by 'randomProp'
return {quote: this[randomProp]};
};
// create a new instance of 'Quote'
// this guy can be used and manipulated independent of the master 'Quote' class
var module = new Quote();
// let's store the value returned by 'exports' in randomQuote
// note the () at the end of module.exports -- this tells JavaScript to treat the object as a function
var randomQuote = module.exports();
// write your quote to the document -- or do whatever else you want to with it at this point
document.write(randomQuote.quote);
</script>
Instead of return { quote: randomProp } you should use return object[randomProp].
You can access object properties via dot notation or square bracket notation.
Here's an example from jsbin
https://jsbin.com/yopeli/edit?js,console
Related
I am having difficulty in understanding the following code, i have put a comment where i do not understand the concept, what exactly is going on
var ob = {};
var ob2 = ['name'];
for(var op of ob2)
{
ob[op]='at'; // here i dont understand what is happening, why here is an array type brackets
}
console.log(ob);
OUTPUT IS
name:'at'
That is just the syntax for accessing or assigning properties of an object dynamically in javascript.
You can think of it as though you are doing: ob.name = 'at'.
There are two ways to access object properties in JavaScript
var person = {
name: 'Jane'
}
person.name
// or
person['name']
// both return jane
in your case, that iterates through members of the array called ob2
first and only element of that array is a string name and it's given to that object as a prop, which becomes like following
ob['name'] = 'at';
// or
ob.name = 'at';
When to use brackets([]) over dot(.)
If you don't know the prop name at runtime you need to go with brackets, if you do know it you can choose either dot notation or brackets
Basically, it's accessing a property of the object ob. In this case, is accessing and creating new properties.
The loop is getting each index value, and for each assign/create a new property using that index value.
That approach is a dynamically way of creating property-names in an object.
ob['name'] = 'at';
ob.name = 'at'; // Just to illustrate
Read a little the docs here -> JavaScript object basics - Learn web development | MDN
Is there any difference in how these functions operate? The first one is more typically of what I think about when thinking of a constructor.
Example 1: using this to name and set properties. Then using new to create a new Book object.
function Book(name, numPages) {
this.name = name;
this.numPages = numPages;
}
var myBook = new Book('A Good Book', '500 pages');
Example 2: returning a object by using new and just calling the function itself.
function Movie(name, numMinutes) {
return { name:name, numMinutes:numMinutes };
}
var best = new Movie('Forrest Gump', '150');
var other = Movie('Gladiator', '180');
I guess what I'm trying to figure out is if these are different in the way they create an object? If so is one better than the other? Are there different situations where one would work better over the other?
The first one is a constructor, and can therefore be extended by a prototype, and you can test via instanceof wether the result is an Instance of this type.
Downside: if you forget the new-keyword your code will blow up (unless you write a workaround for that into each constuctor)
And you can't really use apply() with a constructor to pass an array of arguments, when you instantiate a new Object; on the other hand, don't do that, even if you can/could.
The second one is a factory, not a constructor. Independant wether you use the new-keyword or not.
with this implementation it creates Objects that look the same but don't share a type or prototype (although the underlying JS-engine recognizes them as similar and so they share the same hidden Class as long as they have the same properties, added in the same order, ... different topic)
long story short, neither performance nor memory-footprint suffer from this approach (anymore)
But you can't check wether they are of the same type, and you don't have a shared prototype that may affect all instances (maybe a pro or a con.)
My goto-approach If I need inheritance, is kind of a mix of both:
(if I just need a data-object I usually use a factory and plain objects).
function Book(conf) {
var book = Object.create(Book.prototype);
//a book like this usually has multiple configs/properties
if(typeof conf === "object"){
for(var k in conf) book[k] = conf[k];
}else if(conf){
//assuming that I get at least the name passed
book.name = String(conf);
}
return book;
}
//I have a prototype that can be extended
//with default-values for example; no idea for a good method
//to add to the prototype in this example ;)
Book.prototype.numPages = 0;
//but I can also use it like a plain function; no error if you
var myBook1 = Book("Peter Pan");
var myBook2 = Book({
name: "American Gods",
author: "Neil Gaiman"
});
If I add the following line to the top of the function I can also use that as a method to cast anything into an Instance of Book without cloning already existing instances
function Book(conf) {
//with this simple line I can also use this as a function to cast anything into a "Book"
if(conf instanceof Book) return conf;
var book = Object.create(Book.prototype);
//...
return book;
}
var data = [
"Peter Pan",
{name: "American Gods"},
//...
];
var books = data.map(Book);
In my opinion, I have the benefits of both worlds with this approach.
Basically, when you use new, the JS engine makes a brand new object for you and injects that as the value of this. It also automatically gives you any methods attach to the prototype of the constructor. Using a constructor also allows you to check if an object is an instanceof something more easily.
function MovieA(title) {
this.title = title;
}
MovieA.prototype.getTitle = function() {
return this.title;
};
function MovieB(title) {
return {
title: title
};
}
MovieB.prototype.getTitle = function() {
return this.title;
};
var a = new MovieA('A');
console.log(a instanceof MovieA); // true
console.log(a.getTitle()); // 'A'
var b = MovieB('B');
console.log(b instanceof MovieB); // false
console.log(b.getTitle()); // Uncaught TypeError: b.getTitle is not a function
Everything that new offers you can be attained through other methods but requires more manual labor.
The second method, factories, tend to work better for unit testing, custom object creation and functional programming. It works better for unit testing because if you have a factory producing all of your objects, you can just replace that factory with a mock-up to test different cases.
var Factory = {
makeThing: function() {
return { name: 'thing' };
}
};
// Want to test the case for if makeThing fails inside of some other code
var MockFactory = {
makeThing: function() {
return null;
};
};
As for when you use either, it all depends. Some people don't use new at all. Others exclusively use new. It all depends on if you need any of the things listed above, how much control you need over the creation of objects, when you want to use this or not, etc. In the end, it's all a matter of preference.
The difference is the constructor used to create the object returned.
new Book('A Good Book', '500 pages');
creates a Book object instance, with the instance inheriting properties from Book.prototype, including a constructor property value of Book. The Book.prototype object itself inherits from Object.prototype.
var other = Movie('Gladiator', '180');
uses Movie as a factory function (new not required) and returns an Object object instance, with the instance inheriting properties directly fromObject.prototype, including a constructor property value of Object.
More briefly stated, Object literal syntax creates an Object object.
I'm trying to learn to program. I've gone through a list of tutorials sites online and I'm stuck on a thing that I think is extremely important for me to understand.
My questions:
(This is what I'd like to understand most) In my for...in loop, why is creating new "obj" objects using the "Contacts" constructor working? It seems like I would need a different name each time I loop so that I don't overwrite the object that I've created the pass before. If this is correct, how do I do this if I don't know anything about the number or value of contacts ahead of time? Additionally, why does the title of the any of the objects in the console logs not say obj? Am I confused about what it means to create an instance of an object? Are the names of these instances unimportant?
Why are all of the properties undefined? Should referencing properties from the temporary "i" variable work?
Creating objects from an unknown total of data entries seems really important. Unfortunately, places like Codecademy fall short here. You always manually create new instances of objects with hardcoded names they give you. But what would happen if there were two of the same name?
Thanks so much for any help I may get on this. Don't hold back from telling me anything else silly that I may be doing.
Here is a link to a console screenshot - http://i.imgur.com/TK4dtfV.png
var TestApp = {};
// my data... taken from wherever
TestApp.jsonContacts = {
contact1: {
name: "Ethan",
age: 24
},
contact2: {
name: "Evan",
age: 30
},
contact3: {
name: "Paul",
age: 9000
}
};
// I know this is silly, just let me pretend...
TestApp.jsonStrung = JSON.stringify(TestApp.jsonContacts);
TestApp.globalContactList = [];
// my constructor function to create instances of Contact
TestApp.Contact = function(name, age){
this.name = name;
this.age = age;
TestApp.globalContactList.push(this);
};
// where I'm taking data and creating new Contact objects
TestApp.instantiateObjects = function(){
// I know this is silly, just let me pretend...
var jsonUnstrung = JSON.parse(TestApp.jsonStrung);
// I think I'm looping through the first set of objects sitting in jsonContacts
for (var i in jsonUnstrung) {
var obj = new TestApp.Contact(i.name, i.age);
console.log(obj);
}
console.log(TestApp.globalContactList);
};
TestApp.instantiateObjects();
In my for...in loop, why is creating new "obj" objects using the "Contacts" constructor working?
A variable is just a holding place for a value. It is not the value itself. If you overwrite a variable with a new value, the previous value it held will continue to exist as long as something is holding a reference to it; it's simply that the variable won't be referring to it anymore.
It seems like I would need a different name each time I loop so that I don't overwrite the object that I've created the pass before.
No, you don't. One variable is fine, as long as you do something with the value before you assign the variable to the next value.
If this is correct, how do I do this if I don't know anything about the number or value of contacts ahead of time?
It doesn't matter how many you have.
Additionally, why does the title of the any of the objects in the console logs not say obj?
console.log() prints out the value that is passed to it. It doesn't care (doesn't know) anything about the variable that you pass to it.
Am I confused about what it means to create an instance of an object?
It seems so. Creating an instance of an object allocates some memory to store that object's values. A variable allows you to gain access to that allocated object.
Are the names of these instances unimportant?
Yes, object instances don't have names.
Why are all of the properties undefined?
i holds the property names of the object you are iterating through, so in this case, the strings "contact1", "contact2", "contact3". These strings don't have a name or age property, so your constructor is receiving two undefined values.
Should referencing properties from the temporary "i" variable work?
You need to use i as a property name to access the property values:
var obj = new TestApp.Contact(jsonUnstrung[i].name, jsonUnstrung[i].age);
In general, it's a good idea not to have side-effects like TestApp.globalContactList.push(this); in a constructor. There may be cases where doing so makes sense, but most of the time, removing that line and doing this would be preferable:
for (var i in jsonUnstrung) {
var contact = jsonUnstrung[i];
var obj = new TestApp.Contact(contact.name, contact.age);
console.log(obj);
TestApp.globalContactList.push(obj);
}
function Dealership = function(){
this.car1="Honda";
this.car2="Chevy";
this.car3="Toyota";
this.carList=[this.car1,this.car2,this.car3];
};
var tomsauto = new Dealership();
tomsauto.car2="Subaru";
console.log(tomsauto.carList); //returns honda chevy toyota
I'm confused as to how the array is processed. Is it static, holding only the variable values it had at instantiation, or should "this.car1" change when I change tom.car1?
When you create the array via that array instantiation expression, the runtime system copies the values of each of those object properties into the array. If you later change the values of the properties, they'll change independently of the array elements.
There's no way in JavaScript to make a property of one object "mirror" the property of another. (Well, no intrinsic way; you can write code to do it.)
As Teemu said: your carList is populated with values, not references.
An easy workaround is to change it to a function getCarList:
var Dealership = function () {
this.car1 = "Honda";
this.car2 = "Chevy";
this.car3 = "Toyota";
this.getCarList = function() {
return [this.car1, this.car2, this.car3];
}
};
var tomsauto = new Dealership();
tomsauto.car2 = "Subaru";
console.log(tomsauto.getCarList());
I think you are running into that problem because of how closures work in JavaScript.
When you declare the function as you did, the value of car1, car2, car3 get evaluated in that scope and then are assigned to the array.
When you do the new Dealership() bit, the values of that array are pretty much set as they were when the function was evaluated. After you do
tomsauto.car2 = "Subaru";
the value of car2 will change, but the array will not because the values are not being re-evaluated.
If you want to know more about how and why, I suggest you read more about closures in javascript and functional languages in general.
I'm making a class that will be recreated many times, and in order to save memory I need to thoroughly delete it. Basically I need to access its containing variable if possible.
Here's the example:
function example(){
this.id=0;
this.action=function(){alert('tost');}
this.close=function(){ delete this;}
}
var foo=new example();
My question is:
How can I get access to the foo variable from within the example function so I can remove it?
window.foo will access that global variable.
this.close=function(){ delete window.foo; }
However, I remember there is something fishy with global variables, delete and window, so you might want to do otherwise, and simply use window.foo = null; for example.
If you want to access a variable defined in another function, you'll want to read the answers to this SO question.
Since what you want is to allow the garbage collector to release that object, you need to ensure that there are no references left to the object. This can be quite tricky (i.e. impossible) because the code manipulating the object can make multiple references to it, through global and local variables, and attributes.
You could prevent direct reference to the object by creating a proxy to access it, unfortunately javascript doesn't support dynamic getters and setters (also called catch-alls) very well (on some browseres you might achieve it though, see this SO question), so you can't easily redirect all field and method (which are just fields anyway) accesses to the underlying object, especially if the underlying object has many fields added to it and removed from it dynamically (i.e. this.anewfield = anewvalue).
Here is a smiple proxy (code on jsfiddle.net):
function heavyobject(destroyself, param1, param2) {
this.id=0;
this.action=function(){alert('tost ' + param1 + "," + param2);};
this.close=function(){ destroyself(); }
}
function proxy(param1, param2) {
object = null;
// overwrites object, the only reference to
// the heavyobject, with a null value.
destroyer = function() { object = null; };
object = new heavyobject(destroyer, param1, param2);
return function(fieldname, setvalue) {
if (object != null) {
if (arguments.length == 1)
return object[fieldname];
else
object[fieldname] = setvalue;
}
};
}
var foo = proxy('a', 'b');
alert(foo("action")); // get field action
foo("afield", "avalue"); // set field afield to value avalue.
foo("action")(); // call field action
foo("close")(); // call field close
alert(foo("action")); // get field action (should be 'undefined').
It works by returning a function that when called with a single argument, gets a field on the wrapped object, and when called with two arguments sets a field. It works by making sure that the only reference to the heavyobject is the object local variable in the proxy function.
The code in heavyobject must never leak this (never return it, never return a function holding a reference to var that = this, never store it into a field of another variable), otherwise some external references may be created that would point to the heavyobject, preventing its deletion.
If heavyobject's constructor calls destroyself() from within the constructor (or from a function called by the constructor), it won't have any effect.
Another simpler proxy, that will give you an empty object on which you can add fields, read fields, and call methods. I'm pretty sure that with this one, no external reference can escape.
Code (also on jsfiddle.net):
function uniquelyReferencedObject() {
object = {};
f = function(field, value) {
if (object != null) {
if (arguments.length == 0)
object = null;
else if (arguments.length == 1)
return object[field];
else
object[field] = value;
}
};
f.destroy = function() { f(); }
f.getField = function(field) { return f(field); }
f.setField = function(field, value) { f(field, value); }
return f;
}
// Using function calls
o = uniquelyReferencedObject();
o("afield", "avalue");
alert(o("afield")); // "avalue"
o(); // destroy
alert(o("afield")); // undefined
// Using destroy, getField, setField
other = uniquelyReferencedObject();
other.setField("afield", "avalue");
alert(other.getField("afield")); // "avalue"
other.destroy();
alert(other.getField("afield")); // undefined
The truth is that you can not delete objects in Javascript.
Then you use delete operator, it accepts the property of some object only.
So, when you use delete, in general you must pass to it something like obj.p. Then you pass just a variable name actually this means 'property of global object', and delete p is the same as delete window.p. Not sure what happens internally on delete this but as a result browser just skip it.
Now, what we actually deleting with delete? We deleting a reference to object. It means object itself is still somethere in memory. To eliminate it, you must delete all references to concrete object. Everythere - from other objects, from closures, from event handlers, linked data, all of them. But object itself doest have information about all this references to it, so there is no way to delete object from object itself.
Look at this code:
var obj = <our object>;
var someAnother = {
...
myObjRef: obj
...
}
var someAnotherAnother = {
...
secondRef : obj
...
}
To eliminate obj from memory you must delete someAnother.myObjRef and someAnoterAnother.secondRef. You can do it only from the part of programm which knows about all of them.
And how we delete something at all if we can have any number of references everythere? There are some ways to solve this problem:
Make only one point in program from there this object will be referenced. In fact - there will be only one reference in our program. and Then we delete it - object will be killed by garbage collector. This is the 'proxy' way described above. This has its disadvantages (no support from language itself yet, and necessarity to change cool and nice obj.x=1 to obj.val('x',1). Also, and this is less obvious, in fact you change all references to obj to references to proxy. And proxy will always remain in memory instead of object. Depending on object size, number of objects and implementation this can give you some profit or not. Or even make things worse. For example if size of your object is near size of proxy itself - you will get no worth.
add to every place there you use an object a code which will delete reference to this object. It is more clear and simple to use, because if you call a obj.close() at some place - you already knows everything what you need to delete it. Just instead of obj.close() kill the refernce to it. In general - change this reference to something another:
var x = new obj; //now our object is created and referenced
x = null;// now our object **obj** still im memory
//but doest have a references to it
//and after some milliseconds obj is killed by GC...
//also you can do delete for properties
delete x.y; //where x an object and x.y = obj
but with this approach you must remember that references can be in very hard to understand places. For example:
function func() {
var x= new obj;// our heavy object
...
return function result() {
...some cool stuff..
}
}
the reference is stored in closure for result function and obj will remain in memory while you have a reference to result somethere.
It hard to imagine object that is heavy itself, most realistic scenario - what you have some data inside it. In this case you can add a cleanup function to object which will cleans this data. Let say you have an gigant buffer (array of numbers for example) as a property of the object, and if you want to free memory - you can just clear this buffer still having object in memory as a couple dozens of bytes. And remember to put your functions to prototype to keep instances small.
Here is a link to some very detailed information on the JavaScript delete operator.
http://perfectionkills.com/understanding-delete/