JS必会的面试题3

55 阅读3分钟

谈谈闭包和闭包使用场景

什么是闭包

  • 概念闭包是作用域的一种特殊应用

触发闭包的情况

  1. 函数被当做返回值被返回
  2. 函数被当做参数被传递
  3. 自执行匿名函数
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // 情况1 函数被当成参数被返回
        function fn(){
            const a = 100
            return function(){
                console.log(a);
            }
        }
        const a = 500
       const cd = fn()
       cd()
        // 情况2:函数被当做参数传递
        function fn(cd){
            const a= 100
            cd()
        } 
        const a = 500
        fn(function (){
            console.log(a);//500 向外层作用域找
        })
        //情况3 自执行匿名函数
        (function(index){
            console.log(index);
        })(10)
    </script>
</body>
</html>

闭包的应用

  1. 隐藏变量
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>闭包隐藏变量</title>
</head>
<body>
    <script>
        function fn(){
            const data = {}
            return {
                set:function(key,val){
                    data[key] = val
                },
                get:function(val){
                    return data[val]
                }
            }
        }
        //隐藏了变量data 在外层作用域中无法调用data
        let json = fn()
        json.set("hello",111)
        json.get("hello")
        console.log(json.get("hello"));
    </script>
</body>
</html>
  1. 解决for i 的问题
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>闭包函数应用场景</title>
    <style>
        button {
            width: 100px;
            height: 100px;
        }
    </style>
</head>

<body>
    <button>1</button>
    <button>2</button>
    <button>3</button>
    <button>4</button>
    <script>
        // 闭包可以解决for...i问题
        // 方法1 自执行函数
        var btn = document.getElementsByTagName("button")
        // for (var i = 0; i < btn.length; i++) {
        //     (function (i) {
        //         btn[i].onclick = function () {
        //             console.log(i);
        //         }
        //     })(i)
        // }
        // 方法2 let方法可以创建块级作用域 拥有自己的作用域
        for(let i =0;i<btn.length;i++){
            btn[i].onclick = function(){
                console.log(i);
            }
        }
    </script>
</body>

</html>

作用域

  • 全局作用域 局部作用域

自由变量

  • 不在自己作用域里的变量 就是自由变量
  • 自由变量的值 在函数定义的地方向上层查找 与函数调用位置无关

javascript是如何执行的

  • 自上而下 从左到右一行一行的执行
  • 如果中间有一行报错 下面的代码不执行
  • 先执行同步代码 再执行异步代码(settimeout、ajax)

