prototype/src/lang/function.js

166 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** section: Language
* class Function
**/
Object.extend(Function.prototype, (function() {
var slice = Array.prototype.slice;
function update(array, args) {
var arrayLength = array.length, length = args.length;
while (length--) array[arrayLength + length] = args[length];
return array;
}
function merge(array, args) {
array = slice.call(array, 0);
return update(array, args);
}
/**
* Function#argumentNames() -> Array
*
* Reads the argument names as stated in the function definition and returns
* the values as an array of strings (or an empty array if the function is
* defined without parameters).
**/
function argumentNames() {
var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
.replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
.replace(/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;
}
/**
* Function#bind(object[, args...]) -> Function
* - object (Object): The object to bind to.
*
* Wraps the function in another, locking its execution scope to an object
* specified by `object`.
**/
function bind(context) {
if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
var __method = this, args = slice.call(arguments, 1);
return function() {
var a = merge(args, arguments);
return __method.apply(context, a);
}
}
/** related to: Function#bind
* Function#bindAsEventListener(object[, args...]) -> Function
* - object (Object): The object to bind to.
*
* An event-specific variant of [[Function#bind]] which ensures the function
* will recieve the current event object as the first argument when
* executing.
**/
function bindAsEventListener(context) {
var __method = this, args = slice.call(arguments, 1);
return function(event) {
var a = update([event || window.event], args);
return __method.apply(context, a);
}
}
/**
* Function#curry(args...) -> Function
* Partially applies the function, returning a function with one or more
* arguments already “filled in.”
*
* Function#curry works just like [[Function#bind]] without the initial
* scope argument. Use the latter if you need to partially apply a function
* _and_ modify its execution scope at the same time.
**/
function curry() {
if (!arguments.length) return this;
var __method = this, args = slice.call(arguments, 0);
return function() {
var a = merge(args, arguments);
return __method.apply(this, a);
}
}
/**
* Function#delay(seconds[, args...]) -> Number
* - seconds (Number): How long to wait before calling the function.
*
* Schedules the function to run after the specified amount of time, passing
* any arguments given.
*
* Behaves much like `window.setTimeout`. Returns an integer ID that can be
* used to clear the timeout with `window.clearTimeout` before it runs.
*
* To schedule a function to run as soon as the interpreter is idle, use
* [[Function#defer]].
**/
function delay(timeout) {
var __method = this, args = slice.call(arguments, 1);
timeout = timeout * 1000
return window.setTimeout(function() {
return __method.apply(__method, args);
}, timeout);
}
/**
* Function#defer(args...) -> Number
* Schedules the function to run as soon as the interpreter is idle.
*
* A “deferred” function will not run immediately; rather, it will run as soon
* as the interpreters call stack is empty.
*
* Behaves much like `window.setTimeout` with a delay set to `0`. Returns an
* ID that can be used to clear the timeout with `window.clearTimeout` before
* it runs.
**/
function defer() {
var args = update([0.01], arguments);
return this.delay.apply(this, args);
}
/**
* Function#wrap(wrapperFunction) -> Function
* - wrapperFunction (Function): The function to act as a wrapper.
*
* Returns a function “wrapped” around the original function.
*
* `Function#wrap` distills the essence of aspect-oriented programming into
* a single method, letting you easily build on existing functions by
* specifying before and after behavior, transforming the return value, or
* even preventing the original function from being called.
**/
function wrap(wrapper) {
var __method = this;
return function() {
var a = update([__method.bind(this)], arguments);
return wrapper.apply(this, a);
}
}
/**
* Function#methodize() -> Function
* Wraps the function inside another function that, at call time, pushes
* `this` to the original function as the first argument.
*
* Used to define both a generic method and an instance method.
**/
function methodize() {
if (this._methodized) return this._methodized;
var __method = this;
return this._methodized = function() {
var a = update([this], arguments);
return __method.apply(null, a);
};
}
return {
argumentNames: argumentNames,
bind: bind,
bindAsEventListener: bindAsEventListener,
curry: curry,
delay: delay,
defer: defer,
wrap: wrap,
methodize: methodize
}
})());