前端JS面试6大核心考点详解
这是前端JS核心面试必考6大知识点的代码分析题+进阶题全集,严格遵循JS执行逻辑,覆盖基础必考题+进阶拔高题,每题附代码、输出结果、逐行详解,完全适配中大厂前端面试要求。
前端JS核心面试题详解(作用域/this/对象/闭包/原型/异步)
核心考点优先级
- this指向、异步(事件循环/Promise)⭐⭐⭐⭐⭐
- 闭包、原型链 ⭐⭐⭐⭐
- 作用域、对象 ⭐⭐⭐
一、作用域 & 作用域链 & 变量提升
核心原理
- 三种作用域:全局作用域、函数作用域、块级作用域(
let/const) - 变量提升:
var声明提升、let/const存在暂时性死区 - 作用域链:内层函数访问变量时,逐级向上查找外层作用域
基础题1:var 变量提升
console.log(a);vara=10;console.log(a);输出:undefined→10
详解:
- JS预编译:
var a声明提升到顶部,赋值保留原地 → 代码等价于:vara;console.log(a);// 仅声明未赋值 → undefineda=10;console.log(a);// 赋值完成 → 10
基础题2:let 暂时性死区
console.log(b);letb=20;输出:Uncaught ReferenceError: Cannot access 'b' before initialization
详解:let/const存在块级作用域和暂时性死区,声明前无法访问,无变量提升。
进阶题1:嵌套作用域 + 作用域链
varnum=1;functionfn1(){varnum=2;functionfn2(){num=3;functionfn3(){console.log(num);}fn3();}fn2();}fn1();console.log(num);输出:3→1
详解:
fn3无自身num,沿作用域链找到fn2修改的num=3,打印3;- 所有修改都在函数作用域内,全局
num不受影响,最终打印1。
二、this指向(面试第一考点)
核心绑定规则(优先级从高到低)
- new 绑定→ 2.显式绑定(call/apply/bind)→ 3.隐式绑定→ 4.默认绑定
- 箭头函数:无自己的this,继承外层作用域的this
基础题1:默认绑定(独立函数调用)
varname="全局";functionfn(){console.log(this.name);}fn();输出:全局
详解:全局独立调用函数,this默认指向全局对象(浏览器=window)。
基础题2:隐式绑定(对象方法调用)
constobj={name:"对象",fn:function(){console.log(this.name);}}obj.fn();输出:对象
详解:谁调用方法,this就指向谁 →obj调用,this=obj。
基础题3:箭头函数this
constobj={name:"箭头",fn:()=>{console.log(this.name);}}obj.fn();输出:undefined(浏览器严格模式)/全局
详解:箭头函数无this,继承外层全局作用域的this,而非obj。
进阶题1:混合绑定优先级(必考)
functionPerson(name){this.name=name;}constobj={};constfn=Person.bind(obj);fn("张三");console.log(obj.name);constp=newfn("李四");console.log(p.name);输出:张三→李四
详解:
bind显式绑定obj,fn("张三")→this=obj,obj.name=张三;- new绑定优先级高于bind,
new fn时this指向新实例,覆盖bind绑定。
进阶题2:定时器 + this + 箭头函数
constobj={name:"定时器",fn1:function(){setTimeout(function(){console.log(this.name);},0);},fn2:function(){setTimeout(()=>{console.log(this.name);},0);}}obj.fn1();obj.fn2();输出:undefined→定时器
详解:
- 普通函数定时器:回调是独立调用 → 默认绑定
window,无name; - 箭头函数定时器:继承
fn2的this(指向obj),正常打印。
三、对象 & 属性 & 深浅拷贝
核心原理
- 对象属性:自有属性/原型属性,
hasOwnProperty判断自有属性 - 浅拷贝:只拷贝一层,引用类型共享内存
- 深拷贝:递归拷贝所有层级,完全独立
基础题1:in / hasOwnProperty
constobj={a:1};console.log('a'inobj);console.log('toString'inobj);console.log(obj.hasOwnProperty('toString'));输出:true→true→false
详解:
in:检查自有+原型属性;hasOwnProperty:仅检查自有属性,toString是Object原型属性。
进阶题1:浅拷贝的坑(高频)
constobj1={a:1,b:{c:2}};constobj2={...obj1};obj2.a=10;obj2.b.c=20;console.log(obj1.a,obj1.b.c);输出:1→20
详解:
- 扩展运算符是浅拷贝,基础类型
a独立,引用类型b共享内存; - 修改
obj2.b.c会同步修改原对象。
四、闭包(高频必考)
核心定义
函数能够访问并保留其词法作用域,即使在词法作用域外执行,就形成闭包。
作用:私有化变量、模块化、保存数据
基础题1:闭包基础
functionouter(){letnum=10;functioninner(){console.log(num);}returninner;}constfn=outer();fn();输出:10
详解:inner在outer外部执行,仍能访问outer的num→ 闭包。
基础题2:经典循环闭包BUG(var)
for(vari=0;i<3;i++){setTimeout(()=>{console.log(i);},0);}输出:3、3、3
详解:
var是函数作用域,循环共用一个i;- 定时器是异步,执行时循环已结束,
i=3。
进阶题1:闭包修复循环(两种方案)
// 方案1:let 块级作用域for(leti=0;i<3;i++){setTimeout(()=>console.log(i),0);}// 方案2:闭包包裹for(vari=0;i<3;i++){(function(j){setTimeout(()=>console.log(j),0);})(i)}输出:0、1、2
详解:
let每次循环生成独立块级作用域;- 立即执行函数创建独立作用域,保存每次的
i值。
五、原型 & 原型链 & 继承
核心原理
- 实例.proto= 构造函数.prototype
- 原型链终点:
Object.prototype.__proto__ = null instanceof:判断构造函数的prototype是否在实例的原型链上
基础题1:原型链属性查找
functionPerson(){}Person.prototype.name="原型";constp=newPerson();p.name="实例";console.log(p.name);deletep.name;console.log(p.name);输出:实例→原型
详解:
- 先找实例自有属性,有则直接用;
- 删除实例属性后,沿原型链找到
Person.prototype的name。
进阶题1:组合继承(面试必考)
functionParent(name){this.name=name;}Parent.prototype.say=function(){console.log(this.name);}functionChild(name,age){Parent.call(this,name);this.age=age;}Child.prototype=Object.create(Parent.prototype);Child.prototype.constructor=Child;constc=newChild("小明",18);c.say();console.log(c.age);输出:小明→18
详解:
Parent.call:继承实例属性;Object.create:继承原型方法,完美实现JS组合继承。
六、异步编程(Promise/async/await/事件循环)
核心原理(浏览器)
- 执行顺序:同步代码 → 微任务 → 宏任务
- 微任务:
Promise.then/catch/finally、process.nextTick - 宏任务:
setTimeout、setInterval、AJAX、DOM事件
基础题1:Promise链式调用
Promise.resolve().then(()=>console.log(1)).then(()=>console.log(2));Promise.resolve().then(()=>console.log(3));输出:1→3→2
详解:微任务队列按顺序执行,第一个链的then1执行完,执行第二个then3,最后执行第一个then2。
进阶题1:async/await 执行顺序(终极面试题)
asyncfunctionasync1(){console.log('async1 start');awaitasync2();console.log('async1 end');}asyncfunctionasync2(){console.log('async2');}console.log('script start');setTimeout(()=>console.log('setTimeout'),0);async1();newPromise(resolve=>{console.log('Promise');resolve();}).then(()=>console.log('Promise then'));console.log('script end');输出:script start→async1 start→async2→Promise→script end→async1 end→Promise then→setTimeout
详解:
- 同步代码优先执行:
script start→async1 start→async2→Promise→script end; await后面的代码 = 微任务,Promise.then也是微任务,按顺序执行;- 最后执行宏任务
setTimeout。
进阶题2:Promise.all 异常处理
constp1=Promise.resolve(1);constp2=Promise.reject("错误");constp3=Promise.resolve(3);Promise.all([p1,p2,p3]).then(res=>console.log(res)).catch(err=>console.log(err));输出:错误
详解:Promise.all只要有一个失败,整体直接失败,返回第一个错误。
总结(面试必背核心结论)
- this:new > 显式 > 隐式 > 默认,箭头函数继承外层this;
- 闭包:函数访问外层作用域变量,解决循环问题用
let/立即执行函数; - 原型链:实例通过
__proto__找原型,自有属性优先级高于原型; - 异步:同步 → 微任务(Promise)→ 宏任务(定时器);
- 作用域:
var函数作用域+提升,let块级作用域+暂时性死区。
