首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

JavaScript 基礎進階(第十四天):定時器、類型轉換、函數深度、作用域與閉包完全指南

     編輯:Java 綜合教學

JavaScript 基礎進階(第十四天):定時器、類型轉換、函數深度、作用域與閉包完全指南

一、定時器

1.1 setTimeout - 一次性延遲執行

// 語法:setTimeout(code, delay, arg1, arg2, ...)
// code: 函數引用或字符串代碼(不推薦字符串)
// delay: 毫秒數
// 返回: timerId (用於 clearTimeout)

const timerId = setTimeout(() => {
    console.log('3 秒後執行');
}, 3000);

// 清除定時器
clearTimeout(timerId);
  • 最小延遲受瀏覽器限制(通常 4ms),非精確實時。
  • 回調函數在主線程空閒時執行,可能超過指定延遲。

1.2 setInterval - 週期性重複執行

// 語法同 setTimeout,返回 timerId
const intervalId = setInterval(() => {
    console.log('每秒執行:', new Date().toLocaleTimeString());
}, 1000);

// 停止
clearInterval(intervalId);
  • 若回調執行時間超過間隔,會導致併發執行/丟幀,建議複雜邏輯用 setTimeout 遞歸替代。

1.3 兩者區別與選型

特性setTimeoutsetInterval
執行次數一次無限循環
間隔精確度單次延遲固定間隔嘗試
停止方式clearTimeout(id)clearInterval(id)
適用場景防抖、節流、延遲初始化、動畫幀輪詢、心跳、定時刷新

遞歸 setTimeout 模擬 setInterval(可動態調整間隔、避免堆積):

function repeat(fn, delay) {
    fn();
    setTimeout(() => repeat(fn, delay), delay);
}
// 使用
repeat(() => console.log('tick'), 1000);

二、類型轉換

2.1 顯式轉換(強制)

// 字符串 -> 數字
Number('123');        // 123
parseInt('123px');    // 123 (忽略非數字後綴)
parseFloat('3.14');   // 3.14
+'123';               // 123 (一元加運算符)

// 數字 -> 字符串
String(123);          // "123"
(123).toString();     // "123"
123 + '';             // "123" (隱式轉換)

2.2 隱式轉換(自動)

'5' - 2;      // 3  (字符串隱式轉數字)
'5' + 2;      // "52" (+ 觸發字符串拼接)
true + 1;     // 2  (boolean -> number)
null + 1;     // 1  (null -> 0)
undefined + 1;// NaN
  • 規則:+ 有字符串則拼接,其餘算術運算符嘗試轉數字。
  • == 比較:雙邊隱式轉換,極易出錯,**嚴禁使用 ==,永遠用 ===**。

三、函數深度解析

3.1 定義方式

// 函數聲明(提升)
function add(a, b) { return a + b; }

// 函數表達式(不提升)
const multiply = function(a, b) { return a * b; };

// 箭頭函數(ES6,無 this/arguments)
const divide = (a, b) => a / b;

// 立即調用函數表達式 (IIFE)
(function() { console.log('IIFE'); })();

3.2 參數機制

  • 傳值調用:基本類型傳值,對象傳引用地址(可修改對象屬性,不可重新賦值參數變量)。
  • arguments 對象:類數組,包含所有實參,arguments.length 獲取實際傳參數量。
  • 剩餘參數:function foo(a, b, ...rest) {},rest 為真數組,ES6 標準。
  • 默認參數:function foo(x = 10) {}。

3.3 返回值與 return 陷阱

// 正確:返回對象字面量
function createUser() {
    return { name: 'Tom' }; // 大括號同行
}

// 錯誤:自動分號插入 (ASI) 導致返回 undefined
function createUserBroken() {
    return
    { name: 'Tom' }; // 等同於 return; { name: 'Tom' };
}

// 最佳實踐:返回對象時大括號不換行,或使用括號包裹
function createUserSafe() {
    return (
        { name: 'Tom' }
    );
}

四、變量作用域與提升

4.1 var - 函數作用域 + 提升

