6時だョ!!全員集合!!

Railsを中心にアウトプットします。

2021年10月19日 JavaScript (JS Primer)

関数をオブジェクトとして扱う

JavaScriptの関数は、オブジェクトの一種です。オブジェクトとして扱えるので、変数に代入できます。

function fn() {
    console.log("さむい");
}
// 関数`fn`を`myFunc`変数に代入している。
const myFunc = fn;
myFunc; 
// => function fn() { console.log("さむい"); }

関数内の処理を実行したい場合、関数名の後ろに()をつけます。関数を実行することを、"関数を呼び出す"と言います。

myFunc(); // => さむい

関数式とは

関数式とは、関数を値として変数へ代入している式のことを言います。 関数宣言は文でしたが、関数式では関数を値として扱っています。 これは、文字列や数値などの変数宣言と同じ定義方法です。

以下の例では、無名関数が関数式です。

// 関数式
const 関数名 = function() {
    // 関数を呼び出したときの処理
    // ...
    return 関数の返り値;
};

関数式は、functionキーワードを使って宣言した関数と、アロー関数 の2種類があります。

関数名()で変数に代入した関数(関数式)が呼び出せます。

const apple = function() {
    // 関数を呼び出したときの処理
    // ...
    return "Ringo";
};

apple(); // => Ringo

2種類の関数

JavaScriptには、functionキーワードを使って定義される一般的な関数と、矢印記号=>を使って定義されるArrow Functionという関数があります。

// functionキーワードを使って、定義される関数
function () {}

// =>を使って定義されるArrow Function関数
() => {}

function fn() {
    console.log("fnが呼び出されました");
}
const myFunc = fn;
myFunc();  // 関数が入った変数も呼び出し時に()が必要

// 上記は関数`fn`をオブジェクトとして参照することで`myFunc`変数に代入しています。
// 関数をオブジェクトとして参照出来ることをファーストクラスファンクションと呼びます。(ファッションではありません)

コールバック関数

関数(メソッド)の引数として指定できる別の関数です。
一方、コールバック関数を引数として使う関数やメソッドのことを高階関数と呼びます。
上記の例では、withPersonというコールバック関数として使うために定義しています。mapメソッドの引数としてwithPersonをコールバック関数に指定しており、配列オブジェクトpeopleの要素を操作しています。今回の場合、mapメソッドが高階関数になります。

上記の例では、mapメソッドの引数として、無名関数をコールバック関数として指定しています。無名関数は、名前の通り関数名を指定しない関数のことです。無名関数の引数valueにmapメソッドで操作するarrayオブジェクトの配列の要素が1つずつ入ります。無名関数内で、valueを2倍にする処理を行います。実際にmapメソッドが返す値は、array配列内の要素を2倍にした結果を新たな配列として作成したものになります。

mapメソッド

mapメソッドとは、変換したい処理を行うときなどに使われるメソッド(関数)です。配列内の値にある処理を順に行わせてその結果を配列として返すメソッドです。

map() メソッドは、与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列を生成します。

// mapメソッドの使い方
array = [1, 2, 3]
const doubleArray = mapメソッドで処理を行う配列.map(配列の要素に対して、行いたい処理(コールバック関数等)){
    // 配列の要素を一つずつ取り出し、コールバック関数で定義された処理を行う
    // 処理を行なった後の値を新しい配列を生成して返す
}; 

---------------------------------
// mapメソッドの例
const array1 = [1, 4, 9, 16];

// 配列の要素を一つずつ取り出して、引数に渡した処理の戻り値を新しい配列として返します。
// 今回は、配列array1の各要素をxに代入して=>以降の処理(今回は要素を2倍にする処理)を行います。
// 処理後の結果を新しい配列の戻り値としてmap1に代入されます。
const map1 = array1.map(x => x * 2);

console.log(map1);
// 出力される値: Array [2, 8, 18, 32]

今回のようにmapメソッドにコールバック関数を使用した場合の処理の流れとしては以下のようになります。
1. 要素を取り出す。
2. それぞれの要素に対してコールバック関数を実行する。
3. それぞれの実行結果をまとめた新しい配列を作る。

ひとことで言うと、mapメソッドで抜き取った要素を用いて、新しい配列を作る前にどういったルールで作るかをコールバック関数で定義していると考えられます。

参考

JavaScript Primer 関数はオブジェクト

2021年10月18日 JavaScript (JavaScript Primer) 関数の引数と分割代入

分割代入とは

分割代入 (Destructuring assignment) 構文は、配列から値を取り出して、あるいはオブジェクトからプロパティを取り出して別個の変数に代入することを可能にする JavaScript の式です。

プロパティに直接値を代入して、キーと値のように紐づけることが出来るようになります。なぜ、分割代入をするのかというと、直接プロパティにアクセスすることができ簡潔に記述が出来るようになるためです。例えば、user.idとしなくてもidとするだけでuserオブジェクトのuser_idプロパティの値などにアクセス出来るようになるのです。

