one-hand-octopus
one-hand-octopus

Reputation: 2743

Why does `super` keyword property returns undefined in an ES6 class?

I'm new to ES6 classes and trying to understand how inheritance works in it. I created a parent class Modal and a child class ChildModal like this:

class Modal {
    constructor(selector, document) {
        this.selector = selector;
        this.document = document;
    }

    get title() {
        return this._title;
    }

    set title(title) {
        if(!title) {
            throw new Error('Original title cannot be empty');
        }
        this._title = title;
    }

    defineNewTitle(newContent) {
        this.title = newContent + this.title;
    } 

    assignNewTitle() {
        $(this.selector).text(this.title);
    }
}

var modal = new Modal("#mainTitle");

modal.title = "Standards";

modal.defineNewTitle("JavaScript ");

modal.assignNewTitle();

class ChildModal extends Modal {
    constructor(selector, title) {
        super(selector, title);
    }

    defineChildTitle(title) {
        this.title = title + this.title;
    }

    assignChildTitle() {
        $(this.selector).text(this.title);
    }
}

var child = new ChildModal("#childTitle");
child.defineChildTitle("Wow ");
child.assignChildTitle();
<!DOCTYPE html>
<html>
<head>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
	<title>Class test</title>
	<h1 id="mainTitle">
		Main title
	</h1>
	<h2 id="childTitle">
		Child title
</head>
<body>
	<script src="Modal.js"></script>
</body>
</html>

I expect the h2 tag being modified to 'Wow JavaScript Standards', but instead it outputs 'Wow undefined'. Why in method defineChildTitle it does not recognise this.title? Thought in the childModal class, this.title should be 'JavaScript Standardas I've inherited theModalclass in the constructor?enter code here`

Upvotes: 0

Views: 1961

Answers (3)

Get Off My Lawn
Get Off My Lawn

Reputation: 36299

You need to define _title in your constructor:

class Modal {
    constructor(selector, document) {
        this.selector = selector;
        this.document = document;
        this._title = ''
    }
}

var child = new ChildModal("#childTitle");
child.title = "JavaScript Standards"
child.defineChildTitle("Wow ");
child.assignChildTitle();

You are currently joining a string with undefined which results in undefined.

Since you are creating two instances, they don't relate to one another, so child.title isn't the same as modal.title, so concatenating the two will result in two different strings.

So, you not only need to set the title in modal.title = 'JavaScript Standards', but you also must set the same thing to child.title = 'JavaScript Standards'.

class Modal {
    constructor(selector, document) {
        this.selector = selector;
        this.document = document;
        this.title = ' '
    }

    get title() {
        return this._title;
    }

    set title(title) {
        if(!title) {
            throw new Error('Original title cannot be empty');
        }
        this._title = title;
    }

    defineNewTitle(newContent) {
        this.title = newContent + this.title;
    } 

    assignNewTitle() {
        $(this.selector).text(this.title);
    }
}

var modal = new Modal("#mainTitle");

modal.title = "Standards";

modal.defineNewTitle("JavaScript ");

modal.assignNewTitle();

class ChildModal extends Modal {
    constructor(selector, title) {
        super(selector, title);
    }

    defineChildTitle(title) {
        this.title = title + this.title;
    }

    assignChildTitle() {
        $(this.selector).text(this.title);
    }
}

var child = new ChildModal("#childTitle");
child.title = "JavaScript Standards"
child.defineChildTitle("Wow ");
child.assignChildTitle();
<!DOCTYPE html>
<html>
<head>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
	<title>Class test</title>
	<h1 id="mainTitle">
		Main title
	</h1>
	<h2 id="childTitle">
		Child title
</head>
<body>
	<script src="Modal.js"></script>
</body>
</html>

Upvotes: 1

Christian Vincenzo Traina
Christian Vincenzo Traina

Reputation: 10384

Under the hoods, ES5 classes still use prototype.

So, another solution could be change modal.title = "Standards"; to Modal.prototype.title = "Standards";.

In this way, you won't change the property in the instance, but you will change the value in the prototype (aka the class definition):

class Modal {
    constructor(selector, document) {
        this.selector = selector;
        this.document = document;
    }

    get title() {
        return this._title;
    }

    set title(title) {
        if(!title) {
            throw new Error('Original title cannot be empty');
        }
        this._title = title;
    }

    defineNewTitle(newContent) {
        this.title = newContent + this.title;
    } 

    assignNewTitle() {
        $(this.selector).text(this.title);
    }
}

var modal = new Modal("#mainTitle");

Modal.prototype.title = "Standards";

modal.defineNewTitle("JavaScript ");

modal.assignNewTitle();

class ChildModal extends Modal {
    constructor(selector, title) {
        super(selector, title);
    }

    defineChildTitle(title) {
        this.title = title + this.title;
    }

    assignChildTitle() {
        $(this.selector).text(this.title);
    }
}

var child = new ChildModal("#childTitle");
child.defineChildTitle("Wow ");
child.assignChildTitle();
<!DOCTYPE html>
<html>
<head>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
	<title>Class test</title>
	<h1 id="mainTitle">
		Main title
	</h1>
	<h2 id="childTitle">
		Child title
</head>
<body>
	<script src="Modal.js"></script>
</body>
</html>

Upvotes: 0

Rengers
Rengers

Reputation: 15218

Class fields such as title, selector, and document are not shared between instances. So when you assign a title to your fist modal variable, it does not influence any other isntances or classes.

When the ChildModal calls super(selector, title), it passes its title argument to the initialiser of Modal. In the Modal class, this is then stored in the document field.

When you then run this.title = title + this.title; the title field does not have a any value. It is undefined. Therefore, the title ends up being "Wow" + undefined, which results in "Wow undefined".

Upvotes: 2

Related Questions