How can i access Closures preserved variables? - javascript

I'm trying to get the Closures preserved variables. I'm not sure if that even possible.
Here is my code:
function MyBooks (author, title){
this.author = author;
this.title = title;
return function addPrice(amount){
return amount;
}
}
var MyBooksObj=MyBooks('Tolkin','Hobbit');
alert(MyBooksObj('100 dollars')); //outpot: 100 dollars
alert("author: " + MyBooksObj.author); //outpot: author: undefined
alert("title: " + MyBooksObj.title); //outpot: title: undefined
Anybody knows how can i access 'author' and 'title' from outside the function using the variable 'MyBooksObj'?
Thanks!

Using the new operator on a function creates a new object with "this" bound to the result.
function MyBooks (author, title){
this.author = author;
this.title = title;
this.addPrice= function (amount){
return amount;
}
}
var MyBooksObj= new MyBooks('Tolkin','Hobbit');
alert(MyBooksObj.addPrice('100 dollars')); //output: 100 dollars
alert("author: " + MyBooksObj.author); //output: auther: Tolkin
alert("title: " + MyBooksObj.title); //output: title: Hobbit

Some error and wrong assumption were made in your code. This is what I feel is going on
No new syntax was used to define your new object.
When you return a function from an object the object loose hold of it internal structure and only return the function.
i.e MyBooks { author: 'Tolkin', title: 'Hobbit' }
becomes
[function: addPrice]
amount variable is not a closure, instead use this.amount which store it values in object memory.
so to correct your code. I will suggest this:`
function MyBooks(author, title){
this.author = author;
this.title = title;
this.amount = 0;
this.addPrice = function(amount){
return this.amount += amount;
}
}
var MyBooksObj= new MyBooks('Tolkin','Hobbit');
alert(MyBooksObj); //ouput object
alert(MyBooksObj.addPrice(100)); //output: 100 dollars
alert(MyBooksObj.addPrice(100)) //output: 200 dollars
alert("author: " + MyBooksObj.author); //output: auther: Tolkin
alert("title: " + MyBooksObj.title); //output: title: Hobbit`

You can also use Class and constructor :
class MyBooks {
constructor(author, title) {
this.author = author;
this.title = title;
}
setAmount(value) {
this.amount = value;
}
getAmount() {
return this.amount;
}
}
var MyBooksObj= new MyBooks('Tolkin','Hobbit');
MyBooksObj.setAmount('100 dollars');
alert(MyBooksObj.getAmount()); //outpout: 100 dollars
alert("author: " + MyBooksObj.author); //outpout: author: Tolkin
alert("title: " + MyBooksObj.title); //outpout: title: Hobbit
More details : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
Also interesting : https://coryrylan.com/blog/javascript-es6-class-syntax

Related

How do I add increment to two specific properties of a class?