// 第1引数のオブジェクトから`id`プロパティを変数`id`として定義する。
function printUserId({ id }) {   // オブジェクトリテラルでidを指定することで分割代入を行うことが出来ます。
    console.log(id); // => 42
}
const user = {
    id: 42
};
printUserId(user);

オブジェクトの分割代入を行うメリットは何でしょうか?.
たくさんのデータを引数として渡したい時があります。その時に特定の受け取って良い値を仮引数に分割代入を使って、指定することが出来ます。そのように指定することで、受け取りたくない値を防ぐことができるかつ、受け取りたい値のみを受け取ることが出来ます。

関数の引数に分割代入を使う

関数の引数に分割代入を使うと、コードを簡略化することができます。

配列の場合

関数の仮引数を[]で囲むことによって、実引数に配列を指定すると分割代入ができます。

function print([first, second, ...other]) {
  console.log(first); // => 1
  console.log(second); // => 2
  console.log(...other); // => 3, 4, 5, 6, 7
}
const array = [1, 2, 3, 4, 5, 6, 7];
print(array);

配列の分割代入とRest Parametersの使用例
上記の例では、仮引数として配列を指定しています。「...other」では、Rest parametersを使って、複数の値を実引数として受け取ることが出来ます。 実際に値が渡るのは、関数の呼び出し時にprint(array)と値を指定している時です。

オブジェクトの場合

関数の仮引数を{}で囲むことによって、実引数にオブジェクトを指定すると分割代入ができます。

function printUserId({ id }) {
  console.log({id}) // => { id: 42 }
  console.log(id); // => 42
}

const user = {
    id: 42,
    name: "yano",
    age: 25,
    mentalAge: 10,
    status: 'free',
};
const other_user = {
    id: 99,
    name: "mayo",
    age: 15,
    mentalAge: 5,
    status: 'free',
};

// 関数の呼び出し
// 関数の呼び出し時に分割代入が行われる。
printUserId(user);  
printUserId(other_user);  

上記の例では、printUserId関数の仮引数を{id}として定義しています。この時点では、分割代入は行われていませんが、実際に関数の呼び出し時に分割代入で{id}に入れたい値を実引数として渡して代入しています。今回は、userオブジェクト{ id: 42, name:"yano",age:25,mentalAge:10,status:free }を代入しており、{id} = { id: 42 }が関数の呼び出し時に行われていることになります。分割代入を行うときに、オブジェクトのどのプロパティを代入するかは、代入したいプロパティ名を変数名として{id}のように指定していきますが、今回はuserオブジェクト全体を代入しています。取り出すときに、(id)と指定のプロパティを指定しているので、idプロパティに対応する値42が出力されます。

仮引数として、{id}を指定している時に分割代入が行われないことによって、さまざまな値を呼び出し時に代入することができます。固定された値のみでとどまらないところがメリットです。

オブジェクトの分割代入

今一度、復習としてオブジェクトの分割代入の例を以下に示します。

// 分割代入
const user = {
  id: 42,
  name: "yano"
};

const { id, name } = user;
console.log({ id, name });  // => { id: 42, name: 'yano' }
console.log({id, name}["id"]) // => 42
console.log({id, name}["name"]) // => yano
console.log(id); // => 42
console.log(name); // => yano
console.log(id, name); // => 42, yano

参考

JavaScript Primer 関数の引数と分割代入

MDN 分割代入

2021年10月16日 JavaScript (JavaScript Primer) 関数と宣言①

関数宣言

関数とは、ある一連の手続き(文の集まり)を1つの処理としてまとめる機能です。 関数を利用することで、同じ処理を毎回書くのではなく、一度定義した関数を呼び出すことで同じ処理を実行できます。

// 関数宣言
function 関数名(仮引数1, 仮引数2) {
    // 関数が呼び出されたときの処理
    // ...
    return 関数の返り値;
}
// 関数呼び出し
const 関数の結果 = 関数名(引数1, 引数2);
console.log(関数の結果); // => 関数の返り値

関数でreturn文を省略した時の挙動

関数でreturn文が実行されると、関数内ではそれ以降の処理は行われません。 また関数が値を返す必要がない場合は、return文では返り値を省略できます。 return文の返り値を省略した場合は、未定義の値であるundefinedを返します。

function fn() {
  // 何も返り値を指定してない場合は`undefined`を返す
  return;
  
  // すでにreturnされているため、この行は実行されません
  // コンソールに"Hello"は表示されません。
  console.log("Hello");
}
console.log(fn()); // => undefined

関数が何も値を返す必要がない場合は、return文そのものを省略できます。 return文そのものを省略した場合は、undefinedという値を返します。

function fn() {
  // return文を書かない。
}

console.log(fn()); // => undefined

[ES2015] デフォルト引数

デフォルト引数(デフォルトパラメータ)は、仮引数に対応する引数が渡されていない場合に、デフォルトで代入される値を指定できます。 次のように、仮引数に対して仮引数 = デフォルト値という構文で、仮引数ごとにデフォルト値を指定できます。

可変長引数

可変長引数とは、固定した数ではなく任意の個数の引数を受け取れる引数のことです。

可変長引数を実現するためには、以下の2つの方法のどちらかを使います。

  • Rest parametersを使う。

  • 関数の中でのみ参照できるargumentsという特殊な変数を利用する。

以下で、2つの方法それぞれを説明します。

Rest parametersを使う

Rest parametersは、仮引数名の前に...をつけた仮引数のことで、残余引数とも呼ばれます。 Rest parametersには、関数に渡された値が配列として代入されます。

Rest parametersは関数の宣言時に、可変長引数を仮引数として提示することが出来ます。そうすることで引数を何個でも複数受け取ることができます。今回は配列変数をrestparametersとして定義することで複数の引数を配列として受け取りその中身を自動で順番に取り出して関数内で使うことが出来ます。

function fn(...args) {
    // argsは引数の値が順番に入った配列
    console.log(args); // => ["a", "b", "c"]
}
fn("a", "b", "c");

Rest parametersと通常の引数を、併用することもできます。併用する場合、末尾にRest parametersを設定します。

function fn(arg1, ...restArgs) {
    console.log(arg1); // => "a"
    console.log(restArgs); // => ["b", "c"]
}
fn("a", "b", "c");

Spread構文を使うと、配列やオブジェクトを展開することができます。 関数を呼び出す際に、Spread関数を使うと引数に配列変数を使って呼び出すことが出来ます。実際に渡される引数の値は、Spread関数で展開された配列変数の中身になります。

function fn(x, y, z) {
    console.log(x); // => 1
    console.log(y); // => 2
    console.log(z); // => 3
}
const array = [1, 2, 3];
// Spread構文で配列を引数に展開して関数を呼び出す
fn(...array);
// 次のように書いたのと同じ意味
fn(array[0], array[1], array[2]);

console.log(...array); // => 1, 2, 3

関数の中でのみ参照できるargumentsという特殊な変数を利用する

可変長引数を扱う方法として、argumentsという関数の中でのみ参照できる特殊な変数があります。 argumentsは関数に渡された引数の値がすべて入ったArray-likeなオブジェクトです。 Array-likeなオブジェクトは、配列のようにインデックスで要素へアクセスできます。 しかし、Arrayではないため、実際の配列とは異なりArrayのメソッドは利用できないという特殊なオブジェクトです。

function fn() {
    // `arguments`はインデックスを指定して各要素にアクセスできる
    console.log(arguments[0]); // => "a"
    console.log(arguments[1]); // => "b"
    console.log(arguments[2]); // => "c"
}
fn("a", "b", "c");

2つの方法がありますが、基本的にはRest parametersが推奨されています。

arguments変数は仮引数の定義とは関係なく、実際に渡された引数がすべて含まれています。 そのため、関数の仮引数の定義部分だけ見ても、実際に関数の要求する引数がわからないという問題を作りやすいです。 Rest parametersであれば、仮引数で可変長を受け入れるかが明確になります。 このように、可変長引数が必要な場合はarguments変数よりも、Rest parametersでの実装を推奨します。

仮引数とは、関数に定義されているパラメーターのことです。
一般的には、仮引数を関数内で定義して呼び出しの際に実引数を渡します。対して、arguments変数は、関数内で使えて、配列のように扱える複数の実引数の情報が全て含まれています。
そのため、arguments変数を使うと以下のような処理の流れになります。まず関数宣言時に仮引数で宣言した後に、呼び出し時に仮引数で引数を渡すのではなく、関数として実引数を渡す流れになります。
ただし、arguments変数は、Array Likeなだけで厳密なArrayではないため、実際の配列とは異なります。arguments変数を使うと、Arrayのメソッドは利用できないという点には注意しましょう。

参考

JavaScript Primer 関数と宣言

2021年10月15日 JavaScript (JavaScript Primer) 明示的な型変換

明示的な型変換

falsyな値とは

JavaScriptでは、どの値がtrueでどの値がfalseになるかは、次のルールによって決まります。

  • falsyな値はfalseになる

  • falsyでない値はtrueになる

falsyな値とは次の7種類の値のことを言います。

  • false
  • undefined
  • null
  • 0
  • 0n
  • NaN
  • ""(空文字列)

falsyな値が本当にfalseになるかを、実際に検証しました。

// これらの値以外はtrueになります。
console.log(Boolean(false)) // => false
console.log(Boolean(undefined)) // => false
console.log(Boolean(null)) // => false
console.log(Boolean(0)) // => false
console.log(Boolean(0n)) // => false
console.log(Boolean(NaN)) // => false
console.log(Boolean("")) // => false

// 空白文字はfalsyな値ではないので、trueになる。
console.log(Boolean(" ")) // => true

Booleanコンストラクタ関数(任意の値 → 真偽値)

