B-Teck!

お仕事からゲームまで幅広く

【雑記】あけましておめでとうございます

2018年ですね。
1月も2/3終わってますがいかがお過ごしですかね。

自分は今月から新しい職場で働いてるんですけど、
最初一週間は会社に慣れるための研修?のようなものだったので、
技術的な領域に触れるのは実質今週からでした。

前職がよく言えばある程度枯れた、悪く言えばレガシーシステムといった感じで、
そもそも対応ブラウザにIE8がいるとか、メンテしてるのがJava1.4とかそんな世界だったので、
まずそこそこモダンな環境のそろった職場にびっくり。
後はベンチャー系というか、Web系というか、そのへんの空気感なのかもしれないんですけど、
どこ行ってもみんな割と楽しそうというか、学校みたいな雰囲気なんですよね。
とりあえず慣れない私服勤務も含めて、早く慣れようとえっちらおっちらやってます。

そんな感じで、ここ二週間はしょっぱなで忙しい感じでしたけど、
そろそろ慣れてきた感じもあるのでこっちの更新時間にももう少し当てられたらいいなぁ…

次は自分がWeb領域に関わることになったので、
SEOについて勉強したことをざっくりまとめる記事になるような気がします。
(いくつか書きかけのストックもあるのでもしかしたら前後するかもですが)

ともあれ、2018年もゆるく更新していくのでどうぞよろしくお願いしますね。

【JavaScript】JavaScriptの変数・関数の巻き上げ(ホイスティング)

ホイスティングとは

JavaScriptは、関数内でvarで宣言した全ての変数が先頭で宣言したことになる。
この、宣言が先頭に移動する仕様をホイスティング、または宣言の巻き上げと呼ぶ。
宣言のみが先頭に移動し、代入された変数は移動しないため、
直感的ではない動作をするケースが有る。

変数の巻き上げ

var scope = 'global';

function test() {
  // 関数内のscopeの宣言のみが巻き上がるので
  // undefinedになる
  console.log(scope); 

  // 宣言は巻き上がっているので代入のみ行われる
  var scope = 'local';
  // 代入されたlocalが出力される
  console.log(scope);
}

test();

見た目上は最初のconsole.log(scope)ではglobalが出力されそうだが、
undefinedが出力される。

その原因は、test()が実際には下記のコードと等価になるせいだ。

function test() {
  // 宣言のみ先頭に移動
  var scope;
  // 値は代入されていないのでundefined
  console.log(scope); 

  scope = 'local';
  // 代入されたlocalが出力される
  console.log(scope);
}

ES2015の場合

実は、ES2015以降ではホイスティングを考慮せず記述することができる。
varではなくletまたはconstで宣言を行うと、宣言より前に使用された変数は
ReferenceErrorが出力されるようになった。

var scope = 'global';

function test() {
  // letで変数が宣言された場合、
  // 宣言は巻き上がらずReferenceErrorとなる
  console.log(scope); 

  let scope = 'local';
  console.log(scope);
}

test();

関数の巻き上げ

関数については、function文で宣言したものについては、その定義ごと巻き上がる。
変数に保持した場合は、変数のときと同様の動作となる。

function文で宣言した場合

定義ごと宣言が巻き上がるため、実行することができる。

function hoge() {
  // fuga()の定義も巻き上がっているので、  
  // 使用することができる。
  fuga();
  function fuga() {
    console.log('fuga');
  }
}

hoge();

varで宣言した場合

宣言のみ巻き上がるが、実行不可能。

function hoge() {
  // fugaの宣言のみ巻き上がるが、
  // undefinedのため関数として実行することができず
  // TypeErrorが発生する
  fuga();

  var fuga = function () {
    console.log('fuga');
  }
}
hoge();

letconstで宣言した場合

巻き上がらないため、実行不可能。

function hoge() {
  // letで宣言しているため定義が巻き上がらず、
  // ReferenceErrorが発生する
  fuga();

  let fuga = () => {
    console.log('fuga');
  }
}

hoge();

【雑記】2017年お疲れ様でした

今年も1年お疲れ様でした。
この1年は、引越し、結婚、母の一周忌、転職と盛りだくさんの1年で、
知人、友人の方には色々とご迷惑、ご心配をおかけしました。

結婚

6月に一緒に住みはじめて、7月に入籍した妻とは、
やはり初めての共同生活なのでぶつかるところもあるけれど、
都度解決しながら前に進めてるのかなと思います。
今後もこうやって一歩ずつ関係を深めていければいいのかなと思います。

転職

来年は1月から新しい職場での勤務で、Androidアプリやるのかな?と思ってたら、
実は配属先はWebアプリの部署だったので、あんまりやってることは変わらなそうです。
とはいえ、扱う領域がBtoBからBtoCに変わったので、今まで以上に考えることが多くなりそう。
ちょっと不安な感じです。
また、これまでの職場、常駐先の皆様にはお世話になりました。
今後も関係の続く方は改めてよろしくお願いしますね。

