前端面试之吊打面试官 读代码篇

以下是我根据面试经历整理的 60 道 JavaScript 读代码类面试题,涵盖变量作用域、闭包、原型链、异步机制、类型转换等核心知识点。每道题附有解析,助你在面试中脱颖而出。


🧠 JavaScript 读代码面试题精选(共 30 题)

1. 变量提升与作用域

题目:

function test() {
  console.log(a);
  var a = 10;
}
test();

输出:

undefined

解析:

变量 a 在函数作用域内声明,虽然赋值在 console.log 之后,但由于变量提升,声明被提升到函数顶部,初始值为 undefined


2. 闭包与循环

题目:

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1000);
}

输出:

3
3
3

解析:

var 声明的变量在函数作用域内共享,循环结束后 i 的值为 3,所有的箭头函数共享这个值。


3. 箭头函数与 this

题目:

const obj = {
  name: 'Alice',
  greet: () => {
    console.log(this.name);
  }
};
obj.greet();

输出:

undefined

解析:

箭头函数不绑定自己的 this,其 this 值取决于外部作用域。在此例中,this 指向全局对象,this.nameundefined


4. 原型链继承

题目:

function Parent() {
  this.name = 'Parent';
}
Parent.prototype.sayName = function() {
  console.log(this.name);
};

function Child() {
  Parent.call(this);
  this.name = 'Child';
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

const child = new Child();
child.sayName();

输出:

Child

解析:

Child 通过 Parent.call(this) 继承了 Parent 的属性,并通过原型链继承了 Parent 的方法。child.sayName() 调用的是继承自 Parent.prototype 的方法,this.name'Child'


5. 异步执行顺序

题目:

console.log('Start');

setTimeout(() => {
  console.log('Timeout');
}, 0);

Promise.resolve().then(() => {
  console.log('Promise');
});

console.log('End');

输出:

Start
End
Promise
Timeout

解析:

同步代码先执行,输出 'Start''End'Promise 的回调属于微任务,setTimeout 的回调属于宏任务。事件循环机制决定微任务优先于宏任务,因此 'Promise''End' 之后,'Timeout' 最后执行。


6. 类型转换

题目:

console.log([] + []);
console.log([] + {});
console.log({} + []);
console.log({} + {});

输出:

""
"[object Object]"
"[object Object]"
"[object Object][object Object]"

解析:

在加法操作中,操作数为对象时,会先调用其 toString 方法转换为字符串。空数组的 toString 返回空字符串,空对象的 toString 返回 "[object Object]"


7. typeof 运算符

题目:

console.log(typeof null);
console.log(typeof undefined);
console.log(typeof NaN);

输出:

object
undefined
number

解析:

typeof null 返回 'object' 是 JavaScript 的历史遗留问题。undefined 类型为 'undefined'NaN 是一种数字类型,typeof NaN 返回 'number'


8. ===== 的区别

题目:

console.log(0 == '0');
console.log(0 === '0');

输出:

true
false

解析:

== 会进行类型转换,字符串 '0' 转为数字 0,所以 0 == '0'true=== 不进行类型转换,类型不同,结果为 false


9. NaN 的比较

题目:

console.log(NaN == NaN);
console.log(NaN === NaN);

输出:

false
false

解析:

NaN 与任何值都不相等,包括它自身。要判断一个值是否为 NaN,应使用 isNaN()Number.isNaN()


10. parseInt 的陷阱

题目:

console.log(parseInt('08'));
console.log(parseInt('08', 10));

输出:

0
8

解析:

在某些旧版本的 JavaScript 中,parseInt 解析以 '0' 开头的字符串时,会默认按八进制解析,'08' 中的 '8' 不是八进制数字,解析结果为 0。指定基数为 10 可避免此问题。


11. 数组的 length 属性

题目:

const arr = [1, 2, 3];
arr.length = 5;
console.log(arr);

输出:

[1, 2, 3, <2 empty items>]

解析:

将数组的 length 属性设置为大于当前长度的值,会在数组末尾添加空项。


12. arguments 对象

题目:

function test(a) {
  arguments[0] = 5;
  return a;
}
console.log(test(10));

输出:

5

解析:

在非严格模式下,函数的参数与 arguments 对象的对应元素共享内存,修改 arguments[0] 会影响参数 a 的值。


13. setTimeout 的作用域

题目:

for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1000);
}

输出:

0
1
2

解析:

let 声明的变量具有块级作用域,每次循环都会创建一个新的 i,因此每个 setTimeout 回调中的 i 值不同。


14. this 的绑定

题目:

const obj = {
  name: 'Alice',
  greet: function() {
    console.log(this.name);
  }
};
const greet = obj.greet;
greet();

输出:

undefined

解析:

将对象的方法赋值给变量后,this 不再指向原对象,而是指向全局对象。在浏览器中,全局对象的 name 属性通常为 undefined


15. callapply

题目:

function greet() {
  console.log(this.name);
}
const obj = { name: 'Alice' };
greet.call(obj);
greet.apply(obj);

输出:

Alice
Alice

解析:

callapply 方法都可以改变函数的 this 指向,call 接受参数列表,apply 接受参数数组。


16. bind 方法

题目:

const obj = {
  name: 'Alice',
  greet: function() {
    console.log(this.name);
  }
};
const greet = obj.greet.bind(obj);
greet();

输出:

Alice

解析:

bind 方法返回一个新的函数,this 被永久绑定到指定对象。


17. Object.is===

题目:

console.log(Object.is(NaN, NaN));
console.log(NaN === NaN);

输出:

true
false

解析:

Object.is 能正确判断 NaNNaN 相等,而 === 判断 NaN === NaNfalse


18. 浮点数精度问题

题目:

console.log(0.1 + 0.2 === 0.3);

输出:

false

解析:

由于浮点数精度问题,0.1 + 0.2 的结果为 0.30000000000000004,不等于 0.3


19. instanceof 操作符

题目:


function Foo() {}
const foo = new Foo();
console.log(foo instanceof Foo);

输出:

true

解析:

instanceof 检查对象的原型链中是否存在构造函数的 prototype 属性。这里 foo 的原型链包含 Foo.prototype,所以返回 true


20. 函数声明与表达式的区别

题目:

console.log(foo);
var foo = function() {
  return 'Hello';
};

输出:

undefined

解析:

变量 foo 声明被提升,但赋值不会提升。因此 console.log(foo) 输出 undefined,而不是函数体。


21. 隐式类型转换

题目:

console.log([] == false);
console.log({} == false);

输出:

true
false

