前端面试题集

1、手动实现一个repeat方法
function repeat(func,times,wait){
//todo
}
const repeatFunc = repeat(console.log,4,1000);
// repeatFunc(‘hello’); 调用repeatFunc方法,每个1秒打印一次,一共打印4次

解析:调用方法时可传参,说明repeat返回的是一个function

① 使用setTimeout实现

function repeat(func, times, wait) {
      return function callback(val) {
        func.call(this, val);
        times-- > 1 && setTimeout(() => {
          callback(val)
        }, wait)
      }
    }
    
const repeatFunc = repeat(console.log, 4, 1000)
repeatFunc('hello')

② 使用setInterval实现

    function repeat(func, times, wait) {
      return function (val) {
        let count = 0;
        //func(val);如果一开始就打印 就先执行一遍再使用计时器,后面次数减1
        let timer = setInterval(() => {
          count++;
          count < times ? '' : clearInterval(timer)
          func(val);
        }, wait)
      }
    }

    const repeatFunc = repeat(console.log, 4, 1000)
    repeatFunc('hello')

2、给定起始日期,返回中间所有月份

 	function getMonths(str1, str2) {
      let arr = [];
      let ms = Math.ceil((new Date(str1) - new Date(str2)) * -1 / 1000 / 3600 / 24 / 30)
      console.log(ms)
      for (let i = 1; i < ms- 1; i++) {
        let ym= str1.split('-');
        let mVal = (((~~ym[1] + i) % 12) ? ((~~ym[1] + i) % 12) : 12);
        arr.push((~~ym[0] + (((~~ym[1] + i) % 12) ? Math.floor((~~ym[1] + i) / 12) : Math.floor((~~ym[1] + i) / 12) - 1)) + '-' + ( mVal>9?mVal:'0'+mVal))
      }
      return arr
    }

	getMonths('2020-01', '2020-06')
    // 输出  ["2020-02", "2020-03", "2020-04", "2020-05"]

3、Promise.resolve(obj) ,obj有有几种可能?
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve

4、如何把真实DOM转为虚拟DOM
https://blog.csdn.net/piano9425/article/details/104553403/

5、实现一个 函数将中文数字转成数字

  function zhToNum(str) {
    let numMap = {
      '零': 0, '一': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '七': 7, '八': 8, '九': 9,
      '壹': 1, '贰': 2, '叁': 3, '肆': 4, '伍': 5, '陆': 6, '柒': 7, '捌': 8, '玖': 9
    }
    let arr = []
    for (let i of [...str]) {
      arr.push(numMap[i])
    }
    return arr.join('')
  }
  zhToNum('零叁陆四玖八')

或者用Map来实现
  function zhToNum(str){
    let numMap = new Map([['零',0],['一',1],['二',2],['三',3],['四',4],['五',5],['六',6],['七',7],['八',8],['九',9],
      ['壹',1],['贰',2],['叁',3],['肆',4],['伍',5],['陆',6],['柒',7],['捌',8],['玖',9]])
    let arr = []
    for(let i of [...str]){
      arr.push(numMap.get(i))
    }
    return arr.join('')
  }
  zhToNum('零叁陆四玖八')

6、for…in 和 object.keys 区别
for…in一般用于对象属性的遍历

let obj = {
    a:1,
    b:2,
}
for(let key in obj){
    console.log(key)    
}
// a
// b

Object.keys()方法会返回 一个对象自身可枚举属性的数组,数组中属性名称的顺序和for…in的顺序一致

let obj = {
    a:1,
    b:2,
}
console.log(Object.keys(obj))
// ["a", "b"]

两者之间最主要的区别就是Object.keys( )不会走原型链,而for in 会走原型链

Object.prototype.test = ‘01';

var obj= {
    a:1,
    b:2,
}
//Object.keys不会输出原型链中的数据;
console.log(Object.keys(obj))
// ["a", "b"]

for(var key in obj){
    console.log(key)
}
// a
// b
// test    //for in 会把原型链中test 输出

7、react中context的理解
https://segmentfault.com/a/1190000017758300

8、一个人一次只能走一级或者两级楼梯,问走到第80级楼梯一共有多少种方法?
注:动态规划的简单例子(斐波那契数列)

function climb(){
	const res = [1,1,2]
	if(n<3) return res[n];
	for(let i=3;i<=n;i++){
		res[i] = res[i-1]+res[i-2]
	}
	return res[n]
}
climb(80) 

9、利用 Generator 函数和for…of循环,实现斐波那契数列

