call parent ctor from ctor of inheriting class - javascript

Is it valid to instead of doing this
function Animal(name, numLegs) {
this.name = name;
this.numLegs = numLegs;
}
Animal.prototype.sayName = function() {
console.log("Hi my name is " + this.name);
};
function Penguin(name) {
this.name = name;
this.numLegs = 2;
}
Penguin.prototype = new Animal();
var penguin = new Penguin("Tux");
penguin.sayName();
do that?
function Animal(name, numLegs) {
this.name = name;
this.numLegs = numLegs;
}
Animal.prototype.sayName = function() {
console.log("Hi my name is " + this.name);
};
function Penguin(name) {
return new Animal(name, 2);
}
Penguin.prototype = new Animal();
var penguin = new Penguin("Tux");
penguin.sayName();
I find the second version more elegant and hoped both version were equivalent in their results, but for the second one codeacademy tells me
Oops, try again. Make sure to create a new Penguin instance called penguin!
while the first one is accepted.

This is not the correct way to call the parent constructor:
function Penguin(name) {
return new Animal(name, 2);
}
The correct way is as follows:
function Penguin(name) {
Animal.call(this, name, 2);
}
The reason is because of the way new works:
Let's say you have a function called ABC.
When you execute new ABC JavaScript creates an instance of ABC.prototype and binds it to this inside of the function ABC which is why you can add properties to this inside ABC.
The constructor function returns this by default unless you return another object explicitly.
The reason Codecademy complains about your code is because you're returning a new Animal(name, 2) which is not an instanceof Penguin.
As I said before the correct way to call the parent constructor is to use ParentConstructor.call(this, arg1, arg2, ...). In this case we are setting the this inside the parent constructor to the same value as this inside the current constructor (which is the instance created by new).
If you want to write elegant code then try this on for size:
function defclass(prototype) {
var constructor = prototype.constructor;
var instance = prototype.instance = function () {};
constructor.prototype = instance.prototype = prototype;
return constructor;
}
function extend(parent, keys) {
var supertype = keys.super = parent.prototype;
var prototype = new supertype.instance;
for (var key in keys) prototype[key] = keys[key];
return defclass(prototype);
}
Using defclass and extend you could rewrite your code as follows:
var Animal = defclass({
constructor: function (name, numLegs) {
this.name = name;
this.numLegs = numLegs;
},
sayName: function () {
console.log("Hi my name is " + this.name);
}
});
var Penguin = extend(Animal, {
constructor: function (name) {
this.super.constructor.call(this, name, 2);
}
});
var penguin = new Penguin("Tux");
penguin.sayName();
How cool is that?

I think the difference is that constructor functions don't return a value. So if you call
new Penguin('bla')
it is not the function Penguin that returns the new Object, it is the new that returns the new Object. So if you let Penguin() return a new Object this will conflict with the new-keyword.
If you want to call the parent-constructor, you can do that as follows:
function Penguin(name) {
Animal.call(this, name, 2);
}
Just additionally: When you assign the prototype of Animal to its sub-prototype Penguin, you call the Function in your example without its paramers. There is a cleaner way to do that:
Penguin.prototype = Object.create(Animal.prototype);
After that you have lost the constructor function of Penguin so you need to reassign it like this:
Penguin.prototype.constructor = Animal;
This is explained in detail here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript
and
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript

In the second example you are NOT returning an instance of Penguin but an instance of Animal. if you want to add more functionality to penguin you need to decorate the Animal class with extra functionality.
function Penguin(name) {
var self = new Animal(name, 2);
self.penguinFunction = function (){
//do something here
}
return self;
}

Related

How to use constructors as a prototype chain?

