In the code I am using a named class "Car". Now if I try to use like var carNew = new Car("ferrari");, then it's throwing me an error. So what is the use of named class expression in ES6?
'use strict';
var car = class Car{
constructor(model){
this.model = model;
}
}
var carNew = new car("ferrari");
console.log(carNew); //car {model: "ferrari"}
console.log(carNew.constructor == car); //true
In case of class expressions you can use class name internally, but not outside of the class:
const Foo = class Self {
static greetingPrefix() {
return 'Hello, ';
}
greeting() {
return Self.greetingPrefix() + 'World';
}
};
const foo = new Foo();
console.log(foo.greeting()); // "Hello, World"
console.log(Self); // throws "Uncaught ReferenceError: Self is not defined"
With the following code
var car = class Car{ /* ... */ }
var carNew = new Car("ferrari"); throws an error, why?
Car has not entered the scope because you've written a class expression, similar to using function in a function expression
var foo = function Bar() {};
foo; // defined
Bar; // undefined
var carNew = new car("ferrari"); works, why?
For the same reasoning as above, the car identifier is defined in your scope and points to the class expression
What is use of named or unnamed class expression [...]?
Lets look back at function again. So now think, if Bar wasn't defined in the scope we were working in, where was it defined?
Well, obviously we have foo.name === 'Bar', but we could also do
var foo = function Bar() {console.log(Bar)};
foo(); // Bar logged, we see Bar === foo here
Great, so we can reference Bar inside the function.
It is exactly the same with class, we can reference Car from within the class expression itself, letting us do recursive behaviours or copy static references to instances etc.
I've added such a reference to make it easy to see in the code below
var car = class Car {
constructor(model) {
this.model = model;
this.foo = Car; // the big C Car that was ReferenceErroring outside
}
};
var bar = new car();
console.log(bar.foo); // logs class Car (=== car)
The same use as of named / not named functions. Look at the functions behavior:
var a = function b() { console.log('b') }
>> undefined
a()
>> b
b()
>> Uncaught ReferenceError: b is not defined(…)
This is very similar to your case.
Where and why you would need to use it? Usually the only use case for functions is to name the functions called once which gives you kind of self-documenting ability:
(function bootstrap() {
//
})();
Here the function name says that your function bootstraps your app.
So I can imagine another case for the class now: you can use it for singleton classes which are used to create one object only:
var a = new (class Car{
constructor(model){
this.model = model;
}
})('ferrari');
console.log(a.model);
Here you just name the class to know what it is doing, however the name won't be accessible elsewhere.
It thrown an error because Car variable is not declared.
From JavaScript | MDN documentation :
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, otherwise code like the following will throw a
ReferenceError
So, to use :
var newCar = Car('ferrari');
You have to define the Car variable :
var Car = class Car{
constructor(model){
this.model = model;
}
}
Notice here that we use Car and not car
Concerning unamed and named class expression, here is an example from JavaScript | MDN documentation :
A class expression is another way to define a class. Class expressions
can be named or unnamed. The name given to a named class expression is
local to the class's body.
// unnamed
var Polygon = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
// named
var Polygon = class Polygon {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
Read ES6 classes - JavaScript | MDN for more information
Class expressions
Similarly to functions, there are two kinds of class definitions, two ways to define a class: class declarations and class expressions.
Also similarly to functions, the identifier of a class expression is only visible within the expression:
const MyClass = class Me {
getClassName() {
return Me.name;
}
};
const inst = new MyClass();
console.log(inst.getClassName()); // Me
console.log(Me.name); // ReferenceError: Me is not defined
Related
What is the difference between the following two declarations?
Class.method = function () { /* code */ }
Class.prototype.method = function () { /* code using this.values */ }
Is it okay to think of the first statement as a declaration of a static method, and the second statement as a declaration of an instance method?
Yes, the first function has no relationship with an object instance of that constructor function, you can consider it like a 'static method'.
In JavaScript functions are first-class objects, that means you can treat them just like any object, in this case, you are only adding a property to the function object.
The second function, as you are extending the constructor function prototype, it will be available to all the object instances created with the new keyword, and the context within that function (the this keyword) will refer to the actual object instance where you call it.
Consider this example:
// constructor function
function MyClass () {
var privateVariable; // private member only available within the constructor fn
this.privilegedMethod = function () { // it can access private members
//..
};
}
// A 'static method', it's just like a normal function
// it has no relation with any 'MyClass' object instance
MyClass.staticMethod = function () {};
MyClass.prototype.publicMethod = function () {
// the 'this' keyword refers to the object instance
// you can access only 'privileged' and 'public' members
};
var myObj = new MyClass(); // new object instance
myObj.publicMethod();
MyClass.staticMethod();
Yes, the first one is a static method also called class method, while the second one is an instance method.
Consider the following examples, to understand it in more detail.
In ES5
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.isPerson = function(obj) {
return obj.constructor === Person;
}
Person.prototype.sayHi = function() {
return "Hi " + this.firstName;
}
In the above code, isPerson is a static method, while sayHi is an instance method of Person.
Below, is how to create an object from Person constructor.
var aminu = new Person("Aminu", "Abubakar");
Using the static method isPerson.
Person.isPerson(aminu); // will return true
Using the instance method sayHi.
aminu.sayHi(); // will return "Hi Aminu"
In ES6
class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
static isPerson(obj) {
return obj.constructor === Person;
}
sayHi() {
return `Hi ${this.firstName}`;
}
}
Look at how static keyword was used to declare the static method isPerson.
To create an object of Person class.
const aminu = new Person("Aminu", "Abubakar");
Using the static method isPerson.
Person.isPerson(aminu); // will return true
Using the instance method sayHi.
aminu.sayHi(); // will return "Hi Aminu"
NOTE: Both examples are essentially the same, JavaScript remains a classless language. The class introduced in ES6 is primarily a syntactical sugar over the existing prototype-based inheritance model.
When you create more than one instance of MyClass , you will still only have only one instance of publicMethod in memory but in case of privilegedMethod you will end up creating lots of instances and staticMethod has no relationship with an object instance.
That's why prototypes save memory.
Also, if you change the parent object's properties, is the child's corresponding property hasn't been changed, it'll be updated.
For visual learners, when defining the function without .prototype
ExampleClass = function(){};
ExampleClass.method = function(customString){
console.log((customString !== undefined)?
customString :
"called from func def.");}
ExampleClass.method(); // >> output: `called from func def.`
var someInstance = new ExampleClass();
someInstance.method('Called from instance');
// >> error! `someInstance.method is not a function`
With same code, if .prototype is added,
ExampleClass.prototype.method = function(customString){
console.log((customString !== undefined)?
customString :
"called from func def.");}
ExampleClass.method();
// > error! `ExampleClass.method is not a function.`
var someInstance = new ExampleClass();
someInstance.method('Called from instance');
// > output: `Called from instance`
To make it clearer,
ExampleClass = function(){};
ExampleClass.directM = function(){} //M for method
ExampleClass.prototype.protoM = function(){}
var instanceOfExample = new ExampleClass();
ExampleClass.directM(); ✓ works
instanceOfExample.directM(); x Error!
ExampleClass.protoM(); x Error!
instanceOfExample.protoM(); ✓ works
****Note for the example above, someInstance.method() won't be executed as,
ExampleClass.method() causes error & execution cannot continue.
But for the sake of illustration & easy understanding, I've kept this sequence.****
Results generated from chrome developer console & JS Bin
Click on the jsbin link above to step through the code.
Toggle commented section with ctrl+/
A. Static Method:
Class.method = function () { /* code */ }
method() here is a function property added to an another function (here Class).
You can directly access the method() by the class / function name. Class.method();
No need for creating any object/instance (new Class()) for accessing the method(). So you could call it as a static method.
B. Prototype Method (Shared across all the instances):
Class.prototype.method = function () { /* code using this.values */ }
method() here is a function property added to an another function protype (here Class.prototype).
You can either directly access by class name or by an object/instance (new Class()).
Added advantage - this way of method() definition will create only one copy of method() in the memory and will be shared across all the object's/instance's created from the Class
C. Class Method (Each instance has its own copy):
function Class () {
this.method = function () { /* do something with the private members */};
}
method() here is a method defined inside an another function (here Class).
You can't directly access the method() by the class / function name. Class.method();
You need to create an object/instance (new Class()) for the method() access.
This way of method() definition will create a unique copy of the method() for each and every objects created using the constructor function (new Class()).
Added advantage - Bcos of the method() scope it has the full right to access the local members(also called private members) declared inside the constructor function (here Class)
Example:
function Class() {
var str = "Constructor method"; // private variable
this.method = function () { console.log(str); };
}
Class.prototype.method = function() { console.log("Prototype method"); };
Class.method = function() { console.log("Static method"); };
new Class().method(); // Constructor method
// Bcos Constructor method() has more priority over the Prototype method()
// Bcos of the existence of the Constructor method(), the Prototype method
// will not be looked up. But you call it by explicity, if you want.
// Using instance
new Class().constructor.prototype.method(); // Prototype method
// Using class name
Class.prototype.method(); // Prototype method
// Access the static method by class name
Class.method(); // Static method
With ES6, JavaScript has proper class syntax, allowing you to declare them either as class A {} or const A = class {}. The latter are class expressions.
I'm working on a project where everything is customizable. The user will create a new file with the same name except with a prefix, and anything in their file with overwrite that of the original. However, it seems as though you cannot do this with the constructor of a class expression.
const A = class {};
A.constructor = function(name) {
this.name = name;
}
You would expect the above to work, (or even A.prototype.constructor) but it doesn't.
How does one properly define a constructor in this manner? I can't have it within the class declaration.
class syntax creates a constructor function and fills in the object assigned to its prototype property. There isn't a "class" separate from the constructor function. In your example, A is a reference to the constructor function created by the class {} expression. While you can replace the value of the constructor property on the object assigned to A.prototype, that won't change what function A refers to, and so won't change what happens via new A.
You can set A.prototype.constructor, and then give class A a constructor that defers to the one defined on the prototype if it's not the same function anymore, forwarding any arguments that were passed. That creates markedly unusual classes (normally, A.prototype.constructor === A is true), but does what you need it to do.
const A = class {
constructor(...args) {
let Constructor = A.prototype.constructor
if (Constructor !== A) {
Constructor.apply(this, args);
}
}
};
(A.prototype.constructor = function(name) {
this.name = name;
}).prototype = A
console.log(
new A('It works!')
)
Edit: Another option, which avoids messing with A.prototype.constructor, would be to have a static Constructor property defined on A that you can change to your liking:
const A = class {
static Constructor = Function.prototype
constructor(...args) {
A.Constructor.apply(this, args);
}
};
A.Constructor = function(name) {
this.name = name;
}
console.log(
new A('It works!')
)
Since ES6 classes are just a syntactical sugar over JavaScript's existing prototype-based inheritance [1] it would (IMO) make sense to hoist it's definition:
var foo = new Foo(1, 2); //this works
function Foo(x, y) {
this.x = x;
this.y = y;
}
But the following won't work:
var foo = new Foo(1, 2); //ReferenceError
class Foo {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
Why are ES6 classes not hoisted?
Why are ES6 classes not hoisted?
Actually they are hoisted (the variable binding is available in the whole scope) just like let and const are - they only are not initialised.
It would make sense to hoist its definition
No. It's never a good idea to use a class before its definition. Consider the example
var foo = new Bar(); // this appears to work
console.log(foo.x) // but doesn't
function Bar(x) {
this.x = x || Bar.defaultX;
}
Bar.defaultX = 0;
and compare it to
var foo = new Bar(); // ReferenceError
console.log(foo.x);
class Bar {
constructor (x = Bar.defaultX) {
this.x = x;
}
}
Bar.defaultX = 0;
which throws an error as you would expect. This is a problem for static properties, prototype mixins, decorators and everything. Also it is quite important for subclassing, which broke entirely in ES5 when you used a class with its non-adjusted prototype, but now throws an error if an extended class is not yet initialised.
While non-hoisted classes (in the sense that they behave like let bindings) can be considered preferable as they lead to a safer usage (see Bergi's answer), the following explanation found on the 2ality blog seems to provide a slightly more fundamental reason for this implementation:
The reason for this limitation [non-hoisting] is that classes can have an extends clause whose value is an arbitrary expression. That expression must be evaluated in the proper “location”, its evaluation can’t be hoisted.
In Javascript all declarations (var, let, const, function, function*, class) are hoisted but it should be declared in same scope.
As you told "ES6 classes are just a syntactical sugar over JavaScript's existing prototype-based inheritance"
So Let's understand what it is?
Here you declared a class which is in fact "special function".Let's assume that your function Foo() and class Foo both are in global scope.
class Foo {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
Following is the compiled code of your class Foo.
var Foo = (function () {
function Foo(x, y) {
this.x = x;
this.y = y;
}
return Foo;
}());
Internally your class is converted to function with the same name inside wrapper function(iife) and that wrapper function returns your function.
Because your function's(class) scope is changed. and you are trying to create object of function in global scope which is in reality not exist.
you get the function in variable Foo once compilation comes to that. so later you have function in var you can create object of that.
Classes are not hoisted because, for example when a class extends an expression rather than a function, error occurs:
class Dog extends Animal {}
var Animal = function Animal() {
this.move = function () {
alert(defaultMove);
}
}
var defaultMove = "moving";
var dog = new Dog();
dog.move();
After hoisting this will become:
var Animal, defaultMove, dog;
class Dog extends Animal {}
Animal = function Animal() {
this.move = function () {
alert(defaultMove);
}
}
defaultMove = "moving";
dog = new Dog();
dog.move();
Such at the point where class Dog extends Animal is interpreted Animal is actually undefined and we get an error. We can easily fix that by moving the Animal expression before the declaration of Dog.
Pls see this great article about the topic:
https://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html
Okay, so I want to create a constructor function in javascript which will count the total number of instances which are created using this constructor.
var Component = function(name) {
this.name = name;
this.add = function(){this.prototype.countInstances++;};
this.add();
};
and
Component.prototype.countInstances=0;
As I understand it correctly, the countInstances variable is added to the prototype and will act like a static copy for all the instances and will act as my counter.
The problem with this code is that since I'm declaring the countInstances after the constructor, I'm getting an error in the constructor code itself. How to correct this??
If you'd like a property to be attached to the class itself, and not instances of the class, you don't want to add the property to prototype:
var Component = function(name) {
this.name = name;
Component.instanceCount++;
};
Component.instanceCount = 0;
This way, you're assigning each name to its instance, and the total instance count to the static class:
var foo = new Component('bar');
var baz = new Component('qux');
console.info(foo.name, baz.name, Component.instanceCount);
>> 'bar', 'qux', 2
As I understand it correctly, the countInstances variable is added to the prototype and will act like a static copy for all the instances and will act as my counter.
No, it will be, in effect, a default value for instances, not a "static." If you put it on Component.prototype, all instances will inherit it via the prototype chain, but changing it via an instance will give that instance its own copy of it. Example:
var Foo = function() {
};
Foo.prototype.bar = 0;
var f1 = new Foo();
var f2 = new Foo();
console.log(f1.bar, f2.bar); // 0, 0 -- both are still using the `bar` on the prototype
++f1.bar;
console.log(f1.bar, f2.bar); // 1, 0 -- f1 now has its own
Foo.prototype.bar += 2;
console.log(f1.bar, f2.bar); // 1, 2 -- f2 is still using the `bar` on the prototype
The problem with this code is that since I'm declaring the countInstances after the constructor, I'm getting an error in the constructor code itself. How to correct this??
No, the problem is that your instances have no this.prototype object. The prototype property on the function is not copied as a prototype property on instances; it's assigned to them as their prototype, which (somewhat confusingly) isn't called prototype. For a long time it didn't have a name outside the spec at all. You can access it via Object.getPrototypeOf(this) or (this will be standard for browser-based JavaScript as of the next spec) the __proto__ property.
But putting it on the prototype probably doesn't make sense. I'd just use a property on the function itself:
var Component = function(name) {
this.name = name;
this.add = function(){Component.instances++;};
this.add();
};
Component.instances = 0;
But you said you wanted to count the number of objects created by the constructor; the above counts the number of times the add method is called. To count the number of instances created by the constructor, increment it in the constructor:
var Component = function(name) {
Component.instances++;
this.name = name;
this.add = function(){/*Presumably you're doing something here*/};
this.add();
};
Component.instances = 0;
var ComponentCounter = 0;
var Component = function(name) {
this.name = name;
this.add = function(){this.prototype.countInstances++;};
this.add();
ComponentCounter++;
// or Component.counter++;
};
// or add it as a property of Component after it has been defined
// Component.counter = 0;
Variables in the prototype belong to the instance so you have to keep track of that data on a variable that is persisted between instances.
We will be able to do the same by using:
function component() {
if(component.prototype.counter) { component.prototype.counter = 0; }
component.prototype.counter++;
this.add = function(){ /*... do something here....*/ }
}
By initiating counter inside the function body, we will be able to keep the count (number of times function called).
What is the best way to define a class? I am aware of the fact that this is most of the times a choice of what you prefer to use, but what is the direct difference between these 3 examples?
Example 1
var Class = (function(){
function Class() {
this.test = 'test'
}
return Class;
})();
var c = new Class();
console.log(typeof Class);
console.log(c.test);
Example 2
var Class2 = function(){
this.test = 'test'
};
var c2 = new Class2();
console.log(typeof Class2);
console.log(c2.test);
Example 3
function Class3(){
this.test = 'test'
};
var c3 = new Class3();
console.log(typeof Class3);
console.log(c3.test);
Sometimes I use it like this as well:
var Class = (function(){
var Private = {}, Public = {};
Private._doSomething = function() {
// something
}
Public.doSomethingElse = function() {
// something else
}
return Public;
})();
Note: The main answer below was written in 2012. See the end for additional notes regarding JavaScript's own class feature (ES2015+).
"Best" is an inherently subjective thing, but I'll try to point out some information about each and let you make up your own mind about "best."
Example 1
...gives you a handy scope (the anonymous function) where you can put truly private class-wide information (and utility functions) which only the Class functions have access to:
var Class = (function(){
var trulyPrivateInformation = 42;
function trulyPrivateUtilityFunction() {
// ...
}
function Class() {
this.test = 'test';
}
return Class;
})();
Example 1 is not "hoisted." It is processed as part of the step-by-step code.
The Class function will have a real name.
Example 2
...creates a function that has no name and assigns it to a variable that has a name. Modern browsers are pretty smart about it, but in theory at least, the function is anonymous, which impacts what information your tools can provide you. that (as of ES2015) has a name (Class2) except in obsolete environments like IE. It doesn't have the private class-wide scope Example 1 has.
Like Example 1, Example 2 is processed as part of the step-by-step code, not hoisted.
Example 3
...is just Example 1 without the private class-wide scope, but it's also "hoisted" — the Class function is defined before any step-by-step code is executed. That means this works:
var c = new Class();
console.log(c.test); // Logs 'test'
function Class() {
this.test = 'test';
}
Note that even though Class is defined lower down, it's done before the code above runs. This isn't true of either Example 1 or Example 2.
As with Example 1 (but not 2), the Class function has a real name.
In 2015, JavaScript got its own class syntax. Here in 2019, it's natively supported by all modern browsers, and you can transpile with tools like Babel if you need to support IE. Here's how class would relate to the OP's question:
Example 1 with class
const Class = (() => {
let trulyPrivateInformation = 42;
function trulyPrivateUtilityFunction() {
// ...
}
return class Class {
constructor() {
this.test = 'test';
}
}
})();
Processed in the step-by-step execution of code. Has truly private scope (within the anonymous scoping function) for truly private stuff. Has a proper name.
Example 2 with class (which is also Example 3 with class)
class Class2 {
constructor() {
this.test = 'test';
}
}
let c2 = new Class2();
console.log(typeof Class2);
console.log(c2.test);
Processed in the step-by-step execution of code. Doesn't have the truly private scope Example 1 has (though private fields and methods are coming soon). Has a proper name.