Tại sao + rất tệ cho ghép?


44

Mọi người cứ nói rằng một trong những vấn đề của JavaScript là sử dụng +[ ví dụ ] để nối chuỗi. Một số người nói rằng vấn đề không được sử dụng +, đó là kiểu ép buộc [xem các bình luận từ ví dụ trước]. Nhưng các ngôn ngữ được gõ mạnh sử dụng + cho các kiểu ghép và ép buộc mà không có vấn đề gì. Ví dụ: trong C #:

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

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

Vậy tại sao việc nối chuỗi trong JavaScript lại là một vấn đề lớn như vậy?


17
tại sao nó lại bad. Ai là Everyoneai
Neal

3
Tôi nghĩ vấn đề là + là một toán tử toán học. Và rằng hàm nối của + làm lẫn lộn quá trình chuẩn đó. bởi vì trong khi "Xin chào" + số có thể hoạt động số + "Xin chào" có thể không.
SoylentGray

3
@ Neal, tôi đồng ý - Tôi muốn biết tất cả những người 'thần bí' này dường như là ai trong tất cả những câu hỏi này.
GrandmasterB

Sử dụng + để nối là OK. Sử dụng gõ động là OK. Sử dụng cả hai trong cùng một ngôn ngữ là xấu ^ H ^ H ^ H dẫn đến sự mơ hồ.
Gerry

6
@Neal - "Mọi người" sẽ là Douglas Crockford. Ông liệt kê nó là "khủng khiếp" trong cuốn sách của mình, "JavaScript: Các bộ phận tốt".
kirk.burleson

Câu trả lời:


68

Hãy xem xét đoạn mã JavaScript này:

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

Điều này sẽ đăng nhập

kết quả là 1020

Mà rất có thể không phải là những gì đã được dự định và có thể là một lỗi khó theo dõi.


