- 函數(shù)調用和函數(shù)的區(qū)別
// 函數(shù)和函數(shù)調用的區(qū)別
function fn () { //函數(shù)
return 1;
}
fn(); //函數(shù)調用
console.log(fn); // ? fn() {return 1;}
console.log(fn()); //1
// 函數(shù)返回函數(shù)的時候調用當前函數(shù)即為return的那個函數(shù)
function fn2 () {
return function () {
return 2;
}
}
fn2//是一個函數(shù)
fn2()//也是一個函數(shù) function () { return 2; }
fn2()(); //2
定時器
- 先看一個例子
function fn (){
console.log(1);
}
setTimeout(fn);
console.log(2);
//上述代碼輸出 2 1撼港,先輸出2后輸出1
setTimeout設定了一個輸出1的事件在0秒后執(zhí)行俭茧,那么瀏覽器會先將目前的代碼都執(zhí)行完成后再回頭執(zhí)行setTimeout中的代碼
function fn (){
console.log(1);
}
var timeId = setTimeout(fn);
console.log('id:' + timeId);//1
console.log(2);
timeId = setTimeout(fn);
console.log('id:' + timeId);//2
//每次要執(zhí)行setTimeout的函數(shù)的時候,就要銷毀這個setTimeout催什,然后下次在定義個相同的setTimeout的時候潭袱,id是不一樣的栋烤。
- 間隔永遠要比要比設置的久
function fn (){
console.log(1);
console.log(new Date()-0);
}
setTimeout(fn);
console.log(new Date()-0);
// 設置了時間延時為0虎敦,但是輸出的date還是有幾十毫秒的差別,這是因為瀏覽器就算是延時為0沥潭,也要先將當前頁面執(zhí)行完邀泉,等‘閑下來’的時候再來執(zhí)行setTimeout
// 所以這樣造成的結果是,間隔永遠要比要比設置的久
定時器的‘怠惰’問題
當瀏覽器失去焦點的時候钝鸽,即用戶不在關注瀏覽器的時候汇恤,定時器的輸出會變慢,比如設置0.5秒執(zhí)行一次拔恰,有可能最小化瀏覽器之后1秒才執(zhí)行一次這么做是為了節(jié)省內存因谎。關于setInterval
setInteval只有一個timerId,和setTimeout不同setTimeout的函數(shù)的傳參
setTimeout(fn,500,1,2,3);
function fn () {
console.log(arguments); //[1,2,3]
}
- 怎么取消定時器 window.clearTimeout(timerId)
var timerId = setTimeout('console.log("設置了clearTimeout,這條會被阻止執(zhí)行")',3000);
window.clearTimeout(timerId); //阻止前面的setTimeout執(zhí)行
var timerId2 = setTimeout('console.log("沒設置clearTimeout,這條會被執(zhí)行")',3000);
異步 和 回調
看下面兩個例子,異步就是不等結果颜懊,去執(zhí)行別的代碼财岔,讓一個回調函數(shù)來通知我結果
function queue () {
setTimeout(function () {
return "您的號碼是233";
},60000); //60s之后才能拿到結果
};
var result = queue();
console.log(result); //undefined 因為queue這個函數(shù)沒有寫return,所以默認返回undefined 拿不到想要的匿名函數(shù)的返回值
這時候想要拿到匿名函數(shù)的返回值,可以創(chuàng)建一個對象或者函數(shù),傳入queue中饭冬,queue中的匿名函數(shù)不用設置return使鹅,因為設置了也拿不到 然后不設置return,直接讓這個創(chuàng)建的代理函數(shù)獲取到值昌抠,就可以輸出.
function queue (agent) { //傳入agent給形參
setTimeout(function () {
agent("您的號碼是233"); // 匿名函數(shù)里不寫return,寫了也拿不到鲁僚,直接用agent函數(shù)獲取這個值
},3000); //60s之后才能拿到結果
};
// 這時候想要拿到匿名函數(shù)的返回值,可以創(chuàng)建一個對象或者函數(shù),傳入queue中炊苫,queue中的匿名函數(shù)不用設置return,因為設置了也拿不到
// 然后不設置return冰沙,直接讓這個創(chuàng)建的代理函數(shù)獲取到值侨艾,就可以輸出
function agent (result) { //創(chuàng)建一個函數(shù)
console.log(result);
}
queue(agent); //將創(chuàng)建的函數(shù)傳入queue
整體思路是,程序還要接著執(zhí)行下面的拓挥,創(chuàng)建一個代理函數(shù)去得到值唠梨,等setTimeout的時間間隔到了之后拿到值之后返回給程序。
在上述例子中侥啤,agent函數(shù)就是一個回調函數(shù)当叭。由全局傳遞給queue茬故,然后queue調用之后又傳回給全局。叫做回調函數(shù)蚁鳖。 “我傳給你磺芭,你調用再傳回給我”
異步和回調往往是同時出現(xiàn)的。因為異步了之后不回調醉箕,就相當于不要這個返回結果了钾腺。
相關的一些問題分析
for (var i = 0; i < 5; i ++) {
console.log(i); //0 1 2 3 4
}
for (var i = 0; i < 5; i ++) {
setTimeout(function () { //for循環(huán)線結束,才運行setTimeout 這時候i的值都是5
console.log(i);
},1000*i);
}
第一次馬上輸出5 第二次是1000毫秒之后輸出5 第三次是2000毫秒之后輸出5 以此類推到第五次,j即每一秒打印一個
setTimeout內部匿名函數(shù)要輸出i時讥裤,找不到i放棒,要到上一級也就是全局中找i,這時候i已經(jīng)為5了己英,而外部的時間間隔i跟全局的i是同一個東西哨查,每次的值為0,1,2,3,4
for (var i = 0; i < 5; i ++) {
(function () {
setTimeout(function () {
console.log(i);
},i * 1000);
})(i);
}
和第二題的結果一樣,1000乘i的那個i是是全局中的i剧辐,每次為0,1,2,3,4
而setTimeout里面的函數(shù)的作用域中要輸出的i不存在寒亥,要到上一級中作用域即立即執(zhí)行函數(shù)中找i,立即執(zhí)行函數(shù)中也沒有i荧关,再到全局作用域中找i溉奕,這時候for循環(huán)已經(jīng)結束了,i為5忍啤,所以每次輸出5,
for (var i = 0; i < 5; i ++) {
(function (i) {
setTimeout(function () {
console.log(i);
},i * 1000);
})(i);
}
和第3題不同加勤,這里立即執(zhí)行函數(shù)寫了一個形參,這個形參相當于一個變量聲明var i = arguments[0];
那么setTimeout里面的函數(shù)在輸出i時同波,找不到i鳄梅,到上一級作用域即立即執(zhí)行函數(shù)中去找,和第3題不同的是這次立即執(zhí)行函數(shù)里面有i了未檩,不會再到全局作用域里找i了戴尸。
寫了形參就相當于定義了i,所以這時候輸出的i是立即執(zhí)行函數(shù)中的i冤狡,這個i傳入的值就是當時的全局中i的值0,1,2,3,4
for (var i = 0; i < 5; i ++) {
setTimeout((function (i) {
console.log(i);
})(i),i * 1000)
}
第5題仍然通過作用域鏈進行理解孙蒙,setTimeout里面的函數(shù)的作用域中要輸出的i,就是立即執(zhí)行函數(shù)中的i悲雳,因為定義了形參挎峦,這個形參每次接收的i是當時全局中的i的值為0,1,2,3,4. 而時間間隔的i就是全局中的i,每次的值為0,1,2,3,4合瓢。
求得結果之后坦胶,針對第5題,我們可以簡化一下代碼,仔細分析一下
for (var i = 0; i < 5; i ++) {
var t1 = function (i) {
console.log(i);
};
var t2 = t1(i);
setTimeout(t2,1000*i);
}
那么把代碼拆開之后我們再來分析顿苇,此時setTimeout里面執(zhí)行的是t2峭咒,t2是t1調用的結果,t1里面沒有寫return岖圈,那么默認返回undefined讹语,所以t2為undefined,此時其實就相當于:
setTimeout(undefined,0);
setTimeout(undefined,1000);
setTimeout(undefined,2000);
setTimeout(undefined,3000);
setTimeout(undefined,4000);
而輸出的值是t1中的console.log(i);我們也可以很清晰的看到蜂科,這個i就是傳入的當時的每次的i值0,1,2,3,4
setTimeout的應用:做個倒計時器
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<select placeholder="選擇一個時間" name="" id="mySelect">
<option value="1" selected>1分鐘</option>
<option value="5">5分鐘</option>
<option value="10">10分鐘</option>
<option value="20">20分鐘</option>
</select>
<button id="start">開始計時</button>
<button id="pause" disabled>暫停計時</button>
<button id="resume" disabled>恢復計時</button>
<div id="show"></div>
<script>
var show = document.querySelector('#show');
var start = document.querySelector('#start');
var pause = document.querySelector('#pause');
var resume = document.querySelector('#resume');
var select = document.querySelector('#mySelect');
// var timeLeft;
// function showtime () {
// show.innerText = timeLeft + '秒';
// timeLeft -= 1;
// if (timeLeft === 0) {
// return;
// }
// setTimeout(showtime,1000);
// }
// start.addEventListener('click',function () {
// var seconds = 60 * parseInt(select.value);
// timeLeft = seconds;
// showtime();
// });
// 上面的代碼能實現(xiàn)效果但是存在bug顽决,連續(xù)點擊start倒計時會越來越快,有多個setTimeout在控制計時
// 所以要解決這個問題导匣,在再一次點擊start的時候才菠,要將上一次的setTimeout清除掉,怎么得到上一次的setTimeout呢,可以通過設置timerId
var timeLeft;
var lastTimerId; // 全局中定義個lastTinmerId
function showtime () {
show.innerText = timeLeft + '秒';
timeLeft -= 1;
if (timeLeft === 0) {
return;
}
lastTimerId = setTimeout(showtime,1000); //執(zhí)行seTimeout時順便給timerId賦值
}
start.addEventListener('click',function () {
var seconds = 60 * parseInt(select.value);
timeLeft = seconds;
// 執(zhí)行函數(shù)前先清除上一次的setTimeout
if (lastTimerId !== undefined) {
window.clearTimeout(lastTimerId);
}
showtime();
pause.disabled = false; //點了start才能點擊pause
});
// 接下來設置暫停和恢復
// 暫停的思路很簡單,恢復的思路就是不要重新賦值timeLeft,保持暫停的時候的timeLeft就可以
pause.addEventListener('click',function () {
if (lastTimerId) {
window.clearTimeout(lastTimerId);
}
resume.disabled = false; //點了pause才能點resume
pause.disabled = true; //點了pause之后不能再點擊pause
})
resume.addEventListener('click',function () {
showtime(); //就一行代碼贡定, showTime()就是把當前的timeLeft打出來赋访,并沒有給timeLeft重新賦值
pause.disabled = false; //點了resume之后就可以點pause
resume.disabled = true; //點了resume之后,不可以繼續(xù)點擊resume
})
</script>
</body>
</html>