With the release of ECMAScript 6 on June 2015, Javascript classes syntax was introduced.
This syntax:
class Polygon {
constructor(width, height) {
this.width = width;
this.height = height;
}
}
is basically same as:
function Polygon(width, height) {
this.width = width;
this.height = height;
}
So what is the benefit of using class instead of traditional function?
And in what condition I should use class instead of function?
There are some differences between Class and Function - most people will start by saying that the Class is "just syntax sugar", but that sugar does matter quite a bit. When the JS parser is processing the JavaScript code the parser will save them in different AST nodes, like shown here the ClassDeclaration and ClassExpression are different node types in the resulting AST tree:
https://github.com/estree/estree/blob/master/es2015.md#classes
You can see that for this parser, the new ES6 Classes spec introduces a number of new AST elements to the syntax:
ClassBody
MethodDefinition
ClassDeclaration
ClassExpression
MetaProperty
Since the AST syntax is not standard, there can be more or less types depending on the parser, but what is important to notice that when the code enters the class declaration or class expression it will be interpreted differently by the JavaScript engine.
This means, that Class and Function declarations can not be exchanged. You can see this if you try to write
class notWorking {
return 1; // <-- creates a parser error
};
This is because when the parser encounters the class -keyword, it will start treating the following code as ClassBody of either ClassDeclaration or ClassExpression and then it expects to find MethodDefinitions.
This is a small problem, because creating private variables becomes a bit more challenging. The function declaration could define a private variable neatly like this:
function myClass() {
var privateVar;
}
The class declaration can not have this:
class myClass {
var privateVar; // ERROR: should be a method
}
This is because the syntax of class allows only methods to be declared inside the class body. At least right now.
However, there exists a proposal for creating private fields:
https://github.com/zenparsing/es-private-fields
Thus, in the future you might be able to say
class myClass {
#privateVar; // maybe this works in the future?
}
There is a separate answer considering the private properties in ES6 Classes, which is suggesting some workarounds, like the use of Symbols:
Private properties in JavaScript ES6 classes
var property = Symbol(); // private property workaround example
class Something {
constructor(){
this[property] = "test";
}
}
Naturally there are more differences between classes and functions. One of them is Hoisting 1 - unlike Functions, you can't declare the Class anywhere in the scope:
An important difference between function declarations and class
declarations is that function declarations are hoisted and class
declarations are not. You first need to declare your class and then
access it
The Class declarations and Function declarations are quite similar;
function foo1() {} // can be used before declaration
class foo2{} // new foo2(); works only after this declaration
The class expressions work quite similarly to function expressions, for example they can be assigned to a variable:
var myClass = class foobar {};
More differences are 1
The Class expression / declaration body is always executed in Strict mode - no need to specify that manually
Classes have special keyword constructor - there can be only one of them, or error is thrown. Functions could have multiple definitions of variable of function named "constructor"
Classes have special keyword super which relates to the parent classes constructor. If you are inside the constructor you can call super(x, y); to call the parent class constructor but inside the Method you can call super.foobar() to create call to any parent class function. This kind of functionality is not available for standard Functions although you might emulate it with some custom hacking.
Inside class body you can define function with static keyword so it can be called using only ClassName.FunctionName() -syntax.
Both class declarations and expressions can use extends keyword like class Dog extends Animal
MethodDeclaration does not need function -prefix, thus you can define function "ok" inside the class "m" like this: class m { ok() { } }. Actually it is not even allowed to define function as class m { function ok() { } }
However, after the parser has completed it's job, the class instance is essentially running the same way as any other object.
The new ES6 Class syntax is essentially more clear way of expressing objects in a traditional OOP way and if you like it, then you should use it.
EDIT: also, the ES6 Class syntax has also another limitation: it does not allow the member functions to use lexically binded using fat arrow. ES7 seems to have experimental feature allowing it. That can be useful for example when binding methods to event handlers, the related question is here.
1 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
class its nothing but a syntactical sugar over javascript logic class creation using function. if you are using a function as class the entire function is act as a constructor, if you want to put other member functions you need to do that in constructor like this.something = ..., or var something = ... in case of private members (if you are not injecting from outside, assume you are creating object with other methods / properties), but in case of class the entire function is not actually act a constructor you can explicitly separate it with other member functions and data.
Related
I am new to learning objects and classes in Javascript. I was just wondering, why would you attach a static method to a class, like so:
class MyClass {
static myFunction(){
console.log('foo');
}
}
When you can just declare a regular, custom function outside of the class like one usually does?
function myFunction() {
console.log('foo');
}
A static "method" is just a regular function that is attached to a class. This is useful when it belongs to that class semantically, and in extreme cases necessary in an inheritance hierarchy. The class name provides a visual namespace for accessing the function, e.g. Map.from does something different than Set.from.
However, you would only ever do that when you already have an existing class. You would never create an empty class only to put a static method inside it. In such a case, a simple object literal with a regular object method suffices:
const MyObject = {
myFunction() {
console.log('foo');
},
};
MyObject.myFunction();
Mostly for namespacing. Imagine you need 10 functions of related feature. Instead of taking up 10 names from global scope, using static methods you use just one global name, being the class name.
Remember that apps often end up being complex, using 3rd party libraries. Name clashes is a real problem when complexity comes to play.
My initial page load instantiates PageInit as follows.
<script>
"use strict";
let PageInit = null;
document.addEventListener("DOMContentLoaded", () => {
PageInit = new IPageInit();
});
</script>
I had to let PageInit=null in order to put that reference into the global name space to make it visible. I suspect that is wrong, because I can't imagine that every class that is instantiated has to be done so in global. (??) So, guidance on the right way to do this is appreciated also.
PageInit(), once it is created has a constructor that calls a class method, which in turns creates an instance of another class:
class IPageInit {
constructor() {
this.InitialiseComponents();
}
//Class Method
InitialiseComponents() {
this.PageContent = new IPageContent();
}
}
Coming from a C# background, I do not create an instance of a class with the class's exact name, as you see above. The class name is "IPageContent", and the instantiated name is just "PageContent". (Again, guidance on the right naming conventions is appreciated.)
PageContent(), when it is instantiated has a constructor that itself creates a property called "TrafficData".
class IPageContent {
constructor() {
this.TrafficData = "Paragraph 13.5";
}
}
Later, in "loose javascript" code on the page, a function tries to make a reference to TrafficData. I've tried two ways:
// page javascript
function LoadTrafficData {
let x = PageContent.TrafficData; // PageContent (itself) is undefined
let x = PageInit.TrafficData; // PageInit is valid object, but TrafficData is undefined
}
As you see, neither way works. I do not know enough about scope and visibility in instantiated JavaScript classes to figure out what is wrong and what to do about it. I am using plain vanilla JS - no frameworks, APIs, or Libraries.
How do I get LoadTrafficData() (a loose function, not a class method) to get a reference to TrafficData?
You are correct that global variables are generally a bad practice. Modern js uses imports to import classes into other classes. For example, you might have a PageService singleton class that creates an instance of your PageContent class and holds it in a class property. Other classes could import your PageService singleton to use its properties.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
Your naming conventions are a bit unusual for JS as well:
Class names are normally capitalized, like PageContent, and instances are lowercase, like pageContent.
Class methods and functions should start with lowercase letters.
In typescript, you would prefix interfaces with an I, like IPageInterface, but you wouldn't normally prefix class names in regular javascript.
To debug your stuff, it's common to do console.log(myVariable) to inspect things. You can also insert a debugger; statement in your code to launch chrome's debugger at that line. To access your global var, you would do: PageInit.PageContent.TrafficData.
class StaticMethodCall {
static staticMethod() {
return 'Static method has been called';
}
static anotherStaticMethod() {
return this.staticMethod() + ' from another static method';
}
}
StaticMethodCall.staticMethod();
// 'Static method has been called'
StaticMethodCall.anotherStaticMethod();
// 'Static method has been called from another static method'
Why this work ? I expected fatal error in this case before.
this in JS is not the same as this in the likes of an actual OO language like Java etc. Where this in the likes of java refers to the current object of a class, in JS there are no 'classes', this in JS just refers to a current context scope, which may be, in your case, a 'static' method on a 'class', which will essentially boil down to being the current scope of a plain old object
When you call a function via a reference like someobject.property(), the this value will be set to someobject. That's a basic feature of the language. Static methods are properties of the constructor function, so when you call them via a reference to the constructor the value of this will be that function.
In languages like Java, the static methods belong to a class rather than an instance so when you use this inside static method you get an compilation error as the method doesn't know which instance to refer to (as there could be many instances of the class).
But the class is just a syntactic sugar for the JavaScript function object, so when you call StaticMethodCall.anotherStaticMethod(); here this will point to the context of the StaticMethodCall class object which is nothing but a function object under the hood. JavaScript functions are also objects which can have properties declared on them. So when you use a constructor function (es6 class) you could declare properties on the function object which is nothing but the static in es6.
Let me explain this way, the following code is what is happening under the hood if you transpile it to es5 through Babel:
//This is what static means in es6 class. they are properties of the constructor function
function StaticMethodCall(){ //This represents your class
}
StaticMethodCall.staticMethod = function(){
return 'Static method has been called';
}
StaticMethodCall.anotherStaticMethod = function(){
return this.staticMethod() + ' from another static method';
}
console.log(StaticMethodCall.staticMethod());
// StaticMethodCall is a reference to the class object, this points to.
console.log(StaticMethodCall.anotherStaticMethod());
Here when you define static methods on es6 classes the methods are added as properties of the constructor function so when you call the static method with the reference of the class StaticMethodCall this is actually pointing to that class/function object.
So this is not the same as you would expect in Java, where you cannot refer to this inside a static context as it does not know which object to refer to (as classes are not objects). In Java, classes are not actually objects like JavaScript.
Another way to verify that es6 class is actually a function is by using the typeof:
class StaticMethodCall{
}
console.log(typeof StaticMethodCall); //outputs "function"
To understand it better you should check the transpiled code from
Babel
the scope of static functions is the same scope of the class (that compiled is a Constructor function).
Static functions are declared as properties on the Constructor function, whether normal functions are declared as properties on the Constructor.prototype and are available consequently on all the instances.
I have been working with JS for a few months now and I stumbled upon this interesting (at least to me) thing.
Take a look at this code:
class myClass
{
constructor()
{
this.x = 0;
}
}
function myFunc(){}
myClass.myFunc = myFunc;
I can call myClass.myFunc(), but myClass is just a function. In fact, I could rewrite it as
function myClass()
{
this.x = 0;
}
and it would behave the same and I would still be able to call myClass.myFunc(). So what actually is a function (like, "under the hood")? It seems to behave more like an object that can be called if it makes any sense, thus being able to have fields of its own.
Also, is this considered bad practice?
In node I have
module.exports = myClass;
module.exports.myFunc = myFunc;
Is this okay to do? Or is this frowned upon? What are the cons, if any?
EDIT: I'm not asking how to have a static method, I'm asking why this happens.
In JavaScript a function is an actual value. Values a categorized by data type (a data type describes a possible set of values). JavaScript has the following data types:
Boolean
Number
String
Undefined
Null
Symbol
Object
A function is obviously neither of the first 6, it is indeed an Object. And as such it can have arbitrary properties, just like any other object.
What makes a function object special?
Per specification objects have "internal methods". You can think of them as being just like normal properties except that they cannot be accessed in user code. Only the JavaScript engine can access them.
Function objects have an internal method called [[Call]] and an internal slot called [[ECMAScriptCode]]. The latter contains a parsed representation of the function body and is evaluated when [[Call]] is invoked (which happens in a call expression (foo())).
Specifically for classes, or rather the functions created through class syntax, there is also the internal [[Construct]] method. It works very similar to [[Call]] but is used when a function is invoked via the new keyword.
See also
What is the main core difference between a javascript function and javascript object?
If functions are objects, where does the function's body go? (referring to an earlier version of the spec)
Why the property "prototype" is absent in definition methods of ES6 classes
You can use static method:
class myClass
{
constructor()
{
this.x = 0;
}
static myFunc(){
return 'Hello';
}
}
module.exports = myClass
or simply export a new instance of myClass:
class myClass
{
constructor()
{
this.x = 0;
}
myFunc(){
return 'Hello';
}
}
module.exports = new myClass
In both approach you can treat myFunc as a static function:
const myclass = require('myClass');
myClass.myFunc();
// will print out 'Hello'
I am trying to figure out what is different between defining local/private properties and methods inside a class compared to WeakMap constructor?
is the purpose of hiding them from outside are the same or is there other thing to use them?
Given your current implementation:
Your let x isn't very useful, because it can only be referenced in the constructor. It can't be used in any of the prototype methods. The WeakMap, on the other hand, can be referenced anywhere. That's a pretty big difference.
Your WeakMap can be referenced anywhere. This means that it's not actually providing any privacy; anything with a reference to an instance and the WeakMap may access the radius value that you wanted to be private.
If you wanted the WeakMap to be properly private, via closures, you could use an IIFE to define the class, eg:
const myClass = (() => {
const weakMap = new WeakMap();
return class Circle {
// etc
})();
That way, the top level only has a reference to the myClass, and not to the weakMap.
I would use your let x in the constructor whenever a variable is only needed in the constructor - not necessarily for the sake of hiding data, but just because it has no use outside of the constructor. See Why is it good programming practice to limit scope?
Note that there is currently a proposal for class fields which will probably eventually be implemented, which includes syntax for private fields, eg:
class Circle {
constructor(radius) {
this.#x = radius;
// ...
This will allow #x to be accessed from anywhere within Circle, but nowhere else. It's functionally equivalent to the WeakMap implementation.