37个基本的JavaScript面试问题 *

最好的JavaScript开发人员和工程师可以回答的全部基本问题. 在我们社区的推动下,我们鼓励专家提交问题并提供反馈.

现在就聘请一名顶级JavaScript开发人员
Toptal logo是顶级自由软件开发人员的专属网络吗, designers, finance experts, product managers, 和世界上的项目经理. 顶级公司雇佣Toptal自由职业者来完成他们最重要的项目.

面试问题

1.

使用时潜在的陷阱是什么 Typeof bar === "object" to determine if bar is an object? 如何避免这个陷阱呢?

View answer

Although Typeof bar === "object" is 一种可靠的检查方法 bar 是一个对象,JavaScript中令人惊讶的问题是 null is also 被认为是一个对象!

因此,令大多数开发人员惊讶的是,下面的代码将记录日志 true (not false) to the console:

var bar = null;
console.log(Typeof bar === "object");  // logs true!

只要一个人意识到这一点,这个问题就可以很容易地通过检查来避免 bar is null:

console.log((bar !== null) && (Typeof bar === "object"));  // logs false

为了让我们的回答完全彻底,还有两件事值得注意:

首先,上面的解决方案将返回 false if bar is a function. 在大多数情况下,这是期望的行为,但在您还希望返回的情况下 true 对于函数,您可以将上述解决方案修改为:

console.log((bar !== null) && ((typeof酒吧= = =“对象”)| | (typeof酒吧= = =“函数”)));

第二,上述解决方案将返回 true if bar is an array (e.g., if var bar = [];). In most cases, 这是期望的行为, 因为数组实际上是对象, 但如果你想 false 对于数组,你可以将上面的解决方案修改为:

console.log((bar !== null) && (Typeof bar === "object") && (toString.call(bar) !== "[对象数组]"));

然而,还有另一种选择返回 false 对于空值、数组和函数,但是 true for objects:

console.log((bar !== null) && (bar.构造函数===对象));

或者,如果你正在使用jQuery:

console.log((bar !== null) && (Typeof bar === "object") && (! $.isArray(bar)));

ES5使数组的情况非常简单,包括它自己的null检查:

console.log(Array.isArray(bar));
2.

下面的代码将输出什么到控制台,为什么?

(function(){
  var a = b = 3;
})();

console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));
View answer

Since both a and b 在函数的封闭范围内定义的, 因为他们所在的这条线是以 var 关键字,大多数JavaScript开发人员都会期望 typeof a and typeof b to both be undefined 在上面的例子中.

However, that is not the case. 这里的问题是,大多数开发者 incorrectly 理解语句 var a = b = 3; 简写:

var b = 3;
var a = b;

But in fact, var a = b = 3; 实际上是:

b = 3;
var a = b;

因此(如果你是的话) not 使用严格模式),代码片段的输出将是:

a defined? false
b defined? true

But how can b be defined outside 封闭函数作用域的? 从声明开始 var a = b = 3; 是语句的简写吗 b = 3; and var a = b;, b 最终成为一个全局变量(因为它前面没有 var 关键字),因此即使在封闭函数之外也仍然在作用域中.

请注意,在严格模式下(i.e., with use strict), the statement var a = b = 3; 会生成一个运行时错误 ReferenceError: b没有定义,从而避免可能导致的任何错误/错误. (这也是为什么你应该使用 use strict 在您的代码中,这是理所当然的!)

3.

下面的代码将输出什么到控制台,为什么?

var myObject = {
    foo: "bar",
    func: function() {
        var self = this;
        console.Log ("outer function: this ..foo = " + this.foo);
        console.外部函数:self.foo = " + self.foo);
        (function() {
            console.Log("内部函数:this。.foo = " + this.foo);
            console.日志("内部函数:self.foo = " + self.foo);
        }());
    }
};
myObject.func();
View answer

以上代码将向控制台输出以下内容:

outer func:  this.foo = bar
outer func:  self.foo = bar
inner func:  this.foo = undefined
inner func:  self.foo = bar

在外部函数中,两者都有 this and self refer to myObject 因此两者都可以适当地引用和访问 foo.

在内部函数中, this 不再指 myObject. As a result, this.foo 在内部函数中未定义,而对局部变量的引用 self 仍然在范围内,并且可以在那里访问.

申请加入Toptal的发展网络

并享受可靠、稳定、远程 自由JavaScript开发者职位

申请成为自由职业者
4.

的意义是什么, and reason for, 将JavaScript源文件的整个内容包装在函数块中?

View answer

这是一种越来越普遍的做法,被许多流行的JavaScript库(jQuery、Node.js, etc.). 该技术在文件的整个内容周围创建一个闭包, 也许最重要的是, 创建一个私有命名空间,从而有助于避免不同JavaScript模块和库之间潜在的名称冲突.

这种技术的另一个特点是允许为全局变量提供易于引用的别名(可能更短). 例如,在jQuery插件中经常使用这种方法. jQuery允许您禁用 $ 引用jQuery命名空间,使用 jQuery.noConflict(). 如果这样做了,您的代码仍然可以使用 $ 使用这种闭包技术,如下所示:

(function($) {/* jQuery插件代码引用$ */})(jQuery);
5.

它的意义是什么?它的好处是什么 'use strict' 在JavaScript源文件的开头?

View answer

这里简短而重要的答案是 use strict 是一种在运行时对JavaScript代码强制执行更严格的解析和错误处理的方法吗. 原本会被忽略或以静默方式失败的代码错误现在将生成错误或抛出异常. 总的来说,这是一个很好的实践.

严格模式的一些主要好处包括:

  • 使调试更容易. 原本会被忽略或以静默方式失败的代码错误现在将生成错误或抛出异常, 提醒您更快地发现代码中的问题,并指导您更快地找到问题的来源.
  • 防止意外的全局变量. 没有严格模式, 将值赋给未声明的变量会自动创建一个具有该名称的全局变量. 这是JavaScript中最常见的错误之一. 在严格模式下,尝试这样做会抛出错误.
  • Eliminates this coercion. 在没有严格模式的情况下,对 this null或undefined的值将自动强制到全局. 这可能会导致许多头假和拔头发的虫子. 在严格模式下,引用a this 值为空或未定义将抛出错误.
  • 禁止重复的参数值. 严格模式在检测到函数的重复命名参数时抛出错误.g., 函数foo(val1, val2, val1){}), 从而捕获代码中几乎肯定存在的错误,否则您可能会浪费大量时间来跟踪这些错误.
    • 注意:过去(在ECMAScript 5中)严格模式不允许重复的属性名(例如.g. Var object = {foo: "bar", foo: "baz"};) but 从ECMAScript 2015开始 现在情况已经不同了.
  • 使eval()更安全. 在方式上有一些不同 eval() 在严格模式和非严格模式下运行. 最重要的是,在严格模式下,在类中声明的变量和函数 eval() statement are not 在包含范围(they)中创建 are 在包含范围内以非严格模式创建,这也可能是问题的常见来源).
  • 的无效使用引发错误 delete. The delete 操作符(用于从对象中删除属性)不能用于对象的不可配置属性. 当尝试删除非可配置属性时,非严格代码将静默失败, 而严格模式在这种情况下会抛出错误.
6.

考虑下面的两个函数. 它们会返回相同的东西吗? Why or why not?

function foo1()
{
  return {
      bar: "hello"
  };
}

function foo2()
{
  return
  {
      bar: "hello"
  };
}
View answer

令人惊讶的是,这两个函数可以 not 返回相同的东西. Rather:

console.日志(“foo1回报:”);
console.log(foo1());
console.日志(“foo2回报:”);
console.log(foo2());

will yield:

foo1 returns:
对象{bar: "hello"}
foo2 returns:
undefined 

这不仅令人惊讶,而且使它特别粗糙的是 foo2() 返回未定义而不抛出任何错误.

这样做的原因是分号在JavaScript中是可选的(尽管省略它们通常是很糟糕的形式). 因此,当行中包含 return 语句(行上没有其他内容)在 foo2(),在返回语句后立即自动插入分号.

由于代码的其余部分完全有效,因此不会抛出错误, 即使它从来没有被调用或做任何事情(它只是一个未使用的代码块,定义了一个属性) bar 哪个等于字符串 "hello").

这种行为也支持在JavaScript中遵循在行尾放置左花括号的惯例, 而不是在新一行的开头. 如图所示,这在JavaScript中不仅仅是一种风格偏好.

7.

下面的代码将输出什么? 解释你的答案.

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

对于这个问题,一个有教养的回答应该是:“你无法确定. it might print out 0.3 and true, or it might not. JavaScript中的数字都以浮点精度处理, and as such, 可能并不总是产生预期的结果.”

上面提供的示例是演示此问题的经典案例. 令人惊讶的是,它将打印出:

0.30000000000000004
false

一个典型的解决方案是用特殊常数比较两个数之间的绝对差 Number.EPSILON:

函数areTheNumbersAlmostEqual(num1, num2) {
	return Math.abs( num1 - num2 ) < Number.EPSILON;
}
console.日志(areTheNumbersAlmostEqual (0.1 + 0.2, 0.3));
8.

当执行下面的代码时,以什么顺序将数字1-4记录到控制台? Why?

(function() {
    console.log(1); 
    setTimeout(函数(){控制台.log(2)}, 1000); 
    setTimeout(函数(){控制台.log(3)}, 0); 
    console.log(4);
})();
View answer

这些值将按以下顺序记录:

1
4
3
2

让我们先解释一下其中可能更明显的部分:

  • 1 and 4 首先显示,因为它们是通过简单调用记录的 console.log() without any delay

  • 2 后显示 3 because 2 在延迟1000毫秒后正在被记录(i.e., 1秒)然而 3 是否在延迟0毫秒后被记录.

OK, fine. But if 3 在0毫秒的延迟后被记录,这是否意味着它正在被记录? 如果是这样,不应该记录下来吗 before 4, since 4 是否由后面的代码行记录?

