JavaScript có phải là ngôn ngữ chuyển tiếp hoặc tham chiếu giá trị không?


1404

Các kiểu nguyên thủy (số, chuỗi, v.v.) được truyền theo giá trị, nhưng các đối tượng không xác định, vì chúng có thể là cả hai giá trị được truyền (trong trường hợp chúng ta coi rằng một biến giữ một đối tượng trong thực tế là một tham chiếu đến đối tượng ) và tham chiếu qua (khi chúng ta xem xét rằng biến đối tượng giữ chính đối tượng đó).

Mặc dù cuối cùng nó không thực sự quan trọng, tôi muốn biết đâu là cách chính xác để trình bày các lập luận thông qua các quy ước. Có một đoạn trích từ đặc tả JavaScript, trong đó xác định những gì nên là ngữ nghĩa liên quan đến điều này?


2
Tôi nghĩ rằng bạn đã vô tình lật các định nghĩa của bạn về giá trị truyền qua và giá trị tham chiếu ... "truyền qua giá trị (trong trường hợp chúng tôi cho rằng một biến giữ một đối tượng trong thực tế là một tham chiếu đến đối tượng) và được truyền -by-Reference (khi chúng tôi cho rằng biến đối tượng giữ chính đối tượng đó) "
Niko Bellic

5
Đúng. Bất kể cú pháp, trong bất kỳ lệnh gọi hàm nào trong bất kỳ ngôn ngữ lập trình nào, tham chiếu qua có nghĩa là dữ liệu được liên kết với biến được truyền sẽ không được sao chép khi truyền vào hàm và do đó mọi sửa đổi được thực hiện bởi hàm cho biến đã truyền sẽ được giữ lại trong chương trình sau khi chức năng gọi kết thúc. Pass-by-value có nghĩa là dữ liệu liên quan đến biến thực sự được sao chép khi được truyền vào hàm và mọi sửa đổi được thực hiện bởi hàm đó cho biến đó sẽ bị mất khi biến đi ra khỏi phạm vi của thân hàm khi hàm trả về.
John Sonderson

5
Câu hỏi cũ này có phần độc hại vì câu trả lời được đánh giá cao của nó là không chính xác. JavaScript hoàn toàn vượt qua giá trị .
Mũi nhọn

6
@DanailNachev Thuật ngữ này rất khó hiểu. Vấn đề là, "vượt qua giá trị" và "vượt qua tham chiếu" là những thuật ngữ có trước rất nhiều tính năng ngôn ngữ lập trình hiện đại hơn. Các từ "giá trị" và "tham chiếu" đề cập cụ thể đến tham số khi nó xuất hiện trong biểu thức gọi hàm. JavaScript luôn đánh giá từng biểu thức trong danh sách tham số gọi hàm trước khi gọi hàm, vì vậy các tham số luôn là các giá trị. Phần khó hiểu là các tham chiếu đến các đối tượng là các giá trị JavaScript phổ biến. Tuy nhiên, điều đó không làm cho nó trở thành một ngôn ngữ "vượt qua tham chiếu".
Mũi nhọn

2
@DanailNachev "vượt qua tham chiếu" cụ thể có nghĩa là nếu bạn có var x=3, y=x; f(x); alert(y === x);chức năng thì f()có thể báo cáo cảnh báo falsehay không true. Trong JavaScript, điều đó là không thể, vì vậy nó không phải là tham chiếu. Thật tốt khi có thể chuyển các tham chiếu đến các đối tượng có thể sửa đổi, nhưng đó không phải là "vượt qua tham chiếu" nghĩa là gì. Như tôi đã nói, thật xấu hổ vì thuật ngữ này rất khó hiểu.
Pointy

Câu trả lời:


1598

Thật thú vị trong JavaScript. Xem xét ví dụ này:

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);
console.log(obj2.item);

Điều này tạo ra đầu ra:

10
changed
unchanged
  • Nếu obj1hoàn toàn không phải là một tài liệu tham khảo, thì việc thay đổi obj1.itemsẽ không có tác dụng gì obj1bên ngoài chức năng.
  • Nếu đối số là một tham chiếu thích hợp, thì mọi thứ sẽ thay đổi. numsẽ 100, và obj2.itemsẽ đọc "changed".

Thay vào đó, tình huống là vật phẩm được truyền vào được truyền theo giá trị. Nhưng mục được truyền theo giá trị tự nó là một tài liệu tham khảo. Về mặt kỹ thuật, điều này được gọi là chia sẻ cuộc gọi .

Trong điều kiện thực tế, điều này có nghĩa là nếu bạn thay đổi chính tham số (như với numobj2), điều đó sẽ không ảnh hưởng đến mục được đưa vào tham số. Nhưng nếu bạn thay đổi NỘI BỘ của tham số, điều đó sẽ lan truyền sao lưu (như với obj1).


32
Điều này hoàn toàn giống nhau (hoặc ít nhất là về mặt ngữ nghĩa) như C #. Đối tượng có hai loại: Giá trị (loại nguyên thủy) và Tham chiếu.
Peter Lee

53
Tôi nghĩ rằng điều này cũng được sử dụng trong Java: tham chiếu theo giá trị.
Jpnh

296
lý do thực sự là trong thay đổi, num, obj1 và obj2 là các tham chiếu. Khi bạn thay đổi thuộc itemtính của đối tượng được tham chiếu bởi obj1, bạn đang thay đổi giá trị của thuộc tính vật phẩm ban đầu được đặt thành "không thay đổi". Khi bạn gán obj2, giá trị của {item: "đã thay đổi"}, bạn đang thay đổi tham chiếu đến một đối tượng mới (ngay lập tức vượt ra khỏi phạm vi khi hàm thoát). Nó trở nên rõ ràng hơn những gì đang xảy ra nếu bạn đặt tên cho hàm params những thứ như numf, obj1f và obj2f. Sau đó, bạn thấy rằng các param đã ẩn tên var bên ngoài.
jinglểula

13
@BartoNaz Không hẳn. Những gì bạn muốn là chuyển tham chiếu theo tham chiếu, thay vì chuyển tham chiếu theo giá trị. Nhưng JavaScript luôn vượt qua tham chiếu theo giá trị, giống như nó vượt qua mọi thứ khác theo giá trị. (Để so sánh, C # có hành vi tham chiếu theo giá trị tương tự như JavaScript và Java, nhưng cho phép bạn chỉ định tham chiếu qua tham chiếu bằng reftừ khóa.) Thông thường, bạn sẽ chỉ có hàm trả về đối tượng mới và thực hiện bài tập tại điểm mà bạn gọi hàm. Ví dụ: foo = GetNewFoo();thay vìGetNewFoo(foo);
Tim Goodman

56
Mặc dù câu trả lời này là phổ biến nhất nhưng nó có thể hơi khó hiểu bởi vì nó nói "Nếu nó hoàn toàn vượt qua giá trị". JavaScript giá trị truyền qua thuần túy. Nhưng giá trị được thông qua là một tài liệu tham khảo. Điều này không bị hạn chế trong việc truyền tham số. Bạn chỉ có thể sao chép biến bằng var obj1 = { item: 'unchanged' }; var obj2 = obj1; obj2.item = 'changed';và sẽ quan sát hiệu ứng tương tự như trong ví dụ của bạn. Do đó, cá nhân tôi tham khảo câu trả lời của Tim Goodman
chiccodoro

476

Nó luôn luôn vượt qua giá trị, nhưng đối với các đối tượng, giá trị của biến là một tham chiếu. Bởi vì điều này, khi bạn vượt qua một đối tượng và thay đổi thành viên của nó , những thay đổi đó vẫn tồn tại bên ngoài chức năng. Điều này làm cho nó nhìn giống như vượt qua tham chiếu. Nhưng nếu bạn thực sự thay đổi giá trị của biến đối tượng, bạn sẽ thấy sự thay đổi đó không tồn tại, chứng tỏ nó thực sự vượt qua giá trị.

Thí dụ:

function changeObject(x) {
  x = { member: "bar" };
  console.log("in changeObject: " + x.member);
}

function changeMember(x) {
  x.member = "bar";
  console.log("in changeMember: " + x.member);
}

var x = { member: "foo" };

console.log("before changeObject: " + x.member);
changeObject(x);
console.log("after changeObject: " + x.member); /* change did not persist */

console.log("before changeMember: " + x.member);
changeMember(x);
console.log("after changeMember: " + x.member); /* change persists */

Đầu ra:

before changeObject: foo
in changeObject: bar
after changeObject: foo

before changeMember: foo
in changeMember: bar
after changeMember: bar

14
@daylight: Thật ra, bạn đã sai; nếu nó được thông qua bởi const ref cố gắng thay đổiObject sẽ gây ra lỗi, thay vì chỉ thất bại. Hãy thử gán một giá trị mới cho một tham chiếu const trong C ++ và trình biên dịch từ chối nó. Theo thuật ngữ của người dùng, đó là sự khác biệt giữa truyền theo giá trị và chuyển qua tham chiếu const.
deworde

5
@daylight: Nó không phải là ref liên tục. Trong changeObject, tôi đã thay đổi xđể chứa một tham chiếu đến đối tượng mới. x = {member:"bar"};Nhân tiện, điều này cũng tương đương với x = new Object(); x.member = "bar"; những gì tôi nói cũng đúng với C #.
Tim Goodman

2
@daylight: Đối với C #, bạn có thể thấy điều này từ bên ngoài hàm, nếu bạn sử dụng reftừ khóa, bạn có thể chuyển tham chiếu theo tham chiếu (thay vì mặc định chuyển tham chiếu theo giá trị), và sau đó thay đổi để trỏ đến a new Object() sẽ tiếp tục .
Tim Goodman

11
@adityamenon Thật khó để trả lời "tại sao", nhưng tôi sẽ lưu ý rằng các nhà thiết kế của Java và C # đã đưa ra một lựa chọn tương tự; đây không chỉ là một sự kỳ lạ của JavaScript. Thực sự, nó rất nhất quán truyền qua giá trị, điều khiến mọi người bối rối là giá trị có thể là một tài liệu tham khảo. Nó không khác nhiều so với việc chuyển một con trỏ xung quanh (theo giá trị) trong C ++ và sau đó hủy bỏ nó để đặt các thành viên. Không ai sẽ ngạc nhiên khi sự thay đổi đó vẫn tồn tại. Nhưng bởi vì những ngôn ngữ này trừu tượng hóa con trỏ và âm thầm làm hội thảo cho bạn, mọi người bị lẫn lộn.
Tim Goodman

41
Nói cách khác, điều khó hiểu ở đây không phải là pass-by-value / pass-by-Reference. Tất cả mọi thứ là vượt qua giá trị, dừng lại đầy đủ. Điều khó hiểu là bạn không thể vượt qua một đối tượng, và bạn cũng không thể lưu trữ một đối tượng trong một biến. Mỗi khi bạn nghĩ rằng bạn đang làm điều đó, bạn thực sự vượt qua hoặc lưu trữ một tham chiếu đến đối tượng đó. Nhưng khi bạn truy cập vào các thành viên của nó, sẽ có một cuộc hội thảo im lặng xảy ra, điều đó sẽ duy trì sự hư cấu rằng biến của bạn giữ đối tượng thực tế.
Tim Goodman

150

Biến không "giữ" đối tượng; nó giữ một tài liệu tham khảo. Bạn có thể gán tham chiếu đó cho một biến khác và bây giờ cả hai đều tham chiếu cùng một đối tượng. Nó luôn luôn vượt qua giá trị (ngay cả khi giá trị đó là tham chiếu ...).

Không có cách nào để thay đổi giá trị được giữ bởi một biến được truyền dưới dạng tham số, điều này có thể xảy ra nếu JavaScript được hỗ trợ chuyển qua tham chiếu.


2
Điều này làm tôi bối rối chỉ một chút. Không vượt qua một tham chiếu qua tham chiếu?

8
Tác giả có nghĩa là bằng cách chuyển một tham chiếu, bạn đang chuyển một giá trị tham chiếu (một cách khác để nghĩ về nó là truyền giá trị của địa chỉ bộ nhớ). Vì vậy, đó là lý do tại sao nếu bạn tái phân phối đối tượng, bản gốc sẽ không thay đổi, bởi vì bạn đang tạo một đối tượng mới ở một vị trí bộ nhớ khác. Nếu bạn thay đổi một thuộc tính, đối tượng ban đầu sẽ thay đổi vì bạn đã thay đổi nó tại vị trí bộ nhớ ban đầu (điều đó không được gán lại).
Huy-Anh Hoàng

113

Hai xu của tôi ... Đây là cách tôi hiểu nó. (Hãy sửa tôi nếu tôi sai)

Đã đến lúc bỏ đi tất cả những gì bạn biết về việc vượt qua giá trị / tham chiếu.

Bởi vì trong JavaScript, nó không quan trọng cho dù nó được truyền theo giá trị hay bằng tham chiếu hay bất cứ điều gì. Vấn đề là đột biến so với việc gán các tham số được truyền vào một hàm.

OK, hãy để tôi làm hết sức mình để giải thích những gì tôi muốn nói. Giả sử bạn có một vài đối tượng.

var object1 = {};
var object2 = {};

Những gì chúng ta đã làm là "gán" ... Chúng ta đã gán 2 đối tượng trống riêng biệt cho các biến "object1" và "object2".

Bây giờ, hãy nói rằng chúng tôi thích object1 hơn ... Vì vậy, chúng tôi "gán" một biến mới.

var favoriteObject = object1;

Tiếp theo, vì bất kỳ lý do gì, chúng tôi quyết định rằng chúng tôi thích đối tượng 2 hơn. Vì vậy, chúng tôi chỉ đơn giản là làm một chút phân công lại.

favoriteObject = object2;

Không có gì xảy ra với object1 hoặc object2. Chúng tôi đã không thay đổi bất kỳ dữ liệu nào cả. Tất cả những gì chúng tôi đã làm là gán lại những gì đối tượng yêu thích của chúng tôi. Điều quan trọng là phải biết rằng cả object2 và FavoritesObject đều được gán cho cùng một đối tượng. Chúng ta có thể thay đổi đối tượng đó thông qua một trong hai biến đó.

object2.name = 'Fred';
console.log(favoriteObject.name) // Logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // Logs Joe

OK, bây giờ chúng ta hãy xem các nguyên thủy như chuỗi chẳng hạn

var string1 = 'Hello world';
var string2 = 'Goodbye world';

Một lần nữa, chúng tôi chọn một yêu thích.

var favoriteString = string1;

Cả hai biến yêu thíchString và string1 của chúng tôi đều được gán cho 'Hello world'. Bây giờ, nếu chúng ta muốn thay đổi yêu thích của chúng ta thì sao ??? Chuyện gì sẽ xảy ra???

favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'

Uh oh .... chuyện gì đã xảy ra Chúng tôi không thể thay đổi chuỗi1 bằng cách thay đổi FavoritesString ... Tại sao ?? Bởi vì chúng tôi đã không thay đổi đối tượng chuỗi của chúng tôi . Tất cả những gì chúng tôi đã làm là "TÁI TẠO" biến yêu thíchString thành một chuỗi mới. Điều này về cơ bản ngắt kết nối nó khỏi chuỗi1. Trong ví dụ trước, khi chúng tôi đổi tên đối tượng của mình, chúng tôi đã không chỉ định bất cứ điều gì. (Chà, không phải cho chính biến , ... tuy nhiên, chúng tôi đã gán thuộc tính name cho một chuỗi mới.) Thay vào đó, chúng tôi chỉ đơn giản là biến đổi đối tượng giữ các kết nối giữa 2 biến và các đối tượng bên dưới. (Ngay cả khi chúng ta đã muốn sửa đổi hoặc đột biến đối tượng chuỗi bản thân, chúng tôi không thể có, bởi vì các chuỗi thực sự bất biến trong JavaScript.)

Bây giờ, chuyển sang các hàm và truyền tham số .... Khi bạn gọi hàm và truyền tham số, về cơ bản, việc bạn đang làm là "gán" cho một biến mới và nó hoạt động chính xác như khi bạn chỉ định sử dụng dấu bằng (=).

Lấy những ví dụ này.

var myString = 'hello';

// Assign to a new variable (just like when you pass to a function)
var param1 = myString;
param1 = 'world'; // Re assignment

console.log(myString); // Logs 'hello'
console.log(param1);   // Logs 'world'

Bây giờ, điều tương tự, nhưng với một chức năng

function myFunc(param1) {
    param1 = 'world';

    console.log(param1);   // Logs 'world'
}

var myString = 'hello';
// Calls myFunc and assigns param1 to myString just like param1 = myString
myFunc(myString);

console.log(myString); // logs 'hello'

OK, bây giờ chúng ta hãy đưa ra một vài ví dụ sử dụng các đối tượng thay vì ... trước tiên, không có chức năng.

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Assign to a new variable (just like when you pass to a function)
var otherObj = myObject;

// Let's mutate our object
otherObj.firstName = 'Sue'; // I guess Joe decided to be a girl

console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Sue'

// Now, let's reassign the variable
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object has no influence on the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';

Bây giờ, điều tương tự, nhưng với một cuộc gọi chức năng

function myFunc(otherObj) {

    // Let's mutate our object
    otherObj.firstName = 'Sue';
    console.log(otherObj.firstName); // Logs 'Sue'

    // Now let's re-assign
    otherObj = {
        firstName: 'Jack',
        lastName: 'Frost'
    };
    console.log(otherObj.firstName); // Logs 'Jack'

    // Again, otherObj and myObject are assigned to 2 very different objects
    // And mutating one object doesn't magically mutate the other
}

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Calls myFunc and assigns otherObj to myObject just like otherObj = myObject
myFunc(myObject);

console.log(myObject.firstName); // Logs 'Sue', just like before

OK, nếu bạn đọc qua toàn bộ bài đăng này, có lẽ bây giờ bạn đã hiểu rõ hơn về cách các hàm gọi hoạt động trong JavaScript. Không có vấn đề gì nếu một cái gì đó được thông qua bởi tham chiếu hoặc theo giá trị ... Vấn đề là sự phân công so với đột biến.

Mỗi khi bạn chuyển một biến cho một hàm, bạn sẽ "Gán" cho dù tên của biến tham số là gì, giống như nếu bạn sử dụng dấu bằng (=).

Luôn nhớ rằng dấu bằng (=) có nghĩa là gán. Luôn nhớ rằng việc truyền tham số cho hàm trong JavaScript cũng có nghĩa là gán. Chúng giống nhau và 2 biến được kết nối theo cùng một cách chính xác (nghĩa là chúng không có, trừ khi bạn tính rằng chúng được gán cho cùng một đối tượng).

Lần duy nhất "sửa đổi một biến" ảnh hưởng đến một biến khác là khi đối tượng cơ bản bị đột biến (trong trường hợp đó bạn không sửa đổi biến, nhưng chính đối tượng đó.

Không có điểm nào trong việc phân biệt giữa các đối tượng và nguyên thủy, bởi vì nó hoạt động theo cùng một cách chính xác như thể bạn không có chức năng và chỉ sử dụng dấu bằng để gán cho một biến mới.

Gotcha duy nhất là khi tên của biến bạn truyền vào hàm giống với tên của tham số hàm. Khi điều này xảy ra, bạn phải xử lý tham số bên trong hàm như thể nó là một biến hoàn toàn mới riêng tư đối với hàm (vì nó là)

function myFunc(myString) {
    // myString is private and does not affect the outer variable
    myString = 'hello';
}

var myString = 'test';
myString = myString; // Does nothing, myString is still 'test';

myFunc(myString);
console.log(myString); // Logs 'test'

2
Đối với bất kỳ lập trình viên C, hãy nghĩ về char *. foo(char *a){a="hello";} không làm gì cả, nhưng nếu bạn thực hiện foo(char *a){a[0]='h';a[1]='i';a[2]=0;}thì nó bị thay đổi bên ngoài vì alà một vị trí bộ nhớ được truyền bởi giá trị tham chiếu một chuỗi (mảng char). Truyền các cấu trúc (tương tự như các đối tượng js) theo giá trị trong C được cho phép, nhưng không được khuyến nghị. JavaScript chỉ đơn giản là thực thi các thực tiễn tốt nhất này và che giấu hành trình không cần thiết và thường không mong muốn ... và nó chắc chắn giúp bạn đọc dễ dàng hơn.
Technosaurus

2
Điều này là chính xác - các thuật ngữ pass-by-valuepass-by-Reference có ý nghĩa trong thiết kế ngôn ngữ lập trình và những ý nghĩa đó hoàn toàn không liên quan gì đến đột biến đối tượng. Đó là tất cả về cách các tham số chức năng hoạt động.
Pointy

2
Bây giờ tôi đã hiểu rằng obj1 = obj2 có nghĩa là cả obj1 và obj2 hiện đang trỏ đến cùng một vị trí tham chiếu và nếu tôi sửa đổi các phần bên trong của obj2, tham chiếu obj1 sẽ hiển thị cùng một phần bên trong. Làm cách nào để sao chép một đối tượng sao cho khi tôi thực hiện source = { "id":"1"}; copy = source /*this is wrong*/; copy.id="2"nguồn đó vẫn là {"id": "1"}?
Machtyn

1
Tôi đã đăng một câu trả lời khác với các định nghĩa truyền thống để hy vọng giảm sự nhầm lẫn. Các định nghĩa truyền thống về "pass-by-value" và "pass-by-Reference" đã được xác định trở lại trong ngày của các con trỏ bộ nhớ trước khi hội thảo tự động. Hoàn toàn hiểu rõ rằng giá trị của một biến đối tượng thực sự là vị trí con trỏ bộ nhớ, không phải đối tượng. Mặc dù cuộc thảo luận của bạn về chuyển nhượng và đột biến có lẽ hữu ích, nhưng không cần thiết phải loại bỏ các thuật ngữ truyền thống cũng như các định nghĩa của chúng. Đột biến, gán, chuyển qua giá trị, chuyển qua tham chiếu, v.v. không được mâu thuẫn với nhau.
C Perkins

"Số" có phải là "bất biến" không?
ebram khalil

72

Hãy xem xét những điều sau đây:

  1. Các biến là con trỏ tới các giá trị trong bộ nhớ.
  2. Việc gán lại một biến chỉ đơn thuần là trỏ con trỏ vào một giá trị mới.
  3. Việc chỉ định lại một biến sẽ không bao giờ ảnh hưởng đến các biến khác đang trỏ đến cùng một đối tượng

Vì vậy, hãy quên "vượt qua tham chiếu / giá trị" đừng bị treo lên "vượt qua tham chiếu / giá trị" bởi vì:

  1. Các thuật ngữ chỉ được sử dụng để mô tả hành vi của một ngôn ngữ, không nhất thiết phải thực hiện cơ bản thực tế. Do sự trừu tượng này, các chi tiết quan trọng cần thiết cho một lời giải thích hợp lý đã bị mất, điều này chắc chắn dẫn đến tình trạng hiện tại khi một thuật ngữ duy nhất không mô tả đầy đủ hành vi thực tế và thông tin bổ sung phải được cung cấp
  2. Các khái niệm này ban đầu không được xác định với mục đích mô tả javascript và vì vậy tôi không cảm thấy bắt buộc phải sử dụng chúng khi chúng chỉ làm tăng thêm sự nhầm lẫn.

Để trả lời câu hỏi của bạn: con trỏ được thông qua.


// code
var obj = {
    name: 'Fred',
    num: 1
};

// illustration
               'Fred'
              /
             /
(obj) ---- {}
             \
              \
               1


// code
obj.name = 'George';


// illustration
                 'Fred'


(obj) ---- {} ----- 'George'
             \
              \
               1


// code
obj = {};

// illustration
                 'Fred'


(obj)      {} ----- 'George'
  |          \
  |           \
 { }            1


// code
var obj = {
    text: 'Hello world!'
};

/* function parameters get their own pointer to 
 * the arguments that are passed in, just like any other variable */
someFunc(obj);


// illustration
(caller scope)        (someFunc scope)
           \             /
            \           /
             \         /
              \       /
               \     /
                 { }
                  |
                  |
                  |
            'Hello world'

Một số ý kiến ​​cuối cùng:

  • Thật hấp dẫn khi nghĩ rằng người nguyên thủy được thi hành bởi các quy tắc đặc biệt trong khi các đối tượng thì không, nhưng nguyên thủy chỉ đơn giản là sự kết thúc của chuỗi con trỏ.
  • Để làm ví dụ cuối cùng, hãy xem xét tại sao một nỗ lực chung để xóa một mảng không hoạt động như mong đợi.


var a = [1,2];
var b = a;

a = [];
console.log(b); // [1,2]
// doesn't work because `b` is still pointing at the original array

Câu hỏi tiếp theo để có thêm tín dụng;) Làm thế nào để thu gom rác hoạt động? Nếu tôi xoay vòng một biến qua một triệu {'George', 1}giá trị, nhưng chỉ sử dụng một trong số chúng tại một thời điểm, thì các biến khác được quản lý như thế nào? Và điều gì xảy ra khi tôi gán một biến cho giá trị của biến khác? Sau đó tôi có chỉ vào một con trỏ hay chỉ vào điểm của toán hạng bên phải không? Có var myExistingVar = {"blah", 42}; var obj = myExistingVar;kết quả trong việc objchỉ đến {"blah", 42}, hoặc đến myExistingVar?
Michael Hoffmann

@MichaelHoffmann Những câu hỏi này xứng đáng với câu hỏi SO của riêng họ và có lẽ đã được trả lời tốt hơn tôi có thể quản lý. Điều đó đang được nói, 1)tôi đã chạy một cấu hình bộ nhớ trong các công cụ phát triển trình duyệt cho một chức năng vòng lặp như chức năng bạn đã mô tả và thấy các đột biến trong việc sử dụng bộ nhớ trong suốt quá trình lặp. Điều này dường như chỉ ra rằng các đối tượng giống hệt nhau mới thực sự được tạo ra trong mỗi lần lặp của vòng lặp. Khi những chiếc gai đột ngột rơi xuống, người thu gom rác chỉ dọn sạch một nhóm những vật thể không sử dụng này.
geg

