为什么在 JavaScript 中直接调用和用逻辑与调用方法结果不同?

const obj = {
    name:"222",
    test(){
        return this.name
    }
}
console.log((obj.test)())

结果为222

const obj = {
    name:"222",
    test(){
        return this.name
    }
}
console.log((obj && obj.test)())

结果为空值,
想问一下这两个有什么不一样吗,感觉都是先获取到obj.test,而且obj.test是用括号包起来的,然后调用一下,但是两个结果的obj.test调用的结果却是不一眼的,第一个this是obj,第二个是window感觉.

不是很清楚,希望可以得到解惑

阅读 421
3 个回答

其实可以从 AST 的角度去理解,调用表达式被称为 CallExpression

它具有两个子节点,在线查看 AST

  • callee 被调用函数
  • arguments 函数参数

(obj.test)() 的 callee 是 MemberExpression

(obj && obj.test)() 的 callee 是一个 LogicalExpression

当 callee 是 MemberExpression 时,MemberExpression 的 object 也是就 obj 会作为这个 CallExpression 的 this 传递进去,这就是我们熟知的 this 概念

obj.test() ,函数题里的 this 是 obj 。

(obj && obj.test)() ,这个 this 确实不是 obj

后一个大概相当于 let f = obj && obj.test; f()

只有字面上按照 a.b() 的形式调用的,this 才会使 a 。如果被调用的函数时通过其他方式得到的,比如 obj && obj.test 就是用 && 得到的,那么通常就是类似 f() 方式的调用了,非严格模式下 this 会是 window

新手上路,请多包涵

在 JavaScript 中,这两种调用方式的行为不同是因为 this 的绑定规则不同。关键在于 引用丢失(reference loss) 和 this 绑定的隐式丢失。

第一种情况:(obj.test)()
console.log((obj.test)());
// 输出: "222"
这里 (obj.test) 只是对 obj.test 的简单引用,并没有改变 this 的绑定。因此,当直接调用 obj.test() 时,this 仍然指向 obj,所以 this.name 返回 "222"。

第二种情况:(obj && obj.test)()
console.log((obj && obj.test)());
// 输出: undefined(或报错,取决于严格模式)
这里的情况不同:

obj && obj.test 是一个逻辑表达式,它的返回值是 obj.test 方法本身(而不是一个绑定 obj 的方法引用)。
当这个返回值(即 test 方法)被直接调用时,它就变成了一个独立函数调用,而不是方法调用。在非严格模式下,this 会指向 global(浏览器中是 window),而在严格模式下是 undefined。
由于 this 不再指向 obj,this.name 就变成了 undefined(或报错)。
关键区别:
obj.test():this 正确绑定到 obj。
(obj && obj.test)():this 绑定丢失,变成全局对象或 undefined。

如果希望保持 this 绑定,可以:
显式绑定 this:
console.log((obj && obj.test.bind(obj)());
直接调用(避免中间引用):
obj && obj.test();

推荐问题