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 () {};
Related
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 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
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'; }
Let's say I have the namespace,
var Namespace = {
A : function() {
alert('Hello!');
},
B : function() {
// Call A() from here, do other stuff
}
}
In this namespace, I intend for A to be a helper function to B. That is to say, A() will never be called outside the namespace. It will only be called by the functions within the namespace.
What's the best way to address the issue of a local/helper function within a namespace? The way I see it there are two possibilities:
// Method #1
var Namespace = {
A: function() {
alert('Method #1');
},
B : function() {
Namespace.A();
}
}
Namespace.B();
// Method #2
function Namespace2() {
var A = function() {
alert('Method #2');
};
this.B = function() {
A();
}
}
var ns2 = new Namespace2();
ns2.B();
In the first method, it is ugly and awkard to type Namespace.A() (repeatedly) in every function within the namespace. This leads me to prefer Method #2. But I was curious what was the best practice here.
I recommend placing the "namespace" inside a function scope. Everything not explicitly public will be naturally private:
var Namespace = (function() {
var self = {};
// Private
var A = function() {
...
};
// Public
self.B = function() {
A();
}
return self;
}());
Namespace.B(); // Works
Namespace.A(); // Doesn't work
You can call it using this statement
this.A();
Well you can event use a third option where the Namespace is created in it's own scope:
var Namespace = (function(){
var A = function() {
alert('scoped method');
};
function Namespace() {
var A1 = function() {
alert('Namespace "private" method');
};
Namespace.prototype.B1 = function(){
A(); //will run
A1(); //will run with no errors
};
};
Namespace.prototype.B = function(){
A(); //will run
A1(); //ERROR!
};
return Namespace;
})();
If you only intend to use A inside B, why not define it inside B?
var Namespace = {
B: function() {
var A = function() {
...
}
A();
}
};
Namespace.B();
var Namespace = {
A : function() {
alert('Hello!');
},
B : function() {
Namespace.A();
},
}
note the Semi-colon at the end
I've been using the below, I've seen other code using function() { } and then using the this keyword, what is the difference here, have I actually instantiated an object below?
var MyObj = {
propertyOne: 'a',
Method: (function() {
function MyFuncOne() {}
function MyFuncTwo() {}
return {
MyFuncOne: MyFuncOne,
MyFuncTwo: MyFuncTwo
}
})()
}
Yes, you've instantiated a "singleton" object with two methods.
I believe the outer curly braces are unnecessary, and you could just write:
var MyObj =
(function() {
function MyFuncOne() {}
function MyFuncTwo() {}
return {
MyFuncOne: MyFuncOne,
MyFuncTwo: MyFuncTwo
};
})();
Another way to do it is:
var MyObj =
(function() {
var obj = {};
obj.MyFuncOne = function() {};
obj.MyFuncTwo = function() {};
return obj;
})();
Wrapping your JS in (function() { /* code here */ })() is good practice for preventing variables leaking into global scope. In this case, you're using it to assemble an object.
The only reason I can think of for doing something like this is if you wanted to have some private variables that were shared between the two functions (after changing it to make it legal javascript):
var MyObj = (function() {
var x,y,z; // these will be accessible only to
// the MyFuncOne and MyFuncTwo functions
function MyFuncOneA() {}
function MyFuncTwoA() {}
return {
MyFuncOne: MyFuncOneA,
MyFuncTwo: MyFuncTwoA
}
})();
I had to change your syntax to even make it work because as you had it myObj = {{...}} which isn't very useful and may have not even been valid.
Other than this private, but shared variables notion, it's just extra (and confusing) syntax for declaring two methods on an object which there are much clearer ways to do.
If you weren't using the private variables, then the above example is functionally the same as this much simpler syntax which makes a lot more sense to me:
var MyObj = {
MyFuncOne: function() {},
MyFuncTwo: function() {}
};
Using a function with the this keyword allows you to do some more things than are possible (or, at least, easy) with an object literal (which is what your anonymous function above returns). Most commonly, creating "types".
function Animal () { }
Animal.prototype.speak = function () {
return "";
};
var dog = new Animal();
dog instanceof Animal; // returns true
This also makes inheritance easier:
function Feline () { }
Feline.prototype = new Animal;
Feline.prototype.speak = function () {
return "meow";
};
function Lion () { }
Lion.prototype = new Feline;
Lion.prototype.speak = function () {
return "roar";
};
function Cat () { }
Cat.prototype = new Feline;
var leo = new Lion();
var baxter = new Cat();
leo.speak(); // returns "roar"
baxter.speak(); // returns "meow" - from prototype chain
leo instanceof Feline; // returns true
leo instanceof Animal; // returns true
leo instanceof Cat; // returns false
Demo: http://jsfiddle.net/hEnJf/