Параметр функции не определен [дубликат]

Одна из возможностей заключается в использовании сериализации:

Apache Commons предоставляет SerializationUtils

679
задан Shog9 27 July 2009 в 23:54
поделиться

18 ответов

setTimeout(function() {
    postinsql(topicId);
}, 4000)

Вам нужно подавать анонимную функцию как параметр вместо строки, последний метод не должен работать даже по спецификации ECMAScript, но браузеры просто снисходительны. Это правильное решение, никогда не полагайтесь на передачу строки как «функции» при использовании setTimeout() или setInterval(), она медленнее, потому что она должна быть оценена, и это просто неправильно.

UPDATE:

Как сказал Хоблин в своих комментариях к вопросу, теперь вы можете передать аргументы функции внутри setTimeout с помощью Function.prototype.bind()

Пример:

setTimeout(postinsql.bind(null, topicId), 4000);
946
ответ дан Nidhin David 27 August 2018 в 22:58
поделиться
  • 1
    window.setTimeout является методом DOM и, как таковой, не определяется спецификацией ECMAScript. Передача строки всегда работала в браузерах и была стандартом de facto - на самом деле возможность добавления объекта функции была добавлена ​​позже, с JavaScript 1.2 - она ​​явно является частью спецификации HTML5 ( [д0] whatwg.org/specs/web-apps/current-work/multipage/… [/ д0]). Однако использование строки вместо объекта функции обычно считается плохим, потому что это по существу форма задержки eval(). – Miles 27 July 2009 в 22:41
  • 2
    var temp = setTimeout (function () {postinsql (topicId);}, 4000); clearTimeout (температура); ?? – Josh Mc 19 June 2012 в 05:00
  • 3
    Что произойдет, если topicId изменится после истечения времени ожидания, но до вызова функции? – pilau 2 November 2012 в 02:19
  • 4
    @pilau - это именно моя проблема: если переменные, используемые в анонимной функции, изменяются до таймаута (например, в цикле for), то он также будет меняться внутри функции. Поэтому в моем примере установка 5 разных тайм-аутов в цикле for фактически закончилась тем же использованием переменных. Будьте осторожны при использовании этого ответа! – Cristian 8 May 2013 в 09:27
  • 5
    @pilau с использованием другого закрытия поможет topicId = 12; function postinsql (topicId) {console.log (topicId); } function setTimeOutWithClosure (topicId) {setTimeout (function () {postinsql (topicId);}, 1000)} setTimeOutFunction (topicId); TopicID = 13; – Halis Yılboğa 23 December 2013 в 10:31

Как я решил этот этап?

так же:

setTimeout((function(_deepFunction ,_deepData){
    var _deepResultFunction = function _deepResultFunction(){
          _deepFunction(_deepData);
    };
    return _deepResultFunction;
})(fromOuterFunction, fromOuterData ) , 1000  );

setTimeout ждать ссылки на функцию, поэтому я создал ее в закрытии, которое интерпретирует мои данные и возвращать функцию с хорошим экземпляром моих данных!

Возможно, вы можете улучшить эту часть:

_deepFunction(_deepData);

// change to something like :
_deepFunction.apply(contextFromParams , args); 

Я протестировал ее на chrome, firefox и IE, и она хорошо работает, я не знаю о производительности, но мне нужно, чтобы он работал.

образец теста:

myDelay_function = function(fn , params , ctxt , _time){
setTimeout((function(_deepFunction ,_deepData, _deepCtxt){
            var _deepResultFunction = function _deepResultFunction(){
                //_deepFunction(_deepData);
                _deepFunction.call(  _deepCtxt , _deepData);
            };
        return _deepResultFunction;
    })(fn , params , ctxt)
, _time) 
};

// the function to be used :
myFunc = function(param){ console.log(param + this.name) }
// note that we call this.name

// a context object :
myObjet = {
    id : "myId" , 
    name : "myName"
}

// setting a parmeter
myParamter = "I am the outer parameter : ";

//and now let's make the call :
myDelay_function(myFunc , myParamter  , myObjet , 1000)

// this will produce this result on the console line :
// I am the outer parameter : myName

Возможно, вы можете изменить подпись, чтобы сделать ее более уступчивой:

