このブログのコンセプトって○○なん、知ってた? 何それ気になる〜

【コーディング週間日報】7/11(日)〜 7/17(土)の学習

たーやま

7/11(日)〜 7/17(土)に行ったことを記載しました。

目次

7/11(日)のアウトプット

ブラウザとJavaScript

スレッド:処理される一連の流れのこと。JavaScript で一番関わるのは、Main Thread です。

Main Thread

 JavaScript の実行とレンダリングを行います。

JavaScript への重い処理や高負荷なレンダリング処理があった場合、画面の更新がされなくなる恐れがあります。

たーやま

処理速度は 60fps が目安です。

セリフに出てきた「fps」は、フレームレートのことで、1秒間でどれくらい画面更新をしているかという単位です。

より詳しい解説は、こちらにございます。

同期処理・非同期処理

同期処理では、1つの処理が終わったら次の処理を始めます。そして、メインスレッドでコードが処理されていきます。

タスクキューとコールスタック

実行待ちの非同期処理の列のことをタスクキューと言います。

非同期処理の実行順を管理しており、「先入れ先出し」の仕組みがあります。

たーやま

コンビニみたいやな(笑)

タスクキューはコールスタックと連携して、非同期処理の実行順を決定しています。

7/13(火)のアウトプット

json ファイル

JavaScript からサーバー上にデータを取りに行くときや、サーバーに対してデータをリクエストする時に使うファイルです。

基本的に JavaScript の配列やオブジェクトの書き方でいいですが、異なる点もあります。

・文字列はシングルクォーテーションで囲めない
▶︎ダブルクォーテーションを使います
・プロパティはダブルクォーテーションで囲む
・格納されている要素の最後にカンマはつけられない

fetch

サーバー上からデータを取得することができます。

たーやま

fetch て PHP + MySQL でやったよな…。

それでは fetch がどのような戻り値を返すのかをみていきましょう。

console.log(fetch('users.json')); // Promise {<pending>}

Promise が返ってきたので、then が使えます。then のコールバック関数に返ってくる値もみてみましょう。

fetch('users.json').then(function (response) {
    console.log(response);
});

response にはサーバーから返ってきたデータが格納されています。 よくみると status: 200 と ok: true という値が見えますよね。

status は http status を表し、200なら問題ありません。

また ok は true だと、サーバーからデータが取得できています。

さらに分解してみると、プロトタイプの中にもデータが入っていました。これらの値は、取得したデータによって変わります。

では json のデータを取得してみましょう。

fetch('users.json').then(function (response) {
    return response.json();
}).then(function (json) {
    console.log(json);
});

実行すると、json で書いた値が取得できます。

[
  {
    "name": "Bob",
    "age": 23
  },
  {
    "name": "Tim",
    "age": 30
  },
  {
    "name": "Sun",
    "age": 25
  }
]
たーやま

このまんまが実行結果ではありません。配列とオブジェクトリテラル内の値が取れました。

7/14(水)のアウトプット

ES modules と Common JS

これらを説明する前に、モジュールについて…。

モジュールというのは、機能ごとに分割して管理しやすくする仕組みのことです。

JavaScript における代表的なモジュールとして、ESM と CJS というものがあります。ESM は ES module で、CJS は Common JS の略称です。

Common JS

Node.js でモジュール管理する仕組みを言います。読み込みや外部ファイルに出したいときは、require と exports を使います。

ES module

ECMA Script に基づいてモジュール管理をします。読み込みや露出をしたい時は、import とexport を使います。

モジュール管理は、ブラウザ側で行っています。

import / export

モジュールの読み込みと露出を行ってみます。

<script type="module" src="moduleB.js"></script>

まず、HTML 上で ES module を読み込みたい場合は、type 属性を module にする必要があります

たーやま

src だけじゃあかんのやな。

// moduleA.js

export let moduleA = 0;
export function a() {
    console.log("a called");
}

// moduleB.js

import { moduleA, a } from './moduleA.js';
console.log(moduleA); // 0
a(); // a called

moduleA.js で変数や関数に export をし、moduleB.js で読み込みました。

import { moduleA as changeA, a as funcA } from './moduleA.js';
console.log(changeA); // 0
funcA(); // a called

また、名前を変えたい時は、as を使うと変更できます。

