2022/03/4(金)~03/7(月) JSprimer(データ型とリテラル)

はじめに

プログラミング言語には、動的型付け言語と静的型付け言語という2種類のタイプがあります。

動的言語と静的言語の違い

  • 両者の違いは、あらかじめデータ型を決めるか(宣言する)どうかという部分です。

動的型付け言語

言語変数などのデータ型の宣言がいらない以下のようなプログラミング言語のことです。

例) Python Ruby JavaScript PHP など

静的型付け言語

変数などのデータ型の宣言があらかじめ必要なプログラミング言語のことです。以下のような言語が当てはまります。

例) C/C++ C# Java Go Scala Swift など

データ型

JavaScriptは動的型付け言語に分類される言語であるため、静的型付け言語のような変数の型はありません。 しかし、文字列、数値、真偽値といった値の型は存在します。 これらの値の型のことをデータ型と呼びます。 データ型を大きく分けると、プリミティブ型とオブジェクトの2つに分類されます。

  • JSは、動的型付け言語であるため変数に型をつけることはありませんが、変数に代入される値には型が存在します。
  • JSのデータ型は、プリミティブ型オブジェクトの2つがあります。

プリミティブ型

  • プリミティブ型(基本型)は、最初から用意されている基本的な値の型のことです。
  • プリミティブ型の値は、一度作成したらその値自体を変更できないというイミュータブルimmutable)の特性を持ちます。
  • イミュータブルは、オブジェクトが作成された後は状態を変更できないオブジェクトです。
  • 値を変更できないというのは、メソッドや関数を使って値を直接操作、変更したりすることができないという意味です。
  • ただし、プリミティブ値を代入することで新しい値に置き換えることは可能です。その場合も値の状態が変わった訳ではありません。変数自体が他の参照に代わるだけです。
  • プリミティブ型基本型
    • 真偽値(Boolean): trueまたはfalseのデータ型
    • 数値(Number): 42 や 3.14159 などの数値のデータ型
    • 巨大な整数(BigInt): ES2020から追加された9007199254740992nなどの任意精度の整数のデータ型
    • 文字列(String): "JavaScript" などの文字列のデータ型
    • undefined: 値が未定義であることを意味するデータ型
    • null: 値が存在しないことを意味するデータ型
    • シンボル(Symbol): ES2015から追加された一意で不変な値のデータ型

Primitive (プリミティブ)

イミュータブルな値の例⏬

// 文字列値を持つ新しいオブジェクトが作成されます。文字列はイミュータブル(不変)な値です。
var immutableString = "Hello";

console.log(immutableString);  //=> Hello

// 今、既存の値に "World" を追加しています。(ここで重要なのは、文字列HelloにWorldを追加した新しい文字列を作成していることです。)
immutableString = immutableString + "World";

// 確かに、既存の値にworldを追加した値が出力されるが、これは既存の値を変更したものではなく、新しい値を作成したものを参照しています。
console.log(immutableString); //=> HelloWorld
  • ここで重要なのは、"Hello"という既存の文字列を変更しているわけではないことです。
  • 既存の文字列を参照する変数immutableStringと" World"を連結して新たな文字列「"Hello World"」を生成しimmutableStringに再代入しています。
  • この時、新たに作成された文字列は新しいメモリー空間を参照することになります。ここからも既存の値自体が変更されたわけではないことが分かります。
  • 既存の値が参照されていたメモリーは使わなくなるため、ガベージコレクションに追加されます。
  • まとめると、イミュータブルな値は変更できません。上記の例は、一見値の変更が行われているように見えます。しかし、実際は変更されているのではなく、変更が加えられた新しいオブジェクトが作成されているだけなのです。(元の値は、変わっていません。)

  • 元の値を変更したい時使われるのは、既存の値ではなくそれをコピーして参照した値になります。こうすることで、元の値に影響が及ぶことはありません。

ミュータブルな型とイミュータブルな型の相違を知ろう

JavaScriptでイミュータブルなプログラミングをする

オブジェクト

  • オブジェクトは複数のプリミティブ型の値またはオブジェクトからなる値の集合のことです。
  • オブジェクトは、一度作成した後もその値自体を変更できるためミュータブルmutable)の特性を持ちます。
  • オブジェクトは、値そのものではなく値への参照を経由して操作されるため、参照型のデータとも言います。
  • プリミティブ型以外のデータをオブジェクトであると覚えていれば問題ありません。

