DrillLab
练习Practice

动手做Get your hands on it

练习跟着课文走 —— 每节课尾都有本课的练习。这一页是全部练习的总库,想集中刷题的时候来。 每个练习都写清了它来自哪一节,卡住了就回去看那一节。Practice follows the lessons — every lesson ends with the exercises for that lesson. This page is the whole library, for when you want to drill in one sitting. Each exercise names the lesson it came from, so you can go back when you stall.

0 / 148个做对过you got right
筛一下Filter these面试八股Interview questions写整块Write a block
难度LevelL1 → L4 和上面四档同一个意思:给你的东西越来越少L1 → L4: the same idea as the four tiers above — less is handed to you全部难度All levelsL1L1L2L2L3L3L4L4

已筛到你正在学的《面试八股》。想看全部就点上面的「全部」。Filtered to Interview questions — the course you are on. Use “All” above to see everything.

练习Exercises

筛出 11 个练习(共 148 个)。Showing 11 of 148.
来自From 缺口一 · Dropdown、Tabs、星级评分Gap 1 · dropdown, tabs and star rating · 面试八股Interview questions
L3写整块Write a block自己写出星级评分Write the star rating yourselfDrillLab 自出Written by DrillLab

hover 预览 + 点击选中 + 再点清零。 检查器会查 ??onMouseLeave 的位置和无障碍。

Hover to preview, click to pick, click the same star again to reset. The checker looks at ??, where onMouseLeave sits, and accessibility.

要求Requirements
  • hover 到第 n 颗时前 n 颗显示为选中样式(预览)Hovering star n shows the first n stars in the filled style (a preview)
  • 鼠标移出整个组件后回到已选值Moving the mouse out of the whole component goes back to the picked value
  • 点第 n 颗设为 n 分;再点同一颗清零Clicking star n sets the score to n; clicking the same star again resets to zero
  • 每颗星是 button,带 aria-label,键盘可用Every star is a button with an aria-label, and works from the keyboard
  • 显示值必须是派生的,不许再开第三个 stateThe shown value has to be derived; a third piece of state is not allowed
TSXsrc/components/StarRating/index.tsx
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.

来自From 缺口二 · useRef 操作 DOM,与写一个自定义 hookGap 2 · using useRef on the DOM, and writing a custom hook · 面试八股Interview questions
L3写整块Write a block自己写出 useLocalStorageWrite useLocalStorage yourselfDrillLab 自出Written by DrillLab

四个考点全都会被检查:惰性初始化、try/catch 兜底、 依赖带 key、as const

All four points get checked: lazy initialisation, a try/catch fallback, key in the dependency list, and as const.

要求Requirements
  • 读 localStorage 只在首次渲染发生一次(惰性初始化)Reading localStorage happens once, on the first render (lazy initialisation)
  • 读和写都要有 try/catchBoth the read and the write need a try/catch
  • JSON 序列化 / 反序列化JSON serialising and deserialising
  • effect 的依赖里要有 key 和 valueThe effect's dependency list holds key and value
  • 返回元组,用 as constReturn a tuple, using as const
TypeScriptsrc/hooks/useLocalStorage.ts
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.

来自From 缺口四 · Kanban 看板:一次改两个数组Gap 4 · a Kanban board: changing two arrays in one update · 面试八股Interview questions
L3写整块Write a block写出 moveCardWrite moveCardDrillLab 自出Written by DrillLab

一次操作同时改两个数组,而且不许碰原 board。 检查器会查两个边界和「未动的列复用引用」。

One action changes two arrays, and the board you were handed must not be touched. The checker looks at the two edge cases and at whether untouched columns keep their original reference.

要求Requirements
  • from === to 时返回同一个引用,不造新对象When from === to, return the same reference and build no new object
  • 找不到卡时返回同一个引用When the card is not found, return the same reference
  • 源列用 filter 去掉,目标列用展开追加Drop it from the source column with filter, and append to the target column with spread
  • 只改这两列,其余列复用原数组Only those two columns change; the rest keep their original arrays
  • 不许 push / splice / 直接赋值No push, no splice, no direct assignment
TypeScriptsrc/components/Kanban/index.tsx
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.

