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
Related
I am setting up JS modules for a project, where I'm using prototypes to do OO programming (since I'm not sure the framework supports ES6 class syntax). One module contains "classes" for a specific purpose, and the other module imports and uses these.
I looked through all the documentation I could find on how to properly set up inheritance with JS prototypes and how to export and import modules. The below code example illustrates what I'm going for.
MyModule.js:
function A(x, y) {
this.x = x;
this.y = y;
}
A.prototype.foo = function(bla) {
// ...
}
function B(x, y, z) {
A.call(this, x, y);
this.z = z;
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
export { B };
MainModule.js:
import * as MyModule from "./MyModule.js"
var b;
function bar() {
b = new MyModule.B(1, 2, 3);
b.foo(4); // <-- error happens here
}
When I try to run bar, b.foo is undefined. Member variables (e.g b.z) work fine, but functions added to the prototypes do not.
Anyone have any ideas? Thanks in advance!
it looks like you're using es6 syntax so you could just do something like
export function B(x, y, z) {
A.call(this, x, y);
this.z = z;
}
then import
import { B as MyModule } from "./MyModule.js"
or whatever name you want to give it
So it turns out Qt (which is what I was using) prefers the ES6 class syntax to do what I was trying to make it do. Once I switched over to that, the import/export and inheritance and everything else worked. Thanks to everyone who commented, I can now mark this as solved.
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
This question already has answers here:
this.constructor.prototype -- can't wholly overwrite, but can write individual props?
(2 answers)
Closed 8 years ago.
I'm working with a fairly simple Point2D data structure I built to be inheritable for say a Point3D in the future and I've followed all the guides and similar questions I can find, but none seem help with my issue.
I've defined Point2D as follows:
function Point2D (px, py)
{
this.x = px;
this.y = py;
Point2D.prototype =
{
constructor: Point2D,
move:function (mx, my)
{
this.x = mx;
this.y = my;
},
translate:function (dx, dy)
{
this.x += dx;
this.y += dy;
}
};
};
I instantiate the object as follows:
var p2d1 = new Point2D(2,3);
Then I call one of the methods as follows:
p2d1.move(1,2);
And the result is:
TypeError: Object #<Point2D> has no method 'move'
I have not idea why my methods don't resolve.
I've messed around with it for a good while and found that I can declare Point2D methods this way and they will work.
Point2D.prototype.move = function () {};
Can anyone explain why they first style of replacing the entire prototype does not work, but adding functions to the existing prototype does work?
When you call new Point() the first time, Point.prototype is still an "empty" prototype. I.e. the instance that is created doesn't inherit any methods.
You change (replace) the prototype after the instance was already created. JavaScript has assign by value, not assign by reference. Quick example:
var a = 5;
var b = {c: a};
a = 10;
b.c is still 5, since assigning to a doesn't change what b.c refers to.
Point2D.prototype.move = function () {};
works because you are not replacing Point2D.prototype, you are simply mutating the existing object.
Overall, assignments to *.prototype should take place outside the constructor:
function Point2D (px, py) {
this.x = px;
this.y = py;
};
Point2D.prototype = { };
I am not sure, but defining the prototype inside the declaration of the "class" is unusual and to me, hard to define exactly how things would be resolved. When doing manual inheritence, I tend to follow more these patterns:
function Foo() {
this.bar = ...
}
Foo.prototype.baz = function() { ... }
OR
function Foo() { ... }
function Bar() { ... }
Foo.prototype = new Bar();
OR
Foo.prototype = {
blah: ...
}
Also I wouldn't usually create a "constructor" property manually, as this is a side effect of setting the prototype, but I know some popular libraries do this. In the middle example above, Foo.prototype.constructor == Bar.
If you really want to warp your brain create a second instance of Point2D and watch it have the move method available and working!
So here is what is happening.
define Point2D class
create instance of Point2D class
create initialization object
create execution context object per new keyword usage
attach prototype to execution context (at this point just Object)
run constructor method
assign value of x
assign value of y
assign new prototype value to Point2D class
what you want to do is to move the prototype setting out to the same scope as the class definition.
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.
I'm using class members to hold constants. E.g.:
function Foo() {
}
Foo.CONSTANT1 = 1;
Foo.CONSTANT2 = 2;
This works fine, except that it seems a bit unorganized, with all the code that is specific to Foo laying around in global scope. So I thought about moving the constant declaration to inside the Foo() declaration, but then wouldn't that code execute everytime Foo is constructed?
I'm coming from Java where everything is enclosed in a class body, so I'm thinking JavaScript might have something similar to that or some work around that mimics it.
All you're doing in your code is adding a property named CONSTANT with the value 1 to the Function object named Foo, then overwriting it immediately with the value 2.
I'm not too familiar with other languages, but I don't believe javascript is able to do what you seem to be attempting.
None of the properties you're adding to Foo will ever execute. They're just stored in that namespace.
Maybe you wanted to prototype some property onto Foo?
function Foo() {
}
Foo.prototype.CONSTANT1 = 1;
Foo.prototype.CONSTANT2 = 2;
Not quite what you're after though.
You must make your constants like you said :
function Foo() {
}
Foo.CONSTANT1 = 1;
Foo.CONSTANT2 = 2;
And you access like that :
Foo.CONSTANT1;
or
anInstanceOfFoo.__proto__.constructor.CONSTANT1;
All other solutions alloc an other part of memory when you create an other object, so it's not a constant. You should not do that :
Foo.prototype.CONSTANT1 = 1;
IF the constants are to be used inside of the object only:
function Foo() {
var CONSTANT1 = 1,CONSTANT2 = 2;
}
If not, do it like this:
function Foo(){
this.CONSTANT1=1;
this.CONSTANT2=2;
}
It's much more readable and easier to work out what the function does.
If you're using jQuery, you can use $.extend function to categorize everything.
var MyClass = $.extend(function() {
$.extend(this, {
parameter: 'param',
func: function() {
console.log(this.parameter);
}
});
// some code to do at construction time
}, {
CONST: 'const'
}
);
var a = new MyClass();
var b = new MyClass();
b.parameter = MyClass.CONST;
a.func(); // console: param
b.func(); // console: const
First, I recommend moving your class declaration inside of an IIFE. This cleans up the code, making it more self-contained, and allows you to use local variables without polluting the global namespace. Your code becomes:
var Foo = (function() {
function Foo() {
}
Foo.CONSTANT1 = 1;
Foo.CONSTANT2 = 2;
return Foo;
})();
The problem with assigning constants directly to the class as attributes is that those are writable. See this snippet:
var output = document.getElementById("output");
var Foo = (function() {
function Foo() {
}
Foo.CONSTANT1 = 1;
Foo.CONSTANT2 = 2;
return Foo;
})();
Foo.CONSTANT1 = "I'm not very constant";
output.innerHTML = Foo.CONSTANT1;
<div id="output"></div>
The best solution I have found is to define read-only properties for accessing the constants outside of the class.
var output = document.getElementById("output");
var Foo = (function() {
const CONSTANT1 = "I'm very constant";
function Foo() {
}
Object.defineProperty(Foo, "CONSTANT1", {
get: function() {
return CONSTANT1;
},
});
return Foo;
})();
Foo.CONSTANT1 = "some other value";
output.innerHTML = Foo.CONSTANT1;
<div id="output"></div>
(Technically you could ditch the const CONSTANT1 statement and just return the value from the property definition, but I prefer this because it makes it easier to see all the constants at a glance.)
what you are doing is fine (assuming you realize that your example is just setting the same property twice); it is the equivalent of a static variable in Java (as close as you can get, at least without doing a lot of work). Also, its not entirely global, since its on the constructor function, it is effectively namespaced to your 'class'.
Your constants are just variables, and you won't know if you try and inadvertently overwrite them. Also note that Javascript lacks the notion of "class".
I'd suggest you create functions that return values that you need constant.
To get the taste of Javascript, find Javascript: the Good Parts and learn the idiomatic ways. Javascript is very different from Java.
Also with namespaces
var Constants = {
Const1: function () {
Const1.prototype.CONSTANT1 = 1;
Const1.prototype.CONSTANT2 = 2;
},
Const2: function () {
Const2.prototype.CONSTANT3 = 4;
Const2.prototype.CONSTANT4 = 3;
}
};
You said your coming from Java - why don't you store that class in 1 file then and constants at the end of the file. This is what I use:
filename: PopupWindow.js
function PopupWindow() {
//private class memebers
var popup, lightbox;
//public class memeber or method (it is the same in JS if I am right)
this.myfuncOrmyMemeber = function() {};
}
//static variable
PopupWindow._instance = null;
//same thing again with constant-like name (you can't have "final" in JS if I am right, so it is not immutable constant but its close enough ;) - just remember not to set varibales with BIG_LETTERS :D)
PopupWindow.MY_CONSTANT = 1;
//yea, and same thing with static methods again
PopupWindow._getInstance = function() {};
So only difference is the position of static stuff. It is not nicly aligned inside class curly braces, but who cares, its always ctrl+click in IDE (or I use ctr+l to show all class methods - IntellijIdea can do that in JS dunno how about other IDEs) so your not gonna search it by your eye ;)
Yea and I use _ before static method - it is not needed, I don't know why I started to do that :)