htsign's blog

ノンジャンルで書くつもりだけど技術系が多いんじゃないかと思います

Wandbox のエディタ設定を言語ごとに保存して自動で切り替えする UserScript

表題の通り。
Wandbox さん非常に便利なんですが、言語を切り替えてもエディタ設定は切り替わらないんですよね。

例えば F# はタブ文字をインデントに使えませんが、Go はタブ文字でのインデントを推奨しています。
これら言語を切り替えるときに設定も都度触るのが非常に億劫だと常々思っていました。

というわけで、そのちょっとした不満を解消するために書きました。
設計は「まぁ Proxy 使えば未定義のプロパティも初期化できるやろ」くらいのかなり適当なノリでやってしまったので、メンテが非常につらい感じになっています。
変数の命名も適当です。適当に書いてたら名前が被りそうだったので、普段はオブジェクトをバインドした定数に UPPER_SNAKE_CASE は使わないんですが、他に考えるのも面倒だったので苦し紛れに使っています。

gist.github.com

Promise を再帰させるとどうなるか

ただの興味本位です。

以下のコードを各環境で動かしていきます。

const p = n => new Promise(resolve => { console.log(n); resolve(p(n + 1)); });
p(1);

なるべく公平を期すため、ブラウザの場合は about:blank にて実行します。

環境

PC1:

種別 詳細
OS Windows 10
CPU AMD Rizen 5 1600
MEM DDR4 PC4-24000 16GB x2 (空き 20000MB程度)

PC2:

種別 詳細
OS macOS 10.14.6 (Mojave)
CPU Intel Core i5 5250U
MEM LPDDR3 PC3-12800 4GB (空き1000MB程度)

結果

環境 再帰回数 備考
Edge 44 1000 20回置きにウェイトが入る。終了時は特に出力なし
Safari 3674 コンソールにはエラーが表示されず、
ブラウザページにポップアップでMaximum call stack size exceeded.と表示され終了
Firefox 69 (PC1) 582 too much recursion の例外で終了
Firefox 69 (PC2) 2946 too much recursion の例外で終了
Chrome 77 (PC1) 1934 RangeError: Maximum call stack size exceeded の例外で終了
Chrome 77 (PC2) 1929 RangeError: Maximum call stack size exceeded の例外で終了
node 10.16.3 (PC1) 1818 エラーを吐かずにクラッシュして終了
node 10.16.3 (PC2) 1847 RangeError: Maximum call stack size exceeded の例外で終了
node 12.11.1 (PC1) 1806 エラーを吐かずにクラッシュして終了
node 12.11.1 (PC2) 1835 RangeError: Maximum call stack size exceeded の例外で終了

この規則性のない結果から、おそらく Promise における再帰については厳密な仕様はなく実装依存であることが推定されますが、仕様書のどの辺りを見ればよいのか分からず…。
同じバージョンでも環境によって結果が異なるので、メモリ等の状態によっても変わるのかなと思いますが、ブラウザのソースコード読む気力もなく、「とりあえず Promise の再帰はなるべく避けよう」という辺りで落ち着けておきます。

JScript(not JavaScript)のArrayに対するメソッド群を追加する超単純なポリフィル

今さらこんなコードを書くこと自体恥ずべきことだと思うのだけど、勤務先の業務システムがJSPで実装されており、クライアントサイドはレガシーなIEが想定されています(嘆かわしいですが、よくある光景ですね…)

そしてこれまたよくある光景なのですが、秘伝のソースであるため、

<%-- yyyy/mm/dd add someone IE11対応 --%>
<META HTTP-EQUIV=X-UA-Compatible CONTENT=IE=5>
<%-- /IE11対応 --%>

みたいなのが書いてあるわけです。
お前それどこがIE11対応やねん、といったツッコミはさておき。

そんな環境ですと、おもむろにF12を押してデバッグをしようにも配列やArrayLikeを扱う上で非常につらみを感じるわけです。
「じゃあlodash使えばいいじゃん!」と

(function(s){
  s.src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js";
  document.body.appendChild(s);
})(document.createElement("script"))

なんてコードを実行してみても、lodashの実行中にコケたりするんです。
そのときに知ったんですが、lodashってIE9+なんですね…。

便利なライブラリが使えないとなると、ある程度自分で書かざるをえないのでブックマークレットを作成して活用しています。

というのが前置き。
当たり前ですがコードの持ち出しは禁止なので、実際のコードとはちょっと違うと思います。

javascript:(function(d,i,l,x,a){d("indexOf",function(v){for(i=0,l=this.length;i<l;++i)if(this[i]===v)return i;return -1});d("includes",function(v){return!!~this.indexOf(v)});d("map",function(f,t){x=this,a=[];for(i=0,l=x.length;i<l;++i)a.push(f.call(t,x[i],i,x));return a});d("filter",function(f,t){x=this,a=[];for(i=0,l=x.length;i<l;++i)f.call(t,x[i],i,x)&&a.push(x[i]);return a});d("forEach",function(f,t){x=this;for(i=0,l=x.length;i<l;++i)f.call(t,x[i],i,x)});d("reduce",function(f,v){x=this,v=v!==void 0?v:x.shift();for(i=0,l=x.length;i<l;++i)v=f(v,x[i],i,x);return v});d("every",function(f,t){return!!this.reduce(function(p,c){return p&&f.call(t,c)},1)});d("some",function(f,t){return!!this.reduce(function(p,c){return p||f.call(t,c)},0)})})(function(n,f){if(![][n])Array.prototype[n]=f})

追記 (2018/06/11 01:59)

改めてGistに起こしました。
ES5 or above polyfills for ES3 (It's not compatible with ECMA-262 strictly) · GitHub

lenという変数を定義してまでいちいちthis.lengthを代入している理由は、賢明な諸兄はもちろんご存知かと思いますが、forループで毎回Array#lengthにアクセスしてパフォーマンスが落ちるからです。
なので最初に長さを持ってしまいます。
その結果、高階関数に渡す関数がオブジェクト自身の要素数に副作用を起こすものである場合、意図しない動作になります。

まぁ自分しか使わないのでどうでもいいんですが。