-
Notifications
You must be signed in to change notification settings - Fork 639
/
function.js
167 lines (155 loc) · 5.29 KB
/
function.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/** section: Language
* class Function
*
* Extensions to the built-in `Function` object.
**/
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 interpreter's 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
}
})());