uhyohyo.net

JavaScript初級者から中級者になろう

十一章第五回 プリミティブについて

このページの最終更新日:

今回はプリミティブの話です。この言葉は第一章第二回で初登場し、九章第七回でも軽く解説しています。今回はES5の視点も絡めつつさらに細かく解説します。

プリミティブには数値、文字列、真偽値、undefined,nullの5種類がありました。すなわちプリミティブはオブジェクト以外のものです。

typeof演算子

ここで、typeof演算子を紹介します。これは単項演算子であり、渡された値の種類を得られるものです。 typeof とします。返り値は文字列で、これは渡された値の種類です。返り値には次の種類があります。

"string"
プリミティブで、文字列。
"number"
プリミティブで、数値。
"boolean"
プリミティブで、真偽値。
"undefined"
undefined。
"object"
関数以外のオブジェクト及びnull。
"function"
関数。

このように、与えられた値がオブジェクトかどうか、そしてプリミティブならどの型のプリミティブかを判定することができます。ただし、注意点が2つあります。

1つは、オブジェクトの場合も返り値が2種類あるという点です。関数の場合とそれ以外のオブジェクトの場合ですね。そしてもう1つが問題です。プリミティブのうちnullが特殊な扱いを受けています。というのもtypeof null"object"を返すのです。typeofは大嘘つきですね。オブジェクトではないものに対しても"object"を返します。こんなおかしなことになっている理由は歴史的経緯なので仕方ありません。罠にはまらないように気をつけましょう。

例えば、ある変数aの中身がオブジェクトかどうか判定するには、次のようなコードを書く必要があります。(関数ではないということにしましょう。)


if(typeof a=="object" && a!=null){
}

ちなみに、このtypeofの動作はさすがにおかしいということで、新しいバージョンのJavaScriptではtypeof null"null"にしようという動きが一時期ありました。しかしこれは断念されました。

プリミティブとプロパティ

ここからは文字列のメソッドを紹介していきたいのですが、その前に1つ九章第七回の復習をしましょう。

プリミティブはオブジェクトではないですから、プロパティは無いのでした。逆に言うと、プロパティを持つのがオブジェクトなのです。

それにもかかわらずプリミティブのプロパティを参照することができ、その場合はプリミティブに対応したオブジェクトのインスタンスが一時的に作られていたのでした。

文字列の場合はStringオブジェクト、数値の場合はNumberオブジェクト、真偽値の場合はBooleanオブジェクトが生成されます。

nullとundefinedには対応するオブジェクトがないので変換できず、nullやundefinedのプロパティを参照しようとするとエラーになります。

なので、以下で文字列のメソッドとして紹介するものはString.prototypeに存在するメソッドです。

文字列のメソッド

文字列のメソッドについては四章第一回でも少し紹介したので、忘れている方はあわせて参照してください。もちろん今回はES5で追加されたメソッドも紹介していきますが、ES5より前からあるメソッドについてもまだ紹介していないものがあったので紹介しています。

charCodeAt

これはcharAtと少し似ています。charAtは引数(1つ)で指定された位置の文字1つを返しますが、charCodeAtはそのかわりにその文字のコードポイントを返します。例えば文字aはUnicodeコードポイントU+0061(十進数で97)を持つので、"abc".charCodeAt(0)は97となります。

concat

その文字列に、引数で渡された文字列(複数可)を全てつなげた文字列を返します。配列とは異なり文字列は+で繋げられるので使う機会はやや少ないかもしれません。


"aaa".concat("bbb","ccc","ddd")	// "aaabbbcccddd"

lastIndexOf

indexOfと同じく文字列を検索するメソッドですが、lastIndexOfは後ろから検索します

というのも、実はindexOfはマッチする文字列が複数ある場合一番最初の位置を返します。


"01234567890123456789".indexOf("123")	// 1

それに対し、lastIndexOfは、一番最後のものを返します。ただし、返される位置はindexOfと同じく、前から数えた位置なので注意してください。