Mutable (ミュータブル)

  • オブジェクト(複合型)
    • プリミティブ型以外のデータ
    • オブジェクト、配列、関数、正規表現、Dateなど

  • typeof演算子を使うことで、値のデータ型を調べることができます。
  • typeof演算子は、基本的にプリミティブ型またはオブジェクトかを判別するものです。
  • typeof nullが"object"となるのは、歴史的経緯のある仕様のバグです。本来は、nullと出力されるべきです。

typeof演算子

console.log(typeof true);// => "boolean"
console.log(typeof 42); // => "number"
console.log(typeof 9007199254740992n); // => "bigint"
console.log(typeof "JavaScript"); // => "string"
console.log(typeof Symbol("シンボル"));// => "symbol"
console.log(typeof undefined); // => "undefined"
console.log(typeof null); // => "object"
console.log(typeof ["配列"]); // => "object"
console.log(typeof { "key": "value" }); // => "object"
console.log(typeof function() {}); // => "function"

リテラル

  • リテラルとはプログラム上で数値や文字列など、データ型の値を直接記述できるように定義された構文のことです。
  • リテラル表現がない場合は、その値を作る関数(コンストラクタ関数など)に引数を渡して作成する形になります。これでは大変冗長なため、よく使われる値にはリテラルが用意されています。

  • リテラル表現を持つ4つのプリミティブ型

    • 真偽値
    • 数値
    • 文字列
    • null
  • リテラル表現が用意されているオブジェクト

リテラル表現を持つプリミティブ型①:真偽値(Boolean)

真偽値にはtruefalseリテラルがあり、trueとfalseの値を返します。

true; // => true
false; // => false

リテラル表現を持つプリミティブ型②:数値(Number)

数値には42のような整数リテラルと3.14159のような浮動小数点数リテラルがあります。

❶整数リテラル

整数リテラルには次の4種類があります。 Image from Gyazo

  • 10進数:0から9の数字の数字の組み合わせ
console.log(1); // => 1
console.log(10); // => 10
console.log(255); // => 255
  • 2進数:0bからはじまり、数値の0と1で表現される2進数リテラルは、ビットを表現するのによく利用されています。 bは2進数を表すbinaryを意味しています。
console.log(0b1111); // => 15
console.log(0b10000000000); // => 1024
  • 8進数: 0o(または0O)から始まり、0から7までの数字の組み合わせで表現される。ファイルのパーミッションを表現するのによく利用されます。
console.log(0o644);  // => 420
console.log(0o777);  // => 511
  • 16進数: 0xからはじまり、0から9までの数字とaからfまたはAからFのアルファベットの組み合わせで表現されます。文字のコードポイントやRGB値の表現などに利用されています。
console.log(0xFF); // => 255
// 小文字で書いても意味は同じ
console.log(0xff); // => 255
console.log(0x30A2); // => 12450

浮動小数リテラル

  • .ドット)を含んだ数値
// 0からはじまる浮動小数点数は、0を省略できる
.123; // => 0.123
  • eまたはEを含んだ数値
// eは指数(exponent)を意味する記号です。今回は、2の8乗なので、e8と記述します。
2e8; // => 200000000

上記を含む数値を浮動小数リテラルとしてみなします。

リテラル表現を持つプリミティブ型③: [ES2020] BigIntとは?

bigint型は、数値型よりも大きな整数を扱えるプリミティブ型です。 bigint型のリテラルは整数値の末尾にnをつけて書きます。 サバイバルTypeScript

  • 数値リテラルが正確に扱える数値の最大値は253-1(2の53乗から1引いた値)です。
  • 数値リテラルで253-1(9007199254740991)よりも大きな値を表現したり計算すると間違った結果となる場合があります。
  • その問題を解決するためにES2020でBigIntという新しい整数型のデータ型とリテラルが追加されました。
  • BigIntでは253-1(9007199254740991)よりも大きな整数を正しく表現できます。
// 数値の後ろにnをつけます。(数値は、整数のみ)
console.log(1n); // => 1n
// 2^53-1より大きな値も扱える
console.log(9007199254740992n); // => 9007199254740992n

// BigIntは、整数を扱うデータ型であるため、小数点を含めた場合は構文エラー
1.2n; // => SyntaxError

[ES2021] Numeric Separatorsとは?

