No Ways to Have Class-Based Objects in JavaScript

What techniques can be used to define a class in JavaScript, and what are their trade-offs?

Here's the way to do it without using any external libraries:

// Define a class like this
function Person(name, gender){

// Add object properties like this
this.name = name;
this.gender = gender;
}

// Add methods like this. All Person objects will be able to invoke this
Person.prototype.speak = function(){
alert("Howdy, my name is" + this.name);
};

// Instantiate new objects with 'new'
var person = new Person("Bob", "M");

// Invoke methods like this
person.speak(); // alerts "Howdy, my name is Bob"

Now the real answer is a whole lot more complex than that. For instance, there is no such thing as classes in JavaScript. JavaScript uses a prototype-based inheritance scheme.

In addition, there are numerous popular JavaScript libraries that have their own style of approximating class-like functionality in JavaScript. You'll want to check out at least Prototype and jQuery.

Deciding which of these is the "best" is a great way to start a holy war on Stack Overflow. If you're embarking on a larger JavaScript-heavy project, it's definitely worth learning a popular library and doing it their way. I'm a Prototype guy, but Stack Overflow seems to lean towards jQuery.

As far as there being only "one way to do it", without any dependencies on external libraries, the way I wrote is pretty much it.

Can JavaScript be considered a class-based language?

Javascript is an object oriented language and always has been.

The new ES6 class syntax merely provides a prettier way to declare something that you could already do in the language before ES6 (you just had to manually configure somethings that are now done for you).

The new ES6 class syntax still uses the prototype in exactly the same way that objects in ES5 and before did. So, there is really nothing new under the covers. ES6 codifies into actual language syntax something that most developers were already doing (defining objects, inheriting from other object definitions, etc...).

So, the ES6 class syntax really just makes things more convenient for the developer. It does not fundamentally change what Javascript is capable of or how it works.

Before ES6, javascript had no way to create classes (I know it has ts own way of creating them which are constructors....) and it is a prototype-based language.

This statement is a bit misguided. ES6 still works the same way ES5 did. It still uses the prototype. Object definitions from the class keyword are created the same way they were in ES5 (using the prototype). We just now have a built-in syntax rather than declaring a constructor manually and then adding methods to the prototype. So, there are syntax improvements, but not fundamental changes in how it works once the object is declared.

Now, after adding class declarations and new inheritance ways can it be considered a class-based language like Java and C++?

Not really. It's still a prototype-based language and even the class declaration just ends up building a prototype. I would say Javascript is very object-oriented (always has been). I would not attempt to compare it to Java and C++ in this regard because it's kind of like comparing apples and oranges. They go about things differently and there's no absolute measure of "more" or "less" class-based that is objective or useful.

I would recommend spending time on the strengths and weaknesses of different languages you know or are considering learning so you understand what they can best be used for and don't spend any time trying to figure out which is "more" or "better" than the other. They are very different and can all be used to do a wide variety of things. Some are better tools in the toolkit for some things vs. others based on their individual strengths.

Does JavaScript have classes?

Technically, the statement "JavaScript has no classes" is correct.

Although JavaScript is object-oriented language, it isn't a class-based language—it's a prototype-based language. There are differences between these two approaches, but since it is possible to use JavaScript like a class-based language, many people (including myself) often simply refer to the constructor functions as "classes".

How to get a JavaScript object's class?

There's no exact counterpart to Java's getClass() in JavaScript. Mostly that's due to JavaScript being a prototype-based language, as opposed to Java being a class-based one.

Depending on what you need getClass() for, there are several options in JavaScript:

  • typeof
  • instanceof
  • obj.constructor
  • func.prototype, proto.isPrototypeOf

A few examples:

function Foo() {}
var foo = new Foo();

typeof Foo; // == "function"
typeof foo; // == "object"

foo instanceof Foo; // == true
foo.constructor.name; // == "Foo"
Foo.name // == "Foo"

Foo.prototype.isPrototypeOf(foo); // == true

Foo.prototype.bar = function (x) {return x+x;};
foo.bar(21); // == 42

Note: if you are compiling your code with Uglify it will change non-global class names. To prevent this, Uglify has a --mangle param that you can set to false is using gulp or grunt.

Is JavaScript object-oriented?

IMO (and it is only an opinion) the key characteristic of an object orientated language would be that it would support polymorphism. Pretty much all dynamic languages do that.

The next characteristic would be encapsulation and that is pretty easy to do in Javascript also.

However in the minds of many it is inheritance (specifically implementation inheritance) which would tip the balance as to whether a language qualifies to be called object oriented.

