Skip to content

Instantly share code, notes, and snippets.

@getify
Last active March 21, 2024 09:11
Show Gist options
  • Save getify/3b4f46cdd0b204eb03f2ba36e84e5948 to your computer and use it in GitHub Desktop.
Save getify/3b4f46cdd0b204eb03f2ba36e84e5948 to your computer and use it in GitHub Desktop.
an illustration (non-trivial example) of many newer JS class features
// abstract class, not intended to be instantiated directly
class CalendarItem {
static #UNSET = Symbol("unset")
static #isUnset(v) {
return v === this.#UNSET;
}
static {
for (let [idx,msg] of [
"ID is already set.",
"ID is unset.",
"Don't instantiate 'CalendarItem' directly.",
].entries()) {
this[`ERROR_${(idx+1)*100}`] = msg;
}
}
static isSameItem(item1,item2) {
if (#ID in item1 && #ID in item2) {
return item1.#ID === item2.#ID;
}
else {
return false;
}
}
#ID = CalendarItem.#UNSET
#setID(id) {
if (CalendarItem.#isUnset(this.#ID)) {
this.#ID = id;
}
else {
throw new Error(CalendarItem.ERROR_100);
}
}
description = null
startDateTime = null
constructor() {
// enforcing: abstract class, no direct instantiation
if (new.target !== CalendarItem) {
let id = Math.round(Math.random() * 1e9);
this.#setID(id);
}
else {
throw new Error(CalendarItem.ERROR_300);
}
}
getID() {
if (!CalendarItem.#isUnset(this.#ID)) {
return this.#ID;
}
else {
throw new Error(CalendarItem.ERROR_200);
}
}
getDateTimeStr() {
if (this.startDateTime instanceof Date) {
return this.startDateTime.toUTCString();
}
}
summary() {
console.log(`(${
this.getID()
}) ${
this.description
} at ${
this.getDateTimeStr()
}`);
}
}
class Reminder extends CalendarItem {
#complete = false; // <-- no ASI, semicolon needed
[Symbol.toStringTag] = "Reminder"
constructor(description,startDateTime) {
super();
this.description = description;
this.startDateTime = startDateTime;
}
isComplete() {
return !!this.#complete;
}
markComplete() {
this.#complete = true;
}
summary() {
if (this.isComplete()) {
console.log(`(${this.getID()}) Complete.`);
}
else {
super.summary();
}
}
}
class Meeting extends CalendarItem {
#getEndDateTimeStr() {
if (this.endDateTime instanceof Date) {
return this.endDateTime.toUTCString();
}
}
endDateTime = null; // <-- no ASI, semicolon needed
[Symbol.toStringTag] = "Meeting"
constructor(description,startDateTime,endDateTime) {
super();
this.description = description;
this.startDateTime = startDateTime;
this.endDateTime = endDateTime;
}
getDateTimeStr() {
return `${
super.getDateTimeStr()
} - ${
this.#getEndDateTimeStr()
}`;
}
}
var callMyParents = new Reminder(
"Call my parents to say hi",
new Date("July 7, 2022 11:00:00 UTC")
);
callMyParents.toString();
// [object Reminder]
callMyParents.summary();
// (586380912) Call my parents to say hi at
// Thu, 07 Jul 2022 11:00:00 GMT
callMyParents.markComplete();
callMyParents.summary();
// (586380912) Complete.
callMyParents instanceof Reminder;
// true
callMyParents instanceof CalendarItem;
// true
callMyParents instanceof Meeting;
// false
var interview = new Meeting(
"Job Interview: ABC Tech",
new Date("June 23, 2022 08:30:00 UTC"),
new Date("June 23, 2022 09:15:00 UTC")
);
interview.toString();
// [object Meeting]
interview.summary();
// (994337604) Job Interview: ABC Tech at Thu,
// 23 Jun 2022 08:30:00 GMT - Thu, 23 Jun 2022
// 09:15:00 GMT
interview instanceof Meeting;
// true
interview instanceof CalendarItem;
// true
interview instanceof Reminder;
// false
Reminder.isSameItem(callMyParents,callMyParents);
// true
Meeting.isSameItem(callMyParents,interview);
// false
@mahdisoultana
Copy link

wow it's awesome tutorial ❤ i love it
thank you @getify

@muletasolomon
Copy link

thank you @getify

@jjaaccoobb
Copy link

static #UNSET = Symbol("unset")
What do the hashes signify? Please kindly link to MDN article.

if (new.target !== CalendarItem) {
From where does new come?

Thanks in advance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment