thủ tục toàn cầu trong Ruby không thực sự thủ tục toàn cầu. Chúng là những phương pháp, giống như mọi thứ khác. Cụ thể, khi bạn xác định những gì trông giống như thủ tục toàn cầu, bạn là thực sự là xác định phương thức cá nhân riêng lẻ là Object
. Vì mọi đoạn mã trong Ruby được đánh giá trong ngữ cảnh của một đối tượng, điều này cho phép bạn sử dụng các phương thức đó như là các thủ tục chung, vì self
là bộ nhận mặc định và self
là một đối tượng có lớp kế thừa từ Object
.
Vì vậy, đây:
# file1.rb
def foo
puts 123
end
thực sự là tương đương với
# file1.rb
class Object
private
def foo
puts 123
end
end
Bây giờ bạn có một "quy trình toàn cầu" được gọi foo
, mà bạn có thể gọi như thế này:
foo
Lý do lý do bạn có thể gọi nó như thế này, là cuộc gọi này thực sự là tương đương với
self.foo
và self
là một đối tượng bao gồm Object
trong chuỗi gốc của nó, do đó nó được thừa hưởng các foo
phương pháp riêng.
[Lưu ý: chính xác, không thể gọi phương thức riêng tư với người nhận rõ ràng, ngay cả khi người nhận rõ ràng là self
. Vì vậy, để được thực sự gàn dở, nó là thực tương đương với self.send(:foo)
và không self.foo
]
Các A.new.foo
trong bạn file2.rb
là một cá trích đỏ:. Bạn có thể chỉ cần cũng cố gắng Object.new.foo
hoặc [].foo
hay 42.foo
và nhận được kết quả tương tự .
Bằng cách này: puts
và require
là mình ví dụ như vậy "thủ tục toàn cầu", mà thực sự là phương pháp tin trên Object
(hay chính xác hơn, họ có phương pháp riêng trên Kernel
được trộn vào Object
).
Trên một sidenote: nó là thực sự phong cách xấu để đặt cuộc gọi đến require
bên trong một định nghĩa lớp, bởi vì nó làm cho nó trông giống như mã require
d được bằng cách nào đó scoped hoặc namespaced bên trong lớp, đó là tất nhiên sai. require
chỉ cần chạy mã trong tệp, không có gì khác.
Vì vậy, trong khi
# file2.rb
class A
require 'file1.rb'
end
là mã hoàn toàn hợp lệ, nó cũng rất khó hiểu. Nó là tốt hơn để sử dụng sau, ngữ nghĩa tương đương, mã:
# file2.rb
require 'file1.rb'
class A
end
Bằng cách đó nó là hoàn toàn rõ ràng đối với người đọc của mã mà file1.rb
là không có cách nào scoped hoặc namespaced bên A
.
Ngoài ra, thường được ưu tiên rời khỏi đuôi tệp, tức là sử dụng require 'file1'
thay vì require 'file1.rb'
. Điều này cho phép bạn thay thế tệp Ruby bằng, ví dụ, mã gốc (cho MRI, YARV, Rubinius, MacRuby hoặc JRuby), mã byte JVM trong một tệp .jar
hoặc .class
(cho JRuby), mã byte CIL trong .dll
tệp (cho IronRuby) và vân vân, mà không phải thay đổi bất kỳ cuộc gọi nào trong số các cuộc gọi require
của bạn.
Một nhận xét cuối cùng: cách thành ngữ để tránh né bảo vệ truy cập là sử dụng send
, không phải instance_eval
, tức là sử dụng A.new.send(:foo)
thay vì A.new.instance_eval {foo}
.
Bạn đã thử 'A.new.instance_eval {foo}' chưa? Nó không làm việc cho tôi (ruby 1.9.2). – knut
Bạn có nhầm lẫn với 'require with' include' không? –
@knut tôi đã làm. Nó hoạt động. – alexloh