javascript经典面试题

1、作用域

(function() {
   var a = b = 5;
})();
 
console.log(b);  //5
复制代码

这个问题的陷阱就是,在立即执行函数表达式(IIFE)中,有两个赋值,但是其中变量a使用关键词var来声明。这就意味着a是这个函数的局部变量。与此相反,b被分配给了全局作用域(译注:也就是全局变量)。

这个问题另一个陷阱就是,在函数中没有使用”严格模式” ('use strict')。如果 严格模式开启,那么代码就会报错 ” Uncaught ReferenceError: b is not defined” 。请记住,如果这是预期的行为,严格模式要求你显式地引用全局作用域。所以,你需要像下面这么写:


(function() {
   'use strict';
   var a = window.b = 5;
})();
 
console.log(b);

复制代码

答案:
5

2、创建"原生('native')方法"

在 String 对象上定义一个 repeatify 函数。这个函数接受一个整数参数,来明确字符串需要重复几次。这个函数要求字符串重复指定的次数。举个例子:

console.log('hello'.repeatify(3));
复制代码

应该打印出hellohellohello.

答案:

String.prototype.repeatify = String.prototype.repeatify || function(times){
	var str = '';
	
	for(var i = 0;i < times;i++){
		str += this; 
	}
	return str;
}
复制代码

这个问题测试了开发人员对 javascript 中继承及原型(prototype)属性的知识。这也验证了开发人员是否有能力扩展原生数据类型功能(虽然不应该这么做)。

在这里,另一个关键点是,看你怎样避免重写可能已经定义了的方法。这可以通过在定义自己的方法之前,检测方法是否已经存在。

String.prototype.repeatify = String.prototype.repeatify || function(times) {/* code here */}; 
复制代码

3、变量提升

function test() {
   console.log(a);
   console.log(foo());
   
   var a = 1;
   function foo() {
      return 2;
   }
}
 
test();
复制代码

答案:
undefined
2

这个结果的原因是,变量和函数都被提升(hoisted) 到了函数体的顶部。因此,当打印变量a时,它虽存在于函数体(因为a已经被声明),但仍然是undefined。换言之,上面的代码等同于下面的代码:

function test() {
   var a;
   function foo() {
      return 2;
   }
 
   console.log(a);
   console.log(foo());
   
   a = 1;
}
 
test();
复制代码

4、javascript中this是怎么工作的


var fullname = 'John Doe';
var obj = {
   fullname: 'Colin Ihrig',
   prop: {
      fullname: 'Aurelio De Rosa',
      getFullname: function() {
         return this.fullname;
      }
   }
};
 
console.log(obj.prop.getFullname());
 
var test = obj.prop.getFullname;
 
console.log(test());
复制代码

答案:
这段代码打印结果是:Aurelio De RosaJohn Doe 。原因是,JavaScript中关键字this所引用的是函数上下文,取决于函数是如何调用的,而不是怎么被定义的

在第一个console.log()getFullname()是作为obj.prop对象的函数被调用。因此,当前的上下文指代后者,并且函数返回这个对象的fullname属性。相反,当getFullname()被赋值给test变量时,当前的上下文是全局对象window,这是因为test被隐式地作为全局对象的属性。基于这一点,函数返回windowfullname,在本例中即为第一行代码设置的。

5、call()和apply()

修复前一个问题,让最后一个console.log() 打印输出Aurelio De Rosa.

答案:
这个问题可以通过运用call()或者apply()方法强制转换上下文环境。如果你不了解这两个方法及它们的区别,我建议你看看这篇文章 function.callfunction.apply之间有和区别。 下面的代码中,我用了call(),但apply()也能产生同样的结果:


console.log(test.call(obj.prop));

复制代码

6、闭包

var nodes = document.getElementsByTagName('button');
for (var i = 0; i < nodes.length; i++) {
   nodes[i].addEventListener('click', function() {
      console.log('You clicked element #' + i);
   });
}
复制代码

请问,如果用户点击第一个和第四个按钮的时候,控制台分别打印的结果是什么?为什么?

答案:

