经典js面试题解答汇总二

1.JS重复输出一个给定的字符串

方法一:while循环
function repeatStringNumTimes(string, times) {
  var repeatedString = "";
  while (times > 0) {
    repeatedString += string;
    times--;
  }
  return repeatedString;
}
repeatStringNumTimes("abc", 3);
方法二:for循环
function repeatStringNumTimes(string, times) {
  var repeatedString = "";
  for(var i = 0; i < times ;i++) {
    repeatedString += string;
  }
  return repeatedString;
}
repeatStringNumTimes("abc", 3)
方法三:递归
function repeatStringNumTimes(string, times) {
  if(times < 0) 
    return "";
  if(times === 1) 
    return string;
  else 
    return string + repeatStringNumTimes(string, times - 1);
}
repeatStringNumTimes("abc", 3);
方法四:使用ES6 repeat() 方法
function repeatStringNumTimes(string, times) {
  return times > 0 ? string.repeat(times) : "";
}
repeatStringNumTimes("abc", 3);

repeat() 方法构造并返回一个新字符串,该字符串包含被连接在一起的指定数量的字符串的副本。 这个方法有一个参数 count
表示重复次数,介于0和正无穷大之间的整数 : [0, +∞)
。表示在新构造的字符串中重复了多少遍原字符串。重复次数不能为负数。重复次数必须小于 infinity,且长度不会大于最长的字符串

2.函数声明相关

第一题:无返回值
    var x = 1,
      y = 0,
      z = 0;
    function add(n) {
      n = n + 1;
    }
    console.log(y);//0
    y = add(x);
    z = x + y;
    console.log("y1:" + y);//undefined
    console.log("z1:" + z);//NaN

    function add(n) {
      n = n + 3;
    }
    y = add(x);
    z = x + y;
    console.log("y2:" + y);//undefined
    console.log("z2:" + z);//NaN

add函数没有返回值,而没有明确返回值的,全部返回undefined,所以y为undefined,z是一个数和undefined相加,所以输出为NaN
这道题还有要注意的地方!!
js存在变量和函数的声明提权,所以add函数存在同名覆盖的情况

第二题:有返回值和提升问题
var x=1,
    y=0,
    z=0;
function add(n){
    return n=n+1;
}
y=add(x);
z=x+y;
console.log("y1:"+y);//4
console.log("z1:"+z);//5

function add(n){
   return n=n+3;
}
y=add(x);
z=x+y;
console.log("y2:"+y);//4
console.log("z2:"+z);//5

这道题与上面类似,如果不考虑变量和函数提升的话,很可能会做错。
看似调用的是不同的函数,实际上覆盖后只有第二个add函数存在,所以最后都输出为4,5

第三题:有返回值和不提升问题
var x = 1,
      y = 0,
      z = 0;
    var add = function (n) {
      return n = n + 1;
    }
    y = add(x);
    z = x + y;
    console.log("y1:" + y);//2
    console.log("z1:" + z);//3

    var add = function (n) {
      return n = n + 3;
    }
    y = add(x);
    z = x + y;
    console.log("y2:" + y);//4
    console.log("z2:" + z);//5

这三道题类似,但考点不同,上面说了变量和函数存在提升的问题,本题为什么又没有提升呢?

  • 仔细观察可以发现第二题和第三题定义函数的方式不同,题二用的函数声明,题三用的函数表达式,而只有函数声明才存在函数提升
  • 函数表达式可以理解为给一个变量赋值,所以var add变量仍然要提升,但赋值的匿名函数却不提升。按照顺序执行,给add赋值的函数不同,运行结果不同
第四题:混合
    function x() {
      alert(2)
    };
    x(); //output 3
    var x = function () {
      alert(0)
    };
    x(); //output 0
    var x = function () {
      alert(1)
    };
    x(); //output 1
    function x() {
      alert(3)
    };
    x(); //output 1

注意:函数提升在变量提升之前,这道题提升后的过程如下:

    
    function x() {
      alert(3)
    };
    var x;
    x(); //output 3
    x = function () {
      alert(0)
    };
    x(); //output 0
    x = function () {
      alert(1)
    };
    x(); //output 1
    x(); //output 1
第五题
  console.log(a);
  console.log(a());
  var a = 3;

  function a() {
    console.log(10)
  }
  console.log(a)
  a = 6;
  console.log(a());

函数提升不会被变量声明覆盖,但是会被变量赋值之后覆盖
本题相当于如下过程,第一次输出a是个函数相当于var
a = function(){};第二次调用a,a函数本身会输出10,由于a函数没有返回值,前面已经讲过,函数没有返回值的返回undefined;第三次输出a,由于a已经被变量赋值操作所覆盖,所以输出3;第四次由于a=6覆盖,a不是个函数,所以再调用会报错。

function a() {
    console.log(10)
  }
  var a
  console.log(a);
  console.log(a());
  a = 3;
  console.log(a)
  a = 6;
  console.log(a());

输出结果:
在这里插入图片描述

3.作用域范围

