keywords:string
,indexOf
,concat
,split
,reverse
,join
字串( String )
普遍的看法是『 string 基本上只是字元所組成的 array 』,但實際上並不等於,string 與 array 相似的只有表面而已。
相似的地方:
- 它們都是 array-like
- 都有 length 特性
- indexOf(..) 方法 ( ES5 只有 array 版本的 )
- Array.prototype.indexOf() 回傳給定元素於陣列中第一個被找到之索引,若不存在於陣列中則回傳 -1。
- String.prototype.indexOf() 回傳給定元素於字串中第一個被找到之索引,若不存在於字串中則回傳 -1。
- concat(..) 方法
- Array.prototype.concat() 被用來合併兩個或多個陣列。 此方法不會改變原本的陣列,回傳一個包含呼叫者陣列本身的值,作為代替的是回傳一個新陣列。
- String.prototype.concat() 被用來聯集一個以上的字串,原字串不會被改變並回傳一個新的字串。
看看例子:
let a = 'egg';
let b = ['e','g','g'];
a.length; // 3
b.length; // 3
a.indexOf('g'); // 1
b.indexOf('g'); // 1
let c = a.concat('cake'); // 'eggcake'
let d = b.concat(['c','a','k','e']); // ['e','g','g','c','a','k','e']
a === c; // false
b === d; // false
a; // 'egg'
b; // ['e','g','g']
不相似的地方:
- string 是不可變的 ( immutable )
- array 是可變的( mutable )
看看例子:
a[1] = 'G';
b[1] = 'G';
a; // egg
b; // ['e','G','g']
a[1]
這種字元位置的存取動作,並非總是有效 舊版 IE 就不允許這種語法 ( 雖然現在可以了 ),但比較正確的做法是 a.charAt(1)
不可變的 string V.S. 可變的 array
不可變的 string:所有會更動內容的 string 方法都無法就地( in-place )修改,必須建立並回傳一個新的 string
let a = 'egg'; let c = a.toUpperCase(); a === c; // false a; 'egg' c; 'EGG'
可變的 array:許多會變更陣列內容的陣列方法實際上都是就地進行修改動作
let b = ['e','g','g']; b.push('!'); b; // ['e','g','g','!']
處理不可變的 string 時想要使用不會變動內容的 array 方法
雖然 array 方法無法直接被 string 取用,不過可以借取 ( borrow ) 🚩注意只有不會變動內容的 array 方法可借取
看看例子:
let a = 'egg';
a.join('-'); // TypeError: a.join is not a function
a.map(item => item.toUpperCase()); // TypeError: a.map is not a function
Array.prototype.join.call(a,'-'); // 'e-g-g'
時常搞不清楚 .call 到底是怎麼一回事,所以都會把上面這行想像成是這樣 ( 僅方便理解 ): a.join('-');
Array.prototype.map.call(a,item => item.toUpperCase() + '.').join(''); // 'E.G.G'
僅方便理解的樣子: a.map(item => item.toUpperCase());
處理不可變的 string 時想要使用會變動內容的 array 方法
- Array.prototype.reverse() 就地 ( in place ) 的反轉 array,即第一個元素變成最後一個,而最後一個元素變成第一個。
看看例子:
let a = 'egg';
let b = ['e','g','g'];
a.reverse(); // TypeError: a.reverse is not a function
b.reverse(); // ['g', 'g', 'e']
Array.prototype.reverse.call(a);
// TypeError: Cannot assign to read only property '0' of object '[object String]'
因為 reverse 是 array 就地修改內容的方法,那如果想要反轉字串怎做呢? 🤔
先把字串轉成 array 就可以用 reverse 方法了,然後再轉回 string 😏
a.split('').reverse().join(''); // gge
等等 太快了! split()? join()?
String.prototype.split() 將字串依照第一個參數( 分離器 )劃分為有序列的子串字集放入 array,並回傳該 array。
const str = 'Egg is good'; str.split(); // ['Egg is good'] str.split(''); // ['E', 'g', 'g', ' ', 'i', 's', ' ', 'g', 'o', 'o', 'd'] str.split(' '); // ['Egg', 'is', 'good'] str; // 'Egg is good'
Array.prototype.join() 將 array or array-like object 依照 , 或是傳入的分隔符號聯集建立並回傳一個新 string,如果 array 只有一個項目將回傳該值,並不會有任何的分隔符號。
const el = ['egg', 'water', 'fruit']; el.join(); // egg,water,fruit el.join('-'); // egg-water-fruit ['egg'].join(); // egg
回到上方的 string 想達到使用 array.reverse 一樣效果的例子:
a.split('').reverse().join(''); // gge step1: a.split(''); // ['e','g','g'] step2: a.reverse(); // ['g', 'g', 'e'] step3: a.join(''); // gge
要小心這種作法不適用於含有複雜 ( unicode ) 字元、astral符號、多位元組字元等的 string
Tip:如果字串較常把它當作字元所成陣列來對待,比較好的方法是實際上儲存為 array,而非 string,如果需要用到 string 的時候再呼叫 join(..) 轉成 string 即可。