JavaScript面试题

本文最后更新于 2025年7月30日 下午

枚举属性

操作符 自有属性 继承属性 可枚举属性 不可枚举属性
in 操作符 返回 返回 返回 返回
for/in 循环 返回 返回 返回 不返回
hasOwnProperty() 返回 不返回 返回 返回
propertyIsEnumerable() 返回 不返回 返回 不返回
Object.keys() 返回 不返回 返回 不返回
Object.getOwnPropertyNames() 返回 不返回 返回 返回
Reflect.ownKeys() 返回 不返回 返回 返回

虚拟 DOM 和 diff 算法

参考:https://juejin.cn/post/6994959998283907102

好处:

  • 跨平台
  • 只需要操作数据,开发效率更高
  • 通过 DOM diff 算法,可减少不必要的 DOM 操作,提高性能

可迭代对象

可迭代对象是指实现了[Symbol.iterator]方法属性的对象。注意,区分类数组对象。
类数组对象(伪数组对象)是指可以通过索引属性访问元素并且拥有一个 length 属性的对象。
可迭代对象和类数组对象并非是相互排斥的。比如,字符串既是可迭代对象又是类数组对象。

迭代器模式(特别是在 ECMAScript 这个语境下)描述了一个方案,即可以把有些结构称为“可迭代对象”(iterable),因为它们实现了正式的 Iterable 接口,而且可以通过迭代器 Iterator 消费。

可迭代对象是一种抽象的说法。基本上,可以把可迭代对象理解成数组或集合这样的集合类型的对象。它们包含的元素都是有限的,而且都具有无歧义的遍历顺序。不过,可迭代对象不一定都是集合对象,也可以是仅仅具有类似数组行为的其他数据结构。基本上,可以使用 for…of 循环的对象都是可迭代对象。

ES6 新语法

参考:
https://juejin.cn/post/6854818580660387853
https://juejin.cn/post/6844903581426925581

  • let、const
  • Set、Map、Symbol
  • for-of/for-in
  • 模块导入导出
  • 函数默认参数
  • 箭头函数
  • 展开运算符
  • 对象的解构
  • 字符串模版

箭头函数和普通函数

  • 箭头函数没有没有原型对象。
  • 箭头函数 this 值为其所在上下文的 this 值。
  • 箭头函数箭头函数不能使用 arguments、super、new.target。
  • 箭头函数不能定义生成器函数。

for-in 循环和 for-of 循环

  • for-in 循环:可用于遍历数组索引下标值和对象键名,大部分用于对象。
  • for-of 循环:可用于遍历可迭代对象(Array、String、Map、Set 等等),不能遍历类数组对象和对象。但是 for-of 循环和 Object.keys()、Object.vales()、Object.entries() 配合可用来遍历对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const a = { sfd: 45, eee: 93 };
const b = ["sfd", 45, "eee", 93];
for (let s in a) {
console.log(s);
}
// sfd
// eee
for (let s in b) {
console.log(s);
}
// 0
// 1
// 2
// 3
for (let s of a) {
console.log(s);
}
// Uncaught TypeError: a is not iterable
for (let s of Object.keys(a)) {
console.log(s);
}
// sfd
// eee

判断数据类型

  • Object.prototype.toString.call() 方法
  • typeof 操作符
  • instanceof 操作符
  • constructor 属性

此外,还有 Array.isArray(obj) 方法、isNaN(obj) 方法等可以精准判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
var und = undefined;
var nul = null;
var boo = true;
var num = 1;
var str = "xys";
var obj = new Object();
var arr = [1, 2, 3];
var fun = function () {};
var date = new Date();
var reg = /a/g;
var err = new Error();
var arg;
(function getArg() {
arg = arguments;
})();

console.log(Object.prototype.toString.call(und)); // [object Undefined]
console.log(Object.prototype.toString.call(nul)); // [object Null]
console.log(Object.prototype.toString.call(boo)); // [object Boolean]
console.log(Object.prototype.toString.call(num)); // [object Number]
console.log(Object.prototype.toString.call(str)); // [object String]
console.log(Object.prototype.toString.call(obj)); // [object Object]
console.log(Object.prototype.toString.call(arr)); // [object Array]
console.log(Object.prototype.toString.call(fun)); // [object Function]
console.log(Object.prototype.toString.call(date)); // [object Date]
console.log(Object.prototype.toString.call(reg)); // [object RegExp]
console.log(Object.prototype.toString.call(err)); // [object Error]
console.log(Object.prototype.toString.call(arg)); // [object Arguments]

console.log(typeof und); // undefined
console.log(typeof nul); // object
console.log(typeof boo); // boolean
console.log(typeof num); // number
console.log(typeof str); // string
console.log(typeof obj); // object
console.log(typeof arr); // object
console.log(typeof fun); // function
console.log(typeof date); // object
console.log(typeof reg); // object
console.log(typeof err); // object
console.log(typeof arg); // object

