Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call Object.prototype method on Global Scope

Tags:

javascript

This code throws an error.

try {
  alert(hasOwnProperty('window'));
} catch(e) {
  alert(e); // Type Error : can't convert undefined to object
}

but this code doesn't throws an error.

try {
  alert(this.hasOwnProperty('window')); // true (if on browser)
} catch(e) {
  // through catch block
  alert(e);
}

Live Example | Live Source

As far as I kwon, func(arg) is equal to this.func(arg) if this is Global Object. Why does such a thing happen?

like image 915
Hoshinoma Araki Avatar asked Jul 06 '13 08:07

Hoshinoma Araki


1 Answers

I think what's going on is that we have an interaction of strict mode code and non-strict mode code. And in fact, when I dug up a copy of Firefox 3.6.15 (which doesn't support strict mode), I don't get the error using the link I posted to your question (it alerts "true" twice).

The code you've shown is, obviously, non-strict mode code. But what about the hasOwnProperty implementation of the browser? I suspect it's strict, on browsers where strict mode is supported.

When you say

func();

...what the browser does is look up func using standard identifier resolution, and then call it as though you did this:

func.call(undefined);

If func is a loose-mode function, then within the call to func, this is the global object. But, if func is a strict-mode function, this within the call is undefined.

In contrast, with this:

this.func();

...it again looks up func (this time via property resolution using the prototype chain) and then effectively does this:

this.func.call(this);

In either strict or loose mode, that means this within the function will be this. (And of course, at global scope, this is the global object.)

Here's an example of this interaction using code we can see rather than hasOwnProperty:

(function() {
  "use strict";

  window.strictFunction = function() {
    display("strictFunction: this === window? " +
            (this === window));
    display("strictFunction: typeof this: " +
            typeof this);
  };

})();

strictFunction();
strictFunction.call(undefined);

As you can see, that's loose code except for the bit defining a strictFunction function on window. Then we call that function twice from loose code. The result is this:

strictFunction: this === window? false
strictFunction: typeof this: undefined
strictFunction: this === window? false
strictFunction: typeof this: undefined

In contrast, if we do that with a loose function, the result is:

looseFunction: this === window? true
looseFunction: typeof this: object
looseFunction: this === window? true
looseFunction: typeof this: object

Complete example: Live Copy | Live Source

<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Fun With Strict Interactions</title>
  <style>
    body {
      font-family: sans-serif;
    }
    p {
      margin: 0;
    }
  </style>
</head>
<body>
  <script>
    (function() {
      "use strict";

      window.strictFunction = function() {
        display("strictFunction: this === window? " +
                (this === window));
        display("strictFunction: typeof this: " +
                typeof this);
      };

    })();
    (function() {

      window.looseFunction = function() {
        display("looseFunction: this === window? " +
                (this === window));
        display("looseFunction: typeof this: " +
                typeof this);
      };

    })();

    display("Direct call:");
    strictFunction();
    looseFunction();

    display("<hr>Call with <code>.call(undefined)</code>:");
    strictFunction.call(undefined);
    looseFunction.call(undefined);

    display("<hr>Call with <code>.call(window)</code>:");
    strictFunction.call(window);
    looseFunction.call(window);

    function display(msg) {
      var p = document.createElement('p');
      p.innerHTML = String(msg);
      document.body.appendChild(p);
    }
  </script>
</body>
</html>

Output (using a JavaScript engine that supports strict mode):

Direct call:
strictFunction: this === window? false
strictFunction: typeof this: undefined
looseFunction: this === window? true
looseFunction: typeof this: object
--
Call with .call(undefined):
strictFunction: this === window? false
strictFunction: typeof this: undefined
looseFunction: this === window? true
looseFunction: typeof this: object
--
Call with .call(window):
strictFunction: this === window? true
strictFunction: typeof this: object
looseFunction: this === window? true
looseFunction: typeof this: object
like image 186
T.J. Crowder Avatar answered Nov 06 '22 10:11

T.J. Crowder