JavaScript: Cannot set property of undefined - javascript

I'm pretty new to JS and I'm trying to wrap my head around the object topic in JS.
What I'm trying to do is to set a property of an object prototype to an uninitialized array, so that I can later add multiple objects to that array (for instances of the prototype object)
My code looks like this so far:
function cocktail(){
this.prototype.ingredients = [];
this.printIngredients = function() {
var i;
for (i = 0; i<this.ingredients.length; ++i) {
console.log(this.ingredients.fluid);
console.log(this.ingredients.amount);
}
}
}
var Mojito = new cocktail();
Mojito.ingredients.push({"fluid":"White Rum", "amount":0.05});
Mojito.printIngredients();
That throws:
TypeError: Cannot set property 'ingredients' of undefined
If I change my code into :
this.ingredients = [];
it works but the printIngredients() method prints undefined twice. When I do:
var array = [];
array.push({"a":1, "b":2});
console.log(array[0].a, array[0].b)
everything works as I would expect it to. Can someone clarify what I'm doing wrong and where my thoughts got mixed up?

Change your code to
function cocktail(){
this.ingredients = []; //this doesn't have prototype property
this.printIngredients = function() {
var i;
for (i = 0; i<this.ingredients.length; ++i) {
console.log(this.ingredients[i].fluid);//use the counter variable to get the fluid value at current counter value
console.log(this.ingredients[i].amount);//use the counter variable to get the amount value at current counter value
}
}
}
var Mojito = new cocktail();
console.log(Mojito.ingredients)
Mojito.ingredients.push({"fluid":"White Rum", "amount":0.05});
Mojito.printIngredients();

Alternatively, if you are familiar with class-based languages, you could use modern JavaScript to avoid some of the confusion.
class Cocktail {
constructor() {
this.ingredients = []
}
printIngredients() {
// let is like var, but scoped to blocks instead of functions
// for...of iterates on values instead of keys/indices
for (let ingredient of this.ingredients) {
console.log(ingredient.fluid)
}
}
}
This kind of JavaScript is available from:
Chrome 49
Edge 13
Firefox 44
Node.js 6
Documentation:
Classes
let
for...of

well, first you want to remove printIngredients method out of the constructor function, it will improve performance when it comes to a larger project since you don't have to create different copies every time you instantiate the constructor function, secondly, it is a convention to capitalize the first letter of your constructor. Last but not least, use let and const as they limit the scope to block rather than var that's function scope.
function Cocktail(){
this.ingredients = [];
}
Cocktail.prototype.printIngredients = function() {
// for in ... iterates on keys rather than values
for (let i in this.ingredients) {
console.log(this.ingredients[i].fluid);//use the counter variable to get the fluid value at current counter value
console.log(this.ingredients[i].amount);//use the counter variable to get the amount value at current counter value
}
}
const Mojito = new Cocktail();
console.log(Mojito.ingredients)
Mojito.ingredients.push({"fluid":"White Rum", "amount":0.05});
Mojito.printIngredients();

Related

create a new variable automaticaly