Here are my instructions, I am having issues with getting two properties 'numberOfLikes' and 'comments' to use increment to adjust the amount of likes and comments. I don't know if I should use a for loop or if I just need the increment operator. I'm new to coding and apologize in advance.
/*
In the space below, add to the existing skeleton of a Tweet class.
A tweet should have a (dynamic) author, content, timeStamp, numberOfLikes, and comments.
A tweet should be able to increment the numberOfLikes and add to the list of comments.
Create several instances of your Tweet and log them to the console. Make sure the
tweet object instances behave as expected.
*/
class Tweet {
constructor(author, content, timeStamp, numberOfLikes, comments) {
this.author = author;
this.content = content;
this.timeStamp = timeStamp;
this.numberOfLikes = numberOfLikes;
this.comments = comments;
}
};
//This is code I was playing around with, doesn't work
this.add = function(numberOfLikes){
for(i = 0; i < numberOfLikes.length; i++){
console.log("You have " + numberOfLikes + " likes");
}
}
this.add = function(comments) {
for(i = 0; i < comments.length; i++) {
console.log("You have " + comments + " comments");
}
}
var tweet1 = new Tweet("Rihanna", "Fenty Beauty", "12:31 A.M.", 120193, 6782);
Thanks in advance!
A tweet should be able to increment the numberOfLikes
This should be a function to increase the numberOfLikes.
and add to the list of comments.
comments is probably an array. This means that you need a function to add a comment, to the list of your comments.
class Tweet {
constructor(author, content, timeStamp, numberOfLikes, comments) {
this.author = author;
this.content = content;
this.timeStamp = timeStamp;
this.numberOfLikes = numberOfLikes;
this.comments = comments;
}
increaseNumberOfLikes() {
this.numberOfLikes++
}
addComment(commentText) {
this.comments.push(commentText)
}
};
let tweet1 = new Tweet("The Weekend", "Some content", "15:31 P.M.", 9800, ["so cool", "do it again"])
tweet1.increaseNumberOfLikes()
tweet1.addComment("Great Song!")
console.log(tweet1)
You should create more tweets like above.
You can create functions that use += and array#push to increment numbers and add values to arrays.
Incrementing tweets:
incrementLikes(increment = 1) {
this.numberOfLikes += increment
}
Adding a comment to the array:
addComment(comment) {
this.comments.push(comment)
}
I also noticed that in your post you mentioned that this.comments was a list. So I made that change when initializing the class.
new Tweet("Rihanna", "Fenty Beauty", "12:31 A.M.", 120193, ["amazing", "wow"]);
Demo:
class Tweet {
constructor(author, content, timeStamp, numberOfLikes, comments) {
this.author = author;
this.content = content;
this.timeStamp = timeStamp;
this.numberOfLikes = numberOfLikes;
this.comments = comments;
}
incrementLikes(increment = 1) {
this.numberOfLikes += increment
}
addComment(comment) {
this.comments.push(comment)
}
};
var tweet1 = new Tweet("Rihanna", "Fenty Beauty", "12:31 A.M.", 120193, ["amazing", "wow"]);
tweet1.incrementLikes()
console.log(tweet1.numberOfLikes)
tweet1.incrementLikes()
console.log(tweet1.numberOfLikes)
tweet1.addComment("This is a comment")
console.log(tweet1.comments)
Within the class body you need to define prototypal functions like this:
class Tweet {
constructor(author, content, timeStamp, numberOfLikes, comments) {
this.author = author;
this.content = content;
this.timeStamp = timeStamp;
this.numberOfLikes = numberOfLikes;
this.comments = comments;
}
like() {
this.numberOfLikes++;
}
comment(comment) {
this.comments.push(comment);
}
}
const tweet1 = new Tweet("Rihanna", "Fenty Beauty", "12:31 A.M.", 120193, ["I hate I hate know-it-alls"]);
console.log(tweet1.numberOfLikes);
tweet1.like();
tweet1.like();
console.log(tweet1.numberOfLikes);
console.log(tweet1.comments);
tweet1.comment("I love you Rihanna!!")
console.log(tweet1.comments);
.as-console-wrapper { min-height: 100%!important; top: 0; }
This should work. You need to put the function definitions inside the class definition. I also modified the function names, because they conflict with each other.
class Tweet {
constructor(author, content, timeStamp, numberOfLikes, comments) {
this.author = author;
this.content = content;
this.timeStamp = timeStamp;
this.numberOfLikes = numberOfLikes;
this.comments = comments;
}
addLikes(numberOfLikes){
this.numberOfLikes += numberOfLikes
}
addComments(comments) {
this.comments += comments
}
};
// Initial tweet instance
var tweet1 = new Tweet("Rihanna", "Fenty Beauty", "12:31 A.M.", 120193, 6782);
// Call modifiers
tweet1.addLikes(5)
tweet1.addComments(7)
// Check the variables were modified
console.log(tweet1.numberOfLikes)
console.log(tweet1.comments)

Class object overlapping

