经典js面试题解答汇总

经典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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值