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

这两天自己在写代码的时候,出现一个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

相关文章
相关标签/搜索
每日一句
    每一个你不满意的现在,都有一个你没有努力的曾经。
公众号推荐
   一个健康类的公众号,欢迎关注
小青桔健康