This is a general question. But an answer in JavaScript would suit me the best.
I'm searching for a way to create variables after a pattern automatically.
For example, I want to create a while-loop in which a variable gets declared. In set loop, I want to create the variable car1. However, in the next loop pass I want to do the same thing BUT call the Variable car2 this time.
I'll try to write it in pseudocode:
//this should happen in the first loop
while(true){
var car1 = 1 + 2;
console.log(car1)
}
//this should happen in the second loop
while(true){
var car2 = 1 + 2;
console.log(car)
}
//In both cases "3" should be the output. But different Variables
Contrary to this example. I want to do all this in a single while loop. And on every while loop, a new variable should be created. So car1,car2,car3,car4.
Thanks in advance!
Maybe you can use an array and add an item every loop iteration or a hash map with a naming convention you set for the keys
You can try to use globalThis or window.
function nameFunction(name, f) {
return {
[name](...args) {
return f(...args)
}
}[name]
}
// use globalThis
for (let i = 1; i <= 3; i++) {
const funcName = `car${i}`;
const func =
nameFunction(funcName, () => console.log(`this is car ${i}`));
globalThis[funcName] = func;
}
car1(); car2(); car3();
// use window
for (let i = 4; i <= 6; i++) {
const funcName = `car${i}`;
const func =
nameFunction(funcName, () => console.log(`this is car ${i}`));
window[funcName] = func;
}
car4(); car5(); car6();
I hope this link will help you with this problem.
JS Dynamic Variable
Here they have discussed 2 ways of solving the problem. (One with "eval" and the other with "windows" object.

How to access variables and methods of objects within an array [resolved]

So I have a class that looks something like this:
class Car {
constructor(name, pos) {
this.carName = name;
this.carPos = pos;
}
}
I then end up creating an array of objects using this class as so:
newCar = new Car("Fusion","100 100");
let carManifest = [newCar]
I later in my code go on to carManifest.push() several other Car objects into the array, building it up. Eventually I run into a for loop such that:
for (index = 0; index < carManifest.length; index++) {
if(carManifest[index].carName === 'Honda')
<Do Stuff>
}
Whenever I go to access carManifest[index].carName I get "undefined". I also had a get method within the object for the same variable, but using that in this context wouldn't even compile. I am aware that the array itself does not have these variables, but as far as I'm aware there is no way to declare an array to be of a specific class in JS like you can in Java and C.
Could anyone help me out? I am new to JS and have been stumped on this for the past hour. I will be glad to update this if there is not enough info. Thanks!
Note: Not sure if it matters but I am using Node.js
Change your code from
let carManifest = [newCar]
to push item to array as
carManifest.push(newCar);
/*
function Car (name, pos) {
this.carName = name;
this.carPos = pos;
}
*/
class Car {
constructor(name, pos) {
this.carName = name;
this.carPos = pos;
}
}
newCar = new Car("Fusion","100 100");
let carManifest = [];
carManifest.push(newCar);
for (index = 0; index < carManifest.length; index++) {
if(carManifest[index].carName === 'Fusion'){
console.log(carManifest[index].carName);
}
}
Are you pushing other variables or objects into the same array? There is nothing wrong with the code examples you're showing, you are able to access the variables of the objects this way. You'll have to show more code, where exactly are you getting undefined?

Repeating Value and Replacing Value with JS

Newbie in JS here. I'm having trouble replacing and repeating the value from the function. Here's the code:
function Phone(ring) {
this.ring = ring;
}
function updateRing(newRing) {
this.newRing = ring;
}
var samsung = new Phone('Chim');
samsung.ring(2); // Needs to compute to "Chim, Chim"
var htc = new Phone('Dada');
htc.ring(3); // Needs to compute to "Dada, Dada, Dada"
htc.updateRing('Riri');
htc.ring(1); // Needs to compute to "Riri"
For the repeat value for the first function, I tried using this.repeat but it didn't work inside the Phone function.
For the updateRing function, I couldn't get the code to replace the this.ring.
I stripped down all the useless code I had written. Thanks in advance for any help.
You can repeat strings with string.repeat()
let a = "ring"
console.log(a.repeat(2))
But to get the comma separator to work cleanly you can make a disposable array and join() is with a comma.
let ringString = Array(3).fill("ring").join(", ")
console.log(ringString)
For the others, you probably want to use classes, which are pretty easy, but don't run on IE without a ployfill. Or prototypes, which can be a little confusing at first. Here's an example using prototypes to define methods on your Phone object:
function Phone(ring) {
// changed to ring_tone too prevent clash with this.ring method
this.ring_tone = ring;
}
// you need to define these on the prototype to you can use `this`
Phone.prototype.updateRing = function(newRing) {
// don't need to define a this.newRing, just update the ring
this.ring_tone = newRing;
}
Phone.prototype.ring = function(n) {
return new Array(n).fill(this.ring_tone).join(', ')
}
var samsung = new Phone('Chim');
console.log(samsung.ring(2)); // Needs to compute to "Chim, Chim"
var htc = new Phone('Dada');
console.log(htc.ring(3)); // Needs to compute to "Dada, Dada, Dada"
htc.updateRing('Riri');
console.log(htc.ring(1)); // Needs to compute to "Riri"
1) You're calling samsung.ring as a function, even though it's just an instance variable of Phone.
2) The reason why this.repeat didn't work is because repeat isn't a method of "this," which refers to Phone.
Try this instead:
var samsung = new Phone('Chim');
samsung.ring.repeat(2);
var htc = new Phone('Dada');
htc.ring.repeat(3);
Maybe try this:
class Phone {
constructor(sound) {
this.sound = sound;
}
ring(number) {
var i;
for (i = 0; i < number; i++) {
console.log(this.sound + ", ");
}
}
updateRing(newSound) {
this.sound = newSound;
}
}
var samsung = new Phone('Chim');
samsung.ring(2);
samsung.updateRing('Riri');
samsung.ring(1);
Codepen - https://codepen.io/anon/pen/MGRJOB?editors=0010

