Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
128 views
in Technique[技术] by (71.8m points)

Cases where 'this' is the global Object in Javascript

I'm playing around with different ways to call a function that is an attribute of an Object in Javascript and looking at which type of calls set 'this' to be the Object and which set 'this' to be the Global Object.

This is my test code:

var foo = {
  bar: function(){ 
    console.log('this:' + this);
  }
}

console.log('calling foo.bar()');
foo.bar();

console.log('
calling (foo.bar)()');
(foo.bar)();

console.log('
calling f=foo; f.bar()');
f = foo; f.bar();

console.log('
calling f=foo.bar; f()');
f = foo.bar; f();

console.log('
calling (f=foo.bar)()');
(f = foo.bar)();

And this is the result:

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]

My question is, why does f=foo.bar; f(); and (f=foo.bar)(); assign 'this' to be the Global Object

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The key of knowing the implicit behavior of this on function calls, relies on knowing how the Reference Type woks.

The Reference Type consists of two components (in ECMAScript 3), the base object and the property name (in ECMAScript 5, it has a third component the strict flag -I will talk about strict mode later- (1)).

When a function is invoked, the this value is determined implicitly by getting the base object of the reference (by the internal GetBase operation).

For example, in the foo.bar reference, the base object is foo and the property name is "bar":

foo.bar(); // `this` will point to `foo`

When you do an assignment, the reference is lost, we don't have anymore a base object and a property name, we just have a value:

(f=foo.bar)(); // `this` will point to the global object

It doesn't happen only with assignments, it happens with other operations that use the internal GetValue operation:

// `this` will point to the global object    
(0, foo.bar)();   // The Comma Operator
(0 || foo.bar)(); // Binary Logical Operators
(1 && foo.bar)();
// etc..

It doesn't happens for example if you surround a reference with parentheses (formally called The Grouping Operator):

(foo.bar)(); // `this` will point to `foo`

The Grouping Operator (again, the parentheses ;) doesn't use GetValue internally, this was designed in that way because the typeof and delete operators are allowed to work with parenthesised expressions:

delete (foo.bar);

If the grouping operator used GetValue, the delete operator would be unable to get a base object where to remove the property bar.

Returning to the implicit this value, there are some tricky cases, for example, using the with statement:

with (foo) {
  bar(); // `this` will refer to `foo` !
}

As you can see, invoking bar(); inside the with block, will still bind the this value to the foo object.

The this value isn't set from the reference, it comes from the current Environment Record (I'll maybe write something about it later) introduced by the with statement.

Also, as you may note, for non-references, the this value will point to the global object, for example:

(function () {})(); // this will point to the global object

We just have a value, not a reference (Remember, a reference is a resolved name binding).


(1)NOTE: On ECMAScript 5 Strict Mode, the this value will be undefined for all the cases described where the this value is set implicitly to the Global object.

This was made as a security measure mostly due the fact that people often forgot to use the new operator when invoking constructor functions, causing a bad behavior and pollution on the global scope.

For example:

function Foo () {
  this.prop = 'foo';
}
Foo(); // no `new` operator, boom!

As you known now, the Foo reference doesn't have a direct base object, this will point to the global object and it will unintentionally create a property on it.

On Strict Mode, the code would simply give you a TypeError, because the this value would be undefined.

Also as you may remember in the beginning I mentioned that the Reference Type has a third component, the strict flag.

Your original example:

(f=foo.bar)();

Would probably not even work on Strict Mode, because assignments to undeclared identifiers (such as f seems) are disallowed, (another security measure to avoid global object pollution).

More info:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...