คำหลัก "ใหม่" ของ JavaScript ถือเป็นอันตรายหรือไม่ [ปิด]


568

อีกคำถามผู้ใช้ชี้ให้เห็นว่าคำหลักที่เป็นอันตรายต่อการใช้งานและนำเสนอวิธีการแก้ปัญหาในการสร้างวัตถุที่ไม่ได้ใช้งานnew newฉันไม่เชื่อว่าเป็นเรื่องจริงส่วนใหญ่เป็นเพราะฉันใช้ Prototype, Scriptaculous และไลบรารี่ JavaScript ที่ยอดเยี่ยมอื่น ๆ และทุกคนใช้newคีย์เวิร์ด

เมื่อวานนี้ฉันดูการพูดคุยของ Douglas Crockford ที่โรงละคร YUI และเขาพูดอย่างเดียวกันว่าเขาไม่ได้ใช้newคำหลักในรหัสของเขาอีกต่อไป ( Crockford บน JavaScript - Act III: Function the Ultimate - 50:23 นาที )

การใช้newคำหลักนั้น "ไม่ดี" หรือไม่ ข้อดีและข้อเสียของการใช้มันคืออะไร?


90
การใช้คำหลักใหม่นั้นไม่เลวเลย แต่ถ้าคุณลืมคุณจะต้องเรียกตัวสร้างวัตถุว่าเป็นฟังก์ชันปกติ หากคอนสตรัคเตอร์ของคุณไม่ตรวจสอบบริบทการดำเนินการมันจะไม่สังเกตเห็นว่า 'นี่' ชี้ไปที่วัตถุอื่น (โดยปกติคือวัตถุทั่วโลก) แทนที่จะเป็นอินสแตนซ์ใหม่ ดังนั้นคอนสตรัคเตอร์ของคุณจะเพิ่มคุณสมบัติและวิธีการไปยังวัตถุทั่วโลก (หน้าต่าง) หากคุณตรวจสอบเสมอว่า 'นี่' เป็นตัวอย่างของวัตถุของคุณในฟังก์ชั่นวัตถุคุณจะไม่พบปัญหานี้เลย
Kristian B

5
ฉันไม่เข้าใจสิ่งนี้ newหนึ่งในมือทาดั๊กใช้ แต่เมื่อคุณดูที่ห้องสมุด YUI คุณต้องใช้newทุกที่ var myDataSource = new Y.DataSource.IO({source:"./myScript.php"}); เช่น
aditya_gaur

2
@aditya_gaur นั่นเป็นเพราะถ้าคุณต้องการการเริ่มต้นของวัตถุคุณต้องแฮ็กอัพinitเมธอดหากคุณใช้Object.createวิธีการและเรียกมันว่าหลังจากนั้น ง่ายกว่าที่จะใช้newซึ่งทำทั้งสองอย่างตั้งค่าเชนลูกโซ่ต้นแบบและเรียกรหัสเริ่มต้นบางอย่าง
Juan Mendes

67
ฉันไม่คิดว่าสิ่งนี้ควรถูกปิด ใช่เป็นไปได้ว่าจะสร้างแรงบันดาลใจให้กับผู้ต่อต้านครอคฟอร์ดบางคน แต่เรากำลังพูดถึงคำแนะนำยอดนิยมเพื่อหลีกเลี่ยงคุณสมบัติทางภาษาที่สำคัญที่นี่ กลไกที่ทำให้ทุกวัตถุ JQuery มีน้ำหนักเกือบจะไม่มีอะไร (ที่เกี่ยวข้องกับหน่วยความจำ) เกี่ยวข้องกับการใช้คำหลัก 'ใหม่' ชอบวิธีการของโรงงานที่จะเรียกใช้ใหม่อย่างต่อเนื่อง แต่ไม่ลดตัวเลือกสถาปัตยกรรมและศักยภาพในการทำงานของคุณอย่างมากโดยใช้ตัวอักษรวัตถุ พวกเขามีสถานที่ของพวกเขาและผู้สร้างมีสถานที่ของพวกเขา มันเป็นคู่มือลงวันที่และหมัด
Erik Reppen

2
TLDR: การใช้newไม่เป็นอันตราย ถนัดnewเป็นอันตรายและดังนั้นจึงไม่ดี แต่ใน ES5 คุณสามารถใช้โหมดเข้มงวดซึ่งปกป้องคุณจากอันตรายนั้นและอื่น ๆ อีกมากมาย
jkdev

คำตอบ:


603

Crockford ได้ทำสิ่งต่างๆมากมายเพื่อเป็นที่นิยมในเทคนิค JavaScript ที่ดี จุดยืนของเขาเกี่ยวกับองค์ประกอบสำคัญของภาษาได้ก่อให้เกิดการอภิปรายที่เป็นประโยชน์มากมาย ที่กล่าวว่ามีคนจำนวนมากเกินไปที่จะประกาศว่า "ไม่ดี" หรือ "เป็นอันตราย" เป็นข่าวประเสริฐแต่ละครั้งปฏิเสธที่จะมองข้ามความคิดเห็นของชายคนหนึ่ง บางครั้งก็น่าหงุดหงิด

