ふみぽん's diary

技術的な備忘録が主のブログ

【JavaScript】Storage(ストレージ)について 『+ todoアプリ』

JavaScript】Storage(ストレージ)の勉強


JavaScriptでストレージ操作

Web Storage(ストレージ)

JavaScriptの世界では、書いたスクリプトからコンピューターに勝手に書き込むことは許容されるべきではないという考えのようです。 例えば、buttonをクリックしたらコンピューター内のファイルを書き換えてしまうようのことがあるべきではないということ。

ただ、例外が、、、

例外となる手段として、「クッキー(cookie)」という仕組みです。
Webアプリケーションで、クッキーを利用することでクライアントに対して文字列を保存することができるようになります。

でも、このクッキーはJavaScriptでは操作しにくいようです (クッキーについては、次回調べてみようと思います。本当はクッキーを使ってコード書く予定でした。。。)

では、クッキーの代替技術は?

それが、Web Strage(ストレージ)です!
ストレージとは、ブラウザ内臓のデータ保管庫のことです。
ストレージには、データと特定するためのkeyと値valueの組み合わせで保存します。

key value
color1 あか
color2 ピンク
... ...

ローカルストレージとセッションストレージ

ストレージは、ローカルストレージセッションストレージに分類されます。
以下で、両者の違いを見ていきます。

ローカルストレージ
  • オリジン単位でデータを保管
  • ブラウザを閉じてもデータがストレージから消滅しない
ちなみに、「オリジン」とは
【http://www.xyz:8080/」のように「スキーマ://ホスト名:ポート番号」の組み合わせで表せる単位のこと
セッションストレージ
  • セッション(ブラウザを開いている間)で維持されるデータを管理
  • ブラウザを閉じるとデータがストレージから消滅する

では、どのように使い分ける?

結論としては、
セッションストレージでまかなえるのであれば、優先して利用するべき

その理由としては、 ローカルストレージには、以下のようなデメリットがある。  

  • 明示的にデータを削除しない限り、データが消えない  

=> ゴミがたまる変数名が被ったりすると予期せぬ動きになる


データ保存/取得方法

記法 コード
プロパティ構文 strage.キー名
ブラケット構文 strage['キー名']
メソッド構文(取得) strage.getItem('キー名')
メソッド構文(保存) strage.setItem('キー名', '値')

保存について - ストレージに保存できる値の方は文字列

オブジェクトを保存するには - 復元可能な文字列に変換してから保存する

以下、変換及び復元のサンプルコード

const toString = () => {
  const obj1 = {
    name: "xxx",
    price :2300,
  }
  console.log(obj1); // => オブジェクトとして出力

  //オブジェクト→文字列(変換)
  const str = JSON.stringify(obj1);
  console.log(str); // => 文字列として出力

  //文字列→オブジェクト(復元)
  const obj2 = JSON.parse(str);
  console.log(obj2); // => オブジェクトとして出力
}

サンプルコード

ストレージを使ってtodoリストのアプリケーションを作りましたので、
スクリプトファイルを載せておきます。

//ストレージへのアクセスのために、Strageオブジェクトを変数に格納
let strage = localStorage;    //=> ローカルストレージの場合
// let strage = sessionStorage; => セッションストレージの場合


//キーを指定してストレージに保存している値を取得
let todoTasks = strage.getItem("todoTasks");

if(todoTasks !== null){
  //ストレージに保管されていた文字列からオブジェクトを復元
  todoTasks = JSON.parse(todoTasks);
} else {
  todoTasks = [];
}

const todoForm = document.getElementById("todo_form");
const todoList = document.getElementById("todo_list");

const todoInput = document.querySelector("#todo_form input");

const addItem = (event) => {
  //formによる画面遷移を止める
  event.preventDefault();

    const taskString = todoInput.value;
  if(taskString.length === 0){
    alert("やること入力して");
    return;
  }

  todoTasks.push({
    text: todoInput.value,
    done: false,
  });

  render();
  todoInput.value = "";
}

