[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"\u002Fresource\u002Fdocument\u002Flist?undefined":3,"\u002Fresource\u002Fdocument\u002Fquery\u002Fzjf5qapwqtqiohcn?undefined":462,"\u002Fresource\u002Fadvertise\u002Flist?type=all?undefined":467},{"data":4,"status":460,"success":461},[5,148,202,291,332,370,420],{"books":6,"desc":145,"id":8,"image":146,"title":147},[7,40,63,78,93,105,117],{"cateId":8,"chapters":9,"desc":36,"id":11,"time":37,"title":38,"video":39},1,[10,15,18,21,24,27,30,33],{"bookId":11,"id":12,"indexOrder":13,"name":14},24,"8egfulw98v3h680j",0,"JavaSE 笔记（一）走进Java语言",{"bookId":11,"id":16,"indexOrder":13,"name":17},"pew6po6wrou23pk3","JavaSE 笔记（二）面向过程编程",{"bookId":11,"id":19,"indexOrder":13,"name":20},"eldst1fgrbdkmfs7","JavaSE 笔记（三）面向对象基础",{"bookId":11,"id":22,"indexOrder":13,"name":23},"48zphgkpjto8cath","JavaSE 笔记（四）面向对象高级篇",{"bookId":11,"id":25,"indexOrder":13,"name":26},"6r4llai92yc15j98","JavaSE 笔记（五）泛型程序设计",{"bookId":11,"id":28,"indexOrder":13,"name":29},"k6fmxd6qabgkwm9i","JavaSE 笔记（六）集合类与IO",{"bookId":11,"id":31,"indexOrder":13,"name":32},"qrd0xfttsz32gpqg","JavaSE 笔记（七）多线程与反射",{"bookId":11,"id":34,"indexOrder":13,"name":35},"td5tgn04nqmkrryt","JavaSE 笔记（八）GUI程序开发","基于Java25全新录制的SE课程",2025,"JavaSE 核心内容","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV163GGz2E8c",{"cateId":8,"chapters":41,"desc":59,"id":8,"time":60,"title":61,"video":62},[42,44,46,49,51,53,55,57],{"bookId":8,"id":43,"indexOrder":13,"name":14},"ibeeuwsbbi00undq",{"bookId":8,"id":45,"indexOrder":13,"name":17},"dncxjecdv4wciqcp",{"bookId":8,"id":47,"indexOrder":13,"name":48},"jviyz2hsht9ete5k","JavaSE 笔记（三）面向对象基础篇",{"bookId":8,"id":50,"indexOrder":13,"name":23},"qb9i6q9fap7bg1cc",{"bookId":8,"id":52,"indexOrder":13,"name":26},"hnkrjrkm3hjzeq6s",{"bookId":8,"id":54,"indexOrder":13,"name":29},"erpm32wduoaaqmrx",{"bookId":8,"id":56,"indexOrder":13,"name":32},"lfqtvxr7azumcwja",{"bookId":8,"id":58,"indexOrder":13,"name":35},"qs7gqok56gzc6idr","2022年制作的JavaSE版本",2022,"JavaSE 22年旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1YP4y1o75f\u002F",{"cateId":8,"chapters":64,"desc":75,"id":66,"time":60,"title":76,"video":77},[65,69,72],{"bookId":66,"id":67,"indexOrder":13,"name":68},2,"g96k66kczovvbm1i","JVM 笔记（一）走进JVM",{"bookId":66,"id":70,"indexOrder":13,"name":71},"ydd7n3jg8unc3clg","JVM 笔记（二）内存管理",{"bookId":66,"id":73,"indexOrder":13,"name":74},"r9dq37de0kaeauoi","JVM 笔记（三）类与类加载","了解Java的底层运作机制","Java JVM 虚拟机","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Er4y1r7as\u002F",{"cateId":8,"chapters":79,"desc":90,"id":81,"time":60,"title":91,"video":92},[80,84,87],{"bookId":81,"id":82,"indexOrder":13,"name":83},3,"asncyye9ya18gfar","JUC 笔记（一）再谈多线程",{"bookId":81,"id":85,"indexOrder":13,"name":86},"5tr1sm4ho6ygpt9q","JUC 笔记（二）并发编程核心",{"bookId":81,"id":88,"indexOrder":13,"name":89},"1scf51z5300mzxkh","JUC 笔记（三）并发编程进阶","你也可以成为多线程的主宰者","Java JUC 并发编程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1JT4y1S7K8\u002F",{"cateId":8,"chapters":94,"desc":102,"id":96,"time":60,"title":103,"video":104},[95,99],{"bookId":96,"id":97,"indexOrder":13,"name":98},4,"eedesc445ygiqhil","NIO 笔记（一）基础内容",{"bookId":96,"id":100,"indexOrder":13,"name":101},"ndz9t0uunrmfmv4n","NIO 笔记（二）Netty框架专题","编写畅快的高性能网络服务器","Java NIO 网络编程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1ar4y1J7mC\u002F",{"cateId":8,"chapters":106,"desc":114,"id":108,"time":60,"title":115,"video":116},[107,111],{"bookId":108,"id":109,"indexOrder":13,"name":110},5,"9890i8ofuadpwy2b","[扩展篇] Java 9-17新特性介绍",{"bookId":108,"id":112,"indexOrder":13,"name":113},"tsrkqvb6zpmtwh0n","[扩展篇] JavaSE关键字总结 笔记","精彩仍在继续，不要停止脚步","其他内容","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1tU4y1y7Fg\u002F",{"cateId":8,"chapters":118,"desc":141,"id":120,"time":142,"title":143,"video":144},[119,123,126,129,132,135,138],{"bookId":120,"id":121,"indexOrder":13,"name":122},6,"4db9h32opv7imszh","JavaSE 笔记（一）面向过程编程",{"bookId":120,"id":124,"indexOrder":13,"name":125},"c93u3v37br7hgn1q","JavaSE 笔记（二）面向对象基础篇",{"bookId":120,"id":127,"indexOrder":13,"name":128},"yglsjde9gi1jxkcb","JavaSE 笔记（三）泛型与集合类",{"bookId":120,"id":130,"indexOrder":13,"name":131},"ilhi987n986rmvo3","JavaSE 笔记（四）异常机制",{"bookId":120,"id":133,"indexOrder":13,"name":134},"pqv38vexmenglk4k","JavaSE 笔记（五）IO",{"bookId":120,"id":136,"indexOrder":13,"name":137},"jiq41n87i9ia7ilw","JavaSE 笔记（六）多线程",{"bookId":120,"id":139,"indexOrder":13,"name":140},"wn7x2mge9ws79zps","JavaSE 笔记（七）反射","此版本为早期录制的旧版本",2021,"JavaSE 21年旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Gv411T7pi\u002F","包含JavaSE基础路线全部教程笔记，打下坚实的基础","https:\u002F\u002Fpic2.zhimg.com\u002F80\u002Fv2-bf1a927f037a79f4d57d9ae543430a0d_1440w.webp","JavaSE 系列笔记 ☕️",{"books":149,"desc":199,"id":66,"image":200,"title":201},[150,166,178],{"cateId":66,"chapters":151,"desc":162,"id":153,"time":163,"title":164,"video":165},[152,156,159],{"bookId":153,"id":154,"indexOrder":13,"name":155},21,"iqbc2haub31bwqtz","Lombok 极速上手",{"bookId":153,"id":157,"indexOrder":13,"name":158},"ijay2hay19kn1k031","Mybatis 快速上手",{"bookId":153,"id":160,"indexOrder":13,"name":161},"ru4ogh2waocpn4jo","Maven 快速上手","JavaWeb阶段必须扩展知识点",2024,"常用知识讲解","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1gb421J7ok\u002F",{"cateId":66,"chapters":167,"desc":175,"id":169,"time":163,"title":176,"video":177},[168,172],{"bookId":169,"id":170,"indexOrder":13,"name":171},22,"ek20yvb6huhxizx7","JavaWeb 笔记（一）计算机网络基础",{"bookId":169,"id":173,"indexOrder":13,"name":174},"pgevws6w2krkffa4","JavaWeb笔记（二）Java与数据库","全面升级的JavaWeb课程","JavaWeb 网站开发","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1kS421X7rq\u002F",{"cateId":66,"chapters":179,"desc":196,"id":181,"time":142,"title":197,"video":198},[180,184,187,190,193],{"bookId":181,"id":182,"indexOrder":13,"name":183},7,"ggwwj09j2vkfftvd","JavaWeb 笔记（一）Java网络编程",{"bookId":181,"id":185,"indexOrder":13,"name":186},"sauvq105istskjaz","JavaWeb 笔记（二）数据库基础",{"bookId":181,"id":188,"indexOrder":13,"name":189},"xgbeasmvrhxx9tn4","JavaWeb 笔记（三）Java与数据库",{"bookId":181,"id":191,"indexOrder":13,"name":192},"k7dfwua3bsezvw9q","JavaWeb 笔记（四）前端基础",{"bookId":181,"id":194,"indexOrder":13,"name":195},"ycpagby2v7j4p728","JavaWeb 笔记（五）后端开发","搭建属于自己的Web网站","JavaWeb 旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1CL4y1i7qR\u002F","包含JavaWeb路线全套笔记，从零开始搭建自己的网站！","https:\u002F\u002Fpic3.zhimg.com\u002F80\u002Fv2-df3b38e3012258ed70c23b586309e3f6_1440w.webp","JavaWeb 系列笔记 🚛",{"books":203,"desc":288,"id":81,"image":289,"title":290},[204,220,235,255,273],{"cateId":81,"chapters":205,"desc":216,"id":207,"time":217,"title":218,"video":219},[206,210,213],{"bookId":207,"id":208,"indexOrder":13,"name":209},8,"h7sjo5oy0l03607e","SSM笔记（一）Spring基础",{"bookId":207,"id":211,"indexOrder":13,"name":212},"eve8gq72qmdb46sg","SSM笔记（二）SpringMvc基础",{"bookId":207,"id":214,"indexOrder":13,"name":215},"63v73g0zh1qlr6fk","SSM笔记（三）SpringSecurity基础","Spring的探索之路从这里开始",2023,"JavaSSM 基础部分","[\"https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Kv4y1x7is\u002F\", \"https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Lh4y1M7kx\u002F\", \"https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1fV411M7aS\u002F\"]",{"cateId":81,"chapters":221,"desc":232,"id":223,"time":217,"title":233,"video":234},[222,226,229],{"bookId":223,"id":224,"indexOrder":13,"name":225},16,"0k66v5r6slsfuog4","SpringBoot笔记（一）核心内容",{"bookId":223,"id":227,"indexOrder":13,"name":228},"bqlrnc2yvkaxo8s1","SpringBoot笔记（二）数据交互",{"bookId":223,"id":230,"indexOrder":13,"name":231},"wci9lb9tgea866jt","SpringBoot笔记（三）前后端分离","SpringBoot全新重制版","SpringBoot 新版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1xu4y1m7UP\u002F",{"cateId":81,"chapters":236,"desc":252,"id":238,"time":60,"title":253,"video":254},[237,240,243,246,249],{"bookId":238,"id":239,"indexOrder":13,"name":225},9,"e43gl1ilygps032v",{"bookId":238,"id":241,"indexOrder":13,"name":242},"emnmd8nzfdb3hr50","SpringBoot笔记（二）Git版本控制",{"bookId":238,"id":244,"indexOrder":13,"name":245},"jjlolj5igvttvyhv","SpringBoot笔记（三）Redis数据库",{"bookId":238,"id":247,"indexOrder":13,"name":248},"skgr4ivb5curdoux","SpringBoot笔记（四）其他框架介绍",{"bookId":238,"id":250,"indexOrder":13,"name":251},"le91fqhu4dqui1k4","SpringBoot笔记（五）Linux系统","逐步走向企业级开发","SpringBoot 旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1UL411V7f3\u002F",{"cateId":81,"chapters":256,"desc":270,"id":258,"time":60,"title":271,"video":272},[257,261,264,267],{"bookId":258,"id":259,"indexOrder":13,"name":260},10,"oejzo0l77zeb6a7e","SpringCloud笔记（一）微服务基础",{"bookId":258,"id":262,"indexOrder":13,"name":263},"f6eya9taaelsl35p","SpringCloud笔记（二）微服务进阶",{"bookId":258,"id":265,"indexOrder":13,"name":266},"35v1hbsfcdgagdnw","SpringCloud笔记（三）微服务应用",{"bookId":258,"id":268,"indexOrder":13,"name":269},"a782u84512tyuo1m","SpringCloud笔记（四）消息队列","体验微服务架构带来的魅力","SpringCloud 进阶","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1AL4y1j7RY\u002F",{"cateId":81,"chapters":274,"desc":285,"id":276,"time":142,"title":286,"video":287},[275,278,280,282],{"bookId":276,"id":277,"indexOrder":13,"name":209},11,"efjw75u8a251qxk5",{"bookId":276,"id":279,"indexOrder":13,"name":212},"guc134xb7sl78vju",{"bookId":276,"id":281,"indexOrder":13,"name":215},"u8ekxxucowr2b1tm",{"bookId":276,"id":283,"indexOrder":13,"name":284},"vkpmw9wbej21nei6","SSM笔记（四）MySQL进阶","此教程为2021年旧版教程","JavaSSM 旧版","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1xL4y1H7Tq\u002F","包含Spring全套框架笔记，从开始到Spring Boot，以及众多运维小知识。","https:\u002F\u002Fpic4.zhimg.com\u002F80\u002Fv2-28c3144421220d7c048703281bc34f63_1440w.webp","Spring 系列笔记 🍏",{"books":292,"desc":329,"id":96,"image":330,"title":331},[293,308],{"cateId":96,"chapters":294,"desc":305,"id":296,"time":60,"title":306,"video":307},[295,299,302],{"bookId":296,"id":297,"indexOrder":13,"name":298},12,"jd3e8u5cmvx5gco6","C语言（一）计算机思维导论",{"bookId":296,"id":300,"indexOrder":13,"name":301},"lqv77apvx82nkkio","C语言（二）基础语法",{"bookId":296,"id":303,"indexOrder":13,"name":304},"xb0b9t37gyv96xns","C语言（三）高级特性","包含高等院校需要教授的全部内容","C语言程序设计","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Cr4y137os\u002F",{"cateId":96,"chapters":309,"desc":326,"id":311,"time":60,"title":327,"video":328},[310,314,317,320,323],{"bookId":311,"id":312,"indexOrder":13,"name":313},13,"8a046ps2e4w6k4py","数据结构与算法（一）线性结构篇",{"bookId":311,"id":315,"indexOrder":13,"name":316},"3ma8db91f9zrnkja","数据结构与算法（二）树形结构篇",{"bookId":311,"id":318,"indexOrder":13,"name":319},"0lsjm59k7cgu4tpr","数据结构与算法（三）散列表篇",{"bookId":311,"id":321,"indexOrder":13,"name":322},"0qzy7bogo0g2pusa","数据结构与算法（四）图结构篇",{"bookId":311,"id":324,"indexOrder":13,"name":325},"6gmcxcikcilyxblj","数据结构与算法（五）排序算法篇","虽然很难，但是它是考研必学科目","数据结构与算法","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV13W4y127Ey\u002F","你的内心一直有一个坚定的声音在告诉你，一定要考上一名研究生，向着未来前进吧！","https:\u002F\u002Fpic2.zhimg.com\u002F80\u002Fv2-ac128404efb29ce1c9d1ccc61024f1d1_1440w.webp","C语言 系列笔记 🥬",{"books":333,"desc":367,"id":108,"image":368,"title":369},[334,349,358],{"cateId":108,"chapters":335,"desc":346,"id":337,"time":163,"title":347,"video":348},[336,340,343],{"bookId":337,"id":338,"indexOrder":13,"name":339},17,"urw2e6gg1lprv65w","Kotlin（一）基础语法",{"bookId":337,"id":341,"indexOrder":13,"name":342},"t7lnl87f74f3v1ju","Kotlin（二）类与对象",{"bookId":337,"id":344,"indexOrder":13,"name":345},"v1zzvki0knb1xvml","Kotlin（三）高级特性","包含Kotlin语言完整基础部分","Kotlin程序设计基础","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1P94y1c7tV\u002F",{"cateId":108,"chapters":350,"desc":355,"id":352,"time":163,"title":356,"video":357},[351],{"bookId":352,"id":353,"indexOrder":13,"name":354},18,"ovbzpe7065bye1st","Kotlin扩展（一）","包含Kotlin额外扩展知识","Kotlin扩展篇","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Hg4y1m7Ca\u002F",{"cateId":108,"chapters":359,"desc":364,"id":361,"time":163,"title":365,"video":366},[360],{"bookId":361,"id":362,"indexOrder":13,"name":363},19,"3at7ybv04dmjc0wp","Gradle基础教程","Gradle配置教程（Kotlin）","Gradle教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1Fc411x7xF\u002F","Kotlin让JVM平台焕发新的生机，让语言的表达更加优美","https:\u002F\u002Fpic2.zhimg.com\u002F80\u002Fv2-be815568f7c79c64cdaa171b0409786d_1440w.webp","Kotlin 系列笔记 ☘️",{"books":371,"desc":418,"id":120,"title":419},[372,391,403],{"cateId":120,"chapters":373,"desc":387,"id":375,"time":388,"title":389,"video":390},[374,378,381,384],{"bookId":375,"id":376,"indexOrder":13,"name":377},26,"zjf5qapwqtqiohcn","JavaScript笔记（一）基础语法",{"bookId":375,"id":379,"indexOrder":13,"name":380},"95jc6sjyjwcp9pvp","JavaScript笔记（二）核心知识",{"bookId":375,"id":382,"indexOrder":13,"name":383},"j35cdc1qz8dzq7pn","JavaScript笔记（三）进阶知识",{"bookId":375,"id":385,"indexOrder":13,"name":386},"sdhodlihphnpcg37","JavaScript笔记（四）前端基础","包含JavaScript最新语法规范讲解",2026,"JavaScript教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1xq6gBgESU",{"cateId":120,"chapters":392,"desc":400,"id":394,"time":37,"title":401,"video":402},[393,397],{"bookId":394,"id":395,"indexOrder":13,"name":396},23,"bsisgazdftiz3o9c","HTML5笔记（一）基础内容",{"bookId":394,"id":398,"indexOrder":13,"name":399},"njol93fs34gfwuzf","HTML5笔记（二）高级内容","包含HTML基础内容和相关知识点","HTML5核心教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1BrBiYNEWg",{"cateId":120,"chapters":404,"desc":415,"id":406,"time":37,"title":416,"video":417},[405,409,412],{"bookId":406,"id":407,"indexOrder":13,"name":408},25,"jo74ciirtg8wh90y","CSS笔记（一）基础入门",{"bookId":406,"id":410,"indexOrder":13,"name":411},"ap5ixyomoejuw4ue","CSS笔记（二）盒模型和布局",{"bookId":406,"id":413,"indexOrder":13,"name":414},"4djgk5xy1lzpiuf2","CSS笔记（三）变换和过渡","包含CSS3基础内容和相关知识点","CSS3核心教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1sQeEzFEKi","包含Web前端学习路径全部教程笔记，打下坚实的基础","Web前端 系列笔记",{"books":421,"desc":458,"id":423,"image":368,"title":459},[422,432,450],{"cateId":423,"chapters":424,"desc":429,"id":426,"time":163,"title":430,"video":431},100,[425],{"bookId":426,"id":427,"indexOrder":13,"name":428},20,"o0ab271mkdsas87","Markdown基础语法","编写简洁而又优美的文档","Markdown教程","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1eJ4m157kC",{"cateId":423,"chapters":433,"desc":447,"id":435,"time":60,"title":448,"video":449},[434,438,441,444],{"bookId":435,"id":436,"indexOrder":13,"name":437},14,"6386mh7anqt4tzyv","设计模式（一）面向对象设计原则",{"bookId":435,"id":439,"indexOrder":13,"name":440},"8ftkb38wfn6ox0ug","设计模式（二）创建型",{"bookId":435,"id":442,"indexOrder":13,"name":443},"i1msql1k8y70etey","设计模式（三）结构型",{"bookId":435,"id":445,"indexOrder":13,"name":446},"5434a3cyyjvwhs8s","设计模式（四）行为型","使你的编码水平得到质的飞跃","设计模式系列","https:\u002F\u002Fwww.bilibili.com\u002Fvideo\u002FBV1u3411P7Na\u002F",{"cateId":423,"chapters":451,"desc":456,"id":453,"time":60,"title":457},[452],{"bookId":453,"id":454,"indexOrder":13,"name":455},15,"zj9uvg0sp3b0sok8","Docker 容器技术 笔记","这里包含其他中间件课程笔记","其他中间件笔记","我们对知识的探索从未停止，只有不断地学习，才能走向美好的未来！","其他笔记分类 🌽",200,true,{"data":463,"status":460,"success":461},{"bookId":375,"content":464,"id":376,"indexOrder":8,"introduction":465,"lastUpdate":466,"name":377},"![image-20260123171141130](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F23\u002FL4OIRNwUkY8vhyV.png)\n\n# JavaScript 基础语法\n\n开始本课程之前，建议先完成《HTML5 核心教程》《CSS3 核心教程》这两门前置课程，如果你只是想学习JavaScript的语法做Node.js后端开发，可以跳过前置课程，如果是前端Web学习路线务则必完成前置课程。\n\n欢迎各位小伙伴来到前端路线的JavaScript课程，这一站也是前端三件套（HTML、CSS、JavaScript）中的最后一个，期待在这最后的旅途中与各位小伙伴一起进步！视频中所有的文档、资料，都可以直接在视频下方简介中找到，视频非培训机构出品，纯个人录制，不需要加任何公众号、小程序，直接自取即可。\n\n教程开始之前，提醒各位小伙伴：\n\n- 如果你对某样东西不熟悉，请务必保证跟视频中使用一模一样的环境、一模一样的操作方式去使用，不要自作主张，否则出现某些奇怪的问题又不知道怎么办，就会浪费很多时间。\n- 在学习过程中，尽可能避免出现中文文件夹，包括后面的环境安装、项目创建，都尽量不要放在中文路径下（因为使用中文常常出现奇奇怪怪的问题）建议使用对应的英文单词代替，或者是用拼音都可以，最好只出现英文字母和数字。\n- 本系列教程统一使用 WebStorm 作为集成开发环境，该软件是免费使用的。\n- 请不要抱着应试教育的心态进行学习，学习编程开发是为了掌握技能，而不是单纯为了过考试。\n\n如果觉得本视频对你有帮助，请一键三连支持一下UP主~\n\n## 走进JavaScript\n\n在前面的课程中，我们学习了HTML和CSS，我们已经知道，HTML是控制网页的整体框架结构，也就是用于表示页面上有些什么，怎么排列的，而CSS则是控制页面元素的外观，我们可以通过编写CSS来让网页上的元素变得更漂亮。而JavaScript，就像运行在浏览器上的程序一样，我们可以编写自己的程序逻辑来让网页展现出千变万化的效果。\n\n它可以让我们的网页变成**“动态”**的。注意这里说的动态并非是让网页产生动画效果，而是指网页上的数据可以根据时间或状态动态变化：\n\n- **静态网页**\n  - **内容固定**：网页内容在被编写时就已确定，存储为纯HTML文件（或包含简单CSS\u002FJS），服务器直接返回预先生成的页面。\n  - **无需服务器动态处理**：用户请求时，服务器直接将文件发送给浏览器，内容不会随用户行为、数据变化或时间动态更新。\n  - **示例**：企业官网的“关于我们”页、纯HTML博客文章。\n- **动态网页**\n  - **内容可实时生成**：页面内容由服务器根据用户请求、数据库数据、时间或其他条件动态生成。\n  - **依赖服务器端处理**：服务器会执行代码（如PHP、Python、Node.js等），从数据库或其他数据源获取数据，动态拼接成HTML后返回给浏览器。\n  - **示例**：电商网站商品列表（根据搜索条件实时加载）、在线论坛帖子（用户发布后动态展示）、社交媒体动态更新。\n\n实际上，我们在生活中访问的绝大多数网站都是动态网站，页面上的数据无时无刻都在更新，比如淘宝网上的商品，抖音上的视频，微博上的帖子等等。除此之外，JavaScript还能让我们与页面进行各种交互操作，比如点击卡片弹出消息，按下键盘播放动画等。\n\n这一节，我们就来认识一下JavaScript，看看它到底有什么魔力。\n\n### 计算机编程语言\n\n考虑到各位小伙伴可能还未接触过任何一门编程语言，这里先科普一下什么是编程语言。\n\n计算机虽然名字听着很高级，不过它也是由一个个简单电路组成的：\n\n![image-20230228214034266](https:\u002F\u002Fs2.loli.net\u002F2025\u002F07\u002F07\u002FIym8FTvPsiCLlEr.png)\n\n这是我们在初中就学习过的电路图，不过这种电路太过简单，只能完成一些很基础的的操作，比如点亮小灯泡等。很明显，想要实现计算机这么高级的运算机器，肯定是做不到的，这时我们就需要引入更加强大的数字电路了。\n\n> 用数字信号完成对数字量进行[算术运算](https:\u002F\u002Fbaike.baidu.com\u002Fitem\u002F算术运算\u002F3118202)和[逻辑运算](https:\u002F\u002Fbaike.baidu.com\u002Fitem\u002F逻辑运算\u002F7224729)的电路称为数字电路，或数字系统。由于它具有逻辑运算和逻辑处理功能，所以又称数字逻辑电路。现代的数字电路由半导体工艺制成的若干数字集成器件构造而成，逻辑门是数字逻辑电路的[基本单元](https:\u002F\u002Fbaike.baidu.com\u002Fitem\u002F基本单元\u002F5246264)。\n>\n> 计算机专业一般会在大一开放《数字电路》这门课程，会对计算机底层的数字电路实现原理进行详细介绍。\n\n数字电路中，用电压的高低来区分出两种信号，低电压表示0，高电压表示1，由于只能通过这种方式表示出两种类型的信号，所以计算机采用的是二进制。[二进制](https:\u002F\u002Fbaike.baidu.com\u002Fitem\u002F二进制\u002F361457)是计算技术中广泛采用的一种[数制](https:\u002F\u002Fbaike.baidu.com\u002Fitem\u002F数制\u002F217113)。二进制数据是用0和1两个数码来表示的数。它的[基数](https:\u002F\u002Fbaike.baidu.com\u002Fitem\u002F基数\u002F4260)为2，进位规则是“逢二进一”，借位规则是“借一当二”。\n\n比如我们一般采用的都是十进制表示，比如9再继续加1的话，就需要进位了，变成10，在二进制中，因为只有0和1，所以当1继续加1时，就需要进位了，就变成10了（注意这不是十，读成一零就行了）\n\nCPU是电脑的核心，用于执行我们给到它的各种指令，不过，由于计算机底层只能识别二进制信号，我们发送给CPU的所有指令都必须变成二进制的形式，比如：000001 - 代表开机、000010 - 代表关机、000011 - 代表进行加法运算（随便瞎编的，实际比这个复杂得多）当我们通过电路发送给CPU这样的二进制指令，CPU就能够根据我们的指令执行对应的任务，并通过电路输出执行的结果，同样也是二进制的，这其实就是**机器语言**。\n\n不过随着时代的进步，指令集越来越大，CPU支持的运算类型也越来越多，这样的纯二进制编写指令运行程序实在是太累了。因此，一些编程语言就出现了，从最初的汇编（低级语言）到现在的C、C++、Go、Rust、Java、Python、Swift等等，都是**编程语言**，这些语言可以采用一种更加接近我们人类自然语言的语法进行编写，并将我们编写的代码自动转换为机器可以识别的二进制指令，从而让我们可以以更简单的方式操作计算机进行运算。\n\n截止2025年8月当下比较热门的编程语言排名：\n\n![image-20260123181416085](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F23\u002FEqurpnFCIRlLGsV.png)\n\n不过，这里需要区分一下什么是编译型语言什么是解释型语言：\n\n* **编译型语言：** 编译器**在代码运行前把**整个程序一次性翻译成机器码（例如 `main.c` 被编译成 `main.exe`) 直接供硬件执行。\n* **解释型语言：** 依赖**解释器**逐行读取代码，每读一条就翻译成机器指令，直接给硬件执行（类似“同声传译”）更加灵活，无需编译就能直接运行。\n\n而我们要学习的JavaScript就是**解释型语言**，由浏览器（内部的JavaScript引擎）对代码进行解释执行，从而让CPU进行各种运算。\n\n> 现代 JavaScript 执行环境（如 V8 引擎）中也融入了一些编译优化机制，会自动编译一些代码为机器码，使得其执行过程更接近“即时编译（JIT）”，但这并不改变其核心的“解释型”本质。\n\n### JavaScript的由来\n\nJavaScript（简称JS）是一种广泛使用的脚本语言，主要用于Web开发，同时也在其他领域（如后端、移动应用、数据库等）获得了广泛应用。它的发展历程充满了传奇色彩，从最初的简单脚本发展到如今强大的全栈语言。\n\n> 20世纪90年代初，互联网刚刚兴起，网页主要是静态的，缺乏交互性。网景通讯公司（Netscape）的工程师 **布兰登·艾克（Brendan Eich）** 被要求开发一种能让网页**“活”**起来的**脚本语言**，用于与服务器端交互和处理用户操作。1995年，为了借助当时热门的Java语言做营销，这门新的脚本语言被命名为“JavaScript”（但语法与Java几乎无关）\n>\n> 1995年，布兰登·艾克在短短10天内设计并实现了最初的JavaScript引擎，命名为 **LiveScript**（后改名为JavaScript），随Netscape 2.0发布，提供了基础的语法和浏览器DOM操作（如`alert()`、变量、函数等），微软随后推出了IE3.0，内置了名为 **JScript** 的JavaScript克隆版本，网景则与Sun Microsystems合作推动标准化。这也导致了早期语言特性的分化。t\n>\n> 1997年，ECMA国际（ECMA-262）发布了ECMAScript规范，确立了JavaScript的基础语法，奠定了跨浏览器兼容性的基础。\n>\n> 1998年，W3C推出了第一个DOM标准（Document Object Model），JS开始能更便捷地操作网页结构和样式（如动态创建元素、绑定元素事件）\n>\n> 2005年，Google的Gmail和AdSense大规模应用了AJAX技术（Asynchronous JavaScript and XML），JavaScript通过`XMLHttpRequest`实现异步数据交互，彻底改变了Web体验。此时，JS从“网页点缀”升级为网页核心交互语言。\n>\n> 2006年，JQuery框架横空出世，以“写得更少，做得更多”著称，简化了DOM操作、事件绑定和动画，成为当时全球最流行的JS库。\n>\n> 2013年，React框架推出，引入虚拟DOM和组件化思想，推动了前端开发模式的革新，让复杂交互应用（如Facebook、Instagram）成为可能。\n>\n> 2014年，更加轻量级、入门友好，快速上手的Vue框架推出，在国内开发者中备受欢迎。\n>\n> 2015年，ECMAScript 2015（ES6）引入了箭头函数、`let\u002Fconst`、模块系统（`import\u002Fexport`）、Promise、类（Class）等，语言能力大幅提升。后续ES7至今持续迭代，增加更多新特性，如`Optional Chaining`（`?.`）、`Async\u002FAwait`等，使代码更简洁强大。\n\nECMAScript是规则，JavaScript是在该规则下运行的具体实现（如V8引擎为JavaScript提供执行环境），两者经常被混淆但本质不同，就像\"HTML规范\"和\"网页渲染实现\"的关系。在日常开发中，通常默认将\"JavaScript\"理解为ECMAScript规范支持的全栈语言。\n\n### 在页面中引入JavaScript\n\n前面我们介绍了什么是编程语言，什么是JavaScript，这一节我们来看看如何在页面中引入JavaScript脚本。\n\n和CSS一样，如果想要在HTML中引入JavaScript有多种的方式：\n\n* **内部JavaScript标签：**编写在HTML内的JavaScript代码，和HTML代码一起存在。\n* **外部JavaScript文件：**独立编写的外部JS文件，可以单独引入。\n* **元素事件JavaScript标签：**直接在HTML标签的事件属性中嵌入JavaScript代码。\n\n这与CSS的三种引入方式非常类似，我们来看看如何操作。首先是最简单的一种内部JavaScript标签，我们可以将其写到HTML文档的任何位置，只要此区域被浏览器加载就会自动执行其中的JavaScript代码。我们可以先试试看写到`head`标签内，这样\n\n```html\n\u003Chead>\n  \u003Cmeta charset=\"UTF-8\">\n  \u003Ctitle>Title\u003C\u002Ftitle>\n  \u003Cscript>\n    alert('Hello, World!')\n  \u003C\u002Fscript>\n\u003C\u002Fhead>\n```\n\n这里使用的`alert`是一个JS函数，可以实现页面通知效果，当我们在浏览器打开页面之后会立即收到一个通知。\n\n此外，我们也可以将其放到`body`的任何位置：\n\n```html\n\u003Cbody>\n  \u003Cdiv>我是页面上的一段普通内容\u003C\u002Fdiv>\n  \u003Cscript>\n      alert('Hello, World!')\n  \u003C\u002Fscript>\n\u003C\u002Fbody>\n```\n\n无论放到哪个位置，JS代码都会自动执行，不过，不同的位置会存在一些加载顺序上的区别，我们会在后续DOM章节中为大家详细介绍在不同位置产生的效果。\n\n除了直接在HTML中编写JS代码，我们也可以将其放到一个独立的文件中再单独引入，存放JavaScript代码的文件以`.js`结尾，我们可以直接在里面写入测试的代码：\n\n```js\nalert('Hello, World!');\n```\n\n然后通过`script`标签引入：\n\n```html\n\u003Chead>\n  \u003Cmeta charset=\"UTF-8\">\n  \u003Ctitle>Title\u003C\u002Ftitle>\n  \u003Cscript src=\"js\u002Fscript.js\">\u003C\u002Fscript>\n\u003C\u002Fhead>\n```\n\n和之前一样，无论是直接编写的内部JS代码还是外部引入的JS文件，都可以插入到页面的任意位置上，只要页面加载就会自动执行。\n\n第三种方式比较特殊，HTML存在多种**元素事件**，比如当我们点击某个`div`标签时，就会触发**点击事件**，当我们滚动窗口时，就会触发页面的**滚动事件**，当触发这些事件时，我们可以在HTML标签中增加对于这些事件的处理逻辑，而具体的操作就需要使用JavaScript来进行编写，这里以`onclick`为例：\n\n```html\n\u003Cbody>\n  \u003Cdiv onclick=\"alert('我是页面上的一段普通内容')\">我是页面上的一段普通内容\u003C\u002Fdiv>\n\u003C\u002Fbody>\n```\n\n我们可以尝试点击这个盒子，触发点击事件后也会执行JS代码。有关HTML元素事件，我们会在后续的DOM章节中为大家详细介绍。\n\n介绍完三种JS代码引入方式之后，我们需要额外介绍一个标签，叫做`noscript`，这个标签表示的是在浏览器未开启或不支持JavaScript功能时需要展示的内容，一些比较老旧或是软件内嵌的的浏览器可能未开启JS功能，此时这个标签就非常有用了：\n\n```html\n\u003Cbody>\n  \u003Cdiv>我是页面上的一段普通内容\u003C\u002Fdiv>\n  \u003Cnoscript>请启用JavaScript以获得最佳体验\u003C\u002Fnoscript>\n\u003C\u002Fbody>\n```\n\n我们可以通过手动关闭JS功能来模拟一下，打开开发者工具的设置按钮：\n\n![image-20260123235319009](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F23\u002Ftb1BjyZeOnoM3qX.png)\n\n接着往下滑找到停用JavaScript选项：\n\n![image-20260123235406537](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F23\u002Fe5ot6l7PASNM4yR.png)\n\n接着刷新页面之后，我们就会发现原本应该自动执行的JS脚本不会自动执行，并且页面上出现了我们刚刚设置的`noscript`标签中的内容：\n\n![image-20260123235455334](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F23\u002F7ADVjQW3FNYzTur.png)\n\n### 在浏览器中直接测试\n\n有些时候，我们可能并不想为了测试一两行代码而专门去创建一个 HTML 文件。这时候，利用浏览器自带的 **开发者工具**（Developer Tools）进行直接测试是最方便的选择。\n\n大多数现代浏览器（如 Chrome, Edge, Firefox, WebStorm 内置浏览器）都提供了强大的控制台功能：\n\n1. **打开方式**：在浏览器页面中按下 `F12` 键，或者右键点击页面选择“检查”，然后切换到 **Console（控制台）**选项卡。\n2. **交互式执行**：在控制台的光标处，你可以直接输入 JavaScript 代码并按 `Enter` 键运行。例如，输入 `console.log(\"Hello JS\")` 或 `1 + 1`，它会立即返回结果。\n3. **多行输入**：如果你想编写一段较长的逻辑，可以按 `Shift + Enter` 进行换行，而不是直接执行。\n\n![image-20260123235857398](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F23\u002FMQ8xIEu9nyJkevj.png)\n\n我们可以直接在控制台输入一段JS代码，然后敲击回车就可以执行了：\n\n![image-20260124000000274](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F24\u002FpwsKSylRQArVJZT.png)\n\n这在需要临时测试一些JS代码的情况下非常好用，后续我们会尝试使用这种方式来直接调试页面中已经存在的JS代码。\n\n> 学会在Chrome中控制台调试JS代码，这是你即将成为一名合格前端开发的第一步，如果你运气好的话，说不定还能获得一枚 Google Developer Program的\"Chrome DevTools User\"徽章！\n\n除了在浏览器中直接测试外，我们也可以在WebStorm中直接运行，但是注意需要安装NodeJS\u002FDeno\u002FBun其中一种环境。\n\n至此，有关JavaScript的基础介绍就结束了，接下来我们就开始正式学习一下JavaScript的语法。\n\n## JavaScript入门\n\n这一节我们将从最简单的变量声明开始，逐步介绍JavaScript的语法。\n\n### 注释\n\n和前面HTML、CSS一样，我们可以在JS中编写注释内容，来为某段代码进行解释，以便于我们后续能够快速回顾这段代码的目的。在JS中创建注释非常简单，首先是最基础的单行注释：\n\n```js\n\u002F\u002F 这段代码是用于发出通知，并展示Hello World\nalert('Hello, World!')\n```\n\n单行注释使用`\u002F\u002F`进行表示，后续只要不出现换行的文本都属于注释内容，不会参与到实际的程序执行中。\n\n此外，还有一种方式和CSS是一样的，我们可以使用：\n\n```js\n\u002F* 这段代码是用于发出通知，并展示Hello World *\u002F\nalert('Hello, World!')\n```\n\n这种写法在JS中叫做多行注释，我们可以编写很多行文本内容作为注释：\n\n```js\n\u002F* \n这段代码是用于发出通知\n并展示Hello World\n*\u002F\nalert('Hello, World!')\n```\n\n还有一种看起来更加规范一点的多行注释：\n\n```js\n\u002F**\n * 这段代码是用于发出通知\n * 并展示Hello World\n *\u002F\nalert('Hello, World!')\n```\n\n不过这种注释更多用于后续函数声明的解释中，我们会在后续内容中继续介绍。\n\n### 变量与常量\n\n我们的程序不可能永远都只进行上面那样的简单通知操作，有些时候可能需要计算某些数据，此时我们就需要用到变量了。\n\n那么，什么是变量呢？我们在数学中其实已经学习过变量了：\n\n> 变量，指值可以变的量。变量以非[数字](https:\u002F\u002Fbaike.baidu.com\u002Fitem\u002F数字\u002F6204?fromModule=lemma_inlink)的符号来表达，一般用拉丁字母。变量的用处在于能一般化描述指令的方式。结果只能使用真实的值，指令只能应用于某些情况下。变量能够作为某特定种类的值中任何一个的保留器。\n\n比如一个公式 $ x^2+6=16+6=22 $ 此时`x`就是一个变量，变量往往代表着某个值，比如这里的`x`就代表的是4这个值。在JavaScript中，我们也可以让变量去代表一个具体的值，并且变量的值是可以发生变化的。\n\n要声明一个变量，我们需要使用以下格式：\n\n```js\nvar [变量名称]\n```\n\n这里的变量名称不能随便起，需要遵循以下规则：\n\n1. 变量名可以由字母、数字、下划线或美元符`$`组成，但是不能以数字开头（虽然直接写中文也可以，但是不推荐）\n2. 变量名不能为系统关键字或保留字，比如上面的`var`就是一个关键字，有关JS的更多关键字，我们会在后续学习中逐步认识。\n3. 应该使用有意义的名称，达到见名知意的目的（我们一般采用英文单词）最好以小写字母开头。\n\n当然各位小伙伴没必要刻意去进行记忆，我们会在学习的过程中逐步认识到这些关键字。新手要辨别一个单词是否为关键字，只需要通过WebStorm的高亮颜色进行区分即可，比如：\n\n![image-20260124002950027](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F24\u002FAS47ehLFvbW5ZJN.png)\n\n深色模式下，关键字会高亮为橙色，浅色模式下会高亮为深蓝色，普通的代码都是正常的灰白色。这里我们写了一个变量`a`，但是这个变量一开始没有任何值，比如现在我们要让这个变量表示10，那么就可以将10赋值给这个变量：\n\n```js\nvar a = 10\n```\n\n只需要使用等号就可以为变量设定一个初始值，也可以叫做**赋值**操作，此时，变量`a`就代表10这个数字了。\n\n我们可以使用`console.log`来打印某个变量的值或函数执行的结果，它是 JavaScript 中最常用的**调试工具**，本质是一个函数调用（有关函数的创建和使用我们会在下一章详细介绍）用于在**浏览器控制台**或**Node.js 终端**中输出信息，帮助开发者查看程序运行过程中的变量值、状态变化、错误提示等：\n\n```js\n\u002F* 注意存在多行代码时，程序会从上往下执行，注意每一句代码一定要换行 *\u002F\nvar a = 10\nconsole.log(a)\n```\n\n![image-20260124003331884](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F24\u002FtrXgoP5MsaInvbJ.png)\n\n可以看到，在控制台中成功输出了变量`a`的值，并且在控制台的右边会显示这一次输出是哪里进行的。这里的`script.js:2`表示是这个JS文件执行到第二行的代码打印的结果。\n\n除了在创建变量时给于初始值，我们也可以在后续的代码中设置变量的值：\n\n```js\nvar a\na = 20  \u002F\u002F通过等号为变量a赋值为20\nconsole.log(a)\n```\n\n注意如果存在多次赋值操作，那么最后一次的执行结果才是`a`的最终值：\n\n```js\nvar a\na = 20\na = 30  \u002F\u002F这里再次进行了一次赋值操作，使得a变成了30\nconsole.log(a)\n```\n\n除了简单的赋值，我们还可以使用数学中的加减乘除运算来实现更多效果，比如：\n\n```js\nvar a = 1 + 2  \u002F\u002F让a获得1+2的值\nconsole.log(a)  \u002F\u002F得到3\n```\n\n```js\nvar a = 10   \u002F\u002F让a初始等于10\na = a + 8  \u002F\u002F让a等于当前a的值加上8\nconsole.log(a)  \u002F\u002F得到18\n```\n\n```js\nvar a = 10   \u002F\u002F让a初始等于10\na = a - 5  \u002F\u002F让a等于当前a的值减去5\nconsole.log(a)  \u002F\u002F得到5\n```\n\n```js\nvar a = 10   \u002F\u002F让a初始等于10\na = a * a  \u002F\u002F让a等于当前a的值乘以a\nconsole.log(a)  \u002F\u002F得到100\n```\n\n```js\nvar a = 10   \u002F\u002F让a初始等于10\na = a \u002F 2  \u002F\u002F让a等于当前a的值除以2\nconsole.log(a)  \u002F\u002F得到5\n```\n\n我们还可以一次性创建多个变量，可以逐行编写或是用逗号分割连续编写：\n\n```js\nvar a = 10, b = 9  \u002F\u002F使用逗号分割可以在一行写多个变量\nconsole.log(a)\n```\n\n多个变量之间也是可以互相进行运算的：\n\n```js\nvar a = 10, b = 9\na = a + b + 10  \u002F\u002F让a等于a当前值加上b的值再加上10\nconsole.log(a)  \u002F\u002F得到29\n```\n\n```js\nvar a = 10, b = 9\nconsole.log(a - b)  \u002F\u002F直接打印a - b的值\n```\n\n有关更多运算相关的知识点，我们会在后续章节中专门介绍，这里仅做了解即可。\n\n在了解完变量之后，相信各位小伙伴一个对于编程有了一个新的认识，有了变量我们就可以做更多好玩的事情了。只不过这里需要补充一个非常重要的知识点，不知道各位小伙伴是否有注意到WebStorm中的黄标：\n\n![image-20260124011244965](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F24\u002FZgqIar2h8WRVbG6.png)\n\n为什么WebStorm不推荐我们使用呢？这是因为`var` 关键字在声明变量时存在一些历史遗留问题和容易引发错误的特性，因此 ES6 及后续标准更推荐使用 `let` 和 `const` 来替代 `var`，以下是不推荐使用 `var` 的主要原因：\n\n1. `var` 声明的变量存在**变量提升**（Hoisting）特性，即变量声明会被提升到其作用域的顶部，但赋值不会提升。\n2. `var` 声明的变量属于其所在的**函数作用域**（或全局作用域），而非块级作用域。\n3. 在同一作用域内使用 `var` 重复声明变量时，不会报错，而是直接覆盖已有变量的值，这会导致逻辑错误。\n4. `var`无法定义一个不可修改的变量，也就是我们下面马上要提到的常量。\n\n因此，我们更推荐大家使用`let`关键字来声明变量，它很好地解决了`var`导致的一些问题，后续课程中我们也会使用`let`进行讲解，不再使用`var`关键字声明变量。变量使用`let`关键字来声明，效果和之前是一样的：\n\n```js\nlet a = 10, b = 9\nconsole.log(a - b)\n```\n\n介绍完变量，我们接着来介绍一下常量，有时候我们希望变量的值一直保持不变，我们就可以将其指定为常量，这里我们介绍一下`const`关键字，它可以用于声明一个常量：\n\n```js\nconst a = 10\nconsole.log(a)\n```\n\n看上去和之前讲解的变量没有任何区别，我们可以尝试去修改它的值：\n\n```js\nconst a = 10\na = 20   \u002F\u002F会发生报错\nconsole.log(a)  \u002F\u002F这里不会执行\n```\n\n![image-20260124011607651](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F24\u002FT1ZQ4PsFGMEVNHu.png)\n\n此时浏览器控制台出现了一段红色的错误信息：`Assignment to constant variable.`，表示我们在为一个常量进行赋值操作，这是不允许的，所以浏览器直接出现了一段报错，并且出现报错后，由于程序发生问题，后续的JS代码也没有继续执行了。\n\n所以，和变量不同的是，常量必须存在默认赋值，并且值一旦确定，后续就不可进行修改，因此，我们一般将那些声明后不会再发生改变的变量设置为常量，后续可能会出现变化的设置为变量。\n\n虽然常量不可变化，但是我们也可以将其加入到运算中：\n\n```js\nconst a = 20\nlet b = 5, c = 10\nc = b + a - 2\nconsole.log(c)\n```\n\n这里还需要提及一下，我们也可以直接在控制台中访问已经加载执行的JS代码，包括其中声明的变量或函数：\n\n![image-20260124012256698](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F24\u002FiP6Cb3EosU8cGtd.png)\n\n> 可能会有小伙伴注意到，这里的VM653:1其实是一个虚拟环境，**VM 代表“Virtual Machine”**，Chrome 会为**动态注入的脚本、扩展程序、或 eval 执行的代码**创建一个独立的“沙箱环境”或“虚拟上下文”，这些代码会被临时分配一个唯一的 `VM` 编号，比如我们这里手动在控制台执行的JS代码就是动态插入的调试代码，不像上面的结果那样显示具体的JS文件或HTML文件代码位置。\n\n最后要注意的是，无论是变量还是常量，一旦创建之后是不允许重复创建的，否则浏览器会出现标识符已声明错误：\n\n![image-20260124141103424](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F24\u002FMq8R5AT6SkEUdCb.png)\n\n而`var`声明的变量则不受限制，但这很容易导致一些逻辑上的错误，因此不建议使用。\n\n### 计算机中的二进制（选学）\n\n在计算机中，所有的内容都是二进制形式表示。十进制是以10为进位，如9+1=10；二进制则是满2进位（因为我们的计算机是电子的，电平信号只有高位和低位，你也可以暂且理解为通电和不通电，高电平代表1，低电平代表0，由于只有0和1，因此只能使用2进制表示我们的数字！）比如1+1=10=2^1+0，一个位也叫一个bit，8个bit称为1字节，16个bit称为一个字，32个bit称为一个双字，64个bit称为一个四字，我们一般采用字节来描述数据大小。\n\n111 = 7\n\n注意这里的bit跟我们生活中的网速MB\u002Fs是不一样的，小b代表的是bit，大B代表的是Byte字节（8bit = 1Byte字节），所以说我们办理宽带的时候，100Mbps这里的b是小写的，所以说实际的网速就是100\u002F8 = 12.5 MB\u002Fs了。\n\n> 十进制的7 -> 在二进制中为 111 = 2^2 + 2^1 + 2^0\n\n现在有4个bit位，最大能够表示多大的数字呢？\n\n- 最小：0000 => 0\n- 最大：1111 => 23+22+21+20 => 8 + 4 + 2 + 1 = 15\n\n在Java中，无论是小数还是整数，他们都要带有符号（和C语言不同，C语言有无符号数）所以，首位就作为我们的符号位，还是以4个bit为例，首位现在作为符号位（1代表负数，0代表正数）：\n\n- 最小：1111 => -(22+21+2^0) => -7\n- 最大：0111 => +(22+21+2^0) => +7 => 7\n\n现在，我们4bit能够表示的范围变为了-7~+7，这样的表示方式称为**原码**。虽然原码表示简单，但是原码在做加减法的时候，很麻烦！以4bit位为例：\n\n> 1+(-1) = 0001 + 1001 = 怎么让计算机去计算？（虽然我们知道该去怎么算，但是计算机不知道！）\n\n我们得创造一种更好的表示方式！于是我们引入了**反码**：\n\n- 正数的反码是其原码本身\n- 负数的反码是在其原码的基础上, 符号位不变，其余各个位取反\n\n经过上面的定义，我们再来进行加减法：\n\n> 1+(-1) = 0001 + 1110 = 1111 => -0 （直接相加，这样就简单多了！）\n\n思考：1111代表-0，0000代表+0，在我们实数的范围内，0有正负之分吗？0既不是正数也不是负数，那么显然这样的表示依然不够合理！根据上面的问题，我们引入了最终的解决方案，那就是**补码**，定义如下：\n\n- 正数的补码就是其本身 （不变！）\n- 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1（即在反码的基础上+1，此时1000表示-8）\n- 对补码再求一次补码就可得该补码对应的原码。\n\n比如-7原码为1111，反码为1000，补码就是1001了，-6原码为1110，反码为1001，补码就是1010。所以在补码下，原本的1000就作为新增的最小值-8存在。\n\n所以现在就已经能够想通，-0已经被消除了！我们再来看上面的运算：\n\n> 1+(-1) = 0001 + 1111 = (1)0000 => +0 （现在无论你怎么算，也不会有-0了！）\n\n所以现在，1111代表的不再是-0，而是-1，相应的，由于消除-0，负数多出来一个可以表示的数（1000拿去表示-8了），那么此时4bit位能够表示的范围是：-8~+7（Java使用的就是补码！）在了解了计算机底层的数据表示形式之后，我们再来学习这些基本数据类型就会很轻松了。\n\n### 基本数据类型\n\n前面我们为大家介绍了变量和常量，这一节我们接着来看看基本数据类型，除了我们前面认识的数字，在JS中还存在多种多样的数据类型，比如字符串、布尔、空值等共7种基本数据类型，以及我们会在下一章介绍的对象类型。我们先来介绍一下之前使用过的数字类型：\n\n```js\nconst a = 5\nconst b = -7  \u002F\u002F负数直接在前面添加负号即可\nconsole.log(a)\n```\n\n这与我们在数学中的用法非常接近，当然，数字类型（Number）除了整数外，我们还可以使用小数：\n\n```js\nconst a = 5.5\nconsole.log(a)\n```\n\n有些时候，我们可能很难分清楚一个很大的数字到底是多少，所有很多人都喜欢吧大数字用逗号来分割，比如10亿这个数字，就是：`1,000,000,000`，这样看着就非常的直观，在JS中，我们也可以使用类似的表示方式：\n\n```js\nlet a = 1_000_000_000\n```\n\n除了一个明确的数字之外，数字类型还存在两个比较特殊的值：\n\n- `Infinity`：表示正无穷大（如 `1 \u002F 0` 的结果）同理，`-Infinity`表示负无穷大（如 `-1 \u002F 0` 的结果）\n- `NaN`：表示“不是数字”（Not a Number），但类型仍为 `Number`\n\n比如，计算1除以0的时候，就会得到无穷大：\n\n```js\nlet a = 1   \u002F\u002F让a初始等于1\na = a \u002F 0  \u002F\u002F让a等于当前a的值除以0\nconsole.log(a)  \u002F\u002F得到Infinity\n```\n\n> JavaScript 中的所有数字（Number 类型）本质上都是 **64位双精度浮点数**，这种格式严格遵循 **IEEE 754 浮点数算术标准**。\n>\n> 在该标准中，浮点数并不是简单的整数，它必须能够处理科学计算中的极端情况。为了保证计算的连续性，标准规定：\n>\n> - **正无穷大 (+∞)**：由一个特定的位模式表示（阶码全为1，尾数全为0）在JS中为`Infinity`\n> - **计算逻辑**：当一个正实数除以一个趋近于 0 的正数时，其结果趋向于无穷大，负数同理\n>\n> JavaScript 的这种处理方式更接近数学中的**极限（Limit）**概念，而不是代数中的未定义。\n> $$\n> \\lim_{x \\to 0^+} \\frac{1}{x} = \\infty\n> $$\n> 如果程序因为除以零就直接崩溃，在处理复杂的图形渲染或物理引擎计算时，一个小小的误差可能导致整个系统瘫痪。通过返回 `Infinity`，程序可以继续运行，开发者也可以通过 `isFinite()` 函数来检查结果是否有效。\n\n此外，它的运算规则也非常直观：\n\n* **Infinity + 正负数 或 0** = `Infinity`\n* **Infinity + Infinity** = `Infinity`\n* **Infinity - Infinity** = `NaN` （当两个相反方向的无穷大相遇时，结果是未定义的，因为我们无法确定哪一个“更大”）\n\n除了`Infinity`，还有一个比较特殊的值`NaN`（Not a Number）表示一个**在数学上无意义或无法表示**的操作结果，比如计算0除以0时，由于在算数上没有定义明确结果：\n\n```js\nlet a = 0 \u002F 0\nconsole.log(a)  \u002F\u002F得到NaN，因为数学上根本不存在0除以0的结果\n```\n\n```js\nlet a = Infinity \u002F Infinity\nconsole.log(a)   \u002F\u002F无穷除以无穷的结果也是NaN，因为不知道哪个更大\n```\n\n包括后续我们学习的其他类型和数字类型进行加减乘除法运算，也有可能得到一个`NaN`结果，同时，`NaN`与任何数字（包括Infinity在内）运算都是`NaN`。\n\n---\n\n我们接着来认识第二个基本数据类型，字符串（String）用于表示文本数据，由零个或多个字符组成，字符可以是字母、数字、符号或空格，比如我们一开始弹出的`Hello World`就是一个字符串：\n\n```js\nconsole.log(\"Hello World\")\n```\n\n字符串需要使用单引号或是双引号来进行囊括：\n\n```js\nconst text = \"你干嘛\"\nconst end = '哎哟'\nconsole.log(text, end)  \u002F\u002F想要一次性打印多个值，可以逗号分割（这其实是传入多个参数到函数中）\n```\n\n字符串可以不包含任何内容，相当于一个空的字符串：\n\n```js\nconst text = \"\"\n```\n\n这里需要注意的是，字符串中如果包含数字，此时这个数字是字符的数字，而不是前面讲解的数字类型的数字：\n\n```js\nconst text = \"255\"   \u002F\u002F字符串中的数字是字符\nconsole.log(text)\n```\n\n一个是“文本”，一个是“数值”，虽然它们看起来很像（比如 `'255'` 和 255）但在内存处理、运算逻辑和比较规则上完全不同。\n\n除了这些可以直接通过键盘打出来的字符外，还有一些比较特殊的字符，比如换行，无论使用单引号还是双引号，我们都不能直接在字符串里面换行：\n\n![image-20260124143209155](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F24\u002F8EmQxsNHl57vOgP.png)\n\n此时代码出现了报错，因为如果将其写为两行，JS会认为这是两句不同的代码，从而解析出现错误。正确的方法是使用转义字符，它可以代表一些键盘上无法打出来或是存在歧义的字符，转义字符一般以`\\`开头，比如换行我们可以使用`\\n`来表示：\n\n```js\nconst text = \"你干嘛\\n哎哟\"\nconsole.log(text)\n```\n\n![image-20260124143343382](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F24\u002FwO3C59ITZVN1yfo.png)\n\n再比如双引号，由于我们使用了双引号囊括字符串，它表示字符串的开始和结束，如果我们使用双引号这个字符，那么就会出现歧义，JS并不理解这里的双引号到底是字符串的结束位置还是单纯需要这个字符，所以也需要进行转义：\n\n```js\nconst text = \"今天来了\\\"9\\\"个大聪明\"\nconsole.log(text)\n```\n\n![image-20260124143548484](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F24\u002F3B2K4yceXhFlUvp.png)\n\n一些常用的转义字符：\n\n| **转义序列** | **含义**        | **实际效果**                               |\n| ------------ | --------------- | ------------------------------------------ |\n| **`\\'`**     | 单引号          | 在单引号字符串中安全使用 `'`               |\n| **`\\\"`**     | 双引号          | 在双引号字符串中安全使用 `\"`               |\n| **`\\\\`**     | 反斜杠          | 打印出一个普通的 `\\`                       |\n| **`\\n`**     | 换行 (New Line) | 文本换到下一行                             |\n| **`\\t`**     | 制表符 (Tab)    | 相当于按了一次 Tab 键（缩进）              |\n| **`\\uXXXX`** | Unicode 字符    | 通过编码显示特殊符号（如 `\\u4f60` 是“你”） |\n\n除了创建一个字符串之外，我们还可以使用加号将两个字符串连接起来：\n\n```js\nlet text = \"你干嘛\"\nconst end = '哎哟'\ntext = text + end  \u002F\u002F让text等于text加上end两个字符串拼接的结果\nconsole.log(text)\n```\n\n是不是感觉很方便，就像我们前面做数字加减法运算一样，字符串也可以通过运算符快速进行拼接。除了拼接字符串之外，我们还可以将字符串和其他的数据类型进行拼接，比如前面的数字类型：\n\n```js\nconst data = 15\nlet text = \"哥们在这跟你说唱\"\ntext = data + text   \u002F\u002F最终数字会以字符形式拼接到字符串中\nconsole.log(text)\n```\n\n任何的数据类型（包括后面的）只要与字符串相加，都会变成字符串拼接的形式，这对于一些需要快速生成拼接字符串的场景非常实用：\n\n```js\nconst count = 8\nconst text = \"今天一共有 \" + count + \" 名用户完成了签到\"\nconsole.log(text)\n```\n\n当然，除了使用加号可以快速拼接字符串之外，在ES6引入的一个非常强大的特性：**模板字符串（Template Literals）**，我们不仅可以使用单引号和双引号囊括字符串，还可以使用反引号来囊括：\n\n```js\nconst text = `我是一个字符串`\n```\n\n不过，在反引号中，我们可以使用`${expression}` 语法来直接向字符串中直接插入一个**表达式**，比如我们还是想实现上面的效果：\n\n```js\nconst count = 8\nconst text = `今天一共有 ${count} 名用户完成了签到`\nconsole.log(text)\n```\n\n这里的表达式可以是一个变量，也可以是一个数学运算，甚至还可以是一个函数调用。\n\n同样的，想要拼接两个字符串也可以使用这种方式来完成：\n\n```js\nconst start = \"你干嘛\"\nconst end = '哎哟'\nconsole.log(`${start}${end}`)  \u002F\u002F不过还是感觉用+更优雅呢？\n```\n\n除了便于我们组装字符串，模版字符串还可以实现自由换行效果，前面如果我们想要实现换行效果需要添加`\\n`转译字符，而现在我们想换就换，只要还为超出反引号的范围，所有的换行都会变成实际的换行：\n\n```js\nconst text = `\n手机就买苹果\n电车就选特斯拉\n超市就逛山姆\n`\nconsole.log(text)\n```\n\n同样的，由于囊括字符串的是反引号，所以单引号和双引号在模版字符串中可以随便使用，不会出现歧义，无需进行转译。\n\n---\n\n我们接着来介绍第三种基本数据类型，**Boolean（布尔值）**它只有两个可用值：`true`（真）或 `false`（假），用于表示逻辑判断的结果，在后续的流程控制章节使用颇多。\n\n```js\nconst isNumber = false\nconsole.log(isNumber)\n```\n\n进行一些逻辑判断会得到一个布尔类型的结果：\n\n```js\nconst a = 20, b = 30\nconsole.log(a > b)  \u002F\u002F判断a大于b的结果，为假，因为a实际上小于b\n```\n\n有关逻辑判断我们会在运算符部分详细介绍。\n\n---\n\n我们接着来介绍一下第四种比较特殊的值， **Undefined（未定义）**顾名思义，它表示变量已声明但未赋值，或访问对象不存在的属性。\n\n```js\nlet a\nconsole.log(a)\n```\n\n![image-20260124150358209](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F24\u002F4qlJZP851Q2whDx.png)\n\n我们也可以主动将某个变量的值设置为`undefined`：\n\n```js\nlet a = 10\na = undefined\nconsole.log(a)\n```\n\n虽然`undefined`看起来好像没什么用，但是在实际开发中见得最多的也是它，因为很多时候变量并不一定能够在需要的时候得到一个正确的初始值。\n\n至此，有关基本数据类型就暂时介绍到这里，剩余的Symbol、Null、BigInt还有对象类型，我们会在下一章继续介绍。\n\n## 运算符\n\n前面我们为大家介绍了如何在JavaScript中创建变量以及多种基本数据类型，同时还体验了简单的数学运算操作，从这一节课开始我们就来学习一些常见的运算符。\n\n### 算术运算符\n\n前面我们体验了大量的算数运算符来进行一些数学运算，比如最基础的加减乘除：\n\n```js\nconsole.log(1 + 2)\nconsole.log(2 - 3)\nconsole.log(2 * 10)\nconsole.log(3 \u002F 2)\n```\n\n其中加法和减法与我们数学中的符号是一样的，我们可以直接从键盘上输入加号和减号代表执行加减法。而乘除法的符号稍微有些变化，其中乘法由于键盘不能直接打出乘号这个字符，所以使用了`*`星号代替，而除法则使用`\u002F`斜杠代替。\n\n这里的加减乘除其实就是**运算符**，而参与运算的变量、常量或字面量，我们称其为**操作数**，在JS中，我们会认识各种各样的运算符，这些运算符有需要两个操作数的，也有需要一个操作数的，不同的运算符有着不同的作用。\n\n这里需要注意的是，和数学一样，运算符的计算顺序是由优先级的，乘除法的优先级更高，其次才是加减法，同一优先级的运算按照从左往右的顺序进行打印：\n\n```js\nconst a = 10 + 5 \u002F 2 + 1\nconsole.log(a) \n```\n\n这里由于除法运算先进行 ，所以得到的结果应该是`13.5`。\n\n不过，上面的加减法运算符除了能让数字相加相减之外，还可以直接用来表示正数或是负数：\n\n```js\nlet a = +2  \u002F\u002F当正号用\nlet b = -2  \u002F\u002F当负号用\nlet c = -a  \u002F\u002F也可以直接对变量取相反数\n```\n\n由于在数学中，正号默认就可以省掉，在JS中也是同样的，我们不写正号一样可以直接代表一个正数。\n\n此外，上面的加法运算符除了能让数字相加之外，表示正数外，还可以实现字符串的拼接：\n\n```js\nconst text1 = \"你干嘛\"\nconst text2 = \"哎哟\"\nconsole.log(text1 + text2)\n```\n\n需要注意的是，字符串无论拼接什么类型的数据，最终都会将其转换为字符串拼接起来。但是使用的时候一定要注意运算的顺序和优先级，比如：\n\n```js\nlet a = \"Hello World\"\na = 9 + 9 + a\nconsole.log(a)  \u002F\u002F这里会得到18HelloWorld，因为加法运算从左往右，先进行的是9+9这个数字运算\n```\n\n顺带一提，如果数字与除字符串外的非数字类型相加，由于自动类型转换机制，也会有不同效果：\n\n1. 布尔值会直接转换为数字，`true` 转换为 **1**，`false` 转换为 **0**\n2. `undefined`值会自动转换为一个无效数字`NaN`\n3. `null` 会被处理为 **0**\n4. 对于对象和数组，JavaScript 会先调用它们的 `valueOf()` 或 `toString()` 方法将其转换为原始值\n\n```js\nlet a = 5\nconsole.log(a + true)  \u002F\u002F得到6\nconsole.log(a + undefined)  \u002F\u002F得到NaN\nconsole.log(a + false)  \u002F\u002F得到5\n```\n\n我们接着继续来认识更多的运算符：\n\n* `%`  -  取余运算符，求一个除法运算的余数\n* `**`  -  指数运算符，可以直接求一个数的幂（ES7新增特性）\n* `++`  -  自增运算符，让一个存储数字的变量自增1\n* `--`  -  自减运算符，让一个存储数字的变量自减1\n\n其中比较简单的是取余运算符，比如一些不能除尽的运算，我想知道10除以3应该余多少，此时就可以考虑使用取余操作：\n\n```js\nconsole.log(10 % 3)\n```\n\n这里得到的结果是1，因为余数就是1，比如我们查看某个数是否为双数，只需要将其对2进行取模操作即可，因为如果是双数一定是可以整除的，如果是单数一定会余1。\n\n然后是指数运算符，和Python有点类似，直接使用两个`**`星号即可表示，前面是底数，后面是指数：\n\n```js\nlet a = 20\nlet b = a ** 2\nconsole.log(b)\n```\n\n这里计算的就是以`a`的值为底，2为指数计算，得到结果为400，注意指数运算的优先级比乘除法更高，这与数学中是一样的。\n\n接着是自增自减运算符，这里以自增为例进行讲解（自减同理）我们可以使用自增运算符来让某个保存数字的变量值+1：\n\n```js\nlet a = 6\na++\nconsole.log(a)\n```\n\n自增运算符可以放在变量的前面或后面，最终都会使得变量发生自增。但是需要注意的是，这两种写法在进行自增操作时，得到的结果会有不同，我们可以接收一下自增之后的结果：\n\n```js\nlet a = 6\nconst b = a++\nconsole.log(b)  \u002F\u002F输出b为6\n```\n\n```js\nlet a = 6\nconst b = ++a\nconsole.log(b)  \u002F\u002F输出b为7\n```\n\n不是说好的自增吗，为什么这两种方式出来的值不一样呢？这其实是因为这两种运算顺序不一样，当`++`在前面时，会先完成自增操作，再得到`a`的最终结果，而当`++`在后面时，会先得到`a`运算之前的结果，再进行自增，所有就会得到不一样的结果了。\n\n这里需要注意的是，自增自减运算符的优先级比乘除法都更高，我们可以来看下面的例子：\n\n```js\nlet a = 2\nconst b = 1 + ++a + a++ + 4 \u002F a++\nconsole.log(b)\n```\n\n看起来很复杂对吧？我们来逐步分析运算的顺序：\n\n1. 首先由于自增运算的优先级是目前最高的，这里我们需要先解决算式里面所有的自增自减运算，从左往右看，最先执行的应该是前面的`++a`运算，因为`++`在前，需要先自增再得到结果，此时：`1 + 3 + a++ + 4 \u002F a++`\n2. 接着按照顺序继续往右计算，此时来到`a++`，因为`++`在后，所以得到的结果应该是自增之前的，所以这里的`a++`还是3，接着是最后面的`a++`，因为前面已经自增完成，所以这里应该得到的是4，此时：`1 + 3 + 3 + 4 \u002F 4`\n3. 接着是优先级高一点的除法运算符，得到：`1 + 3 + 3 + 1`\n4. 最后计算得到结果为：8\n\n还有一个比较有趣的是，如果我们对着一个不是数字类型的变量进行自增自减操作会出现一些不正确的结果：\n\n```js\nlet a = \"Hello\"\nconsole.log(++a)  \u002F\u002F得到NaN，因为参与运算的不是一个数字，结果肯定也不是数字了\n```\n\n```js\nlet a = \"10\"\nconsole.log(++a)  \u002F\u002F可以得到数字11，因为发生了隐式类型转换\n```\n\n这里总结一下目前已经认识的运算符优先级：\n\n| 优先级 |        运算符         |\n| :----: | :-------------------: |\n|   1    | - + ++ --（一元运算） |\n|   2    |          **           |\n|   3    |         * \u002F %         |\n|   4    |    + -（二元运算）    |\n\n课后练习：\n\n```js\nlet a = 20\nlet b = ++(-a)\nconsole.log(b)\n```\n\n这段代码能正确执行吗，结果是什么？\n\n### 括号运算符\n\n前面我们介绍了算术运算符，我们接着来看括号运算符。\n\n我们常常在数学中使用括号提升某些运算的优先级，比如：\n\n> (1 + 7) × (3 - 6) = -24\n\n虽然加法优先级比乘法要低，但是我们给其添加括号之后，相当于提升了内部加法运算的优先级，所以说需要先计算括号中的再去计算括号外的，JS同样支持这种操作，我们可以通过添加括号的方式来提升某些运算的优先级：\n\n```js\nlet a = 3 * (5 + 2)\nconsole.log(a)\n```\n\n此时就会优先计算`5 + 2`的结果，再进行乘法运算。注意如果存在多层优先级提升，所有括号一律使用小括号：\n\n```js\nlet a = (2 + (3 + 1) * 3) * 2\nconsole.log(a)\n```\n\n### 赋值运算符\n\n赋值运算符用于给变量分配值，它可以将右边表达式的结果赋值给左边，最简单的就是我们的变量声明：\n\n```js\nlet a = 10\n```\n\n这里其实就是将10这个字面量给到左边的变量`a`作为值。\n\n和前面一样，赋值运算符除了将值给到变量外，它也会产生结果，我们可以来尝试一下：\n\n```js\nlet a, b\na = b = 10  \u002F\u002F这里让a等于b赋值为10的结果\nconsole.log(a)\n```\n\n可以看到最后`a`的值也是10，说明赋值运算产生的结果就是变量被成功赋值的结果，这里`b`得到了10，那么产生的结果也就是10。利用这种特性，如果我们想统一将某些变量值进行修改，就可以采用这种连等的方式进行赋值。\n\n此外，由于JavaScript不是强类型语言，变量的值可以在不同的类型之间随意变换，比如一个字符串类型的变量，我们也可以将其值修改为数字类型的结果：\n\n```js\nlet a = \"Hello World\"\na = 1\nconsole.log(a)\n```\n\n> 强类型语言对类型的检查非常严格。一旦一个变量被指定了某种数据类型，如果不经过**显式转换**（强制转换），它就永远是这种类型。\n>\n> 弱类型语言对类型的约束很宽松。它会自动、隐式地进行类型转换，试图“猜测”你的意图。\n\n我们接着来看赋值运算符的另一种用法，在之前我们曾尝试过些这种代码：\n\n```js\nlet a = 20\na = a + 10   \u002F\u002F让a等于自己加上10\nconsole.log(a)\n```\n\n这种操作与我们前面说的自增操作有点类似，只是变化量不同而已。对于这种操作，我们可以将其简化为以下形式：\n\n```js\nlet a = 20\na += 10   \u002F\u002F与上面的写法是等价的\nconsole.log(a)\n```\n\n同时，这种写法也会产生结果：\n\n```js\nlet a = 20\nlet b = a += 10\nconsole.log(b)\n```\n\n这里`b`得到的结果就是`a`自增完成之后的值，类似于前面吧`++`写到前面的效果。除了加法之外，只要是前面涉及的算数运算符都可以使用这种方式进行简化。\n\n### 比较运算符\n\n除了对基本类型进行加减乘除这类数学运算，我们还可以对齐进行比较，比如现在我们想要比较两个变量是否相等，就可以使用这里要介绍的比较运算符，首先是相等的判断：\n\n* `==`  -  等于（宽松比较，自动类型转换）\n* `===`  -  严格等于（仅值和类型都相等时，才会判定为相等）\n* `!=`  -  不等于（宽松比较）\n* `!==`  -  严格不等于（类型或值不等）\n\n比如我们要比较`a`和`b`的值是否相等：\n\n```js\nlet a = 2\nlet b = 4\nconst c = a === b  \u002F\u002F使用三等号来进行严格类型判断\nconsole.log(c)\n```\n\n进行比较之后，会得到一个布尔类型的结果，此时由于`a`并不等于`b`，所以会得到一个`false`为结果，表示`a`等于`b`这个判断是假的，不等于比较和等于比较是相反的，这里就不做介绍。\n\n需要注意的是，在JS中存在两种形式的等于和不等于判断，这里我们使用的是严格的等于（三等号）也就是要求类型和值都要完全相等时才比较成功，而宽松版本的等于判断，会考虑自动类型转换后是否相等，比如：\n\n```js\nlet a = 2\nlet b = '2'\nconst c = a == b\nconsole.log(c)\n```\n\n这里`a`和`b`一个是数字值，还有一个是字符串值，很明显两者并不是同一个类型的东西，但是在宽松比较下会自动进行类型转换，从而导致比较得到的结果为真。\n\n除了数字之外，我们也可以判断其他的类型，比如判断两个字符串是否内容一样：\n\n```js\nlet a = 'Hello World'\nlet b = 'Hello World'\nconst c = a === b\nconsole.log(c)\n```\n\n或者判断某个变量是否未进行初始化：\n\n```js\nlet c\nconsole.log(c === undefined)\n```\n\n除了等于判断外，我们也可以使用数学中的大于小于符号来进行比较：\n\n* `>`  -  大于\n* `>=`  -  大于等于\n* `\u003C`  -  小于\n* `\u003C=`  -  小于等于\n\n比如我们要判断：\n\n```js\nlet a = 1\nlet b = 2\nconsole.log(a > b)\n```\n\n对于字符串类型来说，也可以使用大于小于比较，但是比较的是它们的 Unicode 码点，你们可以简单理解为每一个字符在计算机中都有一个自己的编号，比如字符`a`小写字母的代码为97，字符`b`小写字母的代码为98，此时比较的就是字符的代码。字符串比较是**逐字符从左到右逐一对比**，比较过程中：\n\n- 若两个字符的 Unicode 码点相同，则继续比较下一个字符。\n- 若某一位置的字符码点不同，则直接根据码点大小判断字符串的大小。\n- 若一个字符串是另一个字符串的前缀（例如 `\"abc\"` 和 `\"abcd\"`），则**较短的字符串更小**。\n\n```js\nlet a = 'aaaa'\nlet b = 'abbb'\nconsole.log(a \u003C b)\n```\n\n根据上面的规则，显然`b`的字符码点要比`a`更高，所以判断结果为真。\n\n针对于其他类型的大小比较（除目前未讲解的以外）遵循我们前面所说的规则，比如`undefind`会被转换为0，布尔值会被自动转换为1和0这两种：\n\n```js\nlet a = true\nlet b = 0\nconsole.log(a \u003C b)  \u002F\u002F此时由于a自动转换为1，而b是0，所以结果是假\n```\n\n不过有一个特殊值`NaN`需要注意，`NaN` 不大于、不小于或等于任何值，包括自身。\n\n课后作业：大家可以思考一下下面这个比较的结果：\n\n```js\nlet a = true\nlet b = undefined\nconsole.log(a \u003C b)\n```\n\n### 逻辑运算符\n\n逻辑运算符主要用于对布尔类型的值进行计算，比如我们想要同时判断某个数是否为双数并且还不能是负数，这个时候单凭某一个判断就无法实现了，对于这种复杂判断可能会同时产生多个判断结果，我们需要综合这些结果：\n\n```js\nlet a = 8\nconst isEven = a % 2 === 0\nconst isPositive = a > 0\n```\n\n可以看到这里我们判断出了两个结果，一个对应数字是否为双数还有一个对应数字是否大于0，此时我们想要做的是判断这两个结果都为真，我们可以使用与逻辑运算符`&&`：\n\n```js\nconsole.log(isEven && isPositive)\n```\n\n使用逻辑与运算符后，当运算符两边的值都为`true`时，才会得到最终结果为真，否则最终结果为假。\n\n同样的，我们也可以使用逻辑或来判断，比如我们想判断一个数要么是大于0，要么是单数，此时就可以使用逻辑或运算符`||`：\n\n```js\nlet a = -8\nconst isOdd = a % 2 === 1\nconst isPositive = a > 0\nconsole.log(isOdd || isPositive)\n```\n\n此时，只要运算符两边存在至少一个条件为真，那么结果就是真，如果都不是真，那么结果就是假。\n\n需要注意的是，以上两种逻辑判断运算，都是短路运算，也就是说当前一个条件已经能够决定最终结果时，将不在继续后续的判断：\n\n* 当进行逻辑与运算时，如果第一个条件就是`false`，那么后续无论是真还是假，结果都必定为假，所以不会继续向后判断。\n* 当进行逻辑或运算时，如果第一个条件就是`true`，那么后续无论是真还是假，结果都必定为真，所以不会向后继续判断。\n\n此外，还有一种逻辑运算符叫做非运算，它的效果很简单，可以让一个布尔结果取反：\n\n```js\nlet a = -8\nconst isPositive = a > 0\nconsole.log(!isPositive)  \u002F\u002F原本为false那就变成true，原本为true那就变成false\n```\n\n使用`!`表示对结果进行取反，比如这里我们判断出数字是否为负数，结果为假，然后进行了取反操作，得到的结果就是真了。\n\n这里需要注意的是，取反操作由于是一个一元运算符，它的优先级要比上面的两种逻辑运算符更高，比如：\n\n```js\nlet a = -8\nconst isOdd = a % 2 === 1\nconst isPositive = a > 0\nconsole.log(!isOdd && isPositive)\n```\n\n这里同时使用了取反运算和逻辑与运算，但是取反运算的优先级更高，所以`isOdd`的结果变成了相反的值，这里的判断结果就变成了不是奇数且是正数了。如果需要对与或运算的结果进行统一取反，可以使用括号提升优先级：\n\n```js\nlet a = -8\nconst isOdd = a % 2 === 1\nconst isPositive = a > 0\nconsole.log(!(isOdd && isPositive))\n```\n\n此时就变成了对整体结果的取反。大家可以思考一下，此时的判断条件变成什么了呢？\n\n> 想要快速得到逻辑判断的结果，我们，可以利用**逻辑代数的德摩根定律**来进行分析，德摩根定律指出：\n>\n> * 非（A 且 B）等价于（非 A 或 非 B）符号表示为：`!(A && B) ≡ !A || !B`\n> * 非（A 或 B）等价于（非 A 且 非 B）符号表示为：`!(A || B) ≡ !A && !B`\n>\n> 大家也可以尝试自己推断一下\n\n所以这判断的结果就变成了要么不是奇数要么不是正数了。\n\n除了对布尔类型进行与或非运算之外，我们也可以对其他的基本类型进行与或非运算，比如现在有两个数字：\n\n```js\nlet a = -8, b = 1\nconsole.log(a && b)\n```\n\n那么此时运算的结果是什么呢？这里我们需要大概介绍一下：\n\n* 在 JavaScript 中，**值分为“真值”（Truthy）** 和 **“假值”（Falsy）**，这是逻辑运算（如 `&&`、`||`、`!`）的核心判断依据。\n* 这里共有 **7 个固定假值**，这些值在转换为布尔值时会直接被视为 `false`：\n  1. **`false`** —— 明确的布尔假值\n  2. **`0`**（数字零）\n  3. **`-0`**（负零，本质仍是数字零）\n  4. `**空字符串** —— 没有任何内容\n  5. **`null`** —— 表示“无对象”\n  6. **`undefined`** —— 表示“未定义”\n  7. **`NaN`**（Not a Number）—— 非数字类型，本身是假值\n* 所有其他值（包含**4 种常见类型**，也就是除假值外的所有值）都被视为真值：\n  1. **`true`** —— 明确的布尔真值\n  2. **非零数字**（包括正负整数、小数、负数，如 `1`、`-5`、`0.1` 等）\n  3. **非空字符串**（长度 ≥1 的字符串，如 `\"hello\"`、`'a'` 等）\n  4. **对象**（任何对象类型，包括数组、函数、日期等，如 `[]`、`{}`、`function(){}`, `new Date()` 等，即使为空数组 `[]` 也是真值）\n\n所以，当我们使用其他数据类型进行与或非运算时，需要判断其是真值还是假值，这里提供的`a`和`b`都是非零数字，也就是真值，所以这里的判断结果应该是真。但是我们发现，结果好像并不是一个布尔类型的值：\n\n![image-20260124214126147](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F24\u002FEICQWub2kgcSfnO.png)\n\n这是因为，如果逻辑运算的操作数是`true`、对象、非零数字、非空字符串等这类“真值”，则返回该操作数；若为 `false`、`0`、`\"\"`、`null`、`undefined`、`NaN` 等“假值”，则返回该假值。也就是说，实际上JS中的逻辑与和逻辑或运算得到的结果是操作数本身，而非根据判断结果重新给出一个布尔值（这也是和其他语言逻辑运算很大的一个不同）而具体返回的是左边还是右边，取决于当前的判断进行到了那里。\n\n> 一句话概括，逻辑运算符本质只是个值选择器，只是利用布尔逻辑来决定停在哪个值上，并得到这个值的结果。\n\n这里我们对`-8`和`1`这两个值进行了与运算，为了得到结果，由于前者为真，所以需要进一步判断后者，此时后者也为真，结束时由于已经判断到后者位置，所以得到的结果就是后者这个真值本身了，因此结果为`1`。\n\n我们也可以来试试看短路的情况：\n\n```js\nlet a = 0, b = 1\nconsole.log(a && b)\n```\n\n可以看到，此时`a`是一个假值，那么根据我们前面说的规则，与运算在发现第一个元素为假时会直接结束判断，因为结果必定为假。所以这里得到的结果就是`0`。同样的，我们也可以来试试看或运算：\n\n```js\nlet a = 0, b\nconsole.log(a || b)\n```\n\n此时由于`a`为假，但是并未发生短路，所以继续向后判断，来到后面`b`发现也是假，所以结果最终为假，返回假值`b`。\n\n当然，对于逻辑非来说，就非常的简单粗暴，它会根据当前是真值还是假值进行判断，如果是真值就返回`false`，如果是假值就返回`true`，比如：\n\n```js\nlet a = 0\nconsole.log(!a)\n```\n\n有些时候，我们如果需要判断某个数据是真值还是假值，那么可以连续使用两个非运算符：\n\n```js\nlet a = 0\nconsole.log(!!a)\n```\n\n这样相当于将第一次非运算的结果继续进行非运算，这样就可以回到正常的判断逻辑下了。\n\n在JavaScript中还存在一种比较特殊的三元运算符，它可以根据条件判断，来选取想要的值。\n\n```js\n条件 ? 表达式1 : 表达式2\n```\n\n比如我们想根据偶数还是奇数来分别展示字符串：\n\n```js\nlet a = 8\nconst text = a % 2 === 0 ? 'Even' : 'Odd'\nconsole.log(text)\n```\n\n最后，我们还要介绍一个ES 2020新推出的逻辑运算符，**空值合并运算符（Nullish Coalescing Operator）**，它用于处理**默认值**，它的作用是：当左侧操作数是 `null` 或 `undefined` 时，返回右侧的默认值；否则返回左侧操作数本身。\n\n```js\nleftValue ?? defaultValue\n```\n\n比如：\n\n```js\nlet a\nconsole.log(a ?? \"HelloWorld\")\n```\n\n此时由于变量`a`没有设定默认值，那么就是`undefined`，我们使用空值合并运算符来为其增加一个备选，这里就可以得到备选的结果。此外，结合我们之前讲解的赋值操作，还可以实现如下效果：\n\n```js\nlet a\na = a ?? 'Hello World'\na ??= 'Hello World'  \u002F\u002F等价于上面的写法\nconsole.log(a)\n```\n\n这种操作我们称之为**空值合并赋值**，可以快速判断某个变量是否存在初始值并设定备选值。包括与和或运算都是可以像这样的：\n\n```js\nlet a\na = a || 'Hello World'\na ||= 'Hello World'  \u002F\u002F等价于上面的写法\na = a && 666\na &&= 666\nconsole.log(a)\n```\n\n这些用法都是在ES2020中新增的。\n\n老规矩，总结一下目前学习的运算符：\n\n| 优先级 | 类型                      |          运算符          |  结合性  |\n| :----: | ------------------------- | :----------------------: | :------: |\n|   1    | 括号                      |           ( )            | 从左向右 |\n|   2    | 后置自增\u002F自减             |          ++ --           | 从右到左 |\n|   3    | 逻辑非\u002F正负号（一元运算） |          + - !           | 从右到左 |\n|   4    | 前置自增\u002F自减             |          ++ --           | 从右到左 |\n|   5    | 算术运算符 (指数)         |            **            | 从右向左 |\n|   6    | 算术运算符 (乘除)         |          * \u002F %           | 从左向右 |\n|   7    | 算术运算符 (加减)         |           + -            | 从左向右 |\n|   8    | 比较                      |        > \u003C >= \u003C=         | 从左到右 |\n|   9    | 严格相等\u002F不相等           |         ===  !==         | 从左到右 |\n|   10   | 宽松相等\u002F不相等           |          == !=           | 从左到右 |\n|   11   | 逻辑与                    |            &&            | 从左到右 |\n|   12   | 逻辑或\u002F空值合并           |         \\|\\|  ??         | 从左到右 |\n|   13   | 三元运算                  |            ?:            | 从右到左 |\n|   14   | 赋值                      | = += -= *= \u002F= %= **= ??= | 从右向左 |\n\n课后思考，下面的结果应该是什么？\n\n```js\nlet a = -8, b = NaN, c = \"Hello\", d\nconsole.log(a \u003C 0 && b || c && (d ?? \"Default\"))\n```\n\n### 逗号运算符（选学）\n\n在 JavaScript 中，**逗号运算符 (Comma Operator)** 是一个不太常用但非常有意思的特性，由于使用频率极少，这里仅作为选学。\n\n逗号运算符会将多个表达式连接在一起，计算过程如下：\n\n1. 计算第一个表达式。\n2. 计算第二个表达式。\n3. ...以此类推。\n\n比如：\n\n```js\nlet result = (1 + 2, 3 + 4); \nconsole.log(result);  \u002F\u002F 输出 7\n```\n\n逗号运算符在所有 JavaScript 运算符中优先级是**最低**的，因此，在赋值语句中使用它时，通常需要用圆括号将其括起来。\n\n### 位运算符（选学）\n\n本部分涉及到部分计算机底层原理，建议先完成前面的**计算机中的二进制**小节的学习，本节依然作为选学内容。在前面我们介绍过逻辑与或非运算，除了逻辑运算外，还存在一种叫做位运算的操作，可以直接对数据底层的二进制位进行操作。\n\n在 JS 中，虽然数值是以 64 位浮点数存储的，但在进行位运算时，它们会被临时转换为 **32 位有符号整数**，运算后再转换回 64 位浮点数，我们先来认识一下位运算的与或操作：\n\n- **与 (`&`)**：对数据的每一个二进制位进行比较，都为1则该位结果为1\n- **或 (`|`)**：对数据的每一个二进制位进行比较，其中一个为1则该位结果为1\n- **异或 (`^`)**：对数据的每一个二进制位进行比较，其中一个为1其中一个为0则该位结果为1\n\n我们可以来试试看下面这段代码：\n\n```js\nlet a = 9, b = 3\nconsole.log(a & b)\n```\n\n按位与实际上就是让这两个数每一位都进行比较，如果这一位两个数都是1，那么结果就是1，否则就是0：\n\n- a = 9 = 1001\n- b = 3 = 0011\n- c = 1 = 0001（因为只有最后一位，两个数都是1，所以说结果最后一位是1，其他都是0）\n\n同样的，按位或，其实就是只要任意一个为1（不能同时为0）那么结果就是1：\n\n```js\nlet a = 9, b = 3\nconsole.log(a | b)\n```\n\n- a = 9 = 1001\n- b = 3 = 0011\n- c =11= 1011（只要上下有一个是1或者都是1，那结果就是1）\n\n异或的意思就是只有两边不相同的情况下，结果才是1，也就是说一边是1一边是0的情况：\n\n```js\nlet a = 9, b = 3\nconsole.log(a ^ b)\n```\n\n- a = 9 = 1001\n- b = 3 = 0011\n- c =10= 1010（从左往右第二位、第四位要么两个都是0，要么两个都是1，所以说结果为0）\n\n除了与或操作外，我们还可以实现按位取反操作，按位取反操作跟前面的正负号一样，只操作一个数，如果这一位上是1，变成0，如果是0，变成1：\n\n```js\nlet a = 127\nconsole.log(~a)\n```\n\n- 127 = 00000000 00000000 00000000 01111111\n- -128 = 11111111 11111111 11111111 10000000\n\n所以说计算的结果就是-128了。\n\n除了以上的四个运算符之外，还有位移运算符，比如：\n\n```java\nlet a = 1 \u003C\u003C 2;   \u002F\u002F两个连续的小于符号，表示左移运算\nconsole.log(a)\n```\n\n- 1 = 00000001\n- 4 = 00000100（左移两位之后，1跑到前面去了，尾部使用**0**填充，此时就是4）\n\n我们发现，左移操作每进行一次，结果就会x2，所以说，除了直接使用`*`进行乘2的运算之外，我们也可以使用左移操作来完成。\n\n同样的，右移操作就是向右移动每一位咯：\n\n```js\nlet a = 8 >> 2;\nconsole.log(a)\n```\n\n- 8 = 00001000\n- 2 = 00000010（右移两位之后，1跑到后面去了，头部使用**符号位数字**填充，此时变成2）\n\n跟上面一样，右移操作可以快速进行除以2的计算。对于负数来说，左移和右移操作不会改变其符号位上的数字，符号位不受位移操作影响：\n\n```js\nlet a = -8 >> 2;\nconsole.log(a)\n```\n\n- -4 = 11111111 11111111 11111111 11111100\n- -2 = 11111111 11111111 11111111 11111110（前面这一长串1都被推到后面一位了，因为是负数，头部需要使用**符号位数字**来进行填充）\n\n我们来总结一下：\n\n- **左移操作\u003C\u003C：** 高位直接丢弃，低位补0\n- **右移操作>>：** 低位直接丢弃，符号位是什么高位补什么\n\n除此之外，我们也可以使用考虑带符号位的右移操作，一旦符号被移动，那么就会出现：\n\n```js\nlet a = -1 >>> 1;\nconsole.log(a)\n```\n\n- -1 = 11111111 11111111 11111111 11111111\n- 右移： 01111111 11111111 11111111 11111111（无符号右移使用0填充高位）\n\n此时得到的结果就是32位正整数的最大值 2147483647 了，注意，不存在无符号左移。\n\n### 类型运算符\n\n我们接着来介绍一种与类型相关的运算符，由于JS的变量类型并不是明确的类型，所以有些时候可能我们需要知道变量的值是什么类型的，此时就可以使用`typeof`关键字：\n\n```js\nlet a= 1\nconsole.log(typeof a)\n```\n\n此时会输出一个字符串`number`作为结果，不同变量会根据其类型得到不同的类型名称：\n\n- **`\"undefined\"`**：如果操作数未定义。\n- **`\"boolean\"`**：如果操作数是布尔值。\n- **`\"number\"`**：如果操作数是数值。\n- **`\"string\"`**：如果操作数是字符串。\n- **`\"object\"`**：如果操作数是对象、数组或 `null`。\n- **`\"function\"`**：如果操作数是函数。\n\n比如我们想要确定某个变量是否为数字，就可以搭配前面学习的比较运算符进行判断：\n\n```js\nlet a= \"Hello World\"\nconsole.log(typeof a === 'number')\n```\n\n这里得到的结果是`false`因为类型不是`number`。\n\n### 类型转换\n\n类型转换在JS中非常常见，很多时候我们可能都需要转换一些类型到另一种类型，比如我们想把字符串的数字转换为真正可以运算的数字，想把一个布尔类型的值变成一个字符串形式的值，这都需要借助类型转换来完成。\n\nJS中的类型转换分为隐式类型转换和显示类型转换，我们先来介绍一下隐式类型转换，隐式转换最常见的就是，当运算符发现操作数类型不匹配时，则会自动进行类型转换，比如加法运算符：\n\n```js\nlet a= 2, b = true\nconsole.log(a + b)\n```\n\n加法运算符默认情况下会将所有非数字形式的值转换为数字类型再进行运算，这里的`true`代表的就是1，所以计算的结果就是3。如果存在操作数是字符串类型的，那么其他类型的值也会自动转换为字符串类型的值与其进行拼接：\n\n```js\nlet a= 2, b = \"true\"\nconsole.log(a + b)\n```\n\n同样的，除了加法外，减乘除会在运算时自动将值转换为数字类型进行计算，比如：\n\n```js\nlet a= 2, b = \"22\"\nconsole.log(b - a)\n```\n\n这里的字符串先被转换为了数字的`22`，然后再进行减法运算。不仅仅是字符串和数字，甚至两个字符串都会被自动转换：\n\n```js\nlet a= \"10\", b = \"22\"\nconsole.log(b - a)\n```\n\n```js\nlet a= 21, b = false\nconsole.log(b * a)\n```\n\n除了数学运算外，包括我们前面介绍的非严格相等，也会进行隐式类型转换进行比较：\n\n```js\nlet a= 22, b = \"22\"\nconsole.log(b == a)  \u002F\u002F自动转换为相同的\n```\n\n这里进行比较之前，也会将`b`转换为数字进行比较。可能会有小伙伴好奇这里为什么不是把`a`转换为字符串和`b`比较呢？\n\n* 如果比较的一方是 **Number**，另一方无论是什么类型，JS 会尝试将其 **转换为 Number**，然后再进行比较。\n* 如果比较的双方都是 **String**，那么双方会按照字符串的比较规则进行比较。\n\n介绍完隐式类型转换，我们接着来看显式类型转换（Explicit Conversion）除了在运算时自动类型转换，我们也可以使用一些函数来实现手动类型转换：\n\n* **转换为 Number：** 使用 `Number()`、`parseInt()` 或 `parseFloat()`。\n* **转换为 String：** 使用 `String()` 或变量的 `.toString()` 方法。\n* **转换为 Boolean：** 使用 `Boolean()`。\n\n```js\nlet a= 22\nconsole.log(a.toString())\n```\n\n这里就是手动将变量`a`转换为字符串。不过由于我们目前还未接触函数，所以暂时不进行详细讲解，仅做了解即可。\n\n## 流程控制\n\n前面我们给大家介绍了如何在JavaScript中创建变量以及利用运算符来进行各种复杂计算，不过，光有这些还远远不够，为了能够实现更加高级的程序，我们还需要借助流程控制来完成这些操作。\n\n比如，我们希望判断某个变量是否为双数，如果是，那么就在浏览器中打开一个通知弹窗，如果不是，那么就在控制台打印一个HelloWorld，那这个时候应该怎么办呢？这一节，我们将带大家探索多种不同的流程控制结构，来解决这些麻烦发问题。\n\n### 顺序结构\n\n在正常编写代码时，我们的代码都是按照从上往下，从左往右的顺序执行的：\n\n```js\nconsole.log(\"A\")\nconsole.log(\"B\")\nconsole.log(\"C\");console.log(\"D\")\n```\n\n执行顺序为ABCD，相信大家应该已经非常熟悉了。我们接着来介绍一个新的概念，**代码块（Code Block）** 是由一对花括号 `{}` 包裹的一组语句，它们一起构成一个独立的执行上下文。代码块的主要作用是**提高代码的可读性**和**控制变量的作用域**。\n\n```js\n{\n  \u002F\u002F 语句 1\n  \u002F\u002F 语句 2\n  \u002F\u002F ...\n}\n```\n\n比如我们像这样写：\n\n```js\nconsole.log(\"A\")\n{\n    console.log(\"B\")\n}\nconsole.log(\"C\")\n```\n\n此时打印`B`在一个独立的代码块中，代码块中的代码同样也是按照顺序执行的，这里的执行结果依然是ABC\n\n当然，可能会有小伙伴说，那这有啥用呢？不就多套了一层吗？我还不如不写呢。代码块除了能够整合代码，还有一个非常关键的作用，它可以创建一个独立的变量作用域：\n\n```js\n{\n    let a = 10\n    console.log(a)\n}\nconsole.log(a)  \u002F\u002F报错\n```\n\n我们在代码块中编写的变量，无论是`let`还是`const`，是无法在代码块以外的区域访问的。\n\n当然，代码块虽然限制了自己的作用域，但是如果我们在最外层声明变量，在代码块内还是可以访问的，因为代码块也是整个JS文件的一部分：\n\n```js\nlet a = 10\n{\n    console.log(a)\n}\nconsole.log(a)\n```\n\n同样的，代码块内部还可以进一步细分，我们可以嵌套一个代码块来实现更加私有的领域：\n\n```js\n{\n    {\n        let a = 10 \n    }\n    console.log(a)  \u002F\u002F无法访问\n}\nconsole.log(a)  \u002F\u002F无法访问\n```\n\n当然，这里为大家介绍代码块的功能并非是让大家以后都使用代码块来对代码进行分区，这种用法实际上很少，这样做是为了让大家在后续学习其他流程控制语句时能够分清变量的作用域。\n\n需要顺带一提的是，使用我们前面用到的`var`声明变量时，会出现作用域不明确问题：\n\n```js\n{\n    {\n        var a = 10\n    }\n    console.log(a)\n}\nconsole.log(a)\n```\n\n可以看到，明明是在代码块中声明的变量`a`，居然可以被外部访问，这在后续使用选择结构或是循环结构时，可能会导致非常致命的问题，因为变量的声明会**泄露**到作用域以外的区域，这是很严重的污染现象。\n\n### 选择结构\n\n选择结构可以根据条件判断来走向不同的分支，就像我们选择了不同的专业面临不同的就业市场一样。`if`语句可以对条件进行判断，当条件满足时，才会执行其中的内容：\n\n```js\nif(表达式) { 代码块 }\n```\n\n我们可以在`if`语句中传入一个表达式，结果需要是布尔类型的，比如我们想要判断变量是否等于10，如果等于10就弹窗：\n\n```js\nlet a = 10\nif(a === 10) {  \u002F\u002F判断是否等于10，如果是则执行代码块中的内容\n    alert(\"Hello World\")\n}\nconsole.log(a)\n```\n\n如果`if`判断成功，就会进入到代码块中执行其中的内容，然后再继续向后执行。如果没有判断成功，则会直接跳过整个代码块继续向后执行（`if`判断成功与否只会影响其所属代码块的内容是否执行，不影响后续其他代码）\n\n特别的，如果`if`代码块中的代码只有一句，那么我们也可以省略掉代码块：\n\n```js\nlet a = 10\nif(a === 10) alert(\"Hello World\")\nconsole.log(a)\n```\n\n注意，一旦去除`if`的代码块，那么它将只能影响后续的一句代码（即使用分号分隔也会被算作两句代码，只有前者受影响）后续所有代码会被视作`if`语句之外的代码。\n\n上面说到，`if`判断的表达式结果必须是一个布尔类型的结果，但是实际上我们也可以使用其他类型用于判断：\n\n```js\nlet a = NaN\nif(a) {  \u002F\u002F此时直接将a作为判断依据\n    alert(\"Hello World\")\n}\n```\n\n如果我们传入了其他类型的值作为判断的依据，那么会如何进行判断呢？由于判断需要一个布尔类型的结果，所以此时会根据前面介绍的真值假值特性来进行判断，比如这里的`NaN`就是属于假值，所以判断的结果也是假。而如果是：\n\n```js\nlet a = 1\nif(a) {\n    alert(\"Hello World\")\n}\n```\n\n此时因为`a`是一个非0的数字，属于真值，所以这里判断的结果也是真。\n\n除了单分支判断外，我们还可以额外编写不满足条件时的分支代码块，使用`else`关键字：\n\n```js\nlet a = NaN\nif(a) {\n    console.log(\"Hello World\")\n} else {\n    console.log(\"Crazy Thursday vivo ￥50\")\n}\n```\n\n当判断到条件不满足时，会进入到`else`所属的代码块中，这种语法叫做`if-else`语句，它就像两个分支，跟据不同的判断情况从而决定下一步该做什么，这跟我们之前认识的三元运算符性质比较类似。\n\n那如果此时我们需要判断多个分支呢？比如我们现在希望判断学生的成绩，不同分数段打印的等级不一样，比如90以上就是优秀，70以上就是良好，60以上是及格，其他的都是不及格，那么这种我们又该如何判断呢？要像这样进行连续判断，我们需要使用`else-if`来完成：\n\n```js\nlet score = 85\nif(score >= 90)    \u002F\u002F90分以上才是优秀\n    console.log(\"优秀\")\nelse if (score >= 70)    \u002F\u002F当上一级if判断失败时，会继续判断这一级\n    console.log(\"良好\")\nelse if (score >= 60)\n    console.log(\"及格\")\nelse    \u002F\u002F当之前所有的if都判断失败时，才会进入到最后的else语句中\n    console.log(\"不及格\")\n```\n\n我们可以在`if`和`else`之间插入若干个`else if`语句，来实现更多分支的判断。\n\n最后需要说明的是，除了这里讲解的`if`语句之外，包括后续介绍的所有流程控制语句都是支持嵌套使用的：\n\n```js\nconst score =  2\nif(score \u003C 60) {   \u002F\u002F先判断不及格\n    if(score > 30)    \u002F\u002F在内层再嵌套一个if语句进行进一步的判断\n        console.log(\"学习JavaScript\")\n    else\n        console.log(\"学习TypeScript\")\n}\n```\n\n---\n\n前面我们介绍了if语句，我们可以通过一个if语句轻松地进行条件判断，然后根据对应的条件，来执行不同的逻辑，当然除了这种方式之外，我们也可以使用`switch`语句来实现，它更适用于多分支的情况：\n\n```js\nswitch (目标) {   \u002F\u002F我们需要传入一个目标，比如变量，或是计算表达式等\n  case 匹配值:    \u002F\u002F如果目标的值等于我们这里给定的匹配值，那么就执行case后面的代码\n    代码...\n    break    \u002F\u002F代码执行结束后需要使用break来结束，否则会溜到下一个case继续执行代码\n}\n```\n\n比如现在我们要根据学生的等级进行分班，学生有ABC三个等级：\n\n```js\nconst c = 'A'\nswitch (c) {  \u002F\u002F这里目标就是变量c\n    case 'A':    \u002F\u002F分别指定ABC三个匹配值，并且执行不同的代码\n        console.log(\"去尖子班！准备冲刺985大学！\")\n        break   \u002F\u002F执行完之后一定记得break，否则会继续向下执行下一个case中的代码\n    case 'B':\n        console.log(\"去平行班！准备冲刺一本！\")\n        break\n    case 'C':\n        console.log(\"直接去国外申请国际生回来读清北\")\n        break\n}\n```\n\n这里需要注意的是，分支语句结束后，一定要在末尾添加`break`语句，否则会继续顺延向下一个分支继续执行。`switch`可以精准匹配某个值，但是它不能进行范围判断，比如我们要判断分数段，这时用switch就很鸡肋了。\n\n当然除了精准匹配之外，其他的情况我们可以用default来表示：\n\n```js\nswitch (目标) {\n    case: ...\n    default:\n    \t\t其他情况下执行的代码\n}\n```\n\n我们还是以刚才那个例子为例：\n\n```js\nconst c = 'A'\nswitch (c) {\n    case 'A':\n        console.log(\"去尖子班！\")\n        break\n    case 'B':\n        console.log(\"去平行班！\")\n        break\n    case 'C':\n        console.log(\"去差生班！\")\n        break\n    default:   \u002F\u002F其他情况一律就是下面的代码了\n        console.log(\"去读职高，分流\")\n}\n```\n\n目前，我们已经认识了两种选择分支结构语句。\n\n### 循环结构\n\n通过前面的学习，我们了解了如何使用分支语句来根据不同的条件执行不同的代码，我们接着来看第二种重要的流程控制语句：循环语句。\n\n我们在某些时候，可能需要批量执行某些代码：\n\n```js\nconsole.log(\"大烟杆嘴里塞，我只抽第五代\")\nconsole.log(\"大烟杆嘴里塞，我只抽第五代\")\nconsole.log(\"大烟杆嘴里塞，我只抽第五代\")\n```\n\n遇到这种情况，由于我们还没学习循环语句，那么就只能写N次来实现这样的多次执行。但是如果此时要求我们将一句话打印100遍、1000遍、10000遍，那么我们岂不是光CV代码就要搞一下午？\n\n现在，要解决这种问题，我们可以使用`for`循环语句来多次执行：\n\n```js\nfor (表达式1;表达式2;表达式3) { 循环体 }\n```\n\n介绍一下详细规则：\n\n- 表达式1：在循环开始时仅执行一次。\n- 表达式2：每次循环开始前会执行一次，要求为判断语句，用于判断是否可以结束循环，若结果为真，那么继续循环，否则结束循环。\n- 表达式3：每次循环完成后会执行一次。\n- 循环体：每次循环都会执行一次循环体。\n\n一个标准的for循环语句写法如下：\n\n```js\nfor (let i = 0; i \u003C 3; i++) {  \u002F\u002F循环3次\n    console.log(\"大烟杆嘴里塞，我只抽第五代\")\n}\n```\n\n这里我们来解释一下`for`语句里这三个表达式分别代表什么意思：\n\n* `let i = 0`，循环开始前执行**唯一一次**，用于声明并初始化循环变量，这个变量会在整个循环过程中作为“计数器”。\n* `i \u003C 3`，**每次循环前都会检查**条件是否满足，决定循环是否继续执行，判断变量 `i` 是否小于 `3`。如果成立（为 `true`），就执行循环体；如果不成立（为 `false`），循环就停止。\n* `i++`，**每次循环体执行完后执行**，用于更新循环变量的值，每次循环结束后，`i` 的值会加 `1`。\n\n所以，整个循环其实就是让控制一个变量不断变化，当变量不满足条件时结束，我们可以通过调整合理的参数来控制循环的次数。不过，在这种已知循环次数的情况下，最标准的就是上面的写法，通过自增来实现控制。\n\n需要注意的是，和`if`一样，如果循环体的语句只有一条，那么也可以省略花括号：\n\n```js\nfor (let i = 0; i \u003C 3; i++) \n    console.log(\"大烟杆嘴里塞，我只抽第五代\")\n```\n\n还有一个需要注意的是变量的作用域，我们在`for`循环中声明的用于控制循环的变量，也只能在循环体内使用：\n\n```js\nfor (let i = 0; i \u003C 3; i++)\n    console.log(`大烟杆嘴里塞，我只抽第五代${i}`)\nconsole.log(i)  \u002F\u002F报错，无法访问\n```\n\n当然，如果你还是希望将`i`放到外部访问，我们也可以吧变量声明单独提出来：\n\n```js\nlet i = 0  \u002F\u002F在最外层单独声明\nfor (; i \u003C 3; i++)\n    console.log(`大烟杆嘴里塞，我只抽第五代${i}`)\nconsole.log(i)\n```\n\n> 这里可能会有人觉得，我们可以使用`var`来解决问题，因为它可以实现作用域提升，但是仍然是不推荐的，因为它还存在一些其他的问题。\n\n可能很多小伙伴会说，那这个时候`for`循环不就缺失第一个表达式了吗？这实际上是允许的，`for`循环的三个表达式并不强制要求存在，可以选择不写：\n\n```js\nfor (;;)\n    console.log(`大烟杆嘴里塞，我只抽第五代${i}`)\n```\n\n如果什么都不写，那么此时`for`循环将没有起始操作，没有循环判断，没有结束操作，最关键的是由于没有循环判断，也就是说不需要判断任何东西，默认会直接继续下一次循环，这就变成了一个非常恐怖的无限循环，此时网页会卡死，点浏览器刷新都很困难。所以，大家在设计一些复杂循环条件时，一定要注意是否会出现死循环，尤其是后面讲解的`while`循环。\n\n和之前的`if`一样，`for`循环同样支持嵌套使用：\n\n```js\nfor (let i = 0; i \u003C 3; i++) {\n    for (let j = 0; j \u003C 3; j++) {   \u002F\u002F相当于外层的每一轮循环中，还要先等待内层完成循环\n        console.log(`i = ${i}, j = ${j}`)\n    }\n}\n```\n\n![image-20260126155818452](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F26\u002FT3e6Y1CjBwRltiO.png)\n\n上面的代码中，外层循环会执行3轮，而整个循环体又是一个循环语句，那么也就是说，每一轮循环都会执行里面的整个循环，里面的整个循环会执行3，那么总共就会执行3 x 3次，也就是9次打印操作。\n\n除了正常使用循环之外，我们也可以在循环过程中提前终止或是加速循环的进行，这里我们需要认识两个新的关键字：\n\n```js\nfor (let i = 0; i \u003C 3; i++) {\n    if(i === 1) continue   \u002F\u002F比如我们希望当i等于1时跳过这一轮，不执行后面的打印，使用continue来跳过\n    console.log(\"当前i的值为：\"+i)\n}\n```\n\n![image-20260126160323986](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F26\u002Fdf869vIajZtcu7Y.png)\n\n我们可以使用`continue`关键字来跳过本轮循环，直接开启下一轮。这里的跳过是指，循环体中，无论后面有没有未执行的代码，一律不执行，比如上面的判断如果成功，那么将执行`continue`进行跳过，虽然后面还有打印语句，但是不会再去执行了，而是直接结束当前循环，开启下一轮。\n\n除了加速循环外，在某些情况下，我们可能希望提前结束循环：\n\n```js\nfor (let i = 0; i \u003C 3; i++) {\n    if(i === 1) break   \u002F\u002F我们希望当i等于1时提前结束，使用break直接结束循环\n    console.log(\"当前i的值为：\"+i)\n}\n```\n\n![image-20260126160534937](https:\u002F\u002Fs2.loli.net\u002F2026\u002F01\u002F26\u002FGKkqMWRyNz5tAja.png)\n\n我们可以使用`break`关键字来提前终止整个循环，和上面一样，本轮循环中无论后续还有没有未执行的代码，都不会执行了，而是直接结束整个循环，跳出到循环外部。\n\n虽然使用`break`和`continue`关键字能够更方便的控制循环，但是注意在多重循环嵌套下，它只对离它最近的循环生效（就近原则）：\n\n```js\nfor (let i = 0; i \u003C 3; i++) {\n    for (let j = 0; j \u003C 3; j++) {\n        if(i === j) break   \u002F\u002F当i == j时结束循环\n        console.log(`i = ${i}, j = ${j}`)\n    }\n}\n```\n\n可以看到，`break`结束的仅仅是内层循环，外层循环丝毫不受影响。那么，要是我们就是想要终止或者是加速外层循环呢？我们可以为循环语句打上标记：\n\n```js\nouter: for (let i = 0; i \u003C 3; i++) {\n    for (let j = 0; j \u003C 3; j++) {   \u002F\u002F相当于外层的每一轮循环中，还要先等待内层完成循环\n        if(i === j) break outer\n        console.log(`i = ${i}, j = ${j}`)\n    }\n}\n```\n\n如果一个代码块中存在多个循环，那么直接对当前代码块的标记执行`break`时会直接跳出整个外层循环。\n\n---\n\n前面我们介绍了for循环语句，我们接着来看第二种while循环，`for`循环要求我们填写三个表达式，而`while`相当于是一个简化版本，它只需要我们填写循环的维持条件即可：\n\n```js\nwhile(循环条件) 循环体;\n```\n\n相比`for`循环，`while`循环更多的用在不明确具体的结束时机的情况下，而`for`循环更多用于明确知道循环的情况，比如我们现在明确要进行循环10次，此时用for循环会更加合适一些，因为我们可以主动控制循环的起始和结束条件。而又比如我们现在只知道当`i`大于10时需要结束循环，但是`i`在循环多少次之后才不满足循环条件我们并不知道，此时使用while就比较合适了：\n\n```js\nlet i = 100\nwhile (i > 1) {   \u002F\u002F现在唯一知道的是循环条件，只要大于1那么就可以继续除\n    console.log(i);\n    i \u002F= 2;   \u002F\u002F每次循环都除以2\n}\n```\n\n上面的这种不明确具体执行多久的情况就非常适合使用`while`循环。\n\n在`for`循环中提到的`break`和`continue`在这里同样适用：\n\n```js\nlet i = 100\nwhile (i > 1) {\n    i \u002F= 2;   \u002F\u002F每次循环都除以2\n    if(i === 50) continue  \u002F\u002F等于50跳过后续直接下一轮\n    console.log(i);\n}\n```\n\n需要注意的是，在`while`循环中因为更新判断条件大部分都是在循环体内进行的，使用`continue`有可能导致无限循环，比如这种情况就会导致无限循环：\n\n```js\nlet i = 100\nwhile (i > 1) {\n    if(i === 50) continue\n    i \u002F= 2;   \u002F\u002F由于除以2的操作是在continue之后，这将导致i为50时永远无法继续被除，形成无限循环\n    console.log(i);\n}\n```\n\n除了正常使用之外，我们也可以反转循环判断的时机，可以先执行循环内容，然后再做循环条件判断，这里要用到`do-while`语句：\n\n```js\nlet i = 10\ndo {  \u002F\u002F无论满不满足循环条件，先执行循环体里面的内容\n    console.log(\"Hello World!\");\n    i++;\n} while (i \u003C 10) \n```\n\n可以看到，即使条件不满足，但是由于需要先执行循环体，所以无论如何都会执行一次。\n\n至此，面向过程相关的内容就讲解完毕了，从下一章开始，我们将进入函数和面向对象编程的学习。\n\n## 本章练习\n\n本章我们介绍了JavaScript的基本语法以及多种基本数据类型和流程控制语句，这一部分我们来做一些练习，对之前的内容进行加深巩固。\n\n### 寻找水仙花数\n\n>“水仙花数（Narcissistic number）也被称为超完全数字不变数（pluperfect digital invariant, PPDI）、自恋数、自幂数、阿姆斯壮数或阿姆斯特朗数（Armstrong number），水仙花数是指 **一个 3 位数，它的每个位上的数字的 3次幂之和等于它本身。**\n>\n>例如：$1^3 + 5^3+ 3^3 = 153$\n\n现在请你设计一个JavaScript程序，打印出所有1000以内的水仙花数。\n\n### 冒泡排序算法\n\n![img](https:\u002F\u002Fs2.loli.net\u002F2022\u002F09\u002F18\u002Fzy1wuvj6gfHmAZS.jpg)\n\n现在我们要做的是在我们的程序中，也打印出这样的一个乘法表出来，请你设计一个JavaScript程序来实现它。\n\n### 回文串判断\n\n> 回文串（Palindrome String）是指正读和反读都一样的字符串。简单来说，就是将字符串反转后，得到的结果和原字符串完全相同。\n\n- **中文回文串**：如 \"上海自来水来自海上\"、\"星星亮晶晶，晶晶亮星星\"（注意忽略空格或标点符号时）。\n- **英文回文串**：如 \"racecar\"（赛车）、\"level\"（水平）、\"madam\"（女士）。\n- **数字回文串**：如 \"12321\"、\"111\"。\n\n现在请你设计一个JavaScript程序来判断给定的字符串是否为回文串。\n\n### 判断小数或整数\n\n现在请你设计一个JavaScript程序来判断给定的数字是小数还是整数。\n\n### 选择题精选\n\n一、 基础概念与引入\n\n**1. JavaScript 是一种什么样的编程语言？** A. 编译型语言 B. 解释型语言 C. 机器语言 D. 汇编语言\n\n**2. 在 HTML 中引入外部 JavaScript 文件，正确的语法是？** A. `\u003Cscript link=\"main.js\">\u003C\u002Fscript>` B. `\u003Cscript href=\"main.js\">\u003C\u002Fscript>` C. `\u003Cscript src=\"main.js\">\u003C\u002Fscript>` D. `\u003Clink rel=\"script\" href=\"main.js\">`\n\n**3. JavaScript 中关于单行注释和多行注释，描述正确的是？** A. 单行注释使用 `#` B. 多行注释使用 `$` C. 单行注释使用 `\u002F\u002F`，多行注释使用 `\u002F* *\u002F` D. JavaScript 不支持多行注释\n\n二、 变量与数据类型\n\n**4. 使用 ES6 规范声明一个“只读常量”，应该使用哪个关键字？** A. var B. let C. const D. define\n\n**5. 下列哪个不是 JavaScript 的基本数据类型（原始类型）？** A. String B. Number C. Boolean D. Array\n\n**6. 执行 `console.log(typeof NaN)` 的输出结果是？** A. \"number\" B. \"NaN\" C. \"undefined\" D. \"object\"\n\n**7. 表达式 `10 + \"5\"` 的计算结果是？** A. 15 B. 50 C. \"15\" D. \"105\"\n\n三、 变量命名与规范\n\n**8. 以下哪个变量名是非法的？** A. `$price` B. `_index` C. `2nd_user` D. `userName`\n\n**9. 现代 JavaScript 开发中，不再推荐使用 `var` 声明变量的主要原因是？** A. `var` 声明的变量不可修改 B. `var` 存在变量提升且没有块级作用域 C. `var` 占用的内存比 `let` 大 D. `var` 只能存储数字类型\n\n四、 运算符与数学基础\n\n**10. 在 JavaScript 中，`1 \u002F 0` 的执行结果是？** A. 抛出异常并停止运行 B. NaN C. 0 D. Infinity\n\n**11. 关于“补码（Two's Complement）”，下列描述错误的是？** A. 计算机内部使用补码进行减法运算 B. 正数的补码与其原码相同 C. 补码解决了“正负0”的问题 D. 4位二进制补码能表示的最大正数是 8\n\n**12. 在 JavaScript 中，`console.log(0.1 + 0.2)`的执行结果是什么？**  A. 0.3  B.0  C.\"0.10.2\" D. 以上说法都不对\n\n五、 流程控制\n\n**13. 在 `switch` 语句中，用于防止“穿透（Fall-through）”执行的关键字是？** A. stop B. break C. continue D. default\n\n**14. `for` 循环的语法结构 `for(A; B; C)` 中，B 代表的是？** A. 循环初始值 B. 循环判跳条件 C. 循环后置更新语句 D. 循环体内容\n\n**15. 下列哪种循环结构能保证循环体内的代码至少被执行一次？** A. for 循环 B. while 循环 C. do-while 循环 D. if-else 语句\n\n**16. 在 `while` 循环中使用 `continue` 关键字时，若更新条件的语句位于 `continue` 之后，最可能导致的结果是？** A. 语法错误 B. 立即退出循环 C. 形成无限循环（死循环） D. 程序崩溃","欢迎各位小伙伴来到前端路线的JavaScript课程，这一站也是前端三件套（HTML、CSS、JavaScript）中的最后一个，期待在这最后的旅途中与各位小伙伴一起进步！视频中所有的文档、资料，都可以直接在视频下方简介中找到，视频非培训机构出品，纯个人录制，不需要加任何公众号、小程序，直接自取即可。","2026-01-27 19:01:07",{"data":468,"status":460,"success":461},[469,474],{"id":8,"image":470,"link":471,"name":472,"type":473},"\u002Fimage\u002Fadv\u002Frainyun-2025-06.webp","https:\u002F\u002Fwww.rainyun.com\u002Fitbaima_","雨云优惠购","cloud",{"id":66,"image":475,"link":476,"name":477,"type":478},"\u002Fimage\u002Fadv\u002Fsimcard-2025-11.webp","https:\u002F\u002Fmall.itbaima.cn","号卡优惠","simcard"]