我最近看到一些我不完全理解的代码。有一个名为foo
的数组,其中包含Proc
个对象的实例。然后,env对象用于设置要使用的环境:
env = Object.new
foo.each do |f|
env.instance_eval &f # what happens here?
end
使用instance_eval打开对象并将&f
作为参数传递时会发生什么? env在这一点和Proc本身会发生什么?
答案 0 :(得分:2)
为proc更改范围,然后在该上下文中对其进行评估。在内部,所有的proc都作为C struct
存储在内存中,其中包括proc的self
(proc的制作范围)。当您致电instance_eval
时,会在内存中手动将self
值更改为您正在呼叫的对象instance_eval
。如果你探索ruby源代码,你会发现它归结为这个函数:
static VALUE
yield_under(VALUE under, VALUE self, VALUE values)
{
rb_thread_t *th = GET_THREAD();
rb_block_t block, *blockptr;
NODE *cref;
if ((blockptr = GC_GUARDED_PTR_REF(th->cfp->lfp[0])) != 0) {
block = *blockptr;
block.self = self; // <- This is where the scope changes!
th->cfp->lfp[0] = GC_GUARDED_PTR(&block);
}
cref = vm_cref_push(th, under, NOEX_PUBLIC, blockptr);
cref->flags |= NODE_FL_CREF_PUSHED_BY_EVAL;
if (values == Qundef) {
return vm_yield_with_cref(th, 1, &self, cref);
}
else {
return vm_yield_with_cref(th, RARRAY_LENINT(values), RARRAY_PTR(values), cref);
}
}
请注意包含// <- This is where the scope changes!
。
答案 1 :(得分:1)
Proc在env
的上下文中执行。就像你在env
上调用一个方法一样:该块可以访问它的实例变量以及公共和私有方法。
env = Object.new
env.instance_variable_set :@test, "test"
class << env
private
def test
@test
end
end
env.instance_eval { @test } #=> "test"
env.instance_eval { test } #=> "test"