這兩天本身在寫代碼的時候,出現一個BUG,代碼以下:javascript
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高級程序設計:java
1:基礎類型 : Undefined、null、Boolean、Number和String
2:引用類型 : object數組
其中引用類型的賦值操做須要注意,由於引用類型的值是按引用訪問的,且具備動態屬性,會根據取得其引用的變量的操做而改變該引用的內存對象發生改變。取複製變量的例子用圖示的方法來解釋:
以下代碼:dom
var num1 = 5 var num2 = num1
基本類型的賦值就至關於建立一個num1的副本,同時將num2的值等於該副本,兩個變量之間的操做互不影響。
圖示以下:函數
而對於引用類型的複製可不是這樣this
var num1 = obj1 var num2 = num1
這個複製只是將num1的引用賦值給num2,兩者是屬於同一個引用,訪問的都是堆內存中的同一個對象,任何一個該引用的變量發生變化,會對其他使用該引用的變量也發生變化。spa
在JS中函數參數的傳參方式都是按值傳參的
能夠近似當作函數內部聲明一個局部變量名爲參數名字的變量,同時爲其賦值爲參數的值,參數爲引用類型則較爲複雜些,主要是按值傳遞比較難理解。設計
傳遞的參數爲引用類型的話,即函數內部該參數發生了改變會引發堆內存對象的屬性發生改變,那麼爲何不叫按引用訪問,資料中有以下代碼進行解釋:code
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