ขอบเขต (Scope) เป็นแนวคิดสำคัญที่นักพัฒนา Ruby ทุกคนต้องเข้าใจ
ทำไม?
เพราะมันเป็นต้นเหตุของข้อความข้อผิดพลาดและความสับสนมากมาย
ขอบเขตคืออะไร?
ขอบเขตหมายถึงตัวแปรใดบ้างที่สามารถเข้าถึงได้ในจุดเวลาใดเวลาหนึ่ง
ประเภทตัวแปรต่างๆ มีขอบเขตที่ต่างกัน
ขอบเขตอาจ แคบมาก (ตัวแปรกลาง) หรือ กว้างมาก (ตัวแวรกลาง)
คุณควรใช้ขอบเขตที่แคบที่สุดเท่าที่จะเป็นไปได้ เพื่อหลีกเลี่ยงปัญหาการเปลี่ยนแปลงสถานะ (state mutation) และการชนกันของชื่อ (name collision)
สิ่งที่ตามมาคือรายการ ตัวอย่าง ของว่าขอบเขตส่งผลต่อโค้ด Ruby ของคุณอย่างไร
ขอบเขตของตัวแปรกลาง (Local Variables)
ตัวแปรกลางมีขอบเขตแคบที่สุด
โดยเฉพาะ ตัวแปรกลางที่ถูกกำหนดภายในเมธอดจะไม่ tồnอยู่อีกต่อไปหลังจากที่เมธอดคืนค่าแล้ว
ตัวอย่าง:
a = 50 def apple a = 100 puts a end
คุณคิดว่า apple จะพิมพ์อะไร?
คำตอบ:
เมื่อคุณเรียก apple มันจะพิมพ์ 100 เสมอ
ข้อเท็จจริงที่ว่า a ถูกกำหนด นอกเมธอด เป็น 50 ไม่มีผลต่อตัวแปร a ภายในเมธอด เลย
มันคือตัวแปรที่ต่างกัน
คุณสามารถ นึกถึงขอบเขตเหมือนฟองสบู่...
a = 50 ตัวแรกอยู่ในฟองหนึ่ง แล้วเมื่อคุณเรียกเมธอด ใดๆ ก็ตาม คุณจะเข้าสู่ฟองใหม่ที่ ว่างเปล่า
คุณไม่ได้นำตัวแปรกลางใดๆ ข้ามไปฟองใหม่
และเมื่อเมธอดจบ...
ฟองระเบิด
ตัวแปรภายในฟองหายไป และคุณไม่สามารถเข้าถึงได้
ก็คือวิธีที่ตัวแปรกลางทำงาน
ขอบเขตของตัวแปรอินสแตนซ์ (Instance Variables)
ตัวแปรอินสแตนซ์มีขอบเขตกว้างกว่า
โดยเฉพาะ ใช้สำหรับแชร์ข้อมูลภายในออบเจ็กต์ Ruby
ตัวอย่าง:
class Fruit
def more_juice
@quantity = 100
end
def less_juice
@quantity = 50
end
end
ในตัวอย่างนี้ @quantity เป็นตัวแปรเดียวกันสำหรับเมธอด more_juice และ less_juice ทั้งคู่
มันคือค่าที่แชร์ระหว่างเมธอด
แต่นอกคลาสนี้ และแม้บนออบเจ็กต์ Fruit ที่ต่างกัน @quantity จะต่างกันไป
ตัวอย่าง:
orange = Fruit.new apple = Fruit.new orange.more_juice apple.less_juice
ทุกออบเจ็กต์มีชุดตัวแปรอินสแตนซ์ของตัวเอง
ดังนั้นในตัวอย่างนี้ orange จะมี @quantity เท่ากับ 100 และ apple จะมี @quantity เท่ากับ 50
เหมือนคนละคนมีชื่อ อายุ ประเทศ ต่างกันหละ
ขอบเขตทำงานในบล็อกอย่างไร
บล็อก (Blocks) น่าสนใจมากเมื่อพูดถึงขอบเขต
ถ้าเราตามการเปรียบเทียบฟองสบู่อีกครั้ง สิ่งที่บล็อกทำคือ นำตัวแปรกลางจากฟองปัจจุบันมาให้
คุณสามารถเข้าถึงและเปลี่ยนแปลงได้
ตัวอย่าง:
a = []
3.times { a << 1 }
p a # [1, 1, 1]
แต...
ฟองยังคงระเบิด ลบตัวแปรกลางใหม่ๆ ที่ถูกสร้างภายในบล็อกออกไป
น่าสนใจใช่ไหม?
ตัวอย่างของสิ่งที่ผมหมายถึง:
1.times { b = [1,2,3] }
b # NameError
ไม่ chỉนั้น บล็อกยังพก "ฟอง" ณ จุดที่มันถูกสร้างไปด้วย
เอฟเฟกต์ที่เรียกว่า "Closure" (คลอเจอร์)
จำไว้ว่า "ฟอง" นี้คือชุดรวมของตัวแปรทั้งหมดที่เข้าถึงได้ในจุดโค้ดเฉพาะ
มันคือขอบเขตเอง ที่บรรจุอยู่ในออบเจ็กต์
ใน Ruby เราเรียกมันว่า Binding
วิธีใช้ Ruby Bindings
แนวคิดอีกอย่างที่ผมอยากแบ่งปันในบทความนี้คือเกี่ยวกับ bindings
คุณสามารถบันทึกฟองทั้งหมดที่เราพูดถึงไว้ในออบเจ็กต์ Ruby ได้ ออบเจ็กต์ของคลาส Binding
ตัวอย่าง:
def banana a = 100 binding end banana.class # Binding
ออบเจ็กต์ binding นี้คือฟองนั้นแหละ
คุณแม้แต่สามารถ覗มองเข้าไปในฟองและดูว่ามีอะไรอยู่ข้างใน
ตัวอย่าง:
banana.send(:local_variables) # [:a]
ขอบเขตใน Rails คืออะไร?
ขอบเขต (Scope) ใน Rails เป็นสิ่งต่างจากขอบเขตใน Ruby
แล้ว Scope ใน Rails คืออะไร?
มันคือวิธีตั้งชื่อสำหรับคำสั่งฐานข้อมูลแบบกำหนดเอง ประกอบด้วยเมธอด ActiveRecord
ตัวอย่าง:
class User < ApplicationRecord
scope(:with_email) { where.not(email: nil) }
end
User.with_email
อย่าสับสนกับแนวคิด scope ใน Ruby นะครับ 🙂
สรุป
คุณได้เรียนรู้เกี่ยวกับ scopes และ binding objects ใน Ruby!
จำไว้ว่า ขอบเขตกำหนดว่าคุณสามารถเข้าถึงตัวแปรใดได้ ในเวลาใดเวลาหนึ่ง
มันเหมือนฟองสบู่ ตัวแปรกลางมีฟองของตัวเอง ในขณะที่ออบเจ็กต์แชร์ฟองอีกอันสำหรับตัวแปรอินสแตนซ์
ขอบคุณที่อ่านครับ