为什么我在这里可以像调用实例方法一样调用 class 方法?
Why am I able to call the class method as if it were an instance method here?
我正在查看这个例子:
class SQLObject
def self.columns
return @columns if @columns
columns = DBConnection.execute2(<<-SQL).first
SELECT
"#{table_name}".*
FROM
"#{table_name}"
LIMIT
0
SQL
columns.map!(&:to_sym)
@columns = columns
end
def self.table_name
@table_name ||= self.name.underscore.pluralize
end
def insert
column_symbols = self.class.columns.drop(1)
column_names = column_symbols.map(&:to_s).join(", ")
question_marks = (['?'] * column_symbols.count).join(", ")
DBConnection.execute(<<-SQL, *attribute_values)
INSERT INTO
#{self.class.table_name} (#{column_names})
VALUES
(#{question_marks})
SQL
self.id = DBConnection.last_insert_row_id
end
end
而且我很困惑为什么可以在"self.columns"方法中调用table_name方法,就好像它是一个实例方法一样。 "table_name" 方法不是 class 方法吗?因此,它不应该在 "self.columns" 方法中也被称为 "self.class.table_name" 吗?
当在抽象 class 中时,self
指的是正确的 class,而不是对象。这就是为什么您可以在不显式告诉 self
的情况下访问该方法的原因
简短回答:self
是 Ruby 中的隐式接收者。如果您将其遗漏,消息将发送至 self
。换句话说,never 必须在消息发送中说 self.foo
,因为 foo
是 always与 self.foo
.
相同
长答案:Ruby 中没有“class 方法”这样的东西。你所谓的“class方法”实际上只不过是定义在一个对象上的单例方法,而这个对象恰好是一个实例的 class Class
.
实际上,Ruby中也没有单例方法。 单例方法实际上只是一个常规的旧实例方法,它定义在单例class 一个对象。
[注意:这并不是说您不应该使用这些术语。说 "class method" 显然比 "instance method of the singleton class of an object of class Class
" 更简单并且更能传达意图。另请注意,还有 Object#singleton_methods
等方法。 Ruby社区中显然存在这些术语,但重要的是要了解这些概念不存在于Ruby 语言。它们是一种交流工具,而不是实际的 Ruby 概念。]
单例class 是与单个对象关联的class。每个对象都有 one 单例 class 并且该对象是 only 实例("singleton instance",因此得名)它的单例 class.
事实上,对象的继承层次总是从它的单例class开始,即class指针[=一个对象的181=]总是指向它的单例class,而单例class的superclass指针则指向class 创建了这个对象。你只是在继承链中看不到单例class,因为在计算继承链时像Object#class
"skip over"单例class这样的方法。它是,但是,总是在方法查找期间使用。
事实上,方法查找其实很简单(唯一的例外是Module#prepend
,我将忽略这个解释):
- 取消引用接收者的 class 指针。
- 如果 class 的方法 table 有一个消息名称的方法,调用它。停止。
- 如果不是,取消引用 class 的 superclass 指针。
- 转到#2。
- 如果您到达 class 而没有超级 class,请使用消息
method_missing(original_message_name, ...)
, unless the message name is already method_missing
, in which case raise a NoMethodError
重新启动算法。
所以,这一切都归结为两个问题:
- 在定义方法时,你是在哪个class(或模块)中定义的?
- 发送消息时,你要向哪个对象发送消息?
当您定义一个使用def foo.bar
的方法时,Ruby将首先计算表达式foo
然后定义一个方法bar
在结果对象的单例 class 上(definee)。如果您不提供 foo
,即您只说 def bar
,则 bar
定义在 , which is a little bit of a tricky concept. Normally, it is the closest lexically enclosing module definition. If there is no enclosing module definition, i.e. you are at the top-level, the default definee is Object
and the method visibility is private
.
当您使用foo.bar
发送消息时,Ruby将首先计算表达式foo
,然后发送消息bar
到结果对象( 接收器)。如果您不提供 foo
,即如果您只说 bar
,则 默认接收者 是 self
.
这让我们想到下一个问题:
- 什么是
self
?
在方法定义主体中,self
是导致调用此方法的消息发送的接收方。因此,如果您发送消息 foo.bar
,那么在方法 bar
的定义中对 self
的任何引用都将在该方法的这一次执行期间评估为 foo
.
在模块或 class 定义体中,self
是 class 或方法本身。 (这就是为什么使用 def self.bar
定义单例 class 的 单例方法 实例方法在模块或 class 定义体中工作。)
在一个块或 lambda 文字中,self
是词法捕获的,即它是 self
在块或 lambda 文字被写入的地方。 但是,有几个方法改变了 self
的计算方式,最显着的是 BasicObject#instance_eval
, BasicObject#instance_exec
, Module#module_eval
, Module#module_exec
, Module#class_eval
, Module#class_exec
,…方法系列。
如果您在代码中的任何一点始终知道这三个问题的答案(我在哪里定义方法,方法调用的接收者是什么,self
是什么),那么你已经基本理解了Ruby.
最重要的部分
所以,把它们放在一起:
在使用 def self.columns
定义方法时,我们处于 class 定义体中,因此 self
指的是 class 对象本身( SQLObject
).
语法 def foo.bar
在 foo
的单例 class 上定义了一个方法 bar
。在这种情况下,您在 SQLObject
的单例 class 上定义方法 columns
。这意味着对象 SQLObject
是宇宙中唯一将响应消息 columns
.
的对象
这两个也适用于self.table_name
的定义。
在self.columns
的方法体内部,self
会动态引用接收者。
当你发送消息 columns
到 SQLObject
时,即你写 SQLObject.columns
,然后在 SQLObject::columns
方法的主体内,接收者(即 self
) 将被设置为 SQLObject
。 (在这种情况下,接收者将始终是 SQLObject
,因为这是一个单例方法 class。)
所以,在self.columns
的方法体内,当你写消息发送table_name
时,这是一个带有隐式接收者的消息发送。由于隐式接收者是 self
,这相当于 self.table_name
。 self
此时绑定到SQLObject
,所以这相当于SQLObject.table_name
。也就是说,消息被发送到SQLObject
.
现在,根据我上面概述的算法,方法查找首先从 SQLObject
检索 class 指针。如前所述,class 指针始终指向单例 class。因此,我们在 SQLObject
的单例 class 中查看是否找到名为 table_name
的方法,我们找到了!
方法查找完成,一切正常。
我正在查看这个例子:
class SQLObject
def self.columns
return @columns if @columns
columns = DBConnection.execute2(<<-SQL).first
SELECT
"#{table_name}".*
FROM
"#{table_name}"
LIMIT
0
SQL
columns.map!(&:to_sym)
@columns = columns
end
def self.table_name
@table_name ||= self.name.underscore.pluralize
end
def insert
column_symbols = self.class.columns.drop(1)
column_names = column_symbols.map(&:to_s).join(", ")
question_marks = (['?'] * column_symbols.count).join(", ")
DBConnection.execute(<<-SQL, *attribute_values)
INSERT INTO
#{self.class.table_name} (#{column_names})
VALUES
(#{question_marks})
SQL
self.id = DBConnection.last_insert_row_id
end
end
而且我很困惑为什么可以在"self.columns"方法中调用table_name方法,就好像它是一个实例方法一样。 "table_name" 方法不是 class 方法吗?因此,它不应该在 "self.columns" 方法中也被称为 "self.class.table_name" 吗?
当在抽象 class 中时,self
指的是正确的 class,而不是对象。这就是为什么您可以在不显式告诉 self
简短回答:self
是 Ruby 中的隐式接收者。如果您将其遗漏,消息将发送至 self
。换句话说,never 必须在消息发送中说 self.foo
,因为 foo
是 always与 self.foo
.
长答案:Ruby 中没有“class 方法”这样的东西。你所谓的“class方法”实际上只不过是定义在一个对象上的单例方法,而这个对象恰好是一个实例的 class Class
.
实际上,Ruby中也没有单例方法。 单例方法实际上只是一个常规的旧实例方法,它定义在单例class 一个对象。
[注意:这并不是说您不应该使用这些术语。说 "class method" 显然比 "instance method of the singleton class of an object of class Class
" 更简单并且更能传达意图。另请注意,还有 Object#singleton_methods
等方法。 Ruby社区中显然存在这些术语,但重要的是要了解这些概念不存在于Ruby 语言。它们是一种交流工具,而不是实际的 Ruby 概念。]
单例class 是与单个对象关联的class。每个对象都有 one 单例 class 并且该对象是 only 实例("singleton instance",因此得名)它的单例 class.
事实上,对象的继承层次总是从它的单例class开始,即class指针[=一个对象的181=]总是指向它的单例class,而单例class的superclass指针则指向class 创建了这个对象。你只是在继承链中看不到单例class,因为在计算继承链时像Object#class
"skip over"单例class这样的方法。它是,但是,总是在方法查找期间使用。
事实上,方法查找其实很简单(唯一的例外是Module#prepend
,我将忽略这个解释):
- 取消引用接收者的 class 指针。
- 如果 class 的方法 table 有一个消息名称的方法,调用它。停止。
- 如果不是,取消引用 class 的 superclass 指针。
- 转到#2。
- 如果您到达 class 而没有超级 class,请使用消息
method_missing(original_message_name, ...)
, unless the message name is alreadymethod_missing
, in which case raise aNoMethodError
重新启动算法。
所以,这一切都归结为两个问题:
- 在定义方法时,你是在哪个class(或模块)中定义的?
- 发送消息时,你要向哪个对象发送消息?
当您定义一个使用def foo.bar
的方法时,Ruby将首先计算表达式foo
然后定义一个方法bar
在结果对象的单例 class 上(definee)。如果您不提供 foo
,即您只说 def bar
,则 bar
定义在 Object
and the method visibility is private
.
当您使用foo.bar
发送消息时,Ruby将首先计算表达式foo
,然后发送消息bar
到结果对象( 接收器)。如果您不提供 foo
,即如果您只说 bar
,则 默认接收者 是 self
.
这让我们想到下一个问题:
- 什么是
self
?
在方法定义主体中,self
是导致调用此方法的消息发送的接收方。因此,如果您发送消息 foo.bar
,那么在方法 bar
的定义中对 self
的任何引用都将在该方法的这一次执行期间评估为 foo
.
在模块或 class 定义体中,self
是 class 或方法本身。 (这就是为什么使用 def self.bar
定义单例 class 的 单例方法 实例方法在模块或 class 定义体中工作。)
在一个块或 lambda 文字中,self
是词法捕获的,即它是 self
在块或 lambda 文字被写入的地方。 但是,有几个方法改变了 self
的计算方式,最显着的是 BasicObject#instance_eval
, BasicObject#instance_exec
, Module#module_eval
, Module#module_exec
, Module#class_eval
, Module#class_exec
,…方法系列。
如果您在代码中的任何一点始终知道这三个问题的答案(我在哪里定义方法,方法调用的接收者是什么,self
是什么),那么你已经基本理解了Ruby.
所以,把它们放在一起:
在使用 def self.columns
定义方法时,我们处于 class 定义体中,因此 self
指的是 class 对象本身( SQLObject
).
语法 def foo.bar
在 foo
的单例 class 上定义了一个方法 bar
。在这种情况下,您在 SQLObject
的单例 class 上定义方法 columns
。这意味着对象 SQLObject
是宇宙中唯一将响应消息 columns
.
这两个也适用于self.table_name
的定义。
在self.columns
的方法体内部,self
会动态引用接收者。
当你发送消息 columns
到 SQLObject
时,即你写 SQLObject.columns
,然后在 SQLObject::columns
方法的主体内,接收者(即 self
) 将被设置为 SQLObject
。 (在这种情况下,接收者将始终是 SQLObject
,因为这是一个单例方法 class。)
所以,在self.columns
的方法体内,当你写消息发送table_name
时,这是一个带有隐式接收者的消息发送。由于隐式接收者是 self
,这相当于 self.table_name
。 self
此时绑定到SQLObject
,所以这相当于SQLObject.table_name
。也就是说,消息被发送到SQLObject
.
现在,根据我上面概述的算法,方法查找首先从 SQLObject
检索 class 指针。如前所述,class 指针始终指向单例 class。因此,我们在 SQLObject
的单例 class 中查看是否找到名为 table_name
的方法,我们找到了!
方法查找完成,一切正常。