概念:闭包(Closures)。对于每一个JavaScript开发者来说,如果你想在网页中编写5行以上的代码,那么准确理解和恰当使用闭包是非常重要的。如果你想开始学习或者只是想简单地温习一下闭包,那么我强烈建议你去阅读 Colin Ihrig 这个教程:JavaScript Closures Demystified

也就是说,代码打印两次You clicked element #NODES_LENGTH,其中NODES_LENGTH是nodes的结点个数。原因是在for循环完成后,变量i的值等于节点列表的长度。此外,因为i在代码添加处理程序的作用域中,该变量属于处理程序的闭包。你会记得,闭包中的变量的值不是静态的,因此i的值不是添加处理程序时的值(对于列表来说,第一个按钮为0,对于第二个按钮为1,依此类推)。在处理程序将被执行的时候,在控制台上将打印变量i的当前值,等于节点列表的长度。

7、 闭包

修复上题的问题,使得点击第一个按钮时输出0,点击第二个按钮时输出1,依此类推。

有多种办法可以解决这个问题,下面主要使用两种方法解决这个问题。

第一个解决方案使用立即执行函数表达式(IIFE)再创建一个闭包,从而得到所期望的i的值。实现此方法的代码如下:

var nodes = document.getElementsByTagName('button');
for (var i = 0; i < nodes.length; i++) {
   nodes[i].addEventListener('click', (function(i) {
      return function() {
         console.log('You clicked element #' + i);
      }
   })(i));
}
复制代码

另一个解决方案不使用IIFE,而是将函数移到循环的外面。这种方法由下面的代码实现:

function handlerWrapper(i) {
   return function() {
      console.log('You clicked element #' + i);
   }
}

var nodes = document.getElementsByTagName('button');
for (var i = 0; i < nodes.length; i++) {
   nodes[i].addEventListener('click', handlerWrapper(i));
}
复制代码

8、算法 质数

写一个isPrime()函数,当其为质数时返回true,否则返回false。

答案:

我认为这是面试中最常见的问题之一。然而,尽管这个问题经常出现并且也很简单,但是从被面试人提供的答案中能很好地看出被面试人的数学和算法水平。

首先, 因为JavaScript不同于C或者Java,因此你不能信任传递来的数据类型。如果面试官没有明确地告诉你,你应该询问他是否需要做输入检查,还是不进行检查直接写函数。严格上说,应该对函数的输入进行检查。

第二点要记住:负数不是质数。同样的,1和0也不是,因此,首先测试这些数字。此外,2是质数中唯一的偶数。没有必要用一个循环来验证4,6,8。再则,如果一个数字不能被2整除,那么它不能被4,6,8等整除。因此,你的循环必须跳过这些数字。如果你测试输入偶数,你的算法将慢2倍(你测试双倍数字)。可以采取其他一些更明智的优化手段,我这里采用的是适用于大多数情况的。例如,如果一个数字不能被5整除,它也不会被5的倍数整除。所以,没有必要检测10,15,20等等。如果你深入了解这个问题的解决方案,我建议你去看相关的Wikipedia介绍。

最后一点,你不需要检查比输入数字的开方还要大的数字。我感觉人们会遗漏掉这一点,并且也不会因为此而获得消极的反馈。但是,展示出这一方面的知识会给你额外加分。

现在你具备了这个问题的背景知识,下面是总结以上所有考虑的解决方案:

function isPrime(number) {
   // If your browser doesn't support the method Number.isInteger of ECMAScript 6,
   // you can implement your own pretty easily
   if (typeof number !== 'number' || !Number.isInteger(number)) {
      // Alternatively you can throw an error.
      return false;
   }
   if (number < 2) {
      return false;
   }
 
   if (number === 2) {
      return true;
   } else if (number % 2 === 0) {
      return false;
   }
   var squareRoot = Math.sqrt(number);
   for(var i = 3; i <= squareRoot; i += 2) {
      if (number % i === 0) {
         return false;
      }
   }
   return true;
}
复制代码

猜你喜欢

转载自juejin.im/post/5c0174bcf265da611a4798c9