阅读 207

js常见问题(javascript错误解决方案)

js常见问题

1.变量提升和函数提升,函数表达式和函数声明的区别

  • 会把var声明的变量提前到当前作用域声明,只提前声明,不提前赋值

  • 会提前声明函数,只提前声明,不提前调用

  • 当变量和函数同名时,先提升同名的变量,再提升同名的函数,函数的权重高(函数会覆盖变量)

  • 函数声明会提升,函数表达式不会提升(函数表达式会提升等号左边的变量为undefined,不会提升函数)

该题目考查同名变量和同名函数得提升,js的预解析和执行

a(); //我是函数a里面的log
console.log(a) //函数 
var a = 1;
console.log(a) //1
 function a() {
  console.log('我是函数a里面的log')
 }
 console.log(a) //1 fix这里为啥还是打印1


 // 预解析阶段
 // a undefined
 // a 函数

 // 执行阶段
 // a();
 // console.log(a);
 // a = 1;
 // console.log(a);复制代码
函数表达式和函数声明复制代码
 <!-- 题目一:函数表达式 -->
     // console.log(fn); //undefined

     // fn() //报错fn is not defined

     // var fn = function() {
     //     console.log('fn')

     // }



<!-- 题目二:函数声明 -->

     console.log(fn); //函数

     fn() //报错fn is not defined
     function fn() {
         console.log('fn')
     }复制代码

补充2道题目练习一下啦:

var a = 25;

 function abc() {
     alert(a); //undefined现在自己内部找  只提前声明,不提前赋值
     var a = 10;
 }
 abc();
 // 如果变量和函数同名的话,函数优先
 console.log(a); //25
 function a() {
     console.log('aaaaa'); //不打印,函数没有调用
 }
 a(); //报错 ,a 不是一个函数

 var a = 1;
 console.log(a); //1


 //  词法作用域的一个坑,可以画图理解
 var a = {
     x: 1
 }
 var b = a;
 a.x = a = {
     n: 1
 };
 // 运算符的优先级问题:
 // 算术运算符>关系运算符>逻辑运算符>赋值运算符
 console.log(a.x); //undefined 点运算符大于赋值运算符
 console.log(b.x); // {n : 1}复制代码

浅拷贝和深拷贝

浅拷贝

对象的浅拷贝
const obj = {
            name: "bwf",
            age: 18
        }
// 法一 Object.assign
const obj1 = Object.assign({}, obj);
obj1.name = 'wmy'
console.log(obj)

// 法二 扩展运算符
const obj2 = {
...obj1
}
obj2.name = 'wmy'
console.log(obj)复制代码
数组的浅拷贝
// 法一:扩展运算符
 const arr = [1, 2, 3]
 const arr2 = [...arr]
 arr2[0] = 9
console.log(arr, arr2)

// 法二 slice
const arr = [1, 2, 3]
const arr1 = arr.slice();
arr1[0] = 9
console.log(arr, arr1)

// 法三 concat
const arr = [1, 2, 3]
const arr3 = arr.concat()
arr3[0] = 9
console.log(arr, arr3)复制代码

深拷贝

   const person = {
           name: 'bwf',
           age: 18,
           job: {
               name: 'fe',
               salary: 14
           },
           sayHello() {
               console.log(`大家好,我叫${this.name}`)
           }
       }
       // Object.assign只实现了对象第一层的拷贝
       const p1 = Object.assign({}, person);
       p1.name = 'wmy'
       p1.job.salary = 20
       console.log(person)

   // 法一:递归拷贝
   function deepClone(obj) {
       // 定义一个新的对象或数组,遍历源对象或数组,复制到新对像或数组中
       const target = Array.isArray(obj) ? [] : {};
       if (obj && typeof obj === "object") {
           for (const key in obj) {
               // 如果对象的属性又是一个对象,则递归复制
               if (obj[key] && typeof obj[key] === "object") {
                   target[key] = deepClone(obj[key])
               } else {
                   target[key] = obj[key]
               }
           }

       }
       return target;
   }
   const p2 = deepClone(person)
   p2.name = 'wmy'
   p2.job.salary = 20
   console.log(person)


   // 法二:JSON.stringify和JSON.parse
   // 缺点: 无法实现对对象中方法的深拷贝,会显示为undefined
   function deepClone2(obj) {
       var _obj = JSON.stringify(obj)
       var objClone = JSON.parse(_obj)
       return objClone;

   }
   const p3 = deepClone2(person)
   p3.name = 'wmy'
   p3.job.salary = 20
   p3.sayHello = function() {
       console.log('hello')
   }
   console.log(person)
   person.sayHello()复制代码

