関数の基本
関数とは?
- 関数の基本形
function hello(name:string) {
console.log("Hello, " + name + "!")
}
hello("Taro") // "Hello, Taro!"
hello("Hanako") // "Hello, Hanako!"
関数内の変数スコープ
- var
- 関数の外で宣言した場合、そのソースコード全体で利用できる。関数内で宣言したものは、関数内のみ。
- let
- その変数を宣言した構文内(
{}
)のみ。
- その変数を宣言した構文内(
戻り値について
function 関数名(引数):型 { 関数の処理 }
- 戻り値がない場合は、
void
を指定する
function total(max:number):number {
let num = 0
for (let i = 0; i <= max; i++) {
num += i
}
return num
}
function printTotal(n:number):void {
console.log(n + "までの合計:" + total(n))
}
printTotal(10) // "10までの合計:55"
printTotal(123) // "123までの合計:7626"
複数の値を返す
- タプルで返す
- 定義
function 関数名(引数):[ タプル ] { 関数の処理 }
- 呼び出し
let [ 変数1, 変数2, .. ] = function 関数名(引数)
function calcTax(totalPrice:number):[price:number, tax:number] {
const price = totalPrice / 1.1
const tax = totalPrice - price
return [price, tax]
}
function printCalcTax(totalPrice:number):void {
const [price, tax] = calcTax(totalPrice)
console.log(totalPrice + "の本体価格:" + price + ", 税額:" + tax)
}
printCalcTax(3980) // "3980の本体価格:3618.181818181818, 税額:361.818181818182"
printCalcTax(9999) // "9999の本体価格:9090, 税額:909"
引数に条件型を使う
function 関数名(引数1:型1 | 型2, 引数2:型3 | 型4)
function menu(a: "yama" | "umi") {
console.log(a)
}
menu("yama")
menu("kawa") // エラー
オプション引数
function 関数名(引数1, 引数2?, 引数3?, ..)
function 関数名(引数1 = 初期値1, 引数2 = 初期値2 ..)
function showWindow(width:number = 100, height:number = 50) {
console.log(width, height)
}
showWindow(80) // 80, 50
showWindow() // 100, 50
可変長引数
function 関数名(...名前:型[])
function printTotal(...n:number[]) {
let total = 0
for (let i = 0; i < n.length; i++) {
total += n[i]
}
console.log(total)
}
printTotal(2,3,5,7) // 17
関数をさらに掘り下げる
無名関数について
const 定数 = function (引数):戻り値 {処理}
アロー関数
const 定数 = (引数):戻り値 => 処理
- 関数はfunction型の値
- functionとアロー関数の違いは、アロー関数は変数の代入前(アロー関数定義の前)には使用できないこと。
const f = (name:string):void => {
console.log("Hello, " + name + "!")
}
f("taro") // "Hello, taro!"
f("ichiro") // "Hello, ichiro!"
console.log(typeof(f)) // "function"
内部関数について
- 関数内で関数を定義して使う
const f = (n1:number, n2:number):void => {
const inF = (n:number):void => {
console.log("answer:" + n)
}
const sum = n1 + n2
inF(sum) // 内部関数を使用する
}
f(1 ,2) // "answer:3"
引数に関数を使う
- 引数を指定しない
const 定数 = (引数:Function):戻り値の型 => 処理
- 引数を指定する
const 定数 = (引数:(引数:型)=>戻り値の型):戻り値の型 => 処理
- 下の例だと、
(...x:number[])=>number
がf
の型
const calc = (f:(...x:number[])=>number, ...n:number[]):void => {
const result = f(...n)
console.log("answer:" + result)
}
const sum = (...s:number[]) => {
let r = 0
s.forEach(l => r += l)
return r
}
const avg = (...s:number[]) => {
let r = 0
s.forEach(l => r += l)
return r / s.length
}
calc(sum, 1, 2, 3) // "answer:6"
calc(avg, 1, 2, 3) // "answer:2"
戻り値に関数を使う
- f1、f2は関数内に0.1、0.08を保持している
-
(n:number)=>number
が戻り値の型
const f = (tax:number):(n:number)=>number => {
return (n:number) => n * (1 + tax)
}
// 10%を課税する関数を取得
const f1 = f(0.1)
// 8%を課税する関数を取得
const f2 = f(0.08)
console.log(f1(12345)) // 13579.500000000002
console.log(f2(12345)) // 13332.6
クロージャについて
- 現在値
count
と増分n
を関数内に保持している - 定義された環境を保ち、その中で動く関数のことを クロージャ という
- 日本語では 関数閉包 という
const f = (n:number):()=>number => {
let count:number = 0
return ():number=> {
count += n
return count
}
}
// 現在値を保持して、1ずつ増える関数を取得
const f1 = f(1)
// 現在値を保持して、2ずつ増える関数を取得
const f2 = f(2)
// 現在値を保持して、3ずつ増える関数を取得
const f3 = f(3)
for (let i = 0; i < 10; i++) {
console.log(f1(), f2(), f3())
}
関数の高度な機能
エラーと例外
- try構文
try {
// エラーが発生するかもしれない処理
} catch(e) {
// エラー発生後の処理
} finally {
// 常に最後に実行する処理
}
- 例外を発生させる
throw Error("エラーです")
- try構文の例
const f = (arr?:any[]):void => {
try {
if (typeof(arr) === "undefined") {
throw Error("arrは空です")
}
for(let i of arr) {
console.log(i)
}
} catch(e) {
console.log(e)
} finally {
console.log("finally!")
}
}
f(["OK", "NG"]) // "ok" "ng" "finally!"
f() // "arrは空です" "finally!"
総称型(ジェネリクス)について
function 関数名<T>( 引数 ) : 戻り値
- Tでなくてもいい
- 複数の総称型を使うには、
<T,U,V>
のように書く
function getRnd<T>(values: T[]): T {
const r = Math.floor(Math.random() * values.length)
return values[r]
}
const data1 = [0, 2, 4, 6, 8, 10]
const data2 = ["グー", "チョキ", "パー"]
const data3 = [true, false]
for(let i = 0; i< 10; i++) {
const ret1 = getRnd(data1)
const ret2 = getRnd(data2)
const ret3 = getRnd(data3)
console.log(`${ret1}(${typeof ret1}) ${ret2}(${typeof ret2}) ${ret3}(${typeof ret3})`)
}
// "6(number) グー(string) true(boolean)"
// "8(number) グー(string) true(boolean)"
// "0(number) パー(string) false(boolean)"
// "4(number) グー(string) true(boolean)"
// "8(number) チョキ(string) true(boolean)"
// "0(number) チョキ(string) true(boolean)"
// "8(number) チョキ(string) false(boolean)"
// "6(number) パー(string) true(boolean)"
// "2(number) グー(string) false(boolean)"
// "0(number) パー(string) false(boolean)"
ジェネレーターと遅延評価
- ジェネレータの作り方
function* 名前( 引数 )
// ...処理...
yield 値
- ジェネレータは処理を実行し、
yield
があることろで待ち状態になる。.next()
で取り出されると次のyield
まで進んで待ち状態になる。 - 使い方
-
ジェネレータ = 関数()
でジェネレータを取得する -
ジェネレータ.next()
で値を取り出す
-
function* gen(n: number) {
for (let i = 0; i < n; i++) {
yield i
}
}
const g = gen(5)
console.log(g.next()) // 0
console.log(g.next()) // 1
console.log(g.next()) // 2
console.log(g.next()) // 3
console.log(g.next()) // 4
console.log(g.next()) // undefined
非同期処理とPromise
// dミリ秒待ってから、1~nの合計の数を求める
const promiseFunc = (n: number, d: number): Promise<number> => {
console.log(`start: ${n}`)
return new Promise((resultFunc) => {
let total = 0
for (let i = 1; i <= n; i++) {
total += i
}
setTimeout(() => {
// thenの中の関数の関数を呼び出す
resultFunc(total)
}, d)
})
}
// 結果を受け取る関数 Promise<number>なのでnumberの引数を取る
const cb = (total: number) => {
console.log(`result: ${total}`)
}
// 300ミリ秒待って、1~10の合計を表示する(最後に表示)
promiseFunc(10, 300).then(cb)
promiseFunc(100, 200).then(cb)
promiseFunc(1000, 100).then(cb)
// 最初に表示される(最初に表示)
console.log("do something...")
コンソールプログラムを作ろう
コンソールプログラムの仕組み
- ターミナルのコマンドから渡される引数の情報は、
process.argv
から取得でき、テキストの配列になっている
process.argv[0] = nodeコマンドのパス
process.argv[1] = スクリプトのファイルのパス
process.argv[2]以降 = コマンド実行時に渡される引数
数字を素因数分解する
console.log("Node path = " + process.argv[0]);
console.log("script file path = " + process.argv[1]);
// 引数で渡された文字列を、数値の配列にする
const data: number[] = [];
for (let i = 2; i < process.argv.length; i++) {
data.push(Number(process.argv[i]));
}
console.log(data);
// 画面から入力された数値を順に素因数分解する
for (let item of data) {
// 素因数分解する
const res = primeFactor(item);
// 結果を画面表示する
console.log(`${item} = ${res}`);
}
// 素因数分解する関数
function primeFactor(a: number): number[] {
const v: number[] = [];
let x = a;
let n = 2;
while (x > n) {
// 2, 3, 5, と順に割れるか判定して割れたら、v[]に入れ、新たなxを代入する
if (x % n == 0) {
x = x / n;
v.push(n);
} else {
// 割れない場合、2のとき1、3以上の時2を加算する
n += n == 2 ? 1 : 2;
}
}
// 現在の割る数(n)<=割られた数(x)になった場合、もう割れないので割られた数(x)をv[]に入れる
v.push(x);
return v;
}
// [ 10, 100 ]
// 10 = 2,5
// 100 = 2,2,5,5