原文: zhuanlan.zhihu.com/p/33035557
最近看到一個關於JS函數參數傳值策略的討論。不少人會認爲JS的Object類型做爲函數參數是按引用傳遞,而基礎類型是按值傳遞,他們也提出了本身的佐證,並且網上搜索不少文章好像也這麼說。可是這樣的說發是否是正確的呢?讓咱們來探討一下JS裏面的求值策略。javascript
維基百科搜索Evaluate-Strategy你能夠看到求值策略實際上是編程語言裏面的一個經常使用術語。求值策略一般指對某種編程語言的表達式進行求值和計算的一個規則集。而函數參數的傳值策略是其中一個特殊的例子。php
通常業界常見的求值策略有嚴格和非嚴格策略。在「嚴格求值」中,給函數的實參老是在這個函數被調用以前求值,相應的「非嚴格求值」就是在函數調用時求值因此也叫「惰性求值」。前端
和大部分語言(C,java,Python,Ruby等)同樣JS採用的也是嚴格求值策略,不一樣的是在JS裏面參數求值順序從左至右而其餘的實現則是從右至左。java
注:ES6裏面函數增長了默認參數,參數默認值不是傳值的,而是每次都從新計算默認值表達式的值也就是說,參數默認值是惰性求值的。
瞭解JS的函數傳參策略對於咱們理解JS來講意義重大。編程
在此以前咱們先來看一下問題:bash
function magic(num, objectA, objectB) {
num = num * 6;
objectA = {name: 'AA'}
objectB.name = 'BB';
}
const num = 1;
const objectA = {name: 'A'};
const objectB = {name: 'B'};
magic(num, objectA, objectB);
console.log(num); // 1
console.log(objectA); // {name: "A"}
console.log(objectB); // {name: "BB"} something change
複製代碼
JS紅寶書裏面說過這麼一句話: ECMAScript中全部函數的參數都是按值傳遞的,把函數外部的值複製給函數內部的參數,就和把值從一個變量複製到另外一個變量同樣。 上述例子中傳入了三個值,一個Number類型,兩個引用類型。然而當咱們objectB的一個屬性改變以後,竟然改變了傳入的變量的值。到底咋回事?ecmascript
「傳值調用」求值是最多見的求值策略, 被求值了的參數值會被綁定當前函數的變量裏(也就是傳遞的是其值的拷貝)。此策略中,函數內部改變參數值不會影響到外部值,通常來講是給改參數值分配了新內存,並被函數內部調用。編程語言
按引用傳遞接收的不是值拷貝,而是對象的隱式引用,如該對象在外部的直接引用地址。函數內部對參數的任何改變都是影響該對象在函數外部的值,由於二者引用的是同一個對象,也就是說:這時候參數就至關於外部對象的一個別名。 讓咱們來看一個PHP的例子:函數
<?php
function foo(&$var) // 這裏& 表示將參數按引用傳遞
{
$var++;
}
$a=5;
foo($a);
// $a is 6
?>
複製代碼
這個策略還有一些代名詞:「按對象傳遞」或「按對象共享傳遞」,該策略是1974年由Barbara Liskov爲CLU編程語言提出的。 該策略的要點是:函數接收的是對象對於的拷貝(副本),該引用拷貝和形參以及其值相關聯。 這裏出現的引用,咱們不能稱之爲「按引用傳遞」,由於函數接收的參數不是直接的對象別名,而是該引用地址的拷貝。 最重要的區別就是:函數內部給參數從新賦新值不會影響到外部的對象(和上例按引用傳遞的case),可是由於該參數是一個地址拷貝,因此在外面訪問和裏面訪問的都是同一個對象(例如外部的該對象不是想按值傳遞同樣徹底的拷貝),改變該參數對象的屬性值將會影響到外部的對象。ui
看到這裏或許你對上面那個問題有一些眉目了,咱們就來解析一下: 在以前的文章 咱們有講過JavaScript 是一種弱類型或者說動態語言其數據類型能夠分爲兩類:
在作變量聲明時,不一樣的類型內存分配也不同:
因此在複製變量的時候也就會不一樣:
其實紅寶書的那句話還有後文:
基本類型的傳遞如同基本類型的複製同樣,而引用類型值的傳遞,如同引用類型變量的複製同樣。
下面咱們再來看看,這二者的不一樣帶來的參數傳遞的問題:
上面能夠解釋 爲何文章開頭的例子:num 和 objectB兩個參數對與外部的影響。可是還有一個疑問,爲何objectA在函數內部被賦值了一個全新的對象後沒有對外部形成影響呢? 這裏就要說到JS函數對於引用類型的傳遞的求值策略來講就是按共享傳遞。所以,若是你對這個引用進行第二次賦值的時候,實際上把這份引用指向了另一個對象,因此以後對這個對象的操做不會影響到外部的對象。
爲了便於操做基本類型ECMAScript還提供了3種特殊的引用類型:Boolean,Number和String 他與其餘引用類型類似,但同時具備各自的基本類型相應的特殊行爲咱們把它叫作基本包裝類型。
如:Number是和基本數據類型的數值對應的引用類型。能夠建立對象和調用自己的方法。例子代碼以下
var num = new Number(100);
//toFixed方法表示按照指定小數位返回字符串
var t = num.toFixed(3);
console.log(t); //t的值是100.000
複製代碼
那麼咱們來看這個問題:
let num = new Number(9);
function addOne(n) {
n.x = "xx";
n += 1;
}
addOne(num);
console.log(num); // Number {9, x: "xx"}
複製代碼
怎麼來解釋addOne裏面的行爲呢? 就留給各位各抒己見吧。。
總的來講,js這門語言有不少能夠細究的地方。對於求值策略理解有助於解釋咱們平常代碼中遇到的一些疑問,規避一些反模式,提升咱們的代碼魯棒性。以上是個人一些理解望各位批評指正。
上週末咱們的專欄突破����10000粉絲啦👏🎉。原本想第一萬粉絲的時候發個紅包給他,結果很尷尬,不知道哪一個是第10000粉。才發現知乎專欄的粉絲管理太不友好了,因此也開一個公衆號了,若是各位喜歡能夠關注一下啦 。很惋惜「前端雜貨鋪」這個名字被人用了,因此如今名字暫時就叫前端雜貨鋪的新店,微信號fezhuanlan. 歡迎各位客官光臨!!