这是我的测试代码:
var foo = { bar: function(){ console.log('this:' + this); } } console.log('calling foo.bar()'); foo.bar(); console.log('\ncalling (foo.bar)()'); (foo.bar)(); console.log('\ncalling f=foo; f.bar()'); f = foo; f.bar(); console.log('\ncalling f=foo.bar; f()'); f = foo.bar; f(); console.log('\ncalling (f=foo.bar)()'); (f = foo.bar)();
这就是结果:
calling foo.bar() this:[object Object] calling (foo.bar)() this:[object Object] calling f=foo; f.bar() this:[object Object] calling f=foo.bar; f() this:[object global] calling (f=foo.bar)() this:[object global]
我的问题是,为什么f = foo.bar; F();和(f = foo.bar)();将’this’赋值为全局对象
解决方法
参考类型由两个组件(ECMAScript 3)组成,基础对象和属性名称(在ECMAScript 5中,它具有第三个组件严格标志 – 稍后将讨论严格模式(1)).
当一个function is invoked时,通过获取引用的基础对象(通过内部GetBase操作)来隐式地确定该值.
例如,在foo.bar引用中,基础对象为foo,属性名称为“bar”:
foo.bar(); // `this` will point to `foo`
执行作业时,引用将丢失,我们不再有基础对象和属性名称,我们只有一个值:
(f=foo.bar)(); // `this` will point to the global object
它不会发生只有分配,它发生与使用内部GetValue
操作的其他操作:
// `this` will point to the global object (0,foo.bar)(); // The Comma Operator (0 || foo.bar)(); // Binary Logical Operators (1 && foo.bar)(); // etc..
例如,如果您用括号(正式称为The Grouping Operator)包围引用,则不会发生:
(foo.bar)(); // `this` will point to `foo`
分组运算符(再次,圆括号;)不在内部使用GetValue,这是以这种方式设计的,因为typeof和delete运算符被允许使用括号表达式:
delete (foo.bar);
如果分组运算符使用GetValue,那么delete运算符将无法获取一个基础对象去除属性栏.
回到隐含的这个值,有一些棘手的例子,例如使用with语句:
with (foo) { bar(); // `this` will refer to `foo` ! }
你可以看到,调用bar();在with块中,仍然将该值绑定到foo对象.
这个值不是从引用中设置的,它来自当前的环境记录(我可能稍后会写一些关于它的内容),由with语句引入.
另外,您可能会注意到,对于非引用,该值将指向全局对象,例如:
(function () {})(); // this will point to the global object
我们只是一个值,而不是一个引用(记住,一个引用是一个已解析的名称绑定).
(1)注意:在ECMAScript 5严格模式下,对于所有这些值隐含设置为全局对象的情况,该值将不被定义.
这是作为一种安全措施,主要是由于人们经常忘记在调用构造函数时使用新的运算符,从而对全局范围造成不良的行为和污染.
例如:
function Foo () { this.prop = 'foo'; } Foo(); // no `new` operator,boom!
如你所知,Foo引用没有直接的基础对象,这将指向全局对象,它将无意中创建一个属性.
在严格的模式下,代码只会给你一个TypeError,因为这个值将是未定义的.
同样你可能记得在一开始我提到参考类型有一个第三个组件,严格的标志.
你原来的例子:
(f=foo.bar)();
可能甚至不会在严格模式下工作,因为未声明的标识符(如f似乎)的分配是不允许的(另一种安全措施来避免全球对象污染).
更多信息: