资讯专栏INFORMATION COLUMN

一个关于对象引用的bug引发的对于引用类型及数组的简单思考

lijinke666 / 2590人阅读

摘要:图示如下而对于引用类型的复制可不是这样这个复制只是将的引用赋值给,二者是属于同一个引用,访问的都是堆内存中的同一个对象,任何一个该引用的变量发生变化,会对其余使用该引用的变量也发生变化。

这两天自己在写代码的时候,出现一个BUG,代码如下:

    class  Car {
        constructor(carId) {
            this.position = [114, 130]
            this.path = []
            this.speed = Math.floor(Math.random() * 5)
            this.timer = null
        }
        run(){
            this.position[0] += this.speed * (Math.random() * 2 == 1 ? 1 : -1)
            this.position[1] += this.speed * (Math.random() * 2 == 1 ? 1 : -1)
            this.path.push(this.position)
            if(this.path.length > 10){
                this.path.shift()
            }
        }
        start(){
            this.timer = setInterval(function(){
                this.run()
            }, 1000)
        }

        stop(){
            clearInterval(this.timer)
        }
    }
    
    var car = new Car("10086")
    car.start()

代码预期的结果是,记录car的最近10个坐标点。
但是实际结果大失所望,得出的是10个一模一样的坐标点,原因在于调用run方法时,其中坐标的改变是基于其属性position这个数组对象的改变,而数组对象的变量名其实是对数组对象地址的引用,因此导致了最后一个坐标的改变引起了所有坐标的改变。
通过这个BUG对自己的基础知识又进行了一次梳理,归纳以及总结,参考资料为JavaScript高级程序设计:

知识梳理 javascript变量的数据类型:

1:基础类型 : Undefined、null、Boolean、Number和String
2:引用类型 : object

其中引用类型的赋值操作需要注意,因为引用类型的值是按引用访问的,且具有动态属性,会根据取得其引用的变量的操作而改变该引用的内存对象发生改变。取复制变量的例子用图示的方法来解释:
如下代码:

    var num1 = 5
    var num2 = num1

基本类型的赋值就相当于创建一个num1的副本,同时将num2的值等于该副本,两个变量之间的操作互不影响。
图示如下:

而对于引用类型的复制可不是这样

    var num1 = obj1
    var num2 = num1

这个复制只是将num1的引用赋值给num2,二者是属于同一个引用,访问的都是堆内存中的同一个对象,任何一个该引用的变量发生变化,会对其余使用该引用的变量也发生变化。

函数参数的传参

在JS中函数参数的传参方式都是按值传参的
可以近似看成函数内部声明一个局部变量名为参数名字的变量,同时为其赋值为参数的值,参数为引用类型则较为复杂些,主要是按值传递比较难理解。

传递的参数为引用类型的话,即函数内部该参数发生了改变会引起堆内存对象的属性发生改变,那么为什么不叫按引用访问,资料中有如下代码进行解释:

function setName(obj) {
    obj.name = "Nicholas";
    obj = new Object();
    obj.name = "Greg";
}
var person = new Object();
setName(person);
alert(person.name); //"Nicholas"

书中的解释比较简洁,个人理解为如果是按引用传参则会发生堆内存的对象会发生改变,由原本的实例person将被新的new Object的实例,同时将其属性name置为"Greg",即最终obj指向的是new Object的实例,而事实上没有,可以理解为函数的引用类型的参数为引用类型的引用,且这里对引用的处理方式是类似基本类型的值一般,不会发生变化。

图示如下:

归纳总结

基础知识梳理完毕,回到我的BUG,犯的错误就是引用类型的访问方式的错误,path所push的position数组准确来说指向的都是同一个对象,因此position的每次变化,数组中所有的元素都会发生相同的变化,导致path数组的元素均为一致.

为此对数组的方法进行一次归纳,将数组中可以返回新数组副本(即对原数组无影响)的方法,以方便避免像我这种使用导致的BUG

返回新数组副本方法:concat, slice, splice, filter, map

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/90560.html

相关文章

  • 翻译连载 | JavaScript轻量级函数式编程-第6章:值不可变性 |《你不知道JS》姊妹篇

    摘要:但在开始之前应该心中有数值的不可变性并不是说我们不能在程序编写时不改变某个值。这些都是对值的不可变这个概念的误解。程序的其他部分不会影响的赋值。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTML 最坚实的梁柱;分享,是 CSS 里最闪...

    ysl_unh 评论0 收藏0
  • 崩溃bug日志总结1

    摘要:项目中异常分析引发崩溃日志的流程分析解决办法常见的出现场景状态异常非法线程操作。引发崩溃日志的流程分析解释如下所示,释放与此位图关联的本机对象,并清除对像素数据的引用。 目录介绍 1.1 java.lang.UnsatisfiedLinkError找不到so库异常 1.2 java.lang.IllegalStateException非法状态异常 1.3 android.conten...

    Scott 评论0 收藏0
  • JavaScript 编程精解 中文第三版 八、Bug 和错误

    摘要:幸运的是,使用符号创建的构造器,如果在不使用来调用,则始终会报错,即使在非严格模式下也不会产生问题。 来源:ApacheCN『JavaScript 编程精解 中文第三版』翻译项目原文:Bugs and Errors 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 部分参考了《JavaScript 编程精解(第 2 版)》 调试的难度是开始编写代码的两倍。 因此,如...

    wujl596 评论0 收藏0
  • vue 子组件修改props引用类型参数引发思考

    摘要:但是当传递的参数为对象或者数组的时候,是通过引用传入的,所以对于一个引用类型的来说,在子组件中改变这个参数本身将会影响到父组件的数据状态。 问题 父级组件与子组件的通信一般都是通过props来实现的,因为数据流向的单一才能保证数据变化的可追踪性,在vue中props遵循的是单向数据流,原则上子组件修改props是不被允许的。但是当props传递的参数为对象或者数组的时候,是通过引用传入...

    周国辉 评论0 收藏0
  • 崩溃bug日志总结2

    摘要:出现错误引发崩溃日志的流程分析这个错误是应用的方法总数限制造成的。 目录介绍 1.1 java.lang.ClassNotFoundException类找不到异常 1.2 java.util.concurrent.TimeoutException连接超时崩溃 1.3 java.lang.NumberFormatException格式转化错误 1.4 java.lang.Illegal...

    sutaking 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<