途中であげてるんで編集リクエストはあとで受け取る。
まあこれは弊社(Claves)での取り組み方(別に相談してないので独断ですらある)です。
多分そのうち陳腐化するので金科玉条のごとく扱いはしない方が良いです。
書くにあたった動機
若い人間がJavaScriptを書く場合に、
- 参照しているものが古い
- 便利なライブラリとかがあるのに再発明とかしてる
- Railsで書く場合にどう書けば良いのか
などが整理されていないと感じた。
都度説明していたが三回をこえて面倒なので書き下すことにした。
JavaScript? TypeScript?
結論
正直モダンに書くのであればJavaScriptでもTypeScriptでも良いと思っている。
構文的にはTypeScriptはモダンなJavaScriptに型、抽象クラスなどが追加されていると思って良いかと思う。
継承とかゴリゴリ書くのであればTypeScriptは便利だし、後述するReactなんかも
TypeScriptで型の恩恵を受けられるので使った方が良いとは思っている。
JavaScriptを覚えるついでに型関連を覚えると思っておけば良いし普通にTypeScriptを使わない理由がないと思う。
import / requireなど
TS/JSどっちも構文は一緒。
今はimport文を使うんじゃないかな
昔は使いたいライブラリなんかがあったときにはscriptタグでそのJSファイルを読み出して、
グローバル名前空間にそのオブジェクトなり関数なりを食わせて、
その後に書いたJavaScriptで呼び出していた。
行儀が悪いし、使用するライブラリもどこか曖昧。
今はnpm経由でインストールして、import文を書いてクラスを呼び出すっていうのがモダンな感じなのではと感じている、
class Hoge {
constructor(){
}
}
export default Hoge
import Hoge from "./Hoge"
new Hoge()
みたいに使う。
export defaultは補完が聞きづらくて批判があったりするけど僕は補完使ってないし知らない。
不便だと言う人は話をまとめて方針を持ってきてほしい。
export defaultを使わない場合はこんな感じ。
class Hoge {
constructor(){
}
}
export Hoge
class Fuga{
constructor(){
}
}
export Fuga
import {Hoge , Fuga} from "./HogeFuga"
new Hoge()
new Fuga()
各種新しめ構文
let
昔はvarで変数宣言をしていた。
letとvarの違いは、varは関数でのみスコープが生成されるが、letはlexicalなスコープになること。
要は波かっこをまたぐとスコープが切れるってこと。
{
let i = 0;
}
console.log(i) // undefined
if(true){
let i = 0;
}
console.log(i) // undefined
(()=>{
let i = 0;
})()
console.log(i) // undefined
プログラムを書くときには必要な範囲でスコープは極力小さくする方が事故が少ないので、letで宣言を行うべき。
昔は下記のようなことがよく起こっていて
for(var i = 0;i < elements.length ;i++){
elements[i].addEventListener(function(){
// クリックされたときにはiは必ずelements.length - 1なのでelements終端要素のvalueのみがalertされる
alert(elements[i].value)
})
}
こういう対応を行なっていたが、面倒でしょ?
for(var i = 0;i < elements.length ;i++){
(function(){
// elementを固定したいので関数を作ってスコープを作成する
var element = elements[i]
element.addEventListener(function(){
// elementはここでイベントを設定したi番目のelements要素になる
alert(element.value)
})
})()
}
特にvarでのみ宣言が可能なシチュエーションはなくはないが日常的にはなくひどく限定的なので、普段はvarではなくletを使うべきである。
アロー関数
javascriptのfunctionは複数の役割を持っていて、その中で参照されるthisが都度変わる。
<div id="hoge">Hoge</div>
<script type="text/javascript">
var hoge = document.getElementById("hoge")
hoge.addEventListener("click" , function(e){
// thisにはクリックされたHTMLElementが入る
alert(this.innerHTML)
});
(function(){
// Window Objectが入る
alert(this)
})()
function SomeClass(){
}
SomeClass.prototype.hoge = function(){
this.s = this.s || 0
this.s++;
// thisはインスタンス化された自信を参照する
alert(this.s)
}
let someClass = new SomeClass()
someClass.hoge() // 1
someClass.hoge() // 2
let someClass2 = new SomeClass()
someClass2.hoge() // 1
someClass2.hoge() // 2
</script>
このパターンだけでthisが三つの挙動をしていることになる。
とあるオブジェクト内でイベントハンドラ設定して、そのハンドラ内でthisを使用しようとした場合などは、困ったことが起きる
function SomeClass(){
}
SomeClass.prototype.getInnerHTML = function(){
return "SET STRING"
}
SomeClass.prototype.hoge = function(){
console.log(this) // この時点ではthisはSomeClassのインスタンス
var hoge = document.getElementById("hoge")
hoge.addEventListener("click" , function(e){
// thisにはHTMLElementが入っていて、thisはSomeClassのインスタンスではないため、getInnerHTMLは存在しないためにエラーになる
this.innerHTML = this.getInnerHTML()
});
}
こういう問題には
function SomeClass(){
}
SomeClass.prototype.getInnerHTML = function(){
return "SET STRING"
}
SomeClass.prototype.hoge = function(){
console.log(this) // この時点ではthisはSomeClassのインスタンス
var self = this
var hoge = document.getElementById("hoge")
hoge.addEventListener("click" , function(e){
// thisにはHTMLElementが入っている。
// thisが書き換えられるので、SomeClassのインスタンスはselfに保持してgetInnerHTMLを呼び出す
this.innerHTML = self.getInnerHTML()
});
}
こういう対応をしていたが、大変面倒だ。
対象のHTMLELementはvar hogeに参照されているのだし、それを見れば良い。
踏まえると、イベントハンドラのfunction宣言でthisが書き換えられなければうまく行くことがわかる。
アロー関数 ()=>{/* some implemetation */}
をfunctionの代わりに用いることでthisの参照を変えることなく無名関数が定義できる。
function SomeClass(){
}
SomeClass.prototype.getInnerHTML = ()=>{
return "SET STRING"
}
SomeClass.prototype.hoge = ()=>{
console.log(this) // この時点ではthisはSomeClassのインスタンス
var hoge = document.getElementById("hoge")
hoge.addEventListener("click" , (e)=>{
// hogeにはHTMLElementが入っていて、アロー関数を使用したことで、thisはSomeClassのインスタンスのままで、、getInnerHTMLは存在するため動作する
hoge.innerHTML = this.getInnerHTML()
});
}
function構文で無名関数を作り、thisの参照を変えたい、というシチュエーションはほぼない。
普段から無名関数はfunctionではなく()=>{}を使うべきである。
クラス構文
旧来のJSは適当な関数を作って、その中にあるprototypeなる特別なオブジェクトに色々メソッドを追加することで、OOPを実現していた。
継承とかもできたし概ね機能的には問題はなかったが、やっぱりわかりづらい。
```
function SomeClass(){
}
SomeClass.prototype.hoge = function(){
this.s = this.s || 0
this.s++;
alert(this.s)
}
```
普通にクラスで描こう
class SomeClass{
hoge(){
this.s = this.s || 0
this.s++;
alert(this.s)
}
}