来自From 计时两兄弟:debounce 与 throttleTwo timing helpers: debounce and throttle · 面试八股Interview questions
L3写整块Write a block手写 debounce(带 cancel)Write debounce by hand (with cancel)
把「每次都立刻调用」的半成品改成真正的 debounce: 连续调用只在停手 delay 毫秒后执行最后一次,cancel() 能取消挂着的那次。Turn this half-finished version, which calls through immediately every time, into a real debounce: a burst of calls runs only once, delay ms after the last one, and cancel() drops the pending run.
要求Requirements
  • 调用 debounced() 不许立刻执行 fnCalling debounced() must not run fn right away
  • 一串连续调用只在停手 delay 毫秒后执行一次,参数用最后那次的A burst of calls runs once, delay ms after the last call, with the arguments of that last call
  • 两串隔开的调用各自触发一次Two bursts separated by a pause each fire once
  • cancel() 取消还没发生的那次调用cancel() drops the call that has not happened yet
TypeScriptdebounce.ts
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.

来自From 计时两兄弟:debounce 与 throttleTwo timing helpers: debounce and throttle · 面试八股Interview questions
L3写整块Write a block手写 throttle(leading + trailing)Write throttle by hand (leading + trailing)
把「直接透传」的半成品改成真正的 throttle:窗口开头立刻执行, 窗口内压住,窗口结束用最后一次的参数补一枪。Turn this pass-everything-through version into a real throttle: run once at the start of the window, hold back the calls inside it, then run once more at the end of the window with the arguments of the last call.
要求Requirements
  • 第一次调用立刻执行(leading)The first call runs immediately (leading)
  • 窗口内的后续调用不执行Later calls inside the same window do not run
  • 窗口结束时用窗口内最后一次的参数补执行(trailing)When the window closes, run once more with the arguments of the last call in it (trailing)
  • 窗口过了之后再调用,又立刻执行A call after the window has passed runs immediately again
TypeScriptthrottle.ts
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.

来自From 数据与函数:deepClone、flatten、curryData and functions: deepClone, flatten, curry · 面试八股Interview questions
L3写整块Write a block手写 deepClone(防循环)Write deepClone by hand (cycle-safe)
把「直接返回原值」的半成品写成完整的 deepClone:分支覆盖 Date / Map / Set / 数组 / 普通对象,循环引用不爆栈。不许用 JSON.parse(JSON.stringify(x))。Grow this return-the-input version into a full deepClone: branches for Date, Map, Set, arrays and plain objects, and a circular reference must not recurse forever. JSON.parse(JSON.stringify(x)) is not allowed.
要求Requirements
  • 原始值和 null 原样返回Primitives and null come back unchanged
  • 嵌套对象 / 数组逐层克隆,每一层都是新引用Nested objects and arrays are cloned level by level, and every level is a new reference
  • Date 克隆成新 Date;Map / Set 深克隆A Date becomes a new Date; Map and Set are deep-cloned
  • 循环引用不爆栈(WeakMap 登记「原对象 → 克隆」)A circular reference does not recurse forever (a WeakMap records original to clone)
  • 不许用 JSON.parse(JSON.stringify(x))JSON.parse(JSON.stringify(x)) is not allowed
TypeScriptdeepClone.ts
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.

来自From 数据与函数:deepClone、flatten、curryData and functions: deepClone, flatten, curry · 面试八股Interview questions
L3写整块Write a block手写 flatten(depth 语义对齐原生 flat)Write flatten by hand (depth behaves like the built-in flat)
把「只做浅拷贝」的半成品写成真正的 flatten:默认压一层, depth 控制层数,Infinity 全压平,不改输入。不许调用原生 .flat()Grow this shallow-copy version into a real flatten: one level by default, depth decides how many levels, Infinity flattens everything, and the input is never changed. Calling the built-in .flat() is not allowed.
要求Requirements
  • 默认 depth 为 1,与 Array.prototype.flat 一致depth is 1 by default, the same as Array.prototype.flat
  • depth 控制展开层数,Infinity 全压平depth decides how many levels are opened up; Infinity flattens everything
  • depth 0 返回浅拷贝,不是原数组引用depth 0 returns a shallow copy, not a reference to the input array
  • 不改输入数组;不许调用原生 .flat()The input array is never changed, and the built-in .flat() is not allowed
TypeScriptflatten.ts
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.

