Функция в javascript, которая может быть вызвана только один раз
Мне нужно создать функцию, которая может быть выполнена только один раз, в каждый раз после первого, он не будет казнен. Я знаю из C++ и Java о статических переменных, которые могут выполнять эту работу, но я хотел бы знать, есть ли более элегантный способ сделать это?
21 ответов:
если под "не будет выполняться" вы имеете в виду" ничего не будет делать при вызове более одного раза", вы можете создать закрытие:
var something = (function() { var executed = false; return function() { if (!executed) { executed = true; // do something } }; })(); something(); // "do something" happens something(); // nothing happensв ответ на комментарий @Vladloffe (теперь удален): с помощью глобальной переменной другой код может сбросить значение флага "выполнено" (независимо от того, какое имя вы выберете для него). С закрытием, другой код не имеет никакого способа сделать это, либо случайно, либо намеренно.
как указывают другие ответы здесь, несколько библиотек (например подчеркивание и Ramda) есть небольшая функция полезности (обычно называется
once()[*]), который принимает функцию в качестве аргумента и возвращает другую функцию, которая вызывает предоставленную функцию ровно один раз, независимо от того, сколько раз вызывается возвращенная функция. Возвращенная функция также кэширует значение, впервые возвращенное предоставленной функцией, и возвращает его при последующих вызовах.однако, если вы не используете такой сторонняя библиотека, но все же нужна такая функция полезности (а не решение nonce, которое я предложил выше), его достаточно легко реализовать. Самая хорошая версия, которую я видел, это это написал Дэвид Уолш:
function once(fn, context) { var result; return function() { if (fn) { result = fn.apply(context || this, arguments); fn = null; } return result; }; }[*]имейте в виду, однако, что другие библиотеки, такие как это расширение Drupal для jQuery, может иметь функцию с именем
once()это делает что-то совсем другое.
замените его многоразовым NOOP (без операции).
// this function does nothing function noop() {}; function foo() { foo = noop; // swap the functions // do your thing } function bar() { bar = noop; // swap the functions // do your thing }
UnderscoreJs имеет функцию, которая делает это,underscorejs.org/#once
// Returns a function that will be executed at most one time, no matter how // often you call it. Useful for lazy initialization. _.once = function(func) { var ran = false, memo; return function() { if (ran) return memo; ran = true; memo = func.apply(this, arguments); func = null; return memo; }; };
просто укажите на пустую функцию после ее вызова.
var myFunc = function(){ myFunc = function(){}; // kill it as soon as it was called console.log('call once and never again!'); // your stuff here };
говоря о статических переменных, это немного похоже на вариант закрытия:
var once = function() { if(once.done) return; console.log('Doing this once!'); once.done = true; }; once(); once();затем вы можете сбросить функцию, если хотите:
once.done = false;
вы могли бы просто иметь функцию "Удалить себя"
function Once(){ console.log("run"); Once = undefined; } Once(); // run Once(); // Uncaught TypeError: undefined is not a functionно это может быть не лучший ответ, если вы не хотите глотать ошибки.
вы также можете сделать это:
function Once(){ console.log("run"); Once = function(){}; } Once(); // run Once(); // nothing happensмне нужно, чтобы он работал как умный указатель, если нет элементов из типа A, он может быть выполнен, если есть один или несколько элементов, функция не может быть выполнена.
function Conditional(){ if (!<no elements from type A>) return; // do stuff }
попробуй такое
var fun = (function() { var called = false; return function() { if (!called) { console.log("I called"); called = true; } } })()
от какого-то чувака по имени Крокфорд... :)
function once(func) { return function () { var f = func; func = null; return f.apply( this, arguments ); }; }
вот пример JSFiddle -http://jsfiddle.net/6yL6t/
и код:
function hashCode(str) { var hash = 0, i, chr, len; if (str.length == 0) return hash; for (i = 0, len = str.length; i < len; i++) { chr = str.charCodeAt(i); hash = ((hash << 5) - hash) + chr; hash |= 0; // Convert to 32bit integer } return hash; } var onceHashes = {}; function once(func) { var unique = hashCode(func.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1]); if (!onceHashes[unique]) { onceHashes[unique] = true; func(); } }вы могли бы сделать:
for (var i=0; i<10; i++) { once(function() { alert(i); }); }и он будет работать только один раз:)
начальная настройка:
var once = function( once_fn ) { var ret, is_called; // return new function which is our control function // to make sure once_fn is only called once: return function(arg1, arg2, arg3) { if ( is_called ) return ret; is_called = true; // return the result from once_fn and store to so we can return it multiply times: // you might wanna look at Function.prototype.apply: ret = once_fn(arg1, arg2, arg3); return ret; }; }
Если вы используете узел.js или написание JavaScript с browserify, рассмотрим " один раз " модуль НПМ:
var once = require('once') function load (file, cb) { cb = once(cb) loader.load('file') loader.once('load', cb) loader.once('error', cb) }
многоразовые
invalidateфункция, которая работает сsetInterval:var myFunc = function (){ if (invalidate(arguments)) return; console.log('called once and never again!'); // your stuff here }; const invalidate = function(a) { var fired = a.callee.fired; a.callee.fired = true; return fired; } setInterval(myFunc, 1000);попробуйте его на JSBin:https://jsbin.com/vicipar/edit?js, консоль
вариация ответ от обращаться к
Если вы хотите иметь возможность повторно использовать функцию в будущем, то это хорошо работает на основе кода Эда Хоппа выше (я понимаю, что исходный вопрос не требовал этой дополнительной функции!):
var something = (function() { var executed = false; return function(value) { // if an argument is not present then if(arguments.length == 0) { if (!executed) { executed = true; //Do stuff here only once unless reset console.log("Hello World!"); } else return; } else { // otherwise allow the function to fire again executed = value; return; } } })(); something();//Hello World! something(); something(); console.log("Reset"); //Reset something(false); something();//Hello World! something(); something();вывод выглядит так:
Hello World! Reset Hello World!
попытка использовать функцию подчеркивания "один раз":
var initialize = _.once(createApplication); initialize(); initialize(); // Application is only created once.
var init = function() { console.log("logges only once"); init = false; }; if(init) { init(); } /* next time executing init() will cause error because now init is -equal to false, thus typing init will return false; */
Если вы используете Ramda, вы можете использовать функцию "как".
цитата из документации:
раз функция (А... → Б) → (В... → Б) ПАРАМЕТРЫ Добавлено в v0.1. 0
принимает функцию fn и возвращает функцию, которая защищает вызов fn таким образом, что fn может быть вызван только один раз, независимо от того, сколько раз вызывается возвращаемая функция. Первое вычисленное значение возвращается в последующем вызовы.
var addOneOnce = R.once(x => x + 1); addOneOnce(10); //=> 11 addOneOnce(addOneOnce(50)); //=> 11
Я пришел с простым декоратором, который легко решает вашу проблему
function one(func) { return function () { func && func.apply(this, arguments); func = null; } }использование:
var initializer= one( _ =>{ console.log('initializing') }) initializer() // 'initializing' initializer() // nop initializer() // nop
if (!window.doesThisOnce){ function myFunction() { // do something window.doesThisOnce = true; }; };
Это полезно для предотвращения бесконечных циклов (с использованием jQuery):
<script> var doIt = true; if(doIt){ // do stuff $('body').html(String($('body').html()).replace("var doIt = true;", "var doIt = false;")); } </script>Если вы беспокоитесь о загрязнении пространства имен, замените длинную случайную строку на "doIt".
это помогает предотвратить липкое выполнение
var done = false; function doItOnce(func){ if(!done){ done = true; func() } setTimeout(function(){ done = false; },1000) }
Comments