event loop

  1. 同步代码
  2. 遇到异步 会先记录下代码 (放入到webapi中)等待执行时机(settimeout,ajax)时机到了 将之前的代码放入 callback queue(回调函数调用栈
  3. 当 call stack 为空(同步代码执行完)event loop开始工作
  4. Event Loop轮询查找 callback queue(回调函数调用栈)中是否有可执行的代码 如果有 将代码移动到callstack中执行
  5. event loop如果没有找到可以执行的代码 则会继续轮询查找

image.png

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // 代码执行顺序:从左到右 从上到下
        // 如果有一行报错 后面代码不执行
        // 先执行同步代码 再执行异步代码

        //event loop过程
        // 1.同步代码 一行一行放在call stack(调用栈)中执行
        // 2.遇到异步 会先记录下代码 先将异步代码放入到webAPI中 等待执行时机(settimeout ajax)时机到了 将之前的代码放入callback queue(回调函数队列)
        // 3.当call stack为空 (同步diamante执行完)event loop开始工作
        // 4.event loop轮询查找 是否有可执行的代码 如果有将代码移动到 call stack中执行
        // 5.event loop如果没有找到可以执行的代码 则会继续轮询查找
        console.log("stark");
        setTimeout(()=>{
            console.log("settimeout");
        },2000)
        console.log("end");
    </script>
</body>
</html>

么是宏任务和微任务?有什么区别

什么是宏任务 什么是微任务

  • 微任务:promise async....await
  • 宏任务 settimeout setinterval Ajax Dom

二者的区别

  • 先执行微任务 再执行宏任务

宏任务 微任务 Don渲染的关系

  1. call stack清空 触发event loop
  2. 执行微任务
  3. 尝试Dom渲染
  4. 执行宏任务

完整的event loop过程

image.png

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@说人话的前端 - bilibili</title>
    <style>
      div {
        width: 200px;
        height: 200px;
        border: 5px solid red;
      }
    </style>
  </head>
  <body>
    <h1>宏任务与微任务</h1>
    <div id="div1"></div>
  </body>
  <script>
    console.log("start");
    console.log("end");
    setTimeout(() => {
      // alert("setTimeout");
      console.log("setTimeout");
    }, 0);
    Promise.resolve().then(() => {
      // alert("Promise");
      console.log("promise");
    });
  </script>
</html>

手写promise加载图片

出题目的

  • 考察image对象
  • 考察promise
  • 考察async await

知识点

image对象

  • new image()————声明一个image对象
  • onload ————当图片加载成功时执行
  • oneerror ————当图片加载失败时执行
  • src ————设置图片路径

promise

  • 作用 解决回调地狱的问题
  • 语法 :new Promise().then().catch()

async await

  • 作用 promise的语法糖 可以增加代码可读性(用同步的思维写代码)
  • 语法
        (async function(){
            try{
                const oimg = await loadImg("https://robohash.org/1")
                document.body.appendChild(oimg)
                const oimg2 = await loadImg("https://robohash.org/2")
                document.body.appendChild(oimg2)
            }catch(e){
                console.log(e);
            }
        })()
   
        第一种写法
      const op = new Promise((resolve,reject)=>{
          const img = new Image()
          img.src = "https://robohash.org/1"
          img.onload = ()=>{
              resolve(img)
          }
          img.onerror = ()=>{
              reject("图片加载失败")
          }
      })
      op.then((res)=>{
          document.body.append(res)
      }).catch((rej)=>{
          console.log(rej);
      })
      第二种写法
      function loadImg(oimg) {
         return new Promise((resolve, reject) => {
              const img = new Image()
              img.src = oimg
              img.onload = () => {
                  resolve(img)
              }
              img.onerror = () => {
                  reject("图片加载失败")
              }
          })
      }
      loadImg("https://robohash.org/1").then((res) => {
          document.body.appendChild(res)
          return loadImg("https://robohash.org/2")
      }).then((res)=>{
          document.body.append(res)
      })
      .catch((rej) => {
          console.log(rej);
      })
      
      function loadImg(src){
          return new Promise((resolve,reject)=>{
              const img = new Image()
              img.src = src
              img.onload = ()=>{
                  resolve(img)
              }
              img.onerror = ()=>{
                  reject("图片加载错误")
              }
          })
      }

      (async function(){
          try{
              const oimg = await loadImg("https://robohash.org/1")
              document.body.appendChild(oimg)
              const oimg2 = await loadImg("https://robohash.org/2")
              document.body.appendChild(oimg2)
          }catch(e){
              console.log(e);
          }
      })()

promise阅读代码题

知识点

  • JS执行顺序 自上而下 先同步再异步 先微任务再宏任务
  • new promise() = promise.resolve()
  • then和catch内部没有throw new error相当于resolve
  • async function 单独使用相当于返回promise.resolve
  • await后面的代码都是异步的
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Promise阅读代码</title>
</head>

<body>
    <script>
        // Promise.resolve().then(()=>{
        //     console.log(1);
        // }).catch(()=>{
        //     console.log(2);
        // })//输出结果 1
        /*
            考点1
                then和catch内部没有 throw new error 相当于 resolve
        */
        // Promise.resolve().then(() => {
        //     console.log(1);
        // }).catch(() => {
        //     console.log(2);
        // }).then(() => {
        //     console.log(3);
        // })
        // 变种2 
        // Promise.resolve().then(()=>{
        //     console.log(1);
        //     throw new Error()
        // }).catch(()=>{
        //     console.log(3);
        // }).then(()=>{
        //     console.log(2);
        // })//输出结果 1 3 2 
        // 变种3 
        // Promise.reject()
        // .then(()=>{
        //     console.log(1);
        // }).catch(()=>{
        //     console.log(2);
        // }).then(()=>{
        //     console.log(3);
        // }) //输出结果  2 3

        //变种 4
        // Promise.reject()
        // .then(()=>{
        //     console.log(1);
        // }).catch(()=>{
        //     console.log(2);
        //     throw new Error()
        // }).then(()=>{
        //     console.log(3);
        // }) //输出结果2
        // .catch(()=>{
        //     console.log(4);
        // })//输出结果 2 4

        // 考点2 async function =>相当于返回一个promise.resolve
        // async function fn(){
        //     return 100
        // }
        // (async function(){
        //     const a = fn()
        //     const b = await fn()
        //     console.log(a);//async 单独使用返回一个promise对象
        //     console.log(b);//await async和await一起使用表示返回的值
        // })()
        // fn().then(()=>{
        //     console.log(0);
        // }).catch(()=>{
        //     console.log(1);
        // })   

        // 考点3 代码执行顺序
        // async function fn1(){
        //     console.log("fn1 start");
        //     await fn2();
        //     console.log("fn1 end");
        // }
        // async function fn2(){
        //     console.log("fn2 start");
        // }
        // console.log("start");
        // fn1()
        // console.log("end");
        /*
            执行顺序:
            1.start
            2.fn1 start
            3.fn2 start
            4.end
            5.fn1 end
        */

        // 变种1
        async function fn1() {
            console.log("fn1 start");
            await fn2()
            console.log("fn1 end");
            await fn3()
            console.log("fn3 end");
        }
        async function fn2() {
            console.log("fn2");
        }
        async function fn3() {
            console.log("fn3");
        }
        console.log("start");
        fn1()
        console.log("end");
        //await后面的代码全是异步
        /*
        执行结果:
        1.start
        2.fn1 start
        3.fn2
        4.end
        5.fn1 end
        6.fn3 
        7.fn3 end
        */

        //考点4 promise与settimeout执行顺序
        // console.log("start");
        // settimeout(()=>{
        //     console.log("settimeout");
        // })//宏任务
        // console.log("end");
        //执行结果 start end settimeout

        // 变种1
        async function fn1() {
            console.log("fn1 start");
            await fn2()
            console.log("fn1 end");//微任务
        }
        async function fn2() {
            console.log("fn2");
        }
        console.log("start");
        setTimeout(() => {
            console.log("settimeout");
        })//宏任务
        fn1()
        console.log("end");
        /*
        执行结果
        1.start
        2.fn1 start
        3.fn2
        4.end
        5.fn1 end
        6.settimeout
        */
    </script>
</body>

</html>

for..in、for..of 有什么区别?

  • for...in遍历后得到key 遍历可枚举数据:数组 字符串 对象
  • for...of遍历后得到value 遍历可迭代数据:数组 set map 字符串

是否可枚举

Object.getOwnPropertyDescriptors(obj)===>enumerble:true

是否可迭代

arrsymbol.iterator ===>next()

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //for...in遍历得到key 可枚举的数据:数组 字符串 对象
        //for...of得到的是value 可迭代的数据:数组 字符串 set map
        const arr = [10, 20, 30]
        for (let key in arr) {
            console.log(key);
        }
        for (let val of arr) {
            console.log(val);
        }
        // 是否可枚举
        //   Object.getOwnPropertyDescriptor(Array)
        //是否可迭代
        // arr[Symbol.iterator]() -> next()
        const obj = {
            name: "abc",
            age: 20
        }
        for (let key in obj) {
            console.log(obj[key]);
        }

        const set = new Set([10, 20, 30])
        for (let val of set) {
            console.log(val);
        }

        const map = new Map([
            ["a",10],
            ["b",20],
            ["c",30],
        ])
        for(let val in map){
            console.log(val);
        }
    </script>
</body>

</html>