function* fibonacci() {
  let [prev, curr] = [0, 1];
  for (;;) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

for (let n of fibonacci()) {
  if (n > 1000) break;
  console.log(n);
}

10、原生js实现图片懒加载
https://zhuanlan.zhihu.com/p/55311726

11、目前主流的js模块化实现的技术
流行的js模块化规范有CommonJS、AMD、CMD以及ES6的模块系统
各技术的区别

12、二分查找 迭代和递归方式实现


//二分查找的递归算法
	public static int bSearch(int[] a, int x, int left, int right) {
		//index记录找到元素的下标,初始状态是-1
		int index = -1;
		
		if(left <= right){
			int mid = (left+right) / 2;
			if(x > a[mid]) {
				index = bSearch(a, x, mid+1, right);
			} else if(x < a[mid]) {
				index = bSearch(a, x, left, mid-1);
			} else {
				return mid;
			}
		}
		
		return index;
	}
	/*非递归的折半查找*/
	public static int binarySearch(int[] a, int x) {
		int left = 0;
		int right = a.length - 1;
		
		while(left <= right) {
			int mid = (left+ right) / 2;
			if(x > a[mid]) {
				left = mid+1;
			} else if(x < a[mid]) {
				right = mid-1;
			} else {
				return mid;
			}
		}
		
		return -1;
	}

每次迭代在循环内的所有工作话费O(1),因此分析需要确定循环的次数。循环从right-left=leng-1开始并在right-left<= -1结束。每次循环后right-left的值至少将该循环前的值折半;于是,循环的次数最多是[log(n-1)]+2。因此:运行的时间是O(log N),而递归的时间复杂度则需要O(N)。

13、尾递归
函数调用自身,称为递归。如果尾调用自身,就称为尾递归。
递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。

function factorial(n) {
  if (n === 1) return 1;
  return n * factorial(n - 1);
}

factorial(5) // 120

上面代码是一个阶乘函数,计算n的阶乘,最多需要保存n个调用记录,复杂度 O(n) 。

如果改写成尾递归,只保留一个调用记录,复杂度 O(1) 。

function factorial(n, total) {
  if (n === 1) return total;
  return factorial(n - 1, n * total);
}

factorial(5, 1) // 120

Fibonacci 数列 非尾递归和尾递归实现:

//非尾递归
function Fibonacci (n) {
  if ( n <= 1 ) {return 1};

  return Fibonacci(n - 1) + Fibonacci(n - 2);
}

Fibonacci(10) // 89
Fibonacci(100) // 超时
Fibonacci(500) // 超时
//尾递归
function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
  if( n <= 1 ) {return ac2};

  return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}

Fibonacci2(100) // 573147844013817200000
Fibonacci2(1000) // 7.0330367711422765e+208
Fibonacci2(10000) // Infinity

尾递归优化(递归转循环):

function tco(f) {
  var value;
  var active = false;
  var accumulated = [];

  return function accumulator() {
    accumulated.push(arguments);
    if (!active) {
      active = true;
      while (accumulated.length) {
        value = f.apply(this, accumulated.shift());
      }
      active = false;
      return value;
    }
  };
}

var sum = tco(function(x, y) {
  if (y > 0) {
    return sum(x + 1, y - 1)
  }
  else {
    return x
  }
});

sum(1, 100000)
// 100001

上面代码中,tco函数是尾递归优化的实现,它的奥妙就在于状态变量active。默认情况下,这个变量是不激活的。一旦进入尾递归优化的过程,这个变量就激活了。然后,每一轮递归sum返回的都是undefined,所以就避免了递归执行;而accumulated数组存放每一轮sum执行的参数,总是有值的,这就保证了accumulator函数内部的while循环总是会执行。这样就很巧妙地将“递归”改成了“循环”,而后一轮的参数会取代前一轮的参数,保证了调用栈只有一层。

14、全局上下文GO与函数上下文AO
前几天在网上逛帖子的时候,看了几个Js的题,发现都是关于GO与AO的一些应用,正好自己也重新回顾一下。
先看题:(PS:如果有兴趣做做题的小伙伴,可以在下方评论说出自己第一感觉的答案哦)

1.

var a = 1;
    function a(){
      console.log(“aa”);
    };
    function test(){};
    console.log(a);
  2.
    function test(a){
      console.log(a);
      var a = 1;
      console.log(a);
      function a(){};
      console.log(a);
      var b = function(){};
      console.log(b);
      function d(){}

}
    test(2);

答案:
    第一个:输出a为:1
    第二个:输出:function a(){},1,1,function (){}
  
  下面简单的说一下GO与AO
    GO:全局上下文,Global Object,它在全局代码执行前产生
    产生的时候做了什么呢?分为以下几个步骤:
       1:寻找变量声明
       2:寻找函数声明,赋值
       3:代码执行
    AO:Activation Object 活跃对象,函数上下文,在函数执行前产生
    产生的时候做了什么呢?分为以下几个步骤:
       1:寻找形参和变量声明
       2:实参赋值给形参
       3:寻找函数声明,赋值
       4:代码执行
  
  分析第一题:
    主要是GO
      GO = {
        a:undefined  (寻找变量声明)
         ->function a(){}  (寻找函数声明赋值)
         ->1  (代码执行)

test:function test(){}  (寻找函数声明赋值)
      }
      所以代码执行到console时,此时a已经被赋值1,输出1

第二题:
      首先是GO
      GO = {
        test:function test(){…}  (寻找函数声明赋值)
      }
      当test函数要执行时,创建AO
      AO = {
        a:undefined  (寻找形参和变量声明)
         ->2      (实参赋值给形参)
         ->function a(){…} (寻找函数声明,赋值)
         ->1      (代码执行)
        b:undefined  (寻找形参和变量声明)
         ->function(){…} (代码执行)
        d:function d(){…} ()  (寻找函数声明,赋值)
      }
      首先看第一个a的输出:由于输出是在a=1之前,所以输出function a(){}
         第二个a的输出:在a=1之后,赋值完成,所以输出1,
         第三个a的输出:由于function a(){}在AO中已经赋过值,此时直接跳过,输出1
         b输出:function(){…}
   
   最后有道题,小伙伴们自己分析分析,看看结果和自己想的是不是一样(提示要同时考虑GO与AO哦)
      a = 1;
      function test(e){
        function e(){};
        arguments[0] = 2;
        console.log(e);
        if(a){
          var b = 3;
        }
        var c;
        a = 4;
        var a;
        console.log(b);
        f = 5;
        console.log©;
        console.log(a);
      }

var a;
      test(1);
      console.log(a);
      console.log(f);
      最后结果是:2,undefined,undefined,4,1,5

15、手动实现一个json.parse()。
① eval方法实现

var json = '{"a":"1", "b":2}';
var obj = eval("(" + json + ")");  // obj 就是 json 反序列化之后得到的对象

② Function方法实现

function parse(jsonStr){
	return new Function('return'+jsonStr)()
}
parse('{"a":"2","b":""}')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值