最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Function context ("this") in nested functions - Stack Overflow

programmeradmin0浏览0评论

When you invoke a top-level function in Javascript, the this keyword inside the function refers to the default object (window if in a browser). My understanding is that it's a special case of invoking the function as method, because by default it is invoked on window (as explained in John Resig's book, Secrets of the JavaScript Ninja, page 49). And indeed both invocations in the following code are identical.

function func() {
  return this;
}

// invoke as a top-level function
console.log(func() === window); // true

// invoke as a method of window
console.log(window.func() === window); // true

So far so good... Now here is the part I don't understand:

When a function is nested in another function and invoked without specifying an object to invoke on, the this keyword inside the function also refers to window. But the inner function cannot be invoked on window (see code below).

function outerFunc() {
  function innerFunc() {
    return this;
  }

  // invoke without window.* - OK
  console.log(innerFunc() === window); // true

  // invoke on window
  //window.innerFunc(); - error (window has no such method)
  console.log(window.innerFunc) // undefined
}

outerFunc();

It makes perfect sense that the nested function isn't available on window, as it is after all nested... But then I don't understand why the this keyword refers to window, as if the function was invoked on window. What am I missing here?

EDIT

Here is a summary of the great answers below and some of my follow up research.

  • It is incorrect to say that invoking a function "normally" is the same as invoking it as a method of window. This is only correct if the function is defined globally.

  • The function context (the value of the this keyword) does not depend on where / how the function is defined, but on how it is being invoked.

  • Assuming that the code is not running in in strict mode, Invoking a function "normally" will have the function context set to to window (when running in a browser, or the corresponding global object in other environments).

  • An exception to the above rules is the use of bind to create a function. In this case even if the function is invoked "normally", it could have a context other than window. That is, in this case the context is determined by how you create the function, rather than how you invoke it. Although strictly speaking this isn't accurate, because bind creates a new function that internally invokes the given function using apply. The context of that new function will still be determined by the way it's invoked, but it shields the context of the function it internally invokes by using apply.

By invoking "normally" I refer to the following simple way of invocation:

myFunction();

To plete the picture, here is a brief coverage of other ways of invocation and the corresponding context:

  • As a property of an object (method) - the context is the object

  • Using apply or call - the context is specified explicitly

  • With the new operator (as a constructor) - the context is a newly created object

Feel free to update the above as necessary, for the benefit of people with similar questions. Thanks!

When you invoke a top-level function in Javascript, the this keyword inside the function refers to the default object (window if in a browser). My understanding is that it's a special case of invoking the function as method, because by default it is invoked on window (as explained in John Resig's book, Secrets of the JavaScript Ninja, page 49). And indeed both invocations in the following code are identical.

function func() {
  return this;
}

// invoke as a top-level function
console.log(func() === window); // true

// invoke as a method of window
console.log(window.func() === window); // true

So far so good... Now here is the part I don't understand:

When a function is nested in another function and invoked without specifying an object to invoke on, the this keyword inside the function also refers to window. But the inner function cannot be invoked on window (see code below).

function outerFunc() {
  function innerFunc() {
    return this;
  }

  // invoke without window.* - OK
  console.log(innerFunc() === window); // true

  // invoke on window
  //window.innerFunc(); - error (window has no such method)
  console.log(window.innerFunc) // undefined
}

outerFunc();

It makes perfect sense that the nested function isn't available on window, as it is after all nested... But then I don't understand why the this keyword refers to window, as if the function was invoked on window. What am I missing here?

EDIT

Here is a summary of the great answers below and some of my follow up research.

  • It is incorrect to say that invoking a function "normally" is the same as invoking it as a method of window. This is only correct if the function is defined globally.

  • The function context (the value of the this keyword) does not depend on where / how the function is defined, but on how it is being invoked.

  • Assuming that the code is not running in in strict mode, Invoking a function "normally" will have the function context set to to window (when running in a browser, or the corresponding global object in other environments).

  • An exception to the above rules is the use of bind to create a function. In this case even if the function is invoked "normally", it could have a context other than window. That is, in this case the context is determined by how you create the function, rather than how you invoke it. Although strictly speaking this isn't accurate, because bind creates a new function that internally invokes the given function using apply. The context of that new function will still be determined by the way it's invoked, but it shields the context of the function it internally invokes by using apply.

