26个精选的JavaScript面试问题

译者按: 从各个平台精选整理出 26 道由浅入深的题目助你面试

本文采用意译,版权归原作者所有

根据 Stack Overflow 2018 年年度调查报告,JavaScript 已经连续 6 年保持最常用的编程语言的记录。对于一个全栈工程师,JavaScript 可以说是一项必备语言,在面试中总会被问到。我整理了一下FullStack.Cafe上所有常见的 JavaScript 面试问题供大家参考:

Q1: JavaScript 中类型转换是怎样的?

话题: JavaScript
难度: 0

在 JavaScript 中,在两个不同类型之间的转换叫做coercion。在 JavaScript 中有两种形式:显示转换和隐式转换。

下面是一个显示转换的例子:

var a = "42";
var b = Number(a);
a; // "42"
b; // 42 -- the number!

下面是一个隐式转换的例子:

var a = "42";
var b = a * 1; // "42" implicitly coerced to 42 here
a; // "42"
b; // 42 -- the number!

来源: FullStack.Cafe

Q2: JavaScript 中的作用域是怎样的?

话题: JavaScript
难度: ⭐

在 JavaScript 中,每一个函数都有各自的作用域(scope)。作用域可以理解为是一个变量的集合以及相应的如何访问它的规则。只有在函数内部的变量才可以访问到该函数域的变量。

在同一个作用域内部,变量名必须要唯一。作用域可以嵌套。在最内部的作用域中,可以访问任何外部的作用域中的变量。

Q3: 请解释 JavaScript 中的相等判断

话题: JavaScript
难度: ⭐

JavaScript 中的相等判断有严格判断和带隐式转换的判断两种:

  • 严格判断(strict comparision): 比如===,比较的时候不会隐式转换类型;
  • 抽象判断(abstract comparasion):比如==,比较的时候会隐式转换类型。
var a = "42";
var b = 42;

a == b; // true
a === b; // false

一些简单的规则:

  • 如果两边都是布尔类型的值,使用===;
  • 如果两边是 0,"",[],使用===;
  • 所有其它类型,使用==是安全的。而且在很多情况下会简化代码、增加可读性。

Q4: 请解释什么叫做回调函数并提供一个简单的例子

话题: JavaScript
难度: ⭐⭐

回调函数是一个函数,它被作为参数传入另一个函数,当某些操作结束后,该函数被调用。下面是一个简单的例子,当数组被修改后,调用回调函数打印一行日志。

function modifyArray(arr, callback) {
// do something to arr here
arr.push(100);
// then execute the callback function that was passed
callback();
}

var arr = [1, 2, 3, 4, 5];
modifyArray(arr, function() {
console.log("array has been modified", arr);
});

Q5: “use strict”到底有何用处?

话题: JavaScript
难度: ⭐⭐

use strict放在文件的顶部或则函数的第一行来启动更加严格的检查来避免失误引起的错误。比如,下面的代码会抛出错误:

function doSomething(val) {
"use strict";
x = val + 10;
}

因为 x 没有定义,如果使用了use strict,x 是不会被当做全局的变量来看待。下面的代码修复了这个 BUG:

function doSomething(val) {
"use strict";
var x = val + 10;
}

Q6: 请解释 Null 和 Undefined

话题: JavaScript
难度: ⭐⭐

JavaScript 和 TypeScript 有两个最基本的类型nullundefined。它们的含义是不同的:

  • 如果还没有被初始化,则是undefined;
  • 如果不可用,则可以用null来表示;

Q7: 请实现如下函数

话题: JavaScript
难度: ⭐⭐

var addSix = createBase(6);
addSix(10); // returns 16
addSix(21); // returns 27

addSix是一个函数,也就是说 createBase 函数的返回是一个函数。

function createBase(baseNumber) {
return function(N) {
// we are referencing baseNumber here even though it was declared
// outside of this function. Closures allow us to do this in JavaScript
return baseNumber + N;
};
}

var addSix = createBase(6);
addSix(10);
addSix(21);

Q8: 请解释 JavaScript 中的值和类型

话题: JavaScript
难度: ⭐⭐

下面是 JavaScript 内置的可用类型:

  • string
  • number
  • boolean
  • null 和 undefined
  • object
  • symbol (ES6 的新语法)

Q9: 请解释事件冒泡以及如何阻止它?

话题: JavaScript
难度: ⭐⭐

事件冒泡的概念是指:在最内层的元素上绑定的事件被触发后,会按照嵌套的层次由内向外逐步触发。因此,点击某个孩子节点可能会触发父节点的事件。

一个阻止事件冒泡的办法就是使用event.stopPropagation(),在 IE<9 的浏览器上使用event.cancelBubble

function stopPropagation(evt) {
if (typeof evt.stopPropagation == "function") {
evt.stopPropagation();
} else {
evt.cancelBubble = true;
}
}

来源:https://github.com/kennymkchan

Q10. 请解释 JavaScript 中的 let 关键字

话题: JavaScript
难度: ⭐⭐

ES6 允许你使用 let 关键字来申明块作用域({...})的变量。

来源: github.com/getify

Q11: 如何检查一个数字是否是整数?

话题: JavaScript
难度: ⭐⭐

一个最简单的方法是判断除以 1 的余数是否为 0.

function isInt(num) {
return num % 1 === 0;
}

console.log(isInt(4)); // true
console.log(isInt(12.2)); // false
console.log(isInt(0.3)); // false

来源: coderbyte.com

Q12: 什么叫 IIFEs(Immediately Invoked Function Expressions)?

