- Published on
JavaScript Topics
- Authors
- Name
- Kikusan
- Symbol
- let const 関数式
- オブジェクトキーの動的設定
- プロパティ名のショートハンド
- 可変長引数
- 分割代入
- 展開演算子
- 引数と分割代入の初期値
- ショートサーキット評価
- Null合体演算子
- オプショナルチェーン
- thisの取り扱い
- 1. メソッドとして実行された時: 実行オブジェクト
- 2. new を使用した場合: 新規作成オブジェクト
- 3. 関数内ではない場合: undefined
- ローカル関数でthisを扱うには
- モジュールシステム
- ES Modules
- 非同期処理
- Promise
- Promise.all
- Promise.race
- Promise.allSettled
- async/await
- fetch
最近のJavaScriptのメモ内容をまとめます。
Symbol
// 定数として利用する
const VALUE = Symbol();
const VALUE2 = Symbol();
class MyClass {
constructor(value) {
this[VALUE] = value;
}
}
let const 関数式
var
はスコープ単位が関数で再宣言ができてしまう。let
なら再宣言できないし、constなら代入もできない。
なお、関数宣言のfunction
もvar
と同じ問題を抱えているので、関数式を使う。
// 関数式で`const`を使えば再宣言できなくなる。
const func = () => {}
オブジェクトキーの動的設定
const key = 'foo';
const bar = 2;
const obj = {[key]: 1, bar: bar };
console.log(obj); // Object { foo: 1, bar: 2 }
プロパティ名のショートハンド
const aaa = 'bbb';
console.log({aaa}); // Object { aaa: "bbb" }
可変長引数
function sum(...args) {
let result = 0;
for (let arg of args) {
result += arg;
}
return result;
}
分割代入
let [hoge, foo] = [1, 2];
let [hoge, foo, ...other] = [1, 2, 3, 4];
console.log(other); // [3, 4]
let {hoge, foo} = {hoge: 'h', foo: 'f'}; // オブジェクトはキーの名前でプロパティが割り振られる
console.log(hoge); // h
関数の戻り値を配列にすることで、[, foo]
のように一部だけ受け取れるようになった
展開演算子
// ...で引数に配列を展開できる
console.log(Math.max(...[1, 2, 3, 4]))
// オブジェクトでも一階層目のプロパティを展開できるぞ
引数と分割代入の初期値
// 引数初期値
const func = (a, b = 2) => a + b;
console.log(func(1)); // 3
// 分割代入初期値
const {data: ary = []} = {data: [1, 2, 3]};
console.log(ary); // Array [1, 2, 3]
ショートサーキット評価
// 左から初めて真になった値の代入
const hello = undefined || null || 0 || NaN || 'Hello!' || 'Hello2!';
console.log(hello); // Hello!
// 左から初めて偽になった値の代入 (式の評価が真の場合に次に渡される)
const hello2 = 'Hello!' && NaN && 0 && undefined && null && 'Hello2';
console.log(hello2); // NaN
Null合体演算子
??
は||
のnull
とundefined
だけバージョン。
const foo = null ?? 'default string';
console.log(foo);
// expected output: "default string"
const baz = 0 ?? 42;
console.log(baz);
// expected output: 0
オプショナルチェーン
存在しないかもしれないプロパティに?.
をつけてなければundefinedが返る。
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah'
}
};
const dogName = adventurer.dog?.name;
console.log(dogName);
// expected output: undefined
console.log(adventurer.someNonExistentMethod?.());
// expected output: undefined
thisの取り扱い
「関数が実行されるコンテキストオブジェクトへの参照引数」
1. メソッドとして実行された時: 実行オブジェクト
const obj = {
name: "呼び出しオブジェクト",
func() {
console.log(this);
}
}
obj.func(); //Object { name: "呼び出しオブジェクト", func: func() { console.log(this);
new
を使用した場合: 新規作成オブジェクト
2. const func = function() {console.log(this);}
const funcObj = new func(); // [object Object]
3. 関数内ではない場合: undefined
'use strict;'
を使っていないとthis
はWindowオブジェクト
になるが、strictモードではundefinedになる。
console.log(this); // undefined
// WARN: new がない場合、functionはグローバルにある。
const func = function() {console.log(this);}
const funcObj = func(); // undefined
そのためローカル関数では、undefined
になる。
'use strict';
class MyClass {
constructor(name) {
this.name = name;
}
func() {
const internalFunc = function() {
console.log(`this is ${this}`);
}
internalFunc();
}
}
const myClass = new MyClass("Kikusan");
myClass.func(); // "this is undefined"
ローカル関数でthisを扱うには
いくつか方法はあるが、アロー関数で関数定義するのが良い。
アロー関数では、アロー関数が定義されたスコープの一つ外(自身ではthisをもたない)のthisが使われる。
'use strict';
class MyClass {
constructor(name) {
this.name = name;
}
func1() {
const internalFunc = function() {
console.log(`this is ${this}`);
}
const bindedFunc = internalFunc.bind(this); // 1. bindの使用パターン
bindedFunc();
}
func2() {
const _this = this; // 2. thisの差し替えパターン
const internalFunc = function() {
console.log(`this is ${_this}`);
}
internalFunc();
}
func3 = () => {
const internalFunc = () => {
console.log(`this is ${this}`); // 3. アロー関数で定義
}
internalFunc();
}
}
const myClass = new MyClass("Kikusan");
myClass.func1(); // "this is Kikusan"
myClass.func2(); // "this is Kikusan"
myClass.func3(); // "this is Kikusan"
モジュールシステム
歴史的には
- node.jsの
require/module.export
CommonJS - ES6の
import/export
ES Modules - webpackが1.も2.もサポート
つまり、今後ES Modulesに統一されていく流れになると思われる。
webpackを使っていればimport/export
もrequire
も使える。
ES Modules
htmlでは、<script type="module">
の中でimport/export
をするとモジュールを読み込める。
node.jsでは、package.json
に"type": "module"
を記載することで、ES Modulesとして扱われる(書いてなくてもデフォルトでこれ)
- export命令
// 名前付きエクスポート
export {a, b}; // 複数エクスポート
export const c = 1; // 定義と同時にエクスポート
// 名前なしエクスポート import 側で好きな名前をつけられる。(export defaultができるのは1ファイルにつき1回まで)
export default d; //
- import命令
// インポート as で別名をつけられる。パスは相対パスでファイル名をフルで書く。(拡張子の省略はCommonJSの名残)
import {a, b as B} from './exports.js';
// デフォルトエクスポートのインポート
import d from './exports.js';
非同期処理
Promise
非同期処理を同期させるためのオブジェクト
function f(value) {
// 非同期関数の戻り値をPromiseにする
return new Promise((resolve, reject) => {
setTimeout(() => {
if(value) {
//resolve: 引数からくる処理の成功を通知するための関数
resolve(`x${value}x`);
} else {
//reject: 引数からくる処理の失敗を通知するための関数
reject(new Error('入力値が空です'))
}
}, 2000);
})
}
f('name')
// resolve時の処理
.then(
response => {
console.log(response);
}
)
// reject時の処理
.catch(
error => {
console.log(`Error: ${error.message}`);
}
)
// finally
.finally(
() => {
console.log('finally');
}
)
// xnamex
// finally
Promiseを使うことで非同期処理を連結できる
f('name')
.then(
response => {
return f(response); //thenの中でPromiseオブジェクトを返す
}
)
.then( // 2つ目のPromiseを受け取れたら処理する
response => {
console.log(response);
}
)
// どちらのPromiseが失敗しても呼ばれる
.catch(
error => {
console.log(`Error.${error.message}`);
}
)
// xxnamexx
Promise.all
非同期処理の並列実行
Promise.all([
f('name1'),
f('name2'),
f('name3')
])
.then(
response => {console.log(response);})
.catch(
error => {console.log(`Error.${error.message}`);}
);
// ["xname1x", "xname2x", "xname3x"]
Promise.race
非同期処理の最初に完了した1つの値を使う
Promise.race([
f('name1'),
f('name2'),
f('name3')
])
.then(
response => {console.log(response);})
.catch(
error => {console.log(`Error.${error.message}`);}
);
// xname1x
Promise.allSettled
とにかく非同期に実行して、成否と値を得る
Promise.allSettled([
f('name1'),
f(''),
])
then((results) => {
for (const result of results) {
console.log(result);
}
});
// {status: "fulfilled", value: "xname1x"}
// {status: "rejected", reason: Error: 入力値が空です}
async/await
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function
非同期処理を同期処理風に記述する構文
- async: 非同期処理を行う関数であることを表す。 async関数は自動でPromise
- await: 処理が終わるまでその位置で待つ。(メソッドの残りの処理はプールしつつ、呼び出し元にいったん制御を戻してしまう)
// 1. 非同期で実行すべき処理は関数でまとめる
async function serial(value) {
// 2. Promiseを返すf関数の呼び出し
let result = await f(value);
result = await f(result);
result = await f(result);
// 4. 非同期処理後に実行
console.log('処理が終了しました');
return result;
}
serial('name')
.then(response => {
// 5. 最終的な結果を出力
console.log(response);
})
.catch(error => {console.log(`Error.${error.message}`);
});
// 3. 非同期処理中に処理すべき処理
console.log('…他の処理…');
fetch
$.ajax
に似ている標準メソッド。Promiseオブジェクトを返す。
fetch(url, {method: 'POST', body: data})
.then(response => response.text())
.then(text => console.log(text));