A class in a formal classist language can be an object, but it’s a special kind of object with special properties and methods. It is responsible for creating new instances and for defining the behaviour of instances.
class SmartShoe
def initialize(size, colour, type)
@size, @color, @type, @battery = size, colour, type, 50
end
end
# create an instance
NikeUltraSport = SmartShoe.new(10, 'gray', 'sport edition')
Instance behaviour in a classist language is defined with special syntax. If changes are allowed dynamically, they are done with special syntax and/or special methods invoked on the class.
class SmartShoe
def initialize(size, colour, type)
@size, @color, @type, @battery = size, colour, type, 50
end
# define an instance method recharge
def recharge(power_increase)
@battery = power_increase
@battery = 100 if @battery > 100
end
end
# create an instance
NikeUltraSport = SmartShoe.new(10, 'gray', 'sport edition')
# call instance method
NikeUltraSport.recharge(20)
JavaScript splits the responsibility for instances into a constructor and a prototype. A constructor in JavaScript can be any function. Constructors are responsible for creating new instances.
var SmartShoe = function(size, colour, type){
this.size = size, this.colour = colour, this.type = type, this.battery = 100;
}
// create an instance in the classical way
var NikeUltraSport = new SmartShoe(10, 'gray', 'sport edition');
// note: in JS no need to have attr_readers, NikeUltraSport.colour ==> 'gray'
A prototype in JavaScript can be any object. Prototypes are responsible for defining the behaviour of instances. prototypes don’t have special methods or properties.
Instance behaviour in JavaScript is defined by modifying the prototype directly, e.g. by adding functions to it as properties. There is no special syntax for defining a class or modifying a class.
var SmartShoe = function(size, colour, type){
this.size = size, this.colour = colour, this.type = type, this.battery = 100;
}
// define a prototype (~instance) method
SmartShoe.prototype.recharge = function(power_increase){
this.battery += power_increase;
(this.battery > 100) && this.battery = 100;
}
// to export to the class to other files
module.exports = SmartShoe;
The above examples try to apply classical OOP in Javascript prototypes but in reality it's simply a regular JS function. In Javascript, there are no real classes.
There are many ways to emulate classical OOP in JS but the method shown above is the simplest one and is built-in with the language.
Remember:
- Functions can have prototypes.
- Prototypes hold instance methods.
- Instances are new copies of the constructor function.
When writing Javascript, be careful when using the new
keyword, it overloads the meaning of functions and can have dangerous side effects if not used correctly.
For example, if we have a class Car
and we try to define a car instance by wrongly doing something like my_car = Car(2010, 'BMW')
, then this
inside of the function will global this
object instead of the new instance's this
. That will cause overriding of global variables.
Let's try doing this in a more Prototypal OOP way:
var Car = {
current_speed: 0,
current_direction: 'neutral',
drive: function(direction, speed){
this.current_direction = direction;
this.current_speed = speed;
}
};
// create an object new_car and inherit all properties of Car
var my_car = Object.create(Car);
my_car.drive(40, 'forward');
// my_car.current_speed ==> 40
// my_car.current_direction ==> 'forward'
What's ECMAScript?
class Javascript extends ECMAScript
ECMAScript is a subset of JavaScript. JavaScript is basically ECMAScript at it's core but builds upon it. Languages such as ActionScript, JavaScript, JScript all use ECMAScript as its core. As a comparison, AS/JS/JScript are 3 different cars, but they all use the same engine... each of their exteriors is different though, and there have been several modifications done to each to make it unique.Read Full Answer.
What do we mean when we say ES6?
We are referring to the latest standard released by ECMAScript.
Creating classes in ES6:
class SmartShoe{
constructor(size, colour, type){
this.size = size, this.colour = colour, this.type = type, this.battery = 100
}
recharge(power_increase){
this.battery += power_increase
(this.battery > 100) && this.battery = 100
}
}
// create an instance
let nike_ultra_sport = new SmartShoe(10, 'gray', 'sport edition')
// call instance method
nike_ultra_sport.recharge(20)
Note about privacy:
Unfortunately, private object properties don’t exist in JavaScript. We have to fake them. The most common way to do that is to adhere to a simple convention: If a property name is prefixed with an underscore (or, less commonly, suffixed with an underscore), then it should be treated as non-public. We used this approach in the earlier code example. Generally this simple convention works, but the data is technically still accessible to everyone, so we have to rely on our own discipline to do the right thing.Full Article
One way to achieve privacy in a JS class is via privileged methods:
class SmartShoe{
constructor(size, colour, type){
let _size = size, _colour = colour, _type = type, _battery = 100
// _battery becomes accessible without writing this if a
// method is defined inside the constructor
this.recharge(power_increase){
_battery += power_increase
(_battery > 100) && _battery = 100
}
}
}
// create an instance
let nike_ultra_sport = new SmartShoe(10, 'gray', 'sport edition')
// call instance method
nike_ultra_sport.recharge(20)
There are other and better ways of doing this such as using Symbols
and Weak Maps
, both of which are ES6 features.
Static Properties:
class SmartShoe {
// static methods are similar to class methods in ruby
static setDefaults(size, colour, type) {
SmartShoe._defaultShoe = new SmartShoe(size, colour, type);
}
constructor(size, colour, type){
// if no input provided on `new` call
if (arguments.length === 0) {
this._size = SmartShoe._defaultShoe._size;
this._colour = SmartShoe._defaultShoe._colour;
this._type = SmartShoe._defaultShoe._type;
return;
}
// ... check validity of values provided here
// initialize properties for this smart shoe
this._size = size, this._colour = colour, this._type = type, this._battery = 100
// ... methods here
}
}
// call a class method (only one, not per instance)
SmartShoe.setDefaults(8, 'gray', 'regular')
// this instance of SmartShoe will have all the defaults
let default_shoe = new SmartShoe();
Anatomy of classes in ES6:
class MyClass {
constructor(prop_val) {
this._property = prop_val
}
static staticMethod() {
return `I'm a static method. There's only one of meeee!`
}
prototypeMethod() {
return 'oh so prototypical'
}
}
let an_instance = new MyClass(44)
Subclassing in ES6:
// Shoe is a superclass for SmartShoe
class Shoe{
constructor(size, colour, type){
this._size = size, this._colour = colour, this._type = type
}
}
// SmartShoe is a subclass of Shoe
class SmartShoe extends Shoe{
constructor(size, colour){
super(size, colour, 'smart shoe')
this._battery = 100
}
}
let my_smart_shoe = new SmartShoe(10.5, 'black')
-
Javascript is an object-oriented programming language that supports delegating inheritance based on prototypes.
-
Each object has a prototype property, which refers to another (regular) object.
-
Properties of an object are looked up from two places:
- the object itself (Obj.foo), and
- if the property does not exist, on the prototype of the object (Obj.prototype.foo).
-
Important: since this lookup is performed recursively (e.g. Obj.foo, Obj.prototype.foo, Obj.prototype.prototype.foo), each object can be said to have a prototype chain.
-
Assigning to an undefined property of an object will create that property on the object. Properties of the object itself take precedence over properties of prototypes.
-
New objects are created using a constructor, which is a regular function invoked using
new
-
The
new
constructor call (e.g.new Foo()
):- creates a new object,
- sets the prototype of that object to Foo.prototype and
- passes that as
this
to the constructor. (i.e. the dangerous part)
-
The delegating inheritance implemented in Javascript is different from "classical" inheritance: it is based on run time lookups from the prototype property rather than statically defined class constructs. The prototype chain lookup mechanism is the essence of prototypal inheritance.