第一题:全局变量
    (function () {
      var a = b = 5;
    })();
    console.log(b);//5

在js函数中,没有声明直接使用的变量默认为全局变量,所以a为局部变量,b为全局变量。要想声明多个变量时,用逗号隔开:var a=5,b=5。
当使用严格模式时,是不允许使用未定义的变量得,会报错

第二题:
var a = 6;
setTimeout(function () {
    alert(a);//66
    a = 666;
}, 1000);
a = 66;

闭包问题前面已经讲过了,当顺序执行完代码后a=66,然后任务队列里的匿名函数开始执行,就近原则向上查找a的值,所以为66

4.创建 “原生(native)” 方法

其实就是通过原型扩展数组、字符串等的内置方法

第一题:为数组增加sum求和
 Array.prototype.sum = function() {
   var sum = 0;
   for (var i = 0; i < this.length; i++) {
   sum += this[i];
   }
   return sum;
 };
 //此时数组对象中已经存在sum()方法了  可以用数组.sum()进行数据的求和
第二题:为String增加重复字符串的方法
String.prototype.repeatify = String.prototype.repeatify || function(times) {
   var str = '';
   for (var i = 0; i < times; i++) {
      str += this;
   }
   return str;
};

为了避免重写可能已经定义了的方法,可以通过在定义自己的方法之前,检测方法是否已经存在,这一点对为旧浏览器实现向后兼容的函数时十分有用。

5.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());//Aurelio De Rosa
var test = obj.prop.getFullname;
console.log(test());//John Doe

这道题目并不好理解,参考原博主所说:

JavaScript中关键字this所引用的是函数上下文,取决于函数是如何调用的,而不是怎么被定义的。
在第一个console.log(),getFullname()是作为obj.prop对象的函数被调用。因此,当前的上下文指代后者,并且函数返回这个对象的fullname属性。
当getFullname()被赋值给test变量时,当前的上下文是全局对象window,这是因为test被隐式地作为全局对象window的方法。基于这一点,函数返回window的fullname,在本例中即为第一行代码设置的。

在这里插入图片描述

第二题:
var name = "The Window";
var object = {
  name : "My Object",
  getNameFunc : function(){
    return function(){
       return this.name;
    };
   }
  };
console.log(object.getNameFunc()());//The Window

最后一句改写成下面的方式比较好理解

var retFn = object.getNameFunc()
console.log(retFn());
  • 我们知道在JavaScript中,我们声明的JavaScript全局对象、全局函数以及全局变量均自动成为window对象的成员。
  • 本题通过object.getNameFunc()调用对象的方法,返回了一个函数,通过refFn接收
  • retFn是全局变量,接收函数后自动成为了window对象的一个方法
  • 所以当调用retFn时,调用的对象时window,因此输出也是window对象下的name属性:The Window
第三题:
var name = "The Window";
var object = {
  name : "My Object",
  getNameFunc : function(){
    var that = this;
    return function(){
      return that.name;
    };
  }
};

alert(object.getNameFunc()()); 

object.getNameFunc()函数中将this(指向的是对象object)赋值给了that,最后返回函数后输出的就是object里面的name,所以为My Object

详细参考链接:添加链接描述

6.call()和apply()

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

console.log(test.call(obj.prop));//Aurelio De Rosa
console.log(test.apply(obj.prop));//Aurelio De Rosa

扩展:bind()方法虽然不能调用函数,但它也能改变函数内部的this指向,返回的是原函数改变this之后产生的新函数。如果只是想改变 this 指向,并且不想调用这个函数的时候,可以使用bind方法

call、apply、bind三者的异同

  • 共同点 : 都可以改变this指向

  • 不同点:

    • call 和 apply 会调用函数, 并且改变函数内部this指向.
    • call 和 apply传递的参数不一样,call传递参数使用逗号隔开,apply使用数组传递
    • bind 不会调用函数, 可以改变函数内部this指向.
  • 应用场景

    1. call 经常做继承.
    2. apply经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值
    3. bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向

7.数据类型问题

console.log(typeof null);//object
console.log(typeof {});//object
console.log(typeof []);//object
console.log(typeof undefined);//undefined

8.事件循环

function printing() {
   console.log(1);
   setTimeout(function() { console.log(2); }, 1000);
   setTimeout(function() { console.log(3); }, 0);
   console.log(4);
}
printing();

答案:1 4 3 2
原博主回答:

想知道为什么输出顺序是这样的,你需要弄了解setTimeout()做了什么,以及浏览器的事件循环原理。浏览器有一个事件循环用于检查事件队列,处理延迟的事件。UI事件(例如,点击,滚动等),Ajax回调,以及提供给setTimeout()和setInterval()的回调都会依次被事件循环处理。因此,当调用setTimeout()函数时,即使延迟的时间被设置为0,提供的回调也会被排队。回调会呆在队列中,直到指定的时间用完后,引擎开始执行动作(如果它在当前不执行其他的动作)。因此,即使setTimeout()回调被延迟0毫秒,它仍然会被排队,并且直到函数中其他非延迟的语句被执行完了之后,才会执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值