DrillLab
八股题库Question bank

105 道问答题,一道一卡105 questions, one card each

默认只显示问题 —— 先自己在心里答一遍,再展开对答案。答不上来就标「不会」,下次抽认卡会先抽它。Only the question shows by default. Answer it in your head first, then open the answer. Mark the ones you miss; the flashcard round puts those first.

只看这一节One lesson onlythis 与面向对象三问3 道3 questions看全部 105 道 →All 105 questions →
0 / 105道自评过self-assessed
0Got it0模糊Shaky0不会No idea105还没做Not seen
正在读这台浏览器里的标记…Reading your marks from this browser…
标记不是打分,是给下一轮排队Marks are a queue, not a score

标「不会」的题会排到抽认卡最前面,标「会」的进低频池排最后。所以别客气 —— 觉得答得磕磕巴巴就标「模糊」。准备好了就去抽认卡“No idea” jumps to the front of the next round; “got it” drops to the back. So be honest — if it came out shaky, mark it shaky. Then go run a flashcard round.

找一道题Find one
按方向、掌握状态筛Filter by topic and mark

题目Questions

筛出 3 道。3 of 3 questions.
JavaScriptJavaScript#302

什么是面向对象编程

What is Object-Oriented Programming (OOP)

看答案Show answer

一句话:把数据和操作数据的方法打包在一起,用对象来组织程序。

四个特征(背下来):

  • 封装—— 内部细节藏起来, 只暴露必要的接口。JS 里用闭包或#private 字段实现。
  • 继承—— 子类复用父类的能力。
  • 多态—— 同一个方法名, 不同对象有不同行为。
  • 抽象—— 只关心「能做什么」, 不关心「怎么做的」。

JS 的特别之处(这才是考点):它是基于原型(prototype)的, 不是基于类的。class 是 ES6 加的语法糖, 底下还是原型链 ——class A extends B 编译后就是设置A.prototype.__proto__ = B.prototype

原型链一句话:访问一个属性时,对象自己没有就去__proto__ 上找, 一层层往上直到 null和作用域链是一个套路, 只是一个查变量、一个查属性。

