我们知道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; i4da306229a365364db03fbf6bb175852 { 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中传递和拷贝详解的详细内容。更多信息请关注PHP中文网其他相关文章!

去掉重复并排序的方法:1、使用“Array.from(new Set(arr))”或者“[…new Set(arr)]”语句,去掉数组中的重复元素,返回去重后的新数组;2、利用sort()对去重数组进行排序,语法“去重数组.sort()”。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于Symbol类型、隐藏属性及全局注册表的相关问题,包括了Symbol类型的描述、Symbol不会隐式转字符串等问题,下面一起来看一下,希望对大家有帮助。

怎么制作文字轮播与图片轮播?大家第一想到的是不是利用js,其实利用纯CSS也能实现文字轮播与图片轮播,下面来看看实现方法,希望对大家有所帮助!

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于对象的构造函数和new操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

方法:1、利用“点击元素对象.unbind("click");”方法,该方法可以移除被选元素的事件处理程序;2、利用“点击元素对象.off("click");”方法,该方法可以移除通过on()方法添加的事件处理程序。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于面向对象的相关问题,包括了属性描述符、数据描述符、存取描述符等等内容,下面一起来看一下,希望对大家有帮助。

foreach不是es6的方法。foreach是es3中一个遍历数组的方法,可以调用数组的每个元素,并将元素传给回调函数进行处理,语法“array.forEach(function(当前元素,索引,数组){...})”;该方法不处理空数组。

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于BOM操作的相关问题,包括了window对象的常见事件、JavaScript执行机制等等相关内容,下面一起来看一下,希望对大家有帮助。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

Atom编辑器mac版下载
最流行的的开源编辑器

Dreamweaver Mac版
视觉化网页开发工具

安全考试浏览器
Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

DVWA
Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),