news 2026/7/31 22:34:26

前端十年:从0到资深开发者的10堂必修课【第2篇】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
前端十年:从0到资深开发者的10堂必修课【第2篇】

前端十年:从0到资深开发者的10堂必修课

第2篇:进阶篇——JavaScript 语言精髓

如果说 HTML 和 CSS 是前端的皮囊,那么 JavaScript 就是灵魂。掌握 JavaScript 的核心机制——作用域、闭包、原型、异步——是区分“会用”和“精通”的关键分水岭。本篇将深入这些精髓,让你写出更健壮、更优雅的代码。


一、作用域与闭包

作用域决定了变量在代码中的可见性和生命周期。理解作用域是理解闭包的前提,而闭包则是 JavaScript 中最强大的特性之一。

1. 全局/函数/块级作用域、词法作用域

JavaScript 中的作用域主要有三种:

  • 全局作用域:在代码任何地方都能访问的变量,挂载在window(浏览器)或global(Node.js)上。
  • 函数作用域:在函数内部声明的变量,只能在函数内部及嵌套函数中访问。
  • 块级作用域:由letconst声明的变量在{}内形成块级作用域(var没有块级作用域)。

词法作用域(静态作用域):函数的作用域在定义时就已确定,而不是在调用时。这决定了变量的查找路径。

varglobalVar='全局';functionouter(){varouterVar='外部';functioninner(){varinnerVar='内部';console.log(globalVar);// 可以访问全局console.log(outerVar);// 可以访问外部函数的变量console.log(innerVar);// 可以访问自己的变量}inner();}outer();// 在外部无法访问 outerVar 或 innerVar

块级作用域示例

if(true){vara=1;// 函数作用域(实际是全局,因为不在函数内)letb=2;// 块级作用域constc=3;// 块级作用域}console.log(a);// 1console.log(b);// ReferenceError: b is not definedconsole.log(c);// ReferenceError: c is not defined

2. 闭包的定义、常见应用(模块化、防抖节流)

闭包:当一个函数能够记住并访问其词法作用域,即使该函数在其词法作用域之外执行,我们就说这个函数产生了闭包。简单说:函数 + 它对外部变量的引用 = 闭包

经典闭包示例

functioncreateCounter(){letcount=0;returnfunction(){count++;returncount;};}constcounter=createCounter();console.log(counter());// 1console.log(counter());// 2// 外部无法直接访问 count,但闭包保留了它的引用

常见应用

  1. 模块化(封装私有变量)
    利用闭包实现模块模式,隐藏内部实现细节。

    constmodule=(function(){letprivateVar=0;functionprivateMethod(){/* ... */}return{publicMethod:function(){privateVar++;},getVar:function(){returnprivateVar;}};})();module.publicMethod();console.log(module.getVar());// 1console.log(module.privateVar);// undefined
  2. 防抖与节流
    防抖(debounce)和节流(throttle)用于限制高频事件的触发频率,内部通过闭包保存定时器 ID 或上次执行时间。

    // 防抖:多次触发只执行最后一次functiondebounce(fn,delay){lettimer=null;returnfunction(...args){clearTimeout(timer);timer=setTimeout(()=>fn.apply(this,args),delay);};}// 节流:每隔一段时间执行一次functionthrottle(fn,interval){letlastTime=0;returnfunction(...args){constnow=Date.now();if(now-lastTime>=interval){lastTime=now;fn.apply(this,args);}};}

闭包的注意点:由于闭包会持有外部变量的引用,可能导致内存泄漏(尤其是旧版 IE 中涉及 DOM 元素时)。现代引擎已优化,但依然要避免无意中创建大量闭包。


二、原型链与面向对象

JavaScript 的面向对象是基于原型的,而不是传统的类。理解原型链是理解对象继承、new操作符、class语法的关键。

1. 构造函数、原型、原型链查找机制

构造函数:通过new调用的函数,用于创建对象。

functionPerson(name,age){this.name=name;this.age=age;// 不要在这里定义方法,否则每个实例都会创建一份函数副本// this.sayHi = function() { ... }}constp1=newPerson('Alice',25);

原型(prototype):每个函数都有一个prototype属性,指向一个对象。通过该函数创建的实例会继承这个原型对象上的属性和方法。

Person.prototype.sayHi=function(){console.log(`Hi, I'm${this.name}`);};p1.sayHi();// Hi, I'm Alice

原型链:每个对象都有一个隐式原型__proto__(标准中为[[Prototype]]),指向其构造函数的prototype。当访问对象属性时,如果对象本身没有,就会沿着__proto__向上查找,直到Object.prototype甚至null

console.log(p1.__proto__===Person.prototype);// trueconsole.log(Person.prototype.__proto__===Object.prototype);// trueconsole.log(Object.prototype.__proto__);// null

属性查找机制

  1. 检查对象自身是否有该属性。
  2. 如果没有,沿着__proto__到其原型对象上查找。
  3. 重复直到找到或到达null

示例:模拟原型链

functionAnimal(type){this.type=type;}Animal.prototype.getType=function(){returnthis.type;};functionDog(name){Animal.call(this,'犬科');// 借用构造函数this.name=name;}// 继承原型Dog.prototype=Object.create(Animal.prototype);Dog.prototype.constructor=Dog;Dog.prototype.bark=function(){console.log('汪汪');};constd=newDog('旺财');console.log(d.getType());// 犬科(来自 Animal.prototype)d.bark();// 汪汪(来自 Dog.prototype)console.log(d.toString());// 来自 Object.prototype

2. ES6 Class 语法糖与继承

ES6 的class本质上是基于原型的语法糖,写法更接近传统面向对象语言,但底层仍是原型链。

基本语法

classPerson{constructor(name,age){this.name=name;this.age=age;}sayHi(){console.log(`Hi, I'm${this.name}`);}staticinfo(){// 静态方法console.log('这是一个人类');}}constp=newPerson('Bob',30);p.sayHi();Person.info();

继承:使用extendssuper

classStudentextendsPerson{constructor(name,age,grade){super(name,age);// 必须先调用 super 才能使用 thisthis.grade=grade;}study(){console.log(`${this.name}正在学习`);}// 重写父类方法sayHi(){super.sayHi();// 调用父类方法console.log(`我是学生,年级${this.grade}`);}}consts=newStudent('Charlie',18,12);s.sayHi();s.study();

注意:class只是语法糖,内部依然通过原型链实现。例如Student.prototype的原型是Person.prototype


三、异步编程

JavaScript 是单线程语言,但通过事件循环机制实现了非阻塞的异步操作。掌握异步编程是现代前端开发的必备技能。

1. 回调地狱、Promise 原理与 API

回调函数是 JavaScript 最早的异步处理方式。但当多个异步操作嵌套时,容易形成“回调地狱”(callback hell),代码难以阅读和维护。

// 回调地狱示例getData1(function(data1){getData2(data1,function(data2){getData3(data2,function(data3){console.log(data3);});});});

Promise是 ES6 引入的解决方案,代表一个异步操作的最终完成(或失败)及其结果值。Promise 有三种状态:

  • pending:进行中
  • fulfilled(或resolved):已成功
  • rejected:已失败

创建 Promise

constpromise=newPromise((resolve,reject)=>{// 异步操作setTimeout(()=>{if(成功){resolve('成功数据');}else{reject('失败原因');}},1000);});

使用 Promise:通过.then().catch().finally()处理结果。

promise.then(result=>{console.log('成功:',result);returnresult+' 处理';}).then(processed=>{console.log('再次处理:',processed);}).catch(error=>{console.error('失败:',error);}).finally(()=>{console.log('无论成功失败都会执行');});

Promise 静态方法

  • Promise.resolve(value):返回一个成功状态的 Promise。
  • Promise.reject(reason):返回一个失败状态的 Promise。
  • Promise.all(iterable):所有 Promise 成功则返回所有结果数组,任一失败则立即失败。
  • Promise.race(iterable):返回最先完成的 Promise 结果(无论成功或失败)。
  • Promise.allSettled(iterable):返回所有 Promise 的结果(包含状态和值),不会因某个失败而终止。
  • Promise.any(iterable):返回第一个成功的 Promise,如果全部失败则返回 AggregateError。

2. async/await 与错误处理

async/await是 ES2017 引入的语法糖,基于 Promise 使异步代码看起来像同步代码。

async 函数:声明一个函数,使其自动返回一个 Promise。

asyncfunctionfetchData(){// 如果返回非 Promise 值,会自动包装成 Promise.resolvereturn'数据';}fetchData().then(console.log);// '数据'

await:只能在 async 函数内部使用,等待一个 Promise 完成并返回其结果。如果 Promise 被拒绝,会抛出异常,可用try/catch捕获。

asyncfunctionprocess(){try{constdata1=awaitgetData1();constdata2=awaitgetData2(data1);constdata3=awaitgetData3(data2);console.log(data3);}catch(error){console.error('出错啦:',error);}}process();

注意:await会阻塞 async 函数内部的后续代码,但不会阻塞外部(因为 async 函数本身是异步的)。多个无依赖的异步操作可以并发执行,用Promise.all优化:

asyncfunctionparallel(){const[res1,res2]=awaitPromise.all([getData1(),getData2()]);// 使用 res1, res2}

3. 事件循环(宏任务、微任务)

JavaScript 的事件循环(Event Loop)是其异步并发的核心机制。它负责协调执行栈、任务队列(宏任务)和微任务队列。

宏任务(MacroTask):由宿主环境(浏览器/Node)发起,包括:

  • setTimeoutsetInterval
  • setImmediate(Node)
  • I/O 操作
  • UI 渲染(浏览器)
  • postMessageMessageChannel

微任务(MicroTask):由 JavaScript 自身发起,优先级高于宏任务,包括:

  • Promise.then/catch/finally的回调
  • MutationObserver(浏览器)
  • queueMicrotask
  • process.nextTick(Node,但优先级高于微任务)

事件循环流程

  1. 执行一个宏任务(从宏任务队列中取一个)。
  2. 执行过程中产生的微任务会被依次加入微任务队列。
  3. 宏任务执行完后,清空微任务队列中的所有微任务(按添加顺序执行)。
  4. 如果需要渲染,执行渲染。
  5. 开始下一轮循环,执行下一个宏任务。

示例分析

console.log('1');// 同步代码setTimeout(()=>{console.log('2');// 宏任务},0);Promise.resolve().then(()=>{console.log('3');// 微任务});console.log('4');// 同步代码// 输出顺序:1 4 3 2

解释:

  • 执行全局脚本(一个宏任务),输出14
  • 遇到setTimeout,回调被放入宏任务队列。
  • 遇到Promise.then,回调被放入微任务队列。
  • 全局脚本执行完毕,清空微任务队列,输出3
  • 下一轮循环,执行宏任务setTimeout回调,输出2

理解事件循环有助于避免异步执行顺序的陷阱,并优化代码性能。


总结

本篇深入剖析了 JavaScript 的核心精髓:

  • 作用域与闭包:理解了词法作用域和闭包的形成机制,学会了用闭包实现模块化、防抖节流等高级模式。
  • 原型链与面向对象:掌握了构造函数、原型、原型链的查找规则,以及 ES6class的用法与本质。
  • 异步编程:从回调地狱到 Promise,再到 async/await,并理清了事件循环中的宏任务与微任务。

这些知识点是 JavaScript 进阶的必经之路,也是面试中的高频考点。下一篇我们将进入浏览器篇,探索渲染原理、事件机制与 DOM 性能优化,敬请期待!


思考题

  1. 闭包一定会造成内存泄漏吗?如何避免?
  2. Function.prototype.callapplybind与原型链有什么关系?
  3. 以下代码的输出顺序是什么?为什么?
    setTimeout(()=>console.log('A'),0);Promise.resolve().then(()=>console.log('B'));console.log('C');newPromise((resolve)=>{console.log('D');resolve();}).then(()=>console.log('E'));
  4. 使用 async/await 时,如何并行执行多个异步任务并等待所有结果?

欢迎在评论区留下你的答案和疑问,一起讨论进步!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 14:57:01

2026 无人机 AI 算法全景图:7 大场景 50+ 算法详解

2026 无人机 AI 算法全景图:7 大场景 50 算法详解一张图看懂无人机 AI 算法全貌 前言 很多人问我:共达地到底有哪些算法? 今天把我们的算法家底全部公开,7 大场景、50 算法,建议收藏备用。一、飞行辅助类算法 让无人机…

作者头像 李华
网站建设 2026/7/14 14:57:06

具身智能

定义具身智能核心:交互循环技术框架词向量化概率预测具身智能的执行单元电机步进电机控制特点:精确但“盲目”的开环控制优点:控制简单、定位精准、低速扭矩好缺点:丢步风险、发热严重、噪音振动无刷电机比较选型指南舵机舵机内部…

作者头像 李华
网站建设 2026/7/14 14:57:16

Nano-Banana部署优化指南:Euler Ancestral调度器提升生成稳定性

Nano-Banana部署优化指南:Euler Ancestral调度器提升生成稳定性 1. 项目概述与价值 Nano-Banana Studio是一款专注于物理结构拆解风格的AI创作工具,能够将复杂的服装、鞋包或电子产品转化为极具美感的平铺图(Knolling)或分解视图…

作者头像 李华
网站建设 2026/7/14 14:57:14

实时计算化技术中的低延迟处理高吞吐量与容错机制

实时计算技术正成为大数据时代的核心引擎,尤其在金融风控、物联网监测等场景中,低延迟、高吞吐与容错能力直接决定了系统的可靠性。随着数据量爆炸式增长,如何在毫秒级响应海量请求的同时保障系统稳定,成为技术攻坚的关键。本文将…

作者头像 李华