关于对象引用
在使用 js 时,对一些变量初始时可能需要使用默认值初始化, 比如
|
|
这种方式有一点点小问题,比如如果需要修改newConnPref
变量,则有可能发生一些预料之外的问题, 比如:
|
|
这是因为在 js 中对象的赋值是按引用传递的,在上面的代码中, DEFAULT_PREF
与 newConnPref
都指向了同一个对象, 所以在修改 newConnPref
对象的时候,其实修改的是 newConnPref
指向的对象,由于 DEFAULT_PREF
也同时发生了改变。
解决方法
在修改newConnPref
的时候可以使用对象复制的方式进行赋值, 比如:
|
|
这种方式对上面的例子工作良好, 因为我们在修改 newConnPref 中的属性时,先对 newConnPref 对象做了一个复制。
新的问题, 多级对象
可以上面的代码还有一点点小问题,众所周知,js 中的...
操作符进行的是对象的浅复制,这意味着,如果被复制对象有多级结构时, 使用...
操作符复制会产生意想不到的问题, 比如:
|
|
可以看到, 虽然DEFAULT_PREF
与newConnPref
的 idleTimeout 不再相同, 但它们的 auth 对象也指向了同一个对象!所以DEFAULT_PREF.auth.user
也发生了变化!
解决思路
对于上面的问题,看来确保newConnPref
一定要与DEFAULT_PREF
完全不是一个对象才行,那么对对象使用深度复制是一个非常直观的方案,这里可以使用lodash这个库的_.cloneDeep
函数对对象进行深度复制, 比如:
|
|
由于_.cloneDeep
会返回一个全新深度复制的对象,newConnPref
指向的对象完全和DEFAULT_PREF
不一样了,所以后续对 newConnPref 做任何修改也不会影响DEFAULT_PREF
对象。
上面的方法已经工作的相当好了, 还有没有其他方法?
使用函数返回默认值
我们可以把上述代码改造一下, 使用一个函数返回默认值,比如:
|
|
可以看到, 这种方式,pref1
与pref2
也完全指向不同的对象,对它们的修改并不会影响彼此。
比较
使用函数返回默认值对象与使用深拷贝本质上差别不大,目的都是构造对象的深度复制版本,对于使用函数返回默认的方法,js 的运行时可能进行一定的内部优化(比如写时复制技术等),而使用_.cloneDeep
则保证了对象在第一时间深度复制,潜在性能上使用函数返回默认值有可能会在实际运行时更好一些(当然需要在频繁使用 getDefaultPref()的场景下才有区别,如果并不经常使用方法获取默认值,则区别不是特别大。)