jQuery扩展与Angular扩展

63

这两个扩展函数之间有什么区别?

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

尽管jquery.extend有很好的文档记录,但angular.extend缺乏细节,那里的评论没有提供答案。(https://docs.angularjs.org/api/ng/function/angular.extend).

angular.extend是否也提供深拷贝?


它说:“通过复制所有属性”,我会假设使用词语“all”意味着“everything”,这与JS世界中的“深拷贝”是同义词。 - Ohgodwhy
5
@Ohgodwhy:我非常怀疑这是一个“深度复制”。如果一个属性(比如说,p)引用了一个对象,我敢打赌,在调用之后,src.pdst.p两者都会引用同一个对象。 - T.J. Crowder
4
文档存在误导性。 - Ohgodwhy
4个回答

97

angular.extendjQuery.extend 非常相似。它们都会将一个或多个源对象的属性进行浅拷贝,然后将其赋值到目标对象上。例如:

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 提供了一个深度复制

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.

回到extend:我只看到一个显著的区别,那就是jQuery的extend允许您仅指定一个对象,在这种情况下,jQuery本身是目标。

共同点:

  • 它是浅复制。因此,如果src具有引用对象的属性p,则dst将获得一个引用同一对象(而不是对象的副本)的属性p

  • 它们都返回目标对象。

  • 它们都支持多个源对象。

  • 它们都按顺序处理多个源对象,因此在多个源对象具有相同的属性名称的情况下,最后一个源对象将“获胜”。

测试页面:实时演示 | 实时源代码

<!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>

21
值得一提的是,jQuery允许你将布尔值true作为第一个参数来指定进行深度复制。请参见此处:http://api.jquery.com/jQuery.extend/#jQuery-extend-deep-target-object1-objectN - treeface
一个主要的区别是:如果有一个同名属性,extend 只会复制值,而 copy 会复制整个对象,因此如果在 $scope 变量上使用它,您将失去数据绑定!就像 asafge 的答案所述。 - Sebastian
1
angular.extend() 不会为您复制getter和setter。请查看https://github.com/angular/angular.js/issues/8573。 - demisx
1
@demisx:jQuery也不会。它们都复制属性的,我可能会认为这是正确的行为。 - T.J. Crowder

31

这两种方法之间有一个微妙的区别,之前的回答中没有提到。

jQuery的.extend()允许在值被定义时有条件地添加键值对。因此,在jQuery中,这个语句:$.extend({}, {'a': x ? x : undefined}); 如果x未定义,则返回{}

然而,在Angular的.extend()中,这个语句:angular.extend({}, {'a': x ? x : undefined}); 即使x未定义,也会返回{'a': undefined}。所以,无论如何,这个键都会存在。

这可能是好事或坏事,具体取决于您的需要。无论如何,这是两个库之间行为上的差异。


我也遇到了同样的问题。相关示例在这里:http://plnkr.co/edit/2ca7AfIhgolmwaNaYvY4?p=preview - gotoweb
如果你期望这两个函数的行为相同,那么这绝对是一件坏事。甚至在2014年就有一个拉取请求来修复这个问题,但并未被实现:https://github.com/angular/angular.js/pull/8387 - Waruyama

6

1.0.7版的angularjs构建说明,扩展和复制方法不再复制angularjs内部的$$hashKey值。

请参见发布说明@https://github.com/angular/angular.js/blob/master/CHANGELOG.md

angular.copy/angular.extend:在复制/扩展函数中不复制$$hashKey。(6d0b325f,#1875)

通过Chrome开发工具的angular.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只进行浅拷贝。


“在Chrome开发工具中快速测试angular.copy方法表明它确实进行了深拷贝。” 不,它没有。 extend 也不是,反正我没看过 copy - T.J. Crowder
啊,copy可以,extend不行:http://jsbin.com/eketan/2 但问题是关于extend而不是copy - T.J. Crowder
是的 - 当我发布问题时,您已经非常彻底地回答了扩展问题。我想确保复制方法也被评估,因为它可以为他们想要实现的目标提供解决方案。 - Mike Pugh
感谢你们两位对这个问题进行了如此详尽的澄清 :) - Renaud

1
在AngularJS中,.extend()的工作方式类似于jQuery的.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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接