Programming Self-Study Notebook

勉強したことを忘れないように! 思い出せるように!!

JavaScriptで文字数をカウントする

f:id:overworker:20200406080809j:plain:h250
  • 文字数制限がある場合等、文字数を正確にカウントするときの処理

方法1:.lengthで数える(×)

// 半角英数字の場合
const sample1 = "12345abcde";
console.log(sample1.length);               // 10  (〇)

// 全角英数字の場合
const sample2 = "12345ABCDE";
console.log(sample2.length);               // 10  (〇)

// 絵文字の場合
const sample3 = "😀🤣😅😍😲😨😩😱🤬👽";
console.log(sample3.length);               // 20  (×)

方法2:配列に変換する(〇)

// 半角英数字の場合
const sample1 = "12345abcde";
console.log(Array.from(sample1).length);    // 10  (〇)

// 全角英数字の場合
const sample2 = "12345ABCDE";
console.log(Array.from(sample2).length);    // 10  (〇)

// 絵文字の場合
const sample3 = "😀🤣😅😍😲😨😩😱🤬👽";
console.log(Array.from(sample3).length);    // 10  (〇)

方法3:配列に変換する(スプレッド構文を使用する)(〇)

// 半角英数字の場合
const sample1 = "12345abcde";
const buf = [...sample1];
console.log(buf .length);               // 10  (〇)

// 全角英数字の場合
const sample2 = "12345ABCDE"
const buf = [...sample2];
console.log(buf .length);               // 10  (〇)

// 絵文字の場合
const sample3 = "😀🤣😅😍😲😨😩😱🤬👽"
const buf = [...sample3];
console.log(buf .length);               // 10  (〇)

その他の記事について

overworker.hatenablog.jp

JavaScriptで数値文字列を0で桁数をそろえる

f:id:overworker:20200406080809j:plain:h250

桁数(文字数)が合わない数字等の桁あわせを実施する処理

方法1:sliceを使用する

// 3桁で合わせる場合
a=1;
a=('00'+a).slice(-3);
console.log(a);            // '001'

方法2:padStartを使用する(ES2017)

a=1;
a=(''+a).padStart(3,0);
console.log(a);            // '001'

その他の記事について

overworker.hatenablog.jp

JavaScriptの文法(スコープ)

f:id:overworker:20200406080809j:plain:h250
  • 変数、定数、引数が有効な範囲を決めるモノ

スコープと存在

  • 変数などがプログラム中で存在している状態ではなくなっても、JavaScriptがその分のメモリをすぐに回収するとは限りません。
    • その変数に対し、管理の必要がなくなったことを記録するだけ。
    • 実際のメモリ回収は「ガベージコレクション」によって定期的に行われる。

静的スコープと動的スコープ

  • JavaScriptの構文は静的スコープ(構文スコープ:lexicsl scope)
const x = 3;
function f (){
  console.log(x);    // xを参照可能
}

f();

グローバルスコープ

  • プログラムが実行を開始する際に暗黙的に存在するスコープ
  • 大域スコープともいう
  • JavaScriptのプログラムが実行を開始し、関数を呼び出す前にはグローバルスコープで実行される。

ブロックスコープ

  • ブロックスコープで定義された識別子はそれを囲むブロックでしか有効になりません。

変数の隠蔽

  • 同じ名前の変数や定数が異なるスコープで使用され、かつそれぞれのスコープが重複する部分がある場合は後から定義された変数が有効になり、元から定義されている変数へのアクセスができなくなる
{
  let x = 3;
  console.log(x);
  {
    let x = 5;
    console.log(x);
  }
  console.log(x);
}

関数、クロージャ、静的スコープ

  • 関数の周囲にスクープを閉じこむもの
let func;
{
  let o = {memo1:"安全", memo2:"大丈夫!"};
  func = function(){
    console.log(`無名関数の中:${o.memo1}`); //無名関数の中:安全
    return o;
  }
}
let oRes = func();
console.log(oRes);    // {memo1: "安全", memo2: "大丈夫!"}
oRes.memo1 = "危険";
console.log(oRes);    // {memo1: "危険", memo2: "大丈夫!"}

IIFE(即座に実行される関数)

  • 関数式(IIFE:immediately invoked function expression)
  • 即時関数ともいう
  • 関数を宣言するとすぐに実行する
(function(){
  // IIFEの本体
})();

メリット - 独自のスコープを持つ - スコープ内部の情報を外部に渡す(returnする)ことができる。

関数のスコープと巻き上げ

  • varを使って宣言した変数には「巻き上げ」が発生する。
    • スコープ全体でvarで宣言された同じ名前の変数があれば、それが使われている個所で宣言されたものとして扱う
{
console.log(x);  // undefined
var x = 5;
console.log(x);  // x
}
  • var x ~は最初にxが実行されるより後に記述されているが、「巻き上げ」が実施されるため、宣言済み扱いになる。

関数の巻き上げ

  • varを使用すると関数宣言もスコープの先頭に巻き上げられる。

TDZ(Temporal Dead Zone)

  • letで宣言された変数は宣言されるまで存在しないということ

strictモード

  • 'use strict'、または"use strict"を記述することで、暗黙的グローバルが禁止される。
    • グローバルスコープの他の文の前にあれば、スコープ全体がstrictモードで実行される
    • 関数内の他の文の前にあれば、その関数がstrictモードで実行される

参考文献

JavaScriptの文法(関数)

f:id:overworker:20200406080809j:plain:h250

戻り値

  • 関数の値はreturnを使って呼び出し元に戻します。
    • 関数からreturnで戻らない場合や値が指定されないreturnで戻る場合、関数が戻す値はundefinedとなる。
    • 関数は任意の型を返すことができる。

呼び出しと参照

  • JavaScriptで関数はオブジェクトです。
    • 関数を関数の引数として渡すことができる。
    • 関数を変数に代入することができる。
種類 記述方法 詳細
呼び出し 関数名に()をつける 関数の本体が実行される
参照 関数名のみ 実行されない
function getGreeting() {
  return "Hello World!";
}
//呼び出し
console.log(getGreeting()); // "Hello World!"

//参照
console.log(getGreeting);
/*
ƒ getGreeting() {
  return "Hello World!";
}
*/

// 関数を変数に代入する(関数の名前を変更する)
const f = getGreeting;
console.log(f());    // "Hello World!"

// 関数をオブジェクトのプロパティに代入する
const o = {};
o.f = getGreeting;
console.log(o.f());    // "Hello World!"

// 配列の要素として関数を代入する
const arr = [1,2,3];
arr[1] = getGreeting;
console.log(arr[1]());    // "Hello World!"

関数の引数

  • 仮引数がプリミティブ型の変数の場合、仮引数に対し関数内で変更を加えても呼び出し元の値には変化が起こらない。
function f(x){
    console.log(`関数内1:${x}`);
    x = 5;
    console.log(`関数内2:${x}`);
}

const x =10;
console.log(`関数外1:${x}`);
f(x);
console.log(`関数外2:${x}`);
/*
// 表示内容
関数外1:10
関数内1:10
関数内2:5
関数外2:10
*/
  • 仮引数がオブジェクト型の変数の場合、関数内部で別オブジェクトを参照する記述を実行するまでは元のオブジェクトを参照する。
    • 仮引数で受け取ったオブジェクトの既存キーの値を変更する:呼び出し元と同一のオブジェクトを参照
    • 仮引数で受け取ったオブジェクトにキーを追加:別の新しいオブジェクトを参照
function f1(o){
console.log(`    f1-1:${o.message}`);
o.message = "f1内で書き換え";
console.log(`    f1-2:${o.message}`);
}

function f2(o){
  console.log(`    f2-1:${o.message}`);
  o ={
    message:"f2内で同名オブジェクト追加",
  };
  console.log(`    f2-2:${o.message}`);
}

function f3(o){
  console.log(`    f3-1:${o.message}`);
  o.message = "f3内で別名オブジェクト追加";
  o.message2 = "f3内で別名オブジェクト追加";
  console.log(`    f3-2:${o.message}`);
}

