Click here to Skip to main content
Click here to Skip to main content

salient.Delegate - a non-DOM JavaScript Delegate Implementation

, 18 Apr 2010
Rate this:
Please Sign up or sign in to vote.
salient.Delegate is a class that enables the implementation of numerous software design patterns that can accelerate design and development and improve the architecture of your JavaScript code by enabling reliable loose coupling of JavaScript code.

download this article in .doc format

Introduction

salient.Delegate is a class that enables the implementation of numerous software design patterns that can accelerate design and development and improve the architecture of your JavaScript code by enabling reliable loose coupling of JavaScript code.

Overview

The intention of salient.Delegate is to provide a lightweight, robust and easy to use JavaScript delegate that can be used to implement a multitude of design patterns including Event, Observer, Command, Chain of Responsibility patterns.

These patterns can drastically simplify the architecture of an application by means enabling simple and robust implementations of dependancy inversion and clear seperation of concerns.

Client script applications can be designed in a more modular, or encapsulated, fashion facilitating isolated testing and in turn enabling agile methodologies.

Don't let all of these buzz words frighten you. I am simply describing things that we all do, hopefully, in some degree every day, knowingly or not.

While this article is not intended to be any sort of primer on patterns, we certainly will explore a few that should be quite familiar even if not by their formal name.

Disclaimer:

  • This class is not intended to handle DOM events. Of course it can be purposed to that end but there are much more appropriate implementations, notably Dean Edwards' Event implementation.
  • The descriptions and interpretations of GOF design patterns presented are just that, interpretations and approximations. Any input regarding these implementations are especially welcome.

Usage

Lets first examine the API:

Listing 1: salient.Delegate API

var Delegate = function(name)
{
    /// <span class="code-SummaryComment"><summary>
</span>    /// A simple event Delegate implementation
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="name" type="String">The name of the event.</param>
</span>    /// <span class="code-SummaryComment"><returns type="salient.delegate"/>
</span>
};

Delegate.prototype.addHandler = function(fn, context)
{
    /// <span class="code-SummaryComment"><summary>
</span>    /// Adds supplied function reference to the list of functions to be called upon raise of 
    /// this event and returns a token that can be used to remove the DelegateContext
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="fn" type="function">The function to be called by this delegate.</param>
</span>    /// <span class="code-SummaryComment"><param name="context" type="Object">
</span>    /// The context in which the handler should be called. (the this).
    /// If ommited the context will be that of the delegateContext. Probably not the desired effect.
    /// <span class="code-SummaryComment"></param>
</span>    /// <span class="code-SummaryComment"><returns type="String">
</span>    /// The unique identifier of this handler. To be used to remove this handler in the
    /// event that the function is not available for identification.
    /// <span class="code-SummaryComment"></returns>
</span>};
Delegate.prototype.removeHandler = function(handle)
{
    /// <span class="code-SummaryComment"><summary>
</span>    /// Removes a function reference from the list of handlers to this event.
    /// You can pass the numeric token issued when the DelegateContext was
    /// attached or the DelegateContext itself. Closures obviously cannot be passed.
    /// Be sure to retain the handle if needed.
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="handle" type="Object">
</span>    /// the handle token issued or the function reference it was assigned to
    /// <span class="code-SummaryComment"></param>
</span>};

Delegate.prototype.invoke = function()
{
    /// <span class="code-SummaryComment"><summary>
</span>    /// calls the function ref for each DelegateContext that subscribed to
    /// this event with supplied arguments.
    ///
    /// if the function returns the static DelegateCancellation the handler chain is terminated.
    /// any other return values are returned to the caller. If multiple handlers return values they 
    /// will be returned as an array.
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><returns type="Object">the return value(s) of the handler(s). If multiple an array is returned.</returns>
</span>};

 

Event/Observer Pattern

Listing 2: Simple event/observer pattern example

var observedType = function()
{
    var _foo;

    var change = this.propertyChange = new salient.Delegate("observed.propertyChange");

    function onChange(name, value)
    {
        change.invoke(name, value);
    }

    this.setFoo = function(value)
    {
        _foo = value;
        onChange("foo",_foo);
    }
    this.getValue = function()
    {
        return _value;
    }
}


var observerType = function()
{
    var observed = this.observed = new observedType();

    function observedPropertyChanged(name, value)
    {
        alert("observed property " + name + " new value is " + value);
    }

    observed.propertyChange.addHandler(observedPropertyChanged, this);

}

var observer = new observerType();

observer.observed.setFoo("bar");