答案与正确的理解有关 JavaScript事件和定时.

浏览器有一个事件循环,它检查事件队列并处理挂起的事件. 例如,如果一个事件发生在后台(e.g., a script onload 事件),而浏览器正忙(例如.g., processing an onclick),事件被附加到队列中. 当onclick处理程序完成时,将检查队列,然后处理事件.g., the onload 执行脚本).

Similarly, setTimeout() 如果浏览器繁忙,还将其引用函数的执行放入事件队列.

将值作为第二个参数传递给 setTimeout(),它尝试“尽快”执行指定的函数。. 具体地说,函数的执行被放置在事件队列中,以便在下一个计时器滴答时发生. 不过请注意,这是 not immediate; the function is not executed until the next tick. 这就是为什么在上面的例子中,调用 console.log(4) 的调用之前发生 console.log(3) (自从call to console.log(3) 是通过setTimeout调用的,所以会稍微延迟).

9.

编写一个简单的函数(少于160个字符),返回一个布尔值,指示字符串是否为a palindrome.

View answer

下面的一行函数将返回 true if str is a palindrome; otherwise, it returns false.

函数isPalindrome(str) {
  str = str.替换(\ W / g,”).toLowerCase();
  返回(str == str).split('').reverse().join(''));
}

For example:

console.log(isPalindrome("level"));                   // logs 'true'
console.log(isPalindrome("levels"));                  // logs 'false'
console.log(isPalindrome("A car, a man, a maraca"));  // logs 'true'
10.

Write a sum 方法,该方法在使用下面任何一种语法调用时都能正常工作.

console.log(sum(2,3));   // Outputs 5
console.log(sum(2)(3));  // Outputs 5
View answer

有(至少)两种方法可以做到这一点:

METHOD 1

function sum(x) {
  if (arguments.length == 2) {
    返回参数[0]+参数[1];
  } else {
    return function(y) { return x + y; };
  }
}

在JavaScript中,函数提供对对象的访问 arguments 对象,该对象提供对传递给函数的实际参数的访问. 这使我们能够使用 length 属性以确定在运行时传递给函数的参数数量.

如果传递了两个参数,则简单地将它们相加并返回.

否则,我们假定它是在表单中调用的 sum(2)(3),因此返回一个匿名函数,该函数将传递给的参数相加 sum() (在本例中为2)和传递给匿名函数的参数(在本例中为3).

METHOD 2

函数sum(x, y) {
  if (y !== undefined) {
    return x + y;
  } else {
    return function(y) { return x + y; };
  }
}

调用函数时, JavaScript不要求参数的数量与函数定义中的参数数量相匹配. 如果传递的参数数量超过函数定义中的参数数量, 多余的参数将被忽略. On the other hand, 如果传递的参数数量少于函数定义中的参数数量, 缺失的参数的值为 undefined 在函数中引用时. So, 在上面的例子中, 通过简单地检查第二个参数是否未定义, 我们可以确定调用函数的方式并进行相应的处理.

11.

考虑下面的代码片段:

for (var i = 0; i < 5; i++) {
  Var BTN = document.createElement(“按钮”);
  btn.列表末尾(文档.createTextNode('Button ' + i));
  btn.addEventListener('click', function(){控制台.log(i); });
  document.body.appendChild(btn);
}

(a)当用户点击“按钮4”时,会记录到控制台的内容以及原因?

(b)提供一个或多个可按预期工作的替代实现.

View answer

(a)无论用户点击哪个按钮,数字5都会 always 登录到控制台. 这是因为,在点上 onclick 方法被调用(为 any 的按钮),的 for 循环已经完成,变量 i 已经是5了. (如果被采访者足够了解如何谈论执行上下文,那么他们将获得加分, variable objects, activation objects, 内部的“scope”属性有助于闭包行为.)

(b)做到这一点的关键是抓住……的价值 i 在每一个通过 for 通过将其传递给新创建的函数对象来循环. 以下是实现这一目标的四种可能方法:

for (var i = 0; i < 5; i++) {
  Var BTN = document.createElement(“按钮”);
  btn.列表末尾(文档.createTextNode('Button ' + i));
  btn.addEventListener('click', (function(i)) {
    返回函数(){控制台.log(i); };
  })(i));
  document.body.appendChild(btn);
}

或者,您可以将整个调用包装为 btn.addEventListener 在新的匿名函数中:

for (var i = 0; i < 5; i++) {
  Var BTN = document.createElement(“按钮”);
  btn.列表末尾(文档.createTextNode('Button ' + i));
  (function (i) {
    btn.addEventListener('click', function(){控制台.log(i); });
  })(i);
  document.body.appendChild(btn);
}

或者,我们可以替换 for 循环调用数组对象的native forEach method:

['a', 'b', 'c', 'd', 'e'].forEach(function (value, i) {
  Var BTN = document.createElement(“按钮”);
  btn.列表末尾(文档.createTextNode('Button ' + i));
  btn.addEventListener('click', function(){控制台.log(i); });
  document.body.appendChild(btn);
});

