经典js面试题解答汇总
参考博文[https://www.cnblogs.com/moqiutao/p/9872066.html]
在这里记录下来比较适合我思维的实现方式
1.JS找字符串中出现最多的字符
方法一:
let testStr = 'asdasddsfdsfadsfdghdadsdfdgdasd';
function getMax(str) {
let obj = {};
for (let i in str) {
if (obj[str[i]]) {
obj[str[i]]++;
} else {
obj[str[i]] = 1;
}
}
let keys = Object.keys(obj); // 获取对象中所有key的值返回数组
let values = Object.values(obj); // 获取所有value返回数组
let maxVal = Math.max(...values);// 这里可使用es6中的解构
console.log(keys[values.indexOf(maxVal)], maxVal);
}
getMax(testStr);
主要实现方法就是用对象接收遍历的值和次数,obj[‘a’]与obj.a等价
详细参考链接:添加链接描述
方法二:
var arrString = 'abcdaabc';
arrString.split('').reduce(function(res, cur) {
res[cur] ? res[cur] ++ : res[cur] = 1
return res;
}, {})
reduce 为数组中的每一个元素依次执行回调函数function
arr.reduce(callback,[initialValue])
initialValue为第一次调用 callback 的第一个参数,这里就是第一次调用函数时,将res赋值为一个空对象{}
reduce详细参考链接添加链接描述
2.JS实现九九乘法表
方法一:用table实现
table {
/* 表格height属性可设置可不设置,建议不设置 */
width: 600px;
border-collapse: separate;
}
table td {
border: #000000 1px solid;
/* 使<td>标签中的内容居中显示,若是<th>标签,没有此行代码亦可自动居中 */
text-align: center;
}
<script>
document.write("<table>"); //<table></table>添加一个表格样式来显示乘法表
for (var x = 1; x <= 9; x++) {
document.write("<tr>"); //<tr></tr>标签
for (var y = 1; y <= x; y++) {
document.write("<td>" + y + "*" + x + "=" + y * x + "</td>");//亦可用<th>标签来起到居中加粗内容的作用
}
document.write("</tr>");
}
document.write("</table>");
</script>
3.函数返回值问题
第一题:
var fun = function(){
this.name = 'peter';
return {
name: 'jack'
};
}
var p = new fun();
//请问p.name是:
第二题:
var fun = function(){
this.name = 'peter';
return 'jack';
}
var p = new fun();
//请问p.name是:
原博主给出的解答比较详细:
- 1.每个函数都有返回值,如果使用了return语句,则返回return后跟的值,如果没有使用return,则默认返回undefined.
- 2.特别的,如果这个函数是构造函数,则默认返回this对象,如果构造函数内使用了return语句,并且return后跟的是一个对象,则这个构造函数返回的是这个对象
- 3.所以1题中的p = {name: ‘jack’},而2题中的p = {name: ‘peter’}
4.构造函数原型问题
第一题:
var fun = function(){
this.info = {
name : 'peter',
age : 25
}
}
var a = new fun();
var b = new fun();
a.info.name = 'jack';
b.info.name = 'tom';
//请问a.info.name和b.info.name分别是:
原博主回答:
1.先查找构造函数实例里的属性或方法,如果有,就立即返回。
2.如果构造函数的实例没有,就去它的原型对象里找,如果有,就立即返回
答案:jack tom
第二题:
var fun = function(){}
fun.prototype = {
info : {
name : 'peter',
age : 25
}
}
var a = new fun();
var b = new fun();
a.info.name = 'jack';
b.info.name = 'tom';
//请问a.info.name和b.info.name分别是:
这道题主要是考察原型对象,首先要知道什么是原型对象:
JavaScript 规定,每一个函数都有一个prototype 属性,指向另一个对象原型对象prototype(注意这个prototype也是一个对象),这个对象的所有属性和方法,都会被构造函数所拥有。
用构造函数的方式创建对象时(也就是本题使用的方式),方法很好,但存在浪费内存问题,所以产生了原型对象。
构造函数通过原型分配的函数或属性是所有对象所共享的,也就是说本题中的info属性为a,b两个实例对象所共有,使用的是同一地址,当使用
a.info.name = 'jack';
b.info.name = 'tom';
为属性赋值时,存在b.info.name覆盖掉a.info.name的情况,所有最后根据查找规则和上述覆盖的原理,本题答案为 tom tom
第三题:
var fun = function(){}
fun.prototype = {
name : 'peter',
age : 25
}
var a = new fun();
var b = new fun();
a.name = 'jack';
b.name = 'tom';
//请问a.name和b.name分别是:
这道题直接是为a,b两个实例添加了name属性值
关于第二题和第三题的区别,原博主的回答是:
注意:第二题 a.info.name这段代码,首先去访问了实例对象本身是否有info这个对象,发现没有就去原型上查找了,发现原型上有,所以地址共享了得到的值都是Tom;
第三题是有区别的,a.name实例本身没有,给当前a这个实例对象执行赋值操作,没有去访问原型上的name。就相当于第三题先访问了原型上的info对象,第五题没有访问过程,只是在实例上添加了name属性值。
关于这里我的理解是同样是给name赋值,使用 a.info.name,就需要先找到info对象,所以去原型上查找了,结果找到了,进行赋值操作。而a.name就跟如下添加属性一样:
var obj = new Object();
obj.name = 'Kevin'
console.log(obj.name) // Kevin
第四题:
var fun = function(){
this.info = {
name : 'peter',
age : 25
}
}
fun.prototype = {
info : {
name : 'peter',
age : 25
}
}
var a = new fun();
var b = new fun();
a.info.name = 'jack';
b.info.name = 'tom';
//请问a.info.name和b.info.name分别是:
这个虽然原型对象中定义了同样的属性,但根据查找规则 1.自身 2.原型
所以直接在构造函数中查找并返回,与第一题相同,答案为jack tom
5.闭包问题
第一题:
function sayHello(name)
{
var text = 'Hello ' + name;
var sayAlert = function() { console.log(text); }
sayAlert();
}
sayHello("Bob") // 输出"Hello Bob"
首先要知道什么闭包?
闭包(closure)指有权访问另一个函数作用域中变量的函数。简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量。
原博主回答:
本题在sayHello()函数中定义并调用了sayAlert()函数;sayAlert()作为内层函数,可以访问外层函数sayHello()中的text变量。
第二题:
function sayHello2(name)
{
var text = 'Hello ' + name; // 局部变量
var sayAlert = function() { console.log(text); }
return sayAlert;
}
var say2 = sayHello2("Jane");
say2(); // 输出"Hello Jane"
本题与第一题类似,只不过返回了一个函数sayAlert,相当于:
var say2 = function(){console.log(text);}
say2()
所以最后得调用一下say2()
第三题:
for(var i = 0; i < 3; i++){ // 0 1 2 3
function test() {
console.log(i); // 3
}//注意,函数并没有调用执行!!!,只是创建函数而已
}
test();
for循环中,每一次循环都创建了一个函数test(),但由于同名,所以会被覆盖掉。当调用函数时,循环已经结束,i=3,所以只打印出1个3
第四题:
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
每次for循环的时候setTimeout都会执行,但是里面的function则不会执行,被放入任务队列,因此放了5次,所以输出5个5。
注意这里并不是因为时间未到,就算定时器设置的时间为0,仍然会输出5个5,主要原因是单线程问题。
第五题:
function buildList(list) {
var result = [];
for(var i = 0; i < list.length; i++) {
var item = 'item' + list[i];
result.push(
function() {
console.log(item + ' ' + list[i]);
}
);
}
return result;
}
var fnlist = buildList([1,5,7]);
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
- 这道题相对麻烦,可以理解为result中保存了三个匿名函数,因为保存在数组中,不会因为同名函数覆盖。当通过循环调用result中保存的函数时,buildList中的循环已经结束。
- 而由于退出循环时,item=item7,i=3
- 调用函数时list[3]不存在,所以为undefined
- 最终结果为 3个 item7 undefined
第六题:
function newClosure(someNum, someRef)
{
var anArray = [1,2,3];
var num = someNum;
var ref = someRef;
return function(x)
{
num += x;
anArray.push(num);
console.log('num: ' + num + "; " + 'anArray ' + anArray.toString() + "; " + 'ref.someVar ' + ref.someVar);
}
}
closure1 = newClosure(40, {someVar: "closure 1"});
closure2 = newClosure(1000, {someVar: "closure 2"});
closure1(5); // 打印"num: 45; anArray 1,2,3,45; ref.someVar closure 1"
closure2(-10); // 打印"num: 990; anArray 1,2,3,990; ref.someVar closure 2"
原博主回答:
每次调用newClosure()都会创建独立的闭包,它们的局部变量num与ref的值并不相同。
第七题:
function sayAlice()
{
var sayAlert = function() { console.log(alice); }
var alice = 'Hello Alice';
return sayAlert;
}
var sayAlice2 = sayAlice();
sayAlice2(); // 输出"Hello Alice"
这道题值得注意一下,与以往变量定义在后,使用在前,输出undefined不同。
此题返回函数sayAlice2所指向的闭包会包含sayAlice()函数中的所有局部变量,这自然包括了alice变量,因此可以正常打印”Hello Alice”。
第八题:
function setupSomeGlobals() {
var num = 666;
gAlertNumber = function () { console.log(num); }
gIncreaseNumber = function () { num++; }
gSetNumber = function (x) { num = x; }
}
setupSomeGlobals();
gAlertNumber(); // 输出666
gIncreaseNumber();
gAlertNumber(); // 输出667
gIncreaseNumber();
gAlertNumber(); // 输出668
gSetNumber(5);
gAlertNumber(); // 输出5
JavaScript规定,在函数中未定义直接使用的变量默认为全局变量,所以gAlertNumber,gIncreaseNumber,gSetNumber是三个全局变量,其值为三个匿名函数。
当调用函数时,他们操作的num都是保存在内存中的同一个num
第九题:
var elems = document.getElementsByTagName('a');
for (var i = 0; i < elems.length; i++) {
elems[i].addEventListener('click', function (e) {
e.preventDefault();
alert('I am link #' + i);
}, 'false');
}
假设有10个a标签,由于按顺序执行循环已经结束,i=10
当点击任意一个a标签时,才获取到i的值,所以最后都会弹出I am link #10