解析:

  • [] 转换为 '''' == false,再转换为 0 == 0,结果为 true
  • {} 转换为 "[object Object]""[object Object]" == false 结果为 false

22. 解构赋值默认值

题目:

const [a = 10, b = a * 2] = [1];
console.log(a, b);

输出:

复制编辑
1 2

解析:

a 解构为 1b 默认值依赖 a,所以 b = 1 * 2 = 2


23. 对象解构与重命名

题目:

const { x: y = 3 } = { x: undefined };
console.log(y);

输出:

3

解析:

对象中 xundefined,因此使用默认值 3,同时变量被重命名为 y


24. async/await 的执行顺序

题目:

async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}
async function async2() {
  console.log('async2');
}
console.log('script start');
async1();
console.log('script end');

输出:

script start
async1 start
async2
script end
async1 end

解析:

async/await 内部会在 await 后面把后续代码放到微任务队列,所以 'async1 end' 是在 'script end' 后执行的。


25. 事件循环题目

题目:

setTimeout(() => console.log(1));
Promise.resolve().then(() => console.log(2));
console.log(3);

输出:

3
2
1

解析:

同步任务优先,打印 3Promise.then 是微任务,setTimeout 是宏任务。微任务比宏任务先执行,所以输出顺序是 3 2 1


26. 构造函数返回对象

题目:

function A() {
  this.name = 'A';
  return { name: 'B' };
}
const a = new A();
console.log(a.name);

输出:

B

解析:

如果构造函数返回一个对象,那么 new 出来的实例就是这个对象,而不是 this 指向的实例。


27. 数组和对象的 toString

题目:

console.log([1,2,3].toString());
console.log({a:1}.toString());

输出:

"1,2,3"
"[object Object]"

解析:

  • 数组的 toString 返回元素字符串以逗号连接。
  • 对象的 toString 返回 "[object Object]",除非自定义。

28. 字符串对象与原始字符串比较

题目:

console.log('abc' === new String('abc'));

输出:

false

解析:

'abc' 是原始值,new String('abc') 是对象,类型不同,所以严格相等比较返回 false


29. mapforEach 的区别

题目:

const arr = [1, 2, 3];
const res = arr.forEach(x => x * 2);
console.log(res);

输出:

undefined

解析:

forEach 不返回新数组,只是遍历元素。map 才返回新的数组。


30. Symbol 类型特性

题目:

const a = Symbol('desc');
const b = Symbol('desc');
console.log(a === b);

输出:

false

解析:

每个 Symbol 都是独一无二的,即使描述文字相同,两个 Symbol 也不相等。

🚀 JavaScript 面试进阶题精选(进阶 30 题)

31. Promise.all 中某个 Promise 失败

Promise.all([
  Promise.resolve(1),
  Promise.reject('error'),
  Promise.resolve(3)
])
.then(console.log)
.catch(console.error);

输出:

error

解析:

Promise.all 中任一 Promise 失败,整个结果直接进入 catch,不会等其他成功结果。


32. for...in 与对象原型链

Object.prototype.extra = 'extra';
const obj = { a: 1 };
for (let key in obj) {
  console.log(key);
}

输出:

a
extra

解析:

for...in 会枚举对象自身的可枚举属性以及原型链上的可枚举属性。


33. 函数默认参数的惰性求值

let a = 1;
function demo(x = a) {
  console.log(x);
}
a = 2;
demo();

输出:

2

解析:

函数默认参数在调用时动态求值,因此是惰性求值。


34. 数组稀疏元素

const arr = [1,,3];
console.log(arr[1]);
console.log(arr.length);

输出:

undefined
3

解析:

数组中缺省位置不会赋值,元素为 undefined,但 length 会计入该位置。


35. JSON.stringify 的行为

const obj = {
  a: 1,
  b: undefined,
  c: function() {},
  d: Symbol('d')
};
console.log(JSON.stringify(obj));

输出:

{"a":1}

解析:

undefined、函数、Symbol 类型会被 JSON.stringify 忽略。


36. parseInt 的陷阱

console.log(['1', '2', '3'].map(parseInt));

输出:

[1, NaN, NaN]

解析:

map 会将元素和索引传给 parseInt(value, index),当 index = 1 时,相当于 parseInt('2', 1),返回 NaN


37. 数组去重技巧

console.log([...new Set([1,2,2,3,3])]);

输出:

[1,2,3]

解析:

Set 是集合结构,自动去重,配合展开运算符转回数组即可。


38. typeof null

console.log(typeof null);

输出:

object

解析:

历史遗留问题,typeof null 返回 object,是一个 bug,被保留下来了。


39. Promise 的链式调用

Promise.resolve(1)
  .then(x => x + 1)
  .then(x => Promise.resolve(x + 1))
  .then(console.log);

输出:

3

解析:

每个 then 返回值会自动被包装成 Promise,支持链式传递。


40. Object.freeze 与嵌套对象

const obj = Object.freeze({ a: 1, b: { c: 2 } });
obj.b.c = 3;
console.log(obj.b.c);

输出:

3

解析:

Object.freeze 是浅冻结,嵌套对象仍然可变。

以下是我根据面试经历整理的 30 道 JavaScript 读代码类面试题,涵盖变量作用域、闭包、原型链、异步机制、类型转换等核心知识点。每道题附有解析,助你在面试中脱颖而出。


41. NaN 与自身不相等

console.log(NaN === NaN);

输出:

false

解析:

NaN 是唯一一个不等于自身的值,用于表示“非数字”的结果。


42. Symbol 唯一性

console.log(Symbol('foo') === Symbol('foo'));

输出:

false

解析:

每次调用 Symbol() 都会生成唯一的 Symbol,即使描述相同也不相等。


43. instanceof 检测机制

console.log([] instanceof Array);
console.log([] instanceof Object);

输出:

true
true

解析:

[]Array 的实例,ArrayObject 的子类,因此两个都为 true


44. setTimeout 嵌套顺序

setTimeout(() => {
  console.log('first');
}, 0);

Promise.resolve().then(() => {
  console.log('second');
});

console.log('third');

输出:

third
second
first

解析:

Promise.then 是微任务,优先于宏任务 setTimeout 执行。


45. delete 删除变量

var x = 1;
delete x;
console.log(x);

输出:

1

解析:

delete 只能删除对象属性,不能删除通过 var 声明的变量。


46. typeof function

console.log(typeof function(){});

输出:

function

解析:

typeof 对函数返回 function,是唯一不返回基本类型名的情况。


47. arguments 对象

function foo(a, b) {
  arguments[0] = 10;
  console.log(a);
}
foo(5);

输出:

10

解析:

非严格模式下 arguments 与形参共享内存,修改 arguments 会同步到形参。


48. 严格模式下 this

'use strict';
function show() {
  console.log(this);
}
show();

输出:

undefined

解析:

严格模式下,普通函数中的 thisundefined,非严格模式下为 window


49. 标签模板字符串

function tag(strings, value) {
  console.log(strings, value);
}
const name = 'world';
tag`Hello ${name}`;

输出:

["Hello ", ""] "world"

解析:

标签模板字符串会将文本和插值拆分为多个参数传入函数。


50. Object.assign 浅拷贝

const obj1 = { a: { b: 1 } };
const obj2 = Object.assign({}, obj1);
obj2.a.b = 2;
console.log(obj1.a.b);

输出:

2

解析:

Object.assign 是浅拷贝,嵌套对象仍然引用相同的地址。

...

(题目 51~60 即将继续补充)

51. 模块导出默认值

// module.js
export default 42;

// index.js
import value from './module.js';
console.log(value);

输出:

42

解析:

使用 export default 导出的模块可以用任意名称导入,默认导出无需使用花括号。


52. 事件循环与 DOM 操作

document.body.innerHTML = '<button id="btn">Click</button>';
document.getElementById('btn').addEventListener('click', () => {
  console.log('clicked');
});

document.getElementById('btn').click();

输出:

clicked

解析:

调用 click() 方法会模拟点击事件,触发绑定的事件处理函数。


53. Object.freeze 不可变对象

const obj = Object.freeze({ a: 1 });
obj.a = 2;
console.log(obj.a);

输出:

1

解析:

Object.freeze 冻结对象,使其属性无法被修改。


54. async/await 的返回值

async function foo() {
  return 1;
}
foo().then(console.log);

输出:

1

解析:

async 函数总是返回一个 Promise,返回值会被包装在 Promise.resolve 中。


55. Map 与 Object 区别

const map = new Map();
map.set({}, 'val');
console.log(map.size);

输出:

1

解析:

Map 可使用对象作为键,每个对象键都是唯一引用,不会自动转为字符串。


56. typeof null

console.log(typeof null);

输出:

object

解析:

这是 JavaScript 的历史遗留 bug,typeof null 返回 'object'


57. 函数默认参数

function greet(name = 'Guest') {
  console.log(name);
}
greet();

输出:

Guest

解析:

当未传参时,使用默认值 'Guest'


58. Array.from 特性

console.log(Array.from('abc'));

输出:

['a', 'b', 'c']

解析:

Array.from 可将类数组或可迭代对象(如字符串)转换为数组。


59. 对象属性访问顺序

const obj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(obj));

输出:

["2", "7", "100"]

解析:

对象属性是数字时会按照数值升序排列,而非定义顺序。


60. JSON.stringify 忽略函数属性

const obj = {
  a: 1,
  b: () => {}
};
console.log(JSON.stringify(obj));

输出:

{"a":1}

解析:

JSON.stringify 会忽略对象中的函数、undefinedsymbol 类型的属性。


至此,JavaScript 面试中常见的 60 道读代码题汇总完毕,涵盖基础到进阶的多个方面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

像素笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值