學 Ruby 時,時常在不同的方法中看到某個神秘變數 self ,self 到底是誰呢?


何謂 Self ?

Ruby 的官方 API 中這麼寫道:

self 是「當前的物件」,以及當沒有明確指定接收者時,訊息(方法呼叫)的預設接收者。 self 所在的上下文決定 self 所代表的物件。

1
2
3
4
5
class String
def upcase_and_reverse
upcase.reverse
end
end

在這個方法中,upcase 這個訊息會被傳到 self,而 self 就是任何呼叫該方法的字串。




什麼是「目前的物件」?

  • 單純地印出 self
    self 指的是 main ,即是 object 的實體
1
2
3
4
p self
# main
p self.class
# Object

  • 在一個 class / module 中
    self 指的是該 class / module
1
2
3
4
5
6
7
8
class Dog
p self
end
# Dog
module Bird
p self
end
# Bird

  • 在實體方法中
    self 指的是當前 class 的實體
1
2
3
4
5
6
7
8
9
10
class Dog
def instance_method
self
end
end
black = Dog.new
black.instance_method
# <Dog:0x00007f85540ede78>
black.instance_method == black
# true

  • 在類別方法中
    self 指的是當前的 class
1
2
3
4
5
6
7
8
9
class Dog
def self.class_method
self
end
end
Dog.class_method
# Dog
Dog.class_method == Dog
# true

  • 混合類型的實體方法
    即使是在 module 中定義了 hello 方法,self 仍是它所混合的 class 的實體
1
2
3
4
5
6
7
8
9
10
11
12
13
module Greeting
def hello
self
end
end
class Dog
include Greeting
end
black = Dog.new
black.hello
# <Dog:0x00007fdccc93b3d0>
black.hello == black
# true

  • 混合類型的類別方法
    當 extend class 的方法時,self 的行為與普通的類別方法完全一樣
1
2
3
4
5
6
7
8
9
10
11
12
13
module Greeting
def hello
self
end
end

class Dog
extend Greeting
end
Dog.hello
# Dog
Dog.hello == Dog
# true

在 Ruby 中, module 跟 class 都是物件,不論在 class 或 module,self 都是引用它所在的上下文的實體。




什麼是「訊息的預設接收者」?

  • 在一個類別方法中
    self 指的是 Dog ,因此 self.sleep 等於 Dog.sleep
1
2
3
4
5
6
7
8
9
10
class Dog
def self.sleep
puts 'zzz'
end
end
class Dog
def Dog.sleep
puts 'zzz'
end
end

  • 呼叫類別方法,當有明確的接收者時
    可以用 self 去呼叫 sleep() 這個類別方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Dog
def self.sleep
puts 'zzz'
end
Dog.sleep
end
# zzz
class Dog
def self.sleep
puts 'zzz'
end
self.sleep
end
# zzz

  • 呼叫類別方法,當沒有明確的接收者時
    在 class Dog 中, self 指的是 Dog,所以可以省略 self。
    因為沒有傳訊息或接收者物件的小數點符號,當呼叫沒有接收者的方法時,訊息會傳給當前的值。由於當前的值是 Dog(預設接收者),可以直接呼叫類別方法 sleep。
1
2
3
4
5
6
7
class Dog
def self.sleep
puts 'zzz'
end
sleep
end
# zzz

在 Ruby 中,呼叫方法是以 obj.method 的方式,用一個小數點標記,左邊是接收者,右邊是方法。
但是,當接收者是 self 的時候,可以省略接收者和小數點。Ruby 將 self 當做預設的接收者,代表訊息會傳給 self。method == self.method。
必須注意的是,在呼叫寫入方法時(以等號結束的方法),即使是傳訊息給當前的 self ,也不能省略。
例如:呼叫方法 abc =,要寫成 self.abc = “hello”。如果寫成 abc = “hello” ,Ruby 會解讀爲對局部變數的賦值。



參考資料:
https://ruby-doc.org/docs/keywords/1.9/Object.html#method-i-self
https://rubyplus.com/articles/4861-Ruby-Basics-The-Default-Receiver
https://www.honeybadger.io/blog/ruby-self-cheat-sheet/
https://www.iteye.com/blog/fantaxy025025-1108639
http://www.52help.net/tw/ruby/63.mhtml