【ES6】ECMAS6新特性概览(一):变量声明let与const、箭头函数、模板字面量全面解析


🔥 个人主页:空白诗 🔥 热门专栏:【JavaScript】

文章目录

    • [🌿 引言](#🌿 引言)
    • [一、 `let` 和 `const` - 变量声明的新方式 🌟](#一、 letconst - 变量声明的新方式 🌟)
      • [📌 `var`的问题回顾](#📌 var的问题回顾)
      • [📌 `let`的革新](#📌 let的革新)
      • [📌 `const`的不变之美](#📌 const的不变之美)
    • [二、 Arrow Functions - 箭头函数,简洁之美 ✨](#二、 Arrow Functions - 箭头函数,简洁之美 ✨)
      • [📌 语法精简](#📌 语法精简)
      • [📌 普通函数与`this`的动态绑定](#📌 普通函数与this的动态绑定)
      • [📌 箭头函数与静态`this`绑定](#📌 箭头函数与静态this绑定)
      • [📌 小结](#📌 小结)
    • [三、 Template Literals - 字符串模板,插值新体验 💬](#三、 Template Literals - 字符串模板,插值新体验 💬)
      • [📌 基础插值](#📌 基础插值)
      • [📌 多行字符串与保留格式](#📌 多行字符串与保留格式)
      • [📌 表达式与函数](#📌 表达式与函数)
      • [📌 标签化模板字面量](#📌 标签化模板字面量)
      • [📌 小结](#📌 小结)
    • [四、 Default Parameters - 参数默认值,告别undefined ❓](#四、 Default Parameters - 参数默认值,告别undefined ❓)
      • [📌 基本用法](#📌 基本用法)
      • [📌 复合默认值与逻辑判断](#📌 复合默认值与逻辑判断)
      • [📌 注意事项与实践建议](#📌 注意事项与实践建议)
      • [📌 小结](#📌 小结)
    • 总结

🌿 引言

ES6 ,作为ECMAScript 2015 的简称,标志着JavaScript编程语言的一个重要进化节点。它不是渐进的变化,而是一次飞跃式的更新,为开发者带来了一系列强大的新特性与语法糖,极大提升了代码的简洁性、可读性和运行效率。从新的变量声明方式letconst,到优雅的箭头函数模板字符串,再到让对象操作更为灵活的解构赋值增强的对象字面量ES6 的每项改进都旨在让JavaScript适应日益复杂的应用场景,同时保持其作为脚本语言的活力与魅力。本文是深入探索这些核心特性的起点,为你铺开一条通向高效、现代JavaScript编程实践的道路。


一、 letconst - 变量声明的新方式 🌟

ES6 之前的JavaScript世界,var作为声明变量的主力军,虽功不可没,却也因其变量提升(variable hoisting)和函数作用域(function scope)的特性,给开发者带来了不少困惑和潜在错误。随着ES6 的登场,letconst的引入如同一股清流,为变量管理领域带来了革新与严谨性。

📌 var的问题回顾

  • 变量提升(Variabe Hoisting) : 当你在函数内部使用var声明一个变量时,这个声明会被JavaScript引擎默默地"提升"到当前作用域的最顶部。尽管这不会提升变量的赋值操作,但会导致在声明前访问变量时不会抛出ReferenceError错误,而是输出undefined。这种机制有时会导致难以预料的结果,尤其是在涉及条件或循环时。

    javascript 复制代码
    function illustrateVarHoist() {
        console.log(bar); // 输出:undefined,证明变量被提升了
        var bar = "I'm hoisted!";
        console.log(bar); // 输出:I'm hoisted!
    }
    illustrateVarHoist();
  • 作用域限制(Scope Limitations) : var声明的变量遵循函数作用域(function scope)或全局作用域(global scope),而不是块级作用域(block scope)。这意味着,在一个函数内部声明的var变量对整个函数都是可见的,而在任意代码块(如if语句或for循环)中声明的var变量实际上属于包含它的函数作用域或全局作用域。这可能导致在循环或条件判断中预期之外的变量共享问题。

    javascript 复制代码
    for (var i = 0; i < 3; i++) {
        setTimeout(function() {
            console.log(i); // 输出:3,3,3,因为i是在同一个作用域内被修改的
        }, 1000);
    }

以上代码展示了由于var的作用域特性,在循环结束后每个setTimeout回调访问的i都是循环结束时的值(3),而非每次迭代时的值。这强调了使用var时可能遇到的意料之外的副作用。

📌 let的革新

  • 块级作用域(Block Scoping) : 引入let关键字是ES6 对变量作用域处理的一次重大改进。与var的函数作用域不同,let声明的变量严格遵循块级作用域原则。这意味着,如果你在一个特定的代码块(例如if语句、for循环或者一对大括号内)使用let声明变量,那么这个变量仅在此块内有效,出了这个块就无法访问,从而有效避免了变量泄漏到外部作用域,减少了意外的变量覆盖问题。

    javascript 复制代码
    {
        let y = 5;
        console.log(y); // 输出 5
    }
    // console.log(y); // 这里会报错:y is not defined,体现了let的严格块级限制
    
    for (let i = 0; i < 3; i++) {
        setTimeout(function() {
            console.log(i); // 分别输出 0, 1, 2,每一次迭代i都是新的绑定
        }, 1000);
    }

在上面的循环例子中,每次循环迭代都会创建一个新的i变量实例,每个setTimeout回调都能记住自己迭代时i的值,解决了使用var时的闭包陷阱问题,这是let在处理循环中的一个显著优势,极大增强了代码的逻辑清晰度和可预测性。

📌 const的不变之美

  • 常量声明(Constant Declaration) : const关键字的引入,为JavaScript开发者提供了一种定义不可变变量的方式。一旦使用const声明了一个变量并赋予初始值,该变量的值便被视为固定,任何尝试修改的操作都将引发错误。这促进了代码的清晰度和稳定性,鼓励开发者明确哪些数据是程序状态中的常量,减少意外变更的风险。

    javascript 复制代码
    const MAX_SCORE = 100; // 游戏中的最高分,不应改变
    // MAX_SCORE = 200; // 这里会导致错误:Assignment to constant variable.
    
    // 对象和数组的特殊情况:
    const settings = { difficulty: 'hard' };
    // settings = {}; // 错误:不能重新赋值
    settings.difficulty = 'easy'; // 但是可以修改对象的属性,因为const冻结的是引用,非对象内容本身
    console.log(settings); // 输出:{ difficulty: 'easy' }

通过const,开发者能够明确标识出那些设计为不应更改的值,无论是基础数据类型还是对象的引用,这对于维护大型代码库尤其重要,因为它提高了代码的可读性和可维护性。结合let的块级作用域特性,两者共同为JavaScript的变量管理带来了一场革命,推动了更加严谨和高效的编程实践。
这里只做一些简单介绍,更多关于 varletconst以及变量提升作用域逻辑TDZ 的内容在之前的一篇博客:
JavaScript基础知识强化:变量提升、作用域逻辑及TDZ的全面解析


二、 Arrow Functions - 箭头函数,简洁之美 ✨

箭头函数(=>)作为ES6 的一项革新特性,显著简化了函数的语法,同时以一种新颖的方式解决了长期困扰开发者的关于this关键字上下文绑定的问题,为JavaScript函数表达式赋予了新的生命和优雅。

📌 语法精简

与传统函数声明或函数表达式不同,箭头函数采用了一种更加紧凑的语法结构,摒弃了function关键字,并且在函数体仅包含一个表达式时,允许省略花括号 {}return 语句,直接返回该表达式的结果。

javascript 复制代码
// 传统函数声明
function add(a, b) {
    return a + b;
}

// 箭头函数表达式
const add = (a, b) => a + b; // 直接返回求和结果

// 传统函数表达式
[1, 2, 3].map(function(x) {
    return x * x;
});

// 使用箭头函数,提升代码简洁度
[1, 2, 3].map(x => x * x);

📌 普通函数与this的动态绑定

在经典的函数实现中,this的值由函数调用的环境在运行时动态确定,这可能导致在复杂调用链或异步编程中出现预期之外的this行为。

javascript 复制代码
function identify() {
    console.log(this.name);
}

const obj = { name: "Object Context", identify: identify };

// 正常情况下的`this`绑定
obj.identify(); // 输出 "Object Context"

// 独立调用时,非严格模式下的`this`通常指向全局对象
identify(); // 可能在浏览器中输出 "Window" 或在Node.js中输出 "global"

在面试时经常会遇到的一个问题是:如何改变普通函数中this的指向❓

一种是使用.call(), .apply(), 或 .bind()方法,这三种方法在本文中不做详细解释,详细区别可查看另一篇博客:JavaScript中call、apply与bind的区别

一种是手动设置that = this_this去保存当前的this值,然后在需要的地方使用这个变量

还有一种就是使用箭头函数,将在下文详细解释

📌 箭头函数与静态this绑定

箭头函数的核心变革在于其处理this的方式。它们不会创建自己的this,而是直接沿用外层(词法作用域)的this值,这意味着箭头函数内的this在定义时即被固定,调用上下文的改变不会影响到它。

javascript 复制代码
const objArrow = { name: "Arrow Context" };

// 箭头函数捕获定义时的`this`
const identifyArrow = () => console.log(this.name);

// 即便函数被赋值或传递,其`this`依然绑定到定义时的环境
objArrow.identifyArrow = identifyArrow;
objArrow.identifyArrow(); // 明确输出 "Arrow Context"

// 直接调用,箭头函数的`this`遵循外层作用域
identifyArrow(); // 输出取决于外部上下文,可能是全局对象的名称
适用场景
  • 事件处理与回调 :在事件监听或异步操作如setTimeoutPromise回调中,使用箭头函数可以避免this丢失问题。

  • 对象方法 :虽然通常不推荐将箭头函数直接作为对象的方法(因为它无法直接绑定对象实例作为this),但在一些场景下,如需要保持外层作用域的this时,箭头函数依然有用。

  • 函数内部函数 :在另一个函数内部定义的箭头函数,通常用于保留外层的this,避免闭包问题。

注意事项
  • 不可用作构造函数 :箭头函数不能用new调用,因为它没有自己的this,没有原型属性。
  • argumentssuper :箭头函数不绑定自己的arguments,且不能使用super关键字,需要时考虑使用其他方案。

📌 小结

  • 普通函数this是动态的,调用时根据上下文确定,适用于需要灵活上下文的场景。
  • 箭头函数 通过静态绑定this,确保了函数内部的上下文一致性,特别适合用于闭包、事件处理和异步编程等对this稳定有高要求的场景。

通过对比分析,我们深入理解了箭头函数在处理this问题上的独特优势,以及它如何通过简化语法和增强逻辑清晰度来提升JavaScript代码的质量和可维护性。在实际编码实践中,合理选择普通函数与箭头函数,能够有效提升程序的可靠性和开发效率。


三、 Template Literals - 字符串模板,插值新体验 💬

自从ES6 引入模板字面量 (Template Literals)以来,处理字符串和嵌入变量或表达式变得更加直观和简洁。这种以反引号(`````)包围的字符串格式,不仅改善了代码的可读性,还提供了强大的插值功能,让我们能够以一种接近自然语言的方式构造复杂的字符串。

📌 基础插值

基本的插值操作通过${expression}来完成,其中expression可以是任何有效的JavaScript表达式,包括变量、运算符乃至函数调用。

javascript 复制代码
const name = "Alice";
const age = 28;

// 使用模板字面量进行字符串拼接
console.log(`My name is ${name}, and I am ${age} years young.`);

// 输出:My name is Alice, and I am 28 years young.

📌 多行字符串与保留格式

除了变量插值,模板字面量还天然支持多行字符串,无需使用转义字符,这对于编写HTML片段、SQL查询或是任何需要跨多行的文本内容尤其方便。

javascript 复制代码
const poem = `The woods are lovely, dark and deep,
But I have promises to keep,
And miles to go before I sleep,
And miles to go before I sleep.`;

console.log(poem);

📌 表达式与函数

模板字面量中的插值不仅仅局限于简单变量,你甚至可以直接执行函数或计算表达式,并将其结果嵌入到字符串中。

javascript 复制代码
function getGreeting(hour) {
    return hour < 12 ? "Good morning" : "Good afternoon";
}

const currentHour = new Date().getHours();
console.log(`${getGreeting(currentHour)}, ${name}! How's your day going?`);

📌 标签化模板字面量

进一步地,通过"标签模板"功能,你可以自定义模板字面量的处理逻辑。这为字符串的预处理(如过滤、格式化等)提供了无限可能。

javascript 复制代码
function highlight(text) {
    return text.map((part, index) => {
        if (index % 2 === 0) return part;
        else return `<strong>${part}</strong>`;
    }).join('');
}

const sentence = `Cup of coffee, slice of cake`;
console.log(highlight`I'd like a ${sentence}`);

📌 小结

模板字面量以其便捷的插值语法、多行文本的支持以及通过标签化进行的高级处理能力,彻底改变了JavaScript中字符串处理的方式。它们让代码更易于阅读和维护,同时也大大提高了开发者在构建动态内容时的灵活性和效率。掌握这一特性,对于现代JavaScript编程至关重要。


四、 Default Parameters - 参数默认值,告别undefined ❓

ES6 的参数默认值功能是JavaScript函数定义的一次重要进化,它赋予了开发者为函数参数指定默认值的能力,从而在调用函数时即使未提供相应实参,也能避免undefined的困扰。此特性显著提升了代码的健壮性与灵活性,使函数调用更加优雅流畅。

📌 基本用法

在函数声明时,通过在参数名后紧接赋值运算符=及默认值即可设置参数的默认值。如下例所示,当调用greet()时未提供参数,name自动采用默认值Stranger

javascript 复制代码
function greet(name = "Stranger") {
    console.log(`Hello, ${name}!`);
}

greet(); // 输出:Hello, Stranger!
greet("Alice"); // 输出:Hello, Alice!

📌 复合默认值与逻辑判断

默认值的设定远不止限于简单字面量,它能够是任何有效的JavaScript表达式,甚至是函数调用,这为参数处理引入了丰富的逻辑潜力。

javascript 复制代码
function calculateArea(width, height = width * 2) {
    return width * height;
}

console.log(calculateArea(10)); // 未指定高度,使用默认值(宽度的两倍),输出:200
console.log(calculateArea(10, 5)); // 指定了高度,覆盖默认逻辑,输出:50

📌 注意事项与实践建议

  • 顺序与依赖: 如果函数有多个参数且其中某些依赖前面的参数值来计算默认值,务必确保前面的参数都有明确的值,否则默认值逻辑可能不会按预期执行。

  • 默认值共享: 注意默认值如果是引用类型(如数组、对象),所有函数调用将共享同一默认实例,修改一个实例会影响到其他调用。为避免这种情况,可以考虑使用函数来生成默认值。

  • 默认值惰性评估: 默认值在函数定义时计算,对于包含复杂计算或副作用的默认值需谨慎使用。

📌 小结

默认参数机制的加入,标志着JavaScript函数设计步入了一个更加人性化和健壮的新阶段。它不仅简化了函数接口的使用,减少了因遗漏参数导致的错误,还促进了代码的清晰度与可维护性,无疑是现代JavaScript开发中不可或缺的一环。掌握并善用此特性,将使你的代码更加稳健且优雅。


总结

本文深入探讨了ECMAS6 中对JavaScript变量声明方式的重要革新,重点聚焦于letconst的引入,以及箭头函数与模板字面量的使用,这些特性如何重塑了现代JavaScript编程实践。

  • letconst声明 :打破了var的限制,let带来了块级作用域,确保变量在定义块内有效,避免了变量污染;const确保不可变性,加强了代码的安全性与可读性。

  • 箭头函数 :简化了函数语法,提升了可读性,解决了this绑定难题,尤其在异步编程与事件处理中的应用显著。

  • 模板面量:提供了一种动态构造字符串的新方式,支持多行文本与表达式插值,增强了代码的自然度与灵活性。

这些特性综合起来,ECMAS6 不仅增强了JavaScript的严谨性与代码质量,还极大地提高了开发效率,使开发者能编写出更健壮、易维护、可读性强的应用程序。理解这些核心机制,是成为JavaScript高手的关键路径。

后续我们将继续深入了解ES6 新特性,例如class类、模块、异步处理promise、生成器函数generatorsmapset数据结构...等等。当然其中有些内容在之前的博客也有详细探讨过,内容都在👉🏻 🔥 专栏:【JavaScript】。感谢大家支持~ ❤️

相关推荐
腾讯TNTWeb前端团队6 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰9 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪9 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪9 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy10 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom11 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom11 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom11 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom11 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom11 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试