Path: blob/trunk/third_party/closure/goog/functions/functions.js
3983 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview Utilities for creating functions. Loosely inspired by these8* java classes from the Guava library:9* com.google.common.base.Functions10* https://google.github.io/guava/releases/snapshot-jre/api/docs/index.html?com/google/common/base/Functions.html11*12* com.google.common.base.Predicates13* https://google.github.io/guava/releases/snapshot-jre/api/docs/index.html?com/google/common/base/Predicates.html14*15* More about these can be found at16* https://github.com/google/guava/wiki/FunctionalExplained17*/181920goog.provide('goog.functions');212223/**24* Creates a function that always returns the same value.25* @param {T} retValue The value to return.26* @return {function():T} The new function.27* @template T28*/29goog.functions.constant = function(retValue) {30'use strict';31return function() {32'use strict';33return retValue;34};35};363738/**39* Always returns false.40* @type {function(...): boolean}41*/42goog.functions.FALSE = function() {43'use strict';44return false;45};464748/**49* Always returns true.50* @type {function(...): boolean}51*/52goog.functions.TRUE = function() {53'use strict';54return true;55};565758/**59* Always returns `null`.60* @type {function(...): null}61*/62goog.functions.NULL = function() {63'use strict';64return null;65};666768/**69* Always returns `undefined`.70* @type {function(...): undefined}71*/72goog.functions.UNDEFINED = function() {73return undefined;74};7576/**77* Always returns `undefined` (loosely-typed version).78* @type {!Function}79*/80goog.functions.EMPTY = /** @type {?} */ (goog.functions.UNDEFINED);818283/**84* A simple function that returns the first argument of whatever is passed85* into it.86* @param {T=} opt_returnValue The single value that will be returned.87* @param {...*} var_args Optional trailing arguments. These are ignored.88* @return {T} The first argument passed in, or undefined if nothing was passed.89* @template T90*/91goog.functions.identity = function(opt_returnValue, var_args) {92'use strict';93return opt_returnValue;94};959697/**98* Creates a function that always throws an error with the given message.99* @param {string} message The error message.100* @return {!Function} The error-throwing function.101*/102goog.functions.error = function(message) {103'use strict';104return function() {105'use strict';106throw new Error(message);107};108};109110111/**112* Creates a function that throws the given object.113* @param {*} err An object to be thrown.114* @return {!Function} The error-throwing function.115*/116goog.functions.fail = function(err) {117'use strict';118return function() {119'use strict';120throw err;121};122};123124125/**126* Given a function, create a function that keeps opt_numArgs arguments and127* silently discards all additional arguments.128* @param {Function} f The original function.129* @param {number=} opt_numArgs The number of arguments to keep. Defaults to 0.130* @return {!Function} A version of f that only keeps the first opt_numArgs131* arguments.132*/133goog.functions.lock = function(f, opt_numArgs) {134'use strict';135opt_numArgs = opt_numArgs || 0;136return function() {137'use strict';138const self = /** @type {*} */ (this);139return f.apply(self, Array.prototype.slice.call(arguments, 0, opt_numArgs));140};141};142143144/**145* Creates a function that returns its nth argument.146* @param {number} n The position of the return argument.147* @return {!Function} A new function.148*/149goog.functions.nth = function(n) {150'use strict';151return function() {152'use strict';153return arguments[n];154};155};156157158/**159* Like goog.partial(), except that arguments are added after arguments to the160* returned function.161*162* Usage:163* function f(arg1, arg2, arg3, arg4) { ... }164* var g = goog.functions.partialRight(f, arg3, arg4);165* g(arg1, arg2);166*167* @param {!Function} fn A function to partially apply.168* @param {...*} var_args Additional arguments that are partially applied to fn169* at the end.170* @return {!Function} A partially-applied form of the function goog.partial()171* was invoked as a method of.172*/173goog.functions.partialRight = function(fn, var_args) {174'use strict';175const rightArgs = Array.prototype.slice.call(arguments, 1);176return function() {177'use strict';178// Even in strict mode, IE10/11 and Edge (non-Chromium) use global context179// when free-calling functions. To catch cases where people were using this180// erroneously, we explicitly change the context to undefined to match181// strict mode specifications.182let self = /** @type {*} */ (this);183if (self === goog.global) {184self = undefined;185}186const newArgs = Array.prototype.slice.call(arguments);187newArgs.push.apply(newArgs, rightArgs);188return fn.apply(self, newArgs);189};190};191192193/**194* Given a function, create a new function that swallows its return value195* and replaces it with a new one.196* @param {Function} f A function.197* @param {T} retValue A new return value.198* @return {function(...?):T} A new function.199* @template T200*/201goog.functions.withReturnValue = function(f, retValue) {202'use strict';203return goog.functions.sequence(f, goog.functions.constant(retValue));204};205206207/**208* Creates a function that returns whether its argument equals the given value.209*210* Example:211* var key = goog.object.findKey(obj, goog.functions.equalTo('needle'));212*213* @param {*} value The value to compare to.214* @param {boolean=} opt_useLooseComparison Whether to use a loose (==)215* comparison rather than a strict (===) one. Defaults to false.216* @return {function(*):boolean} The new function.217*/218goog.functions.equalTo = function(value, opt_useLooseComparison) {219'use strict';220return function(other) {221'use strict';222return opt_useLooseComparison ? (value == other) : (value === other);223};224};225226227/**228* Creates the composition of the functions passed in.229* For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)).230* @param {function(...?):T} fn The final function.231* @param {...Function} var_args A list of functions.232* @return {function(...?):T} The composition of all inputs.233* @template T234*/235goog.functions.compose = function(fn, var_args) {236'use strict';237const functions = arguments;238const length = functions.length;239return function() {240'use strict';241const self = /** @type {*} */ (this);242let result;243if (length) {244result = functions[length - 1].apply(self, arguments);245}246247for (let i = length - 2; i >= 0; i--) {248result = functions[i].call(self, result);249}250return result;251};252};253254255/**256* Creates a function that calls the functions passed in in sequence, and257* returns the value of the last function. For example,258* (goog.functions.sequence(f, g))(x) is equivalent to f(x),g(x).259* @param {...Function} var_args A list of functions.260* @return {!Function} A function that calls all inputs in sequence.261*/262goog.functions.sequence = function(var_args) {263'use strict';264const functions = arguments;265const length = functions.length;266return function() {267'use strict';268const self = /** @type {*} */ (this);269let result;270for (let i = 0; i < length; i++) {271result = functions[i].apply(self, arguments);272}273return result;274};275};276277278/**279* Creates a function that returns true if each of its components evaluates280* to true. The components are evaluated in order, and the evaluation will be281* short-circuited as soon as a function returns false.282* For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x).283* @param {...Function} var_args A list of functions.284* @return {function(...?):boolean} A function that ANDs its component285* functions.286*/287goog.functions.and = function(var_args) {288'use strict';289const functions = arguments;290const length = functions.length;291return function() {292'use strict';293const self = /** @type {*} */ (this);294for (let i = 0; i < length; i++) {295if (!functions[i].apply(self, arguments)) {296return false;297}298}299return true;300};301};302303304/**305* Creates a function that returns true if any of its components evaluates306* to true. The components are evaluated in order, and the evaluation will be307* short-circuited as soon as a function returns true.308* For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x).309* @param {...Function} var_args A list of functions.310* @return {function(...?):boolean} A function that ORs its component311* functions.312*/313goog.functions.or = function(var_args) {314'use strict';315const functions = arguments;316const length = functions.length;317return function() {318'use strict';319const self = /** @type {*} */ (this);320for (let i = 0; i < length; i++) {321if (functions[i].apply(self, arguments)) {322return true;323}324}325return false;326};327};328329330/**331* Creates a function that returns the Boolean opposite of a provided function.332* For example, (goog.functions.not(f))(x) is equivalent to !f(x).333* @param {!Function} f The original function.334* @return {function(...?):boolean} A function that delegates to f and returns335* opposite.336*/337goog.functions.not = function(f) {338'use strict';339return function() {340'use strict';341const self = /** @type {*} */ (this);342return !f.apply(self, arguments);343};344};345346347/**348* Generic factory function to construct an object given the constructor349* and the arguments. Intended to be bound to create object factories.350*351* Example:352*353* var factory = goog.partial(goog.functions.create, Class);354*355* @param {function(new:T, ...)} constructor The constructor for the Object.356* @param {...*} var_args The arguments to be passed to the constructor.357* @return {T} A new instance of the class given in `constructor`.358* @template T359* @deprecated This function does not work with ES6 class constructors. Use360* arrow functions + spread args instead.361*/362goog.functions.create = function(constructor, var_args) {363'use strict';364/**365* @constructor366* @final367*/368const temp = function() {};369temp.prototype = constructor.prototype;370371// obj will have constructor's prototype in its chain and372// 'obj instanceof constructor' will be true.373const obj = new temp();374375// obj is initialized by constructor.376// arguments is only array-like so lacks shift(), but can be used with377// the Array prototype function.378constructor.apply(obj, Array.prototype.slice.call(arguments, 1));379return obj;380};381382383/**384* @define {boolean} Whether the return value cache should be used.385* This should only be used to disable caches when testing.386*/387goog.functions.CACHE_RETURN_VALUE =388goog.define('goog.functions.CACHE_RETURN_VALUE', true);389390391/**392* Gives a wrapper function that caches the return value of a parameterless393* function when first called.394*395* When called for the first time, the given function is called and its396* return value is cached (thus this is only appropriate for idempotent397* functions). Subsequent calls will return the cached return value. This398* allows the evaluation of expensive functions to be delayed until first used.399*400* To cache the return values of functions with parameters, see goog.memoize.401*402* @param {function():T} fn A function to lazily evaluate.403* @return {function():T} A wrapped version the function.404* @template T405*/406goog.functions.cacheReturnValue = function(fn) {407'use strict';408let called = false;409let value;410411return function() {412'use strict';413if (!goog.functions.CACHE_RETURN_VALUE) {414return fn();415}416417if (!called) {418value = fn();419called = true;420}421422return value;423};424};425426427/**428* Wraps a function to allow it to be called, at most, once. All429* additional calls are no-ops.430*431* This is particularly useful for initialization functions432* that should be called, at most, once.433*434* @param {function():*} f Function to call.435* @return {function():undefined} Wrapped function.436*/437goog.functions.once = function(f) {438'use strict';439// Keep a reference to the function that we null out when we're done with440// it -- that way, the function can be GC'd when we're done with it.441let inner = f;442return function() {443'use strict';444if (inner) {445const tmp = inner;446inner = null;447tmp();448}449};450};451452453/**454* Wraps a function to allow it to be called, at most, once per interval455* (specified in milliseconds). If the wrapper function is called N times within456* that interval, only the Nth call will go through.457*458* This is particularly useful for batching up repeated actions where the459* last action should win. This can be used, for example, for refreshing an460* autocomplete pop-up every so often rather than updating with every keystroke,461* since the final text typed by the user is the one that should produce the462* final autocomplete results. For more stateful debouncing with support for463* pausing, resuming, and canceling debounced actions, use464* `goog.async.Debouncer`.465*466* @param {function(this:SCOPE, ...?)} f Function to call.467* @param {number} interval Interval over which to debounce. The function will468* only be called after the full interval has elapsed since the last call.469* @param {SCOPE=} opt_scope Object in whose scope to call the function.470* @return {function(...?): undefined} Wrapped function.471* @template SCOPE472*/473goog.functions.debounce = function(f, interval, opt_scope) {474'use strict';475let timeout = 0;476return /** @type {function(...?)} */ (function(var_args) {477'use strict';478goog.global.clearTimeout(timeout);479const args = arguments;480timeout = goog.global.setTimeout(function() {481'use strict';482f.apply(opt_scope, args);483}, interval);484});485};486487488/**489* Wraps a function to allow it to be called, at most, once per interval490* (specified in milliseconds). If the wrapper function is called N times in491* that interval, both the 1st and the Nth calls will go through.492*493* This is particularly useful for limiting repeated user requests where the494* the last action should win, but you also don't want to wait until the end of495* the interval before sending a request out, as it leads to a perception of496* slowness for the user.497*498* @param {function(this:SCOPE, ...?)} f Function to call.499* @param {number} interval Interval over which to throttle. The function can500* only be called once per interval.501* @param {SCOPE=} opt_scope Object in whose scope to call the function.502* @return {function(...?): undefined} Wrapped function.503* @template SCOPE504*/505goog.functions.throttle = function(f, interval, opt_scope) {506'use strict';507let timeout = 0;508let shouldFire = false;509let storedArgs = [];510511const handleTimeout = function() {512'use strict';513timeout = 0;514if (shouldFire) {515shouldFire = false;516fire();517}518};519520const fire = function() {521'use strict';522timeout = goog.global.setTimeout(handleTimeout, interval);523let args = storedArgs;524storedArgs = []; // Avoid a space leak by clearing stored arguments.525f.apply(opt_scope, args);526};527528return /** @type {function(...?)} */ (function(var_args) {529'use strict';530storedArgs = arguments;531if (!timeout) {532fire();533} else {534shouldFire = true;535}536});537};538539540/**541* Wraps a function to allow it to be called, at most, once per interval542* (specified in milliseconds). If the wrapper function is called N times within543* that interval, only the 1st call will go through.544*545* This is particularly useful for limiting repeated user requests where the546* first request is guaranteed to have all the data required to perform the547* final action, so there's no need to wait until the end of the interval before548* sending the request out.549*550* @param {function(this:SCOPE, ...?)} f Function to call.551* @param {number} interval Interval over which to rate-limit. The function will552* only be called once per interval, and ignored for the remainer of the553* interval.554* @param {SCOPE=} opt_scope Object in whose scope to call the function.555* @return {function(...?): undefined} Wrapped function.556* @template SCOPE557*/558goog.functions.rateLimit = function(f, interval, opt_scope) {559'use strict';560let timeout = 0;561562const handleTimeout = function() {563'use strict';564timeout = 0;565};566567return /** @type {function(...?)} */ (function(var_args) {568'use strict';569if (!timeout) {570timeout = goog.global.setTimeout(handleTimeout, interval);571f.apply(opt_scope, arguments);572}573});574};575576/**577* Returns true if the specified value is a function.578* @param {*} val Variable to test.579* @return {boolean} Whether variable is a function.580*/581goog.functions.isFunction = (val) => {582return typeof val === 'function';583};584585586