ทำไม [1,2] + [3,4] =“ 1,23,4” ใน JavaScript


405

ฉันต้องการเพิ่มองค์ประกอบของอาเรย์เข้าไปอีกอันหนึ่งดังนั้นฉันจึงลองทำสิ่งนี้:

[1,2] + [3,4]

มันตอบสนองด้วย:

"1,23,4"

เกิดอะไรขึ้น?


1
นี่คือคำถามที่เกี่ยวข้องกับหัวข้อนี้: stackoverflow.com/questions/1724255/why-does-2-2-in-javascript
Xavi

29
Ah-ha-ha ผู้สัมภาษณ์ซาดิสม์สามารถถามอะไรทำนองนั้นสิ่งนี้จะส่งกลับ [1,2] + [5,6,7] [1,2] ทำไม?
shabunc

9
ฉันคิดว่า [1,2] + [3,4] เป็นนิพจน์ที่ได้รับการประเมินมากที่สุดใน firebug ในสัปดาห์นี้หลังจากการแจ้งเตือน ('อึ')
okeen

6
อยากหัวเราะไหม ลอง [] + [], {} + [], {} + {} และ [] + {}
Intrepidd

1
@shabunc - การดูแลที่จะอธิบายว่าทำไม[5,6,7][1,2]เป็น7เพราะใช้รายการสุดท้ายในอาร์เรย์ที่สอง Oo
vsync

คำตอบ:


518

+ผู้ประกอบการไม่ได้ถูกกำหนดสำหรับอาร์เรย์

สิ่งที่เกิดขึ้นคือ Javascript แปลงอาร์เรย์เป็นสตริงและเชื่อมต่อกัน

 

ปรับปรุง

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

ดังนั้นนี่มันไป

ไม่รวม E4X และสิ่งเฉพาะสำหรับการใช้งาน Javascript (as ES5) มี6 ชนิดข้อมูลในตัว :

  1. ไม่ได้กำหนด
  2. โมฆะ
  3. บูลีน
  4. จำนวน
  5. เชือก
  6. วัตถุ

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

ถูกต้อง - Javascript ไม่มีอาร์เรย์แบบดั้งเดิมเช่นนี้ อินสแตนซ์ของวัตถุที่เรียกว่าArrayมีน้ำตาล syntactic เพียงเพื่อบรรเทาความเจ็บปวด

เพิ่มมากขึ้นเพื่อความสับสน, เสื้อคลุมหน่วยงานเช่นnew Number(5), new Boolean(true)และnew String("abc")มีทุกobjectชนิดไม่ได้ตัวเลข booleans หรือสตริงเป็นหนึ่งอาจคาดหวัง อย่างไรก็ตามสำหรับตัวดำเนินการทางคณิตศาสตร์NumberและBooleanทำงานเป็นตัวเลข

ง่ายใช่มั้ย ด้วยวิธีการทั้งหมดที่ทำให้เราสามารถไปยังภาพรวมได้

ประเภทผลลัพธ์ที่แตกต่างกันของ+ประเภทตัวถูกดำเนินการ

            || undefined | null   | boolean | number | string | object |
=========================================================================
 undefined  || number    | number | number  | number | string | string | 
 null       || number    | number | number  | number | string | string | 
 boolean    || number    | number | number  | number | string | string | 
 number     || number    | number | number  | number | string | string | 
 string     || string    | string | string  | string | string | string | 
 object     || string    | string | string  | string | string | string | 

* ใช้กับ Chrome13, FF6, Opera11 และ IE9 การตรวจสอบเบราว์เซอร์และรุ่นอื่น ๆ นั้นจะเป็นแบบฝึกหัดสำหรับผู้อ่าน

หมายเหตุ:ในฐานะที่เป็นแหลมออกโดยCMSสำหรับบางกรณีของวัตถุเช่นNumber, Booleanและคนที่กำหนดเอง+ผู้ประกอบการไม่จำเป็นต้องผลิตผลสตริง มันอาจแตกต่างกันไปขึ้นอยู่กับการนำวัตถุไปใช้กับการแปลงดั้งเดิม ยกตัวอย่างเช่นvar o = { valueOf:function () { return 4; } };การประเมินผลการo + 2;ผลิต6ที่มีnumberการประเมินผลo + '2'การผลิตเป็น'42'string

หากต้องการดูวิธีสร้างตารางภาพรวมโปรดไปที่http://jsfiddle.net/1obxuc7m/


244

