20201227のJavaScriptに関する記事は25件です。

JavaScript DOM操作による要素へのアクセス

class属性、セレクタで要素にアクセスする

class="weather"

index.html
    <!DOCTYPE html>
    <html lang="ja">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>

    <body>
        <h1>JavaScript Practice</h1>
        <dl>
            <dt>今日の天気</dt>
            <dd class="weather">晴れ</dd>
        </dl>
        <script src="index.js"></script>
    </body>

    </html>

1,class属性やセレクタで要素を取得する

document.querySelector("クラス名");

index.js
    // querySelectorでweatherのクラスがついている要素を取得
    let element1 = document.querySelector(".weather");
    console.log(element1);

    // querySelectorでdlセレクタ要素を取得
    let element2 = document.querySelector("dl");
    console.log(element2);

    // DOMのツリー構造を踏まえて指定が可能
    let element3 = document.querySelector("dl dt");
    console.log(element3);

2,コンソール表示結果

3,主な要素取得メソッド

index.js
    // 主な要素取得メソッド
    document.getElementById("引数");           //id名
    document.getElementsByClassName("引数");   //class名
    document.getElementsByTagName("引数");     //要素名
    document.getElementsByName("引数");        //name属性
    document.querySelector("引数");            //セレクタ
    document.querySelectorAll("引数");         //セレクタ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript DOM操作による要素へのアクセス

class属性、セレクタで要素にアクセスする

class="weather"

index.html
    <!DOCTYPE html>
    <html lang="ja">

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>

    <body>
        <h1>JavaScript Practice</h1>
        <dl>
            <dt>今日の天気</dt>
            <dd class="weather">晴れ</dd>
        </dl>
        <script src="index.js"></script>
    </body>

    </html>

1,class属性やセレクタで要素を取得する

document.querySelector("クラス名");

index.js
    // querySelectorでweatherのクラスがついている要素を取得
    let element1 = document.querySelector(".weather");
    console.log(element1);

    // querySelectorでdlセレクタ要素を取得
    let element2 = document.querySelector("dl");
    console.log(element2);

    // DOMのツリー構造を踏まえて指定が可能
    let element3 = document.querySelector("dl dt");
    console.log(element3);

2,コンソール表示結果

3,主な要素取得メソッド

index.js
    // 主な要素取得メソッド
    document.getElementById("引数");           //id名
    document.getElementsByClassName("引数");   //class名
    document.getElementsByTagName("引数");     //要素名
    document.getElementsByName("引数");        //name属性
    document.querySelector("引数");            //セレクタ
    document.querySelectorAll("引数");         //セレクタ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascriptの++iはi++より早いのか?

C言語やjavascriptなどのプログラミング言語にはインクリメント演算子がある。

インクリメント演算子は変数の数値を1加算する動作する。

i++ // 後置インクリメント
++i // 前置インクリメント

インクリメント演算子には前置インクリメントと後置インクリメントがあり、違いは評価値が違うこと。
前置インクリメントは1加算したあとの値、後置インクリメントは1加算する前の値を評価値として返す。

C言語では++iはi++より早い

C言語だと++iはi++よりパフォーマンスが良い。以下の記事が参考になる。

http://daria-sieben.hatenablog.com/entry/2015/08/21/212605

この記事によると++iはi++に比べて一時的な変数を使用しないため動作が早いようだ。

jsでも検証してみた

検証コード↓

const TIMES = 1000000;
let tmp = 0;
function test1() {
    for (let i = 0; i < TIMES ; ++i) {
        ++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;
        ++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;        
        ++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;        
        ++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;
        ++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;
        ++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;
        ++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;        
        ++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;        
        ++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;
        ++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;++tmp;
    }
}

function test2() {
    for (let i = 0; i < TIMES ; ++i) {
        tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;
        tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;
        tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;
        tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;
        tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;
        tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;
        tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;
        tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;
        tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;
        tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;tmp++;
    }
}

performance.mark('test1Begin');
test1();
performance.mark('test1End');

performance.mark('test2Begin');
test2();
performance.mark('test2End');

performance.measure(
    'test1',
    'test1Begin',
    'test1End'
);

performance.measure(
    'test2',
    'test2Begin',
    'test2End'
);

console.log(performance.getEntriesByName('test1')[0]);
console.log(performance.getEntriesByName('test2')[0]);

結果

++i: 169ms
i++: 169ms

ほぼ同じだったのでjavascriptでは前置後置のパフォーマンスの違いを意識しなくて良いようだ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Self-organization mapをJavaScriptで実装した

はじめに

色々な機械学習処理をブラウザ上で試せるサイトを作った」中で実装したモデルの解説の十二回目です。

今回はSelf-organization map(自己組織化マップ)の実装について解説します。

デモはこちらから。(TaskをClusteringにして、ModelのSelf-organization mapを選択)
実際のコードはsom.jsにあります。

なお、可視化部分については一切触れません。

概説

こちらのQiitaの記事に全て書いてあります。

これの潜在空間を多次元に対応させつつ、JavaScriptで実装しました。

潜在空間の多次元化は初期化が面倒なだけで、他の処理は難しくありません。
また、その初期化はグリッドを作成しているだけです。Pythonであればnumpy.meshgridzipあたりを使えば簡単だと思います。

コード

SOM

class SOM {
    // https://qiita.com/tohru-iwasaki/items/e51864269767ccc07254
    constructor(input_size, output_size, resolution = 20) {
        this.in_size = input_size;
        this.out_size = output_size;

        this.resolution = resolution;
        this._sigma0 = 1;

        this._init_method = 'PCA';

        this._epoch = 0;
        this._z = [];
        let z0 = Array(output_size).fill(0);
        do {
            this._z.push([].concat(z0));
            for (let i = output_size - 1; i >= 0; i--) {
                z0[i]++;
                if (z0[i] < this.resolution) break;
                z0[i] = 0;
            }
        } while (z0.reduce((a, v) => a + v, 0) > 0);
        this._y = null;
    }

    get _sigma() {
        return Math.max(this._sigma0 * (1 - this._epoch / 20), 0.2)
    }

    _z_distance(i, j) {
        let d = 0;
        for (let k = 0; k < this.out_size; k++) {
            d += (this._z[i][k] - this._z[j][k]) ** 2;
        }
        return d;
    }

    _find_near_idx(x) {
        const n = x.length;
        const dim = this.in_size;
        const near_idx = [];
        for (let i = 0; i < n; i++) {
            let min_d = Infinity;
            let min_idx = -1;
            for (let k = 0; k < this._y.length; k++) {
                let d = 0;
                for (let j = 0; j < dim; j++) {
                    d += (x[i][j] - this._y[k][j]) ** 2
                }
                if (d < min_d) {
                    min_d = d;
                    min_idx = k;
                }
            }
            near_idx.push(min_idx);
        }
        return near_idx;
    }

    fit(data) {
        const x = data;
        const n = x.length;
        const dim = this.in_size;
        if (!this._y) {
            if (this._init_method === 'random') {
                this._y = Matrix.randn(this._z.length, dim).toArray();
            } else if (this._init_method === 'PCA') {
                const x0 = new Matrix(n, dim, data)
                const xd = x0.cov();
                const [l, pca] = xd.eigen();
                const sl = l.reduce((s, v) => s + v);
                const expl = new Matrix(1, l.length, l.map(v => Math.sqrt(v)));
                expl.repeat(this._z.length, 0)
                expl.mult(x0.select(0, 0, this._z.length, l.length))
                this._y = expl.dot(pca.t).toArray()
            }
        }
        const near_idx = this._find_near_idx(x);

        const r = [];
        for (let i = 0; i < n; i++) {
            r[i] = [];
            const z = this._z[near_idx[i]];
            for (let k = 0; k < this._z.length; k++) {
                let d = this._z_distance(near_idx[i], k);
                r[i][k] = Math.exp(-d / (2 * this._sigma ** 2));
            }
        }

        for (let k = 0; k < this._y.length; k++) {
            let num = Array(dim).fill(0), den = 0;
            for (let i = 0; i < n; i++) {
                den += r[i][k]
                for (let j = 0; j < dim; j++) {
                    num[j] += r[i][k] * x[i][j];
                }
            }
            for (let j = 0; j < dim; j++) {
                this._y[k][j] = num[j] / den;
            }
        }
        this._epoch++;
    }

    predict(x) {
        const near_idx = this._find_near_idx(x);
        return near_idx.map(i => this._z[i])
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript  DOM操作でHTML要素を書き換える

<dd>タグにidが設定されているのでid名を指定して対象のHTML要素を書き換えます!

index.html
 <!DOCTYPE html>
 <html lang="ja">

 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Document</title>
 </head>

 <body>
     <h1>JavaScript Practice</h1>
     <dl>
         <dt>今日の天気</dt>
         <dd id="weather">晴れ</dd>
     </dl>
     <script src="index.js"></script>
 </body>

 </html>

1,要素を取得し書き換える

document.getElementById("id名");

index.js
// DOM操作でHTMLを書き換える
let element = document.getElementById("weather");

// innnerHTMLは記述したテキストもHTMLとして認識
element.innerHTML="<span>曇り</span>";

// textContentは記述したテキストを文字列として認識
element.textContent="<span>曇り</span>";
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript DOM操作でHTML要素を書き換える

<dd>タグにidが設定されているのでid名を指定して対象のHTML要素を書き換えます!

index.html
 <!DOCTYPE html>
 <html lang="ja">

 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Document</title>
 </head>

 <body>
     <h1>JavaScript Practice</h1>
     <dl>
         <dt>今日の天気</dt>
         <dd id="weather">晴れ</dd>
     </dl>
     <script src="index.js"></script>
 </body>

 </html>

1,要素を取得し書き換える

document.getElementById("id名");

index.js
// DOM操作でHTMLを書き換える
let element = document.getElementById("weather");

// innnerHTMLは記述したテキストもHTMLとして認識
element.innerHTML="<span>曇り</span>";

// textContentは記述したテキストを文字列として認識
element.textContent="<span>曇り</span>";
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

js.DOMの基本

ブラウザは、htmlを読み込むと内部的にDOM(Document Object Model)と呼ばれるデータ構造が作られて、その内容に応じてページが描画がされる、という仕組みになっている。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Railsアプリ】販売手数料をJavaScriptで計算しようとしたら出てくる「NaN」を解決できたので、備忘録として残しておきます。

ミス内容

スクリーンショット 2020-12-27 19.59.06.png
入力された金額の販売手数料(10%)を、JavaScriptを使って計算しようという実装を行っていた時に突如現れた「NaN」。
最初のコードは以下の通り。

function calc (){
  // id="item-priceの要素を取得。
  const itemPrice = document.getElementById("item-price");
  // 値が入力された時にイベントを発火。
  itemPrice.addEventListener('input', function(){
    const Tax = 0.1;
    const tax_fee = itemPrice * Tax;
    const profit = itemPrice - tax_fee;
    const taxArea = document.getElementById("add-tax-price");
    const profitArea = document.getElementById("profit");
    taxArea.innerHTML = tax_fee;
    profitArea.innerHTML = profit;
  })
}

window.addEventListener('load', calc)

原因

NaN = Not-a-Number(数字ではない)
計算しようとしている値が数字になっていないよ、と伝えているようです。

上記のコードだと、
getElementByIdで取得した「要素」と、
Tax=0.1という「数値」をかけ算しようとしている。

つまり、要素の値を取得した上で、
「数値」と「数値」をかけ算しなければならないのです><

解決法

itemPrice.valueで、要素の「値(=数値)」を取得しましょう。

function calc (){
  // id="item-priceの要素を取得。
  const itemPrice = document.getElementById("item-price");
  // 値が入力された時にイベントを発火。
  itemPrice.addEventListener('input', function(){
    // 下記を追記して、要素の値を取得。
    const itemPriceValue = itemPrice.value
    const Tax = 0.1;
    const tax_fee = itemPriceValue * Tax;
    const profit = itemPriceValue - tax_fee;
    const taxArea = document.getElementById("add-tax-price");
    const profitArea = document.getElementById("profit");
    taxArea.innerHTML = tax_fee;
    profitArea.innerHTML = profit;
  })
}

window.addEventListener('load', calc)

これで「NaN」が消え、計算機能を実装することができました!

スクリーンショット 2020-12-27 20.21.42.png

おわりに

最初、「NaN」ってナンだよ、、、と思いましたが、要素と数値はかけ算できないという初歩的なミスでした。

このミスを解説してくれたメンターさんが仰った『JavaScriptはHTMLをほじくりまわす言語だ』の一言がなぜか胸に刺さりました^^

プログラミングは面白い。もっと使いこなしたいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】 JSONをオブジェクトに変換 parseメソッド

JSON.parseメソッドとは?

JSON形式で書かれた文字列をJavaScriptのJSONオブジェクトに変換するメソッドです。
変換した場合は、JavaScriptの中でJSONのデータを自由に扱えるようになります。

書き方

変数 = JSON.parse(変換するJSON [, 関数])

実装例

const json = '{"firstName":"太郎","lastName":"山田"}';

const obj = JSON.parse(json); // =>JSONをJavaScriptのオブジェクトに変換

console.log(obj.firstName); // =>太郎

console.log(obj.lastName); // =>山田

parseメソッドの引数に関数を使用する

以下はキーがfirstNameのとき、値をたろうで返し、それ以外はそのまま返します。
指定したキー(firstName)の値のみが変更されています。

const json = '{"firstName":"太郎","lastName":"山田"}';

const a = JSON.parse(json,(key,value)=>{
    if (key=="firstName") {
        return "たろう" ;
    }
    return value ;
} ) ;

console.log(a); // =>{firstName: "たろう", lastName: "山田"}

参照

JavaScript | MDN JSON.parse()

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

リボルバーリストの作成

円形のリストを表示し、スクロールと連動して回転するピストルのリボルバーのような
アニメーションをするリストの作成

リボルバーリスト.gif

デモページ

https://syogo-suganoya.github.io/revolver/

ソース

https://github.com/Syogo-Suganoya/revolver

説明

リストの作成

回転用のリストの作成をします

CSSで子要素の表示を維持しつつ、要素を回転させる方法
https://www.nxworld.net/css-element-rotate.html

こちらの記事を参考に画面作成をします

今回はスクロールとともに回転させる動きを実装するので、下記をはじめとするのanimation部分は不要です

ul {
  animation: rotate 30s linear infinite;
}

スクロールアニメーション

CSS と jQuery で作るスクロールにあわせて回転するロゴ
https://hyper-text.org/archives/2013/07/css_spin_logo.shtm
こちらの記事を参考にスクロールアニメーションを実装します
jsで指定してるセレクタを調整します

変更前
$(window).scroll(function(){
 rotate($(".logo"), $(window).scrollTop()*0.2);
})
変更後
const selList = ['twitter', 'facebook', 'pinterest', 'instagram', 'google'];
const listCnt = selList.length;

$(window).scroll(function () {

 // 1回転する角度
 var angle = $(window).scrollTop();
 // 180pxスクロールするごとに72度回転する
 angle = Math.round(angle / 180) * (360 / listCnt);

 rotate($("ul"), angle);
 rotate($("ul > li"), angle * -1);
})

選択中リストの動的更新

<p class="select-menu">
 twitter
</p>

選択中のメニューを表示している箇所の動的書き換えをjsで実装します

// 選択中リストの動的更新
roleCnt = angle / (360 / listCnt);

// 回転数がリストの要素以上なら要素数分マイナスする
while(true) {
 if (roleCnt < listCnt) {
  break;
 }
 roleCnt -= listCnt;
}

var selText = selList[roleCnt];
$(".select-menu").text(selText);

これで完成です

備考

元ネタはドラマ「リッチマン、プアウーマン」に出てくる「パーソナルファイル」という架空のWebアプリのUIがこのリボルバーリストのような挙動をしていたので試してみましたw

いつかパーソナルファイルが開発されて転入届などがネットで完結できるようになってくれることを期待しています

【公式】リッチマン、プアウーマン
https://fod.fujitv.co.jp/s/genre/drama/ser4389/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React] JavaScriptのクラスとReact

JavaScriptのクラスとReact

※ 個人用の覚え書きでございます。

JavaScriptのクラス(継承)

script.js
class Class_1{
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  info() {
    console.log(`名前は${this.name}です`);
    console.log(`${this.age}歳です`);
  }
}

class Class_2 extends Class_1 {
}

const class_2 = new Class_2("山田",20);

class_2.info();

React(ボタンをクリックすると表示が切り替わる)

app.js
import React from 'react';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {name: '山田'};
  }

  handleClick(f_name){
    this.setState({name: f_name});
  }

  render() {
    return (
      <div>
        <h1>こんにちは、{this.state.name}さん!</h1>

        <button onClick={
          () => {this.handleClick('山田')}}>
          山田
        </button>

        <button onClick={
          () => {this.handleClick('田中')}}>
          田中
        </button>
      </div>
    );
  }
}

export default App;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue 2.x】Composition API の readonly と shallowReadonly で気を付けること

TL;DR

  • @vue/composition-api v1.0.0-beta.21 から readonly(), shallowReadonly() が提供されるようになった
  • readonly() は引数で受け取ったオブジェクトの型を DeepReadonly 化して返しているだけである
  • shallowReadonly() は Vue 3 のものとほぼ同じように使える

readonly()shallowReadonly() とは?

readonly() は Vue 3 ではリリース時から提供されているリアクティビティ API で、リアクティブな状態を受け取ってリードオンリーなリアクティブな状態を返すことを目的とする関数です。


readonly() のコード例1
// Vue 3
import { reactive, readonly } from 'vue';

const original = reactive({
  countA: 0,
  foo: {
    countB: 0,
  },
});
const copy = readonly(original);

original.countA++;
copy.countA++; // Set operation on key "countA" failed: target is readonly.
original.foo.countB++;
copy.foo.countB++; // Set operation on key "countB" failed: target is readonly.

console.log(original.countA); // 1
console.log(copy.countA); // 1
console.log(original.foo.countB); // 1
console.log(copy.foo.countB); // 1


shallowReadonly() も Vue 3 ではリリース時から提供されているリアクティビティ API で、readonly() との違いはリードオンリーになる範囲がオブジェクトの第1階層の値に限定される点です。


shallowReadonly() のコード例1
// Vue 3
import { reactive, shallowReadonly } from 'vue';

const original = reactive({
  countA: 0,
  foo: {
    countB: 0,
  },
});
const copy = shallowReadonly(original);

original.countA++;
copy.countA++; // Set operation on key "countA" failed: target is readonly.
original.foo.countB++;
copy.foo.countB++; // ブラウザのコンソール上に警告が出ない

console.log(original.countA); // 1
console.log(copy.countA); // 1
console.log(original.foo.countB); // 2
console.log(copy.foo.countB); // 2


ユースケース

readonly() を用いることで provide() や props などによって子コンポーネントに状態を渡す際、子コンポーネント側からの状態の変更を防ぐことができます。
このことは Vue 3 ドキュメントのリアクティブの基礎 - readonly でリアクティブオブジェクトの変更を防ぐにも記載があります:

例えば、provide されたリアクティブオブジェクトがある場合、それが注入された場所からの変更は防ぎたいことがあります。そうするために、元のオブジェクトに対する読み取り専用のプロキシを作成します

Vue 2 の readonly()shallowReadonly() について

Vue 2 では Composition API は @vue/composition-api プラグインとして提供されていますが、@vue/composition-api v1.0.0-beta.21 から Vue 2 向けに readonly(), shallowReadonly() が提供されるようになりました。

readonly() は Vue 3 のものとは全く異なる

@vue/composition-api での readonly() の実装2を確認すると、引数で受け取ったオブジェクトの型を DeepReadonly 化して返しているだけであることがわかります。

そのため TypeScript による型チェックをすり抜けてしまった場合3、Vue 3 の readonly() とは異なり状態の変更ができてしまいます:

// Vue 2
import { reactive, readonly } from '@vue/composition-api';

const original = reactive({
  countA: 0,
  foo: {
    countB: 0,
  },
});
const copy = readonly(original);

original.countA++;
copy.countA++; // ブラウザのコンソール上に警告が出ない
original.foo.countB++;
copy.foo.countB++; // ブラウザのコンソール上に警告が出ない

console.log(original.countA); // 2
console.log(copy.countA); // 2
console.log(original.foo.countB); // 2
console.log(copy.foo.countB); // 2

shallowReadonly() は Vue 3 のものとほぼ同じように使える

shallowReadonly() は Vue 3 のものと同様にリードオンリーになる範囲がオブジェクトの第1階層の値に限定されますが、Vue 3 のものとほぼ同じように使うことができます。

Vue 2 では場合によっては readonly() よりも shallowReadonly() を使った方が良いことがあります。
例えば、各プロパティの値がすべてプリミティブ型であるようなオブジェクトには shallowReadonly() を用いることで readonly() よりも確実に状態の変更を防ぐことができます:

// Vue 2
import { reactive, readonly, shallowReadonly } from '@vue/composition-api';

// 各プロパティの値がすべてプリミティブ型(string | number | boolean)である
const original = reactive({
  text1: '',
  text2: '',
  text3: '',
  number1: 0,
  number2: 0,
});

// Not good
const copy1 = readonly(original);

// Better
const copy2 = shallowReadonly(original);


Vue 2 の shallowReadonly() にはできないことについて

@vue/composition-api での shallowReadonly() の実装4を確認すると、引数で受け取ったオブジェクトの(第1階層の)キーでループを回し5、別途定義したオブジェクトに対して同じキーを持つプロパティを定義する6実装となっていることがわかります。


そのため shallowReadonly(obj) 実行時に obj が持っているプロパティの値に限ってリードオンリーとすることができます。
// Vue 2
import { reactive, shallowReadonly } from '@vue/composition-api';

const original = reactive({
  countA: 100,
});
const copy = shallowReadonly(original);

original.countB = 200;

console.log(copy.countA); // 100
console.log(copy.countB); // undefined
// Vue 3
import { reactive, shallowReadonly } from 'vue';

const original = reactive({
  countA: 100,
});
const copy = shallowReadonly(original);

original.countB = 200;

console.log(copy.countA); // 100
console.log(copy.countB); // 200



  1. 2020/12/27 時点でリリースされていない Vue 3 の IE11 互換ビルドを利用すると Vue 3 の readonly(), shallowReadonly() の挙動が本記事で想定しているものと異なるものとなる可能性があります 

  2. @vue/composition-api v1.0.0-beta.22 での readonly() の実装: https://github.com/vuejs/composition-api/blob/v1.0.0-beta.22/src/reactivity/readonly.ts#L42-L46 

  3. 特に Vue では <template> 上の v-model など型チェックが効きにくい場所があったりします 

  4. @vue/composition-api v1.0.0-beta.22 での shallowReadonly() の実装: https://github.com/vuejs/composition-api/blob/v1.0.0-beta.22/src/reactivity/readonly.ts#L48-L97 

  5. for (const key of Object.keys(obj)) { ... } の部分 

  6. Object.defineProperty(readonlyObj, key, { ... }) の部分 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

QuickJSでお手軽ESP32+Javascript実行環境

@binzume さんが投稿をされて、ESP32でもJavascriptが動くことを知りました。( ESP32 と QuickJS で小さなJavaScript実行環境を作る )
しかもES2020に対応しているなんて素敵です。

以下のことができることを目指します。

  • console.logがSerial出力されること(これはすでに@binzumeさんが実装されています)
  • LCD、Wire(I2C)、GPIOをJavascriptから触れるようにします。
  • JavascriptのソースコードをWebから取得します。

3番目が今回のモチベーションで、ESP32起動時にネットワークから取得することで、PlatformIOやArduino IDEから毎度コンパイル・書き込みを行う必要がなくなります。動作確認が終わったら、ROMに埋め込みます。

毎度の通りGitHubに上げておきます。

poruruba/Esp32QuickJS_sample
 https://github.com/poruruba/Esp32QuickJS_sample

QuickJSの用意

@binzumeさんがすでにGitHubにライブラリ化していただいていますので、心配することはありません。

binzume/esp32quickjs
 https://github.com/binzume/esp32quickjs

オリジナルのQuickJSは以下にあります。

bellard/quickjs
 https://github.com/bellard/quickjs
 https://bellard.org/quickjs/

PlatformIOプロジェクトの作成

それではESP32用にJavascript実行環境を作成します。
まずは、PlatformIOのプロジェクトを作成します。

名前はなんでもよいのですが、とりあえず「Esp32QuickJS」とでもしておきます。
Boardには、今回M5Stick-Cを使いました。

image.png

次に、platformio.iniを編集します。
そうです、@binzumeさんが用意していただいているライブラリを指定します。また、同様にtanakamasayukiさんの「ESP32 Lite Pack Library」も使わせていただきました!

platform.ini
[env:m5stick-c]
platform = espressif32
board = m5stick-c
framework = arduino
upload_port = COM6
monitor_port = COM6
lib_deps = 
  tanakamasayuki/ESP32 Lite Pack Library@^1.3.2
  https://github.com/binzume/esp32quickjs.git#v0.0.1
board_build.partitions = no_ota.csv
board_build.embed_txtfiles =
  src/default.js
;  src/main.js

次に、srcフォルダにdefault.jsを置きます。これは、WebからJavascriptのダウンロードに失敗したときに実行するもので、バカ除けです。

src/default.js
console.log("start");
setInterval(() =>{
  console.log('http get failed');
}, 1000);

ESP32用のQuickJSの呼び出し

まずは、mainとなるmain.cppです。

src/main.cpp
#include <WiFi.h>
#include "M5Lite.h"
#include <HTTPClient.h>
#include "quickjs_esp32.h"

//#define LOCAL_JAVASCRIPT // ROMに埋め込む場合にはコメントアウトを外す

const char *wifi_ssid = "【WiFiアクセスポイントのSSID】";
const char *wifi_password = "【WiFiアクセスポイントのパスワード】";
const char *jscode_url = "【Javascriptの取得先URL】";

// see platformio.ini
#ifdef LOCAL_JAVASCRIPT
extern const char jscode_main[] asm("_binary_src_main_js_start");
#else
extern const char jscode_default[] asm("_binary_src_default_js_start");

#define JSCODE_BUFFER_SIZE  10000
char jscode[JSCODE_BUFFER_SIZE];
unsigned long jscode_len = sizeof(jscode);

WiFiClient espClient;
WiFiClientSecure espClientSecure;

void wifi_connect(const char *ssid, const char *password);
long doHttpGet(String url, uint8_t *p_buffer, unsigned long *p_len);
#endif

ESP32QuickJS qjs;

void setup() {
  M5Lite.begin();
  Serial.begin(9600);

  M5Lite.Lcd.setRotation(3);
  M5Lite.Lcd.fillScreen(BLACK);
  M5Lite.Lcd.setTextColor(WHITE, BLACK);
  M5Lite.Lcd.println("[M5StickC]");

#ifdef LOCAL_JAVASCRIPT
  qjs.begin();
  qjs.exec(jscode_main);
#else
  wifi_connect(wifi_ssid, wifi_password);

  long ret;
  ret = doHttpGet(jscode_url, (uint8_t*)jscode, &jscode_len);
  if( ret == 0 ){
    jscode[jscode_len] = '\0';

    qjs.begin();
    qjs.exec(jscode);
  }else{
    qjs.begin();
    qjs.exec(jscode_default);
  }
#endif
}

void loop() {
  M5Lite.update();
  qjs.loop(); // For timer, async, etc.

  if (M5Lite.BtnB.wasPressed()) {
    Serial.println("BtnB pressed");
    esp_restart();
  }
}

#ifndef LOCAL_JAVASCRIPT
void wifi_connect(const char *ssid, const char *password){
  Serial.println("");
  Serial.print("WiFi Connenting");
  M5Lite.Lcd.print("Connecting");

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    M5Lite.Lcd.print(".");
    delay(1000);
  }
  Serial.println("");
  Serial.print("Connected : ");
  Serial.println(WiFi.localIP());

  M5Lite.Lcd.fillScreen(BLACK);
  M5Lite.Lcd.setCursor(0, 0);
  M5Lite.Lcd.println(WiFi.localIP());
}

long doHttpGet(String url, uint8_t *p_buffer, unsigned long *p_len){
  HTTPClient http;

  Serial.print("[HTTP] GET begin...\n");
  // configure traged server and url
  if( url.startsWith("https") )
    http.begin(espClientSecure, url); //HTTPS
  else
    http.begin(espClient, url); //HTTP

  Serial.print("[HTTP] GET...\n");
  // start connection and send HTTP header
  int httpCode = http.GET();
  unsigned long index = 0;

  // httpCode will be negative on error
  if(httpCode > 0) {
      // HTTP header has been send and Server response header has been handled
      Serial.printf("[HTTP] GET... code: %d\n", httpCode);

      // file found at server
      if(httpCode == HTTP_CODE_OK) {
        // get tcp stream
        WiFiClient * stream = http.getStreamPtr();

        // get lenght of document (is -1 when Server sends no Content-Length header)
        int len = http.getSize();
        Serial.printf("[HTTP] Content-Length=%d\n", len);
        if( len != -1 && len > *p_len ){
          Serial.printf("[HTTP] buffer size over\n");
          http.end();
          return -1;
        }

        // read all data from server
        while(http.connected() && (len > 0 || len == -1)) {
            // get available data size
            size_t size = stream->available();

            if(size > 0) {
                // read up to 128 byte
                if( (index + size ) > *p_len){
                  Serial.printf("[HTTP] buffer size over\n");
                  http.end();
                  return -1;
                }
                int c = stream->readBytes(&p_buffer[index], size);

                index += c;
                if(len > 0) {
                    len -= c;
                }
            }
            delay(1);
        }
      }
  } else {
    http.end();
    Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
    return -1;
  }

  http.end();
  *p_len = index;

  return 0;
}
#endif

LOCAL_JAVASCRIPT がDefineされているかどうかで変わります。
定義されていない場合、Wifiに接続し、指定された【Javascriptの取得先URL】からHTTP GetでJavascriptをダウンロードしそれを実行しています。
WiFiアクセスポイントは以下で指定します。
【WiFiアクセスポイントのSSID】
【WiFiアクセスポイントのパスワード】

Javascriptの実行は以下の部分です。

src/main.cpp
    qjs.begin();
    qjs.exec(jscode);

実行エンジンは以下で指定しています。実行エンジンの実装は後ほど説明します。

src/main.cpp
#include "quickjs_esp32.h"
ESP32QuickJS qjs;

Javascriptの動作の確認ができたら、WiFiから取得するのではなくROMに埋め込みます。
その場合は、LOCAL_JAVASCRIPTをDefine定義して、以下にファイルを配置します。

src/main.js

platform.iniで上記ファイルを定義し、以下のようにしてCソースから取り出せます。

extern const char jscode_main[] asm("_binary_src_main_js_start");

ちなみに、すぐにWeb上のJavascriptを再ロードできるように、M5StickCのBボタン(右側面にあるボタン)を押せば、リブートがかかるようにしています。

ESP32用のQuickJSの実装

Javascript実行エンジンは、ESP32に特化してカスタマイズしており、ヘッダファイルinclude/quickjs_esp32.hに記述しています。
※というより、ほぼ @binzume さんのオリジナルです。
ちなみに、QuickJS本体は、@binzumeさんのライブラリを参照しておりquickjs.hにあります。

ソースコードが長いので、詳細はGitHubをご参照ください。

なんかだらだらと長いですが、JavascriptからLCDやI2CやGPIOをたたくためのラッパーを実装しています。
コマンド名は、デバイスタイプごとにサポートする名前が異なります。
整理する意味でざっと上げてみました。

デバイス名 コマンド名
gpio pinMode
^ digitalWrite
^ digitalRead
^ analogRead
wire begin
^ requestFrom
^ beginTransmission
^ endTransmission
^ write
^ available
^ read
wire1 begin
^ requestFrom
^ beginTransmission
^ endTransmission
^ write
^ available
^ read
lcd setRotation
^ setTextColor
^ setTextSize
^ setCursor
^ setBrightness
^ drawPixel
^ drawLine
^ print
^ println
^ fillScreen
^ getWidth
^ getHeight
^ getDepth

Javascriptを実行

それでは、Javascriptを実行してみます。
1秒ごとにLEDをついたり消したりするLチカアプリです。
GPIOの操作には以下のgpioを使います。

import * as gpio from "gpio";

例えばこんな感じです。

main.js
import * as esp32 from "esp32";
import * as gpio from "gpio";
import * as lcd from "lcd";

console.log("start");
gpio.pinMode(10, gpio.OUTPUT);

var led = false;
gpio.digitalWrite(10, led ? gpio.LOW : gpio.HIGH);
lcd.println("Hello World");
lcd.setTextSize(2);

setInterval(async () =>{
try{
  console.log('interval');
led = !led;
  gpio.digitalWrite(10, led ? gpio.LOW : gpio.HIGH);
  }catch(error){
      console.log(error);
  }
}, 1000);

これを、src/main.cpp で指定した【Javascriptの取得先URL】に配備します。
それでは、PlatformIOからコンパイル・書き込みを実施し、M5StickCを起動させてみましょう。
WiFiアクセスポイントに接続後、LCDにHello Worldと表示され、1秒ごとにLEDが付いたり消えたりしたのではないでしょうか。

(応用)ENV Sensorで温度を計測

Groveで接続可能なセンサーユニットを使います。

M5Stack用環境センサユニット
 https://www.switch-science.com/catalog/5690/
(おうっ、販売終了しているではないか。。。)

I2Cで接続します。
I2Cの操作には、lcdを使います。

import * as lcd from “lcd”;

こんな感じです。

main.js
import * as esp32 from "esp32";
import * as gpio from "gpio";
import * as wire from "wire";
import * as lcd from "lcd";

console.log("start");
gpio.pinMode(10, gpio.OUTPUT);

var led = false;
gpio.digitalWrite(10, led ? gpio.LOW : gpio.HIGH);
lcd.println("Hello World");
lcd.setTextSize(2);

class DHT12{
  constructor(wire, scale = 1, id = 0x5c){
    this.CELSIUS = 1;
    this.KELVIN = 2;
    this.FAHRENHEIT = 3;

    this.wire = wire;
    this.scale = scale;
    this.address = id;
  }

  async read(){
    this.wire.beginTransmission(this.address);
    var ret = this.wire.write(0);
    if( ret != 1 )
      throw 'failed';
    var ret = this.wire.endTransmission();
    if( ret != 0 )
      throw 'failed';

    var ret = this.wire.requestFrom(this.address, 5);
    if( ret != 5 )
      throw 'failed';

    var datos = this.wire.read(5);
    await this.sleep_async(50);

    var ret = this.wire.available();
    if( ret != 0 )
      throw 'failed';

    if (datos[4] != (datos[0] + datos[1] + datos[2] + datos[3]) )
      throw 'datos error';

    this.datos = datos;
  }

  async readTemperature(scale){
    await this.read();
    if( scale == undefined )
      scale = this.scale;

    var resultado = 0.0;
    switch(scale) {
      case this.CELSIUS:
        resultado = this.datos[2] + this.datos[3] / 10.0;
        break;
      case this.FAHRENHEIT:
        resultado= (this.datos[2] + this.datos[3] / 10.0) * 1.8 + 32.0;
        break;
      case this.KELVIN:
        resultado= (this.datos[2] + this.datos[3] / 10.0) + 273.15;
        break;
    };
    return resultado;
  }

  async readHumidity(){
    await this.read();

    var resultado = (this.datos[0] + this.datos[1] / 10.0);
    return resultado;
  }

  async sleep_async(msec){
    return new Promise(resolve =>{
      setTimeout(resolve, msec);
    });
  }
}

wire.begin();
var dht12 = new DHT12(wire);
lcd.println("DHT12 start");

setInterval(async () =>{
    try{
      console.log('interval');
      led = !led;
      gpio.digitalWrite(10, led ? gpio.LOW : gpio.HIGH);

      var temp = await dht12.readTemperature();
      console.log(temp);
  }catch(error){
    console.log(error);
  }

}, 1000);

うまくいけば、SerialコンソールとLCDに温度が表示されているかと思います。

終わりに

QuickJSを作ってくれたbellardさんに感謝ですし、見つけてくれた@binzumeさんにも感謝です!

HTTP Getによるファイルダウンロードは以下を参考にしています。
 ESP32でバイナリファイルのダウンロード・アップロード

もし、Javascript(Node.js)上で、ブレークポイントを入れたりとデバッグしたい場合は以下もご検討ください。DHT12のライブラリはこちらで作成しました。
 M5StickCの書き換えが面倒だったので、Node.jsでArduinoっぽくしてみた

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[PlayCanvas] Rigidbodyを持ったTemplateをinstantiateする時の注意点

はじめに

PlayCanvasに Template という機能が実装されています。
これによりEntityやコンポーネントなどを組み合わせたものをアセットとして保存できるようになり、別シーンでも同じ構成物をいちいち作る必要がありません。
またTemplateをインスタンス化することもできるので、シーンにベースEntityを置いておく必要がなくなりました。

まずはインスタンス化

まずは手短にインスタンス化を行います。

Sample
var Generator = pc.createScript('generator');

Generator.attributes.add('tmpl', {type: 'asset', assetType: 'template'});

Generator.prototype.initialize = function() {
    if( this.tmpl ){
        // インスタンス化
        var instance = this.tmpl.resource.instantiate();
        // インスタンスを子Entityに設定
        this.app.root.addChild( instance );
    }
};

インスタンス化と子Entity化はワンセットです。
子Entity化を忘れると表示もされず、メモリの彼方へと消えます。(そして自動開放される)

これであとは座標を変えたりして調整すればいいのですが、そこで注意が必要です。

Rigidbodyを持ったTemplateをインスタンス化

コリジョンを持ったEntityをTemplate化することも勿論あると思います。
先程と同じ手順でインスタンス化し{X:0, Y: 10, Z: 0}の座標に設置して、自由落下させようとしたとします。

Sample
var Generator = pc.createScript('generator');

Generator.attributes.add('tmpl', {type: 'asset', assetType: 'template'});

Generator.prototype.initialize = function() {
    if( this.tmpl ){
        var instance = this.tmpl.resource.instantiate();
        this.app.root.addChild( instance );
        instance.setLocalPosition( 0, 10, 0 );
    }
};

ザックリと実装するならばこんな感じでしょうが、これは正常に動作しません。
これを実行すると、インスタンス化したEntityは指定した座標に設置されず、更に他のコリジョンを持つEntityと衝突すると下記のエラーが出ます。

Uncaught RuntimeError: memory access out of bounds

コンソールを開いて確認するとAmmoが警告を吐いてるので、Rigidbodyを持ったTemplateをインスタンス化直後に座標系を弄ると駄目なようです。(無論、回転も駄目)

対処

ひとまず正常に動作する方法を探した結果、以下の3つの方法が動作するのを確認しました。

Rigidbodyをインスタンス化後にaddする

Templateを作成する時にRigidbodyを外しておき、インスタンス化時にaddComponentでRigidbodyを追加する方法。
細かいパラメータ(反発係数など)を後からセットする手間を考えると現実的じゃない。

Sample
Generator.prototype.initialize = function() {
    if( this.tmpl ){
        var instance = this.tmpl.resource.instantiate();
        this.app.root.addChild( instance );
        instance.setLocalPosition( 0, 10, 0 );
        instance.addComponent('rigidbody');
    }
};

RigidbodyをDisable状態でTemplateにする

Template作成時に、Rigidbodyの状態をDisableにしてからTemplateにする方法。
インスタンス化時にEnableにしてあげればよい。

Sample
Generator.prototype.initialize = function() {
    if( this.tmpl ){
        var instance = this.tmpl.resource.instantiate();
        this.app.root.addChild( instance );
        instance.setLocalPosition( 0, 10, 0 );
        if( instance.rigidbody ){
            instance.rigidbody.enabled = true;
        }
    }
};

rigidbody.teleport()を使用する

そもRigidbodyの機能に依存する方法。
座標だけを変える必要があるならば、この方法が採用できる。

Sample
Generator.prototype.initialize = function() {
    if( this.tmpl ){
        var instance = this.tmpl.resource.instantiate();
        this.app.root.addChild( instance );
        if( instance.rigidbody ){
            instance.rigidbody.teleport(0, 10, 0 );
        }
    }
};

おわりに

こんなん書いてどうするんだと思うかもしれませんが、これを上手く書くとプロジェクト自体を落とせます。
進捗が正常に更新される前に落とされたら最悪プロジェクト自体が死ぬ可能性があるので、共有を…といった感じ。

外部リンク

公式ブログ(日本語訳版)
ユーザーマニュアル - Templates
チュートリアル

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascriptの配列とオブジェクトの分割代入とは

こんにちは!
プログラミング初心者のstraw_engineです。
何も知らないことだらけでとりあえず内定先の方からおすすめしていただいたJavaScript本格入門という本のアウトプットをしようかと思います。

分割代入とは?

分割代入(配列)

分割代入についてここでは記述します。
分割代入とは配列、もしくはオブジェクトを分解して配下の要素/プロパティを個々の変数に分解するための構文です。
ES2015以前であれば配列から値を取り出すために、一つ一つの要素にアクセスする必要があったそうです。
下記みたいな感じです。

var data = [1,2,3,4,5,6];
var x0 = data[0];
var x1 = data[1];
//要素の数だけ書く必要があった。

しかし、ES2015以降はとても便利になって、分割代入によってこのめんどくさい作業が一行で済むようになりました。

let data = [1,2,3,4,5,6];
let [x0,x1,x,2,...others] = data;
console.log(x0); //結果:1
console.log(x1); //結果:2
console.log(others); //結果:[3,4,5,6]

分割代入の際には代入先の変数も[ ]で囲む必要があります。
また、「...」演算子を使用することで、変数に分解されなかった要素を配列で列挙することが可能です。

分割代入(オブジェクト)

次にオブジェクトの分割代入について記述します。
Javascriptでは配列と同様にオブジェクトも分割代入を利用できます。
下記のように使用します。

let book = { title: 'JavaScript入門書', publish:' straw_engine出版社', price: 2500 };
let { price, title, memo = 'なし' } = book;

console.log(title); //結果:JavaScript入門書
console.log(price); //結果:2500
console.log(memo); //結果:なし

ポイントなのがオブジェクトの場合には名前で一つ一つの変数に分解するということです。
上記の結果でわかるように順番ではなく、名前で分解されており、対応する変数がないpublishはスキップされています。
また、「変数名=デフォルト値」でデフォルト値を設定しておくこともできます。
memoのデフォルト値を「なし」に設定しているので「なし」という結果が返ってきています。

・入れ子構造になったオブジェクトの分解

ただ、オブジェクトは入れ子構造になっていることもあります。
そして、入れ子構造になっていても分割代入は利用できます。
入れ子構造になっているオブジェクトを分解するには入れ子の関係がわかるように代入先の変数も{...}で囲みます。

let book = { title: 'JavaScript入門書', publish:' straw_engine出版社', price: 2500, other: { memo: 'あいうえお', publisher: 'straw_engine' } };
let { title, other, other: { memo } } = book;

console.log(title);//結果:JavaScript入門書
console.log(other);//結果:{memo: "あいうえお", publisher: "straw_engine"}
console.log(memo);//結果:あいうえお

otherだけ記述した場合には、otherプロパティの中身が代入されますが、入れ子構造を記述することでmemoプロパティを代入しています。

・異なる名前の変数に代入する

先ほどオブジェクトの場合には対応する名前で変数に代入すると述べましたが、「変数名: 別名」でプロパティとは異なる名前の変数に値を割り当てることもできます。

let book = { title: 'JavaScript入門書', publish:' straw_engine出版社', price: 2500};
let { title: name publish: company} = book;

console.log(name); //結果:JacaScript入門書
console.log(company); //結果:straw_engine出版社

上記ではtitleとpublishのプロパティを変数nameとcompanyにそれぞれ名前を変えて代入しています。
また、上記では変数の宣言と代入を1文でしていますが、宣言と代入を切り離すことも可能です。

let price, title, memo; //変数の宣言
({ price, title, memo = 'なし' }); //変数の代入

まとめ

今回はJavaScript本格入門のアウトプットをしました。
しばらく、この本に沿ってアウトプットをするかと思います。
この本にはもっと詳細にJavaScriptの仕様等について書かれています。
気になった方はぜひ買って確認してみてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

$(window).on('load')関数がIEだと動かない理由と対処法

問題点

jQueryにて以下のようなコードを書くと\$(window).on('load')の中身が動作しない場合がある。

$(function(){
  $(window).on('load', function(){
  });
});

\$(function()内に\$(window).on('load')関数を記述すると\$(window).on('load')の中身が動作しなくなってしまうのだ。
これは以下のように修正すると動くようになる。

$(function(){
});

$(window).on('load', function(){
});

関数の実行タイミングについて

なぜ上のような記述でエラーが出てしまうのか、それはJSの実行タイミングが関係してくる。
まず上に出てきた2つの関数の実行タイミングについておさらいする。

\$(function()
HTML(DOM)の読み込みが終わったら関数の中の処理を実行する。
\$(window).on('load')
画像などページ内にある全てのリソースの読み込みが終わったら関数の中の処理を実行する。

つまり実行される順番としては以下のようになる。
1. HTML(DOM)の読み込みが終わる
2. \$(function()内の処理を実行
3. ページ内にある全てのリソースの読み込みが終わる
4. \$(window).on('load')内の処理を実行

原因

前述より今回の不具合に関してはDOMの読み込みが終わり、\$(function()内の処理が実行されたタイミングでは画像などの読み込みが終わっておらず、\$(window).on('load')内の処理が実行されなかったことにより起こっていると考えられる。
これを解消するには最初に記述した通りに、それぞれの関数を分けて記述し、ちゃんと実行順序を示してあげることで解決する。

$(function(){
});

$(window).on('load', function(){
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

同期処理、Promise、Async/Awaitで相互にエラーハンドリングする一覧

同期処理と非同期処理の間でエラーを渡す

エラー処理の引き渡しの資料はQiitaでもTipsとしていっぱい転がっている。しかし可能性として有りうる同期処理・Promise・Async/Await相互のエラー受け渡しについて言及したものは少ない。そこで、それらをできるだけ網羅してみたつもりである。
読みやすくなる前提知識は以下。

  • 自前のエラーはインスタンスの生成(new Error)で作る
  • エラーはthrowで投げる
  • 同期処理中のエラーはtry-catch構文で拾う
  • PromiseのエラーはPromise.catchメソッドで拾う
  • Async関数内のエラーはthrowで投げ、try-catch構文で拾う

この上で、コードと実行結果を交えて、Uncaughtなエラーをうまく扱っていく。

調べたきっかけ

とあるOSSを使ってみたが、非同期処理の結果が返ってこない。そのときはエラー処理が問題なのかどうかの切り分けができず、苦労することに。

イベントループ、try-catch、非同期処理、Async関数、エラーオブジェクト…

エラーをハンドルしたいだけなのに、なぜこうもMDNのドキュメントを行き来せねばならず、加えて Promise.all を紹介されなければならないのか。
それらを深追いした末に、エラーを非同期処理中で発生させたものを同期処理中にしっかりキャッチしてもらうコードを練習するに至った。

投稿のきっかけ

私はN予備校生のsatsukizzzです。N予備校のWebプログラミング講座では、Node.jsを用いてサーバーサイドを含めJavaScriptで書くという経験をしました。その後に上記のようなことが起きました。
せっかくある程度のレベルで調べ上げたわけなので、N校受講生用の2020年アドベントカレンダーに…と思っていたのに。年末のやっつけ仕事でやろうとした私がアホで、12/25を最後にアドベントカレンダーは閉幕していました。
一方で私の友人関係では同じようにJavaScriptを使っている人たちも多いため、遅ればせながら投稿することとなりました。

環境とブラウザ

  • クライアントサイドのJavaScript
  • Google Chrome 86.0.4240あたり / Firefox 81.0.1あたり

Node.jsでは確認していない(next関数も重要であることは承知している)。
なお、全てのエラー表示の画像はGoogle Chrome 87.0.4280のDeveloper Toolを用いて取得している。

ケース

同期処理から同期処理へ

const tryThrownErrorInSyncToSync = () => {
  try {
    throw new Error('in sync');
  } catch (e) {
    console.error(e);
  }
}
tryThrownErrorInSyncToSync();

image.png

Promiseから同期処理へ(uncaught)

const tryRejectedErrorInPromiseToSync = () => {
  try {
    new Promise((resolve, reject) => reject(new Error('in promise; this is an uncaught error')));
  } catch (e) {
    console.error(e);
  }
}
tryRejectedErrorInPromiseToSync();

image.png

Async関数から同期処理へ

const rejectAsync = async (errorMessage) => {
  throw new Error(errorMessage);
}; // rejectAsync関数は実はPromiseのエラーを返すなら何にでも変えられる
const throwPromise = (errorMessage) => {
  return new Promise((resolve, reject) => reject(new Error(errorMessage)));
}; // つまり、代わりにこのthrowPromise関数でもよい

const tryThrownErrorInAsyncToSync = async () => {
  try {
    rejectAsync('uncaught async error in reject async');
  } catch (e) {
    console.error(e);
  }

  try {
    await rejectAsync('caught async error in reject async');
  } catch (e) {
    console.error(e);
  }
  return;
}

tryThrownErrorInAsyncToSync();

image.png
image.png

同期処理からPromiseへ;rejectで

new Promise((resolve, reject) => {
  reject(new Error('catch rejected error in sync to promise'))
})
.catch(console.error);

image.png

同期処理からPromiseへ;throwで

new Promise((resolve, reject) => {
  throw new Error('catch thrown error in sync to promise; same as rejected one');
})
.catch(console.error);

image.png

PromiseからPromiseへ;コメント付加

//if you need a Promise object with arguments then make a function which returns Promise object. put that function object in Promise.then or Promise.catch directly as below
const returnRejectedPromise = (error) => {
  return new Promise((resolve, reject) => {
    error.message = 'rejected in promise, and ' + error.message;
    reject(error);
  });
}
Promise.reject(new Error('catch rejected error in promise chain'))
.catch(returnRejectedPromise) // added some information and re-rejected
.catch(console.error); // executed

new Promise((resolve, reject) => {
  returnRejectedPromise(new Error('cannot catch rejected error in promise to promise; please chain those promises'));
})
.catch(console.error); // not executed

image.png
image.png

Async関数からPromiseへ;コメント付加

// using async function instead of returnRejectedPromise
const throwPromiseWithAsync = async (error) => {
  error.message = 'thrown in async, and ' + error.message;
  throw error;
}
Promise.reject(new Error('catch rejected error in promise chain'))
.catch(throwPromiseWithAsync) // added some information and re-rejected(thrown in async)
.catch(console.error); // executed

new Promise((resolve, reject) => {
  throwPromiseWithAsync(new Error('cannot catch thrown error in async to promise; please chain those promises'));
})
.catch(console.error); // not executed

image.png
image.png

Promiseから同期処理へ;Resolveしてしまった場合

const promiseCatchResolvedErrorInPromiseToSync = () => {
  new Promise((resolve, reject) => {
    resolve(new Error('resolved with an error in promise; this is caught in fulfilled'));
  })
  .then(value => {console.log(value);}) // catches as "value" of the error
  .catch(error => {console.error(error);}); // not executed
}
promiseCatchResolvedErrorInPromiseToSync();

image.png
つまり、もしエラー終了させたいならthen(value => {console.error(value);})としてやったらいいとおもう。病気。

Promiseから同期処理へ

const promiseCatchRejectedErrorInPromiseToSync = () => {
  new Promise((resolve, reject) => {
    reject(new Error('rejected with an error in promise'));
  })
  .catch(e => console.error(e));
}
promiseCatchRejectedErrorInPromiseToSync();

image.png

Promiseから同期処理へ;エラーオブジェクトを生成しなかった場合

const promiseCatchRejectedInPromiseToSync = () => {
  new Promise((resolve, reject) => {
    reject('rejected without an error in promise');
  })
  .catch(e => console.error(e));
}
promiseCatchRejectedInPromiseToSync();

▼を押して開くと現れる、ブラウザによるスタックトレースのみとなる(エラーオブジェクトのスタックトレースがない)。
image.png

async関数からPromiseへ;returnした場合

//Promise内でAsync functionを実行しエラーを伝える
const asyncReturnError = async () => {
  return new Error('returned with an error in async; this is caught in fulfilled');
};

new Promise((resolve, reject) => {
    asyncReturnError()
    .then(value => {resolve(value);}) //executed
    .catch(error => {reject(error);});
})
.then(value => {console.log(value);}) //executed
.catch(error => {console.error(error);});

image.png

エラーオブジェクトのスタックトレースが表示されるが、エラーの色をしていない。
また、ブラウザのスタックトレースが生成されない。

async関数からPromiseへ;throwした場合

// throwの場合
const asyncThrowError = async () => {
  throw new Error('throw an error in async; this is caught in rejected');
};

new Promise((resolve, reject) => {
    asyncThrowError()
    .then(value => {resolve(value);})
    .catch(error => {reject(error);}); //executed
})
.then(value => {console.log(value);})
.catch(error => {console.error(error);}); //executed

image.png

promiseからasyncへ

//make errors in Promise and transfer them to Async function
async function testErrorsInPromiseToAsync() {
  try {
    await new Promise((resolve, reject) => {
      reject(new Error('rejected in promise to async try block with await(but not useful because the scope of variables is try)'));
    });
  } catch (error) {
    console.error(error);
  }

  new Promise((resolve, reject) => {
    reject(new Error('error in rejected promise to async function without await; this is uncaught'));
  })
  .catch(error => {throw error;});

  await new Promise((resolve, reject) => {
    reject(new Error('error in rejected promise to async function with await'));
  })
  .catch(error => {throw error;});

}
testErrorsInPromiseToAsync()
.catch(console.error);

上から順に
image.png
image.png
image.png

おまけ: setTimeout のラップ

promiseでsetTimeoutをラップ

「setTimeoutはPromiseでラップする」とは例えばこのように。
```JavaScript
const delayedResolve = (ms) => {
return new Promise(resolve => {setTimeout(resolve, ms)});
};

const delayedReject = (ms, error) => {
return new Promise((resolve, reject) => {setTimeout(reject(error), ms)});
};
```

そのうえで、

const throwAfterTimeout = async () => {
  await delayedReject(1000, new Error('error in trying delayedReject'))
  .catch(console.error);
}

const throwInSetTimeout = async () => {
  try {
    await setTimeout(() => {
      throw new Error('error in trying setTimeout; this is uncaught');
    }, 2000);
  } catch (error) {
    console.error(error);
  }
}

const promiseCatchAfterTimeout = () => {
  delayedReject(3000, new Error('error in Promise.catching delayedReject'))
  .catch(console.error);
}

throwAfterTimeout();
throwInSetTimeout();
promiseCatchAfterTimeout();

image.png
image.png
image.png

async関数でsetTimeoutをラップ

const asyncTimeout = async (ms, errorOrValue) => {
  return new Promise((resolve, reject) => {
    if(errorOrValue instanceof Error) setTimeout(() => {reject(errorOrValue)}, ms);
    setTimeout(() => {resolve(errorOrValue);}, ms);
  });
}

asyncTimeout(4000, 'delayed resolve in async timeout')
.then(console.log) //executed
.catch(console.error);

asyncTimeout(4000, new Error('delayed error in async timeout'))
.then(console.log)
.catch(console.error); //executed

image.png

雑記

2020-12-27 公開日

この記事は自分自身へのクイックドキュメントとして存在しています。
一方で、気になるエラーや載せてほしい例があれば
- 記事へのコメント
- 私のtwitter: satsukizzzへのアクション(リプライ等) ... Qiita同様スチームミルクを落とす画像
- N予備校slackのsatsukizzzへのアクション(メンション等) ... ピーチティーの画像
をしてもらえれば追加します。そのときは、皆さんのクイックドキュメントになればなと思います。
詳しい方は用語の勘違いの指摘などしていただけると大変ありがたいです。

年末のやっつけで2か月前に試したものを一つ一つ切り出しただけであるため、後々整理すると思われますが、閲覧者の疑問点に合わせて分類するのもわかりやすいかな?と思うので、まずは公開しました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

promise、asyncとawaitについて(自分用)

  • 非同期処理を行ったあとに、処理を実行したい
  • 500ミリ秒後に、helloを実行し、そのあとにworldを実行したい
setTimeout(() => console.log('hello'), 500);
console.log('world');
  • worldのあとに、helloが実行されてしまった
  • さらにコールバックを使って実現してみよう!
setTimeout(() => {
 console.log('hello');
  setTimeout(() => {
    console.log('world');
  }, 500);
}, 500);
  • できたけど、ネスト化していてわかりづらい(コールバック地獄)
  • promiseでわかりやすくかけるらしい
const promise = new Promise((resolve, reject) => {
    setTimeout(() => { 
        console.log('hello');
        resolve();
    }, 500);
});
promise.then(() => console.log('world!'));
console.log('yeah!'); // ここが実行されたあとに、上の2つが実行される
  • わかりやすくなった!
  • asyncとawaitを組み合わせることでより簡潔にかけるらしい
async function helloWorld(text) {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(text);
    }, 5000);
  });
  const val = await promise; // awaitでpromiseの終了を待つ
  console.log(val);
  console.log('world!');
}

helloWorld('hello');
console.log('Yeah'); // ここが実行されたあとに、上の処理が実行される
  • より処理の流れがわかりやすくなった
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Vue.js]Storeの値がcommit以外で変わっちゃった時の話

前書き

業務でNuxt.jsアプリの改修をしていて、

なんかバグ出てるやん
→ どうやらStoreの値中の配列の値がおかしいぞ
→ 変な値がcommitされているんだな
→ commit値は正常だな・・
→ あ、変なところでまたcommitが呼ばれちゃってるんだな!
→ commitは(正常時の)1回しかされていないぞ・・
→ 

なことがあったので、これを解決した時の話を書いてみます。

体験談

今回のポイントは、異常値を示した値の型が配列と言う点。

最初はVue初級者が故に、
storeの値はcommitから以外で変わり得ない、と言う前提はあったため、
VueXの仕様的に誤った使い方しているんじゃ、、と的外れなあたりをつけてVue周りを調べてました。

そんな時、ふと配列で思い出したのが、
配列は参照型やん、です。

なので、storeから取り出した配列を直接いじってる部分がないかと言う方針で調査を進めました。

そして見つけました。

lodash(JSライブラリ)の_.remove()メソッド

※removeメソッドについてはこちらの記事がとても参考になります。

この処理の前後のstoreの値を確認し、確信しました。

この処理周りを改善し、無事バグ解決。

参照型とは

超ざっくり言うと、値が置いてある場所、のこと。
Java習いたてでよく注意点として出て来ます。

ありがちな事象は以下

ある変数を別の変数にコピーして、値2個できたやん、とか思って片方を変更する。
でも参照型の場合は実際には場所を指しているので、コピーされたのは場所情報だけ、変更されたのはその場所にある値である。
つまり同じ場所を指しているもう一方の変数も、一緒に値が変わってしまう。

文面で起こすの辛いので実例↓

// JSの場合

var a = [1,2,3,4,5]; // 配列定義。

var b = a; // 変数をコピー。実際は配列のある場所だけコピーされる。

b.pop(); // bの末尾削除

console.log(a); // [1,2,3,4]
console.log(b); // [1,2,3,4]

// aも削除されちゃった

まとめ

配列が参照型、と言うのは実際にその類のバグに遭遇しないと意識しないと思うので、
体験談を書き記してみました。

これがあってから改めて意識するようになったのが、

  • Vuexのstoreの中身がcommit以外から変更されるパターンもある(ReactのReduxなどでも同様だと思う)
  • storeなど固定値として保持されている配列を直接いじらない
  • 配列の中身に関するバグがあったら、直接操作を行っていないかを疑う

です。

ちなみにどうしてもいじる必要がある場合は、
例えばlodashの_.cloneDeep()と言うメソッドを使うことで、
配列を完全複製し、それを使用する方法などが回避策になるかなと思います。

この記事を見て、
うわ、こんなバグもありえるのか!、と感心していただけたり、
自分もあったわw、と共感していただける人がいたら幸いです。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[=LOVE]青春"サブリミナル"を再現してみた

アイドルグループ「=LOVE」(イコールラブ、通称イコラブ)さんの8thシングル『青春"サブリミナル"』の歌詞の一部をGitとJavaScriptを使って再現してみました!

手順

gitリポジトリ内で行います。

jsファイルを設置

unkown_by_ut.js
const LOVE = 'love';
let answer

二行目末尾に改行を入れておくのがポイントです。

一つ目のコミット

git add .
git commit -m "さっきまで"

jsファイルに変更を加える

unkown_by_ut.js
const LOVE = 'love';
let answer
=LOVE

二つ目のコミット

git add .
git commit -m "その答え"

完成!

git log

を実行すると、

commit 12fb9840578f2410882ea0e1fa66ac832319d093 (HEAD -> master)
Author: user <user@sample.com>
Date:   Sun Dec 27 10:00:00 2020 +0900

    その答え

commit 2755bace9774959e9ef437c5db06e636ccd94a5a
Author: user <user@sample.com>
Date:   Sun Dec 27 10:00:00 2020 +0900

    さっきまで

さらに

git diff 2755bace9774959e9ef437c5db06e636ccd94a5a 12fb9840578f2410882ea0e1fa66ac832319d093

を実行して、

diff --git a/unkown_by_ut.js b/unkown_by_ut.js
index 6966efa..90b7c6d 100644
--- a/unkown_by_ut.js
+++ b/unkown_by_ut.js
@@ -1,2 +1,3 @@
 const LOVE = 'love';
 let answer
+=LOVE
\ No newline at end of file

差分が「=LOVE」となります。

まとめ

と、いうわけで!
さっきまでと違うもの〜♪ その答えは「=LOVE」~♪
『青春"サブリミナル"』名曲なので、よかったら聴いて下さい!!

あと、ネタ被りを恐れてQiita内で「イコラブ」を検索したところ、
Twitterのいいね数を推定してみる
Twitterのいいね数推定の精度向上を図ってみる
という真っ当な記事を書いている方がいらっしゃいました。私も大場花菜ちゃん推しです。

では、お粗末様でした〜

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[個人開発]経験を共有するWebアプリ作ってみた!

新しいwebアプリ開発しました。「Elder」という人生経験を募集、公開、評価できるサイトです。今回は作ったwebアプリの紹介、サービスのネタの考え方、herokuの無料プランでスリープにさせない方法など書いていきます。今回は前回みたいなヘマしないように頑張ります(^^♪

今回作ったWebアプリはこちら

紹介

Elder-top.png

↑トップページ
人生経験を共有するコミュニティサイトです。
人生経験の募集、エッセイ、一言だけ投稿、などができます。
現在技術系の記事ばかりですが、分野は問いません。

募集中のところはスライドショーになっていて動きます。一か所でも動くところがあると凝ったサイト感が出ます。
イメージカラーはを選びました。
使ってみた感想として、いろんな色とは合わないけどその代わりたくさん使っても不自然になりにくいと感じました。
普通、同じ色ばっかり使うとそれはそれで変な感じになるんですがね。

なぜつくったか

人生経験ってなかなか聞く機会も話す機会もなく、もっと共有したほうがいいと思ったから。

今回使った技術

gem
rails6
ruby2.7
postgresql
heroku free
Logo Garden
gem 'ridgepole'
gem 'slim-rails'
gem 'html2slim'
gem 'pry-rails'
gem "devise"
gem 'devise-i18n'
gem 'devise-i18n-views'
gem 'rails-i18n'
gem 'carrierwave'
gem "kaminari"
gem 'ransack'

Logo Gardenとはロゴ制作サイトです。地味に今回はじめてちゃんとしたロゴを作りました。
Elder.png

ちょい粗いですがこれが完全無料で作れるんですよ。すごいなー(感心)。もっと凝ったものも作れたんですけどシンプルが一番かなーと。
https://www.logogarden.com/

あと、bootstrapは使ってません。

サービスのネタの決め方(個人開発)

なんでサービスを作りたいのか。

  • サービスを届けたい人は本当に喜ぶ?
  • 本当にそのアプリで問題解決することができる?。
  • 有料でも使いたいか(無料で出すとしても)
  • お金を扱わない
  • 初期費用がいらない
  • 個人で管理できる
  • ほぼコンテンツがいらない or 自分でコンテンツが作れる
  • 幸福になりたいのだったら、人を喜ばすことを勉強したまえ。

こんなもんだと思います。まだ成功するかわからないのにこんなこと書くのはあれですが間違ったことも言ってないと思います。
一時的に当たるネタというのはこれのどれかが抜けていると思います。

あと、一応保険かけときます。※個人の意見です

herokuの無料プランで運用

herokuの無料プランで一番きついのは、30分ごとのスリープだと思います。
これがなかったら無料プランで全然いいという方も多いと思います。

僕がやったのはのはHeroku Schedulerです。
10分ごとに自動でサーバーになにかさせるというものです。

 heroku addons:create scheduler:standard
 heroku addons:open scheduler 

ブラウザが開いたらadd jobをクリックして
Run Commandを

$ curl サイトのURL

ScheduleはEvery 10 minutesにして保存。

これでスリープしなくなります。ただバグがあったりするらしいので注意してくださいねー。

参考:
https://casualdevelopers.com/tech-tips/how-to-prevent-idling-of-free-dyno-on-heroku/#HerokudynoHeroku_Scheduler

振り返り

よかったこと

  • 完全無料で作れた(人件費抜き)
  • ロゴも作れた
  • railsコードの再利用ができた

悪かったこと

  • 途中でなんかいもあきらめそうになった

個人でやるとモチベーション管理が一番むずいです...
なにあともあれ使ってくみてださいね(^^♪

https://e-elder.herokuapp.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

人生経験を共有するコミュニティサイト作ってみた! & サービスの考え方

新しいwebアプリ開発しました。「Elder」という人生経験を募集、公開、評価できるサイトです。今回は作ったwebアプリの紹介、サービスのネタの考え方、herokuの無料プランでスリープにさせない方法など書いていきます。今回は前回みたいなヘマしないように頑張ります(^^♪

今回作ったWebアプリはこちら

紹介

Elder-top.png

↑トップページ
人生経験を共有するコミュニティサイトです。
人生経験の募集、エッセイ、一言だけ投稿、などができます。
現在技術系の記事ばかりですが、分野は問いません。

今回使った技術

gem
rails6
ruby2.7
postgresql
heroku free
Logo Garden
gem 'ridgepole'
gem 'slim-rails'
gem 'html2slim'
gem 'pry-rails'
gem "devise"
gem 'devise-i18n'
gem 'devise-i18n-views'
gem 'rails-i18n'
gem 'carrierwave'
gem "kaminari"
gem 'ransack'

Logo Gardenとはロゴ制作サイトです。地味に今回はじめてちゃんとしたロゴを作りました。
Elder.png

ちょい粗いですがこれが完全無料で作れるんですよ。すごいなー(感心)。もっと凝ったものも作れたんですけどシンプルが一番かなーと。
https://www.logogarden.com/

サービスのネタの決め方(個人開発)

なんでサービスを作りたいのか。

  • サービスを届けたい人は本当に喜ぶ?
  • 本当にそのアプリで問題解決することができる?。
  • 有料でも使いたいか(無料で出すとしても)
  • お金を扱わない
  • 初期費用がいらない
  • 個人で管理できる
  • ほぼコンテンツがいらない or 自分でコンテンツが作れる
  • 幸福になりたいのだったら、人を喜ばすことを勉強したまえ。

こんなもんだと思います。まだ成功するかわからないのにこんなこと書くのはあれですが間違ったことも言ってないと思います。
一時的に当たるネタというのはこれのどれかが抜けていると思います。

あと、一応保険かけときます。※個人の意見です

herokuの無料プランで運用

herokuの無料プランで一番きついのは、30分ごとのスリープだと思います。
これがなかったら無料プランで全然いいという方も多いと思います。

僕がやったのはのはHeroku Schedulerです。
10分ごとに自動でサーバーになにかさせるというものです。

 heroku addons:create scheduler:standard
 heroku addons:open scheduler 

ブラウザが開いたらadd jobをクリックして
Run Commandを

$ curl サイトのURL

ScheduleはEvery 10 minutesにして保存。

これでスリープしなくなります。ただバグがあったりするらしいので注意してくださいねー。

参考:
https://casualdevelopers.com/tech-tips/how-to-prevent-idling-of-free-dyno-on-heroku/#HerokudynoHeroku_Scheduler

振り返り

よかったこと

  • 完全無料で作れた(人件費抜き)
  • ロゴも作れた
  • railsコードの再利用ができた

悪かったこと

  • 途中でなんかいもあきらめそうになった

個人でやるとモチベーション管理が一番むずいです...
なにあともあれ使ってくみてださいね(^^♪

https://e-elder.herokuapp.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[個人開発]人生経験を共有するコミュニティサイト作ってみた!

新しいwebアプリ開発しました。「Elder」という人生経験を募集、公開、評価できるサイトです。今回は作ったwebアプリの紹介、サービスのネタの考え方、herokuの無料プランでスリープにさせない方法など書いていきます。今回は前回みたいなヘマしないように頑張ります(^^♪

今回作ったWebアプリはこちら

紹介

Elder-top.png

↑トップページ
人生経験を共有するコミュニティサイトです。
人生経験の募集、エッセイ、一言だけ投稿、などができます。
現在技術系の記事ばかりですが、分野は問いません。

募集中のところはスライドショーになっていて動きます。一か所でも動くところがあると凝ったサイト感が出ます。
イメージカラーはを選びました。
使ってみた感想として、いろんな色とは合わないけどその代わりたくさん使っても不自然になりにくいと感じました。
普通、同じ色ばっかり使うとそれはそれで変な感じになるんですがね。

なぜつくったか

人生経験ってなかなか聞く機会も話す機会もなく、もっと共有したほうがいいと思ったから。

今回使った技術

gem
rails6
ruby2.7
postgresql
heroku free
Logo Garden
gem 'ridgepole'
gem 'slim-rails'
gem 'html2slim'
gem 'pry-rails'
gem "devise"
gem 'devise-i18n'
gem 'devise-i18n-views'
gem 'rails-i18n'
gem 'carrierwave'
gem "kaminari"
gem 'ransack'

Logo Gardenとはロゴ制作サイトです。地味に今回はじめてちゃんとしたロゴを作りました。
Elder.png

ちょい粗いですがこれが完全無料で作れるんですよ。すごいなー(感心)。もっと凝ったものも作れたんですけどシンプルが一番かなーと。
https://www.logogarden.com/

あと、bootstrapは使ってません。

サービスのネタの決め方(個人開発)

なんでサービスを作りたいのか。

  • サービスを届けたい人は本当に喜ぶ?
  • 本当にそのアプリで問題解決することができる?。
  • 有料でも使いたいか(無料で出すとしても)
  • お金を扱わない
  • 初期費用がいらない
  • 個人で管理できる
  • ほぼコンテンツがいらない or 自分でコンテンツが作れる
  • 幸福になりたいのだったら、人を喜ばすことを勉強したまえ。

こんなもんだと思います。まだ成功するかわからないのにこんなこと書くのはあれですが間違ったことも言ってないと思います。
一時的に当たるネタというのはこれのどれかが抜けていると思います。

あと、一応保険かけときます。※個人の意見です

herokuの無料プランで運用

herokuの無料プランで一番きついのは、30分ごとのスリープだと思います。
これがなかったら無料プランで全然いいという方も多いと思います。

僕がやったのはのはHeroku Schedulerです。
10分ごとに自動でサーバーになにかさせるというものです。

 heroku addons:create scheduler:standard
 heroku addons:open scheduler 

ブラウザが開いたらadd jobをクリックして
Run Commandを

$ curl サイトのURL

ScheduleはEvery 10 minutesにして保存。

これでスリープしなくなります。ただバグがあったりするらしいので注意してくださいねー。

参考:
https://casualdevelopers.com/tech-tips/how-to-prevent-idling-of-free-dyno-on-heroku/#HerokudynoHeroku_Scheduler

振り返り

よかったこと

  • 完全無料で作れた(人件費抜き)
  • ロゴも作れた
  • railsコードの再利用ができた

悪かったこと

  • 途中でなんかいもあきらめそうになった

個人でやるとモチベーション管理が一番むずいです...
なにあともあれ使ってくみてださいね(^^♪

https://e-elder.herokuapp.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】getElementById と getElementsByClassNameの違い

プログラミング勉強メモ

2020年12月27日
getElementById と getElementsByClassNameの違いについてのメモです

違い

getElementById では要素が直接返ってきます。
一方で getElementsByClassName は要素の配列 (HTMLCollection) が返ってきます

理由

id属性の値は、他の要素のid属性の値と重複できないから要素が直接返る。
class属性の値は、重複して使う可能性があるから配列の形で返す。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【解決!】Vue-Cliをインストール...できない!

状況

・Vue-Cliを使った本格的なアプリ開発をするためにVue-Cliをインストールしようとした
・過去にもVue-Cliをインストールして使った経験あり!
・コマンドプロンプトを開いて「いざ!やるぞ!」と思っていた矢先、早速出鼻をくじかれた...という話です。

起こったこと①

・コマンドプロンプトで下のようにVue-Cliをインストール

npm -g install vue-cli

・以下エラーが発生

npm WARN deprecated vue-cli@2.9.6: This package has been deprecated in favour of @vue/cli
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated coffee-script@1.12.7: CoffeeScript on NPM has moved to "coffeescript" (no hyphen)
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm ERR! code EEXIST
npm ERR! path C:\Program Files (x86)\Nodist\bin\node_modules\vue-cli\bin\vue
npm ERR! dest C:\Program Files (x86)\Nodist\bin\*vue.cmd*
npm ERR! EEXIST: file already exists, cmd shim 'C:\Program Files (x86)\Nodist\bin\node_modules\vue-cli\bin\vue' ->'C:\Program Files (x86)\Nodist\bin\vue.cmd'
npm ERR! File exists: C:\Program Files (x86)\Nodist\bin\vue.cmd
npm ERR! Remove the existing file and try again, or run npm
npm ERR! with --force to overwrite files recklessly.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\Owner\AppData\Roaming\npm-cache\_logs\2020-12-24T15_55_11_169Z-debug.log

対策①

ポイントは5行目~11行目。
要約すると
「既にインストールしているデータがある時のエラーだよ!」(5行目)
「具体的にはProgram Files (x86)\Nodist\bin\vue.cmdのことだよ!」(8~9行目)
「このfileを消してもう一回やってみて!」(10行目)
とある。
・冒頭書いた通り、一度Vue-Cliはインストールしたことあるので、その時インストールしたファイルたちが残っていた。
 どうもそのファイルが今回のインストールを邪魔している、らしい。
・なので、一旦Program Files (x86)\Nodist\binにあるvue.cmdを消して再度npm -g install vue-cliを実行

起こったこと②

・開発へ進める!と思ったら、またしても以下エラー発生

npm WARN deprecated vue-cli@2.9.6: This package has been deprecated in favour of @vue/cli
npm WARN deprecated request@2.88.2: request has been deprecated, see https://github.com/request/request/issues/3142
npm WARN deprecated coffee-script@1.12.7: CoffeeScript on NPM has moved to "coffeescript" (no hyphen)
npm WARN deprecated har-validator@5.1.5: this library is no longer supported
npm ERR! code EEXIST
npm ERR! path C:\Program Files (x86)\Nodist\bin\node_modules\vue-cli\bin\vue
npm ERR! dest C:\Program Files (x86)\Nodist\bin\vue
npm ERR! EEXIST: file already exists, cmd shim 'C:\Program Files (x86)\Nodist\bin\node_modules\vue-cli\bin\vue' -> 'C:\Program Files (x86)\Nodist\bin\*vue*'
npm ERR! File exists: C:\Program Files (x86)\Nodist\bin\vue
npm ERR! Remove the existing file and try again, or run npm
npm ERR! with --force to overwrite files recklessly.

・さっきと同じようなエラー。よくよく見ると...

npm ERR! EEXIST: file already exists, cmd shim 'C:\Program Files (x86)\Nodist\bin\node_modules\vue-cli\bin\vue' -> 'C:\Program Files (x86)\Nodist\bin\vue'
npm ERR! File exists: C:\Program Files (x86)\Nodist\bin\vue

・消すべきファイルの名前が変わっている。

対策②

・なるほど。過去インストールしたいくつかのファイルが邪魔して今回インストールできないわけだ、と悟り/同じく今回もvueファイルを消す。
・結局、「消す⇒npm -g install vue-cli実行⇒エラー(○○を消してね!)⇒消す⇒...」を10回ほど繰り返した
 ○○に指定されて消したファイルは以下の通り
vue-cli.png
・こんなに何回もエラーを頻発した理由は、これらのファイルたちはnpm -g install vue-cliを実行するとまた最新verが生まれてくるということ。
で、その生まれたファイルたちがエラーの原因となってしまうこともあった。
・結論、上図のファイルたちを一気に消してnpm -g install vue-cliで正常にインストール完了!

エラーの原因

・今回のエラーの原因はどうも先ほどお見せしたファイルたちのインストールした日時らしい。
・元々インストールしたファイルたちは2020/12/20にインストールしたモノだったので、これがある限りエラーを発生しまくった。
・そこで上記のようにファイルたちを一気に消してnpm -g install vue-cliを実行し最新版のファイルが作成され、うまくいった!という流れ。

最後に

大したことではないですが、ググって同じようなエラーコード出ている方々はいらっしゃいましたが、ほとんどの方はMacユーザーの方でした。
そういった方の対策案を参考にした自分はこのエラーも出していました。

スクリーンショット 2020-12-26 205610.png

ここで初めてWindowユーザーである自分は"sudo"や"rm"などのLinuxが使えない、ということを学びました。
参考までに。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む