How to define Singleton pattern with parameter using static keyword? - javascript

According to second answer from here, I'm trying to create Singleton pattern in JS for storing data and invoking its prototypes from the other instances.
A main problem is Singleton doesn't store the data after receives the first instance.
[{…}]
0: {firstName: "John", lastName: "Grand"}
This is how I've done:
export default class Terminal {
static cache(output) {
// Singleton
if (!Terminal.instance) {
Terminal.instance = new Terminal(output);
}
return Terminal.instance;
}
constructor(output) {
// Create an array
this.logs = [];
// Switch to an object
const data = Object.assign({}, output);
// Add the object to the array
this.logs.push(data);
// Inspect
console.log(this.logs);
}
}
// instance 1
import Terminal from './terminal.js';
class Person {
constructor(firstName, lastName, input) {
this.firstName = firstName;
this.lastName = lastName;
// Assign the Singleton
this.input = input || Terminal.cache(this);
}
}
let player1 = new Person('John', 'Grand');
// instance 2
import Terminal from './terminal.js';
class Grocery {
constructor(name, stock, input) {
this.name = name;
this.stock = stock;
// Assign the Singleton
this.input = input || Terminal.cache(this);
}
}
let shop1 = new Grocery('Apple', 12);
I want to let the new keyword inside of the class when I define the Singleton pattern.
Any tips to accomplish my problem?
Thanks.

The cache() method needs to push output onto the logs array when the object already exists.
static cache(output) {
// Singleton
if (!Terminal.instance) {
Terminal.instance = new Terminal(output);
} else {
Terminal.instance.logs.push(Object.assign({}, output));
}
return Terminal.instance;
}

Related

Is there a way to return an JSON object back to it's js object form? [duplicate]

