jcamejo
jcamejo

Reputation: 95

What's the purpose of Object(this) in javascript?

Studying the polyfill for the find method written on the MDN web docs, there's a particular line that I'm not following, let me share the code

if (!Array.prototype.find) {
  Object.defineProperty(Array.prototype, 'find', {
    value: function(predicate) {
      if (this == null) {
        throw TypeError('"this" is null or not defined');
      }

      var o = Object(this);

      var len = o.length >>> 0;

      if (typeof predicate !== 'function') {
        throw TypeError('predicate must be a function');
      }

      var thisArg = arguments[1];

      var k = 0;

      while (k < len) {
        var kValue = o[k];
        if (predicate.call(thisArg, kValue, k, o)) {
          return kValue;
        }
        k++;
      }

      return undefined;
    },
    configurable: true,
    writable: true
  });
}

My question is with the expression var o = Object(this);. What's the purpose of doing so instead of doing var o = this?. Printing the value in both described cases returns the same object.

Is this an abbreviated way of calling var o = new Object(this);?

I have removed the comments from the method to shorten the text, here's the link to the polyfill implementation.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find#Polyfill

Thanks!

Upvotes: 2

Views: 59

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1074168

In strict mode, this isn't always an object. Object(this) makes sure that o is an object, not a primitive.

Here's an example of this being a primitive:

"use strict";

Object.defineProperty(String.prototype, "nishCap", {
    writable: true,
    configurable: true,
    value() {
        console.log(typeof this); // "string"
        const o = Object(this);
        console.log(typeof o);    // "object"
        return o.substring(0,1).toUpperCase() + o.substring(1);
    }
});
const capped = "foo".nishCap();

Note that this even applies to array methods, because you can call them on non-arrays, like Array.prototype.find.call("foo", ch => ch === "o").

Is this an abbreviated way of calling var o = new Object(this);?

No, new Object always creates a new object (and doesn't use the argument you give it). When you call Object as a function, it coerces its argument to object. So a primitive string becomes a String object, a primitive number becomes a Number object, etc.

What's the purpose of doing so instead of doing var o = this?

That polyfill is fairly closely following the spec, which starts out with:

  1. Let O be ? ToObject(this value).

In most cases it wouldn't be important, but I wouldn't be surprised if there were some edge case where leaving it out would cause observable behavior at variance with the spec.

Upvotes: 2

CertainPerformance
CertainPerformance

Reputation: 370689

Because Array.prototype.find can be called with a this value which is not an object. See the specification:

When the find method is called, the following steps are taken:

  1. Let O be ? ToObject(this value).

So, in order to be perfectly compliant with the specification, the polyfill needs Object(this). Otherwise, the implementations will not be the same, as you can see by the following two snippets:

'use strict';
const str = 'abc';
Array.prototype.find.call(
  str,
  (char, i, theThis) => {
    // theThis should be an object, even if called on a non-object:
    console.log(theThis);
  }
);

'use strict';
Object.defineProperty(Array.prototype, 'find', {
  value: function(predicate) {
    if (this == null) {
      throw TypeError('"this" is null or not defined');
    }

    // Without object wrapping:
    var o = this; // <-------------------------------

    var len = o.length >>> 0;

    if (typeof predicate !== 'function') {
      throw TypeError('predicate must be a function');
    }

    var thisArg = arguments[1];

    var k = 0;

    while (k < len) {
      var kValue = o[k];
      if (predicate.call(thisArg, kValue, k, o)) {
        return kValue;
      }
      k++;
    }

    return undefined;
  },
  configurable: true,
  writable: true
});


const str = 'abc';
Array.prototype.find.call(
  str,
  (char, i, theThis) => {
    // The polyfill above had no object wrapping, so this result is not the same:
    console.log(theThis);
  }
);

Upvotes: 1

Related Questions