im still trying to learn and im trying to make a group of actors be added to a movie class, i made it work but i still have problems because if you add another actor the last one dissappears, i tried with a loop but i could do nothing.
class Movie {
constructor(title,year,duration){
this.title = title;
this.year = year;
this.duration = duration;
}
addCast(actors){
this.actors = actors
}
}
class Actor {
constructor(name,age)
{
this.name = name;
this.age = age;
}
}
const terminator = new Movie('Terminator I', 1985, 60);
const arnold = new Actor('Arnold Schwarzenegger', 50);
const otherCast = [
new Actor('Paul Winfield', 50),
new Actor('Michael Biehn', 50),
new Actor('Linda Hamilton', 50)
];
//From here it can not be modified
let movieOne = new Movie("Kong","2018","2h30m");
let movieTwo = new Movie("Joker","2019","2h03m");
let movieThree = new Movie("John Wick 3", "2019", "1h49m");
terminator.addCast(arnold);
terminator.addCast(otherCast);
//To here it can not be modified
console.log({movieOne,movieTwo,movieThree,terminator});
See? Arnold should be in the actors too but it isnt! Thanks for the help in advance.
Another thing, this is for an excercise and i can not modify the lines i commented.
You have
addCast(actors){
this.actors = actors
}
This does not add the passed actor array to the actors on the instance - it replaces the instance's actors with the passed argument. Calling addCast will result in whatever previously existed on actors being lost.
To help reduce bugs, it can help to name methods appropriately - for logic like this, I'd call it setCast, not addCast.
If you want to add onto the end of the existing cast, and you're not sure whether the argument will be a single actor to add or an array of actors to add, use:
addCast(actorOrActors) {
if (Array.isArray(actorOrActors)) {
this.actors.push(...actorOrActors);
} else {
this.actors.push(actorOrActors);
}
}
class Movie {
constructor(title, year, duration) {
this.title = title;
this.year = year;
this.duration = duration;
this.actors = [];
}
addCast(actorOrActors) {
if (Array.isArray(actorOrActors)) {
this.actors.push(...actorOrActors);
} else {
this.actors.push(actorOrActors);
}
}
}
class Actor {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const terminator = new Movie('Terminator I', 1985, 60);
const arnold = new Actor('Arnold Schwarzenegger', 50);
const otherCast = [
new Actor('Paul Winfield', 50),
new Actor('Michael Biehn', 50),
new Actor('Linda Hamilton', 50)
];
//From here it can not be modified
let movieOne = new Movie("Kong", "2018", "2h30m");
let movieTwo = new Movie("Joker", "2019", "2h03m");
let movieThree = new Movie("John Wick 3", "2019", "1h49m");
terminator.addCast(arnold);
terminator.addCast(otherCast);
//To here it can not be modified
console.log({
movieOne,
movieTwo,
movieThree,
terminator
});
This is because in your addCast() method, each time you call it, you are replacing the previous value instead of appending it
You overwrite arnold with your second addActors call.
Only add one actor at a time to an array of actors.
class Movie {
constructor(title,year,duration){
this.title = title;
this.year = year;
this.duration = duration;
this.actors = [];
}
addCast(actor){
this.actors.push(actor);
}
terminator.addCast(arnold);
terminator.addCast(otherCast[0]);
terminator.addCast(otherCast[1]);
terminator.addCast(otherCast[2]);

JavaScript beginner: why does this not work?

My html page is not responding to this code I wrote in JS, i'm a total beginner, and just started learning JS, can somebody tell me why this doesn't work?
/* this is a practice file that'll play with js
nothing strange to look at here folks! */
var firstName = 'Steven';
var lastName = 'Curry';
var fullName = firstName + ' ' + lastName;
function Hotel(HotelName){
this.HotelName = HotelName;
this.numRooms = 20;
this.numGuests;
this.checkAvailability {
if(numRooms != 20 ){
return true;
}
else{
return false;
}
}
this.getHotelName = function(){
//can it work with this dot operator?
return this.HotelName;
}
}
var HiltonHotel = new Hotel('Hilton');
var hName = document.getElementById('hotelName');
hName.textContent = getHotelName();
var el = document.getElementById('name');
el.textContent = fullName;
<!DOCTYPE html>
<html>
<body>
<div id = 'greeting'> Hello
<span id="name">friend</span>!
<h1>Welcome To the <span id = 'hotelName'>Hyatt</span>
</div>
<script
src = "https://stacksnippets.net/js">
</script>
</body>
</html
I'm pretty sure it's ordering and my syntax i need to work on, any advice is greatly appreciated thank you!
Few misunderstandings:
checkAvailability is a function, you are missing parens.
while accessing the getHotelName function, you have to refer to the HiltonHotel variable, to be able to access and call that function.
few minor errors in your html code, while operating in code snippet, you don't have to add a separate script, it's connected together by default.
var firstName = 'Steven';
var lastName = 'Curry';
var fullName = firstName + ' ' + lastName;
function Hotel(HotelName) {
this.HotelName = HotelName;
this.numRooms = 20;
this.numGuests;
this.checkAvailability = function() { // it's a function (missing parens)
if (numRooms != 20) {
return true;
} else {
return false;
}
}
this.getHotelName = function() {
return this.HotelName;
}
}
var WeiHotel = new Hotel('Hilton');
var hName = document.getElementById('hotelName');
hName.textContent = WeiHotel.getHotelName(); // refer to the `WeiHotel` variable
var el = document.getElementById('name');
el.textContent = fullName;
<div id='greeting'> Hello
<span id="name">friend</span>!
<h1>Welcome To the <span id='hotelName'>Hyatt</span></h1>
</div>
An extension to the answer of #KindUser:
You're not using closures anywhere in this class to store some private state. Therefore you should attach the methods to the prototype and not to the instance itself. It's more economic, because now all instances share one function, not one per instance. And the JS engine can optimize that better.
Then, you have another error in checkAvailability: numRooms needs to be addressed as this.numRooms because it is a property of this instance, and there is no variable with this name.
And one about style. If you have something like
if(condition){
return true;
}else{
return false;
}
you can simplify this to:
return condition;
//or if you want to enforce a Boolean value,
//but your condition may return only a truthy/falsy value:
return Boolean(condition);
//sometimes also written as:
return !!(condition);
Next. Stick to the coding standards. In JS a variable/property starting with an uppercase letter would indicate a class/constructor, therefore HotelName, HiltonHotel, WeiHotel are misleading.
And I find the property name hotelName redundant and counter-intuitive. Imo you have a Hotel, it has a name, but that's just an opinion.
var firstName = 'Steven';
var lastName = 'Curry';
var fullName = firstName + ' ' + lastName;
function Hotel(name) {
this.name = name;
this.numRooms = 20;
this.numGuests;
}
Hotel.prototype.checkAvailability = function() {
return this.numRooms !== 20;
}
Hotel.prototype.getHotelName = function() {
return this.name;
}
var hotel = new Hotel('Hilton');
var hName = document.getElementById('hotelName');
hName.textContent = hotel.getHotelName(); // refer to the `weiHotel` variable
var el = document.getElementById('name');
el.textContent = fullName;
<div id='greeting'> Hello
<span id="name">friend</span>!
<h1>Welcome To the <span id='hotelName'>Hyatt</span></h1>
</div>
or as an ES6 class (and some playin around):
class Person{
constructor(firstName, lastName){
this.firstName = firstName;
this.lastName = lastName;
}
//this is a getter, you can read it like a property
get fullName(){
return this.firstName + " " + this.lastName;
}
//this function is implicitely called whenever you try to convert
//an instance of `Person` into a string.
toString(){
return this.fullName;
}
}
class Hotel{
constructor(name) {
this.name = name;
this.numRooms = 20;
this.numGuests;
}
checkAvailability() {
return this.numRooms !== 20;
}
getHotelName() {
return this.name;
}
}
var steve = new Person('Steven', 'Curry');
var hotel = new Hotel('Hilton');
var hName = document.getElementById('hotelName');
hName.textContent = hotel.getHotelName(); // refer to the `weiHotel` variable
var el = document.getElementById('name');
el.textContent = steve.fullName;
//this uses the `toString()` method to convert the `Person` steve into a string
//for people, this makes sense, for the Hotel you'd want to think:
// - where do I want to use this?
// - and what should this string contain?
console.log("Hello, I'm " + steve + " and I'm at the "+ hotel.name);
<div id='greeting'> Hello
<span id="name">friend</span>!
<h1>Welcome To the <span id='hotelName'>Hyatt</span></h1>
</div>

JavaScript "Object Method"

function Money(bill, accu, hb, food, webHosting, lynda) {
Money.bill = bill;
Money.accu = accu;
Money.hb = hb;
Money.food = food;
Money.webHosting = webHosting;
Money.lynda = lynda;
Money.total = function () {
(Money.bill + Money.accu + Money.hb + Money.food + Money.webHosting + Money.lynda)
return Money.total;
};
}
var cost = new Money(2500, 5000, 2000, 6000, 1000, 30000);
Money.total();
I have defined everything for a object using a variable at the end.
When I run Money.total(the "money" objects method) it returns as ""function""
Please help.I want the total of everything.
You need to use this instead of `Money' to read/write properties of the newly created object:
function Money(bill, accu, hb, food, webHosting, lynda) {
this.bill = bill;
this.accu = accu;
this.hb = hb;
this.food = food;
this.webHosting = webHosting;
this.lynda = lynda;
this.total = function() {
return this.bill + this.accu + this.hb + this.food + this.webHosting + this.lynda;
}
}
It's unclear what you were trying to do with your 'total' method. If you can describe what that is supposed to do, then we could help with that implementation, but I've provided an implementation that sums the properties.

Javascript: Modify an object from a pointer

I'm making a digital library with three classes: Library, Shelf & Book. Shelves have their contents as an array of books. Books have two methods, enshelf and unshelf. When a book gets unshelfed it's supposed to set delete the instance of itself from the shelf it's on and then set it's location property to null. How can I modify the shelf it's sitting on? In the constructor if I change this.location, it will just give that property a new value instead of modifying the variable it points to. I feel like this is really simple and I'm overlooking something super basic.
var _ = require('lodash');
//books
var oldMan = new Book("Old Man and the Sea", "Ernest Hemingway", 0684801221);
var grapes = new Book("The Grapes of Wrath", "John Steinbeck", 0241952476);
var diamondAge = new Book("The Diamond Age", "Neal Stephenson", 0324249248);
//shelves
var shelf0 = new Shelf(0);
var shelf1 = new Shelf(1);
//libraries
var myLibrary = new Library([shelf0, shelf1], "123 Fake Street");
//these need to accept an unlimited amount of each
function Library(shelves, address) {
this.shelves = shelves; //shelves is an array
this.address = address;
this.getAllBooks = function() {
console.log("Here are all the books in the library: ");
for (var i = 0; i < this.shelves.length; i++) {
console.log("Shelf number " + i + ": ");
for (var j = 0; j < this.shelves[i].contents.length; j++) {
console.log(this.shelves[i].contents[j].name);
}
}
}
}
function Shelf(id) {
this.id = id;
this.contents = [];
}
function Book(name, author, isbn) {
this.name = name;
this.author = author;
this.isbn = isbn;
this.location = null;
this.enshelf = function(newLocation) {
this.location = newLocation;
newLocation.contents.push(this);
}
this.unshelf = function() {
_.without(this.location, this.name); //this doesn't work
this.location = null;
}
}
console.log("Welcome to Digital Library 0.1!");
oldMan.enshelf(shelf1);
myLibrary.getAllBooks();
oldMan.unshelf();
myLibrary.getAllBooks();
Small issue with your unshelf method, easily remedied:
this.unshelf = function() {
this.location.contents =
_.without(this.location.contents, this);
this.location = null;
}
Consider, however, that shelf and unshelf should be methods of Shelf, and not of Book. Also, if you must have this method, surround it with a guard, like so:
this.unshelf = function() {
if (this.location) {
this.location.contents =
_.without(this.location.contents, this);
this.location = null;
}
}
Couple of small issues:
without works on arrays and returns a copy of the array with the elements removed - the original is untouched. So you need to pass location.contents instead of just location and reassign it back to location.contents.
Also you add the whole book to the Shelf, then try to remove it by name, so it doesn't match and get removed. So just pass this to without:
this.unshelf = function() {
if (this.location) {
this.location.contents = _.without(this.location.contents, this);
this.location = null;
}
}

Categories