函數的參數傳遞

来源:http://www.cnblogs.com/zhangxiongcn/archive/2016/11/06/6036786.html
-Advertisement-
Play Games

我們都知道在 ECMAScript 中,數據類型分為原始類型(又稱值類型/基本類型)和引用類型(又稱對象類型);這裡我將按照這兩種類型分別對函數進行傳參,看一下到底發生了什麼。 參數的理解 首先,我們要對函數的參數有一個瞭解: 形參就是函數內部定義的局部變數; 實參向形參傳遞值的時候,就是一個賦值操 ...


我們都知道在 ECMAScript 中,數據類型分為原始類型(又稱值類型/基本類型)和引用類型(又稱對象類型);這裡我將按照這兩種類型分別對函數進行傳參,看一下到底發生了什麼。

參數的理解

首先,我們要對函數的參數有一個瞭解:

形參就是函數內部定義的局部變數;

實參向形參傳遞值的時候,就是一個賦值操作,把實參的值直接複製一份給形參。

原始類型參數傳遞

示例1

  var a = 1;
  function f(b) {
      a = 3;
  }
  f(a);
  console.info(a); // 3

示例1中的代碼比較簡單,解析如下:

  1. 首先,我們定義了一個變數 a,給它賦值為 1;又定義了一個函數 f,函數 f 的形參是 b(此時,相當於在函數 f 里定義了一個變數 var b; 它的值現在是 undefined);
  2. 調用函數 f(a)a 作為實參傳入。這裡可以理解為,給 b 進行了一次賦值操作 b = 1
  3. 接下來繼續執行代碼 a = 3;註意:在函數體裡邊,出現了一個變數 a ,但是它沒有用 var 關鍵字定義,所以,它是一個全局作用域的變數,不是這個函數的局部變數,而在一開始,我們就定義了這樣一個變數 a,所以在這裡就是對之前的 a 進行的又一次賦值操作,把 a 從之前的 1 變成了 3
  4. 執行完 f(a) 之後,把全局變數 a 的值改變了,所以,當我們輸出 a 查看它的值時,就得到了 3 ,而對於函數的形參 b 根本沒有進行任何操作而已。

示例2

  var a = 1;
  function f(a) {
      a = 3;
  }
  f(a);
  console.info(a); // 1

解析:

示例2與示例1的區別,從錶面上看,就是形參 b 變成了 a 。但是,這樣的變化結果就是,對於函數 f 來說,參數起到了作用,當我們對函數 f 進行傳參操作的時候,我們傳入的實參在函數內部就會得到引用。

相比較示例1的第一步,函數 f 內部定義了一個變數 a 它的值是 undefined ,註意:在未執行函數的時候,只是進行了預解析,代碼沒有執行,在調用函數的時候才會開始執行代碼。

執行 f(a) 後,就是傳入實參 1 ,函數內部的變數 a 賦值為 1 ,然後再進行 a = 3 的操作 ,此時,在函數的局部作用域的棧記憶體中有一個變數 a 它的值是 3 ;而在全局作用域的棧記憶體中,也存在一個變數 a ,它的值是 1 ,這兩個變數是兩個不同的變數,只是它們的名字都是 a 而已。

過程如圖:

示例3

  var a = 1;
  function f(b) {
      a = 3;
      b = 10;
  }
  f(a);
  console.info(a); // 3
  console.info(b); // 報錯

解析:

下麵我們再來看一下示例3,先看變數 a ,完全和示例1一樣,這裡就不再詳細說明瞭;再看變數 b ,其實也是和示例2沒任何不一樣的地方的,只是在這裡把形參的名字改變了一下而已,在最後我們輸出 a 的時候,沒有任何問題,結果是 3 ,但是,訪問變數 b 的時候,輸出會報錯,這裡涉及到的就是作用域問題了:在函數內部定義的變數,在函數內部可以訪問,而在函數的外部是無法訪問的。

具體過程可看下圖:

示例4

  var a = 1;
  function f(a) {
      a = 3;
      b = 10;
  }
  f(a);
  console.info(a); // 1
  console.info(b); // 10

解析:

綜合前三個示例,示例4我想大家都應該沒有什麼問題了,我們直接來看圖吧:

引用類型參數傳遞

下麵我們再來兩個引用類型參數的示例;以下示例僅為說明引用類型傳參之後,函數內部的賦值變化,所以用的都是簡單數組進行說明。

