My expectation was that I'd overwrite Vehicle's toString method with a new toString method. However this doesn't seem to work and I don't know why. Based on this article it looks like it should https://strongloop.com/strongblog/an-introduction-to-javascript-es6-classes/ (scroll down to Class Extending)
function Vehicle(make, year) {
this.make = make;
this.year = year;
}
Vehicle.prototype.toString = function() {
return this.make + ' ' + this.year;
};
var vehicle = new Vehicle('Toyota Corolla', 2009);
function Motorcycle(make, year) {
Vehicle.apply(this, [make, year]);
}
Motorcycle.prototype = Object.create(Vehicle.prototype, {
toString: function() {
return 'Motorcycle ' + this.make + ' ' + this.year;
}
});
Motorcycle.prototype.constructor = Motorcycle;
var motorcycle = new Motorcycle('harley', 2010);
console.log(motorcycle.toString()); //TypeError
The properties object given as the second argument of Object.create is supposed to contain property descriptors, not just values. This corrects the issue:
Motorcycle.prototype = Object.create(Vehicle.prototype, {
toString: {
configurable: true, enumerable: true, writable: true,
value: function() {
return 'Motorcycle ' + this.make + ' ' + this.year;
}
}
});
See also the MDN reference for Object.create.
to override the method, first point the Motorcycle's prototype constructor to itself. please read the comments in the below code .
Vehicle.prototype.toString = function() {
return this.make + ' ' + this.year;
};
var vehicle = new Vehicle('Toyota Corolla', 2009);
function Motorcycle(make, year) {
Vehicle.apply(this, [make, year]);
}
Motorcycle.prototype = Object.create(Vehicle.prototype); //Motorcycle.prototype is an object that inherits from Vehicle.prototype
// and is still pointing to the Vehicle constructor
Motorcycle.prototype.constructor = Motorcycle; // changing the constructor back to Motorcycle;
// method overridden after Motorcycle is pointing its own constructor
// now override the toString method
Motorcycle.prototype.toString=function(){
console.log("inside the motorcycle toString ")
}
var motorcycle = new Motorcycle('harley', 2010);
console.log(motorcycle.toString());
Related
My knowledge of words might not be sufficient to find this by myself the explanation on the www. So please excuse me if this might be a duplicate.
I'm currently trying to figure out why we use "this" in a function constructor instead of simply returning an object?
E.g. this JSFiddle
// Using this inside function
function Student1(first,last) {
this.firstName = first;
this.lastName = last;
this.display = function(){
return this.firstName + " " + this.lastName;
};
}
const harry = new Student1("Harry", "Potter");
document.querySelector("div").innerHTML = harry.display();
document.querySelector("div").innerHTML += "<br>";
// Returning object
function Studen2(first,last){
return {
firstName: first,
lastName: last,
display(){
return this.firstName + " " + this.lastName;
}
};
}
const ron = new Student1("Ron", "Weasley");
document.querySelector("div").innerHTML += ron.display();
Anyone mind to explain or guide me to the right direction?
this works with the prototype of the instanciable function, whereas the simple object has another prototype in the chain of prototypes. It has not an own prototype of the instanciable function.
You could add a new method to the prototype and watch the difference.
// Using this inside function
function Student1(first,last) {
this.firstName = first;
this.lastName = last;
this.display = function(){
return this.firstName + " " + this.lastName;
};
}
const harry = new Student1("Harry", "Potter");
Student1.prototype.morning = function () { return 'good morning ' + this.firstName + " " + this.lastName; };
console.log(harry.morning());
// Returning object
function Studen2(first,last){
return {
firstName: first,
lastName: last,
display(){
return this.firstName + " " + this.lastName;
}
};
}
const ron = new Student1("Ron", "Weasley");
Student2.prototype.morning = function () { return 'good morning ' + this.firstName + " " + this.lastName; };
console.log(ron.morning());
Generally you will want to define the object methods on the class prototype, so that they don't have to be reinstantiated every time you create a new instance of the class, e.g.:
function Student1(first,last) {
this.firstName = first;
this.lastName = last;
}
Student1.prototype.display = function() {
return this.firstName + " " + this.lastName;
}
const harry = new Student1("Harry", "Potter");
document.querySelector("div").innerHTML = harry.display();
If you just return an (anonymous) object, it won't have a prototype and you'll have to define the function every time the construction function gets called.
Also, in your example:
harry instanceof Student1 // true
ron instanceof Student2 // false
So you can't use instanceof.
I'm trying implement a simple call to forEach to run the logMe function on all items in the automobiles array. The output is unexpected. All variables read "undefined."
function Automobile(year, make, model, type) {
this.year = year;
this.make = make;
this.model = model;
this.type = type;
}
Automobile.prototype.logMe = function(boolVal) {
if (boolVal == true) {
console.log(this.year + ' ' + this.make + ' ' + this.model + ' ' + this.type);
} else {
console.log(this.year + ' ' + this.make + ' ' + this.model);
}
}
var automobiles = [
new Automobile(2010, "Toyota", "Tacoma", "Pickup"),
new Automobile(2005, "Lotus", "Elise", "Roadster"),
new Automobile(2008, "Subaru", "Outback", "Wagon")
];
automobiles.forEach(Automobile.prototype.logMe.bind(true)); //the problem
automobiles[0].logMe(true); //test logMe function
The output:
undefined undefined undefined
undefined undefined undefined
undefined undefined undefined
1995 Honda Accord Sedan
The first argument to Function.bind() is the value for this within the function. In your sample, this is bound to true, which is why you get undefined for the property values.
forEach will pass the element as the first argument to the callback. Thus, instead of passing a bound method, you can define a lambda that calls logMe on its 1st argument.
automobiles.forEach(function(car, i, cars) {car.logMe(true);});
Function.prototype.bind creates a new function and you must pass in the value of this - the construct which calls the function.
If you want to create an array of functions that can be called later like this, you should be using bind like below, otherwise just call the function inside the forEach.
var caller = [];
automobiles.forEach(function(element) {
caller.push(Automobile.prototype.logMe.bind(element));
}, caller);
caller[0](true);
Demo below:
function Automobile(year, make, model, type) {
this.year = year;
this.make = make;
this.model = model;
this.type = type;
}
Automobile.prototype.logMe = function(boolVal) {
if (boolVal == true) {
console.log(this.year + ' ' + this.make + ' ' + this.model + ' ' + this.type);
} else {
console.log(this.year + ' ' + this.make + ' ' + this.model);
}
}
var automobiles = [
new Automobile(2010, "Toyota", "Tacoma", "Pickup"),
new Automobile(2005, "Lotus", "Elise", "Roadster"),
new Automobile(2008, "Subaru", "Outback", "Wagon")
];
var caller = [];
automobiles.forEach(function(element) {
caller.push(Automobile.prototype.logMe.bind(element));
}, caller);
caller[0](true); //test logMe function
Im trying to understand javascript prototyping principles and cant get some basic things to work.
One thing that im trying to achieve is, to create a base object, that handles constructor input, and sets values based on that input, or default values if no constructor parameter is given.
Also, i cant quite figure out how to store this this in a variable so that it would point to correct instance of the object( parent object perhaps )
Below are 2 versions which i have tried to create basic inheritance. ( first one ive seen used before, but it doesnt allow me to handle constructor parameters passed to extended object using its base object's constructor.
The second version...is something i came up with to enable it, but...i have never seen anyone using prototypes like this and im sure its the wrong way ( since the prototype property is a function instead of an object)
Whats the correct way of solving both of the problems.
var Person = function(conf) {
if (!conf) {conf = {};}
var _person = this;
this.first_name = conf.first_name;
this.last_name = conf.last_name;
this.greet = function() {
alert("Hi, im " + this.first_name + " " + this.last_name );
}
this.callback_greet = function() {
alert("Hi, im " + _person.first_name + " " + _person.last_name );
console.log("this:", this, " _person:", _person );
}
}
var Student = function(conf) {
if (!conf) {conf = {};}
/* id like to pass this conf on to Person constructor */
this.report = function() {
alert( this.first_name + " " + this.last_name + " is ready to study" );
}
}
Student.prototype = new Person();
var Teacher = function(conf) {
if (!conf) {conf = {};}
this.teach = function() {
alert( this.first_name + " " + this.last_name + " is ready to teach...maggots" );
}
}
Teacher.prototype = new Person();
student = new Student({first_name: "Mike", last_name: "Stud"});
//student.first_name="Mike";
//student.last_name="Stud";
student.greet();
/* alerts Hi, im Mike Stud */
teacher = new Teacher();
teacher.first_name="John";
teacher.last_name="Smith";
teacher.teach();
/* alerts John Smith is ready to teach...maggots */
teacher.callback_greet ();
/* both alerted values are undefined */
//_________________________
//_______Version 2 _______
//_________________________
var Person = function(conf) {
if (!conf) {conf = {};}
var _person = this;
this.first_name = conf.first_name;
this.last_name = conf.last_name;
this.greet = function() {
alert("Hi, im " + this.first_name + " " + this.last_name );
}
this.callback_greet = function() {
alert("Hi, im " + _person.first_name + " " + _person.last_name );
console.log("this:", this, " _person:", _person );
}
}
var Student = function(conf) {
if (!conf) {conf = {};}
this.prototype = Person;
this.prototype(conf);
this.report = function() {
alert( this.first_name + " " + this.last_name + " is ready to study" );
}
}
var Teacher = function(conf) {
if (!conf) {conf = {};}
this.prototype = Person;
this.prototype(conf);
this.teach = function() {
alert( this.first_name + " " + this.last_name + " is ready to teach...maggots" );
}
}
var Principal = function(conf) {
if (!conf) {conf = {};}
this.prototype = Teacher;
this.prototype(conf);
this.dicipline_teachers = function() {
alert( this.first_name + " " + this.last_name + " thy all mighty principal is watching you" );
}
}
student = new Student({first_name: "Mike", last_name: "Stud"});
student.greet();
/* alerts Hi, im Mike Stud */
teacher = new Teacher({first_name: "John", last_name: "Smith"});
teacher.teach();
/* alerts John Smith is ready to teach...maggots */
principal = new Principal({first_name: "David", last_name: "Faustino"});
principal.teach();/* alerts David Faustino is ready to teach...maggots */
principal.dicipline_teachers();/* David Faustino thy all mighty principal is watching you*/
Well, your second version is actually… somewhat… correct!
What your snippet
var Student = function(conf) {
this.prototype = Person;
this.prototype(conf);
does here:
Have a Student instance as this when being invoked with new Student()
Create a property on the instance that contains a function (in this case, the parent constructor), which essentially creates a method on the instance
Call that as a method. Which means, the Person function is invoked with its this pointing to the instance that we have here - and then Person does its setup on that instance.
This is just what we want. Maybe except for creating the unnessary property. Notice that the name of this property being prototype is totally irrelevant for its function, you could have use myParentConstructor or so as well.
In the standard JavaScript inheritance, we do a similar thing to that method call - we want to call the parent constructor on the current (child) instance so that it does get set up. However, we use the .call() method for that.
Now we also want to use the prototype. In your code, all the methods greet, report, teach and dicipline_teachers could be shared amongst the instances, so they can - and should - go on the ConstructorFn.prototype. To let all teachers, students, and principals inherit these methods we need to set up a prototype chain (inheritance hierarchy). We don't want to use new Person as that would call the constructor and set up things like first_name on the prototype object where they would to be shared - but we don't want that. Instead, we use Object.create.
Alltogether, your code would look like this:
function Person(conf) {
if (!conf) {conf = {};}
var _person = this;
this.first_name = conf.first_name;
this.last_name = conf.last_name;
this.callback_greet = function() {
alert("Hi, im " + _person.first_name + " " + _person.last_name );
console.log("this:", this, " _person:", _person );
};
}
Person.prototype.greet = function() {
alert("Hi, im " + this.first_name + " " + this.last_name );
};
function Student(conf) {
Person.call(this, conf);
}
Student.prototype = Object.create(Person.prototype, {constructor:{value:Student}});
Student.prototype.report = function() {
alert( this.first_name + " " + this.last_name + " is ready to study" );
};
function Teacher(conf) {
Person.call(this, conf);
}
Teacher.prototype = Object.create(Person.prototype, {constructor:{value:Teacher}});
Teacher.prototype.teach = function() {
alert( this.first_name + " " + this.last_name + " is ready to teach...maggots" );
};
function Principal(conf) {
Teacher.call(this, conf);
}
Principal.prototype = Object.create(Teacher.prototype, {constructor:{value:Principal}});
Principal.prototype.dicipline_teachers = function() {
alert( this.first_name + " " + this.last_name + " thy all mighty principal is watching you" );
};
(1) It's best to handle setting default values after a proper sanity check on the type(s) of value(s) passed:
var Constr = function (conf) {
if (!!conf && !(conf instanceof Object)) {
throw new Error('An invalid parameter was passed to Constr.');
}
if (!conf) { // Prevent "Can't read property 'name' of undefined."
conf = {};
}
this.name = conf.name || null; // Set defaults this way.
};
(2) You'll want to use Object.create() and Object.apply() to get this right:
var Person = function (param1, param2) {
this.param1 = param1;
this.param2 = param2;
}
Person.prototype.cough = function () {
// Do stuff.
}
var Student = function (param1, param2, paramN) {
Person.call(this, param1, param2);
this.paramN = paramN; // Define a new property on the subclass
}
// Invoke the superclass to have the subclass inherit properties and methods.
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
I need to be able to add many functions to a Javascript class and I thought that you could do that with the className.prototype = function(){} but maybe I was not correct on this.
Car.prototype.toAnotherString = function () {
return this.model + " has done " + this.miles + " miles";
};
Question: Ist he prototype declared correctly in this class and can the class Car be declared some how with out the function name>
function Car( model, year, miles ) {
this.model = model;
this.year = year;
this.miles = miles;
this.toString = function () {
return this.model + " has done " + this.miles + " miles";
};
Car.prototype.toAnotherString = function () {
return this.model + " has done " + this.miles + " miles";
};
}
var civic = new Car( "Honda Civic", 2009, 20000 );
var mondeo = new Car( "Ford Mondeo", 2010, 5000 );
console.log( civic.toString() );
console.log( mondeo.toString() );
console.log( civic.toAnotherString() );
console.log( mondeo.toAnotherString() );
New Code:
So is this how the prototype should be added.
function Car( model, year, miles ) {
this.model = model;
this.year = year;
this.miles = miles;
this.toString = function () {
return this.model + " has done " + this.miles + " miles";
};
}
Car.prototype.toAnotherString = function () {
return this.model + " has done " + this.miles + " miles";
};
No, it's not: the line defining a method on Car.prototype should be placed separately:
function Car (model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
this.toString = function () {
return this.model + " has done " + this.miles + " miles";
};
}
Car.prototype.toAnotherString = function () {
return this.model + " has done " + this.miles + " miles";
};
See, the major advantage of prototypes is ability to create a method function just once. But in your code, this line will be executed each time the Car constructor is called, creating a new instance of the function again and again. And that defeats the purpose of prototype.
But how this function will know about the object it's called for, you may ask? That's done with so-called function context trickery. See, when you call civic.toAnotherString(), inside toAnotherString this object will be referring to the same object as civic. And when you call mondeo.toAnotherString(), this will refer to the same object as mondeo.
But wait, there's more! You can call this method off one object, yet pass another one as its context (i.e., this):
civic.toAnotherString.call(mondeo); // or .apply(mondeo)
And, lo and behold, even though the method seems to belong to civic object, it'll actually act as though it was attached to mondeo one.
This - ability to switch this inside a method - is one of the most powerful JS features. I'd suggest studying this (really, no pun intended) tutorial on MDN up-to-bottom, as well as corresponding articles on Function.call, Function.apply and Function.bind.
Prototyping should be done outside of the initializing function, like this:
function Car( model, year, miles ) {
this.model = model;
this.year = year;
this.miles = miles;
this.toString = function () {
return this.model + " has done " + this.miles + " miles";
};
}
Car.prototype.toAnotherString = function () {
return this.model + " has done " + this.miles + " miles";
};
I am following an online tutorial and I am at a prototype section. My alert comes back with
function() { return this.brand + ' ' + this.model; }
Anyone know the reason?
function Car(model, brand) {
this.model = model;
this.brand = brand;
}
Car.prototype.fullName = function() {
return this.brand + ' ' + this.model;
}
var s = new Car("G5", "Pontiac");
var full = s.fullName;
alert(full);
s.fullName is the function itself. If you wanted to call this function you would have to write s.fullName().