Using classes - JavaScript | MDN
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_classes • 230 KB fetched
Open original page
Using classes - JavaScript | MDN
*
Skip to main content
*
Skip to search
HTML
HTML: Markup language
HTML reference
*
Elements
*
Global attributes
*
Attributes
*
See all…
HTML guides
*
Responsive images
*
HTML cheatsheet
*
Date & time formats
*
See all…
Markup languages
*
SVG
*
MathML
*
XML
CSS
CSS: Styling language
CSS reference
*
Properties
*
Selectors
*
At-rules
*
Values
*
See all…
CSS guides
*
Box model
*
Animations
*
Flexbox
*
Colors
*
See all…
Layout cookbook
*
Column layouts
*
Centering an element
*
Card component
*
See all…
JavaScript JS
JavaScript: Scripting language
JS reference
*
Standard built-in objects
*
Expressions & operators
*
Statements & declarations
*
Functions
*
See all…
JS guides
*
Control flow & error handing
*
Loops and iteration
*
Working with objects
*
Using classes
*
See all…
Web APIs
Web APIs: Programming interfaces
Web API reference
*
File system API
*
Fetch API
*
Geolocation API
*
HTML DOM API
*
Push API
*
Service worker API
*
See all…
Web API guides
*
Using the Web animation API
*
Using the Fetch API
*
Working with the History API
*
Using the Web speech API
*
Using web workers
All
All web technology
Technologies
*
Accessibility
*
HTTP
*
URI
*
Web extensions
*
WebAssembly
*
WebDriver
*
See all…
Topics
*
Media
*
Performance
*
Privacy
*
Security
*
Progressive web apps
Learn
Learn web development
Frontend developer course
*
Getting started modules
*
Core modules
*
MDN Curriculum
*
Check out the video course from Scrimba, our partner
Learn HTML
*
Structuring content with HTML module
Learn CSS
*
CSS styling basics module
*
CSS layout module
Learn JavaScript
*
Dynamic scripting with JavaScript module
Tools
Discover our tools
*
Playground
*
HTTP Observatory
*
Border-image generator
*
Border-radius generator
*
Box-shadow generator
*
Color format converter
*
Color mixer
*
Shape generator
About
Get to know MDN better
*
About MDN
*
Advertise with us
*
Community
*
MDN on GitHub
Blog
Toggle sidebar
*
Web
*
JavaScript
*
Guide
*
Using classes
Theme
*
OS default
*
Light
*
Dark
English (US)
Remember language
Learn more
*
Deutsch
*
English (US)
*
Français
*
日本語
*
中文 (简体)
Using classes
* Previous
* Next
JavaScript is a prototype-based language — an object's behaviors are specified by its own properties and its prototype's properties. However, with the addition of classes , the creation of hierarchies of objects and the inheritance of properties and their values are much more in line with other object-oriented languages such as Java. In this section, we will demonstrate how objects can be created from classes.
In many other languages, classes , or constructors, are clearly distinguished from objects , or instances. In JavaScript, classes are mainly an abstraction over the existing prototypical inheritance mechanism — all patterns are convertible to prototype-based inheritance. Classes themselves are normal JavaScript values as well, and have their own prototype chains. In fact, most plain JavaScript functions can be used as constructors — you use the new operator with a constructor function to create a new object.
We will be playing with the well-abstracted class model in this tutorial, and discuss what semantics classes offer. If you want to dive deep into the underlying prototype system, you can read the Inheritance and the prototype chain guide.
This chapter assumes that you are already somewhat familiar with JavaScript and that you have used ordinary objects.
In this article
*
Overview of classes
*
Declaring a class
*
Constructor
*
Instance methods
*
Private fields
*
Accessor fields
*
Public fields
*
Static properties
*
Extends and inheritance
*
Why classes?
Overview of classes
If you have some hands-on experience with JavaScript, or have followed along with the guide, you probably have already used classes, even if you haven't created one. For example, this may seem familiar to you :
js
const bigDay = new Date(2019, 6, 19);
console.log(bigDay.toLocaleDateString());
if (bigDay.getTime() < Date.now()) {
console.log("Once upon a time...");
}
On the first line, we created an instance of the class Date , and called it bigDay . On the second line, we called a method toLocaleDateString() on the bigDay instance, which returns a string. Then, we compared two numbers: one returned from the getTime() method, the other directly called from the Date class itself , as Date.now() .
Date is a built-in class of JavaScript. From this example, we can get some basic ideas of what classes do:
* Classes create objects through the new operator.
* Each object has some properties (data or method) added by the class.
* The class stores some properties (data or method) itself, which are usually used to interact with instances.
These correspond to the three key features of classes:
* Constructor;
* Instance methods and instance fields;
* Static methods and static fields.
Declaring a class
Classes are usually created with class declarations .
js
class MyClass {
// class body...
}
Within a class body, there are a range of features available.
js
class MyClass {
// Constructor
constructor() {
// Constructor body
}
// Instance field
myField = "foo";
// Instance method
myMethod() {
// myMethod body
}
// Static field
static myStaticField = "bar";
// Static method
static myStaticMethod() {
// myStaticMethod body
}
// Static block
static {
// Static initialization code
}
// Fields, methods, static fields, and static methods all have
// "private" forms
#myPrivateField = "bar";
}
If you came from a pre-ES6 world, you may be more familiar with using functions as constructors. The pattern above would roughly translate to the following with function constructors:
js
function MyClass() {
this.myField = "foo";
// Constructor body
}
MyClass.myStaticField = "bar";
MyClass.myStaticMethod = function () {
// myStaticMethod body
};
MyClass.prototype.myMethod = function () {
// myMethod body
};
(function () {
// Static initialization code
})();
Note:
Private fields and methods are new features in classes with no trivial equivalent in function constructors.
Constructing a class
After a class has been declared, you can create instances of it using the new operator.
js
const myInstance = new MyClass();
console.log(myInstance.myField); // 'foo'
myInstance.myMethod();
Typical function constructors can both be constructed with new and called without new . However, attempting to "call" a class without new will result in an error.
js
const myInstance = MyClass(); // TypeError: Class constructor MyClass cannot be invoked without 'new'
Class declaration hoisting
Unlike function declarations, class declarations are not hoisted (or, in some interpretations, hoisted but with the temporal dead zone restriction), which means you cannot use a class before it is declared.
js
new MyClass(); // ReferenceError: Cannot access 'MyClass' before initialization
class MyClass {}
This behavior is similar to variables declared with let and const .
Class expressions
Similar to functions, class declarations also have their expression counterparts.
js
const MyClass = class {
// Class body...
};
Class expressions can have names as well. The expression's name is only visible to the class's body.
js
const MyClass = class MyClassLongerName {
// Class body. Here MyClass and MyClassLongerName point to the same class.
};
new MyClassLongerName(); // ReferenceError: MyClassLongerName is not defined
Constructor
Perhaps the most important job of a class is to act as a "factory" for objects. For example, when we use the Date constructor, we expect it to give a new object which represents the date data we passed in — which we can then manipulate with other methods the instance exposes. In classes, the instance creation is done by the constructor .
As an example, we would create a class called Color , which represents a specific color. Users create colors through passing in an RGB triplet.
js
class Color {
constructor(r, g, b) {
// Assign the RGB values as a property of `this`.
this.values = [r, g, b];
}
}
Open your browser's devtools, paste the above code into the console, and then create an instance:
js
const red = new Color(255, 0, 0);
console.log(red);
You should see some output like this:
Object { values: (3) […] }
values: Array(3) [ 255, 0, 0 ]
You have successfully created a Color instance, and the instance has a values property, which is an array of the RGB values you passed in. That is pretty much equivalent to the following:
js
function createColor(r, g, b) {
return {
values: [r, g, b],
};
}
The constructor's syntax is exactly the same as a normal function — which means you can use other syntaxes, like rest parameters :
js
class Color {
constructor(...values) {
this.values = values;
}
}
const red = new Color(255, 0, 0);
// Creates an instance with the same shape as above.
Each time you call new , a different instance is created.
js
const red = new Color(255, 0, 0);
const anotherRed = new Color(255, 0, 0);
console.log(red === anotherRed); // false
Within a class constructor, the value of this points to the newly created instance. You can assign properties to it, or read existing properties (especially methods — which we will cover next).
The this value will be automatically returned as the result of new . You are advised to not return any value from the constructor — because if you return a non-primitive value, it will become the value of the new expression, and the value of this is dropped. (You can read more about what new does in its description .)
js
class MyClass {
constructor() {
this.myField = "foo";
return {};
}
}
console.log(new MyClass().myField); // undefined
Instance methods
If a class only has a constructor, it is not much different from a createX factory function which just creates plain objects. However, the power of classes is that they can be used as "templates" which automatically assign methods to instances.
For example, for Date instances, you can use a range of methods to get different information from a single date value, such as the year , month , day of the week , etc. You can also set those values through the setX counterparts like setFullYear .
For our own Color class, we can add a method called getRed which returns the red value of the color.
js
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
getRed() {
return this.values[0];
}
}
const red = new Color(255, 0, 0);
console.log(red.getRed()); // 255
Without methods, you may be tempted to define the function within the constructor:
js
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
this.getRed = function () {
return this.values[0];
};
}
}
This also works. However, a problem is that this creates a new function every time a Color instance is created, even when they all do the same thing!
js
console.log(new Color().getRed === new Color().getRed); // false
In contrast, if you use a method, it will be shared between all instances. A function can be shared between all instances, but still have its behavior differ when different instances call it, because the value of this is different. If you are curious where this method is stored in — it's defined on the prototype of all instances, or Color.prototype , which is explained in more detail in Inheritance and the prototype chain .
Similarly, we can create a new method called setRed , which sets the red value of the color.
js
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
getRed() {
return this.values[0];
}
setRed(value) {
this.values[0] = value;
}
}
const red = new Color(255, 0, 0);
red.setRed(0);
console.log(red.getRed()); // 0; of course, it should be called "black" at this stage!
Private fields
You might be wondering: why do we want to go to the trouble of using getRed and setRed methods, when we can directly access the values array on the instance?
js
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
}
const red = new Color(255, 0, 0);
red.values[0] = 0;
console.log(red.values[0]); // 0
There is a philosophy in object-oriented programming called "encapsulation". This means you should not access the underlying implementation of an object, but instead use well-abstracted methods to interact with it. For example, if we suddenly decided to represent colors as HSL instead:
js
class Color {
constructor(r, g, b) {
// values is now an HSL array!
this.values = rgbToHSL([r, g, b]);
}
getRed() {
return hslToRGB(this.values)[0];
}
setRed(value) {
const rgb = hslToRGB(this.values);
rgb[0] = value;
this.values = rgbToHSL(rgb);
}
}
const red = new Color(255, 0, 0);
console.log(red.values[0]); // 0; It's not 255 anymore, because the H value for pure red is 0
The user assumption that values means the RGB value suddenly collapses, and it may cause their logic to break. So, if you are an implementor of a class, you would want to hide the internal data structure of your instance from your user, both to keep the API clean and to prevent the user's code from breaking when you do some "harmless refactors". In classes, this is done through private fields .
A private field is an identifier prefixed with # (the hash symbol). The hash is an integral part of the field's name, which means a private field can never have name clash with a public field or method. In order to refer to a private field anywhere in the class, you must declare it in the class body (you can't create a private element on the fly). Apart from this, a private field is pretty much equivalent to a normal property.
js
class Color {
// Declare: every Color instance has a private field called #values.
#values;
constructor(r, g, b) {
this.#values = [r, g, b];
}
getRed() {
return this.#values[0];
}
setRed(value) {
this.#values[0] = value;
}
}
const red = new Color(255, 0, 0);
console.log(red.getRed()); // 255
Accessing private fields outside the class is an early syntax error. The language can guard against this because #privateField is a special syntax, so it can do some static analysis and find all usage of private fields before even evaluating the code.
js
console.log(red.#values); // SyntaxError: Private field '#values' must be declared in an enclosing class
Note:
Code run in the Chrome console can access private elements outside the class. This is a DevTools-only relaxation of the JavaScript syntax restriction.
Private fields in JavaScript are hard private : if the class does not implement methods that expose these private fields, there's absolutely no mechanism to retrieve them from outside the class. This means you are safe to do any refactors to your class's private fields, as long as the behavior of exposed methods stay the same.
After we've made the values field private, we can add some more logic in the getRed and setRed methods, instead of making them simple pass-through methods. For example, we can add a check in setRed to see if it's a valid R value:
js
class Color {
#values;
constructor(r, g, b) {
this.#values = [r, g, b];
}
getRed() {
return this.#values[0];
}
setRed(value) {
if (value < 0 || value > 255) {
throw new RangeError("Invalid R value");
}
this.#values[0] = value;
}
}
const red = new Color(255, 0, 0);
red.setRed(1000); // RangeError: Invalid R value
If we leave the values property exposed, our users can easily circumvent that check by assigning to values[0] directly, and create invalid colors. But with a well-encapsulated API, we can make our code more robust and prevent logic errors downstream.
A class method can read the private fields of other instances, as long as they belong to the same class.
js
class Color {
#values;
constructor(r, g, b) {
this.#values = [r, g, b];
}
redDifference(anotherColor) {
// #values doesn't necessarily need to be accessed from this:
// you can access private fields of other instances belonging
// to the same class.
return this.#values[0] - anotherColor.#values[0];
}
}
const red = new Color(255, 0, 0);
const crimson = new Color(220, 20, 60);
red.redDifference(crimson); // 35
However, if anotherColor is not a Color instance, #values won't exist. (Even if another class has an identically named #values private field, it's not referring to the same thing and cannot be accessed here.) Accessing a nonexistent private element throws an error instead of returning undefined like normal properties do. If you don't know if a private field exists on an object and you wish to access it without using try / catch to handle the error, you can use the in operator.
js
class Color {
#values;
constructor(r, g, b) {
this.#values = [r, g, b];
}
redDifference(anotherColor) {
if (!(#values in anotherColor)) {
throw new TypeError("Color instance expected");
}
return this.#values[0] - anotherColor.#values[0];
}
}
Note:
Keep in mind that the # is a special identifier syntax, and you can't use the field name as if it's a string. "#values" in anotherColor would look for a property name literally called "#values" , instead of a private field.
There are some limitations in using private elements: the same name can't be declared twice in a single class, and they can't be deleted. Both lead to early syntax errors.
js
class BadIdeas {
#firstName;
#firstName; // syntax error occurs here
#lastName;
constructor() {
delete this.#lastName; // also a syntax error
}
}
Methods, getters, and setters can be private as well. They're useful when you have something complex that the class needs to do internally but no other part of the code should be allowed to call.
For example, imagine creating HTML custom elements that should do something somewhat complicated when clicked/tapped/otherwise activated. Furthermore, the somewhat complicated things that happen when the element is clicked should be restricted to this class, because no other part of the JavaScript will (or should) ever access it.
js
class Counter extends HTMLElement {
#xValue = 0;
constructor(
Links found on this page
- Skip to main content [direct]
- HTML: Markup language [direct]
- Elements [direct]
- Global attributes [direct]
- Attributes [direct]
- See all… [direct]
- Responsive images [direct]
- HTML cheatsheet [direct]
- Date & time formats [direct]
- See all… [direct]
- SVG [direct]
- MathML [direct]
- XML [direct]
- CSS: Styling language [direct]
- Properties [direct]
- Selectors [direct]
- At-rules [direct]
- Values [direct]
- See all… [direct]
- Box model [direct]
- Animations [direct]
- Flexbox [direct]
- Colors [direct]
- See all… [direct]
- Column layouts [direct]
- Centering an element [direct]
- Card component [direct]
- See all… [direct]
- JavaScript: Scripting language [direct]
- Standard built-in objects [direct]
- Expressions & operators [direct]
- Statements & declarations [direct]
- Functions [direct]
- See all… [direct]
- Control flow & error handing [direct]
- Loops and iteration [direct]
- Working with objects [direct]
- See all… [direct]
- Web APIs: Programming interfaces [direct]
- File system API [direct]
- Fetch API [direct]
- Geolocation API [direct]
- HTML DOM API [direct]
- Push API [direct]
- Service worker API [direct]
- Using the Web animation API [direct]
- Using the Fetch API [direct]
- Working with the History API [direct]
- Using the Web speech API [direct]
- Using web workers [direct]
- All web technology [direct]
- Accessibility [direct]
- HTTP [direct]
- URI [direct]
- Web extensions [direct]
- WebAssembly [direct]
- WebDriver [direct]
- Media [direct]
- Performance [direct]
- Privacy [direct]
- Security [direct]
- Progressive web apps [direct]
- Learn web development [direct]
- Getting started modules [direct]
- Core modules [direct]
- MDN Curriculum [direct]
- Check out the video course from Scrimba, our partner [direct]
- Structuring content with HTML module [direct]
- CSS styling basics module [direct]
- CSS layout module [direct]
- Dynamic scripting with JavaScript module [direct]
- Playground [direct]
- HTTP Observatory [direct]
- Border-image generator [direct]
- Border-radius generator [direct]
- Box-shadow generator [direct]
- Color format converter [direct]
- Color mixer [direct]
- Shape generator [direct]
- About MDN [direct]