最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 翻译系列之你能回答7个闭包的面试题么?

    正文概述 掘金(iFency)   2021-02-16   544

    作为js开发,必须知道闭包是什么。在前端面试中,很可能会被问到闭包的概念。

    我整理了7个有趣且比较有难度的问题。

    准备好一只笔和一张纸,尽量不看答案或者敲代码运行。我估算你大概需要30分钟。

    尽情开始吧!

    如果你需要学习闭包,我推荐看闭包简单的例子

    目录

    1、哪个是闭包

    2、参数问题

    3、谁的谁

    4、棘手的闭包

    5、消息对还是错

    6、恢复封装

    7、智能相乘

    小结

    1、哪个是闭包

    思考一下三个函数:clickHandler, immediate 和 delayedReload:

    let countClicks = 0;
    button.addEventListener('click', function clickHandler() {
      countClicks++;
    });
    
    const result = (function immediate(number) {
      const message = `number is: ${number}`;
      return message;
    })(100);
    
    setTimeout(function delayedReload() {
      location.reload();
    }, 1000);
    

    问题:以上哪个是闭包以及为什么?

    答案:
    
    判断是否是闭包的简单规则就是,一个函数是否能访问外部函数的变量
    1、clickHandler函数是闭包,因为它能访问外部的countCLicks。
    2、immediate函数不是闭包,因为它没有访问到外部的任何一个变量。
    3、delayedReload函数是闭包,因为它访问到全局变量location,也就是最顶层的函数域。
    

    2、 参数问题

    以下代码打印出什么?

    (function immediateA(a) {
      return (function immediateB(b) {
        console.log(a); // 打印出什么
      })(1);
    })(0);
    
    答案:
    
    打印出 0
    因为immediateA函数的参数是0,因此传输给a为0.
    然后immediateB又是在immediateA的函数里,而且它是一个闭包的,所以immediateB里的a能访问到外面immediateA的a,所以打印出 0
    

    3: 谁的谁

    以下的代码块打印出什么

    let count = 0;
    (function immediate() {
      if (count === 0) {
        let count = 1;
        console.log(count); // What is logged?
      }
      console.log(count); // What is logged?
    })();
    
    答案:
    
    打印出 1 和 0
    因为一开头声明了count = 0,然后在immediaye函数是一个闭包,因为它的count能访问到一开头声明的那个count,所以此时count是0,然后在条件块上,因为满足count===0的条件,所以进入条件块里,然后因为let具有块级作用域,所以用let声明count时,此时的count为1,所以第一个console.log(count)打印出1
    第二个console.log(count)因为是在immediate函数里,而count是会访问到外部的count,也就是一开头声明的那个count,所以为0
    

    4: 棘手的闭包

    以下的代码块打印出什么

    for (var i = 0; i < 3; i++) {
      setTimeout(function log() {
        console.log(i); // What is logged?
      }, 1000);
    }
    
    答案:
    
    打印出 3,3,3
    该代码块执行有两个阶段:
    阶段一:
    1、for循环有3次,每次循环时都会创建一个新的log函数,而log函数里的setTimeout()是在1000ms后开始执行的。
    2、循环完成后,i就变成3,而setTimeout还没开始执行的。
    阶段二:
    第二个就是发生在1000ms后:
    1、setTimeou()就开始执行的,因为是闭包的,所以里面的i能访问到外部的i,而外部的i此时就是3,所以打印出3,之后的setTimeou也是如此的。
    这就是为什么打印出3,3,3的原因。
    

    挑战另一个问题:如何让这个代码块打印出0,1,2?请在下面的评论写下你的答案。

    5: 消息对还是错

    以下代码块打印出什么

    function createIncrement() {
      let count = 0;
      function increment() { 
        count++;
      }
    
      let message = `Count is ${count}`;
      function log() {
        console.log(message);
      }
      
      return [increment, log];
    }
    
    const [increment, log] = createIncrement();
    increment(); 
    increment(); 
    increment(); 
    log(); // What is logged?
    
    答案:
    
    打印出 Count is 0
    increment被调用3次,每次count都+1,3次后就成为3.
    message变量是在createIncrement函数内,它的初始化是“count is 0"。然而,即使count增加1,message始终保持“count is 0"
    log函数是一个闭包,它能访问到外部的message,所以打印出“count is 0"
    

    挑战另一个问题:如何让message同步显示count的数?请在下面的评论写下你的答案。

    6、恢复封装

    createStack函数创建stack的实例:

    function createStack() {
      return {
        items: [],
        push(item) {
          this.items.push(item);
        },
        pop() {
          return this.items.pop();
        }
      };
    }
    
    const stack = createStack();
    stack.push(10);
    stack.push(5);
    stack.pop(); // => 5
    
    stack.items; // => [10]
    stack.items = [10, 100, 1000]; // 破坏封装
    

    这stack运行看起来正常的,但有一个小小的问题,items属性被暴露了,所以任何人能直接修改这个属性。

    这确实会破坏stack的封装,按理来说应该只有push和pop方法被公开的,而items就不应该被公开的。

    利用闭包的概念来重构上面的createStack函数,实现items不能被初createStack函数之外访问。

    function createStack() {
      // 写下你的代码
    }
    
    const stack = createStack();
    stack.push(10);
    stack.push(5);
    stack.pop(); // => 5
    
    stack.items; // => undefined
    
    答案:
    
    function createStack() {
      const items = [];
      return {
        push(item) {
          items.push(item);
        },
        pop() {
          return items.pop();
        }
      };
    }
    const stack = createStack();
    stack.push(10);
    stack.push(5);
    stack.pop(); // => 5
    stack.items; // => undefined
    

    7、智能相乘

    在multiply函数内,写下两个数相乘。

    function multiply(num1, num2) {
      // Write your code here...
    }
    

    如果multiply有两个参数,则返回两个参数相乘的结果

    如果multiply只有一个参数,比如说const anotherFunc = multiply(num1),则返回anotherFunc函数,然后anotherFunc函数又赋值给一个参数num2,则返回num1 * num2的结果

    multiply(4, 5); // => 20
    multiply(3, 3); // => 9
    
    const double = multiply(2);
    double(5);  // => 10
    double(11); // => 22
    
    答案:
    
    function multiply(number1, number2) {
      if (number2 !== undefined) {
        return number1 * number2;
      }
      return function doMultiply(number2) {
        return number1 * number2;
      };
    }
    multiply(4, 5); // => 20
    multiply(3, 3); // => 9
    const double = multiply(2);
    double(5);  // => 10
    double(11); // => 22
    
    

    小结

    对比你的答案:

    1、如果正确有5个以及以上,说明你的理解能力强;

    2、如果没达到,我推荐你看看闭包的例子

    还想挑战吗?来试试这个7个比较难的js面试题


    起源地下载网 » 翻译系列之你能回答7个闭包的面试题么?

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元