読者です 読者をやめる 読者になる 読者になる

abcdefGets

ゲッツ!

typescript 2.3 RC

typescript 2.3 rcがアナウンスされた

主な変更点は以下の通り

–strictオプションの追加

以下の型チェックオプションを有効にする

  • –noImplicitAny
  • –strictNullChecks
  • –noImplicitThis
  • –alwaysStrict

以下の様に部分的にOFFにもできる

{
    "compilerOptions": {
        "strict": true,
        "noImplicitThis": false
    }
}

generateor、iteratorのES3、ES5対応

--downlevelIterationフラグをONにすることで、
generatorとiteratorがES3、ES5共にトランスパイルできるようになった。

Async generators & iterators

ES Proposalのasync iteratorとasync generatorに対応した。

async iteratorの構文

for await (let item of items) {
    /*...*/
}

async generatorの構文

async function* asyncGenName() {
    /*...*/
}

ただし、Async generatorとAsync iteratorを使うためには、 Symbol.asyncIteratorが必要なので、以下のようにして、polyfilを作る必要がある。

(Symbol as any).asyncIterator = Symbol.asyncIterator || Symbol.from("Symbol.asyncIterator");

(Symbol as any).asyncIterator = Symbol.asyncIterator || "__@@asyncIterator__";

まとめ

遂に(async) generator、iteratorがES3、ES5でも使えるようになってよかったね。

gulp-uglifyでプロパティ名をmangleする

全然ドキュメントがなかったので備忘録。

ClosureCompilerみたいにプロパティ名もmangleしたい。
こんなの

const x = {
  doSomething() {return ...}
  doNothing() {}
}
x.doSomething();
x.doNothing();
const x = {
  a() {return ...}
  b() {}
}

x.a();
x.b();

設定

gulp.task('minify', () => {
  const uglify = require('gulp-uglify');
  gulp.src(['src/index.js'])
    .pipe(uglify({
      mangle: true,
      compress: true,
      mangleProperties: {
        ignore_quoted: true
      }
    }));
});

これが基本。

で、特定のプロパティ名のリネームを防ぎたい場合は、reservedという機能を使う

gulpfile.js

gulp.task('minify', () => {
  const uglify = require('gulp-uglify');
  const Uglify     = require('uglify-js');

  let reserved = Uglify.readReservedFile('./reserved.json');
  reserved = Uglify.readDefaultReservedFile(reserved);

  gulp.src(['src/index.js'])
    .pipe(uglify({
      mangle: true,
      compress: true,
      mangleProperties: {
        reserved: reserved.props,
        ignore_quoted: true
      }
    }));
});

reserved.json

{
  "vars": [  ],
  "props": [ "doSomething", "doNothing" ]
}

これでdoSomethingdoNothingはmangleされなくなる。

上記の記述の

let reserved = Uglify.readReservedFile('./reserved.json');
reserved = Uglify.readDefaultReservedFile(reserved);

の部分ではUglifyjs2が必要なので、別途npm installしてくだはい。
後は必要なプロパティ名をガンガンpropsに突っ込んでいけばOK。

ただ、ClosureCompilerもそうだけど、プロパティ名のmangleにはそれなりのリスクがあるので、
コンパイル後にも統合テストをしたほうが良い。

まとめ

gulp-uglifyが不親切でつらい。

ES6のComputedPropertyNameとトランスパイラ

ES6のComputed Property Nameは非常に便利だが、トランスパイラを併用すると問題が起きがちである。
それを確認していく。

Base

const SYMBOL = Symbol('foo-bar-baz');

const obj = {
    [SYMBOL]: 1,
    name: 'brn',
    job: 'engineer'
}

typescript

var SYMBOL = Symbol('foo-bar-baz');
var obj = (_a = {},
    _a[SYMBOL] = 1,
    _a.name = 'brn',
    _a.job = 'engineer',
    _a);
var _a;

babel

'use strict';

var _obj;

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var SYMBOL = Symbol('foo-bar-baz');

var obj = (_obj = {}, _defineProperty(_obj, SYMBOL, 1), _defineProperty(_obj, 'name', 'brn'), _defineProperty(_obj, 'job', 'engineer'), _obj);

これを見るとすぐわかるが、トランスパイルされたjs内では、
オブジェクトを一度構築してからプロパティを追加している。
SYMBOLプロパティは仕方ないが、他のプロパティも別途追加になっているため、(Babelはもっとひどくて、definePropertyになっている…)
例えば、v8ではDictionaryモードになってしまう。つまりパフォーマンスに重大な問題を引き起こす。
そのため、パフォーマンスクリティカルな場合はComputedPropertyの利用は避けよう。

Ecmascript decorator を使ってキャッシュする

表題の通り ES proposal のdecoratorをつかってメモ化のようなことをするライブラリを前に書いたので、 キレイにして、GitHubに公開した。

GitHub - brn/cache-decorator: javascript method/function cache decorator.

インストール

npm install cache-decorator --save

yarn add cache-decorator --save

使い方

メソッドのキャッシュ

For javascript/babel

import {
  cache,
  CacheType,
  CacheScope
} from 'cache-decorator';

class Example {
  @cache({type: CacheType.MEMO, scope: CacheScope.INSTANCE})
  expensiveCalc(args) {...}
}

For typescript

tsconfig.json

{
  "compilerOptions": {
    ...
    paths: {
      "cache-decorator": ["node_modules/cache-decorator/lib/index.d.ts"]
    }
  },
}
import {
  cache,
  CacheType,
  CacheScope
} from 'cache-decorator';

class Example {
  @cache({type: CacheType.MEMO, scope: CacheScope.INSTANCE})
  public expensiveCalc(args) {...}
}

関数のキャッシュ

For javascript/babel

import {
  fcache,
  CacheType
} from 'cache-decorator';

const cachedFn = fcache((args) => {
  ...
}, {type: CacheType.MEMO})

For typescript

tsconfig.json

{
  "compilerOptions": {
    ...
    paths: {
      "cache-decorator": ["node_modules/cache-decorator/lib/index.d.ts"]
    }
  },
}
import {
  cache,
  CacheType,
  CacheScope
} from 'cache-decorator';

const cachedFn = fcache((args: Object) => {
  ...
}, {type: CacheType.MEMO})

cacheのオプション引数

interface CacheOption {
  type?: CacheType;
  scope?: CacheScope;
  ttl?: number;
  compare?: (prev: any, next: any) => boolean;
}

type: CacheType

default value: CacheType.SINGLETON

SINGLETON

  • キャッシュを探す
  • もしキャッシュがあればその値を変えす。なければメソッドを実行して、その結果を保存する.
  • 結果を返す

MEMO

  • キャッシュを引数のリストとともに検索し、保存されている引数リストと比較する.
  • もし値が見つかれば結果を返し、見つからなければメソッドを実行し、結果と引数のペアをキャッシュに保存する。
  • 結果を返す

scope: CacheScope

default value: CacheScope.INSTANCE

INSTANCE

キャッシュされた値はインスタンス毎の領域に保存され、インスタンス間で値が共有されない。

GLOBAL

すべてのキャッシュはグローバルな領域で管理され、すべてのインスタンスで値が共有される。

ttl: number

default value: null

指定されたミリ秒でキャッシュを破棄する。

compare: Function

default value: (a, b) => a.length === b.length && a.every((v, i) => v === b[i])

CacheType.MEMOが指定された場合に引数を比較するのに使う比較関数。

まとめ

メモ化は結構面倒なので、decoratorというシンプルな形でまとめられてよかった。
使い勝手も悪くないので結構重宝している。

Function.prototype.bind のパフォーマンスについて

ふとパフォーマンスが気になったので調査した。
記憶が正しければ、callよりも遅いはず。

というわけでレッツ検証

事前準備

package.json

{
  "name": "bench",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "author": "brn",
  "license": "MIT",
  "devDependencies": {
    "benchmark": "^2.1.3"
  }
}

bench.js

/**
 * @fileoverview
 * @author Taketoshi Aono
 */

const Benchmark = require('benchmark');
const suite = new Benchmark.Suite;

const bind = (() => {}).bind({});
const bindWithArgs = ((a, b, c) => {}).bind({}, 1, 2, 3);
const call = () => {};
const callargs = {};
const callcall = () => call.call(callargs)
// add tests
suite
  .add('bind', () => {
    bind();
  })
  .add('bind with args', () => {
    bindWithArgs();
  })
  .add('call', () => {
    callcall();
  })
  .on('cycle', function(event) {
    console.log(String(event.target));
  })
  .on('complete', function() {
    console.log('Fastest is ' + this.filter('fastest').map('name'));
  })
  .run({ 'async': true });

node bench.js

結果は V8限定ですが,