ES2021から、数値リテラル内の区切り文字としてを追加できるNumeric Separatorsがサポートされています。 Numeric Separatorsは、数値リテラル内では区切り文字としてが追加できます。

1_000_000_000_000;

リテラル表現を持つプリミティブ型④: 文字列(String)

文字列リテラル共通のルールとして、同じ記号で囲んだ内容を文字列として扱います。 文字列リテラルとして次の3種類のリテラルがありますが、その評価結果はすべて同じ"文字列"になります。

  • ""または、''で囲んだ範囲が文字列リテラルになります
  • "(ダブルクォート)と'(シングルクォート)、バッククォートの3種類どちらで記述してもまったく同じ意味として評価されます。
console.log("文字列"); // => "文字列"
console.log('文字列'); // => "文字列"
console.log(`文字列`); // => "文字列"

ダブルクォートとシングルクォート

  • "(ダブルクォート)と'(シングルクォート)はまったく同じ意味となります。
  • もちろん、評価結果も同じです。
  • シングルクォートとダブルクォートの文字列リテラル改行を入れるには、エスケープシーケンスを使わないと構文エラーになってしまいます。
 // 文字列リテラルは同じ記号で囲む必要があるため、次のように文字列の中に同じ記号が出現した場合は、 \'のように\(バックスラッシュ)を使ってエスケープしなければなりません。
'8 o\'clock'; // => "8 o'clock"

// 文字列内部に出現しない別のクォート記号を使うことで、エスケープをせずに書くこともできます。
"8 o'clock"; // => "8 o'clock"


// ダブルクォートとシングルクォートどちらも、改行をそのままでは入力できないため、構文エラー(SyntaxError)となります。
"複数行の
文字列を
入れたい"; // => SyntaxError: "" string literal contains an unescaped line break

// 改行の代わりに改行記号のエスケープシーケンス(\n)を使うことで複数行の文字列を書くことができます。
"複数行の\n文字列を\n入れたい";

[ES2015] テンプレートリテラル

