ฉบับที่ 2

Python: range is not an iterator!

บทความ
Trey Hunner
แหล่งที่มา

หลายคนเข้าใจว่า range ในภาษา Python เป็น Iterator แต่ความเป็นจริงนั้นไม่ใช่

ภาษา Python มีศัพท์สองคำที่เกี่ยวข้องกับการวนลูปได้ คำแรกคือ Iterables อะไรที่ไล่วนลูปได้ก็คือสิ่งนี้ เช่น List และ Tuple เป็นต้น โดยอาศัยความสามารถของ Iterators หรือตัวที่ใช้สำหรับการวนลูปเป็นผู้เล่นสำคัญในการคืนค่าแต่ละครั้งของการวนลูป

Python
1# [1, 2] เป็น List มีความสามารถเป็น Iterable
2# ใช้ iter เพื่อดึง Iterator ออกจาก Iterable ได้
3my_iterator = iter([1, 2])
4วนลูปได้ข้อมูลแต่ละค่าผ่านการเรียก next ไปที่ Iterator
5next(my_iterator) # 1
6next(my_iterator) # 2
7next(my_iterator)
8# เมื่อค่าข้อมูลสิ้นสุด Python จะทำการโยน StopIteration ออกมา
9# Traceback (most recent call last):
10# File "<stdin>", line 1, in <module>
11# StopIteration

Iterators มีคุณสมบัติในการเป็น Iterables อีกด้วย นั่นคือเราสามารถใช้ iter เพื่อดึง Iterator ออกมาจาก Iterator นั้นๆ ซึ่งค่าที่ได้ก็คือตัวมันเอง

คุณสมบัติอีกประการที่สำคัญคือ เมื่อทำการวนลูปครบทุกค่าแล้วเราจะไม่ได้ค่าอะไรอีกจาก Iterator นั้น

Python
1my_iterator = iter([1, 2])
2# วนลูปเพื่อสร้าง list
3[x**2 for x in my_iterator] # [1, 4]
4# ใช้ทุกค่าหมดไปแล้ว รอบถัดไปไม่คืนค่าใดๆ อีก
5[x**2 for x in my_iterator] # []

แต่ range ก็วนลูปได้นะ แสดงว่าเราเรียก iter เพื่อดึง Iterator ออกมาได้ แล้ว range จะเป็น Iterator ด้วยรึเปล่า? เพื่อเป็นการพิสูจน์เราจะลองทดสอบ range ในฐานะของ Iterator กัน

Python
1# ถ้า range เป็น Iterator ต้องสามารถใช้คู่กับ next ได้
2next(range(3)) # TypeError: 'range' object is not an iterator
3
4numbers = range(3)
5tuple(numbers) # (0, 1, 2)
6# ถ้า range เป็น Iterator เมื่อวนครบทุกค่าแล้ว ไม่ควรได้ค่าใดออกมาอีก
7tuple(numbers) # (0, 1, 2)

จบนะ! สรุปแล้ว range ไม่ใช่ Iterator แต่เป็นสิ่งที่ผู้เขียนเรียกว่า lazy sequences lazy sequences** นั่นเอง

Swapping Images with the Sizes Attribute

บทความ
Scott Jehl
แหล่งที่มา

โลกใบนี้อุปกรณ์ที่เข้าถึงเว็บไซต์ได้ต่างมีขนาดแตกต่างกัน ครั้นจะให้อุปกรณ์เล็กจิ๋วโหลดภาพขนาดเบอเร่อแบบเดียวกับ Desktop คงเป็นเรื่องไม่ดีแน่ เหตุนี้เราจึงทำ Responsive Image ด้วยการบอกเบราเซอร์ว่า เห้ยแกถ้าขนาดจอเล็กก็โหลดภาพเล็กๆ ใหญ่ๆไว้ให้จอใหญ่ๆดูดเอา ด้วยความสามารถของ srcset ฝันของเราจึงเป็นจริง

