JavaScript」タグアーカイブ

ES6: Octal number

ES6 では数値リテラルのプリフィクスが追加されていた。
0o で 8 進数、0b で 2 進数ということらしい。
新8進数と2進数の数値リテラルが実装された – JS.next

SpiderMonkey, JavaScriptCore, V8 すべて現行版にて実相済み。

2018 からはコレだね、古いブラウザを完全に駆逐しようぜ!!!
サポートをとっくに終了した IE6 がまだ一部で生き残っていることを筆者は知っている。
死ねよ。

8 進数なんてパーミッションを弄る時くらいしか使わないので気が付かなかった。
ちなみに 076 という表記は SpiderMonkey のみエラーになった。
更に Python3 も同様、いつのまに…

8 進数は英語で Octal number だから o なのか。
2 進数は Binary number、なるほど。
C 言語もこうしてくれよ、076 なんて表記はどう考えてもおかしい。
スマホがメインのウエブブラウザになった時代に 0120 を間違えるとかetc…

また覚書ページを書き換えなきゃ、忙しい正月になりそう。

連番ファイルのダウンロード

とあるサイトの連番ファイルをダウンロードしたくなった。
んで、そんなの bash シェルスクリプトが使える環境 (macOS, Linux) なら超簡単で。

#!/bin/sh

# 001.jpg - 200.jpg だった場合
for (( i=1; i<201; i++ )); do
    n=`printf %03d.jpg $i`
    wget http://なんちゃらどっとこむ/${n}.jpg
done

# 1.jpg - 200.jpg だった場合
#for (( i=1; i<201; i++ )); do
#    wget http://なんちゃらどっとこむ/${i}.jpg
#done

以上。

なんだよこの for 文は使い道があるじゃないの!
wget コマンドは一部ディストリビューションでは dnf や apt で。
google-chrome が使っているから依存関係で勝手に入るはずだけーがさ。

エロゲ専用に落ちぶれた某 OS はアプリや拡張を使うんでしょ?
プログラミングができないサルだけがいまだに使っている OS だよねwwwwww

いや、せっかくだからサイトの JPEG リンク抜き出しをやりたいぞ。
とりあえず URL を読み込んで、それから

href="空白のない文字列jpeg"

を正規表現で抜き出して slice すれば簡単に wget できそうだ。
Python3 の urllib を使えばアホみたく簡単そうだけど、あえて gjs で。

#!/usr/bin/gjs

const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;

if (ARGV.length == 0) {
    print("URL を指定してください");
} else {
    let file = Gio.File.new_for_uri(ARGV[0]);
    let fstream = file.read(null);
    let dstream = new Gio.DataInputStream({base_stream: fstream});
    for (;;) {
        let [val, len] = dstream.read_line_utf8(null);
        if (val.match(/\<\/html\>/gi) !== null)
            break;
        // href="空白のない文字列jpeg" の取得
        let links = val.match(/href=[\'|"]\S+(jpg|jpeg)[\'|"]/gi);
        if (links !== null) {
            for (let link of links) {
                let url = link.slice(6, link.length - 1);
                print(`${url} を取得します`);
                GLib.spawn_command_line_async(`wget ${url}`);
            }
        }
    }
}
//ex: ft=js

書いてみた。

相対パスの場合はガン無視だけど昨今は WordPress ベースが大半だしこれでいいか。
つか相対パスにしている人はそもそも連番ばかりなので最初のでいい。

テキトーなエロ画像サイトで上記を試す。
普通にダウンロードされた、後はリネームして順番を合わせるだけ。
自分が書いたコードが思った通りに動くって本当に面白い。

ES6 Promise

ES6 には Promise という非同期関数がある。

Promiseと仲良くなって気持ち良く非同期処理を書こう – Qiita

なるほど、Promise 関数と then 関数を使えばいいのか。
てなわけで、毎度のようにコイツで GUI アプリを起動してみよう。

#!/usr/bin/gjs

const GLib = imports.gi.GLib;

function asyncFunction(guiApp) {
    return new Promise(function (resolve, reject) {
        print("__DO__");
        GLib.spawn_command_line_sync(guiApp);
        resolve("__DONE__");
    });
}

asyncFunction("eog")
    .then(function (value) {
        print(value);
    })
    .catch(function (error) {
        print(error);
    });

print("__EOL__");

だめヤン!
非同期ならこの状態で __EOL__ まで抜けているはずだ。

で、昨日気が付いたんだけど思い込みによる勘違いだった。
非同期になるのは Promise 関数内ではなく then 関数内であるようだ。

#!/usr/bin/gjs

const GLib = imports.gi.GLib;

function asyncFunction() {
    return new Promise(function (resolve, reject) {
        resolve(null);
    });
}

print("__START__");

asyncFunction()
    .then(function (value) {
        GLib.spawn_command_line_sync("eog");
    });

print("__EOL__");

コレなら __EOL__ まで抜けているのにアプリは実行されたままになる。
GUI アプリを起動するコードだと動作を把握しやすい、Linux の長所。

んで、実は単純な非同期処理なら省略表記があって。

#!/usr/bin/gjs

const GLib = imports.gi.GLib;

Promise.resolve(null).then(function (value) {
    GLib.spawn_command_line_sync("eog");
});

print("__EOL__");

以上。

超シンプルな非同期処理のできあがり。
Promise 関数は前処理で then が非同期の実行という解釈でいいかな。
前処理(確認作業)が不要なら省略表記で、と使い分けできるね。

アロー関数を使えばクラスメソッドも問題なく使えるようだ。

#!/usr/bin/gjs

const GLib = imports.gi.GLib;

class PromiseTest {
    get command() {return this._command;}
    set command(cmd) {this._command = cmd;}
    asyncFunc() {
        Promise.resolve(null).then((value)=> {
            GLib.spawn_command_line_sync(this.command);
        });
    }
}

let test = new PromiseTest();
test.command = "evince";
test.asyncFunc();

print("__EOL__");

ES6 フル活用!

えぇ… JavaScript !!!
と記法で超嫌っていた JavaScript が今ではこんなに面白いとは予測できなかったYO!

GNOME 3.26 gjs class

おまたせ、gjs の ES6 class でアプリを作る方法がやっと解ったよ。
PyGObject みたく親切に例外を吐くなんてしてくれないので厳しい。

注意点、constructor はマジでコンストラクタです。
Python の __init__ 等と違う、動的言語でまさかこの仕様だとは。
細かい説明はしないけど、つまり super() 以外は何もできない。
継承で色々行う場合は下記のように別関数にする等の必要あり。

/*
const MyWindow = new Lang.Class({
    Name: 'MyWindow',
    Extends: Gtk.ApplicationWindow,

    _init: function(app) {
        this.parent({application: app});
        this.createContents();
        // etc...
*/
var MyWindow = GObject.registerClass({
    GTypeName: "MyWindow",
}, class MyWindow extends Gtk.ApplicationWindow {
    constructor(props={}) {
        super(props);
    }
    create() {
        this.createContents();
        // etc...

更に constructor の引数はベースへの property を JSON でに限定。
他を入れるとエラーになるのでとにかくこう書く以外に手段は無いみたい。

override で継承元の関数を呼ぶには super からドットで呼び出す。
Lang.Class ではないので this.parent() は使えない。

var MyApplication = GObject.registerClass({
    GTypeName: "MyApplication"
}, class MyApplication extends Gtk.Application {
    constructor(props={}) {
        super(props);
    }
    vfunc_startup() {
        //this.parent();
        super.vfunc_startup();

コレはすぐ気が付くか。

それ以外は今までのコードをそのまんま使えるようだ。
var にする必要があるのは外部から使う class だけでいいみたい。

本当は物凄い遠回りをしたんだけど以上であるようだ。

ということで、もう少し実験してから Y901x を更新する。
ぶっちゃけ動画プレイヤーは gnome-mpv でイイんだけど
Y901x は gjs のサンプルコードがメインの仕事だったことを忘れる所だった。

GNOME 3.26 gjs p2

散々探してやっと見つけた。

Inventing GObject ES6 classes | The Mad Scientist Review

なんかイッパイ手段があるみたい。
一番簡単そうな手段で試してみる。

/* btn.js */

const Gtk = imports.gi.Gtk;
const GObject = imports.gi.GObject;

var Btn = GObject.registerClass({
    GTypeName: "BtnClass"
}, class Btn extends Gtk.Button {
    constructor(props={}) {
        super(props);
    }
    // function
    set_button_label(txt) { this.label = txt; }
    // getter
    get prop() { return this.label }
});

#!/usr/bin/gjs

imports.gi.versions.Gtk = "3.0";
imports.searchPath.unshift('.');

const Gtk = imports.gi.Gtk;
const Btn = imports.btn;

class Test extends Gtk.Window {
    constructor() {
        super();
        this.connect("hide", ()=> {
            Gtk.main_quit();
        });
        this.ll = "カワイイ";
        var btn = new Btn.Btn({label: "睦ちゃん"});
        btn.connect("clicked", (widget)=> {
            // getter
            var s = widget.prop;
            // function
            widget.set_button_label(`${s} ${this.ll}`);
        });
        this.add(btn);
        this.show_all();
    }
}

Gtk.init(null);
let test = new Test();
Gtk.main();

で動かしてみる。

何も警告は出なくなった、思いっきり const を使っているのに。
これで安心してコード分割ができるぞい。
ただ、ちょっぴり面倒臭い。

関数定義や getter/setter は検索して一番解りやすかったページが以下。

JavaScriptにもクラスがやってきた!JavaScriptの新しいclass構文をマスターしよう | HTML5Experts.jp

コピペして console.log を print に書き換えてと。
うん gjs 1.50.2 で問題なく動くんだね。
だけど gir で継承すると…

#!/usr/bin/gjs

imports.gi.versions.Gtk = "3.0";

const Gtk = imports.gi.Gtk;
Gtk.init(null);

//* gir @ Error
class Btn extends Gtk.Button {
    constructor() { super(); }
    put() { print("func test"); }
}
//*/

/* no gir @ OK!
class Btn {
    constructor() {}
    put() { print("func test"); }
}
*/
var btn = new Btn();
btn.put();

何でや!

getter/setter も同様だった、理由が解らない。
上記のように GObject.registerClass をするしかないっぽい。
class が使えるようになって面倒になるとは思わなかったよ。