Royi Namir
Royi Namir

Reputation: 148534

Javascript's Object(this) usages in polyfills?

I saw here the Array.prototype.forEach()'s polyfill and I have a question about its implementation :

/*1*/   if (!Array.prototype.forEach)
/*2*/   {
/*3*/     Array.prototype.forEach = function(fun /*, thisArg */)
/*4*/     {
/*5*/       "use strict";
/*6*/   
/*7*/       if (this === void 0 || this === null)
/*8*/         throw new TypeError();
/*9*/   
/*10*/       var t = Object(this);
/*11*/       var len = t.length >>> 0;
/*12*/       if (typeof fun !== "function")
/*13*/         throw new TypeError();
/*14*/   
/*15*/       var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
/*16*/       for (var i = 0; i < len; i++)
/*17*/       {
/*18*/         if (i in t)
/*19*/           fun.call(thisArg, t[i], i, t);
/*20*/       }
/*21*/     };
/*22*/   }

Looking at line #10 : why did they use Object(this) ?

As I searched its usages I saw this :

The Object constructor creates an object wrapper for the given value. If the value is null or undefined, it will create and return an empty object, otherwise, it will return an object of a type that corresponds to the given value. If the value is an object already, it will return the value.

So they wanted to check if it's null || undefined .

Ok , But they already checked it in lines #7-#8 !

Question :

What is the reason (which I'm missing) that they used Object(this) ?

Upvotes: 5

Views: 229

Answers (3)

Haagenti
Haagenti

Reputation: 8144

I was searching SO for this question because I wandered myself why this was. The currently correct answer left me not at a 100% satisfaction. So I was trying to execute code that would break if I removed the Object(this) and just did var O = this;. The definition of the forEach function can be found here: ECMAScript forEach. So you can see why vars are created and there names etc. The polyfil tries to be exact as it can be.

Since the OP wants to only know about the Object(this) I'll focus on that. I've found a reason why this is needed. Try this code:

Array.prototype.forEach.call("abc", func);

Basiscally we loop over the abc string. The Object(this) makes the abc to:

String {0: "a", 1: "b", 2: "c", length: 3, [[PrimitiveValue]]: "abc"}

If we did not do this abc would be just plain "abc". Then in the loop of the polyfill we do this:

kValue = O[k];

In the case of the "abc" string this would get each letter of the string. But this would not work and would give undefined on IE <8.

Why this doesn't work is explained in this quote for other question: "This notation does not work in IE7. The first code snippet will return undefined in IE7. If you happen to use the bracket notation for strings all over your code and you want to migrate to .charAt(pos), this is a real pain: Brackets are used all over your code and there's no easy way to detect if that's for a string or an array/object." (source)

But when using Object(this) this will be no problem.

Another quote which I've found handy:

"NOTE 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." (source)

Upvotes: 1

Bergi
Bergi

Reputation: 664599

So they wanted to check if it's null || undefined.

No. The purpose is

The Object constructor creates an object wrapper for the given value. If the value is null or undefined, it will create and return an empty object, otherwise, it will return an object of a type that corresponds to the given value. If the value is an object already, it will return the value.

It will convert primitives to objects, like "foo" to new String("foo").

This is strictly not really necessary, because the property access of .length and the indices would convert a primitive to an object anyway. It would make a difference however when the third callback parameter is used, you can expect an object there (and the same one for each invocation).

What is the reason that they used Object(this)?

Mostly to reproduce step #1 of the forEach spec, which is to call ToObject on the argument. In the spec this is required to be able to use [[Get]] and [[HasProperty]] internal methods on it.

Also, by converting a primitive to an object only once you gain speed.

Upvotes: 3

Niet the Dark Absol
Niet the Dark Absol

Reputation: 324650

The purpose is not to check for undefined-ness. Rather, it is to handle primitives.

For instance, if this were a number such as 42, then it would be Object(42), which is a Number object with a value of 42.

This means you can then treat the input like an object from then on, even if it was a primitive originally.

To test, try using the polyfill on a string - strings have a .length property. As an example, try...

[].forEach.call("Hello!",function(letter) {console.log(letter);});

Upvotes: 0

Related Questions