- 文档
- 教程
- 前端
【免费下载链接】zh.javascript.info
现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。
数组(Array)是 JavaScript 中最常用的有序集合,但它的本质仍然是对象:arr[0]与obj[key]是同一种语法。当数组中存放的是函数,并通过arr[2]()这样的下标语法调用时,this究竟指向谁?本文以现代 JavaScript 教程中的一道经典习题("在数组上下文调用")为骨架,结合仓库中「数组」「对象方法与 this」「Reference Type」等章节的源码级原理,彻底讲清数组元素调用函数的this绑定规则,帮助你建立"函数调用方式决定this"的清晰认知。
题目:结果是什么?为什么?
这道题出自 1-js/05-data-types/04-array/3-call-array-this/task.md,重要性标记为 5(importance: 5),是数组章节中一道高频易错的思维题:
let arr = ["a", "b"]; arr.push(function() { alert( this ); }); arr[2](); // ?先别急着运行,想一想:arr[2]取出来的是一个函数,再紧跟()调用它。此时函数内部的this是什么?alert(this)会弹出什么内容?
标准答案:this指向数组本身
答案在 solution.md 中给出了非常精炼的解释:
arr[2]()调用从句法来看可以类比于obj[method](),与obj对应的是arr,与method对应的是2。
也就是说,arr[2]()本质上就是"从对象arr上取出名为2的属性(一个函数),并以对象方法的方式调用它"。因此它接收this引用的对象就是arr,alert(this)会输出整个数组:
let arr = ["a", "b"]; arr.push(function() { alert( this ); }) arr[2](); // a,b,function(){...}调用后arr一共有 3 个元素:最开始的两个字符串"a"、"b",再加上后来通过push添加进来的一个函数。所以this的字符串化结果就是由逗号连接的"a,b,function(){...}"。
为什么arr[2]()等价于obj[method]()?
数组是"特殊对象",下标访问本质是属性访问
要理解上面这个结论,必须回到数组的本质。在 1-js/05-data-types/04-array/article.md 的「内部」一节明确写道:
数组是一种特殊的对象。使用方括号来访问属性
arr[0]实际上是来自于对象的语法。它其实与obj[key]相同,其中arr是对象,而数字用作键(key)。
也就是说,arr[2]中的数字下标2,对 JavaScript 引擎而言就是一个普通的属性键(key)。这一点在数组章节中其实早有铺垫——数组可以存储任何类型的元素,包括函数:
// 混合值 let arr = [ 'Apple', { name: 'John' }, true, function() { alert('hello'); } ]; // 获取索引为 3 的函数并执行 arr[3](); // hello正因为数组元素可以是函数,而数组本身是对象,arr[3]()与arr[2]()就完全落入了"对象方法调用"的范畴。
this的值取决于调用方式,而不是函数定义位置
在 1-js/04-object-basics/04-object-methods/article.md 的「方法中的 this」一节中,教程给出了最核心的规则:
this的值就是在点之前的这个对象,即调用该方法的对象。
更一般地,这一章还强调了一个容易被其他语言背景开发者误解的事实:JavaScript 的this是"自由"的,它的值在调用时计算,取决于"点符号前/调用者上下文里"的是什么对象,而不是函数声明在哪里。例如:
let user = { name: "John" }; let admin = { name: "Admin" }; function sayHi() { alert( this.name ); } user.sayHi = sayHi; admin.sayHi = sayHi; user.sayHi(); // John(this == user) admin.sayHi(); // Admin(this == admin)同一个函数,挂到不同对象上、以不同对象调用,this就不同。回到题目:arr[2]()中函数是以arr为调用者被调用的,this自然就是arr。
深层机制:Reference Type(引用类型)
如果你还想知道"为什么arr[2]()能正确把this传给函数,而let f = arr[2]; f()就不行",那就需要进入高阶主题 1-js/99-js-misc/04-reference-type/article.md 一探究竟。
obj.method()在语法上包含两个操作:
- 点
'.'(或方括号'[...]')取出属性obj.method的值; ()立即执行它。
this的信息从第一步传到第二步,靠的是一个语言内部的"规范类型"——Reference Type。它是一个三元组合(base, name, strict):
base:来源对象(这里就是arr);name:属性名(这里就是数字2);strict:严格模式下为true。
当()被作用在这个 Reference Type 上时,调用会接收到完整的对象与方法信息,从而把this设置为base(即arr)。这也是为什么只有直接以obj.method()或obj['method']()语法调用时this才被正确传递——方括号与点号在这里作用相同,arr[2]()正是后者。
反过来,一旦把函数取出来赋给变量再调用,Reference Type 会被整体丢弃,只剩下裸函数,this就丢失了:
let f = arr[2]; f(); // this === undefined(严格模式),报错或输出 undefined这恰好印证了 solution.md 的结论:arr[2]()是"对象方法式调用",而非"独立函数调用"。
延伸思考:这道题教会我们什么?
1. 任何数组元素都可以"变成方法"。只要元素是函数,通过下标调用时它就是该数组的方法,this绑定到数组。这在编写以数组为上下文的小型回调、策略函数时可能造成"意外惊喜"——例如arr[0]()内部的this会是整个数组,而非undefined或全局对象。
2.alert(this)输出数组内容。数组有自己的toString实现,返回以逗号分隔的元素列表(见 数组章节的 toString 一节),所以this(即数组)被alert字符串化后显示为a,b,function(){...}。
3. 丢失this是常见 bug 源头。动态取出函数再调用(let f = obj.m; f()、回调传参、解构赋值等)都会触发 Reference Type 被丢弃,导致this丢失。解决方案包括bind、箭头函数或在调用点保持obj.method()语法——这与题目形成完美对照:同样是数组里的函数,直接arr[2]()与先赋值再调用,结果截然不同。
小结
- 数组是特殊对象,
arr[i]本质是obj[key]形式的属性访问; - 数组可存储函数,
arr[i]()等价于"以数组为调用者的方法调用",this指向数组本身; - 本题中
alert(this)输出a,b,function(){...},因为数组被字符串化为逗号分隔的元素列表; - 深层原因是 Reference Type 机制:只有
obj.method()/obj['method']()直接调用才保留this绑定,拆开赋值即丢失。
如果希望进一步吃透相关原理,建议按顺序阅读仓库中的 数组完整章节、对象方法与 this 以及高阶主题 Reference Type,并结合本题的 task.md 与 solution.md 亲手运行验证。
- 文档
- 教程
- 前端
【免费下载链接】zh.javascript.info
现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。
相关推荐
JavaScript 数组上下文中的方法调用:深入剖析 `arr[2]()` 的 this 绑定
JavaScript 数组上下文中的方法调用:深入剖析 arr 2 的 this 绑定 在 JavaScript 中,数组可以存放任意类型的元素——包括函数。当
文档/教程前端基于 replexica 的开源本地化工程工具链:从 Lingo CLI 到 React 编译器的 i18n 实战指南
基于 replexica 的开源本地化工程工具链:从 Lingo CLI 到 React 编译器的 i18n 实战指南 replexica 是 Lingo.de
文档教程前端Modern JavaScript Tutorial 精读:数组上下文中的方法调用 arr[2]() 与 this 绑定原理
Modern JavaScript Tutorial 精读:数组上下文中的方法调用 arr 2 与 this 绑定原理 本篇技术文章围绕现代 JavaScrip
文档/教程前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考