来自From 数据与函数:deepClone、flatten、curryData and functions: deepClone, flatten, curry · 面试八股Interview questions
L3写整块Write a block手写 curry(部分应用可复用)Write curry by hand (partial applications stay reusable)
把「直接返回 fn」的半成品写成真正的 curry:参数攒够fn.length 就执行,可任意分组, 部分应用复用互不污染。Grow this return-fn-directly version into a real curry: run as soon as fn.length arguments have arrived, accept them in any grouping, and let a partial application be reused without one call affecting another.
要求Requirements
  • 攒够 fn.length 个参数就执行Runs as soon as fn.length arguments have arrived
  • 参数可以任意分组:c(1)(2)(3) / c(1, 2)(3) / c(1)(2, 3)Arguments may come in any grouping: c(1)(2)(3), c(1, 2)(3), c(1)(2, 3)
  • 部分应用可复用:const add1 = c(1) 之后多次调用互不污染A partial application is reusable: after const add1 = c(1), calling add1 many times gives independent results
TypeScriptcurry.ts
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.

来自From 异步与结构:Promise.all、EventEmitter、LRUAsync and structure: Promise.all, EventEmitter, LRU · 面试八股Interview questions
L3写整块Write a block手写 Promise.all + allSettledWrite Promise.all and allSettled by hand
把两个「直接 resolve 空数组」的半成品写成真的。不许调用原生 Promise.all / Promise.allSettled。Both half-finished functions just resolve with an empty array. Make them real. Calling the built-in Promise.all or Promise.allSettled is not allowed.
要求Requirements
  • 结果按输入顺序排,不是完成顺序(下标写入,不许 push)Results follow the input order, not the finishing order (write by index; push is not allowed)
  • 空数组立刻 resolve([])An empty array resolves with [] straight away
  • 数组里混普通值也行Plain values mixed into the array are fine
  • 任何一个 reject,整体立刻 reject,不等慢的If any one rejects, the whole thing rejects at once and does not wait for the slow ones
  • allSettled 永不 reject,逐项报 { status, value | reason }allSettled never rejects; it reports { status, value | reason } for each item
TypeScriptpromiseAll.ts
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.

来自From 异步与结构:Promise.all、EventEmitter、LRUAsync and structure: Promise.all, EventEmitter, LRU · 面试八股Interview questions
L3写整块Write a block手写 EventEmitterWrite an EventEmitter by hand
把空骨架填成完整的 EventEmitter:on / off / once / emit。 重点:once 触发时不能挤掉同一事件的其他监听器。Fill the empty skeleton in to a complete EventEmitter: on, off, once and emit. The point to watch: when a once listener fires, it must not push aside the other listeners on the same event.
要求Requirements
  • on 注册;emit 按注册顺序调用所有监听器并传参on registers a listener; emit calls every listener in registration order and passes the arguments along
  • off 只移除指定的那一个监听器off removes only the one listener it was given
  • once 只触发一次,且不挤掉同一事件的其他监听器once fires exactly once, and does not push aside the other listeners on the same event
  • emit 返回「有没有人在听」emit returns whether anyone was listening
TypeScriptemitter.ts
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.

来自From 异步与结构:Promise.all、EventEmitter、LRUAsync and structure: Promise.all, EventEmitter, LRU · 面试八股Interview questions
L3写整块Write a block手写 LRUCache(用 Map,不写链表)Write an LRUCache by hand (use a Map, no linked list)
把「什么都没存」的骨架写成真正的 LRU:超容量淘汰最久未使用, get 和 put 都要刷新「最近用过」。This skeleton stores nothing. Turn it into a real LRU: over capacity, drop the entry that has gone unused the longest, and both get and put must mark an entry as recently used.
要求Requirements
  • get / put 基本读写;get 不到返回 undefinedBasic reads and writes through get and put; a miss on get returns undefined
  • 超容量时淘汰最久未使用的那条Over capacity, drop the entry that has gone unused the longest
  • get 命中要刷新「最近用过」A hit on get marks that entry as recently used
  • put 已存在的 key:更新值并刷新put on a key that already exists updates the value and marks it as recently used
  • capacity 为 1 也要正确A capacity of 1 still behaves correctly
TypeScriptlru.ts
This check is textual: it looks for the right constructs, it does not run your code
提示Hints共 4 级,已看 0 级4 levels, 0 opened
先自己想两分钟。想不出来再点右上角 —— 提示是一级一级放的,不会一次给完。Think for two minutes first. Then use the button above — hints come one level at a time, never all at once.

看答案之前,先确认你已经自己动手写过一遍。看懂别人的答案和自己写出来,是两种能力。Before you open this, make sure you have written it yourself once. Following someone else's answer and producing your own are two different skills.