1
@MichaelHoffmann 2)Về một cái gì đó như var a = b, javascript không cung cấp cơ chế sử dụng con trỏ và do đó, một biến không bao giờ có thể trỏ đến một con trỏ (như bạn có thể trong C), mặc dù chắc chắn công cụ javascript sử dụng chúng. Vì vậy, ... var a = bsẽ chỉ a"đến điểm của toán hạng bên phải"
geg

Tôi đã hỏi câu hỏi số 1 ở đây (cụ thể là về Chrome vì cách triển khai có thể khác nhau ở mọi trình duyệt) stackoverflow.com/q/42778439/539997 và tôi vẫn đang cố gắng nghĩ cách đặt câu hỏi số 2. Bất kỳ trợ giúp được đánh giá cao.
Michael Hoffmann

1
Không cần phải quên "vượt qua tham chiếu / giá trị" ! Những thuật ngữ này có ý nghĩa lịch sử mô tả chính xác những gì bạn cố gắng mô tả. Nếu chúng ta vứt bỏ các thuật ngữ và định nghĩa lịch sử và trở nên quá lười biếng để tìm hiểu ý nghĩa ban đầu của chúng, thì chúng ta sẽ mất khả năng giao tiếp hiệu quả giữa các thế hệ. Sẽ không có cách nào tốt để thảo luận về sự khác biệt giữa các ngôn ngữ và hệ thống khác nhau. Thay vào đó, các lập trình viên mới cần học và hiểu các thuật ngữ truyền thống và lý do và chúng đến từ đâu. Nếu không, chúng ta cùng nhau mất kiến ​​thức và hiểu biết.
C Perkins

