JavaScript常见面试题三

简介:   JavaScript常见面试题三:1.下列代码行1-4如何排序,使之能够在执行代码时输出到控制台? 为什么?  (function() { console.log(1);  setTimeout(function(){console.

  JavaScript常见面试题三:1.下列代码行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() 而没有任何延迟输出的

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

  好的。但是,既然 3 是0毫秒延迟之后输出的,那么是否意味着它是立即输出的呢?如果是的话,那么它是不是应该在 4 之前输出,既然 4 是在第二行输出的?

  要回答这个问题,你需要正确理解JavaScript的事件和时间设置。

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

  同样的, setTimeout() 也会把其引用的函数的执行放到事件队列中,如果浏览器正忙的话。

  当setTimeout()的第二个参数为0的时候,它的意思是“尽快”执行指定的函数。具体而言,函数的执行会放置在事件队列的下一个计时器开始。但是请注意,这不是立即执行:函数不会被执行除非下一个计时器开始。这就是为什么在上述的例子中,调用 console.log(4) 发生在调用 console.log(3) 之前(因为调用 console.log(3) 是通过setTimeout被调用的,因此会稍微延迟)。

  

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

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

  function isPalindrome(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("A car, a man, a maraca")); // logs 'true'

  

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

  console.log(sum(2,3)); // Outputs 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)这样的形式调用,所以我们返回一个匿名函数,这个匿名函数合并了传递到 sum()的参数和传递给匿名函数的参数。

  方法2

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

  } else { return function(y) { return x + y; };

  }

  }

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

  

  4.请看下面的代码片段:

  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);

  }

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

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

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

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

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

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

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

  }

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

  for (var i = 0; i < 5; i++) { 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);

  }

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

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

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

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

  });

  

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

  var arr1 = "john".split('');var arr2 = arr1.reverse();var arr3 = "jones".split('');

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

  输出结果是:

  "array 1: length=5 last=j,o,n,e,s""array 2: length=5 last=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下标表示数组中的最后一个元素,等等。

相关文章
|
14天前
|
前端开发 JavaScript 网络协议
前端最常见的JS面试题大全
【4月更文挑战第3天】前端最常见的JS面试题大全
33 5
|
3月前
|
存储 前端开发 JavaScript
十个超级有用的JavaScript的高阶面试技巧!
十个超级有用的JavaScript的高阶面试技巧!
|
3天前
|
JavaScript 前端开发 测试技术
「一劳永逸」送你21道高频JavaScript手写面试题(上)
「一劳永逸」送你21道高频JavaScript手写面试题
20 0
|
1月前
|
设计模式 JavaScript 前端开发
最常见的26个JavaScript面试题和答案
最常见的26个JavaScript面试题和答案
42 1
|
1月前
|
存储 JavaScript 前端开发
【JavaScript】面试手撕浅拷贝
引入 浅拷贝和深拷贝应该是面试时非常常见的问题了,为了能将这两者说清楚,于是打算用两篇文章分别解释下深浅拷贝。 PS: 我第一次听到拷贝这个词,有种莫名的熟悉感,感觉跟某个英文很相似,后来发现确实Copy的音译,感觉这翻译还是蛮有意思的
45 6
|
1月前
|
JavaScript 前端开发
【JavaScript】面试手撕节流
上篇我们讲了防抖,这篇我们就谈谈防抖的好兄弟 -- 节流。这里在老生常谈般的提一下他们两者之间的区别,顺带给读者巩固下。
51 3
|
2月前
|
前端开发 JavaScript UED
【JavaScript】面试手撕防抖
防抖: 首先它是常见的性能优化技术,主要用于处理频繁触发的浏览器事件,如窗口大小变化、滚动事件、输入框内容改变等。在用户连续快速地触发同一事件时,防抖机制会确保相关回调函数在一个时间间隔内只会被执行一次。
36 0
|
2月前
|
前端开发 JavaScript 算法
【JavaScript】面试手撕数组排序
这章主要讲的是数组的排序篇,我们知道面试的时候,数组的排序是经常出现的题目。所以这块还是有必要进行一下讲解的。笔者观察了下前端这块的常用算法排序题,大概可以分为如下
24 2
|
2月前
|
JavaScript 前端开发 索引
【JavaScript】面试手撕数组原型链(易)
续借上文,这篇文章主要讲的是数组原型链相关的考题,有些人可能会纳闷,数组和原型链之间有什么关系呢?我们日常使用的数组forEach,map等都是建立在原型链之上的。举个🌰,如我有一个数组const arr = [1,2,3]我想要调用arr.sum方法对arr数组的值进行求和,该如何做呢?我们知道数组没有sum函数,于是我们需要在数组的原型上定义这个函数,才能方便我们调用,具体代码如下。接下来我们就是采用这种方式去实现一些数组常用的方法。
39 6
|
2月前
|
JavaScript 前端开发
【JavaScript】面试手写题精讲之数组(上)
该专题主要是讲解我们在面试的时候碰到一些JS的手写题, 确实这种手写题还是比较恶心的。有些时候好不容易把题目写出来了,突然面试官冷不丁来一句有没有更优的解法,直接让我们僵在原地。为了解决兄弟们的这些困扰,这个专题于是就诞生啦。我们会将一些常见的不是最优解的答案作为对比,方便大家更好理解。
37 3