正在加载...

this 关键字

同一个标签函数,可以先说出杯子的名字,再找不到那件商品,然后又说出另一只杯子的名字。花括号没动。变的是调用方式。JavaScript 在调用时选定 this,不是在你敲函数的时候。

普通方法调用之后

方法那一页把函数放在对象上,用 product.tag() 跑起来。那种形状里,this 就是点号前面的对象。那一页停在这里。这一页函数还在原处,调用变了:从对象上摘下来、用 call 对准别处、用 bind 锁住,或者写成箭头。

调用决定 this 的五种形状

📊 方法、裸调用、call、bind、箭头

形状 例子 this 落在哪里
方法 product.tag() 点号前面的对象
裸调用 let show = product.tag; show() 不是那个对象(严格模式下是 undefined)
call product.tag.call(cup) 你交给 call 的对象,只对这一次
bind product.tag.bind(product) 以后的调用都锁在那个对象上
箭头 写在另一个函数里的 () => this.name 外层函数的 this,不是点号前面的对象

先给调用起名字,再读 this。把函数放在对象上,并不会把 this 冻住。作决定的是调用。bind 为以后的调用提前作决定。箭头不理调用,去看包着它的那个函数。

方法调用——this 是点号前面的对象

javascript
"use strict";
let product = {
name: "Mug",
tag() {
return this.name;
}
};
console.log(product.tag());

点号仍然点出对象

product.tag() 运行 tag,并把 this 设成 product。控制台打印 Mug。这是方法那一页的规则,留在这里当基线。后面四次调用是故意离开这条基线的。

裸调用——对象不再跟着走

javascript
"use strict";
let product = {
name: "Mug",
tag() {
if (this == null) {
return "missing";
}
return this.name;
}
};
let show = product.tag;
console.log(show());

把函数从对象上摘下来,杯子就跟着点号走了

show 和 product.tag 是同一个函数。show() 的点号前面没有对象,严格模式下 this 就是 undefined。守卫返回 missing。拿掉守卫,this.name 会抛错。这一页守着严格模式,结果才好预料。非严格脚本可能把裸调用指到全局对象上。

用 call 把 this 对准另一个对象

javascript
"use strict";
let product = {
name: "Mug",
tag() {
return this.name;
}
};
let cup = { name: "Cup" };
console.log(product.tag.call(cup));

call 把函数借给一个对象用一次

product.tag.call(cup) 跑的仍是杯子上的 tag,但这一次 this 是 cup。控制台打印 Cup。product.name 还是 Mug。call 不会把函数贴到 cup 上。额外参数放在数组里时,apply 做的是同一件事——这个标签用不到它们。

用 bind 把对象锁住

javascript
"use strict";
let product = {
name: "Mug",
tag() {
return this.name;
}
};
let show = product.tag.bind(product);
console.log(show());

bind 把对象留给以后的裸调用

bind 返回一个新函数。里面的 this 一直是 product,哪怕 show() 没有点号。控制台打印 Mug。原来的 tag 不动。按钮、计时器或其他函数准备在前面不带对象地调用方法时,就用 bind。

箭头从外层函数取 this

javascript
"use strict";
let shelf = {
name: "Shelf",
make() {
let product = {
name: "Mug",
tag: () => this.name
};
return product.tag();
}
};
console.log(shelf.make());

箭头不从点号取 this

product.tag() 看起来像方法调用,可 tag 是箭头。箭头没有自己的 this。它用的是 make 里的 this,而 make 是按 shelf.make() 跑的,所以 this 是 shelf。控制台打印 Shelf,不是 Mug。若 product 上是普通方法,就会打印 Mug。箭头在更早的一页出现过;这一页只看它们对 this 做了什么。

让调用和工作对上

对象在点号前面?就把 this 读成那个对象。函数摘下来了,又没绑定?别指望那个对象。换一个对象、只跑一次?用 call。以后不带点号的调用仍要看到这只杯子?先 bind。写的是箭头?往外看包着它的函数,不要看点号前面的对象。构造函数和类也会设置 this,那是后面的页面。下一课是 JSON:描述数据的文本,不是又一条 this 规则。

把锁好的标签放到页面上

用 bind 把 tag 锁在杯子上,再不带点号地调用那个新函数。把结果写进一个段落。

页面演示——锁住的标签

html
<!DOCTYPE html>
<html>
<head>
<title>this-keyword — page</title>
</head>
<body>
<p id="line">…</p>
<script>
"use strict";
let product = {
name: "Mug",
tag() {
return this.name;
}
};
let show = product.tag.bind(product);
let box = document.getElementById("line");
box.textContent = show();
</script>
</body>
</html>

段落显示 Mug。show() 的点号前面没有对象,这一行仍然写出杯子的名字,因为 bind 锁住了 this。只改 product.name,刷新,段落会跟着走。下一课:JavaScript 中的 JSON —— 描述数据的文本,不是 this 的新规则。

练习步骤

  1. 调用 product.tag(),读到 Mug。
  2. 把函数放进 show,在严格模式下调用 show(),读到 missing。
  3. 调用 product.tag.call(cup),读到 Cup。
  4. 把 tag 绑到 product 上,不带点号调用新函数,读到 Mug。
  5. 运行 shelf.make(),读到 Shelf,不是 Mug。
  6. 在 HTML 演示里只改 product.name,刷新,再读段落。

常见失误

  • 因为函数待在对象上,就以为 this 被冻住了
  • 在严格模式下调用 show(),却还指望 Mug
  • 以为 call 把 tag 复制到了 cup 上
  • 指望 bind 改写原来的 tag,而不是返回新函数
  • 把箭头当成方法来写,却指望点号前面的对象
  • 把下一页当成又一条 this 规则——JSON 是数据的文本格式

小结

  • 选定 this 的是调用,不是花括号
  • object.method() 把 this 设成点号前面的对象
  • 严格模式下的裸调用不会把 this 指向那个对象
  • call 把 this 设成你传入的对象,只对这一次
  • bind 返回一个 this 保持锁定的函数
  • 箭头从包围它的函数里取 this
  • 绑定后的调用可以填上页面里的一行
  • 下一课:JSON,描述数据的文本

🧠 测试您的知识

准备开始

测试您的知识

通过这个互动测验挑战自己,看看你对这个主题的理解程度如何

❓
8
问题
🎯
70%
及格要求
♾️
∞
时间
🔄
∞
尝试次数

📝 说明

  • 仔细阅读每个问题
  • 为每个问题选择最佳答案
  • 您可以随时重新参加测验
  • 您的进度将显示在顶部