เพราะเหตุใด + จึงไม่ดีสำหรับการต่อข้อมูล


44

ทุกคนยังคงบอกว่าหนึ่งในปัญหาของ JavaScript กำลังใช้+[ ตัวอย่าง ] สำหรับการต่อสตริง บางคนบอกว่าไม่ได้ใช้ปัญหา+มันเป็นประเภทข่มขู่ [ดูความคิดเห็นจากตัวอย่างก่อนหน้านี้] แต่ภาษาที่พิมพ์อย่างรุนแรงใช้ + สำหรับการเรียงต่อกันและประเภทการรวมตัวโดยไม่มีปัญหาใด ๆ ตัวอย่างเช่นใน C #:

int number = 1;
MyObject obj = new MyObject();

var result = "Hello" + number + obj;
// is equivalent to
string result = "hello" + number.ToString() + obj.ToString();

ดังนั้นทำไมการต่อสตริงใน JavaScript จึงเป็นปัญหาใหญ่


17
badเหตุผลที่มันเป็น คือใครEveryone?
Neal

3
ฉันคิดว่าปัญหาคือ + เป็นตัวดำเนินการทางคณิตศาสตร์ และฟังก์ชั่นการต่อข้อมูลของ + ทำให้เกิดความสับสนว่ากระบวนการมาตรฐานนั้น เพราะในขณะที่ "Hello" + หมายเลขอาจทำงานได้หมายเลข + "Hello" อาจไม่ทำงาน
SoylentGray

3
@ จริงฉันเห็นด้วย - ฉันต้องการที่จะรู้ว่าใครทุกคนเหล่านี้ลึกลับ 'ทุกคน' ดูเหมือนจะอยู่ในคำถามเหล่านี้ทั้งหมด
GrandmasterB

การใช้ + สำหรับการต่อข้อมูลก็โอเค การใช้การพิมพ์แบบไดนามิกก็โอเค การใช้ทั้งสองในภาษาเดียวกันนั้นไม่ดี ^ H ^ H ^ H ทำให้เกิดความกำกวม
เจอร์รี่

6
@Neal - "ทุกคน" จะเป็น Douglas Crockford เขาระบุว่าเป็น "น่ากลัว" ในหนังสือของเขา 'JavaScript: The Good Parts'
kirk.burleson

คำตอบ:


68

พิจารณาโค้ด JavaScript ชิ้นนี้:

var a = 10;
var b = 20;
console.log('result is ' + a + b);

นี้จะเข้าสู่ระบบ

ผลที่ได้คือ 1020

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