HTML
1<img src="imgs/bike.jpg"
2 srcset="imgs/bike.jpg 480w, imgs/bike-1200.jpg 1200w, imgs/bike-2000.jpg 2000w"
3 sizes="100vw"
4 alt="A red bicycle">

ตัวอย่างข้างต้นเป็นการบอกว่า เราจะโหลดภาพ imgs/bike.jpg มาแสดงเมื่อขนาดหน้าจอตั้งแต่ 480px แต่น้อยกว่า 1200px แสดงภาพ imgs/bike-1200.jpg เมื่อขนาดหน้าจอเป็น 1200px ขึ้นไป เมื่อไหร่ที่ขนาดจอเป็น 2000px หรือมากกว่า จึงหยิบภาพที่ใหญ่สุดคือ imgs/bike-2000.jpg มาแสดงผล ทั้งนี้ sizes จะเป็นตัวช่วยกำกับว่าเราแสดงผลภาพที่ความกว้างเท่าไร ในที่นี้คือ 100% ของความกว้าง Viewport

ทว่า sizes เป็นค่าที่โกหกกันได้ เราสามารถเซ็ตค่า sizes เกินหรือต่ำกว่าค่าจริงที่แสดงได้ เช่น เราต้องการทำแว่นขยาย ในตอนต้นภาพของเราแสดงขนาดปกติ แต่เมื่อเราทำการกดแว่นขยายจึงใช้ JavaScript เปลี่ยนค่าของ sizes เป็น 200vw เป็นผลให้เบราเซอร์โหลดภาพที่ใหญ่กว่ามาแสดงผล เพียงเท่านี้เราก็สามารถทำคุณสมบัติการขยายภาพอย่างง่ายได้แล้วละ

Loading Third-Party JavaScript

บทความ
Addy Osmani and Arthur Evans
แหล่งที่มา

Optimize เว็บเพจมาแสนดี แต่ทำไมยังช้า?

Third-party scripts เช่น Twitter, Facebook หรือ Video player embeds แบบ Youtube ต่างทำให้เว็บเราช้าได้ทั้งสิ้น ผู้เขียนจึงแนะนำว่าเมื่อเราทำงานกับ scripts เหล่านี้ ลองทำการวัดประสิทธิภาพซักหน่อยดีไหม ผ่านเทคนิคต่างๆเยอะแยะมากกกก เช่น

ใช้ Chrome DevTools แจกแจง Third-party scripts

ภายใต้ Network panel ของ Chrome DevTools เราเห็นอยู่แล้วว่าเว็บไซต์ของเรามีการโหลดทรัพยากรจากที่ใดบ้าง แต่คงจะดีกว่าใช่ไหมหละถ้ามันจะแสดงด้วยว่าสิ่งไหนเป็นของ Third-party เจ้าใด เพียงเรากด CMD + Shift + P แล้วพิมพ์ Show third party badges ลงไปคุณสมบัตินี้ก็พร้อมใช้งาน

Show third party badges

ลองบลอคทรัพยากรที่น่าสงสัยด้วย Chrome DevTools

เมื่อเราเปิดการแสดง badges แล้ว เราจะเห็นว่าสคริปต์ไหนมาจากเจ้าใด หากเราสงสัยว่าสิ่งนี้หละที่ทำให้เว็บเราช้า เราก็ลองบลอคมันออกไปได้ด้วยการเลือก Block Request URL ใน Network Panel หากการบลอคนี้ทำให้เว็บของเราเร็วขึ้นปี๊ด ไอ้เจ้านี่หละคือจุดอ่อน!

Block Request URL

Performance Panel

ทว่าแล้วเราจะรู้ได้อย่างไรว่าสคริปต์จากเจ้าไหนที่สมควรจับไปประหาร

ภายใต้ Performance Panel ของ Chrome DevTools สามารถวัดประสิทธิภาพจำแนกตาม vendor ได้

Performance Panel