最后,最简单的解决方案,如果你在ES6/ES2015上下文中,是使用 let i instead of var i:

for (let i = 0; i < 5; i++) {
  Var BTN = document.createElement(“按钮”);
  btn.列表末尾(文档.createTextNode('Button ' + i));
  btn.addEventListener('click', function(){控制台.log(i); });
  document.body.appendChild(btn);
}
12.

Assuming d 是作用域中的“空”对象,例如:

var d = {};

使用下面的代码完成?

['斑马','马'].forEach(函数(k) {
	d[k] = undefined;
});
View answer

上面显示的代码片段在对象上设置了两个属性 d. 理想情况下,对带有unset键的JavaScript对象执行的任何查找计算结果为 undefined. 但是运行这段代码将这些属性标记为对象的“自有属性”.

这是一种确保对象具有给定属性集的有用策略. 将此对象传递给 Object.keys 将返回一个数组与这些设置键以及(即使他们的值是 undefined).

13.

下面的代码将输出什么到控制台,为什么?

var arr1 = "john".split('');
var arr2 = arr1.reverse();
Var arr3 = "jones".split('');
arr2.push(arr3);
console.Log(“数组1:长度=”+ arr1.长度+ " last=" + arr1.slice(-1));
console.Log("数组2:长度=" + arr2.长度+ " last=" + arr2.slice(-1));
View answer

记录的输出将是:

"数组1:length=5 last=j, 0,n,e,s"
"数组2:length=5 last=j, 0,n,e,s"

arr1 and arr2 are the same (i.e. ['n','h','o','j', ['j','o','n','e','s']]),原因如下:

  • 调用数组对象的 reverse() 方法不仅 return 数组倒序,它也倒序数组 itself (i.e., in this case, arr1).

  • The reverse() 方法返回对数组本身的引用.e., in this case, arr1). As a result, arr2 只是引用(而不是复制) arr1. 因此,当做任何事情的时候 arr2 (i.e., when we invoke arr2.push(arr3);), arr1 会受到影响吗 arr1 and arr2 仅仅是对同一个对象的引用吗.

在回答这个问题时,这里有几个侧面的观点有时会让人出错:

  • 将数组传递给 push() 方法推入那个 entire array as a single 元素放到数组的末尾. 结果,声明 arr2.push(arr3); adds arr3 在其整体作为一个单一的元素结束 arr2 (i.e., it does not 连接两个数组,这就是 concat() method is for).

  • 与Python一样,JavaScript在调用数组方法时也使用负下标 slice() as a way of referencing elements at the end of the array; e.g.,下标-1表示数组中的最后一个元素,依此类推.

14.

下面的代码将输出什么到控制台,为什么 ?

console.Log (1 + 2 + 2);
console.Log(1 + +“2”+“2”);
console.Log(1 + -“1”+“2”);
console.Log (+"1" +"1" +" 2");
console.log("A" - "B" + "2");
console.log("A" - "B" + 2);
View answer

以上代码将向控制台输出以下内容:

"122"
"32"
"02"
"112"
"NaN2"
NaN

Here’s why…

这里的基本问题是JavaScript (ECMAScript)是一种松散类型语言,它对值执行自动类型转换以适应正在执行的操作. 让我们通过上面的每个例子来看看这是如何实现的.

Example 1: 1 + "2" + "2" Outputs: "122" Explanation: 中执行的第一个操作 1 + "2". 因为其中一个操作数("2") is a string, JavaScript假定它需要执行字符串连接,因此转换的类型 1 to "1", 1 + "2" yields "12". Then, "12" + "2" yields "122".

Example 2: 1 + +"2" + "2" Outputs: "32" Explanation: 根据操作顺序,第一个要执行的操作是 +"2" (the extra + before the first "2" 被视为一元操作符). 因此,JavaScript将的类型转换为 "2" ,然后应用一元数 + sign to it (i.e.,将其视为正数). 因此,下一个操作是现在 1 + 2 这当然会产生 3. 但是,我们有一个数字和字符串(i)之间的操作.e., 3 and "2"), 因此,JavaScript再次将数值类型转换为字符串并执行字符串连接, yielding "32".

Example 3: 1 + -"1" + "2" Outputs: "02" Explanation: 这里的解释与前面的示例相同,除了一元操作符是 - rather than +. So "1" becomes 1,然后变成 -1 when the - 应用,然后将其添加到 1 yielding 0,然后将其转换为字符串并与final "2" operand, yielding "02".

Example 4: +"1" + "1" + "2" Outputs: "112" Explanation: Although the first "1" 操作数类型转换为基于一元数的数值 + 前面的运算符, 然后,当它与第二个字符串连接时,它会立即转换回字符串 "1" 操作数,然后将其与最终的 "2" 操作数,产生字符串 "112".

Example 5: "A" - "B" + "2" Outputs: "NaN2" Explanation: Since the - 操作符不能应用于字符串,因为两者都不能 "A" nor "B" 可以转换为数值, "A" - "B" yields NaN 然后将其与字符串连接 "2" to yield “NaN2”.

