javascript second - 我怎樣才能傳遞參數給setTimeout()回調?




typescript jsp (17)

我有一些JavaScript代碼,如下所示:

function statechangedPostQuestion()
{
  //alert("statechangedPostQuestion");
  if (xmlhttp.readyState==4)
  {
    var topicId = xmlhttp.responseText;
    setTimeout("postinsql(topicId)",4000);
  }
}

function postinsql(topicId)
{
  //alert(topicId);
}

我得到一個錯誤, topicId沒有被定義在我使用setTimeout()函數之前,一切都在工作。

我想在一段時間後調用postinsql(topicId)函數。 我該怎麼辦?


Answers

我最近遇到了需要在循環中使用setTimeout的獨特情況。 了解這可以幫助您了解如何將參數傳遞給setTimeout

方法1

按照Sukima的suggestion使用forEachObject.keys

var testObject = {
    prop1: 'test1',
    prop2: 'test2',
    prop3: 'test3'
};

Object.keys(testObject).forEach(function(propertyName, i) {
    setTimeout(function() {
        console.log(testObject[propertyName]);
    }, i * 1000);
});

我推薦這種方法。

方法2

使用bind

var i = 0;
for (var propertyName in testObject) {
    setTimeout(function(propertyName) {
        console.log(testObject[propertyName]);
    }.bind(this, propertyName), i++ * 1000);
}

JSFiddle: http://jsfiddle.net/MsBkW/ : http://jsfiddle.net/MsBkW/

方法3

或者,如果您無法使用forEachbind ,請使用IIFE

var i = 0;
for (var propertyName in testObject) {
    setTimeout((function(propertyName) {
        return function() {
            console.log(testObject[propertyName]);
        };
    })(propertyName), i++ * 1000);
}

方法4

但是如果你不關心IE <10,那麼你可以使用法比奧的suggestion :

var i = 0;
for (var propertyName in testObject) {
    setTimeout(function(propertyName) {
        console.log(testObject[propertyName]);
    }, i++ * 1000, propertyName);
}

方法5(ES6)

使用塊範圍變量:

let i = 0;
for (let propertyName in testObject) {
    setTimeout(() => console.log(testObject[propertyName]), i++ * 1000);
}

儘管我仍然推薦在ES6中使用forEach來使用Object.keys


Hobblin已經在這個問題上評論過了,但它應該是一個真正的答案!

使用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



David Meister的回答似乎處理了在調用setTimeout()之後但在調用匿名函數之前可能立即改變的參數。 但它太笨重,不太明顯。 我發現了一種使用IIFE(即時調用函數表達式)完成相同事情的優雅方式。

在下面的例子中, currentList變量被傳遞給IIFE,它將其保存在閉包中,直到調用延遲函數。 即使變量currentList在顯示的代碼之後立即改變, setInterval()也會做正確的事情。

如果沒有這種IIFE技術, setTimeout()函數肯定會為DOM中的每個h2元素調用,但所有這些調用只會看到最後一個 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>

這是一個已經“正確”回答的非常古老的問題,但我想我會提到另一種在這裡沒有人提到的方法。 這是從優秀的下劃線庫複製和粘貼:

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

你可以根據你想要的參數傳遞setTimeout調用的函數, 作為額外的獎勵(當然,通常是獎勵),當你調用setTimeout時傳遞給你的函數的參數值被凍結,所以如果它們改變了值在setTimeout()被調用和超時之間的某個時間點上,好吧......這並不是那麼令人沮喪的:)

這裡有一個小提琴,你可以看到我的意思 - http://jsfiddle.net/thedavidmeister/7t2bV/



這適用於所有瀏覽器(IE是一個古怪的球)

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

在做一些研究和測試之後,唯一正確的實現是:

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

setTimeout會將所有額外的參數傳遞給你的函數,以便在那里處理。

匿名函數可以用於非常基本的東西,但在必須使用“this”的對象實例中,無法使其工作。 任何匿名函數都會將“this”更改為指向窗口,因此您將失去對象引用。


