ลำดับความสำคัญของตัวดำเนินการกับตัวดำเนินการ Javascript Ternary


116

ดูเหมือนว่าฉันจะพันหัวของฉันไปรอบ ๆ ส่วนแรกของรหัสนี้ (+ =) ร่วมกับตัวดำเนินการด้านท้ายไม่ได้

h.className += h.className ? ' error' : 'error'

วิธีที่ฉันคิดว่ารหัสนี้ใช้งานได้มีดังต่อไปนี้:

h.className = h.className + h.className ? ' error' : 'error'

แต่นั่นไม่ถูกต้องเพราะนั่นทำให้เกิดข้อผิดพลาดในคอนโซลของฉัน

ดังนั้นคำถามของฉันคือฉันจะเชื่อมต่อรหัสนี้อย่างถูกต้องได้อย่างไร?

คำตอบ:


141
h.className = h.className + (h.className ? ' error' : 'error')

คุณต้องการให้ตัวดำเนินการทำงานให้h.classNameเจาะจงเกี่ยวกับมันให้ดีขึ้น
แน่นอนว่าไม่ควรเกิดอันตราย h.className += ' error'แต่นั่นเป็นอีกเรื่องหนึ่ง

นอกจากนี้โปรดทราบว่า+มีความสำคัญเหนือกว่าตัวดำเนินการ ternary: JavaScript Operator Precedence


3
ฉันคิดว่าควรสังเกตว่าแม้ว่าจะไม่เกิดอันตรายใดh.className += ' error'ๆ แต่ก็ยังเว้นช่องว่างไว้ที่จุดเริ่มต้นของสตริงหากในตอนแรกว่างเปล่า ฉันเชื่อว่าจุดสำคัญของการดำเนินการสามส่วนคือการสร้างสตริงที่ดูสะอาดตา
JMTyler

@JMTyler - นั่นคือสิ่งที่ฉันบ่งบอก - ถ้าทุกอย่างทำเพียงเพื่อรักษาพื้นที่ไว้ตั้งแต่เริ่มต้นฉันก็ไม่คุ้มค่า (ตัวพิมพ์เล็กขอบรวมถึงตัวเลือก jQuery หรือ XPath ที่แน่นอน) ยังไงก็ขอบคุณ
Kobi

@ Kobi +1 สำหรับคำเตือนลำดับความสำคัญของผู้ปฏิบัติงานเพียงอย่างเดียว!
Ed Chapel

129

คิดแบบนี้:

<variable> = <expression> ? <true clause> : <false clause>

วิธีดำเนินการคำสั่งโดยทั่วไปมีดังนี้:

  1. ไม่<expression>ประเมินเป็นจริงหรือไม่ก็ประเมินเท็จ?
  2. หาก<expression>ประเมินจริงแล้วค่าของการ<true clause>ได้รับมอบหมายให้<variable>, <false clause>ถูกละเว้นและคำสั่งต่อไปที่จะดำเนินการ
  3. หาก<expression>ประเมินการเท็จแล้ว<true clause>จะถูกละเว้นและความคุ้มค่าของการได้รับมอบหมายให้<false clause><variable>

สิ่งสำคัญที่ต้องคำนึงถึงตัวดำเนินการ ternary ในภาษานี้และภาษาอื่น ๆ ก็คือรหัสใดก็ตามที่อยู่ใน<expression>ควรให้ผลลัพธ์แบบบูลีนเมื่อประเมิน: ไม่ว่าจะเป็นจริงหรือเท็จ

ในกรณีของตัวอย่างของคุณให้แทนที่ "กำหนดให้" ในคำอธิบายของฉันด้วย "เพิ่มใน" หรือคล้ายกันสำหรับเลขคณิตชวเลขที่คุณใช้อยู่ถ้ามี