Suppose that I have a javascript constructor:
function Person(name) {
this.name = name;
this.hello = function () { return "It's a-me, " + name + "!"; };
}
the Person "type" has a convenient method, hello that I would like to re-use on another type Student. I would like for a Student to have the following structure:
function Student(name) {
this.name = name;
this.hello = function () { return "It's a-me, " + name + "!"; };
this.books = [];
}
One option is to use the code for Student as-is above. This is sub-optimal for the usual reasons, such as that if I want it to mirror the Person type, then I have to manually keep their code in sync. Anyway, this is not good.
A second option (from this answer) is to do something like:
function Student(name) {
Person.call(this, name);
this.books = [];
}
When I mario = new Student("mario") I get the following:
Object { name: "mario", hello: hello(), books: [] }
I've successfully achieved the inheritance that I wanted, but this has the unfortunate property of placing all of the desired properties into my object. Notably, for example, there is a "hello" property on mario. It would be nice if that "hello" property could be looked up in the prototype chain.
How can I neatly create a prototype chain given the relevant object constructors?
When you create an object with new, the this value of your constructor function is set to the object, and that object's prototype is set to the prototype of the constructor Function being called.
That's why your properties are currently being added to the created object.
function Student {
this.name = name
}
const student = new Student('John')
// is (almost) equivalent to the following
const student = {}
student.name = 'John'
But if you want to add properties to the prototype instead, so that you can use inheritance, then in ES5 Javascript you can do so by assigning properties directly to the prototype of your constructor function.
function Person(name) {
this.name = name;
}
// Person is a function
// Its prototype is an instance of Object, and it has no properties
// i.e. something like Person.prototype = new Object()
Person.prototype.hello = function() {
return 'It is I, ' + this.name
}
// Now Person.prototype has one property, "hello", which is a function.
function Student(name) {
Person.call(this, name)
this.books = [];
}
// Student is a function
// Its prototype is also an instance of Object with no properties
// the following is the magic line
Student.prototype = Object.create(Person.prototype)
// We replace the prototype of the Student function with a new object, but
// Object.create() allows us to set the prototype to an existing object, in this case Person.prototype,
// Person.prototype is itself an instance of Object, and we previously
// added the "hello" function to it as a property.
const student = new Student('John')
// So what happens here?
// First a new object is created, and its prototype is set to Student.prototype
// Then we call Person.call(this)
// Which executes the body of the Person function
// So you get the properties on the object itself through the body of the Person and Student functions
// And you get the shared functionality through the prototype chain of instance -> Student.prototype -> Person.prototype -> Object.prototype
Hope that helps!
You can use prototyping method or class sugar method as you want.
Here is a simple example :
function Student(name) {
this.name = name;
this.books = [];
}
Student.prototype.hello = function(){
return "It's a-me, " + this.name + "!";
}
Student.prototype.addBook = function(book){
this.books.push(book);
}
Student.prototype.getBooks = function(){
return this.books;
}
let mario = new Student("Mario");
console.log(mario.hello());
mario.addBook("prototyping");
mario.addBook("chain");
console.log(mario.getBooks());
class Person {
constructor(name) {
this.name = name;
this.books = [];
}
hello(){
return "It's a-me, " + this.name + "!";
}
addBook(book){
this.books.push(book);
}
getBooks(){
return this.books;
}
}
let luigi = new Person("Luigi");
console.log(luigi.hello());
luigi.addBook("classSugar");
luigi.addBook("classType");
console.log(luigi.getBooks());
For longer chains use Object.assign, here is an example of making a GradStudent that is both a Student and a Person and has the personality of a Comedian and also has the properties and methods of a 4th class GameCharacter:
(function() {
//Person
function Person(name) {
this.name = name;
this.helloString = "Hello my name is "
}
Person.prototype.name = "Bob";
Person.prototype.hello = function() {
return this.helloString + this.name;
};
//Student
function Student(name, books) {
Person.call(this, name);
this.books = books;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.books = ["math","reading"];
//Comedian
function Comedian(name) {
Person.call(this,name);
};
Comedian.prototype = Object.create(Person.prototype);
Comedian.prototype.constructor = Comedian;
Comedian.prototype.hello = function() {
return "I don't know what my parents where thinking when they name me Squat, just kidding, my name is " + this.name;
};
//GameCharacter
function GameCharacter(power) {
this.power = power;
};
GameCharacter.prototype = new Object();
GameCharacter.prototype.constructor = GameCharacter;
GameCharacter.prototype.gainPower = function(power) {
this.power += ", "+power;
};
GameCharacter.prototype.statePower = function() {
return this.power;
};
//GradStudent
function GradStudent(name, books, degree) {
Comedian.call(this, name);
Student.call(this,name,books);
GameCharacter.call(this, "jumping");
this.degree = degree;
this.gainPower("flying");
}
GradStudent.prototype = Object.create(Student.prototype);
Object.assign(GradStudent.prototype, Comedian.prototype, GameCharacter.prototype);
GradStudent.prototype.constructor = GradStudent;
var gradStudent = new GradStudent("Bill",["C", "C++", "JavaScript"], "B.S.");
console.log(gradStudent.hello() + " I have a " + gradStudent.degree +" I am studying " + gradStudent.books.toString() + ". \n In a game I play my power's are "+ gradStudent.statePower() + ". \n Is gradStudent also a Student? " + (gradStudent instanceof Student) + "" );
var otherStudent = new Student("Jennifer" ,["english", "science"]);
console.log(gradStudent.books.toString() + " " + otherStudent.books.toString());
})();
GradStudent is an instance of Student, it's a type of Student, and can also do all the things Comedian and GameCharacter does. The value of Object.assign is that kind of multiple inheritance.
I can accomplish such a thing with the following:
function Student(name) {
Object.setPrototypeOf(this, new Person(name));
this.books = [];
}
However, I'm not familiar enough with javascript to know what possible problems might arise with this solution. Coming from other OO style languages, it feels weird for the prototype of mario to be an actual instance of a Person, but I suppose everything in js is an instance, in some sense, so this might just be bias on my part.

Javascript prototype methods vs object methods [duplicate]

When using a constructor function in JavaScript to create a class, is it possible to redefine the class's method later?
Example:
function Person(name)
{
this.name = name;
this.sayHello = function() {
alert('Hello, ' + this.name);
};
};
var p = new Person("Bob");
p.sayHello(); // Hello, Bob
Now I'd like to redefine sayHello like this:
// This doesn't work (creates a static method)
Person.sayHello() = function() {
alert('Hola, ' + this.name);
};
so when I create another Person, the new sayHello method will be called:
var p2 = new Person("Sue");
p2.sayHello(); // Hola, Sue
p.sayHello(); // Hello, Bob
EDIT:
I realize I could send in an argument like "Hello" or "Hola" to sayHello to accomplish the different output. I also realize I could simply assign a new function to p2 like this:
p2.sayHello = function() { alert('Hola, ' + this.name); };
I'm just wondering if I can redefine the class's method so new instances of Person will use the new sayHello method.
is it possible to redefine the class's method later?
Yes. However, you must not assign the new function to a property of the Person constructor, but to the instance itself:
var p2 = new Person("Sue");
p2.sayHello(); // Hello, Sue
p2.sayHello = function() {
alert('Hola, ' + this.name);
};
p2.sayHello(); // Hola, Sue
If you want to do this for all new instances automatically (and have not used the prototype for the method, which you easily could exchange as in #dystroy's answer), you will need to decorate the constructor:
Person = (function (original) {
function Person() {
original.apply(this, arguments); // apply constructor
this.sayHello = function() { // overwrite method
alert('Hola, ' + this.name);
};
}
Person.prototype = original.prototype; // reset prototype
Person.prototype.constructor = Person; // fix constructor property
return Person;
})(Person);
To have a different function for p2, you can just set the sayHello property of p2 :
p2.sayHello = function(){
alert('another one');
}
p2.sayHello();
If you use prototype, then you can also change it for all instances of Person (and still you can overwrite it for a specific person) :
function Person(name)
{
this.name = name;
};
Person.prototype.sayHello = function() {
alert('Hello, ' + this.name);
};
var p = new Person("Bob");
// let's set a specific one for p2
p2.sayHello = function(){
alert('another one');
}
// now let's redefine for all persons (apart p2 which will keep his specific one)
Person.prototype.sayHello = function(){
alert('different!');
}
p.sayHello(); // different!
p2.sayHello(); // another one
To solve your issue You can use Reflect object
class Person {
public name: string;
constructor(name: string) {
this.name = name;
}
public sayHello(): void {
console.log(`Hello, ${this.name}`)
}
}
const p = new Person("Bob");
p.sayHello(); // Hello, Bob
Reflect.defineProperty(Person.prototype, 'sayHello', { value: function() {
console.log(`Goodbye, ${this.name}`)
}})
p.sayHello(); // Goodbye, Bob

Making a function constructor function in Javascript

How, if possible, do I create a function constructor function?..a constructor that also has prototype methods.
I know how to create an object constructor function i.e.
Function Thing(val) { this.prop1 = val }
Thing.prototype.action = function()...
But to make create a new function with prototype prop and methods, the best I can come up with is, for example:
Function Func(val) {
var func = function(x) {return val*x};
func.val = val;
func.__proto__ = proto;
return func;
}
const proto = {
product: function(x) { return this.val*x },
};
For the sake of code hygiene, is there a more elegant solution?
try...
function Person(name) {
this.name = name;
}
// create a new instance
var person = new Person('codechimp');
console.log('person.constructor is ' + person.constructor);

How can I use a method from inherited class

I have the inheritance chain Vehicle -> Motorized -> Car implemented:
function Vehicle()
{
var m_name;
this.setName = function(pName) {
m_name = pName;
};
this.getName = function() {
return m_name;
};
}
function Motorized()
{
var m_started = false;
this.start = function() {
m_started = true;
console.log(getName() + " started");
};
}
function Car()
{ }
//set up the inheritance chain
Motorized.prototype = new Vehicle();
Car.prototype = new Motorized();
// use
var lCar = new Car;
lCar.setName("Focus");
console.log(lCar.getName()); // Focus
lCar.start(); // ReferenceError: getName is not defined
When I invoke lCar.start() (defined in function Motorized), I get an ReferenceError: getName is not defined. How can I use the inherted method getName() in my subclass Motorized?
Because Javascript doesn't know where to look for your getName() method. You can clarify the syntax declaring a self variable that always points to the right object, like this:
function Vehicle()
{
var self = this; // Vehicle
var m_name;
this.setName = function(pName) {
self.m_name = pName;
};
this.getName = function() {
return self.m_name;
};
}
function Motorized()
{
var self = this; // Motorized
var m_started = false;
this.start = function() {
/*
`self` is Motorized, with proto Vehicle, so
has a getName() method.
`this` instead is the object where you call
start() from, i.e. Car, in the example down below.
*/
self.m_started = true;
console.log(self.getName() + " started");
};
}
function Car()
{ }
//set up the inheritance chain
Motorized.prototype = new Vehicle();
Car.prototype = new Motorized();
// use
var lCar = new Car;
lCar.setName("Focus");
console.log(lCar.getName()); // Focus
lCar.start(); // Focus started
Note that in this case, using the keyword this instead of self throughout the code would have worked as well, but you definitely cannot omit it before getName(). Also, if you are planning to add more code later on, such as event handlers in jQuery, having a clear reference to the class you're coding in can be useful, as this can become easily ambiguous, at least from the human point of view.
Anyway, whether using self is a bad coding pattern or not is the topic of this question; the point in your example is that you need to call self.getName() or this.getName().

How to access a superclass instance variable from the subclass?

everybody!
Suppose that I have this class in JavaScript:
function Animal()
{
this.name = "name";
}
Animal.prototype.someMethod =
function ()
{
}
and this subclass:
function Cat()
{
Animal.call(this);
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
Cat.prototype.someMethod =
function ()
{
// I want to access the superclass "name" instance variable here
}
What's the syntax to access the superclass "name" instance variable from the overriden method in the Cat class?
Thank you.
Marcos
UPDATED: Well, if you want to see the real code, here it is. The problem is with the abc variable (just a test variable that I was using).
var pesquisaAcervo;
$(
function ()
{
carregadoBase();
if ($("#form\\:tipoPesquisa").val() == "SIMPLES")
{
pesquisaAcervo = new PesquisaAcervoSimples();
}
else
{
pesquisaAcervo = new PesquisaAcervoAvancada();
}
pesquisaAcervo.paginaCarregada();
}
);
// --- PesquisaAcervo ----------------------------------------------------------
function PesquisaAcervo()
{
$("*:visible[id^='form:materiaisPesquisa']").
change(this.materialMudado).keyup(this.materialMudado);
this.abc = 10;
}
PesquisaAcervo.prototype.paginaCarregada =
function ()
{
$("#cabecalhoPesquisa a").click(this.exibirDicasPesquisa);
$("#cabecalhoPesquisa select").
change(function () {$("#form").submit();}).
keyup(function () {$(this).change();});
$("*:visible[class*='foco']").focus().select();
};
PesquisaAcervo.prototype.materialMudado =
function ()
{
};
PesquisaAcervo.prototype.exibirDicasPesquisa =
function ()
{
};
// --- PesquisaAcervoSimples ---------------------------------------------------
function PesquisaAcervoSimples()
{
PesquisaAcervo.call(this);
$("#form\\:campos").change(
function ()
{
$("#textoCampo").text($("#form\\:campos :selected").text() + ":");
}
).keyup(function () {$(this).change();}).change();
$("#pesquisaSimples a").click(
function ()
{
pesquisaAcervo = new PesquisaAcervoAvancada();
$("#pesquisaSimples").parent().hide();
$("#pesquisaAvancada").parent().show();
$("#form\\:tipoPesquisa").val("AVANCADO");
}
);
}
PesquisaAcervoSimples.prototype = new PesquisaAcervo();
PesquisaAcervoSimples.prototype.constructor = PesquisaAcervoSimples;
PesquisaAcervoSimples.prototype.materialMudado =
function ()
{
alert(this.abc); // "undefined" here
};
// --- PesquisaAcervoAvancada --------------------------------------------------
function PesquisaAcervoAvancada()
{
PesquisaAcervo.call(this);
}
PesquisaAcervoAvancada.prototype = new PesquisaAcervo();
PesquisaAcervoAvancada.prototype.constructor = PesquisaAcervoAvancada;
Your actual code reveals the problem. The issue is with how you're calling materialMudado. It's being invoked as the callback for an event. The keyword this inside the callback will refer to the target of the event (which has no abc property), not to the object that the function "belongs" to.
Here's a simple demonstration:
function Test() {};
Test.prototype.callback = function() {
alert(this);
}
var t = new Test();
$(document).click(t.callback);
Output (after clicking page):
[object HTMLDocument]
Compare to this:
function Test() {};
Test.prototype.callback = function() {
alert(this);
}
var t = new Test();
$(document).click(function() {
t.callback();
});
Output:
[object Object]
In this second example we close over the variable t, retaining a reference to it.
Applying this to your example produces something like this:
function PesquisaAcervo() {
var that = this;
var callback = function() {
that.materialMudado();
};
$("*:visible[id^='form:materiaisPesquisa']").
change(callback).keyup(callback);
this.abc = 10;
}
this.name should work. I don't see you overriding the name property in your Cat function so you should be able to just do this.name and the protopical chain will do the work to find the first instance of this property which should be Animal.name.
There is no such thing as an override for an instance variable. An instance variable is just a property on the this object. You can read it with:
var x = this.name;
or assign to it with:
this.name = "foo";
this.name will access the name whether you have an instance of an Animal object or an instance of a Cat object.
If you want to assign to the name property in the Cat constructor, you can just do so with
this.name = "Cat";
Once you have a working instance of an object, properties are just properties and there is no distinction for whether a property was created by a superclass or a subclass. They're just properties of the object at that point and you access all of them the same way with the this.propertyName syntax.
Just use the this keyword:
function Animal()
{
this.name = "name";
}
Animal.prototype.someMethod2 =
function ()
{
}
function Cat()
{
Animal.call(this);
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat;
Cat.prototype.someMethod =
function ()
{
alert(this.name);// I want to access the superclass "name" instance variable here
}
var c = new Cat();
c.someMethod();
Add this code to the bottom, I've just added an alert to your someMethod method...
In your example, Cat derives everything from Animal, so it has access to the name variable

Categories

Resources