123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744 |
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width,initial-scale=1">
- <title>函数的扩展 | 彪哥博客</title>
- <meta name="generator" content="VuePress 1.9.5">
- <link rel="icon" href="/blog/img/favicon.ico">
- <meta name="description" content="web前端技术博客,专注web前端学习与总结。JavaScript,js,ES6,TypeScript,vue,React,python,css3,html5,Node,git,github等技术文章。">
- <meta name="keywords" content="前端博客,个人技术博客,前端,前端开发,前端框架,web前端,前端面试题,技术文档,学习,面试,JavaScript,js,ES6,TypeScript,vue,python,css3,html5,Node,git,github,markdown">
- <meta name="theme-color" content="#11a8cd">
-
- <link rel="preload" href="/blog/assets/css/0.styles.dc03b589.css" as="style"><link rel="preload" href="/blog/assets/js/app.90754bd5.js" as="script"><link rel="preload" href="/blog/assets/js/2.106f41fb.js" as="script"><link rel="preload" href="/blog/assets/js/3.6748bd5c.js" as="script"><link rel="preload" href="/blog/assets/js/95.cddef6ae.js" as="script"><link rel="prefetch" href="/blog/assets/js/10.cad3aa70.js"><link rel="prefetch" href="/blog/assets/js/100.08a8b2d8.js"><link rel="prefetch" href="/blog/assets/js/101.2aabb12c.js"><link rel="prefetch" href="/blog/assets/js/102.13f3cc4d.js"><link rel="prefetch" href="/blog/assets/js/103.c19aee03.js"><link rel="prefetch" href="/blog/assets/js/104.91a6aec1.js"><link rel="prefetch" href="/blog/assets/js/105.71de1aa4.js"><link rel="prefetch" href="/blog/assets/js/106.207422de.js"><link rel="prefetch" href="/blog/assets/js/107.bf754f60.js"><link rel="prefetch" href="/blog/assets/js/108.87ddbf21.js"><link rel="prefetch" href="/blog/assets/js/109.de6075c6.js"><link rel="prefetch" href="/blog/assets/js/11.f2e9eca8.js"><link rel="prefetch" href="/blog/assets/js/110.f19c57ae.js"><link rel="prefetch" href="/blog/assets/js/111.da99a105.js"><link rel="prefetch" href="/blog/assets/js/112.9dd75c6f.js"><link rel="prefetch" href="/blog/assets/js/113.9322f157.js"><link rel="prefetch" href="/blog/assets/js/114.c5150cc6.js"><link rel="prefetch" href="/blog/assets/js/115.4fb547bf.js"><link rel="prefetch" href="/blog/assets/js/116.2ddf1aa8.js"><link rel="prefetch" href="/blog/assets/js/117.3c970f48.js"><link rel="prefetch" href="/blog/assets/js/118.921e1d54.js"><link rel="prefetch" href="/blog/assets/js/119.0141defb.js"><link rel="prefetch" href="/blog/assets/js/12.98512c60.js"><link rel="prefetch" href="/blog/assets/js/120.de47a761.js"><link rel="prefetch" href="/blog/assets/js/121.a0b3693a.js"><link rel="prefetch" href="/blog/assets/js/122.6c7dd225.js"><link rel="prefetch" href="/blog/assets/js/123.dbff103c.js"><link rel="prefetch" href="/blog/assets/js/124.493776ef.js"><link rel="prefetch" href="/blog/assets/js/125.554c9fbf.js"><link rel="prefetch" href="/blog/assets/js/126.2421fe84.js"><link rel="prefetch" href="/blog/assets/js/127.805f10a4.js"><link rel="prefetch" href="/blog/assets/js/128.6543adba.js"><link rel="prefetch" href="/blog/assets/js/129.d7c56b92.js"><link rel="prefetch" href="/blog/assets/js/13.a79fa0c7.js"><link rel="prefetch" href="/blog/assets/js/130.593d21f0.js"><link rel="prefetch" href="/blog/assets/js/131.4c90d8b8.js"><link rel="prefetch" href="/blog/assets/js/132.4ad12bdc.js"><link rel="prefetch" href="/blog/assets/js/133.485de1b9.js"><link rel="prefetch" href="/blog/assets/js/134.78bc8f57.js"><link rel="prefetch" href="/blog/assets/js/135.47498729.js"><link rel="prefetch" href="/blog/assets/js/136.d99350df.js"><link rel="prefetch" href="/blog/assets/js/137.57ba6d3f.js"><link rel="prefetch" href="/blog/assets/js/138.d976c801.js"><link rel="prefetch" href="/blog/assets/js/139.766f20b7.js"><link rel="prefetch" href="/blog/assets/js/14.0fdf0c78.js"><link rel="prefetch" href="/blog/assets/js/140.0d9b8fbc.js"><link rel="prefetch" href="/blog/assets/js/141.128b6e26.js"><link rel="prefetch" href="/blog/assets/js/142.e004a584.js"><link rel="prefetch" href="/blog/assets/js/143.5a19a0b2.js"><link rel="prefetch" href="/blog/assets/js/144.5f397211.js"><link rel="prefetch" href="/blog/assets/js/145.cde5f3a0.js"><link rel="prefetch" href="/blog/assets/js/146.4185092c.js"><link rel="prefetch" href="/blog/assets/js/147.ac0e55d3.js"><link rel="prefetch" href="/blog/assets/js/148.30f02604.js"><link rel="prefetch" href="/blog/assets/js/149.760a79ed.js"><link rel="prefetch" href="/blog/assets/js/15.fce722b2.js"><link rel="prefetch" href="/blog/assets/js/150.3a1675b7.js"><link rel="prefetch" href="/blog/assets/js/151.0d73a46e.js"><link rel="prefetch" href="/blog/assets/js/152.c9c054d4.js"><link rel="prefetch" href="/blog/assets/js/153.b007c8a2.js"><link rel="prefetch" href="/blog/assets/js/154.a3acc6bf.js"><link rel="prefetch" href="/blog/assets/js/155.b5c0abcd.js"><link rel="prefetch" href="/blog/assets/js/156.2ba750fc.js"><link rel="prefetch" href="/blog/assets/js/157.fe92af0c.js"><link rel="prefetch" href="/blog/assets/js/158.e16fda40.js"><link rel="prefetch" href="/blog/assets/js/159.7fc7fd44.js"><link rel="prefetch" href="/blog/assets/js/16.871928af.js"><link rel="prefetch" href="/blog/assets/js/160.b581a0f4.js"><link rel="prefetch" href="/blog/assets/js/161.97acae68.js"><link rel="prefetch" href="/blog/assets/js/162.cec1b9a3.js"><link rel="prefetch" href="/blog/assets/js/163.a00f98f7.js"><link rel="prefetch" href="/blog/assets/js/164.cbf4cf52.js"><link rel="prefetch" href="/blog/assets/js/165.bc7a523a.js"><link rel="prefetch" href="/blog/assets/js/166.ca626fb4.js"><link rel="prefetch" href="/blog/assets/js/167.0ca68106.js"><link rel="prefetch" href="/blog/assets/js/168.2e605db8.js"><link rel="prefetch" href="/blog/assets/js/169.401b96d4.js"><link rel="prefetch" href="/blog/assets/js/17.2399cb2b.js"><link rel="prefetch" href="/blog/assets/js/170.7c8b0366.js"><link rel="prefetch" href="/blog/assets/js/171.c3155533.js"><link rel="prefetch" href="/blog/assets/js/172.b659d767.js"><link rel="prefetch" href="/blog/assets/js/173.62c681db.js"><link rel="prefetch" href="/blog/assets/js/174.5c66f092.js"><link rel="prefetch" href="/blog/assets/js/175.d41dd28b.js"><link rel="prefetch" href="/blog/assets/js/176.e60d7f0a.js"><link rel="prefetch" href="/blog/assets/js/177.10de95b1.js"><link rel="prefetch" href="/blog/assets/js/178.f301674d.js"><link rel="prefetch" href="/blog/assets/js/179.77bb52e9.js"><link rel="prefetch" href="/blog/assets/js/18.c338fe95.js"><link rel="prefetch" href="/blog/assets/js/180.d2a4e612.js"><link rel="prefetch" href="/blog/assets/js/181.a53e32e0.js"><link rel="prefetch" href="/blog/assets/js/182.38687994.js"><link rel="prefetch" href="/blog/assets/js/183.544fef00.js"><link rel="prefetch" href="/blog/assets/js/184.711e54a6.js"><link rel="prefetch" href="/blog/assets/js/185.20075148.js"><link rel="prefetch" href="/blog/assets/js/186.08c67f20.js"><link rel="prefetch" href="/blog/assets/js/187.7ca2d0c6.js"><link rel="prefetch" href="/blog/assets/js/188.cd167879.js"><link rel="prefetch" href="/blog/assets/js/189.e8e2eb21.js"><link rel="prefetch" href="/blog/assets/js/19.6b963460.js"><link rel="prefetch" href="/blog/assets/js/190.8b557318.js"><link rel="prefetch" href="/blog/assets/js/191.10d0f80b.js"><link rel="prefetch" href="/blog/assets/js/192.127fef4c.js"><link rel="prefetch" href="/blog/assets/js/193.781690eb.js"><link rel="prefetch" href="/blog/assets/js/194.4b375e2e.js"><link rel="prefetch" href="/blog/assets/js/195.003e3d67.js"><link rel="prefetch" href="/blog/assets/js/196.7a3f55e5.js"><link rel="prefetch" href="/blog/assets/js/197.30d4c5b4.js"><link rel="prefetch" href="/blog/assets/js/198.628c2c1a.js"><link rel="prefetch" href="/blog/assets/js/199.d7c8cbb8.js"><link rel="prefetch" href="/blog/assets/js/20.839dae41.js"><link rel="prefetch" href="/blog/assets/js/200.1fed86d2.js"><link rel="prefetch" href="/blog/assets/js/201.5ba078d9.js"><link rel="prefetch" href="/blog/assets/js/202.b49b23f4.js"><link rel="prefetch" href="/blog/assets/js/203.d88a03d9.js"><link rel="prefetch" href="/blog/assets/js/204.fb928277.js"><link rel="prefetch" href="/blog/assets/js/205.432c3d8d.js"><link rel="prefetch" href="/blog/assets/js/206.ed726599.js"><link rel="prefetch" href="/blog/assets/js/207.c3794556.js"><link rel="prefetch" href="/blog/assets/js/208.416f7a9e.js"><link rel="prefetch" href="/blog/assets/js/209.d396aad5.js"><link rel="prefetch" href="/blog/assets/js/21.bd21bd29.js"><link rel="prefetch" href="/blog/assets/js/210.02e4ee2f.js"><link rel="prefetch" href="/blog/assets/js/211.0f8a9304.js"><link rel="prefetch" href="/blog/assets/js/212.9220dd3d.js"><link rel="prefetch" href="/blog/assets/js/213.89521ebd.js"><link rel="prefetch" href="/blog/assets/js/214.cd5ec468.js"><link rel="prefetch" href="/blog/assets/js/215.0084d772.js"><link rel="prefetch" href="/blog/assets/js/216.ebd468b9.js"><link rel="prefetch" href="/blog/assets/js/217.fac21407.js"><link rel="prefetch" href="/blog/assets/js/218.9793e19a.js"><link rel="prefetch" href="/blog/assets/js/219.b99343b6.js"><link rel="prefetch" href="/blog/assets/js/22.e6bcf65d.js"><link rel="prefetch" href="/blog/assets/js/220.8065033b.js"><link rel="prefetch" href="/blog/assets/js/221.97268a80.js"><link rel="prefetch" href="/blog/assets/js/222.22bf261f.js"><link rel="prefetch" href="/blog/assets/js/223.d5ff1bff.js"><link rel="prefetch" href="/blog/assets/js/224.364b4b11.js"><link rel="prefetch" href="/blog/assets/js/225.bd8b00c7.js"><link rel="prefetch" href="/blog/assets/js/226.78192713.js"><link rel="prefetch" href="/blog/assets/js/227.06ec006e.js"><link rel="prefetch" href="/blog/assets/js/228.166546a8.js"><link rel="prefetch" href="/blog/assets/js/229.fd44d2be.js"><link rel="prefetch" href="/blog/assets/js/23.ebbc3fd3.js"><link rel="prefetch" href="/blog/assets/js/230.109a1752.js"><link rel="prefetch" href="/blog/assets/js/231.c6faedb6.js"><link rel="prefetch" href="/blog/assets/js/232.f938a3a8.js"><link rel="prefetch" href="/blog/assets/js/233.0dc59324.js"><link rel="prefetch" href="/blog/assets/js/234.bb73adca.js"><link rel="prefetch" href="/blog/assets/js/235.97ed69db.js"><link rel="prefetch" href="/blog/assets/js/236.a8c51930.js"><link rel="prefetch" href="/blog/assets/js/237.6ae31c88.js"><link rel="prefetch" href="/blog/assets/js/238.2f5c56ae.js"><link rel="prefetch" href="/blog/assets/js/239.506a4e9f.js"><link rel="prefetch" href="/blog/assets/js/24.19bd04ec.js"><link rel="prefetch" href="/blog/assets/js/25.b4de33d1.js"><link rel="prefetch" href="/blog/assets/js/26.0bb98ba9.js"><link rel="prefetch" href="/blog/assets/js/27.df98327e.js"><link rel="prefetch" href="/blog/assets/js/28.31289bac.js"><link rel="prefetch" href="/blog/assets/js/29.45af5621.js"><link rel="prefetch" href="/blog/assets/js/30.d5c08e66.js"><link rel="prefetch" href="/blog/assets/js/31.78e43a68.js"><link rel="prefetch" href="/blog/assets/js/32.53ca76ee.js"><link rel="prefetch" href="/blog/assets/js/33.081e8ef6.js"><link rel="prefetch" href="/blog/assets/js/34.cb1866c1.js"><link rel="prefetch" href="/blog/assets/js/35.314ba98e.js"><link rel="prefetch" href="/blog/assets/js/36.1f6a5fae.js"><link rel="prefetch" href="/blog/assets/js/37.45e6d22f.js"><link rel="prefetch" href="/blog/assets/js/38.70b82353.js"><link rel="prefetch" href="/blog/assets/js/39.df6c26ac.js"><link rel="prefetch" href="/blog/assets/js/4.44654b1a.js"><link rel="prefetch" href="/blog/assets/js/40.80101c19.js"><link rel="prefetch" href="/blog/assets/js/41.2b5e8c27.js"><link rel="prefetch" href="/blog/assets/js/42.c6ded3fe.js"><link rel="prefetch" href="/blog/assets/js/43.6d9424d6.js"><link rel="prefetch" href="/blog/assets/js/44.835e4b5c.js"><link rel="prefetch" href="/blog/assets/js/45.d74d29a2.js"><link rel="prefetch" href="/blog/assets/js/46.d15a7dc0.js"><link rel="prefetch" href="/blog/assets/js/47.8d66ca97.js"><link rel="prefetch" href="/blog/assets/js/48.3c1102e1.js"><link rel="prefetch" href="/blog/assets/js/49.e17a3436.js"><link rel="prefetch" href="/blog/assets/js/5.88de390f.js"><link rel="prefetch" href="/blog/assets/js/50.6750f186.js"><link rel="prefetch" href="/blog/assets/js/51.9f93af9f.js"><link rel="prefetch" href="/blog/assets/js/52.f3ef3b5e.js"><link rel="prefetch" href="/blog/assets/js/53.a6bacd25.js"><link rel="prefetch" href="/blog/assets/js/54.dbb7c9ab.js"><link rel="prefetch" href="/blog/assets/js/55.2562d0c8.js"><link rel="prefetch" href="/blog/assets/js/56.14ea4931.js"><link rel="prefetch" href="/blog/assets/js/57.a2fad780.js"><link rel="prefetch" href="/blog/assets/js/58.8165b971.js"><link rel="prefetch" href="/blog/assets/js/59.556cab0d.js"><link rel="prefetch" href="/blog/assets/js/6.277038ca.js"><link rel="prefetch" href="/blog/assets/js/60.f048aa7c.js"><link rel="prefetch" href="/blog/assets/js/61.bdb307a8.js"><link rel="prefetch" href="/blog/assets/js/62.37a94f10.js"><link rel="prefetch" href="/blog/assets/js/63.74811780.js"><link rel="prefetch" href="/blog/assets/js/64.81f21b8a.js"><link rel="prefetch" href="/blog/assets/js/65.d970ff03.js"><link rel="prefetch" href="/blog/assets/js/66.cb805d9b.js"><link rel="prefetch" href="/blog/assets/js/67.39f85baa.js"><link rel="prefetch" href="/blog/assets/js/68.7f79766a.js"><link rel="prefetch" href="/blog/assets/js/69.fa8624bd.js"><link rel="prefetch" href="/blog/assets/js/7.e0a6d1b0.js"><link rel="prefetch" href="/blog/assets/js/70.1f3e978d.js"><link rel="prefetch" href="/blog/assets/js/71.13cd9358.js"><link rel="prefetch" href="/blog/assets/js/72.739b22a8.js"><link rel="prefetch" href="/blog/assets/js/73.95f69ae2.js"><link rel="prefetch" href="/blog/assets/js/74.b6624f6a.js"><link rel="prefetch" href="/blog/assets/js/75.b0d9aa06.js"><link rel="prefetch" href="/blog/assets/js/76.681b78df.js"><link rel="prefetch" href="/blog/assets/js/77.46f6e413.js"><link rel="prefetch" href="/blog/assets/js/78.aebd00ee.js"><link rel="prefetch" href="/blog/assets/js/79.1b784d15.js"><link rel="prefetch" href="/blog/assets/js/8.9428e7ee.js"><link rel="prefetch" href="/blog/assets/js/80.1f550d53.js"><link rel="prefetch" href="/blog/assets/js/81.101cc131.js"><link rel="prefetch" href="/blog/assets/js/82.077c8298.js"><link rel="prefetch" href="/blog/assets/js/83.2e375d11.js"><link rel="prefetch" href="/blog/assets/js/84.38102a34.js"><link rel="prefetch" href="/blog/assets/js/85.24532d6a.js"><link rel="prefetch" href="/blog/assets/js/86.1dabbf00.js"><link rel="prefetch" href="/blog/assets/js/87.763da0f2.js"><link rel="prefetch" href="/blog/assets/js/88.ff6e5f7c.js"><link rel="prefetch" href="/blog/assets/js/89.187e5e16.js"><link rel="prefetch" href="/blog/assets/js/9.da143545.js"><link rel="prefetch" href="/blog/assets/js/90.3c8cff94.js"><link rel="prefetch" href="/blog/assets/js/91.a50bd44d.js"><link rel="prefetch" href="/blog/assets/js/92.5484868f.js"><link rel="prefetch" href="/blog/assets/js/93.c8ee75e3.js"><link rel="prefetch" href="/blog/assets/js/94.b18a3e9b.js"><link rel="prefetch" href="/blog/assets/js/96.80e5a938.js"><link rel="prefetch" href="/blog/assets/js/97.1f5e5197.js"><link rel="prefetch" href="/blog/assets/js/98.e3a275c8.js"><link rel="prefetch" href="/blog/assets/js/99.d33bf89e.js">
- <link rel="stylesheet" href="/blog/assets/css/0.styles.dc03b589.css">
- </head>
- <body class="theme-mode-light">
- <div id="app" data-server-rendered="true"><div class="theme-container sidebar-open have-rightmenu"><header class="navbar blur"><div title="目录" class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/blog/" class="home-link router-link-active"><img src="/blog/img/logo.png" alt="彪哥博客" class="logo"> <span class="site-name can-hide">彪哥博客</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/blog/" class="nav-link">首页</a></div><div class="nav-item"><a href="http://fseller.com" target="_blank" rel="noopener noreferrer" class="nav-link external">
- 个人游戏网站
- <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="前端" class="dropdown-title"><a href="/blog/web/" class="link-title">前端</a> <span class="title" style="display:none;">前端</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>前端文章</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blog/pages/8143cc480faf9a11/" class="nav-link">JavaScript</a></li></ul></li><li class="dropdown-item"><h4>学习笔记</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blog/note/javascript/" class="nav-link">《JavaScript教程》</a></li><li class="dropdown-subitem"><a href="/blog/note/js/" class="nav-link">《JavaScript高级程序设计》</a></li><li class="dropdown-subitem"><a href="/blog/note/es6/" class="nav-link">《ES6 教程》</a></li><li class="dropdown-subitem"><a href="/blog/note/vue/" class="nav-link">《Vue》</a></li><li class="dropdown-subitem"><a href="/blog/note/react/" class="nav-link">《React》</a></li><li class="dropdown-subitem"><a href="/blog/note/typescript-axios/" class="nav-link">《TypeScript 从零实现 axios》</a></li><li class="dropdown-subitem"><a href="/blog/note/git/" class="nav-link">《Git》</a></li><li class="dropdown-subitem"><a href="/blog/pages/51afd6/" class="nav-link">TypeScript</a></li><li class="dropdown-subitem"><a href="/blog/pages/4643cd/" class="nav-link">JS设计模式总结</a></li></ul></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="页面" class="dropdown-title"><a href="/blog/ui/" class="link-title">页面</a> <span class="title" style="display:none;">页面</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/pages/8309a5b876fc95e3/" class="nav-link">HTML</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/0a83b083bdf257cb/" class="nav-link">CSS</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="技术" class="dropdown-title"><a href="/blog/technology/" class="link-title">技术</a> <span class="title" style="display:none;">技术</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/pages/9a7ee40fc232253e/" class="nav-link">技术文档</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/4c778760be26d8b3/" class="nav-link">GitHub技巧</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/117708e0af7f0bd9/" class="nav-link">Nodejs</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/41f87d890d0a02af/" class="nav-link">博客搭建</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="更多" class="dropdown-title"><a href="/blog/more/" class="link-title">更多</a> <span class="title" style="display:none;">更多</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/pages/f2a556/" class="nav-link">学习</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/aea6571b7a8bae86/" class="nav-link">面试</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/2d615df9a36a98ed/" class="nav-link">心情杂货</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/baaa02/" class="nav-link">实用技巧</a></li><li class="dropdown-item"><!----> <a href="/blog/friends/" class="nav-link">友情链接</a></li></ul></div></div><div class="nav-item"><a href="/blog/about/" class="nav-link">关于</a></div><div class="nav-item"><a href="/blog/pages/beb6c0bd8a66cea6/" class="nav-link">收藏</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="索引" class="dropdown-title"><a href="/blog/archives/" class="link-title">索引</a> <span class="title" style="display:none;">索引</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/categories/" class="nav-link">分类</a></li><li class="dropdown-item"><!----> <a href="/blog/tags/" class="nav-link">标签</a></li><li class="dropdown-item"><!----> <a href="/blog/archives/" class="nav-link">归档</a></li></ul></div></div> <a href="https://github.com/heBody/blog" target="_blank" rel="noopener noreferrer" class="repo-link">
- GitHub
- <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <div class="sidebar-hover-trigger"></div> <aside class="sidebar" style="display:none;"><div class="blogger"><img src="/blog/img/head.jpg"> <div class="blogger-info"><h3>彪哥</h3> <span>爱好前端</span></div></div> <nav class="nav-links"><div class="nav-item"><a href="/blog/" class="nav-link">首页</a></div><div class="nav-item"><a href="http://fseller.com" target="_blank" rel="noopener noreferrer" class="nav-link external">
- 个人游戏网站
- <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="前端" class="dropdown-title"><a href="/blog/web/" class="link-title">前端</a> <span class="title" style="display:none;">前端</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><h4>前端文章</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blog/pages/8143cc480faf9a11/" class="nav-link">JavaScript</a></li></ul></li><li class="dropdown-item"><h4>学习笔记</h4> <ul class="dropdown-subitem-wrapper"><li class="dropdown-subitem"><a href="/blog/note/javascript/" class="nav-link">《JavaScript教程》</a></li><li class="dropdown-subitem"><a href="/blog/note/js/" class="nav-link">《JavaScript高级程序设计》</a></li><li class="dropdown-subitem"><a href="/blog/note/es6/" class="nav-link">《ES6 教程》</a></li><li class="dropdown-subitem"><a href="/blog/note/vue/" class="nav-link">《Vue》</a></li><li class="dropdown-subitem"><a href="/blog/note/react/" class="nav-link">《React》</a></li><li class="dropdown-subitem"><a href="/blog/note/typescript-axios/" class="nav-link">《TypeScript 从零实现 axios》</a></li><li class="dropdown-subitem"><a href="/blog/note/git/" class="nav-link">《Git》</a></li><li class="dropdown-subitem"><a href="/blog/pages/51afd6/" class="nav-link">TypeScript</a></li><li class="dropdown-subitem"><a href="/blog/pages/4643cd/" class="nav-link">JS设计模式总结</a></li></ul></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="页面" class="dropdown-title"><a href="/blog/ui/" class="link-title">页面</a> <span class="title" style="display:none;">页面</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/pages/8309a5b876fc95e3/" class="nav-link">HTML</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/0a83b083bdf257cb/" class="nav-link">CSS</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="技术" class="dropdown-title"><a href="/blog/technology/" class="link-title">技术</a> <span class="title" style="display:none;">技术</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/pages/9a7ee40fc232253e/" class="nav-link">技术文档</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/4c778760be26d8b3/" class="nav-link">GitHub技巧</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/117708e0af7f0bd9/" class="nav-link">Nodejs</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/41f87d890d0a02af/" class="nav-link">博客搭建</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="更多" class="dropdown-title"><a href="/blog/more/" class="link-title">更多</a> <span class="title" style="display:none;">更多</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/pages/f2a556/" class="nav-link">学习</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/aea6571b7a8bae86/" class="nav-link">面试</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/2d615df9a36a98ed/" class="nav-link">心情杂货</a></li><li class="dropdown-item"><!----> <a href="/blog/pages/baaa02/" class="nav-link">实用技巧</a></li><li class="dropdown-item"><!----> <a href="/blog/friends/" class="nav-link">友情链接</a></li></ul></div></div><div class="nav-item"><a href="/blog/about/" class="nav-link">关于</a></div><div class="nav-item"><a href="/blog/pages/beb6c0bd8a66cea6/" class="nav-link">收藏</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="索引" class="dropdown-title"><a href="/blog/archives/" class="link-title">索引</a> <span class="title" style="display:none;">索引</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/categories/" class="nav-link">分类</a></li><li class="dropdown-item"><!----> <a href="/blog/tags/" class="nav-link">标签</a></li><li class="dropdown-item"><!----> <a href="/blog/archives/" class="nav-link">归档</a></li></ul></div></div> <a href="https://github.com/heBody/blog" target="_blank" rel="noopener noreferrer" class="repo-link">
- GitHub
- <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav> <ul class="sidebar-links"><li><a href="/blog/pages/f344d070a1031ef7/" class="sidebar-link">ECMAScript 6 简介</a></li><li><a href="/blog/pages/c1edd70a6b7c7872/" class="sidebar-link">let 和 const 命令</a></li><li><a href="/blog/pages/b1ab10a62f7564da/" class="sidebar-link">变量的解构赋值</a></li><li><a href="/blog/pages/ca89eca8adeba5f4/" class="sidebar-link">字符串的扩展</a></li><li><a href="/blog/pages/a650b4a0ebfc9350/" class="sidebar-link">字符串的新增方法</a></li><li><a href="/blog/pages/0473261a6ab0ee8c/" class="sidebar-link">正则的扩展</a></li><li><a href="/blog/pages/5dfea9a0f2d1a392/" class="sidebar-link">数值的扩展</a></li><li><a href="/blog/pages/8ed309d668b20264/" aria-current="page" class="active sidebar-link">函数的扩展</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level2"><a href="/blog/pages/8ed309d668b20264/#函数参数的默认值" class="sidebar-link">函数参数的默认值</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#基本用法" class="sidebar-link">基本用法</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#与解构赋值默认值结合使用" class="sidebar-link">与解构赋值默认值结合使用</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#参数默认值的位置" class="sidebar-link">参数默认值的位置</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#函数的-length-属性" class="sidebar-link">函数的 length 属性</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#作用域" class="sidebar-link">作用域</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#应用" class="sidebar-link">应用</a></li></ul></li><li class="sidebar-sub-header level2"><a href="/blog/pages/8ed309d668b20264/#rest-参数" class="sidebar-link">rest 参数</a></li><li class="sidebar-sub-header level2"><a href="/blog/pages/8ed309d668b20264/#严格模式" class="sidebar-link">严格模式</a></li><li class="sidebar-sub-header level2"><a href="/blog/pages/8ed309d668b20264/#name-属性" class="sidebar-link">name 属性</a></li><li class="sidebar-sub-header level2"><a href="/blog/pages/8ed309d668b20264/#箭头函数" class="sidebar-link">箭头函数</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#基本用法-2" class="sidebar-link">基本用法</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#使用注意点" class="sidebar-link">使用注意点</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#不适用场合" class="sidebar-link">不适用场合</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#嵌套的箭头函数" class="sidebar-link">嵌套的箭头函数</a></li></ul></li><li class="sidebar-sub-header level2"><a href="/blog/pages/8ed309d668b20264/#尾调用优化" class="sidebar-link">尾调用优化</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#什么是尾调用" class="sidebar-link">什么是尾调用?</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#尾调用优化-2" class="sidebar-link">尾调用优化</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#尾递归" class="sidebar-link">尾递归</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#递归函数的改写" class="sidebar-link">递归函数的改写</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#严格模式-2" class="sidebar-link">严格模式</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/8ed309d668b20264/#尾递归优化的实现" class="sidebar-link">尾递归优化的实现</a></li></ul></li><li class="sidebar-sub-header level2"><a href="/blog/pages/8ed309d668b20264/#函数参数的尾逗号" class="sidebar-link">函数参数的尾逗号</a></li><li class="sidebar-sub-header level2"><a href="/blog/pages/8ed309d668b20264/#function-prototype-tostring" class="sidebar-link">Function.prototype.toString()</a></li><li class="sidebar-sub-header level2"><a href="/blog/pages/8ed309d668b20264/#catch-命令的参数省略" class="sidebar-link">catch 命令的参数省略</a></li></ul></li><li><a href="/blog/pages/e34009d60d8bc4b2/" class="sidebar-link">数组的扩展</a></li><li><a href="/blog/pages/b5e3e0a0ff6e9c25/" class="sidebar-link">对象的扩展</a></li><li><a href="/blog/pages/e85e68947502cf90/" class="sidebar-link">对象的新增方法</a></li><li><a href="/blog/pages/02c86eb2792f3262/" class="sidebar-link">Symbol</a></li><li><a href="/blog/pages/0c21dae358fca16b/" class="sidebar-link">Set 和 Map 数据结构</a></li><li><a href="/blog/pages/f56ec2ab97d60483/" class="sidebar-link">Proxy</a></li><li><a href="/blog/pages/74de3e45e4491e95/" class="sidebar-link">Reflect</a></li><li><a href="/blog/pages/2810ae8985e9bd52/" class="sidebar-link">Promise 对象</a></li><li><a href="/blog/pages/48df907ad3570f3d/" class="sidebar-link">Iterator 和 for-of 循环</a></li><li><a href="/blog/pages/718b48ed9ce0adce/" class="sidebar-link">Generator 函数的语法</a></li><li><a href="/blog/pages/75af7031eb66847b/" class="sidebar-link">Generator 函数的异步应用</a></li><li><a href="/blog/pages/3777253e65bac487/" class="sidebar-link">async 函数</a></li><li><a href="/blog/pages/e831e1593c82bbe0/" class="sidebar-link">Class 的基本语法</a></li><li><a href="/blog/pages/83f8c3a0cd87dd83/" class="sidebar-link">Class 的继承</a></li><li><a href="/blog/pages/efe2fb04eb8ac5fb/" class="sidebar-link">Module 的语法</a></li><li><a href="/blog/pages/a79ca2e64ceae213/" class="sidebar-link">Module 的加载实现</a></li><li><a href="/blog/pages/984bf549204bb266/" class="sidebar-link">编程风格</a></li><li><a href="/blog/pages/32c35f7651d6e58e/" class="sidebar-link">读懂 ECMAScript 规格</a></li><li><a href="/blog/pages/16121351be68691b/" class="sidebar-link">异步遍历器</a></li><li><a href="/blog/pages/a2ba314746bfdbdd/" class="sidebar-link">ArrayBuffer</a></li><li><a href="/blog/pages/7188882b8d65af1b/" class="sidebar-link">最新提案</a></li><li><a href="/blog/pages/e97bc1e5626b082c/" class="sidebar-link">装饰器</a></li><li><a href="/blog/pages/1cf50330655efc69/" class="sidebar-link">函数式编程</a></li><li><a href="/blog/pages/6a8e2dc558da1b39/" class="sidebar-link">Mixin</a></li><li><a href="/blog/pages/8e8f80f69b775a56/" class="sidebar-link">SIMD</a></li><li><a href="/blog/pages/ea6f3b870f6dab69/" class="sidebar-link">参考链接</a></li></ul> </aside> <div><main class="page"><div class="theme-vdoing-wrapper "><div class="articleInfo-wrap" data-v-06970110><div class="articleInfo" data-v-06970110><ul class="breadcrumbs" data-v-06970110><li data-v-06970110><a href="/blog/" title="首页" class="iconfont icon-home router-link-active" data-v-06970110></a></li> <li data-v-06970110><a href="/blog/note/es6/#《ES6 教程》笔记" data-v-06970110>《ES6 教程》笔记</a></li></ul> <div class="info" data-v-06970110><div title="作者" class="author iconfont icon-touxiang" data-v-06970110><a href="javascript:;" data-v-06970110>阮一峰</a></div> <div title="创建时间" class="date iconfont icon-riqi" data-v-06970110><a href="javascript:;" data-v-06970110>2020-02-09</a></div> <!----></div></div></div> <!----> <div class="content-wrapper"><div class="right-menu-wrapper"><div class="right-menu-margin"><div class="right-menu-title">目录</div> <div class="right-menu-content"></div></div></div> <h1><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAABGpJREFUSA3tVVtoXFUU3fvOI53UlmCaKIFmwEhsE7QK0ipFEdHEKpXaZGrp15SINsXUWvBDpBgQRKi0+KKoFeJHfZA+ED9KKoIU2gYD9UejTW4rVIzm0VSTziPzuNu1z507dibTTjL4U/DAzLn3nL3X2o91ziX6f9wMFdh6Jvbm9nNSV0msViVO6tN1Rm7NMu2OpeJ9lWBUTDxrJbYTS0hInuwciu9eLHlFxCLCZEk3MegsJmZ5K/JD6t7FkFdEvGUo1g7qJoG3MHImqRIn8/nzY1K9UPKKiJmtnUqHVE3Gbuay6vJE/N2FEmuxFjW2nUuE0yQXRRxLiTUAzs36zhZvOXJPdX850EVnnLZkB8prodQoM5JGj7Xk2mvC7JB8tG04Ef5PiXtG0UtxupRQSfTnBoCy554x18yJHI6I+G5Eru4LHmPJZEQsrvPUbMiA8G/WgMK7w7I+ez7++o2ANfbrjvaOl1tFMs+htG3IrZH9/hDX1Pr8Tc0UvH8tcX29KzAgIGcEkINyW5BF9x891hw6VYqgJHEk0huccS7vh3C6gTiODL+26huuBtbct8eZnqLML8PkxGYpuPZBqtqwkSjgc4mB5gbgig5i+y0UDK35LMxXisn9xQtK+nd26gTIHsHe/oblK/b29fUmN/8Y+9jAQrnBp56m1LcDlDp9irKTExSKduXJVWSqdBMA08pEJnEIOB3FPPMybu/oeV8zFeYN3xx576Q6RH+VmplE4ncQV5v+5rzSoyOU7PuEAg8g803PwBJ0CExno/jcMbN8tONYeOmHiuUNryvm3fRUy4tMPVLdAGkUhNWuggGrJcXPv+ouCjz0MKUHz1J2/E8IC9nqTabcxgaBYM0hPhD5Y65FsbxRQKxCQrDjDctW7PUM3HuZunFyifSAqEfuzCp48Il24luWUWZoyJCaPR82jE0+kFA643wRFVni4RYSq3ohJO2pZ7B5dO4xkDWbEpossJPLSrPjYID8rS2UHTlvyNxqIGsg674XJJ7vnh5L7PNwC4hh2sjCI96mzszOTpxLF0T7l88Yz7lAuK6OnL8gXLOnTvpzSb22YG8W7us3jSebFHeeqnXRG1vt+MoUM84LQIBmMsCTAcOauTh0T0l0neQK7m2bLMt2mGxU3HYssS0J2cdv5wljlPsrIuZLAG/2DOZIXgCYT8uMGZN+e2kSirfxZOPCsC0f24nTZzspnVn9VePS1Z5vubmAGGXG8ZFno9Hel0yfA5ZPhF7Dh972BQJ2qCpgH67lmWtBYbvk6sz02wjky2vXyz0XErP/kFB619js1BtwfOV4OPRqOQBjy3Qbk18vigUPPSD5ceHnwck7W9bhAqZdd7SuG7w4/P2F/GaJh8c7e9qgow+Q7cGBo+98WsLkuktFqiZabtXuQTu/Y5ETbR0v7tNSFnvrmu6pjdoan2KjMu8q/Hmj1EfCO2ZGfEIbIXKUlw8qaX9/b2oeSJmFksSeT/Fn0V3nSypChh4Gjh74ybO9aeZ/AN2dwciu2/MhAAAAAElFTkSuQmCC">函数的扩展<!----></h1> <div class="theme-vdoing-content content__default"><h1 id="函数的扩展"><a href="#函数的扩展" class="header-anchor">#</a> 函数的扩展</h1> <h2 id="函数参数的默认值"><a href="#函数参数的默认值" class="header-anchor">#</a> 函数参数的默认值</h2> <h3 id="基本用法"><a href="#基本用法" class="header-anchor">#</a> 基本用法</h3> <p>ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">log</span><span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> y</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- y <span class="token operator">=</span> y <span class="token operator">||</span> <span class="token string">'World'</span><span class="token punctuation">;</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Hello'</span><span class="token punctuation">)</span> <span class="token comment">// Hello World</span>
- <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Hello'</span><span class="token punctuation">,</span> <span class="token string">'China'</span><span class="token punctuation">)</span> <span class="token comment">// Hello China</span>
- <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Hello'</span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">)</span> <span class="token comment">// Hello World</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div> <p>上面代码检查函数<code>log</code>的参数<code>y</code>有没有赋值,如果没有,则指定默认值为<code>World</code>。这种写法的缺点在于,如果参数<code>y</code>赋值了,但是对应的布尔值为<code>false</code>,则该赋值不起作用。就像上面代码的最后一行,参数<code>y</code>等于空字符,结果被改为默认值。</p> <p>为了避免这个问题,通常需要先判断一下参数<code>y</code>是否被赋值,如果没有,再等于默认值。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> y <span class="token operator">===</span> <span class="token string">'undefined'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- y <span class="token operator">=</span> <span class="token string">'World'</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">log</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y <span class="token operator">=</span> <span class="token string">'World'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Hello'</span><span class="token punctuation">)</span> <span class="token comment">// Hello World</span>
- <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Hello'</span><span class="token punctuation">,</span> <span class="token string">'China'</span><span class="token punctuation">)</span> <span class="token comment">// Hello China</span>
- <span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Hello'</span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">)</span> <span class="token comment">// Hello</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>可以看到,ES6 的写法比 ES5 简洁许多,而且非常自然。下面是另一个例子。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">Point</span><span class="token punctuation">(</span><span class="token parameter">x <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> y <span class="token operator">=</span> <span class="token number">0</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">this</span><span class="token punctuation">.</span>x <span class="token operator">=</span> x<span class="token punctuation">;</span>
- <span class="token keyword">this</span><span class="token punctuation">.</span>y <span class="token operator">=</span> y<span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">const</span> p <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Point</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- p <span class="token comment">// { x: 0, y: 0 }</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>除了简洁,ES6 的写法还有两个好处:首先,阅读代码的人,可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档;其次,有利于将来的代码优化,即使未来的版本在对外接口中,彻底拿掉这个参数,也不会导致以前的代码无法运行。</p> <p>参数变量是默认声明的,所以不能用<code>let</code>或<code>const</code>再次声明。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token parameter">x <span class="token operator">=</span> <span class="token number">5</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">let</span> x <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">// error</span>
- <span class="token keyword">const</span> x <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token comment">// error</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>上面代码中,参数变量<code>x</code>是默认声明的,在函数体中,不能用<code>let</code>或<code>const</code>再次声明,否则会报错。</p> <p>使用参数默认值时,函数不能有同名参数。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// 不报错</span>
- <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> x<span class="token punctuation">,</span> y</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token comment">// ...</span>
- <span class="token punctuation">}</span>
- <span class="token comment">// 报错</span>
- <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> x<span class="token punctuation">,</span> y <span class="token operator">=</span> <span class="token number">1</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token comment">// ...</span>
- <span class="token punctuation">}</span>
- <span class="token comment">// SyntaxError: Duplicate parameter name not allowed in this context</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>另外,一个容易忽略的地方是,参数默认值不是传值的,而是<strong>每次都重新计算默认值表达式的值</strong>。也就是说,参数默认值是<strong>惰性求值</strong>的。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">let</span> x <span class="token operator">=</span> <span class="token number">99</span><span class="token punctuation">;</span>
- <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token parameter">p <span class="token operator">=</span> x <span class="token operator">+</span> <span class="token number">1</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 100</span>
- x <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">;</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 101</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>上面代码中,参数<code>p</code>的默认值是<code>x + 1</code>。这时,每次调用函数<code>foo</code>,都会重新计算<code>x + 1</code>,而不是默认<code>p</code>等于 100。</p> <h3 id="与解构赋值默认值结合使用"><a href="#与解构赋值默认值结合使用" class="header-anchor">#</a> 与解构赋值默认值结合使用</h3> <p>参数默认值可以与解构赋值的默认值,结合起来使用。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>x<span class="token punctuation">,</span> y <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// undefined 5</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">x</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// 1 5</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">x</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token literal-property property">y</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// 1 2</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// TypeError: Cannot read property 'x' of undefined</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面代码只使用了对象的解构赋值默认值,没有使用函数参数的默认值。只有当函数<code>foo</code>的参数是一个对象时,变量<code>x</code>和<code>y</code>才会通过解构赋值生成。如果函数<code>foo</code>调用时没提供参数,变量<code>x</code>和<code>y</code>就不会生成,从而报错。通过提供函数参数的默认值,就可以避免这种情况。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>x<span class="token punctuation">,</span> y <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// undefined 5</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>上面代码指定,如果没有提供参数,函数<code>foo</code>的参数默认为一个空对象。</p> <p>下面是另一个解构赋值默认值的例子。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">fetch</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> <span class="token punctuation">{</span> body <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">,</span> method <span class="token operator">=</span> <span class="token string">'GET'</span><span class="token punctuation">,</span> headers <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">'http://example.com'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
- <span class="token comment">// "GET"</span>
- <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">'http://example.com'</span><span class="token punctuation">)</span>
- <span class="token comment">// 报错</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>上面代码中,如果函数<code>fetch</code>的第二个参数是一个对象,就可以为它的三个属性设置默认值。这种写法不能省略第二个参数,如果结合函数参数的默认值,就可以省略第二个参数。这时,就出现了双重默认值。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">fetch</span><span class="token punctuation">(</span>url<span class="token punctuation">,</span> <span class="token punctuation">{</span> body <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">,</span> method <span class="token operator">=</span> <span class="token string">'GET'</span><span class="token punctuation">,</span> headers <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">'http://example.com'</span><span class="token punctuation">)</span>
- <span class="token comment">// "GET"</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中,函数<code>fetch</code>没有第二个参数时,函数参数的默认值就会生效,然后才是解构赋值的默认值生效,变量<code>method</code>才会取到默认值<code>GET</code>。</p> <p>作为练习,请问下面两种写法有什么差别?</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// 写法一</span>
- <span class="token keyword">function</span> <span class="token function">m1</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>x <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> y <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token punctuation">[</span>x<span class="token punctuation">,</span> y<span class="token punctuation">]</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token comment">// 写法二</span>
- <span class="token keyword">function</span> <span class="token function">m2</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>x<span class="token punctuation">,</span> y<span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token literal-property property">x</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token literal-property property">y</span><span class="token operator">:</span> <span class="token number">0</span> <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token punctuation">[</span>x<span class="token punctuation">,</span> y<span class="token punctuation">]</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>上面两种写法都对函数的参数设定了默认值,区别是写法一函数参数的默认值是空对象,但是设置了对象解构赋值的默认值;写法二函数参数的默认值是一个有具体属性的对象,但是没有设置对象解构赋值的默认值。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// 函数没有参数的情况</span>
- <span class="token function">m1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// [0, 0]</span>
- <span class="token function">m2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// [0, 0]</span>
- <span class="token comment">// x 和 y 都有值的情况</span>
- <span class="token function">m1</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">x</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token literal-property property">y</span><span class="token operator">:</span> <span class="token number">8</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// [3, 8]</span>
- <span class="token function">m2</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">x</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token literal-property property">y</span><span class="token operator">:</span> <span class="token number">8</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// [3, 8]</span>
- <span class="token comment">// x 有值,y 无值的情况</span>
- <span class="token function">m1</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">x</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// [3, 0]</span>
- <span class="token function">m2</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">x</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// [3, undefined]</span>
- <span class="token comment">// x 和 y 都无值的情况</span>
- <span class="token function">m1</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// [0, 0];</span>
- <span class="token function">m2</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// [undefined, undefined]</span>
- <span class="token function">m1</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">z</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// [0, 0]</span>
- <span class="token function">m2</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">z</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// [undefined, undefined]</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br></div></div><h3 id="参数默认值的位置"><a href="#参数默认值的位置" class="header-anchor">#</a> 参数默认值的位置</h3> <p>通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// 例一</span>
- <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">x <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span> y</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token punctuation">[</span>x<span class="token punctuation">,</span> y<span class="token punctuation">]</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// [1, undefined]</span>
- <span class="token function">f</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span> <span class="token comment">// [2, undefined])</span>
- <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// 报错</span>
- <span class="token function">f</span><span class="token punctuation">(</span><span class="token keyword">undefined</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// [1, 1]</span>
- <span class="token comment">// 例二</span>
- <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> y <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">,</span> z</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token punctuation">[</span>x<span class="token punctuation">,</span> y<span class="token punctuation">,</span> z<span class="token punctuation">]</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// [undefined, 5, undefined]</span>
- <span class="token function">f</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// [1, 5, undefined]</span>
- <span class="token function">f</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">)</span> <span class="token comment">// 报错</span>
- <span class="token function">f</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token keyword">undefined</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token comment">// [1, 5, 2]</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br></div></div><p>上面代码中,有默认值的参数都不是尾参数。这时,无法只省略该参数,而不省略它后面的参数,除非显式输入<code>undefined</code>。</p> <p>如果传入<code>undefined</code>,将触发该参数等于默认值,<code>null</code>则没有这个效果。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token parameter">x <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">,</span> y <span class="token operator">=</span> <span class="token number">6</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token keyword">undefined</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
- <span class="token comment">// 5 null</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中,<code>x</code>参数对应<code>undefined</code>,结果触发了默认值,<code>y</code>参数等于<code>null</code>,就没有触发默认值。</p> <h3 id="函数的-length-属性"><a href="#函数的-length-属性" class="header-anchor">#</a> 函数的 length 属性</h3> <p>指定了默认值以后,函数的<code>length</code>属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,<code>length</code>属性将失真。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">a</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token comment">// 1</span>
- <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">a <span class="token operator">=</span> <span class="token number">5</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token comment">// 0</span>
- <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> c <span class="token operator">=</span> <span class="token number">5</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token comment">// 2</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面代码中,<code>length</code>属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。比如,上面最后一个函数,定义了 3 个参数,其中有一个参数<code>c</code>指定了默认值,因此<code>length</code>属性等于<code>3</code>减去<code>1</code>,最后得到<code>2</code>。</p> <p>这是因为**<code>length</code>属性的含义是,该函数预期传入的参数个数**。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。同理,后文的 rest 参数也不会计入<code>length</code>属性。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>args</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token comment">// 0</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>如果设置了默认值的参数不是尾参数,那么<code>length</code>属性也不再计入后面的参数了。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">a <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> b<span class="token punctuation">,</span> c</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token comment">// 0</span>
- <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span> c</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token comment">// 1</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><h3 id="作用域"><a href="#作用域" class="header-anchor">#</a> 作用域</h3> <p>一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">var</span> x <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
- <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> y <span class="token operator">=</span> x</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">f</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span> <span class="token comment">// 2</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面代码中,参数<code>y</code>的默认值等于变量<code>x</code>。调用函数<code>f</code>时,参数形成一个单独的作用域。在这个作用域里面,默认值变量<code>x</code>指向第一个参数<code>x</code>,而不是全局变量<code>x</code>,所以输出是<code>2</code>。</p> <p>再看下面的例子。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">let</span> x <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
- <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">y <span class="token operator">=</span> x</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">let</span> x <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 1</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面代码中,函数<code>f</code>调用时,参数<code>y = x</code>形成一个单独的作用域。这个作用域里面,变量<code>x</code>本身没有定义,所以指向外层的全局变量<code>x</code>。函数调用时,函数体内部的局部变量<code>x</code>影响不到默认值变量<code>x</code>。</p> <p>如果此时,全局变量<code>x</code>不存在,就会报错。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">y <span class="token operator">=</span> x</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">let</span> x <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// ReferenceError: x is not defined</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>下面这样写,也会报错。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">var</span> x <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
- <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token parameter">x <span class="token operator">=</span> x</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token comment">// ...</span>
- <span class="token punctuation">}</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// ReferenceError: x is not defined</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面代码中,参数<code>x = x</code>形成一个单独作用域。实际执行的是<code>let x = x</code>,由于暂时性死区的原因,这行代码会报错”x 未定义“。</p> <p>如果参数的默认值是一个函数,该函数的作用域也遵守这个规则。请看下面的例子。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">let</span> foo <span class="token operator">=</span> <span class="token string">'outer'</span><span class="token punctuation">;</span>
- <span class="token keyword">function</span> <span class="token function">bar</span><span class="token punctuation">(</span><span class="token function-variable function">func</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> foo<span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">let</span> foo <span class="token operator">=</span> <span class="token string">'inner'</span><span class="token punctuation">;</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">bar</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// outer</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面代码中,函数<code>bar</code>的参数<code>func</code>的默认值是一个匿名函数,返回值为变量<code>foo</code>。函数参数形成的单独作用域里面,并没有定义变量<code>foo</code>,所以<code>foo</code>指向外层的全局变量<code>foo</code>,因此输出<code>outer</code>。</p> <p>如果写成下面这样,就会报错。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">bar</span><span class="token punctuation">(</span><span class="token function-variable function">func</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> foo<span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">let</span> foo <span class="token operator">=</span> <span class="token string">'inner'</span><span class="token punctuation">;</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">bar</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// ReferenceError: foo is not defined</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中,匿名函数里面的<code>foo</code>指向函数外层,但是函数外层并没有声明变量<code>foo</code>,所以就报错了。</p> <p>下面是一个更复杂的例子。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">var</span> x <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
- <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> <span class="token function-variable function">y</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> x <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">var</span> x <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span>
- <span class="token function">y</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 3</span>
- x <span class="token comment">// 1</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>上面代码中,函数<code>foo</code>的参数形成一个单独作用域。这个作用域里面,首先声明了变量<code>x</code>,然后声明了变量<code>y</code>,<code>y</code>的默认值是一个匿名函数。这个匿名函数内部的变量<code>x</code>,指向同一个作用域的第一个参数<code>x</code>。函数<code>foo</code>内部又声明了一个内部变量<code>x</code>,该变量与第一个参数<code>x</code>由于不是同一个作用域,所以不是同一个变量,因此执行<code>y</code>后,内部变量<code>x</code>和外部全局变量<code>x</code>的值都没变。</p> <p>如果将<code>var x = 3</code>的<code>var</code>去除,函数<code>foo</code>的内部变量<code>x</code>就指向第一个参数<code>x</code>,与匿名函数内部的<code>x</code>是一致的,所以最后输出的就是<code>2</code>,而外层的全局变量<code>x</code>依然不受影响。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">var</span> x <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
- <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> <span class="token function-variable function">y</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> x <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- x <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span>
- <span class="token function">y</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 2</span>
- x <span class="token comment">// 1</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><h3 id="应用"><a href="#应用" class="header-anchor">#</a> 应用</h3> <p>利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">throwIfMissing</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'Missing parameter'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token parameter">mustBeProvided <span class="token operator">=</span> <span class="token function">throwIfMissing</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> mustBeProvided<span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
- <span class="token comment">// Error: Missing parameter</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>上面代码的<code>foo</code>函数,如果调用的时候没有参数,就会调用默认值<code>throwIfMissing</code>函数,从而抛出一个错误。</p> <p>从上面代码还可以看到,参数<code>mustBeProvided</code>的默认值等于<code>throwIfMissing</code>函数的运行结果(注意函数名<code>throwIfMissing</code>之后有一对圆括号),这表明参数的默认值不是在定义时执行,而是在运行时执行。如果参数已经赋值,默认值中的函数就不会运行。</p> <p>另外,可以将参数默认值设为<code>undefined</code>,表明这个参数是可以省略的。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token parameter">optional <span class="token operator">=</span> <span class="token keyword">undefined</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> ··· <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><h2 id="rest-参数"><a href="#rest-参数" class="header-anchor">#</a> rest 参数</h2> <p>ES6 引入 rest 参数(形式为<code>...变量名</code>),用于获取函数的多余参数,这样就不需要使用<code>arguments</code>对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>values</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// values是一个数组,该变量将多余的参数放入数组中</span>
- <span class="token keyword">let</span> sum <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
- <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> val <span class="token keyword">of</span> values<span class="token punctuation">)</span> <span class="token punctuation">{</span>
- sum <span class="token operator">+=</span> val<span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">return</span> sum<span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">add</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span> <span class="token comment">// 10</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br></div></div><p>上面代码的<code>add</code>函数是一个求和函数,利用 rest 参数,可以向该函数传入任意数目的参数。</p> <p>下面是一个 rest 参数代替<code>arguments</code>变量的例子。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// arguments变量的写法</span>
- <span class="token keyword">function</span> <span class="token function">sortNumbers</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>arguments<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token comment">// rest参数的写法</span>
- <span class="token keyword">const</span> <span class="token function-variable function">sortNumbers</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>numbers</span><span class="token punctuation">)</span> <span class="token operator">=></span> numbers<span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面代码的两种写法,比较后可以发现,rest 参数的写法更自然也更简洁。</p> <p><code>arguments</code>对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用<code>Array.prototype.slice.call</code>先将其转为数组。rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。下面是一个利用 rest 参数改写数组<code>push</code>方法的例子。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">push</span><span class="token punctuation">(</span><span class="token parameter">array<span class="token punctuation">,</span> <span class="token operator">...</span>items</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- items<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">item</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- array<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
- <span class="token function">push</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// 报错</span>
- <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> <span class="token operator">...</span>b<span class="token punctuation">,</span> c</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token comment">// ...</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>函数的<code>length</code>属性,不包括 rest 参数。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">a</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token comment">// 1</span>
- <span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>a</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token comment">// 0</span>
- <span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> <span class="token operator">...</span>b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length <span class="token comment">// 1</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><h2 id="严格模式"><a href="#严格模式" class="header-anchor">#</a> 严格模式</h2> <p>从 ES5 开始,函数内部可以设定为严格模式。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token string">'use strict'</span><span class="token punctuation">;</span>
- <span class="token comment">// code</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>ES2016 做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// 报错</span>
- <span class="token keyword">function</span> <span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b <span class="token operator">=</span> a</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token string">'use strict'</span><span class="token punctuation">;</span>
- <span class="token comment">// code</span>
- <span class="token punctuation">}</span>
- <span class="token comment">// 报错</span>
- <span class="token keyword">const</span> <span class="token function-variable function">doSomething</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>a<span class="token punctuation">,</span> b<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token string">'use strict'</span><span class="token punctuation">;</span>
- <span class="token comment">// code</span>
- <span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token comment">// 报错</span>
- <span class="token keyword">const</span> <span class="token function-variable function">doSomething</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>a</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
- <span class="token string">'use strict'</span><span class="token punctuation">;</span>
- <span class="token comment">// code</span>
- <span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token keyword">const</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span>
- <span class="token comment">// 报错</span>
- <span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span>a<span class="token punctuation">,</span> b<span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token string">'use strict'</span><span class="token punctuation">;</span>
- <span class="token comment">// code</span>
- <span class="token punctuation">}</span>
- <span class="token punctuation">}</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br></div></div><p>这样规定的原因是,函数内部的严格模式,同时适用于函数体和函数参数。但是,函数执行的时候,先执行函数参数,然后再执行函数体。这样就有一个不合理的地方,只有从函数体之中,才能知道参数是否应该以严格模式执行,但是参数却应该先于函数体执行。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// 报错</span>
- <span class="token keyword">function</span> <span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token parameter">value <span class="token operator">=</span> <span class="token number">070</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token string">'use strict'</span><span class="token punctuation">;</span>
- <span class="token keyword">return</span> value<span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>上面代码中,参数<code>value</code>的默认值是八进制数<code>070</code>,但是严格模式下不能用前缀<code>0</code>表示八进制,所以应该报错。但是实际上,JavaScript 引擎会先成功执行<code>value = 070</code>,然后进入函数体内部,发现需要用严格模式执行,这时才会报错。</p> <p>虽然可以先解析函数体代码,再执行参数代码,但是这样无疑就增加了复杂性。因此,标准索性禁止了这种用法,只要参数使用了默认值、解构赋值、或者扩展运算符,就不能显式指定严格模式。</p> <p>两种方法可以规避这种限制。第一种是设定全局性的严格模式,这是合法的。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token string">'use strict'</span><span class="token punctuation">;</span>
- <span class="token keyword">function</span> <span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b <span class="token operator">=</span> a</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token comment">// code</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>第二种是把函数包在一个无参数的立即执行函数里面。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">const</span> doSomething <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token string">'use strict'</span><span class="token punctuation">;</span>
- <span class="token keyword">return</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">value <span class="token operator">=</span> <span class="token number">42</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> value<span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><h2 id="name-属性"><a href="#name-属性" class="header-anchor">#</a> name 属性</h2> <p>函数的<code>name</code>属性,返回该函数的函数名。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
- foo<span class="token punctuation">.</span>name <span class="token comment">// "foo"</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>这个属性早就被浏览器广泛支持,但是直到 ES6,才将其写入了标准。</p> <p>需要注意的是,ES6 对这个属性的行为做出了一些修改。如果将一个匿名函数赋值给一个变量,ES5 的<code>name</code>属性,会返回空字符串,而 ES6 的<code>name</code>属性会返回实际的函数名。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">var</span> <span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token comment">// ES5</span>
- f<span class="token punctuation">.</span>name <span class="token comment">// ""</span>
- <span class="token comment">// ES6</span>
- f<span class="token punctuation">.</span>name <span class="token comment">// "f"</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面代码中,变量<code>f</code>等于一个匿名函数,ES5 和 ES6 的<code>name</code>属性返回的值不一样。</p> <p>如果将一个具名函数赋值给一个变量,则 ES5 和 ES6 的<code>name</code>属性都返回这个具名函数原本的名字。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">const</span> <span class="token function-variable function">bar</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token function">baz</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token comment">// ES5</span>
- bar<span class="token punctuation">.</span>name <span class="token comment">// "baz"</span>
- <span class="token comment">// ES6</span>
- bar<span class="token punctuation">.</span>name <span class="token comment">// "baz"</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p><code>Function</code>构造函数返回的函数实例,<code>name</code>属性的值为<code>anonymous</code>。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Function</span><span class="token punctuation">)</span><span class="token punctuation">.</span>name <span class="token comment">// "anonymous"</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p><code>bind</code>返回的函数,<code>name</code>属性值会加上<code>bound</code>前缀。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token function">foo</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>name <span class="token comment">// "bound foo"</span>
- <span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>name <span class="token comment">// "bound "</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><h2 id="箭头函数"><a href="#箭头函数" class="header-anchor">#</a> 箭头函数</h2> <h3 id="基本用法-2"><a href="#基本用法-2" class="header-anchor">#</a> 基本用法</h3> <p>ES6 允许使用“箭头”(<code>=></code>)定义函数。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">var</span> <span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token parameter">v</span> <span class="token operator">=></span> v<span class="token punctuation">;</span>
- <span class="token comment">// 等同于</span>
- <span class="token keyword">var</span> <span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">v</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> v<span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">var</span> <span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token number">5</span><span class="token punctuation">;</span>
- <span class="token comment">// 等同于</span>
- <span class="token keyword">var</span> <span class="token function-variable function">f</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token number">5</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token keyword">var</span> <span class="token function-variable function">sum</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">num1<span class="token punctuation">,</span> num2</span><span class="token punctuation">)</span> <span class="token operator">=></span> num1 <span class="token operator">+</span> num2<span class="token punctuation">;</span>
- <span class="token comment">// 等同于</span>
- <span class="token keyword">var</span> <span class="token function-variable function">sum</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">num1<span class="token punctuation">,</span> num2</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> num1 <span class="token operator">+</span> num2<span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>如果<strong>箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用<code>return</code>语句返回</strong>。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">var</span> <span class="token function-variable function">sum</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">num1<span class="token punctuation">,</span> num2</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> num1 <span class="token operator">+</span> num2<span class="token punctuation">;</span> <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// 报错</span>
- <span class="token keyword">let</span> <span class="token function-variable function">getTempItem</span> <span class="token operator">=</span> <span class="token parameter">id</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token literal-property property">id</span><span class="token operator">:</span> id<span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"Temp"</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token comment">// 不报错</span>
- <span class="token keyword">let</span> <span class="token function-variable function">getTempItem</span> <span class="token operator">=</span> <span class="token parameter">id</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">id</span><span class="token operator">:</span> id<span class="token punctuation">,</span> <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">"Temp"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>下面是一种特殊情况,虽然可以运行,但会得到错误的结果。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">let</span> <span class="token function-variable function">foo</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token literal-property property">a</span><span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// undefined</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>上面代码中,原始意图是返回一个对象<code>{ a: 1 }</code>,但是由于引擎认为大括号是代码块,所以执行了一行语句<code>a: 1</code>。这时,<code>a</code>可以被解释为语句的标签,因此实际执行的语句是<code>1;</code>,然后函数就结束了,没有返回值。</p> <p>如果箭头函数只有一行语句,且不需要返回值,可以采用下面的写法,就不用写大括号了。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">let</span> <span class="token function-variable function">fn</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">void</span> <span class="token function">doesNotReturn</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>箭头函数可以与变量解构结合使用。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">const</span> <span class="token function-variable function">full</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> first<span class="token punctuation">,</span> last <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> first <span class="token operator">+</span> <span class="token string">' '</span> <span class="token operator">+</span> last<span class="token punctuation">;</span>
- <span class="token comment">// 等同于</span>
- <span class="token keyword">function</span> <span class="token function">full</span><span class="token punctuation">(</span><span class="token parameter">person</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> person<span class="token punctuation">.</span>first <span class="token operator">+</span> <span class="token string">' '</span> <span class="token operator">+</span> person<span class="token punctuation">.</span>last<span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>箭头函数使得表达更加简洁。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">const</span> <span class="token function-variable function">isEven</span> <span class="token operator">=</span> <span class="token parameter">n</span> <span class="token operator">=></span> n <span class="token operator">%</span> <span class="token number">2</span> <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">;</span>
- <span class="token keyword">const</span> <span class="token function-variable function">square</span> <span class="token operator">=</span> <span class="token parameter">n</span> <span class="token operator">=></span> n <span class="token operator">*</span> n<span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>上面代码只用了两行,就定义了两个简单的工具函数。如果不用箭头函数,可能就要占用多行,而且还不如现在这样写醒目。</p> <p>箭头函数的一个用处是简化回调函数。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// 正常函数写法</span>
- <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">x</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> x <span class="token operator">*</span> x<span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token comment">// 箭头函数写法</span>
- <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token number">2</span><span class="token punctuation">,</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token parameter">x</span> <span class="token operator">=></span> x <span class="token operator">*</span> x<span class="token punctuation">)</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>另一个例子是</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// 正常函数写法</span>
- <span class="token keyword">var</span> result <span class="token operator">=</span> values<span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> a <span class="token operator">-</span> b<span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token comment">// 箭头函数写法</span>
- <span class="token keyword">var</span> result <span class="token operator">=</span> values<span class="token punctuation">.</span><span class="token function">sort</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=></span> a <span class="token operator">-</span> b<span class="token punctuation">)</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>下面是 rest 参数与箭头函数结合的例子。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">const</span> <span class="token function-variable function">numbers</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>nums</span><span class="token punctuation">)</span> <span class="token operator">=></span> nums<span class="token punctuation">;</span>
- <span class="token function">numbers</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span>
- <span class="token comment">// [1,2,3,4,5]</span>
- <span class="token keyword">const</span> <span class="token function-variable function">headAndTail</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">head<span class="token punctuation">,</span> <span class="token operator">...</span>tail</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">[</span>head<span class="token punctuation">,</span> tail<span class="token punctuation">]</span><span class="token punctuation">;</span>
- <span class="token function">headAndTail</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span>
- <span class="token comment">// [1,[2,3,4,5]]</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><h3 id="使用注意点"><a href="#使用注意点" class="header-anchor">#</a> 使用注意点</h3> <p>箭头函数有几个使用注意点。</p> <p>(1)函数体内的**<code>this</code>对象**,就是<strong>定义时所在的对象</strong>,而不是使用时所在的对象。</p> <p>(2)<strong>不可以当作构造函数</strong>,也就是说,不可以使用<code>new</code>命令,否则会抛出一个错误。</p> <p>(3)<strong>不可以使用<code>arguments</code>对象</strong>,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。</p> <p>(4)<strong>不可以使用<code>yield</code>命令</strong>,因此箭头函数不能用作 Generator 函数。</p> <p>上面四点中,第一点尤其值得注意。<code>this</code>对象的指向是可变的,但是在箭头函数中,它是固定的。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'id:'</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">var</span> id <span class="token operator">=</span> <span class="token number">21</span><span class="token punctuation">;</span>
- <span class="token function">foo</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token number">42</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token comment">// id: 42</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>上面代码中,<code>setTimeout</code>的参数是一个箭头函数,这个箭头函数的定义生效是在<code>foo</code>函数生成时,而它的真正执行要等到 100 毫秒后。如果是普通函数,执行时<code>this</code>应该指向全局对象<code>window</code>,这时应该输出<code>21</code>。但是,箭头函数导致<code>this</code>总是指向函数定义生效时所在的对象(本例是<code>{id: 42}</code>),所以输出的是<code>42</code>。</p> <p>箭头函数可以让<code>setTimeout</code>里面的<code>this</code>,绑定定义时所在的作用域,而不是指向运行时所在的作用域。下面是另一个例子。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">Timer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">this</span><span class="token punctuation">.</span>s1 <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
- <span class="token keyword">this</span><span class="token punctuation">.</span>s2 <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
- <span class="token comment">// 箭头函数</span>
- <span class="token function">setInterval</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">this</span><span class="token punctuation">.</span>s1<span class="token operator">++</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token comment">// 普通函数</span>
- <span class="token function">setInterval</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">this</span><span class="token punctuation">.</span>s2<span class="token operator">++</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">var</span> timer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Timer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'s1: '</span><span class="token punctuation">,</span> timer<span class="token punctuation">.</span>s1<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">3100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'s2: '</span><span class="token punctuation">,</span> timer<span class="token punctuation">.</span>s2<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">3100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token comment">// s1: 3</span>
- <span class="token comment">// s2: 0</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br></div></div><p>上面代码中,<code>Timer</code>函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的<code>this</code>绑定定义时所在的作用域(即<code>Timer</code>函数),后者的<code>this</code>指向运行时所在的作用域(即全局对象)。所以,3100 毫秒之后,<code>timer.s1</code>被更新了 3 次,而<code>timer.s2</code>一次都没更新。</p> <p>箭头函数可以让<code>this</code>指向固定化,这种特性很有利于封装回调函数。下面是一个例子,DOM 事件的回调函数封装在一个对象里面。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">var</span> handler <span class="token operator">=</span> <span class="token punctuation">{</span>
- <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token string">'123456'</span><span class="token punctuation">,</span>
- <span class="token function-variable function">init</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 注意这里不是使用箭头函数,如这里使用箭头函数将时this指向全局对象</span>
- document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span>
- <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">doSomething</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>type<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">,</span>
- <span class="token function-variable function">doSomething</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">type</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Handling '</span> <span class="token operator">+</span> type <span class="token operator">+</span> <span class="token string">' for '</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token punctuation">}</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><p>上面代码的<code>init</code>方法中,使用了箭头函数,这导致这个箭头函数里面的<code>this</code>,总是指向<code>handler</code>对象。否则,回调函数运行时,<code>this.doSomething</code>这一行会报错,因为此时<code>this</code>指向<code>document</code>对象。</p> <p><code>this</code>指向的固定化,并不是因为箭头函数内部有绑定<code>this</code>的机制,实际原因是箭头函数根本没有自己的<code>this</code>,导致内部的<code>this</code>就是外层代码块的<code>this</code>。正是因为它没有<code>this</code>,所以也就不能用作构造函数。</p> <p>所以,箭头函数转成 ES5 的代码如下。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// ES6</span>
- <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'id:'</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token comment">// ES5</span>
- <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">var</span> _this <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
- <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'id:'</span><span class="token punctuation">,</span> _this<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><p>上面代码中,转换后的 ES5 版本清楚地说明了,箭头函数里面根本没有自己的<code>this</code>,而是引用外层的<code>this</code>。</p> <p>请问下面的代码之中有几个<code>this</code>?</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'id:'</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">var</span> f <span class="token operator">=</span> <span class="token function">foo</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token keyword">var</span> t1 <span class="token operator">=</span> <span class="token function">f</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// id: 1</span>
- <span class="token keyword">var</span> t2 <span class="token operator">=</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// id: 1</span>
- <span class="token keyword">var</span> t3 <span class="token operator">=</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token number">4</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// id: 1</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><p>上面代码之中,只有一个<code>this</code>,就是函数<code>foo</code>的<code>this</code>,所以<code>t1</code>、<code>t2</code>、<code>t3</code>都输出同样的结果。因为所有的内层函数都是箭头函数,都没有自己的<code>this</code>,它们的<code>this</code>其实都是最外层<code>foo</code>函数的<code>this</code>。</p> <p>除了<code>this</code>,以下三个变量在箭头函数之中也是不存在的,指向外层函数的对应变量:<code>arguments</code>、<code>super</code>、<code>new.target</code>。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
- console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'args:'</span><span class="token punctuation">,</span> arguments<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">foo</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">6</span><span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">)</span>
- <span class="token comment">// args: [2, 4, 6, 8]</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div><p>上面代码中,箭头函数内部的变量<code>arguments</code>,其实是函数<code>foo</code>的<code>arguments</code>变量。</p> <p>另外,由于箭头函数没有自己的<code>this</code>,所以当然也就不能用<code>call()</code>、<code>apply()</code>、<code>bind()</code>这些方法去改变<code>this</code>的指向。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token punctuation">[</span>
- <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">this</span><span class="token punctuation">.</span>x<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">x</span><span class="token operator">:</span> <span class="token string">'inner'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
- <span class="token punctuation">]</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">x</span><span class="token operator">:</span> <span class="token string">'outer'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token comment">// ['outer']</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中,箭头函数没有自己的<code>this</code>,所以<code>bind</code>方法无效,内部的<code>this</code>指向外部的<code>this</code>。</p> <p>长期以来,JavaScript 语言的<code>this</code>对象一直是一个令人头痛的问题,在对象方法中使用<code>this</code>,必须非常小心。箭头函数”绑定”<code>this</code>,很大程度上解决了这个困扰。</p> <h3 id="不适用场合"><a href="#不适用场合" class="header-anchor">#</a> 不适用场合</h3> <p>由于箭头函数使得<code>this</code>从“动态”变成“静态”,下面两个场合不应该使用箭头函数。</p> <p>第一个场合是<strong>定义对象的方法,且该方法内部包括<code>this</code></strong>。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">const</span> cat <span class="token operator">=</span> <span class="token punctuation">{</span>
- <span class="token literal-property property">lives</span><span class="token operator">:</span> <span class="token number">9</span><span class="token punctuation">,</span>
- <span class="token function-variable function">jumps</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
- <span class="token keyword">this</span><span class="token punctuation">.</span>lives<span class="token operator">--</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中,<code>cat.jumps()</code>方法是一个箭头函数,这是错误的。调用<code>cat.jumps()</code>时,如果是普通函数,该方法内部的<code>this</code>指向<code>cat</code>;如果写成上面那样的箭头函数,使得<code>this</code>指向全局对象,因此不会得到预期结果。这是因为对象不构成单独的作用域,<strong>导致<code>jumps</code>箭头函数定义时的作用域就是全局作用域。</strong></p> <p>第二个场合是<strong>需要动态<code>this</code>的时候,也不应使用箭头函数</strong>。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">var</span> button <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'press'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- button<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
- <span class="token keyword">this</span><span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">toggle</span><span class="token punctuation">(</span><span class="token string">'on'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>上面代码运行时,点击按钮会报错,因为<code>button</code>的监听函数是一个箭头函数,导致里面的<code>this</code>就是全局对象。如果改成普通函数,<code>this</code>就会动态指向被点击的按钮对象。</p> <p>另外,如果函数体很复杂,有许多行,或者函数内部有大量的读写操作,不单纯是为了计算值,这时也不应该使用箭头函数,而是要使用普通函数,这样可以提高代码可读性。</p> <h3 id="嵌套的箭头函数"><a href="#嵌套的箭头函数" class="header-anchor">#</a> 嵌套的箭头函数</h3> <p>箭头函数内部,还可以再使用箭头函数。下面是一个 ES5 语法的多重嵌套函数。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">insert</span><span class="token punctuation">(</span><span class="token parameter">value</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token punctuation">{</span><span class="token function-variable function">into</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">array</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token punctuation">{</span><span class="token function-variable function">after</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">afterValue</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- array<span class="token punctuation">.</span><span class="token function">splice</span><span class="token punctuation">(</span>array<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>afterValue<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token keyword">return</span> array<span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">insert</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">into</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">after</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//[1, 2, 3]</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>上面这个函数,可以使用箭头函数改写。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">let</span> <span class="token function-variable function">insert</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">value</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token function-variable function">into</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">array</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token function-variable function">after</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">afterValue</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
- array<span class="token punctuation">.</span><span class="token function">splice</span><span class="token punctuation">(</span>array<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>afterValue<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token keyword">return</span> array<span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token function">insert</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">into</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">after</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//[1, 2, 3]</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>下面是一个部署管道机制(pipeline)的例子,即前一个函数的输出是后一个函数的输入。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">const</span> <span class="token function-variable function">pipeline</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>funcs</span><span class="token punctuation">)</span> <span class="token operator">=></span>
- <span class="token parameter">val</span> <span class="token operator">=></span> funcs<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">b</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">,</span> val<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token keyword">const</span> <span class="token function-variable function">plus1</span> <span class="token operator">=</span> <span class="token parameter">a</span> <span class="token operator">=></span> a <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span>
- <span class="token keyword">const</span> <span class="token function-variable function">mult2</span> <span class="token operator">=</span> <span class="token parameter">a</span> <span class="token operator">=></span> a <span class="token operator">*</span> <span class="token number">2</span><span class="token punctuation">;</span>
- <span class="token keyword">const</span> addThenMult <span class="token operator">=</span> <span class="token function">pipeline</span><span class="token punctuation">(</span>plus1<span class="token punctuation">,</span> mult2<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token function">addThenMult</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span>
- <span class="token comment">// 12</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>如果觉得上面的写法可读性比较差,也可以采用下面的写法。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">const</span> <span class="token function-variable function">plus1</span> <span class="token operator">=</span> <span class="token parameter">a</span> <span class="token operator">=></span> a <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span>
- <span class="token keyword">const</span> <span class="token function-variable function">mult2</span> <span class="token operator">=</span> <span class="token parameter">a</span> <span class="token operator">=></span> a <span class="token operator">*</span> <span class="token number">2</span><span class="token punctuation">;</span>
- <span class="token function">mult2</span><span class="token punctuation">(</span><span class="token function">plus1</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
- <span class="token comment">// 12</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>箭头函数还有一个功能,就是可以很方便地改写 λ 演算。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// λ演算的写法</span>
- fix <span class="token operator">=</span> λf<span class="token punctuation">.</span><span class="token punctuation">(</span>λx<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">(</span>λv<span class="token punctuation">.</span><span class="token function">x</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">(</span>λx<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">(</span>λv<span class="token punctuation">.</span><span class="token function">x</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
- <span class="token comment">// ES6的写法</span>
- <span class="token keyword">var</span> <span class="token function-variable function">fix</span> <span class="token operator">=</span> <span class="token parameter">f</span> <span class="token operator">=></span> <span class="token punctuation">(</span><span class="token parameter">x</span> <span class="token operator">=></span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">v</span> <span class="token operator">=></span> <span class="token function">x</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
- <span class="token punctuation">(</span><span class="token parameter">x</span> <span class="token operator">=></span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">v</span> <span class="token operator">=></span> <span class="token function">x</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面两种写法,几乎是一一对应的。由于 λ 演算对于计算机科学非常重要,这使得我们可以用 ES6 作为替代工具,探索计算机科学。</p> <h2 id="尾调用优化"><a href="#尾调用优化" class="header-anchor">#</a> 尾调用优化</h2> <h3 id="什么是尾调用"><a href="#什么是尾调用" class="header-anchor">#</a> 什么是尾调用?</h3> <p>尾调用(Tail Call)是函数式编程的一个重要概念,本身非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">x</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token function">g</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>上面代码中,函数<code>f</code>的最后一步是调用函数<code>g</code>,这就叫尾调用。</p> <p>以下三种情况,都不属于尾调用。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token comment">// 情况一</span>
- <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">x</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
- <span class="token keyword">let</span> y <span class="token operator">=</span> <span class="token function">g</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token keyword">return</span> y<span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token comment">// 情况二</span>
- <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">x</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token function">g</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token comment">// 情况三</span>
- <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">x</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
- <span class="token function">g</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><p>上面代码中,情况一是调用函数<code>g</code>之后,还有赋值操作,所以不属于尾调用,即使语义完全一样。情况二也属于调用后还有操作,即使写在一行内。情况三等同于下面的代码。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">x</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
- <span class="token function">g</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token keyword">return</span> <span class="token keyword">undefined</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>尾调用不一定出现在函数尾部,只要是最后一步操作即可。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token parameter">x</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">if</span> <span class="token punctuation">(</span>x <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token function">m</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">return</span> <span class="token function">n</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中,函数<code>m</code>和<code>n</code>都属于尾调用,因为它们都是函数<code>f</code>的最后一步操作。</p> <h3 id="尾调用优化-2"><a href="#尾调用优化-2" class="header-anchor">#</a> 尾调用优化</h3> <p>尾调用之所以与其他调用不同,就在于它的特殊的调用位置。</p> <p>我们知道,函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数<code>A</code>的内部调用函数<code>B</code>,那么在<code>A</code>的调用帧上方,还会形成一个<code>B</code>的调用帧。等到<code>B</code>运行结束,将结果返回到<code>A</code>,<code>B</code>的调用帧才会消失。如果函数<code>B</code>内部还调用函数<code>C</code>,那就还有一个<code>C</code>的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。</p> <p>尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">let</span> m <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
- <span class="token keyword">let</span> n <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>
- <span class="token keyword">return</span> <span class="token function">g</span><span class="token punctuation">(</span>m <span class="token operator">+</span> n<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token comment">// 等同于</span>
- <span class="token keyword">function</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token function">g</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token comment">// 等同于</span>
- <span class="token function">g</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><p>上面代码中,如果函数<code>g</code>不是尾调用,函数<code>f</code>就需要保存内部变量<code>m</code>和<code>n</code>的值、<code>g</code>的调用位置等信息。但由于调用<code>g</code>之后,函数<code>f</code>就结束了,所以执行到最后一步,完全可以删除<code>f(x)</code>的调用帧,只保留<code>g(3)</code>的调用帧。</p> <p>这就叫做“尾调用优化”(Tail call optimization),即只保留内层函数的调用帧。如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存。这就是“尾调用优化”的意义。</p> <p>注意,只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则就无法进行“尾调用优化”。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">addOne</span><span class="token punctuation">(</span><span class="token parameter">a</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
- <span class="token keyword">var</span> one <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
- <span class="token keyword">function</span> <span class="token function">inner</span><span class="token punctuation">(</span><span class="token parameter">b</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
- <span class="token keyword">return</span> b <span class="token operator">+</span> one<span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">return</span> <span class="token function">inner</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面的函数不会进行尾调用优化,因为内层函数<code>inner</code>用到了外层函数<code>addOne</code>的内部变量<code>one</code>。</p> <p>注意,目前只有 Safari 浏览器支持尾调用优化,Chrome 和 Firefox 都不支持。</p> <h3 id="尾递归"><a href="#尾递归" class="header-anchor">#</a> 尾递归</h3> <p>函数调用自身,称为递归。如果尾调用自身,就称为尾递归。</p> <p>递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">factorial</span><span class="token punctuation">(</span><span class="token parameter">n</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">if</span> <span class="token punctuation">(</span>n <span class="token operator">===</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token number">1</span><span class="token punctuation">;</span>
- <span class="token keyword">return</span> n <span class="token operator">*</span> <span class="token function">factorial</span><span class="token punctuation">(</span>n <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">factorial</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span> <span class="token comment">// 120</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码是一个阶乘函数,计算<code>n</code>的阶乘,最多需要保存<code>n</code>个调用记录,复杂度 O(n) 。</p> <p>如果改写成尾递归,只保留一个调用记录,复杂度 O(1) 。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">factorial</span><span class="token punctuation">(</span><span class="token parameter">n<span class="token punctuation">,</span> total</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">if</span> <span class="token punctuation">(</span>n <span class="token operator">===</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">return</span> total<span class="token punctuation">;</span>
- <span class="token keyword">return</span> <span class="token function">factorial</span><span class="token punctuation">(</span>n <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> n <span class="token operator">*</span> total<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">factorial</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// 120</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>还有一个比较著名的例子,就是计算 Fibonacci 数列,也能充分说明尾递归优化的重要性。</p> <p>非尾递归的 Fibonacci 数列实现如下。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">Fibonacci</span> <span class="token punctuation">(</span><span class="token parameter">n</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">if</span> <span class="token punctuation">(</span> n <span class="token operator"><=</span> <span class="token number">1</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">return</span> <span class="token number">1</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token keyword">return</span> <span class="token function">Fibonacci</span><span class="token punctuation">(</span>n <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token function">Fibonacci</span><span class="token punctuation">(</span>n <span class="token operator">-</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">Fibonacci</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token comment">// 89</span>
- <span class="token function">Fibonacci</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span> <span class="token comment">// 超时</span>
- <span class="token function">Fibonacci</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span> <span class="token comment">// 超时</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>尾递归优化过的 Fibonacci 数列实现如下。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">Fibonacci2</span> <span class="token punctuation">(</span><span class="token parameter">n <span class="token punctuation">,</span> ac1 <span class="token operator">=</span> <span class="token number">1</span> <span class="token punctuation">,</span> ac2 <span class="token operator">=</span> <span class="token number">1</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">if</span><span class="token punctuation">(</span> n <span class="token operator"><=</span> <span class="token number">1</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">return</span> ac2<span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token keyword">return</span> <span class="token function">Fibonacci2</span> <span class="token punctuation">(</span>n <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> ac2<span class="token punctuation">,</span> ac1 <span class="token operator">+</span> ac2<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">Fibonacci2</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span> <span class="token comment">// 573147844013817200000</span>
- <span class="token function">Fibonacci2</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span> <span class="token comment">// 7.0330367711422765e+208</span>
- <span class="token function">Fibonacci2</span><span class="token punctuation">(</span><span class="token number">10000</span><span class="token punctuation">)</span> <span class="token comment">// Infinity</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>由此可见,“尾调用优化”对递归操作意义重大,所以一些函数式编程语言将其写入了语言规格。ES6 亦是如此,第一次明确规定,所有 ECMAScript 的实现,都必须部署“尾调用优化”。这就是说,ES6 中只要使用尾递归,就不会发生栈溢出(或者层层递归造成的超时),相对节省内存。</p> <h3 id="递归函数的改写"><a href="#递归函数的改写" class="header-anchor">#</a> 递归函数的改写</h3> <p>尾递归的实现,往往需要改写递归函数,确保最后一步只调用自身。做到这一点的方法,就是把所有用到的内部变量改写成函数的参数。比如上面的例子,阶乘函数 factorial 需要用到一个中间变量<code>total</code>,那就把这个中间变量改写成函数的参数。这样做的缺点就是不太直观,第一眼很难看出来,为什么计算<code>5</code>的阶乘,需要传入两个参数<code>5</code>和<code>1</code>?</p> <p>两个方法可以解决这个问题。方法一是在尾递归函数之外,再提供一个正常形式的函数。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">tailFactorial</span><span class="token punctuation">(</span><span class="token parameter">n<span class="token punctuation">,</span> total</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">if</span> <span class="token punctuation">(</span>n <span class="token operator">===</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">return</span> total<span class="token punctuation">;</span>
- <span class="token keyword">return</span> <span class="token function">tailFactorial</span><span class="token punctuation">(</span>n <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> n <span class="token operator">*</span> total<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">function</span> <span class="token function">factorial</span><span class="token punctuation">(</span><span class="token parameter">n</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token function">tailFactorial</span><span class="token punctuation">(</span>n<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">factorial</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span> <span class="token comment">// 120</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>上面代码通过一个正常形式的阶乘函数<code>factorial</code>,调用尾递归函数<code>tailFactorial</code>,看起来就正常多了。</p> <p>函数式编程有一个概念,叫做柯里化(currying),意思是将多参数的函数转换成单参数的形式。这里也可以使用柯里化。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">currying</span><span class="token punctuation">(</span><span class="token parameter">fn<span class="token punctuation">,</span> n</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">m</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token function">fn</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> m<span class="token punctuation">,</span> n<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">function</span> <span class="token function">tailFactorial</span><span class="token punctuation">(</span><span class="token parameter">n<span class="token punctuation">,</span> total</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">if</span> <span class="token punctuation">(</span>n <span class="token operator">===</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">return</span> total<span class="token punctuation">;</span>
- <span class="token keyword">return</span> <span class="token function">tailFactorial</span><span class="token punctuation">(</span>n <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> n <span class="token operator">*</span> total<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">const</span> factorial <span class="token operator">=</span> <span class="token function">currying</span><span class="token punctuation">(</span>tailFactorial<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token function">factorial</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span> <span class="token comment">// 120</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br></div></div><p>上面代码通过柯里化,将尾递归函数<code>tailFactorial</code>变为只接受一个参数的<code>factorial</code>。</p> <p>第二种方法就简单多了,就是采用 ES6 的函数默认值。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">factorial</span><span class="token punctuation">(</span><span class="token parameter">n<span class="token punctuation">,</span> total <span class="token operator">=</span> <span class="token number">1</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">if</span> <span class="token punctuation">(</span>n <span class="token operator">===</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">return</span> total<span class="token punctuation">;</span>
- <span class="token keyword">return</span> <span class="token function">factorial</span><span class="token punctuation">(</span>n <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">,</span> n <span class="token operator">*</span> total<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token function">factorial</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span> <span class="token comment">// 120</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面代码中,参数<code>total</code>有默认值<code>1</code>,所以调用时不用提供这个值。</p> <p>总结一下,递归本质上是一种循环操作。纯粹的函数式编程语言没有循环操作命令,所有的循环都用递归实现,这就是为什么尾递归对这些语言极其重要。对于其他支持“尾调用优化”的语言(比如 Lua,ES6),只需要知道循环可以用递归代替,而一旦使用递归,就最好使用尾递归。</p> <h3 id="严格模式-2"><a href="#严格模式-2" class="header-anchor">#</a> 严格模式</h3> <p>ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。</p> <p>这是因为在正常模式下,函数内部有两个变量,可以跟踪函数的调用栈。</p> <ul><li><code>func.arguments</code>:返回调用时函数的参数。</li> <li><code>func.caller</code>:返回调用当前函数的那个函数。</li></ul> <p>尾调用优化发生时,函数的调用栈会改写,因此上面两个变量就会失真。严格模式禁用这两个变量,所以尾调用模式仅在严格模式下生效。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">restricted</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token string">'use strict'</span><span class="token punctuation">;</span>
- restricted<span class="token punctuation">.</span>caller<span class="token punctuation">;</span> <span class="token comment">// 报错</span>
- restricted<span class="token punctuation">.</span>arguments<span class="token punctuation">;</span> <span class="token comment">// 报错</span>
- <span class="token punctuation">}</span>
- <span class="token function">restricted</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><h3 id="尾递归优化的实现"><a href="#尾递归优化的实现" class="header-anchor">#</a> 尾递归优化的实现</h3> <p>尾递归优化只在严格模式下生效,那么正常模式下,或者那些不支持该功能的环境中,有没有办法也使用尾递归优化呢?回答是可以的,就是自己实现尾递归优化。</p> <p>它的原理非常简单。尾递归之所以需要优化,原因是调用栈太多,造成溢出,那么只要减少调用栈,就不会溢出。怎么做可以减少调用栈呢?就是采用“循环”换掉“递归”。</p> <p>下面是一个正常的递归函数。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">sum</span><span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> y</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">if</span> <span class="token punctuation">(</span>y <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token function">sum</span><span class="token punctuation">(</span>x <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> y <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> x<span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token punctuation">}</span>
- <span class="token function">sum</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">100000</span><span class="token punctuation">)</span>
- <span class="token comment">// Uncaught RangeError: Maximum call stack size exceeded(…)</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br></div></div><p>上面代码中,<code>sum</code>是一个递归函数,参数<code>x</code>是需要累加的值,参数<code>y</code>控制递归次数。一旦指定<code>sum</code>递归 100000 次,就会报错,提示超出调用栈的最大次数。</p> <p>蹦床函数(trampoline)可以将递归执行转为循环执行。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">trampoline</span><span class="token punctuation">(</span><span class="token parameter">f</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">while</span> <span class="token punctuation">(</span>f <span class="token operator">&&</span> f <span class="token keyword">instanceof</span> <span class="token class-name">Function</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- f <span class="token operator">=</span> <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">return</span> f<span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>上面就是蹦床函数的一个实现,它接受一个函数<code>f</code>作为参数。只要<code>f</code>执行后返回一个函数,就继续执行。注意,这里是返回一个函数,然后执行该函数,而不是函数里面调用函数,这样就避免了递归执行,从而就消除了调用栈过大的问题。</p> <p>然后,要做的就是将原来的递归函数,改写为每一步返回另一个函数。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">sum</span><span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> y</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">if</span> <span class="token punctuation">(</span>y <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token function">sum</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> x <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> y <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> x<span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面代码中,<code>sum</code>函数的每次执行,都会返回自身的另一个版本。</p> <p>现在,使用蹦床函数执行<code>sum</code>,就不会发生调用栈溢出。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token function">trampoline</span><span class="token punctuation">(</span><span class="token function">sum</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">100000</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
- <span class="token comment">// 100001</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p>蹦床函数并不是真正的尾递归优化,下面的实现才是。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">tco</span><span class="token punctuation">(</span><span class="token parameter">f</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">var</span> value<span class="token punctuation">;</span>
- <span class="token keyword">var</span> active <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
- <span class="token keyword">var</span> accumulated <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
- <span class="token keyword">return</span> <span class="token keyword">function</span> <span class="token function">accumulator</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- accumulated<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>arguments<span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>active<span class="token punctuation">)</span> <span class="token punctuation">{</span>
- active <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
- <span class="token keyword">while</span> <span class="token punctuation">(</span>accumulated<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span>
- value <span class="token operator">=</span> <span class="token function">f</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> accumulated<span class="token punctuation">.</span><span class="token function">shift</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- active <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
- <span class="token keyword">return</span> value<span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token punctuation">}</span><span class="token punctuation">;</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">var</span> sum <span class="token operator">=</span> <span class="token function">tco</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> y</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">if</span> <span class="token punctuation">(</span>y <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> <span class="token function">sum</span><span class="token punctuation">(</span>x <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span> y <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span>
- <span class="token punctuation">}</span>
- <span class="token keyword">else</span> <span class="token punctuation">{</span>
- <span class="token keyword">return</span> x
- <span class="token punctuation">}</span>
- <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
- <span class="token function">sum</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">100000</span><span class="token punctuation">)</span>
- <span class="token comment">// 100001</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br></div></div><p>上面代码中,<code>tco</code>函数是尾递归优化的实现,它的奥妙就在于状态变量<code>active</code>。默认情况下,这个变量是不激活的。一旦进入尾递归优化的过程,这个变量就激活了。然后,每一轮递归<code>sum</code>返回的都是<code>undefined</code>,所以就避免了递归执行;而<code>accumulated</code>数组存放每一轮<code>sum</code>执行的参数,总是有值的,这就保证了<code>accumulator</code>函数内部的<code>while</code>循环总是会执行。这样就很巧妙地将“递归”改成了“循环”,而后一轮的参数会取代前一轮的参数,保证了调用栈只有一层。</p> <h2 id="函数参数的尾逗号"><a href="#函数参数的尾逗号" class="header-anchor">#</a> 函数参数的尾逗号</h2> <p>ES2017 <a href="https://github.com/jeffmo/es-trailing-function-commas" target="_blank" rel="noopener noreferrer">允许<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>函数的最后一个参数有尾逗号(trailing comma)。</p> <p>此前,函数定义和调用时,都不允许最后一个参数后面出现逗号。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">clownsEverywhere</span><span class="token punctuation">(</span>
- <span class="token parameter">param1<span class="token punctuation">,</span>
- param2</span>
- <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span>
- <span class="token function">clownsEverywhere</span><span class="token punctuation">(</span>
- <span class="token string">'foo'</span><span class="token punctuation">,</span>
- <span class="token string">'bar'</span>
- <span class="token punctuation">)</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>上面代码中,如果在<code>param2</code>或<code>bar</code>后面加一个逗号,就会报错。</p> <p>如果像上面这样,将参数写成多行(即每个参数占据一行),以后修改代码的时候,想为函数<code>clownsEverywhere</code>添加第三个参数,或者调整参数的次序,就势必要在原来最后一个参数后面添加一个逗号。这对于版本管理系统来说,就会显示添加逗号的那一行也发生了变动。这看上去有点冗余,因此新的语法允许定义和调用时,尾部直接有一个逗号。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token function">clownsEverywhere</span><span class="token punctuation">(</span>
- <span class="token parameter">param1<span class="token punctuation">,</span>
- param2<span class="token punctuation">,</span></span>
- <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">/* ... */</span> <span class="token punctuation">}</span>
- <span class="token function">clownsEverywhere</span><span class="token punctuation">(</span>
- <span class="token string">'foo'</span><span class="token punctuation">,</span>
- <span class="token string">'bar'</span><span class="token punctuation">,</span>
- <span class="token punctuation">)</span><span class="token punctuation">;</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>这样的规定也使得,函数参数与数组和对象的尾逗号规则,保持一致了。</p> <h2 id="function-prototype-tostring"><a href="#function-prototype-tostring" class="header-anchor">#</a> Function.prototype.toString()</h2> <p><a href="https://github.com/tc39/Function-prototype-toString-revision" target="_blank" rel="noopener noreferrer">ES2019<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 对函数实例的<code>toString()</code>方法做出了修改。</p> <p><code>toString()</code>方法返回函数代码本身,以前会省略注释和空格。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token comment">/* foo comment */</span> <span class="token function">foo</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
- foo<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
- <span class="token comment">// function foo() {}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>上面代码中,函数<code>foo</code>的原始代码包含注释,函数名<code>foo</code>和圆括号之间有空格,但是<code>toString()</code>方法都把它们省略了。</p> <p>修改后的<code>toString()</code>方法,明确要求返回一模一样的原始代码。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">function</span> <span class="token comment">/* foo comment */</span> <span class="token function">foo</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
- foo<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
- <span class="token comment">// "function /* foo comment */ foo () {}"</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><h2 id="catch-命令的参数省略"><a href="#catch-命令的参数省略" class="header-anchor">#</a> catch 命令的参数省略</h2> <p>JavaScript 语言的<code>try...catch</code>结构,以前明确要求<code>catch</code>命令后面必须跟参数,接受<code>try</code>代码块抛出的错误对象。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">try</span> <span class="token punctuation">{</span>
- <span class="token comment">// ...</span>
- <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
- <span class="token comment">// 处理错误</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>上面代码中,<code>catch</code>命令后面带有参数<code>err</code>。</p> <p>很多时候,<code>catch</code>代码块可能用不到这个参数。但是,为了保证语法正确,还是必须写。<a href="https://github.com/tc39/proposal-optional-catch-binding" target="_blank" rel="noopener noreferrer">ES2019<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 做出了改变,允许<code>catch</code>语句省略参数。</p> <div class="language-javascript line-numbers-mode"><pre class="language-javascript"><code><span class="token keyword">try</span> <span class="token punctuation">{</span>
- <span class="token comment">// ...</span>
- <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">{</span>
- <span class="token comment">// ...</span>
- <span class="token punctuation">}</span>
- </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div></div></div> <div class="page-edit"><div class="edit-link"><a href="https://github.com/heBody/blog/edit/master/docs/《ES6 教程》笔记/08.函数的扩展.md" target="_blank" rel="noopener noreferrer">编辑</a> <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></div> <div class="tags"><a href="/blog/tags/?tag=ES6" title="标签">#ES6</a></div> <div class="last-updated"><span class="prefix">上次更新:</span> <span class="time">2022/12/14, 19:36:42</span></div></div> <div class="page-nav-wapper"><div class="page-nav-centre-wrap"><a href="/blog/pages/5dfea9a0f2d1a392/" class="page-nav-centre page-nav-centre-prev"><div class="tooltip">数值的扩展</div></a> <a href="/blog/pages/e34009d60d8bc4b2/" class="page-nav-centre page-nav-centre-next"><div class="tooltip">数组的扩展</div></a></div> <div class="page-nav"><p class="inner"><span class="prev">
- ←
- <a href="/blog/pages/5dfea9a0f2d1a392/" class="prev">数值的扩展</a></span> <span class="next"><a href="/blog/pages/e34009d60d8bc4b2/">数组的扩展</a>→
- </span></p></div></div></div> <div class="article-list"><div class="article-title"><a href="/blog/archives/" class="iconfont icon-bi">最近更新</a></div> <div class="article-wrapper"><dl><dd>01</dd> <dt><a href="/blog/pages/922650/"><div>
- Git修改分支名
- <!----></div></a> <span class="date">08-11</span></dt></dl><dl><dd>02</dd> <dt><a href="/blog/pages/55f894/"><div>
- CSS给table的tbody添加滚动条
- <!----></div></a> <span class="date">06-29</span></dt></dl><dl><dd>03</dd> <dt><a href="/blog/pages/829589/"><div>
- 我做了一个手写春联小网页,祝大家虎年暴富
- <span class="title-tag">
- 原创
- </span></div></a> <span class="date">01-28</span></dt></dl> <dl><dd></dd> <dt><a href="/blog/archives/" class="more">更多文章></a></dt></dl></div></div></main></div> <div class="footer"><div class="icons"><a href="mailto:30363811@qq.com" title="发邮件" target="_blank" class="iconfont icon-youjian"></a><a href="https://github.com/heBody" title="GitHub" target="_blank" class="iconfont icon-github"></a></div>
- Copyright © 2016-2022
- <span>Hesb | <a href="https://github.com/heBody/blob" target="_blank">MIT License</a></span></div> <div class="buttons"><div title="返回顶部" class="button blur go-to-top iconfont icon-fanhuidingbu" style="display:none;"></div> <div title="去评论" class="button blur go-to-comment iconfont icon-pinglun" style="display:none;"></div> <div title="主题模式" class="button blur theme-mode-but iconfont icon-zhuti"><ul class="select-box" style="display:none;"><li class="iconfont icon-zidong">
- 跟随系统
- </li><li class="iconfont icon-rijianmoshi">
- 浅色模式
- </li><li class="iconfont icon-yejianmoshi">
- 深色模式
- </li><li class="iconfont icon-yuedu">
- 阅读模式
- </li></ul></div></div> <!----> <!----> <!----></div><div class="global-ui"><div></div></div></div>
- <script src="/blog/assets/js/app.90754bd5.js" defer></script><script src="/blog/assets/js/2.106f41fb.js" defer></script><script src="/blog/assets/js/3.6748bd5c.js" defer></script><script src="/blog/assets/js/95.cddef6ae.js" defer></script>
- </body>
- </html>
|