myNass_setTimeOut = function (fn , _time , params , ctxt ){
return setTimeout((function(_deepFunction ,_deepData, _deepCtxt){
            var _deepResultFunction = function _deepResultFunction(){
                //_deepFunction(_deepData);
                _deepFunction.apply(  _deepCtxt , _deepData);
            };
        return _deepResultFunction;
    })(fn , params , ctxt)
, _time) 
};

// and try again :
for(var i=0; i<10; i++){
   myNass_setTimeOut(console.log ,1000 , [i] , console)
}

И окончательно ответить на исходный вопрос:

 myNass_setTimeOut( postinsql, 4000, topicId );

Надеюсь, это поможет!

ps: извините, но английский это не мой родной язык!

2
ответ дан Anonymous0day 27 August 2018 в 22:58
поделиться
  • 1
    Это слишком сложно, по сравнению с другими ответами. – Dan Dascalescu 17 April 2015 в 07:21

Обратите внимание, что причина, по которой topicId была «не определена» в сообщении об ошибке, заключается в том, что она существовала как локальная переменная, когда был запущен setTimeout, но не тогда, когда произошел отложенный вызов postinsql. Важное значение имеет значение переменной, особенно когда вы пытаетесь что-то вроде передачи «this» в качестве ссылки на объект.

Я слышал, что вы можете передать topicId в качестве третьего параметра функции setTimeout. Не так много деталей, но я получил достаточно информации, чтобы заставить ее работать, и она успешна в Safari. Я не знаю, что они означают о «миллисекундовой ошибке». Проверьте это здесь:

http://www.howtocreate.co.uk/tutorials/javascript/timers

2
ответ дан billy 27 August 2018 в 22:58
поделиться

Это очень старый вопрос с уже «правильным» ответом, но я думал, что упомянул еще один подход, который никто не упомянул здесь. Это скопировано и вставлено из превосходной библиотеки underscore :

_.delay = function(func, wait) {
  var args = slice.call(arguments, 2);
  return setTimeout(function(){ return func.apply(null, args); }, wait);
};

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

Вот скрипка , где вы можете видеть, что я имею в виду.

43
ответ дан brasofilo 27 August 2018 в 22:58
поделиться
  • 1
    Этот ответ на самом деле работает, но у вас, похоже, есть библиотека, которой я не занимаюсь. Вот небольшое исправление для его работы: вместо slice.call используйте Array.prototype.slice.call (аргументы, 2) – Melanie 3 October 2013 в 17:44
  • 2
    @Melanie "некоторая библиотека"? Я сказал в ответ, что это библиотека подчёркивания - underscorejs.org . Но да, Array.prototype.slice псевдонимы, чтобы срезать внутри этой библиотеки, поэтому вы должны сделать это сами, если вы не используете его, хорошее место :) – David Meister 4 October 2013 в 15:33

Некоторые ответы правильные, но запутанные.

Я отвечаю на это еще раз, 4 года спустя, потому что я все еще сталкиваюсь с чрезмерно сложным кодом, чтобы решить именно этот вопрос.

Прежде всего, не передавайте строку в качестве первого параметра при вызове setTimeout, потому что он эффективно вызывает вызов медленной функции «eval».

Итак, как мы передаем параметр функции таймаута? Используя закрытие:

settopic=function(topicid){
  setTimeout(function(){
    //thanks to closure, topicid is visible here
    postinsql(topicid);
  },4000);
}

...
if (xhr.readyState==4){
  settopic(xhr.responseText);
}

Некоторые предложили использовать анонимную функцию при вызове функции таймаута:

if (xhr.readyState==4){
  setTimeout(function(){
    settopic(xhr.responseText);
  },4000);
}

Синтаксис работает. Но к тому времени, когда вызывается оконечное устройство, т. Е. Через 4 секунды, объект XHR может быть не таким. Поэтому важно предварительно привязать переменные .