24

Một đối tượng bên ngoài một chức năng được truyền vào một chức năng bằng cách đưa ra một tham chiếu đến đối tượng bên ngoài.

Khi bạn sử dụng tham chiếu đó để thao tác đối tượng của nó, đối tượng bên ngoài sẽ bị ảnh hưởng. Tuy nhiên, nếu bên trong hàm bạn quyết định trỏ tham chiếu đến một thứ khác, bạn hoàn toàn không ảnh hưởng đến đối tượng, bởi vì tất cả những gì bạn đã làm là chuyển hướng tham chiếu đến một thứ khác.


20

Hãy nghĩ về nó như thế này: Nó luôn luôn vượt qua giá trị. Tuy nhiên, giá trị của một đối tượng không phải là chính đối tượng đó, mà là một tham chiếu đến đối tượng đó.

Đây là một ví dụ, truyền một số (một kiểu nguyên thủy)

function changePrimitive(val) {
    // At this point there are two '10's in memory.
    // Changing one won't affect the other
    val = val * 10;
}
var x = 10;
changePrimitive(x);
// x === 10

Lặp lại điều này với một đối tượng mang lại kết quả khác nhau:

function changeObject(obj) {
    // At this point there are two references (x and obj) in memory,
    // but these both point to the same object.
    // changing the object will change the underlying object that
    // x and obj both hold a reference to.
    obj.val = obj.val * 10;
}
var x = { val: 10 };
changeObject(x);
// x === { val: 100 }

Một ví dụ nữa:

function changeObject(obj) {
    // Again there are two references (x and obj) in memory,
    // these both point to the same object.
    // now we create a completely new object and assign it.
    // obj's reference now points to the new object.
    // x's reference doesn't change.
    obj = { val: 100 };
}
var x = { val: 10 };
changeObject(x);
// x === { val: 10}

19

Một lời giải thích rất chi tiết về sao chép, vượt qua và so sánh theo giá trị và bằng cách tham khảo nằm trong chương này của cuốn sách "JavaScript: The Definitive Guide" .

Trước khi chúng ta rời khỏi chủ đề thao túng các đối tượng và mảng bằng cách tham chiếu, chúng ta cần làm rõ một điểm của danh pháp.

Cụm từ "vượt qua tham chiếu" có thể có một số ý nghĩa. Đối với một số độc giả, cụm từ đề cập đến một kỹ thuật gọi hàm cho phép một hàm gán các giá trị mới cho các đối số của nó và để các giá trị được sửa đổi đó hiển thị bên ngoài hàm. Đây không phải là cách thuật ngữ được sử dụng trong cuốn sách này.

Ở đây, chúng tôi chỉ đơn giản là tham chiếu đến một đối tượng hoặc mảng - chứ không phải chính đối tượng - được truyền cho một hàm. Một hàm có thể sử dụng tham chiếu để sửa đổi các thuộc tính của đối tượng hoặc các phần tử của mảng. Nhưng nếu hàm ghi đè tham chiếu bằng tham chiếu đến một đối tượng hoặc mảng mới, thì sự sửa đổi đó không thể nhìn thấy bên ngoài hàm.

Người đọc quen thuộc với ý nghĩa khác của thuật ngữ này có thể thích nói rằng các đối tượng và mảng được truyền theo giá trị, nhưng giá trị được truyền thực sự là một tham chiếu chứ không phải là chính đối tượng.


Wow, điều này là vô cùng khó hiểu. Ai trong tâm trí của họ sẽ định nghĩa một thuật ngữ được thiết lập tốt có nghĩa là ngược lại chính xác và sau đó sử dụng nó theo cách đó? Không có thắc mắc rất nhiều câu trả lời ở đây về câu hỏi này rất bối rối.
Jörg W Mittag

16

JavaScript luôn luôn là giá trị truyền qua ; tất cả mọi thứ là loại giá trị.

Các đối tượng là các giá trị và các hàm thành viên của các đối tượng là các giá trị (hãy nhớ rằng các hàm là các đối tượng hạng nhất trong JavaScript). Ngoài ra, liên quan đến khái niệm rằng mọi thứ trong JavaScript là một đối tượng ; cái này sai. Chuỗi, ký hiệu, số, booleans, null và không xác định là nguyên thủy .

Đôi khi, họ có thể tận dụng một số chức năng và thuộc tính thành viên được kế thừa từ các nguyên mẫu cơ sở của họ, nhưng điều này chỉ để thuận tiện. Điều đó không có nghĩa là bản thân chúng là đối tượng. Hãy thử làm như sau để tham khảo:

x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);

Trong cả hai cảnh báo, bạn sẽ tìm thấy giá trị không xác định.


12
-1, không phải lúc nào cũng vượt qua giá trị. Từ MDC: "Nếu bạn truyền một đối tượng (tức là giá trị không nguyên thủy, chẳng hạn như Mảng hoặc đối tượng do người dùng xác định) làm tham số, tham chiếu đến đối tượng được truyền cho hàm."
Nick

37
@Nick: Nó luôn luôn vượt qua giá trị. Giai đoạn = Stage. Một tham chiếu đến đối tượng được truyền theo giá trị cho hàm. Đó không phải là thông qua tham khảo. "Truyền bằng tham chiếu" gần như có thể được coi là truyền chính biến, thay vì giá trị của nó; bất kỳ thay đổi nào mà hàm thực hiện đối với đối số (bao gồm thay thế nó bằng một đối tượng khác hoàn toàn!) sẽ được phản ánh trong trình gọi. Đó là bit cuối cùng không thể có trong JS, bởi vì JS không chuyển qua tham chiếu - nó chuyển các tham chiếu theo giá trị. Sự khác biệt là tinh tế, nhưng khá quan trọng để hiểu những hạn chế của nó.
cHao

1
Đối với các trình xếp chồng trong tương lai ... Về tài liệu tham khảo này của bạn: x = "teste"; x.foo = 12;vv Chỉ vì một tài sản không tồn tại lâu, điều đó không có nghĩa đó không phải là một đối tượng. Như MDN nói: Trong JavaScript, hầu hết mọi thứ đều là một đối tượng. Tất cả các kiểu nguyên thủy trừ null và không xác định được coi là các đối tượng. Chúng có thể được gán các thuộc tính (thuộc tính được gán của một số loại không liên tục) và chúng có tất cả các đặc tính của các đối tượng. liên kết
slacktracer

9
MDN là một wiki do người dùng chỉnh sửa và nó sai ở đó. Tham chiếu quy phạm là ECMA-262. Xem S. 8 "Loại đặc tả tham chiếu", giải thích cách giải quyết các tham chiếu, và 8.12,5 ["Đặt]]", được sử dụng để giải thích AssignExpression cho một tham chiếu và, để ghép đối tượng 9,9 ToObject. Đối với các giá trị nguyên thủy, Michael đã giải thích ToObject làm gì, như trong đặc tả. Nhưng cũng thấy s. 4.3.2 giá trị nguyên thủy.
Garrett

1
@WonderLand: Không, anh ấy không. Những người chưa bao giờ có thể vượt qua tham chiếu có thể không bao giờ hiểu được sự khác biệt giữa chuyển qua tham chiếu và chuyển tham chiếu theo giá trị. Nhưng họ ở đó, và họ quan trọng. Tôi không quan tâm đến việc thông tin sai cho mọi người chỉ vì nó nghe có vẻ dễ dàng hơn.
cHao

12

Trong JavaScript, loại giá trị chỉ kiểm soát xem giá trị đó sẽ được chỉ định bằng bản sao giá trị hay bản sao tham chiếu .

Các giá trị nguyên thủy luôn được gán / truyền bằng giá trị sao chép :

  • null
  • undefined
  • chuỗi
  • con số
  • boolean
  • biểu tượng trong ES6

Giá trị hợp chất luôn được gán / chuyển bằng bản sao tham chiếu

  • các đối tượng
  • mảng
  • chức năng

Ví dụ

var a = 2;
var b = a; // `b` is always a copy of the value in `a`
b++;
a; // 2
b; // 3

var c = [1,2,3];
var d = c; // `d` is a reference to the shared `[1,2,3]` value
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]

Trong đoạn trích trên, vì 2là một nguyên hàm vô hướng, agiữ một bản sao ban đầu của giá trị đó và bđược gán một bản sao khác của giá trị. Khi thay đổi b, bạn không thể thay đổi giá trị tronga .

Nhưng cả hai cdlà các tham chiếu riêng biệt cho cùng một giá trị được chia sẻ [1,2,3], đó là một giá trị gộp. Điều quan trọng cần lưu ý là không choặc không d"sở hữu" [1,2,3]giá trị - cả hai chỉ là các tham chiếu ngang hàng với giá trị. Vì vậy, khi sử dụng một trong hai tham chiếu để sửa đổi ( .push(4)) arraychính giá trị được chia sẻ thực tế , nó chỉ ảnh hưởng đến một giá trị được chia sẻ và cả hai tham chiếu sẽ tham chiếu giá trị mới được sửa đổi [1,2,3,4].

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