const render = () => {
  todoList.innerHTML = "";

  console.log(todoTasks);

  todoTasks.forEach(function(task) {
    //未完了・完了を管理するチェックボックス
    const checkBox = document.createElement("input");
    checkBox.type = "checkbox";
    checkBox.checked = task.done;
    checkBox.addEventListener("click", function(event){
      task.done = event.target.checked;
    });

    //追加されたタスクを表示するため
    const span = document.createElement("span");
    span.textContent = task.text;

    //テキストをクリックしてもチェックされるようにラベルの子要素とする
    const label = document.createElement("label");
    label.appendChild(checkBox);
    label.appendChild(span);

    //削除ボタン
    const deleteButton = document.createElement("button");
    deleteButton.textContent = "削除";
    deleteButton.addEventListener("click",function() {
      const index = todoTasks.indexOf(task);
      if(0 <= index){
        todoTasks.splice(index,1);
      }
      render();
    });

    const taskList = document.createElement("li");
    taskList.appendChild(label);
    taskList.appendChild(deleteButton);

    todoList.appendChild(taskList);
  });
  //オブジェクトを文字列に変換してからストレージに保存する
  strage.setItem("todoTasks" , JSON.stringify(todoTasks));
};

todoForm.addEventListener("submit",addItem);

render();

【JavaScript】varとletの違いを調べてみた

過去に以下のような記事を書きました。

fumipow2317.hatenablog.com

大変ありがたいことに以下のようなコメントをいただきました。ありがとうございますm( _ _ )m

なぜvarを使うとバグの温床になるのかという点を書いてもらうと、もっと良い記事になりそうです。

説明できないと思い、コード書いて試してみましたので記事にします。


varとletの違い

表形式で両者の違いをまとめてみます。

var let
再代入 できる できる
再宣言 できる できない
スコープ 関数スコープ  ブロックスコープ

ここでスコープについて補足です。

関数スコープとは、関数ごとに作られるスコープのこと

const func = () => {
 // === ここから関数スコープ ===
 var str = "umumum";
 console.log(str);

 ...(その他処理)

// === ここまで関数スコープ ===
}

ブロックスコープとは、ブロック{ }ごとに作られるスコープのこと


それでは まず、varを使って変数定義したサンプルコードから確認します。

const output = () => {
  var num = 10;
  console.log(num); // 10

  {
    var num = 99999999; // 再宣言
    console.log(num);  // 99999999

    var str = "ガムかむ"
  }

  console.log(num); // 99999999
  console.log(str); // ガムかむ
};
output();
// 10     (1行目)
// 99999999 (2行目)
// 99999999 (3行目)
// ガムかむ  (4行目)

関数outputを実行した結果の3行目に注目します。
varで宣言した変数numでは、再宣言が可能なのでvar num = 99999999;で変数numに格納された値が99999999となりました。

4行目にも注目します。 varで宣言した変数strは、関数outputで作られた関数スコープで使用できます。
そのためconsole.log(str);での出力が可能になっています。


次に、letを使って変数定義したサンプルコードを確認します。

const output = () => {
  let num = 10;
  console.log(num); // 10

  {
    let num = 99999999;
    console.log(num);  // 99999999

    let str = "ガムかむ"
  }

  console.log(num); // 10
  console.log(str); // not defined
};
output();
// 10     (1行目)
// 99999999 (2行目)
// 10     (3行目)
// Uncaught ReferenceError: str is not defined  (4行目)

関数outputを実行した結果の3行目に注目します。
let num = 99999999;はブロック内で使用できる変数numを定義しているだけです。
letでは再宣言はできないですし、ブロックスコープなのでnumの値が10 -> 99999999にはなりません。

同じく4行目にも注目します。
strが定義されていないというエラーになります。
原因は単純です。let str = "ガムかむ"で宣言している変数str
ブロックスコープ外のconsole.log(str);では参照できない変数だからです。


なぜvarは推奨されないのか

両者の違いを上記で確認したところで、本記事の本題です。 varが推奨されない理由としては、次のことが挙げられると思います。

  • 再宣言が可能であるために、予期せぬ変数値の書き換えが発生しやすくなる
    関数スコープが広いと、気付かずに同じ変数名を使用して
    別の目的で変数宣言してしまうこともあり得ると思います。
    そんなシーンでは予期しない書き換えが起こることがイメージできます。

