' P '

whatever I will forget

Certified Javascript Developer Objects, Functions, and Classes Section

Objects, Functions, and Classes

Decorator → 試験にはでない

  • デコレータとは、既存の関数をラップし、その動作を拡張するラッパー関数のこと.
    ascii.jp

  • クラスメソッドの装飾に使用するデコレーター関数には、3つのパラメータが渡されます.

  • 最初のパラメータはtargetです.
    • targetは、装飾が必要なクラスのオブジェクトを指します
  • 第2パラメータは、装飾が必要なプロパティのnameです.
    • クラスメソッドデコレーターとして使用する場合は、装飾されるメソッドの名前になります.
  • 第3パラメータはdescriptorで、これは装飾されたメソッドにアクセスするためのオブジェクトであり、そのvalueプロパティを通じてアクセスすることができます.
// decorator function definition
function log(target, name, descriptor) {
  const original = descriptor.value;
  if (typeof original === 'function') {
    descriptor.value = function(...args) {
      console.log(`Arguments: ${args}`);
      try {
        const result = original.apply(this, args);
        console.log(`Result: ${result}`);
        return result;
      } catch (e) {
        console.log(`Error: ${e}`);
        throw e;
      }
    }
  }
  return descriptor;
}

class Example {
    // the use of decorator
    @log
    sum(a, b) {
        return a + b;
    }
}

const e = new Example();
e.sum(1, 2);
// Arguments: 1,2
// Result: 3

Classの装飾

  • クラスを装飾するために使用する場合、デコレータ関数に渡されるパラメータは1つだけ.
    • このパラメータは、装飾されるクラスであるtargetオブジェクト.
function addUserType(target) {
    target.prototype.userType = 'INTERNAL';
}

//Below is the User Class.
@addUserType
class User {
  constructor(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
  }
}

デコレータ関数を使用する

  • ラッパー関数には関数参照のみが渡されるので、引数は指定しない.
    • cachedStudentRecordRetrievalDecorator(retrieveStudentRecord()) としない.
let map = new Map();

async function retrieveStudentRecord(id) {
  const data = await fetch(`https://acb-schools.edu/api/?student=${id}`);
  return (await data.json());
}
function cachedStudentRecordRetrievalDecorator(fn) {
  return async function (id) {
    const cachedRecord = map.get(id);
    if (cachedRecord)
      return cachedRecord;

    const record = await fn(id);
    map.set(id, record);
    return record;
  }
}

// Decoratorの宣言
const findStudentRecord = cachedStudentRecordRetrievalDecorator(retrieveStudentRecord);
findStudentRecord(10).then(record => console.log(record));

readonly関数の実装

  • プロパティの読み取り専用動作は、書き込み可能なプロパティ記述子にのみ依存する
  • descriptorwritablefalseに設定する
function readonly(target, name, descriptor) {
    descriptor.writable = false;
    return descriptor;
}

例外処理を含むデコレータ

  • クラスとクラスプロパティには、複数のデコレータを使用することができます。
  • デコレータの評価は上から下だが、関数は下から上に処理される.
  • 例外処理を行いたい場合は、例外処理メソッドを先に呼び出してから通常処理を行う.
class Cart {
   @cacheData
   @handleException
  getCart() {
     //Method implementation
   }
}

アロー関数

  • アロー関数は特殊で、「自分だけの」thisを持ちません.
  • アロー関数からthisを参照する場合、外側の「通常の」関数から取得されます.
  • 例えば、ここでarrow()は、外側のuser.sayHi()メソッドからthisを使用しています:
let user = {
  firstName: "Ilya",
  sayHi() {
    let arrow = () => alert(this.firstName);
    arrow();
  }
};

user.sayHi(); // Ilya

javascript.info


メソッド

Object

Object.assign()

  • すべての列挙可能な自身のプロパティの値を、 1 つ以上のコピー元オブジェクトからコピー先オブジェクトにコピーするために使用.
同じプロパティを持つオブジェクトのマージ
  • 下記の構文になるため注意.
  • 違うプロパティ同士のオブジェクトの場合は {}は不要.
const o1 = { a: 1, b: 1, c: 1 };
const o2 = { b: 2, c: 2 };
const o3 = { c: 3 };

const obj = Object.assign({}, o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }

developer.mozilla.org

Object.entries()

  • Object.entries() メソッドは、引数に与えたオブジェクトが所有する、文字列をキーとした列挙可能なプロパティの組 [key, value] からなる配列を返します。
const object1 = {
  a: 'somestring',
  b: 42
};

for (const [key, value] of Object.entries(object1)) {
  console.log(`${key}: ${value}`);
}

developer.mozilla.org

Object.freeze()

Object.freeze() メソッドは、オブジェクトを変更できない状態にします. - オブジェクトに新しいプロパティを追加したり、既存のプロパティを修正・削除したり、オブジェクトのプロパティの属性を変更したり、オブジェクトのプロトタイプを変更したりすることができなくなります.

developer.mozilla.org