34
ответ дан Community 27 August 2018 в 22:58
поделиться
  • 1
    +1 для читаемого решения, немного отличающегося от моего. Хотя у вас есть setTimeout внутри функции setopic, у меня есть функция fDelayed (ваш settopic) внутри функции setTimeout. – Dominic 20 January 2014 в 11:02
  • 2
    Примечание: .bind не будет работать для IE8 и ниже [ref: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ]. Я закончил использование решения Schien: stackoverflow.com/a/21213723/1876899 – cjspurgeon 22 April 2014 в 19:35
  • 3
    Если в среде, использующей bind, вы также находитесь в среде, которая предлагает Object.keys и forEach. Вы можете потерять цикл for и получить «бесплатный». (как у двух птиц с одним камнем, свободным от ресурсов). – Sukima 8 July 2014 в 13:08
  • 4
    @David Sherret, если вы не использовали его раньше, определенно проверьте библиотеку async ( github.com/caolan/async ). Мы широко используем его в Sails и добились отличных результатов за последние 2 года. Он предоставляет методы как параллельно, так и последовательно для асинхронных forEach, map, reduce и т. Д. – mikermcneil 23 October 2015 в 12:26

Я знаю, что он старый, но я хотел добавить свой (предпочтительный) вкус к этому.

Я думаю, что довольно читаемый способ добиться этого - передать функцию topicId функции, которая, в свою очередь, использует аргумент для ссылки на идентификатор темы внутри. Это значение не изменится, даже если topicId снаружи будет изменено вскоре после.

var topicId = xmlhttp.responseText;
var fDelayed = function(tid) {
  return function() {
    postinsql(tid);
  };
}
setTimeout(fDelayed(topicId),4000);

или короткое:

var topicId = xmlhttp.responseText;
setTimeout(function(tid) {
  return function() { postinsql(tid); };
}(topicId), 4000);
3
ответ дан Dominic 27 August 2018 в 22:58
поделиться

В современных браузерах «setTimeout» получает третий параметр, который отправляется как параметр для внутренней функции в конце таймера.

Пример:

var hello = "Hello World";
setTimeout(alert, 1000, hello);

Подробнее:

577
ответ дан Fabio Phms 27 August 2018 в 22:58
поделиться
  • 1
    Я не уверен, почему этот ответ не был выбран как лучший. Использование анонимной функции работает, конечно, но если вы можете просто передать третий параметр в исходный вызов функции setTimeout ... почему бы и нет? – Kris Schouw 21 September 2011 в 18:36
  • 2
    Потому что он не работает в версиях IE все еще очень в дикой природе. – Aaron 8 February 2012 в 08:32
  • 3
    Этот ответ фактически дал мне возможность передать объект события, другие методы этого не сделали. У меня уже была анонимная функция. – Glenn Plas 10 October 2012 в 23:40
  • 4
    Совсем лучше. Если у вас есть код, который изменяет ваш параметр между & quot; setTimeout & quot; вызов и фактическое выполнение анонимной функции - анонимная функция получит измененное значение, а не то, что было во время вызова setTimeout. например: for (var i = 0; i & lt; 100; i ++) {setTimeout (function () {console.write (i);}, 0); } это будет журнал "100" 100 раз (проверено на FF). Текущий ответ помогает избежать этого. – root 14 December 2012 в 19:21
  • 5
    Это работает в IE10 и, возможно, в некоторых более низких версиях. – Beachhouse 13 May 2013 в 19:56

Ответ Дэвида Майстера, похоже, позаботится о параметрах, которые могут измениться сразу после вызова setTimeout (), но до вызова анонимной функции. Но это слишком громоздко и не очень очевидно. Я обнаружил элегантный способ сделать почти то же самое, используя IIFE (сразу inviked выражение функции).

В приведенном ниже примере переменная currentList передается в IIFE, которая сохраняет ее при ее закрытии , пока не будет вызвана функция задержки. Даже если переменная currentList изменяется сразу после показанного кода, setInterval() будет поступать правильно.

Без этой техники IIFE функция setTimeout(), безусловно, будет вызвана для каждого h2 элемент в DOM, но все эти вызовы будут видеть только текстовое значение последнего элемента h2.

