I write a extend method to achieve inheritance in javascript:
function Class() {}
Class.prototype.create = function () {
var instance = new this();
instance.init();
return instance;
}
// extend method
Class.extend = Class.prototype.extend = function (props) {
var SubClass = function () {};
SubClass.prototype = Object.create(this.prototype);
for (var name in props) {
SubClass.prototype[name] = props[name];
}
SubClass.prototype.constructor = SubClass;
if (this.prototype.init) {
SubClass.prototype.callSuper = this.prototype.init;
}
SubClass.extend = SubClass.prototype.extend;
SubClass.create = SubClass.prototype.create;
return SubClass;
}
// level 1 inheritance
var Human = Class.extend({
init: function () {
}
});
// level 2 inheritance
var Man = Human.extend({
init: function () {
this.callSuper();
}
})
// level 3 inheritance
var American = Man.extend({
init: function () {
this.callSuper();
}
})
// initilization
American.create();
Then the develop tool report Maximum call stack size exceeded
I think the callSuper method cause the problem, callSuper call init, and init call callSuper, both with the same context.
But I don't know how to fixed it!
Can anyone could help me? How to set the correct context?
You have a scope problem. Here is the solution:
function Class() {}
Class.prototype.create = function () {
var instance = new this();
instance.init();
return instance;
}
// extend method
Class.extend = Class.prototype.extend = function (props) {
var SubClass = function () {},
self = this;
SubClass.prototype = Object.create(this.prototype);
for (var name in props) {
SubClass.prototype[name] = props[name];
}
SubClass.prototype.constructor = SubClass;
if (this.prototype.init) {
SubClass.prototype.callSuper = function() {
self.prototype.init();
}
}
SubClass.extend = SubClass.prototype.extend;
SubClass.create = SubClass.prototype.create;
return SubClass;
}
// level 1 inheritance
var Human = Class.extend({
init: function () {
console.log("Human");
}
});
// level 2 inheritance
var Man = Human.extend({
init: function () {
console.log("Man");
this.callSuper();
}
})
// level 3 inheritance
var American = Man.extend({
init: function () {
console.log("American");
this.callSuper();
}
})
// initilization
American.create();
The key moment is to wrap init method in a closure:
SubClass.prototype.callSuper = function() {
self.prototype.init();
}
Here is a jsfiddle containing the solution http://jsfiddle.net/krasimir/vGHUg/6/
Related
So i have this code:
function Class1() {
this.i = 1;
var that=this;
function nn() {
return 21;
}
this.aa = function() {
nn();
};
this.bb = function() {
this.aa();
};
this.cc = function() {
this.bb();
};
}
var o = new Class1();
var b=o.cc();
alert(b); //undefined
But when the alert is fired, I get an undefined error and not 21, Does the private method can not use a return? Thanks!
When using the function() {} syntax to define a function, you always explicitly need to return the value, i.e. not only from nn, but from all intermediate functions as well.
function Class1() {
this.i = 1;
var that = this;
function nn() {
return 21;
}
this.aa = function() {
return nn();
}
this.bb = function() {
return this.aa();
}
this.cc = function() {
return this.bb();
}
}
var o = new Class1();
var b = o.cc();
alert(b); // "21"
Apart from the answer above, the 'this' context seems weird in your functions. Maybe you are better of with arrow functions if you dont want to bind the this context to each function. I also think that it is better to actually separate private and public functions when using a 'class' like this.
function Class1() {
var _nn = function () {
return 21;
}
var _aa = function () {
return _nn();
}
var _bb = function () {
return _aa();
}
var cc = function () {
return _bb();
};
return {
cc
};
}
var o = new Class1();
var a = o.cc();
console.log(a);
Much easier to understand that it is only cc that is a public function.
So with arrow function it would instead look like this, and you can use the Class1 this context inside of your private functions without doing
var that = this; or using bind.
function Class1() {
this.privateThing = 'private';
var _nn = () => { return this.privateThing; };
var _aa = () => { return _nn(); };
var _bb = () => { return _aa(); };
var cc = () => { return _bb(); };
return {
cc
};
}
I searched for this issue for quite a long time. Din't find any answer to satisfy my question. What I am trying is:
function myClass() {
function privateFunction () {
publicFunction(); //Error
}
}
myClass.prototype.publicFunction = function() {
this.variable = 1;
}
myClass.prototype.publicFunction2= function() {
return this.variable;
}
This is giving me error. I am not getting what the real problem is:
What I tried:
this.publicFunction();
Then:
myClass.publicFunction();
Then:
myClass.prototype.publicFunction();
This works but it overrides for each object. Acts as if it is static across different JS objects.
You haven't declared the prototype functions correctly. You are also missing the this keyword when calling the function publicFunction.
The private function (privateFunction) is not a member of the class, so if you want to call it as a function, you have to specify the context for it.
function myClass() {
function privateFunction () {
this.publicFunction();
}
privateFunction.call(this);
document.write(this.publicFunction2()); // show value in Stackoverflow snippet
}
myClass.prototype.publicFunction = function() {
this.variable = 1;
}
myClass.prototype.publicFunction2 = function() {
return this.variable;
}
var myClassPrototype = new myClass();
Wouldn't a closure be enough?
First, I'd rename myClass to MyClass by convention
function MyClass() {
var myInstance = this;
function privateFunction () {
// closure
myInstance.publicFunction();
}
}
MyClass.prototype.publicFunction = function() {
this.variable = 1;
}
MyClass.prototype.publicFunction2= function() {
return this.variable;
}
Now you should be able to instanciate it this way
var myInstance = new MyClass();
Now you can see that privateFunction is never called, and it woud be a bit redundant to call it, but I just tried to show how to technically achieve it.
Try this:
function myClass() {
function privateFunction(obj) {
obj.privilegedFunction1();
};
this.privilegedFunction1 = function () {
this.variable = 1;
};
this.privilegedFunction2 = function () {
privateFunction(this);
};
}
myClass.prototype.publicFunction2 = function () {
return this.variable;
}
var test = new myClass();
test.privilegedFunction2();
console.log(test.publicFunction2());
And this:
function myClass() {
function privateFunction(obj) {
obj.publicFunction1();
};
this.privilegedFunction2 = function () {
privateFunction(this);
};
}
myClass.prototype.publicFunction1 = function () {
this.variable = 1;
}
myClass.prototype.publicFunction2 = function () {
return this.variable;
}
var test = new myClass();
test.privilegedFunction2();
console.log(test.publicFunction2());
You may want to read some about public vs private vs privileged members in Javascript. Like this article: http://javascript.crockford.com/private.html
Key points:
Public (prototype) members have no access to private.
Privileged (this) members have access to private.
Private function may access privileged and public members through the passed context parameter.
You aren't accessing it because it's inside a private function.
Try it like this:
function myClass() {
function privateFunction () {
}
this.publicFunction = function() {
alert('ok')
}
}
then if you do
var obj = new myClass()
obj.publicFunction()
you can see the alert
In order to inherit the class you will need some other things. Here is a complete example.
Now here is the relevant js code. Put it in a file to test it:
function Operators() {
//mandatory
var self = this
//private
var IPT_X = '#x'
var IPT_Y = '#y'
//public
this.x = 0
this.y = 0
this.showOperators = function() {
//use of a private property (IPT_X) and a public property (this.x)
$(IPT_X).val(this.x)
$(IPT_Y).val(this.y)
}
this.clean = function() {
this.x = 0
this.y = 0
// call to a local public method
this.showOperators()
}
this.updateOperators = function(_x, _y) {
// use of a public property when call from
// derived class method is necessary
self.x = _x
self.y = _y
}
}
function Randomizer() {
// mandatory for derived classes
Operators.call(this)
// mandatory for overloaded methods with call to the inherited method
var parentUpdateOperators = this.updateOperators
var self = this
// private
function getRandomNumber() {
return Math.round(Math.random() * 1000)
}
// public
this.updateOperators = function(_x, _y) {
// call to inherited method of superior class
parentUpdateOperators(_x, _y)
// call to method of superior class
self.showOperators()
}
this.populateRandomNumbers = function() {
// call to public local method (this.updateOperators())
// and to a local private method (getRandomNumber())
this.updateOperators(getRandomNumber(), getRandomNumber())
}
// init
this.populateRandomNumbers()
}
// Mandatory for derived classes. Allows access to superior classes with
// more than 2 levels of inheritance ("grandfather" classes)
Randomizer.prototype = Object.create(Operators.prototype)
function Operations() {
Randomizer.call(this)
var self = this
//private
var IPT_RES = '#res'
var BTN_SUM = '#sum'
var BTN_SUBTRACT = '#subt'
var BTN_MULTIPLY = '#mult'
var BTN_DIVISION = '#div'
var BTN_CLEAN = '#clean'
var BTN_RAND = '#rand'
function calcSum() {
return self.x + self.y
}
function calcSubtraction() {
return self.x - self.y
}
function calcMultiplication() {
return self.x * self.y
}
function calcDivision() {
return self.x / self.y
}
function showRes(val) {
$(IPT_RES).val(val)
}
//public
this.sum = function() {
// call to 2 local private methods
showRes(calcSum())
}
this.subtract = function() {
showRes(calcSubtraction())
}
this.multiply = function() {
showRes(calcMultiplication())
}
this.division = function() {
showRes(calcDivision())
}
// init
$(BTN_SUM).on('click', function() { self.sum() })
$(BTN_SUBTRACT).on('click', function() { self.subtract() })
$(BTN_MULTIPLY).on('click', function() { self.multiply() })
$(BTN_DIVISION).on('click', function() { self.division() })
$(BTN_CLEAN).on('click', function() { self.clean() })
$(BTN_RAND).on('click', function() { self.populateRandomNumbers() })
}
Operations.prototype = Object.create(Randomizer.prototype)
var obj = new Operations()
If you're going to test it here is the html code:
X: <input id='x'>
<br>
Y: <input id='y'>
<br>
Res: <input id='res'>
<br>
<input id='sum' type='button' value='+'>
<input id='subt' type='button' value='-'>
<input id='mult' type='button' value='*'>
<input id='div' type='button' value='/'>
<input id='clean' type='button' value='C'>
<input id='rand' type='button' value='Rand'>
don't forget to add the jquery file.
Here is a JSFiddle with that code in it:
http://jsfiddle.net/vqqrf2cb/24/
I have this function: (which is I guess abstract factory for creating javascript objects)
var $class = function(definition) {
var constructor = definition.constructor;
var parent = definition.Extends;
if (parent) {
var F = function() { };
constructor._superClass = F.prototype = parent.prototype;
constructor.prototype = new F();
}
for (var key in definition) {
constructor.prototype[key] = definition[key];
}
constructor.prototype.constructor = constructor;
return constructor;
};
A use it for defining classes C/java syntax like with polymorphism and extending:
var Bullet = $class({
Extends: GameObject,
constructor: function(texturePath, x, y, ctx, direction, passable, player) {
GameObject.call(this, texturePath, x, y, ctx, 1, 1, passable, new Array(8, 8, 0, 0));
},
isActive: function() {
},
getPlayer: function() {
},
update: function(dt) {
},
destroy: function() {
},
processCollision: function() {
}
});
And then calling:
var bullet = new Bullet(params);
I tried to rewrite it into nodejs module like this:
(function(){
var $class = function(definition) {
var constructor = definition.constructor;
var parent = definition.Extends;
if (parent) {
var F = function() { };
constructor._superClass = F.prototype = parent.prototype;
constructor.prototype = new F();
}
for (var key in definition) {
constructor.prototype[key] = definition[key];
}
constructor.prototype.constructor = constructor;
return constructor;
};
module.exports.createClass = function() {
return $class();
}
});
And then call it with:
var c = require(__dirname + "\\Class");
var Bullet = c.createClass({
Extends: GameObject,
constructor: function() {}
});
But it doesn't work, can you please help me with rewriting?
UPDATE: I rewrited it from #Salem answer, but I lost extending and polymorphism in process.
In order to have extending I simply have to write instead of
Extends: ParentClass
this:
Extends: ParentClass.constructor
I've expected that polymorphism would be something like:
// in class which is extended from ParentClass
ParentClass.method();
// in parent class adding line
module.exports.method = ParentClass.method;
But this is undefined. So where is the catch?
FINALLY I used mochiscript module for nodejs, it is even better syntax sugar with more object oriented functionality.
In your code, createClass is a function without any parameter. Also you call $class without any paramter also.
You don't need to wrap all your code in a function, because everything you declare there won't be accessible from outside unless you export it. So it should be something like this:
var func = function(definition) {
var constructor = definition.constructor;
var parent = definition.Extends;
if (parent) {
var F = function() { };
constructor._superClass = F.prototype = parent.prototype;
constructor.prototype = new F();
}
for (var key in definition) {
constructor.prototype[key] = definition[key];
}
constructor.prototype.constructor = constructor;
return constructor;
};
module.exports.createClass = func;
This means that if you require this module as X, the only thing you can access is X.createClass, and not X.func or anything else.
I am trying to get this function to get the correct scope for its "this" operator, but no luck. Inside the AssetName = function(options){ code block, I want the "this" to point to the class AssetName. What is it that I am missing? The scope of this right from the beginning is window.
Assetname: function(options){
var Base = WM.Utility.GenericFilter()
options = options;
if (typeof Object.create !== "function") {
// For older browsers that don't support object.create
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
var AssetName = {};
AssetName = function(options){
return function(){
var self = this;
debugger;
// Call the super constructor.
Base.call(this, options);
this.$mod.on('change', '#asset-name-quick-search', self,
this.search);
this.$mod.on('click', '.close', self, this.remove);
this.initTypeAhead();
this.$selectionList = this.$mod.find("#asset-name-selection-list");
this.assetListItems = [];
return this;
}(options, AssetName);
}
// The AssetName class extends the base GenericFilter class.
AssetName.prototype = Object.create(Base.prototype);
AssetName.prototype.initTypeAhead = function(){
var options = {};
options.source = _.pluck(this.collection, 'asset_name');
options.items = 8;
this.$mod.find('#asset-name-quick-search').typeahead(options);
};
AssetName(options);
return AssetName;
},
AssetName = function(options){
return function(){
var self = this;
debugger;
// Call the super constructor.
Base.call(this, options);
this.$mod.on('change', '#asset-name-quick-search', self, this.search);
this.$mod.on('click', '.close', self, this.remove);
this.initTypeAhead();
this.$selectionList = this.$mod.find("#asset-name-selection-list");
this.assetListItems = [];
return this;
}(options, AssetName);
}
change to
AssetName = function(options){
var aa = function(){
var self = this;
debugger;
// Call the super constructor.
Base.call(this, options);
this.$mod.on('change', '#asset-name-quick-search', self, this.search);
this.$mod.on('click', '.close', self, this.remove);
this.initTypeAhead();
this.$selectionList = this.$mod.find("#asset-name-selection-list");
this.assetListItems = [];
return this;
};
aa.call(AssetName,options);
}
In your code, the function aa is called as aa(options); so this is window.
[update]
I fix the bug with the following code:
AssetName = function (options) {
AssetName = function (options) {
var aa = function () {
alert(this);
return this;
};
aa.call(this, options);
}
AssetName.prototype.initTypeAhead = function () {
alert(1);
}
return new AssetName(options);;
};
var test = AssetName();
test.initTypeAhead();
But I suggest how about writing the code like bellow:
AssetName = function (options) {
AssetName = function (options) {
alert(this);
}
AssetName.prototype.initTypeAhead = function () {
alert(1);
}
return new AssetName();
};
var test = AssetName();
test.initTypeAhead();
You cam just move your var self = this out side of the anonymous returned function. Then you can use just use self.
Suppose I have a constructor:
function Constructor(input) {
this.input = input
}
Constructor.prototype.method = function() {
console.log('a')
}
But I want to make another class using a copy of the constructor, but changing the prototypes.
function Constructor2(input) {
this.input = input
}
Constructor2.prototype.method = function() {
console.log('b')
}
I don't want to redefine the constructor. How would you do this? Ideally it would be something as simple as:
var Constructor2 = inherits(Constructor)
Constructor2.prototype.method = // overwrite the inherited `method()`
var inherits = function(childCtor, parentCtor) {
/** #constructor */
function tempCtor() {};
tempCtor.prototype = parentCtor.prototype;
childCtor.superClass_ = parentCtor.prototype;
childCtor.prototype = new tempCtor();
/** #override */
childCtor.prototype.constructor = childCtor;
};
// How to use it:
var Constructor1 = function() {
//add all your methods, variables etc
};
Constructor1.prototype.myMethod = function() {
};
var Contructor2 = function() {
Contructor1.call(this); // Call the super class constructor
};
inherits(Contstructor2, Constructor1);
// Constructor2 now inherits from Constructor1
// override, add methods variables etc, whatever you need.
// Have fun!
Okay, much easier just to use apply:
function newConstructor(Super) {
function Construct() {
Super.apply(this, arguments)
}
require('util').inherits(Construct, Super)
return Construct
}
Here's a nasty-ish solution:
function Constructor1(input) {
this.input = input;
}
Constructor1.prototype.method = function() {
console.log('a');
}
// be careful here: evals the string value of Constructor1 with references to "Constructor1" changed to "Constructor2"
eval(Constructor1.toString().replace("Constructor1", "Constructor2"));
Constructor2.prototype.method = function() {
console.log('b');
}
var c1 = new Constructor1(1);
var c2 = new Constructor2(2);
console.log(c1.constructor === c2.constructor) // true
c1.method() // a
c2.method() // b