js高级之面试题--比较常见的面试问题--递归,排序,闭包,this指向,作用域,设计模式,异步编程,宏任务微任务

[前端高频面试] 2026年中级、高级面试题 注意: 别紧张,目光注视面试官,不要有小动作,自信!!自信!!自信!! 阅读详情

js高级之面试题精讲

一、面试题难点之递归

递归是一种解决复杂的未知次数循环的问题的解决方案。其实表现是在一个函数内部自己调用自己。

如:求n的阶乘 – 求n的阶乘可以不使用递归实现,只是用来演示递归的使用

function factorial(n){
  return n == 1 ? 1 : n * factorial(n-1)
}

我们明显看出在factorial函数内部调用了自己。这就是函数递归。

此时如果我们调用这个函数,求5的阶乘,其调用过程如果

===> factorial(5)
===> 5 * factorial(4)
===> 5 * 4 * factorial(3)
===> 5 * 4 * 3 * factorial(2)
===> 5 * 4 * 3 * 2 * factorial(1)
===> 5 * 4 * 3 * (2 * 1)
===> 5 * 4 * (3 * 2)
===> 5 * (4 * 6)
===> 5 * 24
===> 120

所以这是一个层层递进,层层回归的过程 —— 简称 递归

上面只是一个简单的示例。在面试题中常见的场景主要如下:

1.深拷贝

function deepClone(value) {  
    if (value == null) return value;  
    if (typeof value !== 'object') return value;
    if (value instanceof RegExp) return new RegExp(value);  
    if (value instanceof Date) return new Date(value);  
    // 我要判断 value 是对象还是数组 如果是对象 就产生对象 是数组就产生数组  
    let obj = new value.constructor;  
    for(let key in value){    
        obj[key] = deepClone(value[key]); // 看一看当前的值是不是一个对象  
    }  
    return obj;
}

2.二分排序

var arr = [3, 1, 4, 6, 5, 7, 2];

function quickSort(arr) {
    if(arr.length == 0) {
        return [];    
    }
    var cIndex = Math.floor(arr.length / 2);
    var c = arr.splice(cIndex, 1);
    var l = [];
    var r = [];
    for (var i = 0; i < arr.length; i++) {
        if(arr[i] < c) {
            l.push(arr[i]);
        } else {
            r.push(arr[i]);
        }
    }
    return quickSort(l).concat(c, quickSort(r));
}
console.log(quickSort(arr));

3.数组扁平化

var arr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10];
var newArray = [];
function getArray(array) {
    array.forEach(function(e) {
        if (typeof e === "object") {
            getArray(e);
        } else {
            newArray.push(e);
        }
    });
}
getArray(arr);

二、面试题难点之闭包

1.常见闭包面试题

function fun(n, o) {
    console.log(o);
    return {
        fun: function(m) {
            return fun(m, n);
        }
    };
}

var a = fun(0); // ?
a.fun(1); // ?
a.fun(2); // ?
a.fun(3); // ?
var b = fun(0).fun(1).fun(2).fun(3); // ?
var c = fun(0).fun(1); // ?
c.fun(2); // ?
c.fun(3); // ?

三、面试题难点之作用域和this指向

1.常见面试题

x = 1;
var obj = {
    x: 2,
    dbl: function () {
        this.x *= 2;
        x *= 2;
        console.log(x);
        console.log(this.x);
    }
};
// 说出下面的输出结果
obj.dbl();
var func = obj.dbl;
func();
var funcBind = obj.dbl.bind(obj);
funcBind();
function Foo() {
    getName = function () { alert (1); };
    return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}

Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();

四、面试题难点之设计模式

设计模式是所有的编程语言中都可以实现的一些高级技巧,现在大家代码量还少,可能还无法理解,所以不用尝试自己推导,而是把这些东西掌握使用就好。

学习设计模式,有助于写出可复用和可维护性高的程序

设计模式的原则是“找出 程序中变化的地方,并将变化封装起来”,它的关键是意图,而不是结构。

单例模式

单例模式是指某个类永远只有一个实例对象,这个模式的好处是只有一个实例对象,那么所有的数据就可以在任何位置共享,便于管理这些需要共享的数据。

