Path: blob/trunk/third_party/closure/goog/functions/functions.js
2868 views
// Copyright 2008 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Utilities for creating functions. Loosely inspired by the16* java classes: http://goo.gl/GM0Hmu and http://goo.gl/6k7nI8.17*18* @author [email protected] (Nick Santos)19*/202122goog.provide('goog.functions');232425/**26* Creates a function that always returns the same value.27* @param {T} retValue The value to return.28* @return {function():T} The new function.29* @template T30*/31goog.functions.constant = function(retValue) {32return function() { return retValue; };33};343536/**37* Always returns false.38* @type {function(...): boolean}39*/40goog.functions.FALSE = goog.functions.constant(false);414243/**44* Always returns true.45* @type {function(...): boolean}46*/47goog.functions.TRUE = goog.functions.constant(true);484950/**51* Always returns NULL.52* @type {function(...): null}53*/54goog.functions.NULL = goog.functions.constant(null);555657/**58* A simple function that returns the first argument of whatever is passed59* into it.60* @param {T=} opt_returnValue The single value that will be returned.61* @param {...*} var_args Optional trailing arguments. These are ignored.62* @return {T} The first argument passed in, or undefined if nothing was passed.63* @template T64*/65goog.functions.identity = function(opt_returnValue, var_args) {66return opt_returnValue;67};686970/**71* Creates a function that always throws an error with the given message.72* @param {string} message The error message.73* @return {!Function} The error-throwing function.74*/75goog.functions.error = function(message) {76return function() { throw Error(message); };77};787980/**81* Creates a function that throws the given object.82* @param {*} err An object to be thrown.83* @return {!Function} The error-throwing function.84*/85goog.functions.fail = function(err) {86return function() { throw err; };87};888990/**91* Given a function, create a function that keeps opt_numArgs arguments and92* silently discards all additional arguments.93* @param {Function} f The original function.94* @param {number=} opt_numArgs The number of arguments to keep. Defaults to 0.95* @return {!Function} A version of f that only keeps the first opt_numArgs96* arguments.97*/98goog.functions.lock = function(f, opt_numArgs) {99opt_numArgs = opt_numArgs || 0;100return function() {101return f.apply(this, Array.prototype.slice.call(arguments, 0, opt_numArgs));102};103};104105106/**107* Creates a function that returns its nth argument.108* @param {number} n The position of the return argument.109* @return {!Function} A new function.110*/111goog.functions.nth = function(n) {112return function() { return arguments[n]; };113};114115116/**117* Like goog.partial(), except that arguments are added after arguments to the118* returned function.119*120* Usage:121* function f(arg1, arg2, arg3, arg4) { ... }122* var g = goog.functions.partialRight(f, arg3, arg4);123* g(arg1, arg2);124*125* @param {!Function} fn A function to partially apply.126* @param {...*} var_args Additional arguments that are partially applied to fn127* at the end.128* @return {!Function} A partially-applied form of the function goog.partial()129* was invoked as a method of.130*/131goog.functions.partialRight = function(fn, var_args) {132var rightArgs = Array.prototype.slice.call(arguments, 1);133return function() {134var newArgs = Array.prototype.slice.call(arguments);135newArgs.push.apply(newArgs, rightArgs);136return fn.apply(this, newArgs);137};138};139140141/**142* Given a function, create a new function that swallows its return value143* and replaces it with a new one.144* @param {Function} f A function.145* @param {T} retValue A new return value.146* @return {function(...?):T} A new function.147* @template T148*/149goog.functions.withReturnValue = function(f, retValue) {150return goog.functions.sequence(f, goog.functions.constant(retValue));151};152153154/**155* Creates a function that returns whether its argument equals the given value.156*157* Example:158* var key = goog.object.findKey(obj, goog.functions.equalTo('needle'));159*160* @param {*} value The value to compare to.161* @param {boolean=} opt_useLooseComparison Whether to use a loose (==)162* comparison rather than a strict (===) one. Defaults to false.163* @return {function(*):boolean} The new function.164*/165goog.functions.equalTo = function(value, opt_useLooseComparison) {166return function(other) {167return opt_useLooseComparison ? (value == other) : (value === other);168};169};170171172/**173* Creates the composition of the functions passed in.174* For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)).175* @param {function(...?):T} fn The final function.176* @param {...Function} var_args A list of functions.177* @return {function(...?):T} The composition of all inputs.178* @template T179*/180goog.functions.compose = function(fn, var_args) {181var functions = arguments;182var length = functions.length;183return function() {184var result;185if (length) {186result = functions[length - 1].apply(this, arguments);187}188189for (var i = length - 2; i >= 0; i--) {190result = functions[i].call(this, result);191}192return result;193};194};195196197/**198* Creates a function that calls the functions passed in in sequence, and199* returns the value of the last function. For example,200* (goog.functions.sequence(f, g))(x) is equivalent to f(x),g(x).201* @param {...Function} var_args A list of functions.202* @return {!Function} A function that calls all inputs in sequence.203*/204goog.functions.sequence = function(var_args) {205var functions = arguments;206var length = functions.length;207return function() {208var result;209for (var i = 0; i < length; i++) {210result = functions[i].apply(this, arguments);211}212return result;213};214};215216217/**218* Creates a function that returns true if each of its components evaluates219* to true. The components are evaluated in order, and the evaluation will be220* short-circuited as soon as a function returns false.221* For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x).222* @param {...Function} var_args A list of functions.223* @return {function(...?):boolean} A function that ANDs its component224* functions.225*/226goog.functions.and = function(var_args) {227var functions = arguments;228var length = functions.length;229return function() {230for (var i = 0; i < length; i++) {231if (!functions[i].apply(this, arguments)) {232return false;233}234}235return true;236};237};238239240/**241* Creates a function that returns true if any of its components evaluates242* to true. The components are evaluated in order, and the evaluation will be243* short-circuited as soon as a function returns true.244* For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x).245* @param {...Function} var_args A list of functions.246* @return {function(...?):boolean} A function that ORs its component247* functions.248*/249goog.functions.or = function(var_args) {250var functions = arguments;251var length = functions.length;252return function() {253for (var i = 0; i < length; i++) {254if (functions[i].apply(this, arguments)) {255return true;256}257}258return false;259};260};261262263/**264* Creates a function that returns the Boolean opposite of a provided function.265* For example, (goog.functions.not(f))(x) is equivalent to !f(x).266* @param {!Function} f The original function.267* @return {function(...?):boolean} A function that delegates to f and returns268* opposite.269*/270goog.functions.not = function(f) {271return function() { return !f.apply(this, arguments); };272};273274275/**276* Generic factory function to construct an object given the constructor277* and the arguments. Intended to be bound to create object factories.278*279* Example:280*281* var factory = goog.partial(goog.functions.create, Class);282*283* @param {function(new:T, ...)} constructor The constructor for the Object.284* @param {...*} var_args The arguments to be passed to the constructor.285* @return {T} A new instance of the class given in {@code constructor}.286* @template T287*/288goog.functions.create = function(constructor, var_args) {289/**290* @constructor291* @final292*/293var temp = function() {};294temp.prototype = constructor.prototype;295296// obj will have constructor's prototype in its chain and297// 'obj instanceof constructor' will be true.298var obj = new temp();299300// obj is initialized by constructor.301// arguments is only array-like so lacks shift(), but can be used with302// the Array prototype function.303constructor.apply(obj, Array.prototype.slice.call(arguments, 1));304return obj;305};306307308/**309* @define {boolean} Whether the return value cache should be used.310* This should only be used to disable caches when testing.311*/312goog.define('goog.functions.CACHE_RETURN_VALUE', true);313314315/**316* Gives a wrapper function that caches the return value of a parameterless317* function when first called.318*319* When called for the first time, the given function is called and its320* return value is cached (thus this is only appropriate for idempotent321* functions). Subsequent calls will return the cached return value. This322* allows the evaluation of expensive functions to be delayed until first used.323*324* To cache the return values of functions with parameters, see goog.memoize.325*326* @param {function():T} fn A function to lazily evaluate.327* @return {function():T} A wrapped version the function.328* @template T329*/330goog.functions.cacheReturnValue = function(fn) {331var called = false;332var value;333334return function() {335if (!goog.functions.CACHE_RETURN_VALUE) {336return fn();337}338339if (!called) {340value = fn();341called = true;342}343344return value;345};346};347348349/**350* Wraps a function to allow it to be called, at most, once. All351* additional calls are no-ops.352*353* This is particularly useful for initialization functions354* that should be called, at most, once.355*356* @param {function():*} f Function to call.357* @return {function():undefined} Wrapped function.358*/359goog.functions.once = function(f) {360// Keep a reference to the function that we null out when we're done with361// it -- that way, the function can be GC'd when we're done with it.362var inner = f;363return function() {364if (inner) {365var tmp = inner;366inner = null;367tmp();368}369};370};371372373/**374* Wraps a function to allow it to be called, at most, once per interval375* (specified in milliseconds). If the wrapper function is called N times within376* that interval, only the Nth call will go through.377*378* This is particularly useful for batching up repeated actions where the379* last action should win. This can be used, for example, for refreshing an380* autocomplete pop-up every so often rather than updating with every keystroke,381* since the final text typed by the user is the one that should produce the382* final autocomplete results. For more stateful debouncing with support for383* pausing, resuming, and canceling debounced actions, use {@code384* goog.async.Debouncer}.385*386* @param {function(this:SCOPE, ...?)} f Function to call.387* @param {number} interval Interval over which to debounce. The function will388* only be called after the full interval has elapsed since the last call.389* @param {SCOPE=} opt_scope Object in whose scope to call the function.390* @return {function(...?): undefined} Wrapped function.391* @template SCOPE392*/393goog.functions.debounce = function(f, interval, opt_scope) {394var timeout = 0;395return /** @type {function(...?)} */ (function(var_args) {396goog.global.clearTimeout(timeout);397var args = arguments;398timeout = goog.global.setTimeout(function() {399f.apply(opt_scope, args);400}, interval);401});402};403404405/**406* Wraps a function to allow it to be called, at most, once per interval407* (specified in milliseconds). If the wrapper function is called N times in408* that interval, both the 1st and the Nth calls will go through.409*410* This is particularly useful for limiting repeated user requests where the411* the last action should win, but you also don't want to wait until the end of412* the interval before sending a request out, as it leads to a perception of413* slowness for the user.414*415* @param {function(this:SCOPE, ...?)} f Function to call.416* @param {number} interval Interval over which to throttle. The function can417* only be called once per interval.418* @param {SCOPE=} opt_scope Object in whose scope to call the function.419* @return {function(...?): undefined} Wrapped function.420* @template SCOPE421*/422goog.functions.throttle = function(f, interval, opt_scope) {423var timeout = 0;424var shouldFire = false;425var args = [];426427var handleTimeout = function() {428timeout = 0;429if (shouldFire) {430shouldFire = false;431fire();432}433};434435var fire = function() {436timeout = goog.global.setTimeout(handleTimeout, interval);437f.apply(opt_scope, args);438};439440return /** @type {function(...?)} */ (function(var_args) {441args = arguments;442if (!timeout) {443fire();444} else {445shouldFire = true;446}447});448};449450451/**452* Wraps a function to allow it to be called, at most, once per interval453* (specified in milliseconds). If the wrapper function is called N times within454* that interval, only the 1st call will go through.455*456* This is particularly useful for limiting repeated user requests where the457* first request is guaranteed to have all the data required to perform the458* final action, so there's no need to wait until the end of the interval before459* sending the request out.460*461* @param {function(this:SCOPE, ...?)} f Function to call.462* @param {number} interval Interval over which to rate-limit. The function will463* only be called once per interval, and ignored for the remainer of the464* interval.465* @param {SCOPE=} opt_scope Object in whose scope to call the function.466* @return {function(...?): undefined} Wrapped function.467* @template SCOPE468*/469goog.functions.rateLimit = function(f, interval, opt_scope) {470var timeout = 0;471472var handleTimeout = function() {473timeout = 0;474};475476return /** @type {function(...?)} */ (function(var_args) {477if (!timeout) {478timeout = goog.global.setTimeout(handleTimeout, interval);479f.apply(opt_scope, arguments);480}481});482};483484485