注意点

Booleanを使った型変換は、楽をするための型変換であり、正確に真偽値を得るための方法ではありません。 そのため、型変換をする前にまず別の方法で解決できないかを考えることも大切です。

Stringコンストラクタ関数(数値 → 文字列)

注意点

シンボルを文字列結合演算子(+)で文字列に変換できません。

Numberコンストラクタ関数(文字列 → 数値)

注意点

Numberコンストラクタ関数、Number.parseInt、Number.parseFloatは、 数字以外の文字列を渡すとNaN(Not a Number)を返します。

以下の例の場合、Number関数で数値に変換しようとしています。数値を文字列リテラルで囲ったものは数値へと変換できるので、数値と評価されるのですが、文字を文字列リテラルで囲った一般的な文字列は中身の部分が数値ではなく数値へと変換できないのでNaNと評価されてしまうのです。

// 数値はそのまま出力されます。
Number(1111); // => 1111

// 文字列リテラルで数字を囲っても数値として反映されます。
Number("1111"); // => 1111

数字以外の値はNaNとして出力されます。

// みかんという文字列は数字ではないため、数値へは変換できません。
Number("みかん"); // => NaN

// 未定義の値も、もちろんNaNになります。
Number(undefined); // => NaN

NaNとは

NaNはNot a Numberの略称であり、Number型の値です。

注意点

NaNしか持っていない特殊な性質として、自分自身と一致しないというものがあります。 この特徴を利用することで、ある値がNaNであるかを判定できます。

また、NaNは何と演算しても結果がNaNとなってしまうので、計算過程のどの値がNaNであるかを判別するのは困難です。

Number.isNaN() メソッド

引数がNaN かどうか評価します。
返り値は与えられた値が NaN であり、かつその型が Number である場合は true、それ以外の場合は false です。

空文字列かどうかを判定する

文字列が空文字かどうかを判定したい時、boolean関数を使ってfalsyな値を判定することで空文字かどうかを区別することが出来ます。しかし、falsyな値とは空文字以外にも0やundefindの時も含まれるので厳密にはboolean関数を使って完璧に空文字と判断することはできません。

// 空文字列かどうかを判定
function isEmptyString(str) {
    // `str`がfalsyな値なら、`isEmptyString`関数は`true`を返す
    // boolean関数で、'str'でfalsyな値(空文字、0、undefindの時)はfalseを返すようにする。
    return !Boolean(str);
}
// 空文字列列の場合は、trueを返す
console.log(isEmptyString("")); // => true
// falsyな値の場合は、trueを返す
console.log(isEmptyString(0)); // => true
// undefinedの場合は、trueを返す
console.log(isEmptyString()); // => true

しかし、以下のように記述することで完璧な空文字を判断することが出来ます。 空文字列とは「String型で文字長が0の値」であると定義できるので、isEmptyString関数を以下のように定義します。

// 空文字列かどうかを判定
function isEmptyString(str) {
    // String型でlengthが0の値の場合はtrueを返す
    return typeof str === "string" && str.length === 0;
}
console.log(isEmptyString("")); // => true
// falsyな値でも正しく判定できる
console.log(isEmptyString(0)); // => false
console.log(isEmptyString()); // => false

boolean関数では、厳密に真偽値を得るために有効な方法ではありません。そのため、そのことを理解して使う必要があります。また、今回のようにboolean関数を使わない別の方法を模索する必要があります。

まとめ

この章では暗黙的な型変換と明示的な型変換について学びました。

  • 暗黙的な型変換は意図しない結果となりやすいため避ける
  • 比較には等価演算子(==)ではなく、厳密等価演算子(===)を利用する
  • 演算子による暗黙的な型変換より、明示的な型変換をする関数を利用する
  • 真偽値を得るには、明示的な型変換以外の方法もある

参考

JavaScript Primer
Number.isNaN() メソッド

2021年10月14日 JavaScript (JavaScript Primer) 演算子

前回の記事の続きです。

ビット演算子

ビット演算子では、オペランドである数値を符号付き32ビット整数(0と1からなる32個のビットの集合)として扱います。
たとえば、1という数値は符号付き32ビット整数のビットでは、00000000000000000000000000000001 として表現されます。 わかりやすく4ビットごとに区切ると 0000_0000_0000_0000_0000_0000_0000_0001 のような32ビットの集合となります。 符号付き32ビット整数では、先頭の最上位ビット(一番左のビット)は符号を表し、0の場合は正の値、1の場合は負の値であることを示しています。

2の補数の求め方

JavaScript Primerの2の補数の説明を引用します。

符号付き32ビット整数では負の数値は、2の補数形式という形式で表現されます。2の補数とは、それぞれのビットを反転して1ビットを足した値となります。

2の補数の求め方の手順
1. 各ビットを反転させる
2. 1ビットプラスにする

ビット論理積

