基本的なコードの書き方に慣れてきた頃、「もっと効率的に、もっと分かりやすくコードを書きたい」と感じていませんか。同じような処理を何度も書いたり、スクリプトが長くなりすぎて、どこで何をしているのか見失いそうになったりすることもあるかもしれません。
そんな悩みを解決してくれる強力な武器が、今回特集する「ユーザー定義関数」です。関数とは、簡単に言えば「一連の処理をひとまとめにしたオリジナルの命令セット」のことです。
この「ユーザー定義関数」を使いこなせるようになると、コードの再利用性が格段に上がり、スクリプト全体が非常に読みやすく、管理しやすくなります。複雑な処理も、小さな関数に分割することで、シンプルに考えられるようになるのです。
この記事では、GAS初心者の方に向けて、ユーザー定義関数の作り方と使い方を基礎から徹底的に解説します。
ユーザー定義関数
はい、承知いたしました。 前回のイントロに続き、PDFのセクション1「ユーザー定義関数を利用する」 の内容に基づき、記事を作成します。著作権に配慮し、コード例はすべてオリジナルで作成します。
ユーザー定義関数
ユーザー定義関数とは?
GASのスクリプトを書き進めると、「同じような処理を何度も書いているな」と感じることがあります。そんな時に役立つのが「ユーザー定義関数」です。
ユーザー定義関数とは、あなたが独自に定義する「処理のまとまり」のことです 。例えば、「消費税を計算する」「スプレッドシートの特定範囲をクリアする」といった一連の操作に名前を付け、一つの命令セットとして保存しておくイメージです。
実は、私たちが最初から使っている function myFunction() {} のようなものも、ユーザー定義関数の一つです 。
関数を使う最大のメリットは、以下の2点です。
- 再利用性の向上: 一度作れば、必要な時に何度でもその「処理のまとまり」を呼び出すことができます 。
- 可読性と管理性の向上: 複雑な処理を機能ごとに小さな関数に分割することで、コード全体が「何をしているのか」が分かりやすくなります 。
関数の基本的な作り方(定義)
GASで関数を定義(作成)する方法は決まっています。基本構文は以下の通りです 。
function 関数名(引数1, 引数2, ...) {
// ここに実行したい処理を書く
return 戻り値; // 結果を返す
}
- function: 関数を定義することを示すキーワード(おまじない)です 。
- 関数名: あなたが自由に決めることができる関数の名前です。変数名と同じルール(英数字とアンダースコアが使えるなど)に従い、プロジェクト内で重複しない名前を付けましょう 。
- (引数): 関数に処理を依頼する際に渡す「情報」です。( ) の中に記述します。複数ある場合はカンマ(,)で区切ります 。不要な場合は省略可能です 。
- { }: この中に、関数に実行させたい具体的な処理を記述します。
- return 戻り値: 関数の処理結果を、呼び出し元に返すための命令です 。
returnが実行されると、その時点で関数の処理は終了します 。戻り値が不要な場合は省略可能です 。
関数から別の関数を呼び出してみよう
関数の強力な点は、関数の中からさらに別の関数を呼び出せることです 。
例えば、「商品の本体価格(税抜)と税率(%)を渡すと、税込価格を計算してログに出力する」という処理を考えてみましょう。
// メインの処理を実行する関数
function mainFunction() {
let price = 1500; // 本体の価格
let taxRate = 10; // 税率 (10%)
// 別の関数(calculateTax_)を呼び出し、結果を受け取る
let priceWithTax = calculateTax_(price, taxRate);
console.log('本体価格: ' + price + '円');
console.log('税込価格: ' + priceWithTax + '円');
}
// 税込価格を計算する関数
function calculateTax_(basePrice, rate) {
let tax = basePrice * (rate / 100);
let total = basePrice + tax;
// 計算結果(total)を呼び出し元に返す (戻り値)
return total;
}
実行結果:
本体価格: 1500円
税込価格: 1650円
処理の流れ:
mainFunctionが実行されます。- 変数
price(1500) とtaxRate(10) が準備されます。 calculateTax_(price, taxRate)の部分でcalculateTax_関数が呼び出されます 。- この時、引数として
priceの値(1500)とtaxRateの値(10)がcalculateTax_関数に渡されます。 calculateTax_関数側では、basePriceに 1500、rateに 10 がコピーされて 処理が始まります。- 関数内で計算が実行されます (
totalは 1650 になります) 。 return total;によって、計算結果の 1650 がmainFunctionの呼び出し元に返されます 。- 返された 1650 が、
mainFunctionのpriceWithTaxという変数に代入されます 。 - 最後に
console.logが実行され、結果が表示されます。
このように、具体的な計算処理を別の関数に切り出すことで、mainFunction は「税込価格を取得して表示する」というメインの役割に集中でき、コードがすっきりします。
「戻り値」がない関数
関数は必ずしも return で値を返す必要はありません 。処理を実行するだけが目的の関数も多くあります 。return 文を省略した場合、その関数は「戻り値なし」となります 。
例えば、指定されたシートの特定セルに固定のメッセージを書き込む関数を作ってみます。
function mainFunction2() {
// 戻り値がない関数を呼び出す
// 値を受け取る変数 (例: let result = ...) は不要
writeMessageToSheet_('A1', '処理完了');
writeMessageToSheet_('A2', 'GASへようこそ');
}
// 指定セルにメッセージを書き込む(戻り値なし)
function writeMessageToSheet_(cellAddress, message) {
// 現在アクティブなスプレッドシートのシートを取得
let sheet = SpreadsheetApp.getActiveSheet();
// 指定されたセル(cellAddress)にメッセージ(message)を設定
sheet.getRange(cellAddress).setValue(message);
// この関数には return がない [cite: 117]
}
この writeMessageToSheet_ 関数は、mainFunction2 から呼び出されると、スプレッドシートへの書き込み処理を実行しますが、mainFunction2 には何の値も返しません。
実行メニューに表示させない「プライベート関数」
GASのエディタ画面で、実行する関数を選択するプルダウンメニューを見たことはありますか?
ここで問題なのが、作成した関数がすべてこのリストに表示されてしまうことです 。 先の例では、mainFunction や mainFunction2 は直接実行したいですが、calculateTax_ や writeMessageToSheet_ は、他の関数から呼び出されることを前提とした「部品」です 。
これらが実行リストに並んでいると、どれがメインの処理か分かりにくくなります。
この問題を解決するのが「プライベート関数」です。 作り方は簡単で、関数名の末尾にアンダースコア ( _ ) を付けるだけです 。
// 末尾に _ がないので、実行リストに表示される
function mainFunction() {
// ...
}
// 末尾に _ があるので、プライベート関数になる
function calculateTax_(basePrice, rate) {
// ...
}
// 末尾に _ があるので、プライベート関数になる
function writeMessageToSheet_(cellAddress, message) {
// ...
}
このように末尾に _ を付けた関数は、実行リストに表示されなくなります 。 これにより、実行リストは本当に実行したい(メインの)関数だけが並ぶ、すっきりとした状態に保つことができます 。
引数の便利な使い方
関数に情報を渡す「引数」には、さらに便利な機能があります。ここでは「デフォルト引数」と「残余引数」を紹介します 。
デフォルト引数
「引数が渡されなかった場合に、自動で設定される初期値」を決めておくことができます 。 書式は (引数名 = デフォルト値) です 。
例えば、ユーザーに挨拶する関数で、敬称が指定されなければデフォルトで「様」を付ける、といった使い方ができます。
// title のデフォルト引数に '様' を設定
function showGreeting_(name, title = '様') {
console.log(name + title + '、こんにちは。');
}
function mainFunction3() {
// 引数を2つ渡す場合
showGreeting_('鈴木', '部長');
// 引数を1つだけ渡す場合
showGreeting_('佐藤'); // titleは省略された
}
実行結果:
鈴木部長、こんにちは。
佐藤様、こんにちは。
2回目の呼び出しでは title が省略されたため、デフォルト値の「様」が自動的に使われました 。
残余引数(ざんよひきすう)
「引数がいくつ渡されるか分からない」場合に、それらをまとめて「配列」として受け取る機能です 。 書式は (...引数名) です 。
例えば、渡された複数のテストの点数をすべて合計して平均点を出す関数を考えてみましょう。
// ...scores で複数の点数を「配列」として受け取る
function calculateAverage_(...scores) {
if (scores.length === 0) {
return 0; // 点数がなければ0を返す
}
let total = 0;
// scores は配列なので for...of ループが使える
for (let score of scores) {
total += score;
}
let average = total / scores.length;
console.log('平均点: ' + average);
}
function mainFunction4() {
// 引数を3つ渡す
calculateAverage_(80, 90, 70);
// 引数を5つ渡す
calculateAverage_(100, 85, 90, 95, 80);
}
実行結果:
平均点: 80
平均点: 90
calculateAverage_ 関数は、3個や5個など、異なる数の引数に柔軟に対応できています。
(補足) 配列を引数として渡す(スプレッド構文)
残余引数を持つ関数(例: calculateAverage_)に、配列のデータを渡したい場合もあります。 その場合、配列変数の前に ... を付けます(スプレッド構文といいます)。
function mainFunction5() {
let mathScores = [60, 70, 65];
// 配列の前に ... を付けて渡す
// これは calculateAverage_(60, 70, 65) と同じ意味になる
calculateAverage_(...mathScores);
}
実行結果:
平均点: 65
配列 mathScores が展開され、calculateAverage_ 関数に個別の引数(60, 70, 65)として渡されました。
デバッグ
スクリプトの「バグ」を見つける味方、「デバッガ」
スクリプトが長くなったり複雑になったりすると、タイプミスや論理的な間違いによって、意図した通りに動作しないことが増えてきます。
こうしたプログラムの誤りのことを一般に「バグ (Bug)」と呼びます 。そして、このバグを見つけて修正する作業のことを「デバッグ (Debug)」と呼びます 。
GASには、このデバッグ作業を効率的に行うための強力なツール「デバッガ」が標準で用意されています 。デバッガを使うと、スクリプトの実行を途中で一時停止させ、その時点での変数の値などを確認しながら、1行ずつ慎重に処理を進めることができます。
デバッガの基本的な使い方(ステップバイステップ)
ここでは、デバッガの使い方を、具体的なステップで見ていきましょう。 例として、複数の商品の合計金額から割引を計算する、以下のスクリプトを使います。
function calculateMain() {
let itemPrice = 500;
let quantity = 3;
let discountRate = 0.1; // 10% 割引
// 小計を計算
let subtotal = itemPrice * quantity;
// 割引額を計算する関数を呼び出す
let discountAmount = getDiscount_(subtotal, discountRate);
// 最終的な支払い金額
let finalPrice = subtotal - discountAmount;
console.log('小計: ' + subtotal);
console.log('割引額: ' + discountAmount);
console.log('最終価格: ' + finalPrice);
}
// 割引額を計算するプライベート関数
function getDiscount_(price, rate) {
let discount = price * rate;
return discount;
}
1. ブレークポイントを設定する
まず、スクリプトの実行を一時停止したい行に「ブレークポイント」を設定します。 エディタの行番号の左側をクリックすると、その行に紫色の丸が表示されます。これがブレークポイントです 。
ここでは、calculateMain 関数の最初の行(let itemPrice = 500;)に設定してみましょう。
ブレークポイントは複数設定することも可能で、もう一度クリックすると解除できます 。
2. [デバッグ] を実行する
関数選択メニューで calculateMain が選ばれていることを確認し、[実行] ボタンの隣にある [デバッグ] ボタンをクリックします 。
すると、スクリプトの実行が開始され、設定したブレークポイントの行で処理が一時停止します。その行は紫色にハイライトされます 。
3. デバッガ画面の見方
実行が一時停止すると、画面右側に「デバッガ」という操作パネルが表示されます 。
このパネルには主に2つの重要な領域があります。
- コールスタック: 現在実行している関数の一覧が表示されます 。今は
calculateMainが表示されているはずです。 - 変数: その時点で参照できる変数のスコープ(Local, Globalなど)と、その値の一覧が表示されます 。
デバッガの操作ボタン
デバッガパネルの上部には、処理をコントロールするためのボタンが並んでいます 。 ここでは主要な4つのボタンの動作を見てみましょう 。
② ステップオーバー (1行ずつ実行)
このボタンを押すと、現在停止している行の処理を1行だけ実行し、次の行で再び一時停止します 。
let itemPrice = 500; の行で停止している状態で、[ステップオーバー] を1回押してみましょう。 紫色のハイライトが次の行(let quantity = 3;)に移動します。
同時に、右側のデバッガパネルの「変数」>「Local」セクションを見てください。
itemPrice の値が 500 となったことが確認できます 。
このように、1行ずつ処理を進めながら、変数の値が意図した通りに変わっていくかを確認するのがデバッグの基本です。
③ ステップイン (関数の中に入る)
[ステップオーバー] を何度か押し、let discountAmount = getDiscount_(...); の行まで進めてください。
この行は、私たちが定義した getDiscount_ 関数を呼び出しています。 ここで [ステップオーバー] を押すと、getDiscount_ 関数の処理が(内部的に)すべて実行され、次の行(let finalPrice = ...;)に進みます。
しかし、もし getDiscount_ 関数自体の動作が怪しい場合、その「中身」を調べたいはずです。 そういう時に [ステップイン] ボタンを使います 。
let discountAmount = ...; の行で [ステップイン] を押すと、処理のハイライトが getDiscount_ 関数の内部(let discount = price * rate; の行)にジャンプします 。
同時に、「コールスタック」には getDiscount_ が追加され、「変数」>「Local」には getDiscount_ の引数である price (1500) と rate (0.1) が表示されます。
④ ステップアウト (関数から抜け出す)
getDiscount_ 関数の中に入ったはいいものの、「この関数はもう問題ない」と分かった場合、関数の最後まで1行ずつ進めるのは面倒です。 [ステップアウト] ボタンを押すと、現在の関数の残りの処理をすべて実行し、その関数を呼び出した元の行(この場合は calculateMain 関数の let discountAmount = ...;)の直後に戻ることができます 。
① 再開 (次のブレークポイントまで進む)
このボタンを押すと、処理が再開されます 。 スクリプトのこの先に別のブレークポイントがあればそこで再び停止し 、なければスクリプトの最後まで実行されます 。
デバッガは、最初はとっつきにくいかもしれませんが、使えるようになるとバグ修正の時間が劇的に短縮されます。ぜひマスターしましょう。
スコープ
変数の有効範囲、「スコープ」を理解しよう
スクリプトを書いていく上で、変数や関数を宣言する場所は非常に重要です。なぜなら、「どこで宣言されたか」によって、その変数や関数が「どこから参照(利用)できるか」の範囲が決まるからです。
この「参照できる範囲」のことを スコープ (Scope) と呼びます 。
スコープのルールを理解していないと、「変数が定義されていません」というエラー(ReferenceError) に悩まされたり、意図せず他の場所で定義した変数の値を上書きしてしまったりする「バグ」の原因になります。
GASのスコープは、大きく分けて次の2種類があります 。
- グローバルスコープ: プロジェクト全体から参照できる、最も広い範囲 。
- ローカルスコープ: ある特定の範囲(関数の中など)からのみ参照できる、限定的な範囲 。
それぞれ詳しく見ていきましょう。
グローバルスコープ(グローバル変数)
関数の「外側」で宣言された変数は、グローバル変数 と呼ばれます 。 グローバル変数はグローバルスコープを持ち、同じプロジェクト内であれば、どのスクリプトファイルの、どの関数からでも参照することができます 。
// 関数の外で宣言(グローバルスコープ)
let companyName = 'GASブログサポート';
function showCompany() {
// グローバル変数を参照
console.log('会社名: ' + companyName);
}
function showGreeting() {
// 別の関数からも同じグローバル変数を参照
console.log(companyName + 'へようこそ!');
}
// 実行結果(showCompany を実行)
// 会社名: GASブログサポート
// 実行結果(showGreeting を実行)
// GASブログサポートへようこそ!
便利な反面、注意点があります。プロジェクト内のどこからでもアクセスできるため、複数のファイルや関数で同じ名前のグローバル変数を定義してしまうと、名前が競合し、意図しない動作を引き起こす可能性があります 。グローバル変数の名前は、プロジェクト内で重複しないように注意深く管理する必要があります 。
ローカルスコープ(ローカル変数)
関数の「内側」で宣言された変数や、関数の引数は、ローカル変数 と呼ばれます 。 ローカル変数はローカルスコープを持ち、その変数が宣言された 関数の中でのみ 利用可能です 。
// グローバル変数
let globalMessage = 'こんにちは';
function functionA() {
// ローカル変数 (functionA の中で宣言)
let localMessageA = 'functionA です';
console.log(globalMessage); // OK
console.log(localMessageA); // OK
}
function functionB() {
// functionB のローカル変数(たまたま functionA と同じ名前)
let localMessageA = 'functionB です';
console.log(globalMessage); // OK
console.log(localMessageA); // OK (これは functionB のもの)
// console.log(localMessageA); // エラーになる!
// (functionA で宣言された localMessageA は、functionB からは参照できない)
}
functionA を実行すると、globalMessage と localMessageA の両方が正しく表示されます。 しかし、functionB の中で(コメントアウトされている行の)functionA の localMessageA を使おうとすると、「localMessageA is not defined」というエラーが発生します 。functionA のローカル変数は、functionA の外からは「見えない」ためです。
また、functionA と functionB の両方で localMessageA という同名の変数を宣言していますが、これらはそれぞれが属する関数内でのみ有効な「別の変数」として認識されるため、問題ありません 。
ローカルスコープのさらに詳しい種類
ローカルスコープは、さらに「関数スコープ」と「ブロックスコープ」に分けることができます 。
まず ブロック (Block) とは、{ }(波括弧)で囲まれた範囲のことです 。 if 文や for 文、while 文の { } などがこれにあたります。
- 関数スコープ:
functionの{ }全体が有効範囲です。 - ブロックスコープ:
ifやforなどの{ }の中だけが有効範囲です 。
let や const を使って宣言された変数は、この ブロックスコープ に従います。
function checkTemperature(temp) {
// 関数スコープの変数
let baseMessage = '現在の温度は ' + temp + ' 度です。';
if (temp > 30) {
// ブロックスコープの変数 (if文の { } の中だけで有効)
let advice = '暑すぎます!水分補給をしてください。';
console.log(baseMessage + advice); // OK
} else {
// こちらもブロックスコープ
let advice = '快適な温度です。';
console.log(baseMessage + advice); // OK
}
// console.log(advice);
// エラーになる!
// 'advice' は if/else の { } ブロックの中で宣言されたため、
// ブロックの外側であるここでは参照できない 。
}
checkTemperature(35);
// 実行結果:
// 現在の温度は 35 度です。暑すぎます!水分補給をしてください。
checkTemperature(25);
// 実行結果:
// 現在の温度は 25 度です。快適な温度です。
advice という変数は if と else のそれぞれのブロック内で宣言されているため、そのブロックを抜けると無効になります 。
このブロックスコープのルールは for 文でも同様です。
function loopTest() {
// 1回目のループ
// この 'i' は、1つ目の for ブロック内でのみ有効
for (let i = 0; i < 3; i++) {
console.log('Loop1: ' + i);
}
// 2回目のループ
// この 'i' は、2つ目の for ブロック内でのみ有効
// 1回目の 'i' とは別物なので、名前が衝突しない
for (let i = 0; i < 2; i++) {
console.log('Loop2: ' + i);
}
// console.log(i); // エラーになる! (i は for の外では参照できない)
}
for 文の初期化処理で宣言された let i は、その for 文のブロック内でのみ有効なローカル変数となります 。そのため、同じ関数内で同じ let i を使った for 文を複数書いても、変数が衝突せず安全に動作します 。
関数の高度な概念
関数は「モノ」でもある? 関数オブジェクトの概念
これまでの説明で、関数は「処理を実行するための命令」という側面を見てきました。しかしGAS(JavaScript)では、関数はもう一つの顔を持っています。それは、関数自身が「オブジェクト」であるという性質です 。
オブジェクトとは、「モノ」や「データ」のようなものです。 数値や文字列を変数に代入できるように、関数も「オブジェクト」として変数に代入することができます 。
関数を変数に代入する
まずは、すでに名前が定義されている関数を、変数に代入する例を見てみましょう。
// 円の面積を計算する関数
function calculateCircleArea_(radius) {
return radius * radius * 3.14;
}
function mainFunction6() {
// calculateCircleArea_ という「関数オブジェクト」を
// 'calc' という名前の変数に代入する
let calc = calculateCircleArea_;
// 'calc' 変数を、元の関数名と同じように呼び出せる
let area = calc(10); // calculateCircleArea_(10) と同じ
console.log('面積: ' + area);
}
実行結果:
面積: 314
let calc = calculateCircleArea_; という行に注目してください 。 ここでは、calculateCircleArea_() のように () を付けて「実行」しているのではなく、calculateCircleArea_ という関数オブジェクトそのものを calc という変数に「代入」しています。
その結果、calc 変数は calculateCircleArea_ 関数とまったく同じ機能を持つようになり、calc(10) のように呼び出すことが可能になります 。
名前がない関数? 「無名関数」とは
関数はオブジェクトとして扱えるため、さらに一歩進んだ使い方ができます。それが「無名関数(むめいかんすう)」です 。
無名関数とは、その名の通り「名前を持たない関数」のことです。 通常の関数は function 関数名() {} のように定義しますが、無名関数は 関数名 の部分を省略して定義します 。
無名関数の書式:
function(引数1, 引数2, ...) {
// 実行したい処理
return 戻り値; [cite: 613]
}
「名前がないのにどうやって呼び出すの?」と疑問に思うかもしれません。 この無名関数は、先ほど説明した「関数オブジェクト」として、変数に直接代入して使われるのが一般的です 。
function mainFunction7() {
// 変数 'subtract' に、無名関数(引き算)を直接代入する
let subtract = function(a, b) { [cite: 620]
return a - b; [cite: 622]
}; [cite: 624] // オブジェクトを変数に代入する文なので、最後にセミコロン(;)を付けます
// 'subtract' 変数を関数として呼び出す
let result = subtract(100, 30); [cite: 628]
console.log('結果: ' + result);
}
実行結果:
結果: 70
mainFunction7 の中で subtract という名前の関数を定義したわけではなく、「引き算の処理を行う無名関数オブジェクト」を作成し、それを subtract という変数に入れた、という流れです 。
この「関数を変数に入れる」「無名関数」という概念は、一見すると奇妙に感じるかもしれませんが、GASのさまざまな場面(特にスプレッドシートの操作やWebアプリ化など)で非常に強力なテクニックとして登場します。
関数をスプレッドシートの「メニュー」から呼び出す
これまで作成した関数は、GASエディタの [実行] ボタンを押して動作させてきました。しかし、これではスクリプト開発者しか関数を実行できません。
GASの真価は、スプレッドシートの利用者(GASのコードを知らない人)でも簡単に処理を実行できるようにすることにあります。
それを実現する最も簡単な方法が、スプレッドシートのメニューバーに「カスタムメニュー」を追加し、そこから関数を呼び出せるようにすることです。
自動で実行される特別な関数「onOpen()」
スプレッドシートにカスタムメニューを追加するには、onOpen() という特別な名前の関数を使います。
onOpen という名前の関数を定義しておくと、そのスプレッドシートが開かれた(または再読み込みされた)タイミングで、GASが自動的にその関数を実行してくれます 。
カスタムメニューの作成手順
カスタムメニューは、この onOpen() 関数の中で以下の4ステップで作成します。
- UI(ユーザーインターフェース)を取得する
SpreadsheetApp.getUi()という命令で、スプレッドシートのUI(メニューバーなど)を操作するためのオブジェクトを取得します 。 - メニュー本体を作成する 取得したUIオブジェクトに対し、
.createMenu('メニュー名')で、メニューバーに表示したい名前のメニューを新しく作成します 。 - メニュー項目(アイテム)を追加する 作成したメニューに対し、
.addItem('表示名', '呼び出す関数名')で、クリック可能な項目を追加します 。この時、実際に実行させたい関数の名前を(プライベート関数の_を除かずに)文字列として指定します。 - UIに反映する 最後に
.addToUi()を呼び出すことで、作成したメニューをスプレッドシートのUIに正式に追加(反映)します 。
実践コード例(商品リストの操作)
では、実際に「商品リスト」を操作するカスタムメニューを作成してみましょう。 新しく sample5-4.gs などのスクリプトファイルを作成して、以下のコードを入力してください。
// サンプルデータ(グローバル変数)
// 著作権保護のため、PDFのデータとは異なるオリジナルのデータを使用します
const PRODUCT_DATA = [
['商品ID', '商品名', '価格', '在庫数'],
['P-001', '高性能ノートPC', 150000, 10],
['P-002', 'ワイヤレスイヤホン', 18000, 30],
['P-003', '24インチモニター', 25000, 15],
['P-004', 'メカニカルキーボード', 12000, 20]
];
/**
* スプレッドシートを開いた時に自動実行される関数
* カスタムメニューを作成します。
*/
function onOpen() {
// 1. UIオブジェクトを取得 [cite: 798]
let ui = SpreadsheetApp.getUi();
// 2. メニュー本体を作成 [cite: 801]
let menu = ui.createMenu('商品リスト操作');
// 3. メニュー項目を追加 [cite: 806]
menu.addItem('初期データを挿入', 'setupProductList_');
menu.addItem('価格(安い順)で並べ替え', 'sortProductsByPrice_');
// 4. UIに反映 [cite: 816]
menu.addToUi();
}
/**
* (メニュー用) 初期データをシートに書き込むプライベート関数
*/
function setupProductList_() {
let sheet = SpreadsheetApp.getActiveSheet();
// データをクリア
sheet.clear();
// データを挿入する範囲を取得
let rows = PRODUCT_DATA.length;
let cols = PRODUCT_DATA[0].length;
let range = sheet.getRange(1, 1, rows, cols);
// データを一括書き込み
range.setValues(PRODUCT_DATA);
// ヘッダー(1行目)を装飾
let headerRange = sheet.getRange(1, 1, 1, cols);
headerRange.setFontWeight('bold'); // 太字 [cite: 748]
headerRange.setHorizontalAlignment('center'); // 中央揃え
}
/**
* (メニュー用) 商品を価格(3列目)の昇順で並べ替えるプライベート関数
*/
function sortProductsByPrice_() {
let sheet = SpreadsheetApp.getActiveSheet();
// ヘッダー(1行目)を除いたデータ範囲を取得
// (getLastRow() はデータの最終行を取得)
let startRow = 2; // 2行目から
let lastRow = sheet.getLastRow();
let lastCol = sheet.getLastColumn();
// データがなければ終了
if (lastRow < startRow) {
return;
}
let range = sheet.getRange(startRow, 1, lastRow - 1, lastCol);
// 3列目(価格)を昇順 (ascending: true) で並べ替え
range.sort({column: 3, ascending: true});
}
実行と動作確認
このスクリプトは、GASエディタから onOpen を実行する必要はありません(実行してもメニューは追加されません)。
- 上記スクリプトを保存します。
- スプレッドシート(
lesson5)のタブに戻ります。 - ブラウザの再読み込みボタンを押すか、F5キーを押してページを再読み込みします 。
- 再読み込みが完了すると、メニューバーに「商品リスト操作」というメニューが追加されています。
動作確認:
- 「商品リスト操作」メニューをクリックし、「初期データを挿入」を選択します。 →
setupProductList_関数が呼び出され 、シートに商品データが書き込まれます。 - 次に、「商品リスト操作」→「価格(安い順)で並べ替え」を選択します。 →
sortProductsByPrice_関数が呼び出され 、シートのデータが価格の安い順に並べ替えられます。
このように、onOpen() を使うことで、GASの知識がない人にも、あなたが作った便利な機能を「メニュー」という形で提供できるようになります。
オブジェクトとメソッド
データと「処理」をまとめる、メソッドを持つオブジェクト
これまでの章で、GASでスプレッドシートやUI(ユーザーインターフェース)を操作する際に、SpreadsheetApp.getUi() や range.sort(...) のように、「オブジェクト」の後に「.(ドット)」を付け、() を伴う命令を実行してきました。この getUi() や sort() が「メソッド」と呼ばれるものです。
メソッドとは、そのオブジェクトに関連する「処理」や「動作」のことです。
これまではGoogleが用意したオブジェクトのメソッドを使ってきましたが、4日目(P.125付近)で学んだ「ユーザー定義オブジェクト」(プロパティだけを持つもの)に、私たち自身で「メソッド」を追加することも可能です。
データ(プロパティ)と、そのデータを操作するための処理(メソッド)を一つのオブジェクトにまとめることで、コードがより整理され、管理しやすくなります。
メソッドを持つオブジェクトの定義方法
メソッドを持つオブジェクトは、プロパティを定義するのと似た書式で作成できます。メソッドの実体は、実は「無名関数」です 。
書式:
let オブジェクト名 = {
// プロパティ
プロパティ名1: 値1,
プロパティ名2: 値2,
// メソッド
メソッド名1: function(引数1, 引数2, ...) {
// メソッドが実行する処理
return 戻り値; // 戻り値がある場合
},
メソッド名2: function() {
// 引数や戻り値がない場合の処理
}
};
メソッド名の後にコロン(:)を書き、function(引数) で処理内容を定義します 。
実践コード例(オリジナルの電卓オブジェクト)
person オブジェクトの代わりに、現在の計算値を保持し、それに加算・減算ができる「電卓」オブジェクトを作成してみましょう。 スクリプトファイル sample5-5.gs を作成して、以下のコードを入力・実行してください。
// sample5-5.gs
function func5_5_1() {
// 1. メソッドを持つ 'calculator' オブジェクトを生成
let calculator = {
// プロパティ (現在の計算値)
currentValue: 0,
// メソッド (値を加算する)
add: function(num) {
this.currentValue = this.currentValue + num;
console.log('加算 + ' + num + ' | 現在値: ' + this.currentValue);
},
// メソッド (値を減算する)
subtract: function(num) {
this.currentValue = this.currentValue - num;
console.log('減算 - ' + num + ' | 現在値: ' + this.currentValue);
},
// メソッド (現在の値を取得する)
getValue: function() {
return this.currentValue;
}
};
// 2. オブジェクトのメソッドを呼び出す
calculator.add(100);
calculator.subtract(30);
calculator.add(5);
// 3. 最終的な値を取得して表示
let finalResult = calculator.getValue();
console.log('最終結果: ' + finalResult);
}
実行結果:
加算 + 100 | 現在値: 100
減算 - 30 | 現在値: 70
加算 + 5 | 現在値: 75
最終結果: 75
処理の流れ
- オブジェクトの生成:
func5_5_1が実行されると、calculatorオブジェクトが生成されます。この時点で、プロパティcurrentValueには初期値0が設定されます 。 - メソッドの呼び出し:
calculator.add(100);でaddメソッドが呼び出されます。calculator.subtract(30);でsubtractメソッドが呼び出されます。calculator.add(5);でaddメソッドが再度呼び出されます。
- 結果の取得:
calculator.getValue();でgetValueメソッドが呼び出され、その時点でのcurrentValueの値(75)が戻り値として返され、finalResultに代入されます。 - ログ出力: 最終結果がコンソールに表示されます。
オブジェクト自身を指す「this」キーワード
calculator オブジェクトの add メソッド内に、this.currentValue という記述がありました。
add: function(num) {
// 'this' は 'calculator' オブジェクト自身を指す
this.currentValue = this.currentValue + num;
}
この this キーワードは、そのメソッドが属しているオブジェクト自身を指す、特別な「一人称」のキーワードです 。
- オブジェクトの「外側」からは
calculator.currentValueのようにオブジェクト名(主語)を指定してアクセスします 。 - オブジェクトの「内側」(メソッドの中)からは
this.currentValueのようにthisを使って、自分自身のプロパティや他のメソッドにアクセスします 。
add メソッド内の this.currentValue は、calculator.currentValue と同じものを指している、ということです。
メソッドの正体は「プロパティ」
ここで、重要な概念をお伝えします。 実は、add や subtract といったメソッドは、その実体は「関数オブジェクトが代入されたプロパティ」に過ぎません 。
// これは...
currentValue: 0,
// これと...
add: function(num) { ... }
上記は、「currentValue というプロパティに数値 0 を代入する」ことと、「add というプロパティに無名関数オブジェクトを代入する」ことが、同じ構文で並んでいるだけなのです。
this キーワードやメソッドの概念は、オブジェクト指向プログラミングの第一歩です。データと処理をセットで管理するこの考え方に慣れていきましょう。
まとめ
今回は、GASのコーディング効率を飛躍的に高める「ユーザー定義関数」について特集しました。
関数とは、一連の処理をひとまとめにし、名前を付けて再利用可能にする仕組みのことです 。基本的な function での関数の定義方法 、処理結果を返す return 、そして実行メニューを整理整頓できるプライベート関数(関数名の末尾に _ を付ける) について学びました。
また、スクリプトが意図通りに動かない「バグ」を見つけるための強力なツール「デバッガ」の使い方も解説しました 。ブレークポイントを設定し 、ステップ実行 することで、変数の値を確認しながら原因を特定できます。
さらに、変数がどこから参照できるかを定義する「スコープ」(グローバルとローカル) 、関数をデータのように扱う「関数オブジェクト」 、onOpen() を利用したスプレッドシートのカスタムメニュー作成 、そしてオブジェクトに独自の処理(メソッド)を持たせる方法 と this キーワード についても触れました。
これらをマスターすることで、コードはよりシンプルに、そして管理しやすくなります。














