对象的浅拷贝与深拷贝
我们可以用一个简单的例子来说明浅拷贝和深拷贝的区别。
假设有一个原始的对象 obj
,它有一个属性 name
和一个引用类型属性 body
,body
又包含了身高、体重和血型三个属性。对象的结构如下:
const obj = {
name: 'Tom',
body: {
height: 180,
weight: 70,
bloodType: 'A'
}
}
浅拷贝
浅拷贝使用 Object.assign()
或者展开运算符 ...
进行复制,它会复制对象本身的属性值,但对于对象中的对象或数组等引用类型的属性值,它只是将其引用地址复制了一份。这就意味着,新对象和原对象的引用类型属性其实是同一个对象。
下面是浅拷贝的例子:
javascriptCopy code
const shallowCopy = Object.assign({}, obj);
// 或者使用展开运算符
// const shallowCopy = { ...obj };
现在 shallowCopy
是一个新的对象,它和 obj
具有相同的属性和属性值。我们可以在 shallowCopy
中修改身高的值:
arduinoCopy code
shallowCopy.body.height = 190;
这时,obj
对象的身高属性也会发生变化,因为 shallowCopy
和 obj
其实共享同一个身体信息对象。可以使用 console.log
来查看它们之间的关系:
arduinoCopy code
console.log(shallowCopy.body === obj.body); // true
深拷贝
深拷贝会递归地将原对象中的所有引用类型的属性值都复制一份,这样就可以得到一个与原对象完全独立的新对象,修改新对象中的引用类型属性不会影响原对象中的属性值。
下面是深拷贝的例子:
javascriptCopy code
const deepCopy = JSON.parse(JSON.stringify(obj));
这里使用了 JSON 序列化和反序列化来进行深拷贝。JSON.stringify()
方法可以将对象转换为字符串,而 JSON.parse()
方法则可以将字符串转换为对象,这样就得到了一个全新的对象。
现在我们可以在 deepCopy
中修改身高的值:
arduinoCopy code
deepCopy.body.height = 200;
这时,obj
对象的身高属性不会受到影响,因为 deepCopy
和 obj
的身体信息对象是两个不同的对象,它们互相独立。可以使用 console.log
来查看它们之间的关系:
arduinoCopy code
console.log(deepCopy.body === obj.body); // false
希望这个例子能帮助你更好地理解浅拷贝和深拷贝。
同步与异步
在 JavaScript 中,同步和异步是两种不同的代码执行模式。
同步代码是按照代码的书写顺序依次执行,每一行代码都要等待上一行代码执行完毕后才能执行,直到所有的代码都执行完毕,程序才能继续往下执行。同步代码会阻塞后面代码的执行,如果前面的代码执行时间很长,会导致整个程序运行变慢,甚至出现假死的情况。
异步代码则不同,它是在后台执行,不会阻塞主线程,当异步操作完成后,会将结果返回给主线程,主线程再去执行对应的回调函数。异步代码一般使用回调函数、Promise、async/await 等方式来实现。
JavaScript 中常见的异步操作包括:
-
定时器:使用
setTimeout
或setInterval
方法可以在指定的时间后执行某个函数,例如:javascriptCopy code console.log('start'); setTimeout(() => { console.log('after 2 seconds'); }, 2000); console.log('end');
上面的代码中,
setTimeout
方法会在 2 秒后执行回调函数,但是主线程不会阻塞,继续执行下面的代码。 -
回调函数:在异步操作完成后,会将结果作为参数传递给回调函数进行处理。例如:
javascriptCopy code function getData(callback) { setTimeout(() => { const data = { name: 'Tom', age: 18 }; callback(data); }, 2000); } console.log('start'); getData((data) => { console.log(data); }); console.log('end');
上面的代码中,
getData
函数会在 2 秒后返回一个数据对象,并将数据对象作为参数传递给回调函数进行处理。 -
Promise:使用 Promise 可以更方便地处理异步操作,可以通过
then
方法注册成功的回调函数,通过catch
方法注册失败的回调函数,例如:javascriptCopy code function getData() { return new Promise((resolve, reject) => { setTimeout(() => { const data = { name: 'Tom', age: 18 }; resolve(data); }, 2000); }); } console.log('start'); getData().then((data) => { console.log(data); }).catch((error) => { console.log(error); }); console.log('end');
上面的代码中,
getData
函数返回一个 Promise 对象,可以使用then
方法注册成功的回调函数,使用catch
方法注册失败的回调函数。当 Promise 对象的状态发生变化时,对应的回调函数会被执行。
异步代码的优势在于它可以提高程序的响应速度,避免阻塞主线程,提升用户体验。但是在编写异步代码时,需要注意异步代码的执行顺序,避免出现意外的结果。
原文链接:https://juejin.cn/post/7214400156594569275 作者:二木同学_