// later
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]

Khi chúng tôi thực hiện nhiệm vụ b = [4,5,6], chúng tôi hoàn toàn không làm gì để ảnh hưởng đến nơi avẫn đang tham chiếu ( [1,2,3]). Để làm điều đó, bsẽ phải là một con trỏ athay vì tham chiếu đến array- nhưng không có khả năng nào như vậy tồn tại trong JS!

function foo(x) {
    x.push( 4 );
    x; // [1,2,3,4]

    // later
    x = [4,5,6];
    x.push( 7 );
    x; // [4,5,6,7]
}

var a = [1,2,3];

foo( a );

a; // [1,2,3,4]  not  [4,5,6,7]

Khi chúng ta truyền vào đối số a, nó gán một bản sao của atham chiếu x. xalà các tham chiếu riêng biệt chỉ vào cùng một [1,2,3]giá trị. Bây giờ, bên trong hàm, chúng ta có thể sử dụng tham chiếu đó để tự biến đổi giá trị ( push(4)). Nhưng khi chúng tôi thực hiện chuyển nhượng x = [4,5,6], điều này sẽ không ảnh hưởng đến việc tham chiếu ban đầu ađang trỏ đến đâu - vẫn chỉ vào [1,2,3,4]giá trị (hiện đã được sửa đổi) .

Để truyền một cách hiệu quả một giá trị ghép (như một array) bằng cách sao chép giá trị, bạn cần tạo một bản sao của nó theo cách thủ công, để tham chiếu được truyền không chỉ đến bản gốc. Ví dụ:

foo( a.slice() );

Giá trị hợp chất (đối tượng, mảng, v.v.) có thể được truyền bằng bản sao tham chiếu

function foo(wrapper) {
    wrapper.a = 42;
}

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

Ở đây, objhoạt động như một trình bao bọc cho thuộc tính nguyên thủy vô hướng a. Khi được chuyển đến foo(..), một bản sao của objtham chiếu được truyền vào và được đặt thành wrappertham số. Bây giờ chúng ta có thể sử dụng wrappertham chiếu để truy cập đối tượng chia sẻ và cập nhật thuộc tính của nó. Sau khi chức năng kết thúc, obj.asẽ thấy giá trị cập nhật 42.

Nguồn


Trước tiên, bạn nêu "Giá trị hợp chất luôn được gán / chuyển bằng bản sao tham chiếu" và sau đó bạn nêu "gán một bản sao của tham chiếu cho x". Trong trường hợp bạn gọi là "giá trị ghép", giá trị biến thực tế là tham chiếu (tức là con trỏ bộ nhớ). Như bạn đã giải thích, tham chiếu được sao chép ... vì vậy giá trị biến được sao chép , một lần nữa nhấn mạnh rằng TÀI LIỆU THAM KHẢO LÀ GIÁ TRỊ. Điều đó có nghĩa là JavaScript là giá trị truyền cho tất cả các loại. Pass-by-value có nghĩa là truyền một bản sao của giá trị biến. Nó không quan trọng rằng giá trị là một tham chiếu đến một đối tượng / mảng.
C Perkins

Bạn giới thiệu thuật ngữ mới (giá trị sao chép / tham chiếu-bản sao) và điều đó chỉ làm cho mọi thứ phức tạp hơn. Chỉ có bản sao, thời gian. Nếu bạn vượt qua một nguyên thủy, bạn đã truyền một bản sao của dữ liệu nguyên thủy thực tế, nếu bạn vượt qua một đối tượng, bạn đã chuyển một bản sao của vị trí bộ nhớ của đối tượng. Đó là tất cả những gì bạn cần nói. Bất cứ điều gì nhiều hơn chỉ làm mọi người bối rối hơn.
Scott Marcus

9

tốt, đó là về 'hiệu suất' và 'tốc độ' và trong từ đơn giản là 'quản lý bộ nhớ' trong ngôn ngữ lập trình.

trong javascript, chúng ta có thể đặt các giá trị thành hai lớp: type1 - objectstype2 - tất cả các loại giá trị khác như string& boolean& etc

nếu bạn tưởng tượng bộ nhớ như hình vuông bên dưới mà trong mỗi một trong số chúng chỉ có thể lưu một giá trị type2:

nhập mô tả hình ảnh ở đây

mỗi loại 2 giá trị (màu xanh lá cây) là một hình vuông trong khi giá trị loại1 (màu xanh) là một nhóm trong số chúng :

nhập mô tả hình ảnh ở đây

vấn đề là nếu bạn muốn chỉ ra một giá trị type2, địa chỉ là đơn giản nhưng nếu bạn muốn làm điều tương tự cho giá trị type1 thì không dễ chút nào! :

nhập mô tả hình ảnh ở đây

và trong một câu chuyện phức tạp hơn:

nhập mô tả hình ảnh ở đây

vì vậy ở đây tài liệu tham khảo có thể giải cứu chúng ta: nhập mô tả hình ảnh ở đây

trong khi mũi tên màu xanh lá cây ở đây là một biến điển hình, thì màu tím là biến đối tượng, vì mũi tên màu xanh lá cây (biến điển hình) chỉ có một nhiệm vụ (và đó là một giá trị điển hình), chúng ta không cần tách giá trị của nó khỏi vì vậy chúng tôi di chuyển mũi tên màu xanh lá cây với giá trị của bất cứ nơi nào nó đi và trong tất cả các nhiệm vụ, chức năng và vv ...

nhưng chúng ta không thể làm điều tương tự với mũi tên màu tím, chúng ta có thể muốn di chuyển tế bào 'john' ở đây hoặc nhiều thứ khác ..., vì vậy mũi tên màu tím sẽ bám vào vị trí của nó và chỉ những mũi tên điển hình được gán cho nó sẽ di chuyển ...

một tình huống rất khó hiểu là bạn không thể nhận ra biến tham chiếu của mình thay đổi như thế nào, hãy xem một ví dụ rất hay:

let arr = [1, 2, 3, 4, 5]; //arr is an object now and a purple arrow is indicating it
let obj2 = arr; // now, obj2 is another purple arrow that is indicating the value of arr obj
let obj3 = ['a', 'b', 'c'];
obj2.push(6); // first pic below - making a new hand for the blue circle to point the 6
//obj2 = [1, 2, 3, 4, 5, 6]
//arr = [1, 2, 3, 4, 5, 6]
//we changed the blue circle object value (type1-value) and due to arr and obj2 are indicating that so both of them changed
obj2 = obj3; //next pic below - changing the direction of obj2 array from blue circle to orange circle so obj2 is no more [1,2,3,4,5,6] and it's no more about changing anything in it but we completely changed its direction and now obj2 is pointing to obj3
//obj2 = ['a', 'b', 'c'];
//obj3 = ['a', 'b', 'c'];

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây


8

Đây là một chút giải thích cho việc vượt qua theo giá trị và chuyển qua tham chiếu (JavaScript). Trong khái niệm này, họ đang nói về việc truyền biến bằng tham chiếu và truyền biến theo tham chiếu.

Truyền theo giá trị (kiểu nguyên thủy)

var a = 3;
var b = a;

console.log(a); // a = 3
console.log(b); // b = 3

a=4;
console.log(a); // a = 4
console.log(b); // b = 3
  • áp dụng cho tất cả các kiểu nguyên thủy trong JavaScript (chuỗi, số, Boolean, không xác định và null).
  • a được cấp phát bộ nhớ (giả sử 0x001) và b tạo bản sao của giá trị trong bộ nhớ (giả sử 0x002).
  • Vì vậy, việc thay đổi giá trị của một biến không ảnh hưởng đến biến khác, vì cả hai đều nằm ở hai vị trí khác nhau.

Truyền bằng tham chiếu (đối tượng)

var c = { "name" : "john" };
var d = c;

console.log(c); // { "name" : "john" }
console.log(d); // { "name" : "john" }

c.name = "doe";

console.log(c); // { "name" : "doe" }
console.log(d); // { "name" : "doe" }
  • Công cụ JavaScript gán đối tượng cho biến cvà nó trỏ đến một số bộ nhớ, giả sử (0x012).
  • Khi d = c, trong bước này dtrỏ đến cùng một vị trí (0x012).
  • Thay đổi giá trị của bất kỳ thay đổi giá trị cho cả hai biến.
  • Chức năng là đối tượng

Trường hợp đặc biệt, vượt qua tham chiếu (đối tượng)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
  • Toán tử bằng (=) thiết lập không gian bộ nhớ hoặc địa chỉ mới

Trong trường hợp được gọi là trường hợp đặc biệt của bạn, nó không phải là toán tử gán mà gây ra sự phân bổ không gian bộ nhớ, nó là chính đối tượng theo nghĩa đen . Ký hiệu khung curley gây ra việc tạo ra một đối tượng mới. Thuộc tính cđược đặt thành một bản sao của tham chiếu của đối tượng mới.
georgeawg

6

chia sẻ những gì tôi biết về tài liệu tham khảo trong JavaScript

Trong JavaScript, khi gán một đối tượng cho một biến, giá trị được gán cho biến đó là một tham chiếu đến đối tượng:

var a = {
  a: 1,
  b: 2,
  c: 3
};
var b = a;

