2010-03-21 28 views
100

Trong Javascript mọi đối tượng có phương thức valueOf() và toString(). Tôi đã nghĩ rằng phương thức toString() đã được gọi bất cứ khi nào một chuyển đổi chuỗi được gọi, nhưng dường như nó được trumpO bởi valueOf().valueOf() vs. toString() trong Javascript

Ví dụ, mã

var x = {toString: function() {return "foo"; }, 
     valueOf: function() {return 42; }}; 
window.console.log ("x="+x); 
window.console.log ("x="+x.toString()); 

sẽ in

x=42 
x=foo 

này đập vào mắt tôi như ngược .. nếu x là một số phức tạp, ví dụ, tôi muốn valueOf() để cung cấp cho cho tôi độ lớn của nó, nhưng bất cứ khi nào tôi muốn chuyển đổi thành một chuỗi, tôi sẽ muốn một cái gì đó như "a + bi". Và tôi sẽ không muốn phải gọi toString() một cách rõ ràng trong các bối cảnh ngụ ý một chuỗi.

Đây có phải là cách thực hiện không?

+5

Bạn đã thử 'window.console.log (x);' hoặc 'alert (x);'? – Li0liQ

+4

Họ cung cấp cho "đối tượng" và "foo" tương ứng. Nội dung thú vị. – brainjam

+0

Thực ra, cảnh báo (x); cho "foo" và window.console.log (x); cung cấp "foo {}" trong Firebug và toàn bộ đối tượng trong bảng điều khiển Chrome. – brainjam

Trả lời

95

Lý do tại sao ("x =" + x) cho "x = giá trị" chứ không phải "x = chuỗi "là như sau. Khi đánh giá "+", javascript trước tiên thu thập các giá trị nguyên thủy của các toán hạng, và sau đó quyết định xem có nên áp dụng phép nối hay nối hay không, dựa trên kiểu của mỗi nguyên thủy.

Vì vậy, đây là cách bạn nghĩ rằng nó hoạt động

a + b: 
    pa = ToPrimitive(a) 
    if(pa is string) 
     return concat(pa, ToString(b)) 
    else 
     return add(pa, ToNumber(b)) 

và đây là những gì thực sự xảy ra

a + b: 
    pa = ToPrimitive(a) 
    pb = ToPrimitive(b)* 
    if(pa is string || pb is string) 
     return concat(ToString(pa), ToString(pb)) 
    else 
     return add(ToNumber(pa), ToNumber(pb)) 

Đó là, toString được áp dụng cho các kết quả của valueOf, không để đối tượng ban đầu của bạn .

Để tham khảo thêm, hãy xem phần 11.6.1 The Addition operator (+) trong Đặc tả ngôn ngữ ECMAScript.


* Khi được gọi trong bối cảnh chuỗi, ToPrimitive không invoke toString, nhưng đây không phải là trường hợp ở đây, bởi vì '+' không thực thi bất kỳ bối cảnh loại.

+2

Không nên có điều kiện trong khối "thực sự" đọc "nếu (pa là chuỗi && pb là chuỗi)"? I.e "&&" thay vì "||" ? – brainjam

+3

Tiêu chuẩn chắc chắn nói "hoặc" (xem liên kết). – user187291

+0

Vâng, bạn nói đúng. – brainjam

64

Dưới đây là một ít chi tiết hơn, trước khi tôi nhận được câu trả lời:

var x = { 
    toString: function() { return "foo"; }, 
    valueOf: function() { return 42; } 
}; 

alert(x); // foo 
"x=" + x; // "x=42" 
x + "=x"; // "42=x" 
x + "1"; // 421 
x + 1; // 43 
["x=", x].join(""); // "x=foo" 

Chức năng toStringkhông "vu cáo" bởi valueOf nói chung. Tiêu chuẩn ECMAScript thực sự trả lời câu hỏi này khá tốt. Mỗi đối tượng đều có thuộc tính [[DefaultValue]], được tính theo yêu cầu. Khi yêu cầu tài sản này, thông dịch viên cũng cung cấp một "gợi ý" cho loại giá trị mà nó mong đợi. Nếu gợi ý là String thì toString được sử dụng trước valueOf. Tuy nhiên, nếu gợi ý là Number thì valueOf sẽ được sử dụng trước tiên. Lưu ý rằng nếu chỉ có một cái, hoặc nó trả về một số nguyên thủy, nó sẽ thường gọi người kia là lựa chọn thứ hai.

Toán tử + luôn cung cấp gợi ý Number, ngay cả khi toán hạng đầu tiên là giá trị chuỗi. Mặc dù nó yêu cầu x cho biểu diễn Number của nó, vì toán hạng đầu tiên trả về một chuỗi từ [[DefaultValue]], nó nối chuỗi.

Nếu bạn muốn đảm bảo rằng toString được gọi để ghép nối chuỗi, hãy sử dụng một mảng và phương thức .join("").

(ActionScript 3.0 hơi làm thay đổi hành vi của +, tuy nhiên. Nếu một trong hai toán hạng là một String, nó sẽ đối xử với nó như một nhà điều hành chuỗi nối và sử dụng các gợi ý String khi nó gọi [[DefaultValue]]. Vì vậy, trong AS3, ví dụ này sản lượng "foo, x = foo, foo = x, foo1, 43, x = foo".)

+0

Cũng lưu ý rằng nếu 'valueOf' hoặc' toString' trả về không nguyên thủy, chúng bị bỏ qua. Nếu không tồn tại, hoặc không trả về nguyên thủy, thì một 'TypeError' được ném ra. – bcherry

+1

Cảm ơn bcherry, đây là tầm cỡ của câu trả lời tôi đã hy vọng. Nhưng không nên x + "x ="; sản lượng "42x ="? Và x + "1"; năng suất 421? Ngoài ra, bạn có URL cho phần liên quan của tiêu chuẩn ECMAScript không? – brainjam

+0

Rất tiếc, vâng. Lỗi đánh máy và lỗi khi thử nghiệm '" 1 "+ 1', tương ứng. Tôi đã chỉnh sửa bài đăng gốc. Cảm ơn :) Dưới đây là đặc tả ECMAScript 3 (pdf): http://www.mozilla.org/js/language/E262-3.pdf Bạn đang tìm kiếm '8.6.2.6 [[DefaultValue]] (gợi ý) ', nằm ở trang 35. – bcherry