首頁 web前端 js教程 深入理解JavaScript系列(19):求值策略(Evaluation strategy)詳解_基礎知識

深入理解JavaScript系列(19):求值策略(Evaluation strategy)詳解_基礎知識

May 16, 2016 pm 04:11 PM
javascript

介紹

本章,我們將說明在ECMAScript傳遞參數給函數function的策略。

電腦科學裡對這個策略一般稱為「evaluation strategy」(大叔註:有的人說翻譯成求值策略,有的人翻譯成賦值策略,通看下面的內容,我覺得稱為賦值策略更為恰當,anyway,標題還是寫成大家容易理解的求值策略吧),例如在程式語言為求值或計算表達式設定規則。向函數傳遞參數的策略是一個特殊的case。

http://dmitrysoshnikov.com/ecmascript/chapter-8-evaluation-strategy/
寫這篇文章的原因是因為論壇上有人要求準確解釋一些傳參的策略,我們在這裡給了相應的定義,希望對大家有幫助。

許多程式設計師都確信在JavaScript中(甚至其它一些語言),物件是按引用傳參,而原始值類型按值傳參,此外,很多文章都說到這個“事實”,但有多人真正理解這個術語,而且又有多少是正確的?我們本篇講逐一講解。

一般理論

需要注意到,在賦值理論裡一般有2中賦值策略:嚴格--意思是說參數在進入程序之前是經過計算過的;非嚴格--意思是參數的計算是根據計算要求才去計算(也就是相當於延遲計算)。

然後,這裡我們考慮基本的函數傳參策略,從ECMAScript出發點來說是非常重要的。首先要注意的是,在ECMAScript中(甚至其他的語如,C,JAVA,Python和Ruby中)都使用了嚴格的參數傳遞策略。

另外傳遞參數的計算順序也是很重要的-在ECMAScript是左到右,而且其它語言實現的反省順序(從右向做)也是可以用的。

嚴格的傳參策略也分為幾種子策略,其中最重要的一些策略我們在本章詳細討論。

下面討論的策略不是全部都用在ECMAScript中,所以在討論這些策略的具體行為的時候,我們使用了偽代碼來展示。

以數值傳遞

按值傳遞,很多開發人員都很了解了,參數的值是呼叫者傳遞的物件值的拷貝(copy of value),函數內部改變參數的值不會影響到外面的物件(該參數在外面的值),一般來說,是重新分配了新記憶體(我們不關注分配記憶體是怎麼實現的-也是是棧也許是動態記憶體分配),該新記憶體區塊的值是外部物件的拷貝,並且它的值是用到函數內部的。

複製程式碼 程式碼如下:

bar = 10
 
procedure foo(barArg):
  barArg = 20;
end
 
foo(bar)
 
// foo內部改變值不會影響內部的bar的值
print(bar) // 10

但是,如果該函數的參數不是原始值而是複雜的結構物件是時候,將帶來很大的效能問題,C 就有這個問題,將結構作為值傳進函數的時候——就是完整的拷貝。

我們來給一個一般的例子,用下面的賦值策略來檢驗一下,想想一個函數接受2個參數,第1個參數是物件的值,第2個是個布林型的標記,用來標記是否完全修改傳入的物件(給物件重新賦值),還是只修改該物件的一些屬性。

複製程式碼 程式碼如下:

// 註:以下都是偽代碼,不是JS實作
bar = {
  x: 10,
  y: 20
}
 
procedure foo(barArg, isFullChange):
 
  if isFullChange:
    barArg = {z: 1, q: 2}
    exit
  end
 
  barArg.x = 100
  barArg.y = 200
 
end
 
foo(bar)
 
// 按值傳遞,外部的物件不會改變
print(bar) // {x: 10, y: 20}
 
// 完全改變物件(賦為新值)
foo(bar, true)
 
//也沒有改變
print(bar) // {x: 10, y: 20}, 而不是{z: 1, q: 2}

依引用傳遞

另外一個眾所周知的按引用傳遞接收的不是值拷貝,而是物件的隱式引用,如該物件在外部的直接引用位址。函數內部對參數的任何改變都是影響該對像在函數外部的值,因為兩者引用的是同一個對象,也就是說:這時候參數就相當於外部對象的一個別名。

偽代碼:

複製程式碼 程式碼如下:

procedure foo(barArg, isFullChange):
 
  if isFullChange:
    barArg = {z: 1, q: 2}
    exit
  end
 
  barArg.x = 100
  barArg.y = 200
 
end
 
// 使用和上例相同的物件
bar = {
  x: 10,
  y: 20
}
 
// 依引用呼叫的結果如下:
foo(bar)
 
// 物件的屬性值已經改變了
print(bar) // {x: 100, y: 200}
 
// 重新賦新值也影響了這個物件
foo(bar, true)
 
// 此刻該物件已經是新物件了
print(bar) // {z: 1, q: 2}

此策略可以更有效地傳遞複雜對象,例如具有大批量屬性的大結構對象。

依共享傳遞(Call by sharing)
上面2個策略大家都是知道的,但這裡要講的一個策略可能大家不太了解(其實是學術上的策略)。但是,我們很快就會看到這正是它在ECMAScript中的參數傳遞策略中扮演關鍵角色的策略。

這個策略還有一些代名詞:「按物件傳遞」或「依物件共享傳遞」。

此策略是1974年由Barbara Liskov為CLU程式語言提出的。

此策略的重點是:函數接收的是物件對於的拷貝(副本),該引用拷貝和形參以及其值相關聯。

這裡出現的引用,我們不能稱之為“按引用傳遞”,因為函數接收的參數不是直接的物件別名,而是該引用位址的拷貝。

最重要的區別是:函數內部給參數重新賦新值不會影響到外部的物件(和上例按引用傳遞的case),但是因為該參數是一個位址拷貝,所以在外面存取和裡面存取的都是同一個物件(例如外部的該物件不是想按值傳遞一樣完全的拷貝),改變該參數物件的屬性值將會影響到外部的物件。

複製程式碼 程式碼如下:

procedure foo(barArg, isFullChange):
 
  if isFullChange:
    barArg = {z: 1, q: 2}
    exit
  end
 
  barArg.x = 100
  barArg.y = 200
 
end

//還是使用這個物件結構
bar = {
  x: 10,
  y: 20
}
 
// 依貢獻傳遞會影響物件
foo(bar)
 
// 物件的屬性被修改了
print(bar) // {x: 100, y: 200}
 
// 重新賦值沒有作用
foo(bar, true)
 
// 依然是上面的值
print(bar) // {x: 100, y: 200}


這個處理的假設前提是大多數語言裡用到的對象,而不是原始值。

依共享傳遞是按值傳遞的特例

以共享傳遞這個策略很多語言裡都使用了:Java, ECMAScript, Python, Ruby, Visual Basic等。此外,Python社群已經使用了這個術語,至於其他語言也可以用這個術語,因為其他的名稱往往會讓大家感覺到混亂。大多數情況下,例如在Java,ECMAScript或Visual Basic中,此策略也稱之為按值傳遞-意味著:特殊值-引用拷貝(副本)。

一方面,它是這樣的-傳遞給函數內部用的參數只是綁定值(引用位址)的一個名稱,並不會影響外部的物件。

另一方面,如果不深入研究,這些術語真的被認為吃錯誤的,因為很多論壇都在說如何將物件傳遞給JavaScript函數)。

一般理論確實有按值傳遞的說法:但這時候這個值就是我們所說的位址拷貝(副本),因此並沒喲破壞規則。

在Ruby中,這個策略稱為依引用傳遞。再說一下:它不是按照大結構的拷貝來傳遞(例如,不是按值傳遞),而另一方面,我們沒有處理原始對象的引用,並且不能修改它;因此,這個跨術語的概念可能更會造成混亂。

理論裡沒有像按值傳遞的特殊case一樣來面試按引用傳遞的特殊case。

但仍有必要了解這些策略在上述提到的技術中(Java, ECMAScript, Python, Ruby, other),實際上-他們使用的策略就是依共享傳遞。

依共用與指標

對於С/С ,這個策略在思想上和按指針值傳遞是一樣的,但有一個重要的區別——該策略可以取消引用指針以及完全改變對象。但在一般情況下,分配一個值(位址)指標到新的記憶體區塊(即先前引用的記憶體區塊保持不變);透過指標改變物件屬性的話會影響阿東外部物件。

因此,和指標類別,我們可以明顯看到,這是按位址值傳遞。 在這種情況下,按共享傳遞只是“語法糖”,像指針賦值行為一樣(但不能取消引用),或者像引用一樣修改屬性(不需要取消引用操作),有時候,它可以被命名為“安全指針」。

然而,С/С 如果在沒有明顯指標的解引用的情況下,引用物件屬性的時候,還具有特殊的語法糖:

複製程式碼 程式碼如下:

obj->x instead of (*obj).x

和C 關係最緊密的這種意識形態可以從「智慧指標」的實現中看到,例如,在boost :: shared_ptr裡,重載了賦值操作符以及拷貝建構函數,而且還使用了物件的引用計數器,透過GC刪除對象。 這種資料類型,甚至有類似的名字- 共享_ptr。

ECMAScript實作

現在我們知道了ECMAScript中將物件作為參數傳遞的策略了-依共用傳遞:修改參數的屬性將會影響到外部,而重新賦值將不會影響外部物件。但是,正如我們上面提到的,其中的ECMAScript開發人員一般都稱之為是:按值傳遞,只不過該值是引用地址的拷貝。

JavaScript發明者布倫丹·艾希也寫到了:傳遞的是引用的拷貝(地址副本)。所以論壇裡大家曾說的值傳遞,在這種解釋下,也是對的。

更確切地說,這種行為可以理解為簡單的賦值,我們可以看到,內部是完全不同的對象,只不過引用的是相同的值——也就是地址副本。

ECMAScript程式碼:

複製程式碼 程式碼如下:

var foo = {x: 10, y: 20};
var bar = foo;
 
alert(bar === foo); // true
 
bar.x = 100;
bar.y = 200;
 
alert([foo.x, foo.y]); // [100, 200]

即兩個識別符(名稱綁定)綁定到記憶體中的同一個對象, 共享這個對象:

foo value: addr(0xFF) => {x: 100, y: 200} (address 0xFF) 而重新賦值分配,綁定是新的物件識別碼(新位址),而不影響已經先前綁定的物件 :

複製程式碼 程式碼如下:

bar = {z: 1, q: 2};
 
alert([foo.x, foo.y]); // [100, 200] – 沒改變
alert([bar.z, bar.q]); // [1, 2] – 但現在引用的是新物件

即現在foo和 bar,有不同的值和不同的位址:
複製程式碼 程式碼如下:

foo value: addr(0xFF) => {x: 100, y: 200} (address 0xFF)
bar value: addr(0xFA) => {z: 1, q: 2} (address 0xFA)

再強調一下,這裡所說物件的值是位址(address),而不是物件結構本身,將變數賦值給另外一個變數──是賦值值的參考。因此兩個變數引用的是同一個記憶體位址。下一個賦值卻是新位址,是解析與舊物件的位址綁定,然後綁定到新物件的位址上,這就是和按引用傳遞的最重要差異。

此外,如果只考慮ECMA-262標準所提供的抽象層次,我們在演算法裡看到的只有「值」這個概念,實現傳遞的「值」(可以是原始值,也可以是物件),但是按照我們上面的定義,也可以完全稱之為“按值傳遞”,因為引用地址也是值。

然而,為了避免誤解(為什麼外部物件的屬性可以在函數內部改變),這裡依然需要考慮實現層面的細節——我們看到的按共享傳遞,或者換句話說——按安全指標傳遞,而安全性指標不可能去解除引用和改變物件的,但可以去修改該物件的屬性值。

術語版本

讓我們來定義ECMAScript中該策略的術語版本。

可以稱為「以值傳遞」-這裡所說的值是一個特殊的case,也就是該值是地址副本(address copy)。從這個層面我們可以說:ECMAScript中除了異常之外的物件都是按值傳遞的,這實際上是ECMAScript抽象的層面。

或針對這種情況下,專門稱之為“按共享傳遞”,透過這個正好可以看到傳統的按值傳遞和按引用傳遞的區別,這種情況,可以分成2個種情況:1 :原始值按值傳遞;2:物件按共享傳遞。

「透過引用型別將物件到函數」這句話和ECMAScript無關,而且它是錯的。

結論

我希望這篇文章有助於宏觀了解更多細節,以及在ECMAScript中的實作。一如既往,如果有任何問題,歡迎討論。

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡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)

如何使用WebSocket和JavaScript實現線上語音辨識系統 如何使用WebSocket和JavaScript實現線上語音辨識系統 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術 WebSocket與JavaScript:實現即時監控系統的關鍵技術 Dec 17, 2023 pm 05:30 PM

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統 如何利用JavaScript和WebSocket實現即時線上點餐系統 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統 如何使用WebSocket和JavaScript實現線上預約系統 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript與WebSocket:打造高效率的即時天氣預報系統 JavaScript與WebSocket:打造高效率的即時天氣預報系統 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

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

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

javascript如何使用insertBefore javascript如何使用insertBefore Nov 24, 2023 am 11:56 AM

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

JavaScript與WebSocket:打造高效率的即時影像處理系統 JavaScript與WebSocket:打造高效率的即時影像處理系統 Dec 17, 2023 am 08:41 AM

JavaScript是一種廣泛應用於Web開發的程式語言,而WebSocket則是一種用於即時通訊的網路協定。結合二者的強大功能,我們可以打造一個高效率的即時影像處理系統。本文將介紹如何利用JavaScript和WebSocket來實作這個系統,並提供具體的程式碼範例。首先,我們需要明確指出即時影像處理系統的需求和目標。假設我們有一個攝影機設備,可以擷取即時的影像數

See all articles