index.html 391 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width,initial-scale=1">
  6. <title>面向对象 | 彪哥博客</title>
  7. <meta name="generator" content="VuePress 1.9.5">
  8. <link rel="icon" href="/blog/img/favicon.ico">
  9. <meta name="description" content="web前端技术博客,专注web前端学习与总结。JavaScript,js,ES6,TypeScript,vue,React,python,css3,html5,Node,git,github等技术文章。">
  10. <meta name="keywords" content="前端博客,个人技术博客,前端,前端开发,前端框架,web前端,前端面试题,技术文档,学习,面试,JavaScript,js,ES6,TypeScript,vue,python,css3,html5,Node,git,github,markdown">
  11. <meta name="theme-color" content="#11a8cd">
  12. <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/138.d976c801.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/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/95.cddef6ae.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">
  13. <link rel="stylesheet" href="/blog/assets/css/0.styles.dc03b589.css">
  14. </head>
  15. <body class="theme-mode-light">
  16. <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">
  17. 个人游戏网站
  18. <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">
  19. GitHub
  20. <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">
  21. 个人游戏网站
  22. <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">
  23. GitHub
  24. <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/0796ba76b4b55368/" class="sidebar-link">基础</a></li><li><a href="/blog/pages/74d2ab3fbfeaaa68/" class="sidebar-link">内置对象</a></li><li><a href="/blog/pages/659b5af5e2e704e0/" aria-current="page" class="active sidebar-link">面向对象</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level2"><a href="/blog/pages/659b5af5e2e704e0/#一、实例对象与new命令" class="sidebar-link">一、实例对象与new命令</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_1、对象是什么" class="sidebar-link">1、对象是什么</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_2、构造函数" class="sidebar-link">2、构造函数</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_3、new命令" class="sidebar-link">3、new命令</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#基本用法" class="sidebar-link">基本用法</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#new-命令的原理" class="sidebar-link">new 命令的原理</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#new-target-属性" class="sidebar-link">new.target 属性</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_4、object-create-创建实例对象" class="sidebar-link">4、Object.create()创建实例对象</a></li></ul></li><li class="sidebar-sub-header level2"><a href="/blog/pages/659b5af5e2e704e0/#二、this关键字" class="sidebar-link">二、this关键字</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_1、涵义" class="sidebar-link">1、涵义</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_2、实质" class="sidebar-link">2、实质</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_3、使用场合" class="sidebar-link">3、使用场合</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_1-全局环境" class="sidebar-link">(1)全局环境</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_2-构造函数" class="sidebar-link">(2)构造函数</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_3-对象的方法" class="sidebar-link">(3)对象的方法</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_4、使用注意点" class="sidebar-link">4、使用注意点</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_1-避免多层-this" class="sidebar-link">(1)避免多层 this</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_2-避免数组处理方法中的-this" class="sidebar-link">(2)避免数组处理方法中的 this</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_3-避免回调函数中的-this" class="sidebar-link">(3)避免回调函数中的 this</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_5、绑定-this-的方法" class="sidebar-link">5、绑定 this 的方法</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#function-prototype-call" class="sidebar-link">Function.prototype.call()</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#语法" class="sidebar-link">语法</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#应用-调用对象的原生方法" class="sidebar-link">应用:调用对象的原生方法</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#function-prototype-apply" class="sidebar-link">Function.prototype.apply()</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#语法-2" class="sidebar-link">语法</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#应用" class="sidebar-link">应用:</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#_1-获取数组最大-最小元素" class="sidebar-link">(1)获取数组最大/最小元素</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#_2-将数组的空元素变为undefined" class="sidebar-link">(2)将数组的空元素变为undefined</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#_3-转换类似数组的对象" class="sidebar-link">(3)转换类似数组的对象</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#_4-绑定回调函数的对象" class="sidebar-link">(4)绑定回调函数的对象</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#function-prototype-bind" class="sidebar-link">Function.prototype.bind()</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#语法-3" class="sidebar-link">语法</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#bind要注意的点" class="sidebar-link">bind要注意的点:</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#_1-每一次返回一个新函数" class="sidebar-link">(1)每一次返回一个新函数</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#_2-结合回调函数使用" class="sidebar-link">(2)结合回调函数使用</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#_3-结合call方法使用-改写数组方法调用形式" class="sidebar-link">(3)结合call方法使用(改写数组方法调用形式)</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#总结-call-、apply-、bind-的区别" class="sidebar-link">总结,call()、apply()、bind()的区别</a></li></ul></li><li class="sidebar-sub-header level2"><a href="/blog/pages/659b5af5e2e704e0/#三、对象的继承" class="sidebar-link">三、对象的继承</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_1、原型对象概述" class="sidebar-link">1、原型对象概述</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_1-构造函数的缺点" class="sidebar-link">(1)构造函数的缺点</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_2-prototype-属性的作用" class="sidebar-link">(2)prototype 属性的作用</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_3-原型链" class="sidebar-link">(3)原型链</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_4-constructor-属性" class="sidebar-link">(4)constructor 属性</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#作用" class="sidebar-link">作用</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_2、instanceof-运算符" class="sidebar-link">2、instanceof 运算符</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#语法-4" class="sidebar-link">语法</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#用处" class="sidebar-link">用处</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_3、构造函数的继承" class="sidebar-link">3、构造函数的继承</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_4、多重继承" class="sidebar-link">4、多重继承</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_5、模块" class="sidebar-link">5、模块</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_1-基本的实现方法" class="sidebar-link">(1)基本的实现方法</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_2-封装私有变量" class="sidebar-link">(2)封装私有变量:</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#_2-1-构造函数的写法" class="sidebar-link">(2-1)构造函数的写法</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#_2-2-立即执行函数的写法" class="sidebar-link">(2-2)立即执行函数的写法</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_3-模块的放大模式-向模块添加新方法" class="sidebar-link">(3)模块的放大模式(向模块添加新方法)</a></li><li class="sidebar-sub-header level4"><a href="/blog/pages/659b5af5e2e704e0/#_4-输入全局变量-保证独立性" class="sidebar-link">(4)输入全局变量(保证独立性)</a></li></ul></li><li class="sidebar-sub-header level2"><a href="/blog/pages/659b5af5e2e704e0/#四、object-对象的相关方法" class="sidebar-link">四、Object 对象的相关方法</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_1、object-getprototypeof-获取原型对象" class="sidebar-link">1、Object.getPrototypeOf() 获取原型对象</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_2、object-setprototypeof-设置原型对象" class="sidebar-link">2、Object.setPrototypeOf() 设置原型对象</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#使用object-setprototypeof方法模拟new命令" class="sidebar-link">使用`Object.setPrototypeOf`方法模拟`new`命令</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_3、-object-create-创建实例对象-指向目标对象的原型" class="sidebar-link">3、 Object.create() 创建实例对象,指向目标对象的原型</a></li><li class="sidebar-sub-header level5"><a href="/blog/pages/659b5af5e2e704e0/#内部实现原理" class="sidebar-link">内部实现原理</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_4、object-prototype-isprototypeof-判断某个对象是否为参数对象的原型" class="sidebar-link">4、Object.prototype.isPrototypeOf()判断某个对象是否为参数对象的原型</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_5、object-prototype-proto-返回该对象的原型-可读写" class="sidebar-link">5、Object.prototype.__proto__ 返回该对象的原型,可读写</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_6、获取原型对象方法的比较" class="sidebar-link">6、获取原型对象方法的比较</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_7、object-getownpropertynames" class="sidebar-link">7、Object.getOwnPropertyNames()</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_8、object-prototype-hasownproperty" class="sidebar-link">8、Object.prototype.hasOwnProperty()</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_9、in-运算符和-for-in-循环" class="sidebar-link">9、in 运算符和 for...in 循环</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_10、对象的拷贝" class="sidebar-link">10、对象的拷贝</a></li></ul></li><li class="sidebar-sub-header level2"><a href="/blog/pages/659b5af5e2e704e0/#五、严格模式" class="sidebar-link">五、严格模式</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_1、设计目的" class="sidebar-link">1、设计目的</a></li><li class="sidebar-sub-header level3"><a href="/blog/pages/659b5af5e2e704e0/#_2、启用方法" class="sidebar-link">2、启用方法</a></li></ul></li><li class="sidebar-sub-header level2"><a href="/blog/pages/659b5af5e2e704e0/#文档" class="sidebar-link">文档</a></li></ul></li><li><a href="/blog/pages/d61b1cb4cdac1f63/" class="sidebar-link">异步操作</a></li><li><a href="/blog/pages/7d961b8030c6099e/" class="sidebar-link">DOM</a></li><li><a href="/blog/pages/10b2761db5a8e089/" class="sidebar-link">事件</a></li><li><a href="/blog/pages/bab4930124ad2c10/" 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/javascript/#《JavaScript教程》笔记" data-v-06970110>《JavaScript教程》笔记</a></li></ul> <div class="info" data-v-06970110><div title="作者" class="author iconfont icon-touxiang" data-v-06970110><a href="https://github.com/heBody" target="_blank" title="作者" class="beLink" data-v-06970110>heBody</a></div> <div title="创建时间" class="date iconfont icon-riqi" data-v-06970110><a href="javascript:;" data-v-06970110>2020-01-12</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,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAABH1JREFUSA3tVl1oHFUUPmdmd2ltklqbpJDiNnXFmgbFktho7YMPNiJSSZM0+CAYSkUELVhM6YuwIPpgoOKDqOBDC0XE2CQoNtQXBUFTTcCi+Wlh1V2TQExsUzcltd3M9Tt3ZjZzZ2fT+OJTL8yeM+eee757fmeJbq//KQL8X3DUSFOcfr7cRsRtxNQMWueeVzOkaITIGqQHNg5y8+jNW9ldM7A6nTpAjuolUikAwq7CE3WcM2RRDz+XGVgN3FptU/aUSlvq9Pa3iZ1+sgAqJyyAFqkipd9dqiwHF3P65YycLWc/6sqGrvoEoIp6DOFaX5h6+dnfjkWprwqsPk0dUGq5vySwDImC10KxFHgGL1SWoc92O3eVht09qdXNH11I2SsTsJYqMWzihqGMi+A+Garf3BAuuLI5oGlULyNfyB/HYNujwktOfRrMr5t77NmevqaUopx0grnKAyvVpmwUDB4x6FPXuGvYLTDwWsejwgtgkYKPqRJg8SV6xaiZ3ZTppGneS4yfH5/66fZSDHv+QZci/+h5c5UHtpy67JUqGppM0sh0Nc1dW6/N1W5Yoqat8/TU/VnadmdeW2PLLSyh0cvxBs3KbqTmwYPpxN4do/mzE8nEpvX/UMu2Wbp74zUAK5q6WkHns7V0eWkdPbPzd3rxkTGybadYySumVzhcaJFbs5UrEkQ/+CK8gF5dnh/6ciIZ73gwQ927L1IitoxKLXYP3SjYdOrHHfTZhRRlFyrorafPk20B3HPD1y2G3qKZME5Jcf3t/HUC13/8tSd++vqFveMUTwAUxSUFI1QekR1+bIze3D9MF2aq6cPvG72CgnldWCFqyRw3lwH8ZMerjTD9ElRO7Gv44wNpC90aASqGfVlz/Rx17srQ57/UU26hkhQqUB7dBR71WmzQhHUnblGmVOEw0jhbV1n9OlXUDCIRGaNV5Jp43N516fN7JmnTHdfp7Hgy0luO4aMhtkLL8Bi3bUWYvzh5Mn1dTxrL6QmGuRhGL/TiTTxRoEdTszSaq9GR0NGA3KdkOz3hqSV3MIDhQ5IVX/Ivx3umBti2es2h4eZby7x8br1rkf7Mo90AqC8aQ3sJeNzqFRu+vSANAQe3PL7l0HGOAdwDCeZYvNKeoZp1Qfs6Aipndh86HmFRi0LAnEO47wsqM6cdfjh3jBPUzhZy7nvlUfFsamED1VQt6aISHVymXZ/B2aCtIG8AI8xfobj2d3en1wWVhOeHELKmLQ1s211s88comkv4UCwWyF787mJdYXtNfhKAXVqnKTq8QZvGAGGOfaTo5pGZ/PwbUCr5+DPr/1J92JNHr9aOl/F3iI5+O1nfybsGxoimvZ3ViWSluDITw3P37mypheDIPY0tw7+O/5ApbkYw+zpfaUVu32Pi98+defdUhEpZkRFq0aqyNh9FuL9hpYbEm6iwi0z2REd09ZmyENEbuhjDWzKvZXTqKYaBIr3tt5kuPtQBZFvEUwHt60vfCNu41XsksH9Ij1BMMz1Y0OOunHNShFIP5868g5zeXmuLwL9T4b6Q2+KejgAAAABJRU5ErkJggg==">面向对象<!----></h1> <div class="theme-vdoing-content content__default"><h1 id="面向对象编程"><a href="#面向对象编程" class="header-anchor">#</a> 面向对象编程</h1> <h2 id="一、实例对象与new命令"><a href="#一、实例对象与new命令" class="header-anchor">#</a> 一、实例对象与new命令</h2> <h3 id="_1、对象是什么"><a href="#_1、对象是什么" class="header-anchor">#</a> 1、对象是什么</h3> <p>面向对象编程(Object Oriented Programming,缩写为 OOP)是目前主流的编程范式。它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。</p> <p>每一个对象都是<strong>功能中心</strong>,具有明确分工,可以完成<strong>接受信息、处理数据、发出信息</strong>等任务。对象<strong>可以复用</strong>,通过继承机制还<strong>可以定制</strong>。因此,面向对象编程具有<strong>灵活、代码可复用、高度模块化</strong>等特点,容易维护和开发,比起由一系列函数或指令组成的传统的<strong>过程式编程</strong>(procedural programming),更适合多人合作的<strong>大型软件项目</strong>。</p> <p>那么,“对象”(object)到底是什么?我们从两个层次来理解。</p> <p><strong>(1)对象是单个实物的抽象。</strong></p> <p>一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个与远程服务器的连接也可以是对象。当<strong>实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程。</strong></p> <p><strong>(2)对象是一个容器,封装了属性(property)和方法(method)。</strong></p> <p><strong>属性</strong>是对象的状态,<strong>方法</strong>是对象的行为(完成某种任务)。比如,我们可以把动物抽象为<code>animal</code>对象,使用“属性”记录具体是那一种动物,使用“方法”表示动物的某种行为(奔跑、捕猎、休息等等)。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> person <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// 对象</span>
  25. <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span> <span class="token comment">// 属性</span>
  26. <span class="token function-variable function">interest</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">// 方法(行为)</span>
  27. <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">+</span> <span class="token string">'正在喝酒'</span>
  28. <span class="token punctuation">}</span>
  29. <span class="token punctuation">}</span>
  30. person<span class="token punctuation">.</span><span class="token function">interest</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 张三正在喝酒</span>
  31. <span class="token comment">// 真实世界: 有一个人,名字叫张三,他正在喝酒。</span>
  32. <span class="token comment">// 对象模拟: 一个人抽象为一个对象person,对象是一个容器,内部封装了属性name叫张三,他正在喝酒封装成方法interest</span>
  33. </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><h3 id="_2、构造函数"><a href="#_2、构造函数" class="header-anchor">#</a> 2、构造函数</h3> <p>面向对象编程的第一步,就是要生成对象。前面说过,对象是单个实物的抽象。通常需要一个模板,表示某一类实物的共同特征,然后对象根据这个模板生成。</p> <p>典型的面向对象编程语言(比如 C++ 和 Java),都有“类”(class)这个概念。所谓“类”就是对象的模板,对象就是“类”的实例。但是,JavaScript 语言的对象体系,不是基于“类”的,而是<strong>基于构造函数(constructor)和原型链(prototype)</strong>。</p> <p>JavaScript 语言<strong>使用构造函数(constructor)作为对象的模板</strong>。</p> <p><strong>所谓”构造函数”,就是专门用来生成实例对象的函数</strong>。</p> <p>它就是对象的模板,描述实例对象的基本结构。</p> <p>一个构造函数,可以生成多个实例对象,这些实例对象都有相同的结构。</p> <p>构造函数就是一个普通的函数,但是有自己的特征和用法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Vehicle</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>
  34. <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> <span class="token number">1000</span><span class="token punctuation">;</span>
  35. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  36. </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>Vehicle</code>就是构造函数。为了与普通函数区别,构造函数名字的第一个字母通常大写。</p> <p>构造函数的特点有两个。</p> <ul><li>函数体内部使用了<code>this</code>关键字,代表了所要生成的对象实例。</li> <li>生成对象的时候,必须使用<code>new</code>命令。</li></ul> <p>下面先介绍<code>new</code>命令。</p> <h3 id="_3、new命令"><a href="#_3、new命令" class="header-anchor">#</a> 3、new命令</h3> <h4 id="基本用法"><a href="#基本用法" class="header-anchor">#</a> 基本用法</h4> <p><code>new</code>命令的作用:<strong>执行构造函数,返回一个实例对象</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Vehicle</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>
  37. <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> <span class="token number">1000</span><span class="token punctuation">;</span> <span class="token comment">// this指向实例对象v</span>
  38. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  39. <span class="token keyword">var</span> v <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vehicle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// new 执行一个构造函数,返回一个实例对象给v</span>
  40. v<span class="token punctuation">.</span>price <span class="token comment">// 1000</span>
  41. </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>new</code>命令,让构造函数<code>Vehicle</code>生成一个实例对象,保存在变量<code>v</code>中。这个新生成的实例对象,从构造函数<code>Vehicle</code>得到了<code>price</code>属性。<code>new</code>命令执行时,构造函数内部的<code>this</code>,就代表了新生成的实例对象,<code>this.price</code>表示实例对象有一个<code>price</code>属性,值是1000。</p> <p>使用<code>new</code>命令时,根据需要,<strong>构造函数也可以接受参数</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Vehicle</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">p</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  42. <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> p<span class="token punctuation">;</span>
  43. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  44. <span class="token keyword">var</span> v <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vehicle</span><span class="token punctuation">(</span><span class="token number">500</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// new 命令时,构造函数可以接受参数</span>
  45. </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>new</code>命令本身就可以执行构造函数,所以后面的构造函数可以带括号,也<strong>可以不带括号</strong>。下面两行代码是等价的,但是为了表示这里是函数调用,<strong>推荐使用括号</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 推荐的写法</span>
  46. <span class="token keyword">var</span> v <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vehicle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  47. <span class="token comment">// 不推荐的写法</span>
  48. <span class="token keyword">var</span> v <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Vehicle</span><span class="token punctuation">;</span>
  49. </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>new</code>命令,直接调用构造函数会发生什么事?</p> <p>这种情况下,构造函数就变成了普通函数,并不会生成实例对象。而且由于后面会说到的原因,<code>this</code>这时代表全局对象,将造成一些意想不到的结果。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Vehicle</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>
  50. <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> <span class="token number">1000</span><span class="token punctuation">;</span>
  51. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  52. <span class="token keyword">var</span> v <span class="token operator">=</span> <span class="token function">Vehicle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 忘记使用new命令,构造函数变成普通函数,不会生成实例,函数内部this指向window</span>
  53. v <span class="token comment">// undefined 函数为普通函数且没有返回值,所有v为undefined</span>
  54. price <span class="token comment">// 1000 函数内部this指向window, price变成全局属性,等价于window.price</span>
  55. </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>Vehicle</code>构造函数时,忘了加上<code>new</code>命令。结果,变量<code>v</code>变成了<code>undefined</code>,而<code>price</code>属性变成了全局变量。因此,应该非常小心,避免不使用<code>new</code>命令、直接调用构造函数。</p> <p>为了保证构造函数必须与<code>new</code>命令一起使用,一个解决办法是,<strong>构造函数内部使用严格模式</strong>,即第一行加上<code>use strict</code>。这样的话,一旦忘了使用<code>new</code>命令,直接调用构造函数就会报错。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Fubar</span><span class="token punctuation">(</span><span class="token parameter">foo<span class="token punctuation">,</span> bar</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  56. <span class="token string">'use strict'</span><span class="token punctuation">;</span>
  57. <span class="token keyword">this</span><span class="token punctuation">.</span>_foo <span class="token operator">=</span> foo<span class="token punctuation">;</span> <span class="token comment">// 严格模式中this不能指向window对象,不加new调用this等于undefined,给undefined添加属性会报错</span>
  58. <span class="token keyword">this</span><span class="token punctuation">.</span>_bar <span class="token operator">=</span> bar<span class="token punctuation">;</span>
  59. <span class="token punctuation">}</span>
  60. <span class="token function">Fubar</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  61. <span class="token comment">// TypeError: Cannot set property '_foo' of undefined</span>
  62. </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>Fubar</code>为构造函数,<code>use strict</code>命令保证了该函数在严格模式下运行。由于<strong>严格模式中,函数内部的<code>this</code>不能指向全局对象</strong>,默认等于<code>undefined</code>,导致不加<code>new</code>调用会报错(JavaScript 不允许对<code>undefined</code>添加属性)。</p> <p><strong>另一个解决办法</strong>,构造函数内部判断是否使用<code>new</code>命令,如果发现没有使用,则直接返回一个实例对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Fubar</span><span class="token punctuation">(</span><span class="token parameter">foo<span class="token punctuation">,</span> bar</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  63. <span class="token comment">// instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链(),返回布尔值。</span>
  64. <span class="token comment">// 语法: &lt;实例对象&gt; instanceof &lt;构造函数&gt;</span>
  65. <span class="token keyword">if</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 keyword">instanceof</span> <span class="token class-name">Fubar</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 或 使用 (!new.target) 判断是否使用new命令</span>
  66. <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Fubar</span><span class="token punctuation">(</span>foo<span class="token punctuation">,</span> bar<span class="token punctuation">)</span><span class="token punctuation">;</span>
  67. <span class="token punctuation">}</span>
  68. <span class="token keyword">this</span><span class="token punctuation">.</span>_foo <span class="token operator">=</span> foo<span class="token punctuation">;</span>
  69. <span class="token keyword">this</span><span class="token punctuation">.</span>_bar <span class="token operator">=</span> bar<span class="token punctuation">;</span>
  70. <span class="token punctuation">}</span>
  71. <span class="token function">Fubar</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 punctuation">.</span>_foo <span class="token comment">// 1</span>
  72. <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Fubar</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 punctuation">)</span><span class="token punctuation">.</span>_foo <span class="token comment">// 1</span>
  73. </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></div></div><p>上面代码中的构造函数,不管加不加<code>new</code>命令,都会得到同样的结果。</p> <h4 id="new-命令的原理"><a href="#new-命令的原理" class="header-anchor">#</a> new 命令的原理</h4> <p>使用<code>new</code>命令时,它后面的函数依次执行下面的步骤。</p> <ol><li><p>创建一个空对象,作为将要返回的实例对象。</p></li> <li><p>将这个空对象的原型,指向构造函数的<code>prototype</code>属性。</p></li> <li><p>将这个空对象赋值给函数内部的<code>this</code>关键字。</p></li> <li><p>开始执行构造函数内部的代码。(代码中this指向空对象(实例对象))</p></li> <li><p>返回实例对象(或自定义对象)</p></li></ol> <p>也就是说,构造函数内部,<code>this</code>指的是一个新生成的空对象,<strong>所有针对<code>this</code>的操作,都会发生在这个空对象上</strong>。构造函数之所以叫“构造函数”,就是说这个函数的目的,就是操作一个空对象(即<code>this</code>对象),将其“构造”为需要的样子。</p> <p>如果构造函数内部有<code>return</code>语句,而且<code>return</code>后面跟着一个对象,<code>new</code>命令会返回<code>return</code>语句指定的对象;否则,就会不管<code>return</code>语句,返回<code>this</code>对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Vehicle</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>
  74. <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> <span class="token number">1000</span><span class="token punctuation">;</span>
  75. <span class="token keyword">return</span> <span class="token number">1000</span><span class="token punctuation">;</span> <span class="token comment">// 1000 非对象,被忽略,返回的是this对象;如果是return {},则会返回{}</span>
  76. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  77. <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Vehicle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token number">1000</span>
  78. <span class="token comment">// false</span>
  79. </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>Vehicle</code>的<code>return</code>语句返回一个数值。这时,<code>new</code>命令就会忽略这个<code>return</code>语句,返回“构造”后的<code>this</code>对象。</p> <p>但是,如果<code>return</code>语句返回的是一个跟<code>this</code>无关的新对象,<code>new</code>命令会返回这个新对象,而不是<code>this</code>对象。这一点需要特别引起注意。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Vehicle</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>
  80. <span class="token keyword">this</span><span class="token punctuation">.</span>price <span class="token operator">=</span> <span class="token number">1000</span><span class="token punctuation">;</span>
  81. <span class="token keyword">return</span> <span class="token punctuation">{</span> <span class="token literal-property property">price</span><span class="token operator">:</span> <span class="token number">2000</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">// return的是一个对象,会被返回出去。</span>
  82. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  83. <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Vehicle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>price
  84. <span class="token comment">// 2000</span>
  85. </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>Vehicle</code>的<code>return</code>语句,返回的是一个新对象。<code>new</code>命令会返回这个对象,而不是<code>this</code>对象。</p> <p>另一方面,如果对普通函数(内部没有<code>this</code>关键字的函数)使用<code>new</code>命令,则会返回一个空对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 内部沒有this关键字,会返回一个空对象</span>
  86. <span class="token keyword">return</span> <span class="token string">'this is a message'</span><span class="token punctuation">;</span>
  87. <span class="token punctuation">}</span>
  88. <span class="token keyword">var</span> msg <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  89. msg <span class="token comment">// {}</span>
  90. <span class="token keyword">typeof</span> msg <span class="token comment">// &quot;object&quot;</span>
  91. </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>getMessage</code>是一个普通函数,返回一个字符串。对它使用<code>new</code>命令,会得到一个空对象。这是因为**<code>new</code>命令总是返回一个对象,要么是实例对象,要么是<code>return</code>语句指定的对象**。本例中,<code>return</code>语句返回的是字符串,所以<code>new</code>命令就忽略了该语句。</p> <p><code>new</code>命令简化的内部流程,可以用下面的代码表示。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 构造函数</span>
  92. <span class="token keyword">function</span> <span class="token function">Person</span><span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span>age</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  93. <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name
  94. <span class="token keyword">this</span><span class="token punctuation">.</span>age <span class="token operator">=</span> age
  95. <span class="token punctuation">}</span>
  96. <span class="token comment">// 自定义_new</span>
  97. <span class="token keyword">function</span> <span class="token function">_new</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  98. <span class="token comment">// 将 arguments 对象转为数组</span>
  99. <span class="token keyword">var</span> args <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><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>
  100. <span class="token comment">// 取出构造函数</span>
  101. <span class="token keyword">var</span> constructor <span class="token operator">=</span> args<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>
  102. <span class="token comment">// 创建一个空对象,继承构造函数的 prototype 属性</span>
  103. <span class="token keyword">var</span> context <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>constructor<span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
  104. <span class="token comment">// 执行构造函数</span>
  105. <span class="token keyword">var</span> result <span class="token operator">=</span> <span class="token function">constructor</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>
  106. <span class="token comment">// 如果返回结果是对象,就直接返回,否则返回 context 对象</span>
  107. <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> result <span class="token operator">===</span> <span class="token string">'object'</span> <span class="token operator">&amp;&amp;</span> result <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token operator">?</span> result <span class="token operator">:</span> context<span class="token punctuation">;</span>
  108. <span class="token punctuation">}</span>
  109. <span class="token comment">// 自定义_new2</span>
  110. <span class="token keyword">function</span> <span class="token function">_new2</span><span class="token punctuation">(</span><span class="token comment">/* 构造函数 */</span> constructor<span class="token punctuation">,</span> <span class="token comment">/* 构造函数参数 */</span> params<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  111. <span class="token comment">// 创建一个空对象,继承构造函数的 prototype 属性</span>
  112. <span class="token keyword">var</span> context <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>constructor<span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
  113. <span class="token comment">// 执行构造函数</span>
  114. <span class="token keyword">var</span> result <span class="token operator">=</span> <span class="token function">constructor</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span>context<span class="token punctuation">,</span> params<span class="token punctuation">)</span><span class="token punctuation">;</span>
  115. <span class="token comment">// 如果返回结果是对象,就直接返回,否则返回 context 对象</span>
  116. <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> result <span class="token operator">===</span> <span class="token string">'object'</span> <span class="token operator">&amp;&amp;</span> result <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token operator">?</span> result <span class="token operator">:</span> context<span class="token punctuation">;</span>
  117. <span class="token comment">// (当用户在构造函数内部自定义返回对象的话则使用该对象,否则返回context)</span>
  118. <span class="token punctuation">}</span>
  119. <span class="token comment">// 通过自定义_new 返回实例</span>
  120. <span class="token keyword">var</span> actor <span class="token operator">=</span> <span class="token function">_new</span><span class="token punctuation">(</span>Person<span class="token punctuation">,</span> <span class="token string">'张三'</span><span class="token punctuation">,</span> <span class="token number">28</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  121. actor<span class="token punctuation">.</span>name <span class="token comment">// 张三</span>
  122. <span class="token comment">// 通过自定义_new2 返回实例</span>
  123. <span class="token keyword">var</span> actor2 <span class="token operator">=</span> <span class="token function">_new2</span><span class="token punctuation">(</span>Person<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">'李四'</span><span class="token punctuation">,</span> <span class="token number">29</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  124. actor2<span class="token punctuation">.</span>name <span class="token comment">// 李四</span>
  125. <span class="token comment">// 通过new命令 返回实例</span>
  126. <span class="token keyword">var</span> actor3 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Person</span><span class="token punctuation">(</span><span class="token string">'王五'</span><span class="token punctuation">,</span><span class="token number">30</span><span class="token punctuation">)</span>
  127. actor3<span class="token punctuation">.</span>name <span class="token comment">// 王五</span>
  128. </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><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br></div></div><h4 id="new-target-属性"><a href="#new-target-属性" class="header-anchor">#</a> new.target 属性</h4> <p>构造函数内部可以使用<code>new.target</code>属性。如果当前函数是<code>new</code>命令调用,<code>new.target</code><strong>指向当前函数</strong>,否则为<code>undefined</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><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>
  129. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">new</span><span class="token punctuation">.</span>target <span class="token operator">===</span> f<span class="token punctuation">)</span><span class="token punctuation">;</span>
  130. <span class="token punctuation">}</span>
  131. <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
  132. <span class="token keyword">new</span> <span class="token class-name">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// true</span>
  133. </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>使用这个属性,可以<strong>判断函数调用的时候,是否使用<code>new</code>命令</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><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>
  134. <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">new</span><span class="token punctuation">.</span>target<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  135. <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">'请使用 new 命令调用!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  136. <span class="token punctuation">}</span>
  137. <span class="token comment">// ...</span>
  138. <span class="token punctuation">}</span>
  139. <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// Uncaught Error: 请使用 new 命令调用!</span>
  140. </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>new</code>命令,就抛出一个错误。</p> <h3 id="_4、object-create-创建实例对象"><a href="#_4、object-create-创建实例对象" class="header-anchor">#</a> 4、Object.create()创建实例对象</h3> <p>构造函数作为模板,可以生成实例对象。但是,有时拿不到构造函数,只能拿到一个现有的对象。我们希望以这个现有的对象作为模板,生成新的实例对象,这时就可以使用<code>Object.create()</code>方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 这个例子没有构造函数,只有一个对象</span>
  141. <span class="token keyword">var</span> person1 <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// 这个对象用来生成实例对象,它被当成一个模板</span>
  142. <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span>
  143. <span class="token literal-property property">age</span><span class="token operator">:</span> <span class="token number">38</span><span class="token punctuation">,</span>
  144. <span class="token function-variable function">greeting</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>
  145. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Hi! I\'m '</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">+</span> <span class="token string">'.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  146. <span class="token punctuation">}</span>
  147. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  148. <span class="token keyword">var</span> person2 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>person1<span class="token punctuation">)</span><span class="token punctuation">;</span>
  149. person2<span class="token punctuation">.</span>name <span class="token comment">// 张三</span>
  150. person2<span class="token punctuation">.</span><span class="token function">greeting</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// Hi! I'm 张三.</span>
  151. </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>person1</code>是<code>person2</code>的模板,后者继承了前者的属性和方法。</p> <p><code>Object.create()</code>的详细介绍,请看后面的相关章节。</p> <h2 id="二、this关键字"><a href="#二、this关键字" class="header-anchor">#</a> 二、this关键字</h2> <h3 id="_1、涵义"><a href="#_1、涵义" class="header-anchor">#</a> 1、涵义</h3> <p><code>this</code>关键字是一个非常重要的语法点。毫不夸张地说,不理解它的含义,大部分开发任务都无法完成。</p> <p>前一章已经提到,<code>this</code>可以用在构造函数之中,表示实例对象。除此之外,<code>this</code>还可以用在别的场合。但不管是什么场合,<code>this</code>都有一个共同点:<strong>它总是返回一个对象</strong>。</p> <p>简单说,<strong><code>this</code>就是属性或方法“当前”所在的对象</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">this</span><span class="token punctuation">.</span>property <span class="token comment">// this代表property属性当前所在的对象</span>
  152. </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>上面代码中,<code>this</code>就代表<code>property</code>属性当前所在的对象。</p> <p>下面是一个实际的例子。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> person <span class="token operator">=</span> <span class="token punctuation">{</span>
  153. <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span>
  154. <span class="token function-variable function">describe</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>
  155. <span class="token keyword">return</span> <span class="token string">'姓名:'</span><span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">;</span>
  156. <span class="token punctuation">}</span>
  157. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  158. person<span class="token punctuation">.</span><span class="token function">describe</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  159. <span class="token comment">// &quot;姓名:张三&quot;</span>
  160. </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>this.name</code>表示<code>name</code>属性所在的那个对象。由于<code>this.name</code>是在<code>describe</code>方法中调用,而<code>describe</code>方法所在的当前对象是<code>person</code>,因此<code>this</code>指向<code>person</code>,<code>this.name</code>就是<code>person.name</code>。</p> <p>由于对象的属性可以赋给另一个对象,所以属性所在的当前对象是可变的,即<code>this</code><strong>的指向是可变的</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token constant">A</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  161. <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span>
  162. <span class="token function-variable function">describe</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>
  163. <span class="token keyword">return</span> <span class="token string">'姓名:'</span><span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">;</span>
  164. <span class="token punctuation">}</span>
  165. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  166. <span class="token keyword">var</span> <span class="token constant">B</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  167. <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'李四'</span>
  168. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  169. <span class="token constant">B</span><span class="token punctuation">.</span>describe <span class="token operator">=</span> <span class="token constant">A</span><span class="token punctuation">.</span>describe<span class="token punctuation">;</span>
  170. <span class="token comment">/* 此时B的值:
  171. B={
  172. name: '李四',
  173. describe: function () {
  174. return '姓名:'+ this.name;
  175. }
  176. }
  177. */</span>
  178. <span class="token constant">B</span><span class="token punctuation">.</span><span class="token function">describe</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// B内部的this指向B对象</span>
  179. <span class="token comment">// &quot;姓名:李四&quot;</span>
  180. </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></div></div><p>上面代码中,<code>A.describe</code>属性被赋给<code>B</code>,于是<code>B.describe</code>就表示<code>describe</code>方法所在的当前对象是<code>B</code>,所以<code>this.name</code>就指向<code>B.name</code>。</p> <p>稍稍重构这个例子,<code>this</code>的动态指向就能看得更清楚。</p> <div class="language-JS line-numbers-mode"><pre class="language-js"><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>
  181. <span class="token keyword">return</span> <span class="token string">'姓名:'</span><span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">;</span>
  182. <span class="token punctuation">}</span>
  183. <span class="token keyword">var</span> <span class="token constant">A</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  184. <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span>
  185. <span class="token literal-property property">describe</span><span class="token operator">:</span> f
  186. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  187. <span class="token keyword">var</span> <span class="token constant">B</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  188. <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'李四'</span><span class="token punctuation">,</span>
  189. <span class="token literal-property property">describe</span><span class="token operator">:</span> f
  190. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  191. <span class="token constant">A</span><span class="token punctuation">.</span><span class="token function">describe</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// &quot;姓名:张三&quot;</span>
  192. <span class="token constant">B</span><span class="token punctuation">.</span><span class="token function">describe</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// &quot;姓名:李四&quot;</span>
  193. </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></div></div><p>上面代码中,函数<code>f</code>内部使用了<code>this</code>关键字,随着<code>f</code>所在的对象不同,<code>this</code>的指向也不同。</p> <p>只要函数被赋给另一个变量,<code>this</code>的指向就会变。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token constant">A</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  194. <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span>
  195. <span class="token function-variable function">describe</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>
  196. <span class="token keyword">return</span> <span class="token string">'姓名:'</span><span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">;</span>
  197. <span class="token punctuation">}</span>
  198. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  199. <span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">'李四'</span><span class="token punctuation">;</span>
  200. <span class="token keyword">var</span> f <span class="token operator">=</span> <span class="token constant">A</span><span class="token punctuation">.</span>describe<span class="token punctuation">;</span>
  201. <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// &quot;姓名:李四&quot; f函数中的this和name变量所在的对象都为window顶层对象</span>
  202. </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>A.describe</code>被赋值给变量<code>f</code>,内部的<code>this</code>就会指向<code>f</code>运行时所在的对象(本例是顶层对象)。</p> <p>再看一个网页编程的例子。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token operator">&lt;</span>input type<span class="token operator">=</span><span class="token string">&quot;text&quot;</span> name<span class="token operator">=</span><span class="token string">&quot;age&quot;</span> size<span class="token operator">=</span><span class="token number">3</span> onChange<span class="token operator">=</span><span class="token string">&quot;validate(this, 18, 99);&quot;</span><span class="token operator">&gt;</span>
  203. <span class="token operator">&lt;</span>script<span class="token operator">&gt;</span>
  204. <span class="token keyword">function</span> <span class="token function">validate</span><span class="token punctuation">(</span><span class="token parameter">obj<span class="token punctuation">,</span> lowval<span class="token punctuation">,</span> hival</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  205. <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>obj<span class="token punctuation">.</span>value <span class="token operator">&lt;</span> lowval<span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">(</span>obj<span class="token punctuation">.</span>value <span class="token operator">&gt;</span> hival<span class="token punctuation">)</span><span class="token punctuation">)</span>
  206. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Invalid Value!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  207. <span class="token punctuation">}</span>
  208. <span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">&gt;</span>
  209. </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>onChange</code>回调函数,验证这个值是否在指定范围。浏览器会向回调函数传入当前对象,因此<code>this</code>就代表传入当前对象(即文本框),然后就可以从<code>this.value</code>上面读到用户的输入值。</p> <p>总结一下,<strong>JavaScript 语言之中,一切皆对象,运行环境也是对象,所以函数都是在某个对象之中运行,<code>this</code>就是函数运行时所在的对象(环境)</strong>。这本来并不会让用户糊涂,但是 JavaScript 支持运行环境动态切换,也就是说,<code>this</code>的指向是动态的,没有办法事先确定到底指向哪个对象,这才是最让初学者感到困惑的地方。</p> <h3 id="_2、实质"><a href="#_2、实质" class="header-anchor">#</a> 2、实质</h3> <p>JavaScript 语言之所以有 this 的设计,跟内存里面的数据结构有关系。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token literal-property property">foo</span><span class="token operator">:</span> <span class="token number">5</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
  210. </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>上面的代码将一个对象赋值给变量<code>obj</code>。JavaScript 引擎会先在内存里面,生成一个对象<code>{ foo: 5 }</code>,然后把这个对象的内存地址赋值给变量<code>obj</code>。也就是说,变量<code>obj</code>是一个地址(reference)。后面如果要读取<code>obj.foo</code>,引擎先从<code>obj</code>拿到内存地址,然后再从该地址读出原始的对象,返回它的<code>foo</code>属性。</p> <p>原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。举例来说,上面例子的<code>foo</code>属性,实际上是以下面的形式保存的。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token punctuation">{</span>
  211. <span class="token literal-property property">foo</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  212. <span class="token punctuation">[</span><span class="token punctuation">[</span>value<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token number">5</span>
  213. <span class="token punctuation">[</span><span class="token punctuation">[</span>writable<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token boolean">true</span>
  214. <span class="token punctuation">[</span><span class="token punctuation">[</span>enumerable<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token boolean">true</span>
  215. <span class="token punctuation">[</span><span class="token punctuation">[</span>configurable<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token boolean">true</span>
  216. <span class="token punctuation">}</span>
  217. <span class="token punctuation">}</span>
  218. </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>value</code>属性里面。</p> <p>这样的结构是很清晰的,问题在于属性的值可能是一个函数。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token function-variable function">foo</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 punctuation">;</span>
  219. </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>这时,<strong>引擎会将函数单独保存在内存中</strong>,然后再将函数的地址赋值给<code>foo</code>属性的<code>value</code>属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token punctuation">{</span>
  220. <span class="token literal-property property">foo</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  221. <span class="token punctuation">[</span><span class="token punctuation">[</span>value<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token operator">:</span> 函数的地址
  222. <span class="token operator">...</span>
  223. <span class="token punctuation">}</span>
  224. <span class="token punctuation">}</span>
  225. </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-js line-numbers-mode"><pre class="language-js"><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>
  226. <span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token literal-property property">f</span><span class="token operator">:</span> f <span class="token punctuation">}</span><span class="token punctuation">;</span>
  227. <span class="token comment">// 单独执行</span>
  228. <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  229. <span class="token comment">// obj 环境执行</span>
  230. obj<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  231. </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>JavaScript 允许在函数体内部,引用当前环境的其他变量。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><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>
  232. 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>
  233. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  234. </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>x</code>。该变量由运行环境提供。</p> <p>现在问题就来了,由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,<code>this</code>就出现了,<strong>它的设计目的就是在函数体内部,指代函数当前的运行环境</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><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>
  235. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
  236. <span class="token punctuation">}</span>
  237. </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>this.x</code>就是指当前运行环境的<code>x</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><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>
  238. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
  239. <span class="token punctuation">}</span>
  240. <span class="token keyword">var</span> x <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>
  241. <span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span>
  242. <span class="token literal-property property">f</span><span class="token operator">:</span> f<span class="token punctuation">,</span>
  243. <span class="token literal-property property">x</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
  244. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  245. <span class="token comment">// 单独执行</span>
  246. <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 1 f方法当前运行环境为window对象</span>
  247. <span class="token comment">// obj 环境执行</span>
  248. obj<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">// 2 f方法当前运行环境为obj对象</span>
  249. </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>f</code>在全局环境执行,<code>this.x</code>指向全局环境的<code>x</code>;在<code>obj</code>环境执行,<code>this.x</code>指向<code>obj.x</code>。</p> <h3 id="_3、使用场合"><a href="#_3、使用场合" class="header-anchor">#</a> 3、使用场合</h3> <p><code>this</code>主要有以下几个使用场合。</p> <h4 id="_1-全局环境"><a href="#_1-全局环境" class="header-anchor">#</a> (1)全局环境</h4> <p>全局环境使用<code>this</code>,它指的就是顶层对象<code>window</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">this</span> <span class="token operator">===</span> window <span class="token comment">// true</span>
  250. <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>
  251. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">===</span> window<span class="token punctuation">)</span><span class="token punctuation">;</span>
  252. <span class="token punctuation">}</span>
  253. <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// true</span>
  254. </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>window</code>。</p> <h4 id="_2-构造函数"><a href="#_2-构造函数" class="header-anchor">#</a> (2)构造函数</h4> <p>构造函数中的<code>this</code>,指的是实例对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">Obj</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">p</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  255. <span class="token keyword">this</span><span class="token punctuation">.</span>p <span class="token operator">=</span> p<span class="token punctuation">;</span> <span class="token comment">// this指向实例对象,在实例对象上定义属性p等于p值</span>
  256. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  257. <span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Obj</span><span class="token punctuation">(</span><span class="token string">'Hello World!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 通过new构造函数来声明实例对象o</span>
  258. o<span class="token punctuation">.</span>p <span class="token comment">// &quot;Hello World!&quot;</span>
  259. </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>Obj</code>。由于<code>this</code>指向实例对象,所以在构造函数内部定义<code>this.p</code>,就相当于定义实例对象有一个<code>p</code>属性。</p> <h4 id="_3-对象的方法"><a href="#_3-对象的方法" class="header-anchor">#</a> (3)对象的方法</h4> <p>如果对象的方法里面包含<code>this</code>,<code>this</code>的指向就是方法运行时所在的对象。该方法赋值给另一个对象,就会改变<code>this</code>的指向。</p> <p>但是,这条规则很不容易把握。请看下面的代码。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span><span class="token punctuation">{</span>
  260. <span class="token function-variable function">foo</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>
  261. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  262. <span class="token punctuation">}</span>
  263. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  264. obj<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">// obj</span>
  265. </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>obj.foo</code>方法执行时,它内部的<code>this</code>指向<code>obj</code>。</p> <p>但是,下面这几种用法,都会改变<code>this</code>的指向。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 情况一</span>
  266. <span class="token punctuation">(</span>obj<span class="token punctuation">.</span>foo <span class="token operator">=</span> obj<span class="token punctuation">.</span>foo<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// window</span>
  267. <span class="token comment">// 情况二</span>
  268. <span class="token punctuation">(</span><span class="token boolean">false</span> <span class="token operator">||</span> obj<span class="token punctuation">.</span>foo<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// window</span>
  269. <span class="token comment">// 情况三</span>
  270. <span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> obj<span class="token punctuation">.</span>foo<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// window</span>
  271. <span class="token comment">//obj.foo 是一个内存地址,它直接取出了 function () {console.log(this);} ,可以把obj.foo看做function () {console.log(this);}</span>
  272. </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>obj.foo</code>就是一个值。这个值真正调用的时候,运行环境已经不是<code>obj</code>了,而是全局环境,所以<code>this</code>不再指向<code>obj</code>。</p> <p>可以这样理解,JavaScript 引擎内部,<strong><code>obj</code>和<code>obj.foo</code>储存在两个内存地址</strong>,称为地址一和地址二。<code>obj.foo()</code>这样调用时,是从地址一调用地址二,因此地址二的运行环境是地址一,<code>this</code>指向<code>obj</code>。但是,<strong>上面三种情况,都是直接取出地址二进行调用,这样的话,运行环境就是全局环境,因此<code>this</code>指向全局环境</strong>。上面三种情况等同于下面的代码。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">//obj.foo 是一个内存地址,它直接取出了 function () {console.log(this);} ,可以把obj.foo看做function () {console.log(this);}</span>
  273. <span class="token comment">// 情况一</span>
  274. <span class="token punctuation">(</span>obj<span class="token punctuation">.</span><span class="token function-variable function">foo</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>
  275. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  276. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  277. <span class="token comment">// 等同于</span>
  278. <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>
  279. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  280. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  281. <span class="token comment">// 情况二</span>
  282. <span class="token punctuation">(</span><span class="token boolean">false</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>
  283. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  284. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  285. <span class="token comment">// 情况三</span>
  286. <span class="token punctuation">(</span><span class="token number">1</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>
  287. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  288. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  289. </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></div></div><p>如果<code>this</code>所在的方法不在对象的第一层,这时<code>this</code>只是指向当前一层的对象,而不会继承更上面的层。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">{</span>
  290. <span class="token literal-property property">p</span><span class="token operator">:</span> <span class="token string">'Hello'</span><span class="token punctuation">,</span>
  291. <span class="token literal-property property">b</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  292. <span class="token function-variable function">m</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>
  293. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>
  294. <span class="token punctuation">}</span>
  295. <span class="token punctuation">}</span>
  296. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  297. a<span class="token punctuation">.</span>b<span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// undefined</span>
  298. </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>a.b.m</code>方法在<code>a</code>对象的第二层,该方法内部的<code>this</code>不是指向<code>a</code>,而是指向<code>a.b</code>,因为实际执行的是下面的代码。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> b <span class="token operator">=</span> <span class="token punctuation">{</span>
  299. <span class="token function-variable function">m</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>
  300. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>
  301. <span class="token punctuation">}</span>
  302. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  303. <span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">{</span>
  304. <span class="token literal-property property">p</span><span class="token operator">:</span> <span class="token string">'Hello'</span><span class="token punctuation">,</span>
  305. <span class="token literal-property property">b</span><span class="token operator">:</span> b
  306. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  307. <span class="token punctuation">(</span>a<span class="token punctuation">.</span>b<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 等同于 b.m()</span>
  308. </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>如果要达到预期效果,只有写成下面这样。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">{</span>
  309. <span class="token literal-property property">b</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  310. <span class="token function-variable function">m</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>
  311. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>
  312. <span class="token punctuation">}</span><span class="token punctuation">,</span>
  313. <span class="token literal-property property">p</span><span class="token operator">:</span> <span class="token string">'Hello'</span>
  314. <span class="token punctuation">}</span>
  315. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  316. </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>this</code>依然会指向全局对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">{</span>
  317. <span class="token literal-property property">b</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  318. <span class="token function-variable function">m</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>
  319. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>
  320. <span class="token punctuation">}</span><span class="token punctuation">,</span>
  321. <span class="token literal-property property">p</span><span class="token operator">:</span> <span class="token string">'Hello'</span>
  322. <span class="token punctuation">}</span>
  323. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  324. <span class="token keyword">var</span> hello <span class="token operator">=</span> a<span class="token punctuation">.</span>b<span class="token punctuation">.</span>m<span class="token punctuation">;</span> <span class="token comment">// 把方法的内存地址赋值给了hello</span>
  325. <span class="token function">hello</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// undefined 内部this指向window</span>
  326. </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>m</code>是多层对象内部的一个方法。为求简便,将其赋值给<code>hello</code>变量,结果调用时,<code>this</code>指向了顶层对象。为了避免这个问题,可以只将<code>m</code>所在的对象赋值给<code>hello</code>,这样调用时,<code>this</code>的指向就不会变。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> hello <span class="token operator">=</span> a<span class="token punctuation">.</span>b<span class="token punctuation">;</span>
  327. hello<span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// Hello this指向不变</span>
  328. </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="_4、使用注意点"><a href="#_4、使用注意点" class="header-anchor">#</a> 4、使用注意点</h3> <h4 id="_1-避免多层-this"><a href="#_1-避免多层-this" class="header-anchor">#</a> (1)避免多层 this</h4> <p>由于<code>this</code>的指向是不确定的,所以切勿在函数中包含多层的<code>this</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  329. <span class="token function-variable function">f1</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>
  330. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// this指向o对象</span>
  331. <span class="token keyword">var</span> <span class="token function-variable function">f2</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">// 方法会另起一个内存地址</span>
  332. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// this指向window对象</span>
  333. <span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  334. <span class="token punctuation">}</span>
  335. <span class="token punctuation">}</span>
  336. o<span class="token punctuation">.</span><span class="token function">f1</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  337. <span class="token comment">// Object</span>
  338. <span class="token comment">// Window</span>
  339. </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>this</code>,结果运行后,第一层指向对象<code>o</code>,第二层指向全局对象,因为实际执行的是下面的代码。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">temp</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>
  340. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  341. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  342. <span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  343. <span class="token function-variable function">f1</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>
  344. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  345. <span class="token keyword">var</span> f2 <span class="token operator">=</span> <span class="token function">temp</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  346. <span class="token punctuation">}</span>
  347. <span class="token punctuation">}</span>
  348. </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>this</code>的变量。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  349. <span class="token function-variable function">f1</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>
  350. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  351. <span class="token keyword">var</span> that <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token comment">// 使用变量保存this</span>
  352. <span class="token keyword">var</span> <span class="token function-variable function">f2</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>
  353. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>that<span class="token punctuation">)</span><span class="token punctuation">;</span>
  354. <span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  355. <span class="token punctuation">}</span>
  356. <span class="token punctuation">}</span>
  357. o<span class="token punctuation">.</span><span class="token function">f1</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  358. <span class="token comment">// Object</span>
  359. <span class="token comment">// Object</span>
  360. </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></div></div><p>上面代码定义了变量<code>that</code>,固定指向外层的<code>this</code>,然后在内层使用<code>that</code>,就不会发生<code>this</code>指向的改变。</p> <p>事实上,使用一个变量固定<code>this</code>的值,然后内层函数调用这个变量,是非常常见的做法,请务必掌握。</p> <p>JavaScript 提供了严格模式,也可以硬性避免这种问题。严格模式下,如果函数内部的<code>this</code>指向顶层对象,就会报错。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> counter <span class="token operator">=</span> <span class="token punctuation">{</span>
  361. <span class="token literal-property property">count</span><span class="token operator">:</span> <span class="token number">0</span>
  362. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  363. counter<span class="token punctuation">.</span><span class="token function-variable function">inc</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>
  364. <span class="token string">'use strict'</span><span class="token punctuation">;</span>
  365. <span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token operator">++</span>
  366. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  367. <span class="token keyword">var</span> f <span class="token operator">=</span> counter<span class="token punctuation">.</span>inc<span class="token punctuation">;</span> <span class="token comment">// 这里直接取出方法的内存地址赋值给f,运行环境为window</span>
  368. <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  369. <span class="token comment">// TypeError: Cannot read property 'count' of undefined</span>
  370. </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>inc</code>方法通过<code>'use strict'</code>声明采用严格模式,这时内部的<code>this</code>一旦指向顶层对象,就会报错。</p> <h4 id="_2-避免数组处理方法中的-this"><a href="#_2-避免数组处理方法中的-this" class="header-anchor">#</a> (2)避免数组处理方法中的 this</h4> <p>数组的<code>map</code>和<code>foreach</code>方法,允许提供一个函数作为参数。这个函数内部不应该使用<code>this</code> 。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  371. <span class="token literal-property property">v</span><span class="token operator">:</span> <span class="token string">'hello'</span><span class="token punctuation">,</span>
  372. <span class="token literal-property property">p</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">'a1'</span><span class="token punctuation">,</span> <span class="token string">'a2'</span> <span class="token punctuation">]</span><span class="token punctuation">,</span>
  373. <span class="token function-variable function">f</span><span class="token operator">:</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>
  374. <span class="token keyword">this</span><span class="token punctuation">.</span>p<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> <span class="token comment">// 这个方法的运行环境为window</span>
  375. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>v <span class="token operator">+</span> <span class="token string">' '</span> <span class="token operator">+</span> item<span class="token punctuation">)</span><span class="token punctuation">;</span>
  376. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  377. <span class="token punctuation">}</span>
  378. <span class="token punctuation">}</span>
  379. o<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  380. <span class="token comment">// undefined a1</span>
  381. <span class="token comment">// undefined a2</span>
  382. </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></div></div><p>上面代码中,<code>foreach</code>方法的回调函数中的<code>this</code>,其实是指向<code>window</code>对象,因此取不到<code>o.v</code>的值。原因跟上一段的多层<code>this</code>是一样的,就是内层的<code>this</code>不指向外部,而指向顶层对象。</p> <p>解决这个问题的一种方法,就是前面提到的,使用中间变量固定<code>this</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  383. <span class="token literal-property property">v</span><span class="token operator">:</span> <span class="token string">'hello'</span><span class="token punctuation">,</span>
  384. <span class="token literal-property property">p</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">'a1'</span><span class="token punctuation">,</span> <span class="token string">'a2'</span> <span class="token punctuation">]</span><span class="token punctuation">,</span>
  385. <span class="token function-variable function">f</span><span class="token operator">:</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>
  386. <span class="token keyword">var</span> that <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
  387. <span class="token keyword">this</span><span class="token punctuation">.</span>p<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>
  388. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>that<span class="token punctuation">.</span>v<span class="token operator">+</span><span class="token string">' '</span><span class="token operator">+</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span>
  389. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  390. <span class="token punctuation">}</span>
  391. <span class="token punctuation">}</span>
  392. o<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  393. <span class="token comment">// hello a1</span>
  394. <span class="token comment">// hello a2</span>
  395. </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>this</code>当作<code>foreach</code>方法的第二个参数,固定它的运行环境。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  396. <span class="token literal-property property">v</span><span class="token operator">:</span> <span class="token string">'hello'</span><span class="token punctuation">,</span>
  397. <span class="token literal-property property">p</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">'a1'</span><span class="token punctuation">,</span> <span class="token string">'a2'</span> <span class="token punctuation">]</span><span class="token punctuation">,</span>
  398. <span class="token function-variable function">f</span><span class="token operator">:</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>
  399. <span class="token keyword">this</span><span class="token punctuation">.</span>p<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>
  400. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>v <span class="token operator">+</span> <span class="token string">' '</span> <span class="token operator">+</span> item<span class="token punctuation">)</span><span class="token punctuation">;</span>
  401. <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  402. <span class="token punctuation">}</span>
  403. <span class="token punctuation">}</span>
  404. o<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  405. <span class="token comment">// hello a1</span>
  406. <span class="token comment">// hello a2</span>
  407. </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></div></div><p>ES6箭头函数</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span>
  408. <span class="token literal-property property">v</span><span class="token operator">:</span> <span class="token string">'hello'</span><span class="token punctuation">,</span>
  409. <span class="token literal-property property">p</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">'a1'</span><span class="token punctuation">,</span> <span class="token string">'a2'</span> <span class="token punctuation">]</span><span class="token punctuation">,</span>
  410. <span class="token function-variable function">f</span><span class="token operator">:</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>
  411. <span class="token keyword">this</span><span class="token punctuation">.</span>p<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">item</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span> <span class="token comment">// 箭头函数使this指向为o对象</span>
  412. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>v<span class="token operator">+</span><span class="token string">' '</span><span class="token operator">+</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span>
  413. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  414. <span class="token punctuation">}</span>
  415. <span class="token punctuation">}</span>
  416. o<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  417. <span class="token comment">// hello a1</span>
  418. <span class="token comment">// hello a2</span>
  419. </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></div></div><h4 id="_3-避免回调函数中的-this"><a href="#_3-避免回调函数中的-this" class="header-anchor">#</a> (3)避免回调函数中的 this</h4> <p>回调函数中的<code>this</code>往往会改变指向,最好避免使用。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  420. o<span class="token punctuation">.</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>
  421. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">===</span> o<span class="token punctuation">)</span><span class="token punctuation">;</span>
  422. <span class="token punctuation">}</span>
  423. <span class="token comment">// jQuery 的写法</span>
  424. <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'#button'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> o<span class="token punctuation">.</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>
  425. </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>false</code>。原因是此时<code>this</code>不再指向<code>o</code>对象,而是指向按钮的 DOM 对象,因为<code>f</code>方法是在按钮对象的环境中被调用的。这种细微的差别,很容易在编程中忽视,导致难以察觉的错误。</p> <p>为了解决这个问题,可以采用下面的一些方法对<code>this</code>进行绑定,也就是使得<code>this</code>固定指向某个对象,减少不确定性。</p> <h3 id="_5、绑定-this-的方法"><a href="#_5、绑定-this-的方法" class="header-anchor">#</a> 5、绑定 this 的方法</h3> <p><code>this</code>的动态切换,固然为 JavaScript 创造了巨大的灵活性,但也使得编程变得困难和模糊。有时,需要把<code>this</code>固定下来,避免出现意想不到的情况。JavaScript 提供了<code>call</code>、<code>apply</code>、<code>bind</code>这三个方法,来切换/固定<code>this</code>的指向。</p> <h4 id="function-prototype-call"><a href="#function-prototype-call" class="header-anchor">#</a> Function.prototype.call()</h4> <p>函数实例的<code>call</code>方法,可以<strong>指定函数内部<code>this</code>的指向(即函数执行时所在的作用域),然后在所指定的作用域中,调用该函数。</strong></p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  426. <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>
  427. <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
  428. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  429. <span class="token function">f</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">===</span> window <span class="token comment">// true</span>
  430. <span class="token function">f</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token operator">===</span> obj <span class="token comment">// true 使函数内this指向obj,相当于把函数放入obj对象内运行。</span>
  431. </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>this</code>指向全局环境(浏览器为<code>window</code>对象);<code>call</code>方法可以改变<code>this</code>的指向,指定<code>this</code>指向对象<code>obj</code>,然后在对象<code>obj</code>的作用域中运行函数<code>f</code>。</p> <p><strong><code>call</code>方法的参数,应该是一个对象</strong>。如果参数为空、<code>null</code>和<code>undefined</code>,则<strong>默认传入全局对象</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> n <span class="token operator">=</span> <span class="token number">123</span><span class="token punctuation">;</span>
  432. <span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token literal-property property">n</span><span class="token operator">:</span> <span class="token number">456</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
  433. <span class="token keyword">function</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  434. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>n<span class="token punctuation">)</span><span class="token punctuation">;</span>
  435. <span class="token punctuation">}</span>
  436. <span class="token comment">// 参数为空、null、undefined时默认传入全局对象</span>
  437. <span class="token function">a</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 comment">// 123</span>
  438. <span class="token function">a</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token comment">// 123</span>
  439. <span class="token function">a</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token keyword">undefined</span><span class="token punctuation">)</span> <span class="token comment">// 123</span>
  440. <span class="token function">a</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span> <span class="token comment">// 123</span>
  441. <span class="token function">a</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token comment">// 456</span>
  442. </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>a</code>函数中的<code>this</code>关键字,如果指向全局对象,返回结果为<code>123</code>。如果使用<code>call</code>方法将<code>this</code>关键字指向<code>obj</code>对象,返回结果为<code>456</code>。可以看到,如果<code>call</code>方法没有参数,或者参数为<code>null</code>或<code>undefined</code>,则等同于指向全局对象。</p> <p>如果<code>call</code>方法的参数是一个原始值,那么这个<strong>原始值会自动转成对应的包装对象</strong>,然后传入<code>call</code>方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><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>
  443. <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
  444. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  445. <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 number">5</span><span class="token punctuation">)</span>
  446. <span class="token comment">// Number {[[PrimitiveValue]]: 5} // Number的实例对象</span>
  447. </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>call</code>的参数为<code>5</code>,不是对象,会被自动转成包装对象(<code>Number</code>的实例),绑定<code>f</code>内部的<code>this</code>。</p> <p><code>call</code>方法还可以接受多个参数。</p> <h5 id="语法"><a href="#语法" class="header-anchor">#</a> 语法</h5> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token function">func</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>thisValue<span class="token punctuation">,</span> arg1<span class="token punctuation">,</span> arg2<span class="token punctuation">,</span> <span class="token operator">...</span><span class="token punctuation">)</span>
  448. <span class="token comment">// 第一个参数thisValue为函数内this将要指向的对象</span>
  449. <span class="token comment">// 其余参数为传入函数的参数</span>
  450. </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>call</code>的第一个参数就是<code>this</code>所要指向的那个对象,后面的参数则是函数调用时所需的参数。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">add</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>
  451. <span class="token keyword">return</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span>
  452. <span class="token punctuation">}</span>
  453. <span class="token function">add</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> <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 comment">// 3</span>
  454. </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>call</code>方法指定函数<code>add</code>内部的<code>this</code>绑定当前环境(对象),并且参数为<code>1</code>和<code>2</code>,因此函数<code>add</code>运行后得到<code>3</code>。</p> <h5 id="应用-调用对象的原生方法"><a href="#应用-调用对象的原生方法" class="header-anchor">#</a> 应用:调用对象的原生方法</h5> <p><code>call</code>方法的一个应用是调用对象的原生方法。</p> <div class="language-JS line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  455. obj<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span><span class="token string">'toString'</span><span class="token punctuation">)</span> <span class="token comment">// false 判断obj是否有toString属性,注意这里不是toString()方法</span>
  456. <span class="token comment">// 覆盖掉继承的 hasOwnProperty 方法</span>
  457. obj<span class="token punctuation">.</span><span class="token function-variable function">hasOwnProperty</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>
  458. <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
  459. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  460. obj<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span><span class="token string">'toString'</span><span class="token punctuation">)</span> <span class="token comment">// true</span>
  461. <span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>obj<span class="token punctuation">,</span> <span class="token string">'toString'</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
  462. </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>hasOwnProperty</code>是<code>obj</code>对象继承的方法,如果这个方法一旦被覆盖,就不会得到正确结果。<code>call</code>方法可以解决这个问题,它将<code>hasOwnProperty</code>方法的原始定义放到<code>obj</code>对象上执行,这样无论<code>obj</code>上有没有同名方法,都不会影响结果。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> objArr <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// 类数组对象</span>
  463. <span class="token number">0</span><span class="token operator">:</span><span class="token string">'a'</span><span class="token punctuation">,</span>
  464. <span class="token number">1</span><span class="token operator">:</span><span class="token string">'b'</span><span class="token punctuation">,</span>
  465. <span class="token number">2</span><span class="token operator">:</span><span class="token string">'c'</span><span class="token punctuation">,</span>
  466. <span class="token literal-property property">length</span><span class="token operator">:</span><span class="token number">3</span>
  467. <span class="token punctuation">}</span>
  468. <span class="token keyword">var</span> arr <span class="token operator">=</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>objArr<span class="token punctuation">)</span> <span class="token comment">// 执行数组的slice方法,并把obj指定为方法的this</span>
  469. <span class="token comment">// 或 arr = [].slice.call(obj)</span>
  470. arr <span class="token comment">// ['a','b','c']</span>
  471. </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>objArr</code>是一个类似数组的对象,使用<code>call</code>调用数组的<code>slice</code>方法,指定<code>objArr</code>为<code>slice</code>方法内部的<code>this</code>,<code>slice</code>方法返回值赋给<code>arr</code>。</p> <p>另外来看看数组<code>slice</code>方法的内部实现原理</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">slice</span><span class="token operator">=</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">start<span class="token punctuation">,</span>end</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">//数组方法slice的底层内部实现</span>
  472. <span class="token keyword">var</span> result <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Array</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//新数组</span>
  473. <span class="token keyword">var</span> start <span class="token operator">=</span> start <span class="token operator">||</span> <span class="token number">0</span><span class="token punctuation">;</span>
  474. <span class="token keyword">var</span> end <span class="token operator">=</span> end <span class="token operator">||</span> <span class="token keyword">this</span><span class="token punctuation">.</span>length<span class="token punctuation">;</span> <span class="token comment">//this指向调用的对象,用了call之后,改变this的指向,指向传进来的对象</span>
  475. <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">var</span> i<span class="token operator">=</span>start<span class="token punctuation">;</span> i<span class="token operator">&lt;</span>end<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  476. result<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  477. <span class="token punctuation">}</span>
  478. <span class="token keyword">return</span> result<span class="token punctuation">;</span> <span class="token comment">//返回的为一个新的数组</span>
  479. <span class="token punctuation">}</span>
  480. </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><h4 id="function-prototype-apply"><a href="#function-prototype-apply" class="header-anchor">#</a> Function.prototype.apply()</h4> <p><code>apply</code>方法的作用与<code>call</code>方法类似,也是改变<code>this</code>指向,然后再调用该函数。唯一的区别就是,它接收一个数组作为函数执行时的参数,使用格式如下。</p> <h5 id="语法-2"><a href="#语法-2" class="header-anchor">#</a> 语法</h5> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token function">func</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span>thisValue<span class="token punctuation">,</span> <span class="token punctuation">[</span>arg1<span class="token punctuation">,</span> arg2<span class="token punctuation">,</span> <span class="token operator">...</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
  481. <span class="token comment">// 第一个参数thisValue为函数内this将要指向的对象</span>
  482. <span class="token comment">// 第二个参数为一个数组,数组每一项为传入函数的参数</span>
  483. </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>apply</code>方法的第一个参数也是<code>this</code>所要指向的那个对象,如果设为<code>null</code>或<code>undefined</code>,则等同于指定全局对象。第二个参数则是一个数组,该数组的所有成员依次作为参数,传入原函数。原函数的参数,在<code>call</code>方法中必须一个个添加,但是在<code>apply</code>方法中,必须以数组形式添加。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><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><span class="token punctuation">)</span><span class="token punctuation">{</span>
  484. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>x <span class="token operator">+</span> y<span class="token punctuation">)</span><span class="token punctuation">;</span>
  485. <span class="token punctuation">}</span>
  486. <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 keyword">null</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// 2</span>
  487. <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">null</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">1</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token comment">// 2</span>
  488. </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>apply</code>方法以后,就变成可以接受一个数组作为参数。</p> <p>利用这一点,可以做一些有趣的应用。</p> <h5 id="应用"><a href="#应用" class="header-anchor">#</a> 应用:</h5> <h5 id="_1-获取数组最大-最小元素"><a href="#_1-获取数组最大-最小元素" class="header-anchor">#</a> (1)获取数组最大/最小元素</h5> <p>JavaScript 不提供找出数组最大元素的函数。结合使用<code>apply</code>方法和<code>Math.max/Math.min</code>方法,就可以返回数组的最大/最小元素。</p> <p><strong>利用第二个参数为数组的特点</strong></p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 获取最大值</span>
  489. <span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">10</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">15</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  490. Math<span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> a<span class="token punctuation">)</span> <span class="token comment">// 15</span>
  491. <span class="token comment">//Math.max方法获取最大值</span>
  492. <span class="token comment">//Math.max(10, 2, 4, 15, 9) // 15</span>
  493. <span class="token comment">// 获取最小值</span>
  494. <span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">10</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">15</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  495. Math<span class="token punctuation">.</span><span class="token function">min</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> a<span class="token punctuation">)</span> <span class="token comment">// 15 null等于绑定全局对象</span>
  496. </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><h5 id="_2-将数组的空元素变为undefined"><a href="#_2-将数组的空元素变为undefined" class="header-anchor">#</a> (2)将数组的空元素变为<code>undefined</code></h5> <p>通过<code>apply</code>方法,利用<code>Array</code>构造函数将数组的空元素变成<code>undefined</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token function">Array</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">'a'</span><span class="token punctuation">,</span> <span class="token punctuation">,</span><span class="token string">'b'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
  497. <span class="token comment">// [ 'a', undefined, 'b' ]</span>
  498. </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>undefined</code>的差别在于,数组的<code>forEach</code>方法会跳过空元素,但是不会跳过<code>undefined</code>。因此,遍历内部元素的时候,会得到不同的结果。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'a'</span><span class="token punctuation">,</span> <span class="token punctuation">,</span> <span class="token string">'b'</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  499. <span class="token keyword">function</span> <span class="token function">print</span><span class="token punctuation">(</span><span class="token parameter">i</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  500. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span>
  501. <span class="token punctuation">}</span>
  502. a<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>print<span class="token punctuation">)</span>
  503. <span class="token comment">// a</span>
  504. <span class="token comment">// b</span>
  505. <span class="token function">Array</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> a<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>print<span class="token punctuation">)</span>
  506. <span class="token comment">// a</span>
  507. <span class="token comment">// undefined</span>
  508. <span class="token comment">// b</span>
  509. </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><h5 id="_3-转换类似数组的对象"><a href="#_3-转换类似数组的对象" class="header-anchor">#</a> (3)转换类似数组的对象</h5> <p>另外,利用数组对象的<code>slice</code>方法,可以将一个类似数组的对象(比如<code>arguments</code>对象)转为真正的数组。</p> <p>和使用<code>call</code>方法的效果一样。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><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">apply</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token literal-property property">length</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]</span>
  510. <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">apply</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token number">0</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>
  511. <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">apply</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token number">0</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token literal-property property">length</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, undefined]</span>
  512. <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">apply</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">length</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">// [undefined]</span>
  513. </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>apply</code>方法的参数都是对象,但是返回结果都是数组,这就起到了将对象转成数组的目的。从上面代码可以看到,这个方法起作用的前提是,被处理的对象必须有<code>length</code>属性,以及相对应的数字键。</p> <h5 id="_4-绑定回调函数的对象"><a href="#_4-绑定回调函数的对象" class="header-anchor">#</a> (4)绑定回调函数的对象</h5> <p>前面的按钮点击事件的例子,可以改写如下。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  514. o<span class="token punctuation">.</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>
  515. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">===</span> o<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// true ,如未改变this指向,this将指向 DOM 对象</span>
  516. <span class="token punctuation">}</span>
  517. <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>
  518. o<span class="token punctuation">.</span><span class="token function">f</span><span class="token punctuation">.</span><span class="token function">apply</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span><span class="token punctuation">;</span>
  519. <span class="token comment">// 或者 o.f.call(o);</span>
  520. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  521. <span class="token comment">// jQuery 的写法</span>
  522. <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'#button'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> f<span class="token punctuation">)</span><span class="token punctuation">;</span>
  523. </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></div></div><p>上面代码中,点击按钮以后,控制台将会显示<code>true</code>。由于<code>apply</code>方法(或者<code>call</code>方法)不仅绑定函数执行时所在的对象,还会立即执行函数,因此不得不把绑定语句写在一个函数体内。更简洁的写法是采用下面介绍的<code>bind</code>方法。</p> <h4 id="function-prototype-bind"><a href="#function-prototype-bind" class="header-anchor">#</a> Function.prototype.bind()</h4> <p><code>bind</code>方法用于将函数体内的<code>this</code>绑定到某个对象,然后<strong>返回一个新函数,bind方法并非立即执行一个函数。</strong></p> <h5 id="语法-3"><a href="#语法-3" class="header-anchor">#</a> 语法</h5> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token function">func</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>thisValue<span class="token punctuation">,</span> arg1<span class="token punctuation">,</span> arg2<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>
  524. <span class="token comment">// 第一个参数thisValue为函数内this将要指向的对象</span>
  525. <span class="token comment">// 其余参数为传入函数的参数</span>
  526. <span class="token comment">// bind方法返回一个新函数,并非立即执行,如需执行要在后面加个括号</span>
  527. </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><div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> d <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  528. d<span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 1481869925657</span>
  529. <span class="token keyword">var</span> print <span class="token operator">=</span> d<span class="token punctuation">.</span>getTime<span class="token punctuation">;</span>
  530. <span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// Uncaught TypeError: this is not a Date object.</span>
  531. </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>d.getTime</code>方法赋给变量<code>print</code>,然后调用<code>print</code>就报错了。这是因为<code>getTime</code>方法内部的<code>this</code>,绑定<code>Date</code>对象的实例,赋给变量<code>print</code>以后,内部的<code>this</code>已经不指向<code>Date</code>对象的实例了。</p> <p><code>bind</code>方法可以解决这个问题。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> print <span class="token operator">=</span> d<span class="token punctuation">.</span><span class="token function">getTime</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>d<span class="token punctuation">)</span><span class="token punctuation">;</span>
  532. <span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 1481869925657</span>
  533. </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>bind</code>方法将<code>getTime</code>方法内部的<code>this</code>绑定到<code>d</code>对象,这时就可以安全地将这个方法赋值给其他变量了。</p> <p><code>bind</code>方法的参数就是所要绑定<code>this</code>的对象,下面是一个更清晰的例子。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> counter <span class="token operator">=</span> <span class="token punctuation">{</span>
  534. <span class="token literal-property property">count</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
  535. <span class="token function-variable function">inc</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>
  536. <span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token operator">++</span><span class="token punctuation">;</span>
  537. <span class="token punctuation">}</span>
  538. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  539. <span class="token keyword">var</span> func <span class="token operator">=</span> counter<span class="token punctuation">.</span><span class="token function">inc</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>counter<span class="token punctuation">)</span><span class="token punctuation">;</span>
  540. <span class="token function">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  541. counter<span class="token punctuation">.</span>count <span class="token comment">// 1</span>
  542. </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>counter.inc</code>方法被赋值给变量<code>func</code>。这时必须用<code>bind</code>方法将<code>inc</code>内部的<code>this</code>,绑定到<code>counter</code>,否则就会出错。</p> <p><code>this</code>绑定到其他对象也是可以的。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> counter <span class="token operator">=</span> <span class="token punctuation">{</span>
  543. <span class="token literal-property property">count</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
  544. <span class="token function-variable function">inc</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>
  545. <span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token operator">++</span><span class="token punctuation">;</span>
  546. <span class="token punctuation">}</span>
  547. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  548. <span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span>
  549. <span class="token literal-property property">count</span><span class="token operator">:</span> <span class="token number">100</span>
  550. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  551. <span class="token keyword">var</span> func <span class="token operator">=</span> counter<span class="token punctuation">.</span><span class="token function">inc</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">;</span>
  552. <span class="token function">func</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  553. obj<span class="token punctuation">.</span>count <span class="token comment">// 101</span>
  554. </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></div></div><p>上面代码中,<code>bind</code>方法将<code>inc</code>方法内部的<code>this</code>,绑定到<code>obj</code>对象。结果调用<code>func</code>函数以后,递增的就是<code>obj</code>内部的<code>count</code>属性。</p> <p><code>bind</code>还可以接受更多的参数,将这些参数绑定原函数的参数。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">add</span> <span class="token operator">=</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>
  555. <span class="token keyword">return</span> x <span class="token operator">*</span> <span class="token keyword">this</span><span class="token punctuation">.</span>m <span class="token operator">+</span> y <span class="token operator">*</span> <span class="token keyword">this</span><span class="token punctuation">.</span>n<span class="token punctuation">;</span>
  556. <span class="token punctuation">}</span>
  557. <span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span>
  558. <span class="token literal-property property">m</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
  559. <span class="token literal-property property">n</span><span class="token operator">:</span> <span class="token number">2</span>
  560. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  561. <span class="token keyword">var</span> newAdd <span class="token operator">=</span> <span class="token function">add</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>obj<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">// 第二个参数为add内的x</span>
  562. <span class="token function">newAdd</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span> <span class="token comment">// 20 传入的参数为add内的y</span>
  563. </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>bind</code>方法除了绑定<code>this</code>对象,还将<code>add</code>函数的第一个参数<code>x</code>绑定成<code>5</code>,然后返回一个新函数<code>newAdd</code>,这个函数只要再接受一个参数<code>y</code>就能运行了。</p> <p>如果<code>bind</code>方法的第一个参数是<code>null</code>或<code>undefined</code>,等于将<code>this</code>绑定到全局对象,函数运行时<code>this</code>指向顶层对象(浏览器为<code>window</code>)。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">add</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>
  564. <span class="token keyword">return</span> x <span class="token operator">+</span> y<span class="token punctuation">;</span>
  565. <span class="token punctuation">}</span>
  566. <span class="token keyword">var</span> plus5 <span class="token operator">=</span> <span class="token function">add</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> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  567. <span class="token function">plus5</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token comment">// 15 传入的参数为add内的y</span>
  568. </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>add</code>内部并没有<code>this</code>,使用<code>bind</code>方法的主要目的是绑定参数<code>x</code>,以后每次运行新函数<code>plus5</code>,就只需要提供另一个参数<code>y</code>就够了。而且因为<code>add</code>内部没有<code>this</code>,所以<code>bind</code>的第一个参数是<code>null</code>,不过这里如果是其他对象,也没有影响。</p> <h5 id="bind要注意的点"><a href="#bind要注意的点" class="header-anchor">#</a> bind要注意的点:</h5> <h5 id="_1-每一次返回一个新函数"><a href="#_1-每一次返回一个新函数" class="header-anchor">#</a> (1)每一次返回一个新函数</h5> <p><code>bind</code>方法<strong>每运行一次,就返回一个新函数</strong>,这会产生一些问题。比如,监听事件的时候,不能写成下面这样。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>element<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> o<span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  569. </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>上面代码中,<code>click</code>事件绑定<code>bind</code>方法生成的一个匿名函数。这样会导致无法取消绑定,所以,下面的代码是无效的。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>element<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> o<span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  570. </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>正确的方法是写成下面这样:</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> listener <span class="token operator">=</span> o<span class="token punctuation">.</span><span class="token function">m</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span><span class="token punctuation">;</span>
  571. element<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> listener<span class="token punctuation">)</span><span class="token punctuation">;</span>
  572. <span class="token comment">// ...</span>
  573. element<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> listener<span class="token punctuation">)</span><span class="token punctuation">;</span>
  574. </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><h5 id="_2-结合回调函数使用"><a href="#_2-结合回调函数使用" class="header-anchor">#</a> (2)结合回调函数使用</h5> <p>回调函数是 JavaScript 最常用的模式之一,但是一个常见的错误是,将包含<code>this</code>的方法直接当作回调函数。解决方法就是使用<code>bind</code>方法,将<code>counter.inc</code>绑定<code>counter</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> counter <span class="token operator">=</span> <span class="token punctuation">{</span>
  575. <span class="token literal-property property">count</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
  576. <span class="token function-variable function">inc</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>
  577. <span class="token string">'use strict'</span><span class="token punctuation">;</span>
  578. <span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token operator">++</span><span class="token punctuation">;</span>
  579. <span class="token punctuation">}</span>
  580. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  581. <span class="token keyword">function</span> <span class="token function">callIt</span><span class="token punctuation">(</span><span class="token parameter">callback</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  582. <span class="token function">callback</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  583. <span class="token punctuation">}</span>
  584. <span class="token function">callIt</span><span class="token punctuation">(</span>counter<span class="token punctuation">.</span><span class="token function">inc</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span>counter<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  585. counter<span class="token punctuation">.</span>count <span class="token comment">// 1</span>
  586. </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>callIt</code>方法会调用回调函数。这时如果直接把<code>counter.inc</code>传入,调用时<code>counter.inc</code>内部的<code>this</code>就会指向全局对象。使用<code>bind</code>方法将<code>counter.inc</code>绑定<code>counter</code>以后,就不会有这个问题,<code>this</code>总是指向<code>counter</code>。</p> <p>还有一种情况比较隐蔽,就是某些数组方法可以接受一个函数当作参数。这些函数内部的<code>this</code>指向,很可能也会出错。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span>
  587. <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'张三'</span><span class="token punctuation">,</span>
  588. <span class="token literal-property property">times</span><span class="token operator">:</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>
  589. <span class="token function-variable function">print</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>
  590. <span class="token keyword">this</span><span class="token punctuation">.</span>times<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">n</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 数组的forEach方法内部this指向问题</span>
  591. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 这里的this指向全局对象</span>
  592. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  593. <span class="token punctuation">}</span>
  594. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  595. obj<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  596. <span class="token comment">// 没有任何输出</span>
  597. </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>obj.print</code>内部<code>this.times</code>的<code>this</code>是指向<code>obj</code>的,这个没有问题。但是,<code>forEach</code>方法的回调函数内部的<code>this.name</code>却是指向全局对象,导致没有办法取到值。稍微改动一下,就可以看得更清楚。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>obj<span class="token punctuation">.</span><span class="token function-variable function">print</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>
  598. <span class="token keyword">this</span><span class="token punctuation">.</span>times<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">n</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  599. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token operator">===</span> window<span class="token punctuation">)</span><span class="token punctuation">;</span>
  600. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  601. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  602. obj<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  603. <span class="token comment">// true</span>
  604. <span class="token comment">// true</span>
  605. <span class="token comment">// true</span>
  606. </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>bind</code>方法绑定<code>this</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>obj<span class="token punctuation">.</span><span class="token function-variable function">print</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>
  607. <span class="token keyword">this</span><span class="token punctuation">.</span>times<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">n</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  608. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
  609. <span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 使用bind把this指向的obj对象传入方法内</span>
  610. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  611. obj<span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  612. <span class="token comment">// 张三</span>
  613. <span class="token comment">// 张三</span>
  614. <span class="token comment">// 张三</span>
  615. </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><h5 id="_3-结合call方法使用-改写数组方法调用形式"><a href="#_3-结合call方法使用-改写数组方法调用形式" class="header-anchor">#</a> (3)结合<code>call</code>方法使用(改写数组方法调用形式)</h5> <p>利用<code>bind</code>方法,可以<strong>改写一些 JavaScript 原生方法的使用形式</strong>,以数组的<code>slice</code>方法为例。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><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">slice</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// [1]</span>
  616. <span class="token comment">// 等同于</span>
  617. <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><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 number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// [1]</span>
  618. </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>slice</code>方法从<code>[1, 2, 3]</code>里面,按照指定位置和长度切分出另一个数组。这样做的本质是在<code>[1, 2, 3]</code>上面调用<code>Array.prototype.slice</code>方法,因此可以用<code>call</code>方法表达这个过程,得到同样的结果。</p> <p><code>call</code>方法实质上是调用<code>Function.prototype.call</code>方法,因此上面的表达式可以<strong>用<code>bind</code>方法改写</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> slice <span class="token operator">=</span> <span class="token class-name">Function</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>slice<span class="token punctuation">)</span><span class="token punctuation">;</span>
  619. <span class="token function">slice</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">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 number">0</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token comment">// [1]</span>
  620. </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>Array.prototype.slice</code>变成<code>Function.prototype.call</code>方法所在的对象,调用时就变成了<code>Array.prototype.slice.call</code>。类似的写法还可以用于其他数组方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> push <span class="token operator">=</span> <span class="token class-name">Function</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>push<span class="token punctuation">)</span><span class="token punctuation">;</span>
  621. <span class="token keyword">var</span> pop <span class="token operator">=</span> <span class="token class-name">Function</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>pop<span class="token punctuation">)</span><span class="token punctuation">;</span>
  622. <span class="token keyword">var</span> a <span class="token operator">=</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>
  623. <span class="token function">push</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">)</span>
  624. a <span class="token comment">// [1, 2, 3, 4]</span>
  625. <span class="token function">pop</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span>
  626. a <span class="token comment">// [1, 2, 3]</span>
  627. </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>Function.prototype.call</code>方法绑定到<code>Function.prototype.bind</code>对象,就意味着<code>bind</code>的调用形式也可以被改写。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><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>
  628. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>v<span class="token punctuation">)</span><span class="token punctuation">;</span>
  629. <span class="token punctuation">}</span>
  630. <span class="token keyword">var</span> o <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token literal-property property">v</span><span class="token operator">:</span> <span class="token number">123</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
  631. <span class="token keyword">var</span> bind <span class="token operator">=</span> <span class="token class-name">Function</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token class-name">Function</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>bind<span class="token punctuation">)</span><span class="token punctuation">;</span>
  632. <span class="token function">bind</span><span class="token punctuation">(</span>f<span class="token punctuation">,</span> o<span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 123</span>
  633. </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.prototype.bind</code>方法绑定在<code>Function.prototype.call</code>上面,所以<code>bind</code>方法就可以直接使用,不需要在函数实例上使用。</p> <h4 id="总结-call-、apply-、bind-的区别"><a href="#总结-call-、apply-、bind-的区别" class="header-anchor">#</a> 总结,call()、apply()、bind()的区别</h4> <p>这三个方法都是改变函数内部this指向的。</p> <p>它们的区别是:</p> <p>call()第一个参数为函数内this将要指向的对象,其余参数为传入函数的参数。</p> <p>apply()第一个参数为函数内this将要指向的对象,第二个参数为数组,数组每一项为传入函数的参数。</p> <p>bind()传入参数和call()一样,区别是bind()返回一个新的函数,并非立即执行。</p> <h2 id="三、对象的继承"><a href="#三、对象的继承" class="header-anchor">#</a> 三、对象的继承</h2> <p>面向对象编程很重要的一个方面,就是对象的继承。<strong>A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法。这对于代码的复用是非常有用的</strong>。</p> <p>大部分面向对象的编程语言,都是通过“类”(class)实现对象的继承。传统上,JavaScript 语言的继承不通过 class,而是通过“原型对象”(prototype)实现,本章介绍 JavaScript 的原型链继承。</p> <p>ES6 引入了 class 语法,基于 class 的继承不在这个教程介绍,请参阅《ES6 标准入门》一书的相关章节。</p> <p><strong>ES5 是通过“原型对象”(prototype)实现继承。</strong></p> <h3 id="_1、原型对象概述"><a href="#_1、原型对象概述" class="header-anchor">#</a> 1、原型对象概述</h3> <h4 id="_1-构造函数的缺点"><a href="#_1-构造函数的缺点" class="header-anchor">#</a> (1)构造函数的缺点</h4> <p>JavaScript 通过构造函数生成新对象,因此<strong>构造函数可以视为对象的模板</strong>。实例对象的属性和方法,可以定义在构造函数内部。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Cat</span> <span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> color</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  634. <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
  635. <span class="token keyword">this</span><span class="token punctuation">.</span>color <span class="token operator">=</span> color<span class="token punctuation">;</span>
  636. <span class="token punctuation">}</span>
  637. <span class="token keyword">var</span> cat1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token string">'大毛'</span><span class="token punctuation">,</span> <span class="token string">'白色'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  638. cat1<span class="token punctuation">.</span>name <span class="token comment">// '大毛'</span>
  639. cat1<span class="token punctuation">.</span>color <span class="token comment">// '白色'</span>
  640. </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>Cat</code>函数是一个构造函数,函数内部定义了<code>name</code>属性和<code>color</code>属性,所有实例对象(上例是<code>cat1</code>)都会生成这两个属性,即这两个属性会定义在实例对象上面。</p> <p>通过构造函数为实例对象定义属性,虽然很方便,但是有一个缺点。<strong>同一个构造函数的多个实例之间,无法共享属性,从而造成对系统资源的浪费</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Cat</span><span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> color</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  641. <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
  642. <span class="token keyword">this</span><span class="token punctuation">.</span>color <span class="token operator">=</span> color<span class="token punctuation">;</span>
  643. <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">meow</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>
  644. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'喵喵'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  645. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  646. <span class="token punctuation">}</span>
  647. <span class="token comment">// 解决方法,就是在原型对象(prototype)上创建共同的方法</span>
  648. <span class="token class-name">Cat</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">meow2</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>
  649. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'喵喵'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  650. <span class="token punctuation">}</span>
  651. <span class="token keyword">var</span> cat1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token string">'大毛'</span><span class="token punctuation">,</span> <span class="token string">'白色'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  652. <span class="token keyword">var</span> cat2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Cat</span><span class="token punctuation">(</span><span class="token string">'二毛'</span><span class="token punctuation">,</span> <span class="token string">'黑色'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  653. <span class="token comment">// cat1和cat2是同一个构造函数的两个实例对象,他们无法共享meow属性,从而造成对系统资源的浪费。</span>
  654. cat1<span class="token punctuation">.</span>meow <span class="token operator">===</span> cat2<span class="token punctuation">.</span>meow
  655. <span class="token comment">// false</span>
  656. cat1<span class="token punctuation">.</span>meow2 <span class="token operator">===</span> cat2<span class="token punctuation">.</span>meow2
  657. <span class="token comment">// true</span>
  658. </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></div></div><p>上面代码中,<code>cat1</code>和<code>cat2</code>是同一个构造函数的两个实例,它们都具有<code>meow</code>方法。由于<code>meow</code>方法是生成在每个实例对象上面,所以两个实例就生成了两次。也就是说,每新建一个实例,就会新建一个<code>meow</code>方法。这既没有必要,又浪费系统资源,因为所有<code>meow</code>方法都是同样的行为,完全应该共享。</p> <p><strong>这个问题的解决方法,就是 JavaScript 的原型对象(prototype)。</strong></p> <h4 id="_2-prototype-属性的作用"><a href="#_2-prototype-属性的作用" class="header-anchor">#</a> (2)prototype 属性的作用</h4> <p><strong>JavaScript 继承机制的设计思想就是,原型对象的所有属性和方法,都能被实例对象共享</strong>。也就是说,如果属性和方法定义在原型上,那么所有实例对象就能共享,不仅节省了内存,还体现了实例对象之间的联系。</p> <p>下面,先看怎么为对象指定原型。JavaScript 规定,<strong>每个函数都有一个<code>prototype</code>属性,指向一个对象</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><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 punctuation">}</span>
  659. <span class="token keyword">typeof</span> f<span class="token punctuation">.</span>prototype <span class="token comment">// &quot;object&quot;</span>
  660. </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>f</code>默认具有<code>prototype</code>属性,指向一个对象。</p> <p><strong>对于普通函数来说,该属性基本无用</strong>。但是,<strong>对于构造函数来说,生成实例的时候,该属性会自动成为实例对象的原型</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Animal</span><span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  661. <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
  662. <span class="token punctuation">}</span>
  663. <span class="token comment">// Animal.prototype属性是实例cat1和cat2的原型对象,在上面添加属性,实例就共享了该属性</span>
  664. <span class="token class-name">Animal</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>color <span class="token operator">=</span> <span class="token string">'white'</span><span class="token punctuation">;</span>
  665. <span class="token keyword">var</span> cat1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Animal</span><span class="token punctuation">(</span><span class="token string">'大毛'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  666. <span class="token keyword">var</span> cat2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Animal</span><span class="token punctuation">(</span><span class="token string">'二毛'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  667. cat1<span class="token punctuation">.</span>color <span class="token comment">// 'white'</span>
  668. cat2<span class="token punctuation">.</span>color <span class="token comment">// 'white'</span>
  669. </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>Animal</code>的<code>prototype</code>属性,就是实例对象<code>cat1</code>和<code>cat2</code>的原型对象。原型对象上添加一个<code>color</code>属性,结果,实例对象都共享了该属性。</p> <p>原型对象的属性不是实例对象自身的属性。只要修改原型对象,变动就立刻会体现在<strong>所有</strong>实例对象上。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">Animal</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>color <span class="token operator">=</span> <span class="token string">'yellow'</span><span class="token punctuation">;</span>
  670. <span class="token comment">// color属性并非实例对象cat1/cat2本身的属性,只是当实例本身没有该属性或方法时,它会到原型对象去寻找该属性或方法</span>
  671. cat1<span class="token punctuation">.</span>color <span class="token comment">// &quot;yellow&quot;</span>
  672. cat2<span class="token punctuation">.</span>color <span class="token comment">// &quot;yellow&quot;</span>
  673. </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>color</code>属性的值变为<code>yellow</code>,两个实例对象的<code>color</code>属性立刻跟着变了。这是因为实例对象其实没有<code>color</code>属性,都是读取原型对象的<code>color</code>属性。也就是说,**当实例对象本身没有某个属性或方法的时候,它会到原型对象去寻找该属性或方法。**这就是原型对象的特殊之处。</p> <p>如果<strong>实例对象自身就有某个属性或方法,它就不会再去原型对象寻找这个属性或方法</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>cat1<span class="token punctuation">.</span>color <span class="token operator">=</span> <span class="token string">'black'</span><span class="token punctuation">;</span>
  674. cat1<span class="token punctuation">.</span>color <span class="token comment">// 'black'</span>
  675. cat2<span class="token punctuation">.</span>color <span class="token comment">// 'yellow'</span>
  676. <span class="token class-name">Animal</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>color <span class="token comment">// 'yellow';</span>
  677. </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>cat1</code>的<code>color</code>属性改为<code>black</code>,就使得它不再去原型对象读取<code>color</code>属性,后者的值依然为<code>yellow</code>。</p> <p>总结一下,原型对象的<strong>作用</strong>,<strong>就是定义所有实例对象共享的属性和方法</strong>。这也是它被称为原型对象的原因,而实例对象可以视作从原型对象衍生出来的子对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">Animal</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">walk</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>
  678. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">+</span> <span class="token string">' is walking'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  679. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  680. </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>Animal.prototype</code>对象上面定义了一个<code>walk</code>方法,这个方法将可以在所有<code>Animal</code>实例对象上面调用。</p> <h4 id="_3-原型链"><a href="#_3-原型链" class="header-anchor">#</a> (3)原型链</h4> <p><strong>JavaScript 规定,所有对象都有自己的原型对象(prototype)。一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。因此,就会形成一个“原型链”(prototype chain):对象到原型,再到原型的原型……</strong></p> <p>如果一层层地上溯,所有对象的原型<strong>最终都可以上溯到<code>Object.prototype</code>,即<code>Object</code>构造函数的<code>prototype</code>属性</strong>。也就是说,所有对象都继承了<code>Object.prototype</code>的属性。这就是所有对象都有<code>valueOf</code>和<code>toString</code>方法的原因,因为这是从<code>Object.prototype</code>继承的。</p> <p>那么,<code>Object.prototype</code>对象有没有它的原型呢?回答是<code>Object.prototype</code>的原型是<code>null</code>。<code>null</code>没有任何属性和方法,也没有自己的原型。因此,<strong>原型链的尽头就是<code>null</code></strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span><span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span> <span class="token comment">// Object.getPrototypeOf方法返回对象的原型</span>
  681. <span class="token comment">// null</span>
  682. </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>Object.prototype</code>对象的原型是<code>null</code>,由于<code>null</code>没有任何属性,所以原型链到此为止。<code>Object.getPrototypeOf</code>方法返回参数对象的原型,具体介绍请看后文。</p> <p><strong>读取对象的某个属性时,JavaScript 引擎先寻找对象本身的属性,如果找不到,就到它的原型去找,如果还是找不到,就到原型的原型去找。如果直到最顶层的<code>Object.prototype</code>还是找不到,则返回<code>undefined</code></strong>。如果对象自身和它的原型,都定义了一个同名属性,那么优先读取对象自身的属性,这叫做“覆盖”(overriding)。</p> <p>注意,一级级向上,在整个原型链上寻找某个属性,对性能是有影响的。所寻找的属性在越上层的原型对象,对性能的影响越大。如果寻找某个不存在的属性,将会遍历整个原型链。</p> <p>举例来说,如果让构造函数的<code>prototype</code>属性指向一个数组,就意味着实例对象可以调用数组方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">MyArray</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>
  683. <span class="token class-name">MyArray</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Array</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 构造函数MyArray的原型指向 数组实例</span>
  684. <span class="token class-name">MyArray</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">=</span> MyArray<span class="token punctuation">;</span>
  685. <span class="token keyword">var</span> mine <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// mine是构造函数MyArray的实例</span>
  686. mine<span class="token punctuation">.</span><span class="token function">push</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>
  687. mine<span class="token punctuation">.</span>length <span class="token comment">// 3</span>
  688. mine <span class="token keyword">instanceof</span> <span class="token class-name">Array</span> <span class="token comment">// true 判断实例对象mine是否为构造函数Array的实例</span>
  689. </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>mine</code>是构造函数<code>MyArray</code>的实例对象,由于<code>MyArray.prototype</code>指向一个数组实例,使得<code>mine</code>可以调用数组方法(这些方法定义在数组实例的<code>prototype</code>对象上面)。最后那行<code>instanceof</code>表达式,用来比较一个对象是否为某个构造函数的实例,结果就是证明<code>mine</code>为<code>Array</code>的实例,<code>instanceof</code>运算符的详细解释详见后文。</p> <p>上面代码还出现了原型对象的<code>constructor</code>属性,这个属性的含义下一节就来解释。</p> <h4 id="_4-constructor-属性"><a href="#_4-constructor-属性" class="header-anchor">#</a> (4)constructor 属性</h4> <p><strong><code>prototype</code>对象有一个<code>constructor</code>属性,默认指向<code>prototype</code>对象所在的构造函数。</strong></p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token constant">P</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  690. <span class="token class-name">P</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">===</span> <span class="token constant">P</span> <span class="token comment">// true</span>
  691. </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>constructor</code>属性定义在<code>prototype</code>对象上面,意味着可以被所有实例对象继承。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token constant">P</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  692. <span class="token keyword">var</span> p <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">P</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  693. p<span class="token punctuation">.</span>constructor <span class="token operator">===</span> <span class="token constant">P</span> <span class="token comment">// true p自身没有constructor属性,它是读取原型上的</span>
  694. p<span class="token punctuation">.</span>constructor <span class="token operator">===</span> <span class="token class-name">P</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token comment">// true</span>
  695. p<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span><span class="token string">'constructor'</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
  696. </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>p</code>是构造函数<code>P</code>的实例对象,但是<code>p</code>自身没有<code>constructor</code>属性,该属性其实是读取原型链上面的<code>P.prototype.constructor</code>属性。</p> <h5 id="作用"><a href="#作用" class="header-anchor">#</a> 作用</h5> <p><code>constructor</code>属性的<strong>作用</strong>是,<strong>可以得知某个实例对象,到底是哪一个构造函数产生的</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token constant">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>
  697. <span class="token keyword">var</span> f <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  698. f<span class="token punctuation">.</span>constructor <span class="token operator">===</span> <span class="token constant">F</span> <span class="token comment">// true</span>
  699. f<span class="token punctuation">.</span>constructor <span class="token operator">===</span> RegExp <span class="token comment">// false</span>
  700. </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>constructor</code>属性确定了实例对象<code>f</code>的构造函数是<code>F</code>,而不是<code>RegExp</code>。</p> <p>另一方面,有了<code>constructor</code>属性,就可以从一个实例对象新建另一个实例。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Constr</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  701. <span class="token keyword">var</span> x <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Constr</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  702. <span class="token keyword">var</span> y <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">x<span class="token punctuation">.</span>constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 等同于 new Constr()</span>
  703. y <span class="token keyword">instanceof</span> <span class="token class-name">Constr</span> <span class="token comment">// true</span>
  704. </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>x</code>是构造函数<code>Constr</code>的实例,可以从<code>x.constructor</code>间接调用构造函数。这使得在实例方法中,调用自身的构造函数成为可能。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">Constr</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">createCopy</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>
  705. <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">this<span class="token punctuation">.</span>constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  706. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  707. </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>createCopy</code>方法调用构造函数,新建另一个实例。</p> <p><code>constructor</code>属性<strong>表示原型对象与构造函数之间的关联关系</strong>,如果修改了原型对象,一般会同时修改<code>constructor</code>属性,防止引用的时候出错。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Person</span><span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  708. <span class="token keyword">this</span><span class="token punctuation">.</span>name <span class="token operator">=</span> name<span class="token punctuation">;</span>
  709. <span class="token punctuation">}</span>
  710. <span class="token class-name">Person</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Person <span class="token comment">// true</span>
  711. <span class="token class-name">Person</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// 修改了原型对象,但没有修改原型下的constructor</span>
  712. <span class="token function-variable function">method</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>
  713. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  714. <span class="token comment">// 由于原型对象已被修改,原型下的constructor也被修改</span>
  715. <span class="token class-name">Person</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Person <span class="token comment">// false</span>
  716. <span class="token class-name">Person</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">===</span> Object <span class="token comment">// true // 普通对象的constructor指向object构造函数</span>
  717. </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></div></div><p>上面代码中,构造函数<code>Person</code>的原型对象改掉了,但是没有修改<code>constructor</code>属性,导致这个属性不再指向<code>Person</code>。由于<code>Person</code>的新原型是一个普通对象,而普通对象的<code>constructor</code>属性指向<code>Object</code>构造函数,导致<code>Person.prototype.constructor</code>变成了<code>Object</code>。</p> <p>所以,修改原型对象时,一般要同时修改<code>constructor</code>属性的指向。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 坏的写法</span>
  718. <span class="token class-name">C</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token punctuation">{</span>
  719. <span class="token function-variable function">method1</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span></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>
  720. <span class="token comment">// ...</span>
  721. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  722. <span class="token comment">// 好的写法</span>
  723. <span class="token class-name">C</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token punctuation">{</span>
  724. <span class="token literal-property property">constructor</span><span class="token operator">:</span> <span class="token constant">C</span><span class="token punctuation">,</span>
  725. <span class="token function-variable function">method1</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span></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>
  726. <span class="token comment">// ...</span>
  727. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  728. <span class="token comment">// 更好的写法 (只是在原型对象上添加方法)</span>
  729. <span class="token class-name">C</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">method1</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span></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>
  730. </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>constructor</code>属性重新指向原来的构造函数,要么只在原型对象上添加方法,这样可以保证<code>instanceof</code>运算符不会失真。</p> <p>如果不能确定<code>constructor</code>属性是什么函数,还有一个办法:通过<code>name</code>属性,从实例得到构造函数的名称。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><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>
  731. <span class="token keyword">var</span> f <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  732. f<span class="token punctuation">.</span>constructor<span class="token punctuation">.</span>name <span class="token comment">// &quot;Foo&quot;</span>
  733. </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><h3 id="_2、instanceof-运算符"><a href="#_2、instanceof-运算符" class="header-anchor">#</a> 2、instanceof 运算符</h3> <p><code>instanceof</code>运算符,<strong>判断对象是否为某个构造函数的实例,返回一个布尔值</strong>。</p> <h4 id="语法-4"><a href="#语法-4" class="header-anchor">#</a> 语法</h4> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token operator">&lt;</span>实例对象<span class="token operator">&gt;</span> <span class="token keyword">instanceof</span> <span class="token operator">&lt;</span>构造函数<span class="token operator">&gt;</span>
  734. </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><div class="language- line-numbers-mode"><pre class="language-text"><code>var v = new Vehicle();
  735. v instanceof Vehicle // true v是构造函数Vehicel的实例
  736. </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>v</code>是构造函数<code>Vehicle</code>的实例,所以返回<code>true</code>。</p> <p><code>instanceof</code>运算符的左边是实例对象,右边是构造函数。它会<strong>检查右边构造函数的原型对象(prototype),是否在左边对象的原型链上</strong>。因此,下面两种写法是等价的。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>v <span class="token keyword">instanceof</span> <span class="token class-name">Vehicle</span>
  737. <span class="token comment">// 等同于</span>
  738. <span class="token class-name">Vehicle</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">isPrototypeOf</span><span class="token punctuation">(</span>v<span class="token punctuation">)</span>
  739. </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>Object.prototype.isPrototypeOf</code>的详细解释见后文。</p> <p>由于<code>instanceof</code>检查整个原型链,因此同一个实例对象,可能会对多个构造函数都返回<code>true</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> d <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  740. d <span class="token keyword">instanceof</span> <span class="token class-name">Date</span> <span class="token comment">// true</span>
  741. d <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// true</span>
  742. </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>d</code>同时是<code>Date</code>和<code>Object</code>的实例,因此对这两个构造函数都返回<code>true</code>。</p> <p>由于<strong>任意对象(除了<code>null</code>)都是<code>Object</code>的实例</strong>,所以<code>instanceof</code>运算符可以判断一个值是否为非<code>null</code>的对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token literal-property property">foo</span><span class="token operator">:</span> <span class="token number">123</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
  743. obj <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// true</span>
  744. <span class="token keyword">null</span> <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// false</span>
  745. </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>null</code>,其他对象的<code>instanceOf Object</code>的运算结果都是<code>true</code>。</p> <p><code>instanceof</code>的原理是检查右边构造函数的<code>prototype</code>属性,是否在左边对象的原型链上。有一种<strong>特殊情况</strong>,就是左边对象的原型链上,只有<code>null</code>对象。这时,<code>instanceof</code>判断会失真。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  746. <span class="token keyword">typeof</span> obj <span class="token comment">// &quot;object&quot;</span>
  747. Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// false</span>
  748. </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>Object.create(null)</code>返回一个新对象<code>obj</code>,它的原型是<code>null</code>(<code>Object.create</code>的详细介绍见后文)。右边的构造函数<code>Object</code>的<code>prototype</code>属性,不在左边的原型链上,因此<code>instanceof</code>就认为<code>obj</code>不是<code>Object</code>的实例。但是,<strong>只要一个对象的原型不是<code>null</code>,<code>instanceof</code>运算符的判断就不会失真</strong>。</p> <h4 id="用处"><a href="#用处" class="header-anchor">#</a> 用处</h4> <p><code>instanceof</code>运算符的一个用处,是<strong>判断值的类型</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> x <span class="token operator">=</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>
  749. <span class="token keyword">var</span> y <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  750. <span class="token keyword">var</span> <span class="token function-variable function">z</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>
  751. x <span class="token keyword">instanceof</span> <span class="token class-name">Array</span> <span class="token comment">// true</span>
  752. y <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// true</span>
  753. y <span class="token keyword">instanceof</span> <span class="token class-name">Function</span> <span class="token comment">// true</span>
  754. </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>instanceof</code>运算符判断,变量<code>x</code>是数组,变量<code>y</code>是对象,变量<code>z</code>是函数。</p> <p>注意,<code>instanceof</code>运算符<strong>只能用于对象</strong>,<strong>不适用原始类型的值</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> s <span class="token operator">=</span> <span class="token string">'hello'</span><span class="token punctuation">;</span>
  755. <span class="token keyword">var</span> z <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
  756. s <span class="token keyword">instanceof</span> <span class="token class-name">String</span> <span class="token comment">// false</span>
  757. z <span class="token keyword">instanceof</span> <span class="token class-name">Number</span> <span class="token comment">// false</span>
  758. <span class="token comment">// 原始类型并没有实例化,所有都返回false</span>
  759. </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>String</code>对象的实例(因为字符串不是对象),所以返回<code>false</code>。</p> <p>此外,对于<code>undefined</code>和<code>null</code>,<code>instanceof</code>运算符总是返回<code>false</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">undefined</span> <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// false</span>
  760. <span class="token keyword">null</span> <span class="token keyword">instanceof</span> <span class="token class-name">Object</span> <span class="token comment">// false</span>
  761. </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>instanceof</code>运算符,还可以巧妙地解决,调用构造函数时,<strong>忘了加<code>new</code>命令的问题</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Fubar</span> <span class="token punctuation">(</span><span class="token parameter">foo<span class="token punctuation">,</span> bar</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  762. <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span> <span class="token keyword">instanceof</span> <span class="token class-name">Fubar</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 忘加new命令时 this为 全局对象window</span>
  763. <span class="token keyword">this</span><span class="token punctuation">.</span>_foo <span class="token operator">=</span> foo<span class="token punctuation">;</span>
  764. <span class="token keyword">this</span><span class="token punctuation">.</span>_bar <span class="token operator">=</span> bar<span class="token punctuation">;</span>
  765. <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
  766. <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Fubar</span><span class="token punctuation">(</span>foo<span class="token punctuation">,</span> bar<span class="token punctuation">)</span><span class="token punctuation">;</span>
  767. <span class="token punctuation">}</span>
  768. <span class="token punctuation">}</span>
  769. <span class="token function">Fubar</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 punctuation">.</span>_foo <span class="token comment">// 1</span>
  770. <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Fubar</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 punctuation">)</span><span class="token punctuation">.</span>_foo <span class="token comment">// 1</span>
  771. </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>instanceof</code>运算符,在函数体内部判断<code>this</code>关键字是否为构造函数<code>Fubar</code>的实例。如果不是,就表明忘了加<code>new</code>命令。</p> <h3 id="_3、构造函数的继承"><a href="#_3、构造函数的继承" class="header-anchor">#</a> 3、构造函数的继承</h3> <p><strong>让一个构造函数继承另一个构造函数</strong>,是非常常见的需求。</p> <p>这可以分成两步实现。<strong>第一步是在子类的构造函数中,调用父类的构造函数</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Sub</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 comment">// Sub是子类构造函数</span>
  772. <span class="token function">Super</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><span class="token punctuation">;</span> <span class="token comment">// Super是父类构造函数,这的this是子类的实例</span>
  773. <span class="token keyword">this</span><span class="token punctuation">.</span>prop <span class="token operator">=</span> value<span class="token punctuation">;</span>
  774. <span class="token punctuation">}</span>
  775. </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>Sub</code>是子类的构造函数,<code>this</code>是子类的实例。在实例上调用父类的构造函数<code>Super</code>,<strong>就会让子类实例具有父类实例的属性</strong>。</p> <p><strong>第二步,是让子类的原型指向父类的原型,这样子类就可以继承父类原型。</strong></p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 子类的原型指向一个新对象,新对象原型指向父类原型,等于子类原型继承了父类原型,且对子类原型操作不会影响到父类原型</span>
  776. <span class="token class-name">Sub</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token class-name">Super</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
  777. <span class="token class-name">Sub</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">=</span> Sub<span class="token punctuation">;</span>
  778. <span class="token class-name">Sub</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>method <span class="token operator">=</span> <span class="token string">'...'</span><span class="token punctuation">;</span>
  779. </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>Sub.prototype</code>是子类的原型,要将它赋值为<code>Object.create(Super.prototype)</code>,而不是直接等于<code>Super.prototype</code>。否则后面两行对<code>Sub.prototype</code>的操作,会连父类的原型<code>Super.prototype</code>一起修改掉。</p> <p><strong>另外一种写法</strong>是<code>Sub.prototype</code>等于一个父类实例。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">Sub</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Super</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 这个写法会继承父类实例的方法,不推荐</span>
  780. </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>上面这种写法也有继承的效果,但是子类会具有父类实例的方法。有时,这可能不是我们需要的,所以<strong>不推荐</strong>使用这种写法。</p> <p>举例来说,下面是一个<code>Shape</code>构造函数。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">Shape</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  781. <span class="token keyword">this</span><span class="token punctuation">.</span>x <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
  782. <span class="token keyword">this</span><span class="token punctuation">.</span>y <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
  783. <span class="token punctuation">}</span>
  784. <span class="token class-name">Shape</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">move</span> <span class="token operator">=</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>
  785. <span class="token keyword">this</span><span class="token punctuation">.</span>x <span class="token operator">+=</span> x<span class="token punctuation">;</span>
  786. <span class="token keyword">this</span><span class="token punctuation">.</span>y <span class="token operator">+=</span> y<span class="token punctuation">;</span>
  787. console<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span><span class="token string">'Shape moved.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  788. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  789. </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>Rectangle</code>构造函数继承<code>Shape</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 第一步,子类继承父类的实例</span>
  790. <span class="token keyword">function</span> <span class="token function">Rectangle</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  791. <span class="token function">Shape</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><span class="token punctuation">;</span> <span class="token comment">// 调用父类构造函数</span>
  792. <span class="token punctuation">}</span>
  793. <span class="token comment">// 另一种写法</span>
  794. <span class="token keyword">function</span> <span class="token function">Rectangle</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  795. <span class="token keyword">this</span><span class="token punctuation">.</span>base <span class="token operator">=</span> Shape<span class="token punctuation">;</span>
  796. <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">base</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  797. <span class="token punctuation">}</span>
  798. <span class="token comment">// 第二步,子类继承父类的原型</span>
  799. <span class="token class-name">Rectangle</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token class-name">Shape</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
  800. <span class="token class-name">Rectangle</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">=</span> Rectangle<span class="token punctuation">;</span>
  801. </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></div></div><p>采用这样的写法以后,<code>instanceof</code>运算符会对子类和父类的构造函数,都返回<code>true</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> rect <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Rectangle</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  802. rect <span class="token keyword">instanceof</span> <span class="token class-name">Rectangle</span> <span class="token comment">// true</span>
  803. rect <span class="token keyword">instanceof</span> <span class="token class-name">Shape</span> <span class="token comment">// true</span>
  804. </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>上面代码中,子类是整体继承父类。有时只需要<strong>单个方法的继承</strong>,这时可以采用下面的写法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">ClassB</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">print</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">// 单个方法的继承</span>
  805. <span class="token class-name">ClassA</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">print</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><span class="token punctuation">;</span><span class="token comment">// 继承了ClassA的print方法</span>
  806. <span class="token comment">// some code</span>
  807. <span class="token punctuation">}</span>
  808. </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>B</code>的<code>print</code>方法先调用父类<code>A</code>的<code>print</code>方法,再部署自己的代码。这就等于继承了父类<code>A</code>的<code>print</code>方法。</p> <h3 id="_4、多重继承"><a href="#_4、多重继承" class="header-anchor">#</a> 4、多重继承</h3> <p>JavaScript 不提供多重继承功能,即不允许一个对象同时继承多个对象。但是,可以通过变通方法,实现这个功能。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token constant">M1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 构造函数M1</span>
  809. <span class="token keyword">this</span><span class="token punctuation">.</span>hello <span class="token operator">=</span> <span class="token string">'hello'</span><span class="token punctuation">;</span>
  810. <span class="token punctuation">}</span>
  811. <span class="token keyword">function</span> <span class="token constant">M2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 构造函数M2</span>
  812. <span class="token keyword">this</span><span class="token punctuation">.</span>world <span class="token operator">=</span> <span class="token string">'world'</span><span class="token punctuation">;</span>
  813. <span class="token punctuation">}</span>
  814. <span class="token keyword">function</span> <span class="token constant">S</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 子类构造函数S</span>
  815. <span class="token constant">M1</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><span class="token punctuation">;</span>
  816. <span class="token constant">M2</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><span class="token punctuation">;</span>
  817. <span class="token punctuation">}</span>
  818. <span class="token comment">// 继承 M1</span>
  819. <span class="token class-name">S</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token class-name">M1</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
  820. <span class="token comment">// 继承链上加入 M2</span>
  821. Object<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span><span class="token class-name">S</span><span class="token punctuation">.</span>prototype<span class="token punctuation">,</span> <span class="token class-name">M2</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
  822. <span class="token comment">// 指定构造函数</span>
  823. <span class="token class-name">S</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">=</span> <span class="token constant">S</span><span class="token punctuation">;</span>
  824. <span class="token keyword">var</span> s <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">S</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  825. s<span class="token punctuation">.</span>hello <span class="token comment">// 'hello'</span>
  826. s<span class="token punctuation">.</span>world <span class="token comment">// 'world'</span>
  827. </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></div></div><p>上面代码中,子类<code>S</code>同时继承了父类<code>M1</code>和<code>M2</code>。这种模式又称为 Mixin(混入)。</p> <h3 id="_5、模块"><a href="#_5、模块" class="header-anchor">#</a> 5、模块</h3> <p>随着网站逐渐变成“互联网应用程序”,嵌入网页的 JavaScript 代码越来越庞大,越来越复杂。网页越来越像桌面程序,需要一个团队分工协作、进度管理、单元测试等等……开发者必须使用软件工程的方法,管理网页的业务逻辑。</p> <p><strong>JavaScript 模块化编程</strong>,已经成为一个迫切的需求。理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。</p> <p>但是,JavaScript 不是一种模块化编程语言,ES6 才开始支持“类”和“模块”。下面介绍传统的做法,如何利用对象实现模块的效果。</p> <h4 id="_1-基本的实现方法"><a href="#_1-基本的实现方法" class="header-anchor">#</a> (1)基本的实现方法</h4> <p><strong>模块是实现特定功能的一组属性和方法的封装。</strong></p> <p>简单的做法是把模块写成一个对象,所有的模块成员都放到这个对象里面。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> module1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  828.  _count <span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
  829.  <span class="token function-variable function">m1</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>
  830.   <span class="token comment">//...</span>
  831.  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  832.  <span class="token function-variable function">m2</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>
  833.  <span class="token comment">//...</span>
  834.  <span class="token punctuation">}</span>
  835. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  836. </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>m1</code>和<code>m2</code>,都封装在<code>module1</code>对象里。使用的时候,就是调用这个对象的属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 使用</span>
  837. module1<span class="token punctuation">.</span><span class="token function">m1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  838. </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-js line-numbers-mode"><pre class="language-js"><code>module1<span class="token punctuation">.</span>_count <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">;</span>
  839. </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><h4 id="_2-封装私有变量"><a href="#_2-封装私有变量" class="header-anchor">#</a> (2)封装私有变量:</h4> <h5 id="_2-1-构造函数的写法"><a href="#_2-1-构造函数的写法" class="header-anchor">#</a> (2-1)构造函数的写法</h5> <p>我们可以利用构造函数,封装私有变量。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">StringBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  840. <span class="token keyword">var</span> buffer <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// 模块的私有变量</span>
  841. <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">add</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">str</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  842. buffer<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">;</span>
  843. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  844. <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">toString</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>
  845. <span class="token keyword">return</span> buffer<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  846. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  847. <span class="token punctuation">}</span>
  848. </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>buffer</code>是模块的私有变量。一旦生成实例对象,外部是无法直接访问<code>buffer</code>的。但是,这种方法将私有变量封装在构造函数中,导致构造函数与实例对象是一体的,总是存在于内存之中,无法在使用完成后清除。这意味着,构造函数有双重作用,既用来塑造实例对象,又用来保存实例对象的数据,违背了构造函数与实例对象在数据上相分离的原则(即实例对象的数据,不应该保存在实例对象以外)。同时,非常耗费内存。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">StringBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  849. <span class="token keyword">this</span><span class="token punctuation">.</span>_buffer <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  850. <span class="token punctuation">}</span>
  851. <span class="token class-name">StringBuilder</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token punctuation">{</span>
  852. <span class="token literal-property property">constructor</span><span class="token operator">:</span> StringBuilder<span class="token punctuation">,</span>
  853. <span class="token function-variable function">add</span><span class="token operator">:</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">str</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  854. <span class="token keyword">this</span><span class="token punctuation">.</span>_buffer<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">;</span>
  855. <span class="token punctuation">}</span><span class="token punctuation">,</span>
  856. <span class="token function-variable function">toString</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>
  857. <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_buffer<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  858. <span class="token punctuation">}</span>
  859. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  860. </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></div></div><p>这种方法将私有变量放入实例对象中,好处是看上去更自然,但是它的私有变量可以从外部读写,不是很安全。</p> <h5 id="_2-2-立即执行函数的写法"><a href="#_2-2-立即执行函数的写法" class="header-anchor">#</a> (2-2)立即执行函数的写法</h5> <p>另一种做法是使用“立即执行函数”(Immediately-Invoked Function Expression,IIFE),将相关的属性和方法封装在一个函数作用域里面,可以达到不暴露私有成员的目的。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> module1 <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>
  861.  <span class="token keyword">var</span> _count <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
  862.  <span class="token keyword">var</span> <span class="token function-variable function">m1</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>
  863.   <span class="token comment">//...</span>
  864.  <span class="token punctuation">}</span><span class="token punctuation">;</span>
  865.  <span class="token keyword">var</span> <span class="token function-variable function">m2</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>
  866.   <span class="token comment">//...</span>
  867.  <span class="token punctuation">}</span><span class="token punctuation">;</span>
  868.  <span class="token keyword">return</span> <span class="token punctuation">{</span>
  869.   m1 <span class="token operator">:</span> m1<span class="token punctuation">,</span>
  870.   m2 <span class="token operator">:</span> m2
  871.  <span class="token punctuation">}</span><span class="token punctuation">;</span>
  872. <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>
  873. </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></div></div><p>使用上面的写法,外部代码无法读取内部的<code>_count</code>变量。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>console<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span>module1<span class="token punctuation">.</span>_count<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//undefined</span>
  874. </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>上面的<code>module1</code>就是 JavaScript 模块的基本写法。下面,再对这种写法进行加工。</p> <h4 id="_3-模块的放大模式-向模块添加新方法"><a href="#_3-模块的放大模式-向模块添加新方法" class="header-anchor">#</a> (3)模块的放大模式(向模块添加新方法)</h4> <p>如果一个模块很大,必须分成几个部分,或者一个模块需要继承另一个模块,这时就有必要采用“放大模式”(augmentation)。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> module1 <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">mod</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  875.  mod<span class="token punctuation">.</span><span class="token function-variable function">m3</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>
  876.   <span class="token comment">//...</span>
  877.  <span class="token punctuation">}</span><span class="token punctuation">;</span>
  878.  <span class="token keyword">return</span> mod<span class="token punctuation">;</span>
  879. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>module1<span class="token punctuation">)</span><span class="token punctuation">;</span>
  880. </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>module1</code>模块添加了一个新方法<code>m3()</code>,然后返回新的<code>module1</code>模块。</p> <p>在浏览器环境中,模块的各个部分通常都是从网上获取的,有时无法知道哪个部分会先加载。如果采用上面的写法,第一个执行的部分有可能加载一个不存在空对象,这时就要采用&quot;宽放大模式&quot;(Loose augmentation)。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> module1 <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">mod</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  881.  <span class="token comment">//...</span>
  882.  <span class="token keyword">return</span> mod<span class="token punctuation">;</span>
  883. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>window<span class="token punctuation">.</span>module1 <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>
  884. </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>与&quot;放大模式&quot;相比,“宽放大模式”就是“立即执行函数”的参数可以是空对象。</p> <h4 id="_4-输入全局变量-保证独立性"><a href="#_4-输入全局变量-保证独立性" class="header-anchor">#</a> (4)输入全局变量(保证独立性)</h4> <p>独立性是模块的重要特点,模块内部最好不与程序的其他部分直接交互。</p> <p>为了在模块内部调用全局变量,必须显式地将其他变量输入模块。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> module1 <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">$<span class="token punctuation">,</span> <span class="token constant">YAHOO</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  885.  <span class="token comment">//...</span>
  886. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>jQuery<span class="token punctuation">,</span> <span class="token constant">YAHOO</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 向模块内部传入全局变量</span>
  887. </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>module1</code>模块需要使用 jQuery 库和 YUI 库,就把这两个库(其实是两个模块)当作参数输入<code>module1</code>。这样做除了保证模块的独立性,还使得模块之间的依赖关系变得明显。</p> <p><strong>立即执行函数还可以起到命名空间的作用</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">$<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  888. <span class="token keyword">function</span> <span class="token function">go</span><span class="token punctuation">(</span><span class="token parameter">num</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  889. <span class="token punctuation">}</span>
  890. <span class="token keyword">function</span> <span class="token function">handleEvents</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  891. <span class="token punctuation">}</span>
  892. <span class="token keyword">function</span> <span class="token function">initialize</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  893. <span class="token punctuation">}</span>
  894. <span class="token keyword">function</span> <span class="token function">dieCarouselDie</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  895. <span class="token punctuation">}</span>
  896. <span class="token comment">//attach to the global scope</span>
  897. window<span class="token punctuation">.</span>finalCarousel <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// 对外暴露接口</span>
  898. <span class="token literal-property property">init</span> <span class="token operator">:</span> initialize<span class="token punctuation">,</span>
  899. <span class="token literal-property property">destroy</span> <span class="token operator">:</span> dieCarouselDie
  900. <span class="token punctuation">}</span>
  901. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span> jQuery<span class="token punctuation">,</span> window<span class="token punctuation">,</span> document <span class="token punctuation">)</span><span class="token punctuation">;</span>
  902. </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></div></div><p>上面代码中,<code>finalCarousel</code>对象输出到全局,对外暴露<code>init</code>和<code>destroy</code>接口,<strong>内部方法<code>go</code>、<code>handleEvents</code>、<code>initialize</code>、<code>dieCarouselDie</code>都是外部无法调用的</strong>。</p> <h2 id="四、object-对象的相关方法"><a href="#四、object-对象的相关方法" class="header-anchor">#</a> 四、Object 对象的相关方法</h2> <p>JavaScript 在<code>Object</code>对象上面,提供了很多相关方法,处理面向对象编程的相关操作。本章介绍这些方法。</p> <h3 id="_1、object-getprototypeof-获取原型对象"><a href="#_1、object-getprototypeof-获取原型对象" class="header-anchor">#</a> 1、Object.getPrototypeOf() 获取原型对象</h3> <p><code>Object.getPrototypeOf</code>方法<strong>返回参数对象的原型</strong>。这是获取原型对象的标准方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><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>
  903. <span class="token keyword">var</span> f <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  904. Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>f<span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token class-name">F</span><span class="token punctuation">.</span>prototype <span class="token comment">// true</span>
  905. </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>F.prototype</code>。</p> <p>下面是几种特殊对象的原型。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 空对象的原型是 Object.prototype</span>
  906. Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</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 class-name">Object</span><span class="token punctuation">.</span>prototype <span class="token comment">// true</span>
  907. <span class="token comment">// Object.prototype 的原型是 null</span>
  908. Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span><span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token keyword">null</span> <span class="token comment">// true</span>
  909. <span class="token comment">// 函数的原型是 Function.prototype</span>
  910. <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 punctuation">}</span>
  911. Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>f<span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token class-name">Function</span><span class="token punctuation">.</span>prototype <span class="token comment">// true</span>
  912. </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="_2、object-setprototypeof-设置原型对象"><a href="#_2、object-setprototypeof-设置原型对象" class="header-anchor">#</a> 2、Object.setPrototypeOf() 设置原型对象</h3> <p><code>Object.setPrototypeOf</code>方法为参数对象<strong>设置原型</strong>,<strong>返回该参数对象</strong>。它接受两个参数,第一个是现有对象,第二个是原型对象。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><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>
  913. <span class="token keyword">var</span> b <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">1</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  914. Object<span class="token punctuation">.</span><span class="token function">setPrototypeOf</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span> b<span class="token punctuation">)</span><span class="token punctuation">;</span>
  915. Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span> <span class="token operator">===</span> b <span class="token comment">// true</span>
  916. a<span class="token punctuation">.</span>x <span class="token comment">// 1 a对象共享b对象的属性</span>
  917. </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>Object.setPrototypeOf</code>方法将对象<code>a</code>的原型,设置为对象<code>b</code>,因此<code>a</code>可以共享<code>b</code>的属性。</p> <h5 id="使用object-setprototypeof方法模拟new命令"><a href="#使用object-setprototypeof方法模拟new命令" class="header-anchor">#</a> <strong>使用<code>Object.setPrototypeOf</code>方法模拟<code>new</code>命令</strong></h5> <div class="language-js line-numbers-mode"><pre class="language-js"><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>
  918. <span class="token keyword">this</span><span class="token punctuation">.</span>foo <span class="token operator">=</span> <span class="token string">'bar'</span><span class="token punctuation">;</span>
  919. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  920. <span class="token keyword">var</span> f <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  921. <span class="token comment">// 等同于</span>
  922. <span class="token keyword">var</span> f <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">setPrototypeOf</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token class-name">F</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 模拟new命令</span>
  923. <span class="token constant">F</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>f<span class="token punctuation">)</span><span class="token punctuation">;</span>
  924. </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>new</code>命令新建实例对象,其实可以分成两步。第一步,将一个空对象的原型设为构造函数的<code>prototype</code>属性(上例是<code>F.prototype</code>);第二步,将构造函数内部的<code>this</code>绑定这个空对象,然后执行构造函数,使得定义在<code>this</code>上面的方法和属性(上例是<code>this.foo</code>),都转移到这个空对象上。</p> <h3 id="_3、-object-create-创建实例对象-指向目标对象的原型"><a href="#_3、-object-create-创建实例对象-指向目标对象的原型" class="header-anchor">#</a> 3、 Object.create() 创建实例对象,指向目标对象的原型</h3> <p>生成实例对象的常用方法是,使用<code>new</code>命令让构造函数返回一个实例。但是很多时候,只能拿到一个实例对象,它可能根本不是由构建函数生成的,那么能不能从一个实例对象,生成另一个实例对象呢?</p> <p>JavaScript 提供了<code>Object.create</code>方法,用来满足这种需求。**该方法接受一个对象作为参数,然后以它为原型,返回一个实例对象。**该实例完全继承原型对象的属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token comment">// 原型对象</span>
  925. <span class="token keyword">var</span> <span class="token constant">A</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  926. <span class="token function-variable function">print</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>
  927. console<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 punctuation">;</span>
  928. <span class="token punctuation">}</span>
  929. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  930. <span class="token comment">// 实例对象</span>
  931. <span class="token keyword">var</span> <span class="token constant">B</span> <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token constant">A</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 以A为原型,创建了B实例对象,使B继承了A的属性</span>
  932. Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span><span class="token constant">B</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token constant">A</span> <span class="token comment">// true</span>
  933. <span class="token constant">B</span><span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// hello</span>
  934. <span class="token constant">B</span><span class="token punctuation">.</span>print <span class="token operator">===</span> <span class="token constant">A</span><span class="token punctuation">.</span>print <span class="token comment">// true</span>
  935. </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></div></div><p>上面代码中,<code>Object.create</code>方法以<code>A</code>对象为原型,生成了<code>B</code>对象。<code>B</code>继承了<code>A</code>的所有属性和方法。</p> <p>实际上,<code>Object.create</code>方法可以用下面的代码代替。</p> <h5 id="内部实现原理"><a href="#内部实现原理" class="header-anchor">#</a> 内部实现原理</h5> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> Object<span class="token punctuation">.</span>create <span class="token operator">!==</span> <span class="token string">'function'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  936. Object<span class="token punctuation">.</span><span class="token function-variable function">create</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">obj</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 模拟Object.create方法</span>
  937. <span class="token keyword">function</span> <span class="token constant">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 comment">// 创建一个空构造函数F</span>
  938. <span class="token class-name">F</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> obj<span class="token punctuation">;</span> <span class="token comment">// 让F的原型 指向参数obj(obj为传入的原型对象)</span>
  939. <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">F</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 返回一个F的实例</span>
  940. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  941. <span class="token punctuation">}</span>
  942. </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>Object.create</code>方法的实质是新建一个空的构造函数<code>F</code>,然后让<code>F.prototype</code>属性指向参数对象<code>obj</code>,最后返回一个<code>F</code>的实例,从而实现让该实例继承<code>obj</code>的属性。</p> <p>下面三种方式生成的新对象是等价的。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj1 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</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>
  943. <span class="token keyword">var</span> obj2 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">)</span><span class="token punctuation">;</span>
  944. <span class="token keyword">var</span> obj3 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  945. </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>toString</code>和<code>valueOf</code>方法)的对象,可以将<code>Object.create</code>的参数设为<code>null</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 不继承Object的toString和valueOf方法的一个对象</span>
  946. obj<span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  947. <span class="token comment">// TypeError: Object [object Object] has no method 'valueOf'</span>
  948. </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>obj</code>的原型是<code>null</code>,它就不具备一些定义在<code>Object.prototype</code>对象上面的属性,比如<code>valueOf</code>方法。</p> <p>使用<code>Object.create</code>方法的时候,必须提供对象原型,即<strong>参数不能为空,或者不是对象,否则会报错</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  949. <span class="token comment">// TypeError: Object prototype may only be an Object or null</span>
  950. Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token number">123</span><span class="token punctuation">)</span>
  951. <span class="token comment">// TypeError: Object prototype may only be an Object or null</span>
  952. </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>Object.create</code>方法生成的新对象,动态继承了原型。在原型上添加或修改任何方法,会立刻反映在新对象之上。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj1 <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token literal-property property">p</span><span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
  953. <span class="token keyword">var</span> obj2 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>obj1<span class="token punctuation">)</span><span class="token punctuation">;</span>
  954. obj1<span class="token punctuation">.</span>p <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span>
  955. obj2<span class="token punctuation">.</span>p <span class="token comment">// 2 obj2的原型指向obj1,当访问obj2上的p属性时,js引擎会先在obj2本身上找,没找到会去原型上找</span>
  956. </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>obj1</code>会影响到实例对象<code>obj2</code>。</p> <p>除了对象的原型,<code>Object.create</code>方法还可以接受<strong>第二个参数。该参数是一个属性描述对象,它所描述的对象属性,会添加到实例对象,作为该对象自身的属性</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</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>
  957. <span class="token literal-property property">p1</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token comment">// p1为添加到obj实例对象自身的属性</span>
  958. <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token number">123</span><span class="token punctuation">,</span>
  959. <span class="token literal-property property">enumerable</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  960. <span class="token literal-property property">configurable</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  961. <span class="token literal-property property">writable</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  962. <span class="token punctuation">}</span><span class="token punctuation">,</span>
  963. <span class="token literal-property property">p2</span><span class="token operator">:</span> <span class="token punctuation">{</span>
  964. <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token string">'abc'</span><span class="token punctuation">,</span>
  965. <span class="token literal-property property">enumerable</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  966. <span class="token literal-property property">configurable</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  967. <span class="token literal-property property">writable</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
  968. <span class="token punctuation">}</span>
  969. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  970. <span class="token comment">// 等同于</span>
  971. <span class="token keyword">var</span> obj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</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>
  972. obj<span class="token punctuation">.</span>p1 <span class="token operator">=</span> <span class="token number">123</span><span class="token punctuation">;</span>
  973. obj<span class="token punctuation">.</span>p2 <span class="token operator">=</span> <span class="token string">'abc'</span><span class="token punctuation">;</span>
  974. </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>Object.create</code>方法生成的对象,<strong>继承了它的原型对象的构造函数</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token constant">A</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  975. <span class="token keyword">var</span> a <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">A</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  976. <span class="token keyword">var</span> b <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span>
  977. b<span class="token punctuation">.</span>constructor <span class="token operator">===</span> <span class="token constant">A</span> <span class="token comment">// true</span>
  978. b <span class="token keyword">instanceof</span> <span class="token class-name">A</span> <span class="token comment">// true</span>
  979. </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>b</code>对象的原型是<code>a</code>对象,因此继承了<code>a</code>对象的构造函数<code>A</code>。</p> <h3 id="_4、object-prototype-isprototypeof-判断某个对象是否为参数对象的原型"><a href="#_4、object-prototype-isprototypeof-判断某个对象是否为参数对象的原型" class="header-anchor">#</a> 4、Object.prototype.isPrototypeOf()判断某个对象是否为参数对象的原型</h3> <p>实例对象的<code>isPrototypeOf</code>方法,用来<strong>判断该对象是否为参数对象的原型</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o1 <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  980. <span class="token keyword">var</span> o2 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>o1<span class="token punctuation">)</span><span class="token punctuation">;</span>
  981. <span class="token keyword">var</span> o3 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>o2<span class="token punctuation">)</span><span class="token punctuation">;</span>
  982. o2<span class="token punctuation">.</span><span class="token function">isPrototypeOf</span><span class="token punctuation">(</span>o3<span class="token punctuation">)</span> <span class="token comment">// true 判断o2是否为o3的原型</span>
  983. o1<span class="token punctuation">.</span><span class="token function">isPrototypeOf</span><span class="token punctuation">(</span>o3<span class="token punctuation">)</span> <span class="token comment">// true 判断o1是否为o3的原型</span>
  984. </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>o1</code>和<code>o2</code>都是<code>o3</code>的原型。这表明只要实例对象处在参数对象的原型链上,<code>isPrototypeOf</code>方法都返回<code>true</code>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">isPrototypeOf</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">// true</span>
  985. <span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">isPrototypeOf</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">// true</span>
  986. <span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">isPrototypeOf</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">xyz</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">)</span> <span class="token comment">// true</span>
  987. <span class="token class-name">Object</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">isPrototypeOf</span><span class="token punctuation">(</span>Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
  988. </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>Object.prototype</code>处于原型链的最顶端,所以对各种实例都返回<code>true</code>,只有直接继承自<code>null</code>的对象除外。</p> <h3 id="_5、object-prototype-proto-返回该对象的原型-可读写"><a href="#_5、object-prototype-proto-返回该对象的原型-可读写" class="header-anchor">#</a> 5、Object.prototype.__proto__ 返回该对象的原型,可读写</h3> <p>实例对象的<code>__proto__</code><strong>属性</strong>(前后各两个下划线),<strong>返回该对象的原型。该属性可读写</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</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">// 创建实例对象obj,其原型指定为{x:1}</span>
  989. obj<span class="token punctuation">.</span>__proto__ <span class="token comment">// {x: 1} 实例对象obj的__proto__属性,返回obj的原型</span>
  990. Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token comment">// {x: 1}</span>
  991. </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>Object.create</code>创建实例对象obj,指定其原型为<code>{x:1}</code>,访问obj对象的<code>__proto__</code>属性,返回其原型。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  992. <span class="token keyword">var</span> p <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  993. obj<span class="token punctuation">.</span>__proto__ <span class="token operator">=</span> p<span class="token punctuation">;</span> <span class="token comment">// 原型属性可读写</span>
  994. Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token operator">===</span> p <span class="token comment">// true</span>
  995. obj<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token comment">//true</span>
  996. </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>__proto__</code>属性,将<code>p</code>对象设为<code>obj</code>对象的原型。</p> <p>根据语言标准,<code>__proto__</code>属性只有浏览器才需要部署,其他环境可以没有这个属性。它前后的两根下划线,表明它本质是一个内部属性,不应该对使用者暴露。<strong>因此,应该尽量少用这个属性,而是用<code>Object.getPrototypeOf()</code>和<code>Object.setPrototypeOf()</code>,进行原型对象的读写操作</strong>。</p> <p>原型链可以用<code>__proto__</code>很直观地表示。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token constant">A</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  997. <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'张三'</span>
  998. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  999. <span class="token keyword">var</span> <span class="token constant">B</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  1000. <span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'李四'</span>
  1001. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  1002. <span class="token keyword">var</span> proto <span class="token operator">=</span> <span class="token punctuation">{</span>
  1003. <span class="token function-variable function">print</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>
  1004. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
  1005. <span class="token punctuation">}</span>
  1006. <span class="token punctuation">}</span><span class="token punctuation">;</span>
  1007. <span class="token constant">A</span><span class="token punctuation">.</span>__proto__ <span class="token operator">=</span> proto<span class="token punctuation">;</span> <span class="token comment">// 将A的原型指向proto对象</span>
  1008. <span class="token constant">B</span><span class="token punctuation">.</span>__proto__ <span class="token operator">=</span> proto<span class="token punctuation">;</span> <span class="token comment">// 将B的原型指向proto对象</span>
  1009. <span class="token comment">// 共享print方法,都是在调用proto对象内的print方法</span>
  1010. <span class="token constant">A</span><span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 张三</span>
  1011. <span class="token constant">B</span><span class="token punctuation">.</span><span class="token function">print</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// 李四</span>
  1012. <span class="token constant">A</span><span class="token punctuation">.</span>print <span class="token operator">===</span> <span class="token constant">B</span><span class="token punctuation">.</span>print <span class="token comment">// true</span>
  1013. <span class="token constant">A</span><span class="token punctuation">.</span>print <span class="token operator">===</span> proto<span class="token punctuation">.</span>print <span class="token comment">// true</span>
  1014. <span class="token constant">B</span><span class="token punctuation">.</span>print <span class="token operator">===</span> proto<span class="token punctuation">.</span>print <span class="token comment">// true</span>
  1015. </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></div></div><p>上面代码中,<code>A</code>对象和<code>B</code>对象的原型都是<code>proto</code>对象,它们都共享<code>proto</code>对象的<code>print</code>方法。也就是说,<code>A</code>和<code>B</code>的<code>print</code>方法,都是在调用<code>proto</code>对象的<code>print</code>方法。</p> <h3 id="_6、获取原型对象方法的比较"><a href="#_6、获取原型对象方法的比较" class="header-anchor">#</a> 6、获取原型对象方法的比较</h3> <p>如前所述,<code>__proto__</code>属性指向当前对象的原型对象,即构造函数的<code>prototype</code>属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> obj <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  1016. obj<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> <span class="token class-name">Object</span><span class="token punctuation">.</span>prototype
  1017. <span class="token comment">// true</span>
  1018. obj<span class="token punctuation">.</span>__proto__ <span class="token operator">===</span> obj<span class="token punctuation">.</span>constructor<span class="token punctuation">.</span>prototype
  1019. <span class="token comment">// true</span>
  1020. </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>obj</code>,它的<code>__proto__</code>属性,指向构造函数(<code>Object</code>或<code>obj.constructor</code>)的<code>prototype</code>属性。</p> <p>因此,获取实例对象<code>obj</code>的原型对象,有三种方法。</p> <ul><li><code>obj.__proto__</code></li> <li><code>obj.constructor.prototype</code></li> <li><code>Object.getPrototypeOf(obj)</code></li></ul> <p>上面三种方法之中,前两种都不是很可靠。<code>__proto__</code>属性只有浏览器才需要部署,其他环境可以不部署。而<code>obj.constructor.prototype</code>在手动改变原型对象时,可能会失效。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> <span class="token function-variable function">P</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>
  1021. <span class="token keyword">var</span> p <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">P</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  1022. <span class="token keyword">var</span> <span class="token function-variable function">C</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>
  1023. <span class="token class-name">C</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> p<span class="token punctuation">;</span>
  1024. <span class="token keyword">var</span> c <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">C</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  1025. c<span class="token punctuation">.</span>constructor<span class="token punctuation">.</span>prototype <span class="token operator">===</span> p <span class="token comment">// false</span>
  1026. </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>C</code>的原型对象被改成了<code>p</code>,但是实例对象的<code>c.constructor.prototype</code>却没有指向<code>p</code>。所以,在改变原型对象时,一般要同时设置<code>constructor</code>属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token class-name">C</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> p<span class="token punctuation">;</span>
  1027. <span class="token class-name">C</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span>constructor <span class="token operator">=</span> <span class="token constant">C</span><span class="token punctuation">;</span> <span class="token comment">// 如在构造函数的继承中就使用到这个操作</span>
  1028. <span class="token keyword">var</span> c <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">C</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  1029. c<span class="token punctuation">.</span>constructor<span class="token punctuation">.</span>prototype <span class="token operator">===</span> p <span class="token comment">// true</span>
  1030. </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>因此,<strong>推荐使用第三种<code>Object.getPrototypeOf</code>方法,获取原型对象</strong>。</p> <h3 id="_7、object-getownpropertynames"><a href="#_7、object-getownpropertynames" class="header-anchor">#</a> 7、Object.getOwnPropertyNames()</h3> <p><code>Object.getOwnPropertyNames</code>方法<strong>返回一个数组,成员是参数对象本身的所有属性的键名,不包含继承的属性键名</strong>。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyNames</span><span class="token punctuation">(</span>Date<span class="token punctuation">)</span>
  1031. <span class="token comment">// [&quot;parse&quot;, &quot;arguments&quot;, &quot;UTC&quot;, &quot;caller&quot;, &quot;name&quot;, &quot;prototype&quot;, &quot;now&quot;, &quot;length&quot;]</span>
  1032. </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>Object.getOwnPropertyNames</code>方法返回<code>Date</code>所有自身的属性名。</p> <p>对象本身的属性之中,有的是可以遍历的(enumerable),有的是不可以遍历的。<code>Object.getOwnPropertyNames</code>方法返回所有键名,不管是否可以遍历。只获取那些可以遍历的属性,使用<code>Object.keys</code>方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code>Object<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span>Date<span class="token punctuation">)</span> <span class="token comment">// []</span>
  1033. </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><p>上面代码表明,<code>Date</code>对象所有自身的属性,都是不可以遍历的。</p> <h3 id="_8、object-prototype-hasownproperty"><a href="#_8、object-prototype-hasownproperty" class="header-anchor">#</a> 8、Object.prototype.hasOwnProperty()</h3> <p>对象实例的<code>hasOwnProperty</code>方法返回一个布尔值,用于<strong>判断某个属性定义在对象自身,还是定义在原型链上</strong>。</p> <div class="language- line-numbers-mode"><pre class="language-text"><code>Date.hasOwnProperty('length') // true
  1034. Date.hasOwnProperty('toString') // false
  1035. </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>Date.length</code>(构造函数<code>Date</code>可以接受多少个参数)是<code>Date</code>自身的属性,<code>Date.toString</code>是继承的属性。</p> <p>另外,<code>hasOwnProperty</code>方法是 JavaScript 之中唯一一个处理对象属性时,不会遍历原型链的方法。</p> <h3 id="_9、in-运算符和-for-in-循环"><a href="#_9、in-运算符和-for-in-循环" class="header-anchor">#</a> 9、in 运算符和 for...in 循环</h3> <p><code>in</code>运算符<strong>返回一个布尔值,表示一个对象是否具有某个属性</strong>。它不区分该属性是对象自身的属性,还是继承的属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token string">'length'</span> <span class="token keyword">in</span> Date <span class="token comment">// true</span>
  1036. <span class="token string">'toString'</span> <span class="token keyword">in</span> Date <span class="token comment">// true</span>
  1037. </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>in</code>运算符常用于检查一个属性是否存在。</p> <p>获得对象的所有可遍历属性(不管是自身的还是继承的),可以使用<code>for...in</code>循环。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">var</span> o1 <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token literal-property property">p1</span><span class="token operator">:</span> <span class="token number">123</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
  1038. <span class="token keyword">var</span> o2 <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>o1<span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token comment">// o2的原型指向o1,并且在o2上定义一个属性p2</span>
  1039. <span class="token literal-property property">p2</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">value</span><span class="token operator">:</span> <span class="token string">&quot;abc&quot;</span><span class="token punctuation">,</span> <span class="token literal-property property">enumerable</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span>
  1040. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  1041. <span class="token keyword">for</span> <span class="token punctuation">(</span>p <span class="token keyword">in</span> o2<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  1042. console<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span>p<span class="token punctuation">)</span><span class="token punctuation">;</span>
  1043. <span class="token punctuation">}</span>
  1044. <span class="token comment">// p2</span>
  1045. <span class="token comment">// p1 继承的属性</span>
  1046. </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>o2</code>的<code>p2</code>属性是自身的,<code>p1</code>属性是继承的。这两个属性都会被<code>for...in</code>循环遍历。</p> <p>为了在<code>for...in</code>循环中获得对象自身的属性,可以采用<code>hasOwnProperty</code>方法判断一下。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">var</span> name <span class="token keyword">in</span> object <span class="token punctuation">)</span> <span class="token punctuation">{</span>
  1047. <span class="token keyword">if</span> <span class="token punctuation">(</span> object<span class="token punctuation">.</span><span class="token function">hasOwnProperty</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 过滤掉非自身的属性</span>
  1048. <span class="token comment">/* loop code */</span>
  1049. <span class="token punctuation">}</span>
  1050. <span class="token punctuation">}</span>
  1051. </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><strong>获得对象的所有属性(不管是自身的还是继承的,也不管是否可枚举)</strong>,可以使用下面的函数。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">inheritedPropertyNames</span><span class="token punctuation">(</span><span class="token parameter">obj</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  1052. <span class="token keyword">var</span> props <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  1053. <span class="token keyword">while</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  1054. <span class="token comment">// 获取obj对象的所有属性,包括不可枚举的,</span>
  1055. Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyNames</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><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">p</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  1056. props<span class="token punctuation">[</span>p<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
  1057. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  1058. obj <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 获取对象的原型</span>
  1059. <span class="token punctuation">}</span>
  1060. console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span>
  1061. <span class="token keyword">return</span> Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyNames</span><span class="token punctuation">(</span>props<span class="token punctuation">)</span><span class="token punctuation">;</span>
  1062. <span class="token punctuation">}</span>
  1063. </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>obj</code>对象的每一级原型对象“自身”的属性,从而获取<code>obj</code>对象的“所有”属性,不管是否可遍历。</p> <p>下面是一个例子,列出<code>Date</code>对象的所有属性。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token function">inheritedPropertyNames</span><span class="token punctuation">(</span>Date<span class="token punctuation">)</span>
  1064. <span class="token comment">// [</span>
  1065. <span class="token comment">// &quot;caller&quot;,</span>
  1066. <span class="token comment">// &quot;constructor&quot;,</span>
  1067. <span class="token comment">// &quot;toString&quot;,</span>
  1068. <span class="token comment">// &quot;UTC&quot;,</span>
  1069. <span class="token comment">// ...</span>
  1070. <span class="token comment">// ]</span>
  1071. </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><h3 id="_10、对象的拷贝"><a href="#_10、对象的拷贝" class="header-anchor">#</a> 10、对象的拷贝</h3> <p>如果要拷贝一个对象,需要做到下面两件事情。</p> <ul><li>确保拷贝后的对象,与原对象具有同样的原型。</li> <li>确保拷贝后的对象,与原对象具有同样的实例属性。</li></ul> <p>下面就是根据上面两点,实现的对象拷贝函数。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">copyObject</span><span class="token punctuation">(</span><span class="token parameter">orig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 拷贝对象函数</span>
  1072. <span class="token comment">// 创建一个新对象,新对象的原型指向旧对象的原型</span>
  1073. <span class="token keyword">var</span> copy <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>orig<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  1074. <span class="token function">copyOwnPropertiesFrom</span><span class="token punctuation">(</span>copy<span class="token punctuation">,</span> orig<span class="token punctuation">)</span><span class="token punctuation">;</span>
  1075. <span class="token keyword">return</span> copy<span class="token punctuation">;</span>
  1076. <span class="token punctuation">}</span>
  1077. <span class="token keyword">function</span> <span class="token function">copyOwnPropertiesFrom</span><span class="token punctuation">(</span><span class="token parameter">target<span class="token punctuation">,</span> source</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 拷贝旧对象的实例属性</span>
  1078. Object
  1079. <span class="token punctuation">.</span><span class="token function">getOwnPropertyNames</span><span class="token punctuation">(</span>source<span class="token punctuation">)</span>
  1080. <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">propKey</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  1081. <span class="token comment">// 获取每个属性的 属性描述对象</span>
  1082. <span class="token keyword">var</span> desc <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyDescriptor</span><span class="token punctuation">(</span>source<span class="token punctuation">,</span> propKey<span class="token punctuation">)</span><span class="token punctuation">;</span>
  1083. <span class="token comment">// 定义属性,给target对象定义propKey属性,其属性描述对象是desc</span>
  1084. Object<span class="token punctuation">.</span><span class="token function">defineProperty</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> propKey<span class="token punctuation">,</span> desc<span class="token punctuation">)</span><span class="token punctuation">;</span>
  1085. <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  1086. <span class="token keyword">return</span> target<span class="token punctuation">;</span>
  1087. <span class="token punctuation">}</span>
  1088. </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><p>另一种更简单的写法,是利用 ES2017 才引入标准的<code>Object.getOwnPropertyDescriptors</code>方法。</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">copyObject</span><span class="token punctuation">(</span><span class="token parameter">orig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  1089. <span class="token keyword">return</span> Object<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>
  1090. Object<span class="token punctuation">.</span><span class="token function">getPrototypeOf</span><span class="token punctuation">(</span>orig<span class="token punctuation">)</span><span class="token punctuation">,</span>
  1091. Object<span class="token punctuation">.</span><span class="token function">getOwnPropertyDescriptors</span><span class="token punctuation">(</span>orig<span class="token punctuation">)</span>
  1092. <span class="token punctuation">)</span><span class="token punctuation">;</span>
  1093. <span class="token punctuation">}</span>
  1094. </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="五、严格模式"><a href="#五、严格模式" class="header-anchor">#</a> 五、严格模式</h2> <p>除了正常的运行模式,JavaScript 还有第二种运行模式:严格模式(strict mode)。顾名思义,这种模式采用更加严格的 JavaScript 语法。</p> <p>同样的代码,在正常模式和严格模式中,可能会有不一样的运行结果。一些在正常模式下可以运行的语句,在严格模式下将不能运行。</p> <h3 id="_1、设计目的"><a href="#_1、设计目的" class="header-anchor">#</a> 1、设计目的</h3> <p>早期的 JavaScript 语言有很多设计不合理的地方,但是为了兼容以前的代码,又不能改变老的语法,只能不断添加新的语法,引导程序员使用新语法。</p> <p>严格模式是从 ES5 进入标准的,主要目的有以下几个。</p> <ul><li>明确禁止一些不合理、不严谨的语法,减少 JavaScript 语言的一些怪异行为。</li> <li>增加更多报错的场合,消除代码运行的一些不安全之处,保证代码运行的安全。</li> <li>提高编译器效率,增加运行速度。</li> <li>为未来新版本的 JavaScript 语法做好铺垫。</li></ul> <p>总之,严格模式体现了 JavaScript 更合理、更安全、更严谨的发展方向。</p> <h3 id="_2、启用方法"><a href="#_2、启用方法" class="header-anchor">#</a> 2、启用方法</h3> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token string">'use strict'</span><span class="token punctuation">;</span>
  1095. </code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br></div></div><blockquote><p>更多关于严格模式内容:<a href="https://wangdoc.com/javascript/oop/strict.html" target="_blank" rel="noopener noreferrer">https://wangdoc.com/javascript/oop/strict.html<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></p></blockquote> <h2 id="文档"><a href="#文档" class="header-anchor">#</a> 文档</h2> <p>学习文档:<a href="https://wangdoc.com/javascript/" target="_blank" rel="noopener noreferrer">https://wangdoc.com/javascript/<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></p></div></div> <div class="page-edit"><div class="edit-link"><a href="https://github.com/heBody/blog/edit/master/docs/《JavaScript教程》笔记/03.面向对象.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=JavaScript" title="标签">#JavaScript</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/74d2ab3fbfeaaa68/" class="page-nav-centre page-nav-centre-prev"><div class="tooltip">内置对象</div></a> <a href="/blog/pages/d61b1cb4cdac1f63/" class="page-nav-centre page-nav-centre-next"><div class="tooltip">异步操作</div></a></div> <div class="page-nav"><p class="inner"><span class="prev">
  1096. <a href="/blog/pages/74d2ab3fbfeaaa68/" class="prev">内置对象</a></span> <span class="next"><a href="/blog/pages/d61b1cb4cdac1f63/">异步操作</a>→
  1097. </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>
  1098. Git修改分支名
  1099. <!----></div></a> <span class="date">08-11</span></dt></dl><dl><dd>02</dd> <dt><a href="/blog/pages/55f894/"><div>
  1100. CSS给table的tbody添加滚动条
  1101. <!----></div></a> <span class="date">06-29</span></dt></dl><dl><dd>03</dd> <dt><a href="/blog/pages/829589/"><div>
  1102. 我做了一个手写春联小网页,祝大家虎年暴富
  1103. <span class="title-tag">
  1104. 原创
  1105. </span></div></a> <span class="date">01-28</span></dt></dl> <dl><dd></dd> <dt><a href="/blog/archives/" class="more">更多文章&gt;</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>
  1106. Copyright © 2016-2022
  1107. <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">
  1108. 跟随系统
  1109. </li><li class="iconfont icon-rijianmoshi">
  1110. 浅色模式
  1111. </li><li class="iconfont icon-yejianmoshi">
  1112. 深色模式
  1113. </li><li class="iconfont icon-yuedu">
  1114. 阅读模式
  1115. </li></ul></div></div> <!----> <!----> <!----></div><div class="global-ui"><div></div></div></div>
  1116. <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/138.d976c801.js" defer></script>
  1117. </body>
  1118. </html>