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

javascript - What's the point of "var t = Object(this)" in the official implementation of forEach? - S

programmeradmin1浏览0评论

According to the MDC, the ECMA-262, 5th edition gives the implementation of forEach as:

if (!Array.prototype.forEach)
{
  Array.prototype.forEach = function(fun /*, thisp */)
  {
    "use strict";

    if (this === void 0 || this === null)
      throw new TypeError();

    var t = Object(this);
    var len = t.length >>> 0;
    if (typeof fun !== "function")
      throw new TypeError();

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in t)
      fun.call(thisp, t[i], i, t);
    }
  };
}

Can anyone tell me what the line "var t = Object(this)" is doing? How does Object(this) differ from plain this? And what work is that difference doing here?

According to the MDC, the ECMA-262, 5th edition gives the implementation of forEach as:

if (!Array.prototype.forEach)
{
  Array.prototype.forEach = function(fun /*, thisp */)
  {
    "use strict";

    if (this === void 0 || this === null)
      throw new TypeError();

    var t = Object(this);
    var len = t.length >>> 0;
    if (typeof fun !== "function")
      throw new TypeError();

    var thisp = arguments[1];
    for (var i = 0; i < len; i++)
    {
      if (i in t)
      fun.call(thisp, t[i], i, t);
    }
  };
}

Can anyone tell me what the line "var t = Object(this)" is doing? How does Object(this) differ from plain this? And what work is that difference doing here?

Share Improve this question asked Jun 27, 2011 at 20:21 zjmillerzjmiller 2,7876 gold badges30 silver badges30 bronze badges 9
  • Never seen void 0 used like that before either... – Blindy Commented Jun 27, 2011 at 20:27
  • 1 this === void 0 || this === null could be written like so: this == null... same thing. – Šime Vidas Commented Jun 27, 2011 at 20:28
  • 1 I guess so that if you call Array.prototype.forEach.apply(123, function() {}) it does not throw an error, because the in would normally not be defined for numbers. – pimvdb Commented Jun 27, 2011 at 20:30
  • 1 @cwolves No, it doesn't. value == null evaluates to true only for the cases where value is undefined or null, and not for all falsy values. – Šime Vidas Commented Jun 27, 2011 at 20:32
  • @Šime Vidas - Apologies, you're correct :) – user578895 Commented Jun 27, 2011 at 20:34
 |  Show 4 more comments

3 Answers 3

Reset to default 17

The Mozilla implementations just try to emulate exactly the steps that are described in the specification, Object(this); emulates the first step, calling the ToObject internal method:

From Array.prototype.forEach 15.4.4.18:

....

When the forEach method is called with one or two arguments, the following steps are taken:

  1. Let O be the result of calling ToObject passing the this value as the argument.

  2. Let lenValue be the result of calling the [[Get]] internal method of O with the argument "length".

  3. Let len be ToUint32(lenValue).

....

Calling the Object constructor as a function behind the scenes it performs type conversion, internally as described in 15.2.1.1 the ToObject method is called.

There are more things like this if you look carefully, for example, the line:

var len = t.length >>> 0;

They are emulating a call to the ToUint32 internal method, as described in the step 3, using the unsigned right shift operator (>>>).

Edit: The previous lines answer why the Mozilla implementation does it in this way.

You might wonder why the ECMAScript spec. needs to call ToObject, check back the Step 2, and it will start to seem obvious:

  1. Let lenValue be the result of calling the [[Get]] internal method of O with the argument "length".

The spec. needs to ensure that the this value used when the function is called is an object, because primitive values don't have any internal methods, an as you can see on the step 2, the [[Get]](P) internal method is needed, to get the value of the length property.

This is done because for strict functions (and also for built-in functions), you can set primitive values as the function's this value, e.g.:

(function () {"use strict"; return typeof this; }).call(5); // "number"

While for non-strict functions, the this value is always converted to Object:

(function () { return typeof this; }).call(5); // "object"

The probable reason is s9.9 of ECMA-262, about the abstract ToObject operation (as mentioned by @CMS).

When called on null or an undefined value it forces the throwing of a TypeError, but those are already trapped by the previous lines.

However if you were to call:

Array.prototype.forEach.call("123", func() { ... } )

this would fail if it weren't for the type coercion. In particular you can't call index in this if this is a string, but you can call it on the result of ToObject.

This text from 15.4.4.18 is probably relevant:

The forEach function is intentionally generic; it does not require that its this value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the forEach function can be applied successfully to a host object is implementation-dependent.

I write var t = this; all the time. I find that the scope of this is sometimes browser-dependent; in any case it's not always clear what a browser is going to do with the keyword this as scope changes, especially in method closures. I like to dumb down my JS code to a kindergarten level to leave minimal room for individual browsers to do wonky things.

To ensure that I'm always dealing with the this I want to be dealing with when passing this to a method or something, I always write var t = this; as the first line of my method. Then, t is a variable and obeys predictable variable scope rules, and its pointer is assigned at assignment time to the object denoted by this at that time. This way I don't have to worry about a method, other object, or noncompliant browser reinterpreting what this refers to in scope.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论