前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺

这个题集是关于JS一些有点深度,且容易让人犯错的题目,每日更新一道,相信你们也能从中学到知识。

1.如下代码的输出结果:

 	  console.log(1 + "2" + "2");
      console.log(1 + +"2" + "2");
      console.log("A" - "B" + "2");
      console.log("A" - "B" + 2);

前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第1张图片
知识点:隐式强制类型转换。“+”可以进行强制类型转换,而“-”号没有。

2.下面关于块内声明函数的做法哪些是正确的?

A:   if(x){
     
       function foo(){
     
        //   ... 
       }
   }

B:   if(x){
     
       var foo=function(){
     
        //    ..
       }
   }

C:   if(x){
     
       foo=function(){
     
           //..
       }
   }

D: ECMAScript明确规范了块内函数,javascript实现了这个规范。

答案:B

解:一般我们尽量不要在if语句内声明函数,当必须声明函数时使用函数表达式,不要用函数声明方式。不用函数声明方式是为了防止函数声明提升,导致if条件没有起作用。

3.如何判断一个js对象是否是Array,arr是Array实例,请用最准确的方法判断:

let arr = ["小李"];

A: typeof arr
B: arr instanceof Array
C: arr.toString === "[object Array]"
D: Object.prototype.toString.call(arr) === "[object Array]"

 console.log(typeof arr);
 console.log(arr instanceof Array);
 console.log(arr.toString === "[object Array]");
 console.log(Object.prototype.toString.call(arr) === "[object Array]");

前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第2张图片
答案:D
在常规数组中,B、D都可以判别数组,但D是最准确的。

分析:

  • A: typeof(arr)返回的是一个Object
  • B: instanceof 在跨frame对象构建的场景下会失效
  • C: 这是个错误用法,且不能判断是否为数组,Arrays.toString()才是正确用法,返回一个字符串。

4.以下立即执行函数的执行结果:

(function (foo) {
     
        console.log(foo.bar);
        console.log(typeof foo.bar);
      })({
     foo: {
     
          bar: 1
          }
        });

前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第3张图片
这个答案有可能会有人认为是this指针造成的,但仔细看传入的参数就会得出正确结果。

传入后的参数,实际为

参数:foo={
     
		foo:{
     
			bar:1
		}
	};

所以正确调用是:

console.log(foo.foo.bar);
console.log(typeof foo.foo.bar);

5.下面代码中a在什么情况下会打印1:

	  var a=?;
      if(a==1&&a==2&&a==3){
     
          console.log(1);
      }

答案:

var a = {
     
        i: 1,
        toString: function () {
     
          return a.i++;
        },
      };
      
if (a == 1 && a == 2 && a == 3) {
     
        console.log(1);
      }

前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第4张图片
解析:
因为这里使用的是“= =”,会进行隐式类型转换;而不是 “ = = =”完全相等,所以这里重写toString()就可以了。隐式转换会默认调用toString()获取返回值,所以在判断a == 1 && a == 2 && a == 3时,就会三次调用toString()函数,所以依次返回1、2、3使得条件成立,打印1 。

6.以下代码会输出什么:

 	  var a = 10;
      (function () {
     
        console.log(a);
        a = 5;
        console.log(window.a);
        var a = 20;
        console.log(a);
      });

答案:
前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第5张图片
解析:在立即执行函数中,var a=20;语句定义了一个局部变量a,由于js的变量提升机制,局部变量a的声明会提升至立即执行函数体内的最上方,即第一句console.log(a)的上方。

注意:变量提升并不包括赋值,所以最先打印undefined。

7.下面两处打印输出什么:

	  var user = {
     
        count: 1,
        getCount: function () {
     
          return this.count;
        },
      };
      console.log(user.getCount());

      var func = user.getCount;
      console.log(func());

前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第6张图片
解析:这是this指针的问题。func是在全局window中被执行,由于全局中并没有count属性,所以访问不到this.count。如何确保user对象的属性总是能访问到,正确的方法使用时Function.prototype.bind

