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

Ruby 的作用域 Scope

作用域存在于任何編程語(yǔ)言中,如果不夠了解作用域,經(jīng)常會(huì)出現(xiàn)變量未定義、錯(cuò)誤分配變量值等等問(wèn)題,本章節(jié)中會(huì)對(duì) Ruby 的作用域做深度剖析。

1. 作用域是什么

作用域就是變量的有效使用范圍。當(dāng)提到作用域的時(shí)候,您應(yīng)該了解,在Ruby中任何一行在該上下文中,哪些變量可用,哪些變量不可用。

有人會(huì)說(shuō),那么讓變量在全局的任何地方都可用不就好了,如使用全局變量(Global Variable),這樣就不會(huì)因?yàn)樽饔糜蚨鵁馈?/p>

但是當(dāng)您從事了編程工作一段時(shí)間后,您會(huì)發(fā)現(xiàn),全局變量非常地不可控,任何人都有能力修改這個(gè)變量,這個(gè)變量究竟是被誰(shuí)讀了、被誰(shuí)寫(xiě)了,問(wèn)題一單產(chǎn)生很難追蹤。并且全局變量要求我們的命名必須不同,這樣的話在一個(gè)項(xiàng)目中,可能會(huì)出現(xiàn)上千個(gè)變量名。

在之前Ruby的變量章節(jié)中我們講解了4種變量的類型?,F(xiàn)在按照作用域大小來(lái)劃分他們:

  1. 全局變量(Global Variable)的作用域包括頂級(jí)作用域、每一個(gè)類、實(shí)例、局部(任意地方);

  2. 類變量(Class Variable)的作用域包括類、實(shí)例、局部;

  3. 實(shí)例變量(Instance Variable)作用域包括實(shí)例、局部;

  4. 局部變量(Local Variable)作用域僅有局部。

2. 頂級(jí)作用域

頂級(jí)作用域(Top Level Scope)意味著您未調(diào)用任何方法,或者所有的方法都已經(jīng)返回。簡(jiǎn)單來(lái)講,當(dāng)我們剛打開(kāi)irb或在一個(gè)沒(méi)有任何類或方法的Ruby腳本之中,我們所處的就是頂級(jí)作用域。

在Ruby中一切皆為對(duì)象,即使您處于頂級(jí)作用域,也位于一個(gè)對(duì)象之中,它的名字叫做main,屬于類Object。

下面是irb的示例:

$irb
> puts self
main
=> nil
> puts self.class
Object
=> nil

或在一個(gè)空腳本中輸入:

p self
p self.class

# ---- 輸出結(jié)果 ----
main
Object

3. 作用域門(mén)

當(dāng)我們執(zhí)行下面三種操作的時(shí)候會(huì)打開(kāi)作用域門(mén)(Scope Gate),進(jìn)入一個(gè)全新的作用域(完全不同的上下文):

  1. 定義一個(gè)類(class SomeClass);
  2. 定義一個(gè)模塊(module SomeModule);
  3. 定義一個(gè)方法(def some_method)。

我們用一個(gè)局部變量的例子來(lái)解釋作用域門(mén)的概念。

局部變量有這樣的特性,當(dāng)我們輸出一個(gè)一個(gè)未定義的局部變量會(huì)拋出 NameError 的異常。

實(shí)例:

puts a

# ---- 輸出結(jié)果 ----
undefined local variable or method `a' for main:Object (NameError)

Tips:實(shí)例變量和全局變量擁有默認(rèn)值,為nil。

當(dāng)我們?cè)谧饔糜蚍秶鷥?nèi)為局部變量定義,不管代碼是否執(zhí)行,Ruby 的解釋器都會(huì)將這個(gè)局部變量放入作用域。

實(shí)例:

if false
  a = 1 # 代碼不執(zhí)行,但是Ruby的解釋器將局部變量a放入了當(dāng)前作用域
end
p a # nil 代碼未執(zhí)行,因此未初始化

# ---- 輸出結(jié)果 ----
nil

我們可以通過(guò)local_variables這個(gè)方法來(lái)獲取當(dāng)前作用域中所有的局部變量。

實(shí)例:

v0 = 0
def a_method
  v1 = 1
  p local_variables
end
a_method
p local_variables

# ---- 輸出結(jié)果 ----
[:v1]
[:v0]

解釋:當(dāng)我們定義 v0 的時(shí)候,v0 在頂級(jí)作用域中,然后我們定義了a_method開(kāi)啟了一個(gè)新的作用域,之后在a_method里面定義了 v1 變量,因?yàn)樽饔糜蜷T(mén)的限制,v0 并不會(huì)在a_method的作用域里面,因此局部變量列表只打印了 v1 變量。在調(diào)用完a_method方法之后,我們輸出了頂級(jí)作用域的局部變量列表,顯示了而當(dāng)前作用域只有 v0 在作用域內(nèi)部,所以只打印了變量 v0。

def一樣,moduleclass也會(huì)打開(kāi)作用域門(mén),創(chuàng)建一個(gè)新的作用域,外部局部變量無(wú)法進(jìn)行訪問(wèn)。

4. 跨越作用域門(mén)

當(dāng)使用moduleclass、def來(lái)定義模塊、類、方法的時(shí)候會(huì)產(chǎn)生作用域門(mén),大大限制了局部變量的使用范圍,那么我們有沒(méi)有一種方式來(lái)跨越作用域門(mén)呢?

答案是有的,我們需要改變一下定義模塊、類、方法的方式,不使用關(guān)鍵字,而使用方法去定義它們:

  1. 定義類:Class.new;
  2. 定義模塊:Module.new;
  3. 定義方法:define_method。

讓我們改寫(xiě)一下上面的例子:

實(shí)例:

v0 = 0
define_method :a_method do
  v1 = 1
  p local_variables
end
a_method
p local_variables

# ---- 輸出結(jié)果 ----
[:v1, :v0]
[:v0]

解釋:從輸出結(jié)果我們可以看到,v0 成功跨越了作用域門(mén)進(jìn)入到了a_method里面。同時(shí),變量 v1仍然只在方法的作用域里面。

5. 閉包

什么是閉包(Closure),簡(jiǎn)言之在塊的作用域外面定義的變量可以在塊整個(gè)生命周期進(jìn)行訪問(wèn),Ruby 有三種形式的閉包:Block、Proc、Lambda。Block可以將代碼塊傳給方法,Proc 和 Lambda 可以把代碼塊存儲(chǔ)在變量之中。(關(guān)于 Block 請(qǐng)看 Ruby 的塊 章節(jié),Proc 和 Lambda 請(qǐng)看 Proc 和Lambda 章節(jié))。

因此閉包并不是作用域門(mén)。

從跨越作用域門(mén)的例子中可以看到,定義類、模塊、方法的三種形式均使用到了塊,它允許引用作用域外的變量并開(kāi)啟了新的作用域。

實(shí)例:

num = 1
(1..3).each do |i|
  num += i
end
p num
# ---- 輸出結(jié)果 ----

7

解釋:由上面的例子我們可以看到在塊中我們拿到了變量num,并且執(zhí)行了操作。

那要是不希望塊訪問(wèn)到外部變量要怎么辦呢,我們有下面這種形式。

實(shí)例:

hello = 'Hello'
hi = 'Hi'
1.times do |i; hi, hello|
  p i
  hello = 'Hello 2'
  hi = 'Hi 2'
end
p hello
p hi
# ---- 輸出結(jié)果 ----

Hello
Hi

解釋:從輸出結(jié)果我們可以看到,我們并沒(méi)有修改了外部變量。我們?cè)趬K參數(shù)的末尾放置了一個(gè)分號(hào)(;),然后追加我們不希望訪問(wèn)到的外部變量名稱,就可以做到不去訪問(wèn)外部變量。

如果我們?nèi)サ?code>; hi, hello的話,我們會(huì)得到Hello Hi的結(jié)果。

6. 小結(jié)

本章中我們學(xué)習(xí)了作用域,使用classmodule、def會(huì)開(kāi)啟作用域門(mén) ,使用Class.new、Module.new、define_method可以跨越作用域門(mén)。了解了閉包的概念,在閉包外定義的變量可以進(jìn)入閉包內(nèi)部使用,以及可以使用分號(hào)讓外部的變量不可以進(jìn)入閉包。