Matthew Herbst
Matthew Herbst

Reputation: 31983

Why does my Array.prototype.equals function show up as an element in arrays copied using jQuery.extend()?

In my program I have added the Array.prototype.equals() function listed in this SO post.

I have the following function to perform deep-copies of arrays (I am using this strictly for logging purposes - so that I can see what my arrays look like at various points in time in the Chrome console.)

function deepCopy(array) {
    return $.extend(true, [], array);
}

I then use this as such:

console.log("Sorting metrics list by metric name...");
console.log("Metrics list before sort:");
console.log(deepCopy(metrics));
metrics.sort(compare);
console.log("Metrics list after sort:");
console.log(deepCopy(metrics));

For reference, my compare function is:

function compare(a, b) {
  return a.name.localeCompare(b.name);
}

When I view my metrics array in the Chrome console after the deepCopy(), the equals method defined for the Array.prototype is IN the array! Picture:

enter image description here

As you can see from the screen shot, I check the actual value of the metrics array after everything has run, and it clearly does not contain the equals method. This is my reasoning for believing it has something to do with how jQuery().extend handles copying arrays.

Any ideas on how to fix my deepCopy function to stop this? (Or change my prototype function, if needed.)

Upvotes: 0

Views: 1385

Answers (1)

apsillers
apsillers

Reputation: 115950

Two issues here:

  • you're adding an enumerable property on the Array prototype
  • a console oddity: console.log is different from expression evaluation

$.extends loops over all enumerable properties of the source object and adds them as own instance properties (not inherited properties) of the target object. If you make your prototype property non-enumerable, it won't be copied directly onto the target:

Object.defineProperty(Array.prototype, "equals", {
    enumerable: false,
    value: function(array) { /* ... */ }
});

(See some helpful reading: Don’t modify objects you don’t own.)

Your final test where you type metrics into the console doesn't show you the equals property, but it's actually still there. Consider the following console session in Chrome, where we add an enumerable property on the Array prototype and then use an array with $.extends:

> Array.prototype.foo = "egg";
"egg"

> $.extend(true, [], ['bar', 'baz'])
["bar", "baz"]

> console.log($.extend(true, [], ['bar', 'baz']))
["bar", "baz", foo: "egg"] 

> Object.getOwnPropertyNames( $.extend(true, [], ['bar', 'baz']) )
["0", "1", "length", "foo"]

The non-index properties only show up when we explictly use console.log; they don't show up when we just evaluate the expression, even though getOwnPropertyNames confirms they're there.

Upvotes: 2

Related Questions