การใช้ฟังก์ชันการทำงานของnewคำหลักนั้นมีข้อดีหลายประการในการสร้างแต่ละวัตถุตั้งแต่เริ่มต้น:

  1. มรดกต้นแบบ ในขณะที่มักจะมองด้วยความสงสัยและการเยาะเย้ยโดยผู้ที่คุ้นเคยกับภาษา OO ตามระดับเทคนิคการสืบทอดพื้นเมืองของ JavaScript เป็นวิธีที่ง่ายและมีประสิทธิภาพน่าแปลกใจของการใช้รหัส และคำหลักใหม่คือวิธีการที่ยอมรับ (และใช้ได้เฉพาะข้ามแพลตฟอร์ม) ของการใช้งาน
  2. ประสิทธิภาพ. นี่คือผลข้างเคียงของ # 1: หากฉันต้องการเพิ่ม 10 วิธีในทุกวัตถุที่ฉันสร้างฉันสามารถเขียนฟังก์ชันการสร้างที่กำหนดแต่ละวิธีให้กับแต่ละวัตถุใหม่ด้วยตนเอง ... หรือฉันสามารถกำหนดให้กับ ฟังก์ชั่นการสร้างprototypeและใช้newในการประทับตราวัตถุใหม่ ไม่เพียงแค่นี้จะเร็วขึ้นเท่านั้น (ไม่จำเป็นต้องใช้รหัสสำหรับแต่ละวิธีในต้นแบบ) แต่จะหลีกเลี่ยงการทำให้บอลลูนแต่ละวัตถุมีคุณสมบัติแยกต่างหากสำหรับแต่ละวิธี บนเครื่องที่ช้าลง (หรือโดยเฉพาะอย่างยิ่งล่าม JS ที่ช้ากว่า) เมื่อมีการสร้างวัตถุจำนวนมากสิ่งนี้อาจช่วยประหยัดเวลาและหน่วยความจำได้อย่างมาก

และใช่newมีข้อเสียที่สำคัญอย่างหนึ่งโดยอธิบายด้วยคำตอบอื่น ๆ : ถ้าคุณลืมที่จะใช้มันรหัสของคุณจะแตกโดยไม่มีการเตือนล่วงหน้า โชคดีที่ข้อเสียนั้นลดลงอย่างง่ายดายเพียงเพิ่มโค้ดลงในฟังก์ชัน:

function foo()
{
   // if user accidentally omits the new keyword, this will 
   // silently correct the problem...
   if ( !(this instanceof foo) )
      return new foo();

   // constructor logic follows...
}

ตอนนี้คุณสามารถมีข้อได้เปรียบnewโดยไม่ต้องกังวลเกี่ยวกับปัญหาที่เกิดจากการใช้ผิดวัตถุประสงค์โดยไม่ตั้งใจ คุณสามารถเพิ่มการยืนยันในการตรวจสอบหากความคิดของรหัสที่ใช้งานไม่ได้อยู่ในใจของคุณอย่างเงียบ ๆ หรือเป็นบางส่วนแสดงความคิดเห็นให้ใช้การตรวจสอบที่จะแนะนำข้อยกเว้นรันไทม์:

if ( !(this instanceof arguments.callee) ) 
   throw new Error("Constructor called as a function");

(โปรดทราบว่าตัวอย่างนี้สามารถหลีกเลี่ยงการเขียนโค้ดชื่อฟังก์ชั่นคอนสตรัคเตอร์ได้ยากเหมือนตัวอย่างก่อนหน้านี้ซึ่งไม่จำเป็นต้องยกตัวอย่างวัตถุจริง - ดังนั้นจึงสามารถคัดลอกลงในแต่ละฟังก์ชันเป้าหมายโดยไม่มีการแก้ไข)

John Resig ได้อธิบายรายละเอียดเกี่ยวกับเทคนิคนี้ในโพสต์อินสแตนซ์ "ระดับ" แบบเรียบง่ายของเขารวมถึงวิธีการสร้างพฤติกรรมนี้ใน "ชั้นเรียน" ของคุณตามค่าเริ่มต้น แน่นอนคุ้มค่าอ่าน ... เป็นหนังสือที่จะเกิดขึ้นของความลับของนินจา JavaScriptซึ่งพบที่ซ่อนทองในนี้และอื่น ๆ อีกมากมาย "อันตราย" ลักษณะของภาษาจาวาสคริปต์ (คนบทบนwithโดยเฉพาะอย่างยิ่ง enlightening สำหรับพวกเราที่ไล่แรก คุณลักษณะที่มีความผิดพลาดอย่างมากนี้เป็นกลไก)


96
ถ้า (! (อินสแตนซ์ของอาร์กิวเมนต์นี้.callee)) โยนข้อผิดพลาด ("ตัวสร้างเรียกว่าเป็นฟังก์ชัน"); // เพิ่มเติมทั่วไปไม่ต้องการความรู้เกี่ยวกับชื่อตัวสร้างทำให้ผู้ใช้แก้ไขรหัส
บ้าง

5
หากคุณกังวลเกี่ยวกับการแสดง - และมีเหตุผลที่จะเป็นจริง - อย่าทำการตรวจสอบเลย อย่าลืมใช้newหรือใช้ฟังก์ชั่น wrapper เพื่อจดจำสำหรับคุณ ผมสงสัยว่าถ้าคุณอยู่ในจุดที่มันเป็นเรื่องสำคัญที่คุณได้เพิ่มประสิทธิภาพอื่น ๆ หมดแล้วเช่น memoization ดังนั้นสายการสร้างของคุณจะได้รับการแปลเป็นภาษาท้องถิ่นอยู่แล้ว ...
Shog9