หมายเหตุแน่ใจว่าสมบูรณ์แบบแสดงความคิดเห็นเป็นไปอย่างเหมาะสม :) มันข้ามคำอธิบายใด ๆว่าทำไมการแสดงออกซ้ายมือ "กลุ่มกัน" ครั้งแรก (เช่นเพราะ+มีความสำคัญมากกว่าเงื่อนไข / ternary ประกอบการ (ในความเป็นจริงผู้ประกอบการที่มีเงื่อนไขคือเกือบเสมอเป็นคนสุดท้าย ประเมินในนิพจน์ใด ๆ )
Gone Coding

10

+=ไม่สิ่งที่คุณต้องการ แต่ในงบ ternary ที่ด้านขวามือของมันตรวจสอบได้ถ้าh.classNameเป็น falsey ซึ่งมันจะเป็นถ้ามันเป็นไม่ได้กำหนด ถ้าเป็นความจริง (เช่นถ้าระบุชื่อคลาสแล้ว) ข้อผิดพลาดจะถูกเพิ่มด้วยช่องว่าง (เช่นการเพิ่มคลาสใหม่ ) มิฉะนั้นจะถูกเพิ่มโดยไม่มีช่องว่าง

รหัสสามารถเขียนใหม่ได้ตามที่คุณแนะนำ แต่คุณต้องระบุว่าh.classNameจะใช้สำหรับการเปรียบเทียบความจริงแทนที่จะใช้ค่าจริงในตัวดำเนินการที่เกี่ยวข้องดังนั้นอย่ากังวลกับการต่อค่า ในเวลาเดียวกันกับการดำเนินการตามขั้นตอนของคุณ:

h.className = h.className + (h.className ? ' error' : 'error');

13
ใช่แล้วundefinedไม่ได้เป็นเท็จมันเป็นเพียงการปฏิบัติราวกับว่ามันเป็น
David Hedlund

4

ด้านขวามือของตัว=ดำเนินการจะถูกประเมินจากซ้ายไปขวา ดังนั้น,

g.className = h.className + h.className ? ' error' : 'error';`

เทียบเท่ากับ

h.className = (h.className + h.className) ? ' error' : 'error';

ให้เทียบเท่ากับ

h.className += h.className ? ' error' : 'error';

คุณต้องแยกคำสั่ง ternary ในวงเล็บ

h.className = h.className + (h.className ? ' error' : 'error');


1

ฉันรู้ว่านี่เป็นคำถามที่เก่ามาก แต่ฉันไม่พอใจกับคำตอบใด ๆ 100% เนื่องจากคำตอบทั้งหมดดูเหมือนจะไม่สมบูรณ์ ดังนั้นที่นี่เราไปอีกครั้งจากหลักการแรก:

จุดมุ่งหมายโดยรวมของผู้ใช้:

การสรุปโค้ด: "ฉันต้องการเพิ่มerrorชื่อคลาสให้กับสตริงโดยเลือกเว้นวรรคนำหน้าหากมีชื่อคลาสอยู่แล้วในสตริง"

วิธีแก้ปัญหาที่ง่ายที่สุด

ดังที่ Kobi กล่าวไว้เมื่อ 5 ปีที่แล้วการมีพื้นที่ชั้นนำในชื่อคลาสจะไม่ก่อให้เกิดปัญหากับเบราว์เซอร์ที่รู้จักดังนั้นวิธีแก้ปัญหาที่สั้นที่สุดคือ:

h.className += ' error';

ที่ควรได้รับการตอบจริงกับปัญหาที่เกิดขึ้นจริง


อาจเป็นไปได้ว่าคำถามที่ถามคือ ...

1) ทำไมถึงได้ผล?

h.className += h.className ? ' error' : 'error'

ตัวดำเนินการเงื่อนไข / ternary ทำงานเหมือนคำสั่ง if ที่กำหนดผลลัพธ์ของมันtrueหรือพา ธfalseไปยังตัวแปร

ดังนั้นรหัสนั้นจึงทำงานได้เนื่องจากมีการประเมินเพียงดังนี้:

if (h.className IS NOT null AND IS NOT undefined AND IS NOT '') 
    h.className += ' error'
else
    h.className += 'error'

2) แล้วทำไมถึงแตก?

h.className = h.className + h.className ? ' error' : 'error'

คำถามที่รัฐ "ที่ให้ [N] ข้อผิดพลาดในคอนโซลของฉัน" ซึ่งอาจทำให้เข้าใจผิดให้คุณคิดว่ารหัสไม่สามารถทำงานได้ ในความเป็นจริงรหัสต่อไปนี้จะทำงานโดยไม่มีข้อผิดพลาดแต่มันก็ 'ข้อผิดพลาด' ผลตอบแทนถ้าสตริงก็ไม่ว่างเปล่าและ 'ข้อผิดพลาด' ถ้าสตริงเป็นที่ว่างเปล่าและอื่น ๆไม่ได้ตอบสนองความต้องการ

รหัสนั้นจะส่งผลให้สตริงที่มีเพียง' error'หรือ'error'เพราะมันประเมินเป็นรหัสหลอกนี้เสมอ:

if ((h.className + h.className) IS NOT null AND IS NOT undefined AND IS NOT '')
    h.className = ' error'
else
    h.className = 'error'

เหตุผลนี้ก็คือตัวดำเนินการเพิ่มเติม ( +ไปยังโฟล์คทั่วไป) มี "ลำดับความสำคัญ" (6) สูงกว่าตัวดำเนินการเงื่อนไข / ตัวดำเนินการตามเงื่อนไข (15) ฉันรู้ว่าตัวเลขปรากฏย้อนหลัง

ลำดับความสำคัญหมายถึงว่าตัวดำเนินการแต่ละประเภทในภาษาได้รับการประเมินตามลำดับที่กำหนดไว้ล่วงหน้าโดยเฉพาะ (ไม่ใช่แค่จากซ้ายไปขวา)

ข้อมูลอ้างอิง: Javascript Operator Precedence

วิธีเปลี่ยนลำดับการประเมิน:

ตอนนี้เรารู้แล้วว่าทำไมมันถึงล้มเหลวคุณต้องรู้วิธีทำให้มันใช้งานได้

บางคำตอบอื่น ๆ พูดคุยเกี่ยวกับการเปลี่ยนแปลงลำดับความสำคัญแต่คุณไม่สามารถ ลำดับความสำคัญเป็นแบบต่อสายในภาษา นั่นเป็นเพียงการกำหนดกฎเกณฑ์ตายตัว ... อย่างไรก็ตามคุณสามารถเปลี่ยนลำดับการประเมินได้ ...

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

วงเล็บใช้งานได้เพราะ (ตัวดำเนินการจัดกลุ่ม) มีลำดับความสำคัญสูงกว่าตัวดำเนินการอื่น ๆ ทั้งหมด ("ขณะนี้มีระดับ 0")

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

h.className = h.className + (h.className ? ' error' : 'error')

ตอนนี้ฉันจะปล่อยให้คำตอบนี้เป็นสนิมที่ไม่มีใครเห็น :)


1

ฉันต้องการเลือกคำอธิบายของเวย์น:

<variable> = <expression> ? <true clause> : <false clause>

ลองพิจารณาทั้งสองกรณี:

case 1:
h.className += h.className ? 'true' : 'false'     
  • ตัวดำเนินการมอบหมายทำงานได้ดีและเพิ่มมูลค่า
  • เมื่อรันเป็นครั้งแรก o / p: false
  • ครั้งที่ 2. o / p: falsetrue - ค่าต่อท้าย

กรณี 2: h.className = h.className + h.className? 'ถูกผิด'

  • ผลลัพธ์ไม่เหมือนกับกรณีที่ 1
  • เมื่อรันเป็นครั้งแรก o / p: false
  • ครั้งที่ 2. o / p: false - ค่าจะไม่ต่อท้าย

explanation

ในรหัสด้านบนกรณีที่ 1 ทำงานได้ดี

ในขณะที่ case2:

h.className = h.className + h.className ? 'true' : 'false'
is executed as 
 h.className = (h.className + h.className) ? 'true' : 'false'

h.className + h.className=> ถือว่าเป็นนิพจน์สำหรับตัวดำเนินการ ternary เนื่องจากตัวดำเนินการ ternary จะมีลำดับความสำคัญสูงกว่า ดังนั้นผลลัพธ์ของนิพจน์ด้านท้ายจะถูกกำหนดให้เสมอ

คุณต้องกำหนดลำดับความสำคัญโดยใช้วงเล็บ

คุณต้องกำหนดลำดับของการประเมินที่จะพิจารณาด้วยความช่วยเหลือของวงเล็บเพื่อให้กรณีที่ 2 ทำงานเป็นกรณีที่ 1

h.className = h.className + (h.className ? ' error' : 'error') 

1
คำศัพท์ที่นี่ไม่ถูกต้องนัก มีความสำคัญมีอยู่ในภาษาที่คุณไม่กำหนดมัน คุณกำลังกำหนดลำดับของการประเมินโดยใช้วงเล็บ (ซึ่งมีลำดับความสำคัญสูงกว่าตัวดำเนินการอื่น ๆ ทั้งหมด)
Gone Coding

@TrueBlueAussie ฉันยอมรับมัน ฉันขอขอบคุณสำหรับความตั้งใจของคุณในการอ่าน +1
Angelin Nadar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.