ビット論理積演算子(&)はビットごとのAND演算した結果を返します。 AND演算では、オペランドの各ビットがどちらも1の場合は1となり、それ以外の場合は0となります。 次のコードでは、10進数の15と9をAND演算しています。 15は、符号付き32ビット整数では0000_0000_0000_0000_0000_0000_0000_1111となります。 9は、符号付き32ビット整数では0000_0000_0000_0000_0000_0000_0000_1001となります。 これらをAND演算した結果は0000_0000_0000_0000_0000_0000_0000_1001となり、10進数の値である9を返します。

console.log(15 & 9); // => 9
// 同じ位の各ビット同士をAND演算する(上位の`0`は省略)
// 1111
// 1001
// ----
// 1001
console.log(0b1111 & 0b1001); // => 0b1001

AND演算子(&&)

AND演算子(&&)は、左辺の値の評価結果がtrueならば、右辺の評価結果を返します。 一方で、左辺の値の評価結果がfalseならば、そのまま左辺の値を返します。

Nullish coalescing演算子(??)

Nullish coalescing演算子(??)は、左辺の値がnullishであるならば、右辺の評価結果を返します。 nullishとは、評価結果がnullまたはundefinedとなる値のことです。

例えば、以下のようなコードの場合

obj.x = obj.x ?? 42;
  • obj.xがnullまたは、undefindの場合のみ、右辺の値が代入されます。
  • obj.xがnullまたは、undefind以外の場合、左辺の値が代入されます。

OR演算子( || )やAND演算子( && )がtrueかfalseを判断するのに対して、Nullish coalescing演算子(??)はnullまたはundefinedかを判断しています。そのため、OR演算子( || )やAND演算子( && )を使うとnullまたはundefinedかを判断しないので、注意が必要です。

|| (OR演算子)との違いは、OR演算子は左辺が false として判定される場合は右辺が返るのに対し、 ?? では null または undefined に限定されること。

// 左辺がnullishであるため、右辺の値の評価結果を返す
console.log(null ?? "右辺の値"); // => "右辺の値"
console.log(undefined ?? "右辺の値"); // => "右辺の値"
// 左辺がnullishではないため、左辺の値の評価結果を返す
console.log(true ?? "右辺の値"); // => true
console.log(false ?? "右辺の値"); // => false
console.log(0 ?? "右辺の値"); // => 0
console.log("文字列" ?? "右辺の値"); // => "文字列"

条件演算子三項演算子

三項をとる演算子であることから、三項演算子とも呼ばれます。
一つ目の式の処理結果に応じて実行する式が決まります。

条件式 ? Trueのとき処理する式 : Falseのとき処理する式;

if文との違いは、条件演算子は式として書くことができるため値を返します。

const valueA = true ? "A" : "B";
console.log(valueA); // => "A"

暗黙的な型変換とは

暗黙的な型変換とは次のことを言います。
ある処理において、その処理過程で行われる明示的ではない型変換のこと。

暗黙的な型変換では、ある処理の過程でオペランドの型によって、自動型変換が行われます。そのため、思ってもみない型に変換されてしまう危険性があります。
以下に例を記載します。

1 + "2"; // => "12"
// 演算過程で次のように暗黙的な型変換が行われる
// この場合+演算子を使うと文字列結合を優先する仕様となっているため、数値の1を文字列の1に変換してから計算をする
"1" + "2"; // => "12"

この場合、数値の1を文字列型に変換するつもりがなくても自動で型変換されてしまうのが暗黙的な型変換なのです。
なるべく明示的な型変換を使うように心がけましょう。

等価演算子と暗黙的な型変換の関係

等価演算子では、必ず暗黙的な型変換をしてから、右辺と左辺を比較します。暗黙的な型変換をするので、予期せぬ挙動が発生する可能性があります。そのため、等価演算子ではなく、厳密等価演算子を使うべきです。

( 等価演算子による予想できない型変換の種類は比較する値と型の組み合わせの数だけあるため、予測が困難です。)

3つ以上の値と暗黙的な型変換の関係

3つ以上の値を取り扱う場合、演算結果の型を予測するのが困難になります。以下のコードの場合、オペランドの順番によって、演算結果が変わります。

const x = 1, y = "2", z = 3;

// +演算子は文字列結合を優先する。
console.log(x + y + z); // => "123"
console.log(y + x + z); // => "213"
console.log(x + z + y); // => "42"

// 文字列に対する-演算子の定義はない。
// x + yは文字列だが、数値への型変換が実行される。
console.log(x + y - z); // => 9
console.log(typeof(x + y - z) ); // => number

暗黙的な型変換では、結果の値の型がオペランドの型に依存しているため、
意図せぬ挙動に繋がったり、思わぬバグが生じる原因となります。
その対策として、次回は明示的な型変換についてまとめていきたいと思います。

参考

JavaScript Primer

2021年10月12日 JavaScript (JavaScript Primer) データ型とリテラル2

前回の記事の続きです。

リテラル

リテラルとはプログラム上で数値や文字列など、データ型の値を直接記述できるように構文として定義されたものです。

リテラル表現を持つプリミティブ型のデータ型

  • 真偽値
  • 数値
  • 文字列
  • null

オブジェクトの中でよく利用されるリテラル表現

真偽値(Boolean)

真偽値にはtrueとfalseのリテラルがあり、それぞれはtrueとfalseの値を返すリテラルです。

数値(Number)

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

浮動小数点数リテラル これらのリテラルで表現できる数値はIEEE 754の倍精度浮動小数として扱われます。 倍精度浮動小数では64ビットで数値を表現します。 64ビットのうち52ビットを数字の格納のために使い、11ビットを小数点の位置に使い、残りの1ビットはプラスとマイナスの符号です。 そのため、正確に扱える数値の最大値は253-1(2の53乗から1引いた値)となります。

名前 表記例 用途
10進数 42 数値
2進数 0b0001 ビット演算など
8進数 0o777 ファイルのパーミッションなど
16進数 0oEEFF 文字のコードポイント、RGB値など

※ RGB値とは、「赤(R)」「緑(G)」「青(B)」の「光の三原色」から構成される色の表現方法です。カラーコードで使われています。  

文字列(String)

文字列リテラルとして次の3種類のリテラルがありますが、その評価結果はすべて同じ"文字列"になります。

console.log("文字列"); // => "文字列"
console.log('文字列'); // => "文字列"
console.log(`文字列`); // => "文字列"
ダブルクォートとシングルクォート
'8 o\'clock';
// 文字列リテラルは同じ記号で囲む必要があるため、次のように文字列の中に同じ記号が出現した場合は、 \'のように\(バックスラッシュ)を使ってエスケープします。

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

"複数行の\n文字列を\n入れたい";
// 改行を含んだ文字列は定義できないため、構文エラー(SyntaxError)となります。
// 改行の代わりに改行記号のエスケープシーケンス(\n)を使うことで複数行の文字列を書くことができます。**
テンプレートリテラル

バッククォートで囲むことで以下のことが可能になります。

  • 複数行の文字列をエスケープシーケンス(\n)を使わずにそのまま書くことができます。
  • テンプレートリテラル内で${変数名}と書いた場合に、その変数の値を埋め込むことができます。
`複数行の
文字列を
入れたい`; // => "複数行の\n文字列を\n入れたい"

const str = "文字列";
console.log(`これは${str}です`); // => "これは文字列です"

テンプレートリテラルも他の文字列リテラルと同様に同じリテラル記号を内包したい場合は、\を使ってエスケープする必要があります。

`This is \`code\``; // => "This is `code`"

nullリテラル

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

オブジェクトの中でよく利用されるリテラル表現

オブジェクトリテラル

オブジェクトリテラルは{}(波括弧)を書くことで、新しいオブジェクトを作成できます。

const obj = {}; 
// 中身が空のオブジェクトを作成しています。

オブジェクトリテラルはオブジェクトの作成と同時に中身を定義できます。

const obj = {
    "key": "value"
};
// key というキー名と value という値を持つオブジェクトを作成しています。
// オブジェクトが持つキーのことをプロパティ名と呼びます。
// objというオブジェクトはkeyというプロパティを持っている。

配列リテラル

オブジェクトリテラルと並んで、よく使われるリテラルとして配列リテラルがあります。配列(Arrayオブジェクト)とは、複数の値に順序をつけて格納できるオブジェクトの一種です。

const emptyArray = []; // 空の配列を作成しています。
const array = [1, 2, 3]; // 値を持った配列を作成しています。

上記の配列に値を追加してみます。

array.push(4,5);

console.log(array);  

// => [ 1, 2, 3, 4, 5 ]

arrayは可変な値のため中身を書き換えられています。

正規表現リテラル

正規表現リテラルとは、/正規表現のパターン文字列を囲んだものです。以下の例では、\d+というパターン文字列を/で囲んで、正規表現リテラルを作成しています。

const numberRegExp = /\d+/; // 1文字以上の数字にマッチする正規表現
// `numberRegExp`の正規表現が文字列"123"にマッチするかをテストする
console.log(numberRegExp.test("123")); // => true

BigIntとは?

JavaScriptで長い桁の整数を扱いたい場合に用いられるデータ型。ES2020標準で新たに採用された。従来のJavaScriptで数値を表すNumber型に整数を格納する場合、Number . MAX_SAFE_INTEGER 定数として定義された253-1を超える値では精度を維持することができなかった。 BigInt型は新たに導入された整数のみを表す数値型で、任意の桁の正負の整数を正確に保持、計算することができる。リテラルは「255n」のように整数の末尾に「n」を記載するか、BigInt(255) のように表記する。Number型が使える四則演算やビット演算など数値計算の機能は同じように使うことができる。

Numeric Separators

Numeric Separatorsは、数値リテラル内では区切り文字としてが追加できます。数値リテラルを評価する際には単純に無視されます。

1_000_000_000_000;