7/15(木)のアウトプット

JavaScript と、実務で若干考えたところがあったのでまとめます。

モジュールの特徴

同期処理の JavaScript と非同期処理のモジュールを比較してみます。

<!-- HTML -->

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <h1>JavaScript</h1>
    <script src="main.js"></script>
    <script type="module" src="moduleB.js"></script>
</body>

</html>

// JavaScript(main.js)

let elem = document.querySelector('h1');
let txtelem = elem.textContent;

console.log(txtelem); // JavaScript

h1 タグの要素を JavaScript 上で取得し、console で出力させる処理をしました。

ここで、HTML にある main.js を、h1 タグの上にもっていくとどうなるでしょう。

たーやま

エラー発生するでな。

JavaScript は HTML が読み込まれた後に処理されるためにエラーが起こります。

しかしそれは、同期処理だった場合の話です。

非同期処理であればエラーは起こりません。試しに、main.js に defer をつけましょう。

<!-- HTML -->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script src="main.js" defer></script>
    <h1>JavaScript</h1>
    <script type="module" src="moduleB.js"></script>
</body>

</html>

main.js の中身は同じなので割愛しました。これだときちんと表示されます。

defer には非同期処理にさせる働きがあります。

非同期処理というのは、HTML の処理が終わった後に処理されるようになっているため、順番を入れ替えても出力されます。

そして、type=”module” も非同期処理です。なので、moduleB.js を同じようにしても、表示されます。

たーやま

ちなみに、type=”module” はすでに defer が使われてるから書く必要はないで。

input タグのカーソルが…。

業務中、上司からログインページからのログインができないという話がありました。

確認してみると、input タグの1箇所だけカーソルが出てこない状態に…。

ここで、どんな感じだったのかをコードで再現しました。

<body class="form">
    <div class="form-wrap">
        <h2 class="form-title">inputタグに入力できな〜い!</h2>
        <form action="#" method="post" class="form-contents">
            <p class="form-items">
                <label for="username">ユーザーネーム</label>
                <input type="text" name="username" id="username" autocomplete="username">
            </p>
            <p class="form-items">
                <label for="email">メールアドレス</label>
                <input type="email" name="email" id="email" autocomplete="username">
            </p>
            <div class="form-submit">
                <button type="submit" name="login" value="ログイン" class="form-submit-btn">ログイン</button>
            </div>
        </form>
    </div>
</body>
.form {
    &-wrap {
        position: relative;
        max-width: 50%;
        width: 100%;
        margin: 20% auto 0 auto;
        border: 2px solid #e6e6e6;

        &::before {
            position: absolute;
            width: calc(100% + 1rem);
            height: calc(100% + 1rem);
            top: -9px;
            left: -9px;
            border: 1px solid #3b3b3b;
            content: "";
        }
    }

    &-title {
        text-align: center;
    }

    &-items {
        display: flex;
        align-items: center;
        justify-content: space-evenly;
        margin-top: 2em;

        & label {
            width: 30%;
            text-align: center;
        }

        & input {
            width: 48%;
            min-height: 24px;
            border: 1px solid #808080;
            border-radius: 4px;
            outline: none;
        }
    }

    &-submit {
        max-width: 300px;
        width: 100%;
        margin: 2em auto;

        & button {
            width: 100%;
            padding: 5px 0;
        }
    }
}

実行結果は画像のようになり、一見すると問題がないように思います。しかし、インプットタグをクリックしても入力できません。

たーやま

input タグは width 変えたくらいやぞ…?

と思いつつ、しばらく原因を探っていました。

たーやま

そういや前に、擬似要素で flexbox がイカれたことあるよな。

擬似要素が必要な箇所があったので、付け足したことを思い出しました。その z-index を一番下にすることで解決しました。

おそらく、input タグが擬似要素よりも下の階層にあったために反応しなかったのでしょう。

たーやま

でもなんで1箇所だけなんやろ…?
他のinput タグはちゃんとカーソルあってんけどな。

現状は擬似要素の z-index を直したら修正できたということがわかっています。もし確実な原因追求ができたら、またお知らせします。

たーやま

といつつ、作ったコードやとどっちもカーソルがなかったので、やっぱり z-index が絡んでるみたいです。

とりあえず言えることは…

擬似要素は気ィつけや

