instance_eval如何工作,DHH为什么讨厌它?

在他的RailsConf演讲中大约在19:00,David Heinemeier Hansson谈到了instance_eval的缺点:

很长一段时间,我狂妄地反对instance_eval ,这是不使用屈服参数的概念(像do |people| ),只是直接do something ,然后评估你从哪里来的范围内的块(I甚至不知道这是一个连贯的解释)

很长一段时间我不喜欢那样,因为在某种意义上感觉更复杂。 如果你想把自己的代码放在那里,你会触发已经存在的东西吗? 你打算重写什么吗? 当你产生一个特定的variables时,你可以把所有的东西都连接起来,你可以知道(你并没有搞乱其他人的东西)

这听起来很有趣,但是a)我不知道instance_eval是如何工作的,b)我不明白为什么它可能会导致错误/增加复杂性。

有人可以解释吗?

instance_eval所做的事是它在不同实例的上下文中运行块。 换句话说,它改变了self意义,这意味着它改变了实例方法和实例variables的含义。

这产生了认知上的断开:块运行的上下文不是它在屏幕上出现的上下文。

让我来certificate一下@Matt Briggs的例子。 假设我们没有build立表单,我们正在build立一个电子邮件:

 mail do |f| f.subject @subject f.name name end 

在这种情况下, @subject你的对象的一个​​实例variables, name你的类的一个方法。 你可以使用很好的面向对象分解,并将你的主题存储在一个variables中。

 mail do subject @subject name name # Huh?!? end 

这种情况下, @subject邮件生成器对象的一个​​实例variables! 它可能不存在 ! (或者更糟的是,它可能存在并包含一些完全愚蠢的值)。你无法访问你的对象的实例variables。 你甚至怎么称呼你的对象的name方法? 每次你尝试调用它,你都会得到邮件生成器的方法。

基本上, instance_eval很难在DSL代码中使用自己的代码。 所以,只有在这个可能性很小的情况下才能使用。

好的,所以这里的主意不是这样的

 form_for @obj do |f| f.text_field :field end 

你得到这样的东西

 form_for @obj do text_field :field end 

第一种方式非常简单,你最终会看到一个像这样的模式

 def form_for b = FormBuilder.new yield b b.fields.each |f| # do stuff end end 

你得到一个消费者调用方法的构build器对象,然后调用构build器对象上的方法来实际构build表单(或其他)

第二个更神奇

 def form_for &block b = FormBuilder.new b.instance_eval &block b.fields.each |f| #do stuff end end 

在这一个,而不是让build设者的块,我们采取块和评估在build设者的背景下

第二个增加了复杂性,因为你用范围玩游戏,你需要明白,而且消费者需要了解这一点,而且谁写了你的build设者需要明白这一点。 如果每个人都在同一页面上,我不知道这绝对是一件坏事,但是我确实质疑好处与成本,我的意思是,如何去处理一个f。 在你的方法前面?

这个想法是有点危险,因为如果不阅读处理使用instance_eval的对象的所有代码,就无法确定不会破坏某些东西。

另外,如果你说,更新了一个没有改变界面的库,但是改变了很多对象的内部,你确实会造成一些损害。