function f4(o){
  console.log(`    f4-1:${o.message}`);
  o.message = "f4内で別名オブジェクト削除";
  delete o.message2;
  console.log(`    f4-2:${o.message}`);
}
let o ={
  message:"初期値",
};
console.log(`呼び出し元1:${o.message}`);
f1(o);
console.log(`呼び出し元2:${o.message}`);
f2(o);
console.log(`呼び出し元3:${o.message}`);
f3(o);
console.log(`呼び出し元4:${o.message}`);
f4(o);
console.log(`呼び出し元5:${o.message}`);

/*
呼び出し元1:初期値
    f1-1:初期値
    f1-2:f1内で書き換え
呼び出し元2:f1内で書き換え
    f2-1:f1内で書き換え
    f2-2:f2内で同名オブジェクト追加
呼び出し元3:f1内で書き換え
    f3-1:f1内で書き換え
    f3-2:f3内で別名オブジェクト追加
呼び出し元4:f3内で別名オブジェクト追加
    f4-1:f3内で別名オブジェクト追加
    f4-2:f4内で別名オブジェクト削除
呼び出し元5:f4内で別名オブジェクト削除
*/

引数と関数

  • JavaScriptでは、関数は名称のみで区別される(引数の数によらない)
    • すべての関数は任意個の引数を指定して呼び出すことができる。
    • 引数へ値をセットする際に分割代入が可能
    • 呼び出し側で関数定義側が用意している引数が指定されなかった場合、undefinedが指定されたものとして扱われる。
  • 関数定義側は呼び出し側で引数がセットされなかった時のためのデフォルト引数を要しすることができる。
function f(a,b="default",c=3){
  return `${a} - ${b} - ${c}`;
}
let o ={
  message:"初期値",
};
console.log(f(5,6,7)); // 5 - 6 - 7
console.log(f(5,6));   // 5 - 6 - 3
console.log(f(5));     // 5 - default - 3
console.log(f());      // undefined - default - 3

オブジェクトのメソッド

  • オブジェクトのプロパティとして指定される関数のことをメソッドという。
const o  = {
  name: "Takeshi", 
  message: function(){return "Hello!";},
}
console.log(o.name);       // Takeshi
console.log(o.message());  // Hello!

// 上記を省略記法(ショートハンド)で表現する
const o2  = {
  name: "Takeshi", 
  message(){return "Hello!";},
}
console.log(o2.name);       // Takeshi
console.log(o2.message());  // Hello!

this

  • 「オブジェクト」と「オブジェクトのプロパティである関数(メソッド)」を束縛する。
const o  = {
  name: "Takeshi", 
  message(){return `Hello! My name is ${this.name}`;},
}
const message = o.message;
console.log(message === o.message); // true
console.log(o.message());           // "Hello! My name is Takashi"
console.log(message);               // ƒ message(){return `Hello! My name is ${this.name}`;}

関数式と無名関数

  • 関数式とは関数宣言に対し、関数名を省略する記法のコト
    • 関数式は値をとして関数を返す
      • 関数式変数などに代入することが可能
      • 関数式はすぐに呼び出すことも可能
  • 関数式関数宣言の違いは実質的にはない
    • 周囲の文脈を把握する必要がある
const f = function(){
    // ...
}

アロー演算子

  • 無名関数の一種
  • 無名関数を引数に渡す場合によく利用される

特徴

  • functionという単語を省略できる。
  • 引数が一つならば()を省略できる。
  • 関数本体が一つの式となる場合、{}、それにreturnを省略できる。

通常の関数とは異なる点

  • thisが他の変数と同様に、語彙的に束縛される
  • オブジェクトのコンストラクタとして使えない
  • argumentsが使えない
const f1_1 = function(){ return "Hello!";}
const f1_2 = () => "Hello!";
console.log(f1_1());   // Hello!
console.log(f1_2());   // Hello!

const f2_1 = function(name){ return `My name id ${name}`;}
const f2_2 = name => `My name id ${name}`;
console.log(f2_1("Takeshi"));   // My name id Takeshi
console.log(f2_2("Takeshi"));   // My name id Takeshi

const f3_1 = function(a,b){ return a + b;}
const f3_2 = (a,b) => a+b;
console.log(f3_1(3,5));   // 8
console.log(f3_2(3,5));   // 8

call、apply、bind

call

  • すべての関数に対して利用できるメソッド
  • thisを特定の値に指定したうえで、関数を呼び出す
  • callの第一引数にはthisを束縛したい値を指定する
    • 残りの引数には呼び出す関数の引数を入れる
const taro = {name :"Taro"};
const hanako = {name :"Hanako"};

// 通常はthisは使用できない
function greet(){
    return `My name is ${this.name}`;
}
console.log(greet.call(taro));     // My name is Taro
console.log(greet.call(hanako));   // My name is Hanako
console.log(greet());              // error

apply

  • 引数を配列として受け取る(callは引数を直接受け取る)
    • 既に、関数が用意されていて、その値を関数の引数として使いたい場合
const arr = [2,3,-5,15,7];
console.log(Math.min.apply(null,arr)); // -5
console.log(Math.max.apply(null,arr)) // 15;

// スプレッド演算子を利用した場合
console.log(Math.min.apply(...arr)); // -5
console.log(Math.max.apply(...arr)) // 15;
  • 以下のapplyはスプレッド演算子を使用してcallに置き換えることが可能
const taro = {name :"Taro"};
const taro2 = {name :"Taro"};
const arr = [2004,"student"];
function update(birthyear,occupation){
  this.birthyear = birthyear;
  this.occupation = occupation;
};

// applyの場合
update.apply(taro,arr);
console.log(taro);  // {name: "Taro", birthyear: 2004, occupation: "Student"}

update.call(taro2,...arr);
console.log(taro2); // {name: "Taro", birthyear: 2004, occupation: "Student"}

bind

  • thisの値をある関数と永続的に結びつけることができる。
    • 一度bindでセットされた値は、callapply別のbindとも結びつく

参考文献

JavaScriptの文法(演算子)

f:id:overworker:20200406080809j:plain:h250

算術演算子

演算子 説明 備考
+ 加算(文字列連結も可) 3 + 2
- 減算 3 - 2
/ 除算 3 / 2
* 乗算 3 * 2
% 除算の余り 3 % 2
- 単項符号反転 -x 符号が反転する
+ 単項プラス +x xが数値でない場合は数値への返還を試みる
++ 前置インクリメント ++x
++ 後置インクリメント x++
-- 前置デクリメント --x
-- 後置デクリメント x--

比較演算子

厳密等価演算子と等価演算子

演算子 内容 記号
厳密等価演算子
(厳密不等価演算子)
- 同じオブジェクトを参照している
- プリミティブ型でデータ型も値も同じである
===
(!==)
等価演算子
不等価演算子
- 同じオブジェクトを参照している
- 同じ値に変換されるとき
==
(!=)
  • 等価演算子の特徴
    • "1"1が等価とみなされる。
    • ""(空文字)0nullundefinedが等価とみなされる。

関係演算子

演算子 内容
< より大きい a < b
<= 以上 a <= b
> 未満 a > b
>= 以下 a >= b
  • 数値を比較する際の注意

    • NaNNaN自身を含めどの値とも等しくなりません。
      • NaN === NaNfalseNaN == NaNfalse
    • xが数値かどうかを確認する場合は、isNaN(x)を使用する。
      • isNaN(x)xが数字でない場合(NaNを含む)はtrueを返す。それ以外はfalseを返す。
  • 数値は全て倍精度小数点なので、小数部分を含む値を===で比較すると計算誤差が生じて想定通り比較できない場合がある。

    • JavaScriptにはNumber.EPSILONという特殊な定数(二つの定数が等しいとみなすことができる差)がある。
if(Math.abs(x-y) < Number.EPSILON){
  console.log('same value!');
} else {
  console.log('different value!');
}

文字列連結

  • +には「数値の足し算」と「文字列の連結」の意味がある。
    • 演算子の値を左から右に評価する。

      組み合わせ 結果
      両方が数値の場合 足し算
      どちらかが文字列の場合 文字列の連結
console.log(3+5+"8");    // "88"
console.log("3"+5+8);    // "358"

論理演算子

  • truefalse以外も扱うことが可能
    • 数値だけでなくデータ型も評価対象となる。
  • 短絡評価が適用される
    • 最初に記述された条件式から評価を実施し、評価結果が確定した時点で残りの条件式の評価を実施しなくなる。
falseとみなされる値 trueとみなされる値
undefined
null
false
0
NaN
""(空文字列)
任意のオブジェクト
任意の配列
" ”(空白のみの文字列)
文字列"false"
内容 記号
論理積(AND) &&
論理和(OR) ||
論理否定(NOT) !
// inputName に値がセットされていないときはdefaultNameを使用する。
const inputName ="TestName";
const name = inputName || defaultName;
console.log(name);

const inputName2 ="";
const name2 = inputName2 || {name:"defaultName"};
console.log(name2);

条件演算子

const inputName ="TestName";
const profile = {};
profile.name = inputName ? inputName  : defaultName;
console.log(profile);

カンマ演算子

  • 複数の式を組み合わせる
  • 「複数の式を実行するが必要な値は最後の式の結果だけ」という場合に使用する
  • forループの内部で利用されることが多い
// 例1
// 1-1
let x = 0 , y = 100 , z;
z = ( x++ , y++ );
console.log(z);  // 100

// 1-2
x = 0 , y = 100;
z = x++ , y++;
console.log(z);  // 0

グループ化演算子

  • 演算子の優先順位を変えたり、明確にする(...)カッコの組をグループ演算子と呼ぶ。

ビット演算子

  • 32ビットの符号付整数を2の歩数計式で表現したものとして被演算子を扱う。
    • JavaScriptでは数値は全て倍精度浮動小数点数なので、ビット演算を実行する前に処理系が数値をすべて32ビット整数に変換し、結果を返す前に倍精度に変換します。
演算子 説明
& ビット毎のAND 0b1010 & 0b1100 // 結果は 0b1000
| ビット毎のOR 0b1010 | 0b1100 // 結果は 0b1110
^ ビット毎のXOR 0b1010 ^ 0b1100 // 結果は 0b0110
~ ビット毎のNOT ~0b1010 // 結果は 0b0101
<< 左シフト 0b1010 << 1 // 結果は 0b10100
0b1010 << 2 // 結果は 0b101000
>> 符号を維持する右シフト 0b1010 << 1 // 結果は 0b10100
0b1010 << 2 // 結果は 0b101000
>>> ゼロ埋め右シフト
let n = 22;                         
console.log(n.toString(2));         // 10110
console.log((n >> 1).toString(2));  // 1011
console.log(n >>> 1).toString(2));  // 1011 
n = ~n;
console.log(n.toString(2));         // -10111
n++;
console.log(n.toString(2));         // -10110
console.log((n >> 1).toString(2));  // -1011
console.log((n >>> 1).toString(2)); // 1111111111111111111111111110101

typeof演算子

  • JavaScriptの7つのデータ型に正確に対応していない
console.log(typeof undefined);    // "undefined"
console.log(typeof null);         // "object"  <- 実際の型と不一致
console.log(typeof {});           // "object"
console.log(typeof []);           // "object"
console.log(typeof true);         // "boolean"
console.log(typeof 1);            // "number"
console.log(typeof "");           // "string"
console.log(typeof Symbol());     // "symbol"
console.log(typeof function(){}); // "function"

void演算子

  • 演算子を評価したうえで、undefinedを返す

代入演算子

  • 値を変数に代入する
演算子 意味
x += y x = x + y
x -= y x = x - y
x *= y x = x * y
x /= y x = x / y
x %= y x = x % y
x <<= y x = x << y
x >>= y x = x >> y
x >>>= y x = x >>> y
x &= y x = x & y
x |= y x = x | y
x ^= y x = x ^ y

分割演算子

  • オブジェクトや配列を複数の要素に簡単に分割できる