請注意,topicId是根據錯誤消息“未定義”的原因是,它在執行setTimeout時作為局部變量存在,而不是在對postinsql的延遲調用發生時存在。 變量生命週期尤其重要,特別是在嘗試像傳遞“this”作為對象引用時。

我聽說您可以將topicId作為第三個參數傳遞給setTimeout函數。 沒有給出太多的細節,但我得到了足夠的信息來實現它,並且它在Safari中取得了成功。 但我不知道他們對“毫秒級錯誤”的含義。 看看這裡:

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


我想你想要:

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

一般來說,如果您需要將函數作為具有特定參數的回調函數傳遞,則可以使用更高階的函數。 這對ES6非常優雅:

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

setTimeout(someFunction(params), 1000);

或者如果someFunction是第一順序的話:

setTimeout(() => someFunction(params), 1000); 

更換

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

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

或者更好,用一個匿名函數替換字符串表達式

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

編輯:

布朗斯通的評論是不正確的,這將按預期工作,如通過在Firebug控制台中運行所示

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

請注意,我同意其他人的看法,您應該避免將字符串傳遞給setTimeout因為這會在字符串上調用eval() ,而是傳遞一個函數。


我如何解決這個階段?

就像那樣:

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:對不起,但英語不是我的母語!


一些答案是正確的,但是令人費解。

我在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);
}

語法解決。 但是在調用settopic時,即4秒鐘後,XHR對象可能不一樣。 因此, 預先綁定變量很重要。


@Jiri Vetyska感謝這篇文章,但你的例子中有錯誤。 我需要將目標(這個)拖到一個超時函數,我嘗試了你的方法。 在IE9中測試 - 不起作用。 我也做了一些研究,看起來正如w3schools.com/jsref/met_win_settimeout.asp指出的那樣,第三個參數是使用的腳本語言。 沒有提到其他參數。

所以,我遵循了@ 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
}

希望,這對其他人有用。


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

您需要提供一個匿名函數作為參數而不是字符串,後一種方法甚至不應該按照ECMAScript規範工作,但瀏覽器只是寬鬆的。 這是一個合適的解決方案,在使用setTimeout()setInterval()時,不要依賴將字符串作為'函數'來傳遞,因為它必須被評估並且不正確。

更新:

正如Hobblin在他對這個問題的評論中所說的,現在你可以使用Function.prototype.bind()將參數傳遞給setTimeout中的Function.prototype.bind()

例:

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

e.preventDefault();

它只是停止元素的默認操作。

實例Ex.:-

防止超鏈接跟隨URL,阻止提交按鈕提交表單。 如果你有許多事件處理程序,並且你只是想阻止多次出現默認事件,那麼我們需要在函數()的頂部使用。

原因:-

使用e.preventDefault();的原因e.preventDefault(); 就是在我們的代碼中,代碼中出現問題,然後它將允許執行鏈接或表單以提交或允許執行或允許您需要執行的任何操作。 &link或submit按鈕將被提交並仍允許進一步傳播活動。

<!DOCTYPE html>
<html lang="en" dir="ltr">
   <head>
      <meta charset="utf-8">
      <title></title>
   </head>
   <body>
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
      <a href="https://www.google.com" onclick="doSomethingElse()">Preventsss page from redirect</a>
      <script type="text/javascript">
         function doSomethingElse(){
           console.log("This is Test...");
         }
         $("a").click(function(e){
          e.preventDefault(); 
         });
      </script>
   </body>
</html>

返回False;

它只是停止執行函數()。

return false; ”將結束整個過程的執行。

原因:-

使用return false的原因; 是你不想在嚴格模式下再執行該功能。

<!DOCTYPE html>
<html lang="en" dir="ltr">
   <head>
      <meta charset="utf-8">
      <title></title>
   </head>
   <body>
      <a href="#" onclick="returnFalse();">Blah</a>
      <script type="text/javascript">
         function returnFalse(){
         console.log("returns false without location redirection....")
             return false;
             location.href = "http://www.google.com/";
         
         }
      </script>
   </body>
</html>





javascript parameters callback settimeout