What I would like to achieve:
Essentially, I would like my subclass to have a lexically-bound this function. However, I would like the super class to check that the subclass has an instantiation of this lexically-bound function.
This is how I would prefer to write the code, but it doesn't work:
class Animal {
constructor(type) {
this.animalType = type;
if (!(this.bark instanceof Function)) {
throw new Error('Found no bark');
}
}
}
class Dog extends Animal {
bark = () => {
console.log('woof');
}
}
let max = new Dog('dog')
max.bark();
Yet this works:
class Animal {
constructor(type) {
this.animalType = type;
if (!(this.bark instanceof Function)) {
throw new Error('Found no bark');
}
}
}
class Dog extends Animal {}
Dog.prototype.bark = () => {
console.log('woof');
}
let max = new Dog('dog')
max.bark();
and this works:
class Animal {
constructor(type) {
this.animalType = type;
if (!(this.bark instanceof Function)) {
throw new Error('Found no bark');
}
}
bark = () => {
console.log('woof');
}
}
class Dog extends Animal {}
let max = new Dog('dog')
max.bark();
Could someone please explain why my first example is failing. It seems to me that bark() isn't in the prototype chain somehow but I'm not sure why.
Related
I don't understand the error in the code below. I tried calling another class's function from another class. But I gives the error error: Uncaught TypeError: Cannot read property '_name' of undefined
class Person {
constructor() {
this._name = "Name-Person";
}
getName() {
return this._name;
}
}
class Test1 {
constructor() {
let p = new Person();
new Test2(p.getName);
}
}
class Test2 {
constructor(getName) {
console.log(getName());
}
}
new Test1()
How can I fix the error?
When passing the function to Test2 you need to bind p to the function
new Test2(p.getName.bind(p));
class Person {
constructor() {
this._name = "Name-Person";
}
getName() {
return this._name;
}
}
class Test1 {
constructor() {
let p = new Person();
new Test2(p.getName.bind(p));
}
}
class Test2 {
constructor(getName) {
console.log(getName());
}
}
new Test1()
you can use public class field:
class Person {
constructor() {
this._name = "Name-Person";
}
getName = () => {
return this._name;
}
}
class Test1 {
constructor() {
let p = new Person();
new Test2(p.getName);
}
}
class Test2 {
constructor(getName) {
console.log(getName());
}
}
new Test1()
Because you are passing the function and not the entire class or its value, _name does not exist in the context of the Test2 constructor.
A couple of simple solutions is to either pass in the result of getName() to the constructor, or the entire class of Person.
new Test2(p); // And in Test2 use p.getName()
// or
new Test2(p.getName()); // And in Test2 use the result
I have this javascript class :
class UserDTO {
constructor(props) {
this.username = props.username;
this.birthday = props.birthday;
}
}
and I have a class Utils that convert an Entity to DTO:
class Utils {
convertEntityToDTO (entityObj, DTOClass) {
// entityObj is an instance of a Entity,
// DTOClass is a class not an instance
let objDTO = new DTOClass();
Object.getOwnPropertyNames(entityObj)
.filter(prop => DTOClass.hasOwnProperty(prop))
.forEach(prop => {
objDTO[prop] = entityObj[prop];
});
}
}
this doesn't work a class ; hasOwnProperty just work with object; is a way to verify if a property is an attribute of a class or not ? or I have to create an instance to test ?
You can use hasOwnProperty on the instance and getOwnPropertyNames :
class A {
constructor() {
this.ex = 'TEST';
}
}
var a = new A();
console.log(a.hasOwnProperty('ex'));
console.log(Object.getOwnPropertyNames(a));
If instead you want the methods, you need to get the prototype:
class B {
constructor() {}
exMethod() {
console.log('test');
}
}
var b = new B();
console.log(Object.getPrototypeOf(b).hasOwnProperty('exMethod'));
console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(b)));
class Vehicle {
constructor (name, type) {
this.name = name;
this.type = type;
console.log(this.constructor.name);
}
getName () {
return this.name;
}
getType () {
return this.type;
}
static create(name, type) {
return new Vehicle(name, type);
}
}
class Car extends Vehicle {
constructor (name) {
super(name, 'car');
}
getName () {
return 'It is a car: ' + super.getName();
}
}
let car = Car.create('Tesla', 'car');
console.log(car.getName()); // It is a car: Tesla
console.log(car.getType()); // car
The above code use ES6 class keyword to define a Vehicle class and a subclass Car from it. How to return Car instance from Vehicle static method.
Try:
let car = new Car('Tesla')
You can pass the ClassName you want to use within your static function create and create an instance from it.
static create(name, type, objClass) {
return new Function(`return new ${objClass ? objClass : 'Vehicle'}('${name}', '${type}');`)();
}
The Function class receives a String with the expression to evaluate, in your case:
new Function(`return new ${objClass}('${name}', '${type}');`)()
Look at this code
class Vehicle {
constructor(name, type) {
this.name = name;
this.type = type;
}
getName() {
return this.name;
}
getType() {
return this.type;
}
static create(name, type, objClass) {
return new Function(`return new ${objClass ? objClass : 'Vehicle'}('${name}', '${type}');`)();
}
}
class Car extends Vehicle {
constructor(name) {
super(name, 'car');
}
getName() {
return 'It is a car: ' + super.getName();
}
}
let car = Car.create('Tesla', 'car', 'Car');
console.log(car.getName()); // It is a car: Tesla
console.log(car.getType()); // car
let superCar = Vehicle.create('Tesla', 'car');
console.log(superCar.getName()); // Tesla
console.log(superCar.getType()); // car
.as-console-wrapper {
max-height: 100% !important
}
See? now is printing the right output.
Resources
Class Function
I am learning JS and have came across an interesting article about object composition pattern in JS. What I am wondering in this code:
let Magic = (superclass) => class extends superclass {
shout() {
if (super.shout) super.shout();
console.log('Power and wisdom.');
}
};
let Fighting = (superclass) => class extends superclass {
shout() {
if (super.shout) super.shout();
console.log('Strength an courage.');
}
};
class Creature {
constructor(name) {
this.name = name;
}
shout() {
console.log(`I'm ${this.name}! Oorah!!`);
}
};
class DwarfWizard extends Fighting(Magic(Creature)) {
courseObjects(object = {}) {
object.curse = true;
return object;
}
}
new DwarfWizard('Thordalf').shout();
// "I'm Thordalf! Oorah!! Power and wisdom. Strength an courage."
What is the purpose of the function courseObjects in the DwarfWizard class?
courseObjects(object = {}) {
object.curse = true;
return object;
}
I still get the same result even when I comment out the function, so I am wondering what is it's purpose?
Assume we have:
class FinalClass {
...
}
How to modify it to make
class WrongClass extends FinalClass {
...
}
or
new WrongClass(...)
to generate an exception? Perhaps the most obvious solution is to do the following in the FinalClass's constructor:
if (this.constructor !== FinalClass) {
throw new Error('Subclassing is not allowed');
}
Does anyone have a more cleaner solution instead of repeating these lines in each class that supposed to be final (probably with a decorator)?
Inspect this.constructor in the constructor of FinalClass and throw if it is not itself. (Borrowing inspection of the this.constructor instead of this.constructor.name from #Patrick Roberts.)
class FinalClass {
constructor () {
if (this.constructor !== FinalClass) {
throw new Error('Subclassing is not allowed')
}
console.log('Hooray!')
}
}
class WrongClass extends FinalClass {}
new FinalClass() //=> Hooray!
new WrongClass() //=> Uncaught Error: Subclassing is not allowed
Alternatively, with support, use new.target. Thanks #loganfsmyth.
class FinalClass {
constructor () {
if (new.target !== FinalClass) {
throw new Error('Subclassing is not allowed')
}
console.log('Hooray!')
}
}
class WrongClass extends FinalClass {}
new FinalClass() //=> Hooray!
new WrongClass() //=> Uncaught Error: Subclassing is not allowed
______
As you say, you could also achieve this behaviour with a decorator.
function final () {
return (target) => class {
constructor () {
if (this.constructor !== target) {
throw new Error('Subclassing is not allowed')
}
}
}
}
const Final = final(class A {})()
class B extends Final {}
new B() //=> Uncaught Error: Subclassing is not allowed
As Patrick Roberts shared in the comments the decorator syntax #final is still in proposal. It is available with Babel and babel-plugin-transform-decorators-legacy.
constructor.name is easy enough to spoof. Just make the subclass the same name as the superclass:
class FinalClass {
constructor () {
if (this.constructor.name !== 'FinalClass') {
throw new Error('Subclassing is not allowed')
}
console.log('Hooray!')
}
}
const OopsClass = FinalClass
;(function () {
class FinalClass extends OopsClass {}
const WrongClass = FinalClass
new OopsClass //=> Hooray!
new WrongClass //=> Hooray!
}())
Better to check the constructor itself:
class FinalClass {
constructor () {
if (this.constructor !== FinalClass) {
throw new Error('Subclassing is not allowed')
}
console.log('Hooray!')
}
}
const OopsClass = FinalClass
;(function () {
class FinalClass extends OopsClass {}
const WrongClass = FinalClass
new OopsClass //=> Hooray!
new WrongClass //=> Uncaught Error: Subclassing is not allowed
}())