65
การใช้arguments.calleeเพื่อตรวจสอบว่าคุณถูกโทรด้วยnewอาจไม่ดีหรือarguments.calleeไม่เนื่องจากไม่มีในโหมดเข้มงวด ดีกว่าที่จะใช้ชื่อฟังก์ชั่น
Sean McMillan

69
หากฉันสะกดคำผิดรหัสของฉันจะแตก ฉันไม่ควรใช้ตัวระบุ? ไม่ได้ใช้newเพราะคุณสามารถลืมที่จะเขียนมันในรหัสของคุณเป็น rediculous เช่นเดียวกับตัวอย่างของฉัน
Thomas Eding

16
หากคุณเปิดโหมดเข้มงวดหากคุณลืมใช้ใหม่คุณจะได้รับข้อยกเว้นเมื่อคุณลองใช้สิ่งนี้: yuiblog.com/blog/2010/12/14/strict-mode-is-coming-to-townนั่นคือ ง่ายกว่าการเพิ่มอินสแตนซ์ของการตรวจสอบให้กับตัวสร้างแต่ละตัว
stephenbez

182

ฉันเพิ่งอ่านบางส่วนของหนังสือ Crockfords ของเขา "Javascript: The Good Parts" ฉันรู้สึกว่าเขาพิจารณาทุกสิ่งที่เคยกัดเขาว่าเป็นอันตราย:

เกี่ยวกับการเปลี่ยนผ่าน:

ฉันไม่อนุญาตให้เปลี่ยนเคสไปสู่เคสถัดไป ฉันเคยพบข้อผิดพลาดในรหัสของฉันที่เกิดจากการล้มโดยไม่ตั้งใจทันทีหลังจากที่ได้พูดอย่างจริงจังเกี่ยวกับว่าทำไมบางครั้งการตกผ่านมีประโยชน์ (หน้า 97, ISBN 978-0-596-51774-8)

เกี่ยวกับ ++ และ -

ผู้ประกอบการ ++ (เพิ่มขึ้น) และ - (ลดลง) ได้รับการรู้จักให้มีส่วนร่วมกับรหัสที่ไม่ดี พวกเขาเป็นอันดับสองรองจากสถาปัตยกรรมที่ผิดปกติในการเปิดใช้งานไวรัสและการรักษาความปลอดภัยอื่น ๆ (หน้า 122)

เกี่ยวกับใหม่:

หากคุณลืมที่จะรวม คำนำหน้าใหม่เมื่อเรียกใช้ฟังก์ชันตัวสร้างแล้วสิ่งนี้จะไม่ถูกผูกไว้กับวัตถุใหม่ น่าเศร้าที่สิ่งนี้ จะถูกผูกไว้กับวัตถุระดับโลกดังนั้นแทนที่จะเพิ่มวัตถุใหม่ของคุณคุณจะต้องแก้ไขตัวแปรทั่วโลก นั่นมันแย่จริงๆ ไม่มีคำเตือนการคอมไพล์และไม่มีคำเตือนรันไทม์ (หน้า 49)

มีมากขึ้น แต่ฉันหวังว่าคุณจะได้รับภาพ

คำตอบสำหรับคำถามของคุณ: ไม่มันไม่เป็นอันตราย แต่ถ้าคุณลืมที่จะใช้เมื่อคุณควรจะมีปัญหา หากคุณกำลังพัฒนาในสภาพแวดล้อมที่ดีคุณสังเกตเห็นว่า

ปรับปรุง

เกี่ยวกับปีหลังจากที่คำตอบนี้ถูกเขียนรุ่นที่ 5 ของ ECMAScript ได้รับการปล่อยตัวด้วยการสนับสนุนสำหรับโหมดเข้มงวด ในโหมดที่เข้มงวดthisที่ถูกผูกไว้ไม่ถึงวัตถุโลก undefinedแต่


3
ฉันเห็นด้วยอย่างยิ่ง วิธีแก้ปัญหา: บันทึกเอกสารว่าผู้ใช้ต้องสร้างอินสแตนซ์วัตถุของคุณอย่างไร ใช้ตัวอย่างและผู้ใช้มีแนวโน้มที่จะตัด / วาง มีการสร้าง / คุณสมบัติในทุกภาษาที่สามารถนำไปใช้ในทางที่ผิดทำให้เกิดพฤติกรรมที่ผิดปกติ / ไม่คาดคิด มันไม่ได้ทำให้พวกเขาเป็นอันตราย
nicerobot

45
มีการประชุมเพื่อเริ่มการก่อสร้างด้วยตัวอักษรตัวพิมพ์ใหญ่เสมอและฟังก์ชั่นอื่น ๆ ทั้งหมดที่มีตัวอักษรตัวพิมพ์เล็ก
บาง

61
ฉันเพิ่งตระหนักว่า Crockford ไม่พิจารณาในขณะที่เป็นอันตราย ... ฉันไม่ทราบว่าหลายครั้งที่ผมได้สร้างห่วง infinitive เพราะผมได้ลืมที่จะเพิ่มตัวแปร ...
บาง