演算子

演算子の演算する対象のことをオペランドと呼びます。

// この場合、1と2がオペランドとなる。
1 + 2;

2つのオペランドを評価する演算子二項演算子と呼びます。

// 二項演算子とオペランドの関係
左オペランド 演算子 右オペランド

対して、1つのオペランドを評価する演算子単項演算子と呼びます。

let num = 1;
//どちらもnum += 1;と同じ意味
num++;
// または
++num;

二項演算子

[ES2016] べき乗演算子(**)

2つの数値のべき乗を求める演算子です。 左オペランドを右オペランドでべき乗した値を返します。

// べき乗演算子(ES2016)で2の4乗を計算
console.log(2 ** 4); // => 16

単行演算子

単項プラス演算子(+)

単項演算子の+はオペランドを数値に変換します。

数値や数値以外のオペランドを数値に変換できます。

//数値1と文字列1を数値の1に変換
console.log(+1); // => 1
console.log(+"1"); // => 1

// 数値ではない文字列はNaNという値に変換される
console.log(+"文字列"); // => NaN

上記のように数値に変換できない文字列などはNaNという特殊な値へと変換されます。

NaNは"Not-a-Number"の略称で、数値ではないがNumber型の値を表現しています。 NaNはどの値とも(NaN自身に対しても)一致しない特性があり、Number.isNaNメソッドを使うことでNaNの判定を行えます。

// 自分自身とも一致しない
console.log(NaN === NaN); // => false
// Number型である
console.log(typeof NaN); // => "number"
// Number.isNaNでNaNかどうかを判定
console.log(Number.isNaN(NaN)); // => true

数値への変換は、単項プラス演算子(+)を使うべきではありません。なぜなら、変換できない文字列などは、NaNが入ってしまうためです。また、Numberコンストラクタ関数やparseInt関数などの明示的な変換方法が存在するからです。

単項マイナス演算子(-)

単項マイナス演算子はマイナスの数値を記述する場合に利用します。 たとえば、マイナスの1という数値を -1 と書くことができるのは、単項マイナス演算子を利用しているからです。

console.log(-1); // => -1

インクリメント演算子

インクリメント演算子は、後置インクリメント演算子x++)と前置インクリメント演算子++x)の2種類があります。後置インクリメント演算子は、xを最初に評価してから、1を足します。前置インクリメント演算子は、xに1を足してから、xを評価します。

// 後置インクリメント演算子
let x = 1;
console.log(x++); // => 1
console.log(x);   // => 2

// 前置インクリメント演算子
let x = 1;
console.log(++x); // => 2
console.log(x);   // => 2

比較演算子

比較演算子オペランド同士の値を比較し、真偽値を返す演算子です。

厳密等価演算子(===)

厳密等価演算子は、左右の2つのオペランドを比較します。 同じ型で同じ値である場合に、trueを返します。 オペランドがどちらもオブジェクトであるときは、 オブジェクトの参照が同じである場合に、trueを返します。

console.log(1 === 1); // => true
console.log(1 === "1"); // => false

// {} は新しいオブジェクトを作成しています。
const objA = {};
const objB = {};
// 生成されたオブジェクトは異なる参照となります。
console.log(objA === objB); // => false
// 同じ参照を比較している場合
console.log(objA === objA); // => true

厳密不等価演算子(!==)

厳密不等価演算子は、左右の2つのオペランドを比較します。 異なる型または異なる値である場合に、trueを返します。

console.log(1 !== 1); // => false
console.log(1 !== "1"); // => true

等価演算子(==)

等価演算子(==)は、2つのオペランドを比較します。 同じデータ型のオペランドを比較する場合は、厳密等価演算子(===)と同じ結果になります。

console.log(1 == 1); // => true
console.log("str" == "str"); // => true
console.log("JavaScript" == "ECMAScript"); // => false
// オブジェクトは参照が一致しているならtrueを返します。
// {} は新しいオブジェクトを作成しています。
const objA = {};
const objB = {};
console.log(objA == objB); // => false
console.log(objA == objA); // => true

不等価演算子(!=)

不等価演算子(!=)は、2つのオペランドを比較し、等しくないならtrueを返します。

console.log(1 != 1); // => false
console.log("str" != "str"); // => false
console.log("JavaScript" != "ECMAScript"); // => true
console.log(true != true);// => false
// オブジェクトは参照が一致していないならtrueを返します。
const objA = {};
const objB = {};
console.log(objA != objB); // => true
console.log(objA != objA); // => false

分割代入

ある特定の情報だけを参照して、変数に格納したい場合、分割代入を使います。 影響範囲を少なくするため、変数に格納して使用します。

配列の場合

配列の場合は、右辺の配列の各要素を、左辺の配列の各要素に代入します。

const array = [1, 2];
// aには`array`の0番目の値、bには1番目の値が代入されます(分割代入)。
const [a, b] = array;
console.log(a); // => 1
console.log(b); // => 2