テンプレートリテラルは、`(バッククォート)で囲んだ範囲を文字列とするリテラルです。

  • テンプレートリテラルでは、複数行の文字列を改行記号のエスケープシーケンス(\n)を使わずにそのまま書くことができます。
// 改行を含む文字列をテンプレートリテラルで囲むだけで文字列として認識される
`複数行の
文字列を
入れたい`; // => "複数行の\n文字列を\n入れたい"
  • テンプレートリテラル内で${変数名}と書いた場合に、その変数の値を埋め込むことができます。
const str = "文字列";
console.log(`これは${str}です`); // => "これは文字列です"

リテラル表現を持つプリミティブ型⑤:nullリテラル

nullリテラルはnull値を返すリテラルです。 nullは「値がない」ということを表現する値です。

// null値を持つfooという変数を定義=>fooを値がない変数として定義し、参照できる
const foo = null;
console.log(foo); // => null

true、false、nullなどはグローバル変数ではなくリテラルであるため、同じ名前の変数を定義することはできません。 リテラルは変数名として利用できない予約語のようなものであるため、再定義しようとすると構文エラー(SyntaxError)となります。

  • true、false、nullは、リテラルとして定義されているため、変数名として利用できない予約語に設定されています。

リテラル表現が用意されているオブジェクト①: オブジェクトリテラル

オブジェクトはあらゆるものの基礎となります。 そのオブジェクトを作成する方法として、オブジェクトリテラルがあります。 オブジェクトリテラルは{}(中カッコ)を書くことで、新しいオブジェクトを作成できます。

オブジェクトリテラルはオブジェクトの作成と同時に中身を定義できます。 オブジェクトのキーと値を:で区切ったものを {} の中に書くことで作成と初期化が同時に行えます。 キー名には、文字列またはSymbolを指定し、値にはプリミティブ型の値からオブジェクトまで何でも入れることができます。

  • オブジェクトリテラル{}を使って、オブジェクトを作成することができます。
  • オブジェクト自体は、キーと値の組み合わせで記述していきます。
const obj = {}; // 中身が空のオブジェクトを作成


// オブジェクトの作成と初期化を同時に行う
// key というキー名と value という値を持つオブジェクトを作成
const obj = {
    "key": "value"
};
  • オブジェクトが持つキーのことをプロパティ名と呼びます。
  • プロパティを参照するには、.ドット)でつないで参照する方法と、 []ブラケット)で参照する方法があります。
const obj = {
    "key": "value"
};
// ドット記法でkeyプロパティの値を参照
console.log(obj.key); // => "value"

// ブラケット記法でkeyプロパティの値を参照
console.log(obj["key"]); // => "value"

リテラル表現が用意されているオブジェクト②: 配列リテラル

配列リテラルは[と]で値をカンマ区切りで囲み、その値を持つArrayオブジェクトを作成します。 配列(Arrayオブジェクト)とは、複数の値に順序をつけて格納できるオブジェクトの一種です。

配列は0からはじまるインデックス(添字)に、対応した値を保持しています。 作成した配列の要素を取得するには、配列に対してarray[index]という構文で指定したインデックスの値を参照できます。

  • 配列リテラル[]を使うと、配列オブジェクトを作成できます。

  • 配列は0からはじまるインデックス(添字)に、対応した値を保持しているため、値の取得の際は値に対応しているインデックス番号を指定します。

const emptyArray = []; // 空の配列を作成
const array = [1, 2, 3]; // 値を持った配列を作成

// 0番目の要素を参照(インデックス番号で指定)
console.log(array[0]); // => 1
// 1番目の要素を参照(インデックス番号で指定)
console.log(array[1]); // => 2

リテラル表現が用意されているオブジェクト③: 正規表現リテラル

JavaScript正規表現リテラルで書くことができます。 正規表現リテラルは/(スラッシュ)と/(スラッシュ)で正規表現のパターン文字列を囲みます。

  • /(スラッシュ)と/(スラッシュ)で正規表現のパターン文字列を囲むと正規表現リテラルとして表現できます。
  • 実際に囲まれている部分は、正規表現のパターン文字列として認識されます。
// 数字にマッチする特殊文字である\dを使い、1文字以上の数字にマッチする正規表現をリテラルで表現しています。

const numberRegExp = /\d+/; // 1文字以上の数字にマッチする正規表現(バックスラッシュで囲んでいる正規表現リテラル)
// `numberRegExp`の正規表現が文字列"123"にマッチするかをテストする
console.log(numberRegExp.test("123")); // => true

プリミティブ型とオブジェクト

  • 真偽値(Boolean)、数値(Number)、文字列(String)などのプリミティブ型はそれぞれリテラル以外にオブジェクトとして表現する方法もあります。
  • それは、ラッパーオブジェクトを使う方法です。
  • 本来オブジェクト型でないプリミティブ型の値(undefined と null を除く)をそれぞれ対応したラッパーオブジェクトで包んであげることで、あたかもオブジェクトのようにプロパティを参照したりメソッドを実行したりすることができるようになります。
  • ラッパーオブジェクトは、new演算子と対応するコンストラクタ関数を利用して作成します。
  • しかし、プリミティブ型の値は、必要に応じて自動的に対応するラッパーオブジェクトに変換されます。
  • そのため、明示的にラッパーオブジェクトを作成する処理を記述する必要はありません。
  • ラッパーオブジェクトのメソッドやプロバティを使用する際は、プリミティブ型のデータに対して直接メソッドを呼び出したりプロパティを参照するようにしましょう。

new演算子を使って、ラッパーオブジェクトを作成する場合(非推奨)

// 文字列をラップしたStringラッパーオブジェクト
const str = new String("文字列");
// ラッパーオブジェクトは"object"型のデータ
console.log(typeof str); // => "object"
// Stringオブジェクトの`length`プロパティは文字列の長さを返す
console.log(str.length); // => 3

ラッパーオブジェクトに自動的に変換される仕組みを利用して、直接プリミティブ型の値にラッパーオブジェクトのメソッドやプロパティを使用する場合(推奨)

// プリミティブ型の文字列データ
const str = "文字列";
// プリミティブ型の文字列は"string"型のデータ
console.log(typeof str); // => "string"
// プリミティブ型の文字列も`length`プロパティを参照できる
console.log(str.length); // => 3

ラッパーオブジェクトとプリミティブ型

参照

JSPrimer

ミュータブルな型とイミュータブルな型の相違を知ろう

JavaScriptでイミュータブルなプログラミングをする

動的言語と静的言語の違い

Mutable (ミュータブル)

typeof演算子

サバイバルTypeScript

ラッパーオブジェクトとプリミティブ型