bind x 43,404,436 ops/sec ±2.07% (83 runs sampled)
bind with args x 35,140,882 ops/sec ±2.48% (84 runs sampled)
call x 59,048,983 ops/sec ±1.13% (85 runs sampled)
Fastest is call

となりました。 予想通り、callが最速。

ただ、気になるのはbindに引数を束縛した場合さらに遅くなる点。

気になるので調べました。

V8のコミットID
bdf32cf1bc96982ff5a22195d874617ffae03e79
時点のものです。

コード検証

色々さがして、とりあえずx87のbuiltin-x87.ccを見る。
x64でもいいけど、とりあえずx87を調べましょう。

でこれが、bindで生成した関数を呼び出すアセンブラコード生成関数。

void Builtins::Generate_CallBoundFunctionImpl(MacroAssembler* masm,
                                              TailCallMode tail_call_mode) {
  // ----------- S t a t e -------------
  //  -- eax : the number of arguments (not including the receiver)
  //  -- edi : the function to call (checked to be a JSBoundFunction)
  // -----------------------------------
  __ AssertBoundFunction(edi);

  if (tail_call_mode == TailCallMode::kAllow) {
    PrepareForTailCall(masm, eax, ebx, ecx, edx);
  }

  // Patch the receiver to [[BoundThis]].
  __ mov(ebx, FieldOperand(edi, JSBoundFunction::kBoundThisOffset));
  __ mov(Operand(esp, eax, times_pointer_size, kPointerSize), ebx);

  // Push the [[BoundArguments]] onto the stack.
  Generate_PushBoundArguments(masm);

  // Call the [[BoundTargetFunction]] via the Call builtin.
  __ mov(edi, FieldOperand(edi, JSBoundFunction::kBoundTargetFunctionOffset));
  __ mov(ecx, Operand::StaticVariable(ExternalReference(
                  Builtins::kCall_ReceiverIsAny, masm->isolate())));
  __ lea(ecx, FieldOperand(ecx, Code::kHeaderSize));
  __ jmp(ecx);
}

ここで注目したいのが、
Generate_PushBoundArguments(masm);
の部分
束縛した引数をPushするコードだと想像できる。

Generate_PushBoundArgumentsがこちら。

void Generate_PushBoundArguments(MacroAssembler* masm) {
  // ----------- S t a t e -------------
  //  -- eax : the number of arguments (not including the receiver)
  //  -- edx : new.target (only in case of [[Construct]])
  //  -- edi : target (checked to be a JSBoundFunction)
  // -----------------------------------

  // Load [[BoundArguments]] into ecx and length of that into ebx.
  Label no_bound_arguments;
  __ mov(ecx, FieldOperand(edi, JSBoundFunction::kBoundArgumentsOffset));
  __ mov(ebx, FieldOperand(ecx, FixedArray::kLengthOffset));
  __ SmiUntag(ebx);
  __ test(ebx, ebx);
  __ j(zero, &no_bound_arguments);
  {
    // ----------- S t a t e -------------
    //  -- eax : the number of arguments (not including the receiver)
    //  -- edx : new.target (only in case of [[Construct]])
    //  -- edi : target (checked to be a JSBoundFunction)
    //  -- ecx : the [[BoundArguments]] (implemented as FixedArray)
    //  -- ebx : the number of [[BoundArguments]]
    // -----------------------------------

    // Reserve stack space for the [[BoundArguments]].
    {
      Label done;
      __ lea(ecx, Operand(ebx, times_pointer_size, 0));
      __ sub(esp, ecx);
      // Check the stack for overflow. We are not trying to catch interruptions
      // (i.e. debug break and preemption) here, so check the "real stack
      // limit".
      __ CompareRoot(esp, ecx, Heap::kRealStackLimitRootIndex);
      __ j(greater, &done, Label::kNear);  // Signed comparison.
      // Restore the stack pointer.
      __ lea(esp, Operand(esp, ebx, times_pointer_size, 0));
      {
        FrameScope scope(masm, StackFrame::MANUAL);
        __ EnterFrame(StackFrame::INTERNAL);
        __ CallRuntime(Runtime::kThrowStackOverflow);
      }
      __ bind(&done);
    }

    // Adjust effective number of arguments to include return address.
    __ inc(eax);

    // Relocate arguments and return address down the stack.
    {
      Label loop;
      __ Set(ecx, 0);
      __ lea(ebx, Operand(esp, ebx, times_pointer_size, 0));
      __ bind(&loop);
      __ fld_s(Operand(ebx, ecx, times_pointer_size, 0));
      __ fstp_s(Operand(esp, ecx, times_pointer_size, 0));
      __ inc(ecx);
      __ cmp(ecx, eax);
      __ j(less, &loop);
    }

    // Copy [[BoundArguments]] to the stack (below the arguments).
    {
      Label loop;
      __ mov(ecx, FieldOperand(edi, JSBoundFunction::kBoundArgumentsOffset));
      __ mov(ebx, FieldOperand(ecx, FixedArray::kLengthOffset));
      __ SmiUntag(ebx);
      __ bind(&loop);
      __ dec(ebx);
      __ fld_s(
          FieldOperand(ecx, ebx, times_pointer_size, FixedArray::kHeaderSize));
      __ fstp_s(Operand(esp, eax, times_pointer_size, 0));
      __ lea(eax, Operand(eax, 1));
      __ j(greater, &loop);
    }

    // Adjust effective number of arguments (eax contains the number of
    // arguments from the call plus return address plus the number of
    // [[BoundArguments]]), so we need to subtract one for the return address.
    __ dec(eax);
  }
  __ bind(&no_bound_arguments);
}

想像よりなが~いーーー

__ j(zero, &no_bound_arguments);で引数束縛がなければjmpするコードを生成

その後は、BoundArgumentsを保存するstack領域を確保、リターンアドレスをstackに押し込んで、
BoundArgumentsをstackに押し込む。
これをループで行っているのでだいぶ遅そう。

Function.prototype.bindが引数束縛つきで遅くなるのはこれが理由っぽい。

まとめ

  • 最速は() => fn.call(this)形式
  • 次点でfn.bind(this)
  • 引数の束縛はかなり遅くなるので、() => fn.call(this, ...)のがおすすめ。

アロー関数のおかげでFunction.prototype.bind使い所がない。

React.js meetup × React Native meetup に参加した

パネルディスカッション

参加者

@yosuke_furukawa

@koba04

@yositosi (Togetter CEO)

@janus_wel (CureApp CTO)

全体的にReactNativeはWebの技術をどう活かせるか、
ワンソース・マルチユースができるか等の話だった。
あとReactNativeの将来性とか 全体的にReactNativeはWebの技術をどう活かせるか、
ワンソース・マルチユースができるか等の話だった。
あとReactNativeの将来性とか

React Nativeの利点

アプリの開発に、使い慣れたReactの技術を活かせるのは大きいのかもしれない。
そのため、Webの開発者がアプリを開発するときには向いている。

苦労したこと

TogetterさんはReactNativeでアプリを実装した時に、広告の表示部分のブリッジが用意されていなかったので、
自分たちでブリッジを作った
でも、見よう見まねで作ったらすんなり動いた。

ソース共通化できるのか

画面・ページレベルのコンポーネントは分ける必要がある。
共通化するよりも、プラットフォーム毎の最適なデザインを目指すべき。
一つ一つの細かいコンポーネントは再利用できる。

ngCore

懐かしかったw
ReactNativeと似ている。

ngCoreが犯した間違え

  • クローズドソースだった
  • ホットコードプッシュの正当性

AppleからのBanは怖い

React Hot Loaderで開発を更に加速する

@endam

React Hot Loaderの使い方。
あんま使ったことなかったので、ちゃんと使わねばと思った。
jspmやめたら検討しよう。

Inside Bdash

@hokkacha

Bdashの作者による、Bdashの実装アーキテクチャ
Fluxで実装していて、ページ毎にActionCreatorを作り、コントローラにしていた。
SpringとかあのへんのMVCの残り香がした。

hyperapp

@jbucaran

qiita.com

hyperappの作者による、hyperappの紹介。
わずか1kbのRedux + VirtualDomのライブラリ。
たったこれだけのコード量で実装できているのに驚愕。
全コード合わせても300行ほどらしい。
普通に選択肢になるかもしれない。 

小回りの聞くWebViewの使い方

@niryuu

speakerdeck.com

WebViewでWebGLを使ったアプリの実装についての話

ReactNativeでFirebaseのネイティブSDKを操作する

t.co

FirebaseをReactNativeから使う方法についての発表
自分たちはNativeからWebViewのFirebaseを使っていたので、逆の話も聞けてよかった。

Androiderから見るReactNative

@operandOS

t.co