Its easy to load JSON into an object in javascript using eval or JSON.parse.
But if you have a proper "class" like function, how do you get the JSON data into it?
E.g.
function Person(name) {
this.name=name;
this.address = new Array();
this.friendList;
this.promote = function(){
// do some complex stuff
}
this.addAddress = function(address) {
this.address.push(address)
}
}
var aPersonJSON = '{\"name\":\"Bob\",\"address\":[{\"street\":\"good st\",\"postcode\":\"ADSF\"}]}'
var aPerson = eval( "(" + aPersonJSON + ")" ); // or JSON.parse
//alert (aPerson.name); // Bob
var someAddress = {street:"bad st",postcode:"HELL"};
//alert (someAddress.street); // bad st
aPerson.addAddress(someAddress); // fail!
The crux is I need to be able to create proper Person instances from JSON, but all I can get is a dumb object. Im wondering if its possible to do something with prototypes?
I dont want to have to parse each line of the JSON and assign each variable to the coresponding functions attributes, which would be too difficult. The actualy JSON and functions I have are much more complicated than the example above.
I am assuming one could JSONify the functions methods into the JSON string, but as I need to keep the resultant data as small as possible this is not an option - I only want to store and load the data, not the javascript code for the methods.
I also dont want to have to put the data loaded by JSON as a sub object if I can help it (but might be the only way), e.g.
function Person(name) {
this.data = {};
this.data.name=name;
}
var newPerson = new Person("");
newPerson.data = eval( "(" + aPersonJSON + ")" );
alert (newPerson.data.name); // Bob
Any ideas?
You need to use a reviver function:
// Registry of types
var Types = {};
function MyClass(foo, bar) {
this._foo = foo;
this._bar = bar;
}
Types.MyClass = MyClass;
MyClass.prototype.getFoo = function() {
return this._foo;
}
// Method which will provide a JSON.stringifiable object
MyClass.prototype.toJSON = function() {
return {
__type: 'MyClass',
foo: this._foo,
bar: this._bar
};
};
// Method that can deserialize JSON into an instance
MyClass.revive = function(data) {
// TODO: do basic validation
return new MyClass(data.foo, data.bar);
};
var instance = new MyClass('blah', 'blah');
// JSON obtained by stringifying an instance
var json = JSON.stringify(instance); // "{"__type":"MyClass","foo":"blah","bar":"blah"}";
var obj = JSON.parse(json, function(key, value) {
return key === '' && value.hasOwnProperty('__type')
? Types[value.__type].revive(value)
: this[key];
});
obj.getFoo(); // blah
No other way really...
Many frameworks provide an 'extend' function that will copy fields over from one object to another. You can combine this with JSON.parse to do what you want.
newPerson = new Person();
_.extend(newPerson, JSON.parse(aPersonJSON));
If you don't want to include something like underscore you can always copy over just the extend function or write your own.
Coffeescript example because I was bored:
JSONExtend = (obj, json) ->
obj[field] = value for own field, value of JSON.parse json
return obj
class Person
toString: -> "Hi I'm #{#name} and I'm #{#age} years old."
dude = JSONExtend new Person, '{"name":"bob", "age":27}'
console.log dude.toString()
A little late to the party, but this might help someone.
This is how I've solved it, ES6 syntax:
class Page
{
constructor() {
this.__className = "Page";
}
__initialize() {
// Do whatever initialization you need here.
// We'll use this as a makeshift constructor.
// This method is NOT required, though
}
}
class PageSection
{
constructor() {
this.__className = "PageSection";
}
}
class ObjectRebuilder
{
// We need this so we can instantiate objects from class name strings
static classList() {
return {
Page: Page,
PageSection: PageSection
}
}
// Checks if passed variable is object.
// Returns true for arrays as well, as intended
static isObject(varOrObj) {
return varOrObj !== null && typeof varOrObj === 'object';
}
static restoreObject(obj) {
let newObj = obj;
// At this point we have regular javascript object
// which we got from JSON.parse. First, check if it
// has "__className" property which we defined in the
// constructor of each class
if (obj.hasOwnProperty("__className")) {
let list = ObjectRebuilder.classList();
// Instantiate object of the correct class
newObj = new (list[obj["__className"]]);
// Copy all of current object's properties
// to the newly instantiated object
newObj = Object.assign(newObj, obj);
// Run the makeshift constructor, if the
// new object has one
if (newObj.__initialize === 'function') {
newObj.__initialize();
}
}
// Iterate over all of the properties of the new
// object, and if some of them are objects (or arrays!)
// constructed by JSON.parse, run them through ObjectRebuilder
for (let prop of Object.keys(newObj)) {
if (ObjectRebuilder.isObject(newObj[prop])) {
newObj[prop] = ObjectRebuilder.restoreObject(newObj[prop]);
}
}
return newObj;
}
}
let page = new Page();
let section1 = new PageSection();
let section2 = new PageSection();
page.pageSections = [section1, section2];
let jsonString = JSON.stringify(page);
let restoredPageWithPageSections = ObjectRebuilder.restoreObject(JSON.parse(jsonString));
console.log(restoredPageWithPageSections);
Your page should be restored as an object of class Page, with array containing 2 objects of class PageSection. Recursion works all the way to the last object regardless of depth.
#Sean Kinsey's answer helped me get to my solution.
Easiest way is to use JSON.parse to parse your string then pass the object to the function. JSON.parse is part of the json2 library online.
I;m not too much into this, but aPerson.addAddress should not work,
why not assigning into object directly ?
aPerson.address.push(someAddress);
alert(aPerson.address); // alert [object object]
Just in case someone needs it, here is a pure javascript extend function
(this would obviously belong into an object definition).
this.extend = function (jsonString){
var obj = JSON.parse(jsonString)
for (var key in obj) {
this[key] = obj[key]
console.log("Set ", key ," to ", obj[key])
}
}
Please don't forget to remove the console.log :P
TL; DR: This is approach I use:
var myObj = JSON.parse(raw_obj_vals);
myObj = Object.assign(new MyClass(), myObj);
Detailed example:
const data_in = '{ "d1":{"val":3,"val2":34}, "d2":{"val":-1,"val2":42, "new_val":"wut?" } }';
class Src {
val1 = 1;
constructor(val) { this.val = val; this.val2 = 2; };
val_is_good() { return this.val <= this.val2; }
get pos_val() { return this.val > 0; };
clear(confirm) { if (!confirm) { return; }; this.val = 0; this.val1 = 0; this.val2 = 0; };
};
const src1 = new Src(2); // standard way of creating new objects
var srcs = JSON.parse(data_in);
// ===================================================================
// restoring class-specific stuff for each instance of given raw data
Object.keys(srcs).forEach((k) => { srcs[k] = Object.assign(new Src(), srcs[k]); });
// ===================================================================
console.log('src1:', src1);
console.log("src1.val_is_good:", src1.val_is_good());
console.log("src1.pos_val:", src1.pos_val);
console.log('srcs:', srcs)
console.log("srcs.d1:", srcs.d1);
console.log("srcs.d1.val_is_good:", srcs.d1.val_is_good());
console.log("srcs.d2.pos_val:", srcs.d2.pos_val);
srcs.d1.clear();
srcs.d2.clear(true);
srcs.d3 = src1;
const data_out = JSON.stringify(srcs, null, '\t'); // only pure data, nothing extra.
console.log("data_out:", data_out);
Simple & Efficient. Compliant (in 2021). No dependencies.
Works with incomplete input, leaving defaults instead of missing fields (particularly useful after upgrade).
Works with excessive input, keeping unused data (no data loss when saving).
Could be easily extended to much more complicated cases with multiple nested classes with class type extraction, etc.
doesn't matter how much data must be assigned or how deeply it is nested (as long as you restore from simple objects, see Object.assign() limitations)
The modern approach (in December 2021) is to use #badcafe/jsonizer : https://badcafe.github.io/jsonizer
Unlike other solutions, it doesn't pollute you data with injected class names,
and it reifies the expected data hierarchy.
below are some examples in Typescript, but it works as well in JS
Before showing an example with a class, let's start with a simple data structure :
const person = {
name: 'Bob',
birthDate: new Date('1998-10-21'),
hobbies: [
{ hobby: 'programming',
startDate: new Date('2021-01-01'),
},
{ hobby: 'cooking',
startDate: new Date('2020-12-31'),
},
]
}
const personJson = JSON.stringify(person);
// store or send the data
Now, let's use Jsonizer 😍
// in Jsonizer, a reviver is made of field mappers :
const personReviver = Jsonizer.reviver<typeof person>({
birthDate: Date,
hobbies: {
'*': {
startDate: Date
}
}
});
const personFromJson = JSON.parse(personJson, personReviver);
Every dates string in the JSON text have been mapped to Date objects in the parsed result.
Jsonizer can indifferently revive JSON data structures (arrays, objects) or class instances with recursively nested custom classes, third-party classes, built-in classes, or sub JSON structures (arrays, objects).
Now, let's use a class instead :
// in Jsonizer, a class reviver is made of field mappers + an instance builder :
#Reviver<Person>({ // 👈 bind the reviver to the class
'.': ({name, birthDate, hobbies}) => new Person(name, birthDate, hobbies), // 👈 instance builder
birthDate: Date,
hobbies: {
'*': {
startDate: Date
}
}
})
class Person {
constructor( // all fields are passed as arguments to the constructor
public name: string,
public birthDate: Date
public hobbies: Hobby[]
) {}
}
interface Hobby {
hobby: string,
startDate: Date
}
const person = new Person(
'Bob',
new Date('1998-10-21'),
[
{ hobby: 'programming',
startDate: new Date('2021-01-01'),
},
{ hobby: 'cooking',
startDate: new Date('2020-12-31'),
},
]
);
const personJson = JSON.stringify(person);
const personReviver = Reviver.get(Person); // 👈 extract the reviver from the class
const personFromJson = JSON.parse(personJson, personReviver);
Finally, let's use 2 classes :
#Reviver<Hobby>({
'.': ({hobby, startDate}) => new Hobby(hobby, startDate), // 👈 instance builder
startDate: Date
})
class Hobby {
constructor (
public hobby: string,
public startDate: Date
) {}
}
#Reviver<Person>({
'.': ({name, birthDate, hobbies}) => new Person(name, birthDate, hobbies), // 👈 instance builder
birthDate: Date,
hobbies: {
'*': Hobby // 👈 we can refer a class decorated with #Reviver
}
})
class Person {
constructor(
public name: string,
public birthDate: Date,
public hobbies: Hobby[]
) {}
}
const person = new Person(
'Bob',
new Date('1998-10-21'),
[
new Hobby('programming', new Date('2021-01-01')),
new Hobby('cooking', new Date('2020-12-31')
]
);
const personJson = JSON.stringify(person);
const personReviver = Reviver.get(Person); // 👈 extract the reviver from the class
const personFromJson = JSON.parse(personJson, personReviver);

Call class directly and not via instance variable

I have created the following two classes in my classes.js:
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
display() {
console.log(this.firstName + " " + this.lastName);
}
}
module.exports = {
Person
};
As you can see I am exporting the two classes via module.exports.
Person = require('./classes.js');
const someone1 = new Person("First name", "Last name"); // <-- does NOT work
const someone = new Person.Person("First name", "Last name"); // does work
someone.display();
However, when calling the classes I get an error, when calling the class directly.
Any suggestions how to call the class directly?
I appreciate your replies!
If you want all your classes in one file called classes.js, then this should work;
// classes.js
class Person {
// ...
}
class Vehicle {
// ...
}
module.exports = {
Person,
Vehicle
}
// some-other-file.js
const { Person } = require('../classes')
const person = new Person('First', 'Last')
Although to keep things easy to understand, my recommendation would be to split your class into multiple files and then export each class from its file directly;
class Person {
// ...
}
module.exports = Person
const Person = require('../classes/person')
// do something
In this case you're exporting an object containing Person class
module.exports = {
Person
};
So when you import like Person = require('./classes.js')
You're actually importing the exported object. So Person after you've imported is similar to
Person = {
Person
};
You can either change the export by assigning the desired export object to module.exports like:
module.exports = Person
or change your import to use destructuring assignment to import like (notice the curly braces):
const { Person } = require('./classes.js');
If
module.exports = {
Person
};
therefore
Person = require('./classes.js').Person;
Alternatively, you can
module.exports = Person;
and
Person = require('./classes.js');
In classes.js you are exporting Person as an object property. So to make it work as you expect you can either do Destructuring Assignment
const { Person } = require('./classes')
Or just require class directly from classes.js file.
const Person = require('./classes').Person
Alternatively, and it's a better option, in my opinion, you can separate Person to its own file and export it by default.
File ./Person.js
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
display() {
console.log(this.firstName + " " + this.lastName);
}
}
module.exports = Person
And then in your main file just do:
const Person = require('./Person');

