0

Lets say the Array prototype has been augmented with some helper function:

Array.prototype.doSomething = function() { ... }

When I log out a simple array into the console ...

console.dir([1,2,3]);

... I get:

Array [3]
    0: 1
    1: 2
    2: 3
    length: 3
    __proto__: Array[0]
       doSomething: function()

everything looks fine so far.

But as soon as I deep clone the array using $.extend (deep clone because my actual data is more complex - an object with arrays as properties, but it happens in any case) ...

$.extend(true, [], [1,2,3]);

I suddenly get:

Array [3]
    0: 1
    1: 2
    2: 3
    doSomething: function()     // ???
    length: 3
    __proto__: Array[0]
       doSomething: function() 

It looks like the prototype method has been added as an actual item of the array instance.

Does JQuery.extend not test for hasOwnProperty() before copying, or am I doing something wrong here?

Đinh Carabus
  • 3,403
  • 4
  • 22
  • 44
  • 1
    Yes, but yes you're doing something wrong as well, see [How to define method in javascript on Array.prototype and Object.prototype so that it doesn't appear in for in loop](http://stackoverflow.com/q/13296340/1048572) – Bergi Feb 13 '16 at 17:24

2 Answers2

1

It seems that is part of the specification of $.extend in the documentation:

"However, properties inherited from the object's prototype will be copied over."

https://api.jquery.com/jquery.extend/

Emil Stenström
  • 13,329
  • 8
  • 53
  • 75
1

Does JQuery.extend not test for hasOwnProperty() before copying

No it does not. From the source it uses a for ... in loop to iterate over the properties of whatever is being cloned, but does not do a hasOwnProperty check before setting the properties.

https://github.com/jquery/jquery/blob/7103d8ef47e04a4cf373abee0e8bfa9062fd616f/src/core.js#L120

jQuery.extend = jQuery.fn.extend = function() {
  var options, name, src, copy, copyIsArray, clone,
      target = arguments[ 0 ] || {},
      i = 1,
      length = arguments.length,
      deep = false;

  // Handle a deep copy situation
  if ( typeof target === "boolean" ) {
      deep = target;

      // Skip the boolean and the target
      target = arguments[ i ] || {};
      i++;
  }

  // Handle case when target is a string or something (possible in deep copy)
  if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
      target = {};
  }

  // Extend jQuery itself if only one argument is passed
  if ( i === length ) {
      target = this;
      i--;
  }

  for ( ; i < length; i++ ) {

      // Only deal with non-null/undefined values
      if ( ( options = arguments[ i ] ) != null ) {

          // Extend the base object
          for ( name in options ) {
              src = target[ name ];
              copy = options[ name ];

              // Prevent never-ending loop
              if ( target === copy ) {
                  continue;
              }

              // Recurse if we're merging plain objects or arrays
              if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
                  ( copyIsArray = jQuery.isArray( copy ) ) ) ) {

                  if ( copyIsArray ) {
                      copyIsArray = false;
                      clone = src && jQuery.isArray( src ) ? src : [];

                  } else {
                      clone = src && jQuery.isPlainObject( src ) ? src : {};
                  }

                  // Never move original objects, clone them
                  target[ name ] = jQuery.extend( deep, clone, copy );

              // Don't bring in undefined values
              } else if ( copy !== undefined ) {
                  target[ name ] = copy;
              }
          }
      }
  }

  // Return the modified object
  return target;
};

Does it effect your array data? Depending on how you use the array probably not. Iterating over the array elements would still be the same, as long as you use the correct loop procedures. Meaning for(;;), for ... of, or .forEach. And doing JSON.stringify would still give you the correct JSON.

Patrick Evans
  • 41,991
  • 6
  • 74
  • 87
  • Thank you! No it does not affect the data, It just felt wrong when I looked at the console output. I guess they omitted the hasOwnProperty check for performance reasons? Will it work if I use `Object.defineProperty()` with `enumerable` set to false to add the method to Array.prototype? – Đinh Carabus Feb 13 '16 at 16:38
  • To answer my own question, it seems to work this way `Object.defineProperty(Array.prototype, "doSomething", { enumerable: false, ... });` – Đinh Carabus Feb 13 '16 at 17:15