• 前端常见面试题


    1.关于防抖/节流

    题目要求很明确,要求最终输出“searchText1”,并且在500ms后输出“searchText7”,很明显就是关于同一个函数在短时间内重复调用,如何限制其调用频率的功能实现。

     1     function throttle(action, threshold) {
     2         // TODO:
     3     }
     4 
     5     let timer = true;
     6     const triggerScroll = throttle((val) => {
     7         console.log(val);
     8     }, 300);
     9 
    10     triggerScroll("searchText1");
    11     triggerScroll("searchText2");
    12     triggerScroll("searchText3");
    13     triggerScroll("searchText4");
    14     triggerScroll("searchText5");
    15     triggerScroll("searchText6");
    16 
    17     setTimeout(() => {}, 500);
    18 
    19     // 期望输出结果:
    20     // searchText1
    21     // 500ms 后输出 searchText7

    首先,拿到题目后我们先运行一下,运行后报错,显示“triggerScroll is not a function”;由于题目中throttle函数没有返回值,所以triggerScroll接收到的throttle返回值为undefined,首先我们要在throttle函数中return一个函数,也就是throttle在调用时传进去的第一个参数。

    1  function throttle(action, threshold) {
    2     return action
    3  }

    这时再运行,控制台打印“searchText1-6”;这时我们只需要把防抖的通用模板套进去就可以实现了,我这里用的防抖,其实使用节流函数也是可以实现的,原理相同,我这里就不展示了,完整代码如下:

     1     function throttle(action, threshold) {
     2         return action
     3     }
     4 
     5     let timer = true;
     6     const triggerScroll = throttle((val) => {
     7         if (timer) {
     8             timer = false;
     9             setTimeout(() => {
    10                 timer = true;
    11                 console.log(val);
    12             }, 300)
    13         } else {
    14             return false;
    15         }
    16     }, 300);
    17 
    18     triggerScroll("searchText1");
    19     triggerScroll("searchText2");
    20     triggerScroll("searchText3");
    21     triggerScroll("searchText4");
    22     triggerScroll("searchText5");
    23     triggerScroll("searchText6");
    24 
    25     setTimeout(() => {
    26         triggerScroll("searchText7");
    27     }, 500);

    另附上防抖和节流的通用模板:

    //防抖
    let timeid = null;
    function debounce() {
        if (timeid) {
            clearTimeout(timeid);
        }
        timeid = setTimeout(() => {
            //这里写操作
            console.log(Math.random());
        }, 500);
    
    }
    window.onscroll = debounce;//窗口滚动事件防抖
    //节流
    let timeid = null;
    function debounce(func, wait = 0) {
        if (!timeid) {
            timeid = setTimeout(() => {
                console.log(Math.random());
                timeid = null;
            }, 500);
        }
    }
    window.onscroll = debounce;

     2.js预编译以及运行流程

    最终输出结果为“1 1 2 0”。这一题也会涉及到闭包,作用域等知识点。

     1 var i = 0;
     2 function fn() {
     3     i = 1;
     4     console.log(i);
     5     var i;
     6     return function () {
     7         console.log(i);
     8         i += 1;
     9     }
    10 }
    11 
    12 var fun = fn();//1
    13 fun();//1
    14 fun();//2
    15 console.log(i);//0

    首先我们需要了解js运行流程:首先进行语法解析;先通篇的检查js语法,若语法有误,则报错。其实进行预编译;即发生在代码执行之前,为代码的执行做的准备工作,一般表现于变量提升,即把变量的声明提前,但是赋值还是在响应位置赋值,再者就是把整个函数声明提前。最后js运行流程进入最后一步即解析执行。

    搞懂js运行流程再区分作用域,即全局作用域和函数作用域,全局作用域顾名思义就是全局声明的变量或函数等。函数作用域也就是函数内部的作用域,在函数内部中声明的变量或函数是不会传到全局作用域的。

    搞懂这两个概念这一题就能够迎刃而解了,首先全局声明了一个变量i并赋值为0,在声明一个fun接受fn执行后的返回值,所以fn会执行一次,局部变量i在fn函数中重新声明,变量提升,这时函数fn中使用的i都为函数作用域中声明的i,并且重新赋值为1,输出结果1。这里不会影响到全局变量i的值。再观望整体代码,全局变量i未被重新赋值,所以最后一行i输出为0.

    再往下走,fu函数的返回值也是一个函数,所以fun接收到的是一个函数,fun函数被执行两次,注意到在fn函数中,i被重新声明过,所以这里的返回值函数即fun函数的作用域为局部作用域,使用的变量i为局部变量,即再fn函数中声明的i,所以输出1。输出之后i自增1,即 i = i+1,局部作用域i的值在fun函数弟一次执行后变为2,再次执行后,输出i为2。


    3.值类型和引用类型

    这一题主要涉及到值类型和引用类型的存储方式,即堆内存和栈内存。

    1 function fn(obj) {
    2     obj.name = "name";
    3     obj = new Object();
    4     obj.name = "newName";
    5 }
    6 var xt = new Object();
    7 fn(xt);
    8 console.log(xt.name);//name

    首先题目中声明了一个函数fn和一个对象xt,调用fn函数,并把xt传入函数。这是第二行的obj和xt都为引用类型,新增了obj的属性name值也为name。由于此时obj和xt指向的同一片内存空间,所以xt也会新增name属性,并设置值为name。故最后一个输出name。

    在第三行中可以看到obj被重新赋值,即重新开辟了一片内存空间,这时obj依然为新增name属性而不是修改name属性,并将name属性设置为newName,所以这是obj新增的属性不会影响到对象xt的属性。


    4.同步和异步

    这一题主要是考察异步代码和同步代码执行的先后顺序以及js任务队列。

     1    for (let index = 0; index < 5; index++) {
     2         setTimeout(() => {
     3             console.log(index);
     4         }, 200);
     5     }
     6 
     7     for (let j = 0; j < 5; j++) {
     8         setTimeout(() => {
     9             console.log(j);
    10         }, 200);
    11     }

    首先可以看到题目中有两个循环体,两个循环体中分别有两个延时200ms的延时器。做题之前我们应该先了解到JavaScript对于同步代码和异步代码的处理方式,js会先执行同步代码,遇到异步代码(例:延时器、计时器、Promise等)会先放置任务队列,等待触发条件依次执行。这个概念了解清楚后这题就能够迎刃而解了。

    首先两个循环体为同步代码,会先执行。此时第一个循环体会将循环出的五个延时器放置任务队列,等待200ms后执行。第二个循环体也是如此,所以输出为“0 1 2 3 4  0 1 2 3 4”。


    5.宏任务和微任务

    此题主要是考察对于同步异步,以及宏任务和微任务的理解。

    //写出执行结果
    setTimeout(() => {
        console.log(1);
    }, 0);
    
    new Promise((res) => {
        console.log(2);
        for (var index = 0; index < 1000; index++) {
            index = 9999 && res()
    
        }
        console.log(3);
    }).then(() => {
        console.log(4);
    })
    console.log(5);

    此题最终输出结果为“2 3 5 4 1”;

    解答此题我们需要先了解到Promise的特性。Promise是异步编程的一种解决方案,是解决恐怖回调/回调噩梦的一种方案,其就相当于一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

    创建promise对象,function就相当于容器,用于存放异步代码(不仅限于异步,同步也可以),此函数在页面加载时会正常执行,同步代码会进入主线程执行,异步代码会进入任务队列,未来才会得到结果。并且Promise有三种状态,进行时(pending默认值),已成功(fulfilled/resolved),已失败(rejected)一旦状态改变,就不会再变,任何时候都可以得到这个结果,成功后会调用.then()方法,then方法接受两个参数,皆为回调函数,第一个函数promise状态变为已成功时触发的,第二个函数promise状态变为已失败时触发。

    了解了Promise后,我们还需要搞清楚宏任务和微任务的概念。在上面我们提到过,js会见异步代码放置任务队列,宏任务是由宿主(Node、浏览器)发起的,常见的就是计时器和延时器;而微任务是由js引擎发起的,常见的就时Promise;执行顺序为微任务先执行,宏任务后执行。

    这时我们就可以来解答这个题目了,首先遇到延时器,为宏任务,放入任务队列,其实遇到promise,先执行promise的同步代码,输出2,再进入循环,第一次进入循环index被重新赋值为9999,并且将promise的状态改为已成功,再往下走输出3;

    此时promise状态为已成功,触发then方法的第一个函数,但是promise响应为异步响应。并且为微任务,放置任务队列,排在宏任务之前。再往下走就输出5。

    此时同步代码执行完毕,开始执行异步代码,先执行微任务,输出4,在执行延时器代码。输出5。


    好啦。以上就是小谭今天分享的内容啦,也欢迎各位一起来讨论技术呀!文章中有不足的地方小谭也欢迎各位指正探讨,不胜感激!

  • 相关阅读:
    go语言浅析二叉树
    Go语言冒泡、选择、插入、快速排序实战浅析
    go语言教程之浅谈数组和切片的异同
    实测Maven上传jar包到私服的方法归纳
    干货|Dubbo社区开发者日经验分享
    Go语言系列开发之延迟调用和作用域
    mysql中间件分享(Mysql-prxoy,Atlas,DBProxy,Amoeba,cobar,TDDL)
    时间操作
    大型网站架构设计方向初探
    C#输入法
  • 原文地址:https://www.cnblogs.com/xt-2020/p/15406545.html
Copyright © 2020-2023  润新知