Recursively requiring modules in Node

So, let's say I have two classes, each of which requires the other:
Department.js:
const Person = require("./Person");
class Department{
constructor(id){
this.personel = getPersonel(id).map(person => new Person(person));
}
}
Person.js
const Department = require("./Department");
class Person {
constructor(id){
this.department = new Department(getDeptOfPerson(id));
}
}
const person = new Person(1);
const coworkers = person.department.personel;
Now, this doesn't work, and I can kinda understand why. It says "Person" is not a constructor at Department.js. However, if I put both classes in the same file, it works just fine.
So; my question is, how do I work around this? I'd really rather not keep both of these classes in the same file -- is there a better way?
I'm running the latest version of Node.
You can separate initializing objects from fetching data:
// types/Department.js
class Department {
constructor(id, personnel) {
this.id = id;
this.personnel = personnel;
}
}
// types/Person.js
class Person {
constructor(id, department) {
this.id = id;
this.department = department;
}
}
// findDepartment.js
const Department = require('./types/Department');
const Person = require('./types/Person');
function findDepartment(id) {
const personnel = getPersonnel(id).map(person => new Person(person));
return new Department(id, personnel);
}
// findPerson.js
const Department = require('./types/Department');
const Person = require('./types/Person');
function findPerson(id) {
const department = getDeptOfPerson(id);
return new Person(id, department);
}
These can even go back on the types (findDepartment.js → Department.js, Department.findDepartment = function (id) { …) if you really want.

How to define getters and setters in new ECMAScript 2015 JavaScript classes

I'm trying do define a simple class called Mail using the new ECMAScript 2015 JavaScript classes as defined here.
I came up with this constructor and tried the following getter:
class Mail {
constructor(sender, date, subject, text) {
this.sender = sender;
this.date = date;
this.subject = subject;
this.text = text;
}
get sender() {
return this.sender;
}
}
But when I tried to use that method, it didn't work.
var mail = new Mail("#mail.com", Date.now(), "Testing", "Text");
console.log(mail.sender());
Returned:
TypeError: mail.sender is not a function
So, my doubt is:
Is it necessary to define getters and setters like in Java or can I simply access field like mail.sender (or so)?
If it is necessary, how to properly define getters and setters to each of class instance variables?
getter and setter are basically hooks to capture get- and set- actions for properties of an object. They are functions, but they seem (to the code that uses this Object) like a property.
var mail = new Mail("#mail.com", Date.now(), "Testing", "Text");
console.log(mail.sender); //no brackets
and if you add a setter, main.sender = "#foo.bar".
And I'm wondering that your code didn't get stuck in an infinite recursion, since your getter called itself.
class Mail {
constructor(sender, date, subject, text) {
this._sender = sender;
this._date = date;
this._subject = subject;
this._text = text;
}
//this IS `this.sender`!
//it should not `return this.sender`, since this would call this getter again,
//and you get stuck in an infinite recursion.
get sender() {
return this._sender;
}
//and a possible setter, that handles
//instance.sender = whatever;
set sender(value) {
this._sender = value;
}
}
Edit:
Does this mean that I can just ignore get and set methods because I can directly access object field?
you don't need getter or setter to make any property public in JS, simply defineing this property makes it public available. JS works the other way around, you have to take additional efforts to make things private (see closures). In JS everything is by default public.
But you can use getter (and setter) to fulfil additional tasks, like:
class Person{
constructor(firstName, lastName){
//regular public properties
this.firstName = firstName;
this.lastName = lastName;
}
//a composed value, that you can access like a property
get fullName(){
return this.firstName + " " + this.lastName;
}
//you could also add a setter like this
set fullName(value){
[this.firstName, this.lastName] = value.split(/\s+/g);
}
}
var john = new Person("John", "Doe");
console.log(john.fullName);
To use getters and setters, you just need to use and set the property. in your case: mail.sender. This will call the getter if you are using the value, or the setter if you are overriding the current value.
class Mail {
constructor(sender) {
this._sender = sender;
}
get sender() {
return "Sender: " + this._sender;
}
set sender(val) {
this._sender = val.toLowerCase();
}
}
const m = new Mail("TEST");
console.log(m.sender); // => "Sender: TEST"
m.sender = "TEST";
console.log(m.sender); // => "Sender: test"
Note that I used _sender to store the value to prevent a Maximum call stack size exceeded error.
You can use the property like any other property, and it will automatically call the getter or setter.
If what you were looking for is encapsulation, you could design your classes like so:
const Mail = (function() {
let sender; // this is "private"
return {
title: "public", // This is public
get sender() {
return sender;
},
// ...
};
});
In this case, you would then create new Mail objects like so:
const mail = Mail();
You could return a function in the Mail function in order to be able to instantiate new objects with the new keyword.
You actually did more than you need to. You don't need getters as you specified.
Try this:
class Mail {
constructor(sender, date, subject, text) {
this.sender = sender;
this.date = date;
this.subject = subject;
this.text = text;
}
}
var mail = new Mail("#mail.com", Date.now(), "Testing", "Text");
console.log(mail.sender);
JavaScript has no concept of classes.
The class keyword is just syntactic sugar.
What a class do is, it generates a constructor function for you.
const Person = class { // or `class Person`
constructor(name) {
this.name = name;
}
say(msg) {
return `${this.name} says: "${msg}".`;
}
}
That's exactly the same, what you will achieve with the following code.
const Person = function(name) {
this.name = name;
};
Person.prototype = {
say(msg) {
return `${this.name} says: "${msg}".`;
}
};
If you need private variables, you have to work with closures.
const Person = class { // or `class Person`
constructor(name) {
this.getName = () => name;
this.setName = (newName) => {
name = newName;
}
}
say(msg) {
return `${this.getName()} says: "${msg}".`;
}
}
let p = new Person('me');
console.log(p.name); // undefined
console.log(p.getName()); // 'me'
p.setName('he');
console.log(p.getName()); // 'he'
console.log(p.say('something')); // 'he says: "something".'
I advice you to not use class and to avoid new.
If you need an object, you simply can use factory functions:
const Person = function(name) {
let person = Object.create(Person.prototype);
person.name = name;
return person;
};
Person.prototype = {
say(msg) {
return `${this.name} says: "${msg}".`;
}
};

Javascript ,classes to add constructor-function

I'm kinda new with js + ES6 + class; I have problem with creating function inside constructor.
#1. I need to add new Hobby, a person allowed to have plenty hobbies ;
#2. I don't know how to show all the data of students;
another questions are in the comments ,in case if you want to answer it too, if not i'm also fine.
so here's my code :
class Student {
constructor(name,hobbies){
this.name = name;
var hobby = new Set(); //do I set here or inside the function ??
//since the function addHobbies also need, then it's fine to be global right ?
this.hobbies = (hobbies) => { //function ES6 like this right ??
this.hobbies = hobby.add(hobbies);
return this.hobbies; //can I return hobby instead of this.hobbies ??
};
}
setName(newName){
this.name = newName;
}
addHobbies(newHobbies){
this.Hobbies = hobby.add(newHobbies); //it should be like this to add >> to set ?
}
getName(){
return this.name;
}
getHobbies(){
return this.hobbies;
}
}
and how to return all the data ?
let andy = new Student("andy","dance");
let vince = new Student("vince","codding");
so it will show all students-attribute by getCode() ?
do I set here or inside the function ??
That depends on what you need. Do you want each Student instead to have one set of hobbies, or do you want to create a new set every time the function is called?
this.hobbies = (hobbies) => { //function ES6 like this right ??
this.hobbies = hobby.add(hobbies);
That doesn't work at all. You're creating the property with a function value, but when the method is called you're overwriting the property with the return value of the add method.
To make it work, I'd recommend making the .hobbies set an instance property instead of a local variable.
class Student {
constructor(name, ...hobbies) {
this.name = name;
this.hobbies = new Set();
this.addHobbies(...hobbies);
}
getName() {
return this.name;
}
setName(newName) {
this.name = newName;
}
getHobbies() {
return this.hobbies;
}
addHobbies(...newHobbies) {
for (const newHobby of newHobbies)
this.hobbies.add(newHobby);
}
}
Alternatively, if you insist on using a local constructor variable, it would look like this:
class Student {
constructor(name, ...hobbies) {
this.name = name;
this.hobbies = new Set(...hobbies);
this.getHobbies = () => {
return this.hobbies;
};
this.addHobbies = (...newHobbies) => {
for (const newHobby of newHobbies)
this.hobbies.add(newHobby);
};
}
… // further methods (for name etc)
}
Try this:
class Student {
constructor(name, hobbies) {
this.name = name;
// Allow passing both an array of hobbies and a single hobby
this.hobbies = Array.isArray(hobbies) ? new Set(hobbies) : new Set([hobbies]);
}
setName(newName) {
this.name = newName;
}
addHobbies(newHobbies) {
if (Array.isArray(newHobbies)) {
newHobbies.forEach((hobby) => this.hobbies.add(hobby));
} else {
this.hobbies.add(newHobbies);
}
}
getName() {
return this.name;
}
getHobbies() {
return this.hobbies;
}
}
let andy = new Student("andy","dancing");
let vince = new Student("vince",["codding", "running"]);
andy.addHobbies("slipping");
vince.addHobbies(["running", "eating"]);
You are in the correct direction. I have rewritten your class to do what I think is more similar to what you are trying to achieve.
Play with the code at: https://jsbin.com/vejumo/edit?js,console
And here's the rewritten class:
class Student {
constructor(name, hobbies = []){
this.name = name;
// new Set() is used to work with objects. It does not work with well with strings
// Let's use an array to store the hobbies.
// if a hobby or an hobbies array is passed, store it, otherwise set an empty array.
this.hobbies = this.parseHobbies(hobbies);
}
// This function will normalize the hobbies to an Array
parseHobbies(hobbies) {
if (typeof hobbies === "string") {
// hobbies is a string, means it's a single hobby and not an array
return [hobbies];
}
// Assuming the hobbies is a an Array
return hobbies;
}
setName(newName) {
this.name = newName;
}
// this function will allow you to add a single hobby to the array
addHobbies(hobbies = []) {
// Same logic like in constract, this can accept a string or an array
// We use Array.concat and push to append to array
this.hobbies = this.hobbies.concat(this.parseHobbies(hobbies));
}
getName() {
return this.name;
}
getHobbies() {
return this.hobbies
}
// This will return all student attributes.
getAttributes() {
// Return a copy of all the attributes instead of returning references
return Object.assign({}, this);
}
}
let george = new Student("George", "Sports");
george.addHobbies(["Singing", "Fishing"]);
george.addHobbies("Dancing");
console.log(george.getAttributes());

Categories