オブジェクトの場合
オブジェクトの場合は、右辺のオブジェクトのプロパティ値を、左辺に対応するプロパティ名へ代入します。この時、変数名とプロパティ名(キー名)は同じである必要があります。 ここで言うと、obj変数のプロパティ名orange分割代入する際に定義する変数名orangeと名前を一致させなければならないということです。

const obj = {
    "key": "value",
    "orange": "mikan",
    "foo": "hoge"
};

// プロパティ名`orange`の値を、変数`orenge`として定義します。
const { orange } = obj;
console.log(orange); // => "mikan"
console.log(apple); // => ReferenceError: apple is not defined


// 波括弧を付けないとobj自体が出力されてしまいます。
const foo = obj;  
// fooを分割代入したはずなのに、実行してみるとobjの内容になっています。
console.log(foo); // => {"key": "value","orange": "mikan","foo": "hoge"}; 
// このようになるため、波括弧が必要になってきます。  

この章のまとめはまだ途中なので、次回続きからまとめていきます。

参考

JavaScript Primer
「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
BIGINT型 【big integer】

2021年10月9日 JavaScript (JavaScript Primer)値の評価と表示 データ型とリテラル

値の評価とは、入力した値を評価してその結果を返すことです

例えば

  • 1 + 1 という式を評価したら 2 という結果を返します
  • bookTitle という変数を評価したら、変数に代入されている値を返します
  • const x = 1;という文を評価することで変数を定義するが、この文には返り値はありません("undefined"と表示されること)

これらのような値の評価を確認するため方法が紹介されていました。

・ブラウザの開発者ツールのコンソール上でJavaScriptコードを評価する方法
・HTMLからJavaScriptファイルを読み込む方法

REPL

ブラウザやNode.jsなど多くの実行環境には、コードを評価してその結果を表示するREPL(read–eval–print loop)と呼ばれる開発者向けの機能があります。
JS primerではFireFoxを用いられていましたがGoogle Chromeを使っている場合は使用しているブラウザで検証しても良いと思います。

ブラウザの開発者ツールのコンソール上でJavaScriptコードを評価する方法

式や値を入力したり、変数の宣言や宣言した変数を入力することで値が評価されることが確認できます。

HTMLからJavaScriptファイルを読み込む方法

コードを実行してエラーが発生した場合にはエラーメッセージや位置情報などが表示されます。

エラー情報を使ってデバッグすることでエラーの原因を取り除けるはずです。

構文エラーや実行時エラーの典型的なものはMDNのJavaScript エラーリファレンスにまとめられています。

パースとコンパイル

本文内にコードをパース(解釈) と出てきましたが、コンパイルとどのように違うのか疑問に思ったので調べました。

エンジン (ブラウザの場合は組み込まれています) はスクリプトを読み(“パース”)ます。 その後、スクリプト機械語に変換(“コンパイル”)します。 機械語が実行されます。非常に早く動作します。 エンジンは処理の各ステップで最適化を行います。実行時にコンパイルされたスクリプトも見ており、そこを流れるデータを分析し、それ基づいて機械語を最適化します。 最終的に、スクリプトはとても速く実行されます。

つまり、コンパイルする前にスクリプトを読む行為がパースです。

データ型とリテラル

この章ではデータ型とリテラルが紹介されていました。

動的型付け言語 と 静的型付け言語 (予備知識)

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

動的型付け

言語変数などのデータ型の宣言がいらないプログラミング言語PythonRubyJavaScriptPHPなど

静的型付け言語

変数などのデータ型の宣言が必要なプログラミング言語 C/C++、 C#、 Java、 Go

データ型

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

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

  • プリミティブ型は真偽値や数値などの不変な値の型の事です。
  • オブジェクト複数のプリミティブ型の値またはオブジェクトからなる集合であり、一度作成した後もその値自体を変更できるため可変な値 です。 値そのものではなく値への参照を経由して操作されるため、参照型のデータとも言います。

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

プリミティブ型

  • 真偽値(Boolean): trueまたはfalseのデータ型
  • 数値(Number): 42 や 3.14159 などの数値のデータ型
  • 巨大な整数(BigInt): ES2020から追加された 9007199254740992nなどの任意精度の整数のデータ型
  • 文字列(String): "JavaScript" などの文字列のデータ型
  • undefined: 値が未定義であることを意味するデータ型
  • null: 値が存在しないことを意味するデータ型
  • シンボル(Symbol): ES2015から追加された一意で不変な値のデータ型

    オブジェクト

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

プリミティブ型でないものは、オブジェクトであると覚えていれば問題ありません。

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"

typeof nullは本来"null"として出力されることが正しいですが"object"と出力されるのは、歴史的経緯のある仕様のバグとのことです。

まとめ

  • 7種類のプリミティブ型とオブジェクトがある

今日は、データ型とリテラルのデータ型までの内容をまとめました。
次回はリテラルからの内容をまとめます。

参考

JavaScript 入門