5
กันสำหรับ ++, - พวกเขาแสดงสิ่งที่ฉันตั้งใจอย่างชัดเจนในภาษาที่เป็นไปได้ ฉันรัก 'em! การเปลี่ยนผ่านอาจจะชัดเจนสำหรับบางคน แต่ฉันรู้สึกเบื่อหน่าย ฉันใช้พวกเขาเมื่อพวกเขามีความชัดเจนมากขึ้น ('ใช้ปุนไม่สามารถต้านทาน)
PEZ

30
คำตอบของฉันถึง Crockford: การเขียนโปรแกรมยากไปช็อปปิ้ง Sheesh!
Jason Jackson

91

จาวาสคริปต์เป็นภาษาแบบไดนามิกมีวิธี zillion วิธีการเลอะที่ภาษาอื่นจะหยุดคุณ

การหลีกเลี่ยงคุณสมบัติทางภาษาขั้นพื้นฐานเช่นnewบนพื้นฐานที่คุณอาจสับสนคือการถอดรองเท้าใหม่ออกก่อนที่จะเดินผ่านทุ่นระเบิดในกรณีที่คุณอาจทำให้รองเท้าสกปรก

ฉันใช้การประชุมที่ชื่อฟังก์ชั่นเริ่มต้นด้วยอักษรตัวพิมพ์เล็กและ 'ฟังก์ชั่น' ที่เป็นคำจำกัดความของคลาสเริ่มต้นด้วยตัวอักษรตัวพิมพ์ใหญ่ ผลที่ได้คือเงื่อนงำภาพที่น่าสนใจจริงๆว่า 'ไวยากรณ์' ผิด: -

var o = MyClass();  // this is clearly wrong.

ยิ่งไปกว่านั้นนิสัยการตั้งชื่อที่ดียังช่วยได้ หลังจากที่ทุกฟังก์ชั่นทำสิ่งต่าง ๆ และควรมีคำกริยาในชื่อในขณะที่คลาสแสดงถึงวัตถุและเป็นคำนามและคำคุณศัพท์ที่ไม่มีคำกริยา

var o = chair() // Executing chair is daft.
var o = createChair() // makes sense.

มันน่าสนใจว่าสีไวยากรณ์ของ SO ได้ตีความรหัสข้างต้นอย่างไร


8
ใช่ฉันแค่คิดแบบเดียวกันกับการระบายสีไวยากรณ์
BobbyShaftoe

4
เมื่อฉันลืมพิมพ์คำว่า 'ฟังก์ชั่น' คำควรหลีกเลี่ยงค่าใช้จ่ายทั้งหมด อีกครั้งที่ฉันไม่ได้ใช้วงเล็บปีกกาในคำสั่ง if-then แบบหลายบรรทัด ดังนั้นตอนนี้ฉันไม่ใช้วงเล็บปีกกาและเพียงแค่เขียนเงื่อนไขหนึ่งบรรทัด
Hal50000

"นี้เป็นธรรมอย่างชัดเจน" ทำไม? สำหรับชั้นเรียนของฉัน (ซึ่งทำได้ง่ายif (! this instanceof MyClass) return new MyClass()) ฉันชอบnewไวยากรณ์ที่ไม่มีช่องว่าง ทำไม? เพราะฟังก์ชั่นมีทั่วไปมากกว่าฟังก์ชั่นคอนสตรัค การออกไปข้างนอกnewทำให้ง่ายต่อการเปลี่ยนฟังก์ชั่นคอนสตรัคเตอร์เป็นฟังก์ชั่นปกติ ... หรือวิธีอื่น ๆ การเพิ่มเข้าไปnewทำให้รหัสเฉพาะเจาะจงมากขึ้นกว่าที่จะต้องมี และยืดหยุ่นน้อยกว่า เปรียบเทียบกับ Java ที่แนะนำให้ยอมรับListแทนArrayListเพื่อให้ผู้โทรสามารถเลือกการใช้งาน
Stijn de Witt

41

ฉันเป็นมือใหม่กับ Javascript ดังนั้นบางทีฉันอาจไม่ได้มีประสบการณ์มากเกินไปในการให้มุมมองที่ดีกับสิ่งนี้ แต่ฉันต้องการแบ่งปันมุมมองของฉันในสิ่งที่ "ใหม่" นี้

ฉันมาจากโลก C # โดยใช้คำว่า "ใหม่" เป็นธรรมชาติจนเป็นรูปแบบการออกแบบโรงงานที่ดูแปลกสำหรับฉัน

เมื่อฉันแรกรหัสใน Javascript ฉันไม่ทราบว่ามีคำหลัก "ใหม่" และรหัสเช่นเดียวกับในรูปแบบ YUI และมันใช้เวลาไม่นานที่ฉันจะเจอกับหายนะ ฉันไม่ทราบว่าควรจะทำอย่างไรเมื่อดูโค้ดที่ฉันเขียน ความสับสนมากขึ้นคือความคิดของฉันไม่สามารถขนส่งระหว่างขอบเขตวัตถุอินสแตนซ์เมื่อฉัน "แห้ง" รหัส

จากนั้นฉันพบคำหลัก "ใหม่" ซึ่งสำหรับฉันมัน "แยก" สิ่งต่าง ๆ ด้วยคำหลักใหม่มันสร้างสิ่งต่าง ๆ หากไม่มีคำหลักใหม่ฉันรู้ว่าฉันจะไม่สับสนกับการสร้างสิ่งต่าง ๆ นอกจากฟังก์ชั่นที่ฉันเรียกใช้จะให้เบาะแสที่แข็งแกร่งแก่ฉัน