<script>
  // Wait for the document to load.
  $(document).ready(function() {
  $("h2").each(function (index) {

    currentList = $(this).text();

    (function (param1, param2) {
        setTimeout(function() {
            $("span").text(param1 + ' : ' + param2 );
        }, param1 * 1000);

    })(index, currentList);
  });
</script>
2
ответ дан Gurjeet Singh 27 August 2018 в 22:58
поделиться

После выполнения некоторых исследований и тестирования единственная правильная реализация:

setTimeout(yourFunctionReference, 4000, param1, param2, paramN);

setTimeout передаст все дополнительные параметры вашей функции, чтобы их можно было обработать там.

анонимная функция может работать для очень простых вещей, но в пределах экземпляра объекта, где вы должны использовать «это», нет способа заставить его работать. Любая анонимная функция изменит «это», чтобы указать на окно, поэтому вы потеряете ссылку на объект.

116
ответ дан Jiri Vetyska 27 August 2018 в 22:58
поделиться
  • 1
    С грустью в моем сердце я должен сообщить: это не работает в Internet Explorer. : / Все дополнительные параметры выдаются как неопределенные. – Amalgovinus 24 April 2013 в 01:39
  • 2
    Я просто использую var that = this; setTimeout( function() { that.foo(); }, 1000); – Ed Williams 7 June 2013 в 07:41
  • 3
    Это правильно, и это указано в HTML5. [Д0] whatwg.org/specs/web-apps/current-work/multipage/… – Garrett 13 January 2014 в 02:15
  • 4
    Это точно такой же ответ, как Фабио . – Dan Dascalescu 17 April 2015 в 07:16
  • 5
    Согласно developer.mozilla.org/es/docs/Web/API/WindowTimers/setTimeout аргументы обратного вызова для Internet Explorer поддерживаются только в версиях & gt; = 10, будьте осторожны, как на многих сайтах ie8 и ie9 все еще получает определенную долю. – le0diaz 3 June 2015 в 16:09

В общем случае, если вам нужно передать функцию в качестве обратного вызова с определенными параметрами, вы можете использовать функции более высокого порядка. Это довольно элегантно с ES6:

const someFunction = (params) => () => {
  //do whatever
};

setTimeout(someFunction(params), 1000);

Или если someFunction - первый порядок:

setTimeout(() => someFunction(params), 1000); 
1
ответ дан John Hartman 27 August 2018 в 22:58
поделиться

Хобблин уже прокомментировал это по этому вопросу, но на самом деле это должен быть ответ!

Использование Function.prototype.bind() - самый чистый и самый гибкий способ сделать это (с добавленным бонусом к возможности установить контекст this):

setTimeout(postinsql.bind(null, topicId), 4000);

Для получения дополнительной информации см. эти ссылки MDN: https://developer.mozilla.org/en/docs/DOM/window.setTimeout#highlighter_547041 https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/Function/bind#With_setTimeout

22
ответ дан Mark Amery 27 August 2018 в 22:58
поделиться
  • 1
    этот контекст может быть передан с первым аргументом bind setTimeout(postinsql.bind(this, topicId), 4000); – Giuseppe Galano 11 December 2013 в 16:29
  • 2
    @GiuseppeGalano полностью, я упомянул об этом в своем ответе, но это не нужно для этого примера :) – dain 15 December 2013 в 02:47
  • 3
    Увлекательное количество блеска частичных приложений с помощью bind. Это действительно делает для некоторого читаемого кода. – Sukima 8 July 2014 в 13:05
  • 4
    bind () поддерживается только с IE9 +, поэтому этот подход не будет работать для & lt; IE9 – Sanjeev 6 August 2014 в 06:18
  • 5
    @Sanjeev Используйте прорезь ES5, чтобы заставить его работать в более старом IE: github.com/es-shims/es5-shim – gregers 4 March 2016 в 17:24

Самое простое решение для кросс-браузера для поддержки параметров в setTimeout:

setTimeout(function() {
    postinsql(topicId);
}, 4000)

Если вы не возражаете против поддержки IE 9 и ниже:

setTimeout(postinsql, 4000, topicId);

setTimeout desktop browser compatibility [/g1]

setTimeout mobile browser compatibility [/g2]

https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/ SetTimeout

5
ответ дан Michael J. Calkins 27 August 2018 в 22:58
поделиться

Замените

 setTimeout("postinsql(topicId)", 4000);

на

 setTimeout("postinsql(" + topicId + ")", 4000);

или еще лучше, замените строковое выражение на анонимную функцию

 setTimeout(function () { postinsql(topicId); }, 4000);

EDIT:

Комментарий Браунстоуна неверен, это будет работать так, как было показано, как показано на рисунке ниже, в этом случае в консоли Firebug

(function() {
  function postinsql(id) {
    console.log(id);
  }
  var topicId = 3
  window.setTimeout("postinsql(" + topicId + ")",4000); // outputs 3 after 4 seconds
})();

Обратите внимание, что я согласен с другими, что вам следует избегать передачи строка в setTimeout, так как это вызовет eval() в строке и вместо этого передаст функцию.

9
ответ дан Russ Cam 27 August 2018 в 22:58
поделиться
  • 1
    Это не будет работать, потому что результат result postinsql (topicId) будет выполнен с помощью setTimeout. Вам нужно обернуть его в функцию, как с первым ответом, или использовать помощника, например, Prototype .curry () - setTimeout (postinsql.curry (topidId), 4000); – shuckster 27 July 2009 в 22:20
  • 2
    @brownstone: Это неправильно. Строка будет оцениваться при срабатывании таймаута. – Miles 28 July 2009 в 00:09

Я думаю, что вы хотите:

setTimeout("postinsql(" + topicId + ")", 4000);
-1
ответ дан user 27 August 2018 в 22:58
поделиться
  • 1
    Я запускал экземпляры, где это просто не работает (всегда приводя к ошибке «функция не определена»), но использование анонимной функции действительно работает. Это разочаровывает, учитывая, что каждый, кажется, говорит, что синтаксис, указанный выше, должен всегда работать. (может быть, jQuery каким-то образом мешает методу «quote as string»?) – DA. 29 April 2010 в 19:00
  • 2
    Предположим, что topicId - это функция ... Или объект. Это не сработает! – Serafeim 23 November 2011 в 15:41
  • 3
    Это лучший ответ. Многие из этих решений даже не будут соблюдать тайм-аут. Однако почему вы закрываете первую анонимную функцию в скобках? Я не думаю, что они необходимы для этого. – Robert Henderson 2 March 2018 в 00:40
  • 4
    это лучший ответ – pariola 4 July 2018 в 21:45

это работает во всех браузерах (IE - странный)

setTimeout( (function(x) {
return function() {
        postinsql(x);
    };
})(topicId) , 4000);
3
ответ дан user3756459 27 August 2018 в 22:58
поделиться

Поскольку проблема с третьим параметром optonal в IE и использование замыканий не позволяет нам изменять переменные (например, в цикле) и все еще достигая желаемого результата, я предлагаю следующее решение.

Мы можем попытаться использовать рекурсию следующим образом:

var i = 0;
var hellos = ["Hello World1!", "Hello World2!", "Hello World3!", "Hello World4!", "Hello World5!"];

if(hellos.length > 0) timeout();

function timeout() {                
    document.write('<p>' + hellos[i] + '<p>');
    i++;
    if (i < hellos.length)
        setTimeout(timeout, 500);
}

Нам нужно убедиться, что ничего не изменит эти переменные и мы напишем правильное условие рекурсии, чтобы избежать бесконечной рекурсии.

0
ответ дан Vakhtang Tevdorashvili 27 August 2018 в 22:58
поделиться

Вы можете попробовать использовать функции «apply ()» по умолчанию, вы можете передать большее количество аргументов в качестве вашего требования в массиве

function postinsql(topicId)
{
  //alert(topicId);
}
setTimeout(
       postinsql.apply(window,["mytopic"])
,500);
0
ответ дан Vishnu Prasanth G 27 August 2018 в 22:58
поделиться

@Jiri Vetyska благодарит за сообщение, но в вашем примере что-то не так. Мне нужно было передать цель, которая зависла (это) до функции времени ожидания, и я попробовал ваш подход. Протестировано в IE9 - не работает. Я также сделал некоторые исследования, и кажется, что в качестве здесь указан третий параметр - используемый язык сценария.

Итак, я последовал за ответом @ meder и решил проблему с этим кодом:

$('.targetItemClass').hover(ItemHoverIn, ItemHoverOut);

function ItemHoverIn() {
 //some code here
}

function ItemHoverOut() {
    var THIS = this;
    setTimeout(
        function () { ItemHoverOut_timeout(THIS); },
        100
    );
}
function ItemHoverOut_timeout(target) {
    //do something with target which is hovered out
}

Надеюсь, это полезно для кого-то еще.

0
ответ дан Vladislav 27 August 2018 в 22:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: