Kuzunoha-NEのブログ

プログラミングなどの勉強をしてます

【JavaScript】プリントデバッグあれこれ

葛の葉です。

JavaScriptでプリントデバッグあれこれのことを書きます。

とりあえずみて

class Piyo {
    constructor(num, str){
        this.num = num * 3;
        this.str = str;
        this.test = ["唐揚げ", "焼き芋", "醤油指し"]
    }
}

const Test1 = "hogehoge";
const Test2 = {message: "this is message", fruits: "orange", memos:["abc", "def", "ghi"]};
const Test3 = new Piyo(4, "zircon");

console.log('---log---');
console.log(Test1);
console.log(Test2);
console.log(Test3);

// 変数名と共に表示させたい場合
console.log('---VAR = ${VAR}---');
console.log(`Test1 = ${Test1}`);
console.log(`Test2 = ${Test2}`);
console.log(`Test3 = ${Test3}`);

console.log('---JSON.stringify---');
console.log(`Test1 = ${JSON.stringify(Test1)}`);
console.log(`Test2 = ${JSON.stringify(Test2)}`);
console.log(`Test3 = ${JSON.stringify(Test3)}`);

console.log('---{}---');
console.log({Test1});
console.log({Test2});
console.log({Test3});

結果は以下のようになる。

---log---
hogehoge
{ message: 'this is message',
  fruits: 'orange',
  memos: [ 'abc', 'def', 'ghi' ] }
Piyo { num: 12, str: 'zircon', test: [ '唐揚げ', '焼き芋', '醤油指し' ] }
---VAR = ${VAR}---
Test1 = hogehoge
Test2 = [object Object]
Test3 = [object Object]
---JSON.stringify---
Test1 = "hogehoge"
Test2 = {"message":"this is message","fruits":"orange","memos":["abc","def","ghi"]}
Test3 = {"num":12,"str":"zircon","test":["唐揚げ","焼き芋","醤油指し"]}
---{}---
{ Test1: 'hogehoge' }
{ Test2: 
   { message: 'this is message',
     fruits: 'orange',
     memos: [ 'abc', 'def', 'ghi' ] } }
{ Test3: Piyo { num: 12, str: 'zircon', test: [ '唐揚げ', '焼き芋', '醤油指し' ] } }

文字列の中にObjectや配列を入れるとobject Objectという表示までしかしない。そうったObjectや配列はJSON.stringify()を使ってJSON型にすると表示されるということをやっていました。そして、今日、たまたまツイッター見てたら{}を使って表示すると便利ということがわかりました。{}を使うと最初のKeyが変数名になります。

で、そのツイッターのツイートをロストしてしまった。もうしわけない

追記

.[]を使ってプロパティやメンバーを見たい場合{}は使えないようだ。

const Test3 = new Piyo(4, "zircon");

console.log({Test3.test})
/* 
console.log({Test3.test})
                  ^

SyntaxError: Unexpected token .
    at createScript (vm.js:80:10)
    at Object.runInThisContext (vm.js:139:10)
    at Module._compile (module.js:616:28)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10)
    at startup (bootstrap_node.js:188:16)
    at bootstrap_node.js:609:3
*/

そのような場合はJSON.stringify()を使ってあげる。

console.log(`Test3.test = ${JSON.stringify(Test3.test)}`)
// Test3.test = ["唐揚げ","焼き芋","醤油指し"]

【JavaScript】SocketIOで通信しよう

こんばんは葛の葉です。

今回はSocketIOでJavaScriptPythonを通信してみます。

JavaScript側(サーバー側)

前回のWebAPIの記事の際はJavaScriptがクライアント側でしたが、今回はサーバー側に回ります。 SocketIOで通信をする場合、Httpでの通信を確立しないと行けないようです。デフォルトのHttpサーバーとなるhttpモジュールとルーティング機能を持つexpressモジュールと主役のsocket.ioモジュールが必要になります。

npm install --save express
npm install --save socketio

ソースコードは以下のようになります。

# soc.ts

import * as http from "http";

import * as express from "express";
import * as socketIo from "socket.io";

const app = express();
const server = http.createServer(app);
const sio = socketIo(server);

app.get('/', function(req, res){
    res.send('Hello World!')
});

sio.on('connection', (socket)=>{
    socket.on('kuzunoha', function(message) {
        console.log(`GetMessage:message=${message}`);
    });
});

server.listen('3000', function(){
    console.log('hoge')
})

tsc soc.tsとして、コンパイルします。その後、node soc.jsを実施します。

$ node soc.js
hoge
(ここでターミナルは止まっている)

f:id:Kuzunoha-NE:20190905205427p:plain
Hello Worldと表示される

上記の設定を行ったのでhttp://127.0.0.1/にSocketIOでアクセスするとWebSocketを使った通信ができるようになります。Httpでの通信はあくまで疎通のためのものであり、実際にデータのやりとりをする際にはサーバー側とクライアント側にそれぞれ同じイベント名をもたせる必要があります。今回はkuzunohaというイベントをサーバー側に用意しました。kuzunohaイベントを受け取るとmessageというデータを受け取り、そのメッセージをサーバー側のconsole.log()で表示するというものになっています。

Python側(クライアント側)

今回のPythonはクライアント側になります。

使用するモジュールはpython-socketioです。

pypi.org

いくつかPython向けのSocketIO用モジュールはあったのですが、比較的最近更新されているもので、メジャーバージョンが1以上なのはこれくらいしかありませんでした。(調査量は大したもんじゃないですけど)

pip install python-socketio

# soc.py
import socketio

sio = socketio.Client()

def on_connect():
    sio.emit('kuzunoha', 'Korega Kusanagino Kenda')

sio.on('connect', on_connect)
sio.connect('http://127.0.0.1:3000/')
sio.wait()

sio.emitにてkuzunohaというイベント名でon_connectという関数を記載しています。python soc.py実行するとJavaScript側でのコンソールに以下のような変化が行われます。

$ node soc.js
hoge
GetMessage:message=Korega Kusanagino Kenda

これでPythonの情報がNode.jsに送ることができました。

【Node.js】WebAPIを受け取ってみる。

こんばんは葛の葉です。

さて、簡単なWebAPIを作成し、Javascriptで値を受け取るようにしてみます。

pythonでWebAPIをつくる

# app.py
from flask import Flask, request, jsonify
app = Flask(__name__)

@app.route('/')
def index():
    data = {
        'status': 'ok',
        'message': 'Hello'
    }
    return jsonify(data)

@app.route('/test')
def test():
    if request.args.get('name'):
        name = request.args.get('name')
    else:
        name = 'Nanashi'
    data = {
        'status': 'ok',
        'message': f'Hello! {name}! How are you?'
    }
    return jsonify(data)

flaskについては当ブログでも結構扱ってきたから、あんまり説明しなくてもよいかな、と思っています。jsonifyは引数で受け取った辞書型をJSONに変換し、さらにHttpレスポンスのContent-Typeをapplication/jsonにしてくれるものです。上記のPythonプログラムをapp.pyとして作成し、flask runとコマンドを叩いて実行すると以下のようになります。

$ flask run
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

別のターミナルを開いてcurlを使ってレスポンスを受け取ってみます。

// ルート直下へGetリクエスト
$ curl http://127.0.0.1:5000
{"message":"Hello","status":"ok"}

// テストルートへGetリクエストで、パラメータなし
$ curl -G "http://127.0.0.1:5000/test"
{"message":"Hello! Nanashi! How are you?","status":"ok"}

// テストルートへGetリクエストで、パラメータあり
$ curl -G "http://127.0.0.1:5000/test?name=kuzunoha"
{"message":"Hello! kuzunoha! How are you?","status":"ok"}

$ curl -G "http://127.0.0.1:5000/test?name=albatross"
{"message":"Hello! albatross! How are you?","status":"ok"}

$ curl -G "http://127.0.0.1:5000/test?name="
{"message":"Hello! Nanashi! How are you?","status":"ok"}

とまぁこんな感じで簡素なWebAPIが作れました。

Nodeのaxiosというモジュールをつかう

Node.JsにはaxiosというHttpリクエストを投げて受け取るモジュールがあります。ご多分の通り、Node.jsは非同期I/Oなので、普通にリクエストを投げるとレスポンスが帰ってくる前に次のコードが実行されちゃいます。そこでPromiseというオブジェクトを用いることがあるわけですが、このaxiosはHttpRequestをPromiseベースで実行してくれるモジュールになります。

github.com

使い方はこんな感じ。(typescriptです)

// webapp.ts
import axios from "axios";

axios.get('http://localhost:5000/')
.then(function(response) {
    console.log(response.data);
}).catch(function(error){
    console.log(error)
})

axios.get(URL)は見ての通りgetメソッドでURLを取得しにいっています。さて、.thenというPromiseオブジェクトっぽいのが出てきました。

axios.get(URL).then(コールバック関数1).catch(コールバック関数2)

axios.get()が正常に終了したとき.then内のコールバック関数1が実行されます。このときの引数をresposeとしていますが、axiosが受け取ってきたHttpResponseになります。axios.get()が失敗した際はそのerrorを.catch内のコールバック関数2に渡し、実行します。

コンパイルすると以下の感じ。

"use strict";
exports.__esModule = true;
var axios_1 = require("axios");
axios_1["default"].get('http://localhost:5000/')
    .then(function (response) {
    console.log(response.data);
})["catch"](function (error) {
    console.log(error);
});

コンパイルされたwebapp.jsをnodeで実行すると以下のようになります。

$ node webapp.js
{ message: 'Hello', status: 'ok' }

webapp.tsの3行目をaxios.get('http://localhost:4000/')にしてコンパイルして実行すると、そのポートにWepアプリがなければエラーが返ります。

$ node webapp.js
{ Error: connect ECONNREFUSED 127.0.0.1:4000
    at Object._errnoException (util.js:1022:11)
    at _exceptionWithHostPort (util.js:1044:20)
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1198:14)
  code: 'ECONNREFUSED',
  errno: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 4000,
  config: 
   { url: 'http://localhost:4000/',
     method: 'get',
(以下省略)

getメソッドのパラメータを渡すことも可能です。

// webapp.ts
import axios from "axios";

axios.get('http://localhost:5000/test', {
    params:{
        name: "kuzunoha"
    }
})
.then(function(response) {
    console.log(response.data);
}).catch(function(error){
    console.log(error)
})

Nodeで実行すれば以下のように。

$ tsc webapp.ts 
$ node webapp.js
{ message: 'Hello! kuzunoha! How are you?', status: 'ok' }

expressで表示してみる

webapp.tsにexpressで表示してあげればもっとわかりやすくなる。

// webapp.ts
import axios from "axios";
import * as express from "express";

let app = express();

app.get('/', (req, res)=>{
    axios.get('http://localhost:5000/test', {
        params:{
            name: "kuzunoha"
        }
    })
    .then(function(response) {
        const ret = response.data;
        res.send(ret.message);
    }).catch(function(error){
        console.log(error)
    })        
})

app.listen('3000', function(){
    console.log('listen port:3000')
})

ExpressとはWebフレームワークになります。Expressのインスタンスappとしています。

expressjs.com

tsc webapp.ts -> node webapp.jsとして実行してみてください。

$ tsc webapp.ts 
$ node webapp.js
listen port:3000

http:127.0.0.1:3000/にアクセスすると以下のようになります。

f:id:Kuzunoha-NE:20190829211837p:plain
Hello! kuzunoha! How are you?と表示される

app.get(文字列, コールバック関数)は文字列でアクセスするとコールバック関数が実行されるというものです。アクセスが正常であればコールバック関数に受け取ったHttpRequestとこれからクライアントに返すResponseが引数に渡されます。http://127.0.0.1:3000にアクセスしたタイミングで、axios.get()以下が実行されます。

axiospythonflaskアプリケーションに飛んで、情報を取得してきます。その取得してきた情報をhttp://127.0.0.1:3000にアクセスしたクライアントに返しています。

とこのようにすることで、Javascriptは表示に専門、Pythonは内部処理に専門、と分けることができるのです。

【雑記】アクアパーク品川でみたイメージングライトというもの

こんばんは葛の葉です。

勉強用に使っていたPC(Ubuntu18.04)がご臨終となってしまいした。わたしのコマンドの操作ミスで壊したわけですが、それはまぁおいおい話すとして…先日、アクアパーク品川に行ってきました。

このアクアパーク品川にあった「イメージングライト」というものが不思議なもので面白かったので紹介します。

biz.maxell.com

これはプロジェクターのようなのですが、ただの壁に対してかなりきれいに描写しています。

また、センサーがついているようで、指でタッチすると操作することもできます。基本的にはPCの画面出力とマウスの操作と同じようです。

これが業務用から家庭用になって安くなってくれないかなぁとか思ったりします。スマートディスプレイとかにしたら面白いかも、なんて考えておりました。

【Javascript】querySelectorAllでDomをとるかねぇ

こんばんは葛の葉です。

今回はdocument.querySelectorAll()をつかってDomをとります。

下記のようにhtml内でpiyopiyoクラスの要素が複数あります。

<!DOCTYPE html>
<html lang="ja">
<body>
    <div class="hogehoge">
        <div class="piyopiyo">
            <p>大腸菌がしゃっくりする</p>
        </div>
        <div class="piyopiyo">
            <b>ほんど</b>
        </div>
    </div>
    <script src="./test.js"></script>
</body>
</html>

f:id:Kuzunoha-NE:20190815222355p:plain
上記のHTMLのスクリーンショット

const piyopiyos = document.querySelectorAll('.hogehoge .piyopiyo');
console.log(piyopiyos);

document.querySelectorAll(selector)のうち、selectorCSSのクラス指定と方法は同じになります。

取得したDomをプリントしてみる。

piyopiyos変数に代入しましたのでconsoel.log()を使って出力してみます。

NodeList(2) [div.piyopiyo, div.piyopiyo]
0: div.piyopiyo
1: div.piyopiyo
length: 2
__proto__: NodeList
entries: ƒ entries()
forEach: ƒ forEach()
item: ƒ item()
keys: ƒ keys()
length: (...)
values: ƒ values()
constructor: ƒ NodeList()
Symbol(Symbol.iterator): ƒ values()
Symbol(Symbol.toStringTag): "NodeList"
get length: ƒ length()
__proto__: Object

querySelectorAllで返却されるものはNodeListというObjectになります。

NodeListイテレータでも配列でも無いようです。今回、.hogehoge .piyopiyoのクラスに相当する要素は2つあります。以下の2つのHtmlElementが格納されています。

<div class="piyopiyo">
    <p>大腸菌がしゃっくりする</p>
</div>
<div class="piyopiyo">
    <b>ほんど</b>
</div>

piyopiyos[0]が最初の要素になります。

const piyopiyos = document.querySelectorAll('.hogehoge .piyopiyo');
console.log(piyopiyos[0].innerText) // 大腸菌がしゃっくりする
console.log(piyopiyos[1].innerText) // ほんど

forEachを使って要素全てに適応

NodeListはArrayの.forEachと似ているメソッドを持っているようです。コールバック関数を使います。引数はNodeListの内の要素を1つずつ代入していき、すべての要素が一巡するまでループします。pythonで言えばfor piypiyo in piyopiyosといった感じです。

const piyopiyos = document.querySelectorAll('.hogehoge .piyopiyo');

piyopiyos.forEach(function (piyopiyo) {
    console.log(piyopiyo.innerText);
})

// 大腸菌がしゃっくりする
// ほんど

piyopiyoはHtmlElementなのでクラスや属性を追加することも可能です。

const piyopiyos = document.querySelectorAll('.hogehoge .piyopiyo');

piyopiyos.forEach(function (piyopiyo) {
    piyopiyo.setAttribute("align", "center");
})

piyopiyoの要素を持つものをすべてセンタリングします。

f:id:Kuzunoha-NE:20190815230443p:plain
センタリングのHTML

【JavaScript】.addElementListenerを使った関数の呼び出し

葛の葉です。

Javascirptを現在、勉強しております。

さて、今回はhtmlの要素がclickされたときにjavascriptの関数を呼び出す方法を記載します。

まずボタンを作る

以下のようにbuttonを配置します。

<!--test.html-->
<!DOCTYPE html>
<html lang="ja">
    <header>
        <title>TestTitle</title>
    </header>
    <body>
        <button type="button" id="testButton">こんにちは</button>
    </body>
</html>

f:id:Kuzunoha-NE:20190807230037g:plain
押しても反応しないボタン

onclickという手

<script>内にaleart()関数を呼び出す関数を作る。

<script>
    function onAleart(){
        alert('アラートだよ!')
    }
</script>

testButtononclick属性を追加し、値をjavascriptの関数とします。

<button type="button" id="testButton" onclick="onAleart()">こんにちは</button>

最終的にはこのようになります。

<!--test.html-->
<!DOCTYPE html>
<html lang="ja">
    <header>
        <title>TestTitle</title>
    </header>
    <body>
        <button type="button" id="testButton" onclick="onAleart()">こんにちは</button>
    </body>
    <script>
        function onAleart(){
            alert('アラートだよ!')
        }
    </script>
</html>

f:id:Kuzunoha-NE:20190807231133g:plain
Aleartが表示される

.addElementListenerを使う

表題の方法になります。onclickの記載は不要のため削除します。

<button type="button" id="testButton">こんにちは</button>

次に、javascriptを以下のように記載します。

<script>
    function onAleart(){
        alert('アラートだよ!')
    }
    const testButton = document.getElementById('testButton');
    testButton.addEventListener('click', ()=>{
        onAleart();
    })
</script>

document.getElementById('testButton');はhtml内にある全ての要素の内、id属性の値がtestButtonであるものをHtmlElementとして取得します。

HtmlElementに.addEventListenerというメソッドが存在し、第一引数に'click'という文字を渡しています。これはその要素がクリックされた時に発生するということを表しています。

クリック以外にも条件(Event)があって、以下にそれが載っています。

developer.mozilla.org

そしてコールバック関数としてonAleartが呼び出されています。

<!--test.html-->
<!DOCTYPE html>
<html lang="ja">
    <header>
        <title>TestTitle</title>
    </header>
    <body>
        <button type="button" id="testButton">こんにちは</button>
    </body>
    <script>
        function onAleart(){
            alert('アラートだよ!')
        }
        const testButton = document.getElementById('testButton');
        testButton.addEventListener('click', ()=>{
            onAleart();
        })
    </script>
</html>

f:id:Kuzunoha-NE:20190807232623g:plain
onclickと同じ

クリック以外でもいけるか

testButton.addEventListener('keypress', ()=>{
    onAleart();
})

keypressにしたらいろんなキーボードのキーに反応した

【TypeScript】抽象クラスの作成

こんばんは、葛の葉です。

今回は抽象クラスを作ってみます。

前回のインターフェイスをパクります。

kuzunoha-ne.hateblo.jp

動物という抽象クラスを作る

classを作る際の雛形を作る。共通部分を作る部分としてはインターフェイスと似ているようだけど、内部的な処理を書く場合は抽象クラス、外部へ値を渡すものをインターフェイスにするのが基本的らしい。

// Abstracts.ts
export abstract class Animal {
    constructor(
        public foot: number,
        public length: number,
        public tail: boolean,
        public say: string,
    ) { }
    public growth(): void {
        this.foot = this.foot * 3;
        this.length = this.length * 2;
    };
} 

インターフェイスを作る。後ほど、抽象クラスがインターフェイスを使用する。

export interface AnimalAction {
    // 吠えてもらう
    bark(): string;
};

抽象クラスにインターフェイスを突っ込む

// Abstracts.ts
import { AnimalAction } from "./Interfaces"

export abstract class Animal implements AnimalAction {
    constructor(
        public foot: number,
        public length: number,
        public tail: boolean,
        private say: string,
    ) { }
    public growth(): void {
        this.foot = this.foot * 3;
        this.length = this.length * 2;
    };
    public bark(): string {
        return `${this.say}と吠えるんだぜ`
    }
}

抽象クラスはあくまで抽象クラスなので、これをそのまま使うことは出来ない。VSCodeなら警告が出る。コンパイルも通らない。

// sandbox.ts
import { Animal } from "./Abstracts"

let animal = new Animal(3, 5, true, "hello")

f:id:Kuzunoha-NE:20190731210708j:plain
VSCodeでは抽象クラスのインスタンスは作成できません。ts(2511)と表示される

[kuzunoha@:21:03:42:~]$ tsc sandbox.ts 
sandbox.ts:4:14 - error TS2511: Cannot create an instance of an abstract class.

4 let animal = new Animal(3, 5, true, "hello")
               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Found 1 error.

クラスを作る。

import { Animal } from "./Abstracts"

export class Dog extends Animal {
    public bark(): string {
        return `${this.say}だワン!!!`
    }
}

オブジェクトを作る。

// sandbox.ts
import { Dog } from "./Dog"

// 日本語の変数も使えるのだよ
let ポメラニアン = new Dog(3, 5, true, "がるる");

console.log(ポメラニアン.bark())
[kuzunoha@:21:23:49:~]$ node sandbox.js 
がるるだワン!!!