// 1
const obj = {b:2,c:3,d:4};
const {a,b,c} = obj;
console.log(a); // undefined
console.log(b); // 2
console.log(c); // 3
console.log(d); // Uncaught ReferenceError: d is not defined

// 2
const obj = {b:2,c:3,d:4};
let a,b,c;
({a,b,c} = obj);
console.log(a); // undefined
console.log(b); // 2
console.log(c); // 3
console.log(d); // Uncaught ReferenceError: d is not defined

// 3
const arr = [1,2,3];
let [x,y] = arr;
console.log(x); // 1
console.log(y); // 2

// 4
const arr = [1,2,3,4,5];
let [x,y,...rest] = arr;
console.log(x);    // 1
console.log(y);    // 2
console.log(rest); // [3,4,5]

// 5
let a = 5, b = 10;
[a,b] = [b,a];
console.log(a); // 10
console.log(b); // 5

オブジェクト演算子と配列演算子

演算子 説明
. プロパティ(メンバー)へのアクセス
delete (プロパティ(メンバー))の削除
[] 計算値でのメンバーアクセス
in プロパティの有無を調べる
new オブジェクトのインスタンスを生成する
instanceof プロトタイプチェインをテストする
... 展開

参考文献

JavaScriptの文法(制御フロー)

f:id:overworker:20200406080809j:plain:h250

制御フローの例外

語句 内容
break ループを途中で抜ける
continue ループ内で、実施中の繰り返し処理を終了して、次の繰り返し処理に進む。
return 現在の関数を終了する
throw 例外ハンドラでキャッチする必要がある「例外」を示す

制御フロー

while文

while(条件)
    文

以下は無限ループの代表例

while(true){
    console.log('永遠は存在する。');
}

if...else文

if(条件)
    文1
[else
    文2]

do...while文

do
    文
while(条件);
  • は少なくとも1回は実行される

for文

for([初期化],[条件],[再設定])
    文
  • 実施される順序は「①初期化->②条件->③文->④再設定
    • ②条件の結果がfalseの場合、ループを抜ける(③へは進まない。)
      • その他、breakreturnthrowでもループを抜ける。
    • ,を使用すると複数条件をまとめてセットすることができる。

以下は無限ループの代表例

for(;;){
    console.log('永遠は存在する。');
}

switch文

switch(式){
    case 値1:
        文1
        [break;]
    case 値2:
        文2
        [break;]
    ...
    default:
        文default
        [break;]
        
  • 実施される順序は「①:式の評価->②:①の結果と一致するcaseの値を探す。->③:ヒットしたcase以下の文を実施
    • ②で一致する値が見つからない場合
      • defaultがある場合はdefault以下が実行される。
      • defaultがない場合は何も実行されない。
    • switch文のスコープを抜ける条件は以下
      • break文return文continue文throw文が実行される。
      • switch文の一番最後まで到達する。
  • caseの末尾にbreakを記述せずに、次のcaseを記述する制御フローをfall-throughと呼ぶ。
let message = '';
let n = Math.floor(Math.random()*6+1);
switch(n){
  case 1:
  case 3:
  case 5:
    message = '奇数です';
    break;
  default:
    message = '偶数です';
    break;
}
console.log(message);

for...inループ

  • オブジェクトのプロパティのキーに対して処理を繰り返す
for(変数 in オブジェクト)
    文
const profile = {
    name : 'taro',
    sex : 'male',
    age : 25,
    test: {
        test1:1,
        test2:"2",
    },
    sample: {
        sample1,
        sample2,
    }
};
for(let prop in profile){
    if(!profile.hasOwnProperty(prop))continue;
    console.log(prop + ':'+profile[prop]);
}
/*
name:taro
sex:male
age:25
test:[object Object]
sample:sample1,sample2
*/

for...ofループ

  • オブジェクトの各要素に対して処理を繰り返す
for(変数 of オブジェクト)
    文
const check = ['name','sex'];
const profile = {
    name : 'taro',
    sex : 'male',
    age : 25,
};

for(let prop of check){
    // check と一致するキーが存在したら、キーごと削除する。
    if(profile[prop]){
        delete profile[prop];
    }
}
console.log(profile);

参考文献