ในฐานะนักพัฒนา JS นี่คือสิ่งที่ทำให้ฉันนอนไม่หลับ
เผยแพร่แล้ว: 2022-03-11JavaScript เป็นคี่บอลของภาษา แม้ว่าจะได้รับแรงบันดาลใจจาก Smalltalk แต่ก็ใช้รูปแบบ C-like มันรวมแง่มุมของกระบวนทัศน์การเขียนโปรแกรมเชิงขั้นตอน การทำงาน และเชิงวัตถุ (OOP) มีวิธีการมากมายที่มักจะซ้ำซากจำเจในการแก้ปัญหาการเขียนโปรแกรมที่เป็นไปได้เกือบทุกอย่าง และไม่มีความคิดเห็นอย่างแรงกล้าเกี่ยวกับสิ่งที่ต้องการ เป็นการพิมพ์ที่อ่อนแอและมีพลัง ด้วยวิธีการแบบเขาวงกตในการพิมพ์การบังคับขู่เข็ญที่ทำให้นักพัฒนาที่มีประสบการณ์ต้องสะดุดล้ม
JavaScript ยังมีหูด กับดัก และคุณสมบัติที่น่าสงสัยอีกด้วย โปรแกรมเมอร์หน้าใหม่ต้องดิ้นรนกับแนวคิดที่ยากขึ้นบางอย่าง เช่น ให้นึกถึงความไม่ตรงกัน การปิด และการยก โปรแกรมเมอร์ที่มีประสบการณ์ในภาษาอื่น ๆ จะถือว่าสิ่งที่มีชื่อและลักษณะคล้ายกันจะทำงานในลักษณะเดียวกันใน JavaScript และมักผิด อาร์เรย์ไม่ใช่อาร์เรย์จริงๆ อะไรคือข้อตกลงกับสิ่ง this
อะไรเป็นต้นแบบ และสิ่งที่ new
ทำอะไรจริงๆ
ปัญหากับคลาส ES6
ผู้กระทำผิดที่เลวร้ายที่สุดยังใหม่กับเวอร์ชันล่าสุดของ JavaScript ECMAScript 6 (ES6): class การพูดคุยรอบชั้นเรียนบางส่วนเป็นเรื่องที่น่าตกใจและเผยให้เห็นถึงความเข้าใจผิดอย่างลึกซึ้งว่าภาษาทำงานอย่างไร:
“ในที่สุด JavaScript ก็เป็นภาษาเชิงวัตถุ จริง ๆ ที่มีคลาส!”
หรือ:
“ชั้นเรียนทำให้เราไม่ต้องคิดเกี่ยวกับโมเดลการสืบทอดที่เสียหายของ JavaScript”
หรือแม้กระทั่ง:
“คลาสเป็นวิธีที่ปลอดภัยและง่ายกว่าในการสร้างประเภทใน JavaScript”
ข้อความเหล่านี้ไม่รบกวนฉัน เพราะพวกเขาบอกเป็นนัยว่ามีบางอย่างผิดปกติกับการสืบทอดต้นแบบ มากันข้อโต้แย้งเหล่านั้น ข้อความเหล่านี้ทำให้ฉันรำคาญเพราะไม่มีสิ่งใดที่เป็นความจริง และแสดงให้เห็นถึงผลที่ตามมาของแนวทาง "ทุกอย่างสำหรับทุกคน" ของ JavaScript ต่อการออกแบบภาษา: ทำให้โปรแกรมเมอร์ไม่เข้าใจภาษาบ่อยเกินกว่าที่อนุญาต ก่อนที่ฉันจะไปไกลกว่านี้ขอแสดง
JavaScript Pop Quiz #1: อะไรคือความแตกต่างที่สำคัญระหว่าง Code Blocks เหล่านี้?
function PrototypicalGreeting(greeting = "Hello", name = "World") { this.greeting = greeting this.name = name } PrototypicalGreeting.prototype.greet = function() { return `${this.greeting}, ${this.name}!` } const greetProto = new PrototypicalGreeting("Hey", "folks") console.log(greetProto.greet())
class ClassicalGreeting { constructor(greeting = "Hello", name = "World") { this.greeting = greeting this.name = name } greet() { return `${this.greeting}, ${this.name}!` } } const classyGreeting = new ClassicalGreeting("Hey", "folks") console.log(classyGreeting.greet())
คำตอบที่นี่คือ ไม่มี สิ่งเหล่านี้ทำสิ่งเดียวกันได้อย่างมีประสิทธิภาพ เป็นเพียงคำถามที่ว่ามีการใช้ไวยากรณ์คลาส ES6 หรือไม่
จริง ตัวอย่างที่สองมีความหมายมากกว่า ด้วยเหตุผลนั้นเพียงอย่างเดียว คุณอาจโต้แย้งว่า class
เป็นส่วนเสริมที่ดีของภาษา น่าเสียดายที่ปัญหานั้นซับซ้อนกว่าเล็กน้อย
JavaScript Pop Quiz #2: รหัสต่อไปนี้ทำอะไร?
function Proto() { this.name = 'Proto' return this; } Proto.prototype.getName = function() { return this.name } class MyClass extends Proto { constructor() { super() this.name = 'MyClass' } } const instance = new MyClass() console.log(instance.getName()) Proto.prototype.getName = function() { return 'Overridden in Proto' } console.log(instance.getName()) MyClass.prototype.getName = function() { return 'Overridden in MyClass' } console.log(instance.getName()) instance.getName = function() { return 'Overridden in instance' } console.log(instance.getName())
คำตอบที่ถูกต้องคือพิมพ์ไปที่คอนโซล:
> MyClass > Overridden in Proto > Overridden in MyClass > Overridden in instance
ถ้าคุณตอบผิด แสดงว่าคุณไม่เข้าใจจริงๆ ว่า class
คืออะไร นี่ไม่ใช่ความผิดของคุณ เช่นเดียวกับ Array
ที่ class
ไม่ใช่คุณลักษณะของภาษา แต่เป็น syntax obscurantism มันพยายามที่จะซ่อนโมเดลการสืบทอดต้นแบบและสำนวนที่เงอะงะที่มาพร้อมกับมัน และมันบอกเป็นนัยว่า JavaScript กำลังทำสิ่งที่มันไม่ใช่
คุณอาจได้รับแจ้งว่า class
ได้รับการแนะนำให้รู้จักกับ JavaScript เพื่อทำให้นักพัฒนา OOP แบบคลาสสิกมาจากภาษาต่างๆ เช่น Java สะดวกสบายยิ่งขึ้นด้วยโมเดลการสืบทอดคลาส ES6 หากคุณ เป็น หนึ่งในนักพัฒนาเหล่านั้น ตัวอย่างนั้นอาจทำให้คุณตกใจ มันควรจะ. มันแสดงให้เห็นว่าคีย์เวิร์ด class
ของ JavaScript ไม่ได้มาพร้อมกับการรับประกันใดๆ ว่าคลาสนั้นมีไว้เพื่อให้ นอกจากนี้ยังแสดงให้เห็นความแตกต่างที่สำคัญอย่างหนึ่งในแบบจำลองการสืบทอดต้นแบบ: ต้นแบบคือ อินสแตนซ์ของวัตถุ ไม่ใช่ ประเภท
ต้นแบบกับคลาส
ความแตกต่างที่สำคัญที่สุดระหว่างการสืบทอดตามคลาสและต้นแบบคือคลาสกำหนด ประเภท ที่สามารถสร้างอินสแตนซ์ได้ในขณะใช้งานจริง ในขณะที่ต้นแบบนั้นเป็นอินสแตนซ์ของอ็อบเจ็กต์
ลูกของคลาส ES6 เป็นคำจำกัดความ ประเภท อื่นที่ขยายพาเรนต์ด้วยคุณสมบัติและเมธอดใหม่ ซึ่งสามารถสร้างอินสแตนซ์ได้ในขณะรันไทม์ ลูกของต้นแบบเป็น อินสแตนซ์ วัตถุอื่นที่มอบหมายคุณสมบัติใด ๆ ที่ไม่ได้นำไปใช้กับผู้ปกครองให้กับผู้ปกครอง
หมายเหตุด้านข้าง: คุณอาจสงสัยว่าทำไมฉันถึงพูดถึงวิธีการเรียน แต่ไม่ใช่วิธีต้นแบบ นั่นเป็นเพราะว่า JavaScript ไม่มีแนวคิดเกี่ยวกับเมธอด ฟังก์ชันเป็นฟังก์ชันระดับเฟิร์สคลาสใน JavaScript และสามารถมีคุณสมบัติหรือคุณสมบัติของอ็อบเจกต์อื่นๆ ได้
ตัวสร้างคลาสสร้างอินสแตนซ์ของคลาส ตัวสร้างใน JavaScript เป็นเพียงฟังก์ชันเก่าธรรมดาที่ส่งคืนอ็อบเจ็กต์ สิ่งเดียวที่พิเศษเกี่ยวกับคอนสตรัคเตอร์ JavaScript คือ เมื่อเรียกใช้ด้วยคีย์เวิร์ด new
จะกำหนดต้นแบบเป็นต้นแบบของอ็อบเจ็กต์ที่ส่งคืน หากนั่นฟังดูสับสนเล็กน้อยสำหรับคุณ แสดงว่าคุณไม่ได้อยู่คนเดียว—นั่นคือสาเหตุหลักว่าทำไมต้นแบบจึงไม่ค่อยเข้าใจ
เพื่อให้มีจุดที่ดีจริงๆ ลูกของต้นแบบไม่ใช่ สำเนา ของต้นแบบ และไม่ใช่วัตถุที่มี รูปร่าง เหมือนกันกับต้นแบบ เด็กมีการอ้างอิง ถึง ต้นแบบที่มีชีวิต และทรัพย์สินต้นแบบใดๆ ที่ไม่มีอยู่ในเด็กนั้นเป็นการอ้างอิงทางเดียวไปยังทรัพย์สินที่มีชื่อเดียวกันบนต้นแบบ
พิจารณาสิ่งต่อไปนี้:
let parent = { foo: 'foo' } let child = { } Object.setPrototypeOf(child, parent) console.log(child.foo) // 'foo' child.foo = 'bar' console.log(child.foo) // 'bar' console.log(parent.foo) // 'foo' delete child.foo console.log(child.foo) // 'foo' parent.foo = 'baz' console.log(child.foo) // 'baz'
ในตัวอย่างก่อนหน้านี้ ในขณะที่ child.foo
undefined
จะอ้างอิง parent.foo
ทันทีที่เรากำหนด foo
ใน child
นั้น child.foo
มีค่า 'bar'
แต่ parent.foo
คงค่าเดิมไว้ เมื่อเรา delete child.foo
มันจะอ้างอิงถึง parent.foo
อีกครั้ง ซึ่งหมายความว่าเมื่อเราเปลี่ยนค่าของ child.foo
จะอ้างอิงถึงค่าใหม่
มาดูกันว่าเกิดอะไรขึ้น (เพื่อให้เห็นภาพชัดเจนขึ้น เราจะแสร้งทำเป็นว่าสิ่งเหล่านี้คือ Strings
ไม่ใช่ตัวอักษรสตริง ความแตกต่างไม่สำคัญที่นี่):
วิธีทำงานภายใต้ประทุน และโดยเฉพาะอย่างยิ่งลักษณะเฉพาะของ new
และ this
เป็นหัวข้อสำหรับวันอื่น แต่ Mozilla มีบทความโดยละเอียดเกี่ยวกับห่วงโซ่การสืบทอดต้นแบบของ JavaScript หากคุณต้องการอ่านเพิ่มเติม
ประเด็นสำคัญคือต้นแบบไม่ได้กำหนด type
พวกมันคือ instances
และพวกมันสามารถเปลี่ยนแปลงได้ในขณะใช้งานจริง โดยทั้งหมดนั้นมีความหมายและเกี่ยวข้อง
ยังอยู่กับฉัน? กลับไปที่การแยกคลาส JavaScript
JavaScript Pop Quiz #3: คุณใช้ความเป็นส่วนตัวในชั้นเรียนอย่างไร?
คุณสมบัติต้นแบบและคลาสของเราด้านบนไม่ได้ "ห่อหุ้ม" มากเท่ากับ "ห้อยออกไปนอกหน้าต่างอย่างล่อแหลม" เราควรแก้ไข แต่อย่างไร?
ไม่มีตัวอย่างโค้ดที่นี่ คำตอบคือคุณไม่สามารถ
JavaScript ไม่มีแนวคิดเรื่องความเป็นส่วนตัว แต่มีการปิด:
function SecretiveProto() { const secret = "The Class is a lie!" this.spillTheBeans = function() { console.log(secret) } } const blabbermouth = new SecretiveProto() try { console.log(blabbermouth.secret) } catch(e) { // TypeError: SecretiveClass.secret is not defined } blabbermouth.spillTheBeans() // "The Class is a lie!"
คุณเข้าใจไหมว่าเกิดอะไรขึ้น? ถ้าไม่ แสดงว่าคุณไม่เข้าใจการปิด ไม่เป็นไร จริงๆ แล้ว พวกมันไม่ได้น่ากลัวอย่างที่คิดไว้ พวกมันมีประโยชน์มาก และคุณควรใช้เวลาในการเรียนรู้เกี่ยวกับสิ่งเหล่านี้
JavaScript Pop Quiz #4: อะไรคือสิ่งที่เทียบเท่ากับด้านบนโดยใช้คีย์เวิร์ดของ class
?
ขออภัย นี่เป็นอีกคำถามหลอกลวง โดยพื้นฐานแล้วคุณสามารถทำสิ่งเดียวกันได้ แต่ดูเหมือนว่า:
class SecretiveClass { constructor() { const secret = "I am a lie!" this.spillTheBeans = function() { console.log(secret) } } looseLips() { console.log(secret) } } const liar = new SecretiveClass() try { console.log(liar.secret) } catch(e) { console.log(e) // TypeError: SecretiveClass.secret is not defined } liar.spillTheBeans() // "I am a lie!"
แจ้งให้เราทราบว่ามันดูง่ายหรือชัดเจนกว่าใน SecretiveProto
หรือไม่ ในมุมมองส่วนตัวของฉัน มันค่อนข้างแย่—มันทำให้การใช้การประกาศ class
ใน JavaScript แตกสลาย และใช้งานไม่ได้มากอย่างที่คุณคาดหวังจาก Java สิ่งนี้จะชัดเจนโดยต่อไปนี้:

JavaScript Pop Quiz #5: SecretiveClass::looseLips()
ทำอะไรได้บ้าง
ลองหา:
try { liar.looseLips() } catch(e) { // ReferenceError: secret is not defined }
อืม… ที่น่าอึดอัดใจ
JavaScript Pop Quiz #6: ผู้พัฒนา JavaScript ที่มีประสบการณ์คนไหนชอบ—ต้นแบบหรือคลาส?
คุณเดาได้ว่าเป็นอีกคำถามหนึ่งที่หลอกลวง—นักพัฒนา JavaScript ที่มีประสบการณ์มักจะหลีกเลี่ยงทั้งสองอย่างเมื่อทำได้ นี่เป็นวิธีที่ดีในการดำเนินการข้างต้นด้วย JavaScript สำนวน:
function secretFactory() { const secret = "Favor composition over inheritance, `new` is considered harmful, and the end is near!" const spillTheBeans = () => console.log(secret) return { spillTheBeans } } const leaker = secretFactory() leaker.spillTheBeans()
นี่ไม่ใช่แค่การหลีกเลี่ยงความอัปลักษณ์ของมรดก หรือการบังคับใช้การห่อหุ้ม ลองนึกถึงสิ่งที่คุณอาจทำกับ secretFactory
และ leaker
ที่คุณทำไม่ได้ง่ายๆ ด้วยต้นแบบหรือคลาส
ประการหนึ่ง คุณสามารถทำลายโครงสร้างได้เพราะคุณไม่ต้องกังวลกับบริบทของ this
:
const { spillTheBeans } = secretFactory() spillTheBeans() // Favor composition over inheritance, (...)
นั่นเป็นสิ่งที่ดีสวย นอกจากการหลีกเลี่ยงสิ่ง new
และโง่เขลา this
แล้ว ยังช่วยให้เราใช้อ็อบเจ็กต์ของเราแทนกันได้กับโมดูล CommonJS และ ES6 ยังทำให้การจัดองค์ประกอบง่ายขึ้นอีกเล็กน้อย:
function spyFactory(infiltrationTarget) { return { exfiltrate: infiltrationTarget.spillTheBeans } } const blackHat = spyFactory(leaker) blackHat.exfiltrate() // Favor composition over inheritance, (...) console.log(blackHat.infiltrationTarget) // undefined (looks like we got away with it)
ลูกค้าของ blackHat
ไม่ต้องกังวลว่าการ exfiltrate
มาจากไหน และ spyFactory
ไม่ต้องวุ่นวายกับ Function::bind
การเล่นกลบริบทหรือคุณสมบัติที่ซ้อนกันอย่างล้ำลึก โปรดทราบว่าเราไม่ต้องกังวลมากเกี่ยวกับ this
ในโค้ดขั้นตอนซิงโครนัสแบบง่าย แต่มันทำให้เกิดปัญหาทุกประเภทในโค้ดแบบอะซิงโครนัสที่หลีกเลี่ยงได้ดีกว่า
ด้วยความคิดเพียงเล็กน้อย spyFactory
สามารถพัฒนาให้เป็นเครื่องมือจารกรรมที่มีความซับซ้อนสูง ซึ่งสามารถจัดการกับเป้าหมายการแทรกซึมได้ทุกประเภท หรือกล่าวอีกนัยหนึ่งก็คือ หน้าอาคาร
แน่นอน คุณสามารถทำสิ่งนั้นกับคลาสได้เช่นกัน หรือมากกว่า การแบ่งประเภทของคลาส ซึ่งทั้งหมดนั้นสืบทอดมาจาก abstract class
หรือ interface
... ยกเว้นว่า JavaScript ไม่มีแนวคิดของนามธรรมหรืออินเทอร์เฟซ
กลับไปที่ตัวอย่าง Greeter เพื่อดูว่าเราจะนำไปใช้กับโรงงานได้อย่างไร:
function greeterFactory(greeting = "Hello", name = "World") { return { greet: () => `${greeting}, ${name}!` } } console.log(greeterFactory("Hey", "folks").greet()) // Hey, folks!
คุณอาจสังเกตเห็นว่าโรงงานเหล่านี้เริ่มสั้นลงเรื่อยๆ เมื่อเราเดินหน้ากัน แต่อย่ากังวล เพราะโรงงานเหล่านี้ก็ทำแบบเดียวกัน วงล้อฝึกซ้อมกำลังจะหลุดออกมา!
นั่นเป็นเพียงต้นแบบน้อยกว่าต้นแบบหรือรุ่นคลาสของรหัสเดียวกัน ประการที่สอง มันบรรลุการห่อหุ้มคุณสมบัติของมันได้อย่างมีประสิทธิภาพมากขึ้น นอกจากนี้ยังมีหน่วยความจำและประสิทธิภาพการทำงานที่ต่ำกว่าในบางกรณี (อาจดูเหมือนไม่เป็นเช่นนั้นในแวบแรก แต่คอมไพเลอร์ JIT ทำงานอย่างเงียบ ๆ ในเบื้องหลังเพื่อลดการทำซ้ำและการอนุมานประเภท)
จึงปลอดภัยกว่า มักจะเร็วกว่า และเขียนโค้ดแบบนี้ได้ง่ายขึ้น ทำไมเราต้องเรียนอีกครั้ง? แน่นอน การนำกลับมาใช้ใหม่ได้ จะเกิดอะไรขึ้นถ้าเราต้องการคำทักทายที่ไม่มีความสุขและกระตือรือร้น ถ้าเราใช้ ClassicalGreeting
เราอาจกระโดดเข้าสู่ลำดับชั้นของคลาสโดยตรง เราทราบดีว่าเราจำเป็นต้องกำหนดพารามิเตอร์ของเครื่องหมายวรรคตอน ดังนั้นเราจะจัดโครงสร้างใหม่และเพิ่มลูกๆ บางส่วน:
// Greeting class class ClassicalGreeting { constructor(greeting = "Hello", name = "World", punctuation = "!") { this.greeting = greeting this.name = name this.punctuation = punctuation } greet() { return `${this.greeting}, ${this.name}${this.punctuation}` } } // An unhappy greeting class UnhappyGreeting extends ClassicalGreeting { constructor(greeting, name) { super(greeting, name, " :(") } } const classyUnhappyGreeting = new UnhappyGreeting("Hello", "everyone") console.log(classyUnhappyGreeting.greet()) // Hello, everyone :( // An enthusiastic greeting class EnthusiasticGreeting extends ClassicalGreeting { constructor(greeting, name) { super(greeting, name, "!!") } greet() { return super.greet().toUpperCase() } } const greetingWithEnthusiasm = new EnthusiasticGreeting() console.log(greetingWithEnthusiasm.greet()) // HELLO, WORLD!!
เป็นแนวทางที่ดี จนกว่าจะมีคนเข้ามาถามหาฟีเจอร์ที่ไม่เข้ากับลำดับชั้นอย่างชัดเจน และสิ่งทั้งหมดก็ไม่สมเหตุสมผล ปักหมุดความคิดนั้นไว้ในขณะที่เราพยายามเขียนฟังก์ชันการทำงานเดียวกันกับโรงงาน:
const greeterFactory = (greeting = "Hello", name = "World", punctuation = "!") => ({ greet: () => `${greeting}, ${name}${punctuation}` }) // Makes a greeter unhappy const unhappy = (greeter) => (greeting, name) => greeter(greeting, name, ":(") console.log(unhappy(greeterFactory)("Hello", "everyone").greet()) // Hello, everyone :( // Makes a greeter enthusiastic const enthusiastic = (greeter) => (greeting, name) => ({ greet: () => greeter(greeting, name, "!!").greet().toUpperCase() }) console.log(enthusiastic(greeterFactory)().greet()) // HELLO, WORLD!!
ไม่ชัดเจนว่าโค้ดนี้ดีกว่า แม้ว่าจะสั้นกว่าเล็กน้อยก็ตาม อันที่จริง คุณอาจโต้แย้งได้ว่าการอ่านยากขึ้น และบางทีนี่อาจเป็นแนวทางที่ไม่สุภาพ เราไม่สามารถมี unhappyGreeterFactory
ที่ไม่มีความสุขและ GreeterFactory ที่ enthusiasticGreeterFactory
ได้หรือไม่?
จากนั้นลูกค้าของคุณก็เข้ามาและพูดว่า “ฉันต้องการคนต้อนรับคนใหม่ที่ไม่มีความสุขและต้องการให้ทั้งห้องรู้เรื่องนี้!”
console.log(enthusiastic(unhappy(greeterFactory))().greet()) // HELLO, WORLD :(
หากเราจำเป็นต้องใช้คำทักทายที่ไม่มีความสุขอย่างกระตือรือร้นนี้มากกว่าหนึ่งครั้ง เราก็จะทำให้ตัวเองง่ายขึ้น:
const aggressiveGreeterFactory = enthusiastic(unhappy(greeterFactory)) console.log(aggressiveGreeterFactory("You're late", "Jim").greet())
มีแนวทางสำหรับรูปแบบการจัดองค์ประกอบที่ทำงานร่วมกับต้นแบบหรือคลาสต่างๆ ตัวอย่างเช่น คุณอาจคิดใหม่ UnhappyGreeting
และ EnthusiasticGreeting
เป็นผู้ตกแต่ง มันยังคงต้องใช้ต้นแบบมากกว่าวิธีการแบบใช้งานได้จริงที่ใช้ข้างต้น แต่นั่นเป็นราคาที่คุณจ่ายเพื่อความปลอดภัยและการห่อหุ้มของคลาส จริง
ประเด็นคือใน JavaScript คุณไม่ได้รับความปลอดภัยโดยอัตโนมัติ เฟรมเวิร์ก JavaScript ที่เน้นการใช้งาน class
สร้าง "เวทย์มนตร์" มากมายในการเขียนปัญหาประเภทนี้และบังคับให้คลาสปฏิบัติตน ลองดูซอร์สโค้ด ElementMixin
ของ Polymer สักครั้ง ฉันขอท้าให้คุณดู เป็นระดับตัวช่วยสร้างของ JavaScript arcana และฉันหมายความว่าไม่มีการประชดหรือเสียดสี
แน่นอน เราสามารถแก้ไขปัญหาบางอย่างที่กล่าวถึงข้างต้นด้วย Object.freeze
หรือ Object.defineProperties
เพื่อให้เกิดผลมากหรือน้อย แต่ทำไมต้องเลียนแบบแบบฟอร์มโดยไม่มีฟังก์ชัน ในขณะที่ละเลยเครื่องมือที่ JavaScript ทำให้ เราหาไม่เจอในภาษาอย่าง Java คุณจะใช้ค้อนที่เขียนว่า "ไขควง" เพื่อขันสกรูในเมื่อกล่องเครื่องมือของคุณมีไขควงจริงๆ อยู่ข้างๆ หรือไม่
ค้นหาชิ้นส่วนที่ดี
นักพัฒนา JavaScript มักจะเน้นส่วนที่ดีของภาษา ทั้งทางปากและในการอ้างอิงถึงหนังสือที่มีชื่อเดียวกัน เราพยายามหลีกเลี่ยงกับดักที่กำหนดโดยตัวเลือกการออกแบบภาษาที่น่าสงสัยมากกว่านั้น และยึดติดกับส่วนที่ทำให้เราเขียนโค้ดที่สะอาด อ่านได้ ลดข้อผิดพลาด และใช้ซ้ำได้
มีข้อโต้แย้งที่สมเหตุสมผลเกี่ยวกับส่วนใดของ JavaScript ที่เข้าเกณฑ์ แต่ฉันหวังว่าฉันจะทำให้คุณเชื่อว่า class
ไม่ใช่หนึ่งในนั้น หากไม่สำเร็จ หวังว่าคุณคงเข้าใจว่าการสืบทอดใน JavaScript อาจทำให้สับสน และ class
นั้นไม่ได้แก้ไขหรือช่วยให้คุณไม่ต้องเข้าใจต้นแบบ เครดิตเพิ่มเติมหากคุณได้รับคำแนะนำว่ารูปแบบการออกแบบเชิงวัตถุทำงานได้ดีโดยไม่มีคลาสหรือการสืบทอด ES6
ฉันไม่ได้บอกคุณให้หลีกเลี่ยง class
โดยสิ้นเชิง บางครั้งคุณต้องการการสืบทอด และ class
มีไวยากรณ์ที่สะอาดขึ้นสำหรับการทำเช่นนั้น โดยเฉพาะอย่างยิ่ง class X extends Y
นั้นดีกว่าวิธีการต้นแบบแบบเก่ามาก นอกจากนั้น เฟรมเวิร์กส่วนหน้ายอดนิยมจำนวนมากสนับสนุนการใช้งาน และคุณควรหลีกเลี่ยงการเขียนโค้ดแปลกๆ ที่ไม่ได้มาตรฐานบนหลักการเพียงอย่างเดียว ฉันแค่ไม่ชอบที่จะไป
ในฝันร้ายของฉัน ไลบรารี JavaScript ทั้งรุ่นเขียนโดยใช้ class
โดยคาดหวังว่าจะมีการทำงานคล้ายกับภาษายอดนิยมอื่นๆ มีการค้นพบข้อบกพร่องประเภทใหม่ทั้งหมด (ปุนตั้งใจ) คนเก่าฟื้นคืนชีพซึ่งอาจถูกทิ้งไว้ในสุสานของ JavaScript ที่มีรูปแบบไม่ถูกต้องได้อย่างง่ายดายหากเราไม่ได้ตกหลุมพรางของ class
โดยประมาท นักพัฒนา JavaScript ที่มีประสบการณ์ถูกรบกวนโดยสัตว์ประหลาดเหล่านี้เพราะสิ่งที่ได้รับความนิยมไม่ใช่สิ่งที่ดีเสมอไป
ในที่สุด เราทุกคนก็เลิกหงุดหงิดและเริ่มสร้างสรรค์วงล้อใหม่ใน Rust, Go, Haskell หรือใครจะรู้อะไรอีก จากนั้นจึงรวบรวม Wasm สำหรับเว็บ และเฟรมเวิร์กของเว็บและไลบรารีใหม่ๆ ก็เพิ่มขึ้นอย่างรวดเร็วในหลายภาษา
มันทำให้ฉันตื่นขึ้นในตอนกลางคืน