console.log(obj instanceof Object); // true
console.log(arr instanceof Array); // true
console.log(fun instanceof Function); // true
console.log(date instanceof Date); // true
console.log(reg instanceof RegExp); // true
console.log(err instanceof Error); // true
console.log(arg instanceof Arguments); // true

obj.__proto__.constructor === Object; // true
arr.__proto__.constructor === Array; // true
fun.__proto__.constructor === Function; // true
date.__proto__.constructor === Date; // true
reg.__proto__.constructor === RegExp; // true
err.__proto__.constructor === Error; // true
arg.__proto__.constructor === Arguments; // Uncaught ReferenceError

说明:

  • Object.prototype.toString.call() 方法是最准确的方法。
  • typeof 操作符只用于原始值,对于 Null 类型其返回为 Object。
  • instanceof 操作符只用于引用值,且是假定只有一种全局环境,如果网页中包含多个框架多个全局环境,如果从一个框架向另一个框架传入一个数组,那么传入的数组与在第二个框架中原生创建的数组分别具有各自不同的构造函数。
  • constructor 属性只用于引用值,而且其可以被重写,所以不一定准确。

垃圾回收

JavaScript 是使用垃圾回收的语言,也就是说执行环境负责在代码执行时管理内存。在 C 和 C++ 等语言中,跟踪内存使用对开发者来说是个很大的负担,也是很多问题的来源。JavaScript 则为开发者卸下了这个负担,通过自动内存管理实现内存分配和闲置资源回收。

基本思路很简单:确定哪个变量不会再使用,然后释放它所占用的内存。这个过程是周期性的,即垃圾回收程序每隔一定时间(或者说在代码执行过程中某个预定的收集时间)就会自动运行。垃圾回收过程是个近似且不完美的方案,因为某块内存是否还有用,属于“不可判定的”问题,意味着靠算法是解决不了的。

以函数中局部变量的正常生命周期为例。函数中的局部变量会在函数执行时存在。此时,栈(或堆)内存会分配空间以保存相应的值。函数在内部使用了变量,然后退出。此时,就不再需要那个局部变量,它占用的内存可以释放,供后面使用。这种情况下显然不再需要局部变量了,但并不是所有时候都会这么明显。垃圾回收程序必须跟踪记录哪个变量还会使用以及哪个变量不会再使用,以便回收内存。如何标记未使用的变量也许有不同的实现方式。不过,在浏览器的发展史上,用到过两种主要的标记策略:标记清理和引用计数。

JavaScript 最常用的垃圾回收策略是标记清理(mark-and-sweep)。当变量进入上下文,如在函数内部声明一个变量时,这个变量会被加上存在于上下文中的标记。而在上下文中的变量,逻辑上讲,永远不应该释放它们的内存,因为只要上下文中的代码在运行,就有可能用到它们。当变量离开上下文时,也会被加上离开上下文的标记。给变量加标记的方式有很多种。比如,当变量进入上下文时,反转某一位;或者,可以维护“在上下文中”和“不在上下文中”两个变量列表,可以把变量从一个列表转移到另一个列表。标记过程中的实现并不重要,关键是策略。

垃圾回收程序运行的时候,会标记内存中存储的所有变量(标记方法有很多)。然后,它会将所有在上下文中的变量,以及被在上下文中的变量引用的变量的标记去掉。在此之后再被加上标记的变量就是待删除的,原因是任何在上下文中的变量都访问不到它们了。随后垃圾回收程序做一次内存清理,销毁带标记的所有值并收回它们的内存。

  • 通过 const 和 let 声明提升性能。
  • 隐藏类和删除操作。

event.target 和 event.currentTarget

在事件处理程序内部,this 对象始终等于 currentTarget 的值,target 只包含事件的实际目标。

如果事件处理程序直接添加在了意图的目标,则 this、currentTarget 和 target 的值是一样的。如果这个事件处理程序是添加到按钮的父节点(如 document.body)上,那么它们的值就不一样了。由于按钮本身并没有注册事件处理程序,因此 click 事件冒泡到 document.body,从而触发了在它上面注册的处理程序。

CommonJS 和 ES Module

参考:https://juejin.cn/post/6994224541312483336

  • CommonJS
    CommonJS 模块由 JS 运行时实现。
    CommonJs 是单个值导出,本质上导出的就是 exports 属性。
    CommonJS 是可以动态加载的,对每一个加载都存在缓存,可以有效的解决循环引用问题。
    CommonJS 模块同步加载并执行模块文件
  • ES Module
    ES6 Module 静态的,不能放在块级作用域内,代码发生在编译时。
    ES6 Module 的值是动态绑定的,可以通过导出方法修改,可以直接访问修改结果。
    ES6 Module 可以导出多个属性和方法,可以单个导入导出,混合导入导出。
    ES6 模块提前加载并执行模块文件,
    ES6 Module 导入模块在严格模式下。
    ES6 Module 的特性可以很容易实现 Tree Shaking 和 Code Splitting。

