![img01.png]()
Node.js
イベントループによる並行処理
Node.jsの第一の特徴としてあげられるのは、並行処理をマルチスレッドではなくイベントループによって実現するというものです。イベントループは単一のスレッド(シングルスレッド)で動作するため、マルチスレッドのようなリクエスト数の増大に伴う問題が起きづらくなっています。
イベントループは実行すべきタスクをキューに積み、これを1つづつ取り出してシングルスレッドで実行していきます。逐次処理のようですが、これで並行処理を実現できるのは、ここでのタスクが一連の処理をI/Oの発生するタイミングを境に分割した物だからです。プログラムはI/O実行時にその完了後のタスクを指定し、実際にI/Oが完了すると指定されたタスクがキューに追加されます。
Ecmascript, Web標準
JavaScriptの言語使用はECMAScript(ECMA Internationalという団体に属する技術いんかいのTC39での議論で標準化する)標準によって規定されます。また、ブラウザ環境におけるJavaScriptのAPI仕様を定めるものとして、Web標準がある。これはHTML, CSS, JavaScriptなどのブラウザ関連の技術について、ブラウザ館での使用を統一するための標準です。
ECMAScript 標準
ECMAScript標準のバージョンは2015年にリリースされたES2015以降、西暦であらわされるようになりました。ES2015はもともとES6として標準化が進められていたが、以前のバージョンとの期間が空きすぎてしまったために年次リリースができるように改められました。この結果、ES6と呼ばれていた物は最終的にES2015としてリリースされ、以降ES2016, ES2017, ES2018,...ES2020と続いている。
javascriptの基本
変数宣言
変数宣言をするには、[const, let]を用います。
- const: 宣言した変数にには値の再割り当てができない(定数)
- let: 再割り当てが可能
この両者の特徴として、ブロックのスコープ無ことと、同一スコープないで同じ名前の変数を複数同時に宣言できないことがあげられます。
node.js
>constfoo='foo'undefined>letbar='bar'undefined>foo='foo2'UncaughtTypeError:Assignmenttoconstantvariable.>bar='bar2''bar2'
constとletはES2015で導入されましたが、それまではJavaScriptで変数宣言する際は"var"を使用していました。今でも使用可能ですが、すこー部が関数や同じなあ絵の変数を繰り返し宣言可能なことなど、わかりづらく不具合の原因となるので使用しない方が良い。
関数
javascriptで関数を定義する方法はいくつかあります。
>functionadd1(a,b){returna+b}undefined
>constadd2=function(a,b){returna+b}undefined
>constadd3=functionaddFn(a,b){returna+b}undefined
// アロー関数式>constadd4=(a,b)=>{returna+b}undefined// アロー関数式の省力記法({}を省略するとreturnなしで値が返される)>constadd5=(a,b)=>a+bundefined// パラメータが一つならパラメータを囲む()も省力可能>constaddOne=a=>a+1undefined
関数宣言で作る関数はモジュール内でホスティングされ。宣言前に参照できるが、関数式で作成する関すは作る前に参照するとエラーになります。
console.log(add6(1,2))console.log(add7(1,2))functionadd6(a,b){returna+b}constadd7=(a,b)=>a+b>>>// 関数宣言で作成したadd6は宣言前に参照可能3// 関数式で作成したadd7は作る前に参照するとエラーになるUncaughtReferenceError:Cannotaccess'add7'beforeinitialization
オブジェクト
今回は、オブジェクトリテラルから生成されるようなプレーンなオブジェクトについて説明します。
// オブジェクトリテラルによるプレーンなオブジェクトの生成>constobj1={propA:1,propB:2}undefined// プロパティ名を指定して取得(ドット記法)>obj1.propA1// プロパティ名を指定して取得(ブランケット記法)>obj1['propA']1// プロパティの追加>obj1.propC=33>obj1{propA:1,propB:2,propC:3}// プロパティの削除>deleteobj1.propCtrue>obj1{propA:1,propB:2}
プロパティの追加、削除をイミュータブルに行うには、スプレッド構文、レスト構文に使います。
'use strict'constprint=console.logconstobj1={propA:1,propB:2}// スプレッド構文でexecurteを追加constobj2={...obj1,propC:3}// 元のオブジェクトは不変print('obj1',obj1)// 新しく生成されたオブジェクトにキーが追加されるprint('obj2',obj2)#レスト構文でexecuteを削除const{propA,...obj3}=obj2// 元のオブジェクトは不変print('obj2',obj2)// 新しく生成されたオブジェクトにキーが追加されるprint('obj3',obj3)>>>obj1{propA:1,propB:2}obj2{propA:1,propB:2,propC:3}obj2{propA:1,propB:2,propC:3}obj3{propB:2,propC:3}
プロパティの値を取得、設定する際に関数を実行するgetter, setterという機能もあります。
'use strict'constprint=console.logconstprice={value:100,getwithTax(){returnMath.floor(this.value*1.1)},setwithTax(withTax){this.value=Math.ceil(withTax/1.1)}}// getterから値を取得print(price.withTax)// >>> 110// setterで値を設定price.withTax=333// getterで取得される値が更新されることを確認print(price.withTax)// >>> 333// setterにより正しく値が設定されることを確認print(price.value)// >>> 303
配列
配列リテラルで初期化します。
'use strict'constprint=console.log// 配列リテラルで初期化constarr1=['foo','bar']print('arr1.length',arr1.length)// arr1.length 2// 指定したインデックス要素を取得print('arr1[1]',arr1[1])// arr1[1] bar// 指定した要素のインデックスを取得(存在しない場合:-1)print(arr1.indexOf('bar'))// 1// 要素が配列に含まれるかどうかprint(arr1.includes('bar'))// true// 前要素を引数に指定した文字列を結合print(arr1.join('-'))// foo-bar
配列の末尾への要素の追加、削除はそれぞれ push(), pop()を使います。同様に配列の先頭への要素の追加、削除はそれぞれunshift(), shift()を使います。
'use strict'constprint=console.logconstarr1=['foo','bar']// 末尾に要素を追加arr1.push('baz')print('arr1',arr1)// arr1 [ 'foo', 'bar', 'baz' ]// 末尾に要素を複数追加arr1.push('a','b','c')print('arr1',arr1)// arr1 [ 'foo', 'bar', 'baz', 'a', 'b', 'c' ]// 末尾の要素を削除arr1.pop()print(arr1)// [ 'foo', 'bar', 'baz', 'a', 'b' ]// 先頭に要素を追加arr1.unshift('qux')print(arr1)// [ 'qux', 'foo', 'bar', 'baz', 'a', 'b' ]// 先頭の要素を複数追加arr1.unshift('d','e','f')print(arr1)/*
[
'd', 'e', 'f',
'qux', 'foo', 'bar',
'baz', 'a', 'b'
]
*/// 先頭要素を削除arr1.shift()print(arr1)/*
[
'e', 'f', 'qux',
'foo', 'bar', 'baz',
'a', 'b'
]
*/
要素の追加、削除をイミュータブルに行うには、オブジェクトの場合と同様スプレッド構文、レスト構文を使います。
'use strict'constprint=console.logconstarr2=['foo','bar','baz']// スプレッド構文で先頭と末尾に要素を追加constarr3=['a',...arr2,'b','c']print('arr3',arr3)// arr3 [ 'a', 'foo', 'bar', 'baz', 'b', 'c' ]// 末尾の配列はそのままprint('arr2',arr2)// arr2 [ 'foo', 'bar', 'baz' ]// レスト構文で先頭の要素を削除const[head1,head2,...arr4]=arr2print('arr4',arr4)// arr4 [ 'baz' ]// 末尾の配列はそのままprint('arr2',arr2)// arr2 [ 'foo', 'bar', 'baz' ]
配列のレスト構文では、レスト要素が配列の最後でならなければならないという制約があります。配列から要素を削除する際に、これが不都合になる場合はレスト構文の代わりに配列の部分配列を返す'slice()'というメソッドを使います。
'use strict'constprint=console.logconstarr2=['foo','bar','baz']// 切り出したい最初の要素のインデックスと最後の要素の次のインデックスを指定して部分配列を取得するprint(arr2.slice(0,2))// [ 'foo', 'bar' ]// インデックスに負の値を指定すると配列の最後から数えたいんでくすとして扱われるprint(arr2.slice(0,-1))// [ 'foo', 'bar' ]// 第2引数を小着すると、配列の最後まで切り出す意味になるprint(arr2.slice(2))// [ 'baz' ]// 第一引数も第2引数も省略すると、配列の最初から最後までコピーするprint(arr2.slice())// [ 'foo', 'bar', 'baz' ]
配列に対する反服処理はfor文またはfor...of文で記述できます。
'use strict'constprint=console.logconstarr2=['foo','bar','baz']for(consteofarr2){print(e)}>>>foobarbaz
forやfor...ofを使わずに配列の持つメソッドを使って反復処理を実行すつ処理もあります。そのようなメソッドは引数として反復処理の内容を記述した完酢を受け取り、その処理を各要素に適用します。どのメソッドもイミュータブルで、元の配列を変更しません。また、全てのメソッドでコールバック関数が受け取るパラメータは共通で、配列の要素、その要素のインデックス、捜査対象の配列の3つをこの順番で受け取ります。
'use strict'constarr2=['foo','bar','baz']// forEach(): 各要素にコールバック関数の処理を適用し、戻り値はないarr2.forEach(console.log)// map(): 各要素をコールバック関数の戻り値に置き換えた配列を返すconsole.log(arr2.map(e=>e+e))// filter(): コールバック関数が真の値を返す要素の身を含む配列を返すarr2.filter(e=>e.startsWith('b'))// finc(): コールバック関数が真の値を返す最初の要素を返すarr2.find(e=>e.startsWith('b'))>>>foo0['foo','bar','baz']bar1['foo','bar','baz']baz2['foo','bar','baz']['foofoo','barbar','bazbaz']
find()はコールバック関数が一度でもtrueを返せば結果が確定するため、その時点で反復処理を終了します。
クラス
JavaScriptのクラスは、オブジェクト思考言語の経験のある方なら直感的に理解できると思います。privateなメンバーは名前の先頭に#をつけるという、特殊になっています。
'use strict'classFoo{// privateフィールド#privateField=1// publicフィールドpublicField=2// staticなprivateフィールドstatic#staticPrivateField=3// staticなpublicフィールドstaticstaticPublicField=4constructor(parameter){this.filedInitializedInConstructor=parameterconsole.log('Foo constructor')}// privateなgetterget#computed(){returnthis.publicField*2}// privateなsettergetcomputed(){returnthis.#computed}set#computed(value){this.publicField=value/2}setcomputed(value){this.#computed=value}#privateMethod(){returnthis.#privateField}publicMethod(){returnthis.#privateField}static#staticPrivateMethod(){returnthis.#privateField}staticstaticPublicMethod(){returnthis.staticPublicField}}
constfooInstance=newFoo(100)console.log(fooInstance.#privateField)// SyntaxError: Private field '#privateField' must be declared in an enclosing classconsole.log(fooInstance.publicField)// 2 console.log(fooInstance.filedInitializedInConstructor)// 100console.log(fooInstance.#compute)// SyntaxError: Private field '#compute' must be declared in an enclosing classconsole.log(fooInstance.computed)// 4fooInstance.#computed=10// SyntaxError: Private field '#computed' must be declared in an enclosing classfooInstance.computed=10console.log(fooInstance.computed)// 10console.log(fooInstance.publicMethod())// 1console.log(Foo.#staticPrivateField)// SyntaxError: Private field '#staticPrivateField' must be declared in an enclosing classconsole.log(Foo.staticPublicMethod())// 4
継承
classBarextendsFoo{constructor(parameter){super(parameter)this.subClassPublicField=100console.log('Bar constructor')}publicMethod(){returnsuper.publicMethod()*this.subClassPublicField}}constbarInstance=newBar(100)console.log(barInstance.publicField)// 2console.log(barInstance.subClassPublicField)// 100console.log(barInstance.subClassMethod())// TypeError: barInstance.subClassMethod is not a functionconsole.log(Bar.staticPublicField)// 4console.log(Bar.staticPublicMethod())// 4
等価性
JavaScriptには等価性の評価のための演算子として"==="と"=="が存在しますが、常に"==="を使うようにしてください。"==="による等価性の評価は厳密で結果が容易に予測できますが、"=="はそうではないため、利用するとコードの可読性を著しく下げてしまう。
0===''// > false0==''// > true
// プリミティブの場合、同じリテラルで表現されるあたい同士の場合はtrueになる1===1// > true// オブジェクト同士を比較する場合は構造が同じだけで別々のオブジェクトなので、falseになる{'foo':1}==={'foo':1}// > false
commonJs モジュール
modele.ecportsとrequire()
CommonJSモジュールを使う場合、それぞれのJavaScriptファイルが個別のCommonJSモジュールとして扱われます。CommonJSモジュールは、モジュールレベルのスコープでNode.jsが自動的に割り当てるmoduleという変数のexportsプロパティ(module.exports)を通して、外部に関数や変数を後悔します。一方外部モジュールをロードする際には同じくモジュールスコープで割り当てられるrequire()という関数を使います。
math.js
module.exports.add=(a,b)=>a+bmodule.exports.subscribe=(a,b)=>a-b
index.js
constmath=require('./math')constresult=math.add(1,2)console.log(result)>>>3
filenameとdirname
- __filename: ファイル名の絶対パス
- __dirname: ディレクトリ名の絶対パス
strictモード
ファイルの先頭に 'use strict'
と記述すると、そのファイルないでstrictモードが有効になります。strictモードは安全でパフォーマンスに優れたコードの記述を促すためES5で導入されたJavaScriptのモードです。特徴としてはいかがあげられる。
- 無効な処理や意図せぬ結果を引き起こしうる処理をエラーにする
- JavaScriptエンジンによる最適化を阻害する機能を制限または向こうにする
- ECMAScriptの新しいバージョンへの移行を容易にするため、将来使われうる識別子を予約語として変数等の名前に使えないようにし、将来違う意味を持ちうる構文をエラーにする。
'use strict'letmyString='あいうえお'mystring='かきくけこ'console.log*mystring>>>mystring='かきくけこ'ReferenceError:mystringisnotdefined