ということです。

7/16(金)のアウトプット

Proxy

プロパティの処理に独自の処理を追加するためのオブジェクトのことです。

const targetObj = { a: 0 };
const handler = {
    set: function (target, prop, value, receiver) {
        console.log(`[set]: ${prop}`)
        target[prop] = value;
    } 
}
const pxy = new Proxy(targetObj, handler)
pxy.a = 1;

// 実行結果 [set]: a
// targetObj の中身は { a: 1 }

第一引数に格納したいオブジェクト、第二引数に handler というオブジェクトを渡します。

handler は、第一引数のオブジェクトを操作した時に実行されるメソッドを格納しているオブジェクトのことです。

handler では様々なメソッドを使うことができます。まずは set を使いました。

たーやま

メソッドのことをトラップともいいます。

 set にある関数は、target, prop, value, receiver の4つの引数が渡ります。それぞれの役割はこんな感じ。

・target:Proxy で渡した第一引数が渡ります。
・prop:アクセスされたプロパティの名前が渡ります。
・value:新しい値が渡ってきます。
・reciever:インスタンスで作成したオブジェクトが渡ります。

たーやま

ちなみに、target[prop] = value; を書かなかったら targetObj の中身は { a: 0 } です。

get トラップもみてみましょう。get は、値の取得が行われた時の検知ができます。

const targetObj = { a: 0 };
const handler = {
    set: function (target, prop, value, receiver) {
        console.log(`[set]: ${prop}`)
        target[prop] = value;
    },
    get: function (target, prop, receiver) {
        console.log(`[get]: ${prop}`)
        return target[prop];
    } 
}
const pxy = new Proxy(targetObj, handler)
pxy.a = 1;
pxy.a;

// 実行結果 [set]: a, [get]: a
// targetObj の中身は { a: 1 }

get の場合はあたらしい値は渡りませんので、value は必要ありません。

7/17(土)のアウトプット

Reflect

JavaScript には、内部でのみ使用されるメソッドを保持しています。例えば、[[Get]], [[Set]], [[Delete]] などがあります。

その内部メソッドを間接的に呼び出すオブジェクトが reflect です。他にも Reflect は、Proxy と合わせて使用します。

では、内部メソッドを呼び出してみましょう。

class C {
  constructor(a, b) {
    this.a = a;
    this.b = b;
  }
}

let objectOne = new C(1, 2);
console.log(objectOne); // C {a: 1, b: 2}

let objectTwo = Reflect.construct(C, [1, 2]);
console.log(objectTwo); // C {a: 1, b: 2}

console.log('c' in objectOne); // false
console.log(Reflect.has(objectOne, 'c')); // false
たーやま

せやせや。クラスがあったら new 演算子でインスタンス化するんやったな。

new 演算子は、内部的に Reflect.construct と同様の機能を呼んでいます。なので、Reflect.construct でもインスタンス化はできます。

Reflect.construct は第一引数にコンストラクタ関数を、第二引数に配列を渡すと使用できます。

演算子などをつかって表記していたところを関数表記にできる。これが Reflect の1つの特徴です。

console.log('c' in objectOne); // false
console.log(Reflect.has(objectOne, 'c')); // false

次にコチラの部分について…。

in の演算子は内部で Reflect.has と同じメソッドを呼んでいます。なので、Reflect.has で書き直せます。

たーやま

演算子の内部メソッドと同じ内部メソッドを呼んでるから Reflect を使えるってことか。

2つ目の特徴として、Object に格納されている静的メソッドを移植することもできます。

Object に格納されている静的メソッドは、便宜的に Object に格納していました。

しかし、今後静的メソッドが追加された際は Reflect の方が追加される方針で動いています。

なので、ES6 では Object よりも Reflect の方を推奨。また Reflect に移植することによるメリットもあります。

Object.defineProperty の場合はエラーが発生した時の記述が面倒です。

try {
  Object.defineProperty
} catch (e) {
  // 処理を記述
}

しかし、Reflect.defineProperty だとエラー発生時に false が返ってきます。

Reflect.defineProperty を使って Object のプロパティを変更したときは、戻り値が true だと if 文の中身が実行されます。

false の場合は、else の中身が実行されます。

class C {
  constructor(a, b) {
    this.a = a;
    this.b = b;
  }
}

