I have such a code:
function A() {
this.hello = function() {
console.log("I'm A");
}
}
function B() {
this.hello = function() {
// I need to call A.hello here, like parent.hello();
B.prototype.hello(); // This is wrong, TypeError
console.log("I'm B");
}
}
B.prototype = new A();
var b = new B();
b.hello();
#=> TypeError: Cannot call method 'hello' of undefined
I read some similar questions here but they all use this technique, they assign a method to a prototype.
FaqPage.prototype.init = function(name, faq) {
BasePage.prototype.init.call(this, name);
this.faq = faq;
}
FaqPage.prototype.getFaq = function() {
return this.faq;
}
But it is not in my case. My prototype is a parent's instance. How may call a parent method in my case? Or do I have to refactor my code?
You need to assign the this.hello a value, at the moment you are just creating a function to run.
Try the following :
function A() {
this.hello = function() {
console.log("I'm A");
}
}
function B() {
this.hello = function() {
B.prototype.hello(); // Now runs correctly and logs "I'm A"
console.log("I'm B");
}
}
B.prototype = new A();
var b = new B();
b.hello();
By changing the code to be this.hello = function() { } we are creating a property of the object that can be called from outside the object.
The result of calling b.hello(); is :
I'm A
I'm B
Example JSFiddle
Related
Let's say I have classes A and B. Class B has a member which is an instance of class A, let's call it this.a.
When inside B's methods I try to access A's methods as this.a.<methodName>, I get the following error:
TypeError: this.a is undefined
Here is my code:
function A (name) {
this.name = name;
}
A.prototype.foo = function () {
this.name = this.name.toUpperCase();
};
A.prototype.bar = function () {
this.name = this.name.toLowerCase();
};
function B () {
this.a = new A('Hello, World!');
}
B.prototype.methodsOfA = {
foo: this.a.foo, // here is the problem
bar: this.a.bar //
};
B.prototype.executeMethodOfA = function (methodName) {
this.methodsOfA[methodName]();
//the following works if I delete B.prototype.methodsOfA:
//if (methodName.toLowerCase() === 'foo') this.a.foo();
//else if (methodName.toLowerCase() === 'bar') this.a.bar(); //
};
b = new B();
console.log(b.a.name);
b.executeMethodOfA('foo');
console.log(b.a.name);
b.executeMethodOfA('bar');
console.log(b.a.name);
Instead, if I use the following definition:
B.prototype.methodsOfA = {
foo: A.prototype.foo,
bar: A.prototype.bar
};
I get the following error:
TypeError: this.name is undefined
(probably because this in this case is b and a B object has no name property.)
So, how can I access this.a.<methodName> from inside B?
Note: This is a simplified version of a larger problem. I know that what I asked could be solved with class/prototype inheritance, but ideally I would like B not to inherit from A.
My spidey sense says there's a better way to solve this problem, but here is some working code for now..
function A (name) {
this.name = name;
}
A.prototype.foo = function () {
this.name = this.name.toUpperCase();
};
A.prototype.bar = function () {
this.name = this.name.toLowerCase();
};
function B () {
this.a = new A('Hello, World!');
}
B.prototype.methodsOfA = function (methodName) {
var methods = {
foo: this.a.foo.bind(this.a),
bar: this.a.bar.bind(this.a),
}
return methods[methodName];
};
B.prototype.executeMethodOfA = function (methodName) {
this.methodsOfA(methodName)();
};
b = new B();
console.log(b.a.name);
b.executeMethodOfA('foo');
console.log(b.a.name);
b.executeMethodOfA('bar');
console.log(b.a.name);
You had two problems:
this in the context of an object didn't refer to the this you were thinking of. It was referring to the window, because methodsOfA, as a plain object, won't be injected with this. I changed it to a proper method, and now this is the this you want.
You need to bind the A methods to your local instance of A itself. This is because methods are only given the expected this pointer when invoked in the style a.foo(). Assigning a.foo to another name and invoking it, as you're doing, will lose the this context. You can force the correct context with bind() as you see above.
The way you referning this is wrong. In your case this refers to the global context i.e) window.
you can simply use Object.create(A.prototype) to simply get all methods of A.
while executing this.methodsOfA[methodName]() - it calls the A's method with B's context. So, there is no property called name in your B context - it fail.
you have to call A's method with the context of A which is stored in B's context i.e) this.a
this.methodsOfA[methodName].call(this.a);
function A (name) {
this.name = name;
}
A.prototype.foo = function () {
this.name = this.name.toUpperCase();
};
A.prototype.bar = function () {
this.name = this.name.toLowerCase();
};
function B () {
this.a = new A('Hello, World!');
}
B.prototype.methodsOfA = Object.create(A.prototype);
B.prototype.executeMethodOfA = function (methodName) {
this.methodsOfA[methodName].call(this.a);
};
b = new B();
console.log(b.a.name);
b.executeMethodOfA('foo');
console.log(b.a.name);
b.executeMethodOfA('bar');
console.log(b.a.name);
I'd like to add a method "bar" to a parent class A, after a subclass B are is defined, so that the method is inherited. Is it possible?
I tried the following code
function A() {
this.foo = function () {
console.log('foo')
}
}
function B() {
A.call(this)
}
// (trying to) add a new method to A
A.prototype.bar = function () {
console.log('bar');
}
// It works with instances of A
var a = new A()
a.foo() // ok
a.bar() // ok
// but not with an instance of B
var b = new B()
b.foo() // this works
b.bar() // not this one <------
/*
Exception: b.bar is not a function
#Scratchpad/3:17:1
*/
Any suggestion, please?
If you need just fix your code, you can link methods like this:
function B() {
A.call(this)
for(var i in A.prototype){ this[i] = A.prototype[i]; }
}
But i think it is bad way.
function A() {
this.foo = function () {
console.log('foo');
};
}
function B() {
A.call(this);
}
// (trying to) add a new method to A
A.prototype.bar = function () {
console.log('bar');
};
B.prototype = Object.create(A.prototype);
// It works with instances of A
var a = new A() ;
a.foo() ; // ok
a.bar() ; // ok
// but not with an instance of B
var b = new B() ;
b.foo() ; // this works
b.bar() ;
I case of functional-type of inheritance - you can't add methods, that don't exists in class. Use prototypes
// if you define the prototype as an object
var A = {
foo: function() {
console.log('foo');
}
};
// and define constructors using Object.create
function newA() {
return Object.create(A);
};
function newB() {
return Object.create(newA());
};
// you can add methods to the prototype
A.bar = function () {
console.log('bar');
};
// it works with instances of A
var a = newA()
a.foo();
a.bar();
// and instances of B
var b = newB();
b.foo();
b.bar();
// you can even modify the prototype after the fact
A.baz = function() {
console.log('baz');
};
// and that will work as well
a.baz();
b.baz();
http://jsfiddle.net/5s8ahvLq/
If you don't want the latter behavior of being able to edit the protoype after the fact, use Object.assign or something like underscore or lodash that provides that functionality:
function newA() {
return Object.create(Object.assign({}, A));
}
You're missing:
B.prototype.__proto__ = A.prototype
If you don't like using __proto__ you can use:
B.prototype = Object.create(A.prototype);
I'm trying to do inheritance in javascript. First of all, looking on the web I found this
function A() {}
function B(){}
B.prototype = new A() ;
B.prototype.constructor = B ;
This works, however when I use the prototype property of B it doesn't work anymore ( http://jsfiddle.net/jeanluca/eQBUx/ )
function A() {}
A.prototype.bar = function(){ return 'A'; }
function B() {}
B.prototype.bar = function(){ return 'B'; }
I realize you could do
function B(){ this.bar = function(){ ... } } ;
But I think this is definitely slower than defining it using the prototype. So how could I do inheritance in the second situation ?
Thnx
Here's your code:
function A() {}
A.prototype.bar = function(){ return 'A';}
function B() {}
B.prototype.bar = function(){ return 'B'; }
B.prototype = new A() ; // replaces B's "bar" with A's "bar
var b = new B ;
console.log(b.bar());
As you can see the problem is in your 6th line. You're first setting B.prototype.bar to a function in line 5 and then you immediately set B.prototype to new A in line 6 (effectively undoing what you did in line 5). The solution is to put line 6 before line 5:
function A() {}
A.prototype.bar = function(){ return 'A';}
function B() {}
B.prototype = new A() ; // now it will work
B.prototype.bar = function(){ return 'B'; }
var b = new B ;
console.log(b.bar());
See the demo for yourself: http://jsfiddle.net/eQBUx/1/
In addition I agree with Bergi: Stop using the new keyword.
Update: After reading your comment and understanding your problem in greater detail I would recommend you use my augment library for inheritance:
var A = Object.augment(function () {
this.constructor = function () {};
this.bar = function () {
return "A";
};
});
var B = A.augment(function (base) {
this.constructor = function () {};
this.bar = function () {
return "B" + base.bar.call(this);
};
});
var b = new B;
console.log(b.bar());
See the demo: http://jsfiddle.net/eQBUx/2/
Using this to assign properties breaks the prototype chain. It's very inefficient and you can't use it to get inheritance. So .. don't?
You're creating a property on a the prototype object which you completely replace afterwards. Do it the other way round, create the bar method on the new object. And don't use new!
function B() {}
// first create the prototype object
B.prototype = Object.create(A.prototype);
// then assign properties on it
B.prototype.bar = function(){ return 'B'; }
I'm switching from using a Javascript revealing module pattern and what I have below seems to work. What I want to know is if what I'm doing is correct and does it follow best practices. For example, is the way I'm preserving the 'this' state and calling an init function in the constructor correct?
var testApp = function(){
//Kick it off
this.init();
};
testApp.prototype = {
getUsers: function(callback){
//do stuff
},
buildUserTable: function(data){
//do stuff
},
refreshTable: function(){
//Example
this.getUsers();
},
init: function(){
//Preserve 'this'
var instance = this;
//Callback + init
this.getUsers(function(data){
instance.buildUserTable(data);
});
$('.formSection .content').hide();
$('.formSection .content:first').slideDown('slow').addClass('selected');
}
};
window.onload = function () {
var form = new testApp();
};
You're overriding the prototype completely. You can't deal with inheritance that way.
Since {} is an object you are implicitly inheriting from Object but nothing else.
Inheritance looks like this:
function A() {};
function B() {};
B.prototype = new A();
var b = new B();
console.log(b instanceof A); // "true"
B now inherits from A and Object.
If you now do:
B.prototype = {
foo: function () {}
};
var b = new B();
console.log(b instanceof A); // "false"
You're not longer inhering from A;
How to add functions to a prototype? Use this notation:
B.prototype.foo = function () {};
I hope this question makes sense. Can I ever do something like the following:
function constructor_function() {...code...};
var a = new constructor_function();
a();
If a constructor returns an object, that will be the value of the new .. expression. A function is an object, so you do what you want:
function ConstructorFunction() {
return function () { alert("A function!"); };
}
var a = new ConstructorFunction();
a();
function Wrapper(constr, func) {
return function() {
var f = func();
constr.apply(f, arguments);
return f;
}
}
function ConstructorFunction() {
return function() {
console.log("A function");
}
}
function Constructor() {
this.foo = 42;
}
var MyFunction = Wrapper(Constructor, ConstructorFunction);
var o = MyFunction();
o(); // A function
console.log(o.foo); // 42
To both manipulate the this state as intended and have the object returned be a function is difficult to do without a lot of extra hoops to jump through.
This is about as easy I could make it. You have your standard Constructor function that manipulates this as an object. Then you have your ConstructorFunction which is the function you want the constructor to return. This must be a factor that returns a new function each time it's called.
You wrap the two together to get a function which returns an object which both has the returned function and the manipulation of state.
Live example
You can return a function from the constructor, but then it's not really a constructor. You can just use the function as a regular function, the new keyword is not needed:
function constructor_function() {
return function() { alert(42); }
};
var a = constructor_function();
a();
As a function is an object, you can even add properties to it:
function constructor_function() {
var t = function() { alert(42); }
t.size = 1337;
return t;
};
var a = constructor_function();
a();
alert(a.size);