4
ภาพประกอบที่ดีมากของปัญหาพื้นฐาน: การต่อสตริงไม่ได้กำหนดไว้อย่างดีเมื่อประเภท (สตริง + int + int ในตัวอย่าง) ผสมกัน ดีกว่าที่จะมีโอเปอเรเตอร์ที่แตกต่างอย่างสิ้นเชิง (เช่น Perl's '.') ด้วยซีแมนติกส์ที่กำหนดไว้อย่างดี
Bruce Ediger

9
หรือวิธีการของงูใหญ่ของการแก้นี้ซึ่งจะบังคับให้คุณห่อaและbในstr(a)และstr(b)สาย; ValueError: cannot concatenate 'str' and 'int'มิฉะนั้นคุณจะได้รับ
Aphex

6
@FrustratedWithFormsDesigner: เพิ่มวงเล็บ 'result is ' + (a + b).
Jon Purdy

18
สิ่งที่อยู่ใน C # คุณสามารถทำสิ่งเดียวกันและคุณจะได้รับผลลัพธ์เดียวกัน - System.Console.WriteLine("result is " + 10 + 20);แต่ไม่มีใครเคยบ่นเกี่ยวกับสิ่งนั้นใน C # นั่นคือสิ่งที่ฉันไม่เข้าใจ - อะไรคือสิ่งที่เกี่ยวกับ JavaScript ที่ทำให้สับสนมากขึ้น?
กำหนดค่า

7
@configurator: เนื่องจากมีคนบอกว่า C # นั้นสมบูรณ์แบบและจาวาสคริปต์นั้นยอดเยี่ยมมาก พวกเขาผิดทั้งคู่ แต่ชื่อเสียงของภาษาดูเหมือนจะติดอยู่การรับรู้นับเป็นจำนวนมากโดยเฉพาะอย่างยิ่งในการฝึกงาน
gbjbaanb

43

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

เราไม่ได้พูดถึง"Hello" + numberเมื่อเราพูดถึงการแสดงเรากำลังพูดถึงการสร้างสายอักขระที่ค่อนข้างใหญ่โดยผนวกเข้าด้วยกันซ้ำ ๆ

var combined = "";
for (var i = 0; i < 1000000; i++) {
    combined = combined + "hello ";
}

ในสตริง JavaScript (และ C # สำหรับเรื่องนั้น) จะไม่เปลี่ยนรูป พวกเขาไม่สามารถเปลี่ยนแปลงได้ถูกแทนที่ด้วยสตริงอื่นเท่านั้น คุณอาจทราบcombined + "hello "ว่าไม่ได้แก้ไขcombinedตัวแปรโดยตรง- การดำเนินการสร้างสตริงใหม่ที่เป็นผลมาจากการเชื่อมสองสตริงเข้าด้วยกัน แต่คุณต้องกำหนดสตริงใหม่นั้นให้กับcombinedตัวแปรหากคุณต้องการเปลี่ยน

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

C # มีปัญหาเดียวกันที่แน่นอนซึ่งแก้ไขได้ในสภาพแวดล้อมนั้นโดยคลาสStringBuilder ใน JavaScript คุณจะได้รับประสิทธิภาพที่ดีขึ้นมากโดยสร้างอาร์เรย์ของสตริงทั้งหมดที่คุณต้องการต่อกันแล้วรวมเข้าด้วยกันหนึ่งครั้งในตอนท้ายแทนที่จะเป็นล้านครั้งในลูป:

var parts = [];
for (var i = 0; i < 1000000; i++) {
    parts.push("hello");
}
var combined = parts.join(" ");

3
นี่คือคำตอบที่ยอดเยี่ยม ไม่เพียงตอบคำถามเท่านั้น แต่ยังได้รับการศึกษาด้วย
Charles Sprayberry

3
นี่ไม่ใช่สิ่งที่ฉันตั้งใจเลย แต่ก็ไม่ได้เกี่ยวข้องกับคำถามทั้งหมด
กำหนดค่า

4
ขอโทษ ดูเหมือนว่ามีคนอย่างน้อย 7 คนที่เข้าใจคำถามของคุณผิดไปจากที่ใช้คำ
Joel Mueller

9
เรื่องประสิทธิภาพการทำงาน: อาจเป็นเช่นนี้ในปี 2011 แต่ตอนนี้วิธี + โอเปอเรเตอร์นั้นเร็วกว่าเมธอด array.join (อย่างน้อยใน v8) ดู jsperf นี้: jsperf.com/string-concatenation101
UpTheCreek

2
ความคิดใด ๆ ที่วิธีการรวมสร้างสตริงหนึ่งจากหลายส่วนในขั้นตอนเดียวแทนที่จะรวมกันแล้วหนึ่งโดยหนึ่งดังนั้นจึงสร้างสตริงกลางเหล่านั้นทั้งหมดหรือไม่
Angelo.Hannes

14

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

3 * 5 => 15
"3" * "5" => 15
2 + " fish" => "2 fish"
"3" + "5" => "35"  // hmmm...

อาจจะง่ายกว่านั้นการใส่ "+" มากเกินไปในที่ที่มีการแปลงประเภทอัตโนมัติจะทำลายความสัมพันธ์ที่คาดหวังของผู้ประกอบการ +:

("x" + 1) + 3 != "x" + (1 + 3)

2
นอกจากนี้สมบัติการสลับการเติมจะหายไปจากการต่อข้อมูล3 + 5 == 5 + 3แต่"3" + "5" != "5" + "3"
Caleth

10

ปัญหาทั่วไปที่นำมาสำหรับการต่อข้อมูลสตริงจะหมุนรอบสองสามประเด็น:

  1. +สัญลักษณ์การโอเวอร์โหลด
  2. ข้อผิดพลาดง่าย ๆ
  3. โปรแกรมเมอร์ขี้เกียจ

# 1

ปัญหาแรกและสำคัญที่สุดที่มีการใช้+สำหรับการต่อสตริงและการเพิ่มคือการที่คุณกำลังใช้+สำหรับการต่อสตริงและการเพิ่ม

ถ้าคุณมีตัวแปรaและbและคุณตั้งค่าc = a + bมีความคลุมเครือขึ้นอยู่กับประเภทของและa bความกำกวมนี้บังคับให้คุณทำตามขั้นตอนพิเศษเพื่อลดปัญหา:

String Concat:

c = '' + a + b;

ส่วนที่เพิ่มเข้าไป:

c = parseFloat(a) + parseFloat(b);

นี่นำฉันมาสู่จุดที่สอง

# 2

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

a = prompt('Pick a number');
b = prompt('Pick a second number');
alert( a + b );

จะสร้างผลลัพธ์ที่ไม่เข้าใจง่ายเพราะการแจ้งจะส่งคืนค่าสตริงของอินพุต

# 3

จำเป็นต้องพิมพ์parseFloatหรือNumber()ทุกครั้งที่ฉันต้องการเพิ่มนอกจากนี้เป็นที่น่าเบื่อและน่ารำคาญ ผมคิดว่าตัวเองเป็นพอสมาร์ทจำไม่เลอะประเภทแบบไดนามิกของฉัน แต่ที่ไม่ได้หมายความว่าฉันไม่เคย messed up ประเภทแบบไดนามิกของฉัน ความจริงของเรื่องก็คือว่าฉันขี้เกียจเกินไปที่จะพิมพ์ออกมาparseFloatหรือNumber()เวลาที่ฉันทำนอกจากนี้ทุกคนเพราะผมทำมากนอกจากนี้

วิธีการแก้?

ไม่ใช่ทุกภาษาที่ใช้+สำหรับการต่อสตริง PHP ใช้.เพื่อเชื่อมสตริงเข้าด้วยกันซึ่งจะช่วยแยกความแตกต่างระหว่างเวลาที่คุณต้องการเพิ่มตัวเลขและเมื่อคุณต้องการเข้าร่วมสตริง

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

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