// b.c is referencing to a.c value
console.log(b.c) // Output: 3
// Changing value of b.c
b.c = 4
// Also changes the value of a.c
console.log(a.c) // Output: 4


1
Đây là một câu trả lời quá đơn giản mà không nói gì mà những câu trả lời trước đó không giải thích rõ hơn. Tôi bối rối về lý do tại sao bạn gọi ra mảng như một trường hợp đặc biệt.
Quentin

1
"Các đối tượng được lưu trữ dưới dạng tham chiếu " là sai lệch. Điều tôi nghĩ bạn muốn nói là khi gán một đối tượng cho một biến, giá trị được gán cho biến đó là một tham chiếu đến đối tượng.
RobG

điều này không giải quyết vấn đề cập nhật một đối tượng bên trong hàm không cập nhật đối tượng bên ngoài hàm. Đó là toàn bộ bức tranh nơi nó dường như hoạt động như các giá trị thay vì tham chiếu. Do đó -1
amaster

@amaster Cảm ơn bạn đã chỉ ra điều đó! Bạn có thể đề nghị một chỉnh sửa, xin vui lòng?
xameeramir

Haha, tôi đã thử ... chỉnh sửa đề xuất của tôi đã thay đổi quá nhiều amd không được phép
amaster

4

Ngữ nghĩa !! Đặt định nghĩa cụ thể sẽ nhất thiết làm cho một số câu trả lời và nhận xét không tương thích vì chúng không mô tả cùng một điều ngay cả khi sử dụng cùng một từ và cụm từ, nhưng điều quan trọng là phải vượt qua sự nhầm lẫn (đặc biệt đối với các lập trình viên mới).

Trước hết, có nhiều mức độ trừu tượng mà dường như không phải ai cũng nắm bắt được. Các lập trình viên mới hơn đã học về ngôn ngữ thế hệ thứ 4 hoặc thứ 5 có thể gặp khó khăn trong việc tập trung vào các khái niệm quen thuộc với lắp ráp hoặc lập trình viên C không được các con trỏ chuyển sang con trỏ tới con trỏ. Tham chiếu qua không chỉ đơn giản là khả năng thay đổi đối tượng được tham chiếu bằng biến tham số hàm.

Biến : Khái niệm kết hợp của một biểu tượng tham chiếu một giá trị tại một vị trí cụ thể trong bộ nhớ. Thuật ngữ này thường quá tải để được sử dụng một mình trong việc thảo luận chi tiết.

Ký hiệu : Chuỗi văn bản được sử dụng để chỉ biến (tức là tên biến).

Giá trị : Các bit cụ thể được lưu trữ trong bộ nhớ và được tham chiếu bằng ký hiệu của biến.

Vị trí bộ nhớ : Nơi lưu trữ giá trị của một biến. (Bản thân vị trí được thể hiện bằng một số tách biệt với giá trị được lưu trữ tại vị trí.)

Tham số hàm : Biến được khai báo trong định nghĩa hàm, được sử dụng để tham chiếu các biến được truyền cho hàm.

Đối số chức năng : Biến bên ngoài chức năng được người gọi chuyển đến chức năng.

Biến đối tượng : Biến có giá trị cơ bản cơ bản không phải là "đối tượng", thay vào đó giá trị của nó là một con trỏ (giá trị vị trí bộ nhớ) đến một vị trí khác trong bộ nhớ nơi lưu trữ dữ liệu thực tế của đối tượng. Trong hầu hết các ngôn ngữ thế hệ cao hơn, khía cạnh "con trỏ" được ẩn một cách hiệu quả bằng cách hủy tham chiếu tự động trong các ngữ cảnh khác nhau.

Biến nguyên thủy : Biến có giá trị IS là giá trị thực. Ngay cả khái niệm này có thể phức tạp bởi tự động đấm bốc và bối cảnh giống như đối tượng của các ngôn ngữ khác nhau, nhưng ý tưởng chung là giá trị của biến là giá trị thực được biểu thị bằng ký hiệu của biến chứ không phải là con trỏ đến vị trí bộ nhớ khác.

Các đối số chức năng và tham số không giống nhau. Ngoài ra, giá trị của một biến không phải là đối tượng của biến (như đã được chỉ ra bởi nhiều người khác nhau, nhưng dường như bị bỏ qua). Những sự phân biệt này là rất quan trọng để hiểu đúng.

Truyền qua giá trị hoặc Gọi bằng cách chia sẻ (đối với các đối tượng): Giá trị của đối số hàm được sao chép vào một vị trí bộ nhớ khác được tham chiếu bởi ký hiệu tham số của hàm (bất kể nó nằm trên ngăn xếp hay đống). Nói cách khác, tham số hàm nhận được một bản sao của giá trị của đối số đã qua ... VÀ (quan trọng) giá trị của đối số KHÔNG BAO GIỜ ĐƯỢC CẬP NHẬT / THAY ĐỔI / THAY ĐỔI bởi chức năng gọi. Hãy nhớ rằng, giá trị của một biến đối tượng KHÔNG phải là chính đối tượng, mà nó là con trỏ tới đối tượng, do đó, việc chuyển một biến đối tượng theo giá trị sẽ sao chép con trỏ vào biến tham số hàm. Giá trị của tham số hàm trỏ đến cùng một đối tượng trong bộ nhớ. Dữ liệu đối tượng có thể được thay đổi trực tiếp thông qua tham số hàm, NHƯNG giá trị của đối số hàm KHÔNG BAO GIỜ ĐƯỢC CẬP NHẬT, do đó, nó sẽ tiếp tục trỏ đến cùngđối tượng xuyên suốt và thậm chí sau khi gọi hàm (ngay cả khi dữ liệu của đối tượng bị thay đổi hoặc nếu tham số hàm được gán hoàn toàn một đối tượng khác). Không thể kết luận rằng đối số hàm được truyền bằng tham chiếu chỉ vì đối tượng được tham chiếu có thể cập nhật thông qua biến tham số hàm.

Gọi / Chuyển qua tham chiếu Truyền qua : Giá trị của đối số hàm có thể / sẽ được cập nhật trực tiếp bởi tham số hàm tương ứng. Nếu nó giúp, tham số hàm trở thành một "bí danh" hiệu quả cho đối số - chúng thực sự tham chiếu đến cùng một giá trị tại cùng một vị trí bộ nhớ. Nếu một đối số hàm là một biến đối tượng, khả năng thay đổi dữ liệu của đối tượng không khác gì trường hợp truyền qua giá trị vì tham số hàm vẫn sẽ trỏ đến cùng một đối tượng như đối số. Nhưng trong trường hợp biến đối tượng, nếu tham số hàm được đặt thành một đối tượng hoàn toàn khác, thì đối số cũng sẽ trỏ đến đối tượng khác - điều này không xảy ra trong trường hợp truyền qua giá trị.

JavaScript không vượt qua bằng tham chiếu. Nếu bạn đọc kỹ, bạn sẽ nhận ra rằng tất cả các ý kiến ​​trái chiều hiểu sai ý nghĩa của giá trị truyền qua và họ kết luận sai rằng khả năng cập nhật dữ liệu của một đối tượng thông qua tham số hàm đồng nghĩa với "giá trị truyền qua".

Đối tượng sao chép / sao chép : Một đối tượng mới được tạo và dữ liệu của đối tượng ban đầu được sao chép. Đây có thể là một bản sao sâu hoặc bản sao nông, nhưng điểm quan trọng là một đối tượng mới được tạo ra. Tạo một bản sao của một đối tượng là một khái niệm riêng biệt với giá trị truyền qua. Một số ngôn ngữ phân biệt giữa đối tượng lớp và cấu trúc (hoặc tương tự) và có thể có hành vi khác nhau để truyền các biến của các loại khác nhau. Nhưng JavaScript không tự động làm bất cứ điều gì như thế này khi truyền các biến đối tượng. Nhưng sự vắng mặt của nhân bản đối tượng tự động không chuyển thành tham chiếu qua.


4

JavaScript chuyển các kiểu nguyên thủy theo giá trị và loại đối tượng bằng tham chiếu

Bây giờ, mọi người thích cãi nhau vô tận về việc "vượt qua tham chiếu" có phải là cách chính xác để mô tả những gì Java et al. thực sự làm Vấn đề là đây:

  1. Vượt qua một đối tượng không sao chép đối tượng.
  2. Một đối tượng được truyền cho một chức năng có thể có các thành viên của nó được sửa đổi bởi chức năng đó.
  3. Giá trị nguyên thủy được truyền cho hàm không thể được sửa đổi bởi hàm. Một bản sao được thực hiện.

Trong cuốn sách của tôi đó được gọi là đi qua tham chiếu.

- Brian Bi - Những ngôn ngữ lập trình nào được thông qua tham chiếu?


Cập nhật

Đây là một phản bác về điều này:

Không có "vượt qua tham chiếu" có sẵn trong JavaScript.


@Amy Bởi vì đó là mô tả vượt qua giá trị, không vượt qua tham chiếu. Câu trả lời này là một câu trả lời hay cho thấy sự khác biệt: stackoverflow.com/a/3638034/3307720
vào

@nasch Mình hiểu sự khác biệt. # 1 và # 2 đang mô tả ngữ nghĩa pass-by-ref. # 3 là mô tả ngữ nghĩa truyền qua giá trị.
Amy