代码示例:

var _instance = null;
function Manager(){
  if(!_instance){
    _instance = new Manager();
  }
  return _instance;
}
class Manager{
  constructor(){
    if(Manager._instance){
      return Manager._instance;
    }
		Manager._instance = this;
  }
}

观察者模式(订阅发布模式)

1. 定义

也称作观察者模式,定义了对象间的一种一对多的依赖关系,当一个对象的状态发 生改变时,所有依赖于它的对象都将得到通知

2. 核心

取代对象之间硬编码的通知机制,一个对象不用再显式地调用另外一个对象的某个接口。

在JS中通常使用注册回调函数的形式来订阅

写法有很多,但是思想都一样: 先给对应的事件或者状态注册多个回调函数,当触发这个事件时,调用这些函数。

var event = {
  add : function(type,fn){
  	if(!this[type]){
      this[type] = []
    }
		this[type].push(fn)
	},
  remove: function(type,fn){
    var index = this[type].indexOf(fn);
    this[type].splice(index,1)
  },
  trriger(type){
    if(this[type]){
      this[type].forEach(e=>{
        e();
      })
    }
  }
}

设计模式不仅仅只是这两种,详细参考

五、面试题难点之异步编程

1.执行栈

想要想明白异步编程的执行顺序,首先要知道js代码是如何执行的。此时有一个概念一定要先知道:执行栈

执行栈,也称“调用栈”,是一种拥有 后进先出 的数据结构,被用来存储代码运行时创建的所有执行上顺序。

当 JavaScript 引擎第一次遇到你的脚本时,它会创建一个全局的执行环境并且压入当前执行栈。每当引擎遇到一个函数调用,它会为该函数创建一个新的执行环境并压入栈的顶部。

引擎会执行处于栈顶的执行环境的函数。当该函数执行结束时,执行环境从栈中弹出,控制流程到达当前栈中的下一个执行环境。

让我们通过下面的代码示例来理解:

 console.log('全局环境开始');
let a = 10;

function first() {
  console.log('函数1');
  second();
  console.log('两次回到函数1');
}

function second() {
  console.log('函数2');
}

first();
console.log('全局环境结束');

上面的代码可以用这样的过程来理解

image-20210331151641670

1.首先是全局的执行环境入栈

2.在全局环境下调用了first函数,再把first函数的环境压入栈中

3.在first函数里面调用了second函数,再把second函数的环境压入栈中

4.second执行完毕,于是把second的执行环境从栈中移除(先进后出,后入先出)

5.回到first的执行环境,再把fist的代码执行完成,从执行栈中再移除

6.最后把全局的执行环境也出栈,整个程序执行完成

2.EventLoop

2.1 javascript是单线程的

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。

JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

2.2 EventLoop和任务队列

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。

如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。

JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。

于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

具体来说,异步执行的运行机制如下。

1.所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
2.主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
3.一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
4.主线程不断重复上面的第三步。

于是就会形成下面这样一个执行模型

image-20210331154715816

2.3 MacroTask(宏任务)和 MicroTask(微任务)

而在代码的执行过程中,我们还把所有的分为两个大类,宏任务微任务

宏任务微任务
script环境Promise的then/catch回调
setInterval/setTimeout 定时器Object.observe(先忽略)
requestAnimationFrame 浏览器的帧循环(先忽略)Proxy(先忽略)
UI Rendering 浏览器的UI渲染(先忽略)

Event Loop中,每一次循环称为tick,每一次tick的任务如下:

执行栈在执行完同步任务后,查看执行栈是否为空,如果执行栈为空,就会去执行Task(宏任务),每次宏任务执行完毕后,检查微任务(microTask)队列是否为空,如果不为空的话,会按照先入先出的规则全部执行完微任务(microTask)后,设置微任务(microTask)队列为null,然后再执行宏任务,如此循环

以下面的代码为例:

console.log('1');
setTimeout(function() {
    console.log('2');
    new Promise(function(resolve) {
        console.log('3');
        resolve();
    }).then(function() {
        console.log('4')
    })
})
new Promise(function(resolve) {
    console.log('5');
    resolve();
}).then(function() {
    console.log('6')
})
console.log('7')

一开始的执行过程如下

image-20210331161826299

然后进入循环

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-97yvZzMV-1626498202286)(assets/image-20210331162756345.png)]

所以最终的结果为: 1,5,7,6,2,3,4

2.4 实战时间

setTimeout(function () {
  console.log('1');
})
new Promise(function (resolve) {
  console.log('2');
  for (var i = 0; i < 1000; i++) {
    i == 99 && resolve();
  }
  console.log('3');
}).then(function () {
  console.log('4');
})
console.log('5')
console.log('start')
setTimeout(() => {
  console.log('setTimeout')
}, 0)
new Promise((resolve) => {
  console.log('promise')
  resolve()
}).then(() => {
  console.log('then1')
}).then(() => {
  console.log('then2')
})
console.log('end')
console.log('start')
setTimeout(() => {
  console.log('timer1')
  Promise.resolve().then(function() {
    console.log('promise1')
  })
}, 0)
setTimeout(() => {
  console.log('timer2')
  Promise.resolve().then(function() {
    console.log('promise2')
  })
}, 0)
Promise.resolve().then(function() {
  console.log('promise3')
})
console.log('end')
2026前端面试备战指南:从核心机制到项目深挖的实战策略 前端技术栈持续演进,面试考察早已从单一的知识记忆转向对原理理解、工程实践与项目落地能力的综合评估。JavaScript的事件循环、闭包、原型链等核心机制,是理解框架运行逻辑和解决实际性能问题的基础;Vue与React的响应式原理、虚拟DOM diff算法,则决定了能否在复杂业务中做出合理的技术选型。从浏览器渲染到HTTP缓存,从工程化构建到前端架构,每个技术点都最终指向真实的应用场景——如何平衡首屏加载速度、如何设计可维护的组件体系、如何在团队协作中推动方案落地。本文系统梳理了前端面试的考察维度、高频考点 阅读详情

相关推荐

前端面试全攻略:从JavaScript核心到工程化实践

前端开发作为现代Web技术的核心领域,其技术体系涵盖从基础语言特性到复杂工程实践的完整知识链。JavaScript作为前端开发的基石,其核心概念如作用域链、闭包机制和原型继承构成了理解现代框架原理的基础。在工程实践层面,Webpack等构建工具通过模块化打包、Tree Shaking和代码分割等技术显著提升了前端应用的性能表现。随着React Fiber架构和Vue3响应式系统的演进,框架原理的深度理解成为区分工程师水平的关键指标。这些技术最终服务于实际业务场景,如通过虚拟DOM优化渲染性能、利用HTTP/

weixin_30781775的博客 435

前端面试宝典

关于前端面试前端岗位面试主要涉及的模块包括:对JS的掌握程度、对HTTP协议的熟悉程度、对Git的熟悉程度、然后就是最核心的Vue+React+Webpack;面试和笔试有所不同:笔试通常考察的是求职者的编码能力,不是朝夕之功可成的;而面试主要考察的是原理,我说你听,你理解了之后就还能再次复述给别人,某种程度上有点思(chui)想(shui)性质,可以速成;很多原理性质的东西看似与代码毫不相干,为什么企业面试还要反复考察呢?其真实意义在于:深入理解原理后,你一旦掉坑有一个排查的方向,让你优化时你也有个大致的方向,让你做代码复用和架构设计时你也有个大致方向,而不是坐井观天、日复一日地用着几个熟悉的API而沾沾自喜;面试的重要性,谁跳槽谁明白~~课程特色零基础教学,由浅入深,通俗易懂,物超所值;全课程基于【面试官系列】进行“二次创作”,主要是提炼和简化了面试官的原文;让你一下子抓住要点!让你一下子抓住要点!让你一下子抓住要点!课程收益深入理解JavaScript原理深入理解HTTP协议深入理解Vue/React原理深入理解Webpackk打包构建原理写在最后就连太阳光照到地球都需要8分钟,所以,你也需要时间,耐心一点,持续修炼。

别再手动调参了!用Optuna+PyTorch搞定Transformer时序预测超参数(附完整代码)

本文详细介绍了如何利用Optuna和PyTorch实现Transformer时序预测模型的自动化超参数调优。通过贝叶斯优化技术,显著提升单变量时序预测的效率和准确性,并附有完整代码实现。文章涵盖模型架构设计、数据预处理、Optuna调参实战及生产环境部署建议,为开发者提供一站式解决方案。

weixin_33704234的博客 558

一天10个JS面试题(一)

JSthis指向,map和foreach的区别,递归的底层原理,JS的基本类型有哪些,判断JS的数据类型,创建对象的方法,创建函数的方法,

不熬夜的臭宝的博客 2115

2024年Web前端最新【面试题JavaScript递归的理解_js面试题 递归,最新BAT大厂面试者整理的Web前端面试题

6 * (5 * (4 * (3 * (2 * (1 * 1)))))) // {{rotuerViews(children)} // 递归遍历子路由。// 120复制代码。(“输入的数字不能小于 0”)// 递归functionsum(n) {// 循环var sum = 0;

2401_84447149的博客 738

常见js面试题递归算法

常见js面试题递归算法

qq_39213199的博客 411

怎么理解函数的防抖和节流

防抖: 就是指触发事件后在n秒内函数只能执行一次,如果在n秒内又触发了事件,则会重新计算函数执行时间。 例如:设定1000毫秒执行,当你触发事件了,他会1000毫秒后执行,但是在还剩500毫秒的时候你又触发了事件,那就会重新开始1000毫秒之后再执行 <body> <input type="text" class="ipt" /> <script> var timerId = null document.querySelector(

qq_39639537的博客 315

前端高頻面試題(一)

前端JS面试高频题目涵盖了多个方面,包括基础语法、数据结构算法、DOM操作、异步编程、模块化、框架/库使用等。以下是一些常见的前端JS面试高频题目及简要解析:

警警的博客 1679

JavaScript面试核心知识点与实战技巧解析

JavaScript作为现代Web开发的核心语言,其异步编程作用域机制和原型继承等特性是理解前端工程化的基础。事件循环模型通过任务任务的协同调度实现非阻塞执行,Promise和async/await则提供了更优雅的异步解决方案。在实际开发中,闭包常用于状态封装,而this绑定规则直接影响函数执行上下文。这些概念不仅是面试高频考点,更是实现组件通信、性能优化等工程实践的理论基础。本文通过变量提升、类型转换等典型面试题,结合数组扁平化、防抖节流等实用代码示例,系统梳理了从ES6新特性到虚拟DOM原理的知

weixin_30617737的博客 299

添加面试题(1)

递归是什么?使用场景举例? 递归就是函数直接或者间接调用自身的一种方法,来解决比较复杂的函数逻辑, 优缺点: ​ 优点: 1. 解决重复执行任务 2. 处理不定层级数据 缺点: 1. 时间 空间的消耗较大 2. 重复计算 3. 栈溢出 使用场景:树形菜单,递归组件,快速排序等 // 快速排序主要是利用递归来实现 function quick(ary) { // 4、结束循环 if (ary.length <= 1) {

Helloxd020406的博客 501

知识梳理——JavaScript

1. 说说前端中的事件流 HTML中与javascript交互是通过事件驱动来实现的,例如鼠标点击事件onclick、页面的滚动事件onscroll等等,可以向文档或者文档中的元素添加事件侦听器来预订事件。想要知道这些事件是在什么时候进行调用的,就需要了解一下“事件流”的概念。 什么是事件流:事件流描述的是从页面中接收事件的顺序. DOM2级事件流包括下面几个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段 addEventListener:addEventListener 是DOM2 级事件新增的指定事件处

wytraining 1625

2025腾讯前端面试深度拆解:从八股到原理,核心考点全突破

前端面试的核心已从背诵八股转向对原理与落地能力的考察。无论是事件循环中的任务任务调度,还是浏览器渲染流程、闭包与内存泄漏,再到虚拟DOM的diff策略和大文件上传的Web Worker优化,这些基础技术概念共同构成前端工程师的底层能力拼图。理解它们的原理,不仅有助于定位业务性能瓶颈、保障页面安全,更能支撑复杂场景下的架构设计。在大厂面试中,面试官通过层层追问源码与场景题,检验候选人对知识点的真实掌握程度。围绕2025年前端面试的高频考点,系统拆解从JavaScript异步机制到工程化性能优化的完整知识

weixin_30243533的博客 395

2021前端面试题总结

var let const的区别 1.var可以重复声明,let不可以 2.let有块级作用域,var没有 3.var会与window相映射,let不会 4.var可以在声明前使用,let有暂存死区,不能在声明前使用 5.const声明之后必须赋值,否则就会报错 6.const声明的是不可变的量,改变就会报错 7.const和let一样,不与window相映射,有块级作用域,在声明之前访问会报错 const定义的量是否能够改变 定义基本数据类型的常量是不可变的,定义引用类型的常量是可更改的,但是只改变属性不

m0_46423771的博客 1190

前端面试题集合

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34015860的博客 329

2026前端面试题!(附答案及解析)(2026.9月最新版)

文章目录1.JSONP原理2. cookie、sessionStorage、localStorage的区别3.link和a标签区别4 vue中的跨域问题5 webpack面试题6 浏览器与服务器的交互原理7 什么是CSS盒模型 >>> -IE盒模型和W3C盒模型8 A解释一下闭包,B为什么要使用闭包?9 解释一下作用域链10 如何处理不让别人盗用你的图片,访问你的服务器资源?11 如何优化网站性能vue路由面试题1.对于MVVM的理解?2 Vue的生命周期1 什么是vue生命

weixin_43617604的博客 8万+

前端面试锦集

基本面试: 做过最满意的项目是什么?项目背景?为什么要做这件事情?最终达到什么效果?你处于什么样的角色,起到了什么方面的作用?在项目中遇到什么技术问题?具体是如何解决的? 如果再做这个项目,你会在哪些方面进行改善? 基础扎实: IT行业,哪些发展好的同学都是具备扎实基础知识,如果理解计算机基础会更好,因为我们面临很多非前端计算问题的。 主动思考: 被动完成任务成长会很慢的,需要有自己的想法,而不是仅仅是完成任务的。 自动学习: 前端领域知识淘汰速度很快,需要经常学习新知识。 追溯深度: 遇到问题是多研究背.

Front小思的博客 6701

12月前端面试别焦虑,大多数人已躺平,竞争很少的放心面!!!

12月征战了5天,前端面试一个岗位虽然还是能看到好多人,但其实内心都躺平了一半,完全构不成竞争力,别说面试,估计问个Fiber原理都模糊了。这个时间段比较好浑水摸鱼,容易提高印象分,好好准备,offer概率还是有所提高的!今天小编给大家总结了最快,最全,最新,经过很多人面经整理出来的面试题库,放心吃,主打一个让你们吃得放心,我也用的放心!!!根据不同经验,整理了不同的内容。

WYiQIU的博客 660

建议0-8年经验的前端开发准备到这种程度再去面试...(2026年面试必考宝典)

一般使用的是防抖和节流,节流函数通过控制每次时间执行的时间间隔,控制短时间多次执行方法。防抖函数是推迟每次事件执行的时间减少不必要的查询。但是网络慢的时候,还是会重复提交,没有显示状态,用户不知道有没有真的提交。所以就给按钮添加一个加载状态,查了发现el-button自带了loading属性,传参的时候传一个submit函数,是一个Promise,promise状态改变的时候把loading状态改成false。然后点击按钮会有加载动画,加载的时候,按钮是禁用的。

WYiQIU的博客 1316

大厂前端面试最新整理笔记(50万字经验总结)

外。

WYiQIU的博客 790
上一篇: nodejs之旅--npm和yarn的安装以及使用--Promise对象的常见方法和使用
下一篇: Vue3学习之旅--爱上Vue3--Vue3基础语法(二)--模板语法--条件渲染--列表渲染--为什么Vue的v-for需要绑定key属性
尤雨东
博客等级 码龄7年 381粉丝 152原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尤雨东

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值