這五個(gè) JavaScript 問題,99% 的程序員嘴上都懂,代碼里全寫錯(cuò)了
這些問題不是那種“八股文式考點(diǎn)”。 它們更像是:決定你寫出來的代碼,究竟是“能跑”,還是“好用”的那幾道分水嶺。
你以為只是隨手搜一眼“再確認(rèn)一下”, 然后瀏覽器里瞬間打開五個(gè)標(biāo)簽頁,越看越亂。 對,就是這一類:看上去簡單,細(xì)摳下去,全是坑。
我們把這五個(gè)問題攤開說清楚。
1. == 和 ===,到底差在哪?
JavaScript 里有兩種“相等”:
==:寬松相等,比較前會(huì)做類型轉(zhuǎn)換===:嚴(yán)格相等,要求值和類型都一致
0 == '0' // true
0 === '0' // false== 會(huì)偷偷幫你做這些事:
- 字符串和數(shù)字相遇:先嘗試把字符串轉(zhuǎn)成數(shù)字
null和undefined會(huì)被當(dāng)成“差不多一家人”- 布爾值會(huì)被轉(zhuǎn)成
0或1再比較
表面上看起來“真智能”, 實(shí)際上就是給埋 Bug 鋪平了道路。
為什么你必須在乎?
- 你以為在寫判斷邏輯,其實(shí)在賭 JS 的隱式轉(zhuǎn)換規(guī)則
- 很多“偶爾才出現(xiàn)一次”的線上事故,根源就是
== - 團(tuán)隊(duì)協(xié)作時(shí),一個(gè)人用
==,另一個(gè)人用===,邏輯一合并,現(xiàn)場就開始燒腦
實(shí)用結(jié)論:
除非你非常清楚自己在干什么, 否則默認(rèn)只用:
===和!==。
你不是在跟面試官證明“我懂隱式轉(zhuǎn)換”, 而是在給未來的自己少挖幾個(gè)坑。
2. 事件循環(huán)(Event Loop)到底在干嘛?
一句實(shí)話:如果你搞不清 event loop,所有異步代碼都是“玄學(xué)調(diào)試”。
JavaScript 是單線程的, 但它能同時(shí)處理定時(shí)器、網(wǎng)絡(luò)請求、用戶輸入, 靠的就是事件循環(huán)。
來看一個(gè)最常見的例子:
console.log('1');
setTimeout(() => console.log('2'), 0);
console.log('3');
// 輸出:1, 3, 2為什么不是 1, 2, 3?
因?yàn)檫\(yùn)行順序其實(shí)是:
- 先執(zhí)行同步代碼:打印
1→ 安排一個(gè)定時(shí)器任務(wù) → 打印3 - 當(dāng)前這輪執(zhí)行棧清空以后
- 事件循環(huán)去任務(wù)隊(duì)列里取出
setTimeout的回調(diào) → 打印2
為什么你必須在乎?
async/await本質(zhì)就是 Promise + 事件循環(huán)Promise.then()和setTimeout()不在同一個(gè)隊(duì)列里(微任務(wù) vs 宏任務(wù))- 那些“明明寫對了,卻總是順序抽風(fēng)”的異步 Bug,全藏在這里
只要你不理解:
- 調(diào)用棧(Call Stack)
- 微任務(wù)隊(duì)列(Microtask Queue)
- 宏任務(wù)隊(duì)列(Task/Macrotask Queue)
你調(diào)試異步的時(shí)候,就永遠(yuǎn)像在算命。
3. 閉包(Closures)到底是啥?
一句話版本:
閉包 = 函數(shù) + 它誕生時(shí)所在的詞法作用域。
換成人話: 一個(gè)函數(shù)記住了自己出生時(shí)周圍的那些變量, 哪怕這個(gè)函數(shù)被帶離了“出生地”, 它照樣能訪問當(dāng)時(shí)的那些值。
看一個(gè)標(biāo)準(zhǔn)例子:
function counter() {
let count = 0;
return function() {
count++;
return count;
};
}
const inc = counter();
inc(); // 1
inc(); // 2這里發(fā)生了什么?
counter()執(zhí)行完了,按理說里面的count應(yīng)該被回收- 但我們返回了一個(gè)內(nèi)部函數(shù),這個(gè)函數(shù)還在用
count - JS 看到有人在用這塊變量,就把它“悄悄保留”了下來
- 每次
inc()調(diào)用的,都是同一個(gè)count
為什么你必須在乎?
閉包不是一個(gè)“考點(diǎn)名詞”,而是一堆常用寫法的地基:
- 實(shí)現(xiàn)“私有變量”:外面訪問不到,里面一直記得住
- 做緩存 / Memoization:第一次算,后面用記憶
- Currying:一步步傳參的函數(shù)式寫法
- 甚至很多庫里的事件綁定、hook 機(jī)制,全依賴閉包
你如果只會(huì)說“閉包就是函數(shù)套函數(shù)”, 等同于沒懂。你得知道:閉包 = 生命周期延長 = 狀態(tài)被托管在一個(gè)函數(shù)里。
4. React 里的 Hooks,到底解決了什么問題?
在 Hooks 出現(xiàn)之前, React 想要有狀態(tài)、有生命周期,必須寫 class 組件:
this到處綁來綁去- 生命周期方法分散 (
componentDidMount/componentDidUpdate/componentWillUnmount) - 邏輯拆成幾塊,維護(hù)起來很痛苦
Hooks 出來后,React 徹底轉(zhuǎn)向函數(shù)式組件世界。
一個(gè)最小例子:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
{count}
</button>
);
}我們在這個(gè)例子里做了什么?
- 用
useState在函數(shù)組件里加上了“記憶功能” - 每次點(diǎn)擊,組件重新渲染,但
count的值通過 Hook 被保留下來
為什么你必須在乎?
useEffect:做副作用(網(wǎng)絡(luò)請求、訂閱、手動(dòng)操作 DOM)useRef:拿“不會(huì)觸發(fā)重渲染”的可變引用useCallback:避免把函數(shù) prop 每次都當(dāng)新對象傳下去
Hooks 的本質(zhì),是:
把“狀態(tài)”和“生命周期”這兩件事, 從 class 里拆出來, 變成一套可以復(fù)用、組合、解耦的函數(shù)機(jī)制。
你如果還停留在“會(huì)用 useState 就算會(huì) React Hooks”, 那寫出來的組件,很快就會(huì)變成魔法森林。
5. Angular 里的依賴注入(DI),是在注入什么?
如果你覺得 Angular 難,大概率就卡在這里。
DI(Dependency Injection,依賴注入) 是 Angular 的靈魂之一: 它負(fù)責(zé):
- 把服務(wù)(Service)實(shí)例“發(fā)”給你
- 決定這些實(shí)例在整個(gè)應(yīng)用里是“一份”還是“多份”
- 管理它們的生命周期和作用域
最典型的例子:
@Injectable()
export class UserService {
constructor(private http: HttpClient) {}
}這里發(fā)生了什么?
@Injectable()聲明:這是一個(gè)可以被注入的服務(wù)- 構(gòu)造函數(shù)里聲明
HttpClient,Angular 會(huì)自動(dòng)把實(shí)例“塞”進(jìn)來 - 你不用手動(dòng)
new HttpClient(),也不用到處傳來傳去
為什么你必須在乎?
如果你不理解:
providers放在哪一層(模塊 / 組件)- 服務(wù)是單例,還是每個(gè)組件各一份
- token、scope、樹狀注入的關(guān)系
你在調(diào) Angular 的 Bug 時(shí),就等于在迷宮里跑。
DI 做的,是一件很現(xiàn)實(shí)的事:
把“誰依賴誰”“誰該活多久”這些問題, 從你手寫代碼里抽出來, 變成框架幫你管理的一張圖。
總結(jié)一下:5 個(gè)問題背后的“硬技能”
1. == vs ===
- 明面上:寬松相等 vs 嚴(yán)格相等
- 本質(zhì)上:你要不要賭 JS 的轉(zhuǎn)換規(guī)則?
- 好習(xí)慣:默認(rèn)用
===,需要==時(shí),先詳細(xì)寫出所有可能的 case 再說。
2. 事件循環(huán)(Event Loop)
- 明面上:單線程 + 異步
- 本質(zhì)上:誰先跑、誰后跑,誰永遠(yuǎn)排在隊(duì)伍前面
- 想寫好
async/await,不理解它,等于瞎寫。
3. 閉包(Closures)
- 明面上:函數(shù)里套函數(shù)
- 本質(zhì)上:狀態(tài)被“托管”在函數(shù)和作用域里
- 你在寫的是“行為+記憶”的組合,而不是普通函數(shù)調(diào)用。
4. React Hooks
- 明面上:
useState、useEffect、useRef那堆 API - 本質(zhì)上:把狀態(tài)邏輯拆成可復(fù)用的函數(shù)片段
- 寫得好:組件邏輯清晰、可組合
- 寫得爛:一堆 useEffect 疊羅漢,誰也不敢改。
5. Angular DI
- 明面上:
@Injectable()、constructor(private svc: XxxService) - 本質(zhì)上:一個(gè)集中管理依賴和生命周期的系統(tǒng)
- 你理解它,應(yīng)用結(jié)構(gòu)就清晰; 你忽略它,調(diào)試就會(huì)非常痛苦。
最后的小聲提醒
這 5 個(gè)問題, 之所以反復(fù)出現(xiàn)在面試、文章、爭論里, 并不是因?yàn)榇蠹叶肌安恢馈薄?/span>
而是因?yàn)椤?/span>我們都以為自己“差不多懂了”, 但一寫到具體項(xiàng)目里,就暴露得完全不夠深。
你要拉開和大多數(shù)開發(fā)者的差距, 不靠多會(huì)幾個(gè)新框架, 而是靠:
- 把這些“老問題”真的吃透
- 寫代碼時(shí),知道自己每一行是在利用語言特性,還是在賭運(yùn)氣
當(dāng)你能把這五個(gè)問題講清楚、用到位, 你已經(jīng)悄悄超過了絕大多數(shù)只停留在“會(huì)用 API”層面的開發(fā)者。