// prop change invokes the handler on observer

 

Chain of Responsibility

A traditional Chain Of Responsibility pattern uses a linked list of handlers and the invocation chain is controlled by explicit passing of responsibility within the handlers themselfs. This requires that not only the owner of the COR be aware of all of the handlers, each handler is aware of the next.  Additionally, logic must be emplaced to manage a linked list, adding to the complexity of the implementation.

With a bit of imagination we can generalize this pattern and envision it as a simple array of handlers that are invoked in order of appearance, each invocation posessing the power to terminate the invocation chain by signalling cancellation in the return value.

Using salient.delegate, a Chain Of Responsibility is quite easy to implement because that is the design pattern that salient.Delegate's implementation most closely resembles, albeit shaped a bit differently as described above.

With deliberate addition of handlers you may construct your chain of responsibility and pass the subject as an argument in the .invoke() invocation. If, during any handler invocation, the chain should stop, simply return salient.DelegateCancel.

I will leave it to the reader to implement methods to manipulate  the  handler chain after creation. The handler array is exposed as delegateInstance.handlers.

Listing 3: Simple Chain Of Responsibility pattern example using salient.DelegateCancel

// using the CoinHandler example from BlackWasp
// http://www.blackwasp.co.uk/ChainOfResponsibility.aspx

var Coin = function(diameter, weight)
{
    this.Weight = weight;
    this.Diameter = diameter;
}

var FivePenceHandler = function()
{
    this.handleCoin = function(coin)
    {
        if (Math.abs(coin.Weight - 3.25) < 0.02 && Math.abs(coin.Diameter - 18) < 0.1)
        {
            alert("Captured 5p");
            // instead of falling through and explicitely invoking the next
            // handler in a linked list we simply return a cancellation token
            // and let Delegate break the invocation chain.            
            return salient.DelegateCancel;
        }
    }
}

var TenPenceHandler = function()
{
    this.handleCoin = function(coin)
    {
        if (Math.abs(coin.Weight - 6.5) < 0.03 && Math.abs(coin.Diameter - 24.5) < 0.15)
        {
            alert("Captured 10p");
            return salient.DelegateCancel;
        }
    }
}

var TwentyPenceHandler = function()
{
    this.handleCoin = function(coin)
    {
        if (Math.abs(coin.Weight - 5) < 0.01 & Math.abs(coin.Diameter - 21.4) < 0.1)
        {
            alert("Captured 10p");
            return salient.DelegateCancel;
        }

    }
}

var FiftyPenceHandler = function()
{
    this.handleCoin = function(coin)
    {
        if (Math.abs(coin.Weight - 8) < 0.02 && Math.abs(coin.Diameter - 27.3) < 0.15)
        {
            alert("Captured 50p");
            return salient.DelegateCancel;
        }

    }
}

var OnePoundHandler = function()
{
    this.handleCoin = function(coin)
    {
        if (Math.abs(coin.Weight - 9.5) < 0.02 && Math.abs(coin.Diameter - 22.5) < 0.13)
        {
            alert("Captured £1");
            return salient.DelegateCancel;
        }
    }
}

// an addition to the black wasp implementation
var CounterfeitCoinHandler = function()
{
    this.handleCoin = function(coin)
    {
        alert("Coin is counterfeit");
    }
}

var h5 = new FivePenceHandler();
var h10 = new TenPenceHandler();
var h20 = new TwentyPenceHandler();
var h50 = new FiftyPenceHandler();
var h100 = new OnePoundHandler();
var bogus = new CounterfeitCoinHandler();

var coinChainOfResponsibility = new salient.Delegate("CoinCOR");

coinChainOfResponsibility.addHandler(h5.handleCoin, h5);
coinChainOfResponsibility.addHandler(h10.handleCoin, h10);
coinChainOfResponsibility.addHandler(h20.handleCoin, h20);
coinChainOfResponsibility.addHandler(h50.handleCoin, h50);
coinChainOfResponsibility.addHandler(h100.handleCoin, h100);
coinChainOfResponsibility.addHandler(bogus.handleCoin, bogus);

var tenPence = new Coin(24.49, 6.5);
var fiftyPence = new Coin(27.31, 8.01);
var counterfeitPound = new Coin(22.5, 9);

coinChainOfResponsibility.invoke(tenPence);
coinChainOfResponsibility.invoke(fiftyPence);
coinChainOfResponsibility.invoke(counterfeitPound);

 

Alternately you could implement this pattern using return values. Handler return values are returned to the .invoke() call. If multiple values are returned, e.g. multiple handlers with return values, the .invoke() return value is an array.

