2011-07-07 42 views
290

Tôi đang tìm một số tài liệu đọc toàn diện tốt khi Javascript truyền một cái gì đó theo giá trị và khi tham chiếu và khi sửa đổi mục đã qua ảnh hưởng đến giá trị bên ngoài một hàm và khi nào thì không. Tôi cũng quan tâm đến khi gán cho một biến khác là tham chiếu so với giá trị và liệu nó có tuân theo bất kỳ quy tắc nào khác hơn là truyền tham số hàm hay không.Javascript bằng tham chiếu và theo giá trị

Tôi đã thực hiện rất nhiều tìm kiếm và tìm thấy nhiều ví dụ cụ thể (nhiều người trong số họ ở đây trên SO) mà từ đó tôi có thể bắt đầu ghép các phần của các quy tắc thực sự, nhưng tôi chưa tìm thấy cũng bằng văn bản tài liệu mô tả tất cả.

Ngoài ra, có những cách nào trong ngôn ngữ để kiểm soát liệu nội dung nào đó có được chuyển qua tham chiếu hoặc theo giá trị không?

Dưới đây là một số loại câu hỏi tôi muốn hiểu. Đây chỉ là những ví dụ - Tôi thực sự tìm hiểu các quy tắc ngôn ngữ đi qua, không chỉ là câu trả lời cho các ví dụ cụ thể. Nhưng, dưới đây là một số ví dụ:

function f(a,b,c) { 
    a = 3; 
    b.push("foo"); 
    c.first = false; 
} 

var x = 4; 
var y = ["eeny", "miny", "mo"]; 
var z = {first: true}; 
f(x,y,z); 

Khi nào nội dung của x, y và z thay đổi ngoài phạm vi f cho tất cả các loại khác nhau?

function f() { 
    var a = ["1", "2", "3"]; 
    var b = a[1]; 
    a[1] = "4"; 
    // what is the value of b now for all possible data types that the array in "a" might hold? 
} 

function f() { 
    var a = [{yellow: "blue"}, {red: "cyan"}, {green: "magenta"}]; 
    var b = a[1]; 
    a[1].red = "tan"; 
    // what is the value of b now and why? 
    b.red = "black"; 
    // did the value of a[1].red change when I assigned to b.red? 
} 

Nếu tôi muốn tạo bản sao hoàn toàn độc lập của đối tượng (không có tham chiếu), cách tốt nhất để làm điều đó là gì?

+0

Đây là một đọc đáng giá: [Wiki: Chiến lược Đánh giá] (http://en.wikipedia.org/wiki/Evaluation_strategy). Tôi thích Gọi By Chia sẻ đối tượng để tránh nhầm lẫn như vậy và để lại Call By Reference để thực sự có nghĩa là cái gì khác, nơi nó được áp dụng. Để làm cho một "bản sao" xem xét 'jQuery.extend' (chỉ bản sao nông!) Hoặc tương tự được cung cấp bởi sự lựa chọn khung công tác của bạn. (Tôi nghĩ ECMA ed. 5 giới thiệu 'Object.clone' ...?). Một "bản sao sâu" có thể đạt được với serializing để JSON và trở lại, nơi mà một hoạt động được bảo tồn (nó có thể không phải lúc nào cũng được). Tôi chắc rằng cũng có những chức năng khác được thiết kế cho điều này :) –

+0

Tôi tin rằng jQuery.extend bây giờ (1.8.x +) hỗ trợ nhân bản sâu (sử dụng tham số boolean tùy chọn) –

+3

Trong những năm kể từ câu hỏi này, tôi đã tìm thấy nó dễ nhất để giải thích vấn đề này cho người mới dùng Javascript (đặc biệt là những người biết C/C++) bằng cách chỉ nói điều này: "Trong Javascript, các đối tượng được truyền qua con trỏ và mọi thứ khác được truyền theo giá trị." – jfriend00

Trả lời

495

hiểu biết của tôi là điều này là thực sự rất đơn giản:

  • Javascript là luôn đi qua giá trị, nhưng khi một biến tham chiếu đến một đối tượng (trong đó có mảng), "giá trị" là một tham chiếu đến đối tượng.
  • Thay đổi giá trị của biến số không bao giờ thay đổi nguyên thủy hoặc đối tượng cơ bản, nó chỉ trỏ biến vào một nguyên thủy hoặc đối tượng mới.
  • Tuy nhiên, việc thay đổi thuộc tính của một đối tượng được tham chiếu bởi một biến sẽ làm thay đổi đối tượng bên dưới.

Vì vậy, để làm việc thông qua một số ví dụ của bạn:

function f(a,b,c) { 
    // Argument a is re-assigned to a new value. 
    // The object or primitive referenced by the original a is unchanged. 
    a = 3; 
    // Calling b.push changes its properties - it adds 
    // a new property b[b.length] with the value "foo". 
    // So the object referenced by b has been changed. 
    b.push("foo"); 
    // The "first" property of argument c has been changed. 
    // So the object referenced by c has been changed (unless c is a primitive) 
    c.first = false; 
} 

var x = 4; 
var y = ["eeny", "miny", "mo"]; 
var z = {first: true}; 
f(x,y,z); 
console.log(x, y, z.first); // 4, ["eeny", "miny", "mo", "foo"], false 

Ví dụ 2:

var a = ["1", "2", {foo:"bar"}]; 
var b = a[1]; // b is now "2"; 
var c = a[2]; // c now references {foo:"bar"} 
a[1] = "4"; // a is now ["1", "4", {foo:"bar"}]; b still has the value 
       // it had at the time of assignment 
a[2] = "5"; // a is now ["1", "4", "5"]; c still has the value 
       // it had at the time of assignment, i.e. a reference to 
       // the object {foo:"bar"} 
console.log(b, c.foo); // "2" "bar" 
+51

Mặc dù về mặt kỹ thuật, tôi thích JavaScript là [Chia sẻ theo đối tượng chia sẻ] (http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing). Nó tránh nhầm lẫn và chuyển sang chế độ xem "cấp cao". –

+1

@pst - Điều đó có ý nghĩa, nhưng lưu ý các vấn đề được nêu ra trong trang Wikipedia được tham chiếu - hầu hết mọi người không sử dụng thuật ngữ và cộng đồng Java (lớn) gọi đây là "giá trị theo giá trị". Tuy nhiên, tôi đồng ý rằng nó hơi khó hiểu. – nrabinowitz

+6

Bạn đang nhắc đến "sự nhầm lẫn" nào? Với tôi "pass-by-value" là hoàn toàn rõ ràng. – MEMark

22

Vâng, Javascript luôn đi theo giá trị, nhưng trong một mảng hoặc đối tượng, giá trị là một tham chiếu đến nó, vì vậy bạn có thể 'thay đổi' nội dung.

Nhưng, tôi nghĩ bạn đã đọc nó trên SO; here bạn có các tài liệu bạn muốn:

http://snook.ca/archives/javascript/javascript_pass

+1

Mặc dù về mặt kỹ thuật, tôi thích JavaScript là [Chia sẻ theo đối tượng chia sẻ] (http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing). Nó tránh nhầm lẫn và chuyển sang chế độ xem "cấp cao". –

+0

Tôi thực hiện một fiddle để chơi xung quanh với điều này một chút: http://jsfiddle.net/tkane2000/7weKS/1/ – tkane2000

42

Javascript luôn đèo theo giá trị. Tuy nhiên, nếu bạn chuyển đối tượng vào hàm, giá trị "" thực sự là tham chiếu đến đối tượng đó, vì vậy hàm có thể sửa đổi thuộc tính của đối tượng nhưng không làm biến bên ngoài hàm trỏ tới một đối tượng khác.

Một ví dụ:

function changeParam(x, y, z) { 
    x = 3; 
    y = "new string"; 
    z["key2"] = "new"; 
    z["key3"] = "newer"; 

    z = {"new" : "object"}; 
} 

var a = 1, 
    b = "something", 
    c = {"key1" : "whatever", "key2" : "original value"}; 

changeParam(a, b, c); 

// at this point a is still 1 
// b is still "something" 
// c still points to the same object but its properties have been updated 
// so it is now {"key1" : "whatever", "key2" : "new", "key3" : "newer"} 
// c definitely doesn't point to the new object created as the last line 
// of the function with z = ... 
+4

'Array' là một đối tượng như vậy cũng sẽ thay đổi. –

9
  1. Primitive kiểu biến như chuỗi, số luôn vượt qua như đường chuyền theo giá trị.
  2. Mảng và đối tượng được truyền qua tham chiếu hoặc truyền theo giá trị dựa trên hai điều kiện này.

    • nếu bạn thay đổi giá trị của đối tượng hoặc mảng đó bằng đối tượng hoặc mảng mới thì giá trị đó sẽ vượt qua Giá trị.

      object1 = {item: "car"}; array1=[1,2,3];

    đây bạn đang gán đối tượng mới hoặc mảng đến cũ one.you không thay đổi giá trị tài sản cũ object.so nó là đi qua giá trị.

    • nếu bạn thay đổi giá trị thuộc tính của đối tượng hoặc mảng thì nó được chuyển qua tham chiếu.

      object1.item= "car"; array1[0]=9;

    đây bạn đang thay đổi một giá trị tài sản của tuổi object.you không gán đối tượng mới hoặc mảng đến cũ one.so nó là vượt qua bằng cách tham khảo.

function passVar(object1, object2, number1) { 

     object1.key1= "laptop"; 
     object2 = { 
      key2: "computer" 
     }; 
     number1 = number1 + 1; 
    } 

    var object1 = { 
     key1: "car" 
    }; 
    var object2 = { 
     key2: "bike" 
    }; 
    var number1 = 10; 

    passVar(object1, object2, number1); 
    console.log(object1.key1); 
    console.log(object2.key2); 
    console.log(number1); 

Output: - 
    laptop 
    bike 
    10 
+0

Không. Mọi thứ đều được truyền theo giá trị, luôn luôn. – newacct

+1

chỉ cần đặt trên mã trong giao diện điều khiển của bạn và xem ... giá trị đã thay đổi .. –

+10

Có một thời gian được vinh danh thuật ngữ tranh luận về việc gọi những gì Javascript không "đi qua tham chiếu". Tôi thích bước bên cạnh tranh luận và gọi những gì JS làm cho các đối tượng và mảng "vượt qua bằng con trỏ". Mảng và đối tượng luôn được truyền qua con trỏ. Nếu bạn sửa đổi những gì được truyền vào (truy cập con trỏ), bản gốc được sửa đổi. Nếu bạn gán một mảng hoặc đối tượng khác với biến con trỏ, thì bản gốc không được sửa đổi vì biến của bạn bây giờ "điểm" thành một mảng hoặc đối tượng khác. Phần lớn trong số này là một "cuộc tranh luận về thuật ngữ", bởi vì không có tranh luận về những gì thực sự xảy ra. – jfriend00

Các vấn đề liên quan