ตัวอย่างเช่นเมื่อvar bar=foo();ฉันไม่มีเงื่อนงำว่าแถบใดที่อาจเป็นได้ ... มันเป็นค่าส่งคืนหรือเป็นวัตถุที่สร้างขึ้นใหม่หรือไม่ แต่var bar = new foo();ฉันรู้แน่ว่าแถบนั้นเป็นวัตถุ


3
เห็นด้วยและฉันเชื่อว่ารูปแบบโรงงานควรเป็นไปตามรูปแบบการตั้งชื่อเช่น makeFoo ()
pluckyglen

3
+1 - การปรากฏตัวของ 'ใหม่' ให้คำแถลงเจตนาที่ชัดเจนยิ่งขึ้น
belugabob

4
การตอบสนองนี้เป็นสิ่งที่แปลกเมื่อเกือบทุกอย่างใน JS เป็นวัตถุ ทำไมคุณต้องรู้ว่าฟังก์ชั่นเป็นวัตถุเมื่อฟังก์ชั่นทั้งหมดเป็นวัตถุ?
โจชัวรามิเรซ

@JoshuaRamirez typeof new foo() == "object"จุดคือไม่ได้ว่า มันเป็นเรื่องที่newผลตอบแทนที่เป็นตัวอย่างของfooและคุณรู้ว่าคุณสามารถโทรหาและfoo.bar() foo.bang()อย่างไรก็ตามที่สามารถบรรเทาได้อย่างง่ายดายโดยใช้@return ของ JsDocไม่ใช่ว่าฉันกำลังเรียกร้องให้ใช้รหัสขั้นตอน (หลีกเลี่ยงคำnew)
Juan Mendes

1
@JuanMendes อืม ... โพสต์ของคุณทำให้ฉันดูเหมือนว่าคุณชอบสิ่งใหม่เพราะคำหลักที่ชัดเจนในฐานรหัสของคุณ ฉันขุดมันได้ นั่นเป็นเหตุผลที่ฉันใช้รูปแบบโมดูล ฉันจะมีฟังก์ชั่นที่ชื่อว่า createFoo หรือ NewFoo หรือ MakeFoo ไม่สำคัญตราบใดที่มันชัดเจน ภายในที่ฉันประกาศตัวแปรที่ใช้เป็นปิดภายในของวัตถุตามตัวอักษรที่ส่งคืนจากฟังก์ชั่น วัตถุนั้นกลายเป็นวัตถุของคุณและฟังก์ชันนั้นเป็นเพียงฟังก์ชันการก่อสร้าง
Joshua Ramirez

39

อีกกรณีสำหรับใหม่เป็นสิ่งที่ผมเรียกPooh Coding Winnie the Pooh ติดตามท้องของเขา ฉันพูดไปด้วยภาษาที่คุณใช้ไม่ใช่กับมัน

โอกาสที่ผู้ดูแลภาษาจะปรับภาษาให้เหมาะกับสำนวนที่พวกเขาพยายามจะให้กำลังใจ หากพวกเขาใส่คำหลักใหม่เป็นภาษาที่พวกเขาอาจคิดว่ามันชัดเจนว่าเมื่อสร้างตัวอย่างใหม่

รหัสที่เขียนตามความตั้งใจของภาษาจะเพิ่มประสิทธิภาพในการเผยแพร่แต่ละครั้ง และรหัสที่หลีกเลี่ยงโครงสร้างที่สำคัญของภาษาจะประสบกับเวลา

แก้ไข: และนี่ไปได้ดีกว่าประสิทธิภาพ ฉันไม่สามารถนับเวลาที่ฉันได้ยิน (หรือพูด) "ทำไมพวกเขาถึงทำอย่างนั้น ?" เมื่อค้นหารหัสที่ดูแปลก ๆ มันมักจะกลายเป็นว่าในเวลาที่รหัสถูกเขียนขึ้นมีเหตุผล "ดี" บางอย่างสำหรับมัน การติดตามภาษาของเทารเป็นประกันที่ดีที่สุดสำหรับคุณที่ไม่มีรหัสเย้ยหยันมานานหลายปีนับจากนี้


3
+1 สำหรับลิงก์การเข้ารหัสของ Pooh - ตอนนี้ฉันต้องหาข้อแก้ตัวเพื่อใช้ในการสนทนาของฉัน ...
Shog9

ฮ่า ๆ! ฉันมีประสบการณ์หลายปีในสาขานั้นและฉันสามารถรับรองได้ว่าคุณจะไม่พบปัญหาใด ๆ พวกเขามักจะเรียกฉันว่าพูห์ที่ robowiki.net =)
PEZ

1
ไม่ทราบว่าคุณจะเห็นความคิดเห็นนี้หรือไม่ แต่ลิงก์ Pooh Coding นั้นตายไปแล้ว
Calvin

ขอบคุณสำหรับหัวขึ้น. เราย้าย robowiki.net ไปยังวิกิใหม่เมื่อสัปดาห์ที่แล้วและเนื้อหาเก่าไม่สามารถเข้าถึงได้ในขณะนี้ ฉันจะรีบทำให้การเชื่อมโยงเก่า ๆ ทำงานได้
PEZ

