基础类型与引用(地址)
例子一
let a = 'l am a string'
let b = 6
let obj = {
c: 'l am an obj',
d: 123
}
let obj1 = obj
console.log(obj1) // { c: 'l am an obj', d: 123 }
obj1.c = 'l am an obj1'
console.log(obj) // { c: 'l am an obj1', d: 123 }
console.log(obj1) // { c: 'l am an obj1', d: 123 }
变量obj存的是地址address01,执行let obj1 = obj
后,变量obj1存的还是address01。就好像主卧室(obj),或大房间(obj1),都指的是房间号为address01的房间。所以,你说“换掉大房间的床”,obj1.c = 'l am an obj1'
,等同于“换掉主卧室的床”,因为都是“换掉房间号为address01的房间里的床”。
例子二
let a = 'l am a string'
let b = 6
let obj = {
c: 'l am an obj',
d: 123
}
let obj1 = {}
obj1.c = obj.c
obj1.d = obj.d
console.log(obj1) // { c: 'l am an obj', d: 123 }
obj1.c = 'l am an obj1'
console.log(obj) // { c: 'l am an obj', d: 123 }
console.log(obj1) // { c: 'l am an obj1', d: 123 }
变量obj存的是地址address01,执行let obj1 = {}
后,变量obj1得到了一个新的地址address02。现在不仅有房间号为address01的主卧室(obj),还有房间号为address02的次卧(obj1),在那里你可以布置你自己的新房间。你可以先说“要一张和主卧一样的床”,obj1.c = obj.c
,再说“还要一台和主卧一样的灯”,obj1.d = obj.d
,最后你又觉得床不舒服,“换了张床”,obj1.c = 'l am an obj1'
,但这些都不会影响主卧的床obj.c
。
例子三
let a = 'l am a string'
let b = 6
let obj = {
c: 'l am an obj',
d: 123,
e: [1, 3, 5]
}
let obj1 = {}
obj1.c = obj.c
obj1.d = obj.d
obj1.e = obj.e
console.log(obj1) // { c: 'l am an obj', d: 123, e: [ 1, 3, 5 ] }
obj1.c = 'l am an obj1'
obj1.e[0] = 2
console.log(obj) // { c: 'l am an obj', d: 123, e: [ 2, 3, 5 ] }
console.log(obj1) // { c: 'l am an obj1', d: 123, e: [ 2, 3, 5 ] }
主卧里面还可以有卫生间obj.e的呢,obj.e存的也是地址adress03,只要是对象或数组都存的是地址。这时obj1.e = obj.e
,存的就是主卧的卫生间的房间号address03。目前只有一个卫生间,你知道卫生间的地址adress03,保存在obj1.e中,然后你就知道如何“把卫生间的热水器换掉”,obj1.e[0] = 2
。
例子四
let a = 'l am a string'
let b = 6
let obj = {
c: 'l am an obj',
d: 123,
e: [1, 3, 5]
}
let obj1 = {}
obj1.c = obj.c
obj1.d = obj.d
obj1.e = obj.e
console.log(obj1) // { c: 'l am an obj', d: 123, e: [ 1, 3, 5 ] }
obj1.c = 'l am an obj1'
obj1.e = 'l am not an arr'
console.log(obj) // { c: 'l am an obj', d: 123, e: [ 1, 3, 5 ] }
console.log(obj1) // { c: 'l am an obj1', d: 123, e: 'l am not an arr' }
后来你觉得知道主卧的卫生间的地址adress03有什么用,有那闲工夫还不如用来放个柜子,故obj1.e = 'l am not an arr'
,这样做以后,没有了卫生间的地址,你就不能在主卧的卫生间里捣乱了。
例子五
let a = 'l am a string'
let b = 6
let obj = {
c: 'l am an obj',
d: 123,
e: [1, 3, 5]
}
let obj1 = {}
obj1.c = obj.c
obj1.d = obj.d
obj1.e = []
for (let i = 0; i < obj.e.length; i++) {
obj1.e[i] = obj.e[i]
}
console.log(obj1) // { c: 'l am an obj', d: 123, e: [ 1, 3, 5 ] }
obj1.c = 'l am an obj1'
obj1.e[0] = 2
console.log(obj) // { c: 'l am an obj', d: 123, e: [ 1, 3, 5 ] }
console.log(obj1) // { c: 'l am an obj1', d: 123, e: [ 2, 3, 5 ] }
其实你还是想要个独立卫生间的,obj1.e = []
,这样你得到了房间号为adress04的房间,然后你按照主卧卫生间的样子来改造这个房间for (let i = 0; i < obj.e.length; i++) { obj1.e[i] = obj.e[i] }
。这时候有两个独立的卫生间了,房间号分别是adress04和adress03,尽管布局装饰一样,但还是两个房间,宾馆里的房间还都一样呢,但它们有不同的房间号,实际上是相互独立的房间。你换adress04房间的热水器,obj1.e[0] = 2
,并不会影响房间adress03房间。
抽象例子五的复制过程
即深拷贝
let a = 'l am a string'
let b = 6
let obj = {
c: 'l am an obj',
d: 123,
e: [1, 3, 5]
}
function deepCopy(val) {
if (!val || typeof val !== 'object') {
return val
}
let copy = Array.isArray(val) ? [] : {}
for (key in val) {
if (val.hasOwnProperty(key)) {
if (val[key] && typeof val[key] === 'object') {
copy[key] = deepCopy(val[key])
} else {
copy[key] = val[key]
}
}
}
return copy
}
let obj1 = deepCopy(obj)
obj1.c = 'l am an obj1'
obj1.e[0] = 2
console.log(obj) // { c: 'l am an obj', d: 123, e: [ 1, 3, 5 ] }
console.log(obj1) // { c: 'l am an obj1', d: 123, e: [ 2, 3, 5 ] }
例子六
let a = 'l am a string'
let b = 6
let obj = {
c: 'l am an obj',
d: 123,
e: {}
}
obj.e.parent = obj
let keyValArr = []
let copyArr = []
function deepCopy(val) {
if (!val || typeof val !== 'object') {
return val
}
let copy = Array.isArray(val) ? [] : {}
let index = keyValArr.indexOf(val)
if (index === -1) {
keyValArr.push(val)
copyArr.push(copy)
} else {
return copyArr[index]
}
for (key in val) {
if (val.hasOwnProperty(key)) {
if (val[key] && typeof val[key] === 'object') {
copy[key] = deepCopy(val[key])
} else {
copy[key] = val[key]
}
}
}
return copy
}
let obj1 = deepCopy(obj)
obj1.e.parent.c = 'l am an obj1'
console.log(obj) // { c: 'l am an obj', d: 123, e: { parent: [Circular] } }
console.log(obj1) // { c: 'l am an obj1', d: 123, e: { parent: [Circular] } }
总有人想寻根问祖,obj.e.parent = obj
,如此形成了环,之前的代码就要重新调整。简单来说,就是把复制过程中的源对象[obj, obj.e]
都记录下来,以及对应的拷贝对象[copy, copy.e]
,碰到属性值是之前复制过的对象的情况,obj.e.parent的属性值是obj,就把对应的拷贝对象给它,copy.e.parent = copy
,而不进行递归深拷贝。
从地址的角度来说,复制过程中,把地址都记录下来,用valArr记录源对象的地址[address01, address03],用copyArr记录拷贝对象的地址[address02, address04],当碰到需要拷贝的是之前记录过源对象的地址时,就把对应的拷贝对象的地址返回。例如obj.e.parent的属性值是obj的地址,那么拷贝过程中,obj1.e.parent的属性值就是obj1的地址。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。