Object.seal()

  • メソッドは、新しいプロパティの追加を防ぎます.
  • 既存のプロパティは設定可能である限り、変更することができます.

Object.preventExtensions()

  • メソッドは、オブジェクトに新しいプロパティが追加されるのを防ぐだけです.

Object.create()

  • メソッドは、オブジェクトを作成し、プロトタイプオブジェクトを割り当てる別の方法です.
  • プロトタイプを使用しない場合は、パラメータとして null を渡すことができる.
  • オブジェクトが作成されると、ドットまたはブラケット記法を使って、そのオブジェクトにプロパティを割り当てることができます.
  • 次に、代入演算子(=)を使用して値を代入することができる.
let room1 = Object.create(null);
room1.name = 'Board Room';
room1.capacity = 20;
room1.requireSchedule = true;

Function

Function.prototype.call()

Function.prototype.apply()

  • callapply メソッドは、間接的に関数を呼び出します.
  • これらのメソッドは、最初のパラメータとしてオブジェクトを渡すことで、キーワード this が参照するオブジェクトを確定するために使用することができます.
  • 引数は this キーワードの値に続いて渡されます.
  • call メソッドは、カンマで区切られたリストで引数を受け取ります.
  • apply メソッドは、引数を配列で受け取ります.

Function.prototype.bind()

  • bind メソッドは新しい関数を返す.
  • 返された関数は、this キーワードにbindされた値を持つ.
  • その値は最初のパラメータに由来する.
  • bind メソッドは、this キーワードの値に続く任意のパラメータを使用して新しい関数に引数を結合することもできる.
  • 渡された引数は、バインドされた関数に渡された引数の前に追加される.
  • bindは関数のコピーを返しますが、コンテキストは束縛されています.
  • すぐには実行されないので、下記のように実行を記述しないと実行されない.
const sayHiBind = sayHi.bind(person, 21);
sayHiBind();

または

sayHi.bind(person, 21)();

Others

EventTarget: addEventListener()

  • 引数にoptionsオブジェクトを addEventListenerメソッドで指定すると、イベントリスナーに関する特定のオプションを指定することができる.
  • optionsオブジェクトには4つのオプションが用意されています.
  • optionsオブジェクトの onceプロパティを true に設定すると、イベント・リスナーが初めて呼び出された後、自動的に削除されます.
survey.addEventListener('click', recordResults, {once: true});

developer.mozilla.org

EventTarget: removeEventListener()

  • イベントリスナーを削除するには、イベントターゲット、関数名、イベントリスナー追加時に使用された useCapture フラグの値を一致させることが必要です.
  • 値が一致しない場合、イベントリスナーは削除されません.
document.addEventListener('keypress', handleKeyPress, false);
document.removeEventListener('keypress', handleKeyPress, false);

setTimeout()

  • このメソッドに出会うと、他の console.log (キュー内のメッセージ)が先に処理される.
  • 遅延msが引数に存在しない場合、引数が存在するものよりも先に処理される.

Export / Import

Export

  • JavaScript モジュールを作成するときに使用され、モジュールから関数、オブジェクト、またはプリミティブ値をimportを使用した他Jsファイルで使用できるようにする
  • exportされたモジュールをimportできるので、まずはexportしないといけない.

名前付きExport

  • 複数のモジュールを指定できる
  • importするときは、対応するオブジェクトと同じ名前を指定しないといけない
export function sample() {/* Code for retrieving scores */};
export { name, draw, reportArea, reportPerimeter };
import {sample} from './sample.js';

デフォルトExport

  • 1つのモジュールしか指定できない
  • importする際に、自由に名前を指定できる.
  • 関数をexportする際は、functionキーワードは必須.
export default expression;
export default function (…) {} // class, function* も使用可
export default function name1(…) {} // class, function* も使用可
export { name1 as default, … };
// ファイル test.js
let k; export default k = 12;
// 他のファイル
import m from './test'; // k がデフォルトエクスポートなので、インポートする k の代わりに m を使用することができる点に注意してください
console.log(m);        // log 12 になる

import

  • import文は関数や変数を名前で特定する必要があります.
import {retrieveScores} from './tools.js';
  • import時は *が使用できるが、exportでは使用不可.
  • import時に *使用する際の構文は下記. as nameが必要となる.
import * as name from "module-name";

importの巻き上げ

  • importがコードの下部で宣言されていたとしても問題ない.
// greeting function in tools.js
export function greeting(name) {
  console.log('Welcome ' + name);
}

// Code contained in second module
greeting('User');

import {greeting} from './tools.js';

動的import

  • 動的importの場合はpromiseが返却される.
  • import時にメソッド名を記載する必要はない.
    import('/modules/my-module.js')
      .then(module => {
        module.loadPageInto(main);
      })
      .catch(err => {
        main.textContent = err.message;
      });

