トコロテンの日記

自分の活動内容や普段考えていることをアウトプットするためのブログです。ITに関わる話がメインであり、開発、競技プログラミング、気に入った技術などの話が多くなります。

Treasure2020 1日目

はじめに

こんにちは。トコロテンです。

ついこの間まで梅雨で脳みそ破壊されそうでした(偏頭痛)。 段々と暑くなってきて蝉の声も聞こえるようになると本当に「夏」が始まったんだなぁと実感します。

今年の夏は、VOYAGE GROUPさんのサマーインターンである「Treasure2020」と共に始まりました。 今回は、Treasure2020の初日に学んだことと感想をテキストに起こしたいと思います。

f:id:tokoroten_lab:20200806220543j:plain

↑これかっこよくないですか。僕はかっこいいと思います。

JavaScript

みなさん、JavaScript書けますか?僕は書けません。

😎「でも、書けるようになるから大丈夫。そう、Treasureならね。」

初日は、フロントエンドの講義ということでJavaScriptの歴史やTipsを教えてもらいました。 挙動不審な言語として悪名高いJavaScriptですが、本講義を通して理解が深まりました。

講義の主な内容としては、以下のとおりです。

  • プリミティブ型とオブジェクト型
  • イベントループ(event loop)
  • function式とアロー関数式
  • Promiseオブジェクトとasync/await構文

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

プリミティブ型

プリミティブ型に属するデータ型は、以下の6種類です。

  • 文字列
  • 数値
  • BigInt
  • 真偽値
  • undefined
  • symbol

プリミティブ型は、以下のような特徴を持ちます。

  • オブジェクトではない
  • メソッドを持ない
  • イミュータブル(変更不可)である

しかし、メソッドを持たないといった特徴がありながら以下のコードが動作します。

const hoge = "hoge";

console.log(hoge.toUpperCase());

hogeは、文字列なのでプリミティブ型になりますが、toUpperCase()メソッドを呼び出すことができます。 これは、各プリミティブ型に対応するラッパーオブジェクトといったものが存在し、自動的にオブジェクトに変換が行われているからだそうです(初めて知った)。 例えば、文字列の場合、以下のようにString(stringではないことに注意)に変換されて、メソッドの呼び出しが行われます。

const hoge = new String("hoge");

console.log(hoge.toUpperCase());

オブジェクト型

オブジェクト型は、任意のプロパティを持たせることができるデータ構造です。 分かりやすい例だと、以下のコードにあるobjのようなものです。

const obj = {
    hoge: "hogehoge",
    fuga: "fugafuga",
};

参照渡しと値渡し

プリミティブ型とオブジェクト型では、変数のコピーを行う際の挙動が異なります。 具体的には、以下のようになります。

  • プリミティブ型: 値渡し
  • オブジェクト型: 参照渡し
プリミティブ型(値渡し)
const hoge = "hoge";
let fuga = hoge;

fuga = "fuga";

console.log(hoge === fuga); // => false
console.log(hoge); // => "hoge"
console.log(fuga); // => "fuga"
オブジェクト型(参照渡し)
const hoge = { data: "hoge" };
let fuga = hoge;

fuga.data = "fuga";

console.log(hoge === fuga); // => true
console.log(hoge.data); // => "fuga"
console.log(fuga.data); // => "fuga"

上のコードから分かるように、オブジェクト型は、オブジェクトへの参照をコピーしているだけなので同一オブジェクトのプロパティを変更したりすることができます。 特に、関数にオブジェクト型の引数を渡すときには、内部でプロパティが変更されないかしっかりと考える必要があります。

イベントループ

JavaScriptは、DOMの更新や関数の実行等、様々なタスクを処理しますが、これらは全てシングルスレッドで行われているそうです。 つまり、並行処理はできても並列処理はできないということになります。 したがって、重い処理が走ると全体の処理をブロックしてしまいます。

例えば、以下のコードで実際にcounterDOMの値が更新されるのは、timesの値が1000になってからです。

const button = document.getElementById('heavyCount');
const counter = document.getElementById('counter');
button.addEventListener('click', function() {
    let count = 0;
    let times = 1000;
    function loop() {
        if (count++ < times) {
            counter.innerHTML = count;
            loop();
        } else {
            alert("Done");
        }
    }
    loop();
});

このようになる理由は、loop()関数が実行されるとシングルスレッドであるJavaScriptは、loop()関数が終了するまでDOMの更新タスクを処理することができないためです。

これを回避するためには、以下のようにsetTimeout等の関数を利用して非同期処理を行います。 タスクの一部が終わるごとにスレッドを明け渡し、次のタスクをキューに積むといったことをしてDOMの更新タスクが割り込めるようにします。

const button = document.getElementById('heavyCount');
const counter = document.getElementById('counter');
button.addEventListener('click', function() {
    let count = 0;
    let times = 1000;
    function loop() {
        if (count++ < times) {
            counter.innerHTML = count;
            setTimeout(loop);
        } else {
            alert("Done");
        }
    }
    loop();
});

function式とアロー関数式

JavaScriptの関数の定義方法は、以下のようにfunction式を使う方法とアロー関数式を使う方法の2種類があります。 これら2つの方法は、若干異なる点が存在します。 その中でも、特に重要なthisの束縛のタイミングの違いについて学びました。

  • function式: 実行時にthisを束縛
  • アロー関数式: 定義したときにthisを束縛

以下のコードを見ると関数の定義方法におけるthisの束縛タイミングの違いが分かりやすいと思います。

function regular() {
    return this;
}
console.log(regular() === window); // true

const arrow = () => {
    return this;
}
console.log(arrow() === window); // true

const obj = {
    regular: regular,
    arrow: arrow
}
console.log(obj.regular() === obj); // true
console.log(obj.arrow() === obj); // false

Promiseオブジェクトとasync/await構文

以下のように、Promiseオブジェクトとthen(), catch()を利用したコールバック処理からasync/await構文を利用してコールバックのネストを減らすことができます。 個人的な感想として、今あえてPromiseオブジェクトを使う理由というのは殆ど無いのではないかと思います。

const sleep = (msec) => {
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(msec), msec);
    })
}

// Promise
const f = (msec) => {
    sleep(msec).then((res) => {
        console.log("f: " + res);
    });
}

f(1000);

// async/await
const g = async (msec) => {
    const res = await sleep(msec);
    console.log("g: " + res);
}

g(1000);

開発パート

講義の後半でSkyWayといったサービスを紹介してもらい、実際にSkyWayのSDKを利用してビデオチャットのWebアプリを開発しました。 はじめは、機能としてビデオ通話しか無いものでしたが、講義が終わった後も開発がとても楽しかったため、テキストチャットの機能や画像の送信機能を付けてみました。 開発するにあたって、React+TypeScript+SkyWay SDKを利用しました。

以下の画像が、作成したビデオチャットスクリーンショットです。

f:id:tokoroten_lab:20200806183206j:plain

さいごに

まだ1日しかインターンに参加していませんが本当に勉強になって幸せです。 プログラムに関しては、これまで殆ど独学だったため、人に教えてもらうということが新鮮でした。 今回の講義でレベルの高い方に教えてもらえるのはめちゃくちゃ効率的な勉強に繋がると実感しました。