← 返回题目列表

Object.assign 和对象展开语法有什么区别?

中等 第 22 / 28 题 更新于 2026/07/29
ES6Object.assign对象展开

简化版

Object.assign(target, ...sources) 会把源对象自身可枚举属性复制到目标对象,并返回目标对象;对象展开 { ...obj } 常用于创建新对象并浅拷贝属性。

两者都是浅拷贝,都会触发源对象 getter,也都只复制可枚举自有属性。关键区别在于 Object.assign 会调用目标对象的 setter,而对象展开定义的是新对象属性,行为更接近创建新对象。

详细版

const source = { a: 1 };
const assigned = Object.assign({}, source);
const spread = { ...source };

都是浅拷贝:

const source = { nested: { x: 1 } };
const copy = { ...source };
copy.nested.x = 2;
console.log(source.nested.x); // 2

setter 差异:

const target = {
  set x(value) {
    console.log("setter", value);
  }
};

Object.assign(target, { x: 1 }); // 触发 setter
const obj = { ...target, x: 1 }; // 创建/定义属性

实际项目中,普通合并配置更常用对象展开;需要明确写入已有目标对象时才用 Object.assign

完整版教学

一、先明确两者的共同点

Object.assign 和对象展开都不是深拷贝。

它们复制的是源对象自身可枚举属性的当前值。

source own enumerable properties
  -> read value
  -> write/define on result

面试里最容易先问浅拷贝,再追问 getter、setter、原型和属性描述符。

二、Object.assign 会修改目标对象

const target = { a: 1 };
const result = Object.assign(target, { b: 2 });

console.log(result === target); // true
console.log(target); // { a: 1, b: 2 }

如果不想改原对象,通常把 {} 放在第一个参数。

const next = Object.assign({}, oldConfig, userConfig);

三、对象展开更常用于创建新对象

const next = {
  ...defaultConfig,
  ...userConfig,
  debug: false
};

后面的属性会覆盖前面的同名属性。

数字例子:

const obj = { ...{ a: 1 }, ...{ a: 2 }, b: 3 };
// { a: 2, b: 3 }

四、浅拷贝的边界

嵌套对象只复制引用。

const a = { list: [1, 2] };
const b = { ...a };

b.list.push(3);
console.log(a.list); // [1, 2, 3]

如果需要更新嵌套状态,要逐层复制变化路径,或使用结构共享工具。

五、getter 和 setter 的差异

读取源对象属性时,两者都会触发 getter。

const source = {
  get value() {
    console.log("get");
    return 1;
  }
};

({ ...source });
Object.assign({}, source);

写入目标时,Object.assign 使用赋值语义,可能触发 setter;对象展开在新对象上创建属性。

六、属性描述符不会完整保留

const source = {};
Object.defineProperty(source, "id", {
  value: 1,
  enumerable: true,
  writable: false
});

const copy = { ...source };

copy.id 是普通数据属性,不再保留原来的 writable: false。若要保留描述符,应使用 Object.getOwnPropertyDescriptors

需求更合适的方式
普通配置合并对象展开
写入已有对象Object.assign
保留属性描述符Object.defineProperties

七、常见误区与追问

  • 误区:对象展开就是深拷贝。 它只复制第一层属性值。
  • 误区:Object.assign 不会改任何入参。 它会修改第一个目标对象。
  • 误区:两者会复制原型属性。 它们只复制自身可枚举属性。
  • 误区:属性描述符会原样复制。 普通拷贝拿的是值,不保留完整描述符。
  • 追问:为什么 assign 会触发 setter? 因为它向目标对象执行赋值操作。
  • 追问:多个源对象同名字段谁生效? 后面的源覆盖前面的同名字段。

八、加强记忆

记住一句工程判断:

need new object -> spread
need mutate target -> Object.assign
need descriptors -> defineProperties

答题时先讲共同点:浅拷贝、自有可枚举、后者覆盖前者;再讲差异:是否修改目标对象、setter 行为、描述符保留问题。