JavaScript的值傳遞和引用傳遞

  • 原文: Explaining Value vs. Reference in Javascript
  • 譯者: Fundebug

為了保證可讀性,本文采用意譯而非直譯。另外,本文版權歸原作者所有,翻譯僅用于學習。

JavaScript有5種基本的數據類型,分別是:布爾、null、undefined、String和Number。這些基本類型在賦值的時候是通過值傳遞的方式。值得注意的是還有另外三種類型: Array、Function和Object,它們通過引用來傳遞。從底層技術上看,它們三都是對象。

基本數據類型

如果一個基本的數據類型綁定到某個變量,我們可以認為該變量包含這個基本數據類型的值。

var x = 10;
var y = 'abc';
var z = null;
復制代碼

當我們使用=將這些變量賦值到另外的變量,實際上是將對應的值拷貝了一份,然后賦值給新的變量。我們把它稱作值傳遞

var x = 10;
var y = 'abc';var a = x;
var b = y;console.log(x, y, a, b) // 10, 'abc', 10, 'abc'
復制代碼

ax都包含10,by都包含'abc',并且它們是完全獨立的拷貝,互不干涉。如果我們將a的值改變,x不會受到影響。

var x = 10;
var y = 'abc';
var a = x;
var b = y;
a = 5;
b = 'def';
console.log(x, y, a, b); // 10, 'abc', 5, 'def'
復制代碼

對象

如果一個變量綁定到一個非基本數據類型(Array, Function, Object),那么它只記錄了一個內存地址,該地址存放了具體的數據。注意之前提到指向基本數據類型的變量相當于包含了數據,而現在指向非基本數據類型的變量本身是不包含數據的。

對象在內存中被創建,當我們聲明arr = [],我們在內存中創建了一個數組。arr記錄的是該內存的地址。

var arr = []; // (a)
arr.push(1); // (b)
復制代碼

當執行完(a)之后,內存中創建了一個空的數組對象,其內存地址為#001arr指向該地址。

變量地址對象
arr#001[]

當執行完(b)之后,數組對象中多了一個元素,但是數組的地址依然沒有變,arr也沒有變。

變量地址對象
arr#001[1]

引用傳遞

對象是通過引用傳遞,而不是值傳遞。也就是說,變量賦值只會將地址傳遞過去。

var reference = [1];
var refCopy = reference;
復制代碼
變量地址對象
reference#001[1]
refCopy#001

referencerefCopy指向同一個數組。 如果我們更新referencerefCopy也會受到影響。

reference.push(2);
console.log(reference, refCopy); // [1, 2], [1, 2]
復制代碼
變量地址對象
reference#001[1, 2]
refCopy#001

引用重新賦值

如果我們將一個已經賦值的變量重新賦值,那么它將包含新的數據或則引用地址。

var obj = { first: 'fundebug.com'};
obj = { second: 'fundebug.cn'};
復制代碼

obj從指向第一個對象變為指向第二個對象。

變量地址對象
obj#001{first: 'fundebug.com'}
#002{second: 'fundebug.cn'}

如果一個對象沒有被任何變量指向,就如第一個對象(地址為#001),JavaScript引擎的垃圾回收機制會將該對象銷毀并釋放內存。

== 和 ===

對于引用類型的變量,=====只會判斷引用的地址是否相同,而不會判斷對象具體里屬性以及值是否相同。因此,如果兩個變量指向相同的對象,則返回true

var arrRef = ['Hi!'];
var arrRef2 = arrRef;console.log(arrRef === arrRef2); // true
復制代碼

如果是不同的對象,及時包含相同的屬性和值,也會返回false

var arr1 = ["Hi!"];
var arr2 = ["Hi!"];console.log(arr1 === arr2); // false
復制代碼

如果想判斷兩個不同的對象是否真的相同,一個簡單的方法就是將它們轉換為字符串然后判斷。

var arr1str = JSON.stringify(arr1);
var arr2str = JSON.stringify(arr2);console.log(arr1str === arr2str); // true
復制代碼

另一個方法就是遞歸地判斷每一個屬性的值,直到基本類型位置,然后判斷是否相同。

函數參數

當我們將基本類型數據傳入函數,函數會將這些數據拷貝賦值給函數的參數變量。

var hundred = 100;
var two = 2;
function multiply(x, y) {return x * y;
}
var twoHundred = multiply(hundred, two);
復制代碼

hundred的值拷貝給變量xtwo的值拷貝給變量y

純函數

對于一個函數,給定一個輸入,返回一個唯一的輸出。除此之外,不會對外部環境產生任何附帶影響。我們機會稱該函數為純函數。所有函數內部定義的變量在函數返回之后都被垃圾回收掉。

但是,如果函數的輸入是對象(Array, Function, Object),那么傳入的是一個引用。對該變量的操作將會影響到原本的對象。這樣的編程手法將產生附帶影響,是的代碼的邏輯復雜和可讀性變低。

因此,很多數組函數,比如Array.mapArray.filter是以純函數的形式實現。雖然它們的參數是一個數組變量,但是通過深度拷貝并賦值給一個新的變量,然后在新的數組上操作,來防止原始數組被更改。

我們來看一個例子:

function changeAgeImpure(person) {person.age = 25;return person;
}
var alex = {name: 'Alex',age: 30
};
var changedAlex = changeAgeImpure(alex);
console.log(alex); // { name: 'Alex', age: 25 }
console.log(changedAlex); // { name: 'Alex', age: 25 }
復制代碼

在非純函數changeAgeImpure中,將對象personage更新并返回。原始的alex對象也被影響,age更新為25。

讓我們來看如何實現一個純函數:

function changeAgePure(person) {var newPersonObj = JSON.parse(JSON.stringify(person));newPersonObj.age = 25;return newPersonObj;
}
var alex = {name: 'Alex',age: 30
};
var alexChanged = changeAgePure(alex);
console.log(alex); // { name: 'Alex', age: 30 }
console.log(alexChanged); // { name: 'Alex', age: 25 }
復制代碼

我們通過JSON.sringify將對象變為一個字符串,然后再通過JSON.parse將字符串變回對象。通過該操作會生成一個新的對象。

一道簡單的面試題

值傳遞和引用傳遞經常在面試中被問到,來嘗試回答一下如下代碼如何輸出:

function changeAgeAndReference(person) {person.age = 25;person = {name: 'John',age: 50};return person;
}
var personObj1 = {name: 'Alex',age: 30
};
var personObj2 = changeAgeAndReference(personObj1);
console.log(personObj1); // -> ?
console.log(personObj2); // -> ?
復制代碼

關于Fundebug

Fundebug專注于JavaScript、微信小程序、微信小游戲、支付寶小程序、React Native、Node.js和Java實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了7億+錯誤事件,得到了Google、360、金山軟件、百姓網等眾多知名用戶的認可。歡迎免費試用!

版權聲明

轉載時請注明作者Fundebug以及本文地址:
blog.fundebug.com/2017/08/09/…

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/253050.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/253050.shtml
英文地址,請注明出處:http://en.pswp.cn/news/253050.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

全景攝像技術大有可為

網絡攝像機發展至今,已經基本滿足了“高清”、“日夜監控”、“遠距離監控”的需求,但是 隨著細分市場的發展,超廣角攝像機需求逐漸凸顯出來。主要應用在會議室、辦公室、大廳/大堂、商場、倉庫、車間等大面積開闊的區域,解決原來…

C#編程(五十三)----------字典DictionaryTKey,TValue

字典 關鍵字:Dicitionary 說明: 必須包含命名空間System.Collection.Generic Dictionary里面的每一個元素都是一個鍵值對(由兩個元組組成:鍵和值). 鍵必須是唯一的,而值不需要唯一的. 鍵和值都可以是任意類型(例如:string,int,自定義類型,等等) 通過一個鍵讀取一個值的事件是接…

setInterval只執行一次的原因

1 setInterval(arrow(),2000) 改為: 1 setInterval(arrow,2000) 原因: arrow()這是一個函數調用,函數調用就會有返回值, 而arrow()沒有返回值,所以這里的arrow()是一個undefined,自然你想要的循環執行arrow…

java文件傳輸之文件編碼和File類的使用

---恢復內容開始--- 我們知道,在用戶端和服務端之間存在一個數據傳輸的問題,例如下載個電影、上傳個照片、發一條訊息。在這里我們 就說一下文件的傳輸。 1.文件編碼 相信大家小時候玩過積木(沒玩過也看過吧),看到一個…

Android 模擬輸入那點事

因工作原因,需要用到模擬輸入這個東東,查閱了一些資料,實現方式有多種,我大概分為兩類,命令行類和程序類。 命令行類包括自動化測試組件monkeyrunner,getevent/setevent命令,input命令 程序類包…

arm-linux-gcc:Command not found的問題

標簽: ubuntulinux 2015-05-15 10:47 680人閱讀 評論(0) 收藏 舉報 分類: Ubuntu(23) /etc/profile gcc(9) ARM匯編指令(4) 折騰了一天,終于搞定了。 ubuntu沒有roo…

[No0000111]java9環境變量配置bat

保存成bat(utf-8 無簽名 編碼) 右鍵以管理員權限運行 修改JAVAINSTALLPATH 為JAVA SDK 安裝目錄(默認用C:\PROGRAM FILES\JAVA\)即可; 只在 用戶變量下 創建,會事先保存好用戶原有的“JAVA_HOME,JRE_HOME,P…

去掉浮夸,空杯心態重新面對測試

剛開始一頭扎進軟件測試行業,從踏踏實實的機械化功能測試,到學會和甲方扯皮,到被鄙視的五體投地后抓緊修煉表面功夫來忽悠人,學的最多的反而是怎么與人交流。第一次面對跳槽的機會,我竟然發現自己的測試能力不升反降。…

PASTE Splay

題目描述 我們用文本處理器來處理一個特殊的文本文件,該文本文件共有N行文本,每一行文本僅包含一個自然數,第一行為1、第二行為2,以此類推至N行為自然數N。   假設對該文本文件執行一次“剪切和粘貼”操作含義如下:…

linux 用戶空間通過makefile向程序傳遞參數

一. 用戶空間 因為實際上進行預處理的只是Gcc工具&#xff0c;而make工具只是一個解決依賴關系的工具。所以問題就簡化成如何通過make向gcc傳遞參數。通過簡單的例子來說明&#xff1a;hello.c#include <stdio.h> void main(void) {#ifdef DEBUG printf("y…

Spring---基于Spring IOC的小程序

實現的功能以及各文件間的關系 IHelloMessage&#xff1a;一個接口&#xff0c;用于定義輸出問候信息。 HelloWorld、HelloChina&#xff1a;接口的實現類。在這里表示人在不同的地方 Person&#xff1a;一個人物類&#xff0c;調用IHelloMessage接口&#xff0c;向用戶輸出問候…

Web開發者不可不知的16條原則

HTML已經走過了近20的發展歷程。從HTML4到XHTML&#xff0c;再到最近十分火熱的HTML5&#xff0c;它幾乎見證了整個互聯網的發展。但是&#xff0c;即便到現在&#xff0c;有很多基礎的概念和原則依然需要開發者高度注意。下面&#xff0c;小編向大家介紹這些應該遵循的開發原則…

MIPI DSI協議介紹

原文地址&#xff1a;http://blog.csdn .NET/qq160816/article/details/19555957 一、MIPI MIPI&#xff08;移動行業處理器接口&#xff09;是Mobile Industry Processor Interface的縮寫。MIPI&#xff08;移動行業處理器接口&#xff09;是MIPI聯盟發起的為移動應用處理器制…

NSArray、NSDictionary、NSString存儲、刪改、遍歷

NSString 創建一個NSString實例&#xff1a;NSString *str “this is string”;//字面量語法 常用API&#xff1a; stringWithFormat //創建動態字符串 -&#xff08;NSUInteger&#xff09;length //獲取字符的數量 -isEqualToString: //判斷兩個字符串是否相等 -uppercaseSt…

2018.11.14成立我的博客

2018.11.14成立我的博客轉載于:https://www.cnblogs.com/zengxx/p/9957509.html

130242014018-鄭志良-第2次實驗

一、實驗目的 1&#xff0e;熟悉體系結構的風格的概念 2&#xff0e;理解和應用管道過濾器型的風格。 3、理解解釋器的原理 4、理解編譯器模型 二、實驗環境 硬件&#xff1a; 軟件&#xff1a;Python或任何一種自己喜歡的語言 三、實驗內容 1、實現“四則運算”的簡易翻譯器。…

Hi3516A開發--掛載SD卡和U盤

一、SD卡 1、通過fdisk -l命令確認板子上的Linux系統是否識別SD卡 / # fdisk -l Disk /dev/mmcblk0: 63.8 GB, 63864569856 bytes 255 heads, 63 sectors/track, 7764 cylinders Units cylinders of 16065 * 512 8225280 bytes Device Boot Start …

【BZOJ 4170】 4170: 極光 (CDQ分治)

4170: 極光 Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 121 Solved: 64Description "若是萬一琪露諾&#xff08;俗稱rhl&#xff09;進行攻擊&#xff0c;什么都好&#xff0c;冷靜地回答她的問題來吸引她。對方表現出興趣的話&#xff0c;那就慢慢地反問。在她考…

自動生成web服務器日志解析規則

2019獨角獸企業重金招聘Python工程師標準>>> 當前web服務器的多樣化使得訪問日志的數據清洗變得越來越復雜&#xff0c;企業需要投入專業的數據清洗人員編寫數據清洗規則&#xff08;解析規則或者解析正則&#xff09;&#xff0c;或者需要關心web服務器訪問日志的生…

mybatis一級緩存二級緩存

一級緩存 Mybatis對緩存提供支持&#xff0c;但是在沒有配置的默認情況下&#xff0c;它只開啟一級緩存&#xff0c;一級緩存只是相對于同一個SqlSession而言。所以在參數和SQL完全一樣的情況下&#xff0c;我們使用同一個SqlSession對象調用一個Mapper方法&#xff0c;往往只執…