關于setInterval與setTimeout作用域問題

最近在模仿swiper鼓搗一個輪播圖插件時诈泼,碰到了setInterval的作用域問題膝迎,輪播的方法寫在一個對象里攘须,但是setInterval執(zhí)行這個方法后摧扇,在方法體內無法訪問這個對象的屬性了圣贸,業(yè)務邏輯代碼如下:

function Swiper(num,loopTime) {
    this.num = num;             // 輪播起始位置
    this.loopTime = loopTime;   // 輪播間歇時間
    this.timer = null;          // 保存setInterval定時器對象Id
    this.autoPlay();
}
Swiper.prototype.autoPlay = function() {
    clearInterval(this.timer);
    // 將循環(huán)方法Loop傳進setInterval執(zhí)行
    this.timer = setInterval(this.loop,this.loopTime);
}
Swiper.prototype.loop = function() {
    // 循環(huán)主邏輯
    console.log('num',this.num);
    this.num++;
}

var swiper = new Swiper(0,1000);

以上代碼并沒有按照預期情況(循環(huán)遞增num)來執(zhí)行,執(zhí)行結果為:


圖片.png

分析結果后扛稽,發(fā)現loop方法體內的this并不是指向new出來的swiper對象吁峻,而是window對象,window中沒有num在张,即為undefined用含,自增后,為NaN帮匾。

一耕餐、分析

setInterval(this.loop,this.loopTime)

setInterval是window對象的方法,在setInterval中傳入this.loop方法辟狈,其實是將其作為匿名函數傳入肠缔,this.loop的方法體其實是在window對象上執(zhí)行的,等效為:

setInterval(function() {
    // 循環(huán)主邏輯
    console.log('num',this.num);
    this.num++;
}, this.loopTime);

這樣就一目了然哼转,匿名函數里的this自然是指向window明未,不在指向swiper對象了。
要改變函數作用域壹蔓,一般會用apply/call趟妥,我嘗試用這兩個方法去改變this.loop方法體內的作用域:

setInterval(this.loop.apply(this),this.loopTime);
setInterval(this.loop.call(this),this.loopTime);

但結果都為:


圖片.png

而且只執(zhí)行了一次,為什么只執(zhí)行了一次呢佣蓉?我將Loop方法提出來披摄,放在全局讓window調用:

var num = 0;
function loop() {
    console.log(num);
    num++;
}
setInterval(loop.apply(window),1000);
setInterval(loop.call(window),1000);

結果都只執(zhí)行了一次亲雪,原來apply和call方法在window的setIntaval只會執(zhí)行一次,原因是apply和call執(zhí)行一次后沒return任何值疚膊,那樣這兩個方法就沒法滿足業(yè)務邏輯了义辕。

二、解決方法

  1. 閉包
    這是最常用最簡單的方法:
Swiper.prototype.autoPlay = function() {
    clearInterval(this.timer);
    var self = this;    // 將this對象保存在self中
    this.timer = setInterval(function(){
        self.loop();    // 在匿名函數中使用保存后的self對象寓盗,其指向swiper對象
    },this.loopTime);
}

運行結果符合預期:num 間歇自增


圖片.png
  1. ECMAscript 5 中 Function.prototype.bind 方法
    這個方法會將函數的this值綁定到傳遞給bind方法的參數上灌砖,并返回一個新的函數實例:
window.num = 1;

var obj = { num: 2};
function alertNum() {
    alert(this.num);
}

alertNum();     // 1;

var newAlertNum = alertNum.bind(obj);

newAlertNum();  // 2

通過bind方法來修改setInterval調用:

Swiper.prototype.autoPlay = function() {
    clearInterval(this.timer);
    // 將loop方法bind到swiper對象上
    this.timer = setInterval(this.loop.bind(this),this.loopTime);
}

同樣符合預期:


圖片.png

三、setTimeout

setTimeout也可以用來做循環(huán)調用傀蚌,而且不必擔心像setInterval那樣存在計時器堆疊問題基显,比setInterval有優(yōu)勢(不必像setInterval那樣,調用之前都要用clearInterval清除一下緩存的計時器)善炫,但它同樣屬于window對象撩幽,存在調用時的作用域問題:

var obj = {
    num: 0,
    loop: function() {
        console.log(this.num);
        this.num++;
        setTimeout(this.loop,1000);  // this.num 為undefined
    }
};
obj.loop();

延遲1s后調用發(fā)現this.num就為undefined,解決方法與setInterval一樣:使用bind或者閉包

loop: function() {
        this.num++;
        setTimeout(this.loop.bind(this),1000);
    }


loop: function() {
        var self = this;
        this.num++;
        setTimeout(function() {
            self.loop();
        },1000);
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末箩艺,一起剝皮案震驚了整個濱河市窜醉,隨后出現的幾起案子,更是在濱河造成了極大的恐慌舅桩,老刑警劉巖酱虎,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雨膨,死亡現場離奇詭異擂涛,居然都是意外死亡,警方通過查閱死者的電腦和手機聊记,發(fā)現死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門撒妈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人排监,你說我怎么就攤上這事狰右。” “怎么了舆床?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵棋蚌,是天一觀的道長。 經常有香客問我挨队,道長谷暮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任盛垦,我火速辦了婚禮湿弦,結果婚禮上,老公的妹妹穿的比我還像新娘腾夯。我一直安慰自己颊埃,他們只是感情好蔬充,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著班利,像睡著了一般饥漫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肥败,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天趾浅,我揣著相機與錄音,去河邊找鬼馒稍。 笑死皿哨,一個胖子當著我的面吹牛,可吹牛的內容都是我干的纽谒。 我是一名探鬼主播证膨,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鼓黔!你這毒婦竟也來了央勒?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤澳化,失蹤者是張志新(化名)和其女友劉穎崔步,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體缎谷,經...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡井濒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了列林。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瑞你。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖希痴,靈堂內的尸體忽然破棺而出者甲,到底是詐尸還是另有隱情,我是刑警寧澤砌创,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布虏缸,位于F島的核電站,受9級特大地震影響嫩实,放射性物質發(fā)生泄漏刽辙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一舶赔、第九天 我趴在偏房一處隱蔽的房頂上張望扫倡。 院中可真熱鬧,春花似錦、人聲如沸撵溃。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缘挑。三九已至集歇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間语淘,已是汗流浹背诲宇。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留惶翻,地道東北人姑蓝。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像吕粗,于是被迫代替她去往敵國和親纺荧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法颅筋,類相關的語法宙暇,內部類的語法,繼承相關的語法议泵,異常的語法占贫,線程的語...
    子非魚_t_閱讀 31,631評論 18 399
  • 函數是一塊JavaScript代碼型奥,被定義一次,但可執(zhí)行調用多次池充,js中的函數也是對象桩引,所以js函數可以像其他對象...
    深沉的簡單閱讀 435評論 0 4
  • 第1章 認識JS JavaScript能做什么缎讼?1.增強頁面動態(tài)效果(如:下拉菜單收夸、圖片輪播、信息滾動等)2.實現...
    mo默22閱讀 1,288評論 0 5
  • 1. tab列表折疊效果 html: 能源系統事業(yè)部 崗位名稱: 工作地點 崗位名...
    lilyping閱讀 1,862評論 0 1
  • 窗戶上泛著冷冽的白血崭,我想透過它凝望外面它卻將我呼出的氣息卧惜,冷卻成冰森森然,凌冽白冷我對它說夹纫,多管閑事它卻未有愧疚繼...
    水攸寧閱讀 209評論 1 5