By invoking "normally" I refer to the following simple way of invocation:

myFunction();

To plete the picture, here is a brief coverage of other ways of invocation and the corresponding context:

  • As a property of an object (method) - the context is the object

  • Using apply or call - the context is specified explicitly

  • With the new operator (as a constructor) - the context is a newly created object

Feel free to update the above as necessary, for the benefit of people with similar questions. Thanks!

Share Improve this question edited Feb 4, 2013 at 16:36 M.S asked Feb 3, 2013 at 21:42 M.SM.S 1,6101 gold badge16 silver badges22 bronze badges 1
  • 3 The explanation is ok, but it's only an approximation. Whenever a function is invoked "normally", this refers to the global object (window), unless the function is in strict mode. That's just how it is. See developer.mozilla/en-US/docs/JavaScript/Reference/Operators/…. – Felix Kling Commented Feb 3, 2013 at 21:46
Add a ment  | 

5 Answers 5

Reset to default 1

You can call any function that is in scope with functionName(). Since you haven't called it on an object, it will be called in the context of the default object (window). (IIRC, it will be called in the context of undefined if you are in strict mode).

The default object for context has nothing to do with where a function is defined or what scope that function appears in. It is simply the default object.

If a function is a property of an object, you can call it as reference.to.object.function(), and it will be called in the context of object instead of the default object.

Other things that change the context are the new keyword and the apply, call, and bind methods.

In JavaScript, when a function is invoked without an explicit context, the context is the global object. In the case of web browsers, the global object is window.

Additionally, JavaScript has functional scope, so any variables or functions within a function are not accessible in a scope outside of that function. This is why you can't access window.innerFunc.

Whether a function is nested inside another one has nothing to do with the value of this when the function is called. The only things that matter are:

  • If the function is "found" by traversing a property on an object, then the value of this will be a reference to that object:

    someObject.prop( whatever );
    

    It doesn't matter how the function was declared.

  • If you use call() or apply() to invoke a function, then the value of this is taken from the first argument to whichever of those functions you use.

  • If you've created a bound wrapper for the function with bind(), then the value of this will be as requested when bind() was called.

  • If you're calling a function as a constructor with new, then this will refer to the newly-created object instance.

  • Otherwise, this is either a reference to the global context, or else it's undefined (in "strict" mode or in an ES5-pliant runtime).

The "location" in the code where a function is defined does matter, of course, in that the scope includes whatever symbols it includes, and those are available to the function regardless of how a reference to it is obtained.

It does not depend where the function is declared but how it is called:

var obj = {
   f: function() {
       return this;
   }
}

var f = obj.f;

console.log(obj.f()) // obj
console.log(f()) // window/default obj

Or in other words. The syntax obj.f() executes the function with this=obj while f() executes the function with this=window. In JavaScript the caller specifies the value of this.

When you define func in the global scope, it actually is assigned as a property of the window object. That is, the window object holds all globally scoped variables. (*) Therefore, func and window.func represent the same thing. innerFunc is defined inside a function scope and is not available outside of that scope. Therefore, window.innerFunc is (still) undefined.

However, the this context is determined by how you call the function. When you call a method like obj.method(), the this context is set to obj. On the other hand, you can also call the method on its own:

var f = obj.func;
f(); // in this call: this === window

In this case, you're not calling a function on an object and thus the this context is set to the default. The default however is the global scope and as stated above, this is represented by window.

You can always override the this context by using Function.prototype.call() or Function.prototype.apply() which take a this context as first argument. For example:

var f = obj.func;
f.call(obj); // in this call: this == obj

(*) Note that this only applies to JavaScript running inside a browser. In other environments, this may differ. For example, in Node.js the GLOBAL variable holds the global scope.

发布评论

评论列表(0)

  1. 暂无评论