【前端面试题】—200+道常见JavaScript基础面试题中(附答案)

本文分享了超过200道JavaScript前端面试题,涵盖基础概念、事件流、DOM操作、类型转换等多个方面,深入解析了JavaScript中的重要知识点,如事件冒泡、原型、闭包等,并提供了详细的答案,帮助读者巩固JavaScript技能。
摘要由CSDN通过智能技术生成

af53c04679415af0325d40d820e13bfc.png

编辑整理 | 杨小爱

接上之前的内容《【前端面试题】—200+道常见JavaScript基础面试题上(附答案)》,原本计划分为上下两篇的,因为文章字数与篇幅限制,所以只能将其分成了上中下三篇内容。

现在,我们开始今天的中篇内容。

101、嵌入的 JavaScript代码应该放在什么位置?

应放在以下位置。

(1)放在底部,虽然放在底部照样会阻塞所有内容的呈现,但不会阻塞资源下载。

(2)如果嵌入的 JavaScript代码放在head中,请把嵌入的 JavaScript代码放在CSS头部。

(3)使用 defer的地方(只支持lE)。

(4)不要在嵌入的 JavaScript代码中调用运行时间较长的函数,如果一定要调用,可以用 setTimeout来调用。

102、请说出 JavaScript无阻塞加载的具体方式。

将脚本放在底部。

<link>放在head中,以保证在 JavaScript代码加载前,能加载出正常显示的页面。

< script>标签放在</body>前。

在阻塞脚本中,因为每个< script标签下载时都会阻塞页面的解析,所以限制页面的< script>总数也可以改善性能。它适用于内嵌脚本和外链脚本。

在非阻塞脚本中,等页面完成加载后,再加载 Javascript代码。也就是说,在window.onload事件发出后开始加载代码。

其中, defer属性支持lE4和 Fierfox3.5及更高版本的浏览器。通过动态脚本元素,文档对象模型(DOM)允许使用 JavaScript动态创建HTML的几乎全部文档内容,代码如下。

<script type="text/javascript">
var script=document. createElement("script");
script.type="text/javascript"; 
script.src="file.js";
document.getElementsByTagName("head")[0].appendchild(script);
</script>

此技术的重点在于,无论在何处启动下载,即使在head里,文件的下载和运行都不会阻塞其他页面的处理过程。

103、请解释一下事件冒泡机制。

在一个对象上触发某类事件(比如onclick事件)时,如果此对象定义了此事件的处理程序,那么此事件就会调用这个处理程序;如果没有定义此事件处理程序或者事件返回true,那么这个事件会向这个对象的父级对象传播,从里到外,直至它被处理(父级对象所有同类事件都将被激活),或者它到达了对象层次的最顶层,即 document对象(有些浏览器中是 window)。

冒泡型事件触发顺序是指从最特定的事件目标(触发事件对象)到最不特定的事件目标对象( document对象)。

JavaScript冒泡机制是指如果某元素定义了事件A,如 click事件,如果触发了事件之后,没有阻止冒泡事件,那么该事件将向父级元素传播,触发父类的 click事件。

104、请说出阻止事件冒泡的方法

阻止事件冒泡的方法,包括兼容IE浏览器(e.cancle Bubble)和标准浏览器(e. stopProgation)。下面给出一段示例代码。

