首页 > js教程 > 正文

js中传递和拷贝详解

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

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

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

let obj1 = {
    a : '1',
    b : 2
} 
let obj2 = obj1
obj2.b = 3
console.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) //@1
obj1.a = 6
console.log(obj2.a) //@2
obj2.b.c = 666
console.log(obj1.b.c) //@3
obj2.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] = arr1
    arr2[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 = 6
    console.log(obj1,obj2)
  • …运算符

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

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

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

  • 转为字符串再转回对象

    let obj1 = {a:1,b:{c:2}}
    let obj2 = JSON.parse(JSON.stringify(obj1))
    obj1.b.c = 6
    console.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 = 666
let y = x
let m = 'abc'
let n = m
y = 888
n = 'def'
console.log(x, y)//666,888
console.log(m, n)//'abc','def'

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

let obj1 = {
    a : '1',
    b : 2
} 
let obj2 = obj1
obj2.b = 3
console.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) //@1
obj1.a = 6
console.log(obj2.a) //@2
obj2.b.c = 666
console.log(obj1.b.c) //@3
obj2.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] = arr1
    arr2[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 = 6
    console.log(obj1,obj2)
  • …运算符

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

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

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

  • 转为字符串再转回对象

    let obj1 = {a:1,b:{c:2}}
    let obj2 = JSON.parse(JSON.stringify(obj1))
    obj1.b.c = 6
    console.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中传递和拷贝详解的详细内容,更多请关注php中文网其它相关文章!

  • 相关标签:javascript 详解 拷贝
  • 本文原创发布php中文网 ,转载请注明出处,感谢您的尊重!
  • 独孤九贱(4)_PHP视频教程

    江湖传言:PHP是世界上最好的编程语言。真的是这样吗?这个梗究竟是从哪来的?学会本课程,你就会明白了。 PHP中文网出品的PHP入门系统教学视频,完全从初学者的角度出发,绝不玩虚的,一切以实用、有用...

    独孤九贱(5)_ThinkPHP5视频教程

    ThinkPHP是国内最流行的中文PHP开发框架,也是您Web项目的最佳选择。《php.cn独孤九贱(5)-ThinkPHP5视频教程》课程以ThinkPHP5最新版本为例,从最基本的框架常识开始,将...

    ThinkPHP5实战之[教学管理系统]

    本套教程,以一个真实的学校教学管理系统为案例,手把手教会您如何在一张白纸上,从零开始,一步一步的用ThinkPHP5框架快速开发出一个商业项目。

    PHP入门视频教程之一周学会PHP

    所有计算机语言的学习都要从基础开始,《PHP入门视频教程之一周学会PHP》不仅是PHP的基础部分更主要的是PHP语言的核心技术,是学习PHP必须掌握的内容,任何PHP项目的实现都离不开这部分的内容,通...

    独孤九贱(1)_HTML5视频教程

    《php.cn原创html5视频教程》课程特色:php中文网原创幽默段子系列课程,以恶搞,段子为主题风格的php视频教程!轻松的教学风格,简短的教学模式,让同学们在不知不觉中,学会了HTML知识。 ...

    ThinkPHP5快速开发企业站点[全程实录]更新中...

    本课以最新版ThinkPHP5.0.10为基础进行开发,全程实录一个完整企业点,从后台到前台,从控制器到路由的全套完整教程,不论是你是新人,还是有一定开发经验的程序员,都可以从中学到实用的知识~~

    Thinkphp3.2.3个人博客开发

    ThinkPHP是一个快速、开源的轻量级国产PHP开发框架,是业内最流行的PHP框架之一。本课程以博客系统为例,讲述如何使用TP实战开发,从中学习Thinkphp的实践应用。模版下载地址:http:/...

    PHP实战天龙八部之仿爱奇艺电影网站

    本课程是php实战开发课程,以爱奇艺电影网站为蓝本从零开发一个自己的网站。目的是让大家了解真实项目的架构及开发过程

    独孤九贱(8)_php从零开始开发属于自己的php框架

    本课以一个极简的PHP开发框架为案例,向您展示了一个PHP框架应该具有的基本功能,以及具体的实现方法,让您快速对PHP开发框架的底层实现有一个清楚的认识,为以后学习其实的开发框架打下坚实的基础。

    独孤九贱(3)_JavaScript视频教程

    javascript是运行在浏览器上的脚本语言,连续多年,被评为全球最受欢迎的编程语言。是前端开发必备三大法器中,最具杀伤力。如果前端开发是降龙十八掌,好么javascript就是第18掌:亢龙有悔。...

    直播实录:PHP魔鬼训练营[从零开始制作个人博客]

    本站9月直播课已经结束,本套教程是直播实录,没有报上名或者漏听学员福利来了,赶紧看看吧,说不定这里就有你的菜

    2018前端入门_HTML5

    轻松明快,简洁生动,让你快速走入HTML5的世界,体会语义化开发的魅力

    JavaScript极速入门_玉女心经系列

    JavaScript能够称得上是史上使用最广泛的编程语言,也是前端开发必须掌握的三技能之一:描述网页内容的HTML、描述网页样式的CSS以及描述网页行为的JavaScript。本章节将帮助大家迅速掌握...

    PHP用户注册登录系统视频教程

    《php用户注册登录系统》主要介绍网站的登录注册功能,我们会从最简单的实现登录注册功能开始,增加验证码,cookie验证等,丰富网站的登录注册功能

    独孤九贱(7)_Bootstrap视频教程

    Bootstrap 是最受欢迎的 HTML、CSS 和 JS 框架,用于开发响应式布局、移动设备优先的 WEB 项目。为所有开发者、所有应用场景而设计,它让前端开发更快速、简单,所有开发者都能快速上手...

    PHP学生管理系统视频教程

    《PHP学生管理系统视频教程》主要给大家讲解了HTML,PHP,MySQL之间的相互协作,实现动态的网页显示和获取数据.

    独孤九贱(2)_CSS视频教程

    《php.cn独孤九贱(2)-css视频教程》课程特色:php中文网原创幽默段子系列课程,以恶搞,段子为主题风格的php视频教程!轻松的教学风格,简短的教学模式,让同学们在不知不觉中,学会了CSS知识...

    弹指间学会HTML视频教程

    《弹指间学会HTML视频教程》从最基本的概念开始讲起,步步深入,带领大家学习HTML,了解各种常用标签的意义以及基本用法,学习HTML知识为以后的学习打下基础

    独孤九贱(6)_jQuery视频教程

    jQuery是一个快速、简洁的JavaScript框架。设计的宗旨是“write Less,Do More”,即倡导写更少的代码,做更多的事情。它封装JavaScript常用的功能代码,提供一种简便的...

    最新微信小程序开发视频教程

    《最新微信小程序开发视频教程》本节课程是由微趋道录制,讲述了如何申请一个微信小程序,以及开发中需要使用哪些工具,和需要注意哪些等。

    • 小云云

      学生

    • 想不好签名了...
    • 5865篇
      文章总数
    • 113
      文章总浏览数

    头条

    推荐视频教程

  • javascript初级视频教程
  • jquery 基础视频教程
  • javascript三级联动视频教程
  • 独孤九贱(3)_JavaScript视频教程
  • 独孤九贱(6)_jQuery视频教程
  • 最新更新