Last active
August 29, 2015 13:58
-
-
Save datchley/10177630 to your computer and use it in GitHub Desktop.
Function.prototype extensions, including a bind polyfill and a Function.delay (setTimeout equiv) and Function.repeat (setInterval equiv).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var has_bind_native = Function.prototype.bind ? true : false; | |
| /** | |
| * An ES5 compaitible polyfill for Function.prototype.bind(thisArg, arg1, arg2, ...). Allows you to | |
| * rebind a function to a different this context. This is already available in browsers implementing | |
| * the ES5 standards. | |
| * | |
| * @param {Object} _context - the new object to bind this to in the function | |
| * @returns {Function} - the newly bound function | |
| */ | |
| Function.prototype.bind = Function.prototype.bind || function(_context){ | |
| if(typeof this!=="function"){ | |
| throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); | |
| } | |
| var slice = [].slice, | |
| args = slice.call(arguments, 1), | |
| self = this, | |
| context = _context || (this instanceof Function ? this : window), | |
| bound = function () { | |
| return self.apply(context, args.concat(slice.call(arguments))); | |
| }; | |
| /** @constructor */ | |
| function fn() {} | |
| fn.prototype = self.prototype; | |
| bound.prototype = new fn(); | |
| return bound; | |
| }; | |
| /** | |
| * Delay a function call for `timeout' milliseconds. You can optionally bind the function using a different | |
| * context as well. | |
| * | |
| * @param _this {Object} - the context to bind to the function when calling (defaults to the function if null) | |
| * @param timeout {Number} - the time to wait before calling the function in milliseconds | |
| * @returns id to use with clearTimeout for clearing the timeout | |
| * Any other arguments are passed as standard arguments to the function when called. | |
| */ | |
| Function.prototype.delay = Function.prototype.delay || function(/* context, timeout, arguments */) { | |
| if (typeof this !== 'function') { | |
| throw new TypeError("Function.prototype.delay - being called on a non-function object"); | |
| } | |
| var has_context = (typeof arguments[0] == 'object') ? true : false, | |
| args = [].slice.call(arguments, has_context ? 1 : 0), | |
| timeout = parseInt(args.shift(), 10), | |
| func = this, | |
| context = has_context ? arguments[0] : func; | |
| return window.setTimeout(function() { | |
| func.apply(context, args) | |
| }, timeout); | |
| }; | |
| /** | |
| * Repeat a function on a periodic interval, with an optional upper limit to the number of times to repeat | |
| * the function call. You can optionally bind the function using a different context as well. | |
| * | |
| * @param {Object} context - the context to bind to the function when calling (defaults to the function if null) | |
| * @param {Number} timeout - the time to wait before calling the function in milliseconds | |
| * @param {Number} max - the number of calls to limit this too (null or 0 means no limit) | |
| * @returns {Number} id - the interval id to use with clearInterval | |
| * | |
| * Any other arguments are passed as standard arguments to the function when called. | |
| */ | |
| Function.prototype.repeat = Function.prototype.repeat || function(/* context, timeout, max, arguments */) { | |
| if (typeof this !== 'function') { | |
| throw new TypeError("Function.prototype.repeat - being called on a non-function object"); | |
| } | |
| var has_context = (typeof arguments[0] == 'object') ? true : false, | |
| args = [].slice.call(arguments, has_context ? 1 : 0), | |
| timeout = parseInt(args.shift(), 10), | |
| limit = parseInt(args.shift(), 10) || false, | |
| func = this, | |
| context = has_context ? arguments[0] : func, | |
| interval; | |
| interval = window.setInterval((function() { | |
| var count = 0; | |
| return function() { | |
| count++; | |
| if (limit && count >= limit) { | |
| window.clearInterval(interval); | |
| return; | |
| } | |
| func.apply(context, args); | |
| }; | |
| })(), timeout); | |
| return interval; | |
| }; | |
| // | |
| // UNIT TEST | |
| // | |
| // function to be delayed | |
| function test_timer(a) { | |
| console.log("test_timer: a=%d - (context=%s)", a, typeof this); | |
| } | |
| // function to generate a function to call on an interval | |
| var gen_test_interval = function(ident) { | |
| var count = 0, | |
| fident = ident; | |
| return function(a) { | |
| count++; | |
| console.log("[%d] test_interval(%s): a=%d - (context=%s)", count, fident, a, typeof this); | |
| }; | |
| }; | |
| // generate interval test functions | |
| var test_interval_a = gen_test_interval('a'), | |
| test_interval_b = gen_test_interval('b'); | |
| var _context = { a: 1, b: 2 }; | |
| // test running Function.delay with and w/o new context | |
| test_timer.delay(300, 4); | |
| test_timer.delay(_context, 300, 5); | |
| // test running Function.repeat with and w/o new context | |
| test_interval_a.repeat(1000, 3, 10); | |
| test_interval_b.repeat(_context, 1000, 3, 10); | |
| // test Function.bind | |
| console.log("Using native ES5 Function.bind? ", has_bind_native); | |
| function times(a, b) { | |
| console.log("[rebound] result=%d", (a || this.a) * (b || this.b)); | |
| console.log("-> context=%o", this); | |
| } | |
| rebound = times.bind({ a: 2, b: 3 }); | |
| times(3, 4); | |
| rebound(); | |
| // test binding and then using delay or repeat | |
| function foo() { console.log("[foo] called - (context=%s)", this.name); } | |
| var obj = { name: 'foo.delay' }; // new context | |
| _foo = foo.bind(obj); | |
| _foo.delay(1000); | |
| obj = { name: 'foo.repeat' }; // new context | |
| _foo = foo.bind(obj); | |
| _foo.repeat(1000, 5); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment