对象传播与Object.assign

2020/09/28 08:21 · javascript ·  · 0评论

假设我有一个options变量,并且想要设置一些默认值。

这两种选择的优点/缺点是什么?

使用对象传播

options = {...optionsDefault, ...options};

或使用Object.assign

options = Object.assign({}, optionsDefault, options);

这是承诺让我惊叹。

这并不一定是详尽无遗的。

传播语法

options = {...optionsDefault, ...options};

好处:

  • 如果编写代码以在没有本机支持的环境中执行,则可以仅编译此语法(与使用polyfill相对)。(例如,使用Babel。)

  • 不太冗长。

缺点:

  • 最初编写此答案时,这只是一个建议,而不是标准化的。使用投标时,请考虑一下如果现在用它编写代码会做什么,并且在朝着标准化的方向发展时,它不会变得标准化或更改。此后已在ES2018中标准化。

  • 从字面上看,不是动态的。


Object.assign()

options = Object.assign({}, optionsDefault, options);

好处:

  • 标准化。

  • 动态。例:

    var sources = [{a: "A"}, {b: "B"}, {c: "C"}];
    options = Object.assign.apply(Object, [{}].concat(sources));
    // or
    options = Object.assign({}, ...sources);
    

缺点:

  • 更详细。
  • 如果编写代码以在没有本机支持的环境中执行,则需要polyfill。

这使我感到奇怪。

这与您的要求没有直接关系。该代码未使用Object.assign(),它正在使用object-assign执行相同操作的用户代码()。他们似乎正在用Babel编译该代码(并将其与Webpack捆绑在一起),这就是我在说的:您可以编译的语法。他们显然更喜欢将其包含object-assign在其构建中作为依赖项。

作为参考,对象的休息/传播在ECMAScript 2018中最终确定为阶段4。可以在此处找到建议

大多数情况下,对象重置和散布都以相同的方式进行,主要区别在于散布定义属性,而Object.assign()设置属性这意味着Object.assign()触发设置器。

值得记住的是,除此之外,对象休息/传播1:1映射到Object.assign()并以不同的方式作用于数组(可迭代)传播。例如,在扩展数组时,将扩展空值。但是,使用对象扩展时,空值会默默地扩展为零。

数组(可迭代)传播示例

const x = [1, 2, null , 3];
const y = [...x, 4, 5];
const z = null;

console.log(y); // [1, 2, null, 3, 4, 5];
console.log([...z]); // TypeError

对象传播示例

const x = null;
const y = {a: 1, b: 2};
const z = {...x, ...y};

console.log(z); //{a: 1, b: 2}

这与Object.assign()的工作方式一致,两者均无提示地无提示地排除了null值。

const x = null;
const y = {a: 1, b: 2};
const z = Object.assign({}, x, y);

console.log(z); //{a: 1, b: 2}

我认为,散布运算符与Object.assign当前答案中似乎没有提到的一个大区别是,散布运算符不会将源对象的原型复制到目标对象。如果要向对象添加属性,并且不想更改对象的实例,则必须使用Object.assign

编辑:我实际上已经意识到我的示例具有误导性。扩展运算符Object.assign将第一个参数设置为空对象时对变差在下面的代码示例中,我将error作为Object.assign调用的第一个参数,因此两者并不等效。Object.assign实际上第一个参数被修改然后返回,这就是为什么它保留其原型的原因。我在下面添加了另一个示例:

const error = new Error();
error instanceof Error // true

const errorExtendedUsingSpread = {
  ...error,
  ...{
    someValue: true
  }
};
errorExtendedUsingSpread instanceof Error; // false

// What the spread operator desugars into
const errorExtendedUsingImmutableObjectAssign = Object.assign({}, error, {
    someValue: true
});
errorExtendedUsingImmutableObjectAssign instanceof Error; // false

// The error object is modified and returned here so it keeps its prototypes
const errorExtendedUsingAssign = Object.assign(error, {
  someValue: true
});
errorExtendedUsingAssign instanceof Error; // true

另请参阅:https : //github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md

正如其他人提到的那样,在撰写本文时,它Object.assign()需要一个...polyfill,而对象散布需要一些转堆(也许也可以是polyfill)才能工作。

考虑以下代码:

// Babel wont touch this really, it will simply fail if Object.assign() is not supported in browser.
const objAss = { message: 'Hello you!' };
const newObjAss = Object.assign(objAss, { dev: true });
console.log(newObjAss);

// Babel will transpile with use to a helper function that first attempts to use Object.assign() and then falls back.
const objSpread = { message: 'Hello you!' };
const newObjSpread = {...objSpread, dev: true };
console.log(newObjSpread);

这些都产生相同的输出。

这是Babel到ES5的输出:

var objAss = { message: 'Hello you!' };
var newObjAss = Object.assign(objAss, { dev: true });
console.log(newObjAss);

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var objSpread = { message: 'Hello you!' };
var newObjSpread = _extends({}, objSpread, { dev: true });
console.log(newObjSpread);

到目前为止,这是我的理解。Object.assign()实际上是标准化的,因为...还没有对象传播唯一的问题是浏览器支持前者,将来也支持后者。

在这里玩代码

希望这可以帮助。

我想通过工具总结“扩展对象合并” ES功能在浏览器和生态系统中的状态。

规格

浏览器:即将在Chrome,SF,Firefox中运行(版本60,IIUC)

  • 浏览器支持Chrome 60附带的 “扩展属性” ,包括这种情况。
  • 在当前的Firefox(59)中不支持这种情况,但是在我的Firefox Developer Edition中可以使用。因此,我相信它将在Firefox 60中发布。
  • Safari:未经测试,但Kangax表示它可在Desktop Safari 11.1中运行,但不适用于SF 11
  • iOS Safari:未设置,但Kangax表示它可在iOS 11.3中使用,但不适用于iOS 11
  • 还没有在Edge中

工具:Node 8.7,TS 2.1

链接

代码示例(兼作兼容性测试)

var x = { a: 1, b: 2 };
var y = { c: 3, d: 4, a: 5 };
var z = {...x, ...y};
console.log(z); // { a: 5, b: 2, c: 3, d: 4 }

再次:在撰写本文时,此示例在Chrome(60 +),Firefox Developer Edition(Firefox 60的预览版)和Node(8.7+)中无需转换即可工作。

为什么回答?

我是在提出原始问题后2. 5 写这篇文章的但是我也有同样的问题,这就是Google发送给我的地方。我是SO改善长尾巴任务的奴隶。

由于这是“数组传播”语法的扩展,因此我发现很难用Google搜索,也很难在兼容性表中找到。我能找到的最接近的是Kangax“属性价差”,但该测试在同一表达式中没有两个价差(不是合并)。此外,投标/草稿/浏览器状态页面中的名称都使用“属性传播”,但在我看来,这是社区在提议使用传播语法进行“对象合并”之后到达的“第一主体”。(这可能会解释为什么使用Google如此困难。)因此,我在这里记录我的发现,以便其他人可以查看,更新和编译有关此特定功能的链接。我希望它能流行起来。请帮助在规范和浏览器中传播有关其降落的消息。

最后,我会将此信息添加为评论,但是如果不破坏作者的初衷,就无法对其进行编辑。具体来说,我不能编辑@ChillyPenguin的评论,除非它失去他纠正@RichardSchulte的意图。但是几年后,理查德证明是对的(我认为)。因此,我改写了这个答案,希望它最终会在旧答案上引起关注(可能要花几年时间,但这毕竟是长尾效应的全部内容)。

对象散布运算符(...)在浏览器中不起作用,因为它还不是任何ES规范的一部分,只是一个建议。唯一的选择是用Babel(或类似的东西)编译它。

如您所见,它只是Object.assign({})上的语法糖。

据我所知,这些是重要的区别。

  • Object.assign在大多数浏览器中均可运行(无需编译)
  • ... 对于对象没有标准化
  • ... 保护您免于意外使对象变形
  • ... 没有它会在浏览器中填充Object.assign
  • ... 需要更少的代码来表达相同的想法

NOTE: Spread is NOT just syntactic sugar around Object.assign. They operate much differently behind the scenes.

Object.assign applies setters to a new object, Spread does not. In addition, the object must be iterable.

复制
如果您现在需要该对象的值,并且不希望该值反映该对象的其他所有者所做的任何更改,请使用此对象。

使用它来创建对象的浅表副本的最佳做法是始终将不可变属性设置为复制-因为可变版本可以传递给不可变属性,所以复制将确保您将始终处理不可变对象

分配
分配与复制有点相反。
Assign将生成一个setter,该setter会将值直接分配给实例变量,而不是复制或保留它。调用assign属性的getter时,它将返回对实际数据的引用。

其他答案很旧,无法获得很好的答案。

下面的示例针对对象字面量,有助于两者如何相互补充以及如何不能相互补充(因此有所不同):

var obj1 = { a: 1,  b: { b1: 1, b2: 'b2value', b3: 'b3value' } };

// overwrite parts of b key
var obj2 = {
      b: {
        ...obj1.b,
        b1: 2
      }
};
var res2 = Object.assign({}, obj1, obj2); // b2,b3 keys still exist
document.write('res2: ', JSON.stringify (res2), '<br>');
// Output:
// res2: {"a":1,"b":{"b1":2,"b2":"b2value","b3":"b3value"}}  // NOTE: b2,b3 still exists

// overwrite whole of b key
var obj3 = {
      b: {
        b1: 2
      }
};
var res3 = Object.assign({}, obj1, obj3); // b2,b3 keys are lost
document.write('res3: ', JSON.stringify (res3), '<br>');
// Output:
  // res3: {"a":1,"b":{"b1":2}}  // NOTE: b2,b3 values are lost

这里还有一些关于数组和对象的小例子:https :
//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax

现在,它已成为ES6的一部分,因此已经标准化,并且也记录在MDN上:https :
//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator

它非常方便使用,并且与对象分解一起意义非凡。

上面列出的一个剩余优点是Object.assign()的动态功能,但是,这与将数组扩展到文字对象内部一样容易。在编译的babel输出中,它完全使用Object.assign()演示的内容

因此正确的答案是使用对象散布,因为它已经标准化,被广泛使用(请参阅react,redux等),易于使用并且具有Object.assign()的所有功能

当您必须使用Object.assign时,我想添加一个简单的示例。

class SomeClass {
  constructor() {
    this.someValue = 'some value';
  }

  someMethod() {
    console.log('some action');
  }
}


const objectAssign = Object.assign(new SomeClass(), {});
objectAssign.someValue; // ok
objectAssign.someMethod(); // ok

const spread = {...new SomeClass()};
spread.someValue; // ok
spread.someMethod(); // there is no methods of SomeClass!

使用JavaScript时不清楚。但是如果您想创建某个类的实例,使用TypeScript会更容易

const spread: SomeClass = {...new SomeClass()} // Error

扩展运算符将数组扩展为函数的单独参数。

let iterableObjB = [1,2,3,4]
function (...iterableObjB)  //turned into
function (1,2,3,4)

我们将创建一个称为身份的函数,该函数仅返回我们提供的任何参数。

identity = (arg) => arg

和一个简单的数组。

arr = [1, 2, 3]

如果您使用arr呼叫身份,我们会知道会发生什么

本文地址:http://javascript.askforanswer.com/duixiangchuanboyuobject-assign.html
文章标签: ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!