letを使用すれば再宣言するとコンソールにエラーが吐かれるので気づくことができますね。


結論

というわけで

再代入の可能性がある変数を宣言する際は、letを使用することが推奨されます。

JavaScript入門⑥

JavaScriptの勉強をしたので備忘録として記事を書きます。(その5)


JavaScriptの関数

ビルドインオブジェクト

JSの仕様で最初から用意されているオブジェクトのこと (Mothオブジェクト・Dateオブジェクト等)

//参考)Mathオブジェクト使って80〜120の整数を出力する
let rundomNum = Math.random() * 40 + 80;
console.log(Math.round(rundomNum));

//参考)Dateオブジェクト
const now = new Date();
console.log(now);
console.log(now.getFullYear());

const birthDay = new Date('2000-11-23');
console.log(birthDay)
console.log(birthDay.getDate());

クラス

JavaScriptでは、雛形となるクラスを用意して、それを利用することで
同じようなオブジェクトを楽に作成するという考え方がある。

以下に「name」と「type」をいう属性をもった同じようなオブジェクトがある。

const poch = {
    name: poch,
    type: chihuahua
}

const hach = {
    name: hach,
    type: shiba
}

仮に同じようなオブジェクトを1万個作ろうとするととても大変。。。
そんな時に使えるのがクラスという概念

まず、雛形クラス(Dog)を作ってみる。

class Dog {
    //コンストラクタ
    constructor(name, type) {
        this.name = name;
        this.type = type;
    }
    //メソッド
    bark() {
        console.log(`${this.name}:wan! wan!!!`)
    }
}

Dogクラスを利用してオブジェクトを生成してみる。

const poch = new Dog("poch", "chihuahua")
poch.bark(); // 実行結果=> poch:wan! wan!!!

雛形クラスを利用すると

  • オブジェクトの生成が楽にできる
  • 同じようなコードを複数回書かなくて良い(共通化できる)

というメリットがありそうです。

継承

継承という考え方もあります。
クラスを作成する際に他のクラス(親クラス)を継承して、
親クラスで定義されている内容を引き継ぐことができる。

以下ではDogクラスを継承したMyDogクラスを作ってみます。

class MyDog extends Dog {  //「extends」を使ってDogクラスを継承しています
    constructor(name, type, age) {
    super(name, type); // ここでは親クラスのDogクラスで定義されているコンストラクタと同じ処理
    this.age = age;
  }
    bark() {
    console.log(`${this.name}:bou! bou!`);
  }
}

インスタンス化して、メソッドを実行してみます。

const solt = new MyDog("solt", "mix", 7);
console.log(solt.age): // 実行結果 => 7
solt.bark(); // 実行結果 => solt:bou! bou!

上記のMyDogクラスでは、barkメソッドを定義しましたが、
仮に定義しなければ、親クラスであるDogクラスで定義されているメソッドが呼び出されます。

JavaScript入門⑤

JavaScriptの勉強をしたので備忘録として記事を書きます。(その5)


JavaScriptの関数

デフォルト引数

関数の実行時に引数がなくても使えるデフォルトの引数を設定することができる

const outputStr = (target = '焼きそば') => {
  console.log(`お腹が空いたら${target}を食べたい`);
}

outputStr(); //デフォルト引数を使って関数実行 => お腹が空いたら焼きそばを食べたい
outputStr('すき焼き');

関数とスコープ

  1. まず自関数のスコープ内にその値があるかどうかを確認する
  2. なければ、1つ外側のスコープにその値があるかどうかを確認する
  3. それを繰り返し、最終的にグローバルスコープにその値があるかどうかを確認する
    →このような動きのことをスコープチェーンという
//関数のスコープ
let a = 'グローバルスコープのa'

const func1 = () => {
  let a = 'func1のa';
  console.log(a); // => func1のa

  const func2 = () => {
    let a = 'func2のa';
    console.log(a); // => func2のa
  }
  func2();
}

console.log(a); // => グローバルスコープのa

func1();

JavaScript入門④

JavaScriptの勉強をしたので備忘録として記事を書きます。(その4)


