はじめに
今まで何となく GAS を書いてGoogleスプレッドシートの改善とかやっていましたが、いつもわからないところを都度ググりつつ何となく JavaScript 書いているのがイヤになってきたので、ここで一度体系的に学習しようと思いました。(GAS ≒ JavaScript ではありますが…)
Salesforceの認定資格 JavaScriptデベロッパー 取得をゴールに見据えつつ、自分が今まで知識としてちゃんと定着できていなかった部分を記録として残していこうと思います。
なお学習には以下書籍を使用しています。(アフィリンクではありません)
また色々問題になりそうなので、記事内に本の内容をそのまま転載するようなことはせず、あくまで頭の中でかみ砕いた内容で記事作成をします。
実行環境
どちらも導入自体がかなり簡単でした!
学習のハードルが低いのは初心者には嬉しいところです。
Visual Studio Code を使う
Visual Studio Code
拡張機能:Live Server
Chrome や Edge の開発者コンソール
Chrome(やEdge)で
「F12→コンソール」から、直接コードを記載可能。
変数とデータ型
データ型
データ型の種類
データ型 | 値 | 備考 |
---|---|---|
String | 文字列 | |
Number | 数値 | 整数または浮動小数点数 |
BigInt | 巨大な数値 | |
Boolean | 真偽値 | true / false |
null | ヌル | |
undefined | 未定義 | |
Symbol | シンボル | 一意で不変な値 |
Object | オブジェクト |
プリミティブ型
文字列(String)
- 文字列リテラルはシングル、ダブル、バッククォートで表現可
- バッククォートのみ、
${変数名}
で変数内の値を直接参照可
// 文字列リテラルはシングル、ダブル、バッククォートで表現可
console.log('こんにちは'); // > こんにちは
console.log("こんにちは"); // > こんにちは
console.log(`こんにちは`); // > こんにちは
// バッククォートのみ、${変数名} で変数内の値を直接参照可
let name = "太郎"
console.log('こんにちは ${name}'); // > こんにちは ${name}
console.log("こんにちは ${name}"); // > こんにちは ${name}
console.log(`こんにちは ${name}`); // > こんにちは 太郎
- シングルクォートやダブルクォートを、文字列として表現したい場合は以下の2パターン。
// エスケープシーケンスで逃がす
console.log("こんにちは \"太郎\"さん"); // > こんにちは "太郎"さん
// 別のリテラルで囲う
console.log(`こんにちは "太郎"さん`); // > こんにちは "太郎"さん
console.log("こんにちは '太郎'さん"); // > こんにちは '太郎'さん
数値(Number)
- 2進数は
0b
、8進数は0o
、16進数は0x
で表現。
// 10進数:0以外の数値から始める
console.log(1234); // > 1234
// 2進数:0bから始める(ゼロビー)
console.log(0b11); // > 3
// 8進数:0oから始める(ゼロオー)
console.log(0o12); // > 10
// 16進数:0xから始める(ゼロエックス)
console.log(0x12); // > 18
BigInt
Number型よりも大きな数値を 整数値 として扱える。
Number型は -($2^{53}$ - 1)~($2^{53}$ - 1) までの数値を正確に扱える。
BigIntは、数値の末尾に n
を付けて表現。
// 「2の53乗 + 1 = 9007199254740993」です
// 範囲外のため、間違った値が出力
console.log(2 ** 53 + 1); // > 9007199254740992
// BigIntで表現することで、正確な値が出力
console.log(2n ** 53n + 1n); // > 9007199254740993n
- BigInt と Number 型は、混在して使用不可(エラーになる)
- BigInt は整数値しか扱えない(小数点以下は切り捨て)
// BigInt と Number 型は、混在して使用不可(エラーになる)
console.log(2n + 1); // > Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
// BigInt は整数値しか扱えない
let hoge = 5.1n; // Uncaught SyntaxError: Invalid or unexpected token
// BigInt 同士の割り算は、小数点以下が切り捨てられる
let ten = 10n;
let three = 3n;
console.log(ten / three); // > 3n(※小数点以下が切り捨てられた!)
コラム:安全な整数
Number型は -($2^{53}$ - 1)~($2^{53}$ - 1) が範囲。
これは「IEEE754」という規格に準じた、64ビット倍精度浮動小数点数に由来するそうです。
正常に扱える整数に対しては、「安全な整数」という表現をすることがあり、JavaScript では安全な整数かどうかをNumber.isSafeInteger()
で判定することが可能。
// 安全な整数
console.log( Number.isSafeInteger(2 ** 53 - 1) ); // > true
// 安全な整数ではない
console.log( Number.isSafeInteger(2 ** 53) ); // > false
console.log( Number.isSafeInteger(2 ** 53 + 1) ); // > false
本コラムに関しては、以下の記事を参考に勉強させていただきました!(感謝)
null と undefined
- null は参照を保持していないことを表す
- undefined は変数が未定義であることを表す
let hoge = null; // null は参照を保持していない
let huga; // 値を未設定
console.log( hoge ); // > null
console.log( huga ); // > undefined
オブジェクト型
初期化とプロパティへのアクセス方法
-
{~}
をオブジェクトリテラルと呼び、プロパティと値を:(コロン)
で区切って表現する - ドットか、ブラケットを使って、プロパティにアクセスする
// 人を表す person オブジェクト
let person = {
age: 30,
country: "Japan",
name: { first: "太郎", last: "JS" }
};
// ドットか、ブラケットを使って、プロパティにアクセスする
console.log( person.age ); // > 30
console.log( person["age"] ); // > 30
console.log( person.name.first ); // > 太郎
console.log( person["name"]["first"] ); // > 太郎
- ブラケット記法でのみ、プロパティ名に変数を用いることが可能(ドット記法で下記は実現できない)
// 全ユーザーを表す allUser オブジェクト
let allUser = {
user1: "ユーザーA",
user2: "ユーザーB"
};
// プロパティ名のベース
let propNameBase = "user";
// 変数を使って、プロパティにアクセス
console.log( allUser[ propNameBase + "1" ] ); // > ユーザーA
console.log( allUser[ propNameBase + "2" ] ); // > ユーザーB
プロパティの追加・更新・削除
- オブジェクトには、プロパティの追加・更新・削除が可能
- 追加、更新は変数と同じ感覚で
- 削除は
delete
演算子を使う
// 人を表す person オブジェクト
let person = {
age: 30,
country: "Japan",
name: { first: "太郎", last: "JS" }
};
// person オブジェクトに、性別(sex)を追加
person.sex = "male";
// person オブジェクトの、年齢(age)を更新
person.age = 35;
// person オブジェクトの、国(country)を削除
delete person.country;
console.log( person );
// > age: 35
// > name: {first: '太郎', last: 'JS'}
// > sex: "male"
メソッド
- オブジェクト内に関数を持たせることができ、それをメソッドと呼ぶ
- メソッドは
function
で表現する
// 挨拶を表す aisatsu オブジェクト
let aisatsu = {
japanese: function() { console.log("こんにちは"); },
american: function() { console.log("Hello"); }
};
aisatsu.japanese(); // > こんにちは
aisatsu.american(); // > Hello
// ES6 から、省略記法が可
let aisatsu2 = {
japanese() { console.log("こんにちは"); },
american() { console.log("Hello"); }
};
aisatsu2.japanese(); // > こんにちは
aisatsu2.american(); // > Hello
演算子
等価演算子
今まで何となく使っていた ==
と ===
の違い等、まとめてみます。
-
==
を抽象的な等価性と呼ぶ -
===
を厳格な等価性と呼ぶ -
==
では値が等しいことを確認し、===
では値と型が等しいことを確認する
// 1 と 1 の等価性
console.log( 1 == 1 ); // > true
console.log( 1 === 1 ); // > true
// 1 と "1" の等価性
console.log( 1 == "1" ); // > true
console.log( 1 === "1" ); // > false
// 1 と true の等価性
console.log( 1 == true ); // > true
console.log( 1 === true ); // > false
こう見ると、基本は ===
を使ったほうが良さそうな感触を得ました。
関係演算子
-
>
,>=
,<
,<=
は他言語でも見受けられる一般的な使い方(なので省略) -
in
:prop in obj
の場合、プロパティ prop が、オブジェクト obj 内に存在するか確認できる
// 人を表す person オブジェクト
let person = {
name: "太郎",
age: 18
};
// name プロパティは存在する
console.log( "name" in person ); // > true
// country プロパティは存在しない
console.log( "country" in person ); // > false
論理積と論理和
お馴染みの論理積(&&
)と論理和(||
)ですが、式としては判定が終わった段階の結果を boolean
としてではなく、そのまま値として返す挙動のようです。
// 論理積(&&)の結果を確認
console.log( "truthy1" && "truthy2" ); // > truthy2
console.log( "truthy1" && "truthy2" && "truthy3"); // > truthy3
console.log( "truthy" && null ); // > null
console.log( null && "truthy" ); // > null
console.log( null && undefined ); // > null
// 論理和(||)の結果を確認
console.log( "truthy1" || "truthy2" ); // > truthy1
console.log( "truthy1" || "truthy2" || "truthy3"); // > truthy1
console.log( "truthy" || null ); // > truthy
console.log( null || "truthy" ); // > truthy
console.log( null || undefined ); // > undefined
null 合体演算子
??
で表す。左のオペランドが null
か undefined
の場合、右のオペランドを返す。(逆なら左のオペランドをそのまま返す)
// a が null なので、右のオペランドが返る
let a = null;
console.log( a ?? "null or undefined です" ); // > null or undefined です
// b が undefined なので、右のオペランドが返る
let b = undefined;
console.log( b ?? "null or undefined です" ); // > null or undefined です
// c が null や undefined じゃないので、c そのものが返る
let c = 0;
console.log( c ?? "null or undefined です" ); // > 0
制御構文
for...in 文
オブジェクトの各要素を順番に返してくれる。
ただし返す要素の順番が担保されているわけではない点に注意!
// 色を表す colors オブジェクト
const colors = { red: "赤", yellow: "黄", blue: "青" };
// for...in で列挙
for( const val in colors ) {
console.log(val);
}
// > red
// > yellow
// > blue
コラム:列挙可能性
for...in
で列挙される/列挙されないは、各要素が持っている プロパティ記述子 内に定義されている。
プロパティ記述子の取得は Reflect.getOwnPropertyDescriptor()
で取得可能。
プロパティ記述子の設定は Reflect.defineProperty()
で設定可能。
列挙される/列挙されないは、プロパティ記述子内の enumelable
が boolean
で保持している。
// 色を表す colors オブジェクト
const colors = { red: "赤", yellow: "黄", blue: "青" };
// for...in で列挙(3つとも表示される)
for( const val in colors ) {
console.log(val);
}
// > red
// > yellow
// > blue
// 各要素のプロパティ記述子を見てみる
for( const val in colors ) {
const propInfo = Reflect.getOwnPropertyDescriptor( colors, val );
console.log(propInfo);
}
// > {value: '赤', writable: true, enumerable: true, configurable: true}
// > {value: '黄', writable: true, enumerable: true, configurable: true}
// > {value: '青', writable: true, enumerable: true, configurable: true}
// red のみ、enumerable の値を false に変える
Reflect.defineProperty( colors, "red", { enumerable: false } );
console.log( Reflect.getOwnPropertyDescriptor( colors, "red" ) );
// > {value: '赤', writable: true, enumerable: false, configurable: true}
// ↑falseに変わった!
// for...in で列挙(red が出なくなる)
for( const val in colors ) {
console.log(val);
}
// > yellow
// > blue
コラム:for...in と for...of
似たような機能を持っていますが、主に配列に対して使用すると違いが顕著に現れました。
// 配列を定義
const colorsAry = [ "red", "yellow", "blue" ];
// 色を表す colors オブジェクト
const colorsObj = { red: "赤", yellow: "黄", blue: "青" };
// for...in で配列を列挙すると、インデックスが表示
for( const val in colorsAry ) {
console.log(val);
}
// > 0
// > 1
// > 2
// for...of で配列を列挙すると、配列の中身が表示
for( const val of colorsAry ) {
console.log(val);
}
// > red
// > yellow
// > blue
// for...in でオブジェクトを列挙すると、key が表示
for( const val in colorsObj ) {
console.log(val);
}
// > red
// > yellow
// > blue
// for...of でオブジェクトを列挙すると、エラー
for( const val of colorsObj ) {
console.log(val);
}
// > Uncaught TypeError: colorsObj is not iterable
// ※オブジェクトリテラル {} で定義したオブジェクトは、反復可能オブジェクトではないため
// for...of 文では使えない
オブジェクトに対して for...of を使いたい場合は、Object.keys()
, Object.values()
, Object.entries()
を使う。
-
Object.keys()
:オブジェクトのキーを配列で取得 -
Object.values()
:オブジェクトの値を配列で取得 -
Object.entries()
:オブジェクトのキーと値のペアを配列で取得
// 色を表す colors オブジェクト
const colorsObj = { red: "赤", yellow: "黄", blue: "青" };
// キーの一覧を配列で取得
const keys = Object.keys( colorsObj );
// 値の一覧を配列で取得
const values = Object.values( colorsObj );
// オブジェクトのキーと値のペアを配列で取得
const entries = Object.entries( colorsObj );
// キーの一覧を表示
for( const key of keys ) {
console.log(key);
}
// > red
// > yellow
// > blue
// 値の一覧を表示
for( const val of values ) {
console.log(val);
}
// > 赤
// > 黄
// > 青
// キーと値のペアを表示
for( const entry of entries ) {
console.log(entry[0], entry[1]);
}
// > red 赤
// > yellow 黄
// > blue 青
まとめ
種類 | for...in | for...of | 備考 |
---|---|---|---|
配列 | indexが列挙 | 配列の中身が列挙 | |
オブジェクト {}
|
keyが列挙 | エラー | keys, values, entries を使えば、for...of も使用可 |
使い分け
- 配列:
for
またはfor...of
を使う - オブジェクト:
for...in
を使う。もしくはObject.keys()
,Object.values()
,Object.entries()
を経由してから、for...of
を使う。
最後に
次の記事(※作成中)へ続きます。