63

What is the difference between these two extend functions?

  angular.extend(a,b);
  $.extend(a,b);

While the jquery.extend is well documented the angular.extend lacks details and the comments there provide no answers. (https://docs.angularjs.org/api/ng/function/angular.extend).

Does angular.extend also provide deep copy?

j.wittwer
  • 9,497
  • 3
  • 30
  • 32
Renaud
  • 4,569
  • 7
  • 41
  • 72
  • It states, `by copying all of the properties `, and I would assume the use of the word `all` means `everything`, which is synonymous with `deep copy` in the JS world. – Ohgodwhy May 28 '13 at 17:07
  • 5
    @Ohgodwhy: I very much doubt it's a *deep* copy. If a property (say, `p`) refers to an object, I'll bet you dollars to doughnuts that after the call, both `src.p` and `dst.p` refer to the *same* object. – T.J. Crowder May 28 '13 at 17:07
  • 4
    Misleading angular documentation is. – Ohgodwhy May 28 '13 at 17:08

4 Answers4

97

angular.extend and jQuery.extend are very similar. They both do a shallow property copy from one or more source objects to a destination object. So for instance:

var src = {foo: "bar", baz: {}};
var dst = {};
whatever.extend(dst, src);
console.log(dst.foo);             // "bar"
console.log(dst.baz === src.baz); // "true", it's a shallow copy, both
                                  // point to same object

angular.copy provides a deep copy:

var src = {foo: "bar", baz: {}};
var dst = angular.copy(src);
console.log(dst.baz === src.baz); // "false", it's a deep copy, they point
                                  // to different objects.

Getting back to extend: I only see one significant difference, which is that jQuery's extend allows you to specify just one object, in which case jQuery itself is the target.

Things in common:

  • It's a shallow copy. So if src has a property p that refers to an object, dst will get a property p that refers to the same object (not a copy of the object).

  • They both return the destination object.

  • They both support multiple source objects.

  • They both do the multiple source objects in order, and so the last source object will "win" in case more than one source object has the same property name.

Test page: Live Copy | Live Source

<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>
<meta charset=utf-8 />
<title>Extend!</title>
</head>
<body>
  <script>
    (function() {
      "use strict";
      var src1, src2, dst, rv;

      src1 = {
        a: "I'm a in src1",
        b: {name: "I'm the name property in b"},
        c: "I'm c in src1"
      };
      src2 = {
        c: "I'm c in src2"
      };

      // Shallow copy test
      dst = {};
      angular.extend(dst, src1);
      display("angular shallow copy? " + (dst.b === src1.b));
      dst = {};
      jQuery.extend(dst, src1);
      display("jQuery shallow copy? " + (dst.b === src1.b));
      $("<hr>").appendTo(document.body);

      // Return value test
      dst = {};
      rv = angular.extend(dst, src1);
      display("angular returns dst? " + (rv === dst));
      dst = {};
      rv = jQuery.extend(dst, src1);
      display("jQuery returns dst? " + (rv === dst));
      $("<hr>").appendTo(document.body);

      // Multiple source test
      dst = {};
      rv = angular.extend(dst, src1, src2);
      display("angular does multiple in order? " +
                  (dst.c === src2.c));
      dst = {};
      rv = jQuery.extend(dst, src1, src2);
      display("jQuery does multiple in order? " +
                  (dst.c === src2.c));

      function display(msg) {
        $("<p>").html(String(msg)).appendTo(document.body);
      }
    })();
  </script>
</body>
</html>
AgDude
  • 1,167
  • 1
  • 10
  • 27
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 21
    Also worth mentioning that jQuery allows you to specify boolean `true` as the first argument in order to make a deep copy. See here: http://api.jquery.com/jQuery.extend/#jQuery-extend-deep-target-object1-objectN – treeface Nov 06 '13 at 19:12
  • One major difference: extend is only copying values if there is a property with the same name, copy is copying the whole object, so you lose databinding if you use it on a $scope variable! Like asafge's answer states it. – Sebastian Sep 11 '14 at 06:03
  • 1
    `angular.extend()` won't copy getters and setters for you. See https://github.com/angular/angular.js/issues/8573 – demisx Oct 30 '14 at 15:27
  • 1
    @demisx: Neither does jQuery's. They both copy the *value* of the property, which I'd probably argue is correct behavior. – T.J. Crowder Oct 30 '14 at 15:34
31

There is one subtle difference between the two which was not mentioned in previous answers.

jQuery's .extend() allows you to conditionally add key,value pairs, only if the value is defined. So in jQuery, this: $.extend({}, {'a': x ? x : undefined}); will return {} in case x is undefined.

In Angular's .extend() however, this: angular.extend({}, {'a': x ? x : undefined}); will return {'a': undefined}, even if x is undefined. So the key will be there, no matter what.

This could be a good or a bad thing, depending on what you need. Anyway this is a difference in behavior between the two libraries.

asafge
  • 1,139
  • 1
  • 14
  • 21
  • I've got same problem too. related example here: http://plnkr.co/edit/2ca7AfIhgolmwaNaYvY4?p=preview – gotoweb Jan 22 '15 at 10:34
  • This is definitely a bad thing if you expect both functions to behave the same. There was even a pull request in 2014 to fix this, which was not implemented: https://github.com/angular/angular.js/pull/8387 – Waruyama Oct 10 '18 at 14:03
6

The 1.0.7 angularjs build states that the extend & copy methods no longer copy over the angularjs internal $$hashKey values.

See release notes @ https://github.com/angular/angular.js/blob/master/CHANGELOG.md

angular.copy/angular.extend: do not copy $$hashKey in copy/extend functions. (6d0b325f, #1875)

A quick test of the angular.copy in Chomre dev tools method shows that it does do a deep copy.

x = {p: 3, y: {x: 5}}
Object {p: 3, y: Object}
x
Object {p: 3, y: Object}
z = angular.copy(x);
Object {p: 3, y: Object}
z
Object {p: 3, y: Object}
x
Object {p: 3, y: Object}
z.y.x = 1000
    1000
x
Object {p: 3, y: Object}
p: 3
y: Object
    x: 5
    __proto__: Object
__proto__: Object
z
Object {p: 3, y: Object}
p: 3
y: Object
   x: 1000
   __proto__: Object
__proto__: Object

angular.extend on the other hand does a shallow copy.

Mike Pugh
  • 6,787
  • 2
  • 27
  • 25
  • *"A quick test of the angular.copy in Chomre dev tools method shows that it does do a deep copy."* No, it doesn't. `extend` doesn't, anyway, I didn't look at `copy`. – T.J. Crowder May 28 '13 at 17:21
  • Ah, `copy` does, `extend` doesn't: http://jsbin.com/eketan/2 But the question is about `extend`, not `copy`. – T.J. Crowder May 28 '13 at 17:25
  • Yep - you had already answered the extend question pretty thoroughly when I was posting. I wanted to make sure the copy method is also evaluated since it could provide a solution for what they are trying to accomplish. – Mike Pugh May 28 '13 at 17:30
  • Thanks to both of you for clarifying that matter so thoroughly :) – Renaud May 31 '13 at 11:11
1

.extend() in AngularJS works similarly to jQuery's .extend()

http://jsfiddle.net/Troop4Christ/sR3Nj/

var o1 = {
    a: 1,
    b: 2,
    c: {
        d:3,
        e:4
    }
},
    o2 = {
        b: {
            f:{
                g:5
            }
        }
    };


console.log(angular.extend({}, o1, o2));
console.log(o1);
console.log(o2);
RavenHursT
  • 2,336
  • 1
  • 25
  • 46