js 如何实现对象值复制?

2021-12-08 10:32:10 +08:00
 wunonglin

go 的实现

package main

import "fmt"

type M struct {
	Num int64
}

func T1() {
	fmt.Printf("----T1----\n")
	a := M{1}
	b := a
	fmt.Printf("%+v\n", a)
	fmt.Printf("%+v\n", b)
	a.Num = 2
	fmt.Printf("%+v\n", a)
	fmt.Printf("%+v\n", b)
}

func T2() {
	fmt.Printf("----T2----\n")
	a := &M{1}
	b := a
	fmt.Printf("%+v\n", a)
	fmt.Printf("%+v\n", b)
	a.Num = 2
	fmt.Printf("%+v\n", a)
	fmt.Printf("%+v\n", b)
}

func main() {
	T1()
	T2()
}

输出结果

----T1----
{Num:1}
{Num:1}
{Num:2}
{Num:1}
----T2----
&{Num:1}
&{Num:1}
&{Num:2}
&{Num:2}

js 的实现

function T2(){
    let a = {num: 1}
    let b = a
    console.log(a)
    console.log(b)
    a.num = 2
    console.log(a)
    console.log(b)
}

输出结果

{num: 1}
{num: 1}
{num: 2}
{num: 2}

如何使用 js 实现 golang 的 T1 方法?除了深拷贝。

就类似于“从源 Object 创建一个新的 Object ,内存地址完全是新的,新 Object 也和源完全一样,不会丢失各种属性”,js 自带的好像没看到类似的方法

2748 次点击
所在节点    程序员
39 条回复
joshua7v
2021-12-08 11:10:58 +08:00
structured clone 快来了
虽然浏览器还未开放这个 api
但可以借用浏览器的某些基于此 api 的接口
比如 postmessage 什么的
wunonglin
2021-12-08 11:11:34 +08:00
@WhiteHu #20 哈哈哈哈哈哈。还真有。至少可以在 node 开发的时候用,浏览器可以等未来吧
3dwelcome
2021-12-08 11:11:38 +08:00
我总觉得 JS 并不是什么正经语言,早年发明之初,就没想过现代前端会那么复杂。

JS 可以快速写逻辑,但是编写复杂的算法,还是比不上传统语言。

学我,JS 语法不足,用 webasm 来补全。把你原生 JS 逼死,也只有用深拷贝。
wunonglin
2021-12-08 11:12:23 +08:00
@joshua7v #21 firefox 永远的神,第一个支持了
yaphets666
2021-12-08 11:34:49 +08:00
@3dwelcome 正经语言是正经语言,没考虑过会这么复杂是真的.
libook
2021-12-08 11:43:33 +08:00
JS 这种高度抽象的语言的设计初衷之一,就是让使用者不需要去考虑内存细节,所以赋值操作,对于简单类型是赋值,对于复杂类型是引用。

仅针对题主的例子,不考虑其他情况的话可以这样写:
let b = Object.assgin({},a);
这行代码的意思是把 a 里面的所有成员拿出来,一个一个地赋值给 b 的同名成员,这个赋值操作和等号的赋值操作一样,同样是简单类型赋值、复杂类型引用。因为 Num 是简单的数值型,所以执行了赋值而不是引用,导致修改 b.Num 不会让 a.Num 发生改变。

但如果 Num 的值是一个对象,因为 b.Num 和 a.Num 引用的是同一个 Num 对象,所以修改 Num 对象内的成员后,读取 a 和 b 内的 Num 对像会发现发生了变化,此时如果还需要进一步的值复制,就需要深拷贝。

如果对象是简单的、可以用 JSON 描述的对象,比如不含有 getter 、setter ,没用 Symbol 字段名、没用非 JSON 数据类型等,那么常用的方式是 JSON.stringify 序列化再 JSON.parse 反序列化,完成一个深拷贝。这个在本来就在上游使用 JSON 的场景用得很广泛,比如 HTTP 通信。

JS 有个核心特性叫做原型链,对象值复制跟原型链的思想是矛盾的,前者希望尽可能复用代码,后者希望尽可能复制代码。这也就导致在 JS 里做深拷贝不那么方便。

有不少第三方的 deep clone 库,可以拿来直接用。
libook
2021-12-08 11:46:00 +08:00
@libook #26 勘误:
JS 有个核心特性叫做原型链,对象值复制跟原型链的思想是矛盾的,前者希望尽可能复制代码,后者希望尽可能复用代码。这也就导致在 JS 里做深拷贝不那么方便。
EPr2hh6LADQWqRVH
2021-12-08 11:56:13 +08:00
基础不牢,地动山摇,都十几层楼了还没人抛出 `Object.create()`

既然 js 那就上原型啊

const a = {x: 1};

const b = Object.create(a);

b.x = 2;

assert(a.x === 1);
gadfly3173
2021-12-08 12:11:35 +08:00
@avastms #28 楼主想要的是属性完全相同,内存地址不同的结果,Object.create 只是把源对象作为原型生成了新对象,你对源对象的修改还是会影响到新的对象
bnm965321
2021-12-08 12:12:26 +08:00
@wunonglin 这种的原理也是深拷贝吧
chenstack
2021-12-08 12:13:25 +08:00
@avastms #28 这种方式变成 b 往上找值,a 的改动还会影响 b ,而且 b 没有给字段赋值的话,JSON.stringify(b)返回空的"{}"
gadfly3173
2021-12-08 12:13:48 +08:00
EPr2hh6LADQWqRVH
2021-12-08 12:25:31 +08:00
那自己实现一下吧, 之后维护一下原型链。

const b = Object.assign({}, a);

Object.setPrototypeOf(b, Object.getPrototypeOf(a));
codehz
2021-12-08 14:44:19 +08:00
考虑到还有原生对象,精确的复制大概是不现实的(
加上还可以 proxy
SmallTeddy
2021-12-08 14:49:44 +08:00
@NathanDo 正解
yangzzzzzz
2021-12-08 14:56:05 +08:00
使用 lodash

_.clone(value):浅拷贝。浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间。

_.cloneDeep(value):深拷贝。深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。

_.defaults(object, [sources]):只对比第一层。给对象添加字段,保持原来字段的值。

_.defaultsDeep(object, [sources]):递归对比到最里层。给对象添加字段,保持原来字段的值。
2i2Re2PLMaDnghL
2021-12-08 15:09:45 +08:00
你的验证代码写错了,console.log(b, a.K, b instanceof M)
本该是 b.K
2i2Re2PLMaDnghL
2021-12-08 15:28:46 +08:00
实际上有个挺严重的问题,所谓值复制可能不一定是可行的。

function factory(value){
let num = value;
return {get Num(){return num}, set Num(val){num=val}}
}

let a=factory();
let b=someValueClone(a);

你不可能在这个情况下通用地分离 a.Num 和 b.Num ,因为涉及到作用域的分支性变化。
所以说实话,不如学 Rust 搞 trait Clone ,你自己写的类你自己实现 Clone 去。
hxse
2021-12-08 16:56:10 +08:00
这个问题有那么复杂吗

let a = {num: 1}
let b = {...a}
console.log(a)
console.log(b)
a.num = 2
console.log(a)
console.log(b)

{num: 1}
{num: 1}
{num: 2}
{num: 1}

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/820807

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX