This is what I have:
var Person = function(fname, lname) {
this.fname = fname;
this.lname = lname;
};
Person.prototype = {
getFullName: function() {
return this.fname + " " + this.lname;
},
doStuff: function(stuff) {
return stuff();
}
};
var john = new Person("John", "Doe");
The doStuff function works with other functions, but doing the following returns undefined undefined:
console.log(john.doStuff(john.getFullName));
What's wrong with what what I have and how can I change it to make it work?
Thanks.
It's because this doesn't refer to the object.
You could use the .bind() method in order to set the value of this:
john.doStuff(john.getFullName.bind(john));
However, that's not very flexible, therefore you could just bind it within the doStuff method:
doStuff: function(stuff) {
return stuff.apply(this);
}
If you know foo.doStuff's arg always wants to be called on foo, you can write this in doStuff
// ...
doStuff: function (stuff) {
return stuff.apply(this, Array.prototype.slice.call(arguments, 1));
}
Related
I am learning design patterns in javascript but I have a problem creating a module. I am creating a Person object inside of module and I have combined it with a constructor pattern, just beacuse I am learning it too, but nothing happens.
Can anybody help me, I don't undertand my mistake here
var myModule = (function () {
function Person(id, name) {
this.id = id;
this.name = name;
}
Person.prototype.toString = function () {
return "\nID: " + this.Id + "\nName: " + this.name;
};
return {
newPerson: function (id, name) {
return new Person(id,name);
console.log(Person.toString());
}
};
})();
var x = myModule;
x.newPerson(1, "John");
You should use
var myModule = (function () {
function Person(id, name) {
this.id = id;
this.name = name;
}
return {
newPerson: function (id, name) {
return new Person(id,name);
}
};
})();
var x = myModule;
console.log(x.newPerson(1, "John"));
Forget the toString(), most consoles can fetch the object, and display it in a much better way.
In your case you want to log the toString() of the Person constructor, which would result a string something like this:
"function Person(id, name) {
this.id = id;
this.name = name;
}"
but it does not run, because you put it after the return statement in the newPerson() function, and the return statement stops execution and returns with the results.
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;
}
I've been trying to figure out why this won't work. Would appreciate if some could help me out!
function Person(name, age) {
this.name = name;
this.age = age;
var ageInTenYears = age + 10;
this.sayNameAndAge = function() {
console.log(name + age);
}
}
Person.prototype.sayAge = function() {
console.log(this.age);
}
Person.prototype = {
sayName : function(){
console.log(this.name);
},
sayNameAfterTimeOut : function(time) {
setTimeout(this.sayName, time);
},
sayAgeInTenYears : function() {
console.log(ageInTenYears);
}
}
var bob = new Person('bob', 30);
bob.sayName();
I get this error:
Uncaught TypeError: Object #<Object> has no method 'sayAge'
You are overwriting the entire prototype by doing
Person.prototype = { /* ... */ };
which means that the sayAge method you added before is lost again. Either reverse the order of those assignments or move the sayAge into the other assignment as well.
With Person.prototype = { … };, you're rewriting the prototype object, i.e. replacing the old one with a completely new object. Cou can do that, but then make sure that you're not defining any methods beforehand (like you do with .sayAge above).
There are several things wrong with the code, I made some comments where I corrected it. If you have any questions you can comment on this answer:
function Person(name, age) {
this.name = name;
this.age = age;
//var ageInTenYears = age + 10; //<--Why var, you can't
// use this anywhere but in the Person constuctor body
this.ageInTenYears=age+10;
}
Person.prototype = {
sayName : function(){
console.log(this.name);
},
sayNameAfterTimeOut : function(time) {
// check out this link about what this can be
// https://stackoverflow.com/a/19068438/1641941
var me=this;
setTimeout(function(){
me.sayName();
}, time);
},
sayAgeInTenYears : function() {
// you defined it as var so cannot use
// ageInTenYears outside the constructor body
//console.log(ageInTenYears);
console.log(this.ageInTenYears);
}
};
Person.prototype.sayAge = function() {
console.log(this.age);
};
Person.prototype.sayNameAndAge = function() {
console.log(this.name + this.age);
};
//just for good measure, someone may do
// Person.prototype.haveBaby=function(){
// return new this.constructor();
Person.prototype.constructor=Person;
var bob = new Person('bob', 30);
bob.sayName();
More on prototype, inheritance/mixin, overriding and calling super: https://stackoverflow.com/a/16063711/1641941
It works when I write (http://jsfiddle.net/XJwvP/):
var Person = function(name) { this.name = name; }
Person.prototype = function () {
var sayHello = function (name) {
alert("Hello, " + name);
};
return {
sayHello: sayHello
};
}();
var person = new Person("Max");
person.sayHello("James");
It doesn't work when I write(http://jsfiddle.net/ZKd4R/):
Array.prototype = function () {
var sayHello = function (name) {
alert("Hello, " + name);
};
return {
sayHello: sayHello
};
}();
var array = new Array();
array.sayHello("James");
Error:
Uncaught TypeError: Object [object Array] has no method 'sayHello'
Why I cannot use the same approach for Array object?
Replacing Array.prototype like you did do not affect new Array instances, because the Array constructor is special in the language (it's a built-in constructor). The specification says at 15.4.2.1:
The [[Prototype]] internal property of the newly constructed object is set to the original Array prototype object, the one that is the initial value of Array.prototype (15.4.3.1).
If you check 15.4.3.1, you'll note that Array.prototype is also not [[Writable]]. I just tested that in Chrome console:
var ap = Array.prototype;
Array.prototype = {};
ap == Array.prototype; // true
#bfavaretto already explained why it not works. Here is the pattern you should use to avoid such problems:
function Person(name) { this.name = name; }
(function(proto) {
function sayHello(name) {
alert("Hello, " + name);
}
proto.sayHello = sayHello;
}(Person.prototype));
var person = new Person("Max");
person.sayHello("James");
(function(proto) {
function sayHello(name) {
alert("Hello, " + name);
}
proto.sayHello = sayHello;
}(Array.prototype));
var array = []; // or `new Array();`
array.sayHello("James");
Let's call it mixin prototype pattern :-)
I have the below code:
function Class () {
this.method = function () {
alert('method');
};
}
new Class().method();
And it works fine, but as I understand, for each new object will be created the function. Is there right way to do this?
Place initialization of instance varibales into the Class function, and shared methods and variables in prototype property:
function Class (var1, var2) {
this.var1 = var1;
this.var2 = var2;
}
Class.prototype.method = function () {
alert(this.var1 + ' ' + this.var2);
};
new Class('var1', 'var2').method();
My approach is almost identical to Speransky's, but I re-declare the prototype object instead of adding methods directly to it.
// Declare the Constructor function.
function MyClass (name, age) {
// Assign any member properties here.
this._name = name;
this.age = age;
}
// Redefine the prototype object to add methods.
MyClass.prototype = {
// Re-point the Constructor function as it will be overwritten.
constructor: MyClass,
// Custom method which all instances of `MyClass` will inherit.
sayHello: function () {
return "My name is " + this._name + ", how do you do?";
}
};
Usage:
var foo = new MyClass("Dave", 22);
foo.sayHello(); // "My name is Dave, how do you do?"
foo.age; // 22
If you wanted to point the MyClass prototype at another object (to setup a simple inheritance model) then you could make use of a mixin, similar to Underscore's extend method:
function BaseClass(name) {
this._name = name;
}
BaseClass.prototype = {
constructor: BaseClass,
sayHello: function () {
return "My name is " + this._name + ", how do you do?";
}
}
class MyClass (name, age) {
// Call the BaseClass constructor function in the context of `this`
BaseClass.call(this, name);
this.age = age;
}
// Mixin the BaseClass's protptype into the MyClass prototype and delcare
// the new methods.
_.extend(MyClass.Prototype, BaseClass.prototype, {
constructor: MyClass,
getAge: function () {
return this.age;
}
});