37个基本的JavaScript面试问题 *
最好的JavaScript开发人员和工程师可以回答的全部基本问题. 在我们社区的推动下,我们鼓励专家提交问题并提供反馈.
现在就聘请一名顶级JavaScript开发人员面试问题
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));
下面的代码将输出什么到控制台,为什么?
(function(){
var a = b = 3;
})();
console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));
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
在您的代码中,这是理所当然的!)
下面的代码将输出什么到控制台,为什么?
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();
以上代码将向控制台输出以下内容:
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开发者职位
这是一种越来越普遍的做法,被许多流行的JavaScript库(jQuery、Node.js, etc.). 该技术在文件的整个内容周围创建一个闭包, 也许最重要的是, 创建一个私有命名空间,从而有助于避免不同JavaScript模块和库之间潜在的名称冲突.
这种技术的另一个特点是允许为全局变量提供易于引用的别名(可能更短). 例如,在jQuery插件中经常使用这种方法. jQuery允许您禁用 $
引用jQuery命名空间,使用 jQuery.noConflict()
. 如果这样做了,您的代码仍然可以使用 $
使用这种闭包技术,如下所示:
(function($) {/* jQuery插件代码引用$ */})(jQuery);
这里简短而重要的答案是 use strict
是一种在运行时对JavaScript代码强制执行更严格的解析和错误处理的方法吗. 原本会被忽略或以静默方式失败的代码错误现在将生成错误或抛出异常. 总的来说,这是一个很好的实践.
严格模式的一些主要好处包括:
- 使调试更容易. 原本会被忽略或以静默方式失败的代码错误现在将生成错误或抛出异常, 提醒您更快地发现代码中的问题,并指导您更快地找到问题的来源.
- 防止意外的全局变量. 没有严格模式, 将值赋给未声明的变量会自动创建一个具有该名称的全局变量. 这是JavaScript中最常见的错误之一. 在严格模式下,尝试这样做会抛出错误.
-
Eliminates
this
coercion. 在没有严格模式的情况下,对this
null或undefined的值将自动强制到全局. 这可能会导致许多头假和拔头发的虫子. 在严格模式下,引用athis
值为空或未定义将抛出错误. -
禁止重复的参数值. 严格模式在检测到函数的重复命名参数时抛出错误.g.,
函数foo(val1, val2, val1){}
), 从而捕获代码中几乎肯定存在的错误,否则您可能会浪费大量时间来跟踪这些错误.- 注意:过去(在ECMAScript 5中)严格模式不允许重复的属性名(例如.g.
Var object = {foo: "bar", foo: "baz"};
) but 从ECMAScript 2015开始 现在情况已经不同了.
- 注意:过去(在ECMAScript 5中)严格模式不允许重复的属性名(例如.g.
-
使eval()更安全. 在方式上有一些不同
eval()
在严格模式和非严格模式下运行. 最重要的是,在严格模式下,在类中声明的变量和函数eval()
statement are not 在包含范围(they)中创建 are 在包含范围内以非严格模式创建,这也可能是问题的常见来源). -
的无效使用引发错误
delete
. Thedelete
操作符(用于从对象中删除属性)不能用于对象的不可配置属性. 当尝试删除非可配置属性时,非严格代码将静默失败, 而严格模式在这种情况下会抛出错误.
考虑下面的两个函数. 它们会返回相同的东西吗? Why or why not?
function foo1()
{
return {
bar: "hello"
};
}
function foo2()
{
return
{
bar: "hello"
};
}
令人惊讶的是,这两个函数可以 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中不仅仅是一种风格偏好.
对于这个问题,一个有教养的回答应该是:“你无法确定. 可能会打印出来 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));
当执行下面的代码时,以什么顺序将数字1-4记录到控制台? Why?
(function() {
console.log(1);
setTimeout(函数(){控制台.log(2)}, 1000);
setTimeout(函数(){控制台.log(3)}, 0);
console.log(4);
})();
这些值将按以下顺序记录:
1
4
3
2
让我们先解释一下其中可能更明显的部分:
-
1
and4
首先显示,因为它们是通过简单调用记录的console.log()
without any delay -
2
后显示3
because2
在延迟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调用的,所以会稍微延迟).
编写一个简单的函数(少于160个字符),返回一个布尔值,指示字符串是否为a palindrome.
下面的一行函数将返回 true
if str
is a palindrome; otherwise, it returns false.
函数isPalindrome(str) {
str = str.replace(/\W/g, '').toLowerCase();
return (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'
Write a sum
方法,该方法在使用下面任何一种语法调用时都能正常工作.
console.log(sum(2,3)); // Outputs 5
console.log(sum(2)(3)); // Outputs 5
有(至少)两种方法可以做到这一点:
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, 在上面的例子中, 通过简单地检查第二个参数是否未定义, 我们可以确定调用函数的方式并进行相应的处理.
考虑下面的代码片段:
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)提供一个或多个可按预期工作的替代实现.
(a)无论用户点击哪个按钮,数字5都会 always 登录到控制台. 这是因为,在点上 onclick
方法被调用(为 any 的按钮),的 for
循环已经完成,变量 i
已经是5了. (如果被采访者足够了解如何谈论执行上下文,那么他们将获得加分, variable 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);
}
Assuming d
是作用域中的“空”对象,例如:
var d = {};
使用下面的代码完成?
['斑马','马'].forEach(函数(k) {
d[k] = undefined;
});
上面显示的代码片段在对象上设置了两个属性 d
. 理想情况下,对带有unset键的JavaScript对象执行的任何查找计算结果为 undefined
. 但是运行这段代码将这些属性标记为对象的“自有属性”.
这是一种确保对象具有给定属性集的有用策略. 将此对象传递给 Object.keys
将返回一个数组与这些设置键以及(即使他们的值是 undefined
).
下面的代码将输出什么到控制台,为什么?
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));
记录的输出将是:
"数组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 invokearr2.push(arr3);
),arr1
会受到影响吗arr1
andarr2
仅仅是对同一个对象的引用吗.
在回答这个问题时,这里有几个侧面的观点有时会让人出错:
-
将数组传递给
push()
方法推入那个 entire array as a single 元素放到数组的末尾. 结果,声明arr2.push(arr3);
addsarr3
在其整体作为一个单一的元素结束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表示数组中的最后一个元素,依此类推.
下面的代码将输出什么到控制台,为什么 ?
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);
以上代码将向控制台输出以下内容:
"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: 虽然第一个 "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
.
如果数组列表太大,下面的递归代码将导致堆栈溢出. 如何在保留递归模式的同时解决这个问题?
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
//处理列表项...
nextListItem();
}
};
方法可以避免潜在的堆栈溢出 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, 该方法从头到尾进行处理,不需要直接递归调用, 因此调用堆栈保持清空, 不管迭代的次数.
闭包是一个内部函数,它可以访问外部(封闭)函数作用域链中的变量. 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
下面的代码行会向控制台输出什么?
console.Log ("0 || 1 = "+(0 || 1));
console.Log ("1 || 2 = "+(1 || 2));
console.log("0 && 1 = "+(0 && 1));
console.log("1 && 2 = "+(1 && 2));
解释你的答案.
代码将输出以下四行:
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
).
代码将输出:
true
false
在JavaScript中,有两组相等操作符. 三等号运算符 ===
如果两侧的两个表达式具有相同的类型和相同的值,则其行为与任何传统的相等操作符一样,计算结果为true. 然而,双相等操作符在比较两个值之前试图强制它们. 因此,通常使用 ===
rather than ==
. 这同样适用于 !==
vs !=
.
下面代码的输出是什么? 解释你的答案.
var a={},
b={key:'b'},
c={key:'c'};
a[b]=123;
a[c]=456;
console.log(a[b]);
这段代码的输出将是 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]
.
下面的代码将输出到控制台:
console.log((function f(n){return ((n > 1) ? N * f(N -1): N)})(10);
解释你的答案.
代码将输出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
考虑下面的代码片段. 控制台输出将是什么,为什么?
(function(x) {
返回(function(y) {
console.log(x);
})(2)
})(1);
输出将是 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
.
下面的代码将输出什么到控制台?为什么?
var hero = {
_name: 'John Doe';
getSecretIdentity:函数(){
return this._name;
}
};
var stoleSecretIdentity =英雄.getSecretIdentity;
console.日志(stoleSecretIdentity ());
console.log(hero.getSecretIdentity ());
这段代码的问题是什么,如何修复.
代码将输出:
undefined
John Doe
The first console.log
prints undefined
因为我们是从 hero
object, so stoleSecretIdentity ()
在全局上下文中被调用(i.e.(窗口对象),其中 _name
属性不存在.
一种解决方法是 stoleSecretIdentity ()
功能如下:
var stoleSecretIdentity =英雄.getSecretIdentity.bind(hero);
创建一个函数,给定页面上的DOM元素,该函数将访问元素本身和 all 它的后代(不仅仅是它的直系子女). 对于访问的每个元素,函数应该将该元素传递给提供的回调函数.
函数的参数应该是:
- a DOM element
- 回调函数(以DOM元素作为参数)
访问树(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
}
}
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);
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
.
因此输出将如上所示.
考虑下面的代码. 输出将是什么,为什么?
(function () {
try {
throw new Error();
} catch (x) {
var x = 1, y = 2;
console.log(x);
}
console.log(x);
console.log(y);
})();
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);
})();
这段代码的输出是什么?
var x = 21;
Var girl = function () {
console.log(x);
var x = 20;
};
girl ();
既不是21也不是20,结果是 undefined
这是因为JavaScript初始化没有被提升.
(为什么它不显示全局值21? 原因是当函数执行时,它检查是否存在一个局部 x
变量存在,但尚未声明它,因此它不会寻找全局变量.)
for (let i = 0; i < 5; i++) {
setTimeout(function(){控制台.log(i); }, i * 1000 );
}
这段代码将打印什么?
It will print 0 1 2 3 4
, because we use let
instead of var
here. The variable i
只在 for
循环的块作用域.
第一条语句返回 true
这是意料之中的.
The second returns false
因为引擎是如何处理算子结合律的 <
and >
. 它比较左和右,所以 3 > 2 > 1
JavaScript翻译为 true > 1
. true
has value 1
,然后进行比较 1 > 1
, which is false
.
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'];
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]
表达式将被求值为true,因为 NULL
将被视为任何其他未定义变量.
注意:JavaScript是区分大小写的,这里我们使用 NULL
instead of null
.
string
typeof 1
will return "number"
and typeof "number"
will return string
.
下面代码的输出是什么?
for (var i = 0; i < 5; i++) {
setTimeout(function(){控制台.log(i); }, i * 1000 );
}
解释你的答案. 在这里使用闭包有什么帮助呢?
所示的代码示例将 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 );
}
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.
下面的代码将输出什么?为什么?
var b = 1;
function outer(){
var b = 2
function inner(){
b++;
var b = 3;
console.log(b)
}
inner();
}
outer();
控制台的输出将是“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"
}
这听起来可能微不足道,事实上,对于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
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全权决定, LLC.
寻找JavaScript开发人员?
Looking for JavaScript开发人员? 查看Toptal的JavaScript开发人员.
Jay Johnston
自由JavaScript开发者
Coding HTML, CSS, 他从1997年参军时就开始学习JavaScript了, Jay喜欢通过电子商务解决方案为客户带来价值, 遗留集成, 以及优化的PHP和javascript驱动的应用程序. 他首选的DevOps环境是AWS, 他在关系数据库服务(RDS)方面有很强的技能(但不限于):, Redshift, Dynamo DB, 数据迁移服务(DMS), Lambda(无服务器和微服务), Cloudwatch, Cloudtrail, and Event Bridge.
Show MoreTyler Standley
自由JavaScript开发者
同时具备较强的沟通技巧和模范的职业道德, Tyler带来了他对各种编程语言的实践经验. 不过,最近他的关注点转向了JavaScript库. 在他的职业生涯中, 他曾作为核心开发人员在多个敏捷团队中工作过,现在对任何与javascript相关的工作都很感兴趣.
Show MoreJustin Michela
自由JavaScript开发者
Justin是一名技术专业人士,对学习充满热情,拥有18年以上领导团队构建企业级分布式应用程序解决现实问题的经验. 贾斯汀坚信,企业的各个方面都需要合作, 从开发到市场再到销售, 是成功的必要条件吗.
Show MoreToptal连接 Top 3% 世界各地的自由职业人才.
加入Toptal社区.