龙空技术网

好程序员Java教程分享JavaScript面试问题及答案(二)

好程序员 59

前言:

现在各位老铁们对“python中的负数下标”大概比较珍视,同学们都需要剖析一些“python中的负数下标”的相关知识。那么小编同时在网摘上收集了一些关于“python中的负数下标””的相关知识,希望小伙伴们能喜欢,大家快快来学习一下吧!

好程序员Java教程分享JavaScript面试问题及答案(二)

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

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

functionisPalindrome(str){

str=str.replace(/\W/g,'').toLowerCase();return(str==str.split('').reverse().join(''));

}

例如:

console.log(isPalindrome("level"));//logs'true'console.log(isPalindrome("levels"));//logs'false'console.log(isPalindrome("Acar,aman,amaraca"));//logs'true'

2、.写一个sum方法,在使用下面任一语法调用时,都可以正常工作。

console.log(sum(2,3));//Outputs5console.log(sum(2)(3));//Outputs5

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

方法1

functionsum(x){if(arguments.length==2){returnarguments[0]+arguments[1];

}else{returnfunction(y){returnx+y;};

}

}

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

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

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

方法2

functionsum(x,y){if(y!==undefined){returnx+y;

}else{returnfunction(y){returnx+y;};

}

}

当调用一个函数的时候,JavaScript不要求参数的数目匹配函数定义中的参数数量。如果传递的参数数量大于函数定义中参数数量,那么多余参数将简单地被忽略。另一方面,如果传递的参数数量小于函数定义中的参数数量,那么缺少的参数在函数中被引用时将会给一个undefined值。所以,在上面的例子中,简单地检查第2个参数是否未定义,就可以相应地确定函数被调用以及进行的方式。

3、请看下面的代码片段:

for(vari=0;i<5;i++){varbtn=document.createElement('button');

btn.appendChild(document.createTextNode('Button'+i));

btn.addEventListener('click',function(){console.log(i);});document.body.appendChild(btn);

}

(a)当用户点击“Button4”的时候会输出什么到控制台,为什么?(b)提供一个或多个备用的可按预期工作的实现方案。

(a)无论用户点击什么按钮,数字5将总会输出到控制台。这是因为,当onclick方法被调用(对于任何按钮)的时候,for循环已经结束,变量i已经获得了5的值。(面试者如果能够谈一谈有关如何执行上下文,可变对象,激活对象和内部“范围”属性贡有助于闭包行为,则可以加分)。

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

for(vari=0;i<5;i++){varbtn=document.createElement('button');

btn.appendChild(document.createTextNode('Button'+i));

btn.addEventListener('click',(function(i){returnfunction(){console.log(i);};

})(i));document.body.appendChild(btn);

}

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

for(vari=0;i<5;i++){varbtn=document.createElement('button');

btn.appendChild(document.createTextNode('Button'+i));

(function(i){

btn.addEventListener('click',function(){console.log(i);});

})(i);document.body.appendChild(btn);

}

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

['a','b','c','d','e'].forEach(function(value,i){varbtn=document.createElement('button');

btn.appendChild(document.createTextNode('Button'+i));

btn.addEventListener('click',function(){console.log(i);});document.body.appendChild(btn);

});

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

vararr1="john".split('');vararr2=arr1.reverse();vararr3="jones".split('');

arr2.push(arr3);console.log("array1:length="+arr1.length+"last="+arr1.slice(-1));console.log("array2:length="+arr2.length+"last="+arr2.slice(-1));

输出结果是:

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

arr1和arr2在上述代码执行之后,两者相同了,原因是:

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

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

这里有几个侧面点有时候会让你在回答这个问题时,阴沟里翻船:

传递数组到另一个数组的push()方法会让整个数组作为单个元素映射到数组的末端。其结果是,语句arr2.push(arr3);在其整体中添加arr3作为一个单一的元素到arr2的末端(也就是说,它并没有连接两个数组,连接数组是concat()方法的目的)。

和Python一样,JavaScript标榜数组方法调用中的负数下标,例如slice()可作为引用数组末尾元素的方法:例如,-1下标表示数组中的最后一个元素,等等。

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

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

原因是…

这里的根本问题是,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,然后将其与1相加,结果为0,再将其转换为字符串,连接后的"2"运算对象,得到"02"。

例4:+"1"+"1"+"2"输出:"112"说明:虽然第一个运算对象"1"因为前缀的一元+运算符类型转换为数值,但又立即转换回字符串,当连接到第二个运算对象"1"的时候,然后又和最后的运算对象"2"连接,产生了字符串"112"。

例5:"A"-"B"+"2"输出:"NaN2"说明:由于运算符-不能被应用于字符串,并且"A"和"B"都不能转换成数值,因此,"A"-"B"的结果是NaN,然后再和字符串"2"连接,得到"NaN2"。

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

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

varlist=readHugeList();varnextListItem=function(){varitem=list.pop();if(item){//processthelistitem...

nextListItem();

}

};

潜在的堆栈溢出可以通过修改nextListItem函数避免:

varlist=readHugeList();varnextListItem=function(){varitem=list.pop();if(item){//processthelistitem...

setTimeout(nextListItem,0);

}

};

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

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

闭包是一个可以访问外部(封闭)函数作用域链中的变量的内部函数。闭包可以访问三种范围中的变量:这三个范围具体为:(1)自己范围内的变量,(2)封闭函数范围内的变量,以及(3)全局变量。

下面是一个简单的例子:

varglobalVar="xyz";

(functionouterFunc(outerArg){varouterVar='a';

(functioninnerFunc(innerArg){varinnerVar='b';console.log("outerArg="+outerArg+"\n"+"innerArg="+innerArg+"\n"+"outerVar="+outerVar+"\n"+"innerVar="+innerVar+"\n"+"globalVar="+globalVar);

})(456);

})(123);

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

outerArg=123innerArg=456outerVar=ainnerVar=bglobalVar=xyz

8、下面的代码将输出什么:

for(vari=0;i<5;i++){

setTimeout(function(){console.log(i);},i*1000);

}

解释你的答案。闭包在这里能起什么作用?

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

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

闭包可以通过为每次迭代创建一个唯一的范围,存储范围内变量的每个唯一的值,来防止这个问题,如下:

for(vari=0;i<5;i++){

(function(x){

setTimeout(function(){console.log(x);},x*1000);

})(i);

}

这就会按预期输出0,1,2,3,和4到控制台。

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

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=11||2=10&&1=01&&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)。

10、执行下面的代码时将输出什么?请解释。

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

代码将输出:

truefalse

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

21.以下代码将输出什么?并解释你的答案。

vara={},

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

a[b]=123;

a[c]=456;

console.log(a[b]);

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

原因为:当设置对象属性时,JavaScript会暗中字符串化参数值。在这种情况下,由于b和c都是对象,因此它们都将被转换为"[objectObject]"。结果就是,a[b]和a[c]均相当于a["[objectObject]"],并可以互换使用。因此,设置或引用a[c]和设置或引用a[b]完全相同。

标签: #python中的负数下标