Hôm nay tôi muốn nói về self
. Nếu bạn đã lập trình Ruby được một thời gian, bạn có thể đã hình thành ý tưởng về self
. Bất cứ khi nào bạn đọc hoặc viết một chương trình, self
ở đó trong tâm trí bạn.
Nhưng đối với những Rubyists ít kinh nghiệm, hãy self
có thể gây khó hiểu. Nó luôn thay đổi, nhưng nó không bao giờ được hiển thị rõ ràng trong mã. Bạn chỉ cần biết.
Rất nhiều vấn đề mà người mới bắt đầu gặp phải là do không hiểu self
. Nếu bạn đã từng "đánh mất" một biến phiên bản hoặc phân vân không biết dữ liệu nào hiển thị cho một mixin, thì đó là do bạn không hiểu self
trong bối cảnh đó.
Trong bài đăng này, chúng ta sẽ xem xét self
trong nhiều tình huống hàng ngày.
self
là gì ?
Bạn có thể đã nghe mọi người nói rằng mọi thứ trong Ruby đều là một đối tượng. Nếu điều đó đúng, điều đó có nghĩa là mọi đoạn mã bạn viết "thuộc về" đối tượng nào đó.
self
là một biến đặc biệt trỏ đến đối tượng "sở hữu" đoạn mã đang thực thi. Ruby sử dụng self
mọi nơi:
- Đối với các biến ví dụ:
@myvar
- Đối với phương pháp và tra cứu liên tục
- Khi xác định các phương thức, lớp và mô-đun.
Về lý thuyết, self
là khá rõ ràng. Nhưng trong thực tế, rất dễ xảy ra những tình huống khó khăn. Đó là lý do tại sao tôi viết bài này.
Ví dụ về self
Bây giờ chúng ta sẽ xem xét một số ví dụ. Nếu những cái đầu tiên có vẻ quá cơ bản đối với bạn, hãy tiếp tục đọc. Chúng được nâng cao hơn.
Bên trong của một phương thức phiên bản
Trong đoạn mã dưới đây, reflect
là một phương thức thể hiện. Nó thuộc về đối tượng chúng tôi đã tạo qua Ghost.new
. Vì vậy, self
chỉ vào đối tượng đó.
class Ghost
def reflect
self
end
end
g = Ghost.new
g.reflect == g # => true
Bên trong của một phương thức lớp
Đối với ví dụ này, reflect
là một phương thức lớp của Ghost
. Với các phương thức của lớp, chính lớp đó "sở hữu" phương thức. self
chỉ vào lớp học.
class Ghost
def self.reflect
self
end
end
Ghost.reflect == Ghost # => true
Nó hoạt động tương tự với các phương thức "lớp" bên trong mô-đun. Ví dụ:
module Ghost
def self.reflect
self
end
end
Ghost.reflect == Ghost # => true
Hãy nhớ rằng, các lớp và mô-đun được coi như các đối tượng trong Ruby. Vì vậy, hành vi này không khác nhiều so với hành vi của phương thức cá thể mà chúng ta đã thấy trong ví dụ đầu tiên.
Bên trong định nghĩa lớp hoặc mô-đun
Một tính năng của Ruby khiến nó trở nên phù hợp với các khung công tác như Rails là bạn có thể thực thi mã tùy ý bên trong các định nghĩa lớp và mô-đun. Khi bạn đặt mã bên trong định nghĩa lớp / mô-đun, nó sẽ chạy giống như bất kỳ mã Ruby nào khác. Sự khác biệt thực sự duy nhất là giá trị của self
.
Như bạn có thể thấy bên dưới, self
trỏ đến lớp hoặc mô-đun đang trong quá trình được xác định.
class Ghost
self == Ghost # => true
end
module Mummy
self == Mummy # => true
end
Các phương thức mixin bên trong
Các phương thức kết hợp hoạt động giống như các phương thức lớp hoặc trường hợp "bình thường" khi nói đến self
. Điều này thật ý nghĩa. Nếu không, mixin sẽ không thể tương tác với lớp mà bạn đã trộn nó vào.
Phương thức phiên bản
Mặc dù reflect
phương thức đã được xác định trong mô-đun, self
của nó là thể hiện của lớp mà nó đã được trộn vào.
module Reflection
def reflect
self
end
end
class Ghost
include Reflection
end
g = Ghost.new
g.reflect == g # => true
Phương thức lớp
Khi chúng tôi extend
một lớp để trộn trong các phương thức của lớp, self
hoạt động chính xác như nó hoạt động trong các phương thức lớp bình thường.
module Reflection
def reflect
self
end
end
class Ghost
extend Reflection
end
Ghost.reflect == Ghost # => true
Bên trong siêu kính
Rất có thể bạn đã thấy phím tắt phổ biến này để xác định nhiều phương thức lớp cùng một lúc.
class Ghost
class << self
def method1
end
def method2
end
end
end
Lớp class << foo
cú pháp thực sự khá thú vị. Nó cho phép bạn truy cập siêu kính của một đối tượng - còn được gọi là "lớp singleton" hoặc "lớp eigenclass." Tôi dự định sẽ đề cập sâu hơn đến kính thiên văn trong một bài đăng trong tương lai. Nhưng hiện tại, bạn chỉ cần biết rằng metaclass là nơi Ruby lưu trữ các phương thức dành riêng cho một đối tượng cụ thể.
Nếu bạn truy cập self
từ bên trong class << foo
khối, bạn sẽ có được siêu kính.
class << "test"
puts self.inspect
end
# => #<Class:#<String:0x007f8de283bd88>
Bên ngoài bất kỳ lớp nào
Nếu bạn đang chạy mã bên ngoài bất kỳ lớp nào, Ruby vẫn cung cấp self
. Nó trỏ tới "main", là một bản sao của Object
:
puts self.inspect # => main