prototype/src/lang/function.js

168 lines
5.3 KiB
JavaScript
Raw Normal View History

2009-03-05 19:56:01 +00:00
/** section: Language
* class Function
2009-03-20 10:46:53 +00:00
*
* Extensions to the built-in `Function` object.
2008-12-14 04:36:59 +00:00
**/
2008-12-11 10:49:41 +00:00
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);
}
2008-12-14 04:36:59 +00:00
/**
* 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).
**/
2008-12-11 10:49:41 +00:00
function argumentNames() {
var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
.replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
2008-12-11 10:42:15 +00:00
.replace(/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;
2008-12-11 10:49:41 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* 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`.
**/
2008-12-11 10:49:41 +00:00
function bind(context) {
2008-12-11 10:42:15 +00:00
if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
2008-12-11 10:49:41 +00:00
var __method = this, args = slice.call(arguments, 1);
2008-12-11 10:42:15 +00:00
return function() {
2008-12-11 10:49:41 +00:00
var a = merge(args, arguments);
return __method.apply(context, a);
2008-12-11 10:42:15 +00:00
}
2008-12-11 10:49:41 +00:00
}
2008-12-14 04:36:59 +00:00
/** 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.
**/
2008-12-11 10:49:41 +00:00
function bindAsEventListener(context) {
var __method = this, args = slice.call(arguments, 1);
2008-12-11 10:42:15 +00:00
return function(event) {
2008-12-11 10:49:41 +00:00
var a = update([event || window.event], args);
return __method.apply(context, a);
2008-12-11 10:42:15 +00:00
}
2008-12-11 10:49:41 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* 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.
**/
2008-12-11 10:49:41 +00:00
function curry() {
2008-12-11 10:42:15 +00:00
if (!arguments.length) return this;
2008-12-11 10:49:41 +00:00
var __method = this, args = slice.call(arguments, 0);
2008-12-11 10:42:15 +00:00
return function() {
2008-12-11 10:49:41 +00:00
var a = merge(args, arguments);
return __method.apply(this, a);
2008-12-11 10:42:15 +00:00
}
2008-12-11 10:49:41 +00:00
}
2008-12-11 10:42:15 +00:00
2008-12-14 04:36:59 +00:00
/**
* 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]].
**/
2008-12-11 10:49:41 +00:00
function delay(timeout) {
var __method = this, args = slice.call(arguments, 1);
timeout = timeout * 1000
2008-12-11 10:42:15 +00:00
return window.setTimeout(function() {
return __method.apply(__method, args);
}, timeout);
2008-12-11 10:49:41 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* 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.
**/
2008-12-11 10:49:41 +00:00
function defer() {
var args = update([0.01], arguments);
2008-12-11 10:42:15 +00:00
return this.delay.apply(this, args);
2008-12-11 10:49:41 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* 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.
**/
2008-12-11 10:49:41 +00:00
function wrap(wrapper) {
2008-12-11 10:42:15 +00:00
var __method = this;
return function() {
2008-12-11 10:49:41 +00:00
var a = update([__method.bind(this)], arguments);
return wrapper.apply(this, a);
2008-12-11 10:42:15 +00:00
}
2008-12-11 10:49:41 +00:00
}
2008-12-14 04:36:59 +00:00
/**
* 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.
**/
2008-12-11 10:49:41 +00:00
function methodize() {
2008-12-11 10:42:15 +00:00
if (this._methodized) return this._methodized;
var __method = this;
return this._methodized = function() {
2008-12-11 10:49:41 +00:00
var a = update([this], arguments);
return __method.apply(null, a);
2008-12-11 10:42:15 +00:00
};
}
2008-12-11 10:49:41 +00:00
return {
argumentNames: argumentNames,
bind: bind,
bindAsEventListener: bindAsEventListener,
curry: curry,
delay: delay,
defer: defer,
wrap: wrap,
methodize: methodize
}
})());