话题: JavaScript
难度: ⭐⭐

IIFE 叫做立即执行表达式,顾名思义,该表达式一被创建就立即执行。

(function IIFE() {
console.log("Hello!");
})();
// "Hello!"

该方法常用语避免污染全局的命名空间,因为所以在 IIFE 中使用的变量外部都无法访问。

来源: stackoverflow.com

Q13: 如果比较 JavaScript 中的两个对象?

话题: JavaScript
难度: ⭐⭐

两个非基本类型的值,比如对象(包括函数和数组)都是通过引用的形式来访问。如果直接通过=====来判断,那么只会简单的判断其引用地址是否相同,而不是它们实际对应的值。

如果数组和字符串做比较,那么数组会通过逗号拼接转换为字符串。通过等号判断的时候,两个相同的数组不会相等,但是和相同数据的字符串比较反而相等。

var a = [1, 2, 3];
var b = [1, 2, 3];
var c = "1,2,3";

a == c; // true
b == c; // true
a == b; // false

如果要深度比较,可以使用第三方库,比如deep-equal或则你自己实现一个比较算法。

Q14: 请解释 ES5 和 ES6 的不同点

话题: JavaScript
难度: ⭐⭐⭐

  • ECMAScript 5 (ES5): 第 5 个 ECMAScript 版本,于 2009 年标准化。该标准几乎所有的浏览器都完全支持。
  • ECMAScript 6 (ES6)/ECMAScript 2015 (ES2015): 第 6 个 ECMAScript 版本,于 2015 年标准化。目前各大浏览器还只是部分支持。

接下来介绍它们主要的区别:

  • 箭头函数和字符串嵌入:
const greetings = name => {
return `hello ${name}`;
};

甚至:

const greetings = name => `hello ${name}`;
  • 常量声明(Const): 如同其它编程语言中的常量一样,但又有不同。这里的const代表了constant reference。也就是说,你可以修改其指向的对象的值。但是你不能修改其 reference 的值。
const NAMES = [];
NAMES.push("Jim");
console.log(NAMES.length === 1); // true
NAMES = ["Steve", "John"]; // error
  • 块作用域变量:ES6 中的新关键字let允许允许开发者将变量的作用域限定在块级别。不会像var一样变量提升。
  • 参数默认值:允许在函数定义的时候指定默认的值。
// Basic syntax
function multiply(a, b = 2) {
return a * b;
}
multiply(5); // 10
  • 类定义和继承

ES6 开始支持定义类(使用class关键字),构造函数(使用constructor关键字),和extend关键字来实现继承。

  • for-of 操作

for...of语句用来迭代访问一个对象的所有属性。

  • Spread 操作符:用于对象合并
const obj1 = { a: 1, b: 2 };
const obj2 = { a: 2, c: 3, d: 4 };
const obj3 = { ...obj1, ...obj2 };
  • Promise: Promises 提供了一个处理异步操作的方法。你可以用回调函数来实现,但是 Promise 更加简洁和可读。
const isGreater = (a, b) => {
return new Promise((resolve, reject) => {
if (a > b) {
resolve(true);
} else {
reject(false);
}
});
};
isGreater(1, 2)
.then(result => {
console.log("greater");
})
.catch(result => {
console.log("smaller");
});
  • 模块的 export 和 import。
const myModule = {
x: 1,
y: () => {
console.log("This is ES5");
}
};
export default myModule;
import myModule from "./myModule";

来源: Bulby.io

Q15: 请解释undefinednot defined的区别

话题: JavaScript
难度: ⭐⭐⭐

在 JavaScript 中,如果你尝试使用不存在的还未申明的变量,JavaScript 会抛出错误var name is not defined。但是如果你用typeof来查看其类型,会返回undefined

我们先来澄清一下声明和定义的区别:var x是一个声明,因为你并没有定义其具体的值,你只是声明其存在性。

var x; // declaring x
console.log(x); //output: undefined

var x = 1同时兼具声明和定义,我们也可以叫它初始化。在 JavaScript 中,每一个变量和函数声明都会被提升到顶部。

如果我们访问一个声明了但是未定义的变量,会返回undefined

var x; // Declaration
if(typeof x === 'undefined') // Will return true

访问一个未声明未定义的变量,会返回 not defined 错误。

console.log(y); // Output: ReferenceError: y is not defined

来源: stackoverflow.com

Q16: 匿名函数和命名函数的区别?

话题: JavaScript
难度: ⭐⭐⭐

var foo = function() {
// anonymous function assigned to variable foo
// ..
};

var x = function bar() {
// named function (bar) assigned to variable x
// ..
};

foo(); // actual function execution
x();

译者补充:匿名函数如果不赋值给某个变量,则无法被调用了;命名函数再次被赋值不是多此一举么。

Q17: JavaScript 中闭包是什么?请提供一个例子

话题: JavaScript
难度: ⭐⭐⭐⭐

闭包是一个定义在其它函数(父函数)里面的函数,它拥有对父函数里面变量的访问权。闭包拥有如下三个作用域的访问权:

  • 自身的作用域
  • 父作用域
  • 全局作用域
var globalVar = "abc";

// Parent self invoking function
(function outerFunction(outerArg) {
// begin of scope outerFunction
// Variable declared in outerFunction function scope
var outerFuncVar = "x";
// Closure self-invoking function
(function innerFunction(innerArg) {
// begin of scope innerFunction
// variable declared in innerFunction function scope
var innerFuncVar = "y";
console.log(
"outerArg = " +
outerArg +
"\