function scopeDemo() {
    console.log(x); // undefined (非報錯,因提升聲明)
    var x = 10;
    if (true) {
        var y = 20; // 無塊級作用域
    }
    console.log(y); // 20 可訪問
}
  • 聲明提升至函數頂部,賦值留在原地。
  • 無塊級作用域,if/for 內 var 洩露到函數外。

4.2 let / const - 塊級作用域 + 暫時性死區 (TDZ)

{
    let a = 1;
    const PI = 3.14;
    // PI = 3; // TypeError: Assignment to constant variable
}
console.log(a); // ReferenceError: a is not defined

// TDZ 示例
function tdz() {
    console.log(x); // ReferenceError (進入 TDZ)
    let x = 1;
}
  • let 可重新賦值,const 綁定不可變(對象/數組內容仍可修改)。
  • 塊級作用域:{} 內聲明外部不可訪問。
  • 不提升(實際提升但進入 TDZ),聲明前訪問拋錯。

4.3 全局對象 window

var globalVar = 'I am global';
console.log(window.globalVar); // 'I am global'
console.log(this.globalVar);   // 'I am global' (全局作用域 this === window)

// 函數聲明也屬性掛載
function foo() {}
console.log(window.foo === foo); // true

五、閉包

5.1 定義

閉包 = 函數 + 該函數定義時的詞法環境。內部函數引用外部函數變量,外部函數返回內部函數,變量得以「保活」。

5.2 經典案例:惰性求和

function lazySum(arr) {
    return function() {
        return arr.reduce((x, y) => x + y, 0);
    };
}
const sumFn = lazySum([1,2,3,4,5]);
console.log(sumFn()); // 15 (真正計算發生在調用時)

5.3 閉包陷阱:循環變量捕獲

// 錯誤:所有函數共享同一個 i,最終 i=3
var funcs = [];
for (var i = 1; i <= 3; i++) {
    funcs.push(function() { return i * i; });
}
console.log(funcs[0]()); // 16 (非 1)

// 修正 1:IIFE 捕獲當前值
var funcs = [];
for (var i = 1; i <= 3; i++) {
    funcs.push((function(n) {
        return function() { return n * n; };
    })(i));
}
console.log(funcs[0]()); // 1

// 修正 2:let 塊級作用域 (ES6 推薦)
let funcs = [];
for (let i = 1; i <= 3; i++) {
    funcs.push(function() { return i * i; });
}
console.log(funcs[0]()); // 1

5.4 閉包應用:封裝私有狀態

function createCounter(initial = 0) {
    let count = initial;
    return {
        inc: () => ++count,
        dec: () => --count,
        get: () => count
    };
}
const c1 = createCounter();
c1.inc(); // 1
c1.inc(); // 2
const c2 = createCounter(10);
c2.inc(); // 11
// 外部無法直接訪問 count,實現了私有化

5.5 閉包應用:柯里化 / 參數預綁定

function makePow(exp) {
    return function(base) {
        return Math.pow(base, exp);
    };
}
const square = makePow(2);
const cube = makePow(3);
console.log(square(5)); // 25
console.log(cube(3));   // 27

六、Math 對象常用方法

Math.random();        // [0,1) 隨機數
Math.floor(Math.random() * 10); // 0-9 隨機整數
Math.pow(2, 10);      // 1024
Math.sqrt(16);        // 4
Math.PI;              // 3.14159...
Math.max(1,5,3);      // 5

七、eval - 動態執行字符串代碼(慎用)

eval('1 + 2'); // 3
// 危險:XSS 攻擊向量、性能差、破壞作用域優化
// 替代方案:JSON.parse、Function 構造器、預定義命令映射表

總結

  • 定時器:setTimeout 一次性、setInterval 循環、遞歸 setTimeout 更可控。
  • 類型轉換:顯式優於隱式,=== 杜絕隱式比較。
  • 函數:一等公民,支持高階函數、閉包、柯里化。
  • 作用域:var 函數級提升,let/const 塊級 + TDZ,優先用 const。
  • 閉包:攜帶詞法環境的函數,實現私有狀態、柯里化、模塊模式,警惕循環捕獲陷阱。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved