一、变量声明三剑客var、let、const 的本质差异变量声明是 JS 代码的基础ES6 的推出彻底重构了 JS 的变量声明体系let和const的出现解决了var长期存在的作用域缺陷三者的核心差异集中在作用域规则、声明特性、赋值约束三个维度。1.1 var函数作用域的 “老牌声明”var是 ES5 及之前唯一的变量声明方式其核心设计围绕函数级作用域展开也是开发中最容易出现全局污染、变量覆盖问题的根源。核心特性函数级作用域仅在声明它的函数内部生效if/for等代码块无法限制其作用域全局声明会自动挂载到window对象上变量提升声明会被提升到当前作用域顶部可在声明前调用值为undefined不会报错允许重复声明同一作用域内可多次声明同一变量后声明的值会直接覆盖前声明代码示例修正原文笔误后的可运行版本// var无块级作用域块内声明可在块外访问 if(true){ var a 10; } console.log(a); // 正常输出10变量提升典型示例console.log(b); // 输出undefined而非引用错误 var b 20; // 上述代码JS引擎实际执行顺序 var b; console.log(b); b 20;1.2 let块级作用域的 “标准解决方案”ES6 引入的let是为了解决var的设计缺陷而生核心特性是块级作用域也是现代 JS 开发中循环变量、临时变量的首选声明方式。核心特性块级作用域仅在声明它的{}代码块内生效if/for/独立{}都会形成独立作用域块外无法访问暂时性死区无变量提升必须先声明后使用声明前访问会直接抛出引用错误禁止重复声明同一作用域内重复声明同一变量会直接报语法错误全局声明不会挂载到window对象上避免全局污染。代码示例修正原文笔误后的可运行版本// let是块级作用域仅在块内生效 if(true){ let a 10; console.log(a); // 块内正常输出10 } console.log(a); // 报错Uncaught ReferenceError: a is not defined1.3 const只读常量的 “终极约束”const同样是 ES6 引入的声明方式用于声明只读常量除了赋值约束外其余核心特性与let完全一致是现代开发中常量、固定引用的首选。核心特性与let一致块级作用域、暂时性死区、禁止重复声明、不挂载window强制初始化声明时必须立即赋值否则直接报语法错误只读约束基本类型数据number/string/boolean 等声明后不可修改引用类型数据object/array/function 等不可修改栈内存中的引用地址但可自由修改堆内存中的内部属性 / 元素。// const声明引用类型不可修改引用地址可修改内部属性 const person{name:张三,age:18}; // 合法操作修改引用类型的内部属性未改变引用地址 person.age20; person.name李四; console.log(person); // 输出 {name: 李四, age: 20} // 非法操作直接修改引用地址相当于重新赋值 person{name:李四,age:18}; // 报错Uncaught TypeError: Assignment to constant variable.二、箭头函数语法简化与 this 绑定的革新ES6 引入的箭头函数不仅极大简化了函数的写法更从根本上解决了传统函数this指向漂移的经典问题是现代 JS 开发中回调函数、简化函数定义的核心工具。2.1 箭头函数的基础语法箭头函数的核心是省略function关键字用连接参数与函数体可根据参数数量、函数体行数灵活简写修正原文笔误后的完整语法示例如下1. 传统函数与箭头函数基础对比// 传统函数写法 function add(a,b){ console.log(aaaaa); return a b; } // 多参数箭头函数参数需用()包裹多行函数体需用{}包裹手动return const add1 (a,b) { console.log(aaaaa); return a b; };2. 单参数简写形式// 只有1个参数时可省略参数的() // 函数体只有一行return语句时可省略{}和return关键字 const add2 a a a; console.log(add2(5)); // 输出103. 无参数写法// 无参数时必须保留() const add3 () console.log(aaa); add3(); // 执行输出aaa2.2 箭头函数的核心特性与传统函数的本质区别箭头函数并非只是传统函数的简写其底层执行机制与传统函数有本质差异核心特性如下this 指向固定不变箭头函数没有自己的this其this继承自定义时所在的外层作用域的 this而非调用时的执行上下文。这是箭头函数最核心的特性彻底解决了传统函数this随调用者变化的问题且无法通过call/apply/bind修改其 this 指向。无 arguments 对象箭头函数内部无法访问传统函数的arguments参数列表如需获取动态参数可使用 ES6 剩余参数...rest替代。不可作为构造函数箭头函数没有prototype原型无法使用new关键字实例化否则会直接报错。不可使用 yield 关键字无法作为 Generator 生成器函数使用。this 指向典型示例const obj { name: 张三, // 传统函数this指向调用者obj traditionalFunc: function() { console.log(this.name); // 输出 张三 }, // 箭头函数this继承定义时的外层作用域全局window arrowFunc: () { console.log(this.name); // 输出 undefined } } obj.traditionalFunc(); obj.arrowFunc();三、setTimeout 延时器的循环陷阱作用域与事件循环的碰撞setTimeout 循环陷阱是 JS 面试超高频考点也是开发中极易踩坑的场景其本质是变量作用域规则与JS 事件循环机制共同作用的结果我们将从现象到本质彻底拆解这一经典问题。3.1 循环陷阱的现象// 循环陷阱var版本 for(var i0;i10;i){ setTimeout(()console.log(i),1000); } // 1秒后连续输出10个10// 标准解决方案let版本 for(let j0;j10;j){ setTimeout(()console.log(j),1000); } // 1秒后依次输出0 1 2 3 4 5 6 7 8 93.2 底层原理深度解析要彻底搞懂循环陷阱必须结合 3 个 JS 核心机制JS 单线程与事件循环JS 是单线程语言setTimeout属于宏任务会被放入异步任务队列必须等待主线程的同步代码for 循环全部执行完毕后才会执行任务队列中的回调函数。无论延时时间是 0 还是 1000ms回调函数一定在 for 循环完全结束后才执行。var 的全局作用域特性var声明的i是全局作用域变量整个 for 循环共用同一个i变量。循环同步执行时只是不断给同一个i重新赋值循环结束后全局的i已经变成了 10。此时执行 10 个回调函数所有回调访问的都是同一个全局i因此连续输出 10 个 10。let 的块级作用域特性let声明的j拥有块级作用域for 循环的每一次迭代都会创建一个独立的、全新的j变量每个循环体的块作用域完全隔离。每一次循环setTimeout 的回调函数都会捕获当前迭代块中独立的j变量这个值不会被后续循环修改。循环结束后10 个回调函数分别访问各自捕获的j因此正常输出 0-9。四、核心知识点汇总表格表 1var、let、const 核心特性对比表特性varletconst作用域函数级作用域无块级作用域块级作用域块级作用域变量提升存在声明提升值为 undefined不存在存在暂时性死区必须先声明后使用不存在存在暂时性死区必须先声明后使用重复声明同一作用域内允许同一作用域内不允许同一作用域内不允许初始化要求声明时可不用初始化声明时可不用初始化声明时必须立即初始化赋值修改可随意修改可随意修改基本类型不可修改引用类型不可修改引用地址可修改内部属性全局声明挂载 window是否否表 2箭头函数与传统函数核心对比表特性传统函数function箭头函数写法必须写 function 关键字语法固定可根据参数和函数体简写语法更简洁this 指向指向调用时的执行上下文调用者可通过 call/apply/bind 修改继承定义时外层作用域的 this固定不变call/apply/bind 无法修改arguments 对象有可访问传入的参数列表无可通过剩余参数...rest 替代构造函数可作为构造函数支持 new 实例化不可作为构造函数无 prototype使用 new 会报错Generator 函数支持可使用 yield 关键字不支持无法使用 yield 关键字表 3setTimeout 循环陷阱解决方案对比表解决方案实现原理优点缺点适用场景let 块级作用域每次循环创建独立块级作用域保存当前迭代值写法最简洁ES6 标准方案无额外性能开销需 ES6 环境支持现代前端开发所有 ES6 环境setTimeout 传参利用 setTimeout 第三个参数传参传入当前迭代值写法简单无需处理作用域极老旧浏览器IE9 及以下不支持第三个参数
零基础入门前端JavaScript 核心语法:var/let/const、箭头函数与 setTimeout 循环陷阱全解析(可用于备赛蓝桥杯Web应用开发)
一、变量声明三剑客var、let、const 的本质差异变量声明是 JS 代码的基础ES6 的推出彻底重构了 JS 的变量声明体系let和const的出现解决了var长期存在的作用域缺陷三者的核心差异集中在作用域规则、声明特性、赋值约束三个维度。1.1 var函数作用域的 “老牌声明”var是 ES5 及之前唯一的变量声明方式其核心设计围绕函数级作用域展开也是开发中最容易出现全局污染、变量覆盖问题的根源。核心特性函数级作用域仅在声明它的函数内部生效if/for等代码块无法限制其作用域全局声明会自动挂载到window对象上变量提升声明会被提升到当前作用域顶部可在声明前调用值为undefined不会报错允许重复声明同一作用域内可多次声明同一变量后声明的值会直接覆盖前声明代码示例修正原文笔误后的可运行版本// var无块级作用域块内声明可在块外访问 if(true){ var a 10; } console.log(a); // 正常输出10变量提升典型示例console.log(b); // 输出undefined而非引用错误 var b 20; // 上述代码JS引擎实际执行顺序 var b; console.log(b); b 20;1.2 let块级作用域的 “标准解决方案”ES6 引入的let是为了解决var的设计缺陷而生核心特性是块级作用域也是现代 JS 开发中循环变量、临时变量的首选声明方式。核心特性块级作用域仅在声明它的{}代码块内生效if/for/独立{}都会形成独立作用域块外无法访问暂时性死区无变量提升必须先声明后使用声明前访问会直接抛出引用错误禁止重复声明同一作用域内重复声明同一变量会直接报语法错误全局声明不会挂载到window对象上避免全局污染。代码示例修正原文笔误后的可运行版本// let是块级作用域仅在块内生效 if(true){ let a 10; console.log(a); // 块内正常输出10 } console.log(a); // 报错Uncaught ReferenceError: a is not defined1.3 const只读常量的 “终极约束”const同样是 ES6 引入的声明方式用于声明只读常量除了赋值约束外其余核心特性与let完全一致是现代开发中常量、固定引用的首选。核心特性与let一致块级作用域、暂时性死区、禁止重复声明、不挂载window强制初始化声明时必须立即赋值否则直接报语法错误只读约束基本类型数据number/string/boolean 等声明后不可修改引用类型数据object/array/function 等不可修改栈内存中的引用地址但可自由修改堆内存中的内部属性 / 元素。// const声明引用类型不可修改引用地址可修改内部属性 const person{name:张三,age:18}; // 合法操作修改引用类型的内部属性未改变引用地址 person.age20; person.name李四; console.log(person); // 输出 {name: 李四, age: 20} // 非法操作直接修改引用地址相当于重新赋值 person{name:李四,age:18}; // 报错Uncaught TypeError: Assignment to constant variable.二、箭头函数语法简化与 this 绑定的革新ES6 引入的箭头函数不仅极大简化了函数的写法更从根本上解决了传统函数this指向漂移的经典问题是现代 JS 开发中回调函数、简化函数定义的核心工具。2.1 箭头函数的基础语法箭头函数的核心是省略function关键字用连接参数与函数体可根据参数数量、函数体行数灵活简写修正原文笔误后的完整语法示例如下1. 传统函数与箭头函数基础对比// 传统函数写法 function add(a,b){ console.log(aaaaa); return a b; } // 多参数箭头函数参数需用()包裹多行函数体需用{}包裹手动return const add1 (a,b) { console.log(aaaaa); return a b; };2. 单参数简写形式// 只有1个参数时可省略参数的() // 函数体只有一行return语句时可省略{}和return关键字 const add2 a a a; console.log(add2(5)); // 输出103. 无参数写法// 无参数时必须保留() const add3 () console.log(aaa); add3(); // 执行输出aaa2.2 箭头函数的核心特性与传统函数的本质区别箭头函数并非只是传统函数的简写其底层执行机制与传统函数有本质差异核心特性如下this 指向固定不变箭头函数没有自己的this其this继承自定义时所在的外层作用域的 this而非调用时的执行上下文。这是箭头函数最核心的特性彻底解决了传统函数this随调用者变化的问题且无法通过call/apply/bind修改其 this 指向。无 arguments 对象箭头函数内部无法访问传统函数的arguments参数列表如需获取动态参数可使用 ES6 剩余参数...rest替代。不可作为构造函数箭头函数没有prototype原型无法使用new关键字实例化否则会直接报错。不可使用 yield 关键字无法作为 Generator 生成器函数使用。this 指向典型示例const obj { name: 张三, // 传统函数this指向调用者obj traditionalFunc: function() { console.log(this.name); // 输出 张三 }, // 箭头函数this继承定义时的外层作用域全局window arrowFunc: () { console.log(this.name); // 输出 undefined } } obj.traditionalFunc(); obj.arrowFunc();三、setTimeout 延时器的循环陷阱作用域与事件循环的碰撞setTimeout 循环陷阱是 JS 面试超高频考点也是开发中极易踩坑的场景其本质是变量作用域规则与JS 事件循环机制共同作用的结果我们将从现象到本质彻底拆解这一经典问题。3.1 循环陷阱的现象// 循环陷阱var版本 for(var i0;i10;i){ setTimeout(()console.log(i),1000); } // 1秒后连续输出10个10// 标准解决方案let版本 for(let j0;j10;j){ setTimeout(()console.log(j),1000); } // 1秒后依次输出0 1 2 3 4 5 6 7 8 93.2 底层原理深度解析要彻底搞懂循环陷阱必须结合 3 个 JS 核心机制JS 单线程与事件循环JS 是单线程语言setTimeout属于宏任务会被放入异步任务队列必须等待主线程的同步代码for 循环全部执行完毕后才会执行任务队列中的回调函数。无论延时时间是 0 还是 1000ms回调函数一定在 for 循环完全结束后才执行。var 的全局作用域特性var声明的i是全局作用域变量整个 for 循环共用同一个i变量。循环同步执行时只是不断给同一个i重新赋值循环结束后全局的i已经变成了 10。此时执行 10 个回调函数所有回调访问的都是同一个全局i因此连续输出 10 个 10。let 的块级作用域特性let声明的j拥有块级作用域for 循环的每一次迭代都会创建一个独立的、全新的j变量每个循环体的块作用域完全隔离。每一次循环setTimeout 的回调函数都会捕获当前迭代块中独立的j变量这个值不会被后续循环修改。循环结束后10 个回调函数分别访问各自捕获的j因此正常输出 0-9。四、核心知识点汇总表格表 1var、let、const 核心特性对比表特性varletconst作用域函数级作用域无块级作用域块级作用域块级作用域变量提升存在声明提升值为 undefined不存在存在暂时性死区必须先声明后使用不存在存在暂时性死区必须先声明后使用重复声明同一作用域内允许同一作用域内不允许同一作用域内不允许初始化要求声明时可不用初始化声明时可不用初始化声明时必须立即初始化赋值修改可随意修改可随意修改基本类型不可修改引用类型不可修改引用地址可修改内部属性全局声明挂载 window是否否表 2箭头函数与传统函数核心对比表特性传统函数function箭头函数写法必须写 function 关键字语法固定可根据参数和函数体简写语法更简洁this 指向指向调用时的执行上下文调用者可通过 call/apply/bind 修改继承定义时外层作用域的 this固定不变call/apply/bind 无法修改arguments 对象有可访问传入的参数列表无可通过剩余参数...rest 替代构造函数可作为构造函数支持 new 实例化不可作为构造函数无 prototype使用 new 会报错Generator 函数支持可使用 yield 关键字不支持无法使用 yield 关键字表 3setTimeout 循环陷阱解决方案对比表解决方案实现原理优点缺点适用场景let 块级作用域每次循环创建独立块级作用域保存当前迭代值写法最简洁ES6 标准方案无额外性能开销需 ES6 环境支持现代前端开发所有 ES6 环境setTimeout 传参利用 setTimeout 第三个参数传参传入当前迭代值写法简单无需处理作用域极老旧浏览器IE9 及以下不支持第三个参数