ブログ

このブログは、今月も無事月間1万アクセスを超えました。
世間の仕事納めの影響かここ二日は落ち込んでますが…。
このペースだと今年度末の3月までには累計30万PV行くかなって感じです。
大したことも書いてないブログですが、今後もよろしくお願いいたします。 f:id:beatdjam:20171229231931j:plain

皆さん、来年もよろしくお願いいたします。
良いお年を!

【本】自殺喫茶を読んだ

自殺喫茶

自殺喫茶

長らく積んでいた自殺喫茶を読了した。

ダウナーで問題を抱えた主人公が、変わり者の女性と、
そうではないクラスメイトに囲まれ、問題に直面し、解決し、成長する。
話の流れとしては、よくあるボーイミーツガール物といった感じで、
タイトルから想像したような重苦しさよりは、青春モノの爽やかさがあった。

作者のKDPデビュー作ということで、端々に荒削りさはあるものの、
キャラクターの個性や会話の雰囲気など最近の作品に通じる空気感もあった。

非常に残念な点として、最後まで読み終わっても回収されない伏線が多数あるという点がある。
続刊が販売される予定だったけれど、事情により発売されず、原稿も消失してしまったということで、
何とも言えないもやもや感が残る。

作中に登場した自殺屋というのは、
現実の日本に存在していれば自殺幇助罪によって処罰の対象となりうる。
とはいうものの、ベルギーなどの積極的安楽死、制度による自殺とその幇助が合法な国もある。
人が自分の死を選ぶことは正しいのかという疑問には恐らく答えは無いのだと思う。
生きていくことにどうしても目的を見いだせない人、生きていくことが困難な人、
日常が苦痛に満ちていて死に希望を見ている人...
理由は様々であるだろうし、個々人の選択は尊重されるべきだけども、
目の前の知人がその道を選ぼうとしていたら多分絶対に止めようとしているなと思うので、
そういった感情を抱いていたキャラクターにはとても共感した。

【JavaScript】最大値と最小値を指定して範囲内の値を持った配列を作成する(ES2015対応版)

この記事は下記の記事をES2015対応機能でリライトしたものです。 beatdjam.hatenablog.com

/** 
* range
* 範囲内の整数値を持った配列を作成する
* @param {number} max 範囲の最大値
* @param {number} min 範囲の最小値(デフォルト値:0)
* @param {function} filer フィルタ関数(デフォルト値:全てtrue)
* @return {array} 作成した配列
*/
function range(max,min = 0,filter =(e)=>{return true}){
  return [...Array(max+1).keys()]
            .filter(e=>{return e>=min})
            .filter(filter);
}
console.log(range(5));
// 第一引数までの配列を作成する
// Array [ 0, 1, 2, 3, 4, 5 ]
console.log(range(5,1));
// 第二引数から第一引数までの配列を作成する
// Array [ 1, 2, 3, 4, 5 ]
console.log(range(5,1,e=>{return e%2 === 0}));
// 第二引数から第一引数までのうち、第三引数の条件に合致する配列を作成する
// Array [ 2, 4 ]

前回は(最小値、最大値、フィルタ関数)という引数の並びだったけど、
最大値までの連番を作る方が用途として多かったので、
(最大値、最小値、フィルタ関数)という並びに変更した。

処理の概要は、

  • Array(max*1)で最大値までの長さの配列を生成
  • keys()で要素の添字のArray Iteratorオブジェクトを取得
  • スプレッド演算子Array Iteratorオブジェクトを展開して、
    0から最大値までの配列を作る
  • 最小値が指定されていなければ0以上、
    指定されていれば最小値以上の値でfilter()する
  • フィルタ関数が指定されていなければ全てtrue、
    指定されていればフィルタ関数でfilter()する

【JavaScript】thisの種類

JavaScriptthisにはいくつかの種類があり、状況によって動作が変わる。
現在はおおまかに分けて5つ?

コンストラクタ呼出し

function Func1(arg1, arg2){
  this.arg1 = arg1;
  this.arg2  = arg2;
}

let obj1 = new Func1('test', 123456);
// obj1.arg1 => 'test'
// obj1.arg2 => 123456

Functionオブジェクトのコンストラクタをnewで呼び出してインスタンスを生成する場合。
この文脈でのthisインスタンス自身を指す。

メソッド呼出し

let obj = {
  val : 'test',
  func1 : function(){
    console.log(this.val);
    // =>'test'
  }  
}
obj.func1();

オブジェクトの中のメソッドからthisを参照した場合。
メソッドの存在するオブジェクトをthisとする。

関数呼び出し

function func1(arg1, arg2){
  this.arg1 = arg1;
  this.arg2  = arg2;
}

func1('test', 123456);
console.log(window.arg1);
//windowオブジェクトにプロパティがセットされる。
//windowはグローバルオブジェクトのため、プロパティもグローバルになってしまう。

Functionオブジェクトをnewをつけずにそのまま呼び出した場合。
普通に呼び出される関数の中でthisを使用すると、Function内にスコープが限定されず、
windowオブジェクトを参照してしまう。

Strictモードを使用した場合はエラーが発生して利用できなくなるため安全。

'use strict';
function func1(arg1, arg2){
  this.arg1 = arg1;
  this.arg2  = arg2;
}

func1('test', 123456);
console.log(window.arg1);
//Exception: TypeError: this is undefined

関数呼び出しの注意点

let obj = {
  val : 'obj prop',
  func1 : function(){
    // メソッド呼び出し
    // obj.val
    console.log(this.val);
    
    function nestedFunc(){
      // 関数呼び出し
      // window.val
      console.log(this.val);      
    }
    nestedFunc();
  }  
}

window.val = 'window.prop';
obj.func1();

メソッド呼出しの中で関数呼出しを行った場合でも、単体で関数呼出しを行った場合と同様にthiswindowを指してしまう。
ECMASript6(ECMAScript2015)の場合は、後述するアロー関数を用いることで、thisを一意にすることができる。
それ以外の環境の場合、下記のような対処法がある。

  • self
let obj = {
  val : 'obj prop',
  func1 : function(){
    let self = this;
    console.log(self.val);
    // =>'obj prop'
    
    function nestedFunc(){
      console.log(self.val);
    // =>'obj prop'
    }
    nestedFunc();
  }  
}

window.val = 'window.prop';
obj.func1();

参照するべきthisを変数に保持してしまう方法。
慣例的にselfthatthisなどの変数名が利用される。
単純だがわかりやすい。

  • bind()
let obj = {
  val : 'obj prop',
  func1 : function(){
    console.log(this.val);
    
    function nestedFunc(){
      console.log(this.val);      
    };
    // thisをbindしたfunctionを定義
    let bindFunc = nestedFunc.bind(this);
    bindFunc();
    //またはそのまま実行
    nestedFunc.bind(this)();
  }  
}
window.val = 'window.prop';
obj.func1();

参照されるべきthisの値をbind()で束縛した新しいFunctionオブジェクトを作成する。 作成した関数を実行することで別のものを参照しないようにする方法。
部分適用はしやすいが、少し可読性が良くない。

個人的には、どちらかと言えばselfの方が嬉しい気持ちになる。

applyまたはcallで呼び出し時

let obj = {
  val : 'obj prop',
}

function func1(){
  console.log(this.val);
}  

func1();
// => undefined
func1.call(obj);
// => 'obj prop'
func1.apply(obj);
// => 'obj prop'

Function.prototype.apply()Function.prototype.call()は、
引数で与えられたオブジェクトをthisとしてセットして呼び出すことができる。

applycallの違い

let obj = {
  val : 'obj prop',
}

function func1(arg1, arg2){
  console.log(this.val + ' ' + arg1 + ' ' + arg2);
}

func1.call(obj, 'test', 'args');
// => 'obj prop test args'
func1.apply(obj, ['test', 'args']);
// => 'obj prop test args'

両方共第一引数はthisにセットする値だが、下記の点が異なる。
* callは第二引数以降に与えた引数が、呼び出すオブジェクトに引数として渡される。
* applyは第二引数に渡した配列の中身が引数として渡される。

アロー関数で呼び出し時

let obj = {
  val : 'obj prop',
  func1 : function(){
    console.log(this.val);
    // アロー関数は、外側のthisを自身のthisとして扱うため、
    // func1のthis.val = obj.valを参照する。
    let nestedFunc = () => {
      console.log(this.val);      
    }
    nestedFunc();
  }  
}

window.val = 'window.prop';
obj.func1();

ECMASript6(ECMAScript2015)以降では、アロー関数という仕組みが提供されている。 アロー関数は、自分が宣言されているスコープのthisを引き継いで関数内でthisとして扱うため、
直感に反しないthisの扱い方をすることができる。

アロー関数の注意点

window.val = 'window val';
let func = () => {
  console.log(this.val);      
}

let obj2 = {val : 2};
func();
// => windos val
func.call(obj2);
// => windos val
func.apply(obj2);
// => windos val

前述したように、アロー関数のthisは外側のスコープのthisを引き継いでセットされる。
アロー関数の場合、callapplyで呼び出してもこの前提は変わらず、thisを置き換えることが出来ない。

【JavaScript】今までブログに載せたJavaScriptのスニペットをまとめた

今までぽろぽろ書いてたJavaScriptとかを、ちょっと書き直したりしてまとめた。
書いたあとで知ったこととか踏まえるともっときれいに書けたり短くかけたりして面白かった。

MyToolBox/js at master · beatdjam/MyToolBox · GitHub

自分が過去に書いたコードはやっぱり過去のコードなので、
時折見返して手を入れたり、思い出して今ならどうするかとか考えると学びがある。