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関数の実装
- プロパティの読み取り専用動作は、書き込み可能なプロパティ記述子にのみ依存する
descriptor
のwritable
をfalse
に設定する
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
メソッド
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 }
Object.entries()
- Object.entries() メソッドは、引数に与えたオブジェクトが所有する、文字列をキーとした列挙可能なプロパティの組 [key, value] からなる配列を返します。
const object1 = { a: 'somestring', b: 42 }; for (const [key, value] of Object.entries(object1)) { console.log(`${key}: ${value}`); }
Object.freeze()
Object.freeze()
メソッドは、オブジェクトを変更できない状態にします.
- オブジェクトに新しいプロパティを追加したり、既存のプロパティを修正・削除したり、オブジェクトのプロパティの属性を変更したり、オブジェクトのプロトタイプを変更したりすることができなくなります.
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()
call
とapply
メソッドは、間接的に関数を呼び出します.- これらのメソッドは、最初のパラメータとしてオブジェクトを渡すことで、キーワード
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});
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'); };
let
とvar
変数の巻き上げ
-let
は宣言前にアクセスされた場合に、ReferenceErrorを発生させる.
- var
はエラーにならないが、常にundefinedとなる.
function sayHi() { console.log(name); console.log(age); var name = "Lydia"; let age = 21; } sayHi(); // undefined // ReferenceError
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());
スコープ
this
- クラス内から
this
キーワードにアクセスした場合(関数からも含む)、クラスは常にstrict mode
で実行されるため、this
キーワードの値は未定義になってしまいます. strict mode
で実行された場合、グローバル変数(上位変数)へのアクセスを防ぐためにundefined
にしてくれるが、new
したClassメソッドを呼び出した場合はstrict mode
が働いてthis
にアクセスできずにエラーになります.
- 子クラスでコンストラクタを使用する場合は、 コンストラクタ内でキーワード
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
- 基本的には、
this
はメソッドが呼び出された.
のobjにbindされる. - 関数の中の関数は、
.
がないのでthis
がなくなる.
|| 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オブジェクト
- 配列風オブジェクト.
- アロー関数では使用できない.
継承
- 子オブジェクト、または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.feedback
にnull
、''
, 'undefined`を挿入しても、プロパティは削除されない.
function removeFeedback(obj) { if (obj.feedback) { delete obj.feedback; } };
for in / for ofの違い
for in
- Propertyがループされる.
for of
- Valueがループされる.