Listing 4: Simple Chain Of Responsibility pattern example using return values

// using the CoinHandler example from BlackWasp
// http://www.blackwasp.co.uk/ChainOfResponsibility.aspx

var Coin = function(diameter, weight)
{
    this.Weight = weight;
    this.Diameter = diameter;
}

var FivePenceHandler = function()
{
    this.handleCoin = function(coin)
    {
        if (Math.abs(coin.Weight - 3.25) < 0.02 && Math.abs(coin.Diameter - 18) < 0.1)
        {
            return "5p";
        }
    }
}

var TenPenceHandler = function()
{
    this.handleCoin = function(coin)
    {
        if (Math.abs(coin.Weight - 6.5) < 0.03 && Math.abs(coin.Diameter - 24.5) < 0.15)
        {
            return "10p";
        }
    }
}

var TwentyPenceHandler = function()
{
    this.handleCoin = function(coin)
    {
        if (Math.abs(coin.Weight - 5) < 0.01 & Math.abs(coin.Diameter - 21.4) < 0.1)
        {
            return "10p";
        }

    }
}

var FiftyPenceHandler = function()
{
    this.handleCoin = function(coin)
    {
        if (Math.abs(coin.Weight - 8) < 0.02 && Math.abs(coin.Diameter - 27.3) < 0.15)
        {
            return "50p";
        }

    }
}

var OnePoundHandler = function()
{
    this.handleCoin = function(coin)
    {
        if (Math.abs(coin.Weight - 9.5) < 0.02 && Math.abs(coin.Diameter - 22.5) < 0.13)
        {
            return "£1";
        }
    }
}


var h5 = new FivePenceHandler();
var h10 = new TenPenceHandler();
var h20 = new TwentyPenceHandler();
var h50 = new FiftyPenceHandler();
var h100 = new OnePoundHandler();

var coinChainOfResponsibility = new salient.Delegate("CoinCOR");

coinChainOfResponsibility.addHandler(h5.handleCoin, h5);
coinChainOfResponsibility.addHandler(h10.handleCoin, h10);
coinChainOfResponsibility.addHandler(h20.handleCoin, h20);
coinChainOfResponsibility.addHandler(h50.handleCoin, h50);
coinChainOfResponsibility.addHandler(h100.handleCoin, h100);

var tenPence = new Coin(24.49, 6.5);
var fiftyPence = new Coin(27.31, 8.01);
var counterfeitPound = new Coin(22.5, 9);

alert(coinChainOfResponsibility.invoke(tenPence) || "bogus coin");
alert(coinChainOfResponsibility.invoke(fiftyPence) || "bogus coin");
alert(coinChainOfResponsibility.invoke(counterfeitPound) || "bogus coin");

NOTE: In that the Coin argument is an object  it is being passed by reference and can me manipulated by each handler in turn. This enables the implementation of what I would like to call a Chain Of Custody pattern.

Command Pattern

Included is a simple interpretation of the Command pattern. The Command patterns allows loose coupling of a function and the code that invokes it. The target needs no foreknowledge of possible invokers and invokers can be added at any time and need no foreknowledge of the command they are meant to invoke other than the command key.  Compare this to the Event pattern in which you explicitly bind a reference to a handler function to the event. Command pattern presents one more level of decoupling. Decoupling with Command pattern can be taken to the extreme of completely encapsulating the behaviour of an object, making private functions invocable via a command. This is a powerful concept that is not typically associated with JavaScript code.

A typical use case for a Command pattern can be inferred from any typical windows application. An application generally has some type of 'Save' functionality. You can generally invoke this functionality from an arbitrary number of places. e.g. Main Menu>File>Save, Toolbar>Save button, Keyboard> CTRL-S, etc.

Instead of writing code to hardwire each of these triggers to the Save function, we can use a Command pattern that drastically simplifies the architecture and implementation of an application.

I am explaining this pretty badly. Let me show some code.

Listing 5: salient.Commands API

var Commands = function()
{
    /// <span class="code-SummaryComment"><summary>
</span>    /// A simple event dispatcher. Useful for implementing a command patter.
    /// Sole purpose is to maintain a hash of unbound delegates that can be 
    /// attached to and invokeed by anyone using string keys.
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><returns type="salient.Commands"></returns>
</span>}

Commands.prototype.publish = function(name)
{
    /// <span class="code-SummaryComment"><summary>
</span>    /// Creates a named event if not exists
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="name" type="String">The command key. Unique to the command manager</param>
</span>}