Example 6: "A" - "B" + 2 Outputs: NaN Explanation: 如前面示例所述, "A" - "B" yields NaN. 但是,将任何操作符应用于NaN和任何其他数字操作数仍然会产生结果 NaN.

15.

如果数组列表太大,下面的递归代码将导致堆栈溢出. 如何在保留递归模式的同时解决这个问题?

var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        //处理列表项...
        nextListItem();
    }
};
View answer

方法可以避免潜在的堆栈溢出 nextListItem 功能如下:

var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        //处理列表项...
        setTimeout(nextListItem, 0);
    }
};

堆栈溢出被消除,因为事件循环处理递归,而不是调用堆栈. When nextListItem runs, if item 不为空时,超时函数(nextListItem)被推入事件队列,函数退出,从而清空调用堆栈. 当事件队列运行其超时事件时,下一个 item 被处理,并将计时器设置为再次调用 nextListItem. Accordingly, 该方法从头到尾进行处理,不需要直接递归调用, 因此调用堆栈保持清空, 不管迭代的次数.

16.

什么是JavaScript中的“闭包”? 提供一个示例.

View answer

闭包是一个内部函数,它可以访问外部(封闭)函数作用域链中的变量. The closure has access to variables in three scopes; specifically: (1) variable in its own scope, (2)封闭函数作用域内的变量, (3)全局变量.

下面是一个例子:

var globalVar = "xyz";

(function outerFunc(outerArg) {
    var outerVar = 'a';
    
    (函数innerFunc(innerArg) {
    var innerVar = 'b';
    
    console.log(
        "outerArg = " + outerArg + "\n" +
        "innerArg = " + innerArg + "\n" +
        "outerVar = " + outerVar + "\n" +
        "innerVar = " + innerVar + "\n" +
        "globalVar = " + globalVar);
    
    })(456);
})(123);

在上面的示例中,来自的变量 innerFunc, outerFunc,全局命名空间为 all in scope in the innerFunc. 因此,上述代码将产生以下输出:

outerArg = 123
innerArg = 456
outerVar = a
innerVar = b
globalVar = xyz
17.

下面的代码行会向控制台输出什么?

console.Log ("0 || 1 = "+(0 || 1));
console.Log ("1 || 2 = "+(1 || 2));
console.log("0 && 1 = "+(0 && 1));
console.log("1 && 2 = "+(1 && 2));

解释你的答案.

View answer

代码将输出以下四行:

0 || 1 = 1
1 || 2 = 1
0 && 1 = 0
1 && 2 = 2

在JavaScript中,两者都是 || and && 逻辑运算符返回第一个完全确定的“逻辑值”时,从左到右评估.

The or (||) operator. 在形式的表达式中 X||Y, X 是否首先评估并解释为布尔值. 如果这个布尔值是 true, then true (1)返回,且 Y 因为“或”条件已经满足,所以不求值. 但是,如果这个布尔值是“false”,我们仍然不知道 X||Y 在求值之前是真还是假 Y,并将其解释为布尔值.

Accordingly, 0 || 1 求值为true(1),也一样 1 || 2.

The and (&&) operator. 在形式的表达式中 X&&Y, X 是否首先评估并解释为布尔值. 如果这个布尔值是 false, then false (0)返回,并且 Y 因为“and”条件已经失败,所以没有被求值. 但是,如果这个布尔值为“真”,我们仍然不知道 X&&Y 在求值之前是真还是假 Y,并将其解释为布尔值.

然而,有趣的是 && 操作符是当表达式求值为“true”时,则返回表达式本身. This is fine, 因为它在逻辑表达式中被视为“真”, 但也可以用于返回该值,当您愿意这样做时. 这就解释了为什么有些令人惊讶的是, 1 && 2 返回2(而您可能期望它返回) true or 1).

18.

当执行以下代码时,输出将是什么? Explain.

console.log(false == '0')
console.日志(false === '0')
View answer

代码将输出:

true
false

在JavaScript中,有两组相等操作符. 三等号运算符 === 如果两侧的两个表达式具有相同的类型和相同的值,则其行为与任何传统的相等操作符一样,计算结果为true. 然而,双相等操作符在比较两个值之前试图强制它们. 因此,通常使用 === rather than ==. 这同样适用于 !== vs !=.

19.

下面代码的输出是什么? 解释你的答案.

var a={},
    b={key:'b'},
    c={key:'c'};

a[b]=123;
a[c]=456;

console.log(a[b]);
View answer

这段代码的输出将是 456 (not 123).

这样做的原因如下:当设置对象属性时,JavaScript将隐式地 stringify 参数值. 在这种情况下,since b and c 都是对象吗 both be converted to "[object Object]". As a result, a[b] anda[c] 都等价于 (“[对象对象]”) 并且可以互换使用. 因此,设置或引用 a[c] 与设置或引用完全相同吗 a[b].

20.

下面的代码将输出到控制台:

console.log((function f(n){return ((n > 1) ? N * f(N -1): N)})(10);

解释你的答案.

View answer

代码将输出10的阶乘(i)的值.e., 10!, or 3,628,800).

