How do I create a jQuery-like $ () wrapper function?
I am creating a small code plugin that allows you to do some things with arrays. I don't want to add functions to the array object using prototype construct, I want people to be able to do something like:
arrayFunction( [1, 2, 3] ).someSpecialArrayFunction();
Thus, leaving the regular array object unchanged.
So I came up with the following construct (which was inspired by the jQuery source code):
var arrayFunction = window.arrayFunction = function(array) {
return new arrayFunction.fn.init(array);
}
arrayFunction.fn = arrayFunction.prototype = {
init: function(array){
this.a = array;
//should I return something here?
},
someSpecialArrayFunction: function(){
//Super cool custom stuff here.
}
}
However, this doesn't work (obviously). What should happen in the init () function?
The error right now is when trying:
arrayFunction(array).someSpecialArrayFunction();
does it say someSpecialArrayFunction () is not a function?
How to do it?
edit
Yes, this is a really simplified example. The actual thing has a few more methods.
Also, I just know how great it would be if it also supported chaning, how would you do that?
source to share
Or simply:
var arrayFunction = function(array) {
var someSpecialArrayFunction = function() {
// do something with array
};
return {
someSpecialArrayFunction: someSpecialArrayFunction
}
};
arrayFunction( [1, 2, 3] ).someSpecialArrayFunction();
Be careful with this though, if you have too many methods it is probably better to use a prototype.
source to share
I'm not sure if this is the best syntax to get what you want, but I suppose this is an abbreviated example ... This is a quick way to get it to work anyway.
var arrayFunction = window.arrayFunction = function(array) {
return new arrayFunction.fn.init(array);
}
arrayFunction.fn = arrayFunction.prototype = {
init: function(array){
var a = array;
return {
someSpecialArrayFunction: function(){
alert (a.join(' - ') ); //Super cool custom stuff here.
}
};
},
}
arrayFunction( [1, 2, 3] ).someSpecialArrayFunction();
source to share
sktrdie's answer looks good.
Another thing to consider is using a flyweight pattern instead of returning an instance of a new object with each call to your wrapper. The ExtJS function Ext.fly does this; it returns a globally shared instance.
source to share
Try something like this:
var mathWrapper = function(array) {
return {
mean : function(){
if (array.length == 0) return(0);
var total = 0;
for (var i = 0; i < array.length; i++) total += array[i];
return(total / array.length);
},
};
}
Calling with something like
document.write(mathWrapper([1,2,3,4,5,6]).mean());
should return the average of the array elements.
source to share
If you don't want t change Array.prototype
, the only real possible solution is to add methods to a specific array object:
function doSomething() {}
function doOther() {}
function wrap(array) {
array.doSomething = doSomething;
array.doOther = doOther;
return array;
}
Please note that I do not define doSomething()
and doOther()
inside wrap()
- otherwise we would create new functional objects for every call that is extremely inefficient.
A more complex option would be the following:
function doSomething() {}
function doOther() {}
function wrap(array) {
if(this instanceof arguments.callee) {
this.doSomething = doSomething;
this.doOther = doOther;
}
else {
arguments.callee.prototype = array;
return new arguments.callee;
}
}
Here we are creating a new wrapper object (and not adding properties to the array itself). The wrapper object will still have access to all properties of the array. There's a small catch though:
var foo = wrap([1,2,3]);
document.writeln(foo.length); // writes 3
foo.push(4);
document.writeln(foo.length); // writes 4 in FF, 3 in IE
foo[4] = 5;
foo[5] = 6;
document.writeln(foo.length); // still writes 4?!
Usage foo[4] =
will result in access to our wrapping object, and the properties 4
and 5
will be set there, not in the array, so its length property will not be updated. In IE, the call will push()
no longer be able to update the array correctly ...
If your wrapper object doesn't need to "redirect" calls to the array object, there are other solutions using closures. The first sktrdie example is one of them. I would object to them because they will create new function objects and with every call. What will i do:
function wrap(array) {
if(this instanceof arguments.callee)
this.array = array;
else return new arguments.callee(array);
}
wrap.prototype.doSomething = function() {};
wrap.prototype.doOther = function() {};
Here you can access the array in doSomething()
and doOther()
through this.array
.
source to share