js中传递和拷贝详解

我们知道js有几种基本的数据类型和其他的复杂数据类型包括(对象,数组,函数),基本数据类型的赋值其实就是值的拷贝,我们称之为值传递,赋值后的变量和原来的变量除了值相等之外并无其他关联。

let x = 666let y = xlet m = 'abc'let n = my = 888n = 'def'console.log(x, y)//666,888console.log(m, n)//'abc','def'

登录后复制登录后复制

复杂数据类型的传递并不是这样子的,因为将一个变量绑定到一个复杂数据类型的时候记录的并不是这个复杂数据的值,而是存放了这个数据的一个地址信息,当将这个变量赋值给另一个变量的时候只是将地址传递了过去,这两个变量指向的其实是一个数据信息,当改变任意一个变量的时候,另外的变量都会受到影响,这种传递方式我们称之为引用传递

let obj1 = {    a : '1',    b : 2} let obj2 = obj1obj2.b = 3console.log(obj1,obj2)//{a: "1", b: 3},{a: "1", b: 3}

登录后复制登录后复制

拷贝

我们知道复杂数据类型的赋值是引用传递,赋值前后的变量会相互影响,在实际项目中我们经常不希望这样子,譬如:

我们在一个view里面有两处用到了data(是一个Array),其一是一个list只要把data按顺序展示即可,其二是一个chart需要把data逆序之后再做数据处理,这时候我们就犯难了,如果直接data.reverse()之后第一处的列表也是逆序的了,这不是我们想见的,我们需要一个方法只复制数组的值,并且这个新数组和原数组的数据地址并不一样,这种复制方式我们称为数组的拷贝。

let obj1 = {a:1, b:{c:2}}let shallowCopy = (src)=> {    let dst = {}    for (let prop in src) {        if (src.hasOwnProperty(prop)) {          dst[prop] = src[prop]        }    }    return dst}let obj2 = shallowCopy(obj1)console.log(obj1,obj2) //@1obj1.a = 6console.log(obj2.a) //@2obj2.b.c = 666console.log(obj1.b.c) //@3obj2.b = {    c: 888}console.log(obj1.b.c) //@4

登录后复制登录后复制

上面的例子可以看出来obj1的第一层属性是复制属性值,没有继承地址的拷贝,但是第二层就是b属性确实共享一块内存地址的,这就是浅拷贝,但是在@4处obj1却没有收到obj2的影响,是因为属性b是一个对象,这种引用传递的重新赋值,计算机会重新分配一块新的内存来存放数据和记录地址信息,所以这时obj1.b.c和obj2.b.c已经不是记录的一个属性值了

也可以理解为:拷贝是之于传递的,直接对复杂数据类型进行赋值是引用传递,不能称之为拷贝,拷贝是对原数据的单纯的数据备份,数据的内存地址信息并不完全一样,这是因为拷贝还分为浅拷贝和深拷贝。

对复杂数据类型的非嵌套的拷贝,就是只拷贝第一层的数据信息的拷贝是浅拷贝,如果第一层的数据有复杂数据类型,则依然采用引用传递的方式,复制的仍然是地址信息,通过其他方式实现的数组对象等的多层嵌套拷贝就是深拷贝。

下面我们再来看下数组和对象如何来实现深浅拷贝:

数组的拷贝

slice方法

let arr1 = [1,2,[3,4]]let arr2 = arr1.slice(0)arr2[2].push(5)arr2.push(6)console.log(arr1,arr2)

登录后复制登录后复制

concat方法

let arr1 = [1,2,[3,4]]let arr2 = arr1.concat()arr2[2].push(5)arr2.push(6)console.log(arr1,arr2)

登录后复制登录后复制

for循环

let arr1 = [1,2,[3,4]]let arr2 = []for(let i = 0; i

登录后复制

…运算符

let arr1 = [1,2,[3,4]]let [...arr2] = arr1arr2[2].push(5)arr2.push(6)console.log(arr1,arr2)

登录后复制登录后复制

以上4种数组的拷贝都是浅拷贝,要实现数组的深拷贝就要递归实现

let deepClone = (src)=> {    let result    (src instanceof Array) ? (result = []) :(result = {})    for (let key in src) {        result[key] = (typeof src[key] === 'object') ? deepClone(src[key]) : src[key]//数组和对象的type都是object    }    return result}   let arr1 = [1,2,[3,4]]let arr2 = deepClone(arr1)arr2[2].push(5)arr2.push(6)console.log(arr1,arr2)

登录后复制登录后复制

可以发现用方面的方法arr1[2]和arr2[2]不一样,同样上面的深拷贝的方法也适用于对象

对象的拷贝

万能的for循环

let obj1 = {a:1,b:{c:2}}let obj2 = {}for(let key in obj1){    obj2[key] = obj1[key]}obj1.b.c = 6console.log(obj1,obj2)

登录后复制登录后复制

…运算符

let obj1 = {a:1,b:{c:2}}let {...obj2} = obj1obj1.b.c = 6console.log(obj1,obj2)

登录后复制登录后复制

Object.assign()

let obj1 = {a:1,b:{c:2}}let obj2 = Object.assign({},obj1)obj1.b.c = 6console.log(obj1,obj2)

登录后复制

上面3种方法是对象的浅拷贝,再介绍2种对象的深拷贝的方法:

转为字符串再转回对象

let obj1 = {a:1,b:{c:2}}let obj2 = JSON.parse(JSON.stringify(obj1))obj1.b.c = 6console.log(obj1,obj2)

登录后复制登录后复制

deepClone方法,就是上面的数组的deepClone方法

相关的概念

纯函数

给定函数一个输入返回一个唯一的输出,并且不对外部环境附带任何影响的函数我们称为纯函数,其内定义的变量在函数返回后都会被垃圾回收机制回收掉。

但是如果函数的参数是数组、对象或函数时,传入的是一个引用,对其操作会影响到原来的数据,这样子写的函数会产生附带的影响,使得可读性变低。

降低影响的方式就是对传入参数进行深度拷贝,并赋给一个新的变量,方式原来的参数被篡改。

我们来看一个纯函数的例子:

let pureFunc = (animal)=> {    let newAnimal = JSON.parse(JSON.stringify(animal))    newAnimal.type = 'cat'    newAnimal.name = 'Miao'    return newAnimal}let wang = {    type: 'dog',    name: 'Wang'}let miao = pureFunc(wang)console.log(wang,miao)

登录后复制登录后复制

通过上面的例子可以看到wang并没有被纯函数所改变值。

大家再来思考一下下面的例子,如果答对了说明你已经对本文所讲的东西有了很深刻的理解了(提醒大家一下—>引用的重新赋值)

let afterChange = (obj)=>{    obj.a = 6    obj = {        a: 8,        b: 9    }    return obj}let objIns = {    a: 1,    b: 2}let objIns2 = afterChange(objIns)console.log(objIns, objIns2)

登录后复制登录后复制

以上就是我对js的引用和传递的理解,如有不当之处敬请谅解,Thanks!

大家还可以看看一些其他的文章加深理解,我推荐这篇Explaining Value vs. Reference in Javascript。

我们在项目中经常会碰到将一个变量赋值给另外一个变量的情形,这其实就是js的传递,但是不同的数据类型赋值后表现并不一样,下面让我们一起来研究一下

传递

我们知道js有几种基本的数据类型和其他的复杂数据类型包括(对象,数组,函数),基本数据类型的赋值其实就是值的拷贝,我们称之为值传递,赋值后的变量和原来的变量除了值相等之外并无其他关联

let x = 666let y = xlet m = 'abc'let n = my = 888n = 'def'console.log(x, y)//666,888console.log(m, n)//'abc','def'

登录后复制登录后复制

复杂数据类型的传递并不是这样子的,因为将一个变量绑定到一个复杂数据类型的时候记录的并不是这个复杂数据的值,而是存放了这个数据的一个地址信息,当将这个变量赋值给另一个变量的时候只是将地址传递了过去,这两个变量指向的其实是一个数据信息,当改变任意一个变量的时候,另外的变量都会受到影响,这种传递方式我们称之为引用传递

let obj1 = {    a : '1',    b : 2} let obj2 = obj1obj2.b = 3console.log(obj1,obj2)//{a: "1", b: 3},{a: "1", b: 3}

登录后复制登录后复制

拷贝

我们知道复杂数据类型的赋值是引用传递,赋值前后的变量会相互影响,在实际项目中我们经常不希望这样子,譬如:

我们在一个view里面有两处用到了data(是一个Array),其一是一个list只要把data按顺序展示即可,其二是一个chart需要把data逆序之后再做数据处理,这时候我们就犯难了,如果直接data.reverse()之后第一处的列表也是逆序的了,这不是我们想见的,我们需要一个方法只复制数组的值,并且这个新数组和原数组的数据地址并不一样,这种复制方式我们称为数组的拷贝。

let obj1 = {a:1, b:{c:2}}let shallowCopy = (src)=> {    let dst = {}    for (let prop in src) {        if (src.hasOwnProperty(prop)) {          dst[prop] = src[prop]        }    }    return dst}let obj2 = shallowCopy(obj1)console.log(obj1,obj2) //@1obj1.a = 6console.log(obj2.a) //@2obj2.b.c = 666console.log(obj1.b.c) //@3obj2.b = {    c: 888}console.log(obj1.b.c) //@4

登录后复制登录后复制

上面的例子可以看出来obj1的第一层属性是复制属性值,没有继承地址的拷贝,但是第二层就是b属性确实共享一块内存地址的,这就是浅拷贝,但是在@4处obj1却没有收到obj2的影响,是因为属性b是一个对象,这种引用传递的重新赋值,计算机会重新分配一块新的内存来存放数据和记录地址信息,所以这时obj1.b.c和obj2.b.c已经不是记录的一个属性值了

也可以理解为:拷贝是之于传递的,直接对复杂数据类型进行赋值是引用传递,不能称之为拷贝,拷贝是对原数据的单纯的数据备份,数据的内存地址信息并不完全一样,这是因为拷贝还分为浅拷贝和深拷贝。

对复杂数据类型的非嵌套的拷贝,就是只拷贝第一层的数据信息的拷贝是浅拷贝,如果第一层的数据有复杂数据类型,则依然采用引用传递的方式,复制的仍然是地址信息,通过其他方式实现的数组对象等的多层嵌套拷贝就是深拷贝。

下面我们再来看下数组和对象如何来实现深浅拷贝:

数组的拷贝

slice方法

let arr1 = [1,2,[3,4]]let arr2 = arr1.slice(0)arr2[2].push(5)arr2.push(6)console.log(arr1,arr2)

登录后复制登录后复制

concat方法

let arr1 = [1,2,[3,4]]let arr2 = arr1.concat()arr2[2].push(5)arr2.push(6)console.log(arr1,arr2)

登录后复制登录后复制

for循环

let arr1 = [1,2,[3,4]]let arr2 = []for(let i = 0; i<arr1.length; i++){    arr2.push(arr1[i])}arr2[2].push(5)arr2.push(6)console.log(arr1,arr2)

登录后复制

…运算符

let arr1 = [1,2,[3,4]]let [...arr2] = arr1arr2[2].push(5)arr2.push(6)console.log(arr1,arr2)

登录后复制登录后复制

以上4种数组的拷贝都是浅拷贝,要实现数组的深拷贝就要递归实现

let deepClone = (src)=> {    let result    (src instanceof Array) ? (result = []) :(result = {})    for (let key in src) {        result[key] = (typeof src[key] === 'object') ? deepClone(src[key]) : src[key]//数组和对象的type都是object    }    return result}   let arr1 = [1,2,[3,4]]let arr2 = deepClone(arr1)arr2[2].push(5)arr2.push(6)console.log(arr1,arr2)

登录后复制登录后复制

可以发现用方面的方法arr1[2]和arr2[2]不一样,同样上面的深拷贝的方法也适用于对象

对象的拷贝

万能的for循环

let obj1 = {a:1,b:{c:2}}let obj2 = {}for(let key in obj1){    obj2[key] = obj1[key]}obj1.b.c = 6console.log(obj1,obj2)

登录后复制登录后复制

…运算符

let obj1 = {a:1,b:{c:2}}let {...obj2} = obj1obj1.b.c = 6console.log(obj1,obj2)

登录后复制登录后复制

Object.assign()

let obj1 = {a:1,b:{c:2}}let obj2 = Object.assign({},obj1)obj1.b.c = 6console.log(obj1,obj2)

登录后复制

上面3种方法是对象的浅拷贝,再介绍2种对象的深拷贝的方法:

转为字符串再转回对象

let obj1 = {a:1,b:{c:2}}let obj2 = JSON.parse(JSON.stringify(obj1))obj1.b.c = 6console.log(obj1,obj2)

登录后复制登录后复制

deepClone方法,就是上面的数组的deepClone方法

相关的概念

纯函数

给定函数一个输入返回一个唯一的输出,并且不对外部环境附带任何影响的函数我们称为纯函数,其内定义的变量在函数返回后都会被垃圾回收机制回收掉。

但是如果函数的参数是数组、对象或函数时,传入的是一个引用,对其操作会影响到原来的数据,这样子写的函数会产生附带的影响,使得可读性变低。

降低影响的方式就是对传入参数进行深度拷贝,并赋给一个新的变量,方式原来的参数被篡改。

我们来看一个纯函数的例子:

let pureFunc = (animal)=> {    let newAnimal = JSON.parse(JSON.stringify(animal))    newAnimal.type = 'cat'    newAnimal.name = 'Miao'    return newAnimal}let wang = {    type: 'dog',    name: 'Wang'}let miao = pureFunc(wang)console.log(wang,miao)

登录后复制登录后复制

通过上面的例子可以看到wang并没有被纯函数所改变值。

大家再来思考一下下面的例子,如果答对了说明你已经对本文所讲的东西有了很深刻的理解了(提醒大家一下—>引用的重新赋值)

let afterChange = (obj)=>{    obj.a = 6    obj = {        a: 8,        b: 9    }    return obj}let objIns = {    a: 1,    b: 2}let objIns2 = afterChange(objIns)console.log(objIns, objIns2)

登录后复制登录后复制

以上就是我对js的引用和传递的理解,如有不当之处敬请谅解,Thanks!

相关推荐:

js函数的按值传递参数

JavaScript参数传递图解教程

js函数参数的按值传递解释

以上就是js中传递和拷贝详解的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2783166.html

(0)
上一篇 2025年3月8日 16:45:12
下一篇 2025年3月8日 16:45:28

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 详解js在html中的加载执行顺序

    js在html中的加载执行顺序 1.加载顺序:引入标记的出现顺序, 页面上的js代码是js的一部分,所以Javascript在页面装载时执行的顺序就是其引入标记的出现顺序, 标记里面的或者通过src引入的外部JS,都是按照其语句出现的顺序执…

    编程技术 2025年4月4日
    100
  • HTML和JS实现简单的计算器

    HTML和JS实现计算器功能的也是很容易的,本文主要和大家分享HTML和JS实现简单的计算器,希望能帮助到大家。 下面是代码:    nbsp;html>无标题文档 var result=””; function jisuan(num…

    编程技术 2025年4月4日
    100
  • 实现在HTML页面加载完毕后运行js方法

    本文主要和大家分享实现在HTML页面加载完毕后运行js方法,主要以代码的方法和大家分享,希望能帮助到大家。 Js方法: window.onload=function (){ var userName=”xiaoming”; alert(us…

    编程技术 2025年4月4日
    100
  • django控件及传参使用详解

    这次给大家带来djangodjango及传参使用详解,django控件及传参使用的django有哪些,下面就是实战案例,一起来看一下。 本文对djangoHTML的表单控件中的单选及多选进行介绍,并说明如何进行参数传递。 1.HTML中的表…

    编程技术 2025年4月4日
    200
  • H5离线应用与客户端存储使用详解

    这次给大家带来H5离线应用与客户端存储使用详解,使用H5离线应用与客户端存储的注意事项有哪些,下面就是实战案例,一起来看一下。 支持离线 Web 应用开发是 HTML5 的另一个重点。所谓离线 Web 应用,就是在设备不能上网的情况下仍然可…

    编程技术 2025年4月4日
    200
  • pushState与replaceState使用步骤详解

    这次给大家带来pushState与replaceState使用步骤详解,pushState与replaceState使用的注意事项有哪些,下面就是实战案例,一起来看一下。 一、简介 HTML5引入了 history.pushState() …

    编程技术 2025年4月4日
    100
  • JavaScript实现获取远程的html到当前页面中

    今天做个项目,需要在当前的html页面中引用一个远程的html页面,百度了一下,发现一个非常好用的代码,这里分享给大家,有相同需求的小伙伴可以来看看 html代码 登录后复制 立即学习“Java免费学习笔记(深入)”; javascript…

    编程技术 2025年4月4日
    200
  • JavaScript模块优化

    给大家分享一下如何才能开发出更好的javascript模块和功能,有兴趣的朋友参考学习下吧。 不少人都曾经在 npm 上发布过自己开发的 JavaScript 模块,而在使用一些模块的过程中,我经常产生“这个模块很有用,但如果能 xxx 就…

    编程技术 2025年4月4日
    100
  • Laravel中where方法的基本用法详解

    Laravel中where方法的基本用法详解 Laravel是一款流行的PHP开发框架,提供了丰富的数据库操作方法,其中where方法是常用的一个之一。本文将详细介绍Laravel中where方法的基本用法,通过具体的代码示例来帮助读者更好…

    2025年4月2日
    100
  • 适用于JavaScript开发人员的10个必备VSCode扩展

    如今,visual studio code无疑是最流行的轻量级代码编辑器。它确实从其他代码编辑器那借鉴了很多,最主要是从sublime和atom那里。然而它的成功关键是源于能提供更好的性能和稳定的表现。 另外,它还提供了如代码智能提示等开发…

    2025年4月2日 编程技术
    200

发表回复

登录后才能评论