js事件循环机制

1请描述事件循环机制 js是单线程的,调用栈中的同步任务执行完毕后,会去轮询堆中的任务,流程: 1执行同步任务,清空call stack 2执行当前的微任务 3尝试dom渲染 4触发事件循环机制

2什么是微任务,什么是宏任务,2者的区别,与dom渲染的关系 同步任务执行完毕后再渲染dom 微任务在dom渲染前执行,宏任务在dom渲染后执行 宏任务是浏览器定义的,常见的宏任务有:setTimeout ajax dom事件 微任务是es6中的 ,常见的微任务有promise async/await

示例:可以体验宏任务,微任务与dom渲染的关系

 //#region 
        // const demo = document.getElementsByClassName('demo')[0]
        // const p1 = `<p>我是p1</p>`
        // const p2 = `<p>我是p2</p>`
        // const p3 = `<p>我是p3</p>`
        // demo.innerHTML += p1
        // demo.innerHTML += p2
        // demo.innerHTML += p3
        // console.log('length', demo.children.length)
        // alert('同步任务执行完毕')

        // setTimeout(() => {
        //     alert('setTimeout 宏任务', demo.children.length)
        // })
        // Promise.resolve().then(() => {
        //     alert('promise 微任务', demo.children.length)
        // })

//#endregion复制代码

题目一:promise 与同步任务

 //#region 
 1.
// Promise.resolve().then(() => {
//     console.log(1)
// }).catch(() => {
//     console.log(2)
// }).then(() => {
//     console.log(3)
// })


2.
// Promise.resolve().then(() => {
//     console.log(1) //1
//     throw new Error('fail')
// }).catch(() => {
//     console.log(2) //2  catch捕获后又返回一个成功的promise
// }).then(() => {
//     console.log(3) //3
// })


3.
// Promise.resolve().then(() => {
//     console.log(1)
//     throw new Error('fail')
// }).catch(() => {
//     console.log(2)
// }).catch(() => {
//     console.log(3)
// })

//#endregion复制代码

题目二 同步任务与async/await

1.
 async function fn() {
    return 100
}
(async function() {
    const a = fn();
    console.log(a) //promise async定义的函数返回的是一个promise
    const b = await fn()  //await 后返回的是函数定义的结果
    console.log(b) //100
})()


2.
  (async function() {
        console.log('start') //start
        const a = await 100;
        console.log('a', a) //100
        const b = await Promise.resolve(200)
        console.log('b', b) //200
        const c = await Promise.reject(300)
        console.log('c', c) //报错了
        console.log('end')
    })()复制代码

闭包

  • 概念:函数中返回函数

  • 作用:实现数据的私有化,因为全部变量会被污染

  • 缺点:内存溢出,怎么溢出的?(面试的时候问过)

  • 应用场景?思考:现在有了块级作用域,应该不需要了吧?(fix)

      function lazy_sum(arr) {
          var sum = function() {
              return arr.reduce(function(x, y) {
                  return x + y;
              });
          }
          return sum;
      }
      // 注意到返回的函数在其定义内部引用了局部变量arr,所以,当一个函数返回了一个函数后,其内部的局部变量还被新函数引用 fix 这句话怎么理解
    
    
      // https://www.liaoxuefeng.com/wiki/1022910821149312/1023021250770016
    
      function count() {
          var arr = [];
          for (var i = 1; i <= 3; i++) {
              arr.push(function() {
                  return i * i;
              });
          }
          return arr;
      }
    
      var results = count();
      var f1 = results[0];
      // var f2 = results[1];
      // var f3 = results[2];
      console.log('f1', f1);
    
    
    
    
      function fn() {
          var a = 1;
          return function() {
              return a
          }
      }
      var f1 = fn() //返回函数
      var a1 = f1(); //1
      a1++;
      a1++;
    
      var a2 = f1(); //1
      a2++;
      a2++;
      console.log(a1, a2)
    
    
    
      // fix 怎么被污染的
      var a = 1;
      var a1 = a;
      a1++;
      a1++;
      var a2 = a;
      a2++;
      a2++;
      console.log(a1, a2); //3 5
    
    
    
      // 实现第一秒打印1,第二秒打印2,。。。
    
    
      for (var i = 1; i < 10; i++) {
          setTimeout(function() {
              //打印10次10 主线程执行完成之后, 才会去轮巡事件循环队列, 看有没有满足条件的函数, 如果有, 拿过来执行
              console.log(i);
          }, 1000 * i);
      }
    
    
      // 1
      for (let i = 1; i < 10; i++) {
          setTimeout(function() {
              console.log(i);
          }, 1000 * i);
      }
    
    
      // 2
      for (var i = 1; i < 10; i++) {
          (function(i) {
              setTimeout(function() {
                  console.log(i);
              }, 1000 * i);
    
          })(i)
      }
    
    
      // 3
      for (var i = 1; i < 10; i++) {
          var fn = (function(num) {
              return function() {
                  console.log(num);
              }
          })(i);
          setTimeout(fn, 1000 * i);
      }
    
    
    
      // 4
      // setTimeout( fn, 毫秒数, 需要传递给fn的参数 )
      // fix 这个是一次性打印出来的呀?
      for (var i = 1; i < 10; i++) {
          setTimeout(function(i) {
              console.log(i);
          }, 1000, i);
      }复制代码

数组和字符串常用方法

  • 数组的方法 juejin.cn/post/703479…

  • 字符串的方法 juejin.cn/post/703481…

节流和防抖的实现

 // debounce 防抖:持续触发事件时不会立马执行回调,结束触发事件后过1s才执行。
    function debounce(fn, wait) {
        let timer;
        return function() {
            if (timer) {
                clearTimeout(timer)
            }
            timer = setTimeout(fn, wait)
        }

    }
// throttle 节流:时间戳实现
const throttle1 = function(func, delay) {
        var prev = Date.now();
        return function() {
            var context = this;
            var args = arguments;
            var now = Date.now();
            if (now - prev >= delay) {
                func.apply(context, args);
                prev = Date.now();
            }
        }
    }
     // throttle 节流:定时器实现
    const throttle2 = function(func, delay) {
        var timer = null;
        return function() {
            var context = this;
            var args = arguments;
            if (!timer) {
                timer = setTimeout(function() {
                    func.apply(context, args);
                    timer = null;
                }, delay);
            }
        }
    }
    function handle() {
            console.log('handle')
        }

    const scrollBox = document.getElementById('scroll-box')
    scrollBox.addEventListener('scroll', throttle2(handle, 1000))复制代码

call,apply,bind的区别和共同点

  • 改变函数执行时的上下文,call,apply的第二个参数不同,call传参数列表,apply传数组,bind返回的是一个函数,调用后才会执行

  • call,apply,bind的应用场景:

// 2.1求数组中的最大值
const arr = [1, 9, 3, 4]
const res1 = Math.max(...arr)
const res2 = Math.max.apply(null, arr)
const res3 = Math.max.call(null, ...arr)复制代码
// 2.2判断变量类型 
const obj = {
    name: 'bwf'
}
console.log(typeof obj) //object
console.log(typeof arr) //object
console.log(typeof null) //object
console.log(Object.prototype.toString.call(obj)) //[object Object]
console.log(Object.prototype.toString.call(arr)) //[object Array]
console.log(Object.prototype.toString.call(null)) //[object Null]复制代码
// 2.3继承

function Person(name, age) {
    this.name = name;
    this.age = age;
}

function Student(name, age) {
    Person.call(this, name, age)

}
const s = new Student('bwf', 18)


作者:xiaobaoyu
链接:https://juejin.cn/post/7034820590036844575

文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