function stopBubble(e){
var evt = e || window.event;
evt.stopPropagation?evt.stopPropagation():(evt. cancelBubble=true);

105、请指出split()与join()函数的区别。

split()将字符串切割成数组,是关于字符串的方法;join()将数组转换成字符串,是关于数组的方法。

对数组执行 join()函数,然后通过参数分隔符字符串将它们连接起来,从而返回一个字符串。对字符串执行 split()函数,然后在参数分隔符处将其断开,从而返回一个数组。

简单地说,split()把一个字符串(根据某个分隔符字符串)切割成若干个字符串并存放在一个数组里。而 join()把数组中的字符串连成一个长串,可以认为它是 split()的逆操作。

106、说说你对原型( prototype)的理解。

JavaScript是一种通过原型实现继承的语言,它与别的高级语言是有区别的,例如Java。C#是通过类型决定继承关系的, Javascript是动态的弱类型语言。总之,可以认为 JavaScript中所有数据都是对象。

在 JavaScript中,原型也是一个对象,用原型可以实现对象的属性继承, JavaScript的对象中都包含了一个" prototype"内部属性,这个属性所对应的就是该对象的原型。

作为对象的内部属性," prototype"是不能直接访问的。

所以,为了方便查看对象的原型, Firefox和 Chrome内核的 JavaScript引擎中提供了" _proto_"这个非标准的访问器(ECMA新标准中引入了标准对象原型访问器" Object. getPrototype( object)")。

原型的主要作用就是实现继承与扩展对象。

107、typeof与 instanceof的区别是什么?

在 JavaScript中,判断一个变量的类型可以用 typeof。

(1)如果是数字类型, typeof返回的值是 number。

比如 typeof("1")返回的值是 number。

(2)如果是字符串类型, typeof返回的值是 string。比如 typeof("123")返回的值是string。

(3)如果是布尔类型, typeof返回的值是 boolean。比如 typeout(true)返回的值是boolean。

(4)如果是对象、数组、null, typeof返回的值是 object。比如 typeof( window)、typeof(document)、 typeof(null)返回的值都是 object。

(5)如果是函数类型, typeof返回的值是 function。比如 typeof(eval)、 typeof(Date)返回的值都是 function

(6)对于不存在的变量、函数或者 undefined,将返回 undefined。比如 typeof(abc)、typeof(undefined)都返回 undefined。

在 JavaScript中, instanceof用于判断某个对象是否被另一个类构造(也就是说,是否是该类的实例化对象)。

当使用 typeof运算符判断引用类型存储值时,会出现一个问题,无论引用的是什么类型的对象,它都返回" object"。ECMAScript引入了另一个Java运算符 instanceof来解决这个问题。

与 typeof运算符相似, instanceof运算符用于识别正在处理的对象的类型与 typeof方法不同的是, instanceof方法要求开发者明确地给出对象的特定类型。

108、什么是事件流?

事件流是指从页面中接收事件的顺序。也就是说,当一个事件产生时,这个事件的传播过程就是事件流。

109、什么是事件冒泡?

IE中的事件流叫事件冒泡。事件冒泡是指事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点(文档)。

对于HTML来说,当一个元素产生一个事件时,它会把这个事件传递给它的父元素,父元素接收到之后,还要继续传递给它的上一级元素,就这样一直传播到 document对象(一些浏览器会传播到 window对象)。

110、什么是事件捕获

事件捕获是指不太具体的元素更早地接收到事件,而最具体的节点最后接收到事件。它们的用意是在事件到达目标之前就捕获它;也就是与冒泡的过程正好相反。

以HTML的 click事件为例, document对象(DOM0级规范要求从 document开始传播,但是现在的浏览器是从 window对象开始的)最先接收到 click事件,然后事件沿着DOM树依次向下传播,一直传播到事件的实际目标。

111、如何清除一个定时器?

清除定时器使用的方法是:window. clearInterval( )。清除循环定时器使用的方法x window. clearTimeout( )。

112、如何在body中添加一个DOM对象?innerHTML和 inner Text有什么区别?

在body中添加DOM的方法是使用body.appendChild(DOM元素)。

innerHTML是指从对象的起始位置到终止位置的全部内容,包括HTML标签。

innerText是指从起始位置到终止位置的内容,但它不包括HTML标签。

113、列出几个 window对象和属性。

window对象如下。

window.event、 window.document、 window.history、 window.screen、 window. navigator、 window. external。

window对象的属性如下。

window //窗口自身
window.se1f //引用本窗口 window= window,self 
window.name //为窗口命名
window. defaultstatus //设定窗口状态栏信息
window.location //URL相关属性信息对象

114、什么是回调函数?

回调函数就是一个通过函数指针调用的函数。如果把函数的指针(地址)作为参数传递给另一个函数,当这个指针用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的执行方直接调用的,而是在特定的事件或条件发生时由另一方调用的,用于对该事件或条件进行响应。

115、什么是自执行函数?它有哪些应用场景?有什么好处?

自执行函数是指声明的一个匿名函数,可以立即调用这个匿名函数作用是创建一个独立的作用域。一般用于框架、插件等场景。

妤处是防止变量弥散到全局,避免各种 JavaScript库冲突;隔离作用域,避免污染,或者截断作用域链,避免闭包造成引用变量无法释放;利用立即执行特性,返回需要的业务函数或对象,避免每次用条件判断来处理。

116、什么是事件委托?它有什么好处?

事件委托指利用冒泡的原理,把事件加到父级上,触发执行效果好处如下。

  • 减少事件数量,提高性能。

  • 预测未来元素,新添加的元素仍然可以触发该事件。

  • 避免内存外泄,在低版本正E中,防止删除元素而没有移除事件造成的内存溢出。

117、节点类型是有哪些?如何判断当前节点类型?

节点有以下类型。

  • 元素节点

  • 属性节点;

  • 文本节点

  • 注释节点;

  • 文档节点。

用 nodeObject.nodeType判断节点类型。其中, nodObject为DOM节点(节点对象)

该属性返回用数字表示节点的类型,例如,元素节点返回1,属性节点返回2。

118、什么是强制(显式)类型转换?什么是隐式类型转换?

强制(显式)类型转换如下。

Boolean(0)
Boolean(new object ()
Number(undefined)
Number (null)
String(null)
parseint()、 parseFloat()、JsoN. parse()、JsON. stringify()

隐式类型转换如下。

在使用算术运算符时,运算符两边的数据类型可以是任意的,比如,一个字符串可以和数字相加。之所以不同的数据类型之间可以做运算,是因为 JavaScript引擎在运算之前会悄悄地对它们进行隐式类型转换。

例如

x+"" //等价于 
String(x)+x //等价于 
Number(x)x-0 //同上
!!x //等价于 Boolean(x),注意前面的双叹号

119、已知id为 icketang的 input输入框,如何获取这个输入框的输入值(不使用第三方框架)?

使用document getElement Byld("icketang").value

120、使用 typeof bar===" object"可以确定bar是不是对象的潜在陷阱,如何避免这个陷阱?

尽管 typeof bar===" object"可以是检查bar是不是对象的可靠方法,但是在 JavaScript中也认为nul是对象。

因此,下面的代码将在控制台中输出true(而不是 false)。

var bar null;
 console. log(typeof bar - object");//true

只要清楚了这一点,同时检查bar是否为nul,就可以很容易地避免问题console. log((bar !== null) &&(typeof bar ===object"));// false

要答全问题,还有其他两件事情值得注意。

首先,上述解决方案将返回 false,当bar是一个函数的时候,在大多数情况下,这是期望结果,但当你想对函数返回true的时候,可以这样修改上面的解决方案。

console. log((bar !- null) &&((typeof bar a"object ")II (typeof bar ==="function")))

其次,当bar是一个数组(例如,当 var bar=口)的时候,上述解决方案将返回true。在大多数情况下,这是期望的结果,因为数组是真正的对象,但当你想对数组返回 false时,可以这样修改上面的解决方案。

console. log((bar !== null) &&(typeof bar ==="object")&&(Object. prototype tostr ing call(bar) !=="[object Array]"))

或者在ES5规范中输入如下内容。

console.log((bar !== null) &&(typeof bar ===object")&&(!Array. isArray(bar)))

121、下面的代码将输出什么到控制合?为什么?

function (){
var a= b= 3;
})();
console. log("a defined " +(typeof a !=='undefined'));
console. log("b defined " (typeof b !== 'undefined'));

输出结果如下。

a defined false 
b defined true

a和b都定义在函数的封闭范围内,并且都始于var关键字,因此大多数 JavaScript开发人员期望 typeof a和 typeof b在上面的例子中都是 undefined。

然而,事实并非如此。问题在于大多数开发人员将语句vara=b=3错误地理解为是以下声明的简写

var b= 3;
var a= b;

但事实上,var a=b=3实际是以下声明的简写

b= 3;
var a= b;

因此(如果你不使用严格模式),该代码段的输出如下。

a defined false 
b defined true

但是,b如何定义在封闭函数的范围之外呢?

既然语句vara=b=3是语句b=3和vara=b的简写,b最终就成为一个全局变量

(因为它没有前缀var关键字),于是需要定义在封闭函数之外。

需要注意的是,在严格模式下(使用 use strict),语句var a=b=3将生成 Reference error;b is not defined的运行时错误,从而避免任何 headfakes或bug

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

var myobject = {
 foo:"bar" 
func:function(){
 var self =this;
console. log("outer func:this foo =" this foo);
console. log(outer func:self. foo =" self foo);
(function(){
console. log("inner func:this foo =" +this .foo);
 console. log("inner func: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 s bar

在外部函数中,this和self都指向 myObject,因此两者都可以正确地引用和访问foo。

在内部函数中,this不再指向 myObject。结果是,this.foo没有在内部函数中定义,相反,指向到本地的变量seIf保持在范围内,并且可以访问(在 ECMAScript5之前,在内部函数中this将指向全局的 window对象;反之,内部函数中的this是未定义的)

123、封装 JavaScript源文件的全部内容到一个函数块有什么意义?

这是一个越来越普遍的做法,已被许多流行的 JavaScript库( jQuery、 Node. js等)采用。这种技术创建了一个围绕文件全部内容的闭包。最重要的是,创建了一个私有的命名空间,有助于避免不同 JavaScript模块和库之间的命名冲突。

这种技术的另一个特点是,允许把一个易于引用的(更短的)别名用于全局变

量。例如, jQuery插件中, jQuery允许你使用 jQuery. no Conflict(),来禁止$引用到jQuery命名空间。在完成这项工作之后,利用这种闭包技术,代码仍然可以使用$,如下所示。

(function(s){/* jQuery plugin code referencing s */} )(jQuery);

124、为了在 script里访问在 script下面的HTML中的元素,可以用什么技术实现?

Javascript不能访问当前 script元素后面定义的HTML元素,但在 window里有个 onload函数,把代码写在 window. οnlοad= function函数体里就可以访问了。

125、以下两个函数会返回相同的结果吗?为什么相同或为什么不相同?

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

这两个函数返回的内容并不相同。通过以下语句查看返回值。

console. log("fool returns:");
console. log(foo1());
console. log("foo2 returns:" );
 console. log(foo2());

输出结果如下。

fool returns:Object 
{
bar:"hello"
}
foo2 returns:undefined

这不仅令人惊讶,而且让人困惑,因为foo2()返回 undefined,却没有任何错误抛出。

原因与这样一个事实有关,即分号在 JavaScript中是一个可选项(尽管省略它们通常是非常糟糕的形式)。结果是,当碰到foo2()中包含 return语句的代码行时(代码行上没有其他任何代码),分号会立即自动插入返回语句之后,也不会拋出错误,因为代码的其余部分是完全有效的,即使它没有得到调用或做任何事情。

这也说明了在 JavaScript中大括号的位置应该放在语句后面的编程风格更符合 JavaScript的语法要求。

126、NaN是什么?它的类型是什么?如何可靠地判断一个值是否等于NaN?

NaN属性代表一个“不是数字”的值。这个特殊的值是因为运算不能执行而导致的,不能执行的原因可能是其中的运算对象之一非数字(例如,"abc"/4),也可能是是运算的结果非数字(例如,除数为零)。

虽然这看上去很简单,但NaN有一些令人惊讶的特点,如果你不知道它们,可能会导致令人头痛的Bug。

首先,虽然NaN意味着“不是数字”,但是它的类型是 Number。

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

此外,NaN和任何内容比较一甚至是它自己本身一结果都是 false 。

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

可以用一种半可靠的方法来判断了一个数字是否等于NaN,使用内置函数 isNaNO但即使使用 isTaNA也并非是一个完美的解决方案。

一个更好的解决办法是使用 value!= value,如果值等于NaN,只会产生true.另外, ECMAScript6中拓展了两个处理NaN的方法。一个是 Number isnaN()函数,比全局 isNaN(函数更可靠,比较的时候不会做数据类型的转换。另一个是 Object is,它可以判断两个NaN是否相等。

127、说明下列代码将输出什么,并解释原因。

console. log(0. 1 +0.2);
console.1og(0.1+0.2==0.3);

它会输出以下内容。

0.30000000000000004
false

原因如下。

十进制数0.1对应二进制数0.00011001100110011…(循环0011)。

十进制数0.2对应二进制数0.0011001100110011…(循环001)

两者相加得到达以下结果

0.00011001100110011001100110011001100110011001100110011001…

+0.00110011001l00110011001100110011001100110011001100110011

=0.01001100110011001100110011001100110011001100110011001100…

转换成十进制之后得到0.300000000000004。

128、写出函数 islnteger(x)的实现方法,用于确定x是否是整数。

ECMAScript6引入了一个新的方法,即 Number. isInteger(),它可以用来判断一个数字是否是整数。在 ECMAScript6之前, isInteger的实现会更复杂。在 ECMAScript规格说明中,整数只是在概念上存在,即,数字值总是存储为浮点数值。

考虑到这一点,有如下几种实现方案

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

下面的解决方法虽然不如上面那个方法优雅,但也是可行的。

function isInteger(x){
 return Math. round(x) === x;

请注意, Math. ceil()和 Math. floor()在上面的实现中等同于 Math. round()。

或者使用以下实现方案。

function isInteger (x){
return (typeof x ==='number ')&&(x %1===0);
}

一个比较普遍的错误解决方案如下。

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

虽然这个以 parselnt函数为基础的方法在x取许多值时能良好地工作,但是一旦x取值相当大,它就会无法正常工作。问题在于 parselnt()在解析数字之前强制把第一个参数转换为字符串。

因此,一旦数目变得足够大,它的字符串就会表达为指数形式(例如le+21)。

因此, parselnt()函数就会解析le+21,但当解析到‘e'字符的时候,就会停止解析,因此只会返回1。

129、下列代码行1~4如何排序,才能使之能够在执行代码时输出到控制台?为什么?

(function(){
console. log(1);
setTimeout(function(){ console. log(2)}, 1000);
setTimeout(function (){ console. log(3)},0);
console. log(4)
})();

序号如下

1
4
3
2

让我们先来解释比较显而易见的那部分。

1和4之所以放在前面,是因为它们只是简单调用 console.log(O,而没有任何延迟输出。

2之所以放在3的后面,是因为2是延迟了1000ms(即,ls)之后输出的,而3是延迟了0ms之后输出的。

浏览器有一个事件循环,它会检查事件队列和处理未完成的事件。例如,如果时间发生在后台(例如,脚本的 onload事件),浏览器正忙(例如,处理一个 onclick),那么事件会添加到队列中。当 onclick处理程序完成后,检查队列,然后处理该事件(例如,执行 onload脚本)。

同样,如果浏览器正忙。setTimeout0也会把其引用的函数的执行放到事件队列中当 setTimeout的第二个参数为0的时候,它代表“尽快”执行指定的函数。具体是指,函数的执行会从事件队列的下一个计时器开始。但是请注意,这不是立即执行:函数不会执行,除非下一个计时器开始计时,这就是为什么在上述例子中调用 console. log(4)

发生在调用 console. log(3)之前(因为调用 console. log(3)是通过 setTimeout完成的,因此会稍微延迟)。

130、写一个简单的函数(少于80个字符),要求返回一个布尔值,指明字符串是否为回文结构。

下面这个函数在str是回文结构的时候返回true;否则,返回 false

function isPalindrome(str){
str= str.replace(/\W/g, ' ').toLowercase();
return (str = str .split('  ').reverse().join(' ')'));
}

例如:

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

131、写一个sum方法,在使用任意语法调用时,都可以正常工作。

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

(至少)有两种方法可以做到。

方法1,代码如下。

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

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

如果传递两个参数,那么只须加在一起并返回。

否则,假设它以sum(2)(3)这样的形式调用,所以返回一个匿名函数,这个匿名函数合并了传递到sumO的参数和传递给匿名函数的参数。

方法2,代码如下。

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

当调用一个函数的时候, JavaScript不要求参数的数目匹配函数定义中的参数数量如果传递的参数数量大于函数定义中的参数数量,那么多余参数将简单地被忽略。

另一方面,如果传递的参数数量小于函数定义中的参数数量,那么在函数中引用缺少的参数时将会给一个 undefined值。

所以,在上面的例子中,简单地检查第2个参数是否未定义,就可以确定函数的调用方式。

132、请看下面的代码片段并回答以下问题。

for (var i = 0;i< 5;i++){
var btn= document.createElement ('button );
btn. appendchild(document createTextNode('Button +i));
btn.addEventListener('click', function(){ console. log(i);});
document .body. appendchild (btn);

(1)当用户单击“ Button4”的时候会输出什么到控制台,为什么?

(2)提供一个或多个可获取当前i的值的实现方案。

(1)无论用户单击什么按钮,数字5将总会输出到控制台。这是因为,当调用onclick方法(对于任何按钮)的时候,for循环已经结柬,变量i已经获得了5的值。

(2)要让代码工作的关键是,通过传递到一个新创建的函数对象,在每次传递通过for循环时,捕捉到i值。下面是3种可能实现的方法。

可以通过闭包实现对循环变量i的存储

for (var i=0:i< 5:1++){
var btn =document .createElement ('button');
btn.appendchild(document createTextNode( 'Button ' +i));
btn. addEventListener('click',(function (i) { return function() { console.
1og(i);};}) (i) );
document .body. appendChild(btn);}

或者,可以封装全部调用到新匿名函数中的btn.addEventListener。

for (var i = 0;i< 5:1++){
var btn= document createElement ('button');
btn. appendChild(document .createTextNode('Button '+i));
(function (i){ btn.addEventListener('click', function(){ console. log(i);
});})(i);
document .body. appendChild(btn);

也可以调用数组对象的本地 for Each方法来替代for循环。

Array(5).fill(0). forEach(function (value, i){
var btn = document createElement('button ');
btn.appendChild(document.createTextNode('Button'+));
btn.addEventListener('click',function(){ console. log(i);});
document .body.appendchild (btn);

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

var arrl ="john"split(' ');
var arr2= arrl. reverse();
var arr3 ="jones",split(' ');
arr2 push(arr3);
console. log("array 1:length="+ arrl. length +" last="+ arrl. slice(-1));
console.log("array 2:length="+ arr2 .length+" last="+ arr2. slice (-1));

输出结果是如下

"array1:length=5 last=j,o,n,e,s"
“array2:length-5 last=j,o,n,e,s"

在上述代码执行之后,arr1和arr2相同,原因如下。

调用数组对象的 reverse()方法并不只是返回反顺序的阵列,它也反转了数组本身的顺序(即,在这种情况下,指的是arr1)。

 reverse()方法返回一个对数组本身的引用(在这种情况下,即arr1)。其结果为,arr2仅仅是一个对arr1的引用(而不是副本)。因此,当对arr2做了任何事情(即当调用arr2 .push(arr3))时,arrl也会受到影响,因为arr1和arr2引用的是同一个对象。

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

console,10g(1+"2"+"2");
console.1og(1+ +"2"+"2");
console.1og(1+-"1"+"2");
console. log(+"1"+"1"+"2");
console. log("A"-B"+"2");
console. log("A"- "B"+2);

上面的代码将输出以下内容到控制台。

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

JavaScript( ECMAScript)是一种弱类型语言,它可对值进行自动类型转换,以适应正在执行的操作。

例1:1+"2”+"2”输出"122"。下面分析原因。1+"2”是执行的第一个操作,由于其中一个运算对象("2")是字符串, JavaScript会假设它需要执行字符串连接,因此会将1的类型转换为"1",1+"2"结果就是"12"。然后,"12"+"2"就是"122"。

例2:1++"2"+"2”输出"32"。下面分析原因。根据运算的顺序,要执行的第一个运算是+"2”(第一个"2"前面的“+”被视为一元运算符),因此, JavaScript将"2"的类型转换为数字。其结果是,接下来的运算就是1+2,这当然是3.然后我们需要在个数字和一个字符串之间进行运算(即,3和"2")。同样, JavaScript会将数值类型转换为字符串,并执行字符串的连接,产生"32”。

例3:1+-"1”+"2”输出"02"。下面分析原因。这里的解释和前一个例子相同,除了此处的一元运算符是“-”,当应用“-”时"1"变为了-1.然后将其与1相加,结果为0,再将其转换为字符串,连接最后的"2”,得到"02"。

例4:十"1″+"1”+"2”输出"12"。下面分析原因。虽然第一个运算对象”1"因为前缀的一元"艹"运算符类型转换为数值,但是当连接到笫二个运算对象"I"的时候,立即转换回字符串。最后与"2"连接,产生了字符串"112”。

例5:"A""B"+"2”输出"NaN2"。说明:由于运算符“-”对"A"和"B"处理的时候,都不能转换成数值,因此"A"-"B"的结果是NaN,然后再和字符串"2"连接,得到"NaN2”。

例6:"A"-"B"+2输出NaN。说明:参见前一个例子,"A"-"B"结果为NaN,但是,应用任何运算符到NaN与其他任何的数字运算对象上,结果仍然是NaN。

135、下面的递归代码在数组列表偏大的情况下会导致堆栈溢出,在保留递归模式的基础上,怎么解决这个问题?

var list= readHugeList ();
var nextListItem function()I{
   var item-=list. pop();
  if (item){
nextListItem();
}
};
潜在的堆栈溢出可以通过修改 nextListltem函数避免。
var list readHugeList();
 var nextListItem function(){
    var item =list .pop();
  if (item){
setTimeout( nextListItem, 0);
}
};

堆栈溢出之所以会被消除,是因为事件循环操纵了递归,而不是调用堆栈。当nextlistItem运行时,如果item不为空, timeout函数( nextlistltem)就会被推到事件队列,该函数退出,因此就清空调用堆栈。当事件队列运行其 timeout事件且进行到下个iem时,把定时器设置为再次调用 nextlistltem。因此,该方法从头到尾都没有直接的递归调用,所以无论迭代次数的多少,调用堆栈一直保持清空的状态。

136、JavaScript中的“闭包”是什么?请举一个例子。

闭包是一个可以访问外部(封闭)函数作用域链中变量的内部函数。闭包可以访问3种范围中的变量,这3个范围具体如下。

  • 自己范围内的变量。

  • 封闭函数范围内的变量。

  • 全局变量。

下面是一个简单的例子。

var globalVar ="hello";
(function outer Func (outerArg){
         var outerVar ='a';
(function innerFunc(innerArg) {
 var innervar ='b'
 console. log("outerArg =" outerArg +" "+"innerArg =" innerArg+" "+ "outerVar="+ outerVar +  " "+"innerVar="+ innerVar
+""+"globalVar =" +globalVar);
})(200);
})(100);

在上面的例子中,来自于 innerFunc、outerFunc和全局命名空间的变量都在 innerFunc的范围内。因此,上面的代码将输出如下结果 。

outerArg= 100
innerArg= 200
outerVar =a 
innerVar=b 
globalVar= hello

137、下面的代码将输出什么?闭包在这里能起什么作用?

for (var i =0; i < 5:1++){ 
(function() 
{settimeout (function () {
 console. log(i); 1,i *1000 );
 })();
 }

上面的代码不会按预期显示值0、1、2、3和4,而是会显示5、5、5、5和5。

原因是,在循环中执行的每个函数将先整个循环完成之后执行,因此,将会引用存储在i中的最后一个值,那就是5。

闭包可以为每次迭代创建一个唯一的作用域,存储作用域内的循环变量。如下代码会按预期输出0、1、2、3和4到控制台。

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

138、以下代码行将输出什么到控制台?

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

该代码将输出以下结果。

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

详细分析如下。

在JavaScript中,‖ 和 && 都是逻辑运算符,从左至右进行运算。

  • “或”(‖)运算符。在形如X || Y的表达式中,首先,计算X并将其表示为一个布尔值。如果这个布尔值是true,那么返回true(1),不再计算Y,因为“或”的条件已经满足。如果这个布尔值为false,那么,我们仍然无法知道X || Y是真还是假,直到计算Y,并且也把它表示为一个布尔值。

因此,0 ‖1的计算结果为true(1),同理计算1 ‖ 2。

  • “与”(&&)运算符。在形如X&&Y的表达式中,首先,计算X并将其表示为一个布尔值。如果这个布尔值为false,那么返回false(0),不再计算Y,因为“与”的条件已经失败。如果这个布尔值为true,但是,我们仍然不知道X&&Y是真还是假,直到计算Y,并且也把它表示为一个布尔值。

不过,关于&&运算符最有趣的地方在于,当一个表达式的计算结果为“true”的时候,就返回表达式本身。这就解释了为什么1&&2返回2(而不是返回true或1)。

139、下面的代码将输出什么?请解释。

console.log(false ==0')
console.log(false ===10')

代码将输出以下结果:

true 
false

在JavaScript中,有两种等于运算符。3个等于运算符===的作用类似于传统的等于运算符:如果两侧的表达式有相同的类型和相同的值,那么计算结果为true。而双等于运算符会只强制比较它们的值。因此,总体上而言,使用===而不是==的做法更好。!==与!=亦是同理。

140、以下代码将输出什么?解释你的答案。

var a=(),b=(key:'b'),c=(key:'c');
 a[b]=123; 
 a[c]=456; 
 console.log(a[b]);

这段代码将输出456(而不是123)。

原因是,当设置对象属性时,JavaScript会隐式地将[ ]内的变量转换成字符串。在这种情况下,由于b和c都是对象,因此,它们都将被转换为"[object Object]"。结果就是, a[b]和a[c]均相当于a["[object Object]"],并可以互换使用。因此,设置或引用a[c]和设置或引用a[b]完全相同。

总结

今天就先分享到这里,后面还有60道JavaScript面试题,会在下期内容中跟大家分享,希望,我分享的这期内容对大家有所帮助。

推荐阅读

【前端面试题】—200+道常见JavaScript基础面试题上(附答案)

【前端面试题】—53道常见NodeJS基础面试题(附答案)

【前端面试题】—18道有关混合开发的面试题(附答案)

【前端面试题】—21道有关移动端的面试题(附答案)

【前端面试题】—19道有关前端测试的面试题(附答案)

【前端面试题】—26道HTTP和HTTPS的面试题(附答案)

【前端面试题】—18道有关模块化开发的面试题(附答案)

【前端面试题】—21道关于性能优化的面试题(附答案)

【前端面试题】—44道常见Augluar基础面试题下(附答案)

【前端面试题】—31道有关前端工程化的面试题(附答案)

【前端面试题】—47道基础的VueJS面试题(附答案)

【前端面试题】—16道设计模式面试题(附答案)

【前端面试题】—17道面向对象的面试题(附答案)

【前端面试题】—33道基础CSS3面试题(附答案)

【前端面试题】—59道CSS面试题(附答案)

【前端面试题】—42道常见的HTML5面试题(附答案)

学习更多技能

请点击下方公众号

c2b4f29b4a9b5220a33a7e546272fd60.gif

008dae03aa5ea7a857f84904321a2467.png

ceadf81814e3c624ad7c581cad37f650.png

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值