24

ฉันเขียนโพสต์เกี่ยวกับวิธีการลดปัญหาการโทรคอนสตรัคเตอร์โดยไม่มีคำหลักใหม่
ส่วนใหญ่เป็นการสอน แต่มันแสดงให้เห็นว่าคุณสามารถสร้าง Constructor ที่ทำงานด้วยหรือไม่newและไม่ต้องการให้คุณเพิ่มรหัส boilerplateเพื่อทดสอบthisใน Constructor ทุกตัว

http://js-bits.blogspot.com/2010/08/constructors-without-using-new.html

นี่คือส่วนสำคัญของเทคนิค:

/**
 * Wraps the passed in constructor so it works with
 * or without the new keyword
 * @param {Function} realCtor The constructor function.
 *    Note that this is going to be wrapped
 *    and should not be used directly 
 */
function ctor(realCtor){
  // This is going to be the actual constructor
  return function wrapperCtor(){
    var obj; // object that will be created
    if (this instanceof wrapperCtor) {
      // Called with new
      obj = this;
    } else {
      // Called without new. Create an empty object of the
      // correct type without running that constructor
      surrogateCtor.prototype = wrapperCtor.prototype;
      obj = new surrogateCtor();
    }
    // Call the real constructor function
    realCtor.apply(obj, arguments);
    return obj;
  }

  function surrogateCtor() {}
}

นี่คือวิธีการใช้งาน:

// Create our point constructor
Point = ctor(function(x,y){
  this.x = x;
  this.y = y;
});

// This is good
var pt = new Point(20,30);
// This is OK also
var pt2 = Point(20,30);

6
การหลีกเลี่ยงarguments.calleeนั้นยอดเยี่ยมมาก!
Gregg Lind

2
surrogateConstructorควรเป็นsurrogateCtor(หรือในทางกลับกัน)
David Conrad

ฉันยังคงเก็บรวบรวมยูทิลิตี้ที่ได้รับแรงบันดาลใจจาก Crockford ที่คล้ายกันและพบว่ามันทำให้ฉันทำงานเพื่อหาการใช้งานเมื่อฉันเริ่มโครงการใหม่ และนี่ก็เป็นส่วนพื้นฐานของการเขียนโปรแกรมเช่นการสร้างอินสแตนซ์ของวัตถุ ทำงานทั้งหมดนี้เพียงแค่เปิดใช้งานรหัสการเริ่มต้นแบบสุ่มหรือไม่ ฉันซาบซึ้งถึงความเฉลียวฉลาดของเสื้อคลุมนี้จริง ๆ แล้วมันดูเหมือนว่าจะทำให้เกิดการเขียนโค้ดที่ไม่ใส่ใจ
โจ Coder

1
@ ตัวแปลงสัญญาณที่ฉันพูดถึงนี้มีวัตถุประสงค์เพื่อการสอนเท่านั้นฉันไม่ได้สมัครเป็นสมาชิกของรูปแบบความหวาดระแวงของการเข้ารหัส อย่างไรก็ตามถ้าฉันกำลังเขียนไลบรารีคลาสใช่แล้วฉันจะเพิ่มคุณสมบัตินี้ในลักษณะที่โปร่งใสสำหรับผู้โทร
Juan Mendes

22

เหตุผลที่ไม่ได้ใช้คำหลักใหม่นั้นง่ายมาก:

หากไม่ใช้งานเลยคุณจะหลีกเลี่ยงข้อผิดพลาดที่มาพร้อมกับการข้ามไปโดยไม่ตั้งใจ รูปแบบการก่อสร้างที่ YUI ใช้เป็นตัวอย่างของวิธีที่คุณสามารถหลีกเลี่ยงคำหลักใหม่ทั้งหมด "

var foo = function () {
    var pub= { };
    return pub;
}
var bar = foo();

หรือคุณสามารถทำสิ่งนี้:

function foo() { }
var bar = new foo();

แต่ด้วยการทำเช่นนั้นคุณเสี่ยงต่อการที่บางคนลืมใช้คำหลักใหม่และสิ่งนี้ผู้ประกอบการเป็น fubar ทั้งหมด AFAIK ไม่มีข้อได้เปรียบในการทำเช่นนี้ (นอกเหนือจากที่คุณคุ้นเคย)

ในตอนท้ายของวัน: มันเกี่ยวกับการป้องกัน คุณสามารถใช้คำสั่งใหม่ได้หรือไม่? ใช่. มันทำให้รหัสของคุณอันตรายมากขึ้นหรือไม่? ใช่.

ถ้าคุณเคยเขียน C ++ มันก็เหมือนกับการตั้งค่าพอยน์เตอร์ให้เป็น NULL หลังจากคุณลบมัน


6
ไม่ด้วย "new foo ()" มีการตั้งค่าคุณสมบัติบางอย่างบนวัตถุที่ส่งคืนเช่นตัวสร้าง
บาง

34
ดังนั้นเพื่อให้ชัดเจน: คุณไม่ควรใช้ "ใหม่" เพราะคุณอาจลืมได้หรือไม่ คุณล้อเล่นใช่ไหม
Bombe