8.请输出以下代码的执行结果:

	const promise = new Promise((resolve, reject) => {
     
        console.log(1);
        resolve();
        console.log(2);
      });
      promise.then(() => {
     
        console.log(3);
      });
      console.log(4);

前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第7张图片
解析:Promise构造函数是同步进行的,promise.then中的函数是异步执行的。

9.为什么下面这个代码会爆栈,而使用setTimeout来实现就不会爆栈?

function foo(){
     
	foo();
}

解析:这样会爆栈是因为js的栈模型,由于采用栈调用函数,只有一个函数运行完成才会出栈,而上面的递归由于一直调用foo,所以foo不断进栈,且没有一个foo是执行完成出栈的,导致爆栈。

而当:

function foo(){
     
	setTimeout(()=>{
     
		foo();
	},10)
}

由于setTimeout是异步函数,所以当执行完setTimeout后,foo就执行完了,执行完则出栈,当同步执行完之后,才会回调setTimeout中的回调函数,此时新的foo方法才会进栈,一进一出所以不会爆栈。

10.直接往script标签里放值(即:< script >let a=“xxx”< /script >)和< script src=“a.js”>,有啥区别?

答案:

  1. 执行顺序可能不一样
  2. 外部链接的js文件能够在多个html中引入,会有更好的的扩展性和可维护性。而内部书写的js只能在当前html文件中使用。

11.React和Vue项目在列表组件中写key,作用是什么?

key是给每一个vnode的唯一标识,可以依靠key,更准确,更快的拿到oldVnode中对应的vnode节点。

  • 更准确是因为带key就不是就地复用了,在sameNode函数a.key===b.key对比中可以避免就地复用的情况。所以更加准确。
  • 更快是利用key的唯一性生成map对象来获取对应节点,比遍历方式更快。

12.已知以下副本,编写一个程序将它进行分隔化去除其中重复的部分数组,得到升序和不重复的数组:

副本:

var arr=[
	[11,22,22],
	[13,14,15,15],
	[16,17,18,19,[12,13,[14]]],
	12
]

答案:flat方法接收一个参数n,将深度为n的数组扁平化,去重可以通过set方法,该方法返回一个Set类数组对象,Array.from()方法就是将一个类数组对象或者可遍历对象转换成一个真正的数组。排序可以通过sort方法。

var newArray=Array.from(
	new Set(arr.flat(Infinity))
	).sort((a,b)=>{
     
		a-b;
})

13.请使用原型链相关知识实现b继承n=1;c继承n=2;m=3:

      var obj = function () {
     };

      obj.prototype.n = 1;
      var b = new obj();
      obj.prototype = {
     
        n: 2,
        m: 3,
      };
      var c = new obj();
      console.log(b.n, b.m, c.n, c.m);

14.实现一个函数add,满足以下的输出结果:

	add(1);//1
	add(1)(2);//3
	add(1)(2)(3);//6
	add(1)(2,3)//6
	add(1,2)(3);//6
	add(1,2,3)(4);//10

理解:即多次调用,比如add(1)(2)(3),先调用add(1),然后返回一个func函数,再调用func(2),然后同样返回一个函数,调用func(3)。

答案:

      function add() {
     
        var args = [...arguments];
        var func = function () {
     
          args.push(...arguments);
          return func;
        };
        func.toString = function () {
     
          return args.reduce((x, y) => {
     
            return x + y;
          });
        };
        return func;
      }
      console.log(add(1)(2)(3));
      console.log(add(1, 2, 3)(4));

前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第8张图片
15.一堆扑克牌,将牌堆第一张放到桌子上,再将接下来的牌堆的第一张放到牌底,如此交替;
最后桌上的牌顺序为:(牌底)1,2,3,4,5,6,7,8,9,10,11,12,13(牌顶);原来那堆牌的顺序,用函数实现。

答案:

      function reverse(arr) {
     
        let i = 1;
        let out = [];
        while (arr.length) {
     
          if (i % 2) {
     
            out.unshift(arr.pop());
          } else {
     
            out.unshift(out.pop());
          }
          i++;
        }
        return out;
      }
      console.log(reverse([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]));

