目錄
拷貝
數組的拷貝
物件的拷貝
#相關的概念
首頁 web前端 js教程 js傳遞和拷貝詳解

js傳遞和拷貝詳解

Mar 13, 2018 pm 04:12 PM
javascript 拷貝 詳解

我們知道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] === &#39;object&#39;) ? 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 = &#39;cat&#39;
    newAnimal.name = &#39;Miao&#39;
    return newAnimal
}

let wang = {
    type: &#39;dog&#39;,
    name: &#39;Wang&#39;
}

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函數的按值傳遞參數

JavaScript參數傳遞圖解教學

php和js中json傳遞的簡析

#

以上是js傳遞和拷貝詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Win11管理員權限取得詳解 Win11管理員權限取得詳解 Mar 08, 2024 pm 03:06 PM

Windows作業系統是全球最受歡迎的作業系統之一,其新版本Win11備受矚目。在Win11系統中,管理員權限的取得是一個重要的操作,管理員權限可以讓使用者對系統進行更多的操作和設定。本文將詳細介紹在Win11系統中如何取得管理員權限,以及如何有效地管理權限。在Win11系統中,管理員權限分為本機管理員和網域管理員兩種。本機管理員是指具有對本機電腦的完全管理權限

Oracle SQL中的除法運算詳解 Oracle SQL中的除法運算詳解 Mar 10, 2024 am 09:51 AM

OracleSQL中的除法運算詳解在OracleSQL中,除法運算是一種常見且重要的數學運算運算,用來計算兩個數相除的結果。除法在資料庫查詢中經常用到,因此了解OracleSQL中的除法運算及其用法是資料庫開發人員必備的技能之一。本文將詳細討論OracleSQL中除法運算的相關知識,並提供具體的程式碼範例供讀者參考。一、OracleSQL中的除法運算

PHP模運算子的作用及用法詳解 PHP模運算子的作用及用法詳解 Mar 19, 2024 pm 04:33 PM

PHP中的模運算子(%)是用來取得兩個數值相除的餘數的。在本文中,我們將詳細討論模運算子的作用及用法,並提供具體的程式碼範例來幫助讀者更好地理解。 1.模運算子的作用在數學中,當我們將一個整數除以另一個整數時,就會得到一個商和一個餘數。例如,當我們將10除以3時,商數為3,餘數為1。模運算子就是用來取得這個餘數的。 2.模運算子的用法在PHP中,使用%符號來表示模

linux系統呼叫system()函數詳解 linux系統呼叫system()函數詳解 Feb 22, 2024 pm 08:21 PM

Linux系統呼叫system()函數詳解系統呼叫是Linux作業系統中非常重要的一部分,它提供了一種與系統核心互動的方式。其中,system()函數是常用的系統呼叫函數之一。本文將詳細介紹system()函數的使用方法,並提供對應的程式碼範例。系統呼叫的基本概念系統呼叫是使用者程式與作業系統核心互動的一種方式。使用者程式透過呼叫系統呼叫函數來請求作業系統

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

Linux的curl指令詳解 Linux的curl指令詳解 Feb 21, 2024 pm 10:33 PM

Linux的curl命令詳解摘要:curl是一種強大的命令列工具,用於與伺服器進行資料通訊。本文將介紹curl指令的基本用法,並提供實際的程式碼範例,幫助讀者更好地理解和應用該指令。一、curl是什麼? curl是命令列工具,用於發送和接收各種網路請求。它支援多種協議,如HTTP、FTP、TELNET等,並提供了豐富的功能,如檔案上傳、檔案下載、資料傳輸、代

六張圖講清楚Linux零拷貝技術 六張圖講清楚Linux零拷貝技術 Feb 22, 2024 pm 06:40 PM

大家好,今天讓我們來聊聊Linux零拷貝技術。我們將以sendfile系統呼叫作為切入點,深入探討零拷貝技術的基本原理。零拷貝技術的核心思想是盡量減少資料在記憶體之間的複製,透過優化資料傳輸路徑,提高資料傳輸的效率和效能。 1.零拷貝技術簡介Linux零拷貝技術是一種用於最佳化資料傳輸的技術,透過減少資料在內核態和使用者態之間的複製次數,進而提高資料傳輸的效率。在資料傳輸的過程中,通常需要將資料從核心緩衝區複製到應用程式的緩衝區,再從應用程式緩衝區複製到網路裝置的緩衝區,最終才能完成傳送。零拷貝技術的優

深入了解Promise.resolve() 深入了解Promise.resolve() Feb 18, 2024 pm 07:13 PM

Promise.resolve()詳解,需要具體程式碼範例Promise是JavaScript中一種用來處理非同步操作的機制。在實際開發中,常常需要處理一些需要依序執行的非同步任務,而Promise.resolve()方法就是用來傳回一個已經Fulfilled狀態的Promise物件。 Promise.resolve()是Promise類別的靜態方法,它接受一個

See all articles