16
@Bombe: ในภาษาอื่น ๆ ที่ลืมว่า "ใหม่" จะทำให้เกิดข้อผิดพลาด ใน Javascript มันแค่เกี่ยวกับการขนส่ง คุณสามารถลืมและไม่เคยตระหนัก และเพียงแค่ดูโค้ดผิดพลาดจะไม่ชัดเจนว่าเกิดอะไรขึ้น
Kent Fredric

3
@ Greg: ฉันไม่เห็นว่าเทคนิคแรกที่อนุญาตให้ใช้กับโซ่ต้นแบบได้อย่างไร - ตัวอักษรของวัตถุยอดเยี่ยม แต่การละทิ้งประสิทธิภาพและข้อดีอื่น ๆ ที่ต้นแบบจากความกลัวดูเหมือนจะโง่เง่า
Shog9

5
@Bombe - คุณควรใช้ "ใหม่" เพราะคุณ (และทุกคนที่ใช้รหัสของคุณ) จะไม่ทำผิดพลาดหรือไม่? คุณล้อเล่นใช่ไหม
Greg Dean

20

ฉันคิดว่า "ใหม่" เพิ่มความชัดเจนให้กับรหัส และความคมชัดนั้นคุ้มค่าทุกอย่าง เป็นการดีที่จะรู้ว่ามีหลุมพราง แต่การหลีกเลี่ยงพวกมันโดยการหลีกเลี่ยงความชัดเจนนั้นดูเหมือนจะไม่ใช่วิธีที่ฉัน


16

กรณีที่ 1: newไม่จำเป็นและควรหลีกเลี่ยง

var str = new String('asd');  // type: object
var str = String('asd');      // type: string

var num = new Number(12);     // type: object
var num = Number(12);         // type: number

กรณีที่ 2: newจำเป็นมิฉะนั้นคุณจะได้รับข้อผิดพลาด

new Date().getFullYear();     // correct, returns the current year, i.e. 2010
Date().getFullYear();         // invalid, returns an error

5
ควรสังเกตว่าในกรณีที่ 1 การเรียกนวกรรมิกว่าเป็นฟังก์ชั่นจะเหมาะสมถ้าคุณต้องการแปลงชนิด (และไม่ทราบว่าวัตถุฮัลล์มีวิธีการแปลงเช่นtoString()) หรือไม่ ในกรณีอื่น ๆ ให้ใช้ตัวอักษร ไม่ String('asd')แต่เพียง'asd'และไม่ แต่เพียงNumber(12) 12
แหลม

1
@PointedEars ข้อยกเว้นประการหนึ่งสำหรับกฎนี้ คือArray: Array(5).fill(0)เห็นได้ชัดว่าอ่านได้ง่ายกว่า[undefined, undefined, undefined, undefined, undefined].fill(0)และ(var arr = []).length = 5; arr.fill(0);
yyny

@YoYoYonnY คำสั่งของฉันถูกสร้างขึ้นในบริบทของกรณีที่ 1 ของคำตอบซึ่งหมายถึงการใช้งานnewกับ constructors สำหรับประเภทของวัตถุที่สอดคล้องกับประเภทดั้งเดิม ตัวอักษรที่คุณแนะนำไม่เท่ากับการArrayโทร (ลอง0 in …) และส่วนที่เหลือเป็นข้อผิดพลาดทางไวยากรณ์ :) มิฉะนั้นคุณจะถูกต้องแม้ว่าจะถูกบันทึกไว้ก็ตาม แต่ก็ควรสังเกตด้วยเช่นกันซึ่งArray(5)อาจคลุมเครือหากคุณพิจารณาการใช้งานที่ไม่สอดคล้องดังนั้นการจำลอง Array.prototype.fill(…) )
แหลมแหลม

12

นี่คือบทสรุปที่สั้นที่สุดที่ฉันสามารถทำได้จากข้อโต้แย้งที่แข็งแกร่งสองข้อสำหรับและต่อต้านการใช้ newโอเปอเรเตอร์:

ทะเลาะกับ new

  1. ฟังก์ชั่นที่ออกแบบมาให้สร้างอินสแตนซ์เป็นวัตถุที่ใช้ตัว newดำเนินการอาจมีผลกระทบร้ายแรงหากถูกเรียกใช้อย่างไม่ถูกต้องตามฟังก์ชั่นปกติ รหัสของฟังก์ชันในกรณีดังกล่าวจะถูกดำเนินการในขอบเขตที่เรียกใช้ฟังก์ชันแทนที่จะอยู่ในขอบเขตของวัตถุในท้องถิ่นตามที่ต้องการ สิ่งนี้อาจทำให้ตัวแปรและคุณสมบัติทั่วโลกถูกเขียนทับด้วยผลที่ตามมาหายนะ
  2. ในที่สุดเขียนfunction Func()แล้วเรียกFunc.prototype และเพิ่มเนื้อหาลงไปเพื่อให้คุณสามารถเรียกใช้new Func()เพื่อสร้างวัตถุของคุณดูน่าเกลียดสำหรับโปรแกรมเมอร์บางคนที่ค่อนข้างจะใช้รูปแบบของการสืบทอดวัตถุอื่นสำหรับเหตุผลทางสถาปัตยกรรมและโวหาร

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ลองอ่านหนังสือที่ดีและกระชับของ Douglas Crockford Javascript: The Good Parts ในความเป็นจริงตรวจสอบมันออกไปอยู่ดี

