この記事の内容
個人的に感じた。JavaScriptの罠、有用な点など、随時更新予定
一般編
チェックはnodeでやってるのでもしかしたらnodeのせいかも、JavaScriptはブラウザ依存性は強いので細かくはブラウザによりけりになります。
var, let, const
var
varのは関数スコープでしか切られない。
c=0; // NG。'use strict'を使っていなければグローバル変数になる。
a=10; // OK
{
var a=0;
d=0; // NG、グローバル変数になる。
(function(){
var d=0;
}());
}
要はvarは使うな。
const
constは再代入不可、操作はOK
const obj={};
obj.name='hoge' // OK;
const str='';
str+='hoge' // NG
const i=0;
i++; // NG
for( const a of [ 1, 2, 3, 4, 5 ] ){ // OK
console.log(a);
}
const arr=[ 1, 2, 3, 4, 5 ];
for( const i=0; i<arr.length; i++ ){ ~ } // NG i++があるから
C++だと変化する操作もNGなので勘違いしていた。+=``++
あたりは再代入と解釈されるのでconstでは使えない。(babelではトランスパイル時エラーになる)。
let
再代入可能、どうしても代入したい時に
7つのプリミティブ型
boolean``number``string``symbol``object``null``undefined
boolean/string
真偽値/文字列
number
int(整数)、double(浮動点小数)は存在しない。numberは++
で1.0
足される。
let num=0.5;
num++; // num=1.5
内部的にはすべてIEEE 754の64bit浮動点小数として扱われる。
int(整数)、double(浮動点小数)は存在しないがどちらとして扱いたいかが重要なの時は形名を書くときはint/doubleで書くことを薦める。
symbol
まだちょっと理解していないので勉強中。
object
JavaScriptの最大の特徴。function
やArray
もオブジェクトの一種です。
プロトタイプベース
prototype
に色んな物をはやしていこうとする方法。プリミティブも基本的にはラッパーオブジェクトからprototype
にアクセスできるのでJavaScriptではほぼすべてのモノがprototype
を持っていると言ってもいい。ただし、一部例外があるので注意。
個人的にはprototypeに触るのは禁止、そういったことをしたいときはclass構文で対応することを薦める。
ラッパーオブジェクト
boolean,string,number,symbolに対応するオブジェクトが存在しBoolean、String、Number、Symbol、プリミティブ型のメソッドが呼ばれると一時的にこいつらに変換されそのメソッドが呼ばれる。
toString
JavaScriptはほぼすべてtoString
メソッドを持っている。プリミティブ型も自動的にラッパーオブジェクトに変換されそのtoString
が呼ばれる。+
演算子は文字列の結合の意味もあるので+
を使うと文字列になる場合がある。
nullとundefined
nullは代入不可、undefinedは代入可能。undefinedはvoid 0
で表現可能。しかしnull
は予約語ではないらしい。???null
はリテラル同じくリテラルのtrue
とfalse
も代入出来なかった。
let null=1; // エラー
let undefined=0; // OK
const undefined=null; // OK
undefined=null; // 'use strict' してないならOK
console.log(null, undefined); // null undefined
console.log(Object.prototype.toString.call(null), Object.prototype.toString.call(undefined)); // [object Null] [object Undefined]
console.log(Number(null), Number(undefined)); // 0 NaN
console.log(String(null), String(undefined)); // null undefined
console.log(Array(null), Array(undefined)); // [ null ] [ undefined ]
console.log(Object(null), Object(undefined)); // {} {}
// console.log(null.toString(), undefined.toString()); // 例外 TypedError
nullもundefinedも値を持つので配列には入れられる。[object Null]や[object Undefined]は擬似クラスで実体にアクセスすることは出来ない。メソッドも持てないのでtoStringも呼べない。
巻き上げ
Undefined is not defined
何当たり前のこと言ってんだと思うと死ぬ。
{
console.log(undefined);
const undefined=0;
}
3行目をコメントアウトすると普通に動きます、変数の巻き上げとかとの絡みです。
2行目でグローバル変数のundefined
をプログラマーは期待しているが3行目でconstで定義をしているので
スコープ内変数undefined
が存在するのでそちらを優先する。そしてまだ定義されていないからエラーになる。
まき上げられて常に呼べる関数の巻き上げとは少し挙動が違うので注意。
モジュールインポート
私がやってしまった例として
import * as hoge from './hoge.js'
console.log(hoge) // undefined
const hoge=hoge.fromHTML(document.getElementById('hoge'));
console.log
は変数の巻き上げによりundefinedになる。const hoge
でhogeは定義されているがfromHTML
は定義されてないため死んでるっぽいが死んでる旨のエラーをコンソールに出さずにその後の処理もされないので結構ハマった。
解決策は命名則をきちんとすることだろうがimport/export構文自体がややこしく難しい。
演算子
すべての演算子を列挙することはしないが幾つか取上げる。
詳しくはMDNで
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Expressions_and_Operators
<==
>==
===
,!==
との絡みで存在するかと思ったが存在しない。
**演算子
JavaScriptには**演算子がある、知らなかった。そのまんばべき乗。Math.pow(x,y)
もある。
typeof
instanceof
typeof演算子は単行演算子なので
typeof tmp; // tmpの型の文字列
と使い返ってくるのは文字列です。
instanceofは2つの値を取るので
tmp instanceof Date
Dateは日付に関するブラウザで定義されたインスタンスで、true/falseで返って来ます。
右辺にインスタンス化できないものnumber
等を入れるとReferenceError
を飛ばします。
HTMLElement
やSVGElement
はインスタンス化できるもので例外は飛ばしません、document.getElementById()
などで取ってきたものはHTMLElementのインスタンスです。
型判定
JavaScriptではオブジェクトの拡張性を利用したダッグタイピング的な利用が多いので型で判定するより、メソッドや変数を持っているかいないかで判断したほうが確実、それでも型判定したい時に
typeof
console.log(typeof true); // boolean
console.log(typeof 0); // number
console.log(typeof null); // object
console.log(typeof "null"); // string
console.log(typeof undefined); // undefined
(function(){
const undefined="undefined";
console.log(typeof undefined); // string
})();
console.log(typeof void 0); // undefined
console.log(typeof function(){ return 0; }); // function
console.log(typeof ()=>{ return 0; }); // function
console.log(typeof class C{}); // function
console.log(typeof [ 1, 2, 3 ]); // object
存在(null/undefined)チェックにtypeofを使ってはいけない。存在判定はx!=null
を使う
undefined/null
以外のプリミティブ型のどれに属するか知りたい時に使うべし
関数はfunction
となるが配列はobject
になる。
classもfunction
になる、これはコンストラクタが関数として定義されているためと思われます。
またここから、関数とクラスだけ特別視しようとしてることが伺えます。
Array.isArray
配列専用。JavaScriptの配列はObjectのキーが数字であるものでしか無いのでArrayの判定は非常に難しい。そのため専用のメソッドがある。JavaScriptのisXXX
は一般には型判定用でないので注意。
instanceof
インスタンスを判定する。右辺値によっては例外を飛ばすがそれを除けば大体これでOK
例外
JavaScriptで飛ばすならthrow new
つけないと行番号が分からないのでつけるべき。
Webpackなどのビルドシステムを使うとtry/catch文が追加されるので例外が外に伝搬しない。例外キャッチを使いたければ同じファイル単位内でキャッチさせるべし!
ブラウザ編
制御をユーザーに返す
try{
throw new Error(message);
}
catch(e){
if( c!omfirm(e) ) throw e;
}
時と場合によってはユーザーに制御を返したいかも
ブラウザAPIの一覧っぽいもの
名前 | 機能 | ライブラリなど | 備考 |
---|---|---|---|
WebGL | 3Dグラフィック | three | 言わずとしれた |
SVG | 2Dグラフィック | snapsvg, d3 | snapsvgが完全SVG用、d3はグラフ化ツール |
CanvasAPI | 2Dグラフィック | d3, two | ラスタ形式(SVGはベクター形式) |
WebAudioAPI | 音楽 | イマイチ決定打的なライブラリがない | |
WebSocket | 双方向通信 | サーバーはブラウザでは建てれない | |
WebRTC | P2P | WebSocketとの違いなど | |
XMLHTTPRequest | Ajax | jquery | いらん子、Fetch使いましょう |
Fetch | Ajax | これでjqueryは本格的にいらない子になったはず | |
FullscreenAPI | フルスクリーンモード | jquery.fullscreen | 名前が闇そのもの,Fullscreen/FullScreen+ベンダープリフィックス |
file API | ファイル操作系 |
const fs=require('fs') Node用 |
|
npmからsnagsvgを入れてWebpackで使うとバグってるらしい、ローダーをセットしろとのことらしい。(斜め読みなので間違ってるかも) |
個人的に有用だと思うnpmパッケージたち(一部違う)
個人的に面白そうだと思うパッケージたち、前に上げたライブラリはあげない。
名前 | 機能 | 備考 |
---|---|---|
mathjs | 数学関係、複素数、行列などをサポート | mathとは別物、微妙に惜しい子 |
legendre-poly/gauss-quadrature | ガウスルジャンドル法用パラメーター | 原理は私の拙著で |
fft/ml-fft | 高速フーリエ変換 | まあ当然あるわな |
runge-kutta-4 | 4次のルンゲクッタ法 | 他にも似たようなライブラリはあった |
math-digamma | ディガンマ関数 | |
physic | 物理 | と思ったら中身はほぼからだった |
jsroot | 物理系グラフ化、元はC++ | npmにはない、ライセンスが微妙、ソースも微妙 |
妙に数学系が充実してる。実装まで見てないからどれだけ使えるかは不明だが。mathjsの複素数関係はC++とほぼ同じ結果を返してきたので問題なさそうである。legendre-poly/gauss-quadratureもREADME.mdはしっかりしてたので使えそう。 |
Node編
インタプリンタ終了
> process.exit(0);
普通、exit
かquit
かそのカッコ()
つきだけどnodeはprocessが必要。crt+C
2回でも終了できる。
main関数
(()=>{
console.log("hello world");
})();
即時実行しないと実行してくれない。クライアント側メインだとwindow
のロード時とかで忘れがち。メインだけインデントをずらしたい時に使う。
ファイル操作等
fetch
、XMLHTTPRequest
は存在しない。ブラウザAPIは存在しない。fs
を使う。
const fs=require('fs');
const text=fs.readFileSync(argv.inputs[0], 'utf-8');
(処理)
コールバックとしても渡すことはできるが非同期実行になり(処理)との順番が保証されない。
そもそもHTTPRequest, fetchは通信系メソッドです。ブラウザはfile APIがあります。いや使う側はあんまり気にしないけどね
importが使えない
requireを使う。
Electron
Nodeでブラウザの機能を使ってデスクトップアプリにするらしい。(調査中)
最後に
ブラウザ用とNodeだとだいぶ書き味が違う。大阪弁と秋田弁ぐらい違う、まあ日本語と英語までは違わないのでなんとかはできるが...。もうちょっとベンダーさんたちもNodeさんも仲良くしてください。
後、私は普段は似非C++erをしています。他言語から見ると自然に見える部分もあるかと思います。そういったコメントをいただきました。C++とJavaはやっぱり似てないなと再認識しました。
JavaScriptはコードが仕様書だ!!!を地で行ってる言語だと感じました。