はじめに
プログラマの中には何故かJavaScriptを避けたがる人がいるように思います。
フロントエンドのマルチブラウザ環境や
変化の早さ、テストの難しさなどを敬遠しているのかなと思っています。
[JavaFX] Why, Where, and How JavaFX Makes Sense
だけど、個人的には言語だけ見るとそんな必要もないなと思うのです。
JavaScriptは素直で便利なやつだと思ってます。
そこで、今回はJavaエンジニアに贈るJavaScriptについて、
という気持ちを込めて記事を書きます笑
前提
なんちゃってJavaScript使いなので、正しくない部分もあると思います。
また、ブラウザ上での操作を想定しています。
「普段Javaやってる僕がJavaScriptを調べてみた」ぐらいの内容と思ってください。
僕がどのぐらい普段JavaScript書いているかというと、
たまにajax処理とかUIの描画操作とかイベントハンドリングとかを
JavaScriptでやってcallbackヘルに悩ませられるレベルです。
基本的なところ
- ECMAScript エクマスクリプトと読むらしい。
JavaScriptの標準仕様とされています。
現在はECMAScript5が最新。ECMAScript6策定中。
ECMAScript4は放棄されたので、古い環境(具体的にはIE8とか…)
だとECMAScript 3っぽいです。で、僕の知識もECMAScript 3ぐらい。。
http://ja.wikipedia.org/wiki/ECMAScript
http://kangax.github.io/compat-table/es5/
- ブラウザのWeb API DOM documentやconsoleといったオブジェクトはDOMの仕様に則って、
ブラウザが提供しているAPIということだと思っています。
それをJavaScriptから使っている、ということ。多分。
- JavaScriptにはクラスが無い。
JavaScriptはクラスベースのオブジェクト指向ではないです。
オブジェクトが色んなものを構築しているシンプルな言語だと思います。
(クラス構文を用意する流れは各所であります)
Javaのやり方がJavaScriptで出来ないっていうのは根本的に違っていて、
JavaとクラスとJavaにおけるオブジェクト指向のことは忘れて
頭をクリアにして学んだほうが良いように思います。
それを踏まえて、Javaを普段使っているような人は
Javaだったらこういう手法でこういう目的があるけど
JavaScriptだとやり方わかんないなってなると思うので、
つらつら書いて整理してみることにします。
標準グローバルオブジェクト(カテゴリ別)
Javaで言うところのjava.langパッケージみたいな。どっからでもつかえるやつ。
各種類ごとに見ていきます。
とりあえずここらへん抑えておけばいいだろう、っていう
僕のお得意な発想です。
汎用コンストラクタ
- Array
- Boolean
- Date
- Function
- Iterator
- Number
- Object
- RegExp
- String
- Proxy
- ParallelArray
よく使うのはObject、Array、Dateとかですかね…。
ただ、Dateに関してはブラウザの日時(クライアント側の日時)なので、
システム日付とか呼ばれるものを利用する場合には
サーバー側から取得する必要があります。
非コンストラクタ関数
- encodeURI
- eval
- isNaN
- parseFloat
- parseInt
- uneval
このあたりもよく使う関数ですね。
evalを使うことはあまり推奨されていません。
文字列を関数として実行するものなので、悪意のあるインジェクションとか
気にしないとダメってことですね。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/eval
その他
- Infinity
- Intl
- JSON
- Math
- NaN
- undefined
- null
JSONとかはECMAScript 5からです。。。
ECMAScript 5なんですが、ブラウザによってはJSONをサポートしているものもあります。
ECMAScript 5 compatibility table
もろもろ詳しくは下記参照ということで。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects
グローバルオブジェクト
このあたりを覚えておくと良いと思います。
JavaScriptというよりはWebのAPIなところですが。。
- window ブラウザウィンドウの情報を持つ
- (window.)document DOM操作のための情報をもつ
- console ログ出力用のオブジェクト
- (window.)parent 親ウィンドウの情報をもつ
- (window.)opener ウィンドウを開いたウィンドウの情報を持つ
グローバルオブジェクトの関数
このあたりも、説明するまでもないかなぁという感じです。
- (window.)alert ウィンドウアラート表示
- (window.)open 別ウィンドウ表示
- (window.)close ウィンドウを閉じる
- (window.)confirm 承認ウィンドウ表示
- (window.)document.createElement DOMエレメント作成
- (window.)document.getElementById idでDOMエレメント取得
- (window.)document.getElementsByName nameでDOMエレメント取得
- (window.)document.getElementsByTagName tag名でDOMエレメント取得
- (window.)document.getElementsByClassName class名でDOMエレメント取得
- console.dir 対象を階層化してコンソールに出力
- console.log コンソールに一般タイプのログを出力
- console.time 時間計測用出力(開始)
- console.timeEnd 時間計測用出力(終了)
続いて、イディオム的なところを見ていきます。
ループ
オブジェクトの中身とか見たい時とかよく使います。僕は。
JavaScriptのオブジェクトは連想配列みたいに
キーバリュー構造になっているというのが重要ですね。
何入ってのかな~ってよくループして眺めます。
for(var key in obj ) { console.log("key=" + key + ": value=" + key[obj]); }
・ブラウザのデベロッパーコンソールとか使えばオブジェクトの中身は見えるがな
・console.dirというものがあってだな…
・hasOwnPropertyは使おう
というコメントをもらいました。仰るとおりですね(^^;
ということで、全然for - inのループを使う例としては良くないものでした。。。
console.dirはオブジェクトの中身を階層構造で一気に見れるので
keyとvalueもざっと見れるもののようです。
hasOwnPropertyを使わないとプロトタイプの親のプロパティも全てループしてしまう
ので、セオリーに反すると。なるほど。
サンプルコードもコメントでいただいてます。
var objA = { a: 1, b: 2 }; var objB = Object.create(objA); objB.c = 3; for(x in objB){ console.log(x); } // c, a, b for(x in objB){ if(objB.hasOwnProperty(x)){ console.log(x); } } // c // Object #keys#forEachを使う var m = {a: 1, b: 2}; Object.keys(m).forEach(k => console.log(k + ", " + m[k]));
Function
JavaScriptの関数の実体はFunctionというオブジェクトです。
ただ、それを表現する方法、呼び方がいっぱいあるだけです。多分。
関数の書き方に関しては主に巻き上げが起こるか、起こらないかが
重要なポイントのようです。
http://bonsaiden.github.io/JavaScript-Garden/ja/#function.general
関数文
よくある関数ですね。
function addFuncSentence(a, b) { return a + b };
関数式
変数に代入される関数を関数式と言うっぽいです。
var addFunc = function (a, b) { return a + b }; addFunc(1, 1) // => 2
無名関数
名前を持たないfunctionのことです。Javaでいう無名クラスに近いかなぁ。
(function(a, b) { return a + b; })(1 , 1); // => 2
メソッド
メソッドとは言いますが、オブジェクトのプロパティとして
関数を持つ場合にメソッドと呼びます。
var calculator = { add : function(a, b) { return a + b; } } calculator.add(1, 1) // => 2
クラスという概念は無い。全てオブジェクトです。 (ECMAScript 6ではクラス構文を作る流れもありますが、これまでの歴史としてはありません)
こういう書き方もできます。
var calculator = new Object(); calculator.add = function(a, b) { return a + b; };https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions_and_function_scope
演算子
注意すべきなのは
==と===
基本的に===を使う方が思った挙動をしてくれる。
後は必要に応じてかなぁ。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators
その他プロトタイプチェーンとか
このスライドが素晴らしいです。
http://www.slideshare.net/yuka2py/javascript-23768378
Javaのアレ、JavaScriptだとどうやるの
- クラス継承
JavaScriptにはない。プロトタイプチェーンでの継承が使える。
// 関数オブジェクトの生成 var Greeter = function(){}; // プロトタイプ属性の利用 Greeter.prototype.greet = function(){ console.log("hello")}; var greeter = new Greeter(); greeter.greet(); // => hello // 関数オブジェクトの生成 var AnotherGreeter = function(){}; // プロトタイプ属性の注入 AnotherGreeter.prototype = new Greeter(); var another = new AnotherGreeter(); another.greet(); // => hello
うーん。書いててやっぱりプロトタイプの仕組みはよくわかってないです。
もっと勉強せねば。
- オーバーライド JavaScriptにはない。プロトタイプチェーンなどが使える。
// (上のつづき) another.greet(); // => hello another.greet = function() {console.log("another"); }; // インスタンスに対しての関数が優先される another.greet(); // => another
これだとこのanotherってオブジェクトに対して関数の上書きを行うだけなので、
オーバーライドというのには微妙で、あんまりやる手法でもないそうです(^^;
- オーバーロード JavaScriptにはない。argumentsとかで頑張るぐらいです。 だったら、元から関数名変えたり設計変えたりした方が良いなぁって なると思います。
コメント引用しておきます。。
オーバーロードはふたつパターンがあると思います。引数の数違いと変数の型違い。 前者は arguments で凌ぐか call とか apply 呼び出しをする関数をクッションしてやるとかします。 後者の場合はダックタイピング的な感じでやっちゃえばいいですね :)
var DIRECTION = { NORTH : 1, SOUTH : 2, WEST : 3, EAST : 4 };
- アクセス修飾子 JavaScriptにはない。get/setキーワードとクロージャで頑張るぐらい。
- Getter/Setter getキーワードとsetキーワードがあります
https://developer.mozilla.org/ja/docs/JavaScript/Reference/Operators/set
※サポートされていない場合 (特にIE6-8において) 、スクリプトはシンタックスエラーを引き起こします。
- リフレクション 関数オブジェクトの受け渡しが出来るのであまり使うことは無いと思います。
ただ、やるとすれば、evalとargumentsなどを使うとそれらしいことは出来ます。
※ECMAScript 6ではReflectやProxy Handlerというものが実験段階ですが考案されています。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Reflect https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler
- lamdaとか
- mapとかfilterとか
現状無いです。ECMAScript 6でアロー関数っていうのが提供されるかもしれないみたいですね。
ECMAScript 5からはあります。ECMAScript 6からはアロー関数が入ってさらに書きやすそうですね。
// ES 5 [1, 2, 3].map(function (i) { return i * i }); // ES 6 [for (i of [1, 2, 3]) i * i]; // ES 5 [1,4,2,3,-8].filter(function(i) { return i < 3 }); // ES 6 [for (i of [1,4,2,3,-8]) if (i < 3) i];(https://www.xenophy.com/javascript/8447より引用)
- Javadocのようなもの
>map/filter に関しては現状 underscore or lo-dash を使うのがモダンでないブラウザにも対応していてベター感あります。
おおぉ、こんなものが。ECMAScript5な現状ではこれが良さそうですね。いい情報もらった。
lodash
prototype JavaScript Framework
JSDoc,YUIdoc,docco,doxx,とかいうものがあるらしいです。
Javaエンジニアなら断然JSDocが環境構築、記法的に楽な気がします。
http://qiita.com/opengl-8080/items/a36679f7926f4cac0a81
まとめ
Javaの人からみたJavaScriptというテーマで今回は考えてみました。
見てみると色々便利そうですよね。
プロトタイプチェーンとクロージャ、
関数渡しなどを使えば柔軟なプログラミングが出来る
シンプルな言語です。
ほとんどJavaScriptの基礎的な部分の紹介に費やしてしまった。
Javaエンジニア向けとして書けたのは「Javaのアレどうやるの?」
の部分ぐらいですかね。
もっとジャヴァジャヴァさせたかったのになぁ。。
それと型システムや静的・動的型付けなどについても
調査及び言及できればと思ってたのですが、
それはまた今度ってことで(^^;
あと、jsライブラリの話もしたかったなぁ。。
結論としては、
・グローバルオブジェクト
・グローバル関数
・オブジェクトの種類
・funtionの使い方
を覚えればそんなに難しいものではない!!
(もちろん、言語の世界は広く、どれもこれも極めるのはムズいですけどね。)
5 件のコメント:
幾つか気になったので。
想定が分からないですけど FireFox と firebugs (そうでなくても最近のモダンなブラウザのコンソール)ならループ使って回さなくてもオブジェクトの中身を参照できます。
あと、 console.dir とか使うとわざわざループ使わなくても key, value の組み合わせで表示してくれます。
console.log 以外にも沢山ログ出力用関数が用意されています。
https://developer.mozilla.org/ja/docs/Tools/Web_Console
それと for - in だとプロトタイプの親の値まで巻き込むので推奨されてないか / hasOwnProperty を一緒に使うのが定石です。
var objA = { a: 1, b: 2 };
var objB = Object.create(objA);
objB.c = 3;
for(x in objB){
console.log(x);
} // c, a, b
for(x in objB){
if(objB.hasOwnProperty(x)){
console.log(x);
}
} // c
あるいは次のように書くのがベターそうです。
var m = {a: 1, b: 2};
Object.keys(m).forEach(
k => console.log(k + ", " + m[k]));
JSON は確かに ES5 ですけど IE8 だと使えるとかあるので、この辺参照した方がいいと思います。
http://kangax.github.io/compat-table/es5/
関数( function )についてもこれは明確に違いがあるのでこの辺参照。主に巻上げが起こるか否かですね。
http://bonsaiden.github.io/JavaScript-Garden/ja/#function.general
匿名関数は lambda でしょうね。
equal と strict equal の違いは知っておいた方がいいですが、ほとんどの場合でその違いを意識せずに strict equal を使っていいというのは◎です。
オーバーライドの例は危険だと思っていて、その例だと「オブジェクト」に対してなので全てのオブジェクトのメソッドを上書きするわけではないので注意ですね(あと、あんまやんないです(笑))。
オーバーロードはふたつパターンがあると思います。引数の数違いと変数の型違い。
前者は arguments で凌ぐか call とか apply 呼び出しをする関数をクッションしてやるとかします。
後者の場合はダックタイピング的な感じでやっちゃえばいいですね :)
get/set に触れるならついでに defineProperty 周りも知っておくといい気がします。あまり積極的に使っている例は見たことない気がしますが…。
アロー演算子は根本的に匿名関数のシンタックスシュガーだと捉えてよかったと思うので、 lambda と言えば lambda ですけど、それを lambda と言うなら匿名関数は lambda でいいと思います(少なくとも JS 界隈だと匿名関数 ≒ lambda だと思われている気が)。
map/filter に関しては現状 underscore or lo-dash を使うのがモダンでないブラウザにも対応していてベター感あります。
長々と失礼しました :P
>あやぴーさん
色々ひどくてすみません笑
> FireFox と firebugs (そうでなくても最近のモダンなブラウザのコンソール)ならループ使って回さなくてもオブジェクトの中身を参照できます
確かにわざわざログとして出力するほどでもないですね(><)
> console.dir とか使うとわざわざループ使わなくても key, value の組み合わせで表示してくれます
>console.log 以外にも沢山ログ出力用関数が用意されています
>https://developer.mozilla.org/ja/docs/Tools/Web_Console
console.dir…!存在は知ってはいたのですが、全然ピンときてませんでした。なるほど!
>それと for - in だとプロトタイプの親の値まで巻き込むので推奨されてないか / hasOwnProperty を一緒に使うのが定石です。
なるほど、親含めて全部出力しちゃいますね。そのセオリー全然知らなかったんで勉強になりました。
>Object.keys(m).forEach(
>k => console.log(k + ", " + m[k]));
ES5からなんですかね?色々便利になってますねー。
>JSON は確かに ES5 ですけど IE8 だと使えるとかあるので、この辺参照した方がいいと思います。
>http://kangax.github.io/compat-table/es5/
あ、そうですね。ブラウザのバージョンによってはというのは
全然説明不足でしたね(^^;
>関数( function )についてもこれは明確に違いがあるのでこの辺参照。主に巻上げが起こるか否かですね。
>http://bonsaiden.github.io/JavaScript-Garden/ja/#function.general
うぅ、functionの説明になってませんでしたね。。。
>オーバーライドの例は危険だと思っていて、その例だと「オブジェクト」に対してなので全てのオブジェクトのメソッドを上書きするわけではないので注意ですね(あと、あんまやんないです(笑))。
あ、確かに笑
プロトタイプを意識したJavaScriptって今までそこまで触れてきていなくて、
もっと勉強したいところではあります!
>オーバーロードはふたつパターンがあると思います。引数の数違いと変数の型違い。
>前者は arguments で凌ぐか call とか apply 呼び出しをする関数をクッションしてやるとかします。
>後者の場合はダックタイピング的な感じでやっちゃえばいいですね :)
はい、これに関しては頭の中では分かっていたというかアバウトな認識はありました。
が、それを書く技量がなかったです。すみません(><)
>get/set に触れるならついでに defineProperty 周りも知っておくといい気がします。
definePropertyなんてものがあるんですねー!
>map/filter に関しては現状 underscore or lo-dash を使うのがモダンでないブラウザにも対応していてベター感あります。
なるほどー!そうなんですね。便利そうですね、覚えておきます!
Object.keys(m).forEach(k => console.log(k + ", " + m[k]));
これは ES6 から使えるアローを使っているので、そこだけ function で書き直せば ES5 でいけますね。
さらにどうでもいいですが、 for-in は Array に対しても使うことが出来ますが、これはプロパティを巻き込むという別の理由があるのでやっちゃいけないことです。
他にもハマりどころが沢山あるので下記のドキュメントは全部目を通しておくといいかと思います。
http://bonsaiden.github.io/JavaScript-Garden/ja/
ES6 が出るとまた状況も変わると思いますが、現状は Babel とか使わないと ES6 なコードあまり書けないの(結局ブラウザの対応がまばらだし ES5 に全部倒すのが安全なので Babel みたいなトランスパイラが必要ぽい)でパーフェクト JavaScript あたりを読みましょう :)
あと、関数のところらへんで高階関数とかに触れておくといいのかなーというか、 JS ってやっぱり関数を返す関数とか関数を引数に取るとかそういうのがあるから楽しいので( Lisp 脳)その辺も是非勉強して Lisper になってください(違
グローバルオブジェクトについてですが
Array
Boolean
Date
Function
Iterator ←Fx独自
Number
Object
RegExp
String
Proxy ←ES6
ParallelArray ←Fx独自&廃止
+
Symbol,Mapやらなんやら ←ES6
JSON ←ES仕様からは分離されたが基本的にES5に付随するもの
Intl ←ES仕様からは分離されたが基本的にES6に付随するもの
グローバルのparseIntなど数学系関数 → ES6でNumber下に改良されたものが新設されそちらを推奨
uneval ←Fx独自
オブジェクトのkeyの列挙 → ES5のObject.keysが推奨
>匿名さん
色々ご指摘ありがとうございますー!(><)
いやぁ、全然そのあたり整理できていませんでした。
整理して学び直そうと思います!
そして、この記事にまとめられそうであれば分かる形で追記します…。
新しいコメントは書き込めません。