ข้อโต้แย้งในความโปรดปรานของ new

  1. การใช้newโอเปอเรเตอร์พร้อมกับการมอบหมายต้นแบบนั้นรวดเร็ว
  2. สิ่งนั้นเกี่ยวกับการเรียกใช้รหัสฟังก์ชันของตัวสร้างในเนมสเปซส่วนกลางนั้นสามารถป้องกันได้โดยง่ายถ้าคุณใส่รหัสบิตไว้ในฟังก์ชันตัวสร้างของคุณเพื่อตรวจสอบว่ามีการเรียกใช้อย่างถูกต้องหรือไม่และในกรณีที่ไม่ได้ จัดการสายอย่างเหมาะสมตามต้องการ

ดูโพสต์ของ John Resigสำหรับคำอธิบายอย่างง่ายของเทคนิคนี้และสำหรับคำอธิบายที่ลึกกว่าของโมเดลการสืบทอดที่เขาสนับสนุน


อัปเดตเกี่ยวกับอาร์กิวเมนต์ที่ขัดแย้ง: # 1 สามารถบรรเทาได้โดยใช้'use strict';โหมด (หรือวิธีการในลิงก์ของคุณ) # 2 มีน้ำตาลใน synatic ในES6อย่างไรก็ตามพฤติกรรมจะเหมือนเดิม
ninMonkey

9

ฉันเห็นด้วยกับ pez และบางอย่างที่นี่

มันดูเหมือนชัดเจนกับผมว่า "ใหม่" คือการสร้างตัวเองวัตถุบรรยายที่รูปแบบยูอิเกร็กคณบดีอธิบายถูกบดบัง

คนเป็นไปได้ที่จะเขียนvar bar = foo;หรือvar bar = baz();ที่ baz ไม่ได้เป็นวิธีการสร้างวัตถุดูเหมือนไกลอันตรายมากขึ้น


8

ฉันคิดว่าสิ่งใหม่เป็นสิ่งชั่วร้ายไม่ใช่เพราะถ้าคุณลืมที่จะใช้มันโดยไม่ได้ตั้งใจอาจทำให้เกิดปัญหา แต่เพราะมันทำให้เกิดการถ่ายทอดทางพันธุกรรมทำให้ภาษาเข้าใจยากขึ้น

JavaScript เป็นแบบเชิงวัตถุ var newObj=Object.create(oldObj)ดังนั้นวัตถุที่ทุกคนต้องได้รับการสร้างขึ้นจากวัตถุอื่นเช่นดังนั้น oldObjนี่เรียกว่าต้นแบบของnewObj (ดังนั้น "ต้นแบบตาม") ซึ่งหมายความว่าถ้าคุณสมบัติไม่พบในnewObjแล้วมันจะค้นหาในoldObj newObjโดยค่าเริ่มต้นจึงจะเป็นวัตถุที่ว่างเปล่า แต่เนื่องจากห่วงโซ่ต้นแบบของมันดูเหมือนจะมีค่าทั้งหมดของoldObj

ในทางตรงกันข้ามถ้าคุณทำvar newObj=new oldObj()ต้นแบบของnewObjคือoldObj.prototypeซึ่งยากที่จะเข้าใจโดยไม่จำเป็น

เคล็ดลับคือการใช้

Object.create=function(proto){
  var F = function(){};
  F.prototype = proto;
  var instance = new F();
  return instance;
};

มันอยู่ในฟังก์ชั่นนี้และเฉพาะที่นี่ที่ควรจะใช้ใหม่ หลังจากนี้เพียงแค่ใช้Object.create ()วิธีการ วิธีนี้แก้ไขปัญหาต้นแบบ


7
พูดตามตรงฉันไม่ได้บ้ากับเทคนิคนี้ - มันไม่ได้เพิ่มอะไรใหม่และอย่างที่คำตอบของคุณแสดงให้เห็นว่ามันสามารถกลายเป็นไม้ค้ำได้ IMHO การทำความเข้าใจกับโซ่ต้นแบบและการสร้างอินสแตนซ์ของวัตถุนั้นมีความสำคัญต่อการทำความเข้าใจ JavaScript ... แต่ถ้ามันทำให้คุณอึดอัดที่จะใช้มันโดยตรงจากนั้นใช้ฟังก์ชั่นตัวช่วยในการดูแลรายละเอียดบางอย่าง การทำ FWIW: การเปลี่ยนแปลง (มีประโยชน์มากกว่า) ในส่วนนี้เป็นส่วนหนึ่งของ ECMAScript 5th ed std และมีอยู่แล้วในเบราว์เซอร์บางตัว - ดังนั้นคุณควรระวังไม่ให้นิยามใหม่แบบสุ่ม!
Shog9

BTW: ฉันไม่แน่ใจว่าทำไมคุณถึงสร้าง CW นี้ แต่ถ้าคุณต้องการโพสต์ใหม่ด้วยการแก้ไขการจัดรูปแบบที่ฉันทำโปรดใช้ความระมัดระวังเพื่อหลีกเลี่ยงช่องทำเครื่องหมายนั้น ...
Shog9

2
ยากที่จะเข้าใจโดยไม่จำเป็น? var c = new Car()เป็นเช่นเดียวกับการทำvar c = Object.create(Car.prototype); Car.call(c)
Juan Mendes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.