I'm new at JS programming. I have to write a php webapplication which uses ajax calls for every action (can't reload the page). So I have the php code done, and the JS as well, but I don't know what does JS OOP mean. I did google search for hours, and all I could figure out was "JS has no classm but there are tricks". So I would like to ask you to lead me to the right way and show me how to make it right.
I have four js files:
content.js, function.js, onload.js and validate.js
For now, I only do care about the function.js. It has code only for the ajax calls. Their names are:
NewMember(), SearchMembers(), ShowDetails(), DeleteMembers() and EditMember()
I'm going to show you only the first function (I don't want to waste your time by duplicating since each function is totally similar)
function NewMember() {
if (checkFields() == true) {
Loading("start");
var dataArray = {
first_name : $('#first_name').val(),
last_name : $('#last_name').val(),
birth_date : $('#birth_date').val(),
home_phone : $('#home_phone').val(),
work_phone : $('#work_phone').val(),
email : $('#email').val(),
email_again : $('#email_again').val(),
country : $('#country').val(),
city : $('#city').val(),
address : $('#address').val(),
comment : $('#comment').val()
};
var JSONString = JSON.stringify(dataArray);
$.ajax({
type: 'POST',
url : 'adapter.php',
data: {
data : JSONString,
action : 'create' },
success: function(msg) {
alert(msg);
resetFieldValues();
SearchMembers();
},
error: function() {
alert("Oops! Something went wrong, please reload the page!");
}
});
}
}
My question is, how would you re-write this by using the JS OOP? Full code is not neccessary, but please give me "easy" answers since I'm really a newbie. I'm really confused :/ Thanks for your help!
JavaScript is a completely object-oriented language.
Instead of the Classical Inheritance model that programmers are usually familiar with, JavaScript has a unique take on things with its model of Prototypical Inheritance.
Consider classical inheritance in PHP:
<?php
class Arachnid {
public $legs = 8;
function crawl(){ /***/ }
}
class Spider extends Arachnid {
function creep(){ /***/ }
}
class OrbSpider extends Spider {
function buildBeautifulWeb(){ /***/ }
}
?>
This is arguably the simplest methodology in JavaScript for the same faculty:
function Arachnid () {}
Arachnid.prototype.legs = 8;
Arachnid.prototype.crawl = function () { return "crawling" };
function Spider () { /* this is a constructor, so don't define spider's methods in here -- they'd be redefined for every constructor call (each instance) */ }
Spider.prototype = new Arachnid(); // Spider inherits from a new basic Arachnid
Spider.prototype.constructor = Spider;
Spider.prototype.creep = function () { return "creeping" }; // Attach Spider's methods to its prototype for best performance
function OrbSpider () {}
OrbSpider.prototype = new Spider(); // we wholly overwrite the natural prototype to a basic arachnid
OrbSpider.prototype.constructor = OrbSpider; // so we must replace the constructor for instanceof to work
OrbSpider.prototype.buildBeautifulWeb = function () { return "webbing" };
// Testing
var orb = new OrbSpider();
console.log( orb.buildBeautifulWeb() ); //>> webbing
console.log( orb.creep() ); //>> creeping
console.log( orb.crawl() ); //>> crawling
console.log( orb.legs ); //>> 8
console.log( orb instanceof OrbSpider ); //>> true
console.log( orb instanceof Spider); //>> true
console.log( orb instanceof Arachnid); //>> true
In JavaScript, you create Prototype Chains. When you ask an object for a property, JavaScript will first check the object itself for that property -- if it isn't found, JavaScript will check the object's prototype object. If the property isn't there either, JavaScript will check that prototype object's own prototype -- and so on, and so forth, bubbling up the Prototype Chain until the property is found (or not).
JS means JavaScript. OOP means Object Oriented Programming. This is programming JavaScript in an OOP manner. It doesn't have a formal definition of classes in the programming language per say, it has functions, variables and objects (yes, objects, even if it does not have classes). Basically your JavaScript acts as part of your controller and should call PHP functions to carry out requests on the server. Remember OOP usually takes the MVC (Model View Controller) methodology.
So this code should fire when a message is received from an action. Pass the information to the PHP on the server and the PHP should save the information (as a JSON file or in a SQL database for example).
It's important to note that "object-oriented programming" doesn't necessarily mean "class-based programming", so looking online for "how to write a class in JavaScript" will lead you up a blind alley. (JavaScript instead implements prototype-based programming which should be viewed as a different way to do OOP.)
In a lot of the simple cases you'll have in client-side scripts, you don't need to worry about inheritance, you just want to expose an object which has public methods and private state. So instead of diving right into prototypal inheritance (which takes a bit of getting used to), start by learning how to implement encapsulation, and privately scoped variables.
Here's a succinct introduction to basic public and private members, and below (and in this demo) is a quick abstract example of encapsulation (but no inheritance) using JavaScript's object model:
var my_object = function(start_value) {
/* private variables */
var some_number;
/* private methods */
function garble() {
some_number = 42;
}
/* public methods and properties */
var inst = {
'set_number': function(new_value) {
if ( new_value < 0 ) {
garble();
}
else {
some_number = new_value;
}
},
'get_number': function() {
return some_number;
},
'increment_number': function() {
some_number++;
}
}
// Initialisation
inst.set_number(start_value);
return inst;
}
var foo = new my_object(1);
foo.increment_number();
var bar = new my_object(-20);
bar.increment_number();
document.getElementById('foo').innerHTML = foo.get_number();
document.getElementById('bar').innerHTML = bar.get_number();
Note: I'm not an expert on JS OOP, and there may be gotchas or bad practices in my example above; if anyone more experienced spots one, please let me know in a comment.
Related
I've been researching the use of factory functions as an alternative to constructors in JavaScript. Most of my research has stemmed from the information provided by Eric Elliot in his blog posts such as:
http://ericleads.com/2012/09/stop-using-constructor-functions-in-javascript
Whilst I agree with most of what's said here I do miss having the automatic initialization aspect of constructors so I wanted to implement something similar within my factory function:
var myFactory = function (options) {
var inst = _.create({
initialize : function (options) {
var allowedProps = ['name', 'type'];
this.options = options;
_.extend(this, _.pick(options, allowedProps));
},
test : function () {
if(this.type === 'alpha') {
return true;
}
return false;
}
})
inst.initialize.call(inst, options);
return inst;
};
var x = myFactory({ name : 'shiny', type : 'alpha' });
var y = myFactory({ name : 'happy', type : 'beta' });
1) Is this behavior itself just re-introducing some of the problems that come with constructors?
2) Am I violating the Open/Closed principle by extending the instance with 'options' within the initialize method? It's open for extension (it can be used as a prototype) but I can also modify the prototype itself through 'options'.
I've got a bad feeling about this pattern and feel like I should be giving ultimate control of initialization to the consumer. It's heavily inspired by Backbone's method of extension which relies on constructors.
Further reading:
https://tsherif.wordpress.com/2013/08/04/constructors-are-bad-for-javascript/
1) Is this behavior itself just re-introducing some of the problems that come with constructors?
No, altough it is re-introducing constructors. Just swap initialize for constructor and you'd have nearly the same result. Only with very unidiomatic syntax :-)
2) Am I violating the Open/Closed principle by extending the instance with 'options' within the initialize method?
No, the point that the article makes how constructors would break the open/closed principle was that for swapping them out with a different factory you had to remove new all over your code (which in fact you dont). But if you extend your class, you will not have to change its invocations.
I've got a bad feeling about this pattern and feel like I should be giving ultimate control of initialization to the consumer.
Well, if you want to do that just remove the initializer and let the consumer do the extend:
var x = _.extend(myFactory(), { name : 'shiny', type : 'alpha' });
var y = _.extend(myFactory(), { name : 'happy', type : 'beta' });
In Ruby I think you can call a method that hasn't been defined and yet capture the name of the method called and do processing of this method at runtime.
Can Javascript do the same kind of thing ?
method_missing does not fit well with JavaScript for the same reason it does not exist in Python: in both languages, methods are just attributes that happen to be functions; and objects often have public attributes that are not callable. Contrast with Ruby, where the public interface of an object is 100% methods.
What is needed in JavaScript is a hook to catch access to missing attributes, whether they are methods or not. Python has it: see the __getattr__ special method.
The __noSuchMethod__ proposal by Mozilla introduced yet another inconsistency in a language riddled with them.
The way forward for JavaScript is the Proxy mechanism (also in ECMAscript Harmony), which is closer to the Python protocol for customizing attribute access than to Ruby's method_missing.
The ruby feature that you are explaining is called "method_missing" http://rubylearning.com/satishtalim/ruby_method_missing.htm.
It's a brand new feature that is present only in some browsers like Firefox (in the spider monkey Javascript engine). In SpiderMonkey it's called "__noSuchMethod__" https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/NoSuchMethod
Please read this article from Yehuda Katz http://yehudakatz.com/2008/08/18/method_missing-in-javascript/ for more details about the upcoming implementation.
Not at the moment, no. There is a proposal for ECMAScript Harmony, called proxies, which implements a similar (actually, much more powerful) feature, but ECMAScript Harmony isn't out yet and probably won't be for a couple of years.
You can use the Proxy class.
var myObj = {
someAttr: 'foo'
};
var p = new Proxy(myObj, {
get: function (target, methodOrAttributeName) {
// target is the first argument passed into new Proxy, aka. target is myObj
// First give the target a chance to handle it
if (Object.keys(target).indexOf(methodOrAttributeName) !== -1) {
return target[methodOrAttributeName];
}
// If the target did not have the method/attribute return whatever we want
// Explicitly handle certain cases
if (methodOrAttributeName === 'specialPants') {
return 'trousers';
}
// return our generic method_missing function
return function () {
// Use the special "arguments" object to access a variable number arguments
return 'For show, myObj.someAttr="' + target.someAttr + '" and "'
+ methodOrAttributeName + '" called with: ['
+ Array.prototype.slice.call(arguments).join(',') + ']';
}
}
});
console.log(p.specialPants);
// outputs: trousers
console.log(p.unknownMethod('hi', 'bye', 'ok'));
// outputs:
// For show, myObj.someAttr="foo" and "unknownMethod" called with: [hi,bye,ok]
About
You would use p in place of myObj.
You should be careful with get because it intercepts all attribute requests of p. So, p.specialPants() would result in an error because specialPants returns a string and not a function.
What's really going on with unknownMethod is equivalent to the following:
var unk = p.unkownMethod;
unk('hi', 'bye', 'ok');
This works because functions are objects in javascript.
Bonus
If you know the number of arguments you expect, you can declare them as normal in the returned function.
eg:
...
get: function (target, name) {
return function(expectedArg1, expectedArg2) {
...
I've created a library for javascript that let you use method_missing in javascript: https://github.com/ramadis/unmiss
It uses ES6 Proxies to work. Here is an example using ES6 Class inheritance. However you can also use decorators to achieve the same results.
import { MethodMissingClass } from 'unmiss'
class Example extends MethodMissingClass {
methodMissing(name, ...args) {
console.log(`Method ${name} was called with arguments: ${args.join(' ')}`);
}
}
const instance = new Example;
instance.what('is', 'this');
> Method what was called with arguments: is this
No, there is no metaprogramming capability in javascript directly analogous to ruby's method_missing hook. The interpreter simply raises an Error which the calling code can catch but cannot be detected by the object being accessed. There are some answers here about defining functions at run time, but that's not the same thing. You can do lots of metaprogramming, changing specific instances of objects, defining functions, doing functional things like memoizing and decorators. But there's no dynamic metaprogramming of missing functions as there is in ruby or python.
I came to this question because I was looking for a way to fall through to another object if the method wasn't present on the first object. It's not quite as flexible as what your asking - for instance if a method is missing from both then it will fail.
I was thinking of doing this for a little library I've got that helps configure extjs objects in a way that also makes them more testable. I had seperate calls to actually get hold of the objects for interaction and thought this might be a nice way of sticking those calls together by effectively returning an augmented type
I can think of two ways of doing this:
Prototypes
You can do this using prototypes - as stuff falls through to the prototype if it isn't on the actual object. It seems like this wouldn't work if the set of functions you want drop through to use the this keyword - obviously your object wont know or care about stuff that the other one knows about.
If its all your own code and you aren't using this and constructors ... which is a good idea for lots of reasons then you can do it like this:
var makeHorse = function () {
var neigh = "neigh";
return {
doTheNoise: function () {
return neigh + " is all im saying"
},
setNeigh: function (newNoise) {
neigh = newNoise;
}
}
};
var createSomething = function (fallThrough) {
var constructor = function () {};
constructor.prototype = fallThrough;
var instance = new constructor();
instance.someMethod = function () {
console.log("aaaaa");
};
instance.callTheOther = function () {
var theNoise = instance.doTheNoise();
console.log(theNoise);
};
return instance;
};
var firstHorse = makeHorse();
var secondHorse = makeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
This doesn't work for my use case as the extjs guys have not only mistakenly used 'this' they've also built a whole crazy classical inheritance type system on the principal of using prototypes and 'this'.
This is actually the first time I've used prototypes/constructors and I was slightly baffled that you can't just set the prototype - you also have to use a constructor. There is a magic field in objects (at least in firefox) call __proto which is basically the real prototype. it seems the actual prototype field is only used at construction time... how confusing!
Copying methods
This method is probably more expensive but seems more elegant to me and will also work on code that is using this (eg so you can use it to wrap library objects). It will also work on stuff written using the functional/closure style aswell - I've just illustrated it with this/constructors to show it works with stuff like that.
Here's the mods:
//this is now a constructor
var MakeHorse = function () {
this.neigh = "neigh";
};
MakeHorse.prototype.doTheNoise = function () {
return this.neigh + " is all im saying"
};
MakeHorse.prototype.setNeigh = function (newNoise) {
this.neigh = newNoise;
};
var createSomething = function (fallThrough) {
var instance = {
someMethod : function () {
console.log("aaaaa");
},
callTheOther : function () {
//note this has had to change to directly call the fallThrough object
var theNoise = fallThrough.doTheNoise();
console.log(theNoise);
}
};
//copy stuff over but not if it already exists
for (var propertyName in fallThrough)
if (!instance.hasOwnProperty(propertyName))
instance[propertyName] = fallThrough[propertyName];
return instance;
};
var firstHorse = new MakeHorse();
var secondHorse = new MakeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
I was actually anticipating having to use bind in there somewhere but it appears not to be necessary.
Not to my knowledge, but you can simulate it by initializing the function to null at first and then replacing the implementation later.
var foo = null;
var bar = function() { alert(foo()); } // Appear to use foo before definition
// ...
foo = function() { return "ABC"; } /* Define the function */
bar(); /* Alert box pops up with "ABC" */
This trick is similar to a C# trick for implementing recursive lambdas, as described here.
The only downside is that if you do use foo before it's defined, you'll get an error for trying to call null as though it were a function, rather than a more descriptive error message. But you would expect to get some error message for using a function before it's defined.
Introductory reading:
Prototypes as "classes"
OO JS
Following the patterns described above I create libraries/APIs as the following
var Proto = {
constructor: function () {
this.works = true;
},
method: function () {
return this.works;
}
};
Now for library users to interact with my prototypes (which do not supply factory functions) they have to instantiate and initialize the object
// instantiate
var p = Object.create(Proto);
// initialize
p.constructor();
This is an unfriendly and verbose way of forcing users to instantiate and initialize my objects.
personally since I use pd in all my applications I have the following sugar
// instantiate or initialize
var p = Proto.new();
// or without bolting onto Object.prototype
var p = pd.new(Proto);
However I think it's unkind to force pd onto users so I'm not sure what's the best way to make my libraries usable.
People create new instances of Proto and call .constructor themself
Force people to use pd
Give .create style factory functions
Give up and use new <Function> and .prototype
1 and 2 have already been mentioned.
3 would basically be
Proto.create = pd.new.bind(pd, Proto);
4 would make me sad but confirming to a known standard way of doing things increases usability.
Generally when using non-standard OO patterns what are the easiest mechanisms to allow people to use my library in their application?
I'm currently tempted to say
// here is my Prototype
Proto;
// here is how you instantiate a new instance
var p = Object.create(Proto);
// here is how you initialize it
// yes instantiation and initialization are seperate and should be.
p.constructor();
// Want sugar, use pd.new
For now, you probably make it easiest on your library clients if you use a small API that helps you with building a traditional constructor function, using syntax that looks almost like prototypes-as-classes. Example API usage:
// Superclass
var Person = Class.extend({
constructor: function (name) {
this.name = name;
},
describe: function() {
return "Person called "+this.name;
}
});
// Subclass
var Worker = Person.extend({
constructor: function (name, title) {
Worker.super.constructor.call(this, name);
this.title = title;
},
describe: function () {
return Worker.super.describe.call(this)+" ("+this.title+")";
}
});
var jane = new Worker("Jane", "CTO");
Implementations:
Simple JavaScript Inheritance
I’ve reimplemented Resig’s API, in a way that is possibly easier to understand: rauschma/class-js
I think the way to go is providing the new(Prototype, [arguments]) function as per the "use pd" option. It should even not be that bad from a dependency point of view (since you could have packaged this function separately anyway and is has just a couple of lines of code)
Offering a special function also fits in a sort of historic perspective. If you go way back to Smalltalk or even in more recent cases like Python you have separate functions for object creation (new) and initialization (init, the constructor) and the only only reason we don't notice the separation is because they provide syntactic sugar for object instantiation.
I'm interesting in improving my javascript code to be properly OOP.... currently I tend to do something like this:
jQuery(document).ready(function () {
Page.form = (function () {
return {
//generate a new PDF
generatePDF: function () {
},
//Update the list of PDFs available for download
updatePDFDownloads: function () {
},
/*
* Field specific functionality
*/
field: (function () {
return {
//show the edit prompt
edit: function (id, name) {
},
//refresh the value of a field with the latest from the database
refresh: function (id) {
}
};
}())
};
}());
});
In the end it's just mainly organized functions I suppose... what's a good resource where I can learn to program javascript in an OOP manner, or what suggestions would you have for improving my current style of programming?
It seems like I should do a sort of model prototype and have my form object inherit from that prototype.
(I'm using jQuery instead of $ because of conflicts with prototypeJS)
Your question is quite broad so I don't think a complete answer is possible here. But here are a few points.
Regarding the code you have shown. You're jumping a couple of redundant hoops.
Unless you're accessing the DOM in some way, there is no need to wrap your code in jQuery(document).ready()
There is no need to return an object from a self calling anonymous function unless you're closing over some private functions or data
The object you have created can be created more simply (a good thing) like this
var Page = {
form: {
//generate a new PDF
generatePDF: function () {
},
//Update the list of PDFs available for download
updatePDFDownloads: function () {
},
/*
* Field specific functionality
*/
field: {
//show the edit prompt
edit: function (id, name) {
},
//refresh the value of a field with the latest from the database
refresh: function (id) {
}
}
}
};
It's easier to read and less confusing, only do things that buy you something. see cargo cult programming
Here's an example using a self calling anonymous function to create private members
var Obj = (function() {
privateFunction( param ) {
// do something with param
}
var privateVar = 10;
return {
// publicMethod has access to privateFunction and privateVar
publicMethod: function() {
return privateFunction( privateVar );
}
}
})();
The structure you have used, object literals are very good, as you say, at grouping a set of functions (methods) and properties. This is a kind of namespace. It is also a way of creating a Singleton. You may also want to create many objects of the same Class.
JavaScript doesn't have classes like traditional OO languages (I'll get to that) but at the simplest level it's very easy to create a 'template' for creating objects of a particular type. These 'templates' are normal functions called constructors.
// a constructor
// it creates a drink with a particular thirst quenchingness
function Drink( quenchingness ) {
this.quenchingness = quenchingness;
}
// all drinks created with the Drink constructor get the chill method
// which works on their own particular quenchingness
Drink.prototype.chill = function() {
this.quenchingness *= 2; //twice as thirst quenching
}
var orange = new Drink( 10 );
var beer = new Drink( 125 );
var i_will_have = ( orange.quenchingness > beer.quenchingness )
? orange
: beer; //beer
var beer2 = new Drink( 125 );
beer2.chill();
var i_will_have = ( beer2.quenchingness > beer.quenchingness )
? beer2
: beer; //beer2 - it's been chilled!
There's a lot to know about constructors. You'll have to search around. There are lots of examples on SO.
Inheritance, the foundation of OO, is not that intuitive in js because it is prototypal. I won't go into that here because you will more than likely not use js's native prototypal inheritance paradigm directly.
This is because there are libraries that mimic classical inheritance very effectively, Prototype (inheritance) or mootools (Class) for example. There are others.
Many say that inheritance is overused in OO and that you should favour composition and this brings me to what I initially set out to recommend when I started this rambling answer.
Design patterns in JavaScript are as useful as in any OO language and you should familiarise yourself with them
I recommend you read Pro JavaScript Design Patterns.
There, that's it
Some good sources for Object-Oriented JavaScript and JavaScript in general...
Online Articles
How to "properly" create a custom object in JavaScript?
https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript
http://mckoss.com/jscript/object.htm
http://ejohn.org/blog/simple-javascript-inheritance/
JavaScript: How To Get Private, Privileged, Public And Static Members (Properties And Methods)
Books
JavaScript: The Good Parts - Douglas Crockford
Object-Oriented JavaScript - Stoyan Stefanov
I hope this helps.
Hristo
There isn't one correct way... Some people use a framework to define their object, I like to just extend prototype directly. Anyhow, I wanted to say that Oran Looney has some good posts on OO mechanics in JS:
http://oranlooney.com/classes-and-objects-javascript/
Its also worth looking at his other articles:
http://oranlooney.com/deep-copy-javascript/
http://oranlooney.com/functional-javascript/
The top 3 I suggest to read is
JavaScript and Object Oriented Programming (OOP)
Classical Inheritance in JavaScript
Prototypal Inheritance in JavaScript
Have a nice reading!
The code we are using follows this basic structure:
//Create and define Global NameSpace Object
( function(GlobalObject, $, undefined)
{
GlobalObject.Method = function()
{
///<summary></summary>
}
}) (GlobalObject = GlobalObject || {}, jQuery);
//New object for specific functionality
( function(Functionality.Events, $, undefined)
{
//Member Variables
var Variable; // (Used for) , (type)
// Initialize
GlobalObject.Functionality.Events.Init = function()
{
///<summary></summary>
}
// public method
this.PublicMethod = function(oParam)
{
///<summary></summary>
///<param type=""></param>
}
// protected method (typically define in global object, but can be made available from here)
GlobalObject.Functionality.ProtectedMethod = function()
{
///<summary></summary>
}
// internal method (typically define in global object, but can be made available from here)
GlobalObject.InternalMethod = function()
{
///<summary></summary>
}
// private method
var privateMethod = function()
{
///<summary></summary>
}
}) (GlobalObject.Funcitonality.Events = GlobalObject.Funcitonality.Events || {}, jQuery )
The strength to this is that it initializes the Global object automatically, allows you to maintain the intergrity of your code, and organizes each piece of functionality into a specific grouping by your definition. This structure is solid, presenting all of the basic syntactical things you would expect from OOP without the key words. Even setting up intelisense is possible with javascript, and then defining each peice and referencing them makes writing javascript cleaner and more manageable. Hope this layout helps!
I dont think it matters what language you use, good OOP is good OOP. I like to split up my concerns as much as possible by using an MVC architecture. Since JavaScript is very event based, I also use the observer design pattern mostly.
Heres a tutorial you can read about MVC using jQuery.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I've been developing jQuery plugins for quite some time now, and I like to think I know how to design one well by now. One issue keeps nagging me though, and that is how to deal with private functions in a powerful yet elegant manner.
My plugins generally look something like this:
(function($) {
$.fn.myplugin = function(...) {
...
// some shared functionality, for example:
this.css('background-color', 'green');
...
};
$.fn.mypluginAnotherPublicMethod = function(...) {
...
// some shared functionality, for example:
this.css('background-color', 'red');
...
};
}(jQuery));
Now my question is: how to neatly DRY up that shared functionality? An obvious solution would be to put it in a function within the plugin's namespace:
var fill = function($obj, color) {
$obj.css('background-color', color);
};
Although this solution is effective and nicely namespaced, I really dislike it. For one simple reason: I have to pass it the jQuery object. I.e. I have to call it like this: fill(this, 'red');, while I would like to call it like this: this.fill('red');
Of course we could achieve this result by simply putting fill into jQuery.fn. But that feels very uncomfortable. Imagine having ten plugins developed based on this approach and each plugin putting five of those 'private' functions into the jQuery function namespace. It ends up in a big mess. We could mitigate by prefixing each of these functions with the name of the plugin they belong to, but that doesn't really make it more attractive. These functions are supposed to be private to the plugin, so we do not want to expose them to the outside world at all (at least not directly).
So there's my question: does anyone of you have suggestions for how to get the best of both worlds. That is; plugin code being able to call 'private' plugin functions in a way similar to this.fill('red') (or this.myplugin.fill('red') or even this.myplugin().fill('red') etc.), while preventing jQuery function namespace pollution. And of course it should be light-weight, as these private functions might be called very frequently.
UPDATE: Thanks for your suggestions.
I especially like David's idea of defining an object type that holds the 'private' functions and wraps a jQuery object. The only problem with it is that it still disallows me from chaining 'private' and 'public' functions. Which was big reason to want a syntax like this.fill('red') to begin with.
I ended up with a solution which I consider not tremendously elegant, but appealing to the 'best of both worlds' cause:
$.fn.chain = function(func) {
return func.apply(this, Array.prototype.slice.call(arguments, 1));
};
Which allows for constructs like:
this.
find('.acertainclass').
chain(fill, 'red').
click(function() {
alert("I'm red");
});
I cross-posted my question in other places, which also collected some interesting responses:
http://forum.jquery.com/topic/jquery-plugin-design-pattern-common-practice-for-dealing-with-private-functions
http://groups.google.com/group/jquery-en/browse_thread/thread/fa8ccef21ccc589a
One thing first: if you would like to call something like this.fill('red'); where this is an instance of jQuery, you have to extend the jQuery prototype and make fill() "public". jQuery provides guidelines for extending it's prototype using so called "plugins" that can be added using $.fn.fill, which is the same as jQuery.prototype.fill.
In jQuery callbacks, this is often a reference to the HTML Element, and you can't add prototypes to those (yet). That is one of the reason why jQuery wraps elements and return jQuery instances that can be easily extended.
Using the (function(){})(); syntax, you can create and execute "private" javascript on the fly, and it all disappears when it's done. Using this technique, you can create your own jQuery-like syntax that wraps jQuery into your own private chainable object.
(function(){
var P = function(elem) {
return new Private(elem);
};
var Private = function(elem) {
this.elem = jQuery(elem);
}
Private.prototype = {
elem: null,
fill: function(col) {
this.elem.css('background',col);
return this;
},
color: function(col) {
this.elem.css('color', col);
return this;
}
}
$.fn.myplugin = function() {
P(this).fill('red');
};
$.fn.myotherplugin = function() {
P(this).fill('yellow').color('green');
};
})();
$('.foo').myplugin();
$('.bar').myotherplugin();
console.log(typeof P === 'undefined') // should print 'true'
This way, the P stands for your own toolbox of "private" functions. They won't be available anywhere else in the code or in the jQuery namespace unless you attach them somewhere. You can add as many methods as you like in the Private object, and as long as you return this, you can also chain them jQuery-style as I did in the example.
How about (within the plugin's scope):
var fill = function ()
{
(function (color)
{
this.css ('backgrorund-color', color);
//.. your stuff here ...
}).apply (this, arguments);
}
$.fn.myplugin = function ()
{
fill ('green');
}
That way, fill will retain the jQuery context you're in, and is still private to your plugin
Amended: the above is incorrect w.r.t. scoping, Try the following instead:
var fill = function (color)
{
if (!$this) return; // break if not within correct context
$this.css ('backgrorund-color', color);
//.. your stuff here ...
}
$.fn.myplugin = function ()
{
var $this = $(this); // local ref to current context
fill ('green');
}
You might want to take a look at how the jQuery UI Widget Factory is implemented.
The basic approach is like this:
(function($){
$.fn.myplugin = function(method)
{
if (mp[method]) // map $('foo').myplugin('bar', 'baz') to mp.bar('baz')
{
return mp[method].apply(this, Array.prototype.slice.call(arguments, 1));
}
else if (typeof method === 'object' || ! method)
{
return mp.init.apply(this, arguments); // if called without arguments, init
}
else
{
$.error('Method ' + method + ' does not exist on $.myplugin');
}
};
// private methods, internally accessible via this.foo, this.bar
var foo = function() { … };
var bar = function() { … };
var private = { // alternative approach to private methods, accessible via this.private.foo
foo : function() { … },
bar : function() { … }
}
var mp = { // public methods, externally accessible via $.myplugin('foo', 'bar')
init : function( options )
{
return this.each(function()
{
// do init stuff
}
},
foo : function() { … },
bar : function() { … }
};
})(jQuery);
Unfortunately, "private" methods (or any property for that matter) can never be called with a "this" prefix in javascript. Anything which is called like this.myFunc(myArgs) must be publicly available.
And "private" methods can only be called from within the scope in which they were defined.
Your original solution is the only one that will work. Yes, it's a pain having to pass in this, but there's no more verbosity than there would be if your impossible request was possible:
this.fill('green');
//or
fill(this,'green');
As you can see, they both take up exactly the same number of characters in your code.
Sorry to say, but you're stuck with this as a solution, unless you want to create a new namespace and make them not private - which is simply going to add to the amount of code you need to write, i.e. what you indirectly called "not directly exposed":
this.myplugin.fill('green');
...is more verbose, thus kind of defeats the purpose.
Javascript is not like other languages, there are no "private" members per-se, only members accessible within closures, which can sometimes be used in a similar way to private members, but is more of a "workaround", and not the "real-deal" private members you are looking for.
It can be difficult to come to terms with this (I often struggle), but don't try to mould javascript into what you understand from other languages, take it for what it is...