Question:
A seemingly simple question that I've been researching on and off the for past 2 weeks (please go easy as I'm new to all this!):
How does one neatly implement inheritance in JavaScript when using Require.js and the Revealing Module Pattern?
Example:
Here is an example module which is the base class of some type of 'Component':
define('Component', [], function () {
"use strict";
var _privateVar = 10;
var _doPrivateThings = function () { /* do stuff */ };
var init = function () { /* do stuff */ };
var update = function () { /* do stuff */ };
return {
init : init,
update : update
};
});
Next I want to implement CakeComponent which should inherit everything from Component and allow me to edit/add methods and properties:
define('CakeComponent', ['Component'], function (Component) {
"use strict";
// Setup inheritance
var CakeComponent = function() {}
CakeComponent.prototype = new Component();
// Add/edit methods/properties
CakeComponent.prototype.newMethod = function () { /* do stuff */ };
return {
init : CakeComponent.init,
update : CakeComponent.update,
newMethod : CakeComponent.newMethod
};
});
Firstly, I'm not sure if that makes complete sense, but secondly, my CakeComponent feels a bit gross because now I've got this CakeComponent redundancy everywhere and I've had to 're-reveal' the init and update methods.
I would really prefer something like this (I realise this doesn't make sense, it's really just pseudo-code):
define('CakeComponent', ['Component'], function (Component) {
"use strict";
this.extends(Component);
var newMethod = function () { /* do stuff */ };
return {
newMethod : newMethod
};
});
Any tips or suggestions would really be appreciated. Thanks.
Further Details
Maybe I should always be creating a class object within the define wrapper? I've seen people do this but it seemed unnecessary until I came across this problem.
Would the .call() method on the function object be useful at all in this context? e.g. using Component.call()
#Bergi please see below:
define([], function () {
"use strict";
var Component = function () {
var _privateVar = 10;
var _doPrivateThings = function () { /* do stuff */ };
this.init = function () { /* do stuff */ };
this.update = function () { /* do stuff */ };
};
return Component;
});
I have seen this model used before, called Universal Module Definition:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['Component'], factory);
} else {
root.CakeComponent = factory(root.Component);
}
}(this, function (Component) {
return {
newMethod: function(){ /* do stuff */ }
};
}));
You could try this, which is not "real" inheritance - in case it does not work - depending on environment, you might need to pass the base functions as well, which is a pity:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['Component'], factory);
} else {
root.CakeComponent = factory(root.Component);
}
}(this, function (Component) {
return {
init: Component.init,
update: Component.update,
newMethod: function(){ /* do stuff */ }
};
}));
You can read more to this Topic in this fantastic article about the Universal Module Definition
Related
I am optimizing my code for user with JSlint, i came to a small issue that i am trying to find a solution to. In the code below which works, JSlint complains about .bind(this). If i remove .bind(this), then the code does not know what is "this.selectorCache.get()" nor "this.someFunc()".
Is there a way to get this code working by removing .bind(this)?
/*jslint this:true, devel: true */
/*global jQuery, $, window, SelectorCache */
"use strict";
$(function () {
window.myApp = (function () {
var _this = this;
this.selectorCache = new SelectorCache();// selector cache function
this.someFunc = function () {
return 0;
};
this.selectorCache.get('#someID').click(function () {
if (_this.this.selectorCache.get('#someOtherID').val() === 1){
console.log(_this.someFunc());
}
}.bind(this));
}.bind(this));
}.bind(this));
Store the this context to another variable and use it in the callback.
I'd recommend you to use the bind(this) though and to find out why exactly JSLint complains.
window.myApp = (function () {
var _this = this;
this.selectorCache = new selectorCache();// selector cache function
this.someFunc = function () {
return 0;
}
this.selectorCache.get('#someID').click(function () {
if _this.selectorCache.get('#someOtherID').val() === 1{
console.log(_this.someFunc());
}
});
}
The solution #Erazhihel proposed should fix the problem. Another minimal way to fix it is by using ES6's arrow function instead.
/* global jQuery, $, window */
"use strict";
$(function() {
window.myApp = (function () {
this.selectorCache = new selectorCache();// selector cache function
this.someFunc = function (){
return 0;
}
this.selectorCache.get('#someID').click(() => {
if (this.selectorCache.get('#someOtherID').val() === 1){
console.log(this.someFunc());
}
};
});
});
(function (window, $, undefined) {
"use strict";
var methods = {};
var thingOnPage = $('.class');
methods.init = function () {
};
methods.1 = function() {
};
methods.2= function() {
};
methods.3= function() {
};
if (thingOnPage.length > 0) {
methods.init();
}
}(window, jQuery));
This gets compiled and included on every page, but the init method will only be called if there is a specific class on the page.
My question is, would I be able to make this more efficient if the conditional statement wrapped the declaration of all the methods?
Thanks
I'm using John Resig's simple OOP Class that is adapted to use "use strict" and taken from SO post.
In all examples I see the usage of Class.extend like so:
var MyObj = Class.extend({
init:function(){},
prop: "Property"
});
But I found a large disadvantage for me of using it in such way - I cannot have "private" variables, so I cannot store reference to this like var $this = this;.
I found the solution for my case, and now I using the Class.extend in following way:
var MyObj = Class.extend(new function(){
var $this = this;
this.init = function(){};
this.prop = "Property";
});
Everything works in my case, but I want to know if there some things that can cause me problems in a long run?
Does this way my application will consume much more memory in browser?
What alternative ways I have to implement my needs?
Note: I need to store $this, because I use heavily events and callbacks, so I want to refer "original" this easy to have access to all methods and properties on object.
EDIT: As requested, this is my code example:
(function () {
"use strict";
window.QuickPlay = Class.extend(new function () {
var $this = this;
this.init = function (initData) {
$this.elementsToHide.push(initData.el);
$(function () {
playProcessStart();
Sys.Application.add_load(function () {
$find("ctl00_ContentPlaceHolderMain_ctrlPlayPopup1").add_closed(function () { $this.setElementsVisibility(""); });
});
$this.setElementsVisibility("hidden");
});
};
this.elementsToHide = [];
this.setElementsVisibility = function (visibility) {
$.each($this.elementsToHide, function (i) {
$("#" + this).css("visibility", visibility);
});
};
});
} ());
You can use module pattern and maintain all the OOP. These kind of pattern gives your code more security and better organization.
//these are namespaces in javascript
window.project = window.project || {}; //this kind declarations prevents recreate the object
project.group = project.group || {};
//in the line below we can use $ instead jQuery, and use window and document instead ask for the browser every time.
(function (window, document, $) {
"use strict";
project.group.NameOfYourModule = function () {
var privateAttribute = true,
students = 32, //It's is a best practice declare everything in an unique var.
privateMethod = function () {
alert('Now I know OOP using jQuery');
};
return {
init: function () {
//this is a public method and we can initiate some private method;
privateMethod();
//we call a public method using this
this.publicMethod();
},
publicMethod: function () {
//this is a public method
}
};
};
$(function () {
var myclass = new project.group.NameOfYourModule(); //instantiate you class
myclass.init(); //initiate some public method
});
}(window, document, jQuery));
Working example at JsFiddle
How to work with Inheritance and Module Pattern here
I cannot have "private" variables
Of course you can. Either in the (currently unnecessary) (function () { … } ()); wrapper, or in your constructor (the init thing).
new function () {
Avoid that pattern! If you really need your code to work as it does now, use
(function () {
"use strict";
// Here's the place where you could put a private, static variable
// for example `var elementsToHide = [];`
var $this = {
init: function (initData) {
$this.elementsToHide.push(initData.el);
$(function () {
playProcessStart();
Sys.Application.add_load(function () {
$find("ctl00_ContentPlaceHolderMain_ctrlPlayPopup1").add_closed(function () {
$this.setElementsVisibility("");
});
});
$this.setElementsVisibility("hidden");
});
},
elementsToHide: [],
setElementsVisibility: function (visibility) {
$.each($this.elementsToHide, function (i) {
$("#" + this).css("visibility", visibility);
});
}
};
window.QuickPlay = Class.extend($this);
}());
I want to know if there are some things that can cause me problems
Yes. Multiple instances will hardly work, as they all do reference the same elementsToHide array. And you're not using any instance methods at (only a constructor and static elements on your class), so the class pattern seems quite unnecessary. Use a module instead. If you need single instances (and classes), the code should look like this:
"use strict";
window.QuickPlay = Class.extend({
init: function (initData) {
var $this = this;
this.elementsToHide = [];
$(function () {
playProcessStart();
$this.elementsToHide.push(document.getElementById(initData.el));
Sys.Application.add_load(function () {
$find("ctl00_ContentPlaceHolderMain_ctrlPlayPopup1").add_closed(function () {
$this.setElementsVisibility("");
});
});
$this.setElementsVisibility("hidden");
});
},
setElementsVisibility: function (visibility) {
$(this.elementsToHide).css("visibility", visibility);
}
});
I have the following code in my javascript module, however this requires me to make the functions visible to the outside world.
var mymodule = function() {
var self = null,
init = function () {
self = this;
$('.actionButton').click(function () {
var worklistId = $(this).data('worklistid'),
action = $(this).data('action');
self[action] && self[action](worklistId); //watchout methods marked as not used are used by this invocation
})
},
send = function () {
// some logic
},
finish = function () {
// some logic
},
delete = function () {
// some logic
};
return {
init: init,
send: send,
finish: finish,
delete: delete
};
}();
mymodule.init();
So the only thing I want to return in my module is the init function. However when I do this I cant invoke the functions, because the object (self) only contains the init function visible on the outside.
return {
init: init
};
Is there any solution to invoke my functions like this without making them visible to the outside world? Please no if else statements, because my workflow is bigger then the 3 actions in this example. I want to make my module as closed as possible because this reduces the dependencies.
Update
Here is a updated jsfiddle with one of the proposed solutions, however this is giving me another issue. http://jsfiddle.net/marcofranssen/bU2Ke/
Something like this would work:
var mymodule = function() {
var self = this;
init = function () {
$('.actionButton').click(function () {
var worklistId = $(this).data('worklistid'), action = $(this).data('action');
self[action] && self[action](worklistId); //watchout methods marked as not used are used by this invocation
})
}
self.send = function () {
console.log('send');
}
self.finish = function () {
console.log('finish');
}
self.delete = function (item) {
console.log('delete');
};
return {
init: init,
};
}();
mymodule.init();
Here's the fiddle:
http://jsfiddle.net/yngvebn/SRqN3/
By setting the self-variable to this, outside the init-function, and attaching the send, finish and delete functions to self, you can use the self[action] syntax from within the init-function
Yes, there is an easy (but perhaps slightly messy) way you can do this without making the functions visible to the global object:
var privateFunctions = { deleter: deleter, send: send};
Then, instead of self[action]();, just do privateFunctions[action](); and you're good to go.
Note that I changed delete to deleter, because delete is a reserved keyword...
var mymodule = function() {
var self = {},
init = function () {
$('.actionButton').click(function () {
var worklistId = $(this).data('worklistid'),
action = $(this).data('action');
self[action] && self[action](worklistId); //watchout methods marked as not used are used by this invocation
})
};
self.send = function () {
// some logic
};
self.finish = function () {
// some logic
};
self.delete = function () {
// some logic
};
return{
init:init
}
}();
mymodule.init();
This should Work!!
Even if you return an object just with the init property and you populate the rest dynamically such that your module uses them, you would still be making them visible to the outside at runtime. Anyone who wants to debug your module would easily get to them.
You can still create anonymous methods at runtime and they would also be visible together with their implementation.
In your code example, it is vague what "self" really is. You should keep it simple, use encapsulated functions as "private" methods and return a "public" (or "privileged" as Crockford calls it) function that have access to them.
This is the YUI way of doing singletons with private functions and variables. Example pattern:
var mymodule = (function() {
var internal = {
'send': function() {},
'finish': function() {},
'delete': function() {}
};
return {
'init': function(action) {
// access to internals, f.ex:
if ( internal.hasOwnProperty(action) ) {
internal[action].call(this); // bring the caller context
}
}
};
}());
mymodule.init('send');
I'm used to using this pattern all over my code, and I like it:
var UserWidget = (function(){
var url = "/users",
tmpl = "#users li", $tmpl;
function load() {
$tmpl = $(tmpl);
$.getJSON(url, function(json){
$.each(json, function(i, v) {
appendUser(v);
});
});
}
...
return {
load: load
};
})();
However, I have many "widget" objects. "ProfileWidget", "PlayerWidget" etc etc. and there's certain actions that each widget share. So ideally, if we're thinking object-orientally, I want each widget object to inherit some methods from a main "Widget" class.
How can I do this without changing this lovely pattern I've been using?
To be more clear I'd like to be able to do something like this:
var Widget = (function() {
function init() {
console.log("wow yeah");
}
})();
// have UserWidget inherit somehow the Widget stuff
var UserWidget = (function() { ...
UserWidget.init(); // -> "wow yeah"
Keep in mind these solutions are not what I'd typically reccomend and they are just to satisfy the question.
What about closing over everything so that its accessible from your "sub classes" (demo)
var Widget = (function () {
var init = function () {
console.log("wow yeah");
};
var User = (function () {
var load = function () {
init();
};
return {
'load': load
};
} ());
return { 'User': User };
} ());
// Usage: This loads a user and calls init on the "base"
Widget.User.load();
Another way (demo) that you might like is to just use proper inheritance, but within the closure and then return one and only one instance of that new function. This way lets you keep User and whatever else an object
// Closing around widget is completely unneccesarry, but
// done here in case you want closures and in case you
// dont want another instance of widget
var Widget = (function () {
// definition that we'll end up assigning to Widget
function widget() {
console.log("base ctor");
}
// sample method
widget.prototype.init = function () {
console.log("wow yeah");
};
// put widget in Widget
return widget;
} ());
var User = (function () {
function user() { }
user.prototype = new Widget();
// TODO: put your User methods into user.prototype
return new user();
} ());
var Player = (function () {
function player() { }
player.prototype = new Widget();
// TODO: put your Player methods into player.prototype
return new player();
} ());
User.init();
Player.init();
I decided to use Crockford's object:
// function from Douglas Crockford, comments from me
function object(o) {
// define a new function
function F() {}
// set the prototype to be the object we want to inherit
F.prototype = o;
// return a new instance of that function, copying the prototype and allowing us to change it without worrying about modifying the initial object
return new F();
}
// Usage:
var Widget = (function() {
function init() {
console.log("wow yeah");
}
return {
init: init
};
})();
var UserWidget = (function() {
var self = object(Widget); // inherit Widget
function priv() {}
self.pub = "boom";
...
return self;
})();
UserWidget.init() // -> "wow yeah"
This works great for me and I like it!
You could use Object.create(obj), which I believe is what you're looking for.
Without using new, you'll have to use the __proto__ property rather than prototype, so this won't work in all browsers.
var Widget = {
init: function () {
console.log("wow yeah");
}
};
var UserWidget = (function(){
var url = "/users",
tmpl = "#users li",
$tmpl;
function load() {
$tmpl = $(tmpl);
$.getJSON(url, function(json){
$.each(json, function(i, v) {
appendUser(v);
});
});
}
return {
load: load
};
})();
UserWidget.__proto__ = Widget;
UserWidget.init();
Demo: http://jsfiddle.net/mattball/4Xfng/
Here's a simple example of prototyping in JS... For more detail on this topic read "JavaScript: The Good Parts"
// widget definition
var Widget = {
init: function () {
alert('wow yeah!');
}
};
// user widget definition
var UserWidget = function () { };
UserWidget.prototype = Widget;
UserWidget.prototype.load = function () { alert('your code goes here'); }
// user widget instance
var uw = new UserWidget();
uw.init(); // wow yeah!
uw.load(); // your code goes here
Hope this helps!