引子
我們都知道,JavaScript資料型別分成兩大類,基本型別(或稱為原始型別)和參考型別。
基本類型的值是保存在堆疊記憶體中的簡單資料段,它們是按值存取的。 JS中有五種基本型別:Undefined、Null、Boolean、Number和String。
引用類型的值是保存在堆記憶體中的對象,它的值是按引用存取的。引用型別主要有Object、Array、Function、RegExp、Date。
物件是擁有屬性和方法的,所以我們看到下面這段程式碼一點也不奇怪.
var favs=['鸡蛋','莲蓬']; favs.push('秋葵'); console.log(favs);//["鸡蛋", "莲蓬", "秋葵"] console.log(favs.length);//3
Array是引用類型,所以它自然可以擁有屬性(length)和方法(push),這天經地義地就像夏天一定要吃冰淇淋一樣。但是,再看下面的程式碼,仔細想想,這這這,合法嗎?
var realMessage="Said I love you but I lied"; var myMessage=realMessage.substring(5,15); console.log(myMessage); //"I love you"
有一個心碎的女紙任性地對一個用來分手的字符串任性地執行了“substring”方法,然後開心地看著剪輯版睡過去了。可是可是可是,不是說string是基本型別嗎,為什麼它可以有方法? ?還有沒有王法啊青天大老爺!
其實,這一切,都是因為有個叫做「基本包裝類型」的東東。這個基本包裝類型特別耿直,是真正的「事了拂衣去,深藏功與名」!
基本包裝類型
除了一開始提到的Object、Array等參考類型,JavaScript也為我們提供了三種特殊的參考類型:String、Number和Boolean,方便我們操作對應的基本型別。
繼續看上面的剪輯字串的例子,有沒有註意到,儘管使用了substring方法,realMessage本身的值是不會變的,呼叫這個方法只是傳回了一個新的字串。
這就是基本包裝類型的作用了。本來你是沒有方法的,但是你想用方法的時候,你儘管調,對應的基本包裝類型有這個方法就行。例如上面的substring方法,string這個基本型別是不可能有這個方法的,但是String這個包裝型別有啊,它會吭吭哧哧地把這個方法執行完把結果回傳。在執行到:
realMessage.substring(5,15)
這行程式碼時,發生了很多事。
首先,它會從記憶體中讀取realMessage的值。當處於這種讀取模式的時候,後台就開始工作了。 JS高程是這樣描述後台完成的這些動作的:
1.建立一個String類型的實例;
2.在實例上呼叫指定的方法;
3.銷毀這個實例
上面的範例可以用這樣的程式碼來說明:
var _realMessage=new String("Said I love you but I lied"); var myMessage=_realMessage.substring(5,15); _realMessgae=null; //方法调用后即销毁
所以,這樣我們就明白了,並不是基本類型string執行了自身方法,而是後台為它創建了一個對應的基本包裝類型String,它根據基本類型的值實例化出了一個實例,讓這個實例去呼叫指定方法,最後銷毀自己,感天動地有木有。
注意最後一步基本包裝類型「會銷毀」的特性,這決定了我們不能為基本類型值添加自訂屬性和方法。
var me="sunjing"; me.age=18; console.log(me.age);//undefined
我為「me「這個字串增加了age屬性,值設為美好的18歲,然並卵,再次造訪時,這個屬性已經渺無蹤跡了。這是因為:
執行到第二行程式碼屬性賦值時,後台創建了一個基本包裝類型的實例,這個age屬性確實掛到實例上去了,但是緊跟著,這個實例就被銷毀了。執行到第三行時,又重新建立了新的基本包裝類型的實例,自然是沒有age屬性的。
顯示使用基本包裝類型
除了在字串處於讀取模式下,後台會幫我們建立基本包裝類型實例時,我們自己也可以顯示地建立。
var str=new String("hello"); var str2=str.toUpperCase(); console.log(str2);//"HELLO:
這樣與後台幫我們創建時變數中保存的東西是不同的。
var str1=new String("hello"); var str2="hello"; typeof str1 //"object" typeof str2 //"string"
總結
多虧了有基本包裝類型,我們操作string、boolean、number這三種基本類型更方便了。每當讀取這三種基本型別值時,後台會建立對應的包裝類型實例,這個實例會呼叫指定方法,調用完就會被銷毀。這種短暫的生命週期決定了我們不能為基本類型添加自訂的屬性和方法。
我們再來看看javascript中String類別的subString()方法和slice()方法
最近在看《Javascript高級程式設計》一書,在書中發現一些以前沒有接觸過的且比較實用的技巧和知識點,想透過部落格記錄一下,以加深記憶。
在該書2.8.4節講到String類別中的subString()方法和slice()方法,其用法和回傳結果都基本上相同,如下範例:
var strObj = new String("hello world"); alert(strObj.slice(3)); // 输出结果:"ol world" alert(strObj.subString(3)); // 输出结果:"ol world" alert(strObj.slice(3, 7)); // 输出结果:"lo w" alert(strObj.subString(3,7)); // 输出结果:"lo w"
由以上代码的输出结果可已看出,slice()方法和subString()方调用方法法和输出结果完全一样,这两种方法返回的都是要处理的字符串的子串,都接受一个或两个参数,第一个参数是要获取的子串的起始位置,第二个参数是要获取子串的终止位置,如果第二个参数省略终止位置就默认为字符串的长度,且两个方法都不改变String对象自身的值。
为什么有两个功能完全相同的方法呢?事实上,这两个方法并不完全相同,不过只在参数为负值时,他们处理参数的方式稍有不同。
对于负数参数,slice()方法会用字符串的长度加上参数,subString()方法将其作为0处理,例如:
var strObj = new String("hello world"); alert(strObj.slice(-3)); // 输出结果:"rld" alert(strObj.subString(-3)); // 输出结果:"hello world" alert(strObj.slice(3,-4)); // 输出结果:"lo w" alert(strObj.subString(3,-4)) // 输出结果:"hel"
这样既可看到slice()和subString()方法的主要不同。当只有参数-3时,slice()返回"rld",subString()则返回"hello world"。这是因为对于字符串"hello world",slice(-3)将被转换成slice(8),而subString(-3)则转化成subString(0)。同样,使用3和-4差别也是很明显。slice()方法将被转换成slice(3,7),与前面的例子相同,返回"lo w"。而subString()方法则将这个两个参数解释为subString(0,3),实际上是:subString(0,3),因为subString()总是把较小的参数作为起始位,较大的数字最为终止位。