Rubyしか書けないエンジニアから脱却するためのJavaScript Primerお勉強①
はじめに
エンジニアとして就職してから1年とちょっと経ちました。
その間ほとんどずっとRuby on RailsでのAPI開発しかして来なかったため、そろそろ他の言語も勉強してみようと思いJavaScriptを一から勉強します!
ちなみに現時点でのjs知識は
- 基本文法は何となく理解している
- ふわふわな知識でちょこっとちょっと触れるくらい
ご覧の通りちょっとした知識しかないため、まずはJavaScript Primerを全て読んでみたいと思います!
(ネットで無料で見れるのありがたすぎる... 製作者の方々、本当にありがとうございます)
第一部
変数と宣言
const
は再代入不可、let
は再代入可能、var
は使わない
基本はconst
で、次にlet
を使えば良さそう
同じスコープ内で変数の再宣言はできないっぽい
let a = 'a'; // undefined let a = 'aa'; //Uncaught SyntaxError: Identifier 'a' has already been declared
分割代入はこんな感じ
const [b, c] = ['b', 'c']; b // 'b' c // 'c' // 要素数が足りない場合はundefinedが入るっぽい const [e, f] = ['e']; e // 'e' f // undefined
値の評価と表示
変数宣言はundefined
を返す
Rubyだとselfの値が返ってくるのでちょっと違和感
そもそも変数に関数を入れれるのに『変数宣言』って呼んで良いのかは疑問
この辺りRubyと結構違いそうなので気をつける
データ型とリテラル
プリミティブ型とそれ以外のオブジェクト型
RubyでいうところのArrayもオブジェクト型
Ruby的にはクラスがメソッドを持っている感じだけど、jsではオブジェクトがプロパティを持っているという理解で合ってるのかな?
ここに関しては続きの章で解説があるらしいので一旦保留
演算子
基本的にはRubyと同じ感じ
ただこれでなんで2になるのかは分からない...
let num = +'2'; num // 2
Rubyみたいにオブジェクトに対してメソッドを呼ぶみたいな考え方じゃないのかも...
===
は比較、==
は暗黙的な型変換してからの比較
jsのfalseは
- false
- undefined
- null
- 0
- 0n
- NaN
- ""
数が多いよ...
||
演算子だとfalsyな値全てがfalse
として扱われてしまう
??
演算子を使えばnull
orundefined
のみfalse
として評価される
暗黙的な型変換
暗黙的な型変換が行われるのは分かったけど1 + true
が2
になるのは衝撃すぎる
基本的に暗黙的な型変換に頼るような実装はやめましょうみたいな理解でいいのかな?
関数と宣言
仮引数が渡されなかった場合はundefinedになって、多く渡した時は無視される
下の例だとnum2がundefinedになってnum1 * num2
の結果がNaNになる
const multiplication = (num1, num2) => { return num1 * num2; end multiplication(2,3) // => 6 multiplication(2) // => NaN multiplication(2,3,4)// => 6
Rubyと違ってエラーにならないのか〜
Rubyでいう可変長引数はこんな感じ
function fn(...args) { return args; } fn(1,2,3,4,5) // => [1, 2, 3, 4, 5] //////////////// function fn(num1, num2) { return num1 * num2; } fn(...[2,3]) // => 6
argments
という関数の中でのみ参照できる特殊な変数がある
function fn() { console.log(arguments); } fn(1, 2, 3) // => [Arguments] { '0': 1, '1': 2 }
Arrayっぽいけど実際はObjectの形で値が入ってるため、インデックスを使って値は取れるけどArrayのメソッドは使えないみたい
ただそもそもArrayもObject型なのに、Arrayのみで使えるメソッドが定義されてるってどういうこと??
この辺りからちょっと理解が難しい...
jsの関数は第一級関数と言って変数に代入できる
function double (num) { return num * 2; } const fn_1 = double; // ()付きで渡してないので関数が変数に代入される const fn_2 = double(5); // ()付きで渡してるので関数の戻り値が代入される
Arrow Functionの書き方で関数式を短く書ける
const fnA = (x) => { return x; }; const fnB = x => { return x; }; // 仮引数がひとつなら()を省略できる const fnC = x => x; // 一行のみの場合はブロックとreturnを省略できる x => x; // 無名関数だとこんな感じで書ける
引数として渡される関数の事をコールバック関数というのは何となく知ってた
オブジェクトのプロパティである関数をメソッドと呼ぶらしい
第一級関数で関数を値として扱えるからオブジェクトの値に関数を定義できるってことかな?
ここが全くRubyと違うのでびっくりした
const obj = { method: num => num }; obj.method(10) // => 10 // こういうい書き方もできる(推奨らしい) const obj = { method(num) { return num; } }
obj.hoge
でオブジェクトに新しくプロパティを追加できる
感想
やっぱりRubyとは全然違うなぁという感想
色々触ってみないと本質的なところが分からないと思うので、とりあえずJavaScript Primer終えたら違う本買ってみようと思います!
続きは後日
RubyGoldに合格しました!!
はじめに
11月末から勉強していたRubyGold試験に88点で合格しました!
既にたくさんの合格しました系の記事はあるのですが、簡単に自分も書いてみます🙋♂️
Ruby歴
業務でRubyを使った開発は昨年の10月から現在までおよそ一年弱の経験があります。
勉強期間
日数で数えると11/29~12/22
途中の1週間でなぜかモチベが爆下がりして全然勉強できなかったので、結局のところ2~3週間くらいでした。
平日はだいたい1時間くらい、土日は4時間くらいやっていたので合計すると3,40時間ぐらいな感じです。
勉強に使用した教材
メタプログラミングRuby
5章までをざっと流し読み&分からなかった問題があるときにその範囲を再度読み直してました。
公式教科書
章末の練習問題、模擬試験は割と本試験に近い感じなので絶対に全て覚えといた方が良さそう。
自分は合計で10週ぐらいして完璧に回答できるようにしときました。
Rex
Silverでもお世話になったWeb問題集。
定数探索とeval系の難しい問題が結構載ってる印象。
なのでRexではその辺りを完璧にしておけばOK。
あくまで公式問題集で深く勉強していない範囲を詰めていくイメージ。
自分はめちゃくちゃやりこみました↓
過去受験した人たちのありがたいブログ
大体全部の範囲を勉強できたら、過去の受験者の方達のブログ記事などを漁って、漏れがないか確認してました。
自分もいくつか記事書いてます↓
Rubyの特異クラス内のインスタンス変数について - 日々徒然
Rubyのeval三兄弟についてのまとめ(instance_eval, class_eval, module_eval) - 日々徒然
感想
RubyGold試験の勉強を通してだいぶRubyのことが理解できたなと思いました。
特にメタプログラミングRubyの内容は、今までなんとなくRubyを書いていた自分からするとどれも知らなかったことばかりで、とても勉強になりました。
よく資格試験は意味がない!みたいに言われたりしますが、業務でRubyを使っていくなら勉強しておいて損は無いと思います(受験料が高いですが...)
ブログでのアウトプットも同時にできたので、自分的には大満足の資格試験でした!!
Rubyのeval三兄弟についてのまとめ(instance_eval, class_eval, module_eval)
はじめに
RubyGoldのお勉強でeval三兄弟について調べたのでまとめた
instance_eval
オブジェクトに対する操作
インスタンスに対してinstance_eval
すると、そのインスタンスの特異メソッドを作成してくれる
例
class Piyo def cry p "piyo" end end piyo = Piyo.new piyo.cry #=> "piyo" piyo.instance_eval do def cry p "piyo" * 2 end end piyo.cry #=> "piyopiyo"
self
に対してcry
メソッドを定義(再定義)した結果、piyopiyoが出力された
ちなみに文字列とブロックを渡す場合で挙動が変わる
ブロックを渡した場合はそのブロックの外側に対する定義、文字列を渡した場合はselfに対する定義になる
こちらも例
class A def const p CONST end end a = A.new # ブロックの外側のObjectにCONSTが定義される a.instance_eval do CONST = "Object#A" end # selfであるインスタンスaにCONSTが定義される a.instance_eval <<-EOS CONST = "eval#A" EOS a.const #=> "Object#A" class << a def singleton_const p CONST end end a.singleton_const #=> "eval#A"
class_eval
ブロックor文字列を渡すと、渡した物をそのクラス内で定義してくれる
class A end A.class_eval do def call p "A" end end A.new.call #=> "A"
こいつもブロックor文字列で動きが変わる
ブロックが渡された場合は定数とクラス変数のスコープがブロックの外側、文字列が渡された時はselfの定義内になる
例
class A def const p CONST p Object::CONST end end # ブロックの外側のObjectのコンテキストでCONSTが定義される A.class_eval do CONST = "block#A" end # selfであるAのコンテキスト内でCONSTが定義される A.class_eval <<-EOS CONST = "string#A" EOS A.new.const #=> "string#A" #=> "block#A"
つまりinstance_eval
もclass_eval
も
ブロックが渡された場合はブロックの外側、文字列が渡されたらselfのコンテキスト内に対しての操作になる
module_eval
class_eval
と一緒
三兄弟(双子と一人)
え、instance_evalにもクラス渡せるよね?
instance_eval
はBasicObjectのメソッドのため、クラスをレシーバーにしても実行できる
わざわざクラスに対してinstance_eval
することも無いとは思うけど気になったので調べてみた
class A end # クラスに対してinstance_eval A.instance_eval do # selfはAなのでAに対する特異メソッド == Aのクラスメソッドが定義される def call p "A" end # selfはAなのでAに対するインスタンスメソッドが定義される define_method :define_call do p "define#call" end end A.call => "A" A.new.define_call => "define#call"
class B end b = B.new # Bクラスのインスタンスbに対してinstance_eval b.instance_eval do # selfはbなのでbに対する特異メソッドが定義される def call p "B" end # selfはbなのでdefine_methodが呼び出せない、エラーになる # define_singleton_methodを呼べば、bの特異メソッドとして定義可能 define_method :define_call do p "define#call" end end
つまりinstance_eval
はselfに対するメソッド定義、class_eval
はselfのコンテキスト内でメソッド定義してくれている
まとめ
RubyGoldだと文字列とブロックを渡した時の定数参照的な問題がでるっぽい...
だいぶ説明足りてないけど、ひとまずヨシ
(完全に理解する)Rubyの定数探索
はじめに
RubyGoldの試験対策をしていく中で、定数探索についてかなり色々調べたのでまとめてみた
基本的な考え方
定数探索の基本的な考え方はレキシカルスコープ→継承ツリー→トップレベル
これだけ覚えておけば大体なんとかなる
基本的なパターン
class A CONST = "A" def const p CONST end end A.new.const #=> "A"
これはCONSTを呼び出してる場所のレキシカルスコープ内にCONSTがあるため、そのままA
が出力される
外部から呼び出すパターン
class A CONST = "A" end class B def const p A::CONST end end B.new.const #=> "A"
レキシカルスコープ、継承以外から呼び出す場合はクラス::定数名
でそのクラスに定義されている定数が呼べる
継承するパターン
class A CONST = "A" end class B < A def const p CONST end end B.new.const #=> "A"
レキシカルスコープに無い && オブジェクトのスーパークラスに無い場合は継承ツリーを辿って定数を探す
この場合はBのインスタンスB.new
のスーパークラスBにCONSTが存在しないため、継承しているAで探す
レキシカルスコープ VS 継承 part1
class A CONST = "A" end class B < A CONST = "B" def const p CONST end end B.new.const #=> "B"
定数はレキシカルスコープ→継承の順番で探索されるため、今回はBが出力される
レキシカルスコープ VS 継承 part2
class A CONST = "A" def const p CONST end end class B < A CONST = "B" end B.new.const #=> "A"
意外にハマりそうな仕様
Aを継承しているためBのインスタンスに対して#constメソッドが呼べるが、 その際に参照されるCONSTはBでは無くconstメソッドを定義しているAのレキシカルスコープが優先される
レキシカルスコープ VS トップレベル
CONST = "Object" class A CONST = "A" def const p CONST end end A.new.const #=> "A"
レキシカルスコープが優先される
継承 VS トップレベル
CONST = "Object" class A CONST = "A" end class B < A def const p CONST end end B.new.const #=> "A"
継承が優先される
特異クラスの定数参照
ここから複雑になる
基本的にレキシカルスコープ→継承の順番で探索することを覚えていれば大丈夫なので、特異クラスの継承が分かっていれば定数探索も分かる
レキシカルスコープ part 1
class A CONST = "A" class << A def const p CONST end end end A.const #=> "A"
CONSTは特異クラスと同じレキシカルスコープで定義されているため参照可能
特異クラスのレキシカルスコープ part 2 (レキシカルスコープ VS レキシカルスコープ)
class A CONST = "class#A" class << self CONST = "singlton#A" def const p CONST end end end A.const #=> "singlton#A"
同一レキシカルスコープ内だと特異メソッド側で定義した値が呼ばれる
特異クラスのレキシカルスコープ part 3 (再オープン)
class A CONST = "class#A" class << self CONST = "singlton#A" end end class << A def const p CONST end end A.const #=> "singlton#A"
特異クラスを再オープンした場合も特異クラスのレキシカルスコープ内に定義される
特異クラスから元クラスの定数は参照できないパターン
class A CONST = "A" end class << A def const p CONST end end A.const #=> uninitialized constant #<Class:A>::CONST
これはエラーになる
探索の流れ↓
CONSTはAの特異クラスのレキシカルスコープ内に無いため継承ツリーを辿ってCONSTを探す
なのでAの特異クラスのスーパークラスはAのスーパークラスの特異クラスになるためA.superclass => Object
の特異クラスに探しに行く
以下の例だと定数が参照できる
class A end CONST = "class#object" # => ObjectにCONSTを定義 class << Object CONST = "singlton#object" # => Objectの特異クラスにCONSTを定義 end class << A def const p CONST end end A.const #=> "singlton#object"
定数探索の順番は
Aの特異クラスのレキシカルスコープ→Aのスーパークラス(Object)の特異クラス
今回はObjectの特異クラス class << Object
にあるCONSTが呼び出された
特異クラスのスーパークラスはクラスのスーパークラスの特異クラス
この呪文を覚えていれば大丈夫
まとめ
定数探索とメソッド探索で若干違うので、勘違いしないように注意します
Rubyの特異クラス内のインスタンス変数について
はじめに
Ruby Goldのお勉強中によく分からない挙動をしていたので手元で動かしてみた。
特異クラス内のインスタンス変数
まずはこのコード
class A @a = 1 def call_a p @a end class << self @a = 2 def call_a p @a end end end
Aクラスの中でインスタンス変数@aに1を代入&特異クラス内で@aに2を代入
メソッドを呼んでみる
A.new.call_a => nil A.call_a => 1
A.new.call_a
がnilになるのは分かる(initialize
でインスタンス変数に値がセットされてない)けど、A.call_a
で2じゃなくて1になるのがよく分からなかった...
けど、手を動かしてみるとなるほどなという結果
class << self
の中でself
を呼んでみる
class A @a = 1 def call_a puts @a end class << self @a = 2 p self # => #<Class:A> def call_a puts @a end end end
#<Class:A>
はA
クラスの特異クラス。
つまり@aは特異クラス内のインスタンス変数ということなので、特異クラス内に特異クラスを作りレシーバーを特異クラスにしてメソッドを定義すれば取ってこれる(何言ってるか謎)
class A @a = 1 def call_a puts @a end class << self @a = 2 def call_a puts @a end class << self def call_a_from_singleton_class puts @a end end end end A.singleton_class.call_a_from_singleton_class # => 2
singleton_class
は自身の特異クラスを返すので、それをレシーバーにしてもう一つネストの深い特異クラスのスコープの中でメソッドを定義する。
まとめ
そもそも特異クラスのスコープの中にインスタンス変数を定義するのかどうか分からない。
自分は見たことがない。
まぁまたちょっとRubyに詳しくなったのでヨシ✋
RubyGold対策②
前回の記事
続きやってく。
refine
変更するクラスを与えるとusing
以降から、その定義内容が反映されるみたい。
module内で定義する。
class Piyo def cry puts "ぴよぴよ" end def call_cry cry end end module Hoge refine Piyo do def cry puts "ほげぴよぴよ" end end end piyo = Piyo.new piyo.cry # => ぴよぴよ using Hoge piyo.cry => ほげぴよぴよ piyo.call_cry => ぴよぴよ
クラスの中でusing
すると、そのクラス内での呼び出しはrefine
が使われるっぽい。
class Piyo def cry puts "ぴよぴよ" end def call_cry cry end end module Hoge refine Piyo do def cry puts "ほげぴよぴよ" end end end class HogePiyo using Hoge def call_cry Piyo.new.cry end end HogePiyo.new.call_cry # => "ほげぴよ" Piyo.new.cry # => "ぴよぴよ" using Hoge HogePiyo.new.call_cry # => "ほげぴよ" Piyo.new.cry # => "ほげぴよ"
require, load
require
は一度だけ読み込み、拡張子補完あり
load
は無条件に読み込み、拡張子補完なし
るりまが言うにはこういうことらしい↓
require はライブラリのロード、load は設定ファイルの読み込みなどに使うのが典型的な用途です。
clone, dup
dup
はオブジェクトの内容をコピー
clone
は↑に加えてfreeze、特異メソッドなど完全なコピー
浅いコピーなので、深いコピーする時にはMarshal
モジュールを使う
特殊変数
$0は実行中のファイル名
$1~$nは正規表現のn番目のカッコにマッチした文字列
分かりやすくまとめてくださってる方がいたので↓
lazy
遅延評価してくれるやつ
chunk
結果によってグループ分けしてくれるやつ
Fiber
本当に知らなかった機能No1
Fiber.new
にブロックを渡して処理を記述→resume
メソッドで実行する
fiber = Fiber.new do p "ふぁいばー" Fiber.yield # 処理を停止 p "ふぁいばー2" end fiber.resume # => ふぁいばー fiber = Fiber.new do p "ふぁいばー" Fiber.yield "停止"# 引数を渡すと、停止した時に返る p "ふぁいばー2" end p fiber.resume # => ふぁいばー # => 停止
自分レベルのよわよわエンジニアにはイマイチ使いどころがわからん...
感想
知らない機能いっぱい...
RubyGold対策①
はじめに
下半期の目標で『RubyGold取得』を掲げているものの、11月末まで全然勉強できなかった(しなかった)ので、これから本腰入れて勉強開始しようと思います。
Rubyの実行環境
分かりやすくまとまってるサイト
Ruby Gold対策(実行環境) - 気軽に楽しくプログラムと遊ぶ
多重代入
なんとなく分かっていたけど、右辺に*がつくパターンってどんな動きだっけ?ってなった
るりましっかり読もう
irb(main):001:0> x,y = [1,2,3] irb(main):002:0> x => 1 irb(main):003:0> y => 2 irb(main):004:0> x,*y = [1,2,3] irb(main):005:0> x => 1 irb(main):006:0> y => [2, 3] irb(main):007:0> x,*y = *[1,2,3] irb(main):008:0> x => 1 irb(main):009:0> y => [2, 3] irb(main):010:0> x,y = *[1,2,3] irb(main):011:0> x => 1 irb(main):012:0> y => 2
Rational
初見でした。
IntとRationalの計算はRational,FloatとRationalの計算はFloatになるみたい。
irb(main):029:0> 4/5r => (4/5) irb(main):030:0> 4.0/5r => 0.8 irb(main):031:0>
ブロック
ブロック引数を{}
で囲むときには()
省略不可、do~end
ならOK
throw/catch
これも初見。
raise/rescue
っぽい感じがする。
irb(main):053:1* def puts_hoge irb(main):054:1* puts "catch前" irb(main):055:1* throw :exit irb(main):056:1* puts "hoge" irb(main):057:0> end => :puts_hoge irb(main):058:0* catch :exit do irb(main):059:0* puts_hoge irb(main):060:-> end catch前 => nil # hogeが出力されない
返り値はthrowに渡されたオブジェクトっぽい
Kernel.#throw (Ruby 3.0.0 リファレンスマニュアル)
super
()
付きと無しで違いあり。
いままでそこまで意識して使ってなかった...
たしかにassign_attributes
をオーバーライドする時とかsuper
だけ書いてる...
public, private, protectedの違い
この辺り実は全然気にしてなかった
特にRailsを書いてる時は「このメソッドは外から呼び出す必要ないからprivate
で良いか〜」ぐらいの認識。
伊藤さんのこのブログが分かりやすかった↓
JavaやC#の常識が通用しないRubyのprivateメソッド - give IT a try
自分でも動かしてみた
class Hoge protected def hoge_protected puts "protectedです〜" end private def hoge_private puts "privateです〜" end end class Piyo < Hoge def call hoge_protected # サブクラス内から呼び出せる hoge_private # サブクラス内から呼び出せる Piyo.new.hoge_protected # サブクラス内でサブクラスのインスタンスから呼び出せる end def call_with_reciever Hoge.new.hoge_protected # サブクラス内で自身のインスタンスから呼び出せる Hoge.new.hoge_private # レシーバー付けたらもちろんエラー end end class Hogepiyo def call_hoge Hoge.new.hoge_protected # サブクラスじゃないと呼べない end def call_piyo Piyo.new.hoge_protected # サブクラスじゃないと呼べない end end
privateをサブクラスから呼び出せるのは知らなかった...
piyo.call
だとPiyo
クラスがself
になるから、メソッド探索の時にsuperクラスのhoge_private
を見つけて呼び出してる感じなのかな?
protected
はself
が自分自身orサブクラスのインスタンスでも呼び出せる。
どっちもサブクラス内で呼べるけど、レシーバーを付けるときはprotected
みたいな感じで覚えとけばとりあえず混乱しなさそう。
ちなみにオーバーライドできるから要注意
class Hoge protected def hoge_protected puts "protectedです〜" end private def hoge_private puts "privateです〜" end end class Piyo < Hoge def call hoge_protected # オーバーライドされる hoge_private # オーバーライドされる Piyo.new.hoge_protected # オーバーライドされる end def call_with_reciever Hoge.new.hoge_protected # オーバーライドされない end protected def hoge_protected puts "オーバーライド後のprotectedです〜" end private def hoge_private puts "オーバーライド後のprivateです〜" end end
感想
private
とprotected
に関しては、まだ理解が浅い感じがする...