"01234567890123456789".lastIndexOf("123")	// 11

ちなみに、indexOfやlastIndexOfは第2引数を渡すことができて、それは検索開始位置です。

indexOfの場合、その位置より前に条件にあうものがあっても無視されます。


"012340123401234".indexOf("abc",2)	// 6

この場合、2番目(0から数えるので最初の"2"の位置)以降から探すので、最初の"123"は無視されて、次の123がヒットして返されます。

lastIndexOfの場合、前から数えた位置を渡してあげるとその位置より後ろは無視されます。

localeCompare

localeとは「地域」、Compareとは「比較」のことです。このメソッドは2つの文字列を比較するメソッドです。返り値は数値です。具体的には、

a.localeCompare(b)

のように文字列aとbを比較したとき、aのほうがbより先なら負の値、aとbが同じなら0、aのほうがbより後なら正の値を返します。

このような返り値はどこかで見ましたね。そう、sortに渡すコールバック関数ですね。ということは、このメソッドは文字列をいい感じに並べたい時に使うことができそうです。

なお、正とか負とかいっても、実際にどんな値になるかは具体的には分かりません。ですから、興味があるのは正か負か0かだけで、具体的な値は特に意味が無いものとして考えるのがよいでしょう。

ではどのような基準で文字列が比較されるのかというと、それは辞書順です。


console.log("x".localeCompare("y"));	// 負
console.log("Z".localeCompare("A"));	// 正

この順序は必ずしもコードポイント順と同じにはなりません。例えば、スペイン語で使われるアルファベット“ñ”は、コードポイントU+00F1を持ち、普通のaからzのアルファベット(U+0061〜U+007A)よりずっと後ろにあります。ところが、localeCompareで比べると多くのブラウザではñはnとoの間にあるのではないかと思います。


console.log("n".localeCompare("ñ"));	// 負
console.log("ñ".localeCompare("o"));	// 負

これは、スペイン語等の辞書においてÑがNとOの間に来ることと合致しています。

そして、メソッド名のlocaleとは、ここでは多言語対応のことです。というのも、文字列の適切な並べ方は言語によって異なります。このメソッドは、指定した言語になるべく適したような並び順を提供してくれます。ブラウザができる範囲でですが。注意すべきなのは、このメソッドの結果はブラウザによって異なる可能性が多いにあるということです。本記事のサンプルコードでは正や負という結果が示されていますがこれはChrome 60の結果です。

そこで、localeCompareの第2引数に言語を指定することができます。言語は文字列で指定します。例えば日本語は"jp"で、英語は"en"です。言語を表す文字列はIETF言語タグで指定することになっていますので、さらなる詳細に興味がある方は調べてみてください。言語を指定することで、なるべくその言語に適した並び順となります。

例えば、MDNで紹介されていた例を引用します。


console.log('ä'.localeCompare('z', 'de')); // 負
console.log('ä'.localeCompare('z', 'sv')); // 正

同じ'ä''z'を比較しているのに、第2引数の言語タグにより結果が変わりました。上の言語タグ'de'はドイツ語で、下の言語タグ'sv'はスウェーデン語を意味します。実は、ドイツ語の辞書順ではäは普通のaと同じく扱われるのに対してスウェーデン語ではäはzの後に来ることになっています。そのためこのような違いが生じています。

また、localeStringは第3引数でさらに細かいオプションが指定可能です。しかし使う機会はほとんど無いでしょうから、詳しくはここでは解説しません。興味がある方は調べてみてください。

search

次に紹介するsearchは、以前紹介したmatch・replaceの仲間で、正規表現をマッチさせるメソッドです。

matchの場合はマッチに関する情報を配列で返して、replaceの場合は置き換え後の文字列を返しました。searchはもっと単純で、indexOfの正規表現版のようなものです。つまり、引数として渡された正規表現で文字列を検索し、マッチしたらその位置を返し、マッチしなかったら-1を返します。