Commands.prototype.unpublish = function(name)
{
    /// <span class="code-SummaryComment"><summary>
</span>    /// Detaches and removes the named command, if present.
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="name" type="String">The command key. Unique to the command manager</param>
</span>}

Commands.prototype.subscribe = function(fn, context, name)
{
    /// <span class="code-SummaryComment"><summary>
</span>    /// Registers function as an eventHandler for named command. The command is published if necessary
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="fn" type="function">The function to be called on this event</param>
</span>    /// <span class="code-SummaryComment"><param name="context" type="Object">The context in which the handler should be called. (the this).</param>
</span>    /// <span class="code-SummaryComment"><param name="name" type="String">The command key. Unique to the command manager</param>
</span>    /// <span class="code-SummaryComment"><returns type="String">event handle token</returns>
</span>}

Commands.prototype.unsubscribe = function(name, handle)
{
    /// <span class="code-SummaryComment"><summary>
</span>    /// Detaches eventHandler from command. If handle is null all eventHandlers for named command are detached.
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="name" type="String">The command key. Unique to the command manager</param>
</span>    /// <span class="code-SummaryComment"><param name="handle" type="Object" optional="true">
</span>    /// Optional Function or token. If empty the named command is detached from all eventHandlers
    /// <span class="code-SummaryComment"></param>
</span>}

Commands.prototype.invoke = function(name, sender, args)
{
    /// <span class="code-SummaryComment"><summary>
</span>    /// Signals all eventHandlers to this command with the source (sender) and args
    /// <span class="code-SummaryComment"></summary>
</span>    /// <span class="code-SummaryComment"><param name="name" type="String">The command key. Unique to the command manager</param>
</span>    /// <span class="code-SummaryComment"><param name="sender" type="Object" optional="true">Optional. </param>
</span>    /// <span class="code-SummaryComment"><param name="args" type="Object" optional="true">Optional. </param>
</span>    /// <span class="code-SummaryComment"><exception cref="Error">
</span>    /// If command is not registered.
    /// <span class="code-SummaryComment"></exception>
</span>}

Commands.prototype.clear = function()
{
    /// <span class="code-SummaryComment"><summary>
</span>    /// Detaches and clears all commands
    /// <span class="code-SummaryComment"></summary>
</span>}

Listing 6: A simple Command pattern example

// a simple Command pattern implementation that enforces
// a clean encapsulation and seperation of concerns.

var myAppCore = new function()
{
    // creating a private reference and a priveledge accessor
    // in one expression.
    var commands = this.commands = new salient.Commands();

    // command targets use standard event handler signature
    function save(sender, args)
    {
        alert("I saved " + args);
        // the event handler signature protocol also presents
        // a reference to the invoker in the sender argument.
        // This violates the spirit of seperation but enables
        // a lot of interesting implementation possibilities.
        
    }

    // add a keyed command that exposes a private function
    // subscribing also publishes the command if it is not already

    commands.subscribe(save, this, "SAVE");
    
}


var myAppSaveButton =
{
    click: function()
    {
        myAppCore.commands.invoke("SAVE", this, "name of doc  (invoked via button)");
    }
}

var myAppKeyboardSave=
{
    press: function()
    {
        myAppCore.commands.invoke("SAVE", this, "name of doc (invoked via keyboard)");
    }
}

myAppSaveButton.click();
myAppKeyboardSave.press();

This implementation publishes core functionality, Save, as a command named "SAVE".   If an invocation is attempted on a non existant command, an exception is thrown.

A further level of decoupling can be accomplished by simply publishing a command in the application scope.  You can then attach/detach subscribers at will. Invocation of commands that are published but have no subscribers results in a noop.

In that the Command pattern is especially useful in a single threaded runtime like JavaScript I will revisit this subject and present a more formal implementation of the Command pattern, including command journalling to enable 'undo'/'redo' in a later article.

Implementation

salient.Delegate, at it's core, is a variation of a Chain of Responsibility pattern in that it is basically an array of context objects (handlers) that are called in sequence upon invocation of the delegate.  In addition to the standard behaviour you would expect from an event,  the addition of return value(s), arbitrary numbers of arguments, arbitrary scoping and the capability of cancelling the invocation chain leads me to characterize it as a Delegate for lack of a better term.

Initially, the invocation chain was implemented as a simple iteration of an array of callbacks. While this works quite well when everything is in it's proper place, an error during the invocation of a handler leaves us with 2 options: silently swallow the error and continue the invocation chain or let the error propigate and break the invocation chain.

What I really had in mind was a more robust behaviour more closely resembling the native handling of events, in that an error can occur in one handler that can be handled or not but does not affect the invocation of any subsequent handlers.

I struggled with this for quite some time until I happened to visit Dean's blog and read this post, Callbacks vs Events, in which he describes a means of wrapping a callback in the native event model. Exactly what I was after. With a not-too-significant degree of adaptation I was able to implement his idea without altering the advertised behavior or API of salient.Delegate.

Listing 7: salient.Delegate and salient.Commands source

// 
// salientJS JavaScript Library v1.0
// http://skysanders.net/code/salientJs/
// 
// Copyright (c) 2009 Sky Sanders - sky@skysanders.net
// Dual licensed under the MIT and GPL licenses.
// http://skysanders.net/code/salientJs/license.txt
//
// this code for this demo is an exerpt from the salientJS javascript library.
// for full functionality see the main project

var salient = {};
(function()
{
    var DelegateContext = function(fn, context, token)
    {
        /// <span class="code-SummaryComment"><summary>
</span>        /// Holds a function reference to be called in response to an event.
        /// <span class="code-SummaryComment"></summary>
</span>        /// <span class="code-SummaryComment"><param name="fn" type="function">The function to be called on this event</param>
</span>        /// <span class="code-SummaryComment"><param name="context" type="Object">The context in which the handler should be called. (the this).</param>
</span>        /// <span class="code-SummaryComment"><param name="token" type="String">
</span>        /// The unique identifier of this handler. To be used to remove this handler in the event that the function is not available for identification.
        /// <span class="code-SummaryComment"></param>
</span>        /// <span class="code-SummaryComment"><field name="fn" type="function">The function to be called on this event.</field>
</span>        /// <span class="code-SummaryComment"><field name="context" type="Object">The context in which the handler should be called. (the this).</field>
</span>        /// <span class="code-SummaryComment"><field name="token" type="String">
</span>        /// The unique identifier of this handler. To be used to remove this handler in the event that the function is not available for identification.
        /// <span class="code-SummaryComment"></field>
</span>        /// <span class="code-SummaryComment"><returns type="DelegateContext"></returns>
</span>        this.fn = fn;
        this.token = token;
        this.context = context;

    };


    DelegateContext.prototype.destroy = function()
    {
        /// <span class="code-SummaryComment"><summary>
</span>        /// nulls all references contained by this handler
        /// <span class="code-SummaryComment"></summary>
</span>        /// <span class="code-SummaryComment"><returns type="null">for use in destroy pattern</returns>
</span>
        this.fn = null;
        this.token = null;
        this.context = null;
        return null;
    };


    var Delegate = function(name)
    {
        /// <span class="code-SummaryComment"><summary>
</span>        /// A simple event Delegate implementation
        /// <span class="code-SummaryComment"></summary>
</span>        /// <span class="code-SummaryComment"><param name="name" type="String">The name of the event.</param>
</span>        /// <span class="code-SummaryComment"><returns type="salient.delegate"/>
</span>
        if (name instanceof salient.Delegate)
        {
            return name;
        }

        this.name = name;
        this.id = delegate_guid++;
        this.handlers = [];
    };


    Delegate.prototype.addHandler = function(fn, context)
    {
        /// <span class="code-SummaryComment"><summary>
</span>        /// Adds supplied function reference to the list of functions to be called upon raise of 
        /// this event and returns a token that can be used to remove the DelegateContext
        /// <span class="code-SummaryComment"></summary>
</span>        /// <span class="code-SummaryComment"><param name="fn" type="function">The function to be called by this delegate.</param>
</span>        /// <span class="code-SummaryComment"><param name="context" type="Object">
</span>        /// The context in which the handler should be called. (the this).
        /// If ommited the context will be that of the delegateContext. Probably not the desired effect.
        /// <span class="code-SummaryComment"></param>
</span>        /// <span class="code-SummaryComment"><returns type="String">
</span>        /// The unique identifier of this handler. To be used to remove this handler in the
        /// event that the function is not available for identification.
        /// <span class="code-SummaryComment"></returns>
</span>        var handler = this.find(fn);
        if (!handler)
        {
            delegate_guid++;
            var token = this.name + "_" + this.id + "_" + delegate_guid;
            handler = new DelegateContext(fn, context, token);
            this.handlers.push(handler);
        }
        return handler.token;
    };

    Delegate.prototype.removeHandler = function(handle)
    {
        /// <span class="code-SummaryComment"><summary>
</span>        /// Removes a function reference from the list of handlers to this event.
        /// You can pass the numeric token issued when the DelegateContext was
        /// attached or the DelegateContext itself. Closures obviously cannot be passed.
        /// Be sure to retain the handle if needed.
        /// <span class="code-SummaryComment"></summary>
</span>        /// <span class="code-SummaryComment"><param name="handle" type="Object">
</span>        /// the handle token issued or the function reference it was assigned to
        /// <span class="code-SummaryComment"></param>
</span>
        var i = this.indexOf(handle);
        if (i < 0)
        {
            throw new Error("handle does not exists");
        }

        var handler = this.handlers[i];
        handler = handler.destroy();
        this.handlers[i] = null;
        //this.handlers.splice(i, 1);

    };

    Delegate.prototype.invoke = function()
    {
        /// <span class="code-SummaryComment"><summary>
</span>        /// calls the function ref for each DelegateContext that subscribed to
        /// this event with supplied arguments.
        ///
        /// if the function returns the static DelegateCancellation the handler chain is terminated.
        /// any other return values are returned to the caller. If multiple handlers return values they 
        /// will be returned as an array.
        /// <span class="code-SummaryComment"></summary>
</span>        /// <span class="code-SummaryComment"><returns type="Object">the return value(s) of the handler(s). If multiple an array is returned.</returns>
</span>        var results = [];
        for (var i = 0; i < this.handlers.length; i++)
        {
            var handler = this.handlers[i];

            if (handler !== null)
            {
                handler.args = arguments;

                currentHandler = handler;

                dispatchFakeEvent();

                if (handler.result instanceof DelegateCancellation)
                {
                    break; // cancel the execution chain
                }
                if (typeof (handler.result) !== "undefined")
                {
                    results.push(handler.result);
                }
            }
        }

        return results.length === 0 ? undefined : (results.length === 1 ? results[0] : results);
    };

    Delegate.prototype.destroy = function()
    {
        /// <span class="code-SummaryComment"><summary>
</span>        /// detaches all handlers from this event.
        /// <span class="code-SummaryComment"></summary>
</span>        /// <span class="code-SummaryComment"><returns type="null">for use in destroy pattern</returns>
</span>
        for (var i = 0; i < this.handlers.length; i++)
        {
            var handler = this.handlers[i];
            handler = handler.destroy();
        }

        this.handlers = [];
        return null;
    };

    Delegate.prototype.count = function()
    {
        /// <span class="code-SummaryComment"><summary>
</span>        /// Handler count
        /// <span class="code-SummaryComment"></summary>
</span>        /// <span class="code-SummaryComment"><returns type="Number">handler count</returns>
</span>
        return this.handlers.length;
    };

    Delegate.prototype.indexOf = function(handle)
    {
        /// <span class="code-SummaryComment"><summary>
</span>        /// Returns the index of the handle specified. Returns -1 if not found.
        /// <span class="code-SummaryComment"></summary>
</span>        /// <span class="code-SummaryComment"><param name="handle" type="Object" mayBeNull="false" optional="false">the handle token issued or the function reference it was assigned to</param>
</span>        /// <span class="code-SummaryComment"><returns type="Number"></returns>
</span>
        for (var i = 0; i < this.handlers.length; i++)
        {
            if (this.handlers[i] === null)
            {
                continue;
            }
            if (this.handlers[i].token === handle || this.handlers[i].fn === handle)
            {
                return i;
            }
        }
        return -1;
    };

    Delegate.prototype.find = function(handle)
    {
        /// <span class="code-SummaryComment"><summary>
</span>        /// Find a handler by token or function reference
        /// <span class="code-SummaryComment"></summary>
</span>        /// <span class="code-SummaryComment"><param name="handle" type="Object" mayBeNull="false" optional="false">the handle token issued or the function reference it was assigned to</param>
</span>        /// <span class="code-SummaryComment"><returns type="salient.DelegateContext"></returns>
</span>
        var i = this.indexOf(handle);
        if (i >

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Sky Sanders
Software Developer (Senior) Salient Solutions
United States United States
My name is Sky Sanders and I am an end-to-end, front-to-back software solutions architect with more than 20 years experience in IT infrastructure and software development, the last 10 years being focused primarily on the Microsoft .NET platform.
 
My motto is 'I solve problems.' and I am currently available for hire.
 
I can be contacted at sky.sanders@gmail.com

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 18 Apr 2010
Article Copyright 2009 by Sky Sanders
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid