先來看2014年阿裡巴巴前端線上筆試題:
var a = 1; var obj = { b: 2 }; var fn = function () {}; fn.c = 3; function test(x, y, z) { x = 4; y.b = 5; z.c = 6; return z; } test(a, obj, fn); alert(a + obj.b + fn.c); 答案是12,如果回答得正確,就沒必要看下去。If not,就跟我一起來學習下好啦~ (一)基本類型 由於學js沒多久,對賦值究竟是引用還是復制不是很清楚(如果是引用,那一個值改變會影響另一個,如果是復制,那各玩各的,互不影響),遇到這類問題往往就很迷糊。花了半天時間參閱了一些資料,整理如下。 首先,Javascript的基本類型有Boolean,String,Number,還有Undefined和Null,啊你肯定會說干嘛又扯最無聊的any book的語法第一章內容,了解這個又怎麼樣,還真的有用呢。首先得明白的是,只有字面量的Boolean,String和Number,以及undefined+null(exact undefined null,區分大小寫)才是屬於基本類型的,new出來的不算。 即: alert(typeof false);//"boolean" var b = new Boolean(false); alert(typeof b);//"object" 同樣,new String('aaa')這種都不是基本類型,直接的a='aaa',a是基本類型,這就是:字面量的才是基本類型。 另一方面,Object,Function,Array其實都是構造函數,因為可以直接new Object()等,所以它們都是函數,so (Object instanceof Function === true) &&(Function instanceof Object===true). 要注意的是,基本類型沒有屬性和方法,但它們可以調用對應基本包裝類型的方法,可以來看下面的例子: var a = 'ot'; a.age = 18;//hahahahaha,yeah,forever 18 alert(a.age);//undefined a.length = 160; alert(a.length);//2 第二步的a.age = 18其實在實現的時候分成三步: var s1 = new String('ot'); s1.age = 18; s1 = null; 即基本類型每次調用類型或方法都會創建新對象,隨後便銷毀。 同樣,在alert(a.age)的時候,分為下列步驟: var s2 = new String('ot'); alert(s2.age); s2=null; 因為沒有對s2定義age方法,所以輸出為undefined。在alert(a.length)的時候,由於s3=new String('ot'),String有length方法,所以,會輸出結果,當然,上一步的賦值操作的對象也一定已經被銷毀了。 (二)引用or Copy Clear about基本類型後,要了解的是,基本類型變量存在棧內部,每賦值一次就創建一個新的copy,然後play with itself. 而除了基本類型之外的引用類型,則存在堆內存中,只能引用。可以來看下面的例子: var ot = new Object();//創建一個對象,把地址賦值給ot,即ot指向這個地址 var op = ot;//把ot的值賦給op,所以op也是指向那個對象 op.age = 18; alert(ot.age);//18 op = new String('sunshine');//把新創建對象的地址賦給op,ot當然不變 alert(ot.length);//undefined 看了注釋部分,相信已經能理解了。 (三)參數傳遞 Javascript的參數傳遞為值傳遞,我們來看下面的例子: function setAge(i) { alert(i);//24 i = 18; alert(i);//18 }; var ot = 24; setAge(ot); alert(ot);//24 把ot的值24傳遞進去,賦值給i,於是第一個alert的是24,然後i重新賦值,於是alert出來的是18,但外層的ot不受影響,因為傳遞值,也就是復制了份內容給i而已。 那傳遞的值為引用類型時會怎麼樣呢?先看例子: function setName(obj) { obj.name = 'ot'; }; var obj2 = new Object(); setName(obj2); alert(obj2.name);//ot 復制代碼 這看起來很像是傳遞的是引用,因為obj.name受到改變了,但其實不是,其實還是值,因為obj2本身的值就是新對象的地址,所以傳進去的就是這個地址。 (四)回到面試題 我們現在再來看前面的面試題: var a = 1; var obj = { b: 2 }; var fn = function () {}; fn.c = 3; function test(x, y, z) { x = 4; y.b = 5; z.c = 6; return z; } test(a, obj, fn); alert(a + obj.b + fn.c); 首先test傳遞進去的實參中,a是基本類型(啊對了,復制了一份值喔喔),obj是object(指向地址呼啦啦,你動我也動呢),fn也當然不是基本類型啦。在執行test的時候,x被賦值為4(跟a沒關系,各玩各的嘛,a仍然為1),y的b被賦值為5,那obj的b也變為5,z的c變為6,那fn的c當然也會是6. 所以alert的結果應該是1+5+6 =12. (其實test不返回z也一樣,z仍然改變的)。