Here’s why:

The named function f() 递归地调用自己,直到它被调用 f(1) 它只是返回 1. 因此,这就是它的作用:

F(1):返回n,即1
F(2):返回2 * F(1),即2
F(3):返回3 * F(2),即6
F(4):返回4 * F(3),即24
F(5):返回5 * F(4),即120
F(6):返回6 * F(5),即720
F(7):返回7 * F(6),即5040
F(8):返回8 * F(7),即40320
F(9):返回9 * F(8),即362880
F(10):返回10 * F(9),即3628800
21.

考虑下面的代码片段. 控制台输出将是什么,为什么?

(function(x) {
    返回(function(y) {
        console.log(x);
    })(2)
})(1);
View answer

输出将是 1,尽管价值 x 从来没有在内部函数中设置. Here’s why:

正如我们的 JavaScript招聘指南, a closure is a function, 以及创建闭包时范围内的所有变量或函数. In JavaScript, a closure is implemented as an “inner function”; i.e.在另一个函数体内定义的函数. 闭包的一个重要特性是内部函数仍然可以访问外部函数的变量.

因此,在本例中,since x 在内部函数中没有定义, 外部函数的作用域搜索一个已定义的变量 x,其值为 1.

22.

下面的代码将输出什么到控制台?为什么?

var hero = {
    _name: 'John Doe';
    getSecretIdentity:函数(){
        return this._name;
    }
};

var stoleSecretIdentity =英雄.getSecretIdentity;

console.日志(stoleSecretIdentity ());
console.log(hero.getSecretIdentity ());

这段代码的问题是什么,如何修复.

View answer

代码将输出:

undefined
John Doe

The first console.log prints undefined 因为我们是从 hero object, so stoleSecretIdentity () 在全局上下文中被调用(i.e.(窗口对象),其中 _name 属性不存在.

One way to fix the stoleSecretIdentity () 功能如下:

var stoleSecretIdentity =英雄.getSecretIdentity.bind(hero);
23.

创建一个函数,给定页面上的DOM元素,该函数将访问元素本身和 all 它的后代(不仅仅是它的直系子女). 对于访问的每个元素,函数应该将该元素传递给提供的回调函数.

函数的参数应该是:

  • a DOM element
  • 回调函数(以DOM元素作为参数)
View answer

访问树(DOM)中的所有元素是一种经典方法 深度优先搜索算法 application. 下面是一个示例解决方案:

函数Traverse(p_element,p_callback) {
   p_callback (p_element);
   Var list = p_element.children;
   for (var i = 0; i < list.length; i++) {
       Traverse(list[i],p_callback);  // recursive call
   }
}
24.

Testing your this JavaScript知识:下面代码的输出是什么?

var length = 10;
function fn() {
	console.log(this.length);
}

var obj = {
  length: 5,
  方法:function(fn) {
    fn();
    arguments[0]();
  }
};

obj.method(fn, 1);
View answer

Output:

10
2

Why isn’t it 10 and 5?

首先,as fn 作为参数传递给函数 method, the scope (this) of the function fn is window. var length = 10; 是在 window level. 它也可以作为 window.length or length or this.length (when this === window.)

method is bound to Object obj, and obj.method 带参数调用 fn and 1. Though method 是否只接受一个参数, while invoking it has passed two parameters; the first is a function callback and other is just a number.

When fn() is called inside method,它在全局级作为参数传递给函数, this.length 都能接触到 var length = 10 (全局声明)不 length = 5 as defined in Object obj.

方法访问JavaScript函数中任意数量的参数 arguments[] array.

Hence arguments[0]() 只是召唤 fn(). Inside fn 现在,这个函数的作用域变成 arguments 数组,并记录的长度 arguments[] will return 2.

因此输出将如上所示.

25.

考虑下面的代码. 输出将是什么,为什么?

(function () {
    try {
        throw new Error();
    } catch (x) {
        var x = 1, y = 2;
        console.log(x);
    }
    console.log(x);
    console.log(y);
})();
View answer
1
undefined
2

var 语句被提升(没有初始化它们的值)到它所属的全局作用域或函数作用域的顶部, 即使是在a里面 with or catch block. 但是,错误标识符只能在 catch block. 它相当于:

(function () {
    var x, y; // outer and hoisted
    try {
        throw new Error();
    } catch (x /* inner */) {
        x = 1; // inner x, not the outer one
        y = 2; // there is only one y, which is in the outer scope
        console.Log (x /* inner */);
    }
    console.log(x);
    console.log(y);
})();
26.

这段代码的输出是什么?

var x = 21;
Var girl = function () {
    console.log(x);
    var x = 20;
};
girl ();
View answer

既不是21也不是20,结果是 undefined

这是因为JavaScript初始化没有被提升.

(为什么它不显示全局值21? 原因是当函数执行时,它检查是否存在一个局部 x 变量存在,但尚未声明它,因此它不会寻找全局变量.)

27.
for (let i = 0; i < 5; i++) {
  setTimeout(function(){控制台.log(i); }, i * 1000 );
}

这段代码将打印什么?

View answer

It will print 0 1 2 3 4, because we use let instead of var here. The variable i 只在 for 循环的块作用域.

28.

下面的代码行输出什么,为什么?

console.log(1 < 2 < 3);
console.log(3 > 2 > 1);
View answer

第一条语句返回 true 这是意料之中的.

The second returns false 因为引擎是如何处理算子结合律的 < and >. 它比较左和右,所以 3 > 2 > 1 JavaScript翻译为 true > 1. true has value 1,然后进行比较 1 > 1, which is false.

29.

如何在数组的开头添加元素? 怎么在最后加一个呢?

View answer
var myArray = ['a', 'b', 'c', 'd'];
myArray.push('end');
myArray.unshift('start');
console.log(myArray); // ["start", "a", "b", "c", "d", "end"]

在ES6中,可以使用扩展操作符:

myArray = ['start', ...myArray];
myArray = [...myArray, 'end'];

Or, in short:

myArray = ['start', ...myArray, 'end'];
30.

假设你有这样的代码:

Var a = [1,2,3];

a)这会导致崩溃吗?