+ตัวดำเนินการของ JavaScript มีจุดประสงค์สองประการ: เพิ่มตัวเลขสองตัวหรือเข้าร่วมสองสาย มันไม่มีพฤติกรรมที่เฉพาะเจาะจงสำหรับอาร์เรย์ดังนั้นมันจึงแปลงเป็นสตริงแล้วจึงเข้าร่วม

หากคุณต้องการที่จะเข้าร่วมสองอาร์เรย์ในการผลิตใหม่ใช้วิธีการแทน:.concat

[1, 2].concat([3, 4]) // [1, 2, 3, 4]

ถ้าคุณต้องการเพิ่มองค์ประกอบทั้งหมดจากอาร์เรย์หนึ่งไปยังอีกอย่างมีประสิทธิภาพคุณต้องใช้วิธีการ. push :

var data = [1, 2];

// ES6+:
data.push(...[3, 4]);
// or legacy:
Array.prototype.push.apply(data, [3, 4]);

// data is now [1, 2, 3, 4]

พฤติกรรมของ+ผู้ปฏิบัติงานถูกกำหนดไว้ในECMA-262 5e มาตรา 11.6.1 :

11.6.1 ผู้ประกอบการเพิ่มเติม (+)

ตัวดำเนินการการเติมดำเนินการต่อสตริงหรือการเพิ่มตัวเลข การผลิตAdditiveExpression : AdditiveExpression + MultiplicativeExpressionได้รับการประเมินดังนี้:

  1. อนุญาตเป็นผลมาจากการประเมินlrefAdditiveExpression
  2. อนุญาตเป็นlvalGetValue(lref)
  3. อนุญาตเป็นผลมาจากการประเมินrrefMultiplicativeExpression
  4. อนุญาตเป็นrvalGetValue(rref)
  5. อนุญาตเป็นlprimToPrimitive(lval)
  6. อนุญาตเป็นrprimToPrimitive(rval)
  7. ถ้าType(lprim)เป็นStringหรือType(rprim)เป็นStringแล้ว
    1. ส่งคืนสตริงที่เป็นผลลัพธ์ของการต่อกันToString(lprim)ตามด้วยToString(rprim)
  8. กลับผลของการใช้การดำเนินการนอกเหนือไปจากและToNumber(lprim) ToNumber(rprim)ดูหมายเหตุด้านล่าง 11.6.3

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


7
+1 เนื่องจากคำตอบนี้ไม่เพียง แต่อธิบายปัญหา แต่ยังอธิบายถึงวิธีการแก้ไขให้ถูกต้องอีกด้วย
schnaader

3
มี tmi เล็กน้อยที่นี่ แต่ฉันเห็นด้วยกับ schnaader คำตอบที่ดีที่สุดอธิบายปัญหา / ข้อผิดพลาด / พฤติกรรมที่ถูกถามแล้วแสดงวิธีการสร้างผลลัพธ์ที่ต้องการ +1
matthewdunnam

1
ทำไมคุณจะใช้มากขึ้น verbose Array.prototype.push.apply(data, [3, 4])แทนdata.concat([3,4])?
evilcelery

5
@evilcelery: พวกเขาให้บริการเพื่อวัตถุประสงค์ที่แตกต่างกัน concatสร้างArray ใหม่อีกต่อไปการโทรที่ยาวขึ้นจะเป็นการเพิ่มArray ที่มีอยู่
Jeremy Banks

1
คุณสามารถใช้[].push.apply(data, [3,4])verbosity น้อยกว่าเล็กน้อย นอกจากนี้ที่รับประกันได้ว่าจะทนกับคนอื่น ๆ Arrayที่มีการเปลี่ยนแปลงค่าของ
Sam Tobin-Hochstadt

43

มันจะเพิ่มสองอาร์เรย์ราวกับว่าพวกเขาสตริง

การเป็นตัวแทนสตริงสำหรับอาร์เรย์แรกจะเป็น"1,2"และครั้งที่สองจะเป็น"3,4" ดังนั้นเมื่อ+พบสัญญาณแล้วจะไม่สามารถหาผลรวมของอาร์เรย์และต่อกันเป็นสตริงได้


ใช่นั่นคือคำอธิบายแรกและไม่ซ้ำใครที่มาพร้อมกับความคิด แต่นั่นเป็นพฤติกรรมที่แปลกมากใช่ไหม อาจจะมีบางส่วนที่มืดมิดการทำงาน / การเปลี่ยนแปลงที่ไม่ทราบ
แน่ชัด


21

ใน JavaScript ตัวดำเนินการเพิ่มไบนารี ( +) ดำเนินการทั้งการบวกเชิงตัวเลขและการต่อสตริง อย่างไรก็ตามเมื่ออาร์กิวเมนต์แรกไม่ใช่ตัวเลขหรือสตริงก็จะแปลงเป็นสตริง (ดังนั้น " 1,2") จากนั้นก็จะทำเช่นเดียวกันกับตัวที่สอง " 3,4" และต่อกันเป็น " 1,23,4"

