JavaScriptのthisとは何か
開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質
- 作者: Cody Lindley,和田祐一郎
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/06/19
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
thisとは
もっとも基本的な考え方は、
this
とは、関数を呼び出すオブジェクトへのリンク。this
とは、関数のスコープ内で有効な値で、実行中の関数をプロパティまたはメソッドとして保持しているオブジェクトへの参照である。
よって、実際にthis
が何を指しているかは、関数定義時ではなく、実行時のコンテキストに依存する。
// このfooはグローバルオブジェクトのスコープで定義 var foo = 'I am foo of global scope'; // このfooはmyObjectのプロパティ var myObject = { foo: 'I am myObject.foo' } var sayFoo = function() { console.log(this.foo); }; myObject.sayFoo = sayFoo; // グローバルスコープから実行 sayFoo(); // 出力: 'I am foo of global scope', thisはグローバルオブジェクト // myObjectのメソッドとして実行 myObject.sayFoo(); // 出力: 'I am myObject.foo', thisはmyObject
※ 私が確認したところJSFiddleでは上記が一部期待通りに動かない。以下の動作が原因のようだが。
var foo = 'foo'; console.log(this.foo); // JSFiddle上では、出力: undefined
call()やapply()で実行する場合
function.call(object, . . . )
やfunction.apply(object, [. . .])
の形でfunction
を実行した場合、
関数の呼び出し元は第一引数のobject
になる。
つまり、関数内のthis
が参照するのは、このobject
だ。
call()やapply()で関数を呼び出すと、その関数内の
this
が指すオブジェクトを自分で設定することができる。
var myObject = {}; var myFunction = function(param1, param2) { this.foo = param1; this.bar = param2; console.log(this); } // 出力: Window {..., foo: "foo", bar: "bar"…} myFunction('foo', 'bar'); // 出力: Object {foo: "foo", bar: "bar"} myFunction.call(myObject, 'foo', 'bar'); myFunction.apply(myObject, ['foo', 'bar']);
入れ子関数の中のthis
二つ以上の関数を入れ子にした場合、前述の基本に反した動作をするので注意が必要だ。
入れ子関数の中の
this
は、グローバルオブジェクトを参照する。
var myObject = { func1: function() { console.log(this); // 出力: myObject var func2 = function() { console.log(this); // 出力: window(グローバルオブジェクト) }(); } }; myObject.func1();
この仕様はECMAScript5で修正されるという記載があったが、 現時点Chromeで確認してもこの通りだっった。
※ OSX El Capitan 10.11.3, Google Chrome 51.0.2704.84 (Official Build) (64 ビット) で確認
コンストラクタ関数の場合
コンストラクタ関数内のthis
が何を指すかは、new
演算子を使って実行したか否かで変わる。
var Person = function(name) { this.name = name } // newあり。インスタンス生成 var bonnie = new Person('Bonnie Parker'); console.log(bonnie); // 出力: Person {name: "Bonnie Parker"} // new無し。ただの関数実行 var clyde = Person('Clyde Barrow'); console.log(clyde); // 出力: undefined console.log(window.name); // 出力: Clyde Barrow
new
でコンストラクタを実行した場合、インスタンスが生成される。
そして、コンストラクタ関数内のthis
はこの生成されたインスタンスである。
new
無しの場合、ただの関数呼び出しであるので、上記の場合だとthis
はグローバルオブジェクト(window)になる。
参考
開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質
- 作者: Cody Lindley,和田祐一郎
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/06/19
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
AtomにEmmetを入れて効かなくなったキーバインディングを解決する
Emmet for Atom
Emmet — the essential toolkit for web-developers
Emmetというツールがある。
htmlやcssを独自の省略記法で入力(してから展開)できるのでコーディングが早くなる。
このEmmetをAtomで利用するパッケージがあるが、
これをインストールすると、既存のキーバインディングが一部上書きされて効かなくなってしまう。
Atomのキーバインディング修正
まず、気付いたのは
Ctrl
+e
: 行末へ移動Ctrl
+d
: カーソル次の文字削除
が効かなくなっていることだ。
Settings(Cmd
+ ,
) > Keybindings > your keymap file > keymap.cson
このkeymap.cson
ファイルを修正することで行う。(ファイルの場所は~/.atom/keymap.cson
)
You can override these keybindings by copying and pasting them into your keymap file
で、報告されている通り
#workaround for emmet mapping of expand-abbreviation '.editor:not(.mini)': 'ctrl-e': 'unset!' # remove all bindings 'ctrl-e': 'editor:move-to-end-of-line' # remap core binding 'alt-cmd-e' : 'emmet:expand-abbreviation' # alternate binding for emmet
と記載すればCtrl - e
で行末に移動できるようになる。
Ctrl - d
の方は
'.platform-darwin atom-text-editor:not([mini])': 'ctrl-d': 'unset!'
で次の文字削除ができるようになった。(私はOS X環境である)
参考
HTML/CSSを爆速コーディング Emmet入門 第1回 Emmetを薦める理由 | Adobe Creative Station
JSFiddleで外部jsライブラリを読み込む
開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質
- 作者: Cody Lindley,和田祐一郎
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/06/19
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
これまで、JSは対応が必要になったら使う程度で、じっくり学習したことがなかったので、 書籍で少しずつ勉強している。
JSFiddle
まず、この便利な実行環境のサービスをを知らなかった。 詳しくは 以下で。
Underscore.js
書籍の中でUnderscore.jsが紹介されている。
underscore.js または、圧縮された underscore-min.js を読み込んで利用する。
この説明も外部リソースに譲る。
http://dotinstall.com/lessons/basic_underscorejsdotinstall.com
JSFiddleで外部jsライブラリを読み込む
正当なやり方は、JSFiddleの画面左側にある"External Resources"から行う。
ここをクリックするとフォームが現れるので、ライブラリのURIを入力して+マークを押せば設定完了だ。
適切なCDNを利用する
外部ライブラリURIとして、https://raw.githubusercontent.com/jashkenas/underscore/master/underscore-min.js のような、github上のrawファイルを指定してはいけない。
Refused to execute script from 'https://raw.githubusercontent.com/jashkenas/underscore/master/underscore-min.js' because its MIME type ('text/plain') is not executable, and strict MIME type checking is enabled.
githubはCDNではないというアラートが表示される。実際、 MINEタイプが 'text/plain'
となり、jsのライブラリとして実行することができない。
単純に動作を確認したいのであれば、cdnjsでホストされているURIを指定したり、
RawGitというサービスで、githubから変換されたURIを得ることができるので、これを指定する。
上記サービスは、本番環境での利用は非推奨なので、そいう場合は自前でCDNを用意しなければいけない。
追記:Google Hosted Libraries
jQuery等、有名なライブラリはGoogleがホストしているので、ここから読み込むのが良さそうだ。
参考情報
githubのjavascriptやcssをrawgit使ってCDN経由ぽく読み込んで使ってみるテスト for jsfiddle - tweeeetyのぶろぐ的めも
Rubyでマージソート
クイックソートは、元データの並び方が悪いと、ソートの効率がバブルソートと大差がなくなってしまう(最悪時間計算量が O(n2))
こんな場合、より効率的にソートできるアルゴリズムがマージソートである。
マージソートの流れを簡単に説明すると、
- 全体を小さなブロックに区切る
- 全体を2つに分ける
- 分けた2つを、さらにそれぞれ2つに分ける
- ブロックの大きさが1になるまで分け続ける
- 各ブロック同士をソートしながらつなぎ合わせる(マージ)
- 全体がソート済みになったら完了
WikipediaにRubyの実装例があるので、コメント追記しながら処理を追ってみる。
def mergesort lst _mergesort_ lst.dup # 副作用で配列が壊れるので、複製を渡す end def _mergesort_ lst return lst if (len = lst.size) <= 1 # pop メソッドの返す値と副作用の両方を利用して、lst を二分する lst2 = lst.pop(len >> 1) _merge_(_mergesort_(lst), _mergesort_(lst2)) end # 二つの配列をソートしてつなぎ合わせる(マージ) def _merge_ lst1, lst2 len1, len2 = lst1.size, lst2.size result = Array.new(len1 + len2) a, b = lst1[0], lst2[0] # i: マージ後の配列resultのインデックス # j: lst1のインデックス # k: lst2のインデックス i, j, k = 0, 0, 0 loop { if a <= b result[i] = a i += 1 ; j += 1 break unless j < len1 a = lst1[j] else result[i] = b i += 1 ; k += 1 break unless k < len2 b = lst2[k] end } while j < len1 do result[i] = lst1[j] i += 1 ; j += 1 end while k < len2 do result[i] = lst2[k] i += 1 ; k += 1 end result end
実行例
[2] pry(main)> ary = ('a'..'z').to_a.shuffle => ["h", "w", "y", "j", "c", "f", "g", "o", "p", "b", "q", "s", "e", "d", "a", "i", "z", "k", "n", "v", "x", "m", "u", "r", "l", "t"] [3] pry(main)> mergesort(ary) => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
参考書籍
- 作者: 紀平拓男,春日伸弥
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2011/03/30
- メディア: 単行本
- 購入: 15人 クリック: 255回
- この商品を含むブログ (31件) を見る
問題を早く解くために検討すること
paizaというWebサービスがある。
ITエンジニアの転職情報サービスなのだが、出題された課題を制限時間内に解くコーディングスキルチェック機能もある。
純粋に問題を解くのが面白くて、少しやってみたのだが、
制限時間を与えられると、如何に効率的に早く解けるかを考えるようになる。
そこで気づいたことを以下に書いてみる。
問題を分解する
複雑で一筋縄ではいかない問題を考え続けていると、時間は経っているのに思うように進めていない時がある。
そんな時は、一度冷静になって問題を分解できないか考えてみる。
ひとかたまりに見える厄介な問題を、普通に対処できる問題の集合に分解できないだろうか。
プログラマなら関数を定義する。
対処できる問題の一つを全体から外に切り出す。
これで全体からは関数の中の詳細が見えなくなる。
適切に抽象化さていけば、問題全体は遥かにスッキリして、分かりやすい秩序が見えてくる。
さらに、解く者にとっても、小さいながらも確実に前進している実感がある。大きい問題の前に足踏みしていたような停滞感は軽減されるはずだ。
思えばプログラマに限った話ではない。
『バガボンド』で、宮本武蔵は一乗寺下り松で吉岡一門70名と斬り合うが、
ここで武蔵が考えたのは、 1対1の決闘を流れるように70回繰り返す、というやり方だった。(逆に吉岡側は1対70の決闘を一遍にやりたい)
- 作者: 井上雄彦,吉川英治
- 出版社/メーカー: 講談社
- 発売日: 2007/07/23
- メディア: コミック
- 購入: 4人 クリック: 6回
- この商品を含むブログ (88件) を見る
話が逸れたば、何かに時間がかかってしまっている時は、問題を切り分けるというやり方を検討したい。
急いで局所的な最適化をしない
私がコーディングする時の流れは大雑把に言うと以下だ。
期待通り動くように書く
よりシンプルに読みやすくなるように推敲する
推敲すること自体は大切な習慣だ。「動く」だけのコードをコミットされても困る。当然リファクタリングの余地がないか検討してみる。
しかし、私は時々、まだ問題全体が解けていないにもかかわらず、ある一行を推敲するために時間を使っている自分に気づくことがある。
なかなか定量的な評価は難しいが、この早い段階で局所的な最適化にかける時間があまり効果的ではないように感じられるのだ。
つまり、局所的には完璧でなくとも十分役立つコードを組み合わせて問題全体を解く => リファクタリング
という流れの方が完了まで早く辿り着けるのではないだろうか。
そういえば、37signalsの書籍『小さなチーム、大きな仕事』に似た記述があったので引用する。
建築家は、階の設計が終わるまではシャワーにどのタイルを使うか、また台所にどのメーカーの食器洗浄機を置くかなんて気にしない。 そうした細かなことは後で決めた方がいいとわかっているのだ。...(中略)
問題ではないところで気が取られてしまい、結局は変化していくようなことに対する決断に無駄な時間を費やすことになるのだ。 しばらくの間は細かいことは気にしないことだ。まず根本的なところを固めて、特殊なところは後で考えればいい。
小さなチーム、大きな仕事〔完全版〕: 37シグナルズ成功の法則
- 作者: ジェイソン・フリード,デイヴィッド・ハイネマイヤー・ハンソン,黒沢 健二,松永 肇一,美谷 広海,祐佳 ヤング
- 出版社/メーカー: 早川書房
- 発売日: 2012/01/11
- メディア: 単行本
- 購入: 21人 クリック: 325回
- この商品を含むブログ (36件) を見る
以上は、特に目新しい発見ではないが、ついつい頭から抜け落ちている時がある。
これらが強く意識されたのは制限時間が与えられたり、問題を解くのにかかった時間が計測されて明確に表示されるからだろう。
つまり、普段からポモドーロテクニックなりで工夫して、どの課題にどれだけ時間をかけたか明確に分かるようにしておけば、 早く解くための工夫、効率化は自然に生まれてくるのだと思う。 自分の時間管理に活かしたい。
- 作者: Staffan Noeteberg,渋川よしき,渋川あき
- 出版社/メーカー: アスキー・メディアワークス
- 発売日: 2010/12/16
- メディア: 単行本(ソフトカバー)
- 購入: 13人 クリック: 330回
- この商品を含むブログ (57件) を見る
アメリカの大学院でコンピュータサイエンスを学ぶためのPrerequisites
私は日本の大学工学部を卒業してプログラマになったのだが、専攻が情報工学系ではなかったので
「コンピュータサイエンスをきちんと体系的に学びたい」
という気持ちがあった。
仕事で必要になった内容を、その都度勉強して来たものの、「コンピュータサイエンスの基礎で理解が不十分な部分があるんだろうなあ」と感じていたからだ。
加えて、昔からアメリカに行きたいという気持ちもあったので、「アメリカの大学院に入学してコンピュータサイエンスを専攻するにはどうすればいいか」を調べてみた。
学費、GPA、推薦状、TOEFL、GRE等はコンピュータサイエンスに限らず、アメリカ留学で一般的なことなので今回は触れない。
少し自力で調べた後、専門家である栄陽子さんのところへ相談に行ってみた。
海外留学の情報はWeb上に溢れていて玉石混交と思うが、
栄陽子さんは留学関連の書籍を数多く書かれていて、読んでみると信頼できそうな方だと感じたからだ。
- 作者: 栄陽子
- 出版社/メーカー: 三修社
- 発売日: 2015/02/12
- メディア: Kindle版
- この商品を含むブログを見る
まず言われたのは「大学院に進む前に修めておくべきPrerequisites」だった。
「以下のような科目の単位を大学で取っておいてね」ということだ。(もちろん高GPAが望ましい)
- Data Structure and Algorithm データ構造とアルゴリズム
- C/C++, Java
- principles of programming languages プログラミング言語の原理
- Computer Architecture コンピュータアーキテクチャ
- Operating System オペレーティングシステム
- theory of computation 計算理論
- computer organization and assembly language programming コンピュータ構造とアセンブリ言語プログラミング
- differential and integral Calculus 微積分
- Linear Algebra 線形代数
- Discrete Mathematics, discrete mathematical structures 離散数学、離散構造
- Probability & Statistics 確率と統計
Prerequisitesは当然あるだろうと思っていたものの、どの科目かは自分の中で明確になっていなかった。 具体的な科目リストを出して頂いたことで、整理できて助かった。
そして、「働きながら、これらの科目に相当する単位を取得するには、どの大学が適切か?」について調べてみたのだが、
これについては、また別の記事で書くことにする。
C言語とRubyのクイックソート
クイックソートの流れを簡単に説明すると
- ある適当な値(文字・数)を決めて、それよりも大きいものは後ろへ、小さなものは前へ移動する。
- 2つに分けたそれぞれのグループの中で、また適当な値を決めて、それよりも大きいものは後ろへ、小さなものは前へ移動する。
- またそれぞれの...、と1,2の操作を繰り返す。
- それ以上ブループ分けできなくなったら終了。
本書に載っているC言語による実装を引用する(処理の意図が分かりやすいように、一部コメントは追記した)。
#include <stdio.h> #include <stdlib.h> #include <time.h> #define N 10 // データ件数 int sort[N]; void QuickSort(int bottom, int top, int *data) { int lower, upper, div, temp; if (bottom >= top) { return; } // 先頭の値を「適当な値」とする (これをピボット、と言う) div = data[bottom]; for (lower = bottom, upper = top; lower < upper;) { while (lower <= upper && data[lower] <= div) { // ピボットより大きい値を左から探す lower++; } while (lower <= upper && data[upper] > div) { // ピボット以下の値を右から探す upper--; } if (lower < upper) { // 見つかった値を入れ替え(ピボット以下は前、ピボットより大きい値は後の方へ集まる) temp = data[lower]; data[lower] = data[upper]; data[upper] = temp; } } // 最初に選択した値を中央に移動する temp = data[bottom]; data[bottom] = data[upper]; data[upper] = temp; // ピボットより前部分と後部分を同様にソート QuickSort(bottom, upper - 1, data); QuickSort(upper + 1, top, data); } int main(void) { int i; srand((unsigned int)time(NULL)); printf("ソート準備\n"); for (i = 0; i < N; i++) { // ランダムな値を配列に格納 sort[i] = rand(); printf("%d ", sort[i]); } printf("\nソート開始\n"); QuickSort(0, N - 1, sort); for (i = 0; i < N; i++) { printf("%d ", sort[i]); } printf("\nソート終了\n"); return EXIT_SUCCESS; }
正直、初見では処理を追って理解するのに少し時間がかかった。
class Array def quick_sort return self if self.length <= 1 pivot = shift left, right = partition { |e| e < pivot } left.quick_sort + [pivot] + right.quick_sort end end
C言語よりも遥かに処理を理解しやすい。人間の頭に優しい。
[4] pry(main)> ary = ('a'..'z').to_a.shuffle => ["r", "b", "u", "d", "h", "s", "m", "x", "e", "i", "k", "a", "j", "v", "o", "t", "z", "n", "c", "f", "w", "g", "y", "q", "l", "p"] [5] pry(main)> ary.quick_sort => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]