输出判断题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function test1(aaa, bbb) {
console.log(aaa, arguments[0], bbb, arguments[1]);
aaa = 666;
bbb = 777;
console.log(aaa, arguments[0], bbb, arguments[1]);
arguments[0] = 888;
arguments[1] = 999;
console.log(aaa, arguments[0], bbb, arguments[1]);
}
test1(111, 222);

// 111 111 222 222
// 666 666 777 777
// 888 888 999 999
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function test1(a, b) {
console.log(a, arguments[0], b, arguments[1]);
a = "aaa";
b = "bbb";
console.log(a, arguments[0], b, arguments[1]);
arguments[0] = "trueaaa";
arguments[1] = "truebbb";
console.log(a, arguments[0], b, arguments[1]);
}
function test2(a = "aaabbb", b) {
console.log(a, arguments[0], b, arguments[1]);
a = "aaa";
b = "bbb";
console.log(a, arguments[0], b, arguments[1]);
arguments[0] = "trueaaa";
arguments[1] = "truebbb";
console.log(a, arguments[0], b, arguments[1]);
}
test1();
test1("AAA");
test2();
test2("AAA");

// undefined undefined undefined undefined
// aaa undefined bbb undefined
// aaa trueaaa bbb truebbb

// AAA AAA undefined undefined
// aaa aaa bbb undefined
// trueaaa trueaaa bbb truebbb

// aaabbb undefined undefined undefined
// aaa undefined bbb undefined
// aaa trueaaa bbb truebbb

// AAA AAA undefined undefined
// aaa AAA bbb undefined
// aaa trueaaa bbb truebbb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function test1(arg) {
console.log(arg);
var arg = "hello1";
function arg() {
console.log("aaa");
}
console.log(arg);
}
function test2(arg) {
console.log(arg);
var arg = "hello2";
console.log(arg);
}
test1("hi1");
test2("hi2");

// ƒ arg() {
// console.log('aaa')
// }
// hello1
// hi2
// hello2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function type(t) {
return Object.prototype.toString.call(t).slice(8, -1);
}
function fn1(...args) {
return args;
}
function fn2() {
return arguments;
}
console.log(type(fn1(1, 2, 3)));
console.log(type(fn2(1, 2, 3)));

// Array
// Arguments
1
2
3
4
5
6
7
8
9
10
11
12
13
console.log(a); // Uncaught ReferenceError: a is not defined
a = 12;
function fn() {
console.log(a); // undefined
console.log(b); // Uncaught ReferenceError: Cannot access 'b' before initialization
a = 13;
var a = 14;
let b = 15;
console.log(a); // 14
}
fn();
console.log(a); // 12
console.log(b); // Uncaught ReferenceError: b is not defined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function showcase(value) {
switch (value) {
case "A":
console.log("A");
break;
case "B":
console.log("B");
break;
case undefined:
console.log(undefined);
break;
default:
console.log("unKnown");
}
}
showcase(new String("A"))[([4, 2, 1].reduce(Math.pow), [""].reduce(Math.pow))]; // 'unKnown' // [16, '']

Symbol() === Symbol(); // false

Symbol("aa") === Symbol("aa"); // false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Object.__proto__ === Function.__proto__; // true
Object.__proto__ === Function.prototype; // true
Array.__proto__ === Function.__proto__; // true
Function.prototype === Function.__proto__; // true
Function.prototype; // ƒ () { [native code] }
Object.prototype.prototype; // undefined
Object.prototype.__proto__; // null
Function.prototype.prototype; // undefined

"标准的7个汉字".length; // 7
"11" == new String(11); // true
"11" === new String(11); // false

var s1 = "afa";
var s2 = new String("jodo");
Object.prototype.toString.call(s1); // '[object String]'
Object.prototype.toString.call(s2); // '[object String]'
s1 instanceof Object; // false
s2 instanceof Object; // true
console.log("Value is" + (s === "afa") ? "yes" : "no"); // yes

其它问题

  • JavaScript 标准内置对象
  • 数据类型(基本/集合)
  • 原型、原型链、继承
  • this 指向
  • 数组方法
  • 数组扁平化
  • 期约(Promise)
  • 微任务和宏任务
  • 事件循环

JavaScript面试题
https://xuekeven.github.io/2021/07/16/JavaScript面试题/
作者
Keven
发布于
2021年7月16日
更新于
2025年7月30日
许可协议