basic question: why is the constructor needed in class?
Below is a simple test:
Circle class with a constructor function
Circle2 class without a constructor function.
... However, both classes work and an instance can be created.
Going back to the topic, why is the constructor needed in class?
class Circle {
draw() {
console.log('hello');
}
}
c = new Circle(1);
class Circle2 {
constructor(radius) {
this.radius = radius;
}
draw() {
console.log('hello');
}
}
c2 = new Circle2(1);
if you do c.radius, it has not been defined, but c2.radius will be
When you dont have the constructor, your class will take in arguments but wont do anything with them
When no constructor is declared, the new operator only creates an empty object with Circle.prototype as prototype. If you want to initialize some properties of the class instance, then you need a constructor.
You have another alternative, if the property you want to create doesn't depend on the constructor parameters, then you can use class fields as an equivalent:
class Foo {
// this is equivalent to a constructor initialization but it only takes a static assignment
bar = 'bar';
}
Class fields adds properties on the class instance just like a constructor does.
Related
In python there's something like __call__ for this. Consider the following example:
class MyClass {
__call__() { return 'called!' }
}
const myType = new MyClass();
myType(); // called!
The question is what should I replace __call__ with?
I was doing some research, and most of the answers recommend __proto__, but it doesn't seem to work.
It is not possible out-of-the-box, but you can extend Function, and use the Function constructor to forward a call to __call__. If you have multiple classes that need this feature, extend Function only once into -- let's say -- a Callable class, and then inherit your other classes from that:
class Callable extends Function {
constructor() {
super("...args", "return this.__call__(...args)");
return this.bind(this);
}
}
class Class extends Callable {
__call__() { return 'called!' }
}
let inst = new Class();
console.log(inst());
Background
In JavaScript an object is callable when, and only if, it has the [[Call]] internal slot. But there is (currently) no way to give any given object this slot via JavaScript code. One must start with a function object and extend that.
Adding a constructor, inheritance
The above solution allows the constructor to define properties in the usual way: the constructed object is an instance of the class:
class Callable extends Function {
constructor() {
super("...args", "return this.__call__(...args)");
return this.bind(this);
}
}
class Class extends Callable {
constructor(data) {
super();
this.x = data;
}
__call__() { return 'called!' }
}
let inst = new Class(42);
console.log(inst instanceof Class); // true
console.log(inst.x); // 42
console.log(inst());
You can use constructor.
class Example {
constructor() {
// gets called on class initialization
}
}
Inside the constructor you can also call other methods if you want.
However this won't create an invoke function like using PHP's __invoke if that's what you meant. If that's what you're looking for then I don't know.
This link states we can create a constructor using a constructor method.
I wanted to know when would I use that and how is it different from constructor functions. From what I understand is. A regular function is a constructor function when its called with new. What role does "constructor" keyword play in this example
class Cat {
constructor(name,color){
this.name = name;
this.color = color;
}
speak(){
console.log("Meow");
}
}
When instantiating a new instance of this class with new Cat(), you'd pass in the name and color of the cat in the order defined by the constructor arguments. You'll then have a new instance of Cat with the name and color properties defined.
const spot = new Cat('Spot', 'Orange')
That will create a new cat with a name of "Spot" and a color of "Orange". It's just a reserved keyword that is executed when a new instance is created.
It's similar to the ES5 syntax, just with some syntactic sugar:
function Cat(name, color) {
this.name = name;
this.color = color
}
Cat.prototype.speak = function() {
console.log('Meow')
}
const c = new Cat('Spot', 'Orange')
c.speak() // Meow
console.log(c.name, c.color) // Spot Orange
You can read more on MDN
Do not forget that there's no real class in javascript. If you try to transpile your class, you will get something similar to:
var Cat = function () {
function Cat(name, color) {
this.name = name;
this.color = color;
}
Cat.prototype.speak = function speak() {
console.log("Meow");
};
return Cat;
}();
No surprise, the constructor is a constructor function.
The constructor method within a class declaration/expression is required as part of the syntax. It supplies the code to execute when "creating" a new instance of the class. Here "creating" and "constructing" mean the same thing in JavaScript.
The constructor method of the class creates a constructor function object that is the value of the class declaration:
class myClass {
constructor() {
this.bar = "foo";
}
moreInfo() {
console.log( "myClass is the same as its constructor method = "
+ (myClass === this.constructor));
}
}
// what is myClass?
console.log( new myClass().moreInfo());
So, long story short, class declarations create a constructor function of the same name.
Going back to basic JS, all standard functions (that are not either arrow
functions or class declarations or expressions) can be used as constructor functions by design: just because you never use them as a consctructor doesn't mean you couldn't. Of course, the sense of using a particular function as a constructor does depend on whether it was written for the purpose of constructing objects:
function add( a, b) { // Not written as a constructor
return a+b;
}
function record( a, b) { // written as a constructor
this.a = a;
this.b = b
}
Every object prototype in JavaScript comes with a constructor method. The constructor method is the constructor function used to create the object.
For instance, we can turn a number into a string using the String constructor
String(2) === "2";
we can also do
"".constructor(2) === "2"
This works the same because the string's "constructor" points to the String constructor function.
Classes in JavaScript aren't classes as they exist in languages like Java or Ruby since JavaScript uses a prototypal inheritance system. So classes in JavaScript are just sugar syntax for plain constructor functions. This allows developers to take advantage of some OOP features of JavaScript without needing to know about many of the nuances about how its inheritance system works.
So the following:
function Cat (name,color){
this.name = name;
this.color = color;
}
Cat.prototype.speak = function(){
console.log("Meow");
}
Is effectively the same as
class Cat {
constructor(name,color){
this.name = name;
this.color = color;
}
speak(){
console.log("Meow");
}
}
Both can be instantiated using the new keyword
new Cat("Mittens", "black");
The "constructor" as described in the class example points to the underlying JavaScript constructor function. Remember that "class" in JavaScript is just a friendly wrapper around a constructor function that makes using it more similar to using classes in other languages.
When working with a class you'd use the "constructor" method to set some values directly to the object that will be created, rather than on its prototype. The prototype is an object common to all objects created with a class/constructor, that's used to share functionality between them.
I wrote some code:
class Base {
// Default value
myColor = 'blue';
constructor() {
console.log(this.myColor);
}
}
class Derived extends Base {
myColor = 'red';
}
// Prints "blue", expected "red"
const x = new Derived();
I was expecting my derived class field initializer to run before the base class constructor.
Instead, the derived class doesn't change the myColor property until after the base class constructor runs, so I observe the wrong values in the constructor.
Is this a bug? What's wrong? Why does this happen? What should I do instead?
Not a Bug
First up, this is not a bug in TypeScript, Babel, or your JS runtime.
Why It Has To Be This Way
The first follow-up you might have is "Why not do this correctly!?!?". Let's examine the specific case of TypeScript emit. The actual answer depends on what version of ECMAScript we're emitting class code for.
Downlevel emit: ES3/ES5
Let's examine the code emitted by TypeScript for ES3 or ES5. I've simplified + annotated this a bit for readability:
var Base = (function () {
function Base() {
// BASE CLASS PROPERTY INITIALIZERS
this.myColor = 'blue';
console.log(this.myColor);
}
return Base;
}());
var Derived = (function (_super) {
__extends(Derived, _super);
function Derived() {
// RUN THE BASE CLASS CTOR
_super();
// DERIVED CLASS PROPERTY INITIALIZERS
this.myColor = 'red';
// Code in the derived class ctor body would appear here
}
return Derived;
}(Base));
The base class emit is uncontroversially correct - the fields are initialized, then the constructor body runs. You certainly wouldn't want the opposite - initializing the fields before running the constructor body would mean you couldn't see the field values until after the constructor, which is not what anyone wants.
Is the derived class emit correct?
No, you should swap the order
Many people would argue that the derived class emit should look like this:
// DERIVED CLASS PROPERTY INITIALIZERS
this.myColor = 'red';
// RUN THE BASE CLASS CTOR
_super();
This is super wrong for any number of reasons:
It has no corresponding behavior in ES6 (see next section)
The value 'red' for myColor will be immediately overwritten by the base class value 'blue'
The derived class field initializer might invoke base class methods which depend on base class initializations.
On that last point, consider this code:
class Base {
thing = 'ok';
getThing() { return this.thing; }
}
class Derived extends Base {
something = this.getThing();
}
If the derived class initializers ran before the base class initializers, Derived#something would always be undefined, when clearly it should be 'ok'.
No, you should use a time machine
Many other people would argue that a nebulous something else should be done so that Base knows that Derived has a field initializer.
You can write example solutions that depend on knowing the entire universe of code to be run. But TypeScript / Babel / etc cannot guarantee that this exists. For example, Base can be in a separate file where we can't see its implementation.
Downlevel emit: ES6
If you didn't already know this, it's time to learn: classes are not a TypeScript feature. They're part of ES6 and have defined semantics. But ES6 classes don't support field initializers, so they get transformed to ES6-compatible code. It looks like this:
class Base {
constructor() {
// Default value
this.myColor = 'blue';
console.log(this.myColor);
}
}
class Derived extends Base {
constructor() {
super(...arguments);
this.myColor = 'red';
}
}
Instead of
super(...arguments);
this.myColor = 'red';
Should we have this?
this.myColor = 'red';
super(...arguments);
No, because it doesn't work. It's illegal to refer to this before invoking super in a derived class. It simply cannot work this way.
ES7+: Public Fields
The TC39 committee that controls JavaScript is investigating adding field initializers to a future version of the language.
You can read about it on GitHub or read the specific issue about initialization order.
OOP refresher: Virtual Behavior from Constructors
All OOP languages have a general guideline, some enforced explicitly, some implicitly by convention:
Do not call virtual methods from the constructor
Examples:
C# Virtual member call in a constructor
C++ Calling virtual functions inside constructors
Python Calling member functions from a constructor
Java Is it OK to call abstract method from constructor in Java?
In JavaScript, we have to expand this rule a little
Do not observe virtual behavior from the constructor
and
Class property initialization counts as virtual
Solutions
The standard solution is to transform the field initialization to a constructor parameter:
class Base {
myColor: string;
constructor(color: string = "blue") {
this.myColor = color;
console.log(this.myColor);
}
}
class Derived extends Base {
constructor() {
super("red");
}
}
// Prints "red" as expected
const x = new Derived();
You can also use an init pattern, though you need to be cautious to not observe virtual behavior from it and to not do things in the derived init method that require a complete initialization of the base class:
class Base {
myColor: string;
constructor() {
this.init();
console.log(this.myColor);
}
init() {
this.myColor = "blue";
}
}
class Derived extends Base {
init() {
super.init();
this.myColor = "red";
}
}
// Prints "red" as expected
const x = new Derived();
I would respectfully argue this is, in fact, a bug
By doing an unexpected thing, this is undesired behavior that breaks common class extension use cases. Here is the initialization order that would support your use case and that I would argue is better:
Base property initializers
Derived property initializers
Base constructor
Derived constructor
Problems / Solutions
- The typescript compiler currently emits property initializations in the constructor
The solution here is to separate the property initializations from the calling of the constructor functions. C# does this, although it inits base properties after derived properties, which is also counterintuitive. This could be accomplished by emitting helper classes so that the derived class can initialize the base class in an arbitrary order.
class _Base {
ctor() {
console.log('base ctor color: ', this.myColor);
}
initProps() {
this.myColor = 'blue';
}
}
class _Derived extends _Base {
constructor() {
super();
}
ctor() {
super.ctor();
console.log('derived ctor color: ', this.myColor);
}
initProps() {
super.initProps();
this.myColor = 'red';
}
}
class Base {
constructor() {
const _class = new _Base();
_class.initProps();
_class.ctor();
return _class;
}
}
class Derived {
constructor() {
const _class = new _Derived();
_class.initProps();
_class.ctor();
return _class;
}
}
// Prints:
// "base ctor color: red"
// "derived ctor color: red"
const d = new Derived();
- Won't the base constructor break because we're using derived class properties?
Any logic that breaks in the base constructor can be moved to a method that would be overridden in the derived class. Since derived methods are initialized before the base constructor is called, this would work correctly. Example:
class Base {
protected numThings = 5;
constructor() {
console.log('math result: ', this.doMath())
}
protected doMath() {
return 10/this.numThings;
}
}
class Derived extends Base {
// Overrides. Would cause divide by 0 in base if we weren't overriding doMath
protected numThings = 0;
protected doMath() {
return 100 + this.numThings;
}
}
// Should print "math result: 100"
const x = new Derived();
I try to extend a class with another, who has its constructor overrode, but when i instance this class, it doesn't have its own methods, but has its own properties.
Here's an example which doesn't work properly:
class A {
constructor () {
return {
pi: 3.14
}
}
}
class B extends A {
constructor () {
super();
this.c = 10;
}
d () {}
}
let b = new B();
console.log(b);
Here, b is :
Object {
c:10,
pi:3.14
}
So why the 'd' method is missing ?
EDIT:
Here is a concrete case:
I need to extend a class with HTMLElement, which i can instance and use like html element without registering with document.registerElement.
My code is:
class Element{
constructor(){
return document.createElement('div');
}
}
class Editor extends Element{
constructor(){
super();
}
}
and i want to use my class like this:
let editor = new Editor();
document.querySelector('body').appendChild(editor);
ECMAScript6 class methods are methods of an object's prototype object. You will find the method not in the object itself, but in obj.__proto__.
https://reinteractive.com/posts/235-es6-classes-and-javascript-prototypes
Here you can see and maybe understand what is going on internally with prototyped objects. And yes, the ES6 syntax is just a syntax change, not a new technology.
can you please tell me how to do encapsulation in javascript .I have a class Name Car .I want to extend this class with B class .Secondly I want to override and overload the methods in java script.
Here is my fiddle
http://jsfiddle.net/naveennsit/fJGrA/
//Define the Car class
function Car() { }
Car.prototype.speed= 'Car Speed';
Car.prototype.setSpeed = function(speed) {
this.speed = speed;
alert("Car speed changed");
}
//Define the Ferrari class
function Ferrari() { }
Ferrari.prototype = new Car();
// correct the constructor pointer because it points to Car
Ferrari.prototype.constructor = Ferrari;
// replace the setSpeed method
Ferrari.prototype.setSpeed = function(speed) {
this.speed = speed;
alert("Ferrari speed changed");
}
var car = new Ferrari();
car.setSpeed();
can you explain these two lines
Ferrari.prototype = new Car();
This line show Ferrari is extend by car ?
Ferrari.prototype.constructor = Ferrari;
what is the used of this line ?
JS, by design does not provide a built-in way to manage the visibility of members of an object. but its flexible enough to allow us to do encapsulation.
Effective write up i found for you is http://www.codeproject.com/Articles/108786/Encapsulation-in-JavaScript
Ferrari.prototype = new Car()
This method adds Car poperties to the Ferrari's prototype. Whatever is returned by the Car(), is added to the existing Ferrari's prototype.
Ferrari.prototype.constructor = Ferrari
Prototype has a constructor property which is overriden by this call Ferrari.prototype = new Car(). This is manually resetting it again.
prototype and constructor object properties
I have edited your fiddle. http://jsfiddle.net/fJGrA/3/
Using closure in javascript you can hide elements within an object or function.
function foo(args){
//this is a private variable.
var _name = ""
return{
getname:function(){
return _name
}
}
}
bar = new foo()
// name can only be accessed from inside the foo function.
Whenever a variable is created with a var keyword within a function, it is accessible only in the scope of that function. Effectively, being private to it.