読者です 読者をやめる 読者になる 読者になる

(define -ayalog '())

括弧に魅せられて道を外した名前のないプログラマ

JavaScriptのコンストラクタ関数の使い勝手が最高に良くないと思うので考察というか試行錯誤

JavaScriptコンストラクタ関数が嫌いです。って話は前に少し書いた気がする。理由は単純にnewを書きたくない、というかnewの有無で意味が変わるってそもそも相当アレな気がするし、Booleanコンストラクタに至ってはnew使わない方がいいとか意味不明だし、できるだけ安全な方に倒したいからコンストラクタ関数を使いたくない。命名規則コンストラクタ関数を見分けるって、結構辛くないですか?ガチ運用でカバーじゃないですか。

そもそもJavaScriptはプロトタイプベースな言語なのだから、コンストラクタ関数を使う意味が分からんのだよ。って思って色々考えてたんだけど、関数型コンストラクタ関数だったらまぁnew使わなくていいしイイかな?と思ったりする。なんだかんだでそのオブジェクトを生成する値を渡して、オブジェクトを返却してもらうという考え方はしっくりきてしまう。いきなり何かのオブジェクトを作るってなんかどうしてもプロパティの値が空になるものが多い気がするので、気持ち悪さありますよね*1。それだったらコンストラクタ関数ぽい何かってやっぱり必要なのかなって思って、だけどnewは使いたくないという最高のわがままっぷりを発揮しています。

で、プロトタイプ継承とかも考えてたらこんなコードの書き方になってしまった。

book = (title, author, price) ->
  _book = {}
  _book.to_string = ->
    "#{title} / #{author}"
  return _book

picture_book = (title, author, illustrator, price) ->
  _picture_book = Object.create book(title, author, price)
  _picture_book.to_string = ->
    "#{title} / #{author} / #{illustrator}"
  return _picture_book

clean_code = book('Clean Code', 'Robart C. Martin')
console.log clean_code
console.log clean_code.title
console.log clean_code.to_string()

alice_adventures_in_wonderland = picture_book('Alice Adventures in Wonderland', 'Lewis Carroll', 'John Tenniel')
console.log alice_adventures_in_wonderland
console.log alice_adventures_in_wonderland.to_string()

クロージャが効くのでtitleとかauthorに直接のアクセスは出来ない。かつ、newを書かなくてもオブジェクトの生成が出来ている。素晴らしい?これで辛いのはたぶんBook.prototype.calculate_included_tax_priceっていう風に後から、そのオブジェクトのプロトタイプに関数やプロパティを足せないことかな?あと、typeofも使えないコンストラクタの違いで場合わけが出来ない。んー。とりあえず、一旦寝て考えなおそう。

寝て起きたので少し追記

よくよく考えたら、typeofを使って場合分けしなくていいやって考えれば、そんなに気にする必要がないんじゃないかなって気付いた。この考え方は恐らくRubyな人なら、馴染むと思うけどダックタイピングというかガンガンオブジェクトに対して関数やプロパティを呼び出しに行って、存在しないなら呼び出さないかメソッドミッシング的なことをしてしまえば問題ないのでは?とか。どうせ全てはオブジェクトのように振る舞うのだし、そんなに気にする必要ない気がしてきた。
それかプロトタイプに対するプロパティの追加が出来ないのを不便だと思うなら、諦めて空の基底コンストラクタ関数を作ってオブジェクトの生成だけ別の関数型コンストラクタ関数に任せてしまうというのもいいかも。

#空の基底コンストラクタ関数を作る(諦め肝心)
Book = ->

book = (title, author, price) ->
  _book = new Book()
  _book.title = title
  _book.author = author
  _book.price = price

  _book.to_string = ->
    "#{title} / #{author}"
  return _book

clean_code = book('Clean Code', 'Robart C. Martin', 5000)
console.log clean_code #=> Book {title: "Clean Code", author: "Robart C. Martin", price: 5000, to_string: function}
console.log clean_code.to_string() #=> Clean Code / Robart C. Martin

#Bookのプロトタイプに対して関数を追加する
Book.prototype.calculate_included_tax_price = ->
  @price * 1.08

console.log clean_code.calculate_included_tax_price() #=> 5400

たぶん、JavaScriptな人がずっと考えて議論しているであろうことをn周遅れで考えている。

*1:例えばbookオブジェクトなんてtitleもauthorも空になるだろうし、これが逆に関数とかは先に定義してしまえるのでいいかなって感じはするけどさ