変数のインポート

  • モジュールから変数をインポートする場合、その変数はconstを使って定義されているのと同じことになります.
  • モジュール外部で値を変更することはできません.
  • このような変数を提供する場合、必要に応じて変数を更新する関数も提供する必要があります.
  • この場合、変数に変更を加えることができる関数を利用する.

関数式宣言と関数宣言

関数宣言の巻き上げ

  • JavaScript の関数宣言は、それを囲む関数やグローバルスコープの先頭に巻き上げられ、関数を宣言する前に使うことができます。
hoisted(); // logs "foo"

function hoisted() {
  console.log('foo');
}

関数式宣言

  • 関数式は巻き上げられない
notHoisted(); // TypeError: notHoisted is not a function

var notHoisted = function() {
   console.log('bar');
};

developer.mozilla.org

developer.mozilla.org

letvar変数の巻き上げ

-letは宣言前にアクセスされた場合に、ReferenceErrorを発生させる.
- varはエラーにならないが、常にundefinedとなる.

function sayHi() {
  console.log(name);
  console.log(age);
  var name = "Lydia";
  let age = 21;
}

sayHi();
// undefined
// ReferenceError

qiita.com


Iterator / Generator

Generator

  • イテレーターより簡単に実装できることが多い.
  • function*キーワードとyieldキーワードで定義する.
    • yieldはジェネレーター内のreturnキーワードのようなもの.
    • next()で各項目に一度にアクセスが可能.
const makeRandomNum = function*(upperLimit, howMany) {
  let cnt = 1;
  while (cnt <= howMany) {
    let rand = Math.floor(Math.random() * upperLimit) + 1;
    cnt++;
    yield rand;
  };
};
let rand100 = makeRandomNum(100, 5);
console.log(rand100.next());

coliss.com


スコープ

this

  • クラス内からthisキーワードにアクセスした場合(関数からも含む)、クラスは常にstrict modeで実行されるため、thisキーワードの値は未定義になってしまいます.
  • strict modeで実行された場合、グローバル変数(上位変数)へのアクセスを防ぐためにundefinedにしてくれるが、newしたClassメソッドを呼び出した場合は strict modeが働いて thisにアクセスできずにエラーになります.

qiita.com

  • 子クラスでコンストラクタを使用する場合は、 コンストラクタ内でキーワード this を使用する前に super を呼び出す必要があります。スーパークラスのコンストラクタに値を渡すには、superを呼び出す際にその値を含めることができます。

アロー変数

  • アロー関数は特別で、それらは “自身の” this を持ちません.
  • アロー関数は定義された時点でthisが固定される.
this.name = 'test';

const user = {
    name: 'Iiya',
    regular: function () {
        return this.name
    },
    arrow: () => {
        return this.name
    }
}

console.log(user.regular()) //=> iiya
console.log(user.arrow()) //=> test

ja.javascript.info

tcd-theme.com

  • 基本的には、thisはメソッドが呼び出された.のobjにbindされる.
  • 関数の中の関数は、.がないのでthisがなくなる.

qiita.com

|| static

  • staticで定義されたClassメソッドは、クラスインスタンスを介して呼び出すことはできない.
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  static displayName = "Point";
  static distance(a, b) {
    const dx = a.x - b.x;
    const dy = a.y - b.y;

    return Math.hypot(dx, dy);
  }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
p1.displayName; // undefined
p1.distance;    // undefined
p2.displayName; // undefined
p2.distance;    // undefined

console.log(Point.displayName);      // "Point"
console.log(Point.distance(p1, p2)); // 7.0710678118654755

argumentsオブジェクト

developer.mozilla.org

  • 配列風オブジェクト.
  • アロー関数では使用できない.

継承

  • 子オブジェクト、またはextendされた子オブジェクトは、必要なパラメータを渡しながら this のコンテキストを設定する call メソッドを使用して親コンストラクタを呼び出すことにより、継承したプロパティを初期化できます.
function Mascot(name) {
    this.name = name;
}

Mascot.prototype.getGreeting = function () {
    return `Hi, my name is ${this.name}.`;
}

function Owl(name, sound) {
    // Owlコンストラクタ関数内で実行することで、
    // 継承された`name`プロパティの値を設定することができます
    Mascot.call(this, name);
    this.sound = sound;
}

Owl.prototype = Object.create(Mascot.prototype);
Owl.prototype.getGreeting = function () {
    // 親オブジェクトのオーバーライドメソッドを呼び出し
    let greeting = Mascot.prototype.getGreeting.call(this);
    return `${greeting} I ${this.sound}!`;
}

const myOwl = new Owl('Hootie', 'hoot');
const greeting = myOwl.getGreeting();

console.log(greeting);
// Hi, my name is Hootie. I hoot!

オブジェクトのプロパティの削除

  • deleteオペレータを使用する.
  • obj.feedbacknull'', 'undefined`を挿入しても、プロパティは削除されない.
function removeFeedback(obj) {
  if (obj.feedback) {
    delete obj.feedback;
  }
};

for in / for ofの違い

for in

  • Propertyがループされる.

for of

  • Valueがループされる.

qiita.com