Reputation: 95
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
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:
- 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
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:
- 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