JavaScriptの関数

  • 関数の書き方として以下2種類の書き方がある。
    • 関数宣言
    • 関数式 (関数式では最新の仕様でアロー関数を使える)
//関数宣言
function getTriangleArea (base,height){
  return base * height / 2;
}

//関数式
const getTriangleArea = function (base, height){
  return base * height / 2;
}

//アロー関数
const getTriangleArea = (base, height) => {
  return base * height / 2;
}
  • 関数宣言と関数式の違い
    • 関数宣言:宣言と呼び出しの順番を問わない。
    • 関数式:宣言が先、呼び出しが後に書かれている必要がある。
      コードの可読性としては、先に宣言を書いておいた方が読みやすいです。
// 関数宣言
sayHello(); //呼び出しが先でもOK
function sayHello() {
  console.log('hello');
}
// 関数式(アロー関数)
const sayHello = () => {
  console.log('hello');
}
sayHello(); //呼び出しは後

  • アロー関数のさまざまな書き方

引数が1個の時は()を省略できる (引数がない場合は、省略できない)

const getCircleArea = r => {
  return r * r * Math.PI;
}

returnが1行の時はreturnを省略可能

const getCircleArea = r => r * r * Math.PI;

JavaScript入門③

JavaScriptの勉強をしたので備忘録として記事を書きます。(その3)


JavaScriptの配列

const colors = [
  'white',
  'red',
  'blue'
];

for(let i = 0; i < colors.length; i++)
{
  console.log(`${colors[i]}の色が好き`);
  //whiteの色が好き
  //redの色が好き
  //blueの色が好き
}
  • バッククォート内のの${ }について
    ${ }に値はJavaScriptで処理された上で文字列として出力される
    (バッククォートは文字列を生成する記号の一つ)

厳密等価演算子と等価演算子について

//厳密等価演算子
console.log( false === 0); //false
//等価演算子
console.log( false == 0); //true
  • 等価(不等価)演算子オペランドを比較する際に勝手に型変換してしまう。

  • よって、予期しない結果を招きやすいので厳密等価(不等価)演算子を使用する


JavaScriptの暗黙的な型変換

そもそも、暗黙的な型変換とは

  • 明示的に指定しなくてもコンパイラの判断によって自動的に行われる型変換のこと

予期せぬ処理結果となった場合は、typeof演算子を使って確認してみるといいですね。

JavaScript入門②

JavaScriptの勉強をしたので備忘録として記事を書きます。(その2)


JavaScriptの開発環境

ブラウザ上でJavaScriptを動かすためには 以下3つの方法でJavaScriptのコードを書く必要がある。

  • JavaScriptファイルをHTMLファイルから読み込む (bodyタグの最後の方で書かれていることが多い )
<html>
    <head>
    </head>
    <body>
      <script src="./main.js"></script>
    </body>
</html>
  • HTMLファイル内に直接書く
<html>
    <head>
    </head>
    <body>
      <script>
        console.log("Hello!")
      </script>
    </body>
</html>
  • HTMLの要素ないに直接書く
<html>
    <head>
    </head>
    <body>
      <a href="javascript: console.log('Hello!')">Say</a>
    </body>
</html>

JavaScriptの型

  • JavaScriptでは変数の宣言時に型を決める必要はない。
  • これを「動的型付け」をいう。

しかし、基本型とオブジェクト型という概念は存在する。


JavaScriptの基本型

  • 真偽値:boolean
  • 数値:number
    • Javaのように整数や少数または数値の大きさで型が変わるということはない。
  • 文字列:string
    • シングルクォーテーションまたはダブルクォーテションで囲む
  • 未定義:undefined
  • なにもない:null

ちなみにtypeof演算子を使用すると型を調べることができる。
以下、例

let num = 1;
const result = typeof a;
console.log(result); //number


また、
基本型以外の型をオブジェクト型といいます。


JavaScriptのオブジェクト

以下、例

const myProf = {
  name: "hoge",
  age: 20,
  height: "175cm"
}

//オブジェクトのvalue取得方法
console.log(myProf.name); //hoge
console.log(myProf[age]); //20

この例では

  • myProfオブジェクト
  • name,age,heightkey
  • "hoge",20,"175cm"value

となっています。