變量:JS的變量為松散類型(即可以用來保存任何類型的數(shù)據(jù)),換句話說,每個變量僅僅是一個用于保存值的占位符而已。它由變量名和變量值組成,變量名是字符串類型,變量值不會是一個對象,而是儲存在棧中一個固定值,引用類型儲存的是一個堆內(nèi)存中的地址。
基本類型值:在將一個保存著原始值的變量復制給另一個變量時,會將原始值的副本賦值給新變量,從此以后兩個變量時完全獨立的,他們只是擁有相同的value而已。
引用類型值:在將一個保存著對象內(nèi)存地址的變量復制給另一個變量時,會把這個內(nèi)存地址賦值給新變量,他們都指向堆內(nèi)存中同一個對象,他們中任何一個改變都會反映在另一個身上(復制對象時并不會在堆內(nèi)存中新生成一個一摸一樣的對象,只是多了一個保存指向這個對象指針的變量罷了)
形參(parameter)變量只有在被調(diào)用時才分配內(nèi)存單元,調(diào)用結(jié)束,即可釋放所分配的內(nèi)存單元,因此形參只在函數(shù)內(nèi)部有效。
實參(arguments)可以是常量、變量、表達式、函數(shù)等,無論何種類型的量,在進行函數(shù)調(diào)用時,他們都必須有確定的值,以便把這些參數(shù)傳送給形參。
????????EMCAScript規(guī)定在調(diào)用函數(shù)時,可傳入任意數(shù)量,任意類型的參數(shù),可以不跟函數(shù)定義時傳入的形參數(shù)量相對應。為什么會這樣呢?
? ??????原因就是,EMACAScript中的參數(shù)在內(nèi)部是用一個數(shù)組來表示的,函數(shù)接收的始終是這個數(shù)組,而不關心包含哪些參數(shù)。而且,在函數(shù)體內(nèi)也可以通過arguments這個對象來訪問這個參數(shù)數(shù)組,從而獲取傳遞給函數(shù)的每一個參數(shù)。(arguments就是一個對象--函數(shù)的一個內(nèi)部對象,和this一樣。)因此arguments看起來就像一個類數(shù)組了??梢杂胊rguments[n]來訪問各個位置的元素了.
? ??????函數(shù)內(nèi)部,有兩個特殊對象:arguments 和 this。arguments是一個類數(shù)組對象,包含著傳入函數(shù)中的所有參數(shù)。該對象有一個屬性callee,該屬性是一個指針,指向擁有這個arguments對象的函數(shù)。與callee屬性對應的就是caller屬性,arguments對象也擁有caller屬性,即:arguments.caller。但在嚴格模式下訪問它會報錯,而在非嚴格模式下這個屬性始終是undefined。該屬性主要就是為了分清arguments.caller和函數(shù)的caller屬性。函數(shù)的caller屬性指向調(diào)用該函數(shù)的外部函數(shù)。
按值傳遞:函數(shù)的形參是被調(diào)用時所傳實參的副本,修改形參的值并不影響實參。(js基本類型(undefined、null、boolean、number、string)都是按值傳遞的,每次都需要克隆副本,對一些復雜類型,性能較低)
按引用傳遞: 函數(shù)的形參接受實參的隱式引用,不再是實參的副本,函數(shù)形參的值被修改,實參也會被修改。(js對象類型(function、array、date),也叫引用類型)?引用類型在做為參數(shù)傳遞時是按共享傳遞的
共享傳遞:調(diào)用函數(shù)參數(shù)時,函數(shù)接受對象實參引用的副本(既不是按值傳遞的對象副本,也不是按引用傳遞的隱式引用,和按引用傳遞的區(qū)別:在調(diào)用函數(shù)傳遞引用類型的參數(shù)時,傳遞的是對象引用的副本,但是這個對象引用的副本跟原對象引用指向的是同一個地方(即該對象在內(nèi)存中存放的地址)),在共享傳遞中對函數(shù)形參的賦值,不會影響實參的值。
var foo = {name:'foo'};
function test(o){
o.name='test';
o={name:'bar'} }
test(foo);
console.log(foo);
//打印結(jié)果為:Object {name: "test"} ?
以上解釋其實不是本質(zhì),我們繼續(xù)。
其實ECAMScript中所有函數(shù)都是按值傳遞的,在涉及到基本類型與引用類型時有區(qū)別是 內(nèi)存分配不同 造成的。(這里和變量復制時遵循的機制完全一樣,可以簡單理解傳遞參數(shù)時,就是把實參復制給形參的過程)
聲明變量時不同的內(nèi)存分配:基本類型值,儲存在棧(stack)中的簡單數(shù)據(jù)段,他們的值直接儲存在變量訪問的位置,他們占據(jù)的空間是固定的,所以將他們儲存在較小的內(nèi)存區(qū)域-棧中,這樣儲存便于迅速查找變量的值。引用值:儲存在堆(heap)中的對象,放在變量的??臻g中的值是該對象儲存在堆中的地址(一個指針),大小固定指向儲存對象的內(nèi)存地址,引用對象值大小會改變,所以不能把它放在棧中,否則會降低變量查詢速度。
js中是不允許直接訪問保存在堆內(nèi)存中的對象的,所以在訪問一個對象時,首先得到這個對象在堆內(nèi)存中地址,然后再按這個地址去獲得這個對象中的值,這就是傳說中的引用訪問,而基本類型值時可以直接訪問到的。
如果按引用傳遞的話,是變量把它里面的值傳遞(復制)給了參數(shù),這個參數(shù)也指向了原對象,如果在函數(shù)內(nèi)部把這個參數(shù)賦值另一個對象時,這個參數(shù)就會更改它的值為新的內(nèi)存地址指向的新對象,此時原來的變量仍指向原來的對象,這時他們相互獨立;但如果這個參數(shù)改變對象內(nèi)部的屬性的話,這個改變會體現(xiàn)在外部,因為他們共同指向的這個對象被修改了。這也就是上面所說的按值傳遞和共享傳遞的區(qū)別。
2017,8.24 二次理解:
js中所有類型都是按值傳遞的(傳遞的是棧中的內(nèi)容),因為聲明變量時不同方式的內(nèi)存分配(基本類型 儲存在棧中;引用類型值 儲存在堆中,棧中儲存 引用地址)。所以基本類型 按值傳遞的是 棧中的直接值,而引用類型 按值傳遞的是 棧中的 引用地址。
而共享傳遞的意思是:你外部一個對象傳到函數(shù)里做為一個參數(shù),這個參數(shù)是和原對象一樣,是指向堆中值的 一個地址,你在函數(shù)的這個參數(shù)中修改參數(shù)的屬性是會影響到外部對象的,但你要是把這個參數(shù)再賦予一個新對象,這個參數(shù)在這個時候及之后都是 指向另一個堆中值的 一個地址,而這之前的這個參數(shù)(可能參數(shù)名字一樣)仍是指向 原堆中值的 一個地址。