ただし、indexOfのように途中から検索する機能はないので注意しましょう。


//数字の位置を返す例
"abc123def".search(/\d/)	// 3

split

splitは「分ける」という意味です。文字列を、第1引数に渡された文字で区切って、文字列の配列にして返します。例えばこういうことです。


"aaa,bb,c,dddd,ee".split(",")	// ["aaa","bb","c","dddd","ee"]

このように文字列を配列で分けたい場合はわりとよくあるので、重宝します。また、区切り文字は複数文字でも構いませんし、正規表現も利用可能です。


//正規表現:数字で区切る例
"fff1ghi2jjjkkk0lmn".split(/\d/)	// ["fff","ghi","jjjkkk","lmn"]

さらに、splitに正規表現を渡した場合、正規表現の中にグループ化の括弧があればその中の部分が配列に組み入れられます。例えば、さっきの例で数字を()で囲んでみると:


"fff1ghi2jjjkkk0lmn".split(/(\d)/)	// ["fff","1","ghi","2","jjjkkk","0","lmn"]

今回の場合全体を括弧でくくったので、区切り文字も配列の中に現れました。

さらにsplitには第2引数があり、これがある場合配列の要素数の上限を表します。つまり、分けた後に配列の要素数がこの数を超過してしまった場合、多い分は捨てられてから返されます。


"aaa,bb,c,dddd,ee".split(",",3)	// ["aaa","bb","c"]

この例では、ddddとeeが捨てられたことが分かります。

toLowerCase, toUpperCase

これらの関数は引数はありません。LowerCaseとは小文字、UpperCaseとは大文字のことであす。toLowerCaseは、文字列のうち大文字があれば小文字に変換したものを返します。toUpperCaseは逆に、小文字を大文字に変換したものを返します。その他の文字はそのままです。


"Hello, everyone.".toUpperCase()	// "HELLO, EVERYONE."
"ABCDE".toLowerCase()	// "abcde"

注意してほしいのは、英語のアルファベット以外にも大文字小文字は存在します。そういうものも変換できることになっています。


"Ω".toLowerCase()	// "ω"
"φ".toUpperCase()	// "Φ"
"á".toUpperCase()	// "Á"

toLocaleLowerCase, toLocaleUpperCase

上で紹介した関数にLocaleがつきました。先ほどのlocaleCompareと同様に多言語に対応した関数です。ただし、こちらは引数で言語を指定できるわけではありません。ブラウザの言語設定に応じて適切な大文字・小文字変換が行われます。

というのも、大文字小文字の概念が一般とは食い違う言語がたまにあるのです。

その代表例がトルコ語です。トルコ語においては、i(小文字のアイ)を大文字にすると、Iではなくİ(Iに上の点がある形)です。逆に、iはİの小文字ということになります。

そして、普通のIに対する小文字として、ı(上の点がない)があります。

ところが、一般にはIの小文字はiですから、toUpperCaseなどを使うと下のような結果になります。

"İ".toLowerCase()	// "i" (これは正しい)
"i".toUpperCase()	// "I" (トルコ語としては正しくない)

"ı".toUpperCase()	// "I" (これは正しい)
"I".toUpperCase()	// "i" (トルコ語としては正しくない)

普通の人はこうでないと困りますが、トルコ人はこれだと困るわけです。

toLocaleLowerCaseやtoLocaleUpperCaseならば、言語設定がトルコ語の場合のみ"I".toLocaleLowerCase()"ı"などとなります。

このメソッドは(というかそもそも普通のtoLowerCaseなども)あまり使いどころが無いかもしれませんが、もし機会があれば思い出してください。

trim

今回最後に紹介するtrimは、文字列の前後にある空白や改行を消した文字列を返すメソッドです。引数はありません。


"     aaa bbb ccc    ".trim()	// "aaa bbb ccc"

以上で文字列のメソッドの紹介が全て終了しました。次回は数値と真偽値の話です。