4
Minh họa rất hay về vấn đề tiềm ẩn: nối chuỗi không được xác định rõ khi các kiểu (chuỗi + int + int trong ví dụ) bị lẫn lộn. Tốt hơn là có một toán tử hoàn toàn khác (như Perl's '.') Với ngữ nghĩa được xác định rõ.
Bruce Ediger

9
Hoặc cách giải quyết này Python, mà sẽ được buộc bạn phải quấn abstr(a)str(b)các cuộc gọi; nếu không, bạn nhận được một ValueError: cannot concatenate 'str' and 'int'.
Aphex

6
@FrustratedWithFormsDesigner: Thêm dấu ngoặc đơn. 'result is ' + (a + b).
Jon Purdy

18
Điều đó là, trong C # bạn có thể làm điều tương tự, và bạn sẽ nhận được kết quả tương tự - System.Console.WriteLine("result is " + 10 + 20);nhưng không ai phàn nàn về điều đó trong C #. Đó là những gì tôi không hiểu - điều gì về JavaScript khiến nó khó hiểu hơn?
cấu hình

7
@configurator: bởi vì mọi người nói C # là hoàn hảo và javascript là khủng khiếp. Cả hai đều sai, nhưng danh tiếng của một ngôn ngữ dường như vẫn còn, nhận thức được tính rất nhiều, đặc biệt là trên mạng quốc tế.
gbjbaanb

43

Khi bạn nói "xấu", bạn có nghĩa là "không chính xác" hay bạn có nghĩa là "chậm"? Đối số về việc sử dụng các toán tử toán học để thực hiện nối chuỗi được cho là một đối số "không chính xác", nhưng cũng có một lập luận được đưa ra là việc sử dụng +để thực hiện nhiều phép nối chuỗi có thể rất chậm.

Chúng ta sẽ không nói về "Hello" + numberkhi chúng ta nói về hiệu suất, chúng ta đang nói về việc xây dựng một chuỗi tương đối lớn bằng cách liên tục nối vào chuỗi đó.

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

Trong JavaScript (và C # cho vấn đề đó), chuỗi là bất biến. Chúng không bao giờ có thể được thay đổi, chỉ được thay thế bằng các chuỗi khác. Bạn có thể biết rằng combined + "hello "không trực tiếp sửa đổi combinedbiến - thao tác tạo một chuỗi mới là kết quả của việc nối hai chuỗi với nhau, nhưng sau đó bạn phải gán chuỗi mới đó cho combinedbiến nếu bạn muốn thay đổi.

Vì vậy, những gì vòng lặp này đang làm là tạo ra một triệu đối tượng chuỗi khác nhau và loại bỏ 999.999 trong số chúng. Tạo ra nhiều chuỗi liên tục tăng kích thước là không nhanh, và bây giờ trình thu gom rác có rất nhiều việc phải làm để dọn dẹp sau này.

C # có cùng một vấn đề, được giải quyết trong môi trường đó bởi lớp StringBuilder . Trong JavaScript, bạn sẽ có hiệu suất tốt hơn nhiều bằng cách xây dựng một chuỗi tất cả các chuỗi bạn muốn nối và sau đó kết hợp chúng lại với nhau một lần vào cuối, thay vì một triệu lần trong vòng lặp:

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

3
Đây là một câu trả lời tuyệt vời. Nó không chỉ trả lời câu hỏi mà còn giáo dục.
Charles Sprayberry

3
Đây hoàn toàn không phải là ý tôi, nó hoàn toàn không liên quan đến câu hỏi.
cấu hình

4
Lấy làm tiếc. Có vẻ như ít nhất 7 người đã hiểu nhầm câu hỏi của bạn từ cách nó được diễn đạt.
Joel Mueller

9
Hiệu suất lại: Có thể đây là trường hợp trong năm 2011, nhưng bây giờ phương thức toán tử + nhanh hơn nhiều so với phương thức Array.join (ít nhất là trong v8). Xem jsperf này: jsperf.com/opes-concatenation101
UpTheCalet

2
Bất kỳ ý tưởng nào về cách phương thức nối tạo một chuỗi trong nhiều phần trong một bước, thay vì kết hợp sau đó từng chuỗi một, do đó cũng tạo ra tất cả các chuỗi trung gian đó?
Angelo.Hannes

14

Sử dụng + cho cả phép cộng và phép nối không tệ, miễn là bạn chỉ có thể thêm số và chỉ nối chuỗi. Tuy nhiên, Javascript sẽ tự động chuyển đổi giữa các số và chuỗi khi cần, vì vậy bạn có:

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

Có lẽ đơn giản hơn, quá tải "+" khi có chuyển đổi loại tự động phá vỡ tính kết hợp dự kiến ​​của toán tử +:

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

2
Ngoài ra tính chất giao hoán của bổ sung bị thiếu từ ghép 3 + 5 == 5 + 3nhưng"3" + "5" != "5" + "3"
Caleth

10

Vấn đề điển hình đưa ra cho việc nối chuỗi xoay quanh một vài vấn đề:

  1. các +biểu tượng bị quá tải
  2. những lỗi đơn giản
  3. lập trình viên lười biếng

# 1

Vấn đề đầu tiên và quan trọng nhất với việc sử dụng +để nối bổ sung chuỗi là bạn đang sử dụng +để nối thêm chuỗi .

nếu bạn có các biến ab, và bạn đặt c = a + b, có một sự mơ hồ phụ thuộc vào các loại ab. Sự mơ hồ này buộc bạn phải thực hiện các bước bổ sung để giảm thiểu vấn đề:

Chuỗi concat:

c = '' + a + b;

Thêm vào:

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

Điều này đưa tôi đến điểm thứ hai của tôi

# 2

Rất dễ dàng để vô tình truyền một biến thành một chuỗi mà không nhận ra nó. Cũng dễ quên rằng đầu vào của bạn đang ở dưới dạng một chuỗi:

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

Sẽ tạo ra kết quả không trực quan vì dấu nhắc trả về giá trị chuỗi của đầu vào.

# 3

Cần gõ parseFloathoặc Number()mỗi lần tôi muốn làm thêm là tẻ nhạt và khó chịu. Tôi nghĩ bản thân mình đủ thông minh để nhớ rằng không làm rối các loại năng động của mình, nhưng điều đó không có nghĩa là tôi chưa bao giờ làm hỏng các loại năng động của mình . Sự thật của vấn đề là tôi quá lười để loại parseFloathoặc Number()mỗi lần tôi làm thêm, bởi vì tôi làm rất nhiều bổ sung.

Giải pháp?

Không phải tất cả các ngôn ngữ sử dụng +để nối chuỗi. PHP sử dụng .để nối các chuỗi, giúp phân biệt giữa khi bạn muốn thêm số và khi bạn muốn nối chuỗi.

Bất kỳ biểu tượng nào cũng có thể được sử dụng để nối chuỗi, nhưng hầu hết đã được sử dụng và tốt nhất là tránh trùng lặp. Nếu tôi có cách của mình, có lẽ tôi sẽ sử dụng _để nối các chuỗi và không cho phép các _tên biến.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.