题目 27 / 31
给成员表达式加上括号会改变 this 吗?
知识点: this 绑定难度: 进阶
给属性引用加上括号并不会剥离它——效果和直接调用属性完全一样。
使用执行可视化器解题在下方逐步观察每个绑定、函数调用和控制台输出。预测输出结果
预测这四行输出。foo、bar 最终都指向和 person.greeting 相同的函数,但分别以四种不同方式调用。
var name = "Window";
var person = {
name: "Person",
greeting: function () {
console.log(this.name);
}
};
function greeting() {
var foo = person.greeting;
foo();
person.greeting();
(person.greeting)();
(bar = person.greeting)();
}
greeting();查看答案与完整解析
正确输出
Window
Person
Person
Window为什么会这样
foo() 是通过一个普通变量调用的——这个变量本身并不是属性引用,因此走默认绑定,this 指向全局对象(window),输出 "Window"。
person.greeting() 是一次真正的成员表达式调用:从这个引用上读到的接收者是 person,因此走隐式绑定,this.name 读到 "Person"。
(person.greeting)() 只是给同一个成员表达式加了一对括号。分组运算符只影响求值顺序/优先级,并不会像赋值或逗号表达式那样把属性引用变成一个普通值——调用点没有变化,this 仍然是 person,因此再次输出 "Person"。
(bar = person.greeting)() 看起来很像,但外层表达式其实是一次赋值,而不是一个裸的属性引用。赋值表达式求值为它右侧的值——也就是这个函数本身,接收者信息已经被剥离——因此这次调用的调用点和 foo() 完全一样:走默认绑定,this 又是 window,输出 "Window"。
这道题考察什么
- this 绑定取决于调用点的引用形式,而不是哪个变量恰好持有这个函数
- 分组运算符 (...) 永远不会剥离属性引用——只有会产生新值的运算符(赋值、逗号等)才会
- 把方法复制到一个普通变量里(var foo = obj.method)总会丢失它的接收者
运行环境前提
假设是经典的非严格模式脚本,此时一次没有接收者的调用其 this 默认是全局对象。