JS面试题

1. 事件循环的理解

事件循环的理解(面试常问,详细的内容)-优快云博客

JavaScript 是单线程的,它通过 事件循环(Event Loop) 来管理同步和异步任务的执行。任务通常分为 同步任务异步任务。同步任务会在主线程中立即执行,而异步任务则会进入不同的队列,等待合适的时机再执行。

  • 宏任务(Macro Task):大的任务单位,例如整个脚本代码块、setTimeoutsetInterval、DOM 操作等。
  • 微任务(Micro Task):较小的任务单位,例如 Promise 的回调、queueMicrotaskMutationObserver 等。

执行顺序规则

  1. 执行主线程上的所有同步任务
  2. 检查微任务队列,执行所有微任务,直到清空。
  3. 如果微任务队列为空,则执行下一个宏任务
  4. 重复以上步骤。

常见场景题

console.log(1)
setTimeout(()=>{
    console.log(2)
}, 0)
new Promise((resolve, reject)=>{
    console.log('new Promise')
    resolve()
}).then(()=>{
    console.log('then')
})
console.log(3)
// 遇到 console.log(1) ,直接打印 1
// 遇到定时器,属于新的宏任务,留着后面执行
// 遇到 new Promise,这个是直接执行的,打印 'new Promise'
// .then 属于微任务,放入微任务队列,后面再执行
// 遇到 console.log(3) 直接打印 3
// 好了本轮宏任务执行完毕,现在去微任务列表查看是否有微任务,发现 .then 的回调,执行它,打印 'then'
// 当一次宏任务执行完,再去执行新的宏任务,这里就剩一个定时器的宏任务了,执行它,打印 2

2. 判断数据类型的方法

  • typeof:适用于原始类型和函数,缺点是不能准确判断 null 和复杂对象类型。
  • instanceof:适用于判断对象是否为某个构造函数的实例,但在跨作用域时有局限性。
  • Object.prototype.toString.call():非常可靠,几乎可以用于所有类型判断,推荐使用。
  • Array.isArray():判断数组的最佳方式。
  • constructor:可以用于判断对象类型,但可能会被修改,稳定性不如其他方法。

3.闭包

定义:当函数调用的时候,导致内部新函数被定义,并抛出内部被定义的新函数,即为闭包。

内层函数可以访问外层函数的作用域的变量

function createCounter() {
  let count = 0;

  function increment() {
    count++;
    console.log(count);
  }

  return increment;
}

const counter = createCounter();  // 创建闭包
counter();  // 输出 1
counter();  // 输出 2

// 清除闭包引用
counter = null;  // 手动解除对闭包的引用,count 变量将被垃圾回收

4.防抖节流

防抖 是指在某个事件被触发一定时间后再执行回调,如果在这个时间内事件又被触发,则重新计时。这意味着函数在连续触发的情况下只会在最后一次触发后执行一次。

function debounce(func, delay) {
  let timeout; // 定义一个变量用于存储定时器

  return function(...args) {
    const context = this; // 保存当前的上下文

    clearTimeout(timeout); // 清除上一次的定时器
    timeout = setTimeout(() => {
      func.apply(context, args); // 在 delay 后执行目标函数
    }, delay);
  };
}

// 使用示例
const handleResize = debounce(() => {
  console.log('Window resized');
}, 300);

window.addEventListener('resize', handleResize);

节流 是指在一定时间间隔内,无论事件被触发多少次,回调函数只会执行一次。节流可以确保在规定的时间内不会重复执行同一个函数,从而有效控制函数的执行频率。

function throttle(func, delay) {
  let lastTime = 0; // 记录上次执行的时间

  return function(...args) {
    const context = this; // 保存当前的上下文
    const now = Date.now(); // 获取当前时间

    if (now - lastTime >= delay) {
      lastTime = now; // 更新上次执行时间
      func.apply(context, args); // 执行目标函数
    }
  };
}

// 使用示例
const handleScroll = throttle(() => {
  console.log('Scrolled');
}, 300);

window.addEventListener('scroll', handleScroll);

5.  call,apply,bind三者的区别

call、bind、apply 都是 JavaScript 中用于改变函数执行上下文(即 this 指向)的方法。 call 和 apply 的作用是一样的,都是用来调用函数并且改变函数内部的 this 指向。区别在于传参的方式不同,call 的参数是一个一个传递的,而 apply 的参数是以数组的形式传递的。

既然两者功能一样,那该用哪个呢? 在JavaScript 中,某个函数的参数数量是不固定的,因此要说适用条件的话,当你的参数是明确知道数量时用 call ;而不确定的时候用 apply,然后把参数 push 进数组传递进去。当参数数量不确定时,函数内部也可以通过 arguments 这个数组来遍历所有的参数。

bind 方法不会立即执行函数,而是返回一个新的函数,这个新的函数的 this 值被绑定到了指定的对象,调用时也可以传入参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值