其實,引用類型參數的傳遞需要考慮的就是引用類型和原始類型之間的區別:

  1. 引用類型在預解析時候,和原始類型一樣都會在棧區里分配到空間,生成變數;但是賦值的時候,原始類型的賦值依然保存在棧記憶體當中,而引用類型就會在堆記憶體里占用一定空間存放它的數據,而在棧區里生成一個指向堆區的地址。
  2. 對變數進行複製的時候,原始類型的複製是在棧記憶體當中生成一份一樣的數據,可以理解為“完全複製”;而引用類型的複製,只是在棧記憶體中進行複製,兩個變數的地址同時指向堆記憶體中的那一份數據。

示例5

var a = [1];
function f(a){
    a[100] = 3;
}
f(a);
console.info(a); // [1,100:3]

解析:

第一步:全局棧區中生成變數 a ,賦值後,在全局堆區里生成數組 [1]

第二步:傳參,調用 f(a) ,首先在函數棧區中生成變數 a ,此時值為 undefined ,然後把全局變數 a 的值賦給局部函數里的變數 a ,因為是引用類型的數組,所以,函數里的 a 在棧區里也生成一個指向全局堆里的相同 地址1

第三步:執行函數里的代碼 a[100] = 3 ,找到函數棧中的 a ,再對它進行賦值,改變了全局堆中的 [1] ,得到了一個新的數組 [1,100:3]

第四步:訪問全局作用域中的變數 a ,得到指向堆記憶體中的數組 [1,100:3]

示例6

var a = [1];
function f(b){
    b[100] = 3;
    b = [1,2,3];
    console.info(b); // [1,2,3]
}
f(a);
console.info(a); // [1,100:3]

解析:

該示例的關鍵點在第四步,執行到代碼 b = [1,2,3] 的時候,會對函數裡面的變數 b 重新進行一次賦值,這樣會在函數的堆記憶體中生成一個新的 數組,全局的 a 和函數中的 b 的指向地址就不一樣了,所以,它倆的輸出就不一樣了。

總結

在 js 中,原始類型是按值傳遞的;引用類型是按共用傳遞的。

按值傳遞 - call by value

一個外部變數傳遞給一個函數時,函數實參獲取到的實際上是這個外部變數的副本,在函數內部我們對實參進行的修改並不會反應到這個外部變數上。

按引用傳遞 - call by reference

一個外部變數傳遞給一個函數時,函數實參實際上是這個外部變數的一個引用,我們在函數內部對這個實參的任何修改,都會反應到這個外部變數。

按共用傳遞 - call by sharing


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 對於一個對設計模式一無所知的程式員來說,維護成本,無疑是一個致命的問題,所以從今開始,我要開始努力學習,設計模式,在這裡,記錄自己的學習成果 生產類 。 。 。 ...
  • 1.1概述 允許一個對象在其內部狀態改變時改變它的行為,對象看起來似乎修改了它的類。這就是狀態模式的定義。 一個對象的狀態依賴於它的變數的取值情況,對象在不同的運行環境中,可能具有不同的狀態。在許多情況下,對象調用方法所產生的行為效果依賴於它當時的狀態。 例如,一個溫度計(Thermometer)類 ...
  • 例如:The prefix "context" for element "context:annotation-config" is not bound. 這種情況是因為沒有申明該標簽,然後就使用了。解決方發是,在配置文件頭部加入相應的信息即可( 即xmlns:context="http://www ...
  • React框架已經火了好長一段時間了,再不學就out了! 對React還沒有瞭解的同學可以看看我之前的一篇文章,可以快速簡單的認識一下React。 "React入門最好的實例-TodoList" 自己從開始接觸react一竅不通,到慢慢的似懂非懂,通過各種途徑學習也有一陣了。學習過程中還會接觸到很多 ...
  • 效果預覽 實例代碼 <!DOCTYPE html> <html lang="zh" class="no-js"> <head> <meta charset="UTF-8" /> <title>catslider簡單的多商品分類滑動</title> <meta http-equiv="X-UA-Com ...
  • http://www.jxedt.com/wen/kaoshi/3174963987168886819.html http://www.jxedt.com/wen/kaoshi/3174963988918370327.html http://www.jxedt.com/wen/kaoshi/3174 ...
  • 如果運行後界面上不彈出“沒有jquery”的提示以及段落被點擊後消失,則導入成功;否則說明沒有成功,那麼就檢測代碼吧,一般將jQuery放入項目文件下應該是可以用的,不成功的話多數情況是代碼有問題,如某些拼寫錯誤等。 ...
  • 最新學了一個新的運動函數,與最初學習的有所不同,第一個運動是根據運動速度完成運動 ,第二個則是根據運動的時間來完成運動,而且把之前的函數都進行了一些相容處理,在這裡列出了看一下: 第一種animate1 第二種animate2 總結: animate1中各種運動完成的時間是不一致的,而animate ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...