4

I was going through the Array'a find polyfill implementation on MDN:

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

Copy pasting it below:

  // https://tc39.github.io/ecma262/#sec-array.prototype.find
  if (!Array.prototype.find) {
    Object.defineProperty(Array.prototype, 'find', {
      value: function(predicate) {
       // 1. Let O be ? ToObject(this value).
        if (this == null) {
          throw new TypeError('"this" is null or not defined');
        }

        var o = Object(this);

        // 2. Let len be ? ToLength(? Get(O, "length")).
        var len = o.length >>> 0;

        // 3. If IsCallable(predicate) is false, throw a TypeError exception.
        if (typeof predicate !== 'function') {
          throw new TypeError('predicate must be a function');
        }

        // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
        var thisArg = arguments[1];

        // 5. Let k be 0.
        var k = 0;

        // 6. Repeat, while k < len
        while (k < len) {
          // a. Let Pk be ! ToString(k).
          // b. Let kValue be ? Get(O, Pk).
          // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
          // d. If testResult is true, return kValue.
          var kValue = o[k];
          if (predicate.call(thisArg, kValue, k, o)) {
            return kValue;
          }
          // e. Increase k by 1.
          k++;
        }

        // 7. Return undefined.
        return undefined;
      }
    });
  }

Why is it that we need to do var o = Object(this);?

What does Object(this) achieve?

Thanks for any discussion.

bits
  • 8,110
  • 8
  • 46
  • 55
  • The point of doing that is to deal with the cases when `this` is a primitive. To convert it into an object. – Thomas May 19 '17 at 22:19
  • Agreed. Including values like null. ["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."](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) – Will May 19 '17 at 22:21
  • @Will, `null` and `undefined` values are already dealt with here: `if (this == null) throw ...`. The `o = Object(this)` part is only interesting when `this` is a string – Thomas May 19 '17 at 22:22
  • I was thinking along the lines of `this` being a primitive and `Object(this)` converting it into an `Object` too, but could `this` actually be a primitive? How would a primitive call the polyfill? Even if it could, wouldn't it have a length of `undefined` and the rest of the code still function? `1.length` throws a Syntax error, but `var num = 1; num.length` does not throw an error. – Arthur Weborg May 19 '17 at 22:44
  • @Thomas, agreed. I was making a more general comment. The question does not specify whether it pertains only to usage in the context of this particular code or more generally. – Will May 22 '17 at 13:51
  • @ArthurWeborg, `'use strict'; YourClass.prototype.method.call("lorem ipsum")` in strict mode `"lorem ipsum"` won't be converted into a instance of `String`; and `this` will be a primitive. And especially `Array.prototype.method.call(...)` *(on something "arraylike")* is a pretty common theme. – Thomas May 22 '17 at 14:19

2 Answers2

5

In strict mode, a primitive this will not be coerced to an object.

Therefore, explicit coercion to an object using Object(this) is necessary.

Here is a more detailed example:

const array = Array.prototype;

Object.defineProperty(array, 'foo1', { value() { 
  return this.length >>> 0;  }});

Object.defineProperty(array, 'foo2', { value() { 
  "use strict"; 
  return this.length >>> 0; }});

console.log(Array.prototype.foo1.call(undefined));
console.log(Array.prototype.foo2.call(undefined));

The first example runs successfully with a result of 0, because the argument undefined is coerced to an object in non-strict mode. The second example fails, because undefined is not coerced in non-strict mode, and thus this.length errors out.

From MDN:

the value passed as this to a function in strict mode is not forced into being an object (a.k.a. "boxed")

However, in this case, there is already an explicit check for null or undefined:

if (this == null) {
  throw new TypeError('"this" is null or not defined');
}

so I am tempted to say the explicit casting to an object is not necessary. It is probably used here out of an abundance of caution, or as boilerplate.

4

This is a fascinating question... Thanks for your post!

To be honest, I am a bit surprised by Object(this) because JavaScript seems to coerce anything to object (using wrappers) in situations where this could potentially be a primitive value.

If we try to change this with Function.prototype.bind(), JavaScript always returns an object (a function is an object):

var foo = function () {
  console.log(this, typeof this);
}.bind('foo');

var bar = function () {
  console.log(this, typeof this);
}.bind(1337);

var baz = function () {
  console.log(this, typeof this);
}.bind(false);

var qux = function () {
  console.log(this, typeof this);
}.bind(NaN);

var quux = function () {
  console.log(this, typeof this);
}.bind(undefined);

var corge = function () {
  console.log(this, typeof this);
}.bind(null);

var grault = function () {
  console.log(this, typeof this);
}.bind([]);

var garply = function () {
  console.log(this, typeof this);
}.bind({});

var waldo = function () {
  console.log(this, typeof this);
}.bind(/regex/);

var fred = function () {
  console.log(this, typeof this);
}.bind(function () {});

foo(); // String { 0: "f", 1: "o", 2: "o" } object
bar(); // Number { 1337 } object
baz(); // Boolean { false } object
qux(); // Number { NaN } object
quux(); // Window object
corge(); // Window object
grault(); // Array [ ] object
garply(); // Object { } object
waldo(); // /regex/ object
fred(); // function fred<() function   

If we try to change this with Function.prototype.call() or Function.prototype.apply(), once again, JavaScript always returns an object:

Array.prototype.foo = function () {
  console.log(this, typeof this);
};

['foo'].foo(); // Array [ "foo" ] object
Array.prototype.foo.call('bar'); // String { 0: "b", 1: "a", 2: "r"} object
Array.prototype.foo.call(42); // Number { 42 } object
Array.prototype.foo.call(true); // Boolean { true } object
Array.prototype.foo.call(NaN); // Number { NaN } object
Array.prototype.foo.call(undefined); // Window object
Array.prototype.foo.call(null); // Window object
Array.prototype.foo.call({}); // Object { } object
Array.prototype.foo.call(/regex/); // /regex/ object
Array.prototype.foo.call(function () {}); // function () function

In JavaScript, we know that native objects may be useful for type conversion when they are not used as constructors but as regular functions. Number, String and Boolean are quite convenient:

var num = 1337,
    str = '',
    bool = true;
    
console.log(Number(str), typeof Number(str));
console.log(Number(bool), typeof Number(bool));

console.log(String(num), typeof String(num));
console.log(String(bool), typeof String(bool));

console.log(Boolean(num), typeof Boolean(num))
console.log(Boolean(str), typeof Boolean(str));

For the record, here is what we get with explicit conversions through Object():

console.log(typeof Object(false), Object(false) instanceof Boolean);
console.log(typeof Object('bar'), Object('bar') instanceof String);
console.log(typeof Object(42), Object(42) instanceof Number);
console.log(typeof Object(NaN), Object(NaN) instanceof Number);
console.log(typeof Object(undefined), Object(undefined) instanceof Object);
console.log(typeof Object(null), Object(null) instanceof Object);
console.log(typeof Object(['foo']), Object(['foo']) instanceof Array);
console.log(typeof Object({}), Object({}) instanceof Object);
console.log(typeof Object(/regex/), Object(/regex/) instanceof RegExp);
console.log(typeof Object(function () {}), Object(function () {}) instanceof Function);

Now it is obvious that Object(this) can be used to convert any primitive value for this and get a wrapper object instead. If this is already an object, it has no effect:

var obj1 = {baz: 'Baz'},
    obj2 = Object(obj1);

var arr1 = ['foo', 'bar'],
    arr2 = Object(arr1);
    
var reg1 = /regex/,
    reg2 = Object(reg1);
    
var fun1 = function () { return 'Hello!'; },
    fun2 = Object(fun1);
    
console.log(arr1 === arr2);
console.log(obj1 === obj2);
console.log(reg1 === reg2);
console.log(fun1 === fun2);

Moreover, Object is weird because it acts in the same way, whether it is called with new or not:

var foo = Object('foo'),
    bar = new Object('bar');
    
console.log(foo);
console.log(bar);

I may be wrong, but my conclusion is the following: given that this is always coerced to an object, Object(this) is not necessary. However, it indicates explicitly what happens implicitly to avoid ambiguity and improve code comprehension.

What do you think?


EDIT

torazaburo is right: strict mode is the key! When functions are in strict mode, primitive values for this are not coerced! This is probably the most reasonable explanation for explicit conversion with Object(this)...

Function.prototype.bind()

var foo = function () {
  'use strict';
  console.log(this, typeof this);
}.bind('foo');

var bar = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(1337);

var baz = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(false);

var qux = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(NaN);

var quux = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(undefined);

var corge = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(null);

var grault = function () {
  'use strict';
  console.log(this, typeof this);
}.bind([]);

var garply = function () {
  'use strict';
  console.log(this, typeof this);
}.bind({});

var waldo = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(/regex/);

var fred = function () {
  'use strict';
  console.log(this, typeof this);
}.bind(function () {});

foo(); // foo string
bar(); // 1337 number
baz(); // false boolean
qux(); // NaN number
quux(); // undefined undefined
corge(); // null object
grault(); // Array [ ] object
garply(); // Object { } object
waldo(); // /regex/ object
fred(); // function fred<() function

Function.prototype.call()

Array.prototype.foo = function () {
  'use strict';
  console.log(this, typeof this);
};

['foo'].foo(); // Array [ "foo" ] object
Array.prototype.foo.call('bar'); // bar string
Array.prototype.foo.call(42); // 42 number
Array.prototype.foo.call(true); // true boolean
Array.prototype.foo.call(NaN); // NaN number
Array.prototype.foo.call(undefined); // undefined undefined
Array.prototype.foo.call(null); // null object
Array.prototype.foo.call({}); // Object { } object
Array.prototype.foo.call(/regex/); // /regex/ object
Array.prototype.foo.call(function () {}); // function () function
Community
  • 1
  • 1
Badacadabra
  • 8,043
  • 7
  • 28
  • 49
  • 1
    Thanks for such detailed answer. I myself wasn't sure why on earth did they use `Object(this)`, and puzzled the hell out of me. The strict mode seems like the reason. I will accept this answer just because your explanation is step by step like a tutorial, explaining why the puzzle is a puzzle, very detailed and easy to understand for future readers. – bits May 22 '17 at 18:42
  • Note to future readers, please read @torazaburo's answer in addition to this. – bits May 22 '17 at 18:44