会追问:「React 为什么从 class 转向函数组件?」—— 因为 UI 更适合用「输入 → 输出」来描述, 而不是「一个有生命周期的对象」; 而且 class 里 this的绑定问题、逻辑按生命周期而不是按关注点拆分, 都是实际痛点(见 #322)。

In one line: bundle data together with the methods that act on it, and organise the program around objects.

Four pillars — memorise these:

  • Encapsulation — hide the internals, expose only the interface callers need. In JS you get it from closures or #private fields.
  • Inheritance — a subclass reuses what the parent can already do.
  • Polymorphism — same method name, different behaviour per object.
  • Abstraction — you care what it can do, not how it does it.

What makes JS different, and this is the real question: it is prototype-based, not class-based. class is syntax sugar added in ES6; the prototype chain is still underneath — class A extends B compiles down to setting A.prototype.__proto__ = B.prototype.

The prototype chain in one line: read a property, and if the object does not have it the lookup walks up __proto__ one level at a time until null. Same idea as the scope chain — one looks up variables, the other looks up properties.

Follow-up: “Why did React move from classes to function components?” — because UI is easier to describe as “input → output” than as an object with a lifecycle. On top of that, this binding and code split by lifecycle instead of by concern were real, daily pain (see #322).

JavaScript四个特征与原型链The four traits and the prototype chain示意Illustrative
1class Animal {
2 #secret = "私有字段,外部访问不到"; // 封装
3 constructor(name) { this.name = name; }
4 speak() { return `${this.name} 发出声音`; }
5}
6
7class Dog extends Animal {
8 speak() { return `${this.name} 汪汪`; } // 多态:覆盖父类方法
9}
10
11new Dog("旺财").speak(); // "旺财 汪汪"
12
13// class 只是语法糖,底下是原型链
14Object.getPrototypeOf(Dog.prototype) === Animal.prototype; // true
15new Dog("x") instanceof Animal; // true
1class Animal {
2 #secret = "a private field, unreachable from outside"; // encapsulation
3 constructor(name) { this.name = name; }
4 speak() { return `${this.name} makes a sound`; } // encapsulation
5}
6
7class Dog extends Animal {
8 speak() { return `${this.name} woof`; } // polymorphism: overrides the parent
9}
10
11new Dog("旺财").speak(); // the name is data, so it stays: <name> woof
12
13// class is only syntax sugar; a prototype chain sits underneath
14Object.getPrototypeOf(Dog.prototype) === Animal.prototype; // true
15new Dog("x") instanceof Animal; // true
JavaScriptJavaScript#303

this 指向什么

What does 'this' refer to

看答案Show answer

一句话:this调用时决定的, 不是定义时。看「谁调用的」

四条规则,按优先级从高到低—— 这个顺序是标准答案:

  1. new 绑定——new Foo()this 是新创建的对象。
  2. 显式绑定——call / apply /bind 指定的那个。
  3. 隐式绑定——obj.fn()thisobj看点号左边)。
  4. 默认绑定—— 都不满足时, 严格模式下是 undefined, 否则是 window / global

箭头函数是例外,它不参与这四条—— 它没有自己的 this, 用的是定义时外层作用域的 this, 而且 call / bind改不了它

最经典的坑:隐式丢失。const fn = obj.method 之后单独调fn(),点号没了,this 就丢了。 把方法当回调传出去(setTimeout(obj.method)onClick={this.handle}) 都是这个问题 ——这就是 React 类组件必须在构造器里bind 的原因

会追问:「DOM 事件回调里 this 是什么?」—— 普通函数是绑定事件的那个元素(等于 e.currentTarget), 箭头函数则是外层的 this

In one line: this is decided when the function is called, not where it was written. Look at who called it.

Four rules, highest priority first — this order is the model answer:

  1. new binding — with new Foo(), this is the object that was just created.
  2. Explicit binding — whatever you handed to call / apply / bind.
  3. Implicit bindingobj.fn() makes this the obj (look left of the dot).
  4. Default binding — when none of the above applies: undefined in strict mode, otherwise window / global.

Arrow functions are the exception; they do not play by those four rules — an arrow has no this of its own, it uses the this of the scope it was defined in, and call / bind cannot change that.

The classic trap: the implicit binding gets lost. Do const fn = obj.method and then call fn() on its own — the dot is gone, so this is gone. Handing a method off as a callback (setTimeout(obj.method), onClick={this.handle}) is the same bug — and it is exactly why React class components had to bind in the constructor.

Follow-up: “What is this inside a DOM event handler?” — in a normal function it is the element the listener is attached to (the same as e.currentTarget); in an arrow function it is the outer this.

JavaScript四条规则与隐式丢失The four rules, and losing the implicit binding示意Illustrative
1const obj = {
2 name: "obj",
3 show() { console.log(this.name); },
4};
5
6obj.show(); // "obj" 隐式绑定,看点号左边
7const f = obj.show;
8f(); // undefined 隐式丢失
9f.call({ name: "call" }); // "call" 显式绑定
10setTimeout(obj.show, 0); // undefined 传出去就丢了
11setTimeout(() => obj.show(), 0); // "obj" 包一层就保住了
12
13// 箭头函数不参与规则,call 也改不了
14const arrow = () => console.log(this);
15arrow.call({ a: 1 }); // 还是外层的 this
16
17// React 类组件为什么要 bind
18class Btn extends React.Component {
19 constructor(p) {
20 super(p);
21 this.handle = this.handle.bind(this); // 不 bind,onClick 里 this 就是 undefined
22 }
23 handle() { console.log(this.props); }
24}
1const obj = {
2 name: "obj",
3 show() { console.log(this.name); },
4};
5
6obj.show(); // "obj" implicit binding: look left of the dot
7const f = obj.show;
8f(); // undefined the implicit binding is lost
9f.call({ name: "call" }); // "call" explicit binding
10setTimeout(obj.show, 0); // undefined pass it out and the binding is gone
11setTimeout(() => obj.show(), 0); // "obj" one wrapper keeps it
12
13// An arrow function ignores these rules, and call cannot change it either
14const arrow = () => console.log(this);
15arrow.call({ a: 1 }); // still the outer this
16
17// Why a React class component needs bind
18class Btn extends React.Component {
19 constructor(p) {
20 super(p);
21 this.handle = this.handle.bind(this); // without bind, this is undefined in onClick
22 }
23 handle() { console.log(this.props); }
24}
JavaScriptJavaScript#304

call、apply、bind 的区别

What are the differences between call, apply & bind

看答案Show answer

一句话:三个都是改thiscallapply立即执行,bind 返回一个新函数call 参数一个个传,apply 传数组。

是否立即执行参数形式
fn.call(ctx, a, b)立即逐个(Comma)
fn.apply(ctx, [a, b])立即数组(Array)
fn.bind(ctx, a)不执行,返回新函数逐个,且可以只绑一部分

记法:Apply 收 Array,Call 用 Comma。

bind 的两个额外性质(追问点):

  • 能预置参数—— 所以它就是偏函数 (见 #299)。
  • 绑过一次就锁死了—— 再 bindcall都改不回来。new 能突破它, 因为 new 优先级最高。

还会追问:apply 现在还有用吗?」—— 展开语法出来后大部分被fn(...args) 取代了。但转发不定参数时还常用fn.apply(this, args)(防抖里就是这么写的,因为要同时转发thisargs)。

In one line: all three change this. call and apply run the function right away, bind hands you back a new one; call takes its arguments one by one, apply takes an array.

Runs immediately?Argument form
fn.call(ctx, a, b)YesOne by one (Comma)
fn.apply(ctx, [a, b])YesAn array (Array)
fn.bind(ctx, a)No — you get a new functionOne by one, and you may bind only some of them

How to remember it: Apply takes an Array, Call takes Commas.

Two extra properties of bind they will probe:

  • It can preset arguments — which makes it partial application (see #299).
  • Bind once and it is locked — another bind or a call cannot change it back. But new breaks through, because new has the highest priority.

Another follow-up: “Is apply still useful?” — spread syntax replaced most of it with fn(...args). It is still the normal way to forward an unknown argument list: fn.apply(this, args) — that is how debounce is written, because you have to forward this and args at the same time.

JavaScript三者对比与手写 bindThe three compared, and bind written by hand示意Illustrative
1function greet(greeting, mark) {
2 return `${greeting}, ${this.name}${mark}`;
3}
4const who = { name: "小明" };
5
6greet.call(who, "你好", "!"); // "你好, 小明!"
7greet.apply(who, ["你好", "!"]); // 同上,参数是数组
8const hi = greet.bind(who, "你好"); // 返回新函数,还预置了第一个参数
9hi("?"); // "你好, 小明?"
10
11// 手写一个 bind(高频现场题)
12Function.prototype.myBind = function (ctx, ...preset) {
13 const fn = this;
14 return function bound(...args) {
15 // new 调用时 this 是新对象,此时不该用 ctx —— 这是 bind 的规范行为
16 const isNew = this instanceof bound;
17 return fn.apply(isNew ? this : ctx, [...preset, ...args]);
18 };
19};
1function greet(greeting, mark) {
2 return `${greeting}, ${this.name}${mark}`;
3}
4const who = { name: "小明" };
5
6greet.call(who, "你好", "!"); // the greeting, then the name, then the mark
7greet.apply(who, ["你好", "!"]); // the same, but the arguments arrive as an array
8const hi = greet.bind(who, "你好"); // returns a new function with the first argument preset
9hi("?"); // the same string, ending in a question mark
10
11// Write bind by hand (a very common live-coding question)
12Function.prototype.myBind = function (ctx, ...preset) {
13 const fn = this;
14 return function bound(...args) {
15 // With new, this is the new object and ctx must be ignored —— the spec requires it
16 const isNew = this instanceof bound;
17 return fn.apply(isNew ? this : ctx, [...preset, ...args]);
18 };
19};
手写 bind 的加分项就是那个 isNew 判断:规范规定 new 一个 bound 函数时,绑定的 this 应该被忽略。多数人会漏。What earns extra credit when you write bind by hand is that isNew check: the spec says that when a bound function is called with new, the bound this must be ignored. Most people leave it out.

这些题从哪来Where these come from

99 道来自面试题库 #269–#387;TypeScript 深度那 6 道是 DrillLab 自出的(senior 补强,卡片上有标注)。答案都是 DrillLab 写的,所以讲解里的代码块一律标「示意」。每道题都能点回它出处的那一节课。99 questions come from the interview bank (#269–#387); the 6 TypeScript deep-dive ones are DrillLab-made (marked on the card). All answers are written by DrillLab, so every code block here is labelled “demo”. Each card links back to the lesson it came from.