1

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();
4
  • can you provide sample how you want use it and sample input output? Commented Jul 9, 2015 at 16:21
  • @Grundy, ok, I added an example. Commented Jul 9, 2015 at 16:28
  • are you sure that need exactly create object like function instead simple use function? Commented Jul 9, 2015 at 16:33
  • Well, I want to combine function and array. Commented Jul 9, 2015 at 16:35

2 Answers 2

1

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

However,

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
Sign up to request clarification or add additional context in comments.

6 Comments

Just curious, what is invoke?
OP ideally wanted fa('baz') instead using invoke :-)
@Grundy you lose all of the benifits of the prototype if you do that though :(
@PaulS., but what if we add it to prototype for our function?
@Grundy all functions inherit from Function.prototype. The only way to make one which doesn't would be to use Object.setPrototypeOf which isn't a good idea as it's both unexpected by the interpreter and especially unexpected for functions, Object.setPrototypeOf(foo, Object.create(Function.prototype));
|
0

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.

8 Comments

var self = this; self = function(){ here you reassign self, so after it not refers to this
you're right, that line is superfluous. (I paste my code too hastily, it had some unneeded lines and debug logging)
if you remove var self = this; nothing changes
@Grundy, If I remove that line, self becomes globally available :-p But that line's not really the point of the whole thing. (The whole self/this shenanigans was just lazyness on my part to avoid dealing with this changing, I didn't see it was not going to be necessary when I started typing up this answer - I'll edit the code a bit.)
but i mean that you not use this
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.