详解JS的Object.assign()
Object.assign()
通过复制一个或多个对象来创建一个新的对象。
语法
Object.assign(target, ...sources)
描述
如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性。
Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标对象的[[Set]],所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。如果合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到原型,应使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。
String类型和 Symbol 类型的属性都会被拷贝。
在出现错误的情况下,例如,如果属性不可写,会引发TypeError,如果在引发错误之前添加了任何属性,则可以更改target对象。
注意,Object.assign 不会在那些source对象值为 null 或 undefined 的时候抛出错误。
复制一个对象
const obj = { a: 1 };
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
深度拷贝
针对深拷贝,需要使用其他办法,因为 Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。
let obj1 = { a: 0 , b: { c: 0}};
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj1.a = 1;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
obj2.a = 2;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}}
obj2.b.c = 3;
console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}
// Deep Clone
obj1 = { a: 0 , b: { c: 0}};
let obj3 = JSON.parse(JSON.stringify(obj1));
obj1.a = 4;
obj1.b.c = 4;
console.log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}}
合并对象
const o1 = { a: 1 };
const o2 = { b: 2 };
const o3 = { c: 3 };
const obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1); // { a: 1, b: 2, c: 3 }, 注意目标对象自身也会改变。
继承属性和不可枚举属性是不能拷贝的
const obj = Object.create({foo: 1}, { // foo 是个继承属性。
bar: {
value: 2 // bar 是个不可枚举属性。
},
baz: {
value: 3,
enumerable: true // baz 是个自身可枚举属性。
}
});
const copy = Object.assign({}, obj);
console.log(copy); // { baz: 3 }
原始类型会被包装为对象
const v1 = "abc";
const v2 = true;
const v3 = 10;
const v4 = Symbol("foo")
const obj = Object.assign({}, v1, null, v2, undefined, v3, v4);
// 原始类型会被包装,null 和 undefined 会被忽略。
// 注意,只有字符串的包装对象才可能有自身可枚举属性。
console.log(obj); // { "0": "a", "1": "b", "2": "c" }
异常会打断后续拷贝任务
const target = Object.defineProperty({}, "foo", {
value: 1,
writable: false
}); // target 的 foo 属性是个只读属性。
Object.assign(target, {bar: 2}, {foo2: 3, foo: 3, foo3: 3}, {baz: 4});
// TypeError: "foo" is read-only
// 注意这个异常是在拷贝第二个源对象的第二个属性时发生的。
console.log(target.bar); // 2,说明第一个源对象拷贝成功了。
console.log(target.foo2); // 3,说明第二个源对象的第一个属性也拷贝成功了。
console.log(target.foo); // 1,只读属性不能被覆盖,所以第二个源对象的第二个属性拷贝失败了。
console.log(target.foo3); // undefined,异常之后 assign 方法就退出了,第三个属性是不会被拷贝到的。
console.log(target.baz); // undefined,第三个源对象更是不会被拷贝到的。
拷贝访问器
const obj = {
foo: 1,
get bar() {
return 2;
}
};
let copy = Object.assign({}, obj);
console.log(copy); // { foo: 1, bar: 2 } copy.bar的值来自obj.bar的getter函数的返回值
// 下面这个函数会拷贝所有自有属性的属性描述符
function completeAssign(target, ...sources) {
sources.forEach(source => {
let descriptors = Object.keys(source).reduce((descriptors, key) => {
descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
return descriptors;
}, {});
// Object.assign 默认也会拷贝可枚举的Symbols
Object.getOwnPropertySymbols(source).forEach(sym => {
let descriptor = Object.getOwnPropertyDescriptor(source, sym);
if (descriptor.enumerable) {
descriptors[sym] = descriptor;
}
});
Object.defineProperties(target, descriptors);
});
return target;
}
copy = completeAssign({}, obj);
console.log(copy);
// { foo:1, get bar() { return 2 } }
详解JS的Object.assign()的更多相关文章
- 详解js变量、作用域及内存
详解js变量.作用域及内存 来源:伯乐在线 作者:trigkit4 原文出处: trigkit4 基本类型值有:undefined,NUll,Boolean,Number和Strin ...
- [转]javascript console 函数详解 js开发调试的利器
javascript console 函数详解 js开发调试的利器 分步阅读 Console 是用于显示 JS和 DOM 对象信息的单独窗口.并且向 JS 中注入1个 console 对象,使用该 ...
- 详解js和jquery里的this关键字
详解js和jquery里的this关键字 js中的this 我们要记住:this永远指向函数运行时所在的对象!而不是函数被创建时所在的对象.this对象是在运行时基于函数的执行环境绑定的,在全局环境中 ...
- 详解js面向对象编程
转自:http://segmentfault.com/a/1190000000713346 基本概念 ECMA关于对象的定义是:”无序属性的集合,其属性可以包含基本值.对象或者函数.“对象的每个属性或 ...
- 详解js的bind、call、apply
详解js的bind.call.apply 说明 虽然bind.call.apply都是js很基础的一块知识,但是我从未认真总结过这三者的区别. 由于公司后端是用的微服务架构,又没有中间层对接,导致前端 ...
- 详解js中的闭包
前言 在js中,闭包是一个很重要又相当不容易完全理解的要点,网上关于讲解闭包的文章非常多,但是并不是非常容易读懂,在这里以<javascript高级程序设计>里面的理论为基础.用拆分的方式 ...
- Jquery 选择器 详解 js 判断字符串是否包含另外一个字符串
Jquery 选择器 详解 在线文档地址:http://tool.oschina.net/apidocs/apidoc?api=jquery 各种在线工具地址:http://www.ostools ...
- 委托与事件代码详解与(Object sender,EventArgs e)详解
委托与事件代码详解 using System;using System.Collections.Generic;using System.Text; namespace @Delegate //自定义 ...
- 详解JS设计模式
原文链接:www.cnblogs.com 一:理解工厂模式 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式. 简单的工厂模式可以理解为解决 ...
- javascript console 函数详解 js开发调试的利器
Console 是用于显示 JS和 DOM 对象信息的单独窗口.并且向 JS 中注入1个 console 对象,使用该对象 可以输出信息到 Console 窗口中. 使用 alert 不是一样可以显示 ...
随机推荐
- Javaweb学习笔记第八弹
继续MyBatis学习 SQL语句警告提示问题 产生原因:IDEA和数据库没有建立连接,不识别表的信息 解决方式:在IDEA中配置MySQL数据库连接 在IDEA的Maven项目中,如果想要直接通过I ...
- AES 简介 以及 C# 和 js 实现【加密知多少系列】
〇.AES 简介 AES 的全称是 Advanced Encryption Standard,意思是高级加密标准.它的出现主要是为了取代 DES(Data Encryption StandardDat ...
- 获得New Bing资格后,在Ubuntu环境下使用New Bing
技术背景 如今基于GPT-4的New Bing,结合搜索引擎的功能,可以说已经达到了非常高的智力水平.虽说ChatGPT的出现打击了很多的行业,但是对我们来说也未必不是一种机遇.合理的使用ChatGP ...
- 利用 Rainbond 云原生平台简化 Kubernetes 业务问题排查
Kubernetes 已经成为了云原生时代基础设施的事实标准,越来越多的应用系统在 Kubernetes 环境中运行.Kubernetes 已经依靠其强大的自动化运维能力解决了业务系统的大多数运行维护 ...
- Nacos 实现 AP+CP原理[Raft 算法 NO]
来源于网络 一.什么是 Raft算法 Raft 适用于一个管理日志一致性的协议,相比于 Paxos 协议 Raft 更易于理解和去实现它.为了提高理解性,Raft 将一致性算法分为了几个部分,包括领导 ...
- flex布局相关属性记录
<template> <div class="about"> <h3>flex相关的属性</h3> <div class=&q ...
- 有关SpringMVC的一些知识点
Spring的获取对象 1.获取原生对象,直接在在方法李传http相关的对象.2.接受简单数据类型,接受简单数据类型(八种基本类型)参数直接在handler方法里声明,会自动把相同类型进行绑定,但是接 ...
- pandas之使用自定义函数
如果想要应用自定义的函数,或者把其他库中的函数应用到 Pandas 对象中,有以下三种方法: 1) 操作整个 DataFrame 的函数:pipe() 2) 操作行或者列的函数:apply() 3) ...
- Spring Boot 整合 Kafka
Kafka 环境搭建 kafka 安装.配置.启动.测试说明: 1. 安装:直接官网下载安装包,解压到指定位置即可(kafka 依赖的 Zookeeper 在文件中已包含) 下载地址:https:// ...
- Redis(四)主从复制
主从复制 简介 主机数据更新之后根据配置和策略,自动同步数据到备机的Master/Slaver机制,Master以写为主,Slaver以读为主. 这样的机制能够实现: 读写分离:Master以写为主, ...