Javascript does provide a fairly easy means to inherit implementation via prototyping but this is at the expense of encapsulation.

So if your criteria for object orientation is the classic threesome of polymorphism, encapsulation and inheritance then Javascript doesn't pass.

Edit: The supplementary question is raised "how does prototypal inheritance sacrifice encapsulation?" Consider this example of a non-prototypal approach:-

function MyClass() {
var _value = 1;
this.getValue = function() { return _value; }
}

The _value attribute is encapsulated, it cannot be modified directly by external code. We might add a mutator to the class to modify it in a way entirely controlled by code that is part of the class. Now consider a prototypal approach to the same class:-

function MyClass() {
var _value = 1;
}
MyClass.prototype.getValue = function() { return _value; }

Well this is broken. Since the function assigned to getValue is no longer in scope with _value it can't access it. We would need to promote _value to an attribute of this but that would make it accessable outside of the control of code written for the class, hence encapsulation is broken.

Despite this my vote still remains that Javascript is object oriented. Why? Because given an OOD I can implement it in Javascript.

ES6 class variable alternatives

2018 update:

There is now a stage 3 proposal - I am looking forward to make this answer obsolete in a few months.

In the meantime anyone using TypeScript or babel can use the syntax:

varName = value

Inside a class declaration/expression body and it will define a variable. Hopefully in a few months/weeks I'll be able to post an update.

Update: Chrome 74 now ships with this syntax working.


The notes in the ES wiki for the proposal in ES6 (maximally minimal classes) note:

There is (intentionally) no direct declarative way to define either prototype data properties (other than methods) class properties, or instance property

Class properties and prototype data properties need be created outside the declaration.

Properties specified in a class definition are assigned the same attributes as if they appeared in an object literal.

This means that what you're asking for was considered, and explicitly decided against.

but... why?

Good question. The good people of TC39 want class declarations to declare and define the capabilities of a class. Not its members. An ES6 class declaration defines its contract for its user.

Remember, a class definition defines prototype methods - defining variables on the prototype is generally not something you do.
You can, of course use:

constructor(){
this.foo = bar
}

In the constructor like you suggested. Also see the summary of the consensus.

ES7 and beyond

A new proposal for ES7 is being worked on that allows more concise instance variables through class declarations and expressions - https://esdiscuss.org/topic/es7-property-initializers

Coding patterns for initializing objects - constructors (new) vs. Object.create() (Crockford)

Instead of having:

function Person(first, last) {
this.name = {
first: first,
last: last
};
}

Person.prototype.tellName = function() {
return this.name.first + ' ' + this.name.last;
}

You'd just have:

function Person(first, last) {
return {
name: { first: first, last: last },
tellName: function() { return this.name.first + ' ' + this.name.last; }
};
};

Or, if you prefer how person.create() looks, then:

var person = {
create: function(first, last) {
return {
name: { first: first, last: last },
tellName: function() { return this.name.first + ' ' + this.name.last; }
};
}
};

But in the second case you'd have an unnecessary object (person) containing only one function (person.create()).

No need for Object.create nor new since those are for inheritance which you said you do not care about. This would let you do:

var p1 = Person('John', 'Doe');
var p2 = Person('Sven', 'Svensson');

A fun fact is that you can still use new in front of person.create this way if you want but it would offer no effect. If you have to use the existing function you can set the this context explicitly by using .call

// with your original `Person`
var p1 = Person.call({}, 'John', 'Doe');
var p2 = Person.call({}, 'Sven', 'Svensson');

This would not set the prototype either since the function is not called like a constructor. See this answer about what prototypical answer does and doesn't do - in a line it's about sharing functionality it's not about constructing properties of your objects.

Private properties in JavaScript ES6 classes

Private class features is now supported by the majority of browsers.

class Something {
#property;

constructor(){
this.#property = "test";
}

#privateMethod() {
return 'hello world';
}

getPrivateMessage() {
return this.#property;
}
}

const instance = new Something();
console.log(instance.property); //=> undefined
console.log(instance.privateMethod); //=> undefined
console.log(instance.getPrivateMessage()); //=> test
console.log(instance.#property); //=> Syntax error

Angular class members are somehow common to all objects

I think if you just simply change it like this your problem will be solved,

define([], function(){
function MyClass(Dependency1, Dependency2){
var private1;
// Ctor
function MyClass(param1){
this.private1 = param1; //look how I access the object's property
}
}

MyClass.prototype.someMethod(){...}
}

Anyway, If I didn't get your intention or ... a JSfiddle where we can see the problem, would be awesome.



Related Topics



Leave a reply



Submit