第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

首頁 慕課教程 Ruby 入門教程 Ruby 入門教程 33 Ruby 元編程的三種 eval

Ruby 元編程的三種 eval

今天讓我們來學習 Ruby 元編程的三種 eval:eval、instance_eval、class_eval。

1. eval

eval可以將字符串作為代碼進行執(zhí)行,并返回代碼的返回值。

它的使用方法是eval(code_string)。

code_string可以是完整的Ruby代碼、或表達式。

實例:

p eval("1 + 1")

# ---- 輸出結果 ----
2

解釋:在這里"1 + 1"是一個表達式的字符串,我們使用eval來將這個字符串當做表達式進行執(zhí)行,得到返回值2.

當然,如果字符串里面代碼運行報錯也會拋出異常。

實例:

eval(" puts a ")

# ---- 輸出結果 ----

Traceback (most recent call last):
	2: from ruby.rb:1:in `<main>'
	1: from ruby.rb:1:in `eval'
ruby.rb:1:in `eval': undefined local variable or method `a' for main:Object (NameError)

讓我們使用eval來定義一個類。

eval <<-DEFINED_CLASS
  class Person
    def name
      'Andrew'
    end
  end
DEFINED_CLASS
person = Person.new
p person.name

# ---- 輸出結果 ----
"Andrew"

解釋:這里我們使用heredoc來定義了一個多行字符串,里面的內容是定義一個類Person,并在類里面定義了一個實例方法name。從最后輸出結果來看,我們成功的創(chuàng)建了這個類以及對應的實例方法。

Tipseval功能非常強大,靈活度也很高,但是它很危險,有點類似于SQL注入。

比如我們定義了一個方法,可以打印出我們傳入的東西。

實例:

str = "Hi"

eval %Q{
  def method 
    puts #{str}
  end
}
method

# ---- 輸出結果 ----
Hi

但是如果這個str可以被外界篡改,就會產生非常大的問題。

實例:

str = "'Hi'; system('touch i_am_hack && echo \"I am hack\" > i_am_hack'); system('ls');"

eval %Q{
  def method 
    puts #{str}
  end
}
method

# ---- 輸出結果 ----
Hi
你當前所在的目錄結構!!!!

解釋:當輸出Hi的同時,我們創(chuàng)建了一個i_am_hack的文件,并且獲取了執(zhí)行腳本所在文件夾目錄結構。

所以,請輕易不要使用eval。

Tips:有時,eval = evil。

2. instance_eval

從字面意思上來看,我們可以理解為是專門為實例對象做的eval,這個想法是對的。

實例:

class Person
end

person1 = Person.new
person2 = Person.new
person1.instance_eval do 
  def name
    'Andrew'
  end
end
p person1.name
p person2.name

# ---- 輸出結果 ----
"Andrew"
Traceback (most recent call last):
ruby.rb:12:in `<main>': undefined method `name' for #<Person:0x00007fa0d0047f68> (NoMethodError)

解釋:從上面的例子我們可以看到,我們對實例person1增加了一個name方法,增加的這個方法只作用到了person1上面,沒有作用到person2上面,由此我們可以推斷出,instance_eval的修改只是針對被操作的對象。

之前我們學習到,Ruby的類也是對象,那么我們可以對類進行instance_eval操作嗎,答案是可以的。

實例:

class Person
end

Person.instance_eval do 
  def name
    'Andrew'
  end
end

p Person.name

# ---- 輸出結果 ----
"Andrew"

解釋:因為所有的類都是Class類的實例,所有,我們在對類進行instance_eval時,相當于拓展了它的類方法。這種也是instance_eval最常用的功能。

3. class_eval

根據方法名來看,我們猜測了這個是對類做的eval,沒錯確實是這樣的,它常常用于給類添加實例方法。

實例:

class Person
end

Person.class_eval do 
  def name
    'Andrew'
  end
end

person1 = Person.new
person2 = Person.new

p person1.name
p person2.name

# ---- 輸出結果 ----
"Andrew"
"Andrew"

解釋:上面示例中,我們?yōu)?code>Person這個類添加了name的實例方法,由輸出可知,我們成功的在Person的每個實例下都添加了name方法。

我們也可以從定義的方法中獲取到對應類的實例變量。

class Person
  def initialize name
    @name = name
  end
end

Person.class_eval do 
  def name
    @name
  end
end

person1 = Person.new 'Tom'
person2 = Person.new 'Bob'

p person1.name
p person2.name

# ---- 輸出結果 ----
"Tom"
"Bob"

Tips:class_eval 和 instance_eval 后面不僅可以使用 block,還可以直接添加字符串,類似于eval,但是同樣不建議使用,同樣會導致不安全的問題。

實例:

class Person
end

Person.class_eval <<-DOC
  def name
   'Andrew'
  end
DOC

p Person.new.name

# ---- 輸出結果 ----
"Andrew"

4. 小結

今天我們學習了 Ruby 中的三個 eval,其中eval的擴展性最強,但最危險,instance_eval用于擴展類方法,class_eval用于擴展類的實例方法。