Javascript Object scope

This is a simplification of something that I've come up against in a larger project so I hope it makes sense.
I have two Objects:
FirstObj = {
data : [],
init : function()
{
for(var a = 0; a < 20; a++)
{
this.data[a] = 1;
}
}
};
SecondObj = {
init : function()
{
var cell = FirstObj.data[0];
cell = 0;
}
};
I then call the two init methods and log the results:
(function(){
FirstObj.init();
SecondObj.init();
console.log(FirstObj.data);
})()
Now, I was assuming - based on my basis in Actionscript - that the log would show an Array in which the first item is a 0 and the rest all 1 but the 0 does not seem to stick.
Why does the assignment of the 0 value not succeed here and yet works fine if, instead of cell = 0 I target directly at FirstObj.data[0] = 0.
I'm guessing this is a scope thing and I can work round it but I'm trying to get a proper understanding of how JS actually handles this stuff, especially when lumping code into Objects like this (as an aside, is this a good approach in peoples general opinion?).
Thank for any help.
Numbers in JavaScript are something called primitive value types (alongside strings, booleans null and undefined).
This means, that when you do
var cell = FirstObj.data[0];
You're passing the value in FirstObj.data[0] and not a refernece to it.
What you're doing is like:
var x = 5;
var y = x; // y is now 5
y = 4; // y is 4, x is still 5.
Of course, something like FirstObj.data[0] = 0 should work.
Array indexing returns values in Javascript, not references. It means that once data[0] is assigned to cell, further modification of cell will not affect data[0].
Assigning the array itself would result in the behavior you're looking for:
SecondObj = {
init : function()
{
var cells = FirstObj.data;
cells[0] = 0; // Will also affect data[0].
}
};

JavaScript DOM references not holding up

For some reason ss.transition() does not affect the appropriate DOM elements after ss.goTo() is triggered by an onclick. The ss.transition() call under //Init does work as expected. I assume this is a scope problem. Little help?
var ss = {};
ss.goTo = function(i) {
ss.old = ss.current;
ss.current = ss.slides[i];
ss.transition();
}
ss.transition = function() {
ss.old.style.display = "none";
ss.current.style.display = "block";
}
// Hooks
ss.div = document.getElementById("slides");
ss.as = ss.div.getElementsByTagName("a");
// References
ss.slides = [];
for (i in ss.as) {
if (ss.as[i].rel == "slide") {
ss.slides.push(ss.as[i]);
}
}
ss.first = ss.slides[0];
ss.last = ss.slides[ss.slides.length-1];
// Init
ss.current = ss.first;
ss.old = ss.last;
ss.transition();
for (i in ss.as) {
You shouldn't use the for...in loop over an Array or, in this case, NodeList. You'll get member properties you don't want, like item and length. You also can't rely on the items being returned in any particular order; it is very likely that at least ss.last will not be what you expect. If it's a non-item property, ss.old.style.display will definitely fail with an exception, breaking the script.
The correct loop for a sequence is the old-school C construct:
for (var i= 0; i<ss.as.length; i++)
Also, where are you binding the calls to goTo? If you are doing it in a loop with a function inside you, you may well also have the classic loop closure problem. See eg. this question.
The reason for the failure is because you lose the reference to the currently hidden element before you make it show up again. You need to assign old to display:block, then do the switch of old = current, current = variable, then hide old.

Categories