ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Modern JavaScript Tutorial 精读:数组上下文中的方法调用 arr[2]() 与 this 绑定原理

Modern JavaScript Tutorial 精读:数组上下文中的方法调用 arr[2]() 与 this 绑定原理 文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载本篇技术文章围绕现代 JavaScript 教程Modern JavaScript Tutorial数组章节中的经典习题「Calling in an array context」展开系统讲解arr[2]()这种「以数组为宿主对象、以数字索引为方法名」的调用语法为何能正常工作、this为何指向数组本身并深入到底层的 Reference Type 机制帮助读者彻底理解 JavaScript 中obj.method()与obj[method]()的等价关系及this的动态绑定规则。读完本文你将能准确预测数组元素为函数时的调用结果并能够解释(method obj.go)()、(obj.go || obj.stop)()等派生表达式丢失this的原因。题目回顾数组里的函数通过下标调用本习题位于 1-js/05-data-types/04-array/3-call-array-this/task.md原始题目如下let arr [a, b]; arr.push(function() { alert( this ); }); arr[2](); // ?题目问的是最后一行arr[2]()的结果是什么为什么答案this 指向数组输出 a,b,function(){...}答案见 1-js/05-data-types/04-array/3-call-array-this/solution.md非常简洁调用arr[2]()在语法上就是经典的obj[method]()—— 其中obj的角色由arr扮演method的角色由数字索引2扮演。因此arr[2]所引用的函数是以「对象方法」的方式被调用的它自然而然地收到this指向对象arr本身最终输出整个数组let arr [a, b]; arr.push(function() { alert( this ); }) arr[2](); // a,b,function(){...}运行结果之所以是字符串a,b,function(){...}是因为此刻数组一共有 3 个值初始的两个字符串a、b再加上末尾 push 进去的那个函数。alert(this)在展示对象时会把数组通过其内置的toString转换成逗号分隔的列表详见下文「输出格式说明」一节。关键前提数组本质上是对象可以存放任意类型的值要理解这个结论必须先回到数组本身的性质。在 1-js/05-data-types/04-array/article.md 中明确指出数组是一种特殊形态的对象arr[0]这种方括号访问本质上就是对象语法obj[key]其中arr是对象、数字充当键名数组可以存放任意类型的元素包括对象和函数数组中已有的元素个数由length属性记录push会把新元素追加到末尾length自动 1。文章中的原型示例甚至直接演示了「通过索引取出函数并执行」// mix of values let arr [ Apple, { name: John }, true, function() { alert(hello); } ]; // get the object at index 1 and then show its name alert( arr[1].name ); // John // get the function at index 3 and run it arr[3](); // hello回到本题arr.push(function(){...})执行后函数被追加到索引2的位置数组变为[a, b, function]length为 3。arr[2]取出的正是这个函数arr[2]()则调用它。核心机制点语法与方括号语法在方法调用上等价为什么arr[2]()中的this会是arr而不是undefined关键在于 JavaScript 对「方法调用」语法的判定规则。在 1-js/04-object-basics/04-object-methods/article.md 中有这样一条重要说明当函数以obj.f()形式被调用时调用期间this的值就是obj至于obj是来自点语法还是方括号语法并不重要。文章中的示例let user { name: John }; let admin { name: Admin }; function sayHi() { alert( this.name ); } // use the same function in two objects user.f sayHi; admin.f sayHi; // these calls have different this // this inside the function is the object before the dot user.f(); // John (this user) admin.f(); // Admin (this admin) admin[f](); // Admin (dot or square brackets access the method – doesnt matter)注意最后一行admin[f]()用方括号[f]访问并调用方法this依然是admin。把「属性名是字符串f」换成「属性名是数字2」就得到本题的arr[2]()——两者在语法层面完全同构。也就是说this的绑定不取决于方法是怎么被「取」出来的而取决于它是否紧跟调用括号()且构成了成员访问表达式。arr[2]()满足这一条件因此this arr。底层原理Reference Type 如何传递 thisthis能够被正确传递其底层秘密在于 ECMAScript 规范中的 Reference Type引用类型机制。这部分内容在 1-js/99-js-misc/04-reference-type/article.md 中有专门讲解。一个obj.method()语句在引擎内部被拆成两步点.或方括号[]取出属性obj.method括号()立即执行它。问题在于信息对象是谁、方法叫什么如何从第一步传递到第二步如果拆成两行this就会丢失let user { name: John, hi() { alert(this.name); } }; // split getting and calling the method in two lines let hi user.hi; hi(); // Error, because this is undefined为了让user.hi()正常工作JavaScript 使用了一个技巧点或方括号返回的并不是普通的函数值而是一个特殊的规范类型值——Reference Type。它是一个三元组合base宿主对象name属性名strict当前是否处于严格模式。例如严格模式下读取user.hi得到的是内部值(user, hi, true)。当()直接作用在这样一个 Reference Type 值上时调用方拥有宿主对象与属性名的完整信息就能把this正确设置为user。任何其他操作赋值、逻辑或||、条件运算符等都会丢弃 Reference Type、只保留其中的普通函数值于是后续调用便失去this。这正是本习题的对立面arr[2]()是成员访问语法 紧跟调用括号属于 Reference Type 被正确消费的情形所以this完整保留。边界对照哪些写法会丢失 this理解了 Reference Type 后可以对照 1-js/99-js-misc/04-reference-type/3-why-this/task.md 中的习题把「数组上下文」扩展到一般对象体会写法差异let obj, method; obj { go: function() { alert(this); } }; obj.go(); // (1) [object Object] (obj.go)(); // (2) [object Object] (method obj.go)(); // (3) undefined (obj.go || obj.stop)(); // (4) undefined对应解释见 1-js/99-js-misc/04-reference-type/3-why-this/solution.md调用(1)是常规对象方法调用this为obj调用(2)中的括号并不改变运算顺序——点访问依然先发生(obj.go)()与obj.go()等价调用(3)中括号左侧是表达式method obj.go赋值操作把 Reference Type 退化成了普通函数值随后( )以无宿主的方式调用this为undefined调用(4)同理obj.go || obj.stop中的||运算同样丢弃了宿主信息。同理可对照 1-js/99-js-misc/04-reference-type/2-check-syntax/task.md 中的(user.go)()——由于括号不改变「点先求值、括号直接调用」的顺序它依然能正常输出John但若把赋值、||等运算插入括号与点之间this就会丢失。把这一规律映射回本题arr[2]()左侧是纯粹的成员访问arr[2]且紧跟()没有任何中间运算介入所以等价于arr.2风格的方法调用尽管数字键名无法用点语法书写this arr是必然结果。输出格式说明数组的 toString 与 alertalert( this )最终显示a,b,function(){...}的原因同样在 1-js/05-data-types/04-array/article.md 的「toString」一节有交代数组实现了自己的toString返回元素以逗号分隔的字符串。let arr [1, 2, 3]; alert( arr ); // 1,2,3 alert( String(arr) 1,2,3 ); // true因此本题中this即[a, b, function(){...}]被alert展示时会先被转成a,b,function(){...}——函数元素以其源码文本形式参与拼接。实战延伸函数数组的应用模式本习题揭示的「函数可以作为数组元素、并通过索引以方法形式调用」的特性在实际开发中有不少应用场景1. 回调注册表 / 命令队列const tasks []; tasks.push(() console.log(task A)); tasks.push(() console.log(task B)); // 顺序执行注册的每个任务 for (let i 0; i tasks.length; i) { tasks[i](); // this tasks }2. 策略分派表const actions { 0: () left, 1: () right }; function move(dir) { return actions[dir](); // 用动态索引调用this actions }注意一旦把函数从宿主上「拆下来」再调用this就会丢失例如const fn tasks[0]; fn();中的this为undefined严格模式。需要保留宿主时应使用tasks[0].call(tasks)、tasks[0].apply(tasks)或bind参考 1-js/06-advanced-functions/10-bind/article.md 中的解法 2。3. 与箭头函数的对比若数组元素是箭头函数则它没有自己的this无论怎么调用都会从外层词法作用域取this见 1-js/04-object-basics/04-object-methods/article.md 中「Arrow functions have no this」一节。因此本题若把function(){...}换成() {...}alert(this)显示的就是外层作用域的this而非数组本身——这也是面试中常见的变体追问。小结arr[2]()是obj[method]()的数组形态数组充当宿主对象、数字索引充当属性名this指向数组本身输出a,b,function(){...}数组本质是对象可以存放函数等任意类型值push追加后length自动更新点语法与方括号语法在方法调用上等价只要成员访问紧跟()this就能正确绑定底层机制是 Reference Type(base, name, strict)它把宿主信息从属性访问传递给调用括号赋值、||、条件运算符等中间操作会丢弃它导致this丢失alert(数组)经由数组的toString输出逗号分隔的元素列表。本习题是理解 JavaScriptthis动态绑定的重要跳板建议结合 数组章节、对象方法与 this 以及 Reference Type 专题 三篇材料通读即可构建完整的知识闭环。赞分享文档/教程前端【免费下载链接】en.javascript.infoModern JavaScript Tutorial项目地址https://gitcode.com/gh_mirrors/en/en.javascript.info点击查看免费下载相关推荐Claude Code 仓库实战用 /dedupe 命令与多 Agent 并行协作自动发现 GitHub 重复 IssueClaude Code 仓库实战用 /dedupe 命令与多 Agent 并行协作自动发现 GitHub 重复 Issue Claude Code 是运行在终文档/教程前端Modern JavaScript Tutorial 方法链式调用实战理解 return this 的魔力Modern JavaScript Tutorial 方法链式调用实战理解 return this 的魔力 导读 在 Modern JavaScript Tu文档/教程前端JavaScript 对象方法与 this 关键字详解Modern JavaScript Tutorial 实战篇JavaScript 对象方法与 this 关键字详解Modern JavaScript Tutorial 实战篇 导读 在 JavaScript 中对象文档/教程前端上一篇Docker MailserverDMS账户管理完全指南Provisioner、别名、配额与认证架构详解下一篇TiXL 图像风格化算子库指南Lib.image.fx.stylize 的 15 个实时特效实战解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表