Reputation: 15902
I have a FunctionArray object which I want to use to call several functions at once. Basically what you see here is what I have so far, but one thing that would be nice is to just call a function array like myFunctionArrayInstance()
. Is there a way to do that or is it just not possible?
var FunctionArray = function(){
this.apply = function(){
var args = arguments;
this.forEach(function(e,i){
e.apply(args);
});
}
this.call = function(){
var args = arguments;
this.forEach(function(e,i){
e.call(args);
});
}
this.bind = function(){
var args = arguments;
var binds = [];
this.forEach(function(e,i){
binds.push(e.bind(args));
});
return function(){
var args2 = arguments;
binds.forEach(function(e,i){
e.apply(args2);
});
};
};
};
FunctionArray.prototype = [];
And I use it like this.
var initers = new FunctionArray();
initers.push(function(){/*something here*/});
initers.push(function(){/*something here*/});
initers();
Upvotes: 1
Views: 52
Reputation: 4506
That's a cool construct :) Alas, if the constructed objects are descendants of array, it won't be callable. On the other hand, why not make it a function constructor (which would make the instances callable), and augment this instance with array-like mechanisms for storing and retrieving the functions within?
I'm thinking:
(function(){ 'use strict';
function makeFunctionArray(){
var functionArray = function(){
functionArray.functions.forEach(function(f){
f();
});
}
functionArray.functions = [];
functionArray.push=function(fnToAdd){
functionArray.functions.push(fnToAdd);
}
functionArray.pop=function(){
return functionArray.functions.pop();
}
//and so on... you can add any array functionality you need
//(maybe even bracket notation with some fiddling?)
return functionArray;
}
//using it:
var fa = makeFunctionArray();
typeof fa; //'function'
fa.push(function(){console.log(123);});
fa.push(function(){console.log(456);});
fa(); //-> outputs '123' then '456'
})();
Edit: cleaned up code according to Grundy's suggestions.
Upvotes: 0
Reputation: 66404
Your structure looked interesting, but sadly if you want a Function (as opposed to a constructed Object) to have a custom prototype chain you need to use the very costly Object.setPrototypeOf
and I would not be surprised if it produced lots of unexpected results. You'll need to add a method which does the invocation.
I think it would be easier to keep control over everything if you don't put your Array in the prototype and instead have a property on the instance which holds your Functions and copy wrapped versions of the methods over from Array that you need. I also added an invoke and re-implemented length.
Then basically everything is in the prototype except the Array of your functions. After the initial definition creating instances should require very little time/memory.
function FunctionArray() {
this.fns = [];
}
FunctionArray.prototype = Object.create(null);
(function () {
var i, arr = ['push', 'pop', 'shift', 'unshift', 'splice'];
function addToProto(key, fn) {
FunctionArray.prototype[key] = function () {
return fn.apply(this.fns, arguments);
};
}
for (i = 0; i < arr.length; ++i) {
addToProto(arr[i], Array.prototype[arr[i]]);
}
Object.defineProperty(
FunctionArray.prototype,
'length',
{
get: function () {return this.fns.length;},
set: function (x) {return this.fns.length = x;}
}
);
FunctionArray.prototype.item = function (i) {
return this.fns[i];
};
FunctionArray.prototype.invoke = function () {
var i;
for (i = 0; i < this.fns.length; ++i) {
this.fns[i].apply(this, arguments);
}
};
FunctionArray.prototype.call = function (ctx) {
var i, args = arguments.slice(1);
for (i = 0; i < this.fns.length; ++i) {
this.fns[i].apply(ctx, args);
}
};
FunctionArray.prototype.apply = function (ctx, args) {
var i;
for (i = 0; i < this.fns.length; ++i) {
this.fns[i].apply(ctx, args);
}
};
FunctionArray.prototype.bind = function () {
var i;
for (i = 0; i < this.fns.length; ++i) {
this.fns[i] = Function.prototype.bind.apply(this.fns[i], arguments);
}
};
}());
Now you can do something like
var fa = new FunctionArray();
fa.push(function (fizz) {console.log(this, fizz)});
fa.push(function (buzz) {console.log(this, buzz)});
fa.bind({'foo': 'bar'});
fa.length; // 2
fa.invoke('baz'); // logs {foo: "bar"} "baz" twice
It is possible but strongly not reccomended, using Object.setPrototypeOf
function FunctionArray() {
var foo = function () {
return foo.invoke.apply(foo, arguments);
};
Object.setPrototypeOf(foo, FunctionArray.prototype);
foo.fns = [];
return foo;
}
FunctionArray.prototype = Object.create(Function.prototype);
// continue from here as above
Now
var fa = new FunctionArray();
fa.push(function (fizz) {console.log(this, fizz)});
fa.push(function (buzz) {console.log(this, buzz)});
fa.bind({'foo': 'bar'});
fa.length; // 2
fa('baz'); // logs {foo: "bar"} "baz" twice
Upvotes: 1