Функция в JavaScript, которая может быть вызвана только один раз

Мне нужно создать функцию, которая может быть выполнена только один раз, каждый раз после первого она не будет выполнена. Я знаю из С++ и Java о статических переменных, которые могут выполнять эту работу, но я хотел бы знать, есть ли более элегантный способ сделать это?

Ответ 1

Если под "не будет выполнено" вы имеете в виду "ничего не делать при вызове более одного раза", вы можете создать замыкание:

var something = (function() {
    var executed = false;
    return function() {
        if (!executed) {
            executed = true;
            // do something
        }
    };
})();

something(); // "do something" happens
something(); // nothing happens

В ответ на комментарий @Vladloffe (теперь удалено): с помощью глобальной переменной другой код может сбросить значение флага "выполнено" (любое имя, которое вы выберете для него). При закрытии другой код не может сделать это ни случайно, ни преднамеренно.

Как указывают другие ответы, некоторые библиотеки (такие как Underscore и Ramda) имеют небольшую служебную функцию (обычно называемую once() [*]), которая принимает функцию в качестве аргумента и возвращает другую функцию, которая вызывает предоставленную функцию ровно один раз, независимо от того, сколько раз была вызвана возвращенная функция. Возвращенная функция также кэширует значение, сначала возвращаемое предоставленной функцией, и возвращает его при последующих вызовах.

Однако, если вы не используете такую стороннюю библиотеку, но по-прежнему хотите использовать такую вспомогательную функцию (а не одноразовое решение, которое я предлагал выше), ее достаточно легко реализовать. Самая хорошая версия, которую я видел, это , которую написал Дэвид Уолш:

function once(fn, context) { 
    var result;
    return function() { 
        if (fn) {
            result = fn.apply(context || this, arguments);
            fn = null;
        }
        return result;
    };
}

Я был бы склонен изменить fn = null; на fn = context = null;. Нет причины для закрытия сохранять ссылку на context после вызова fn.

[*] Имейте в виду, что другие библиотеки, такие как это расширение Drupal для jQuery, могут иметь функцию с именем once(), которая делает что-то совсем другое.

Ответ 2

Замените его функцией повторного использования 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
}

Ответ 3

Укажите на пустую функцию после ее вызова:

function myFunc(){
     myFunc = function(){}; // kill it as soon as it was called
     console.log('call once and never again!'); // your stuff here
};
<button onClick=myFunc()>Call myFunc()</button>

Ответ 4

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;
    };
  };

Ответ 5

Говоря о статических переменных, это немного похоже на вариант закрытия:

var once = function() {
    if(once.done) return;
    console.log('Doing this once!');
    once.done = true;
};

once(); once(); 

Вы могли бы reset выполнить функцию, если хотите:

once.done = false;

Ответ 6

var quit = false;

function something() {
    if(quit) {
       return;
    } 
    quit = true;
    ... other code....
}

Ответ 7

Вы можете просто иметь функцию "удалить себя"

​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, он может быть выполнен, если есть один или несколько элементов A, функция не может быть выполнена.

function Conditional(){
    if (!<no elements from type A>) return;

    // do stuff
}

Ответ 8

попробуйте это

var fun = (function() {
  var called = false;
  return function() {
    if (!called) {
      console.log("I  called");
      called = true;
    }
  }
})()

Ответ 9

От какого-то чувака по имени Крокфорд...:)

function once(func) {
    return function () {
        var f = func;
        func = null;
        return f.apply(
            this,
            arguments
        );
    };
}

Ответ 10

Вот пример 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);
    });
}

И он будет работать только один раз:)

Ответ 11

Начальная настройка:

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;
    };
}

Ответ 12

Если вы используете Node.js или записываете JavaScript с браузером, рассмотрите "once" npm module:

var once = require('once')

function load (file, cb) {
  cb = once(cb)
  loader.load('file')
  loader.once('load', cb)
  loader.once('error', cb)
}

Ответ 13

Функция многократного использования 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,console

Изменение ответа от Bunyk

Ответ 14

Если вы хотите иметь возможность повторно использовать функцию в будущем, то это хорошо работает на основе приведенного выше кода Эда Хоппа (я понимаю, что первоначальный вопрос не требовал этой дополнительной функции!):

   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!

Ответ 15

Попытка использовать функцию подчеркивания "один раз":

var initialize = _.once(createApplication);
initialize();
initialize();
// Application is only created once.

http://underscorejs.org/#once

Ответ 16

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; */

Ответ 17

Если вы используете Ramda, вы можете использовать функцию "once" .

Цитата из документации:

один раз Функция (a... → b) → (a... → b) ПАРАМЕТРЫ Добавлено в v0.1.0

Принимает функцию fn и возвращает функцию, которая защищает вызов fn таким образом, что fn может когда-либо вызываться только один раз, независимо от того, сколько раз вызывается возвращаемая функция. Первое вычисляемое значение возвращается в последующих вызовах.

var addOneOnce = R.once(x => x + 1);
addOneOnce(10); //=> 11
addOneOnce(addOneOnce(50)); //=> 11

Ответ 18

простой декоратор, который легко написать, когда вам нужно

function one(func) {
  return function () {
     func && func.apply(this, arguments);
     func = null;
  }
}

с помощью:

var initializer= one( _ =>{
      console.log('initializing')
  })

initializer() // 'initializing'
initializer() // nop
initializer() // nop

Ответ 19

делайте это как можно проще

function sree(){
  console.log('hey');
  window.sree = _=>{};
}

Вы можете увидеть результат

script result

Ответ 20

if (!window.doesThisOnce){
  function myFunction() {
    // do something
    window.doesThisOnce = true;
  };
};

Ответ 21

Это полезно для предотвращения бесконечных циклов (с помощью jQuery):

<script>
var doIt = true;
if(doIt){
  // do stuff
  $('body').html(String($('body').html()).replace("var doIt = true;", 
                                                  "var doIt = false;"));
} 
</script>

Если вас беспокоит загрязнение пространства имен, подставьте длинную случайную строку для "doIt".

Ответ 22

Это помогает предотвратить липкое выполнение

var done = false;

function doItOnce(func){
  if(!done){
    done = true;
    func()
  }
  setTimeout(function(){
    done = false;
  },1000)
}