你不知道的javascript(上)


JavaScript 书 图设计丛书 书 978-7-115-38573-4 2015-04 页 208 49.00 类别 javascript 对JavaScript这门语简单JavaScript语杂 语这编应这杂 语经验JavaScript发员认习话 们书们JavaScript发趋势语 书JavaScript语阅读经验JavaScript发员 习 Kyle Simpson 联对JavaScriptHTML5实时/对Web 术书术训师讲师跃员 书书 You Don't Know JS: Types & Grammar 书书 978-1491904190 书 书 O'Reilly 书页 125 O'Reilly Media, Inc.绍 O'Reilly Media过图书杂线务调查议传创识1978 O'Reilly发见证动级们创们 术趋势——过“细”对应为术跃 O'Reilly发满对创导创发扬 O'Reilly为软发员带“动书”创业GNN组织响远 码软运动创Make杂为DIY 锋过缔结纽带O'Reilly议众级 远业领绘创产业为术获选择 O'Reilly现还锋专识传递给计户论过书线务 课项O'Reilly产动摇——发创 业评论 “O'Reilly Radar” ——Wired “O'Reilly业务” ——Business 2.0 “O'Reilly Conference键领绝对” ——CRN “O'Reilly书习题” ——Irish Times “Tim长远阔视实Yogi Berra议 ‘’顾过Tim选择 闪错” ——Linux Journal 联发JavaScript经为页验础术时 JavaScript仅仅闪烁标轨烦弹经过约20 发JavaScript术发变现JavaScript问经 为围软——联——术 为语说总为评对历遗问题 设计问题Brendan Eich经说过JavaScript连给 “”觉Java过过营销 这两语许质别JavaScriptJavaCarnival华 Car车样 JavaScript鉴许语语C风过编显Scheme/List 风编许发编JavaScript编 “Hello World”简单这门语 虽JavaScript现语语 闭 Kyle Simpson 赵译 时别欢东——动电话响 对说这东还为时过们 们 记见电缠满铜线长这长 为现为电 样为铜线缠绕铜线发现 这见缠绕铜线铁氧环线 对样为瘾实这 欢——强 遗现专业东时 够过东现东JavaScript 铁氧......们经热爱东现 运经寻问题bug时间 这为“JavaScript”图书动为JavaScript JavaScript经续 吗细节经阅读标邮 岁达 “闭”对众说对 样这书JavaScript让识对运 时这书现时ES6终稳浏览 实现还习letconst这书 绍 欢这书Kyle对JavaScript细节 过 Shane Hudson www.shanehudson.net 1 编语够储变值对这值进 访问实这储访问变值态带给 态这虽够执简单务 变问题们讨论这变哪换 话说们储哪时们 这问题说设计规则储变这变这规 则为 哪样设这规则 1.1编译 JavaScript归类为“动态”“释执”语实门编译语这实对 说显见闻闻过编语经验传 统编译语编译编译结统进 JavaScript进编译骤传统编译语环节预 杂 传统编译语码执经历骤统为“编译” 词/词Tokenizing/Lexing 这过组对编语说义码块这码块 为词单token虑var a = 2;这为这词 单vara=2 ;词单这门语义 词tokenizing词Lexing间别涩词 单识别过态还态进简单说词单 a词单还词单时调态规则 这过为词 /语Parsing 这过词单组转换级组语结构 树这树为“语树”Abstract Syntax TreeAST var a = 2;语树VariableDeclaration顶级节 Identifier值a节AssignmentExpression节 AssignmentExpression节NumericLiteral值2节 码 AST转换为执码过为码这过语标 细节简单说var a = 2;AST转为组 创a变值储a 统资们讨论围简单 创储变 编译过骤语编译JavaScript杂语 码阶骤对运进优对进优 这进观简单绍发现们绍这 讨论联 JavaScript语编译时间进优为 语JavaScript编译过发构 对JavaScript说编译发码执时间 们讨论JavaScript办JIT迟编译实编 译证 简单说JavaScript码执进编译执JavaScript编 译对var a = 2;这进编译执备马执 1.2 们习这过拟间对话谁进这场对话 1.2.1员 绍对var a = 2;进处过员们这样 对话 头负责JavaScript编译执过 编译 负责语码脏详见节 另负责维护标识变组查询实 严规则执码对这标识访问权 为够JavaScript们样们 问题们这问题 1.2.2对话 见var a = 2;这时认为这们这 实认为这两编译编译时处另则运 时处 们var a = 2;们协 编译这词单词单树结构编译 进码时对这处预 设编译产码够伪码进“为变 为a值2进这变”这 实编译进处 1. var a编译询问经该变 编译该继续进编译则 变为a 2. 编译为运时码这码处a = 2这赋值 运时询问a变 这变继续查该变查1.3节 终a变2赋值给则举 总结变赋值执两动编译变 过运时查该变够对赋值 1.2.3编译话说 为进们绍编译术语 编译编译过码执时过查变a 过查过进协执样查响终查结 们为变a进LHS查询另查类RHS 赌“L”“R”义们别侧侧 东侧侧赋值侧侧 换话说变现赋值侧时进LHS查询现侧时进RHS查询 讲RHS查询简单查变值别LHS查询则试图变 对赋值这说RHS义“赋值侧” 说“侧” RHSretrieve his source value值这“值” 让们继续 虑码 console.log( a ); 对aRHS为这a赋值应查a值这 样值传递给console.log(..) a = 2; 这对a则LHS为实际们值为= 2这赋 值标 LHSRHS义“赋值侧侧”“=赋值侧 侧”赋值还为“赋值标谁 LHS”“谁赋值头RHS” 虑LHSRHS function foo(a) { console.log( a ); // 2 } foo( 2 ); foo(..)调对foo进RHS“foo值给” (..)foo值执类值 这还细节 码隐a = 2这发2传递给foo(..) 时2给a为给a隐值进LHS查询 这还对a进RHS值传给console.log(..)console.log(..) 执对console对进RHS查询检查值 log 为LHSRHS间过对值2进传递进log(..)过变 aRHS查询设log(..)实现2赋值给 许arg1这进LHS查询 倾function foo(a) {...为变赋值 var foofoo function(a) {...这样话这进LHS查询 还细别编译码时处值义 执码时线专门值“给”foo 讨论LHS查询赋值 1.2.4对话 function foo(a) { console.log( a ); // 2 } foo( 2 ); 让们这码处过对话这对话这样 说为foo进RHS见过吗 别说还见过编译刚刚给 们够执foo 还为a进LHS这见过吗 这见过编译为foo 谢总这现2赋值给a 们扰为console进RHS见过吗 咱俩谁谁说这这console对给 哒这log(..) 们对aRHS吗虽记认 这变变动过谢 a值2传递进log(..) …… 1.2.5测验 检验进“对话” function foo(a) { var b = a; return a + b; } var c = foo( 2 ); 1. LHS查询这3处 2.RHS查询这4处 查结 1.3 们说过查变规则实际时顾 块另块时发 变时层继续查该变达层 为 虑码 function foo(a) { console.log( a + b ); } var b = 2; foo( 2 ); // 4 对b进RHSfoo级这 顾间对话进 foo见过b吗对进RHS 过 foo级识见过b 吗对进RHS 给 历链规则简单执查变 级继续查达层时论还查过 链喻 为处过视脑这 这链层执处 顶层 LHSRHS层进查电层还 继续类达顶层变 论查过 1.4 为LHSRHS 为变还该变这两查询为 样 虑码 function foo(a) { console.log( a + b ); b = a; } foo( 2 ); 对b进RHS查询时该变说这“”变为 RHS查询寻变ReferenceError值 ReferenceError类 较执LHS查询时顶层标变 创该变还给运“严” “这变热创” ES5“严”说宽/懒严为 为严动隐创变严LHS查 询败时创变RHS查询败时类ReferenceError RHS查询变尝试对这变值进试图 对类值进调nullundefined类值 另类TypeError ReferenceError别败TypeError则别对结 1.5结 规则处查变标识查对变进 赋值LHS查询获变值RHS查询 赋值导LHS查询调时传导联赋值 JavaScript码执对进编译这过var a = 2这样 两骤 1. var a变这阶码执进 2. a = 2查询LHS查询变a对进赋值 LHSRHS查询执说们标识 级继续查标标识这样级层达 顶层论 RHS导ReferenceErrorLHS导动隐创 变严该变LHS标为标识ReferenceError 严 测验 function foo(a) { var b = a; return a + b; } var c = foo( 2 ); 1. LHS查询这3处 c = ..;a = 2隐变b = .. 2. RHS查询这4处 foo(2..= a;a .... b 2 词 1们“”义为规则这规则 标识进变查 两为编语词 们对这进讨论另动态编语 BashPerl 录A绍动态这为JavaScript词 进对 2.1词阶 1绍过标语编译阶词单词忆词 过对码进检查态过还赋单词语义 这词历础 简单说词义词阶换话说词码时 变块哪词处码时变 这样 绍骗词这词处过 这难实让词词书时 变实 虑码 function foo(a) { var b = a * 2; function bar(c) { console.log( a, b, c ); } bar( b * 3 ); } foo( 2 ); // 2, 4, 12 这级为们级 1标识foo 2foo创标识abarb 3bar创标识c 对应块码哪们级讨论类 现设创 barfoo创们义bar 这说严们讨论图1这边换 话说时现两 时现两级样 1论类 图http://zh.wikipedia.org/wiki/%E6%96%87%E6%B0%8F%E5%9B%BE——译 查 结构间给够这查 标识 码执console.log(..)查abc变 bar(..)查这a 级foo(..)继续查这a这对b讲 样对c说bar(..) acbar(..)foo(..)console.log(..)bar(..)变 foo(..)查 查标识时层义标识 这“应”标识“”标识应查终 运时处级说进见标识为 变动为对浏览window对 过对词间过对对对进访问 window.a 过这术访问变变变 论访问 论哪调论调词时处 词查查级标识abc码foo.bar.baz词查 试图查foo标识这变对访问规则别对barbaz访 问 2.2骗词 词码间义样运时“” 说骗词 JavaScript两实现这认为码这两 们论骗词导 详细释问题这两别 2.2.1eval JavaScripteval(..)为视为书时 这码换话说码码运码 样 这eval(..)过码骗书时词码 实现词环这变懂 执eval(..)码时“”“”码动态进对 词环进进词查 虑码 function foo(str, a) { eval( str ); // 骗 console.log( a, b ); } var b = 2; foo( "var b = 3;", 1 ); // 1, 3 eval(..)调"var b = 3;"这码样处码 变b对经foo(..)词进实 样这码实际foo(..)创变b 变 console.log(..)执时foo(..)时ab远b 输“1, 3”输“1, 2” 这为简洁们传递进“码”变 实际逻辑动态传递进 eval(..)执动态创码为这样动态执 组码码处 认eval(..)执码论变还 对eval(..)处词进术过经们讨论围间 调eval(..)运对进论 eval(..)运书词 严eval(..)运时词 function foo(str) { "use strict"; eval( str ); console.log( a ); // ReferenceError: a is not defined } foo( "var a = 2"); JavaScript还eval(..)setTimeout(..)setInterval(..) 释为动态码这经过时 们 new Function(..)为类码转为动态 这这构语eval(..) 动态码场见为带处损 2.2.2with JavaScript另难现骗词with键 释with这选择这释响词 进 with对对 var obj = { a: 1, b: 2, c: 3 }; // 单调"obj" obj.a = 2; obj.b = 3; obj.c = 4; // 简单 with (obj) { a = 3; b = 4; c = 5; } 实际这仅仅为访问对虑码 function foo(obj) { with (obj) { a = 2; } } var o1 = { a: 3 }; var o2 = { b: 3 }; foo( o1 ); console.log( o1.a ); // 2 foo( o2 ); console.log( o2.a ); // undefined console.log( a ); // 2——a 这创o1o2两对a另foo(..)obj 该对对这对执with(obj) {..}with块们 码对变a进简单词实际LHS查12赋值 给 们o1传递进a = 2赋值o1.a2赋值给这console.log(o1.a) 现o2传递进o2a创这o2.aundefined 实际a = 2赋值创变a这 with对处为词这对 处为义这词标识 with块对处为词这块var 这块with处 eval(..)码处词with 实际传递给对创词 这样们传递o1给with时witho1这 o1.a标识们o2为时a标识进 LHS标识查查1 o2foo(..)标识aa = 2执时动创 变为严 with这对进时标识为让费为说们 现这给释 另eval(..)with严响with 间eval(..) 2.2.3 eval(..)with运时创骗书时义词 问样们实现杂码扩难 吗 JavaScript编译阶进项优优赖够码词进 态预变义执过标识 码发现eval(..)with简单设标识 为词阶eval(..)码这码对进 传递给with创词对 观现eval(..)with优义简单 优 码eval(..)with运变论聪试图 这观围这优码运这 实 2.3结 词书码时编译词阶 够标识哪够预测执过对们进查 JavaScript两“骗”词eval(..)with对 “码”进经词运时质 过对处对标识处 创词样运时 这两编译时对查进优为谨认为这样 优这导码运变们 3 块 们2讨论样“”为 标识变义这齐窝结构 码时义 吗JavaScript结构 吗 3.1 对问题见JavaScript 为创结构创实这 们 虑码 function foo(a) { var b = 2; // 码 function bar() { // ... } // 码 var c = 3; } 这码foo(..)标识abcbar论标识现 处这标识变处们 讨论 bar(..)拥标识foo 标识abcbarfoo(..)foo(..)对们进访 问说这标识进访问码导 ReferenceError错误 bar(); // 败 console.log( a, b, c ); // 败 这标识abcfoobarfoo(..)访问样bar(..) 访问设bar(..)标识 义这变围实 这设计JavaScript变 变值类“动态” 时细处围访问变带 问题 3.2隐实现 对传统认码过带启 码选对进实际这 码“隐” 实际结这码围创说这码 变绑这创换 话说变这“隐”们 为“隐”变术 这隐们权则 权则这则软设计应该 “隐”块对API设计 这则选择变变 访问们这样权则 为过变这变应该码应该 对这变进访问 function doSomething(a) { b = a + doSomethingElse( a * 2 ); console.log( b * 3 ); } function doSomethingElse(a) { return a - 1; } var b; doSomething( 2 ); // 15 这码变bdoSomethingElse(..)应该doSomething(..)实现“ ”给对bdoSomethingElse(..)“访问权”仅“ 险”为们预导 doSomething(..) “”设计这隐doSomething(..) function doSomething(a) { function doSomethingElse(a) { return a - 1; } var b; b = a + doSomethingElse( a * 2 ); console.log( b * 3 ); } doSomething( 2 ); // 15 现bdoSomethingElse(..)访问doSomething(..) 终响设计设计软进实现 规 “隐”变带另处标识间两标识 样间导变值 function foo() { function bar(a) { i = 3; // for环i console.log( a + i ); } for (var i=0; i<10; i++) { bar( i * 2 ); // 环 } } foo(); bar(..)赋值达i = 3foo(..)for环i这 导环为i设为3远满10这 bar(..)赋值变var i = 3; 满这时为i过“变”另 标识var j = 3;软设计样标 识这“隐”选择 1. 间 变载库时们 变隐发 这库够变对这对 库间给为这对间 标识顶级词 var MyReallyCoolLibrary = { awesome: "stuff", doSomething: function() { // ... }, doAnotherThing: function() { // ... } }; 2. 块 另办现块众块选 这库标识过赖库 标识显导另 显见这够违词规则“”们规则强 标识这样规 赖实现5绍块 详细 3.3 们经码变义“隐” 访问 var a = 2; function foo() { // <-- 这 var a = 3; console.log( a ); // 3 } // <-- 这 foo(); // <-- 这 console.log( a ); // 2 虽这术问题为导额问题须 foo()foo这“污”这 须显过foo()调这运码 污够动运这 JavaScript够时这两问题 var a = 2; (function foo(){ // <-- 这 var a = 3; console.log( a ); // 3 })(); // <-- 这 console.log( a ); // 2 们别绍这发 (function...仅function...这显 细节实际别达标 处 达简单function键现仅仅 码function词 则达 达间别们标识绑处 较两码foo绑过foo()调 foo绑达 换话说(function foo(){ .. })为达foo..访问 则foo变隐污 3.3.1 对达场调 setTimeout( function() { console.log("I waited 1 second!"); }, 1000 ); 这达为function()..标识达 则——JavaScript语这 达书简单库倾这风码 虑 1. 栈显义调试难 2. 时经过arguments.callee递 归另发监绑 3. 对码读/让码 达强——间别对这响给 达问题终给达实 setTimeout( function timeoutHandler() { // <-- console.log( "I waited 1 second!" ); }, 1000 ); 3.3.2执达 var a = 2; (function foo() { var a = 3; console.log( a ); // 3 })(); console.log( a ); // 2 对( )为达过另( ) 执这(function foo(){ .. })()( )变达( )执 这 这见给规术语IIFE执达Immediately Invoked Function Expression 对IIFE须IIFE见达虽 IIFE见达优势值实 var a = 2; (function IIFE() { var a = 3; console.log( a ); // 3 })(); console.log( a ); // 2 较传统IIFE欢另进(function(){ .. }())细观 别达( )另()调 调()进( ) 这两选择哪 IIFE另进阶们调传递进 var a = 2; (function IIFE( global ) { var a = 3; console.log( a ); // 3 console.log( global.a ); // 2 })( window ); console.log( a ); // 2 们window对传递进为global码风对对 变“”样变传递东 变为觉这对进码风 这另应场undefined标识认值错误导虽 见为undefined对应传值这样证码块 undefined标识值undefined undefined = true; // 给码挖绝对这样 (function IIFE( undefined ) { var a; if (a === undefined) { console.log( "Undefined is safe here!" ); } })(); IIFE还变码运顺运IIFE执 传递进这UMDUniversal Module Definition项这 显长认为 var a = 2; (function IIFE( def ) { def( window ); })(function def( global ) { var a = 3; console.log( a ); // 3 console.log( global.a ); // 2 }); 达def义这def传递进IIFE 义def传递进调window传global 值 3.4块 见单现JavaScript设计 类单过类单实现维护 优简洁码 JavaScript编语块语发对维 对JavaScript发说这 连带块风码过对这见JavaScript码 for (var i=0; i<10; i++) { console.log( i ); } 们for环头义变i为for环i i绑实 这块处变应该另 var foo = true; if (foo) { var bar = foo * 2; bar = something( bar ); console.log( bar ); } bar变仅ifif块义 var变时哪样为们终这 码为风读伪块这 bar觉 块对权则进扩码隐扩为 块隐 虑for环 for (var i=0; i<10; i++) { console.log( i ); } 为for环应该变i污 发检查码围变 错误变导变变i块话 for环导错误这对证变 码维护 JavaScript块 3.4.1with 们2讨论过with键仅难结构时块 块with对创仅with 3.4.2try/catch JavaScriptES3规规try/catchcatch创块 变仅catch try { undefined(); // 执强 } catch (err) { console.log( err ); // 够执 } console.log( err ); // ReferenceError: err not found err仅catch试图别处时错误 这为经标标JavaScript环IE浏览 两catch样标识错误变 时态检查还发实际这义为变 块态检查还烦发 为这发catch为err1err2发 闭态检查对变检查 许catch创块这论样处查录B 发现 3.4.3let 为们JavaScript块为仅仅这样 JavaScript发块 ES6变现let键var另变 let键变绑{ .. }换话说let为 变隐块 var foo = true; if (foo) { let bar = foo * 2; bar = something( bar ); console.log( bar ); } console.log( bar ); // ReferenceError let变经块为隐发码过 哪块绑变习惯动这块 块导码变 为块显创块这问题变变讲显 码优隐码显块风书 语块 var foo = true; if (foo) { { // <-- 显 let bar = foo * 2; bar = something( bar ); console.log( bar ); } } console.log( bar ); // ReferenceError { .. }为let创绑块 这们if显创块对进构块 动对if语义产响 另显块达请查录B 4们讨论视为现围 let进块进码运“” { console.log( bar ); // ReferenceError! let bar = 2; } 1. 垃圾 另块闭垃圾这简说 实现闭5详细释 虑码 function process(data) { // 这 } var someReallyBigData = { .. }; process( someReallyBigData ); var btn = document.getElementById( "my_button" ); btn.addEventListener( "click", function click(evt) { console.log("button clicked"); }, /*apcturingPhase=*/false ); click击调someReallyBigData变论这process(..)执 间结构垃圾click 闭JavaScript这结构实现 块这顾虑让继续someReallyBigData function process(data) { // 这 } // 这块义销毁 { let someReallyBigData = { .. }; process( someReallyBigData ); } var btn = document.getElementById( "my_button" ); btn.addEventListener( "click", function click(evt) { console.log("button clicked"); }, /*capturingPhase=*/false ); 为变显块对变进绑码 2. let环 let发挥优势讨论for环 for (let i=0; i<10; i++) { console.log( i ); } console.log( i ); // ReferenceError for环头let仅i绑for环块实绑环 环结时值进赋值 过另说时进绑为 { let j; for (j=0; j<10; j++) { let i = j; // 绑 console.log( i ); } } 进绑们5讨论闭时进说 let码 对var隐赖时隐阱letvar则 码构过额 虑码 var foo = true, baz = 10; if (foo) { var bar = 3; if (baz > bar) { console.log( baz ); } // ... } 这码简单构 var foo = true, baz = 10; if (foo) { var bar = 3; // ... } if (baz > bar) { console.log( baz ); } 块级变时变 var foo = true, baz = 10; if (foo) { let bar = 3; if (baz > bar) { // <-- 动码时bar! console.log( baz ); } } 录B绍另块实现码 维护构 3.4.4const letES6还const样创块变值 试图值错误 var foo = true; if (foo) { var a = 2; const b = 3; // if块 a = 3; // ! b = 4; // 错误! } console.log( a ); // 3 console.log( b ); // ReferenceError! 3.5结 JavaScript见单质变处 “隐”这为软设计则 单块变仅处 码块{ .. } ES3try/catch结构catch块 ES6let键var键亲码块变if (..) { let a = 2; } if{ .. }块变变这块 认为块应该为两应该时发 应该选择创读维护优码 4 现为应该经变给 块为样总结为 变这 变现联这细节们讨论 4.1鸡还 觉认为JavaScript码执时执实际这 导这设错误 虑码 a = 2; var a; console.log( a ); 认为console.log(..)输 发认为undefined为var aa = 2们认为变赋 值赋认值undefined输结2 虑另码 console.log( a ); var a = 2; 鉴码现为认为这码 样为输2还认为变a进 ReferenceError 两测对输undefined 发们对鸡还问题 还赋值鸡 4.2编译袭 为这问题们顾1编译忆释 JavaScript码对进编译编译阶 们联2这词 变码执处 var a = 2;时认为这JavaScript实际两 var a;a = 2;义编译阶进赋值执 阶 们码进处 var a; a = 2; console.log( a ); 编译执 类们码实际处 var a; console.log( a ); a = 2; 这过变们码现“动” 这过 换话说鸡赋值 赋值运逻辑变码执 顺严 foo(); function foo() { console.log( a ); // undefined var a = 2; } foo这还实际隐值调执 另值进码经简为 们们讨论foo(..)对var a进显 这码实际为 function foo() { var a; console.log( a ); // undefined a = 2; } foo(); 达 foo(); // ReferenceError, TypeError! var foo = function bar() { // ... }; 这变标识foo()给这foo() 导ReferenceErrorfoo时赋值达 赋值foo()对undefined值进调导TypeError 时记达标识赋值 foo(); // TypeError bar(); // ReferenceError var foo = function bar() { // ... }; 这码经过实际为 var foo; foo(); // TypeError bar(); // ReferenceError foo = function() { var bar = ...self... // ... } 4.3优 变值细节这细节现“” 码变 虑码 foo(); // 1 var foo; function foo() { console.log( 1 ); } foo = function() { console.log( 2 ); }; 输12这码为 function foo() { console.log( 1 ); } foo(); // 1 foo = function() { console.log( 2 ); }; var foo现function foo()...为 变 var现还 foo(); // 3 function foo() { console.log( 1 ); } var foo = function() { console.log( 2 ); }; function foo() { console.log( 3 ); } 虽这论说进义 经导问题 块顶这过码 样 foo(); // "b" var a = true; if (a) { function foo() { console.log("a"); } } else { function foo() { console.log("b"); } } 这为JavaScript发变应该 块 4.4结 们习惯var a = 2;实际JavaScript这认为var aa = 2 两单编译阶务则执阶务 这论现码执进处 这过变“动”顶这过 为 达赋值赋值 别var时则 险问题 5 闭 对础识 们转这门语难话闭 词讨论闭术师 们伪说这Crockford1 1Douglas CrockfordWeb发领术权ECMA JavaScript 2.0标员 员JavaScriptBrendan Eich为JavaScript师——译 继续习还对词问顾2 现 5.1启 对JavaScript经验闭说闭 义牺这 忆时JavaScript闭总觉这门语隐 够涨讽终门还记经 阅读码试图够闭现还忆脑现 “块”时动 时倾马传给诀JavaScript闭 处够识别拥 闭习语 须Luke2样训练 2战电——译 闭词书码时产结为们识创 闭闭创码处见识别拥 响闭维环 码经处闭现终们闭 Neo3见阵4样 3电骇——译 4电骇拥识级计——译 5.2实质问题 张电喻经够 义识别闭 记访问词时产闭词 执 码释这义 function foo() { var a = 2; function bar() { console.log( a ); // 2 } bar(); } foo(); 这码码词查规则bar() 访问变a这RHS查询 这闭吗 术讲许义说认为释bar()对a 词查规则这规则闭 纯术说码bar()foo()闭实 访问认为bar()闭foo()为 简单为bar()foo() 过这义闭进观这码闭 们词闭则隐码 们码闭 function foo() { var a = 2; function bar() { console.log( a ); } return bar; } var baz = foo(); baz(); // 2 ———— 这闭 bar()词够访问foo()们bar()值类 进传递这们bar对值 foo()执值bar()赋值给变baz调baz()实际过 标识调bar() bar()显执这义词执 foo()执foo()销毁为们垃圾 释间foo()虑对进 闭“”处这发实 谁这bar() bar()赐拥foo()闭该够 bar()时间进 bar()对该这闭 变baz实际调调bar访问义时词 预访问变a 这义时词调闭继续访问义时词 论对类值进传递别处调时观闭 function foo() { var a = 2; function baz() { console.log( a ); // 2 } bar( baz ); } function bar(fn) { fn(); // 妈妈这闭 } baz传递给bar调这时现fnfoo()闭 观为够访问a 传递间 var fn; function foo() { var a = 2; function baz() { console.log( a ); } fn = baz; // baz给变 } function bar() { fn(); // 妈妈这闭 } foo(); bar(); // 2 论过传递词对义 论处执这闭 5.3现懂 码为释闭为结构进饰证 闭绝仅仅经过码处闭现让们 懂这实 function wait(message) { setTimeout( function timer() { console.log( message ); }, 1000 ); } wait( "Hello, closure!" ); 为timer传递给setTimeout(..)timerwait(..)闭 还对变message wait(..)执1000timerwait(..)闭 setTimeout(..)对这许 fnfunc类调这timer 词这过 这闭 jQuery说这问题JavaScript码 function setupBot(name, selector) { $( selector ).click( function activator() { console.log( "Activating:" + name ); } ); } setupBot( "Closure Bot 1", "#bot_1" ); setupBot( "Closure Bot 2", "#bot_2" ); 样码码负责闭组 军这实现 质论时访问们词级值类 处传递闭这应时监Ajax请 Web Workers务调实际 闭 3绍IIFE认为IIFE闭对闭义 这观 var a = 2; (function IIFE() { console.log( a ); })(); 虽这码严讲闭为为码IIFE 词执义时执 aa过词查闭发现 术讲闭发义时显说“风动 动动”5 5为it's a tree falling in the forest with no one around to hear it风动 喻义喻观观认间——译 IIFE观闭创闭创 闭闭IIFE闭闭 亲爱读现书务给JavaScript码 类值哪经闭这闭 现懂 5.4环闭 说闭for环见 for (var i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); } 发对闭认识环义时 码检查经发们这绍闭发挥 码检查设论 发 们对这码为预别输1~5 实际这码运时频输6 这为 释6哪这环终i<=5时i值6输 显环结时i终值 细这显见迟调环结时执实时 运时执setTimeout(.., 0)调环结 执输6 这问题码导为语义 们试图设环运时给“获”i 实际环别义们闭 实际i 这样说话i环结构让们误为还杂 实际迟调义环这码 题们闭别环过 闭 3绍过IIFE过执创 们试 for (var i=1; i<=5; i++) { (function() { setTimeout( function timer() { console.log( i ); }, i*1000 ); })(); } 这样吗试试 卖这样为们现显拥词迟 IIFE创闭 仅仅们进闭够细们IIFE 实质为们 变储i值 for (var i=1; i<=5; i++) { (function() { var j = i; setTimeout( function timer() { console.log( j ); }, j*1000 ); })(); } 对这码进进 for (var i=1; i<=5; i++) { (function(j) { setTimeout( function timer() { console.log( j ); }, j*1000 ); })( i ); } 这IIFE过们i传递进话变为j 还i论这码现 IIFE为迟调 闭值变们访问 问题啦 块 细们对们IIFE时创换 话说们块3绍let块 这块变 质这块转换闭这码 运 for (var i=1; i<=5; i++) { let j = i; // 闭块 setTimeout( function timer() { console.log( j ); }, j*1000 ); } 这还Bob Barker6说for环头let还 为这为变环过 结时值这变 6Bob Barker电视节——译 for (let i=1; i<=5; i++) { setTimeout( function timer() { console.log( i ); }, i*1000 ); } 块闭联敌这让为 乐JavaScript员 5.5块 还码闭强们调 强块 function foo() { var something = "cool"; var another = [1, 2, 3]; function doSomething() { console.log( something ); } function doAnother() { console.log( another.join( " ! " ) ); } } 这码这显闭两变something anotherdoSomething()doAnother()两们词这闭 foo() 虑码 function CoolModule() { var something = "cool"; var another = [1, 2, 3]; function doSomething() { console.log( something ); } function doAnother() { console.log( another.join( " ! " ) ); } return { doSomething: doSomething, doAnother: doAnother }; } var foo = CoolModule(); foo.doSomething(); // cool foo.doAnother(); // 1 ! 2 ! 3 这JavaScript为块见实现块为块这 变 们细这码 CoolModule()须过调创块实执 闭创 CoolModule()对语{ key: value, ... }对这对 对变们变隐态 这对类值质块API 这对类值终赋值给变foo过访问API foo.doSomething() 块实际对须jQuery jQuery$标识jQuery块API们 对们拥 doSomething()doAnother()块实闭过调CoolModule()实 现过对传递词时们经创 观实闭 简单块备两 1. 须闭该须调调创块实 2. 闭须这样闭 访问态 对块观调 闭对块 码CoolModule()块创调调 创块实实时对这进简单进实现单 var foo = (function CoolModule() { var something = "cool"; var another = [1, 2, 3]; function doSomething() { console.log( something ); } function doAnother() { console.log( another.join( " ! " ) ); } return { doSomething: doSomething, doAnother: doAnother }; })(); foo.doSomething(); // cool foo.doAnother(); // 1 ! 2 ! 3 们块转换IIFE见3调这值赋值给单 块实标识foo 块 function CoolModule(id) { function identify() { console.log( id ); } return { identify: identify }; } var foo1 = CoolModule( "foo 1" ); var foo2 = CoolModule( "foo 2" ); foo1.identify(); // "foo 1" foo2.identify(); // "foo 2" 块另简单强变为API对 var foo = (function CoolModule(id) { function change() { // API publicAPI.identify = identify2; } function identify1() { console.log( id ); } function identify2() { console.log( id.toUpperCase() ); } var publicAPI = { change: change, identify: identify1 }; return publicAPI; })( "foo module" ); foo.identify(); // foo module foo.change(); foo.identify(); // FOO MODULE 过块实对API对对块实进 删们值 5.5.1现块 块赖载/质这块义进API这 库为观简单绍 var MyModules = (function Manager() { var modules = {}; function define(name, deps, impl) { for (var i=0; iLet me introduce: hippo foo.awesome(); // LET ME INTRODUCE: HIPPO "foo""bar"块过API义"foo""bar"为 赖应 为们应该时间这码闭 块“”们块两为义 证值块API 换话说块块们层发变 5.5.2块 ES6为块级语过块统进载时ES6块 处块导块API员样导API员 块稳识别编译识别们API语义 运时虑进运时块APIAPI 讨论 ES6块API稳API运时变编辑这 这样编译检查对导块API员实API 编译运时“”错误样运动态 ES6块“”须义块浏览 认“块载”载这远们讨论围导块时 载块 虑码 bar.js function hello(who) { return "Let me introduce: " + who; } export hello; foo.js // 仅"bar"块导hello() import hello from "bar"; var hungry = "hippo"; function awesome() { console.log( hello( hungry ).toUpperCase() ); } export awesome; baz.js // 导"foo""bar"块 module foo from "foo"; module bar from "bar"; console.log( bar.hello( "rhino" ) ); // Let me introduce: rhino foo.awesome(); // LET ME INTRODUCE: HIPPO 两码别创foo.jsbar.js码 样bar.js载导这两块们 import块API导别绑变 们hellomodule块API导绑变们foo barexport块标识变导为API这块 义 块闭样处绍闭块 样 5.6结 闭JavaScript满够 达实际标显为值传递词环书 码 记访问词词执这时产 闭 认闭过错环 时闭强实现块 块两(1)为创调(2)值须 对这样创闭 现们发现码处闭们够识别闭 录A动态 2们对动态词JavaScript词 实语词 们简动态词别实际动态 JavaScript另this亲书“this对”详细绍 2词寻变处变规则词 义过发码书阶设eval()with 动态让为运时动态 码时进态实这样们过码说 function foo() { console.log( a ); // 2 } function bar() { var a = 3; foo(); } var a = 2; bar(); 词让foo()a过RHSa输2 动态处们处调换 话说链调栈码 JavaScript动态论码foo()执时输3 function foo() { console.log( a ); // 32 } function bar() { var a = 3; foo(); } var a = 2; bar(); 为这样为foo()a变时顺调栈调foo()查a 词链查foo()bar()调检查bar() 值为3变a 现这 这实为过词码词为础进 对动态动态语过码觉这 词 实JavaScript动态词简单this 动态 别词码说义时动态运时this 词处动态处调 this调这this动态间紧 this详细见书“this对” 录B块 3块ES3发JavaScript块withcatch 块两 ES6let们码终创约块块 码风拥动 们ES6环块 虑码 { let a = 2; console.log( a ); // 2 } console.log( a ); // ReferenceError 这码ES6环ES6环实现这 catch try { throw 2; } catch(a) { console.log( a ); // 2 } console.log( a ); // ReferenceError 这码们见强错误try/catch错误 值2catch变这值头 错catch块ES6环为块 “”说“这码”错码CoffeeScript编译输 码这 ES6码转换ES6环运块 码带处构时过对码进预处时 实这ES6选ES6 环ES6过时码转换对ES6码进处ES5码 B.1 Traceur Google维护为Traceur项该项ES6码转换ES6环 ES5TC39员赖这测试们语义 Traceur们码转换样 { try { throw undefined; } catch (a) { a = 2; console.log( a ); } } console.log( a ); 过这样们块时虑标ES6环 为try/catchES3这样 B.2 隐显 3绍块时们码维护扩 块还这 虑这letletlet对let义 let (a = 2) { console.log( a ); // 2 } console.log( a ); // ReferenceError 隐经let创显进绑显 仅码构时现语过强变 块顶产简洁码这样变 这动var顶let 变块顶处let义块识维 护 这问题letES6Traceur编译这 码 们两选择ES6语码规协 /*let*/ { let a = 2; console.log( a ); } console.log( a ); // ReferenceError 问题另选择编显let过转换 码 发为let-er这问题let-er构时码转换 let对进转换处let义码 let-er应ES6码转换码传递给Traceur let-er还设项--es6启认闭变码类启这设 项时let-er标ES6码过try/catch进hackES3 { let a = 2; console.log( a ); } console.log( a ); // ReferenceError 马ES6环let-erES6环时启设项 这样标ES6码 为ES标显let B.3 简单try/catch带问题尝试“为IIFE创 ”这问题 try/catch术层说try/catch须这 TC39ES6转换try/catchTraceur团队经 Chrome对try/catch进进们显动这 IIFEtry/catch为码进 变这码义thisreturnbreakcontine发变IIFE 进动 问题变块这继续 var码 录Cthis词 这标题详细说thisES6题this词 联们简单讨论 ES6语头这样 var foo = a => { console.log( a ); }; foo( 2 ); // 2 这“头”单调长挖function键简 头让时键盘还简单说 码问题 var obj = { id: "awesome", cool: function coolFn() { console.log( this.id ); } }; var id = "not awesome" obj.cool(); // setTimeout( obj.cool, 100 ); // 问题cool()丢this间绑这问题办长 var self = this; var obj = { count: 0, cool: function coolFn() { var self = this; if (self.count < 1) { setTimeout( function timer(){ self.count++; console.log( "awesome?" ); }, 100 ); } } }; obj.cool(); // ? var self = this这圆满this绑问题问题过 杂们词self过词闭 进标识this绑过发 们欢长东ES6们减 场实习惯问题this ES6头this词为 var obj = { count: 0, cool: function coolFn() { if (this.count < 1) { setTimeout( () => { // 头东 this.count++; console.log( "awesome?" ); }, 100 ); } } }; obj.cool(); // ? 简单说头this绑时为为 this绑规则词this值 这码头预测this进绑 “继”cool()this绑调错 这样码认为头员们经错误给标 this绑规则词规则 换话说为烦this风码词结让 头码两风两风 另导头够们 见3 这“问题”另办this var obj = { count: 0, cool: function coolFn() { if (this.count < 1) { setTimeout( function timer(){ this.count++; // this // 为bind(..) console.log( "more awesome" ); }.bind( this ), 100 ); // look, bind()! } } }; obj.cool(); // 论欢头this词为还欢bind()头 仅仅码 们间为为们们 现们经词还闭this词 录D谢 对书这说许谢 须谢Christen Simpson还谢两EthanEmily们 亲摆电脑书对JavaScript让应 该这样书释JavaScript为牺 时间亏实 还谢O'Reilly编辑Simon St.LaurentBrian MacDonaldm编辑营销 员们们“”图书编辑这尝试过对 谢该书编过议纠错们贡书这 Shelley PowersTim FerroEvan BordenForrest L NorvellJennifer DavisJesse Harlin谢Shane Hudson为书“闭” 谢TC39员员们许识细 连问题这John-David DaltonJuriy “kangax” ZaytsevMathias BynensRick WaldronAxel RauschmayerNicholas ZakasAngus CrollJordan Harband Reginald BraithwaiteDave HermanBrendan EichAllen Wirfs-BrockBradley MeckDomenic DenicolaDavid WalshTim DisneyKris KowalPeter van der ZeeAndrea GiammarchiKit Cambridge谅这 Kickstarter样谢500们这 Jan SzpilanokikoMurali KrishnamoorthyRyan JoyCraig PatchettpdqtraderDale Fukamiray hatfieldR0drigo Perez [Mx]Dan PetittJack FranklinAndrew BerryBrian GrinsteadRob SutherlandSergi MeseguerPhillip GourleyMark WatsonJeff CarouthAlfredo SumaranMartin SachseMarcio BarriosDanAimelyneMMatt SullivanDelnatte Pierre- AntoineJake SmithEugen TudoranceaIrisDavid TrinhsimonstlRay DalyUros Gruber Justin MyersShai ZonisMom & DadDevin ClarkDennis PalmerBrian Panahi JohnsonJosh MarshallMarshallDennis KerrMatt SteeleErik SlagterSacahJustin RainbowChristian NilssonDelapouiteD. PereiraNicolas HoizeyGeorge V. ReillyDan ReevesBruno Laturner Chad JenningsShane KingJeremiah Lee Cohickod3nStan YamaneMarko VucinicJim B Stephen CollinsÆgir ÞorsteinssonEric PedersonOwainNathan SmithJeanetteurphy Alexandre ELISÉChris PetersonRik WatsonLuke MatthewsJustin LoweryMorten Nielsen Vernon KesnerChetan ShenoyPaul TregoingMarc GrabanskiDion AlmaerAndrew Sullivan Keith ElsassTom BurkeBrian AshenfelterDavid StuartKarl SwedbergGraemeBrandon Patrice TuckerEric J. BivonaAdam SpoonerAaron CavanoKelly PackerEric JMartin VagabondadamDirk van Bergendave ♥♫★ furfVedran ZakanjRyan McAllenNatalie getifyDaniel CousineauChris CharltonEric TurnerDavid TurnerJoël GaleranDharma HurwitzJonathan PidgeonJason CampbellJoseph C.SwiftOneJan HohnerDerick Bailey DeanScott Tolinski-Level UpClement BoirieDjordje LukicAnton KotenkoRafael CorralPhilip SchwartzRaymondLuc De BrouwerDavid HayesRhys Brett-BowenDmitryAziz Khoury TaylorPaul DijouMichael KohlerRob CassieMike TierneyCody Leroy LindleytofujiShimon CaboYuya Saitoroberto ricardoBarnett KlaneMike MooreKevin MarxJustin LoveJoe DugginDavid LoidoltEd RicherBrian Che‐ naultGoldFire StudiosCarles AndrésCarlos Nick CooleyDaniel MesquitaRobert SyvarthChris CoyierRémy BachAdam DougalAlistair McLionWolfgang KaufmannPascal PeuckertDave NugentMarkus LiebeltWelling Guzman HalesIan PounceyTimothy Kevin OxleyGeorge Terezakissanjay rajJordan HarbandMarko David J. GroomBBoxYu Dilys SunNate SteinerBrandon SatromBrian WyantWesley tierneyAndrea GiammarchiNico VignolaDon JonesChris HartjesAlex Howesjohn gibbon SilvaGuy Israeli@megalithicDamian CrawfordFelix GliescheApril Carter GrantHeidijim Hurtadoandy ennamoratoPaul SeltmannMelissa GoreDave PollardJack SmithPhilip Da JobeNick DozierPeter WooleyJohn HooverdanMartin A. JacksonHéctor Fernando MacFarlaneBrian LaShombAdrien Maschristopher rossIan LittmanDan AtkinsonElliot Eric Damon WaltersDerry Lozano-HoylandGeoffrey WisemanmkeehnerKatieKScott MartineauKareemBen ThouretUdi NirMorgan Laupiesjory carson-bursonNathan L Smith Paolo Di StefanoSoledad PenadesChris GerberAndrey DolganovWil MooreIIIThomas Rupert WoodTrey CarricoPancho LopezJoël kuijtenTom A MarraJeff JewissJacob Rios Daniel CamposHugh WoodChristian BradfordFrédéric HarperIonuţ Dan PopaJeff Trimble FogelIvan Kolev בר-לבב nuzzaciChristine WilksHans Bergrencharles montgomeryAriel Jacob KatzSue LockwoodMagnus JohanssonJeremy CrapseyGrzegorz Pawłowskinico NicolasDCindy WongReg BraithwaiteLocalPCGuyJon FriskicsChris MerrimanJohn Pena SinaiDan RaineSchabse LaksMichael TervoortAlexandre AbreuAlan Joseph Williams MathewsMatt JaredJuanfranGeorgie KirschnerKenny LeeTed ZhangAmit PahwaInbal Kitt HodsdenPaul McGrawSascha GoldhoferAndrew MetcalfMarkus KroghMichael GreenishTim JonesJose OchoaMichael Brennan-WhiteNaga Harish MuvvaBarkóczi Dávid venkataguruJeff AdamsTrae RobbinsRolf LangenhuijzenJorge AntunesAlex KoloskovHugh Leigh Penny Jr.Robert FergusonMike van HoenselaarHasse Schougaardrajan WigleyDaniel MeeMikeHandyfaceAlex JahrausCarl FurrowRob FoulkrodMax Shishkin HaysJohn ChristopherGiormanoj reddyChad SmithJared HarbourMinoru TODAChris DrenovacEmilisMichael PelikanScott F. WalterJosh FreemanBrandon Hudgeonsvijay chennupatiBill GlennonRobin R.Troy Forsterotaku_coderBradScottFrederick Ostrander Adam BrillSeb FlippenceMichael AndersonJacobAdam RandlettStandardJoshua Clanton Sebastian KoubaChris DeckSwordFireHannes PapenbergRichard WoeberhnzzRob CrowtherJedidiah BroadbentSergey ChernyshevJay-Ar JamonBen Combeeluciano bonachelaMark TomlinsonKit CambridgeMichael MelgaresJacob AdamsAdrian Bruinhout Bev WieberScott PuleoThomas HerzogApril LeoneDaniel MizielińskiKees van GinkelJon AbramsErwin HeiserAvi LaviadDavid newellJean- Francois TurcotNiko RobertsErik DanaCharles NeillAaron HolmesGrzegorz ZiółkowskiNathan YoungmanTimothyJacob MatherMichael AllanMohit SethRyan EwingBenjamin Van Treese,Marcelo SantosDenis WolfPhil KeysChris YungTimo TijhofMartin LekvallAgendineGreg WhitworthHelen HumphreyDougal CampbellJohannes HarthBruno GirinBrian HoughDarren NewtonCraig McPheatOlivier TilleDennis RoethigMathias BynensBrendan StrombergersundeepJohn MeyerRon MaleJohn F Croston IIIgiganteCarl BergenhemB.J. MayRebekah TylerTed FoxberryJordan ReeseTerry SuitorafelizTom KieferDarragh DuffyKevin Vanderbeken Andy PearsonSimon Mac DonaldAbid DinChris JoelTomas TheunissenDavid DickPaul GrockBrandon WoodJohn WeisdgrebbNick JenkinsChuck LaneJohnny Megahan marzsmanTatu TamminenGeoffrey KnauthAlexander TarmolovJeremy TymesChad Auld Sean ParmeleeRob StaenkeDan BenderYannick derwaJoshua JonesGeert PlaisierTom LeZotteChristen SimpsonStefan BruvikJustin FalconeCarlos SantanaMichael WeissPablo VillosladaPeter deHaanDimitris IliopoulosseyDoggyAdam JordensNoah KantrowitzAmol MMatthew WinnardDirk GinaderPhinam BuiDavid RapsonAndrew BaxterFlorian Bougel Michael GeorgeAlban EscalierDaniel SellersSasha RudanJohn GreenRobert Kowalski David I. Teixeira (@ditmaCharles CarpenterJustin YostSam SDenis CiccaleKevin Sheurs Yannick CroissantPau FracésStephen McGowanShawn SearcyChris RuppelKevin LampingJessica CampbellChristopher SchmittSablonsJonathan ReisdorfBunni GekTeddy HuffMichael MullanyMichael FürstenbergCarl HendersonRick YoestingScott Nichols Hernán CiudadAndrew MaierMike StappJesse ShawlSérgio LopesjsulakShawn Price Joel ClermontChris RidmannSean TimmJason FinchAiden MontgomeryElijah ManorDerek GathrightJesse HarlinDillon CurryCourtney MyersDiego CadenasArne de BreeJoão Paulo DubasJames TaylorPhilipp KraeutliMihai PăunSam GharegozloujoshjsMatt MurchisonEric WindhamTimo BehrmannAndrew Halljoshua priceand Théophile Villard 书编辑们谢GitHub协 谢谢们书“拥”进 对JavaScript语现贡们 this对 []Kyle Simpson 译 读这书备时15习JavaScript时过这15 进编发时JavaScript发变 15刚JavaScript时页CSSJavaScriptHTML术为DHTML 动态HTMLJavaScript发变给页动态 态栏动态时钟说实话职业对JavaScript给够 视为编东 2005认识JavaScript门编语应视细 图测试时图 应——标动缩图载页发务请 ——这JavaScript简样 对说样—— 论户还务JavaScript经为门编语语 这 顾这152005给JavaScript够视 说JavaScriptC++C#Java语样为门编语 时“JavaScript”丛书职业对 这丛书赏构对 JavaScript 书“this对”错衔书“闭”进 绍JavaScript语两this键这两对习 说们JavaScript进编础创联扩对 JavaScript创类图这样杂应 绝Web发创过JavaScript对们JavaScript 钮AJAX请间绑剂实经们员 JavaScript创对变样编绑码 这书读习话这书选论绝对 ——Nick Berardi nickberardi.com, Twitter@nberardi 1 this this键JavaScript杂别键动义 经验JavaScript发难说 够进术 ——Arthur C. Clarke 实际JavaScriptthis进发过杂 问认识this对说 “this”沟过见词过难们 “this”词还键见总this键“this” this词 1.1为this 对经验JavaScript发说this杂哪 值们这习吗绍们为 们释为this function identify() { return this.name.toUpperCase(); } function speak() { var greeting = "Hello, I'm " + identify.call( this ); console.log( greeting ); } var me = { name: "Kyle" }; var you = { name: "Reader" }; identify.call( me ); // KYLE identify.call( you ); // READER speak.call( me ); // Hello, KYLE speak.call( you ); // Hello, READER 懂这码们讲现请暂时这问题专为 这码对meyouidentify()speak()针对 对编 this给identify()speak()显传对 function identify(context) { return context.name.toUpperCase(); } function speak(context) { var greeting = "Hello, I'm " + identify( context ); console.log( greeting ); } identify( you ); // READER speak(me); //Hello, KYLE this优隐“传递”对API设计简洁 杂显传递对让码变this则 这样们绍对时动对 1.2误 们释thisthis错误认识 “this”产误两见对this释们错误 1.2.1 们this这语语说说 为见递归调这 调绑处 JavaScript发认为对JavaScript对 调时储态值这时实书 绍许发现对还许储态 过现们这让this们样 们记录foo调码 function foo(num) { console.log( "foo: " + num ); // 记录foo调 this.count++; } foo.count = 0; var i; for (i=0; i<10; i++)="" {="" if="" (i=""> 5) { foo( i ); } } // foo: 6 // foo: 7 // foo: 8 // foo: 9 // foo调 console.log( foo.count ); // 0 -- WTF? console.log语产4输证foo(..)实调4foo.count0显 this错误 执foo.count = 0时对foocount码this.count this对虽对产 负责发问“count预样哪 count”实际话发现这码创变 count见2值为NaN发现这结 问“为为值NaN值”见2 这样问题时许发为this为预试图 难问题们这问题达 创另带count对 function foo(num) { console.log( "foo: " + num ); // 记录foo调 data.count++; } var data = { count: 0 }; var i; for (i=0; i<10; i++) { if (i > 5) { foo( i ); } } // foo: 6 // foo: 7 // foo: 8 // foo: 9 // foo调 console.log( data.count ); // 4 说这实“”问题问题——this 义——术词 词优术丝贬书 “闭”仅仅为对this习this 词办 对this够说过 对词标识变 这两 function foo() { foo.count = 4; // foo } setTimeout( function(){ // }, 10 ); 为foo 传setTimeout(..)调标识这为 还传统现经arguments.callee 运对这对 时达arguments.callee 经应该 对们说另foo标识this对 function foo(num) { console.log( "foo: " + num ); // 记录foo调 foo.count++; } foo.count = 0 var i; for (i=0; i<10; i++) { if (i > 5) { foo( i ); } } // foo: 6 // foo: 7 // foo: 8 // foo: 9 // foo调 console.log( foo.count ); // 4 这样this问题赖变foo词 另强thisfoo对 function foo(num) { console.log( "foo: " + num ); // 记录foo调 // 调见码this实foo this.count++; } foo.count = 0; var i; for (i=0; i<10; i++) { if (i > 5) { // call(..)this对foo foo.call( foo, i ); } } // foo: 6 // foo: 7 // foo: 8 // foo: 9 // foo调 console.log( foo.count ); // 4 这们this话们详细释 1.2.2 见误this这问题杂为 错误 this词JavaScript实 对类见标识“对”过JavaScript码访问 JavaScript 码试图边this隐词 function foo() { var a = 2; this.bar(); } function bar() { console.log( this.a ); } foo(); // ReferenceError: a is not defined 这码错误虽这码们实际 论坛华码这码时伤this 误导 这码试图过this.bar()bar()这绝对们释 调bar()this词标识 编这码发还试图this联foo()bar()词让bar() 访问foo()变a这实现this词东 this词查时这实现 1.3this 错误们this样 们说过this运时进绑编时绑调时 this绑调 调时创动记录时为执这记录 哪调调栈调传this记录 执过 们习寻调执过绑this 1.4结 对时间习thisJavaScript发说this绑 this测尝试错Stack Overflow贴 让this 习thisthis词许这样 释误导过实们错误 this实际调时发绑哪调 2 this 1们对this错误this调时绑 调调 2.1调 this绑过调调码调 细调这问题这this 说寻调寻“调”这简单为编 隐调 调栈为达执调们调 执调 们调栈调 function baz() { // 调栈baz // 调 console.log( "baz" ); bar(); // <-- bar调 } function bar() { // 调栈baz -> bar // 调baz console.log( "bar" ); foo(); // <-- foo调 } function foo() { // 调栈baz -> bar -> foo // 调bar console.log( "foo" ); } baz(); // <-- baz调 们调栈调为this绑 调栈调链们码释样 这烦错另查调栈浏览调试绝 现浏览发JavaScript调试说 给foo()码设码 debugger;语运码时调试暂时调 这调栈this绑发调栈 栈这调 2.2绑规则 们执过调this绑对 须调应规则哪们别释这 规则释规则时们优级 2.2.1认绑 绍调类调这规则应规 则时认规则 码 function foo() { console.log( this.a ); } var a = 2; foo(); // 2 应该变var a = 2对 们质东过币两样 们调foo()时this.a变a为为 调时应this认绑this对 们这应认绑过调foo()调 码foo()带饰进调认绑应 规则 严strict mode对认绑this绑 undefined function foo() { "use strict"; console.log( this.a ); } var a = 2; foo(); // TypeError: this is undefined 这细节虽this绑规则调foo() 运strict mode时认绑绑对严foo()调 function foo() { console.log( this.a ); } var a = 2; (function(){ "use strict"; foo(); // 2 })(); 说应该码strict modenon-strict mode严 严时库严码 这类细节 2.2.2隐绑 另虑规则调对说对拥 过这说误导 码 function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; obj.foo(); // 2 foo()obj论 obj义还义为这严说obj对 调obj说调时obj对“拥”“ ” 论这foo()调时实obj对 对时隐绑规则调this绑这对为调foo()时this绑 objthis.aobj.a样 对链顶层说层响调举说 function foo() { console.log( this.a ); } var obj2 = { a: 42, foo: foo }; var obj1 = { a: 2, obj2: obj2 }; obj1.obj2.foo(); // 42 隐丢 见this绑问题隐绑丢绑对说应认绑 this绑对undefined严 码 function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; var bar = obj.foo; // 别 var a = "oops, global"; // a对 bar(); // "oops, global" 虽barobj.foo实际foo时bar()实 带饰调应认绑 见发传调时 function foo() { console.log( this.a ); } function doFoo(fn) { // fn实foo fn(); // <-- 调 } var obj = { a: 2, foo: foo }; var a = "oops, global"; // a对 doFoo( obj.foo ); // "oops, global" 传递实隐赋值们传时隐赋值结 样 传语传发结样 别 function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; var a = "oops, global"; // a对 setTimeout( obj.foo, 100 ); // "oops, global" JavaScript环setTimeout()实现伪码类 function setTimeout(fn,delay) { // delay fn(); // <-- 调 } 们样调丢this绑见还this为 们调调thisJavaScript库处 调this强绑发DOM这 时让闷遗这选择启这为 论哪this变实际调执 办响绑调们绍过this这“ ”“”语单词fixing这问题 2.2.3显绑 们刚样隐绑时们须对 过这间this间隐绑这对 们对对强调该 JavaScript“”这们[[]]——们详细 绍这问题说call(..)apply(..)严 说JavaScript环时们这两这样 见JavaScript绝创call(..) apply(..) 这两们对们这对绑this 调时这this为this绑对们为显绑 码 function foo() { console.log( this.a ); } var obj = { a:2 }; foo.call( obj ); // 2 过foo.call(..)们调foo时强this绑obj 传值类尔类类this绑对这 值转换对new String(..)new Boolean(..)new Number(..)这 为“” this绑说call(..)apply(..)样们别现 现们虑这 显绑们丢绑问题 1. 绑 显绑变这问题 码 function foo() { console.log( this.a ); } var obj = { a:2 }; var bar = function() { foo.call( obj ); }; bar(); // 2 setTimeout( bar, 100 ); // 2 // 绑barthis bar.call( window ); // 2 们这变样们创bar()动调 foo.call(obj)强foothis绑obj论调bar总动 obj调foo这绑显强绑们为绑 绑应场创传值 function foo(something) { console.log( this.a, something ); return this.a + something; } var obj = { a:2 }; var bar = function() { return foo.apply( obj, arguments ); }; var b = bar( 3 ); // 2 3 console.log( b ); // 5 另创i辅 function foo(something) { console.log( this.a, something ); return this.a + something; } // 简单辅绑 function bind(fn, obj) { return function() { return fn.apply( obj, arguments ); }; } var obj = { a:2 }; var bar = bind( foo, obj ); var b = bar( 3 ); // 2 3 console.log( b ); // 5 绑ES5Function.prototype.bind function foo(something) { console.log( this.a, something ); return this.a + something; } var obj = { a:2 }; var bar = foo.bind( obj ); var b = bar( 3 ); // 2 3 console.log( b ); // 5 bind(..)编码设为this调 2. API调“” 库许JavaScript语环许选 为“”contextbind(..)样调this 举说 function foo(el) { console.log( el, this.id ); } var obj = { id: "awesome" }; // 调foo(..)时this绑obj [1, 2, 3].forEach( foo, obj ); // 1 awesome 2 awesome 3 awesome 这实际过call(..)apply(..)实现显绑这样码 2.2.4new绑 这this绑规则讲们见 JavaScript对误 传统类语“构”类new类时调类 构这样 something = new MyClass(..); JavaScriptnew类语样绝发 认为JavaScriptnew语样JavaScriptnew实际类 语 们义JavaScript“构”JavaScript构new 时调们类实类实际们说 类们new调 举说Number(..)为构时为ES5.1这样 15.7.2 Number构 Numbernew达调时构创对 对Number(..)详请查3new调 这调为构调这细别实际 谓“构”对“构调” new调说发构调时动执 1. 创说构对 2. 这对执[[]]连 3. 这对绑调this 4. 对new达调动这对 们现134暂时过25详细绍 码 function foo(a) { this.a = a; } var bar = new foo(2); console.log( bar.a ); // 2 new调foo(..)时们构对绑foo(..)调thisnew 响调时this绑为们为new绑 2.3优级 现们经调this绑规则调 应应哪规则调应规则该办为这问题 须给这规则设优级这们绍 问认绑优级规则们虑 隐绑显绑哪优级们测试 function foo() { console.log( this.a ); } var obj1 = { a: 2, foo: foo }; var obj2 = { a: 3, foo: foo }; obj1.foo(); // 2 obj2.foo(); // 3 obj1.foo.call( obj2 ); // 3 obj2.foo.call( obj1 ); // 2 显绑优级说时应虑应显绑 现们new绑隐绑优级谁谁 function foo(something) { this.a = something; } var obj1 = { foo: foo }; var obj2 = {}; obj1.foo( 2 ); console.log( obj1.a ); // 2 obj1.foo.call( obj2, 3 ); console.log( obj2.a ); // 3 var bar = new obj1.foo( 4 ); console.log( obj1.a ); // 2 console.log( bar.a ); // 4 new绑隐绑优级new绑显绑谁优级 newcall/apply过new foo.call(obj1)进测试 们绑测试俩优级 码忆绑Function.prototype.bind(..)创 这this绑论绑对们对绑 this 这样绑显绑new绑优级newthis 绑 们这样 function foo(something) { this.a = something; } var obj1 = {}; var bar = foo.bind( obj1 ); bar( 2 ); console.log( obj1.a ); // 2 var baz = newbar(3); console.log( obj1.a ); // 2 console.log( baz.a ); // 3 bar绑obj1new bar(3)们预计样obj1.a为3 new绑obj1调bar(..)this为new绑们 为baz对baz.a值3 们绍“”辅bind function bind(fn, obj) { return function() { fn.apply( obj, arguments ); }; } 惊讶为辅new调this绑刚码 new实this绑 实际ES5Function.prototype.bind(..)杂MDNbind(..)实 现为阅读们对码进 if (!Function.prototype.bind) { Function.prototype.bind = function(oThis) { if (typeof this !== "function") { // ECMAScript 5 // IsCallable throw new TypeError( "Function.prototype.bind - what is trying " + "to be bound is not callable" ); } var aArgs = Array.prototype.slice.call( arguments, 1 ), fToBind = this, fNOP = function(){}, fBound = function(){ return fToBind.apply( ( this instanceof fNOP && oThis ? this : oThis ), aArgs.concat( Array.prototype.slice.call( arguments ) ); } ; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }; } 这bind(..)polyfill码polyfill们说墙腻polyfill码 浏览说浏览bind polyfill码浏览实现对new绑说这polyfill 码ES5bind(..)绍为new绑 polyfill创.prototype new绑赖polyfill码话 newthis码 this instanceof fNOP && oThis ? this : oThis // ... fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); 们详细释这码这杂们讨论围过简单 说这码绑new调话创this换绑 this 为new绑简单吗 new绑预设这样new进 时传bind(..) 绑this传给层这术为“应”“” 举说 function foo(p1,p2) { this.val = p1 + p2; } // null为们绑this // new时this var bar = foo.bind( null, "p1" ); var baz = new bar( "p2" ); baz.val; // p1p2 this 现们优级调应哪规则顺 进 1. new调new绑话this绑创对 var bar = new foo() 2. 过callapply显绑绑调话this绑对 var bar = foo.call(obj2) 3. 对调隐绑话this绑对 var bar = obj1.foo() 4. 话认绑严绑undefined则绑对 var bar = foo() 这样对调说这识this绑过…… 总 2.4绑 规则总这样 场this绑为认为应应绑规则时实际应 认绑规则 2.4.1this nullundefined为this绑对传callapplybind这值调时 实际应认绑规则 function foo() { console.log( this.a ); } var a = 2; foo.call( null ); // 2 传null 见apply(..)“”组传类 bind(..)对进预设这时 function foo(a,b) { console.log( "a:" + a + ", b:" + b ); } // 组“” foo.apply( null, [2, 3] ); // a:2, b:3 // bind(..) 进 var bar = foo.bind( null, 2 ); bar( 3 ); // a:2, b:3 这两传this绑对this话 传值这时null错选择码样 书ES6...apply(..)“” 组foo(...[1,2])foo(1,2)样这样this绑ES6 语还bind(..) 总nullthis绑产实this 库认绑规则this绑对浏览这对 window这导预计对 显见这导许难bug this “”传对this绑这对对产 络军队样们创“DMZ”demilitarized zone军对 ——对56绍 们this绑时总传DMZ对为对this 这对对对产响 这对对欢变ø这 键盘说MacUS键盘⌥+oOption-o这 统许为设键欢ø键盘 这换欢 论JavaScript创对简单Object.create(null)详细 绍请5Object.create(null){}创Object.prototype这 {}“” function foo(a,b) { console.log( "a:" + a + ", b:" + b ); } // 们DMZ对 var ø = Object.create( null ); // 组 foo.apply( ø, [2, 3] ); // a:2, b:3 // bind(..)进 var bar = foo.bind( ø, 2 ); bar( 3 ); // a:2, b:3 变ø仅让变“”码读为ø“this ”这null义过说欢DMZ对 2.4.2间 另创“间”这调 这应认绑规则 间赋值时发 function foo() { console.log( this.a ); } var a = 2; var o = { a: 3, foo: foo }; var p = { a: 4 }; o.foo(); // 3 (p.foo = o.foo)(); // 2 赋值达p.foo = o.foo值标调foo()p.foo() o.foo()们说过这应认绑 对认绑说this绑对调处严 处严处严this绑undefined则this绑 对 2.4.3软绑 们经过绑这this强绑对new时 调应认绑规则问题绑绑 隐绑显绑this 给认绑对undefined值实现绑 时隐绑显绑this 过为软绑实现们 if (!Function.prototype.softBind) { Function.prototype.softBind = function(obj) { var fn = this; // 获 curried var curried = [].slice.call( arguments, 1 ); var bound = function() { return fn.apply( (!this || this === (window || global)) ? obj : this curried.concat.apply( curried, arguments ) ); }; bound.prototype = Object.create( fn.prototype ); return bound; }; } 软绑softBind(..)ES5bind(..)类对进 检查调时thisthis绑对undefined认对obj 绑this则this这码还选详请查bind(..) 绍 们softBind实现软绑 function foo() { console.log("name: " + this.name); } var obj = { name: "obj" }, obj2 = { name: "obj2" }, obj3 = { name: "obj3" }; var fooOBJ = foo.softBind( obj ); fooOBJ(); // name: obj obj2.foo = foo.softBind(obj); obj2.foo(); // name: obj2 <---- fooOBJ.call( obj3 ); // name: obj3 <---- setTimeout( obj2.foo, 10 ); // name: obj <---- 应软绑 软绑foo()动this绑obj2obj3应认绑则 this绑obj 2.5this词 们绍规则经ES6绍这规 则类头 头function键义为“头”=>义头 this标规则层this 们头词 function foo() { // 头 return (a) => { //this继foo() console.log( this.a ); }; } var obj1 = { a:2 }; var obj2 = { a:3 }; var bar = foo.call( obj1 ); bar.call( obj2 ); // 2, 3 foo()创头获调时foo()thisfoo()this绑obj1bar头 this 绑obj1头绑new 头调处时 function foo() { setTimeout(() => { // 这this继foo() console.log( this.a ); },100); } var obj = { a:2 }; foo.call( obj ); // 2 头bind(..)样this绑对还现 见词传统this实际ES6们经头 样 function foo() { var self = this; // lexical capture of this setTimeout( function(){ console.log( self.a ); }, 100 ); } var obj = { a: 2 }; foo.call( obj ); // 2 虽self = this头bind(..)质说们this 经编this风码绝时self = this头 this许应 1. 词错误this风码 2. this风时bind(..)self = this头 这两码风运 这两风码难维护难编 2.6结 运this绑这调 顺应这规则this绑对 1. new调绑创对 2. callapplybind调绑对 3. 对调绑对 4. 认严绑undefined则绑对 调认绑规则“”this绑 DMZ对ø = Object.create(null)护对 ES6头标绑规则词this 说头继层调this绑论this绑这实ES6码 self = this样 3 对 12们绍调this绑对对 为们绑们详细绍对 3.1语 对过两义构 对语这样 var myObj = { key: value // ... }; 构这样 var myObj = new Object(); myObj.key = value; 构对样别键/值 对构须 “构”创对见说语绝 对这样释 3.2类 对JavaScript础JavaScript类术语“语类” string number boolean null undefined object 简单类stringbooleannumbernullundefined对null时 对类这实语bug对null执typeof null时 "object"1实际null类 1这样对层为进JavaScript进为0话 为object类null进00执typeof时 “object”——译 见错误说“JavaScript对”这显错误 实际JavaScript许对类们为杂类 对类术说“调对”JavaScript“ ”为们质对样调对样 另 组对类备额为组组织对 杂 对 JavaScript还对类为对对简单础 类样过实际们杂们详细绍 String Number Boolean Object Function Array Date RegExp Error 这对现说语类type类classJavaString 类 JavaScript们实际这构new产 调——见2构对应类对举说 var strPrimitive = "I am a string"; typeof strPrimitive; // "string" strPrimitive instanceof String; // false var strObject = new String( "I am a string" ); typeof strObject; // "object" strObject instanceof String; // true // 检查sub-type对 Object.prototype.toString.call( strObject ); // [object String] 节们详细绍Object.prototype.toString...过简单说们 认为类ObjecttoString()码strObject String构创对 值"I am a string"对变值这 执获长访问转换为String对 时语动转换String对说显创 对JavaScript认为时构 码 var strPrimitive = "I am a string"; console.log( strPrimitive.length ); // 13 console.log( strPrimitive.charAt( 3 ) ); // "m" 两们访问这样 为动转换String对访问 样发值类42.359.toFixed(2)42转换 new Number(42)对尔说 nullundefined对应构们Date构 对ObjectArrayFunctionRegExp则达说论还构们 对创对构额选项 这两创对们选简单议额选项 时构 Error对码显创时动创new Error(..)这 构创过说 3.3 们过对储类值组们 为 强调们说“”时这值实际储对这 现这值储样对 储对这们针术说样这 值储 码 var myObject = { a: 2 }; myObject.a; // 2 myObject["a"]; // 2 访问myObjecta值们.[].a语为“ 访问”["a"]语为“键访问”实际们访问值 2这两术语换书们见术语“访问” 这两语别.满标识规[".."]语 UTF-8/Unicode为举说为"Super-Fun!" 须["Super-Fun!"]语访问为Super-Fun!标识 [".."]语访问构这说 var myObject = { a:2 }; var idx; if (wantA) { idx = "a"; } // console.log( myObject[idx] ); // 2 对远string值为 转换为虽组标对 转换对组 var myObject = { }; myObject[true] = "foo"; myObject[3] = "bar"; myObject[myObject] = "baz"; myObject["true"]; // "foo" myObject["3"]; // "bar" myObject["[object Object]"]; // "baz" 3.3.1计 过达计们刚刚讲myObject[..]这访问语 场myObject[prefix + name]对时这样 ES6计[]达 var prefix = "foo"; var myObject = { [prefix + "bar"]: "hello", [prefix + "baz"]: "world" }; myObject["foobar"]; // hello myObject["foobaz"]; // world 计场ES6Symbol书详细绍过简单说 们础类预测值术说 说实际值为论说JavaScript值 Symbol.Something这编 var myObject = { [Symbol.Something]: "hello world" } 3.3.2 访问对发欢样 认为对语对为“类”为“” “访问”说“访问” JavaScript语规样 术说远“”对对为“” 实this时这this实调对这 质说变“”为this运时调动态绑 对说间 论值类访问对访问访问 “”访问别发隐绑 this们刚 举说 function foo() { console.log( "foo" ); } var someFoo = foo; // 对 foo 变 var myObject = { someFoo: foo }; foo; // function foo(){..} someFoo; // function foo(){..} myObject.someFoo; // function foo(){..} someFoomyObject.someFoo对说这别 “”对foo()义时this这两别 myObject.someFoothis隐绑对论哪为“” 许辩说义时为调时调 对——详见2为这说 险说“”“”JavaScript换 ES6super说class见录Asuper为 super绑为“”说这语义术别 质样 对达这“”这对——们 对对 var myObject = { foo: function() { console.log( "foo" ); } }; var someFoo = myObject.foo; someFoo; // function foo(){..} myObject.foo; // function foo(){..} 6绍对语这ES6简 语 3.3.3组 组[]访问过们过组结构值储过 值类组值标说值储为 说042 var myArray = [ "foo", 42, "bar" ]; myArray.length; // 3 myArray[0]; // "foo" myArray[2]; // "bar" 组对虽标给组 var myArray = [ "foo", 42, "bar" ]; myArray.baz = "baz"; myArray.length; // 3 myArray.baz; // "baz" 虽论过.语还[]语组length值发变 组键/值对值这 组对对应为进优对储 键/值对组储值标/值对 试图组“”变值 标组 var myArray = [ "foo", 42, "bar" ]; myArray["3"] = "baz"; myArray.length; // 4 myArray[3]; // "baz" 3.3.4对 JavaScript见问题对应该copy() 实际杂为们选择认 举说这对 function anotherFunction() { /*..*/ } var anotherObject = { c: true }; var anotherArray = []; var myObject = { a: 2, b: anotherObject, // c: anotherArray, // 另 d: anotherFunction }; anotherArray.push( anotherObject, myObject ); myObject 们应该还对贝说对a值 对a值2对bcd实们对bcd 对样对说myObject还anotherObject anotherArray这时问题anotherArrayanotherObjectmyObject myObject这样环导环 们应该检测环终环层还应报错选择 们还“”过toString() 码结JavaScript实现对类处 这问题许JavaScript办JavaScript应 哪为标长时间这问题 对JSON说为JSON这 结构值样对对说 var newObj = JSON.parse( JSON.stringify( someObj ) ); 这证对JSON 懂问题ES6义Object.assign(..)实现 Object.assign(..)标对还对 历对举enumerable见码键owned key 绍们=赋值标对标对这样 var newObj = Object.assign( {}, myObject ); newObj.a; // 2 newObj.b === anotherObject; // true newObj.c === anotherArray; // true newObj.d === anotherFunction; // true 节绍“”Object.defineProperty(..) Object.assign(..)=赋值对 writable标对 3.3.5 ES5JavaScript语检测 读 ES5备 码 var myObject = { a:2 }; Object.getOwnPropertyDescriptor( myObject, "a" ); // { // value: 2, // writable: true, // enumerable: true, // configurable: true // } 见这对对应为“”为 值仅仅2还另writableenumerable举 configurable 创时认值们Object.defineProperty(..) configurable对进设 举说 var myObject = {}; Object.defineProperty( myObject, "a", { value: 2, writable: true, configurable: true, enumerable: true } ); myObject.a; // 2 们defineProperty(..)给myObject显 说这 1. Writable writable值 码 var myObject = {}; Object.defineProperty( myObject, "a", { value: 2, writable: false, // not writable! configurable: true, enumerable: true } ); myObject.a = 3; myObject.a; // 2 见们对值败silently failed严这 错 "use strict"; var myObject = {}; Object.defineProperty( myObject, "a", { value: 2, writable: false, // not writable! configurable: true, enumerable: true } ); myObject.a = 3; // TypeError TypeError错误们 们绍gettersetter过简单说writable:false 变义setter严说writable:false话 setter调时应TypeError错误 2. Configurable defineProperty(..) var myObject = { a:2 }; myObject.a = 3; myObject.a; // 3 Object.defineProperty( myObject, "a", { value: 4, writable: true, configurable: false, // enumerable: true } ); myObject.a; // 4 myObject.a = 5; myObject.a; // 5 Object.defineProperty( myObject, "a", { value: 6, writable: true, configurable: true, enumerable: true } ); // TypeError defineProperty(..)产TypeError错误处严尝试 错见configurablefalse单 销 configurable:false 们还writable 态true为falsefalse为true configurable:false还删这 var myObject = { a:2 }; myObject.a; // 2 delete myObject.a; myObject.a; // undefined Object.defineProperty( myObject, "a", { value: 2, writable: true, configurable: false, enumerable: true } ); myObject.a; // 2 delete myObject.a; myObject.a; // 2 见delete语败为 delete删对删对对/ 对这执delete这对/垃圾 delete释C/C++样删对 仅 3. Enumerable 这们绍还两们绍gettersetter时 enumerable 这现对举说for..in 环enumerable设false这现举虽访问 对设true让现举 户义认enumerable这 现举设enumerable:false 们详细绍举这 3.3.6变 时对变论还ES5过 实现 创变说们响标对 标对对组对对响 变 myImmutableObject.foo; // [1,2,3] myImmutableObject.foo.push( 4 ); myImmutableObject.foo; // [1,2,3,4] 设码myImmutableObject经创变为护 myImmutableObject.foo还让foo变 JavaScript变这样 设计发现冻结对许应 设计让应对对值变 1. 对 结writable:falseconfigurable:false创义 删 var myObject = {}; Object.defineProperty( myObject, "FAVORITE_NUMBER", { value: 42, writable: false, configurable: false } ); 2. 扩 对Object.preventExtensions(..) var myObject = { a:2 }; Object.preventExtensions( myObject ); myObject.b = 3; myObject.b; // undefined 严创b败严TypeError错误 3. Object.seal(..)创“”对这实际现对调 Object.preventExtensions(..)现标记为configurable:false 仅删现虽 值 4. 冻结 Object.freeze(..)创冻结对这实际现对调 Object.seal(..)“访问”标记为writable:false这样们值 这应对级别变对对 过们说过这对对响 “冻结”对为这对调Object.freeze(..)历 对这对调Object.freeze(..)为这样 冻结对 3.3.7[[Get]] 访问实现时细节码 var myObject = { a: 2 }; myObject.a; // 2 myObject.a访问这语仅仅myObjet查为a虽 这样 语规myObject.amyObject实际实现[[Get]]调 [[Get]]()对认[[Get]]对查 这值 [[Get]]义执另为 们5绍这为实历[[Prototype]]链链 论[[Get]]值undefined var myObject = { a:2 }; myObject.b; // undefined 这访问变时样词变 对样undefinedReferenceError var myObject = { a: undefined }; myObject.a; // undefined myObject.b; // undefined 值说这两别——们undefined 别实际层[[Get]]对myObject.b进杂处 仅值变值为undefined还变[[Get]] undefined过们绍这两 3.3.8[[Put]] 获值[[Get]]对应[[Put]] 认为给对赋值发[[Put]]设创这实际 这样 [[Put]]发时实际为许对经这这 经这[[Put]]检查这 1. 访问见3.3.9节setter调setter 2. writablefalse严败严 TypeError 3. 该值设为值 对这[[Put]]杂们5讨论[[Prototype]]时详细进 绍 3.3.9GetterSetter 对认[[Put]][[Get]]别值设获 语/级对仅仅 认[[Get]][[Put]]这经书讨论围“JavaScript” 丛书对这问题进讨 ES5gettersetter认应单应 对getter隐获值时调setter隐设 值时调 给义gettersetter两时这义为“访问”“ ”对对访问说JavaScript们valuewritable setget还configurableenumerable 码 var myObject = { //给a义getter get a() { return 2; } }; Object.defineProperty( myObject, // 标对 "b", // { // // 给b设getter get: function(){ return this.a * 2 }, // b现对 enumerable: true } ); myObject.a; // 2 myObject.b; // 4 对语get a() { .. }还defineProperty(..)显义对 创值对这访问动调隐值 访问值 var myObject = { // 给 a 义getter get a() { return 2; } }; myObject.a = 3; myObject.a; // 2 们义agetter对a值进设时set赋值错误 setter们义getter2set义 为让还应义setter样setter单认[[Put]] 为赋值说gettersetter对现义话产 为 var myObject = { // 给 a 义getter get a() { return this._a_; }, // 给 a 义setter set a(val) { this._a_ = val * 2; } }; myObject.a = 2; myObject.a; // 4 实际们赋值[[Put]]值2储另变_a__a_ 惯为——样 3.3.10 们绍过myObject.a访问值undefined这值 储undefined为undefined这两 们访问值对这 var myObject = { a:2 }; ("a" in myObject); // true ("b" in myObject); // false myObject.hasOwnProperty( "a" ); // true myObject.hasOwnProperty( "b" ); // false in检查对[[Prototype]]链见5 hasOwnProperty(..)检查myObject对检查[[Prototype]]链5 讲[[Prototype]]时们详细绍这两别 对过对Object.prototype见5访问hasOwnProperty(..) 对连Object.prototype过Object.create(null)创——见5 这myObejct.hasOwnProperty(..)败 这时强进 Object.prototype.hasOwnProperty.call(myObject,"a")础hasOwnProperty(..) 显绑见2myObject in检查值实际检查 对组说这别4 in [2, 4, 6]结True 为[2, 4, 6]这组0124 1. 举 绍enumerable时们简单释过“举”现详细绍 var myObject = { }; Object.defineProperty( myObject, "a", // 让a样举 { enumerable: true, value: 2 } ); Object.defineProperty( myObject, "b", // 让b举 { enumerable: false, value: 3 } ); myObject.b; // 3 ("b" in myObject); // true myObject.hasOwnProperty( "b" ); // true // ....... for (var k in myObject) { console.log( k, myObject[k] ); } // "a" 2 myObject.b实访问值现for..in环过in “举”“现对历” 组应for..in环时产结为这举仅 值还举对应for..in环历组 传统for环历值 过另举 var myObject = { }; Object.defineProperty( myObject, "a", // 让a样举 { enumerable: true, value: 2 } ); Object.defineProperty( myObject, "b", // 让b举 { enumerable: false, value: 3 } ); myObject.propertyIsEnumerable( "a" ); // true myObject.propertyIsEnumerable( "b" ); // false Object.keys( myObject ); // ["a"] Object.getOwnPropertyNames( myObject ); // ["a", "b"] propertyIsEnumerable(..)检查给对链 满enumerable:true Object.keys(..)组举Object.getOwnPropertyNames(..) 组论们举 inhasOwnProperty(..)别查[[Prototype]]链Object.keys(..) Object.getOwnPropertyNames(..)查对 获in对[[Prototype]] 链见5过递归历对[[Prototype]]链 层Object.keys(..)——举 3.4历 for..in环历对举[[Prototype]]链历值 对值组说标for环历值 var myArray = [1, 2, 3]; for (var i = 0; i < myArray.length; i++) { console.log( myArray[i] ); } // 1 2 3 这实际历值历标值myArray[i] ES5组辅forEach(..)every(..)some(..)辅 调应组别们对调 值处 forEach(..)历组值调值every(..)运调 false“”值some(..)运调true“”值 every(..)some(..)值for环break语类们终历 for..in历对获值为实际历对举 动获值 历组标时顺for环历对时顺 JavaScript样环证 时观顺们 历值组标对ES6历组 for..of环语对义话历对 var myArray = [ 1, 2, 3 ]; for (var v of myArray) { console.log( v ); } // 1 // 2 // 3 for..of环访问对请对过调对next() 历值 组@@iteratorfor..of应组们@@iterator动 历组 var myArray = [ 1, 2, 3 ]; var it = myArray[Symbol.iterator](); it.next(); // { value:1, done:false } it.next(); // { value:2, done:false } it.next(); // { value:3, done:false } it.next(); // { done:true } 们ES6Symbol.iterator获对@@iterator们简单 绍过Symbol见3.3.1节这类iterator时 值虽对@@iterator 对对——这 见调next()为{ value: .. , done: .. }值value 历值done尔值还历值 值“3”done:false须调next() done:true历这ES6发语义过经们 讨论围 组对@@iterator动for..of历这样 许杂过简单说这样为响对类 给历对义@@iterator举说 var myObject = { a: 2, b: 3 }; Object.defineProperty( myObject, Symbol.iterator, { enumerable: false, writable: false, configurable: true, value: function() { var o = this; var idx = 0; var ks = Object.keys( o ); return { next: function() { return { value: o[ks[idx++]], done: (idx > ks.length) }; } }; } } ); // 动历myObject var it = myObject[Symbol.iterator](); it.next(); // { value:2, done:false } it.next(); // { value:3, done:false } it.next(); // { value:undefined, done:true } // for..of历myObject for (var v of myObject) { console.log( v ); } // 2 // 3 们Object.defineProperty(..)义们@@iterator为让 举过们计绍义 对时进 var myObject = { a:2, b:3, [Symbol.iterator]: function() { /* .. */ } } for..of环调myObject对next()时针动对 值历对/值时顺 码历简单传递值过义 结构实现杂历对户义对说结for..of环义组 强对 说Pixel对xy标值线历顺 过滤“远”next()调{ value: .. }{ done: true }ES6 for..of历 实际义“”远“结”总值 递值标识远for..of环这样为远 结 var randoms = { [Symbol.iterator]: function() { return { next: function() { return { value: Math.random() }; } }; } }; var randoms_pool = []; for (var n of randoms) { randoms_pool.push( n ); // 运 if (randoms_pool.length === 100) break; } 这“”们break语 3.5结 JavaScript对var a = { .. }构var a = new Array(..) 过时构选项 许为“JavaScript对”这错误对67观 础类对function类类为标 签[object Array]这对类组 对键/值对过.propName["propName"]语获值访问时 实际调认[[Get]]设值时[[Put]][[Get]]检查对 这话还查[[Prototype]]链见5 过writableconfigurable Object.preventExtensions(..)Object.seal(..)Object.freeze(..)设对 变级别 值——们备getter/setter“访问”举 举这们现for..in环 ES6for..of语历结构组对值for..of寻 义@@iterator对调next()历值 4 对“类” 绍对这绍类对编类们 绍类设计实instantiation继inheritance对态 polymorphism 们这实际对应JavaScript对们绍许 JavaScript发mixin 绍对编论绍时这 实JavaScript码们许伪码—— 紧 4.1类论 类/继码组织结构——软对实问题领 对编强调为质联 为设计为说这计 时为结构 举说单词语为 对应这为计长 设计String类 String类实说们应 们还类对结构进类结构围义 们见“车”“”类 们软义Vehicle类Car类对这进 Vehicle义进载这Vehicle为们 Vehicle义类飞车车东 们软对义“载”义们Vehicle 义义Car时继扩Vehicle这础义Car义 对Vehicle义 虽VehicleCar义实辆车 VINVehicle Identification Number车辆识别码 这类继实 类另态这说类为类为实 际对态许们为础为 类论强议类类为让类类们 JavaScript码这样码读 4.1.1“类”设计 类为设计讨论对设计观 单这说们级对类础 实现级设计对优码础 过规编话说过过编这码过调 层许师还过类过风“码”转换结构 组织码 编Monad经验类设计 对说这类须编础选码 语Java给选择类选——类语 C/C++PHP过类这两语发选择风两 风 4.1.2JavaScript“类” JavaScript哪类长时间JavaScript类语 newinstanceof过ES6class键见录A 这JavaScript实际类简单说 类设计绍实现类为满 对类设计JavaScript类语 虽类语JavaScript类设计类 JavaScript实类语JavaScript“类”库试图这 现实迟对语类JavaScript“类”样 总结软设计类选JavaScript 许发欢类软设计们绍JavaScript 实现类问题 4.2类 许类语“标库”Stack类“栈”结构压弹 Stack类变储时访问为“”让 码隐进删 这语实际Stack创态类员这 们讨论围Stack类仅仅“栈” “栈”须实Stack类对进 4.2.1 “类”“实” 师规宽户户连墙 顶计这阶哪 这样 师——纸——结构纳 们 蓝图计们们还 蓝图实际规蓝图现实 为蓝图实质对蓝图 创 蓝图间间过蓝图结构观获 这门须实——蓝图门应该 哪门 类张蓝图为获对们须类说实 东这东为实话们实调访问 这对类 进栋时蓝图墙这蓝图馆类 实对访问类过这实 对哪类 类实对间间类过 实为对 见头义发 4.2.2 构 类实类构这类为构这 务实态 举说这类伪码编语 class CoolGuy { specialTrick = nothing CoolGuy( trick ) { specialTrick = trick } showOff() { output( "Here's my trick: ", specialTrick ) } } 们调类构CoolGuy实 Joe = new CoolGuy( "jumping rope" ) Joe.showOff() // 这绝绳 CoolGuy类CoolGuy()构执new CoolGuy()时实际调构 对类实们这对调showOff()输 CoolGuy长 显绳让乔为伙 类构类类构new调这样语 构类实 4.3 类继 类语义类义继类 为“类”为“类”这术语显类过 扩 对亲说给显统 亲贡给编语们设类 们变单虽继许 头发红头发红变红间 联 义类对类说类类类 为继为义为 们讨论类类实类类喻误实 际们应类类为类DNA类DNA们这DNA创说实 进沟 们现实类这 经讲继 顾VehicleCar类类继伪码 class Vehicle { engines = 1 ignition() { output( "Turning on my engine." ); } drive() { ignition(); output( "Steering and moving forward!" ) } } class Car inherits Vehicle { wheels = 4 drive() { inherited:drive() output( "Rolling on all ", wheels, " wheels!" ) } } class SpeedBoat inherits Vehicle { engines = 2 ignition() { output( "Turning on my ", engines, " engines." ) } pilot() { inherited:drive() output( "Speeding through the water with ease!" ) } } 为缩码们这类构 们过义Vehicle类设发动驾驶 “”为这类 们义两类CarSpeedBoat们Vehicle继 类别车轮两发动须启动两发 动 4.3.1 态 Car继类drive()Car调inherited:drive()这Car 继drive()pilot()样drive() 这术为态拟态说对态 态话题们现说“对”态继 层层论层说“对”为们 义访问绝对继层说类对“查层” 许语superinherited:义“类”superclass 类类/类 态另继链层义调时动 选择义 码两这样drive()义VehicleCarignition()义 VehicleSpeedBoat 传统类语super还类构过super 调类构说这问题为对类说构 类JS——实际“类”构类Foo.prototype...这样 类JS类类两构对应.prototype对 们构间联简单实现两对ES6类 过super“”这问题见录A 们ignition()态pilot()过对态继 Vehicledrive()drive()过对ignotion() 语哪ignition()Vehicle还SpeedBoat实际SpeedBoat ignition()实Vehicle类调drive()语Vehicle ignition() 换ignition()义态哪类实 这过术细节这细节JavaScript类 [[Prototype]] 类们创实对对继类这对 为super 还记张图吗 这实a1a2b1b2继Bar头 说类Bar应过对态说super访问类Foo为 类仅仅继类为类对继进“” 响类这两响对态访问类 响类类 态类类联类类类继实 4.3.2 继 还记们类类DNA讨论吗时们说这喻为现实绝 亲产类继两类现实喻 类语许继“类”继类义类 对类说这许组这 时带杂问题两类义drive()话类哪 难动类drive()吗这样态继优 还为钻问题变钻问题类D继两类BC这两 类继AAdrive()BC这态Ddrive()时 应选择哪B:drive()还C:drive() 这问题远杂绍这问题为JavaScript进对 JavaScript简单“继”许认为这为 继这挡发们热们尝试样办实现 继们马 4.4 继实时JavaScript对动执为简单说JavaScript 对实“类”对对们联 见5 语类现为JavaScript发拟 类为这们两类显隐 4.4.1 显 们顾VehicleCarJavaScript动实现VehicleCar 为们动实现这许库为extend(..)为 们为mixin(..) // 简单mixin(..): function mixin( sourceObj, targetObj ) { for (var key in sourceObj) { // if (!(key in targetObj)) { targetObj[key] = sourceObj[key]; } } return targetObj; } var Vehicle = { engines: 1, ignition: function() { console.log( "Turning on my engine." ); }, drive: function() { this.ignition(); console.log( "Steering and moving forward!" ); } }; var Car = mixin( Vehicle, { wheels: 4, drive: function() { Vehicle.drive.call( this ); console.log( "Rolling on all " + this.wheels + " wheels!" ); } } ); 们处经类为JavaScript类Vehicle Car对们别进贴 现CarVehicle术说实际 CarignitionVehicle过对ignition() enginesVehicle值1 Car经drive这mixinCar义 实现“类”对“类”见mixin(..)if语 1. 说态 们这语Vehicle.drive.call( this )这说显态还记吗 伪码对应语inherited:drive()们为对态 JavaScriptES6见录A对态CarVehicle drive()为调对们须绝对对们过显 Vehicle对调drive() 执Vehicle.drive()调this绑Vehicle对Car对 见2这们们.call(this)见2drive()Car 对执 Car.drive()标识Vehicle.drive()叠说“”见 5话们实现态为调mixin(..)时Vehicle.drive() Car们访问this.drive()标识叠须 杂显伪态 对态类语CarVehicle间联类义头创 这维护两类联 JavaScript显伪态伪态创 联这维护显伪态拟继进 码杂维护难 伪态导码变杂难阅读难维护应显 伪态为这样偿 2. 顾mixin(..) // 简单mixin(..): function mixin( sourceObj, targetObj ) { for (var key in sourceObj) { // if (!(key in targetObj)) { targetObj[key] = sourceObj[key]; } } return targetObj; } 现们mixin(..)历sourceObjVehicle targetObjCar这进们标对进 标对 们进对Car进话过检查过这 // 另风险 function mixin( sourceObj, targetObj ) { for (var key in sourceObj) { targetObj[key] = sourceObj[key]; } return targetObj; } var Vehicle = { // ... }; // 创对Vehicle进 var Car = mixin( Vehicle, { } ); // Car mixin( { wheels: 4, drive: function() { // ... } }, Car ); 这两叠Vehicle显Car“”这这过 另释CarVehicle欢饼团 样 CarVehicleCar响Vehicle 这过细节实际两间 “响”对对组 两对这说实际拟类 语 JavaScript标对对 对见3对ignition() VehicleCar响 显JavaScript过强虽 对另对这实带处义语 还带们刚对问题 标对显过对继为 处问题发/库“绑”术 说这“诡计”偿 够码读显码难 让对杂 时觉难许应该实际须杂 库实现这细节标问题6试 简单满这问题 3. 继 显变为“继”显隐Douglas Crockford // “传统JS类”Vehicle function Vehicle() { this.engines = 1; } Vehicle.prototype.ignition = function() { console.log( "Turning on my engine." ); }; Vehicle.prototype.drive = function() { this.ignition(); console.log( "Steering and moving forward!" ); }; // “类” Car function Car() { // carVehicle var car = new Vehicle(); // 们对car进 car.wheels = 4; // Vehicle::drive() var vehDrive = car.drive; // Vehicle::drive() car.drive = function() { vehDrive.call( this ); console.log( "Rolling on all " + this.wheels + " wheels!" ); return car; } var myCar = new Car(); myCar.drive(); // 发动 // 盘 // 进 见们Vehicle类对义类对义 话类这对构实 调new Car()时创对绑Carthis见2为们 这对们car对创这对丢 new键调Car()这样结样创丢 对 4.4.2 隐 隐显伪态备样问题 码 var Something = { cool: function() { this.greeting = "Hello World"; this.count = this.count ? this.count + 1 : 1; } }; Something.cool(); Something.greeting; // "Hello World" Something.count; // 1 var Another = { cool: function() { // 隐SomethingAnother Something.cool.call( this ); } }; Another.cool(); Another.greeting; // "Hello World" Another.count; // 1 count态 过构调调Something.cool.call( this )们实际“” Something.cool()Another调过this绑2终结 Something.cool()赋值应Another对Something对 们Something为“”Another 虽这类术this绑Something.cool.call( this )变对 时说这样结构证码 洁维护 4.5 结 类设计许语对类软设计语JavaScript类语 语类 类 传统类实时为实类继时为类 态继链层类类 质实结 JavaScript类样动创对 论显还隐拟类为产语 显伪态OtherObj.methodName.call(this, ...)这让码难懂难维护 显实际拟类为为对别对 对视这导许问题 总说JavaScript拟类偿虽问题 隐 5 34[[Prototype]]链说现们详细绍 4绍拟类为[[Prototype]]链 5.1[[Prototype]] JavaScript对[[Prototype]]实对对 对创时[[Prototype]]赋值 们对[[Prototype]]链为虽见 码 var myObject = { a:2 }; myObject.a; // 2 [[Prototype]]3们说过试图对时发[[Get]] myObject.a对认[[Get]]说检查对这 话 ES6Proxy书围书绍 Proxy话们这对[[Get]][[Put]]讨论 amyObject对[[Prototype]]链 对认[[Get]]说对继续访问对 [[Prototype]]链 var anotherObject = { a:2 }; // 创联anotherObject对 var myObject = Object.create( anotherObject ); myObject.a; // 2 们绍Object.create(..)现创对这 对[[Prototype]]联对 现myObject对[[Prototype]]联anotherObject显myObject.a 访问anotherObject值2 anotherObjecta[[Prototype]]链为话继续查 这过续查[[Prototype]]链话[[Get]] 值undefined for..in历对时查[[Prototype]]链类过链访问 enumerable见3举in检查对时 样查对链论举 var anotherObject = { a:2 }; // 创联anotherObject对 var myObject = Object.create( anotherObject ); for (var k in myObject) { console.log("found: " + k); } // found: a ("a" in myObject); // true 过语进查时查[[Prototype]]链查 链 5.1.1Object.prototype 哪[[Prototype]]“头” [[Prototype]]链终Object.prototype“” 扩对“”说[[Prototype]]链顶设为这Object.prototype对 JavaScript许 应该经说.toString().valueOf()3还绍 过.hasOwnProperty(..)们还绍.isPrototypeOf(..)这 5.1.2设 3过给对设仅仅值现们 讲这过 myObject.foo = "bar"; myObject对为foo访问这赋值语值 foomyObject[[Prototype]]链历类[[Get]]链 foofoomyObject foo链层赋值语myObject.foo = "bar"为 们进绍 foo现myObject现myObject[[Prototype]]链层发 myObjectfoo链层foo为myObject.foo总选择 链层foo 们杂们foomyObject 链层时myObject.foo = "bar"现 1. [[Prototype]]链层为foo访问见3标记为 读writable:falsemyObject为foo 2. [[Prototype]]链层foo标记为读writable:false myObject创运严码错误则这 赋值语总发 3. [[Prototype]]链层foosetter见3调这 setterfoo说myObject义foo这setter 发认为[[Prototype]]链层经[[Put]]赋值发 见这样 foo=赋值 Object.defineProperty(..)见3myObjectfoo 读[[Prototype]]链层隐创 这样为拟类继链层foo类 myObject继这样myObjectfoo读创 实际发类继见45这 myObject对为对读foofoo这 =赋值Object.defineProperty(..)响 对进话显伪态见4说 偿应6绍另简洁设计 隐产码 var anotherObject = { a:2 }; var myObject = Object.create( anotherObject ); anotherObject.a; // 2 myObject.a; // 2 anotherObject.hasOwnProperty( "a" ); // true myObject.hasOwnProperty( "a" ); // false myObject.a++; // 隐 anotherObject.a; // 2 myObject.a; // 3 myObject.hasOwnProperty( "a" ); // true myObject.a++应该过查anotherObject.a别++ myObject.a = myObject.a + 1++过[[Prototype]]查a anotherObject.a获值2给这值1[[Put]]值3赋给myObject a呐 时让anotherObject.a值办 anotherObject.a++ 5.2“类” 现为对联另对这样处这问题 们[[Prototype]]“” 4们说过JavaScript类语类为对说蓝 图JavaScript对 实际JavaScript应该为“对”语为过类创 对语 JavaScript类对为为类对义为说 JavaScript对 5.2.1“类” JavaScript为滥类们细 这 这“类类”为认拥 为prototype举见3另对 function Foo() { // ... } Foo.prototype; // { } 这对为Foo为们过为Foo.prototype访问 这术语对们误导们话“ 为Foo对”觉“贴‘Fooprototype’标签对”这 样 谈这对 释这对调new Foo()见2时创 联这“Fooprototype”对 们验证 function Foo() { // ... } var a = new Foo(); Object.getPrototypeOf( a ) === Foo.prototype; // true 调new Foo()时创a4骤见2给a [[Prototype]]链联Foo.prototype对 暂细这语义 类语类说实东样们4 过这样为实继类“类为对 ”对实说这过 JavaScript类创类实创对 们[[Prototype]]联对认进这对间 联们联 new Foo()对们为a这对链[[Prototype]]联 Foo.prototype对 们两对们间联这样们类实际们 “类”为对让两对联 实际绝JavaScript发new Foo()这调实际创 联这联new Foo()间们标联 对对 这Object.create(..)过们现暂时 绍 JavaScript们对“类”另对“实”们联 视觉说[[Prototype]]图头 这为继们码视为动态语类继 这为对应类“继”义违违读动 态对应语义 “继”这词让产强预见4仅仅“” JavaScript类继为过20误 “继”“”对实 “红”样论标签变实另 ——术语这样们 处处为们“”义 认为这组术语“继”类术语“类”“构 ”“实”“态”严响对JavaScript实 继JavaScript认对JavaScript两对 间创联这样对过访问另对见6 这术语JavaScript对联 还尔JavaScript术语继则对为时 质举说车时说车轮 备 JavaScript对为归结对对实话 继 继样继脑构实 实对B实际构们义B 义东变“”这说义处终为“满” 认对继样过继 JavaScript[[Prototype]] 欢继这术语论脑 实为 5.2.2“构” 码 function Foo() { // ... } var a = new Foo(); 让们认为Foo“类” 们键new类语构类实时另 们执类构Foo()调类时类构调 “构”语义Foo.prototype还另绝码 function Foo() { // ... } Foo.prototype.constructor === Foo; // true var a = new Foo(); a.constructor === Foo; // true Foo.prototype认码时举见3 .constructor这对联Foo们过“构 ”调new Foo()创对.constructor“创这对” 实际a.constructor虽a.constructor实Foo这 aFoo“构”们释 ……JavaScript惯“类”Foofoo “类”显见?! 这惯响new调new调 许JavaScript发责这惊们维护 JavaScript“类”权对JavaScript说义 1. 构还调 码让认为Foo构为们new调“构” 对 实际Foo别构 调new键这调变“构调”实际new 构对调 举说 function NothingSpecial() { console.log( "Don't mind me!" ); } var a = new NothingSpecial(); // "Don't mind me!" a; // {} NothingSpecialnew调时构对赋值给a这 new论构对这调构调 NothingSpecial构 换话说JavaScript对“构”释带new调 构仅new时调变“构调” 5.2.3术 们经绍JavaScript“类”问题 JavaScript发绞脑类为 function Foo(name) { this.name = name; } Foo.prototype.myName = function() { return this.name; }; var a = new Foo( "a" ); var b = new Foo( "b" ); a.myName(); // "a" b.myName(); // "b" 这码另两“类” 1. this.name = name给对ab见2this绑.name 类实值 2. Foo.prototype.myName = ...给Foo.prototype对 现a.myName()觉惊讶这 这码创ab时Foo.prototype对这两对实 这样 头绍认[[Get]]时们绍过[[Prototype]]链对 时过进查 创过ab[[Prototype]]联Foo.prototypeab myName时过见6Foo.prototype 顾“构” 讨论.constructor时们说过a.constructor === Foo为a实 Foo.constructor实这样 这误实际.constructor样给Foo.prototype Foo.prototype.constructor认Foo .constructorFooa对Foo“构”这过 a.constructor过认[[Prototype]]Foo这“构”对 .constructor错误对产误导 举说Foo.prototype.constructorFoo时认创 对换认.prototype对对动获.constructor 码 function Foo() { /* .. */ } Foo.prototype = { /* .. */ }; // 创对 var a1 = new Foo(); a1.constructor === Foo; // false! a1.constructor === Object; // true! Object(..)“构”a1对应该Foo()“构”发认为Foo()执 构问题认为“constructor”“......构”话a1.constructor应 该FooFoo a1.constructor[[Prototype]]链Foo.prototype 这对.constructor过认Foo.prototype对这继续 这给链顶Object.prototype这对.constructor Object(..) 错误观毁 给Foo.prototype.constructor过这动为 举见3 举说 function Foo() { /* .. */ } Foo.prototype = { /* .. */ }; // 创对 // `Foo.prototype` “”丢.constructor // 对Foo.prototype // defineProperty(..)见3 Object.defineProperty( Foo.prototype, "constructor" , { enumerable: false, writable: true, configurable: true, value: Foo // 让.constructorFoo } ); .constructor动这“constructor”错误为“... 构”这误实 实际对.constructor认这过对.prototype “constructor”“prototype”这两词义办记这 “constructor构” .constructor变举见码值 给[[Prototype]]链对为constructor 对进对赋值 [[Get]]查[[Prototype]]链样.constructor标 现应该这 结论对a1.constructor实际们 认们a1.constructor a1.constructor说这 5.3继 们经过许JavaScript拟类为“继”话 JavaScript类 实际们经继a“继”Foo.prototype访 问Foo.prototypemyName()们继类类间 类实间 还记这张图吗仅对实a1Foo.prototype还 Bar.prototypeFoo.prototype类继头图 头这联 这码“风” function Foo(name) { this.name = name; } Foo.prototype.myName = function() { return this.name; }; function Bar(name,label) { Foo.call( this, name ); this.label = label; } // 们创Bar.prototype对联Foo.prototype Bar.prototype = Object.create( Foo.prototype ); // 现Bar.prototype.constructor // 这话动 Bar.prototype.myLabel = function() { return this.label; }; var a = new Bar( "a", "obj a" ); a.myName(); // "a" a.myLabel(); // "obj a" 为thisa话请查2 这码语Bar.prototype = Object.create( Foo.prototype )调 Object.create(..)创“”对对[[Prototype]]联对 Foo.prototype 换话说这语“创Bar.prototype对联Foo.prototype” function Bar() { .. }时样Bar.prototype联认对 这对们Foo.prototype们创对联们 对联对 这两见错误实际们问题 // 样 Bar.prototype = Foo.prototype; // 满产 :( Bar.prototype = new Foo(); Bar.prototype = Foo.prototype创联Bar.prototype对 让Bar.prototypeFoo.prototype对执类Bar.prototype.myLabel = ...赋 值语时Foo.prototype对显这结则Bar对 Foo这样码简单 Bar.prototype = new Foo()创联Bar.prototype对Foo(..) “构调”Foo态对给this 话响Bar()“”设 创联对们须Object.create(..) Foo(..)这样创对对 认对 标对[[Prototype]]联ES6们 过设.__proto__实现这标浏览ES6 辅Object.setPrototypeOf(..)标联 们对两Bar.prototype联Foo.prototype // ES6认Bar.prototype Bar.ptototype = Object.create( Foo.prototype ); // ES6现Bar.prototype Object.setPrototypeOf( Bar.prototype, Foo.prototype ); Object.create(..)带轻损对进垃圾实际 ES6读过论这两语 检查“类” 设对a寻对a对话传统类环检查 实JavaScript对继JavaScript联为 码 function Foo() { // ... } Foo.prototype.blah = ...; var a = new Foo(); 们过a“”联“类” a instanceof Foo; // true instanceof对instanceof问题 a[[Prototype]]链Foo.prototype对 这处对a带.prototypeFoo间两 对ab间过[[Prototype]]链联instanceof实现 .bind(..)绑见2话该 .prototype这样instanceof话标.prototype绑 .prototype 们“构调”绑过这话实际 调标绑instanceof标 instanceof 这谬码试图“类”instanceof两对 // o1联o2辅 function isRelatedTo(o1, o2) { function F(){} F.prototype = o2; return o1 instanceof F; } var a = {}; var b = Object.create( a ); isRelatedTo( b, a ); // true isRelatedTo(..)们F.prototype赋值对o2 o1F“实”显见o1实际继FF构这 误问题键强JavaScript应类语义 instanceof这尴尬 [[Prototype]]简洁 Foo.prototype.isPrototypeOf( a ); // true 们实际Foo们对 Foo.prototypeisPrototypeOf(..)问题a[[Prototype]]链 现过Foo.prototype 样问题样间Foo.prototype 动访问 们两对们间举说 // 简单b现c[[Prototype]]链 b.isPrototypeOf( c ); 这“类”bc间对们换 话说语isPrototypeOf(..)们isRelatedTo(..) 们获对[[Prototype]]链ES5标 Object.getPrototypeOf( a ); 验证这对们样 Object.getPrototypeOf( a ) === Foo.prototype; // true 绝浏览标访问[[Prototype]] a.__proto__ === Foo.prototype; // true 这.__proto__ES6标“”[[Prototype]]对 查过.__proto__.__ptoto__...历链话这 们说过.constructor样.__proto__实际对 a实际.toString().isPrototypeOf(..)样 Object.prototype们举见2 .__proto__实际getter/setter见3 .__proto__实现这样对义见3 Object.defineProperty( Object.prototype, "__proto__", { get: function() { return Object.getPrototypeOf( this ); }, set: function(o) { // ES6setPrototypeOf(..) Object.setPrototypeOf( this, o ); return o; } } ); 访问获值a.__proto__时实际调a.__proto__()调getter虽getter Object.prototype对this对athis绑规则见2 Object.getPrototypeOf( a )结 .__proto__设码ES6Object.setPrototypeOf(..)进设 说对[[Prototype]] 杂术实现“类”说们这 为这码阅读难维护难 ES6class键类Array实现类“类”详 录AES6class语绍 们们讨论过设认.prototype对 [[Prototype]]让对Object.prototype这样对换 认对[[Prototype]]对联读码读 JavaScript对线们类__proto__ 为“dunder”JavaScript__proto__“proto” 5.4对联 现们[[Prototype]]对链对 说这链对继续 [[Prototype]]联对进查继续查 [[Prototype]]类这对链为“链” 5.4.1创联 们经为JavaScript[[Prototype]]类样对间 联 [[Prototype]]义为JavaScript发费这拟类码 创这联 还记吗经说过Object.create(..)现时为 var foo = { something: function() { console.log( "Tell me something good..." ); } }; var bar = Object.create( foo ); bar.something(); // Tell me something good... Object.create(..)创对bar联们对foo这样们 发挥[[Prototype]]烦new构调 .prototype.constructor Object.create(null)创拥说null[[Prototype]]链对这对 进这对链instanceof释过进 总false这[[Prototype]]对“”们 链扰储 们类创两对间过联对够 Object.create(..)“类诡计”创们联 Object.create()polyfill码 Object.create(..)ES5ES5环IE这 话简单polyfill码实现Object.create(..) if (!Object.create) { Object.create = function(o) { function F(){} F.prototype = o; return new F(); }; } 这polyfill码F们过.prototype联 对new F()构对进联 Object.create(..c)拟这应标ES5 Object.create(..)还ES5这 说这应围虑们还绍 var anotherObject = { a:2 }; var myObject = Object.create( anotherObject, { b: { enumerable: false, writable: true, configurable: false, value: 3 }, c: { enumerable: true, writable: false, configurable: false, value: 4 } }); myObject.hasOwnProperty( "a" ); // false myObject.hasOwnProperty( "b" ); // true myObject.hasOwnProperty( "c" ); // true myObject.a; // 2 myObject.b; // 3 myObject.c; // 4 Object.create(..)对这 见3为ES5拟polyfill码实现这 说Object.create(..)对发说polyfill 码够 发严谨们认为拟应该polyfill码 Object.create(..)拟这认为ES5 环Object.create(..)polyfill码义 Object.create义这样 function createAndLinkObject(o) { function F(){} F.prototype = o; return new F(); } var anotherObject = { a:2 }; var myObject = createAndLinkObject( anotherObject ); myObject.a; // 2 赞这严观赞ES5polyfill码选择 5.4.2联备 对间联处“”时备选项这说 认为这[[Prototype]]质 码 var anotherObject = { cool: function() { console.log( "cool!" ); } }; var myObject = Object.create( anotherObject ); myObject.cool(); // "cool!" [[Prototype]]这码这样为让myObject 处时备anotherObject软变“” 难维护 这说应该选择备这设计这JavaScript见 这许应这 ES6为“”Proxy实现“”时 为书讨论围书绍 这别 给发设计软时设调myObject.cool()myObjectcool()时这语 话API设计变“”对维护软发说这 让API设计“”时发挥[[Prototype]]联 var anotherObject = { cool: function() { console.log( "cool!" ); } }; var myObject = Object.create( anotherObject ); myObject.doCool = function() { this.cool(); // }; myObject.doCool(); // "cool!" 这们调myObject.doCool()实际myObject这让们API设计 “”说们实现设计见6 过[[Prototype]]anotherObject.cool() 换话说让API设计们详细释 5.5结 访问对[[Get]]见3查对 [[Prototype]]联对这联实际义“链”链 查时对进历 对Object.prototype链顶说 链toString()valueOf() Object.prototype对语对们 联两对new键词进调调4骤2创 联对对 new调时对.prototype联“对”带new调 为“构调”们实际传统类语类构样 虽这JavaScript传统类语“类”“类继”JavaScript 别进对间过[[Prototype]]链联 “继”结术语“继”对术语 JavaScript实仅仅们维 “”术语为对间 6 为 5详细绍[[Prototype]]说为“类”“继”讨 论[[Prototype]]产误这经续20们杂语 .prototype码见识样阱.constructor伪态 语们还这问题“” 为简单变这杂现们 绝JavaScript发过JavaScript们 这给“类”库处 现仅满这细节们给“”库类们 简单发JavaScript对[[Prototype]] 简单顾5结论[[Prototype]]对链另对 对继续[[Prototype]]联对 进查继续查[[Prototype]] 类这对链为“链” 换话说JavaScript这质对间联 这观对说础 6.1设计 为习观[[Prototype]]们须认识类 见4设计 类设计则识 够举说样应虽见 们试类继设计转换为设计习 过类转换 这维 带们进论训练传够应码实 6.1.1类论 设们软类务“XYZ”“ABC” 类设计这样义类为TaskTask类 义务为义类XYZABC们继Task 为处对应务 类设计继时态说XYZ务Task 义为时过super调这发现许 为“”类类进 对应伪码 class Task { id; // 构Task() Task(ID) { id = ID; } outputTask() { output( id ); } } class XYZ inherits Task { label; // 构XYZ() XYZ(ID,Label) { super( ID ); label = Label; } outputTask() { super(); output( label ); } } class ABC inherits Task { // ... } 现实类XYZ这实执务“XYZ”这实Task 义为XYZ义为ABC类实Task为ABC为 构这实类为实务 为 6.1.2论 现们试为类样问题 义为Task对许JavaScript发诉类 务读为对务 “XYZ”“ABC”义对储对应为务对联 Task对让们时进 执务“XYZ”两对XYZTask协们 这为过类们们别对时 许XYZ对给Task 码简单 Task = { setID: function(ID) { this.id = ID; }, outputID: function() { console.log( this.id ); } }; // 让XYZTask XYZ = Object.create( Task ); XYZ.prepareTask = function(ID,Label) { this.setID( ID ); this.label = Label; }; XYZ.outputTaskDetails = function() { this.outputID(); console.log( this.label ); }; // ABC = Object.create( Task ); // ABC ... = ... 这码TaskXYZ类们对XYZ过Object.create(..)创 [[Prototype]]Task对见5 类说对这编码风为“对联”OLOOobjects linked to other objects们XYZ对ABC对Task对 JavaScript[[Prototype]]对联对论说 JavaScript类“类”这实这选 择对实达显难 对联风码还处 1. 码idlabel员储XYZTask说 [[Prototype]]态XYZABC标Task 2. 类设计们让类Task类XYZoutputTask这样 态优势为则们[[Prototype]]链级别 则语义见4 这设计 应对为类这样实际创维护码为 仅义贯码 3. this.setID(ID)XYZ寻XYZsetID(..)XYZ这 过[[Prototype]]联Task继续寻这时setID(..) 调发this隐绑规则见2虽setID(..)Task运 时this绑XYZ这们码们还this.outputID() 换话说们XYZ进时Task为XYZTask 为对XYZ时这请给另对 Task 这强设计类类继态脑对 类类组织过联组织 API设计实现们 让发过API调XYZ.setID()这们隐 APIXYZ.prepareTask(..)Task.setID(..)细节见5.4.2节 1. 两两对间创环B联A试 A联B错 遗烦这两边 [[Prototype]]链产递归环 严话BA论 这 为发们发现设时检查环 则对查时进检查 2. 调试 们简单绍让发细节说JavaScript规浏览 发对值结构浏览选择进 浏览结这码结Chrome 发 这传统“类构”JavaScript码Chrome发结 function Foo() {} var a1 = new Foo(); a1; // Foo {} 们码达a1输Foo {}Firefox运样码 Object {}为这样这输 Chrome实际说“{}对为Foo构”Firefox说“{} 对Object构”这细别为Chrome动态实际执构过 浏览这额 JavaScript释Chrome function Foo() {} var a1 = new Foo(); a1.constructor; // Foo(){} a1.constructor.name; // "Foo" Chrome输对.constructor.name“” 码 function Foo() {} var a1 = new Foo(); Foo.prototype.constructor = function Gotcha(){}; a1.constructor; // Gotcha(){} a1.constructor.name; // "Gotcha" a1; // Foo {} 们a1.constructor.name为另值GotchaChrome输Foo 问题.constructor.name“”Chrome过 另进 别们这码 var Foo = {}; var a1 = Object.create( Foo ); a1; // Object {} Object.defineProperty( Foo, "constructor", { enumerable: false, value: function Gotcha(){} }); a1; // Gotcha {} GotchaChrome实 .constructor.name实际编书时这为认Chromebug读书 时经a1; // Object {} 这bugChrome调试输“构”Chrome 扩为JavaScript规 “构”对绍对联风编码 Chrome对“构”这样对输Object {}“Object()构 对” 这对联风码对联风编码为设 计时谁“构”对new调类风编 码时Chrome“构”义对联时这 6.1.3较维 现经“类”“”这两设计论别们们维 别 们过FooBar码较两设计对对联实现 “”对风 function Foo(who) { this.me = who; } Foo.prototype.identify = function() { return "I am " + this.me; }; function Bar(who) { Foo.call( this, who ); } Bar.prototype = Object.create( Foo.prototype ); Bar.prototype.speak = function() { alert( "Hello, " + this.identify() + "." ); }; var b1 = new Bar( "b1" ); var b2 = new Bar( "b2" ); b1.speak(); b2.speak(); 类Bar继类Foob1b2两实b1Bar.prototype Foo.prototype这风见应该 们对联风编码 Foo = { init: function(who) { this.me = who; }, identify: function() { return "I am " + this.me; } }; Bar = Object.create( Foo ); Bar.speak = function() { alert( "Hello, " + this.identify() + "." ); }; var b1 = Object.create( Bar ); b1.init( "b1" ); var b2 = Object.create( Bar ); b2.init( "b2" ); b1.speak(); b2.speak(); 这码们样[[Prototype]]b1给BarBar给Foo码 样们实现对间联 这码简洁许们对联杂 类为构new 问问对联风码够实现类风码简洁懂 类风 们两码对应维 类风码维强调实实间 实际这张图/误导为还许术细节须 们图这张杂图头发 现JavaScript强连贯 举说JavaScript访问call(..)apply(..)bind(..)见2 为对对样[[Prototype]]联Function.prototype对 对过调这认JavaScript这 们张简图“”——对 杂线Bar.prototype继Foo.prototype丢.constructor 见5.2.3节“顾‘构’”们还这线这维 处对联时杂 现们对联风码维 过较对联风码显简洁为这码对间 联 “类”杂们变简单许时 6.2类对 们经“类”“为”论维别现实场应 这 Web发场创UI钮 6.2.1“类” 经习惯对设计为类 Widget继类类Button 这jQueryDOMCSS为这们现讨论 这码哪JavaScriptjQueryDojoYUI 问题 这码“类”辅库语纯JavaScript实现类 风码 // 类 function Widget(width,height) { this.width = width || 50; this.height = height || 50; this.$elem = null; } Widget.prototype.render = function($where){ if (this.$elem) { this.$elem.css( { width: this.width + "px", height: this.height + "px" } ).appendTo( $where ); } }; // 类 function Button(width,height,label) { // 调“super”构 Widget.call( this, width, height ); this.label = label || "Default"; this.$elem = $( "
还剩193页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 1 人已下载

下载pdf

pdf贡献者

guoling009

贡献于2016-04-26

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf