Liệu javascript sử dụng chuỗi bất biến hoặc đột biến? Tôi có cần một "trình tạo chuỗi" không?
Liệu javascript sử dụng chuỗi bất biến hoặc đột biến? Tôi có cần một "trình tạo chuỗi" không?
Câu trả lời:
Họ là bất biến. Bạn không thể thay đổi một ký tự trong một chuỗi bằng một cái gì đó như var myString = "abbdef"; myString[2] = 'c'
. Các phương thức thao tác chuỗi như trim
, slice
trả về chuỗi mới.
Theo cùng một cách, nếu bạn có hai tham chiếu đến cùng một chuỗi, việc sửa đổi một tham chiếu sẽ không ảnh hưởng đến chuỗi khác
let a = b = "hello";
a = a + " world";
// b is not affected
Tuy nhiên, tôi đã luôn nghe những gì Ash đề cập trong câu trả lời của anh ấy (rằng sử dụng Array.join nhanh hơn để nối), vì vậy tôi muốn thử nghiệm các phương pháp nối chuỗi khác nhau và trừu tượng hóa cách nhanh nhất vào StringBuilder. Tôi đã viết một số thử nghiệm để xem điều này có đúng không (không phải vậy!).
Đây là những gì tôi tin sẽ là cách nhanh nhất, mặc dù tôi cứ nghĩ rằng việc thêm một cuộc gọi phương thức có thể làm cho nó chậm hơn ...
function StringBuilder() {
this._array = [];
this._index = 0;
}
StringBuilder.prototype.append = function (str) {
this._array[this._index] = str;
this._index++;
}
StringBuilder.prototype.toString = function () {
return this._array.join('');
}
Dưới đây là các bài kiểm tra tốc độ hiệu suất. Tất cả ba người trong số họ tạo ra một chuỗi khổng lồ được tạo thành từ việc nối "Hello diggity dog"
một trăm ngàn lần thành một chuỗi trống.
Tôi đã tạo ra ba loại thử nghiệm
Array.push
vàArray.join
Array.push
, sau đó sử dụngArray.join
Sau đó, tôi đã tạo ra cùng ba bài kiểm tra bằng cách trừu tượng chúng vào StringBuilderConcat
, StringBuilderArrayPush
và StringBuilderArrayIndex
http://jsperf.com/string-concat-without-sringbuilder/5 Hãy đến đó và chạy thử nghiệm vì vậy chúng tôi có thể nhận được một mẫu tốt đẹp. Lưu ý rằng tôi đã sửa một lỗi nhỏ, vì vậy dữ liệu cho các bài kiểm tra đã bị xóa, tôi sẽ cập nhật bảng sau khi có đủ dữ liệu hiệu suất. Đi đến http://jsperf.com/string-concat-without-sringbuilder/5 cho bảng dữ liệu cũ.
Dưới đây là một số con số (Cập nhật mới nhất trong Ma5rch 2018), nếu bạn không muốn theo liên kết. Con số trên mỗi bài kiểm tra là trong 1000 thao tác / giây ( cao hơn là tốt hơn )
| Browser | Index | Push | Concat | SBIndex | SBPush | SBConcat |
---------------------------------------------------------------------------
| Chrome 71.0.3578 | 988 | 1006 | 2902 | 963 | 1008 | 2902 |
| Firefox 65 | 1979 | 1902 | 2197 | 1917 | 1873 | 1953 |
| Edge | 593 | 373 | 952 | 361 | 415 | 444 |
| Exploder 11 | 655 | 532 | 761 | 537 | 567 | 387 |
| Opera 58.0.3135 | 1135 | 1200 | 4357 | 1137 | 1188 | 4294 |
Kết quả
Ngày nay, tất cả các trình duyệt thường xanh xử lý nối chuỗi tốt. Array.join
chỉ giúp IE 11
Nhìn chung, Opera nhanh nhất, nhanh gấp 4 lần Array.join
Firefox đứng thứ hai và Array.join
chỉ chậm hơn một chút trong FF nhưng chậm hơn đáng kể (3x) trong Chrome.
Chrome đứng thứ ba nhưng chuỗi concat nhanh hơn 3 lần so với Array.join
Tạo một StringBuilder dường như không ảnh hưởng quá nhiều đến độ hoàn hảo.
Hy vọng ai đó thấy điều này hữu ích
Trường hợp kiểm tra khác nhau
Vì @RoyTinker nghĩ rằng thử nghiệm của tôi không hoàn hảo, tôi đã tạo ra một trường hợp mới không tạo ra một chuỗi lớn bằng cách nối cùng một chuỗi, nó sử dụng một ký tự khác nhau cho mỗi lần lặp. Nối chuỗi vẫn có vẻ nhanh hơn hoặc nhanh như vậy. Hãy chạy thử nghiệm.
Tôi đề nghị mọi người nên tiếp tục nghĩ đến những cách khác để kiểm tra điều này và thoải mái thêm các liên kết mới vào các trường hợp thử nghiệm khác nhau bên dưới.
join
nối chuỗi với chuỗi, do đó xây dựng mảng trước khi thử nghiệm. Tôi không nghĩ đó là gian lận nếu mục tiêu đó được hiểu (và join
liệt kê mảng bên trong, vì vậy, cũng không phải là gian lận để bỏ qua một for
vòng lặp từ join
bài kiểm tra).
Array.join
từ cuốn sách tê giác :
Trong JavaScript, các chuỗi là các đối tượng bất biến, có nghĩa là các ký tự bên trong chúng có thể không bị thay đổi và bất kỳ thao tác nào trên chuỗi thực sự tạo ra các chuỗi mới. Chuỗi được gán bởi tham chiếu, không phải theo giá trị. Nói chung, khi một đối tượng được gán bởi tham chiếu, một thay đổi được thực hiện cho đối tượng thông qua một tham chiếu sẽ hiển thị thông qua tất cả các tham chiếu khác đến đối tượng. Tuy nhiên, vì các chuỗi không thể thay đổi, bạn có thể có nhiều tham chiếu đến một đối tượng chuỗi và không lo lắng rằng giá trị chuỗi sẽ thay đổi mà bạn không biết
null
undefined
number
và boolean
. Strings được sự phân công của giá trị và không bằng cách tham khảo và được thông qua như vậy. Do đó, chuỗi không chỉ là bất biến, chúng là một giá trị . Thay đổi chuỗi "hello"
thành "world"
giống như quyết định rằng từ bây giờ số 3 là số 4 ... thật vô nghĩa.
var a = "hello";var b=a;a.x=5;console.log(a.x,b.x);
String
các đối tượng được tạo bằng hàm tạo chuỗi là các hàm bao quanh các giá trị chuỗi JavaScript. Bạn có thể truy cập giá trị chuỗi của loại được đóng hộp bằng .valueOf()
hàm - điều này cũng đúng với Number
các đối tượng và giá trị số. Điều quan trọng cần lưu ý là String
các đối tượng được tạo bằng cách sử dụng new String
không phải là chuỗi thực tế mà là trình bao bọc hoặc hộp xung quanh chuỗi. Xem es5.github.io/#x15.5.2.1 . Về cách mọi thứ chuyển đổi thành các đối tượng, hãy xem es5.github.io/#x9.9
Mẹo hiệu suất:
Nếu bạn phải nối các chuỗi lớn, đặt các phần chuỗi vào một mảng và sử dụng Array.Join()
phương thức để có được chuỗi tổng thể. Điều này có thể nhanh hơn nhiều lần để nối một số lượng lớn các chuỗi.
Không có StringBuilder
JavaScript.
Chỉ cần làm rõ cho những tâm trí đơn giản như của tôi (từ MDN ):
Bất biến là các đối tượng có trạng thái không thể thay đổi sau khi đối tượng được tạo.
Chuỗi và số là bất biến.
Bất biến có nghĩa là:
Bạn có thể đặt một điểm tên biến thành một giá trị mới, nhưng giá trị trước đó vẫn được giữ trong bộ nhớ. Do đó cần thu gom rác.
var immutableString = "Hello";
// Trong đoạn mã trên, một đối tượng mới có giá trị chuỗi được tạo.
immutableString = immutableString + "World";
// Chúng tôi hiện đang nối thêm "Thế giới" vào giá trị hiện có.
Điều này có vẻ như chúng ta đang thay đổi chuỗi 'bất biếnString', nhưng chúng ta thì không. Thay thế:
Khi nối thêm "bất biến" với giá trị chuỗi, các sự kiện sau xảy ra:
- Giá trị hiện tại của "infutableString" được lấy
- "Thế giới" được gắn vào giá trị hiện có của "bất biến"
- Giá trị kết quả sau đó được phân bổ cho một khối bộ nhớ mới
- Bây giờ đối tượng "infutableString" trỏ đến không gian bộ nhớ vừa tạo
- Không gian bộ nhớ được tạo trước đây hiện có sẵn để thu gom rác.
Giá trị loại chuỗi là bất biến, nhưng đối tượng String, được tạo bằng cách sử dụng hàm tạo String (), có thể thay đổi, bởi vì nó là một đối tượng và bạn có thể thêm các thuộc tính mới vào nó.
> var str = new String("test")
undefined
> str
[String: 'test']
> str.newProp = "some value"
'some value'
> str
{ [String: 'test'] newProp: 'some value' }
Trong khi đó, mặc dù bạn có thể thêm các thuộc tính mới, bạn không thể thay đổi các thuộc tính đã có
Ảnh chụp màn hình kiểm tra trong bảng điều khiển Chrome
Tóm lại, 1. tất cả giá trị loại chuỗi (kiểu nguyên thủy) là bất biến. 2. Đối tượng String có thể thay đổi, nhưng giá trị loại chuỗi (kiểu nguyên thủy) mà nó chứa là không thay đổi.
new String
tạo ra một trình bao bọc có thể thay đổi xung quanh một chuỗi bất biến
String
đối tượng (trình bao bọc), có nghĩa là nó không bất biến (theo mặc định; giống như bất kỳ đối tượng nào khác mà bạn có thể gọi Object.freeze
trên đó để hiển thị nó không thay đổi). Nhưng một loại giá trị chuỗi nguyên thủy, cho dù có chứa trong một String
trình bao bọc đối tượng hay không, luôn luôn là bất biến.
Về câu hỏi của bạn (trong nhận xét của bạn về câu trả lời của Ash) về StringBuilder trong ASP.NET Ajax, các chuyên gia dường như không đồng ý với câu hỏi này.
Christian Wenz nói trong cuốn sách Lập trình ASP.NET AJAX (O'Reilly) của mình rằng "cách tiếp cận này không có bất kỳ ảnh hưởng nào đến bộ nhớ (trên thực tế, việc triển khai dường như chậm hơn so với cách tiếp cận tiêu chuẩn)."
Mặt khác, Gallo và cộng sự đã nói trong cuốn sách ASP.NET AJAX in Action (Manning) của họ rằng "Khi số lượng chuỗi để nối lớn hơn, trình tạo chuỗi trở thành một đối tượng thiết yếu để tránh hiệu suất giảm rất lớn."
Tôi đoán bạn cũng cần phải làm điểm chuẩn của riêng mình và kết quả cũng có thể khác nhau giữa các trình duyệt. Tuy nhiên, ngay cả khi nó không cải thiện hiệu năng, nó vẫn có thể được coi là "hữu ích" cho các lập trình viên đã quen với việc mã hóa với StringBuilders bằng các ngôn ngữ như C # hoặc Java.
Đó là một bài viết muộn, nhưng tôi đã không tìm thấy một trích dẫn cuốn sách hay trong số các câu trả lời.
Đây là một định nghĩa ngoại trừ từ một cuốn sách đáng tin cậy:
Các chuỗi là bất biến trong ECMAScript, có nghĩa là một khi chúng được tạo, các giá trị của chúng không thể thay đổi. Để thay đổi chuỗi được giữ bởi một biến, chuỗi gốc phải bị hủy và biến chứa đầy một chuỗi khác chứa giá trị mới ... JavaScriptProf Professional JavaScript dành cho nhà phát triển web, Ed 3, tr.43
Bây giờ, câu trả lời trích dẫn đoạn trích của cuốn sách Rhino là đúng về tính bất biến của chuỗi nhưng nói sai "Chuỗi được gán bởi tham chiếu, không phải theo giá trị." (có lẽ ban đầu họ có nghĩa là đặt các từ theo cách ngược lại).
Quan niệm sai lầm "tham chiếu / giá trị" được làm rõ trong "JavaScript chuyên nghiệp", chương có tên "Giá trị nguyên thủy và tham chiếu":
Năm loại nguyên thủy ... [là]: Không xác định, Null, Boolean, Number và String. Các biến này được cho là được truy cập theo giá trị, bởi vì bạn đang thao túng giá trị thực được lưu trữ trong biến. JavaScriptProf Professional JavaScript dành cho nhà phát triển web, Ed 3, trang 85
điều đó trái ngược với các đối tượng :
Khi bạn thao tác với một đối tượng, bạn thực sự đang làm việc trên một tham chiếu đến đối tượng đó chứ không phải chính đối tượng thực tế. Vì lý do này, các giá trị như vậy được cho là được truy cập bằng cách tham chiếu. JavaScriptProf Professional JavaScript dành cho nhà phát triển web, Ed 3, trang 85
Chuỗi JavaScript thực sự là bất biến.
Chuỗi trong Javascript là bất biến