นอกจากสามเทคนิคนี้ ยังมีเทคนิคอื่นอีกมากมายที่ผู้เขียนเสนอไว้ ตามไปเสพซิ!

เมื่อเราจับต้นตอความช้าได้แล้ว เราควรทำอย่างไรกับสคริปต์เหล่านั้น? ผู้เขียนได้ชี้ทางสว่างให้ดังนี้

  • เนื่องจากสคริปต์เหล่านี้จะบลอคการทำงานของเบราเซอร์ ถ้ามันไม่สำคัญว่าต้องใช้งานแต่แรกหรือใช้เพื่อแสดงผลครึ่งบนของเพจ เราสามารถเลื่อนการประมวลผลภายหลังได้ด้วยการใช้ async และ defer
  • ถ้าเซิฟเวอร์ของ third-party เหล่านั้นช้ามาก ลองโฮสต์ด้วยตนเองไหม?
  • หากสคริปต์นั้นให้ความรู้สึกว่า "ใส่เพื่ออออ?" ก็ลบมันทิ้งเถอะนะคนดี
  • ลองใช้ resource hints ดู เช่นใช้ <link rel="dns-prefetch"> เพื่อให้ทำ DNS Resolution แบบ Asynchronous แต่แรก จะได้ไม่ไปบลอคการทำงานในการควานหา IP ภายหลัง

SAY HELLO TO HOUDINI AND THE CSS PAINT API

บทความ
Vincent De Oliveira
แหล่งที่มา

CSS เป็นอะไรที่มีลูกเล่นเพิ่มขึ้นตลอด แต่มันช่างบัดซบที่ของใหม่ออกมาทีไรเบราเซอร์ก็ไม่ซัพพอร์ตซักที รอไปเหอะสองปีถึงจะใช้ได้กับเบราเซอร์ทุกตัว ไอ้เห็ดเผลาะเอ๊ย!

ด้วยความกระสันอยากใช้ของใหม่เราจึงพึ่งพิง CSS polyfills แต่ก็นะอะไรที่เป็นของเทียม มันก็ไม่ใช่ของจริงไง Performance มันไม่ปลื้มมม

ทุกวิกฤติย่อมมีโอกาส Houdini จึงถือกำเนิดบนความร่วมมือของ Apple Google Microsoft Mozilla และ Opera เพื่อให้นักพัฒนาสามารถเข้าถึง CSS Engine ของเบราเซอร์เองเลย เพื่อรังสรรคฟีเจอร์ต่างๆของ CSS ได้อย่างมีประสิทธิภาพ แต่ให้ตายเถอะตอนนี้มีเพียง Google Chrome ที่เข้าขั้นใช้งานฟีเจอร์นี้ได้บ้าง

ตามความเข้าใจข้างต้น Houdini ทำให้เราสามารถสร้างฟีเจอร์ของ CSS ใหม่ขึ้นมาได้ จะสร้างเป็น polyfills ที่มีประสิทธิภาพมากกว่า CSS polyfills แบบเดิมๆที่ใช้ JavaScript แก้ปัญหา หรือจะสร้างฟีเจอร์ใหม่ของเราเองก็ได้ เช่นตัวอย่างนี้จะใช้ CSS Paint API เพื่อสร้างเส้นฟันปลาแบบนี้

Crafting a Jagged Edge

ด้วยการใช้ CSS Properties ที่เราสร้างขึ้นมาใหม่สองตัวคือ --tooth-width และ --tooth-height ดังนี้

CSS
1.slot:nth-child(1) .jagged {
2 --tooth-width: 50px;
3 --tooth-height: 25px;
4}
5
6.slot:nth-child(2) .jagged {
7 --tooth-width: 2rem;
8 --tooth-height: 3rem;
9}
10
11.slot:nth-child(3) .jagged {
12 --tooth-width: calc(33vw - 31px);
13 --tooth-height: 2em;
14}

เริ่มอยากรู้ว่าทำยังไงแล้วใช่ไหมละ เสพต่อในบทความจ้า~