前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第9张图片
16.写一个多重展开函数:如输入arr = [1, [2, [3, 4, 2], 2], 5, [6]],则输出[1,2,3,4,2,2,5,6]

答案(递归):

      var arr = [1, [2, [3, 4, 2], 2], 5, [6]];
      function flatten(arr) {
     
        var array = [];
        arr.forEach((item) => {
     
          if (Array.isArray(item)) {
     
            array.push(...flatten(item)); //如果item是一个数组,则进行递归,返回一个数组,使用扩展运算符展开数组
          } else {
     
            array.push(item);
          }
        });
        return array;
      }
      console.log(flatten(arr));

前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第10张图片
上面使用的是自己写的利用递归实现的,还有一种简单的方法:就是利用Array.flat(Infinity)的方法(在第12题使用过)

      var arr = [1, [2, [3, 4, 2], 2], 5, [6]];
      var array = arr.flat(Infinity);
      console.log(array);

前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第11张图片
17.Promise.all中任何一个Promise出错的时候都会执行拒绝,导致其他正常返回的数据,也无法使用。你有什么办法解决吗?

参考:
关键函数:warp函数

      const p1 = new Promise((resolve, reject) => {
     
        setTimeout(resolve, 200, {
      data: 1 });
      });
      const p2 = new Promise((resolve, reject) => {
     
        setTimeout(resolve, 500, {
      data: 2 });
      });
      const p3 = new Promise((resolve, reject) => {
     
        setTimeout(reject, 300, {
      data: 3 });
      });
      function warp(p) {
     
        return new Promise((resolve, reject) => {
     
          p.then((res) => {
     
            return resolve(res);
          }).catch(() => {
     
            resolve(false);
          });
        });
      }
      (function getData() {
     
        Promise.all([warp(p1), warp(p2), warp(p3)])
          .then((res) => {
     
            console.log("异步请求完成", res);
          })
          .catch((err) => {
     
            console.error("error:", err);
          });
      })();

前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第12张图片
即当Promise.all()中有一个promise出现错误时,使用resolve(false)保证所有promise都是resolved状态。

18.在JS中什么是伪数组?如何将伪数组转换为标准片段?

参考:

伪数组:伪数组是一个对象,这个对象必须有一个length属性,且length的值为Number类型,如果length值不为0,则这个对象的属性名必须使用下标(0~n),伪数组无法直接使用数组的内置方法。

  • 函数的参数对象:arguments
  • Dom对象列表(document.getElementsByTags)

看看函数的参数:

	  function getData() {
     
        console.log(arguments);
      }
      getData(1, "jaja", 6, "ko55");

前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第13张图片
那如何将伪数组转换为真数组呢:

var rightArrary= Array.prototype.slice.call(伪数组)

实例:

      function getData() {
     
        var rightArray = Array.prototype.slice.call(arguments);
        rightArray.push("我是真数组,可以使用数组内置方法push");
        const bool = Object.prototype.toString.call(rightArray) === "[object Array]";
        console.log("我是真数组吗?", bool);
        console.log(rightArray);
      }
      getData(1, "jaja", 6, "ko55");

前端疑难题 每日一题,365天就多掌握365个知识点 查漏补缺_第14张图片
19.nodejs从接收到请求到返回页面经历了什么?

参考:

  • 浏览器输入网址
  • 浏览器通过用户在地址重定向输入的URL合并HTTP请求报文
  • 浏览器发送DNS解析请求,将域名解析成对应的IP地址。
  • 浏览器将报文发送给服务器
  • 服务器接受并发解析请求报文
  • 服务器处理用户请求,并将处理结果封装成HTTP响应报文
  • 服务器将HTTP响应报文返回给浏览器
  • 浏览器接收响应报文并解析
  • 浏览器解析HTML页面并展示,在解析时遇到新的资源将再次发起请求
  • 最终生成一个完整页面。

你可能感兴趣的:(javaScript,vue.js,jquery,javascript,前端,面试)