I was learning es6 so I was trying to convert these code from es5 to es6.
I knew how to make an instance counter in es5. The id built on A.prototype and counter built on A itself. When I built a instance by A, it will trigger counter++ to set id. Thus, it implements inheritance counter of action.
var A = (function () {
function A() {
this.id = ++A.counter;
console.log(this.id);
}
A.counter = 0;
return A;
}());
a = new A(); // 1
b = new A(); // 2
c = new A(); // 3
If I worked in es6, how do I implement same feature?
The other two answers are completely correct. But if you wanted to get super crazy ES6 you could make getters for the properties.
class A {
constructor() {
this._id = A.counter;
console.log(this.id);
}
get id() {
return this._id;
}
static get counter() {
A._counter = (A._counter || 0) + 1;
return A._counter;
}
}
a = new A() // <- 1
b = new A() // <- 2
c = new A() // <- 3
That way your counter and id are both read only, and your counter increments automatically every time you access it...plus it's all tidy inside the class definition instead of needing to go outside it.
Not saying you need to do it this way...but it seems like you're learning about ES6 and this example shows a couple of neat tricks about what you can do with it.
You'd do it in exactly the same way as in ES5:
class A {
constructor() {
this.id = ++A.counter;
console.log(this.id);
}
}
A.counter = 0;
var a = new A(); // 1
var b = new A(); // 2
var c = new A(); // 3
(you can also add the same unnecessary IIFE if you want)
The es6 class can complete you wanted.
id isn't built on A.prototype, but on each A instance. The constructor is to do a instance. So you can look, it does this.id = ++A.counter when class A is built a new instance.
class A {
constructor(){
this.id = ++A.counter;
console.log(this.id)
}
}
A.counter = 0;
a = new A()
b = new A()
c = new A()
Here is a solution which is also fully contained in an ES6 class as BryanGrezeszak's answer but with a different behavior that may be useful in some cases:
class A {
constructor() {
this.constructor.counter = (this.constructor.counter || 0) + 1;
this._id = this.constructor.counter;
console.log(this.id);
}
get id() {
return this._id;
}
}
class B extends A {}
class C extends A {}
a = new B(); // <- 1
b = new C(); // <- 1
c = new B(); // <- 2
d = new A(); // <- 1
The difference with this code is that inherited classes have their own independent counter.
If you don't want to add properties to the constructor, you can replace
this.constructor.counter
with
Object.getPrototypeOf(this).counter
that is the current way of expressing the deprecated
this.__proto__.counter
That way counter will be a static property of the class rather than a property of the (static function) constructor.
Related
I have a piece of code that switches the processing used on an object, a, outside of a function test. The function, test, uses properties of a and a.b and a.c:
let a;
let fB = {method: function(o){o.b.count++;return o.b.value}};
let fC = {method: function(o){o.c.count++;return o.c.value}};
a = new class A {value='Method:'; b={value:'B',count:0}; c={value:'C',count:0}};
function test (a,f) {
console.log(a.value+f.method(a));
}
test(a,fB); // method:B
test(a,fC); // method:C
console.log('Counts:',a.b.count,a.c.count) // Counts: 1 1
I want to write it in a more OO way, something like :
function test (a) {
console.log(a.value+a.method());
}
class B {
constructor(){this.val='B';this.count=0}
method (){this.count++;return this.val}
}
class C {
constructor(){this.val='C';this.count=0}
method (){this.count++;return this.val}
}
class A {
value='Method:';
}
a = new A();
Object.setPrototypeOf(a,new B());
test(a); // method:B
Object.setPrototypeOf(a,new C());
test(a); // method:C
console.log('Counts:',a.b.count,a.c.count) // a.b is undefined
The best I could come up with was :
class B {
constructor(){this.value='B';this.count=0}
method (){this.count++;return this.value}
}
class C {
constructor(){this.value='C';this.count=0}
method (){this.count++;return this.value}
}
class A {
value='Method:';
b=new B();
c=new C();
x=this.b;
switchToB(){this.x = this.b}
switchToC(){this.x = this.c}
method(){return this.x.method()}
}
a = new A();
function test (a) {
console.log(a.value+a.method());
}
test(a); // method:B
a.switchToC();
test(a); // method:C
console.log('Counts:',a.b.count,a.c.count) // Counts: 1 1
I don't much like this, nor an alternative :
class B {
constructor(a){this.value=a.value;this.val='B';this.count=0}
method (){this.count++;return this.val}
}
class C {
constructor(a){this.value=a.value;this.val='C';this.count=0}
method (){this.count++;return this.val}
}
class A {
value='Method:';
b=new BB(this);
c=new CC(this);
}
a = new A();
test(a.b); // method:B
test(a.c); // method:C
console.log('Counts:',a.b.count,a.c.count) // Counts: 1 1
Is there a better way to do this in JS ?
Looking at the first OO attempt that you provided, there are these observations:
No b or c properties are created on a, yet the testing code needs them.
Also the switching mechanism should work with a reference to these b and c properties
B and C are look-alikes so that could be generalised to one Counter class whose constructor takes an argument that distinguishes them (determining the value property).
We could also think of a more lazy initialisation, where a counter object is only created when a switches to it.
There seems to be a relationship between the name "b" and the output "B". Similar between "c" and "C". So one could be derived from the other.
This leads to the following implementation:
class Counter {
constructor(value) {
this.value = value.toUpperCase();
this.count = 0;
}
method() {
this.count++;
return this.value;
}
}
class A {
constructor() {
this.value = 'Method:';
}
activateCounter(name) {
// Create a new counter if not used before
this[name] ??= new Counter(name);
// Redirect the method to use this counter's method
this.method = () => this[name].method();
}
}
function test(a) {
console.log(a.value + a.method());
}
let a = new A();
a.activateCounter("b");
test(a); // method:B
a.activateCounter("c");
test(a); // method:C
console.log('Counts:', a.b.count, a.c.count) // Counts: 1 1
Here a.method() should only be called after a counter was activated with a call of a.activateCounter. This is because the latter will (re)define the method method on a.
I'm invoking a function in constructor where I want to use the variable from child in the function call that's made through parent constructor. For demo purpose I've created a small script to show what I want to do:
class A{
intVal = 1;
constructor() {
this.identifyClass();
}
identifyClass()
{
console.log("I am class A", this.intVal); // This should be the value that B has overwitten
}
}
class B extends A
{
intVal = 2;
}
const x = new B();
So I'd want that the function in parent constructor should use the value that was overwritten by B ie. intVal = 2 but currently it uses the original value. Can I use any workaround for this so that I don't have to create a constructor in B and then invoke the function there?
Note: It's a very complex app where I don't want breaking changes to A which is being used at a lot of places and the Class B is being exposed to public so I don't want the people using Class B to change anything if possible where currently they just overwrite the instance variable
Ok, I think I got it. We create a symbol, if the last argument passed to the constructor is not the symbol, then we create a new object, using this.constructor, passing in the original arguments, plus the symbol, and then call identify. This effectively separates the call to identify from the constructor, allowing us to get this.intVal from the subclass, while still being transparent to the user of the subclass.
const _sym = Symbol();
class A {
intVal = 1;
constructor() {
const args = Array.from(arguments);
const _ = args[args.length - 1];
if (_ !== _sym) {
args.push(_sym);
const el = new this.constructor(...args);
el.identify();
return el;
}
}
identify() {
console.log(this.intVal);
}
}
class B extends A {
intVal = 2;
}
const x = new A();
const y = new B();
Here is the problem:
class A {
value;
constructor() {
this.setUp();
}
setUp() {
this.value = 1;
}
}
class B extends A {
value2;
constructor() {
super();
}
setUp() {
super.setUp();
this.setUp2();
}
setUp2() {
this.value2 = 5;
console.log(this.value2); // However, the value was assigned here.
}
}
let b = new B();
console.log(b.value);
console.log(b.value2); // But here value2 field is undefined.
I noticed, that if turn on ES2015, value2 is also displayed, but in my browser and application doesn't (I use ES6). Does anyone know how to solve the problem?
This is one of the reasons that it's generally not a good idea to call overrideable methods from constructors, exactly this kind of interaction is problematic.
The issue isn't that value2 doesn't get set (as you've shown with your console.log, it does). The problem is that that value gets overwritten with undefined because of the public field declaration. Your
value2;
declaration in the class construct is functionally equivalent to:
value2 = undefined;
That initialization is processed immediately after the call to super in B's constructor, overwriting the value setUp2 put there during the super call.
The solution is not to call overrideable methods from constructors. :-) Instead, do your setup in the constructor, or in a private method (if your environment supports them), or in a utility function you use from the constructor (if you need this logic in multiple places in the class).
So, the simple way is to use initializers:
class A {
value = 1;
}
class B extends A {
value2 = 5;
}
let b = new B();
console.log(b.value); // 1
console.log(b.value2); // 5
...or put that initialization logic in the constructor explicitly, which is what constructors are for:
class A {
value;
constructor() {
this.value = 1;
}
}
class B extends A {
value2;
constructor() {
super();
this.value2 = 5;
}
}
let b = new B();
console.log(b.value); // 1
console.log(b.value2); // 5
But if you need that logic in a function because you use it elsewhere, here's an example of making setUp a private method in each class (again, this assumes you need that functionality in a method because you're going to use it outside the constructor as well as inside):
// >>>>> THIS ONLY WORKS IN ENVIRONMENTS WITH PRIVATE METHODS
// (such as recent versions of Chromium, Chrome, and Brave)
class A {
value;
constructor() {
this.#setUp();
}
#setUp() {
this.value = 1;
}
}
class B extends A {
value2;
constructor() {
super();
this.#setUp();
}
#setUp() {
this.value2 = 5;
}
}
let b = new B();
console.log(b.value); // 1
console.log(b.value2); // 5
If you also need to expose setUp as a public method, you can do that by having it call the private version. Only call the private one from the constructor, though:
// >>>>> THIS ONLY WORKS IN ENVIRONMENTS WITH PRIVATE METHODS
// (such as recent versions of Chromium, Chrome, and Brave)
class A {
value;
constructor() {
this.#internalSetUp();
}
setUp() {
this.#internalSetUp();
}
#internalSetUp() {
this.value = 1;
}
}
class B extends A {
value2;
constructor() {
super();
this.#internalSetUp();
}
setUp() {
super.setUp();
this.#internalSetUp();
}
#internalSetUp() {
this.value2 = 5;
}
}
let b = new B();
console.log(b.value); // 1
console.log(b.value2); // 5
This is going to be a bit tricky but I'll do my best to explain,
Consider the following code:
class A { a() { return true; } }
class B { b() { return new A(); } }
var b = new B();
console.log(b instanceof B); // true
console.log(b.b() instanceof A); // true <--- [1]
It's pretty straightforward to see that (class B).b() is going to return an instance of an object of type(/class) A. And we can evaluate this using the instanceof operator [1].
Now, a problem arises when, for whatever reason, we do not have a definition for class A in our current scope. One scenario where such thing may happen, is when you import/require an object from a library and many of its internal classes are not exposed.
Since there is no definition for A, it is not possible to do <symbol> instanceof A ...
So, how may one actually perform this check under such scenario?
PS: I already tried the Object.prototype.toString... trick to not avail.
You could create a function that traverses the prototype chain and returns a list of all the super classes of an object including the class from which the object was instantiated from.
class Foo {}
class X extends Foo{}
class A extends X{}
class B { b() { return new A(); } }
function getParents(obj) {
const arr = [];
while (obj = Reflect.getPrototypeOf(obj)) {
arr.push(obj.constructor.name);
}
return arr;
}
var b = new B().b();
const parents = getParents(b);
console.log(`b instance of A = ${parents.includes('A')}`);
console.log(`b instance of X = ${parents.includes('X')}`);
console.log(`b instance of Foo = ${parents.includes('Foo')}`);
console.log(`b instance of Object = ${parents.includes('Object')}`);
You could also do this using a recursive function
class Foo {}
class X extends Foo{}
class A extends X{}
class B { b() { return new A(); } }
function getParents(obj, arr = null) {
if (!arr) arr = [];
const protoTypeObj = Reflect.getPrototypeOf(obj);
if (!protoTypeObj) return;
arr.push(protoTypeObj.constructor.name);
getParents(protoTypeObj, arr);
return arr;
}
var b = new B().b();
const parents = getParents(b);
console.log(`b instance of A = ${parents.includes('A')}`);
console.log(`b instance of X = ${parents.includes('X')}`);
console.log(`b instance of Foo = ${parents.includes('Foo')}`);
console.log(`b instance of Object = ${parents.includes('Object')}`);
What is the proper way to create singleton in JS since ES2015?
I know of many ways such as:
(() => {
let instance;
class Singleton{
constructor(){
instance = instance || this;
}
}
window.Singleton = Singleton; // or sth to export this class
})();
var a = new Singleton();
var b = new Singleton(); // a is the same as b
But it doesn't seem like a good way to use "new" operator with a Singleton class. So my question is whether there is a "proper" way to create a Singleton in ES6
This one seems to work for me:
let instance;
export default class AudioContext {
static getInstance() {
if (!instance) {
instance = {
context:new window.AudioContext() || new window.webkitAudioContext(),
contextCreatedAt: new Date()
}
}
return instance;
}
}
I have created 2 instances of AudioContext at different times. I then checked the time in contextCreatedAt (returns the same) and context === context on the 2 - however please elaborate if I am wrong here.
var a = (function () {
// can put private vars/methods here.
var a = 3;
var b = 5;
var sum = () => a+b;
return {// your singleton object
getSum: sum
// ...
};
}());