ลองใช้วิธี "concat" ของ Arrays แทน:

var a = [1, 2];
var b = [3, 4];
a.concat(b) ; // => [1, 2, 3, 4];


14

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


14

[1,2]+[3,4] ใน JavaScript เหมือนกับการประเมิน:

new Array( [1,2] ).toString() + new Array( [3,4] ).toString();

และเพื่อแก้ปัญหาของคุณสิ่งที่ดีที่สุดคือการเพิ่มสองอาร์เรย์ในสถานที่หรือไม่สร้างอาร์เรย์ใหม่:

var a=[1,2];
var b=[3,4];
a.push.apply(a, b);

12

มันทำสิ่งที่คุณขอให้ทำ

สิ่งที่คุณกำลังเพิ่มเข้าด้วยกันคือการอ้างอิงอาร์เรย์ (ซึ่ง JS แปลงเป็นสตริง) ไม่ใช่ตัวเลขตามที่เห็น มันเหมือนกับการเพิ่มสตริงเข้าด้วยกัน: "hello " + "world"="hello world"


5
ฮิฮิมันทำในสิ่งที่ฉันถามเสมอ ปัญหาคือการถามคำถามที่ดี สิ่งที่ฉันสนใจคือการตีความ toString () ของอาร์เรย์เมื่อคุณเพิ่มพวกเขา
okeen

8

จะดีถ้าคุณสามารถโอเวอร์โหลดโอเปอเรเตอร์ใน JavaScript แต่คุณไม่สามารถ: ฉันสามารถกำหนดโอเปอร์เรเตอร์ที่กำหนดเองใน Javascript ได้ไหม คุณสามารถแฮ็กโอเปอเรเตอร์ "==" เท่านั้นซึ่งจะแปลงเป็นสตริงก่อนที่จะเปรียบเทียบ: http://blogger.xs4all.nl/peterned/archive/2009/04/01/462517.aspx


8

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


2
ตัวดำเนินการ + ไม่สามารถถือว่าตัวถูกดำเนินการเป็นสตริงได้เนื่องจาก 1 + 1 == 2 และอื่น ๆ เป็นเพราะ '+' ไม่ได้ถูกกำหนดไว้สำหรับอาร์เรย์ดังนั้นมันจึงเป็นสตริงพวกเขา
okeen

0

คำตอบบางส่วนได้อธิบายถึงวิธีการที่ผลลัพธ์ที่ไม่คาดคิด'1,23,4'เกิดขึ้น( ) และบางส่วนได้อธิบายวิธีการรับสิ่งที่พวกเขาคิดว่าจะเป็นผลลัพธ์ที่ต้องการ ( [1,2,3,4]) เช่นการต่อเรียงอาเรย์ อย่างไรก็ตามลักษณะของผลลัพธ์ที่ต้องการนั้นค่อนข้างคลุมเครือเพราะคำถามดั้งเดิมกล่าวไว้ง่ายๆว่า "ฉันต้องการเพิ่มองค์ประกอบของอาเรย์เข้าไปอีก ... " นั่นอาจหมายถึงการเรียงต่อกันอาร์เรย์ แต่มันอาจจะยังนอกจากเฉลี่ย tuple (เช่นที่นี่และที่นี่ ) คือการเพิ่มค่าสเกลาขององค์ประกอบในอาร์เรย์กับค่าสเกลาขององค์ประกอบที่สอดคล้องกันในครั้งที่สองเช่นการรวม[1,2]และการที่จะได้รับ[3,4][4,6]

สมมติว่าทั้งสองอาร์เรย์มี arity / length เหมือนกันนี่เป็นวิธีแก้ปัญหาง่ายๆ

const arr1 = [1, 2];
const arr2 = [3, 4];

const add = (a1, a2) => a1.map((e, i) => e + a2[i]);

console.log(add(arr1, arr2)); // ==> [4, 6]


-1

ผลลัพธ์อีกรายการที่ใช้เพียงแค่เครื่องหมาย "+" ง่ายๆก็คือ:

[1,2]+','+[3,4] === [1,2,3,4]

ดังนั้นสิ่งนี้ควรใช้งานได้ (แต่!):

var a=[1,2];
var b=[3,4];
a=a+','+b; // [1,2,3,4]

... แต่มันจะแปลงตัวแปร a จาก Array เป็น String! เก็บไว้ในใจ

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