ガチのAndroid勢がReactNativeを使ってどうだったのかの発表。
やっぱ、Nativeの人はこれをメインで使う理由は無いよなぁと思った。
今からReactNative使う上での一番の障害かもしれない。
あと、ネイティブのコンポーネントとReactNativeのブリッジ書けばスター一杯のチャンスらしい。
書こうかな。

SideCI(スポンサーLT)

LintのCIツール。GitHubのPRから実行したり

CureApp(スポンサーLT)

CureAppの紹介。医師からjavascriptエンジニアになった異色のCEOの話。
面白そうなスタートアップだった。

まとめ

ReactNativeは非ゲームなら使っていってもいいと思う。
特にReduxで実装している場合は、Reducer等のドメインは使いまわせるはずなので、
インターフェースのディレクトリだけ分けて、後は使いまわすって言うのがかっこいいかも。
あとは、operandOSさんの発表にあったけども、
ガリガリにチューニングしたいところはネイティブで書いて、
それ以外の面倒なところはReactNativeで書くのがいいかもしれない。

とりあえず、ReactNativeは触っとかないとまずそう。

【RECRUIT Technologies NIGHT vol.5】リクルート流フロントエンド開発 に参加してきた

React / Redux を活用したリクルートテクノロジーズのフロントエンド開発

古川陽介 さん(@yosuke_furukawa)

speakerdeck.com

以下メモ

言いたいこと

フレームワークは作るものに合わせて作る

リクルートのWeb

トップページに検索があり、
検索するとリストビューがでるような一般的なwebサイト

典型的なWebサイト

最近はチャットできたり、
インタラクティブなやつができた。

要望

  • とにかくパフォーマンス
  • NatieアプリっぽいLook & Feel
  • Interactiveな動き(Chat や いいね通知)

これまでのWebフレームワークでは無理

React x Redux x Node でフレームワークを作っている

事例

フロントエンドを作る上でも気をつけていること

  • サーバでもクライアントでもレンダリングする
  • Historyを壊さない
    • 戻る・進むで状態を壊さない
    • 戻る・進む中にはレンダリングスキップする
  • Consumer Driven Contract
    • 従来バックエンドが決めていたAPIの仕様をフロントエンドが手動して要求を書く
    • フロントエンドが使いやすいAPIになる。

【翻訳】リッチなWebアプリケーションのための7つの原則 - from scratch

フロントエンドエンジニアが React Native を触ってみた話

90%ほどiOS/Androidで共有できる
Hot/Live Reloadingが早い

疑似要素が使いたい
疑似要素は無理

shorthandも使えない

cssがちょっと違和感があるので戸惑うかもしれない。

iOS/Androidの違い

プラットフォーム毎にUI/UXのベストプラクティスは違う

iOS/Android

  • Platform.OSで分ける
  • ファイル自体を分ける

何故ReactNativeなのか

  • ユーザー数の多いアプリで採用されている
  • Webとのコード共有ができる

ReactNativeアーキテクチャ

WebのReactをつかったこと Objc,Javaの知識は今の所必要なしに使える。

ReactNativeは想像以上に良さそうだった。
簡単なアプリならWebの延長で作れそうでよい!
是非取り入れて評価してみたい。

リクルートのフロントエンド改革に挑んだ話

太田さん

前提

外注がZip納品して、SvnでBackendが開発されている

フロントエンドがViewを見れないので、CSSの影響調査をすることができない

というまあひどい状況

改革

SIerの意識改革の話。
保守的なエンジニアのやり方を変えるにはどんなものでもいいから定量的な指標が必要で、
たとえ、LOC(Lines of codes 要はコード行数)であれ、前提あれば指標にするしかない。
でなんとかやり方の改革に成功した話。

エンジニアは保守的な人が多いのはまあ結構思う。
ただ、論理的に何%良くなったとか、定量的な指標をしっかり準備しておけば、説得できる人も多いのは確かだなと思ったし、
これはとても参考になる。

成長

バックエンド寄りのフロントエンドの人がCSSを学習するのは早いけど、
フロントエンド寄りの人がバックエンドを学ぶのはやはり時間がかかる。

きちんとした学習コンテンツを用意すべきだった。

ここはかなり同意。
ただ、学習コンテンツがどんなものか、どう動機づけするかがかなりの課題だなと思う。

まとめ

リクルートテクノロジーズさんは、というかリクルートさんは結構外注が多いイメージだったが、
内部でもかなり進んだことにチャレンジしていたっていう印象を受けた。
特にReactNativeのところは今の業務にも活かしていきたい(難しいけど)
全体的に実りのある勉強会だったなぁ。