a[10] = 99;

b)输出结果是什么?

console.log(a[6]);
View answer

a)它不会崩溃. JavaScript引擎将使数组槽3到9为“空槽”.”

b) Here, a[6] will output undefined,但该槽仍然是空的,而不是填满 undefined. 在某些情况下,这可能是一个重要的细微差别. 例如,当使用 map(),空槽将保持为空 map()’s output, but undefined 将使用传递给它的函数重新映射slot:

Var b = [undefined];
b[2] = 1;
console.log(b);             // (3) [undefined, empty × 1, 1]
console.log(b.map(e => 7)); // (3) [7,         empty × 1, 7]
31.

的值是多少 typeof undefined == typeof NULL?

View answer

表达式将被求值为true,因为 NULL 将被视为任何其他未定义变量.

注意:JavaScript是区分大小写的,这里我们使用 NULL instead of null.

32.

下面的代码会返回什么?

console.Log (typeof typeof 1);
View answer

string

typeof 1 will return "number" and typeof "number" will return string.

33.

下面代码的输出是什么?

for (var i = 0; i < 5; i++) {
	setTimeout(function(){控制台.log(i); }, i * 1000 );
}

解释你的答案. 在这里使用闭包有什么帮助呢?

View answer

所示的代码示例将 not display the values 0, 1, 2, 3, and 4 as might be expected; rather, it will display 5, 5, 5, 5, and 5.

这样做的原因是在循环中执行的每个函数都会被执行 after 整个循环已经完成 all 因此,将参考 last value stored in i, which was 5.

Closures 可以通过为每个迭代创建唯一的范围来防止这个问题吗, 将变量的每个唯一值存储在其作用域中, as follows:

for (var i = 0; i < 5; i++) {
    (function(x) {
        setTimeout(function(){控制台.log(x); }, x * 1000 );
    })(i);
}

这将产生预期的结果,将0、1、2、3和4记录到控制台.

在ES2015上下文中,您可以简单地使用 let instead of var 在原始代码中:

for (let i = 0; i < 5; i++) {
	setTimeout(function(){控制台.log(i); }, i * 1000 );
}
34.

What is NaN? What is its type? 如何可靠地测试一个值是否等于 NaN?

View answer

The NaN 属性表示“非数字”的值。. 这个特殊的值是由于一个操作数是非数字而无法执行的操作(例如.g., "abc" / 4),或者因为操作的结果是非数字的.

虽然这看起来很简单, 有几个令人惊讶的特征 NaN 如果一个人没有意识到,这可能会导致拔毛虫.

不过,有一件事 NaN 意思是"非数字"它的类型是,信不信由你, Number:

console.log(typeof NaN === "number");  // logs "true"

Additionally, NaN 与任何事物相比——甚至与自身相比! – is false:

console.log(NaN === NaN);  // logs "false"

A semi-reliable 测试一个数字是否等于NaN的方法是使用内置函数 isNaN(), but even using isNaN() 是一个不完美的解决方案.

一个更好的解决方案是使用 value !== value, which would only 如果值等于NaN,则生成true. 此外,ES6还提供了一个新的 Number.isNaN() 函数,这是一个不同的,更可靠的比旧的全球 isNaN() function.

35.

下面的代码将输出什么?为什么?

var b = 1;
function outer(){
   	var b = 2
    function inner(){
        b++;
        var b = 3;
        console.log(b)
    }
    inner();
}
outer();
View answer

控制台的输出将是“3”。.

示例中有三个闭包,每个闭包都有自己的闭包 var b declaration. 当调用变量时,将按照从局部到全局的顺序检查闭包,直到找到实例. Since the inner closure has a b 变量,这就是将输出的内容.

此外,由于提升inner中的代码将被解释如下:

函数inner () {
    var b; // b is undefined
    b++; // b is NaN
    b = 3; // b is 3
    console.log(b); // output "3"
}
36.

讨论编写函数的可能方法 isInteger(x) 这就决定了 x is an integer.

View answer