@Amy 1, 2 và 3 đều phù hợp với việc truyền theo giá trị. Để vượt qua tham chiếu, bạn cũng cần 4: gán tham chiếu cho một giá trị mới bên trong hàm (với toán tử =) cũng gán lại tham chiếu bên ngoài hàm. Đây không phải là trường hợp với Javascript, làm cho nó vượt qua giá trị. Khi truyền một đối tượng, bạn chuyển một con trỏ đến đối tượng và bạn truyền con trỏ đó theo giá trị.
mũi

Đó thường không phải là những gì có nghĩa là "thông qua tham chiếu". Bạn đã thỏa mãn truy vấn của tôi và tôi không đồng ý với bạn. Cảm ơn.
Amy

"Trong cuốn sách của tôi đó được gọi là đi qua tham chiếu." - Trong mỗi cuốn sách biên dịch, sách thông dịch, sách lý thuyết ngôn ngữ lập trình và sách khoa học máy tính từng được viết, thì không.
Jörg W Mittag

3

Cách đơn giản của tôi để hiểu điều này ...

  • Khi gọi một hàm, bạn đang truyền nội dung (tham chiếu hoặc giá trị) của các biến đối số, chứ không phải chính các biến đó.

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2's content (reference) are being passed here
    foo(var1, var2); 
  • Bên trong hàm, các biến tham số inVar1inVar2nhận nội dung được truyền.

    function foo(inVar1, inVar2){
        //changing contents of inVar1 and inVar2 won't affect variables outside
        inVar1 = 20;
        inVar2 = { prop: 7 };
    }
  • Kể từ khi inVar2nhận được tham chiếu của { prop: 2 }, bạn có thể thay đổi giá trị thuộc tính của đối tượng.

    function foo(inVar1, inVar2){
        inVar2.prop = 7; 
    }

3

Truyền đối số cho một hàm trong JavaScript tương tự như truyền tham số theo giá trị con trỏ trong C:

/*
The following C program demonstrates how arguments
to JavaScript functions are passed in a way analogous
to pass-by-pointer-value in C. The original JavaScript
test case by @Shog9 follows with the translation of
the code into C. This should make things clear to
those transitioning from C to JavaScript.

function changeStuff(num, obj1, obj2)
{
    num = num * 10;
    obj1.item = "changed";
    obj2 = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);    
console.log(obj2.item);

This produces the output:

10
changed
unchanged
*/

#include <stdio.h>
#include <stdlib.h>

struct obj {
    char *item;
};

void changeStuff(int *num, struct obj *obj1, struct obj *obj2)
{
    // make pointer point to a new memory location
    // holding the new integer value
    int *old_num = num;
    num = malloc(sizeof(int));
    *num = *old_num * 10;
    // make property of structure pointed to by pointer
    // point to the new value
    obj1->item = "changed";
    // make pointer point to a new memory location
    // holding the new structure value
    obj2 = malloc(sizeof(struct obj));
    obj2->item = "changed";
    free(num); // end of scope
    free(obj2); // end of scope
}

int num = 10;
struct obj obj1 = { "unchanged" };
struct obj obj2 = { "unchanged" };

int main()
{
    // pass pointers by value: the pointers
    // will be copied into the argument list
    // of the called function and the copied
    // pointers will point to the same values
    // as the original pointers
    changeStuff(&num, &obj1, &obj2);
    printf("%d\n", num);
    puts(obj1.item);
    puts(obj2.item);
    return 0;
}

1
Tôi không nghĩ đây là trường hợp trong JavaScript: `` `javascript var num = 5;
Danail Nachev

@DanailNachev: Mặc dù điều đó có thể đúng về mặt kỹ thuật, sự khác biệt chỉ có thể quan sát được đối với các đối tượng có thể thay đổi mà nguyên thủy ECMAScript không có.
Jörg W Mittag

3

Đối với luật sư ngôn ngữ lập trình, tôi đã xem qua các phần sau của ECMAScript 5.1 (dễ đọc hơn phiên bản mới nhất) và đi xa như hỏi nó trong danh sách gửi thư ECMAScript.

TL; DR : Mọi thứ đều được truyền theo giá trị, nhưng các thuộc tính của Đối tượng là các tham chiếu và định nghĩa của Đối tượng là thiếu tiêu chuẩn.

Xây dựng danh sách đối số

Mục 11.2.4 "Danh sách đối số" cho biết như sau về việc tạo danh sách đối số chỉ bao gồm 1 đối số:

ArgumentList sản xuất: AssignExpression được đánh giá như sau:

  1. Đặt ref là kết quả của việc đánh giá AssignExpression.
  2. Đặt arg là GetValue (ref).
  3. Trả về một danh sách có mục duy nhất là arg.

Phần này cũng liệt kê các trường hợp trong đó danh sách đối số có 0 hoặc> 1 đối số.

Do đó, mọi thứ đều được thông qua bằng cách tham khảo.

Truy cập thuộc tính đối tượng

Mục 11.2.1 "Người truy cập tài sản"

Sản phẩm MemberExpression: MemberExpression [Expression] được đánh giá như sau:

  1. Đặt baseReference là kết quả của việc đánh giá MemberExpression.
  2. Đặt baseValue là GetValue (baseReference).
  3. Đặt propertyNameReference là kết quả của việc đánh giá Biểu thức.
  4. Đặt propertyNameValue là GetValue (propertyNameReference).
  5. Gọi CheckObjectCoercible (baseValue).
  6. Đặt propertyNameString là ToString (propertyNameValue).
  7. Nếu sản xuất cú pháp đang được đánh giá được chứa trong mã chế độ nghiêm ngặt, hãy để nghiêm ngặt là đúng, nếu không thì nghiêm ngặt là sai.
  8. Trả về giá trị của loại Tham chiếu có giá trị cơ sở là baseValue và tên được tham chiếu của nó là propertyNameString và có cờ chế độ nghiêm ngặt.

Do đó, các thuộc tính của Đối tượng luôn có sẵn như là tài liệu tham khảo.

Trên tài liệu tham khảo

Nó được mô tả trong phần 8.7 "Loại đặc tả tham chiếu", tham chiếu không phải là loại thực trong ngôn ngữ - chúng chỉ được sử dụng để mô tả hành vi xóa, loại và toán tử gán.

Định nghĩa của "Đối tượng"

Nó được định nghĩa trong phiên bản 5.1 rằng "Một đối tượng là một tập hợp các thuộc tính". Do đó, chúng ta có thể suy ra rằng giá trị của đối tượng là tập hợp, nhưng giá trị của bộ sưu tập được xác định kém trong thông số kỹ thuật là gì và đòi hỏi một chút nỗ lực để hiểu.


Tôi không bao giờ hết ngạc nhiên khi có bao nhiêu người bị nhầm lẫn bởi sự khác biệt giữa các đối số được truyền bởi giá trị, đối số được truyền bởi tham chiếu, hoạt động trên toàn bộ đối tượng và hoạt động trên các thuộc tính của chúng. Vào năm 1979, tôi đã không lấy được bằng về khoa học máy tính, thay vào đó là thêm 15 giờ hoặc hơn các môn tự chọn CS vào chương trình MBA của tôi. Tuy nhiên, tôi đã sớm nhận ra rằng việc tôi nắm bắt được các khái niệm này ít nhất cũng tốt như bất kỳ đồng nghiệp nào có bằng cấp về khoa học máy tính hoặc toán học. Học lắp ráp, và nó sẽ trở nên khá rõ ràng.
David A. Gray

3

Các tài liệu MDN giải thích rõ ràng mà không quá dài dòng:

Các tham số của một lời gọi hàm là các đối số của hàm . Các đối số được truyền cho các hàm theo giá trị . Nếu hàm thay đổi giá trị của một đối số, thay đổi này không được phản ánh trên toàn cầu hoặc trong hàm gọi. Tuy nhiên, các tham chiếu đối tượng cũng là các giá trị và chúng rất đặc biệt: nếu hàm thay đổi các thuộc tính của đối tượng được tham chiếu, thay đổi đó được hiển thị bên ngoài hàm, (...)

Nguồn: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Fiances#Description


1

Trong ngôn ngữ cấp thấp, nếu bạn muốn truyền một biến bằng tham chiếu, bạn phải sử dụng một cú pháp cụ thể trong việc tạo hàm:

int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
  *age = *age + 1;
}

Đây &agelà một tham chiếu đến myAge, nhưng nếu bạn muốn giá trị bạn phải chuyển đổi tham chiếu, sử dụng *age.

Javascript là một ngôn ngữ cấp cao thực hiện chuyển đổi này cho bạn. Vì vậy, mặc dù các đối tượng được truyền bằng tham chiếu, ngôn ngữ chuyển đổi tham số tham chiếu thành giá trị. Bạn không cần phải sử dụng &, trên định nghĩa hàm, để chuyển nó bằng tham chiếu, cả *trên thân hàm, để chuyển đổi tham chiếu thành giá trị, JS thực hiện điều đó cho bạn.

Đó là lý do tại sao khi bạn cố gắng thay đổi một đối tượng bên trong một hàm, bằng cách thay thế giá trị của nó (tức là age = {value:5}), sự thay đổi đó không tồn tại, nhưng nếu bạn thay đổi thuộc tính của nó (tức làage.value = 5 ), thì nó sẽ xảy ra.

Tìm hiểu thêm


1

Tôi đã đọc qua những câu trả lời này nhiều lần, nhưng KHÔNG THỰC SỰ hiểu được cho đến khi tôi biết về định nghĩa kỹ thuật của "Gọi bằng cách chia sẻ" như thuật ngữ của Barbara Liskov

Các ngữ nghĩa của cuộc gọi bằng cách chia sẻ khác với cuộc gọi theo tham chiếu trong đó gán cho các đối số chức năng trong hàm không hiển thị cho người gọi (không giống như ngữ nghĩa tham chiếu) [cần dẫn nguồn], vì vậy, nếu một biến được truyền, thì không thể để mô phỏng một nhiệm vụ trên biến đó trong phạm vi của người gọi. Tuy nhiên, do hàm có quyền truy cập vào cùng một đối tượng với người gọi (không có bản sao nào được thực hiện), nên các đột biến cho các đối tượng đó, nếu các đối tượng có thể thay đổi, trong hàm sẽ hiển thị cho người gọi, có thể khác với cuộc gọi theo giá trị ngữ nghĩa. Đột biến của một đối tượng có thể thay đổi trong hàm được hiển thị cho người gọi vì đối tượng không được sao chép hoặc nhân bản - nó được chia sẻ.

Đó là, tham chiếu tham số có thể thay đổi nếu bạn đi và truy cập vào chính giá trị tham số. Mặt khác, việc gán cho một tham số sẽ biến mất sau khi đánh giá và không thể truy cập được đối với người gọi hàm.


Không, cho dù một đối tượng có thể thay đổi hay không thực sự không phải là vấn đề. Mọi thứ luôn được thông qua bởi giá trị. Nó chỉ phụ thuộc vào những gì bạn đang vượt qua (một giá trị hoặc một tài liệu tham khảo). Xem này .
Scott Marcus

Những gì cô ấy mô tả là thông qua một tham chiếu BY-VALUE. Không có lý do để giới thiệu thuật ngữ mới.
Sanjeev

1

Cách đơn giản nhất

// Copy JS object without reference
var first = {"a":"value1","b":"value2"};
var clone = JSON.parse( JSON.stringify( first ) ); 

var second = ["a","b","c"];
var clone = JSON.parse( JSON.stringify( second ) ); 

JSON.parse( JSON.stringify( obj ) )là một cách khủng khiếp để các đối tượng nhân bản sâu. Không chỉ nó không chỉ chậm mà còn có thể gây mất dữ liệu.
D. Pardal

0

Tôi đã thấy phương thức mở rộng của thư viện Underscore.js rất hữu ích khi tôi muốn truyền vào một đối tượng dưới dạng tham số có thể được sửa đổi hoặc thay thế hoàn toàn.

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

   var newObj = new MyObject();
   // _.extend(destination, *sources) 
   _.extend(newObj, aObj);
  }
}

0

Lời giải thích ngắn gọn nhất mà tôi tìm thấy là trong hướng dẫn về phong cách AirBNB :

  • Nguyên thủy : Khi bạn truy cập một loại nguyên thủy, bạn làm việc trực tiếp với giá trị của nó

    • chuỗi
    • con số
    • boolean
    • vô giá trị
    • chưa xác định

Ví dụ:

var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • Phức tạp : Khi bạn truy cập một loại phức tạp, bạn làm việc trên một tham chiếu đến giá trị của nó

    • vật
    • mảng
    • chức năng

Ví dụ:

var foo = [1, 2],
    bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

Tức là các kiểu nguyên thủy có hiệu quả được truyền theo giá trị và các kiểu phức tạp được truyền bằng tham chiếu.


Không, mọi thứ luôn được thông qua bởi giá trị. Nó chỉ phụ thuộc vào những gì bạn đang vượt qua (một giá trị hoặc một tài liệu tham khảo). Xem này .
Scott Marcus

-1

Tôi muốn nói đó là pass-by-copy -

Xem xét các đối số và các đối tượng biến là các đối tượng được tạo trong bối cảnh thực thi được tạo khi bắt đầu gọi hàm - và giá trị / tham chiếu thực tế của bạn được truyền vào hàm chỉ được lưu trữ trong đối số + đối tượng biến này.

Nói một cách đơn giản, đối với các kiểu nguyên thủy, các giá trị được sao chép khi bắt đầu gọi hàm, đối với kiểu đối tượng, tham chiếu được sao chép.


1
"pass-by-copy" === vượt qua giá trị
Scott Marcus

-1

Có một số cuộc thảo luận về việc sử dụng thuật ngữ "vượt qua tham chiếu" trong JavaScript ở đây , nhưng để trả lời câu hỏi của bạn:

Một đối tượng được tự động chuyển qua tham chiếu, mà không cần phải nêu cụ thể

(Từ bài viết đã đề cập ở trên.)


7
Bài viết được liên kết không còn bao gồm các tuyên bố đó và tránh sử dụng "vượt qua tham chiếu" hoàn toàn.
C Perkins

Giá trị là một tham chiếu

-2

Một cách dễ dàng để xác định xem một cái gì đó có "vượt qua tham chiếu" hay không là liệu bạn có thể viết hàm "hoán đổi" hay không. Ví dụ: trong C, bạn có thể làm:

void swap(int *i, int *j)
{
    int t;
    t = *i;
    *i = *j;
    *j = t;
}

Nếu bạn không thể làm tương đương với JavaScript, thì đó không phải là "vượt qua tham chiếu".


21
Điều này không thực sự vượt qua tham chiếu. Bạn đang chuyển con trỏ vào hàm và những con trỏ đó đang được truyền theo giá trị. Một ví dụ tốt hơn sẽ là từ khóa & ref của C ++ hoặc từ khóa "ref" của C #, cả hai đều thực sự được chuyển qua tham chiếu.
Matt Greer

Thậm chí dễ dàng hơn là mọi thứ đều được thông qua bởi giá trị trong JavaScript.
Scott Marcus


-3
  1. biến kiểu nguyên thủy như chuỗi, số luôn được truyền dưới dạng giá trị.
  2. Mảng và đối tượng được truyền dưới dạng 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 đang 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ì nó sẽ được chuyển qua Giá trị.

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

    Ở đây bạn đang gán đối tượng hoặc mảng mới cho đối tượng cũ. Bạn không thay đổi giá trị thuộc tính của đối tượng cũ. Vì vậy, nó được truyền theo giá trị.

    • nếu bạn đang thay đổi một giá trị thuộc tính của một đối tượng hoặc mảng thì nó được truyền bởi Reference.

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

    Ở đây bạn đang thay đổi một giá trị thuộc tính của đối tượng cũ. Bạn không gán đối tượng hoặc mảng mới cho đối tượng cũ. Vì vậy, nó được truyền bằng tham chiếu.

    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

1
Toán tử gán không được nhầm lẫn với một lệnh gọi hàm. Khi bạn gán dữ liệu mới cho một biến hiện có, số tham chiếu của dữ liệu cũ sẽ giảm và dữ liệu mới được liên kết với biến cũ. Về cơ bản, biến kết thúc chỉ vào dữ liệu mới. Điều tương tự cũng đúng với các biến tài sản. Vì các bài tập này không phải là các lệnh gọi hàm nên chúng không liên quan gì đến pass-by-value hoặc pass-by-Reference.
John Sonderson

1
Không, mọi thứ luôn được thông qua bởi giá trị. Nó chỉ phụ thuộc vào những gì bạn đang vượt qua (một giá trị hoặc một tài liệu tham khảo). Xem này .
Scott Marcus

-3
  1. Nguyên thủy (số, Boolean, v.v.) được truyền theo giá trị.
    • Chuỗi là bất biến, vì vậy nó không thực sự quan trọng đối với họ.
  2. Các đối tượng được truyền bằng tham chiếu (tham chiếu được truyền theo giá trị).

Không, mọi thứ luôn được thông qua bởi giá trị. Nó chỉ phụ thuộc vào những gì bạn đang vượt qua (một giá trị hoặc một tài liệu tham khảo). Xem này .
Scott Marcus

Tuyên bố thứ hai của bạn là mâu thuẫn với chính nó.
Jörg W Mittag

-5

Các giá trị đơn giản bên trong các hàm sẽ không thay đổi các giá trị đó bên ngoài hàm (chúng được truyền theo giá trị), trong khi các giá trị phức tạp sẽ (chúng được truyền bằng tham chiếu).

function willNotChange(x) {

    x = 1;
}

var x = 1000;

willNotChange(x);

document.write('After function call, x = ' + x + '<br>'); // Still 1000

function willChange(y) {

    y.num = 2;
}

var y = {num: 2000};

willChange(y);
document.write('After function call y.num = ' + y.num + '<br>'); // Now 2, not 2000

đó là vô lý, y sẽ thay đổi vì phạm vi cấp độ chức năng, nó được nâng lên không phải vì nó được thông qua tham chiếu.
Parijat Kalia

Không, mọi thứ luôn được thông qua bởi giá trị. Nó chỉ phụ thuộc vào những gì bạn đang vượt qua (một giá trị hoặc một tài liệu tham khảo). Xem này .
Scott Marcus
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.