首先,看一段代码:
var obj = { foo: function() { if( this === obj ) { console.log('this is the obj !'); } else { console.log('this is not the obj !'); } }}
用不同的方式调用obj的foo方法有不同的结果:
obj.foo(); // this is obj !(obj.foo)(); // this is obj !(0 || obj.foo)(); // this is not obj !
第一种情况,obj.foo(); 无需解释。
最后一种情况也很好解释,(0 || obj.foo)(); 的第一个括号内是一个逻辑运算表达式,按照 || 运算符的处理逻辑,该表达式返回的是 obj.foo 的求值结果。而 obj.foo 的求值结果是一个纯粹的函数值,因而在函数值被调用的执行环境中,this 肯定不是原来的 obj 。
但第二种情况比较费解!
按直觉,(obj.foo) 应该会返回一个纯粹的函数值,因此在该函数值被调用的执行环境中,this 应该不是原来的 obj 。但实际情况并非如此!为什么会这样?
这得从 JavaScript 的成员表达式以及括号()在表达式中的作用说起。
首先,一定要明白 obj.foo 是一个成员表达式,其值包含对象 obj 和属性 foo 两部分。成员表达式的值同时也是一个左值,即,其可以用在赋值表达式的左侧,是可以被赋值的。
当左值参与表达式运算时,会触发左值求值操作,从而将其转换成一般值(俗称,右值)之后才参与运算。这就是为什么 (0 || obj.foo) 中的 || 运算符会触发将 obj.foo 求值为函数值的缘故。
但在 (obj.foo) 表达式中,括号内并没有任何运算符,所以 obj.foo 依旧是成员表达式的值。
那么,包裹表达式的括号 ( ) 会触发对其包裹的表达式进行求值操作吗?
答案是:包裹表达式的括号 ( ) 不会触发求值操作!
因此,( obj.foo ) 依旧返回了成员表达式的值,对其发起调用操作,就是基于对象 obj 和 属性 foo 发起调用,所以其调用执行环境中的 obj 就是 this !
关于 表达式中的括号 () 不触发求值,还可以用以下代码证明:
var x;(x) = 123; // 这是正确的写法,并能正确运行!
上面代码中,x 是一个变量,也是一个左值。如果 (x) 的括号触发了对 x 的求值,其结果一定是 undefined 值,而不再是左值,就不能赋值,上面的代码就会出错。而以上代码运行完全正确,因此可以证明:表达式的括号一定不会求值。
其实,表达式中的括号 () 除了在函数后面用作调用运算符之外,其他情况根本就不是一个运算符,仅仅用于提升运算关系的优先级!