ES6で提供された主な仕様
ECMAScript6とは?
そもそもECMAScriptとは、標準化団体によって標準化された言語仕様で、JavaScriptが動作する際に、元となっている仕様です。
ES6で提供された新たな仕様(1部抜粋)
・class命令によるJava/C#ライクなクラス定義が可能に
・import/export命令によるコードのモジュール化に対応
・関数構文の改善
・let/const命令によるブロックスコープの導入
・for...of命令による値の列挙
・イテレーター/ジェネレーターによる列挙可能なオブジェクトの操作が可能に
・promise、コレクション(Map/Set)/Proxyなどの組み込みオブジェクトを追加
・string/Number/Array/Objectなどの既存組み込みオブジェクトの拡充など
ブロックスコープを有効にする ー let命令
これまでのJavaScriptには、ブロックレベルのスコープはありませんでした。
※ES6を試してみたい方は、https://babeljs.io/repl/を利用ください
if (true) { var i = 1; } conosole.log(i); // 結果:1
ブロックスコープが有効であれば、結果は未定義エラーとなるはずですが、JavaScriptではブロックスコープの概念がないので、変数iはブロック外でも有効となり、結果は1です。そこで、ES6のlet命令を利用することでブロックスコープで有効な変数を宣言できます。
if (true) { let i = 1; } conosole.log(i); // 結果:エラー(i is not defined)
今度は、結果はエラーになります。let命令で宣言された変数はブロック外では無効になったためです。
let命令はスコープ内での変数の重複を認めていない(var命令は可)
ES6でもvar命令は宣言できますが、「スコープは最大限限定すべき」「重複チェックをコンパイラーに任せられる」などの理由から、基本はlet命令を優先して利用することになると思います。
「let命令はスコープ内での変数の重複を認めていない」と書きましたが、どういうことかというと
switch(i) { case 0: let value = 'i:0'; break; case 1: let value = 'i:1'; // 重複エラー break; }
case1の時にも同じ変数名を定義しているので、エラーになってしまいます。
即時関数は利用しない
即時関数は、関数ブロックで疑似的にスコープを形成し、グローバルスコープの汚染を防ぐテクニックです。
(function(){ // 関数の中身 })();
ES5までは当然の仕組みですが、ES6ではコード全体をブロックで括り、配下の変数をlet命令で宣言すれば、即時関数と同じ効果が得られるので、利用する必要がなくなります。
定数を宣言する ー const命令
const命令は、定数を宣言できます。PHPのfinalと同じです。
const data = 100; data = 150; // エラー
ただし、const命令の定数は正しくは「再代入できない」であって、「変更できない」ではない点に注意です。
const data = [1, 2, 3]; data[0] = 10; console.log(data); // 結果:[10,2,3]
配列は参照型なので、dataの参照先に再代入しているわけではなく、中身の要素だけを書き換えているため、constの制約にはかかりません。
const data = [1, 2, 3]; data = [10, 2, 3]; // エラー
配列そのもを変更した場合にはエラーになります。
文字列リテラルへの変数/改行の埋め込みを可能にする ー テンプレート文字列
テンプレート文字列を利用することでエスケープシーケンスで表現していた文字を、文字リテラルの中でそのまま表現できます。テンプレート文字列はクォート「'」「"」の代わりに、「`」(バッククォート)で括ります。
let str = `Hello World`; console.log(str); //Hello「改行」World
これまでであればエスケープシーケンスで表現していた改行文字を、文字列リテラルの中でそのまま表現できます。
また、${}の形式で、変数を文字列に埋め込むことも可能です。今までは、変数と文字は「+」演算子を使って連結する必要がありました。
let name = 'Tom'; console.log(`Hello,${name}!`); // Hello,Tom!
新たなデータ型Symbol
従来のNumber、String、Objectなどの型に加えて、新たにSymbolという型が追加されました。これは下手な説明より見た方がはやいです。
let hoge = Symbol('hoge'); let hoge2 = Symbol('hoge'); console.log(typeof hoge); // 結果:symbol console.log(hoge.toString()); // 結果:symbol(hoge) console.log(hoge === hoge2); // 結果:false
ここで注意するのは一点だけです。Symbolで生成したものは中身が同じであろうと、別物とみなされます。
また、シンボルでは文字列や数値への暗黙的な変換はできません。つまり、Symbolの型と文字列・数値の連結はできません。
ただし、boolean型、object型への変換は可能です。
これって実際どんな場面で活用するの?となると思います。
具体的な利用例をいくつか下記に挙げます。
①定数の値として利用する
var JAVASCRIPT = 0; var RUBY = 1; var PERL = 2; var PYTHON = 3;
一般的に、このような定数は、JAVASCRIPT、RUBY、PERLなどを識別するための定数であって、割り当てられた0、1、2...といった値には意味がありません。ただ、これらの定数を利用するコードでは、定数、数値を利用してもエラーにはなりません。
コードの可読性を考えれば「0」で比較するのは望ましい状態ではありませんし、そもそも「var HOGE = 0;」のような定数が現れた時に、同じ値の定数が混在してしまうのはバグが混入する元です(枠割が似ていれば尚更)
そこで定数の値としてSymbolを利用します。
const JAVASCRIPT = Symbol(); const RUBY = Symbol(); const PERL = Symbol(); const PYTHON = Symbol();
Symbol命令で生成されたシンボルは、同名であってもユニークになります。つまり、定数JAVASCRIPTと等しいのは定数JAVASCRIPTのみです。
②非公開なプロパティを定義する
たとえば以下は、MyClassクラスの中で、privateなSECRETプロパティを定義する例です。
// SECRETプロパティの名前でシンボルで準備 const SECRET = Symbol(); class Myclass { constructor(secret) { this.data1 = 1; this.data2 = 2; // SECRETプロパティに値を設定 this[SECRET] = secret; } // SECRETプロパティを利用したメソッド checkSecret(secret) { return this[SECRET] === secret; } } let c = new Myclass(12345); // メソッド経由ではSECRETプロパティにアクセスできる console.log(c.checkSecret(12345)); // true // SECRETプロパティへの直接アクセスは不可 console.log(c.secret); // undifined // オブジェクトのキー(プロパティ)を列挙 console.log(Object.keys(c)); // ["data1","data2"]; // オブジェクトのキー(プロパティ)を列挙 for (let k in c) { console.log(k); // data1、data2 } // オブジェクトをJSON文字列に変換 console.log(JSON.stringify(c)); // {"data":1,"data2":2}
SECRETプロパティの名前をシンボルとして準備し、「this[SECRET]=~」でプロパティとして定義
シンボルSECRETの値は他からは判別できませんので、SECRETプロパティに直接アクセスすることはできません。for...in命令による列挙、JSON.stringifyメソッドで生成されたJSON文字列にも、シンボルで生成されたプロパティは現れてこない点に注目してください。
ただし、完全に隠ぺいできるわけではなく、getOwnPropertySymbolsメソッドを利用すると、シンボルプロパティにアクセスすることは可能です。
let idsym = Object.getOwngetOwnPropertySymbols(c)[0]; console.log(c[idsym]);
配列/オブジェクトから個々の要素を抽出する ー 分割代入
分割代入(destrucruting assignment)は、配列/オブジェクトを分解し、その要素/プロパティを個々の変数に展開するための構文
let [hoge, foo] = [15, 21]; console.log(hoge); // 15
「...」演算子を利用することで、残りの要素をまとめて配列として取り出すこともできます。
let [hoge, foo, ...other] = [10, 20, 30, 40, 50]; console.log(hoge); // 10 console.log(other); // [30,40,50]
オブジェクトのプロパティに割り当てることもできます。
let {hoge, foo} = {hoge:'ほげ', foo:"ふ~"}; console.log(hoge); // ほげ
入れ子になったプロパティに割り当てる
let data = {hoge: 'ほげ', foo: {piyo: 'ぴよ', goo: 'ぐう'}}; let {hoge, foo, foo: {piyo, goo}} = data; console.log(hoge); // ほげ console.log(foo); // {"piyo":"ぴよ","goo":"ぐう"}
指定されたプロパティが存在しなかった場合の為に、デフォルト値を用意も可能です。
let {hoge = 'ほげ', foo} = {foo: 'ふ~'}; console.log(hoge); // ほげ
宣言のない代入
今までは、宣言と代入をまとめて行っていますが、当たり前なんですけど変数宣言と代入は別々に行えます。
let hoge, foo; [hoge, foo] = [15, 21];
ただし、オブジェクトの分割代入では、前後に()が必須です。なぜなら、左辺の{...}はブロックと見なされ、それ単体で文とすることができないからです。
let hoge, foo; ({hoge, foo} = {hoge:'ほげ', foo: 'ふ~'});
長くなってきたので、今回はここまで区切ります。
次回もES6について書いていきます。
(関数あたりのところを)
Ethereumについて学んでみる
Ethereum(イーサリアム)ってなに?
Etherreumはそもそも分散アプリケーションのためのプラットフォームです。(公式サイト)
分散アプリケーションと中央管理型
gitなどのバージョン管理システムでも、中央管理型と分離型とありますが、それと似たようなものです。
※中央管理型
具体例としては、インスタにアップロードした画像データはFacebook(開発元)が中央で一元管理することにより、サービスが成り立っています。これらのサービスを運営するにあたって、中央で管理するシステムが必須でした。
中央管理型の欠点として良く言われるのが、
・中央管理システムが何かしらの理由で機能しなくなったり、障害が発生した場合のデータ消失などのリスクが大きい
・私的企業が一手に個々のデータを握ることはプライバシーの観点から好ましくない
そこで、Ethereumは「ブロックチェーン」と呼ばれる技術を用いて、特別な管理者のいらないP2P※システム上で、様々なサービス(仮想通貨を扱ったりなど)を実現するためのプラットフォームを提供するものです。
※対等な関係にある端末間を相互に直接接続し、データを送受信する通信方式。Facebookのメッセなど
どういうことかというと、Facebookといったような、中央で管理する機関(企業)を必要とせずに同様のサービスを実現できるプラットフォームを提供します。
ブロックチェーンとは?
ブロックチェーンの技術を一言でいうと、「不正が難しい取引台帳」です。
ここでいう不正は、取引の改ざんや二重取引などです。
仮想通貨のビットコインを使って説明していきます。
※取引改ざん
※二重取引
二重取引のようにAさんが10コインを持っていると仮定して、Bさんに5コインを送金し、Cさんに2コイン送金したとします。
取引内容としては下記のようになり、取引を1つ1つ管理せずに、1つのまとまり(ブロック)として管理します。まとめ方は一定時間内に発生した取引毎です。
・取引:A→B
・取引:A→C
さらに、このブロックにハッシュを付与します。
※ハッシュ:メッセージを特定するための暗号化技術
・取引:A→B ハッシュ
・取引:A→C
一定時間を過ぎた後に新たな取引があった場合には新規にブロックを生成します。
では、BさんからCさんへ送金・CさんからAさんへ送金があったとします。
・取引:B→C ハッシュ
・取引:C→A
この時(新規ブロックを生成時)に、ひとつ前のブロックのハッシュも含めます
ただし、ハッシュはブロックごとに異なります(当たり前ですが)。
この関係により各ブロックは時系列でつながります。
・取引:A→B ハッシュ
・取引:A→C
↓
・取引:B→C ハッシュ
・取引:C→A
改ざんするには後続にあるブロックも改ざんしていく必要があります。
そうしないと整合性がとれないからです。
ブロックがどんどん鎖のようにつながっていく様子からブロックチェーンと名づけられました。
上記では「データ構造」について説明してきましたが、実はブロックチェーンにはもう一つ重要な仕組みがあります。それは「データの管理方法」です。
ブロックチェーンは、「データ構造」と「データの管理方法」で成り立ってます。
次回はデータの管理方法について見て行きます。
ファイルアップロードをドラッグ&ドロップでも可能にする
ファイルアップロードするには?
フロント側のHTMLもしくはPHPファイルにフォームの記述をします。
<form action="" method="post" enctype="multipart/form-data"> <input type="file" value="ファイルを選択" name="image"> <input type="value" value="送信"> </form>
画像を扱う場合にはenctype部分が必須になります。他にもinputタグにaccept属性を指定することにより、画像の形式をpng、jpgのみ受け付けるようにすることなどもできますが、今回は必要最低限で説明していきます。
次に画像ファイルを受け取る裏側を書いていきます。詳しくは説明しないんですが、1行目のif文で画像データが問題なく送られているかチェックします。問題がなければ4・5行目でアップロードしたファイル名と拡張子を取得します
ex)test.png→$filename=test、$extension=png
$tmp_pathでアップロード画像が仮で置かれているパスを取得します。
$file_dir_pathでアップロードした画像をどこに置くかを選択。今回は同階層のuploadフォルダに格納するようにする。
$uniq_nameは同じファイル名でアップロードした場合は上書きされて古い画像が消えてしまうのを避けるために格納する際にユニークな名前をつけて画像が上書きされるのを避けます。
入れ子になった最初のif文でPOSTでアップロードされたファイルか確認。次のif文でアップロードされたファイルを新しい位置に移動する。
移動できたら、パーミッション(アクセス権限)を設定。所有者は読み書きOK、それ以外は読み込みだけOK
if (!isset($_FILES['image']['error']) || !is_int($_FILES['image']['error'])){ echo "ファイルアップロードエラー"; } else { $file_name = $_FILES['image']['name']; $extension = pathinfo($file_name, PATHINFO_EXTENSION); //拡張子取得 $tmp_path = $_FILES['image']['tmp_name']; $file_dir_path = "upload/"; $uniq_name = date("YmdHis").md5(uniqid(microtime(),1)).session_id() . "." . $extension; if (is_uploaded_file($tmp_path)) { if(move_uploaded_file( $tmp_path, $file_dir_path . $uniq_name)) { chmod($file_dir_path . $uniq_name, 0644); } else { echo "Error:アップロードできませんでした。"; } } }
これで必要最低限のシステムはできました。
ファイルアップロードをドラッグ&ドロップでも可能にするには?
今回のゴールは下記のようなドラッグ&ドロップでもアップロードを可能にするシステムです。
簡単な話ではありますが、上記で作ったシステムとjsを使えば実装できます。
jQueryは使わずに生のjsで書いてみます。その前に、フロント部分の記述を少し変更します。
cssは各々で調整してください。
<form action="" method="post" enctype="multipart/form-data"> <div id="drag-drop-area"> <div class="drag-drop-inside"> <p class="drag-drop-info">ここにファイルをドロップ</p> <p>または</p> <input type="file" value="ファイルを選択" name="image"> <p class="drag-drop-buttons"><input id="fileInput" type="file" value="ファイルを選択" name="image"></p> <input type="value" value="送信"> </div> </div> </form>
fileAreaでドラッグ&ドロップの有効範囲を設定。fileInputにfile属性のinputタグを代入しておく。
dragoverイベントはドラッグしている要素がドロップ領域にある間に発生
dragleaveイベントはドラッグしている要素がドロップ領域から出たときに発生
dropイベントはドラッグしている要素がドロップ領域にドロップされたときに発生
その他にもドラッグ&ドロップのイベントはあります。もっと知りたい方は下記のリンクを参考にしてください。
https://app.codegrid.net/entry/dnd-api-1
files変数にドロップされた画像のデータ情報が入り、そのデータをfileInputに渡しているだけです。
これで完成です。
※ドロップした時点でアップロードしたい際には、changeイベントを使えば可能です
var fileArea = document.getElementById('drag-drop-area'); var fileInput = document.getElementById('fileInput'); fileArea.addEventListener('dragover', function(evt){ evt.preventDefault(); fileArea.classList.add('dragover'); }); fileArea.addEventListener('dragleave', function(evt){ evt.preventDefault(); fileArea.classList.remove('dragover'); }); fileArea.addEventListener('drop', function(evt){ evt.preventDefault(); fileArea.classList.remove('dragenter'); var files = evt.dataTransfer.files; fileInput.files = files; });
スマホRPGで例えてみるオブジェクト指向
そもそもオブジェクト指向って何?
オブジェクトとは、物体(スマホとか)を、属性(データ)と操作(メソッド)の集合として、定義して、コンピューターで扱えるようにしたもの。
このオブジェクトを組み合わせて、開発を行うことをオブジェクト指向プログラミングと呼びます。
何を言っているか分かりませんね。
オブジェクト指向の考え方
・クラス/インスタンス化/オブジェクト
・継承
クラス
たとえば、キャラ毎の詳細を見てみます。
これをレア度が「レジェンド」のクラスと呼びます。
レジェンドキャラの設計図、つまり「クラス」
インスタンス化
?の箇所を埋めないと使えるキャラとして成り立ちませんよね。
設計図=クラスの空白部分を埋めて実体化(使えるよう)することをインスタンス化といいます。
元になっている設計図=クラスさえあれば量産することが可能です。
※キャラ画像とステータスさえ変えれば、別物になる
↓
オブジェクトを組み合わせて、1つのソフトウェアを構築するのがオブジェクト指向!
ex)上のオブジェクトを組み合わせてパーティー作ったり
継承
オブジェクトの定義を定めた雛型を「クラス」と呼び、上位のクラスを性質を引き継いだ上で、他の性質を加えた新しいクラスを作成することを「継承」と呼ぶ。
ノーマルキャラ
↑スーパークラス(基本クラス)
レジェンドキャラ(ノーマルキャラにはないスキルが使えたりetc...)
カプセル化
データとそれを動かす(メソッド)操作を一体化して「オブジェクト」として定義し、オブジェクト内の細かい仕様や構造を外部から隠ぺいすること。
外部からは公開された手続きを利用することでしかデータを操作できないようにすることで、ここのオブジェクトの独立性が高まる。
※カプセル化を進めることによりオブジェクト内部の仕様変更が外部に影響しなくなり、ソフトウェアの保守性や開発効率が高まり、プログラムの部分的な再利用が容易になる。
HPのデータに直接減算するようなことはしない
ダメージを与えるメソッドを呼ぶことでHPを減らす処理を行う
ポリモーフィズム
「多様性」「多態性」「多様性」
オブジェクト指向プログラミング言語の持つ性質のひとつであり、同盟のメソッドや型などをオブジェクトの種類に応じて使い分けることができる性質のこと
オブジェクト指向プログラミングでは、関連するひとまとまりのデータとそれを操作する関数などの組み合わせ「オブジェクト」として定義していき、これを組み合わせてプログラムを記述していく。
継承を行う際に、上位のクラスの関数を新しい関数の定義で置き換えることを「オーバーライド」と呼び、クラスによって同一のメソッドで異なる処理が行われる性質をポリモーフィズムといいます。
※ポリモーフィズムの利点としては、メソッド名を統一することで名前を覚える必要がなくなることや、記述ミスを減らせることなどが挙げられます。
/*-----------------------------------
ex)攻撃のメソッドで考えてみる。
①敵のHPにダメージ与える
②敵の攻撃力にダメージを与える(弱体化)
-----------------------------------*/
同じ攻撃でも処理が異なる。
ポリモーフィズムという考え方を使わないと、同じ攻撃でも異なるメソッドを使わなくてはならない。
スキルと攻撃みたいに明確に分ける必要があれば、ポリモーフィズムを使わない方がいい。
JavaScriptで現在から数日~数週間前の日時を取得するには?
まず、JavaScriptで日時を扱うには?
js側で用意している組み込み関数のDateオブジェクトを使用します。
使用方法は
var 変数名 = new Date();
で、Dateオブジェクトを生成します。
今回、次のような日時を使ったグラフを作っていきます。
ステップ1
グラフを簡単に作成するjsのライブラリとしてchart.jsを使っていきます。
リンクをクリックしたら、下の画面に飛ぶので、Get Startedをクリック
ステップ3
今回使うファイルは、Chart.js-master→distフォルダ→Chart.min.jsのみを使います
Chart.min.jsを任意の場所に移動して置いてください。
上記ような操作が面倒な方はCDNも公開されているのでそちらを使っても大丈夫です
https://cdnjs.com/libraries/Chart.js
まず、HTMLファイルの雛型を用意して、グラフを描画するためのcanvasタグを書きつつ、chart.jsを読み込んでおきます
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <div style="width:600px;height: 450px;margin: 0 auto;"> <canvas id="canvas" height="450" width="600"></canvas> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.6/Chart.min.js"></script> <script> </script> </body> </html>
ステップ4
次に、scriptタグの中にchart.jsを使用するためのコードを書いてあげます
chart.jsの解説はしませんが、興味がある方は調べてみてください
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.6/Chart.min.js"></script> <script> var config = { type: 'line', data: { labels : ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], datasets: [{ label: "ページビュー数", fill: false, backgroundColor: "#3A7AC9", borderWidth: 2, borderColor: "rgba(2,63,138,0.8)", pointBorderColor: "#fff", pointBackgroundColor: "rgba(2,63,138,0.8)", pointBorderWidth: 2, pointHoverRadius: 5, pointHoverBackgroundColor: "#1D5191", pointHoverBorderColor: "#fff", pointHoverBorderWidth: 2, tension: 0, data: [1, 59, 90, 81, 56, 55, 40, 30, 10, 40, 48, 58] }, { label: "セッション", fill: false, backgroundColor: "#DB514E", borderWidth: 2, borderColor: "rgba(201,60,58,0.8)", pointBorderColor: "#fff", pointBackgroundColor: "rgba(201,60,58,0.8)", pointBorderWidth: 2, pointHoverRadius: 5, pointHoverBackgroundColor: "#9A1B19", pointHoverBorderColor: "#fff", pointHoverBorderWidth: 2, tension: 0, data: [28, 48, 40, 19, 96, 27, 100, 33, 63, 31, 64, 51] }] }, options: { responsive: true, scales: { xAxes: [{ display: true, stacked: false, gridLines: { display: false } }], yAxes: [{ display: true, ticks: { reverse: false, callback: function(value){ return value; } } }] } } }; // ▼上記のグラフを描画するための記述 window.onload = function() { var ctx = document.getElementById("canvas").getContext("2d"); window.myLine = new Chart(ctx, config); }; </script> </body> </html>
上記のコードを書いてあげてブラウザで見てみると、グラフが出来上がっていると思います
ステップ5
赤枠で囲ってる部分を現在の月と日時に書き換えてあげたいと思います
var config = { の上にscriptを記述していきます
2つDateオブジェクトを生成してあげます
空の配列を用意して、for文で今日の日付から1週間前の経過日数を出して、配列に挿入しています。
<script> var date = new Date(); var before = new Date(); var week = []; for(var i = 0;i < 7;i++) { week.unshift(date.getDate() - i); } var config = { </script>
ステップ6
出力結果が下記になります。今日が21日なので、1週間分がしっかりと取れてますね
ただ、これだと問題があります。今回は月をまたいでいないので、問題が無いように見えますが、例えば、6/1の前日の日付を取ろうとした場合に本来であれば、5/31にならなければならないのに、5/0になります。試してみましょう
for文で回す値を大きくしてあげます。
<script> var date = new Date(); var before = new Date(); var week = []; for(var i = 0;i < 25;i++) { week.unshift(date.getDate() - i); } var config = { </script>
ステップ7
そうすると意図した結果にならないのが分かると思います。これを避けるためには、setDate()メソッドを使用します。
簡単に説明すると、指定した値が1から31の範囲外の場合は、Dateオブジェクトの日付の情報の更新を試みます。例えば0が値として指定された場合、前月の最終日に設定されます。
<script> var date = new Date(); var before = new Date(); var week = []; for(var i = 0;i < 25;i++) { before.setDate(date.getDate() - i); week.unshift(before.getDate()); } var config = { </script>
ステップ8
先月の日時がしっかりとれてますね。
そしたら、配列に月も挿入してあげつつ、chart.jsのlabelsの中身をweekに変更してあげれば完成です
getMonth()に+1している理由はgetMonthメソッドでは1月が0、2月が1といった具合に、1つ下にずれた数字が取得されるからです
※for文の値も直しておきます
<script> var date = new Date(); var before = new Date(); var week = []; for(var i = 0;i < 7;i++) { before.setDate(date.getDate() - i); week.unshift(before.getMonth() + 1 + '月' + before.getDate() + '日'); } var config = { type: 'line', data: { labels : week, </script>