if (Reflect.defineProperty) {
  console.log('trueの値やで');
} else {
  console.log('falseの値やで');
}

// 実行結果:trueの値やで

この場合だと、true の値が返ってきました。

たーやま

長すぎるから一旦区切りますね(笑)

Reflect と Proxy

JavaScript の内部メソッドと Reflect は対になった存在です。しかし、Proxy とも対になっています。

const targetObj = { a: 0 };

const handler = {
  get: function(target, prop, receiver) {
    console.log(`[get]: ${prop}`);
    if(target.hasOwnProperty(prop)) {
      return target[prop];
    } else {
      return -1;
    }
  }
}
const pxy = new Proxy(targetObj, handler);
console.log(pxy.b); // [get]: b, -1

targetObj に対して handler を設定し、get のトラップを設置。

get 内には自分のオブジェクト内に含まれないプロパティに対しては -1 を返すようにしました。

今回は b がプロパティ内に含まれていないので、-1 が返ってきました。

const targetObj = { a: 0 };

const handler = {
  get: function(target, prop, receiver) {
    console.log(`[get]: ${prop}`);
    if(target.hasOwnProperty(prop)) {
      return Reflect.get(target, prop);
    } else {
      return -1;
    }
  }
}
const pxy = new Proxy(targetObj, handler);
console.log(pxy.a); // [get]: a, 0

Reflect で書き換えた場合は、第一引数に target, 第二引数に prop を渡します。無事に値の取得ができました。

では、targetObj を getter トラップ経由で取得してみましょう。

const targetObj = {
  a: 0,
  get value() {
    return this.a;
  }
};

const handler = {
  get: function(target, prop, receiver) {
    console.log(`[get]: ${prop}`);
    if(target.hasOwnProperty(prop)) {
      return Reflect.get(target, prop);
    } else {
      return -1;
    }
  }
}
const pxy = new Proxy(targetObj, handler);
console.log(pxy.value); // [get]: value, 0

value の getter を呼び、a の値を返却するようにしました。value が返却する値が、this.a を参照するので、0 が呼び出されます。

ここで targetObj の getter 内の this を b にしてみましょう。

const targetObj = {
  a: 0,
  get value() {
    return this.b;
  }
};

const handler = {
  get: function(target, prop, receiver) {
    console.log(`[get]: ${prop}`);
    if(target.hasOwnProperty(prop)) {
      return Reflect.get(target, prop);
    } else {
      return -1;
    }
  }
}
const pxy = new Proxy(targetObj, handler);
console.log(pxy.value); // [get]: value, undefined

自分が持っていないプロパティに対しては -1 が出力されるようになっていますが、undefined が出力されました。

getter 経由で呼び出された this は、targetObj に対して b を呼んでいるのと同じになります。つまり、

console.log(targetObj.b);

を呼び出しているのと同じです。

WeakMap

弱い参照でオブジェクトを保持するコレクション。キーは絶対にオブジェクトを設定する必要があります。

let weak = new WeakMap();

let s = {};
weak.set(s, 'valueOne');

s = null;
console.log(weak.get(s)); // valueOne

set でWeakMap の値をセットし、get メソッドで値を出力。WeakMap によって、オブジェクトと値を対になって管理できるようになりました。

WeakMap では、キーで使用しているオブジェクトが削除されると、オブジェクトと値も削除されます。

let weak = new WeakMap();

let s = {};
weak.set(s, 'valueOne');

s = null;
s = {};
console.log(weak.get(s)); // undefined

let で定義した変数 s が null によって削除されたので、undefined が出力されました。

新しくオブジェクトを定義しても、格納されているメモリーが違います。そして、異なる値を入れてしまうと、二度と呼び出すこともできません。

たーやま

削除することを『ガベージコレクション』っていうんやと。

キーから value への参照の保持の仕方が弱いため、WeakMap は弱参照を用いたコレクションになります。

この記事が気に入ったら
いいね または フォローしてね!

よかったらシェアしてね!

この記事を書いた人

大阪出身26歳。宮古島の制作会社に勤めています。
コーディングとデザインにシバかれてます。

生き物や自然が好きやけど、特にクワガタムシが好き。
他、チャリとか釣りとか筋トレとかも好き。

目次
閉じる