这听起来可能微不足道,事实上,对于ECMAscript 6来说是微不足道的,它引入了一个新的 Number.isInteger() 函数正是为了这个目的. 但是,在ECMAScript 6之前,这有点复杂,因为没有等效的 Number.isInteger() 方法提供.

The issue is that, in the ECMAScript specification, integers only exist conceptually; i.e.,数值为 always 存储为浮点值.

考虑到这一点, 最简单、最干净 pre-ECMAScript-6解决方案(它也足够健壮,可以返回 false 即使是非数字值,如字符串或 null (传递给函数)将是按位异或运算符的如下用法:

function isInteger(x) { return (x ^ 0) === x; } 

下面的解决方案也可以工作,尽管没有上面的那么优雅:

函数isInteger(x){返回(typeof x === 'number') && (x % 1 === 0); }

下面的函数(或with) Math.ceil() or Math.floor() in place of Math.round())似乎也很有用,但结果与上面两个函数并不完全相同:

函数isInteger(x){返回数学.round(x) === x; }

区别在于,这些 Math基于解的返回 true for Infinity and -Infinity,而其他的(尤其是ES6) Number.isInteger()) return false.

另一个很常见的 incorrect 解决方案如下:

function isInteger(x) { return parseInt(x, 10) === x; }

While this parseInt基于的方法将会很有效 many values of x, once x 变得很大,它就不能正常工作了. 问题是 parseInt() 在解析数字之前将其第一个参数强制转换为字符串. Therefore, 一旦数量足够大, 它的字符串表示将以指数形式(e.g., 1e+21). Accordingly, parseInt() 然后尝试解析 1e+21,但是当它到达 e 字符,因此将返回的值 1. Observe:

> String(1000000000000000000000)
'1e+21'
> parseInt(1000000000000000000000, 10)
1
> parseInt(1000000000000000000000, 10) === 1000000000000000000000
false
37.

如何克隆对象?

View answer
Var obj = {a: 1,b: 2}
var objclone =对象.assign({},obj);

Now the value of objclone is {a: 1 ,b: 2} 但是指向不同的物体 obj.

不过,请注意潜在的陷阱: Object.assign() 我只做一个浅拷贝, not a deep copy. 这意味着嵌套对象不会被复制. 它们仍然引用与原来相同的嵌套对象:

let obj = {
    a: 1,
    b: 2,
    c: {
        age: 30
    }
};

var objclone =对象.assign({},obj);
console.日志('objclone: ', objclone);

obj.c.age = 45;
console.log('After Change - obj: ', obj);           // 45 - This also changes
console.log('After Change - objclone: ', objclone); // 45

面试不仅仅是棘手的技术问题, 所以这些只是作为一个指南. 并不是每一个值得雇佣的“A”候选人都能回答所有的问题, 回答所有问题也不能保证成为A级考生. 一天结束的时候, 招聘仍然是一门艺术,一门科学,需要大量的工作.

Why Toptal

厌倦了面试候选人? 不知道该问什么才能让你得到一份好工作?

让Toptal为你找到最合适的人.

现在就聘请一名顶级JavaScript开发人员

我们的独家网络JavaScript开发人员

希望找到一份JavaScript开发人员的工作?

让Toptal为你找到合适的工作.

申请成为JavaScript开发人员

工作机会从我们的网络

提出面试问题

提交的问题和答案将被审查和编辑, 并可能会或可能不会选择张贴, 由Toptal全权决定, LLC.

*所有字段均为必填项

寻找JavaScript开发人员?

Looking for JavaScript开发人员? 查看Toptal的JavaScript开发人员.

Jay Johnston

自由JavaScript开发者
United StatesToptal Member Since November 26, 2013

Coding HTML, CSS, 他从1997年参军时就开始学习JavaScript了, Jay喜欢通过电子商务解决方案为客户带来价值, 遗留集成, 以及优化的PHP和javascript驱动的应用程序. 他首选的DevOps环境是AWS, 他在关系数据库服务(RDS)方面有很强的技能(但不限于):, Redshift, Dynamo DB, 数据迁移服务(DMS), Lambda(无服务器和微服务), Cloudwatch, Cloudtrail, and Event Bridge.

Show More

Tyler Standley

自由JavaScript开发者
United StatesToptal Member Since August 3, 2018

同时具备较强的沟通技巧和模范的职业道德, Tyler带来了他对各种编程语言的实践经验. 不过,最近他的关注点转向了JavaScript库. 在他的职业生涯中, 他曾作为核心开发人员在多个敏捷团队中工作过,现在对任何与javascript相关的工作都很感兴趣.

Show More

Justin Michela

自由JavaScript开发者
United StatesToptal Member Since June 26, 2018

Justin是一名技术专业人士,对学习充满热情,拥有18年以上领导团队构建企业级分布式应用程序解决现实问题的经验. 贾斯汀坚信,企业的各个方面都需要合作, 从开发到市场再到销售, 是成功的必要条件吗.

Show More

Toptal连接 Top 3% 世界各地的自由职业人才.

加入Toptal社区.

Learn more